├── .htmlHash.txt ├── ChangeLog.md ├── LICENSE ├── OTASettings.bat ├── OTASettings.command ├── OTASettings.js ├── README.md ├── README_CN.md ├── ap ├── README.md ├── ab.js ├── apIndex.html ├── hash.js ├── hash.min.js ├── main.js ├── main.min.js ├── out.txt └── tools.js ├── app ├── README.md └── empty_example │ ├── README.md │ └── app │ ├── app.cpp │ └── app.h ├── build.bat ├── build.command ├── build.js ├── directory_structure.md ├── doc ├── README.md ├── extented_command_specification.txt └── get_method.js ├── examples ├── README.md ├── aes_256_cbc_example │ ├── README.md │ └── app │ │ ├── app.cpp │ │ └── app.h ├── arraybuffer_example │ ├── README.md │ └── app │ │ ├── app.cpp │ │ └── app.h ├── blink_example │ ├── README.md │ └── app │ │ ├── app.cpp │ │ └── app.h ├── database_example │ ├── README.md │ └── app │ │ ├── app.cpp │ │ └── app.h ├── element_example │ ├── README.md │ └── app │ │ ├── app.cpp │ │ └── app.h ├── empty_example │ ├── README.md │ └── app │ │ ├── app.cpp │ │ └── app.h ├── ota_example │ ├── README.md │ └── app │ │ ├── app.cpp │ │ └── app.h ├── provider_example │ ├── README.md │ └── app │ │ ├── app.cpp │ │ └── app.h ├── sha_digest_example │ ├── README.md │ └── app │ │ ├── app.cpp │ │ └── app.h └── web_serial_example │ ├── README.md │ └── app │ ├── app.cpp │ └── app.h ├── firmware └── README.md ├── lib ├── arraybuffer │ ├── README.md │ └── arraybuffer.hpp ├── config │ ├── README.md │ └── config.h ├── esp32time │ ├── esp32time.cpp │ └── esp32time.h ├── globalmanager │ ├── globalmanager.cpp │ └── globalmanager.h ├── languages │ └── languages.h ├── mycrypto │ ├── README.md │ ├── mycrypto.cpp │ └── mycrypto.h ├── mydb │ ├── README.md │ ├── mydb.cpp │ └── mydb.h ├── myfs │ ├── myfs.cpp │ └── myfs.h ├── mynet │ ├── mynet.cpp │ └── mynet.h ├── mywebsocket │ ├── README.md │ ├── mywebsocket.cpp │ └── mywebsocket.h ├── ota │ ├── README.md │ ├── ota.cpp │ └── ota.h ├── provider │ ├── provider.cpp │ └── provider.h └── softtimer │ ├── softtimer.h │ └── softtimr.cpp ├── partition ├── README.md └── partition.csv ├── platformio.ini ├── scripts ├── .htmlHash.txt ├── ab.js ├── autoOTA.js ├── copyFirmware.js ├── hash.js ├── littlefsbuilder.py └── replaceHtml.js ├── server ├── README.md ├── ab.js ├── ab.min.js ├── aes.js ├── aes.min.js ├── blacklist.json ├── create.js ├── device.js ├── device.min.js ├── globalConfig.json ├── hash.js ├── hash.min.js ├── index.html ├── index.js ├── index.min.js ├── iot.js ├── pro │ └── index.html ├── source │ └── index.html ├── start.bat ├── start.command ├── tools.bat ├── tools.command └── tools.js ├── src └── app │ ├── README.md │ ├── app.cpp │ └── app.h ├── test └── test.cpp ├── todo_list.txt └── tools ├── mklittlefs └── mklittlefs.exe /.htmlHash.txt: -------------------------------------------------------------------------------- 1 | 7864b065b5153d14460ffe001a2eea69e30412ca1013f6bc830a79289f51683e -------------------------------------------------------------------------------- /ChangeLog.md: -------------------------------------------------------------------------------- 1 | 2024/05/01 2 | 3 | English: 4 | 1. Fix bug of build tool. 5 | 2. Now support ESP32-C3. 6 | 7 | 中文: 8 | 9 | 1. 修复了构建工具的bug。 10 | 2. 现在支持ESP32-C3。 11 | 12 | 13 | 2023/09/03 14 | 15 | English: 16 | 1. Bug fixed: path indexing error for S3. 17 | 18 | 中文: 19 | 1. 错误已修复: S3的路径索引错误。 20 | 21 | 2023/09/03 22 | 23 | English: 24 | 1. An auotomatic function added to the codes, it could help you to convert old file format to new. 25 | Check tail of "config.h". 26 | (Better backup database and files before you do this) 27 | 2. ESP32-S3 support added, the default ESP32-S3 env added to the "platformio.ini". 28 | 3. A bug fixed. This bug could make app loop won't run. 29 | 30 | 中文: 31 | 1. 向代码中添加了一个函数可以自动转换旧版本的数据,查看"config.h"的尾部。(最好在转换前先备份数据库和文件) 32 | 2. 现已支持ESP32-S3,默认的ESP32-S3环境参数已添加到 "platformio.ini"。 33 | 3. 修复了一个错误,此错误可能会导致esp32的app loop不会运行。 34 | 35 | 36 | 2023/04/02 37 | 38 | English: 39 | 40 | 1. Unify mark for transfer and type of class Element. 41 | 2. Change function name from "webSerial" to "sendMessageToClient" of class GlobalManager. 42 | 3. Change argument type of "sendMessageToClient" to Element&, which is more convenient to use. New usage: "global->sendMessageToClient(100); global->sendMessageToClient(3.14); global->sendMessageToClient("Hello world!"); ...". 43 | 4. Change bytes format of Element from custom to little endian, attenation, this is NOT compatible with former version, MyDB will be interacted. 44 | 5. Because of bytes format changed, there will be a function added after, it could update former version. 45 | 46 | 中文: 47 | 48 | 1. 统一了用于传输的标志和类 Element 的类型。 49 | 2. 类 GlobalManager 的成员函数 "webSerial" 的名称修改为 "sendMessageToClient"。 50 | 3. "sendMessageToClient" 参数类型修改为 Element& , 这可以让使用更加方便。 51 | 4. 更改了类 Element 存储数据的字节序,由自定义字节序修改为小段存储,注意,这个修改不兼容以前的版本,内置的数据库组件将受到影响。 52 | 5. 由于更改了字节序,之后会添加一个函数用于更新已存储的数据到新的版本。 53 | 54 | 2023/03/30 55 | 56 | English: 57 | 58 | 1. Change data format to standard little endian. 59 | 2. ETYPE_STRING will copy last '\0'. 60 | 3. Move part of processes of create buffer and decode buffer to class Element. 61 | 4. Data processing of JS had been synchronized with cpp. 62 | 63 | 中文: 64 | 65 | 1. 修改数据格式为标准小端存储。 66 | 2. 字符串类型现在会拷贝末尾的 '\0'。 67 | 3. 编解码 Elements 的一部分处理过程移动到类 Element。 68 | 4. JS的数据处理过程现已与C++同步。 69 | 70 | 2023/03/28 71 | 72 | English: 73 | 74 | 1. Bug fixed: 75 | * Non-copy mode of class Element for buffer. 76 | 77 | 2023/03/26 78 | 79 | English: 80 | 81 | 1. Remove redundant function "getU8ALen" and "getBufferLength" of class Element, use "getRawBufferLength" to instead. 82 | 2. Replace data type from uint64_t to uint32_t of static functions in class ArrayBuffer. 83 | 84 | 中文: 85 | 86 | 1. 移除了冗余的成员函数 "getU8ALen" 和 "getBufferLength",使用 "getRawBufferLength" 来代替。 87 | 2. 类 ArrayBuffer 中的静态函数的数据类型由 uint64_t 改为 uint32_t。 88 | 89 | 2023/03/24 90 | 91 | English: 92 | 93 | 1. Reduce RAM cost from 24 bytes to 16 bytes of class Element by reorder member sequence. 94 | 2. Remove member "bufferLength", use union data member to hold length of buffer. 95 | 96 | 中文: 97 | 98 | 1. 通过重新对成员排序,使类 Element 的栈内存消耗由24字节缩小为16字节。 99 | 2. 移除了数据成员 bufferLength,改为使用联合体成员实现同样的功能。 100 | 101 | 2023/03/24 102 | 103 | English: 104 | 105 | 1. Bug fixed: 106 | * Remove "\r\n" in /scripts/replaceHtml.js to avoid too many empty lines in globalmmanager.h following compile time. 107 | * Fix SHA logical error in class Element. 108 | 109 | 2. Add convenient functions of Base64, SHA256 and AES to class Element. 110 | 3. Add more unit test to /test/test.cpp. 111 | 112 | 113 | 中文: 114 | 115 | 1. 错误修复: 116 | * 移除了 /scripts/replaceHtml.js 中多余的 "\r\n" 来避免文件 globalmanager.h 中编译时间后多余的空行。 117 | * 修复了 类 Element 中 SHA 的逻辑错误。 118 | 119 | 2. 给 类 Element 增加了方便的Base64、SHA256、AES 相关函数。 120 | 3. 添加了更多的单元测试到 /test/test.cpp。 121 | 122 | 2023/03/22 123 | 124 | English: 125 | 126 | 1. Add all types supported to class Element, u8, i8, u16, i16, u32, i32, u64, i64, float, double, string, buffer. Some name of enum were changed. Reference to arraybuffer.hpp. 127 | 2. Add convenient method to support Android WebView to get custom ID, Android Studio example project will release later. 128 | 129 | 130 | 中文: 131 | 132 | 1. 给类 Element 添加了所有数据类型的支持,单字节、双字节、四字节的有符号和无符号整数,单精度浮点、双精度浮点,字符串和二进制数组。 修改了某些枚举的名称。 133 | 2. 添加了方便的方法来给安卓WebView快速获取自定义ID,Android Studio 示例工程后续会上传。 134 | 135 | 2023/02/07 136 | 137 | English: 138 | 139 | 1. Fix bug that AP won't start on new device after firmware uploaded. 140 | 141 | 中文: 142 | 143 | 1. 修复了bug: 新设备在烧录固件后AP不会开启。 144 | 145 | 2023/02/05 146 | 147 | English: 148 | 149 | 1. Add auto sync function, which means you could modify source file in /app/appName, it will be copied to /src/app automatically. 150 | 2. Fix bug of web client and add cr, lf, crlf or none to serial mode. The cr, lf, crlf will be appended to content automatically. 151 | 3. Add timeout check process to auto OTA update, the auto OTA update process will be restart when error detected. 152 | 4. Fix bug of tools "replaceHtml.js". 153 | 5. Update version of main system to 42.1.0. Refer to /lib/config/config.h. 154 | 6. Add quick interface to global manager to set RX buffer size of serial0. 155 | 7. Remove a repeat function of global manager "inline uint16_t getRemoteServerOfflineDetectedTimes()". Use "inline bool serverOnline(uint8_t serverStatus = 0)" to instead. 156 | 8. Add feature to let user manually force system won't start AP in any situations. Be careful to use this function, or the system will NOT enable AP when sta connection failed or other status, this will reduce heat from SoC which help to other sensor that sensitive to temperature. 157 | 9. The time of firmware compiled will be copied to /lib/globalmanager/globalmanager.h automatically, it will shows in web client to let user can get time info. 158 | 10. Fix bug that AP won't close automatically. 159 | 11. Fix bug that transfer data from hardware serial to web client. It will use stack to hold data when size of data less than 128 bytes, or it will use heap to hold data. The maximum size of data defined as 81916 bytes, which is large enough. 160 | 161 | Conclusion: Currently the whold system running with no error, it is stable. 162 | 163 | 中文: 164 | 165 | 1. 增加了源代码自动同步辅助功能,在/app/app名称下修改源代码会自动同步到/src/app下。 166 | 2. 修复了前端的bug,给serial数据转发增加了在末尾自动添加特殊字符的功能,可以选择cr、lf、crlf或无特殊字符。 167 | 3. 给自动OTA升级功能增加了超时检测功能,自动OTA在发生错误超时 时会自动重新对设备发起升级请求。 168 | 4. 修复了工具"replaceHtml.js"中的路径引用错误。 169 | 5. 更新系统版本号到 "42.1.0",在头文件/lib/config/config.h中。 170 | 6. 增加了快速设置硬件串口RX缓冲区大小的功能。 171 | 7. 移除了一个globalmanager冗余的函数"inline uint16_t getRemoteServerOfflineDetectedTimes()",现在使用"inline bool serverOnline(uint8_t serverStatus = 0)"代替。 172 | 8. 增加了功能可以让用户手动执行强制禁止开启AP,使用此功能系统在**任何状态下**都**不会**开启AP,这会减少SoC的热量,以减少对周围距离较近的对温度敏感的传感器的影响。 173 | 9. 固件的编译时间会被自动填充到头文件/lib/globalmanager/globalmanager.h中,这个时间可以在前端看到,方便确定固件的具体编译时间。 174 | 10. 修复了AP不会自动关闭的bug。 175 | 11. 修复了串口数据转发的bug,当转发的数据大小小于128字节时,将使用栈空间存放数据,否则将使用堆空间存储数据,最大数据量被定义为81916字节,这足够大了。 176 | 177 | 结论: 当前系统运行无任何错误,非常稳定。 178 | -------------------------------------------------------------------------------- /OTASettings.bat: -------------------------------------------------------------------------------- 1 | node OTASettings.js -------------------------------------------------------------------------------- /OTASettings.command: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | cd $(dirname $0) 3 | node OTASettings.js -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Abc 2 | 3 | [中文](https://github.com/vidalouiswang/Abc/blob/main/README_CN.md) 4 | 5 | ## A basic ESP32 library for beginners 6 | 7 | ## Support ESP32/ESP32-S3/ESP32-C3 8 | 9 | ### At present, the ESP32 firmware built with this library has been running stably for several months and can be used officially. 10 | 11 | ### Note: Clone the code from main branch. 12 | 13 | ## Brief 14 | 15 | This library work with Espressif offical framework esp32-arduino 2.0.3. 16 | 17 | This library is for beginners who want to write their own code, but don't have much foundation. This library can be your starting point for learning esp32, you can use not only all the functions of esp32-arduino, but also esp-idf function, you can learn how to develop ESP32 from simple to further. 18 | 19 | ### Features 20 | 21 | * Built-in convenient, **fast and stable OTA update feature without even one line extra code**. 22 | * Rollback firmware when new frimware error at boot state automatically(esp32-arduino library closed this function from esp-idf, **this library implemented this feature without modify SDK that downloaded by Platform IO**). 23 | * Easy method to store or transfer integer, string, binary data without other format(like json), it will save more space or bandwidth when you storing those data into storage or transfer with internet. 24 | * Built-in convenient RAM database could store key-value typed data, based on LittleFS(esp-idf has this function but it don't has wear-leveling, according to offical document). 25 | * Stable and fast Websocket implement, **transfer a great many of data with high stability**. 26 | * One line code to get SHA1 and SHA256 using ESP32 hardware acceleration with variant types of input data. 27 | * One line code to encrypt or decrypt data with AES-256-CBC. 28 | * Develop **without connect your board to computer**, only provide power supply, use built-in fully automatic OTA update, this allows you could continue developing when you use light laptop or your computer doesn't support more USB port at that time, directly connect your develop board into any power. This feature also fits in those ESP32 had embedded into another devices, cooperate with auto rollback feature, this allows you to do more things with less works. 29 | * Transfer data from web page to serial or transfer data from serial to web page without coding, default settings use the first group of serial port, you can modify it if you like to, or transfer data from(to) more serial port simultaneously. 30 | * More features are in development... 31 | 32 | These features especially designed for beginners, easy to use and have strong scalability. For experts, please ignore. 33 | 34 | # How to use 35 | 36 | IDE requirement: Visual studio code with Platform IO installed. 37 | 38 | First you need to have a VPS, node.js 16 required. 39 | Or you could use your PC or NAS as a server. 40 | 41 | ### Step 1: Clone 42 | 43 | ```console 44 | git clone https://github.com/vidalouiswang/Abc.git 45 | ``` 46 | 47 | ### Step 2: Setup Server 48 | 49 | 1. Use SSH connect to your VPS or NAS 50 | 51 | 2. Install package manager , suppose your OS is Ubuntu 52 | 53 | ```console 54 | sudo apt-get install npm 55 | ``` 56 | 57 | 3. Install n module 58 | ```console 59 | sudo npm install -g n 60 | ``` 61 | 62 | 4. Install Node.js 63 | ```console 64 | sudo n stable 65 | ``` 66 | 67 | 5. Install pm2 68 | ```console 69 | sudo npm install -g pm2 70 | ``` 71 | 72 | Now the version of node.js is lastest stable version, you could use the following command to check 73 | 74 | ```console 75 | node -v 76 | ``` 77 | 78 | If shows "v16.x" means Node.js installed correctly, if it show old version, like "v8.x", you should input command 79 | 80 | ```console 81 | hash -r 82 | ``` 83 | 84 | 6. If you clone the code into your personal computer, you should upload 85 | /server/ 86 | - index.html 87 | - ab.js 88 | - hash.js 89 | - create.js 90 | - iot.js 91 | 92 | These five files to your server 93 | 94 | ```console 95 | cd ~/ 96 | ``` 97 | ```console 98 | mkdir server 99 | ``` 100 | ```console 101 | cd server 102 | ``` 103 | 104 | Then upload five files by any method you like to the folder "server" 105 | 106 | This step could be omit if you clone the code directly to your server, do this: 107 | 108 | ```console 109 | cd Abc/server 110 | ``` 111 | 112 | 7. Install components 113 | ```console 114 | sudo npm install ws 115 | ``` 116 | 117 | 8. Start server 118 | ```console 119 | sudo pm2 start iot.js 120 | ``` 121 | 122 | Now the server configuration is finished, the default port is 12345 , you could modify it if you like, 123 | 124 | in file "globalConfig.json". 125 | 126 | Or, modify it at the bottom in file "iot.js". 127 | 128 | Remember restart server if you modified the port. 129 | 130 | ```console 131 | sudo pm2 restart iot.js 132 | ``` 133 | 134 | Don't forget add rule to let new port could pass through in firewall. 135 | 136 | ### Step 3: Local Use 137 | 138 | 1. Use Platform IO open root folder. 139 | 2. Locate to /src/app/ . 140 | 3. Edit app.h and app.cpp as Arduino way. 141 | 4. Compile and upload(better erase flash first). 142 | 5. Use PC or phone connect to esp32 access point. 143 | 6. Open browser,locate to http://192.168.8.1. 144 | 7. Set arguments by tips and click reboot. 145 | 8. Locate to http://your_domain_or_ip_of_your_server:port/. 146 | 9. Login in with user name and password set in step 7. 147 | 10. Now you could see your device online, click "@" could access built-in providers. 148 | 149 | More information and examples refer to /examples/ , full comments in it. 150 | 151 | ### Check header files for basic information, configs locate to /lib/config/config.h 152 | 153 | ### The LittleFS extra component auto-selection feature has not been tested on Windows, but it should work and currently works fine on macOS 12.5. 154 | 155 | ### More tutorials and documentation are in the works. 156 | 157 | ### If you still don't know how to use, I will make a video and upload to YouTube, the link will be put right here. 158 | 159 | ### Code comments status(full and detailed comments): 160 | 161 | * Local: 162 | 163 | * Header file: 164 | 165 | * Almost all header files 166 | * /examples/*.h 167 | 168 | * Source File: 169 | 170 | * /lib/arraybuffer/arraybuffer.cpp 171 | * /examples/*.cpp 172 | 173 | * Server: 174 | 175 | * /server/iot.js 176 | * /server/create.js 177 | 178 | # Thanks to 179 | 180 | Thanks to the entire staff of Espressif, we have a cheap and easy-to-use ESP32 and other chips. 181 | Thanks to all the developers of Node.js, we have a convenient and easy-to-use JavaScript runtime. 182 | Thanks to all the developers of VSCode and Platform IO, we have an easy-to-use integrated development environment. 183 | 184 | [Espressif](https://github.com/espressif) 185 | 186 | [Node.js](https://github.com/nodejs) 187 | 188 | [Visual Studio Code](https://github.com/microsoft/vscode) 189 | 190 | [Platform IO](https://github.com/platformio) 191 | 192 | [ws](https://github.com/websockets/ws) 193 | 194 | [Brix](https://github.com/brix/crypto-js) 195 | 196 | [Felix Biego](https://github.com/fbiego/ESP32Time) 197 | 198 | # License 199 | 200 | (GNU General Public License v3.0 License) 201 | 202 | Copyright 2022, Vida Wang 203 | 204 | Children are the future of mankind, but there are still many children who are enduring hunger all over the world at this moment. If you are a good person and you like this lib, please donate to UNICEF. 205 | https://unicef.org 206 | -------------------------------------------------------------------------------- /README_CN.md: -------------------------------------------------------------------------------- 1 | # Abc 2 | 3 | ## 一个为新手打造的 esp32 基础库 4 | 5 | ## 支持ESP32/ESP32-S3/ESP32-C3 6 | 7 | ### 目前,使用该库构建的 ESP32 固件已经稳定运行了数月,可以正式使用了。 8 | 9 | ### 备注: 从主分支克隆代码。 10 | 11 | ## 简介 12 | 13 | 这个库和乐鑫官方 esp32-arduino 2.0.3 库一起使用。 14 | 这个库是为了那些想要自己编写代码,但是却又没有太多基础的新手打造的,这个库可以成为你学习 esp32 的起点,你不仅可以使用 esp32-arduino 的所有功能,也能使用 esp-idf 的功能,可以由浅入深的学习如何开发 ESP32。 15 | 16 | ### 功能 17 | 18 | * 内置了方便、快速且稳定的 OTA 升级功能,**无需任何一行额外代码即可使用**。 19 | * 存在故障的固件可以自动回滚,再也不用担心与安装到电饭锅里的 ESP32 失联( esp-idf 有这个功能,但是 esp32-arduino 库把这个功能关闭了,**本库无需修改自动下载的 SDK 即可实现这个功能**),这可以让你在不同开发机器之间快速的同步代码而无需担心出错。 20 | * 非常简单的方法就可以存储和传输整数、字符串、二进制数据而无需再使用其他格式( 比如 json ),这可以让你在存储数据和在网络上进行传输时节省许多空间和带宽。 21 | * 内置了便于使用的内存数据库,使用 LittleFS 进行存储( esp-idf 有这个功能,但是根据官方文档没有磨损均衡)。 22 | * 稳定且快速的 Websocket 实现,**可以非常方便的传输大量数据且具备高稳定性**。 23 | * 一行代码就能用的 SHA1,SHA256 数字摘要功能,基于 ESP32 硬件加速实现,可使用多种不同种类的数据作为输入。 24 | * 一行代码就能用的 AES-256-CBC 加密与解密功能,基于 ESP32 硬件加速实现。 25 | * **无需连接到电脑**,只需要给开发板供电,即可快速开发烧录,使用内置的全自动 OTA 升级功能,当使用轻薄笔记本开发或电脑 USB 接口不够用,暂时又没有拓展坞时,可以直接把开发板插到其他电源上(比如移动电源),这可以让你从容的继续进行开发。此功能同样适用于已部署到设备内的 ESP32,配合故障自动回滚功能开发效率事半功倍。 26 | * 无需一行代码即可把数据从串口转发到前端,或从前端把数据转发到串口,开箱即用,默认使用第一组串口,即串口0,可以根据自己的需要修改到其他串口,或同时转发多个串口。 27 | * 更多功能正在开发中... 28 | 29 | 以上功能均是为新手开发的,使用方便扩展性强,高手请忽略。 30 | 31 | # 如何使用 32 | 33 | ### 各种配置以及教学系列视频: https://space.bilibili.com/4733242 34 | 35 | 开发环境: 安装了 Platform IO 的 Visual studio code. 36 | 37 | 首先你需要有一台 VPS,安装了 node.js 16。 38 | 或者你也可以用你的电脑或 NAS 当做服务器。 39 | 40 | ### 第一步:克隆代码 41 | 42 | ```console 43 | git clone https://github.com/vidalouiswang/Abc.git 44 | ``` 45 | 46 | ### 第二步:配置服务器 47 | 48 | 1. 使用SSH连接你的VPS 49 | 50 | 2. 安装 包管理器 , 假设你的系统是 Ubuntu 51 | 52 | ```console 53 | sudo apt-get install npm 54 | ``` 55 | 56 | 3. 安装n模块 57 | ```console 58 | sudo npm install -g n 59 | ``` 60 | 61 | 4. 安装 Node.js 62 | ```console 63 | sudo n stable 64 | ``` 65 | 66 | 5. 安装 pm2 67 | ```console 68 | sudo npm install -g pm2 69 | ``` 70 | 71 | 现在你的node.js应该已经处于最新稳定版了,可以输入如下命令查看 72 | 73 | ```console 74 | node -v 75 | ``` 76 | 77 | 如果出现 "v16.x" 证明安装已经完成了,如果依然显示旧版本,比如 "v8.x",你可以输入如下命令 78 | 79 | ```console 80 | hash -r 81 | ``` 82 | 83 | 6. 如果你的代码克隆到你的个人电脑,你需要上传 84 | /server/ 85 | - index.html 86 | - ab.js 87 | - hash.js 88 | - create.js 89 | - iot.js 90 | 91 | 这五个文件到你的服务器,在SSH中按如下步骤操作 92 | 93 | ```console 94 | cd ~/ 95 | ``` 96 | ```console 97 | mkdir server 98 | ``` 99 | ```console 100 | cd server 101 | ``` 102 | 103 | 然后把上面的五个文件使用你喜欢的方式上传到server文件夹下 104 | 105 | 如果你直接将代码克隆到服务器则这一步可以省略,直接 106 | ```console 107 | cd Abc/server 108 | ``` 109 | 110 | 7. 安装基础组件 111 | ```console 112 | sudo npm install ws 113 | ``` 114 | 115 | 8. 开启服务器 116 | ```console 117 | sudo pm2 start iot.js 118 | ``` 119 | 120 | 服务器已经配置完毕,默认端口为 12345, 可以自己修改,配置在 "globalConfig.json" 中 121 | 122 | 也可以直接在代码文件 iot.js 最底部修改 123 | 124 | 如果修改了端口记得重新启动服务器 125 | ```console 126 | sudo pm2 restart iot.js 127 | ``` 128 | 129 | 请注意在防火墙放行相应端口 130 | 131 | ### 第三步:本地使用 132 | 133 | 1. 使用 Platform IO 打开根目录。 134 | 2. 定位到路径 /src/app。 135 | 3. 然后编辑 app.h 和 app.cpp,编程方式和 arduino 一样,在函数 setup 和 loop 中添加代码即可。 136 | 4. 编译上传固件(首次使用最好先擦除flash)。 137 | 5. 使用电脑或手机搜索esp32热点,然后连接。 138 | 6. 打开浏览器,进入 http://192.168.8.1。 139 | 7. 根据提示设置参数然后点击重启。 140 | 8. 打开 http://你的服务器ip或域名:端口/。 141 | 9. 使用在第7步中设置的用户明和密码登录。 142 | 10. 你可以看到在线的设备,点击 "@" 可以访问内置Provider。 143 | 144 | 其他功能请参考示例,所有示例均有详细的中英双语注释,也可关注本人B站账号,会不定期发布新的视频。 145 | 146 | ### 参考头文件查看基础信息与各种配置,在 /lib/config/config.h。 147 | 148 | ### LittleFS 额外组件自动选择功能还未在Windows环境测试过,但是它应该可以工作,目前在macOS 12.5工作正常。 149 | 150 | ### 更多教程和文档正在编写中。 151 | 152 | ### 代码注释状态(全部的、详细的双语注释,其他文件也有注释,一般都是开发时用的简单英文注释): 153 | 154 | * 本地: 155 | 156 | * 头文件: 157 | 158 | * 几乎所有的头文件 159 | * 示例的所有头文件 160 | 161 | * 源文件: 162 | 163 | * /lib/arraybuffer/arraybuffer.cpp 164 | * 示例的所有源文件 165 | 166 | * 服务器 167 | 168 | * /server/iot.js 169 | * /server/create.js 170 | 171 | # 感谢 172 | 173 | 感谢乐鑫的全体工作人员,让我们拥有了便宜好用的ESP32以及其他芯片。 174 | 感谢Node.js的全体开发者,让我们拥有了便捷好用的JavaScript运行时。 175 | 感谢VSCode和Platform IO的全体开发者,让我们拥有了好用的集成开发环境。 176 | 177 | [Espressif](https://github.com/espressif) 178 | 179 | [Node.js](https://github.com/nodejs) 180 | 181 | [Visual Studio Code](https://github.com/microsoft/vscode) 182 | 183 | [Platform IO](https://github.com/platformio) 184 | 185 | [ws](https://github.com/websockets/ws) 186 | 187 | [Brix](https://github.com/brix/crypto-js) 188 | 189 | [Felix Biego](https://github.com/fbiego/ESP32Time) 190 | 191 | # 协议 192 | 193 | (GNU General Public License v3.0 License) 194 | 195 | Copyright 2022, Vida Wang 196 | 197 | 孩子是人类的未来,但是现在全世界仍然有许多孩子饱受饥饿,如果你是个善良的人、认可我的代码,请捐助联合国儿童基金会,谢谢。 198 | https://unicef.cn 199 | -------------------------------------------------------------------------------- /ap/README.md: -------------------------------------------------------------------------------- 1 | This folder contains files for AP mode web server. 2 | You can modify apIndex.html and main.js, and then minify them into xxx.min.js. 3 | Before you build firmware, the script will combine js and html into out.html and out.txt, and replace it to globalmanager.h. 4 | 5 | 这个文件夹包含了AP模式前端使用的文件。 6 | 你可以修改apIndex.html和main.js,然后把它们精简到 xxx.min.js。 7 | 在你构建固件的时候脚本会合成 js 和 html 到 out.html 和 out.txt,然后替换globalmanager.h中的内容。 -------------------------------------------------------------------------------- /ap/ab.js: -------------------------------------------------------------------------------- 1 | (function(){function e(e){for(let t=0;t=0?n<256?(o.push(r),o=e({arr:o,byteLength:1,number:n,type:"8"})):n>255&&n<65536?(o.push(a),o=e({arr:o,byteLength:2,number:n,type:"16"})):n>65535&&n<4294967296?(o.push(g),o=e({arr:o,byteLength:4,number:n,type:"32"})):(o.push(h),o=e({arr:o,byteLength:8,number:n,type:"64"})):n>=-128?(o.push(b),o=e({arr:o,byteLength:1,number:n,type:"8"})):n<-128&&n>=-32768?(o.push(f),o=e({arr:o,byteLength:2,number:n,type:"16"})):n<-32768&&n>=-2147483648?(o.push(i),o=e({arr:o,byteLength:4,number:n,type:"32"})):(o.push(y),o=e({arr:o,byteLength:8,number:n,type:"64"}));else{let t=n.toString().length-1;t>5?(o.push(l),o=e({arr:o,byteLength:8,number:n,type:"d"})):(o.push(p),o=e({arr:o,byteLength:4,number:n,type:"f"}))}}else if(0<=typeName.indexOf("bigint"))o.push(h),o=e({arr:o,byteLength:8,number:t[L],type:"64"});else if(0<=typeName.indexOf("uint8array")){o.push(u),o=e({arr:o,byteLength:4,number:t[L].length,type:"32"});for(let e of t[L])o.push(e)}else if(0<=typeName.indexOf("string")){o.push(s);let n=(new TextEncoder).encode(t[L]);o=e({arr:o,byteLength:4,number:n.length+1,type:"32"});for(let e of n)o.push(e);o.push(0)}return new Uint8Array(o).buffer}function n(e,t,n,o,L){if(!e)return;if(!e.byteLength)return;let c,m=[],d=e=>n?{}:[];try{c=new DataView(e)}catch(e){return d()}let U=0;for(;U247&&(t-=256),m.length&&o)break;switch(t){case r:case b:if(++U,e.byteLength-U<1)return d();if(L?m.push({data:t==r?c.getUint8(U):c.getInt8(U),offset:U,length:1}):m.push(t==r?c.getUint8(U):c.getInt8(U)),++U,o)break;break;case a:case f:if(++U,e.byteLength-U<2)return d();L?m.push({data:t==a?c.getUint16(U,!0):c.getInt16(U,!0),offset:U,length:2}):m.push(t==a?c.getUint16(U,!0):c.getInt16(U,!0)),U+=2;break;case g:case i:if(++U,e.byteLength-U<4)return d();L?m.push({data:t==g?c.getUint32(U,!0):c.getInt32(U,!0),offset:U,length:4}):m.push(t==g?c.getUint32(U,!0):c.getInt32(U,!0)),U+=4;break;case h:case y:if(++U,e.byteLength-U<8)return d();L?m.push({data:t==h?c.getBigUint64(U,!0):c.getBigInt64(U,!0),offset:U,length:8}):m.push(t==h?c.getBigUint64(U,!0):c.getBigInt64(U,!0)),U+=8;break;case p:case l:if(++U,e.byteLength-U<(t==p?4:8))return d();L?m.push({data:m.push(t==p?c.getFloat32(U,!0):c.getFloat64(U,!0)),offset:U,length:t==p?4:8}):m.push(t==p?c.getFloat32(U,!0):c.getFloat64(U,!0)),U+=t==p?4:8;break;case u:if(++U,e.byteLength-U<4)return d();let n=c.getUint32(U,!0);if(U+=4,e.byteLength-Ue.byteLength){m.push({offset:U,length:n});break}let w=new Uint8Array(n);for(let e=0;em.length-1?w[n[e]]=null:w[n[e]]=m[e];return w}const r=1,a=2,g=4,h=8,u=10,s=9,b=-1,f=-2,i=-4,y=-8,p=6,l=7;"object"==typeof window?(window.createArrayBuffer=t,window.decodeArrayBuffer=n):module.exports={createArrayBuffer:t,decodeArrayBuffer:n}})(); -------------------------------------------------------------------------------- /ap/apIndex.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Abc 6 | 7 | 8 | 57 | 58 | 59 | 60 |
61 |
62 |
63 |
64 | 65 |
66 |
67 | 68 |
69 |
70 |
71 |
72 | 73 |
74 |
75 | 76 |
77 |
78 |
79 |
80 | 81 |
82 |
83 | 84 |
85 |
86 | 87 |
88 |
89 |
90 |
91 | 92 |
93 |
94 | 95 |
96 |
97 |
98 |
99 | 100 |
101 |
102 | 103 |
104 |
105 | 106 |
107 |
108 | 109 |
110 |
111 | 112 |
113 |
114 |
115 | 118 | 121 | 124 | 125 | 126 | -------------------------------------------------------------------------------- /ap/hash.js: -------------------------------------------------------------------------------- 1 | (function(){let e,r,t,l=(e,r)=>r>>>e|r<<32-e,o=(e,r,t)=>e&r^~e&t,n=(e,r,t)=>e&r^e&t^r&t,f=e=>l(2,e)^l(13,e)^l(22,e),a=e=>l(6,e)^l(11,e)^l(25,e),w=e=>l(7,e)^l(18,e)^e>>>3,A=e=>l(17,e)^l(19,e)^e>>>10,y=(e,r)=>e[15&r]+=A(e[r+14&15])+e[r+9&15]+w(e[r+1&15]),c=new Array(1116352408,1899447441,3049323471,3921009573,961987163,1508970993,2453635748,2870763221,3624381080,310598401,607225278,1426881987,1925078388,2162078206,2614888103,3248222580,3835390401,4022224774,264347078,604807628,770255983,1249150122,1555081692,1996064986,2554220882,2821834349,2952996808,3210313671,3336571891,3584528711,113926993,338241895,666307205,773529912,1294757372,1396182291,1695183700,1986661051,2177026350,2456956037,2730485921,2820302411,3259730800,3345764771,3516065817,3600352804,4094571909,275423344,430227734,506948616,659060556,883997877,958139571,1322822218,1537002063,1747873779,1955562222,2024104815,2227730452,2361852424,2428436474,2756734187,3204031479,3329325298),d="0123456789abcdef",u=(e,r)=>{let t=(65535&e)+(65535&r),l=(e>>16)+(r>>16)+(t>>16);return l<<16|65535&t},h=()=>{e=new Array(8);r=new Array(2);t=new Array(64);r[0]=r[1]=0;e[0]=1779033703;e[1]=3144134277;e[2]=1013904242;e[3]=2773480762;e[4]=1359893119;e[5]=2600822924;e[6]=528734635;e[7]=1541459225},i=()=>{let r,l,w,A,d,h,i,g,s,b,p=new Array(16);r=e[0];l=e[1];w=e[2];A=e[3];d=e[4];h=e[5];i=e[6];g=e[7];for(let e=0;e<16;e++)p[e]=t[3+(e<<2)]|t[2+(e<<2)]<<8|t[1+(e<<2)]<<16|t[e<<2]<<24;for(let e=0;e<64;e++){s=g+a(d)+o(d,h,i)+c[e];s+=e<16?p[e]:y(p,e);b=f(r)+n(r,l,w);g=i;i=h;h=d;d=u(A,s);A=w;w=l;l=r;r=u(s,b)}e[0]+=r;e[1]+=l;e[2]+=w;e[3]+=A;e[4]+=d;e[5]+=h;e[6]+=i;e[7]+=g},g=(e,l)=>{let o,n,f=0;n=r[0]>>3&63;let a=63&l;(r[0]+=l<<3)>29;for(o=0;o+63{let e=r[0]>>3&63;t[e++]=128;if(e<=56)for(let r=e;r<56;r++)t[r]=0;else{for(let r=e;r<64;r++)t[r]=0;i();for(let e=0;e<56;e++)t[e]=0}t[56]=r[1]>>>24&255;t[57]=r[1]>>>16&255;t[58]=r[1]>>>8&255;t[59]=255&r[1];t[60]=r[0]>>>24&255;t[61]=r[0]>>>16&255;t[62]=r[0]>>>8&255;t[63]=255&r[0];i()},b=()=>{let r=0,t=new Array(32);for(let l=0;l<8;l++){t[r++]=e[l]>>>24&255;t[r++]=e[l]>>>16&255;t[r++]=e[l]>>>8&255;t[r++]=255&e[l]}return t},p=()=>{let r=new String;for(let t=0;t<8;t++)for(let l=28;l>=0;l-=4)r+=d.charAt(e[t]>>>l&15);return r},C=(e,r,t)=>{h();g(e,e.length);s();return r?b():p()};"object"==typeof window?window.getHash=C:module.exports=C})(); -------------------------------------------------------------------------------- /ap/hash.min.js: -------------------------------------------------------------------------------- 1 | (function(){let e,r,t,l=(e,r)=>r>>>e|r<<32-e,o=(e,r,t)=>e&r^~e&t,f=(e,r,t)=>e&r^e&t^r&t,n=e=>l(2,e)^l(13,e)^l(22,e),a=e=>l(6,e)^l(11,e)^l(25,e),w=e=>l(7,e)^l(18,e)^e>>>3,A=e=>l(17,e)^l(19,e)^e>>>10,y=(e,r)=>e[15&r]+=A(e[r+14&15])+e[r+9&15]+w(e[r+1&15]),i=new Array(1116352408,1899447441,3049323471,3921009573,961987163,1508970993,2453635748,2870763221,3624381080,310598401,607225278,1426881987,1925078388,2162078206,2614888103,3248222580,3835390401,4022224774,264347078,604807628,770255983,1249150122,1555081692,1996064986,2554220882,2821834349,2952996808,3210313671,3336571891,3584528711,113926993,338241895,666307205,773529912,1294757372,1396182291,1695183700,1986661051,2177026350,2456956037,2730485921,2820302411,3259730800,3345764771,3516065817,3600352804,4094571909,275423344,430227734,506948616,659060556,883997877,958139571,1322822218,1537002063,1747873779,1955562222,2024104815,2227730452,2361852424,2428436474,2756734187,3204031479,3329325298),c="0123456789abcdef",d=(e,r)=>{let t=(65535&e)+(65535&r),l=(e>>16)+(r>>16)+(t>>16);return l<<16|65535&t},h=()=>{e=new Array(8),r=new Array(2),t=new Array(64),r[0]=r[1]=0,e[0]=1779033703,e[1]=3144134277,e[2]=1013904242,e[3]=2773480762,e[4]=1359893119,e[5]=2600822924,e[6]=528734635,e[7]=1541459225},s=()=>{let r,l,w,A,c,h,s,u,g,p,b=new Array(16);r=e[0],l=e[1],w=e[2],A=e[3],c=e[4],h=e[5],s=e[6],u=e[7];for(let e=0;e<16;e++)b[e]=t[3+(e<<2)]|t[2+(e<<2)]<<8|t[1+(e<<2)]<<16|t[e<<2]<<24;for(let e=0;e<64;e++)g=u+a(c)+o(c,h,s)+i[e],g+=e<16?b[e]:y(b,e),p=n(r)+f(r,l,w),u=s,s=h,h=c,c=d(A,g),A=w,w=l,l=r,r=d(g,p);e[0]+=r,e[1]+=l,e[2]+=w,e[3]+=A,e[4]+=c,e[5]+=h,e[6]+=s,e[7]+=u},u=(e,l)=>{let o,f,n=0;f=r[0]>>3&63;let a=63&l;if((r[0]+=l<<3)>29,"string"==typeof e){for(o=0;o+63{let e=r[0]>>3&63;if(t[e++]=128,e<=56)for(let r=e;r<56;r++)t[r]=0;else{for(let r=e;r<64;r++)t[r]=0;s();for(let e=0;e<56;e++)t[e]=0}t[56]=r[1]>>>24&255,t[57]=r[1]>>>16&255,t[58]=r[1]>>>8&255,t[59]=255&r[1],t[60]=r[0]>>>24&255,t[61]=r[0]>>>16&255,t[62]=r[0]>>>8&255,t[63]=255&r[0],s()},p=()=>{let r=0,t=new Array(32);for(let l=0;l<8;l++)t[r++]=e[l]>>>24&255,t[r++]=e[l]>>>16&255,t[r++]=e[l]>>>8&255,t[r++]=255&e[l];return t},b=()=>{let r=new String;for(let t=0;t<8;t++)for(let l=28;l>=0;l-=4)r+=c.charAt(e[t]>>>l&15);return r},C=(e,r,t)=>(h(),u(e,e.length),g(),r?p():b());"object"==typeof window?window.getHash=C:module.exports=C})(); -------------------------------------------------------------------------------- /ap/main.min.js: -------------------------------------------------------------------------------- 1 | (e=>{let t=window,o=e=>document.getElementById(e),n=t.showMsg=((e,t)=>{t=t||3e3;let n=o("msg");try{clearTimeout(n.t)}catch(e){}n.innerHTML=e,n.className+=" msgShow",n.t=setTimeout(()=>{n.className="msg"},t)}),c=!0,r={cn:{ssid:"WiFi名称",wifiPwd:"WiFi密码",user:"用户名",userPwd:"密码",websocketDomain:"WebSocket主机",websocketPort:"WebSocket端口",websocketPath:"WebSocket路径",nickname:"昵称",token:"令牌",setArguments:"设置参数",reboot:"重启",rebootNow:"立即重启",rollback:"回滚固件",deepSleep:"睡眠10分钟",txtError:"错误",txtSet:"设置参数成功",txtDelayReboot:"设备将会在3秒后重启",txtRollback:"回滚操作成功",txtDeepSleep:"设备即将睡眠",txtConnected:"已连接",txtDisconnected:"已断开",txtInvalid:"不合法"},en:{ssid:"WiFi SSID",wifiPwd:"WiFi password",user:"User name",userPwd:"password",websocketDomain:"WebSocket host",websocketPort:"WebSocket port",websocketPath:"WebSocket path",nickname:"Nickname",token:"Token",setArguments:"Submit",reboot:"Reboot",rebootNow:"Reboot immediately",rollback:"Rollback firmware",deepSleep:"Deep sleep 10 minutes",txtError:"Error",txtSet:"Arguments set",txtDelayReboot:"Will reboot in 3 seconds",txtRollback:"Rollback OK",txtDeepSleep:"Will into deep sleep in 3 seconds",txtConnected:"Connected",txtDisconnected:"Disconnected",txtInvalid:" Invalid"}};(e=>{let t=navigator.language;t.length&&(t=t.substring(0,2).toLowerCase(),t.startsWith("en")&&(c=!1));let o=document.getElementsByTagName("a");for(let e of o)e.href&&""!=e.href||(e.href="javascript:void(0);"),e.innerText=c?r.cn[e.id]:r.en[e.id];o=document.getElementsByTagName("input");for(let e of o)"text"!=e.type&&"password"!=e.type||(e.placeholder=c?r.cn[e.id]:r.en[e.id])})();let l=t.connectWebsocket=(e=>{try{t.webSocket=new WebSocket("ws://"+t.location.host+":80"),t.webSocket.binaryType="arraybuffer",t.webSocket.onerror=(e=>{n((c?r.cn.txtError:r.en.txtError)+": "+JSON.stringify(e)),setTimeout(()=>{l()},1e3)}),t.webSocket.onmessage=(e=>{if(e.data&&e.data.byteLength>=2){let t=decodeArrayBuffer(e.data);t&&t.length&&(0==t[0]?n(c?r.cn.txtSet:r.en.txtSet):1==t[0]?n(c?r.cn.txtDelayReboot:r.en.txtDelayReboot):136==t[0]?n(c?r.cn.txtRollback:r.en.rollback):137==t[0]?n(c?r.cn.txtDeepSleep:r.en.deepSleep):t[0])}}),t.webSocket.onopen=function(){n(c?r.cn.txtConnected:r.en.txtConnected)},t.webSocket.onclose=(e=>{n(c?r.cn.txtDisconnected:r.en.txtDisconnected),setTimeout(()=>{l()},1e3)})}catch(e){setTimeout(()=>{l()},1e3)}});t.connectWebsocket();let i=t.launch=function(e){t.webSocket.send(createArrayBuffer(e))};o("setArguments").onclick=(e=>{let t=e=>{n(e+(c?r.cn.txtInvalid:r.en.txtInvalid))},l=o("nickname").value,a=o("ssid").value;if(!a.trim().length)return void t(c?r.cn.ssid:r.en.ssid);let s=o("wifiPwd").value;if(!s.trim().length)return void t(c?r.cn.wifiPwd:r.en.wifiPwd);let d=o("user").value;if(!d.trim().length)return void t(c?r.cn.user:r.en.user);let b=o("userPwd").value;if(!b.trim().length)return void t(c?r.cn.userPwd:r.en.userPwd);let u=o("websocketDomain").value;if(!u.trim().length)return void t(c?r.cn.websocketDomain:r.en.websocketDomain);let k=o("websocketPort").value;k=k.replace(/\s/g,""),k=k.length?k:"80",k=parseInt(k);let w=o("websocketPath").value;w=w.replace(/\s/g,""),w=w.length?w:"/";let m=o("token").value;i([0,l,a,s,new Uint8Array(getHash(d,!0)),new Uint8Array(getHash(b,!0)),u,k,w,m])}),o("reboot").onclick=(e=>{i([1])}),o("rebootNow").onclick=(e=>{i([2])}),o("rollback").onclick=(e=>{i([136])}),o("deepSleep").onclick=(e=>{i([137])}),(e=>{let t=document.getElementsByTagName("input");for(let e of t)"text"!=e.type&&"password"!=e.type||(e.onkeyup=function(e){localStorage[this.id]=this.value});for(let e in localStorage){let t=o(e);t&&(t.value=localStorage[e])}})()})(); -------------------------------------------------------------------------------- /ap/tools.js: -------------------------------------------------------------------------------- 1 | let fs = require("fs"); 2 | 3 | let rootPath = process.argv[process.argv.length - 1]; 4 | 5 | rootPath += "ap/"; 6 | 7 | let html = fs.readFileSync(rootPath + "apIndex.html").toString(); 8 | let js = fs.readFileSync(rootPath + "main.min.js").toString(); 9 | let ab = fs.readFileSync(rootPath + "ab.js").toString(); 10 | let hash = fs.readFileSync(rootPath + "hash.js").toString(); 11 | html = html.replace("main_js", js); 12 | html = html.replace("ab_js", ab); 13 | html = html.replace("hash_js", hash); 14 | html = html.replace(/"/g, "'"); 15 | html = html.replace(/[\r\n]/g, ""); 16 | html = html.replace(/[\s]{2,}/g, " ") 17 | 18 | fs.writeFileSync(rootPath + "out.txt", html); 19 | //fs.writeFileSync(rootPath + "out.html", html); 20 | 21 | console.log("ap index built"); -------------------------------------------------------------------------------- /app/README.md: -------------------------------------------------------------------------------- 1 | This folder stored all single apps. 2 | 3 | 这个文件夹存储了所有单独的app。 -------------------------------------------------------------------------------- /app/empty_example/README.md: -------------------------------------------------------------------------------- 1 | This is an empty example, only show code structure. 2 | 3 | 这是个空工程,仅用于展示代码结构。 -------------------------------------------------------------------------------- /app/empty_example/app/app.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * @file app.cpp 3 | * @brief 4 | * 5 | * Code this file just like Arduino way 6 | * 7 | * 就像使用Arduino框架一样在这个文件中写代码就可以了 8 | * 9 | */ 10 | 11 | #include "app.h" 12 | 13 | void App::setup() 14 | { 15 | /** 16 | * ===English: 17 | * put your code in this function to run it once 18 | * 19 | * ===中文: 20 | * 把你的代码放在这个函数里,代码会被运行一次 21 | */ 22 | 23 | /** 24 | * ===English: 25 | * if you just did an OTA update 26 | * you could do anything here, don't worry any code bugs 27 | * if any error happened, and reboot times reach maximum valve 28 | * firmware will rollback to former version automatically 29 | * 30 | * you could modify the valve yourself 31 | * in file /lib/config/config.h 32 | * 33 | * ! Attention: ONLY when firmware updated by built-in OTA function 34 | * has this feature, not included update through serial lines 35 | * 36 | * ===中文: 37 | * 如果你刚才使用了OTA升级了这个固件 38 | * 你可以在这里写任何代码 39 | * 无须担心任何错误引起无限复位 40 | * 当复位次数超过设定阈值时 41 | * 固件会自动回滚上一个版本 42 | * 43 | * 阈值可以根据自己的需求调整 44 | * 在 /lib/config/config.h 45 | * 46 | * ! 注意: [ 仅 ] 当固件通过内置的OTA升级功能升级后才有此功能 47 | * 直接从串口升级的固件没有此功能 48 | */ 49 | } 50 | 51 | void App::loop() 52 | { 53 | // put your code in this function 54 | // it will run repeatedly, attention attached 55 | 56 | // 把你的代码放在这个函数里,代码会重复的运行,请阅读注意事项 57 | } 58 | 59 | /** 60 | * @brief App instance defined here 61 | * 62 | * 类App实例在此实例化 63 | * 64 | */ 65 | App *app = new App(); -------------------------------------------------------------------------------- /app/empty_example/app/app.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "../src/globalmanager/globalmanager.h" 3 | 4 | /** 5 | * @brief name of app 6 | * 7 | * app名称 8 | * 9 | */ 10 | #define APP_NAME "Abc" 11 | 12 | /** 13 | * @brief version of app 14 | * 15 | * app版本 16 | * 17 | */ 18 | #define APP_VERSION 0 19 | 20 | /** 21 | * @brief default cpu frequency of current app 22 | * 23 | * 这个app默认的cpu频率 24 | * 25 | */ 26 | #define DEFAULT_CPU_FREQ 240 27 | 28 | /** 29 | * @brief indicate app has setup function 30 | * or not 31 | * 32 | * 指示app是否有初始化函数 33 | * 34 | */ 35 | #define APP_HAS_SETUP 36 | 37 | /** 38 | * @brief indicate app has main loop function 39 | * 40 | * 指示app是否有主循环函数 41 | * 42 | */ 43 | #define APP_HAS_LOOP 44 | 45 | /** 46 | * @brief the stack size of current app loop 47 | * 48 | * 当前app循环任务的栈大小 49 | * 50 | */ 51 | #define APP_LOOP_STACK_SIZE 8 * 1024 52 | 53 | /** 54 | * @brief indicate app has custom extra callback 55 | * for local websocket server 56 | * 57 | * 指示app是否有自定义的用于本地websocket服务器的回调函数 58 | * 59 | * 60 | * @note the function declared and defined as C pattern 61 | * not included in class App 62 | * 63 | * 函数以C语言风格声明和定义,不包含在类App内 64 | * 65 | */ 66 | //#define APP_HAS_EXTRA_LOCAL_WEBSOCKET_CALLBACK 67 | 68 | /** 69 | * @brief indicate app has custom extra callback 70 | * for remote websocket client 71 | * 72 | * 指示app是否有自定义的用于远程websocket客户端的回调函数 73 | * 74 | * 75 | * @note the function declared and defined as C pattern 76 | * not included in class App 77 | * 78 | * 函数以C语言风格声明和定义,不包含在类App内 79 | * 80 | */ 81 | //#define APP_HAS_EXTRA_REMOTE_WEBSOCKET_CALLBACK 82 | 83 | /** 84 | * @brief default app class 85 | * you could modify class name, attention attached 86 | * 87 | * 默认的app类 88 | * 你可以修改这个类的名字,请阅读注意事项 89 | * 90 | * @attention the name of instance must be "app" 91 | * refer to the tail of the file "app.cpp" 92 | * 93 | * 类实例的名称必须为 "app",查看 "app.app" 的尾部 94 | * 95 | * @note if you modified the name of he instance, 96 | * you should also modify the name in the file "main.cpp" 97 | * not recommended for beginner 98 | * 99 | * 如果你修改了类实例的名字,你也应该一同修改文件 "main.cpp" 100 | * 里的名字 101 | * 不推荐新手做此操作 102 | * 103 | */ 104 | class App 105 | { 106 | private: 107 | public: 108 | /** 109 | * @brief default constructor, attention attached! 110 | * 默认构造函数,请阅读注意事项! 111 | * 112 | * @attention if you declared and define other constructors 113 | * you should NOT do any actions except set default value 114 | * for variables. 115 | * Because the instance of class App will be define in the tail 116 | * of the app.cpp, and that is NOT included in the system 117 | * GlobalManager::makeSureNewFirmwareValid() measurement scope, 118 | * if any action in constructor cause ESP32 panic reset, 119 | * you will lost connection of this ESP32, then you have to 120 | * use hardware serial to upload new firmware. 121 | * 122 | * 如果你定义了其他构造函数,请确保在构造函数内 [不要] 做 除了给变量赋值 123 | * 以外的其他操作,因为类App的实例在 app.cpp 文件的尾部被实例化, 124 | * 而这不在系统 GlobalManager::makeSureNewFirmwareValid() 的 125 | * 监测范围内,如果构造函数内有任何操作导致ESP32复位,你会丢失与这个 126 | * ESP32的连接,此时你只能再次用硬件串口重新上传固件。 127 | * 128 | */ 129 | inline App(){}; 130 | inline ~App(){}; 131 | 132 | /** 133 | * @brief put your code in this function to run it once 134 | * 135 | * 把你的代码放在这个函数里,代码会被运行一次 136 | * 137 | */ 138 | void setup(); 139 | 140 | /** 141 | * @brief put your code in this function 142 | * it will run repeatedly, attention attached 143 | * 144 | * 把你的代码放在这个函数里,代码会重复的运行,请阅读注意事项 145 | * 146 | * @attention loop function will NOT run when OTA update 147 | * process running 148 | * 149 | * 在OTA升级期间,loop函数不会被运行 150 | * 151 | */ 152 | void loop(); 153 | }; 154 | 155 | extern App *app; -------------------------------------------------------------------------------- /build.bat: -------------------------------------------------------------------------------- 1 | node build.js -------------------------------------------------------------------------------- /build.command: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | cd $(dirname $0) 3 | node build.js -------------------------------------------------------------------------------- /build.js: -------------------------------------------------------------------------------- 1 | let child_process = require("child_process"), fs = require("fs"); 2 | 3 | (e => { 4 | 5 | let config = {}; 6 | 7 | let autoSync = false; 8 | 9 | let language = { 10 | en: { 11 | input: "Input: ", 12 | buildingApp: " Building app: ", 13 | allBuilt: "All firmwares have been built", 14 | errorInput: "Wrong selection, input again\n", 15 | selectBuildApp: "Select one app to build:\n", 16 | selectCopyApp: "Select one app to copy:\n", 17 | copied: "copied\n", 18 | opType: "1: Build single app\n2: Build all\n3: Copy only\n4: Enable auto sync\n5: exit\n:" 19 | }, 20 | cn: { 21 | input: "请输入: ", 22 | buildingApp: "正在构建固件: ", 23 | allBuilt: "所有固件已构建完成", 24 | errorInput: "输入错误,请重新输入\n", 25 | selectBuildApp: "构建哪一个固件:\n", 26 | selectCopyApp: "拷贝哪个固件:\n", 27 | copied: "已拷贝\n", 28 | opType: "1: 单独构建\n2: 全部构建\n3: 仅拷贝\n4: 启用自动同步\n5: 退出\n:" 29 | } 30 | }; 31 | 32 | // build firmware 33 | let buildFirmware = e => { 34 | let stdout = child_process.execSync("export PATH=$PATH:~/.platformio/penv/bin && pio run --target clean"); 35 | console.log(stdout.toString()); 36 | stdout = child_process.execSync("export PATH=$PATH:~/.platformio/penv/bin && pio run"); 37 | console.log(stdout.toString()); 38 | }; 39 | 40 | // create directory 41 | if (!fs.existsSync("./app/")) { 42 | fs.mkdirSync("./app/"); 43 | } 44 | 45 | let apps = fs.readdirSync("./app"); 46 | let appsRoot = "./app/"; 47 | 48 | // load names of app 49 | let refresh = function () { 50 | //load all files and directories 51 | apps = fs.readdirSync("./app"); 52 | for (let i = 0; i < apps.length; i++) { 53 | if ( 54 | !fs.statSync(appsRoot + apps[i]).isDirectory() //remove other files 55 | || 56 | apps[i].trim().endsWith(".md") //remove readme 57 | ) { 58 | apps.splice(i, 1); 59 | i = i >= 0 ? i - 1 : i; 60 | } 61 | } 62 | 63 | let appString = ""; 64 | 65 | for (let i = 0; i < apps.length; i++) { 66 | let json = { 67 | index: i + 1, 68 | name: apps[i] 69 | }; 70 | arrMete.push(json); 71 | appString += json.index.toString() + ": " + json.name + "\n"; 72 | } 73 | 74 | appString += language[config.language].input; 75 | return appString; 76 | }; 77 | 78 | // build all firmware 79 | let buildAll = function () { 80 | for (let i of apps) { 81 | if (fs.statSync(appsRoot + i).isDirectory()) { 82 | console.log(language[config.language].buildingApp + i); 83 | fs.cpSync(appsRoot + i + "/app/", "./src/app/", { recursive: true }); 84 | buildFirmware(); 85 | } 86 | } 87 | console.log(language[config.language].allBuilt); 88 | }; 89 | 90 | let readline = require('readline'); 91 | let rl = readline.createInterface({ 92 | input: process.stdin, 93 | output: process.stdout 94 | }); 95 | 96 | let query = function () { 97 | rl.question(language[config.language].opType, buildType); 98 | }; 99 | 100 | let arrMete = []; 101 | 102 | let buildSingle = function (data) { 103 | data = parseInt(data); 104 | let json = arrMete.find(e => { return e.index == data; }); 105 | if (!json) { 106 | query(); 107 | return; 108 | } 109 | console.log(language[config.language].buildingApp + json.name) 110 | fs.cpSync(appsRoot + json.name + "/app/", "./src/app/", { recursive: true }); 111 | buildFirmware(); 112 | query(); 113 | }; 114 | 115 | let copyOnly = function (data) { 116 | data = parseInt(data); 117 | let json = arrMete.find(e => { return e.index == data; }); 118 | if (!json) { 119 | query(); 120 | return; 121 | } 122 | fs.cpSync(appsRoot + json.name + "/app/", "./src/app/", { recursive: true }); 123 | console.log(json.name + " " + language[config.language].copied); 124 | query(); 125 | }; 126 | 127 | let buildType = function (data) { 128 | data = parseInt(data); 129 | if (data == 1) { 130 | let q = refresh(); 131 | rl.question(language[config.language].selectBuildApp + q, buildSingle); 132 | } else if (data == 2) { 133 | buildAll(); 134 | query(); 135 | } else if (data == 3) { 136 | let q = refresh(); 137 | rl.question(language[config.language].selectCopyApp + q, copyOnly); 138 | } else if (data == 4) { 139 | if (!autoSync) { 140 | autoSync = true; 141 | fs.watch("./", { 142 | recursive: true 143 | }, function (e, fileName) { 144 | if (fileName.startsWith("app/")) { 145 | let m = /app\/(?[^\/]+)\/.+/i.exec(fileName); 146 | if (m) { 147 | m = m.groups.appName; 148 | fs.cpSync(appsRoot + m + "/app/", "./src/app/", { recursive: true }); 149 | } 150 | } 151 | }); 152 | } 153 | 154 | query(); 155 | } else if (data == 5) { 156 | process.exit(); 157 | } else { 158 | rl.question(language[config.language].errorInput + 159 | language[config.language].opType, buildType); 160 | } 161 | }; 162 | config.language = Intl.DateTimeFormat().resolvedOptions().locale.toLowerCase(); 163 | if (config.language == "zh-cn") { 164 | config.language = "cn"; 165 | } else { 166 | config.language = "en"; 167 | } 168 | 169 | query(); 170 | 171 | })(); -------------------------------------------------------------------------------- /doc/README.md: -------------------------------------------------------------------------------- 1 | ### English 2 | 3 | You could use http get to execute provider, example refer to /doc/get_method.js 4 | 5 | ### 中文 6 | 7 | 你可以使用http get 方法执行provider,例子参考 /doc/get_method.js -------------------------------------------------------------------------------- /doc/extented_command_specification.txt: -------------------------------------------------------------------------------- 1 | Extented command has 8 bytes, the format are following: 2 | 扩展命令为8字节,格式如下: 3 | 4 | byte offset: 5 | 字节偏移量: 6 | | 0 | | 1 2 3 4 5 6 7 7 | -----------|--------------------------------|-------------------|------------------------------------------------------------------------------------------- 8 | comments: | mask | | message ID reserved basic command 9 | 注释: | | | 4 bytes 2 bytes 1 byte 10 | -----------|--------------------------------|-------------------|------------------------------------------------------------------------------------------- 11 | bit offset:| | | 12 | 0 | 1 | 2 | 3 4 13 | -----------|--------------------------------|-------------------|------------------------------------------------------------------------------------------- 14 | 1 fixed | 0 delivery once | 0 nothing | targetID position 15 | -----------|--------------------------------|-------------------|------------------------------------------------------------------------------------------- 16 | | 1 delivery should be confirmed | 1 confirm message | 17 | -----------|--------------------------------|-------------------|------------------------------------------------------------------------------------------- 18 | comments: | server will push same message | receiver will set | only show to server 19 | | repeatedly until got confirm | this bit to 1 if | 20 | | if this bit set to 1 by sender | it had received | 21 | | 如果这个bit设置为1,服务器会在收到 | a message which | 22 | | 响应数据之前持续重复发送消息 | should be | 23 | | | confirmed | 24 | 25 | -------------------------------------------------------------------------------- /examples/README.md: -------------------------------------------------------------------------------- 1 | ### English 2 | 3 | How to use: select one example, and copy its inner "app" folder to /src/ , cover original folder. 4 | 5 | ### 中文 6 | 7 | 如何使用: 选择一个例子,然后拷贝其中的 "app" 文件夹到 /src/ ,覆盖里面的文件。 -------------------------------------------------------------------------------- /examples/aes_256_cbc_example/README.md: -------------------------------------------------------------------------------- 1 | This example show you how to encrypt and decrypt AES-256-CBC with one line code. 2 | 3 | 这个例子演示如何使用一行代码加密或解密AES-256-CBC。 -------------------------------------------------------------------------------- /examples/aes_256_cbc_example/app/app.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * @file app.cpp 3 | * @brief 4 | * 5 | * Code this file just like Arduino way 6 | * 7 | * 就像使用Arduino框架一样在这个文件中写代码就可以了 8 | * 9 | */ 10 | 11 | #include "app.h" 12 | 13 | void App::setup() 14 | { 15 | // plain text 16 | // 明文 17 | String plain = "12345678"; 18 | 19 | Serial.printf("Plain text: %s\n", plain.c_str()); 20 | Serial.printf("明文: %s\n", plain.c_str()); 21 | 22 | // key, 32 bytes 23 | // 密匙, 32字节 24 | String key = "abcdefghijklmnopqrstuvwxyz012345"; 25 | 26 | // iv, 16 bytes 27 | // 初始化向量, 16字节 28 | String iv = "0123456789abcdef"; 29 | 30 | // encrypt 31 | // 加密 32 | String cipher = mycrypto::AES::aes256CBCEncrypt( 33 | key, 34 | iv, 35 | plain); 36 | 37 | // the cipher is hex format, lower case 38 | // 密文是16进制字符串,小写 39 | Serial.printf("Encrypted data: 0x%s\n", cipher.c_str()); 40 | Serial.printf("加密后的数据: 0x%s\n", cipher.c_str()); 41 | 42 | // decrypt 43 | // 解密 44 | String decrypted = mycrypto::AES::aes256CBCDecrypt( 45 | key, 46 | iv, 47 | cipher); 48 | 49 | Serial.printf("Decrypted data: %s\n", decrypted.c_str()); 50 | Serial.printf("解密后的数据: %s\n", decrypted.c_str()); 51 | 52 | // ========== 53 | // you could also use traditional method 54 | // 你也可以使用传统方法 55 | 56 | const char *plain2 = "abc"; 57 | 58 | uint32_t encryptOutLen = 0; 59 | 60 | uint8_t *encryptedBuffer = mycrypto::AES::aes256CBCEncrypt( 61 | (uint8_t *)key.c_str(), // key 密匙 62 | (uint8_t *)iv.c_str(), // iv 初始化向量 63 | (uint8_t *)plain2, // plain 明文 64 | strlen(plain2), // length of plain 明文长度 65 | &encryptOutLen // length of output 输出长度 66 | ); 67 | 68 | // output buffer will be set to nullptr 69 | // and output length will be set to 0 70 | // if error occured 71 | 72 | // 如果发生错误,buffer会返回空指针 73 | // 输出长度会返回0 74 | 75 | if (encryptedBuffer && encryptOutLen) 76 | { 77 | // do your thing with buffer and length 78 | // 你可以使用 buffer 和 长度做你想做的事 79 | 80 | // here BTW, for example, decrypt it 81 | // 这里顺便演示解密 82 | 83 | // decrypting part 84 | // 解密部分 85 | uint32_t decryptedOutLen = 0; 86 | 87 | uint8_t *decryptedBuffer = mycrypto::AES::aes256CBCDecrypt( 88 | (uint8_t *)key.c_str(), // key 密匙 89 | (uint8_t *)iv.c_str(), // iv 初始化向量 90 | encryptedBuffer, // cipher 密文 91 | encryptOutLen, // length of cipher 密文的长度 92 | &decryptedOutLen // length of output 输出的长度 93 | ); 94 | 95 | if (decryptedBuffer && decryptedOutLen) 96 | { 97 | // do your thing with buffer and length 98 | // 你可以使用 buffer 和 长度做你想做的事 99 | 100 | delete decryptedBuffer; 101 | decryptedOutLen = 0; 102 | } 103 | else 104 | { 105 | Serial.println("Error occured when decrypting\n"); 106 | Serial.println("解密时发生错误\n"); 107 | } 108 | 109 | // decryption end 110 | // 解密部分结束 111 | 112 | // don't forget release memory 113 | // 别忘记释放内存 114 | delete encryptedBuffer; 115 | 116 | // reset outLen to 0 is more better 117 | // 最好连输出长度也重置 118 | encryptOutLen = 0; 119 | } 120 | else 121 | { 122 | Serial.println("Error occured when encrypting\n"); 123 | Serial.println("加密时发生错误\n"); 124 | } 125 | } 126 | 127 | void App::loop() 128 | { 129 | // put your code in this function 130 | // it will run repeatedly, attention attached 131 | 132 | // 把你的代码放在这个函数里,代码会重复的运行,请阅读注意事项 133 | } 134 | 135 | /** 136 | * @brief App instance defined here 137 | * 138 | * 类App实例在此实例化 139 | * 140 | */ 141 | App *app = new App(); -------------------------------------------------------------------------------- /examples/aes_256_cbc_example/app/app.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | 4 | /** 5 | * @brief name of app 6 | * 7 | * app名称 8 | * 9 | */ 10 | #define APP_NAME "AES" 11 | 12 | /** 13 | * @brief version of app 14 | * 15 | * app版本 16 | * 17 | */ 18 | #define APP_VERSION 0 19 | 20 | /** 21 | * @brief default cpu frequency of current app 22 | * 23 | * 这个app默认的cpu频率 24 | * 25 | */ 26 | #define DEFAULT_CPU_FREQ 240 27 | 28 | 29 | /** 30 | * @brief indicate app has custom extra callback 31 | * for local websocket server 32 | * 33 | * 指示app是否有自定义的用于本地websocket服务器的回调函数 34 | * 35 | * 36 | * @note the function declared and defined as C pattern 37 | * not included in class App 38 | * 39 | * 函数以C语言风格声明和定义,不包含在类App内 40 | * 41 | */ 42 | //#define APP_HAS_EXTRA_LOCAL_WEBSOCKET_CALLBACK 43 | 44 | /** 45 | * @brief indicate app has custom extra callback 46 | * for remote websocket client 47 | * 48 | * 指示app是否有自定义的用于远程websocket客户端的回调函数 49 | * 50 | * 51 | * @note the function declared and defined as C pattern 52 | * not included in class App 53 | * 54 | * 函数以C语言风格声明和定义,不包含在类App内 55 | * 56 | */ 57 | //#define APP_HAS_EXTRA_REMOTE_WEBSOCKET_CALLBACK 58 | 59 | /** 60 | * @brief default app class 61 | * you could modify class name, attention attached 62 | * 63 | * 默认的app类 64 | * 你可以修改这个类的名字,请阅读注意事项 65 | * 66 | * @attention the name of instance must be "app" 67 | * refer to the tail of the file "app.cpp" 68 | * 69 | * 类实例的名称必须为 "app",查看 "app.app" 的尾部 70 | * 71 | * @note if you modified the name of he instance, 72 | * you should also modify the name in the file "main.cpp" 73 | * not recommended for beginner 74 | * 75 | * 如果你修改了类实例的名字,你也应该一同修改文件 "main.cpp" 76 | * 里的名字 77 | * 不推荐新手做此操作 78 | * 79 | */ 80 | class App 81 | { 82 | private: 83 | public: 84 | /** 85 | * @brief default constructor, attention attached! 86 | * 默认构造函数,请阅读注意事项! 87 | * 88 | * @attention if you declared and define other constructors 89 | * you should NOT do any actions except set default value 90 | * for variables. 91 | * Because the instance of class App will be define in the tail 92 | * of the app.cpp, and that is NOT included in the system 93 | * GlobalManager::makeSureNewFirmwareValid() measurement scope, 94 | * if any action in constructor cause ESP32 panic reset, 95 | * you will lost connection of this ESP32, then you have to 96 | * use hardware serial to upload new firmware. 97 | * 98 | * 如果你定义了其他构造函数,请确保在构造函数内 [不要] 做 除了给变量赋值 99 | * 以外的其他操作,因为类App的实例在 app.cpp 文件的尾部被实例化, 100 | * 而这不在系统 GlobalManager::makeSureNewFirmwareValid() 的 101 | * 监测范围内,如果构造函数内有任何操作导致ESP32复位,你会丢失与这个 102 | * ESP32的连接,此时你只能再次用硬件串口重新上传固件。 103 | * 104 | */ 105 | inline App(){}; 106 | inline ~App(){}; 107 | 108 | /** 109 | * @brief put your code in this function to run it once 110 | * 111 | * 把你的代码放在这个函数里,代码会被运行一次 112 | * 113 | */ 114 | void setup(); 115 | 116 | /** 117 | * @brief put your code in this function 118 | * it will run repeatedly, attention attached 119 | * 120 | * 把你的代码放在这个函数里,代码会重复的运行,请阅读注意事项 121 | * 122 | * @attention loop function will NOT run when OTA update 123 | * process running 124 | * 125 | * 在OTA升级期间,loop函数不会被运行 126 | * 127 | */ 128 | void loop(); 129 | }; 130 | 131 | extern App *app; -------------------------------------------------------------------------------- /examples/arraybuffer_example/README.md: -------------------------------------------------------------------------------- 1 | This example show how to use class ArrayBuffer. 2 | 3 | 这个例子演示如何使用类ArrayBuffer。 -------------------------------------------------------------------------------- /examples/arraybuffer_example/app/app.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * @file app.cpp 3 | * @brief 4 | * 5 | * Code this file just like Arduino way 6 | * 7 | * 就像使用Arduino框架一样在这个文件中写代码就可以了 8 | * 9 | */ 10 | 11 | #include "app.h" 12 | 13 | void App::setup() 14 | { 15 | // a temporary array 16 | // 一个临时数组 17 | uint8_t buffer[128] = {0}; 18 | 19 | // set some ramdom value 20 | // 设置点随机数 21 | for (uint32_t i = 0; i < 128; ++i) 22 | { 23 | buffer[i] = random(0, 0xff); 24 | } 25 | 26 | // container 27 | // 容器 28 | Elements container = 29 | { 30 | new Element(0x01), 31 | new Element(0x80), 32 | new Element(0xffff), 33 | new Element(0xffffffff), 34 | new Element("Hello world!"), 35 | new Element(buffer, 1024)}; 36 | 37 | // method A 38 | // 方法A 39 | ArrayBuffer::createArrayBuffer( 40 | &container, 41 | [](uint8_t *outputBuffer, uint64_t length, bool *isBufferDeleted) 42 | { 43 | // this buffer could be send through socket of store to flash 44 | // 这个数组可以被通过网络发送或存储到flash 45 | 46 | // this buffer will be freed automatically 47 | // 这个数组会被自动删除 48 | 49 | // or you could delete it manually 50 | // 或者你也可以手动删除 51 | delete outputBuffer; 52 | (*isBufferDeleted) = true; 53 | }); 54 | 55 | // method B 56 | // 方法B 57 | uint64_t outLen = 0; 58 | uint8_t *outputBuffer = ArrayBuffer::createArrayBuffer(&container, &outLen); 59 | 60 | if (outputBuffer && outLen) 61 | { 62 | // buffer OK 63 | 64 | ArrayBuffer::decodeArrayBuffer( 65 | [](Elements *outputVector) 66 | { 67 | if (outputVector) 68 | { 69 | if (outputVector->size()) 70 | { 71 | for ( 72 | Elements::iterator it = outputVector->begin(); 73 | it != outputVector->end(); 74 | ++it) 75 | { 76 | auto type = (*it)->getType(true); 77 | if (type == NUMBER) 78 | { 79 | Serial.printf("number: %llu\n", (*it)->getNumber()); 80 | } 81 | else if (type == ETYPE_STRING) 82 | { 83 | //Serial.printf("string: %s\n", (*it)->getString().c_str()); 84 | // or 或者 85 | Serial.printf("string: %s\n", (*it)->c_str()); 86 | } 87 | else if (type == U8A) 88 | { 89 | Serial.printf("uint8 array:\n 0x%s\n", (*it)->getHex()); 90 | } 91 | else 92 | { 93 | Serial.println("error"); 94 | } 95 | } 96 | } 97 | } 98 | }, 99 | outputBuffer, 100 | outLen); 101 | } 102 | } 103 | 104 | void App::loop() 105 | { 106 | // put your code in this function 107 | // it will run repeatedly, attention attached 108 | 109 | // 把你的代码放在这个函数里,代码会重复的运行,请阅读注意事项 110 | } 111 | 112 | /** 113 | * @brief App instance defined here 114 | * 115 | * 类App实例在此实例化 116 | * 117 | */ 118 | App *app = new App(); -------------------------------------------------------------------------------- /examples/arraybuffer_example/app/app.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | 4 | /** 5 | * @brief name of app 6 | * 7 | * app名称 8 | * 9 | */ 10 | #define APP_NAME "Class ArrayBuffer" 11 | 12 | /** 13 | * @brief version of app 14 | * 15 | * app版本 16 | * 17 | */ 18 | #define APP_VERSION 0 19 | 20 | /** 21 | * @brief default cpu frequency of current app 22 | * 23 | * 这个app默认的cpu频率 24 | * 25 | */ 26 | #define DEFAULT_CPU_FREQ 240 27 | 28 | 29 | /** 30 | * @brief indicate app has custom extra callback 31 | * for local websocket server 32 | * 33 | * 指示app是否有自定义的用于本地websocket服务器的回调函数 34 | * 35 | * 36 | * @note the function declared and defined as C pattern 37 | * not included in class App 38 | * 39 | * 函数以C语言风格声明和定义,不包含在类App内 40 | * 41 | */ 42 | //#define APP_HAS_EXTRA_LOCAL_WEBSOCKET_CALLBACK 43 | 44 | /** 45 | * @brief indicate app has custom extra callback 46 | * for remote websocket client 47 | * 48 | * 指示app是否有自定义的用于远程websocket客户端的回调函数 49 | * 50 | * 51 | * @note the function declared and defined as C pattern 52 | * not included in class App 53 | * 54 | * 函数以C语言风格声明和定义,不包含在类App内 55 | * 56 | */ 57 | //#define APP_HAS_EXTRA_REMOTE_WEBSOCKET_CALLBACK 58 | 59 | /** 60 | * @brief default app class 61 | * you could modify class name, attention attached 62 | * 63 | * 默认的app类 64 | * 你可以修改这个类的名字,请阅读注意事项 65 | * 66 | * @attention the name of instance must be "app" 67 | * refer to the tail of the file "app.cpp" 68 | * 69 | * 类实例的名称必须为 "app",查看 "app.app" 的尾部 70 | * 71 | * @note if you modified the name of he instance, 72 | * you should also modify the name in the file "main.cpp" 73 | * not recommended for beginner 74 | * 75 | * 如果你修改了类实例的名字,你也应该一同修改文件 "main.cpp" 76 | * 里的名字 77 | * 不推荐新手做此操作 78 | * 79 | */ 80 | class App 81 | { 82 | private: 83 | public: 84 | /** 85 | * @brief default constructor, attention attached! 86 | * 默认构造函数,请阅读注意事项! 87 | * 88 | * @attention if you declared and define other constructors 89 | * you should NOT do any actions except set default value 90 | * for variables. 91 | * Because the instance of class App will be define in the tail 92 | * of the app.cpp, and that is NOT included in the system 93 | * GlobalManager::makeSureNewFirmwareValid() measurement scope, 94 | * if any action in constructor cause ESP32 panic reset, 95 | * you will lost connection of this ESP32, then you have to 96 | * use hardware serial to upload new firmware. 97 | * 98 | * 如果你定义了其他构造函数,请确保在构造函数内 [不要] 做 除了给变量赋值 99 | * 以外的其他操作,因为类App的实例在 app.cpp 文件的尾部被实例化, 100 | * 而这不在系统 GlobalManager::makeSureNewFirmwareValid() 的 101 | * 监测范围内,如果构造函数内有任何操作导致ESP32复位,你会丢失与这个 102 | * ESP32的连接,此时你只能再次用硬件串口重新上传固件。 103 | * 104 | */ 105 | inline App(){}; 106 | inline ~App(){}; 107 | 108 | /** 109 | * @brief put your code in this function to run it once 110 | * 111 | * 把你的代码放在这个函数里,代码会被运行一次 112 | * 113 | */ 114 | void setup(); 115 | 116 | /** 117 | * @brief put your code in this function 118 | * it will run repeatedly, attention attached 119 | * 120 | * 把你的代码放在这个函数里,代码会重复的运行,请阅读注意事项 121 | * 122 | * @attention loop function will NOT run when OTA update 123 | * process running 124 | * 125 | * 在OTA升级期间,loop函数不会被运行 126 | * 127 | */ 128 | void loop(); 129 | }; 130 | 131 | extern App *app; -------------------------------------------------------------------------------- /examples/blink_example/README.md: -------------------------------------------------------------------------------- 1 | This example blink LED. 2 | 3 | 这个例子闪烁LED。 -------------------------------------------------------------------------------- /examples/blink_example/app/app.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * @file app.cpp 3 | * @brief 4 | * 5 | * Code this file just like Arduino way 6 | * 7 | * 就像使用Arduino框架一样在这个文件中写代码就可以了 8 | * 9 | */ 10 | 11 | #include "app.h" 12 | 13 | void App::setup() 14 | { 15 | // set GPIO mode 16 | // 设置GPIO模式 17 | pinMode(this->pinLED, OUTPUT); 18 | digitalWrite(this->pinLED, LOW); 19 | 20 | // check delay timeout valid 21 | // 检查延时是否被误设置成其他值 22 | if (!this->delayTimeout || this->delayTimeout > 100000) 23 | this->delayTimeout = 1000U; 24 | } 25 | 26 | void App::loop() 27 | { 28 | // method A 29 | // 方法A 30 | // digitalWrite(this->pinLED, HIGH); 31 | // delay(this->delayTimeout); 32 | // digitalWrite(this->pinLED, LOW); 33 | // delay(this->delayTimeout); 34 | 35 | // method B 36 | // 方法B 37 | uint64_t t = millis(); 38 | if (t - this->lastActionTime > this->delayTimeout) 39 | { 40 | this->lastActionTime = t; 41 | if (this->isLightOn) 42 | { 43 | digitalWrite(this->pinLED, LOW); 44 | } 45 | else 46 | { 47 | digitalWrite(this->pinLED, HIGH); 48 | } 49 | this->isLightOn = !(this->isLightOn); 50 | } 51 | } 52 | 53 | /** 54 | * @brief App instance defined here 55 | * 56 | * 类App实例在此实例化 57 | * 58 | */ 59 | App *app = new App(); -------------------------------------------------------------------------------- /examples/blink_example/app/app.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | 4 | /** 5 | * @brief name of app 6 | * 7 | * app名称 8 | * 9 | */ 10 | #define APP_NAME "Blink" 11 | 12 | /** 13 | * @brief version of app 14 | * 15 | * app版本 16 | * 17 | */ 18 | #define APP_VERSION 0 19 | 20 | /** 21 | * @brief default cpu frequency of current app 22 | * 23 | * 这个app默认的cpu频率 24 | * 25 | */ 26 | #define DEFAULT_CPU_FREQ 240 27 | 28 | 29 | /** 30 | * @brief indicate app has custom extra callback 31 | * for local websocket server 32 | * 33 | * 指示app是否有自定义的用于本地websocket服务器的回调函数 34 | * 35 | * 36 | * @note the function declared and defined as C pattern 37 | * not included in class App 38 | * 39 | * 函数以C语言风格声明和定义,不包含在类App内 40 | * 41 | */ 42 | //#define APP_HAS_EXTRA_LOCAL_WEBSOCKET_CALLBACK 43 | 44 | /** 45 | * @brief indicate app has custom extra callback 46 | * for remote websocket client 47 | * 48 | * 指示app是否有自定义的用于远程websocket客户端的回调函数 49 | * 50 | * 51 | * @note the function declared and defined as C pattern 52 | * not included in class App 53 | * 54 | * 函数以C语言风格声明和定义,不包含在类App内 55 | * 56 | */ 57 | //#define APP_HAS_EXTRA_REMOTE_WEBSOCKET_CALLBACK 58 | 59 | /** 60 | * @brief default app class 61 | * you could modify class name, attention attached 62 | * 63 | * 默认的app类 64 | * 你可以修改这个类的名字,请阅读注意事项 65 | * 66 | * @attention the name of instance must be "app" 67 | * refer to the tail of the file "app.cpp" 68 | * 69 | * 类实例的名称必须为 "app",查看 "app.app" 的尾部 70 | * 71 | * @note if you modified the name of he instance, 72 | * you should also modify the name in the file "main.cpp" 73 | * not recommended for beginner 74 | * 75 | * 如果你修改了类实例的名字,你也应该一同修改文件 "main.cpp" 76 | * 里的名字 77 | * 不推荐新手做此操作 78 | * 79 | */ 80 | class App 81 | { 82 | private: 83 | /** 84 | * @brief GPIO number 85 | * GPIO 引脚 86 | */ 87 | uint8_t pinLED = 18U; 88 | 89 | /** 90 | * @brief indicate LED state 91 | * 指示LED状态 92 | */ 93 | bool isLightOn = false; 94 | 95 | /** 96 | * @brief last action time 97 | * 上次动作时间 98 | */ 99 | uint64_t lastActionTime = 0; 100 | 101 | /** 102 | * @brief delay timeout 103 | * 延时时间 104 | */ 105 | uint32_t delayTimeout = 1000U; 106 | 107 | public: 108 | /** 109 | * @brief default constructor, attention attached! 110 | * 默认构造函数,请阅读注意事项! 111 | * 112 | * @attention if you declared and define other constructors 113 | * you should NOT do any actions except set default value 114 | * for variables. 115 | * Because the instance of class App will be define in the tail 116 | * of the app.cpp, and that is NOT included in the system 117 | * GlobalManager::makeSureNewFirmwareValid() measurement scope, 118 | * if any action in constructor cause ESP32 panic reset, 119 | * you will lost connection of this ESP32, then you have to 120 | * use hardware serial to upload new firmware. 121 | * 122 | * 如果你定义了其他构造函数,请确保在构造函数内 [不要] 做 除了给变量赋值 123 | * 以外的其他操作,因为类App的实例在 app.cpp 文件的尾部被实例化, 124 | * 而这不在系统 GlobalManager::makeSureNewFirmwareValid() 的 125 | * 监测范围内,如果构造函数内有任何操作导致ESP32复位,你会丢失与这个 126 | * ESP32的连接,此时你只能再次用硬件串口重新上传固件。 127 | * 128 | */ 129 | inline App(){}; 130 | 131 | inline App(uint8_t pinLED, uint32_t delayTimeout = 1000U) 132 | : pinLED(pinLED), delayTimeout(delayTimeout) {} 133 | 134 | inline ~App(){}; 135 | 136 | /** 137 | * @brief put your code in this function to run it once 138 | * 139 | * 把你的代码放在这个函数里,代码会被运行一次 140 | * 141 | */ 142 | void setup(); 143 | 144 | /** 145 | * @brief put your code in this function 146 | * it will run repeatedly, attention attached 147 | * 148 | * 把你的代码放在这个函数里,代码会重复的运行,请阅读注意事项 149 | * 150 | * @attention loop function will NOT run when OTA update 151 | * process running 152 | * 153 | * 在OTA升级期间,loop函数不会被运行 154 | * 155 | */ 156 | void loop(); 157 | }; 158 | 159 | extern App *app; -------------------------------------------------------------------------------- /examples/database_example/README.md: -------------------------------------------------------------------------------- 1 | This example show how to use class MyDB. 2 | 3 | 这个例子演示如何使用类MyDB。 -------------------------------------------------------------------------------- /examples/database_example/app/app.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * @file app.cpp 3 | * @brief 4 | * 5 | * Code this file just like Arduino way 6 | * 7 | * 就像使用Arduino框架一样在这个文件中写代码就可以了 8 | * 9 | */ 10 | 11 | #include "app.h" 12 | 13 | void App::setup() 14 | { 15 | MyDB dbExample("example"); 16 | dbExample.begin(); 17 | 18 | if (!dbExample.loaded) 19 | { 20 | Serial.println("Database load failed 数据库加载失败"); 21 | return; 22 | } 23 | 24 | auto value = dbExample("key"); 25 | if (!value->available()) 26 | { 27 | Serial.println("key does not exists or value unavailable\n键不存在或值不可用"); 28 | } 29 | 30 | // set value 31 | // 设置值 32 | (*value) = "value"; 33 | 34 | // or set it directly 35 | // 或者直接设置 36 | 37 | *dbExample("number_A") = 0; 38 | 39 | // force type 40 | // 强制类型 41 | *dbExample("number_B") = (uint64_t)0xffU; 42 | 43 | *dbExample("number_C") = (uint64_t)millis(); 44 | 45 | (*dbExample("number_A"))++; 46 | 47 | // clear a key/value 48 | // 清除键/值 49 | *dbExample("number_C") = ""; 50 | 51 | // don't forget write it to flash 52 | // 别忘了写入flash 53 | if (!dbExample.flush()) 54 | { 55 | Serial.println("database flush failed\n数据库写入失败"); 56 | } 57 | } 58 | 59 | void App::loop() 60 | { 61 | // put your code in this function 62 | // it will run repeatedly, attention attached 63 | 64 | // 把你的代码放在这个函数里,代码会重复的运行,请阅读注意事项 65 | } 66 | 67 | /** 68 | * @brief App instance defined here 69 | * 70 | * 类App实例在此实例化 71 | * 72 | */ 73 | App *app = new App(); -------------------------------------------------------------------------------- /examples/database_example/app/app.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | 4 | /** 5 | * @brief name of app 6 | * 7 | * app名称 8 | * 9 | */ 10 | #define APP_NAME "Class ArrayBuffer" 11 | 12 | /** 13 | * @brief version of app 14 | * 15 | * app版本 16 | * 17 | */ 18 | #define APP_VERSION 0 19 | 20 | /** 21 | * @brief default cpu frequency of current app 22 | * 23 | * 这个app默认的cpu频率 24 | * 25 | */ 26 | #define DEFAULT_CPU_FREQ 240 27 | 28 | 29 | /** 30 | * @brief indicate app has custom extra callback 31 | * for local websocket server 32 | * 33 | * 指示app是否有自定义的用于本地websocket服务器的回调函数 34 | * 35 | * 36 | * @note the function declared and defined as C pattern 37 | * not included in class App 38 | * 39 | * 函数以C语言风格声明和定义,不包含在类App内 40 | * 41 | */ 42 | //#define APP_HAS_EXTRA_LOCAL_WEBSOCKET_CALLBACK 43 | 44 | /** 45 | * @brief indicate app has custom extra callback 46 | * for remote websocket client 47 | * 48 | * 指示app是否有自定义的用于远程websocket客户端的回调函数 49 | * 50 | * 51 | * @note the function declared and defined as C pattern 52 | * not included in class App 53 | * 54 | * 函数以C语言风格声明和定义,不包含在类App内 55 | * 56 | */ 57 | //#define APP_HAS_EXTRA_REMOTE_WEBSOCKET_CALLBACK 58 | 59 | /** 60 | * @brief default app class 61 | * you could modify class name, attention attached 62 | * 63 | * 默认的app类 64 | * 你可以修改这个类的名字,请阅读注意事项 65 | * 66 | * @attention the name of instance must be "app" 67 | * refer to the tail of the file "app.cpp" 68 | * 69 | * 类实例的名称必须为 "app",查看 "app.app" 的尾部 70 | * 71 | * @note if you modified the name of he instance, 72 | * you should also modify the name in the file "main.cpp" 73 | * not recommended for beginner 74 | * 75 | * 如果你修改了类实例的名字,你也应该一同修改文件 "main.cpp" 76 | * 里的名字 77 | * 不推荐新手做此操作 78 | * 79 | */ 80 | class App 81 | { 82 | private: 83 | public: 84 | /** 85 | * @brief default constructor, attention attached! 86 | * 默认构造函数,请阅读注意事项! 87 | * 88 | * @attention if you declared and define other constructors 89 | * you should NOT do any actions except set default value 90 | * for variables. 91 | * Because the instance of class App will be define in the tail 92 | * of the app.cpp, and that is NOT included in the system 93 | * GlobalManager::makeSureNewFirmwareValid() measurement scope, 94 | * if any action in constructor cause ESP32 panic reset, 95 | * you will lost connection of this ESP32, then you have to 96 | * use hardware serial to upload new firmware. 97 | * 98 | * 如果你定义了其他构造函数,请确保在构造函数内 [不要] 做 除了给变量赋值 99 | * 以外的其他操作,因为类App的实例在 app.cpp 文件的尾部被实例化, 100 | * 而这不在系统 GlobalManager::makeSureNewFirmwareValid() 的 101 | * 监测范围内,如果构造函数内有任何操作导致ESP32复位,你会丢失与这个 102 | * ESP32的连接,此时你只能再次用硬件串口重新上传固件。 103 | * 104 | */ 105 | inline App(){}; 106 | inline ~App(){}; 107 | 108 | /** 109 | * @brief put your code in this function to run it once 110 | * 111 | * 把你的代码放在这个函数里,代码会被运行一次 112 | * 113 | */ 114 | void setup(); 115 | 116 | /** 117 | * @brief put your code in this function 118 | * it will run repeatedly, attention attached 119 | * 120 | * 把你的代码放在这个函数里,代码会重复的运行,请阅读注意事项 121 | * 122 | * @attention loop function will NOT run when OTA update 123 | * process running 124 | * 125 | * 在OTA升级期间,loop函数不会被运行 126 | * 127 | */ 128 | void loop(); 129 | }; 130 | 131 | extern App *app; -------------------------------------------------------------------------------- /examples/element_example/README.md: -------------------------------------------------------------------------------- 1 | This example show how to use class Element. 2 | 3 | 这个例子演示如何使用类Element。 -------------------------------------------------------------------------------- /examples/element_example/app/app.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * @file app.cpp 3 | * @brief 4 | * 5 | * Code this file just like Arduino way 6 | * 7 | * 就像使用Arduino框架一样在这个文件中写代码就可以了 8 | * 9 | */ 10 | 11 | #include "app.h" 12 | 13 | void App::setup() 14 | { 15 | Element x; 16 | Element y = 100; 17 | uint8_t buf[] = {97, 98, 99}; 18 | String hello = "Hello world!"; 19 | 20 | // set value 21 | // 设置值 22 | 23 | // set number 24 | // 设置数字 25 | x = 10; 26 | 27 | x.setNumber(100); 28 | 29 | // set number with given type 30 | // 用指定的类型设置数字 31 | x = (uint16_t)65535; 32 | 33 | // set string 34 | // 设置字符串 35 | x = "Hello world!"; 36 | x = hello; 37 | x = &hello; 38 | x.setString("Hello world"); 39 | x.setString(hello); 40 | x.setString(&hello); 41 | 42 | // set buffer 43 | // 设置二进制数组 44 | x(buf, 3); 45 | x.setUint8Array(buf, 3); 46 | 47 | // set with another element 48 | // 用另一个元素来设置值 49 | x = y; 50 | x = &y; 51 | x.copyFrom(y); 52 | x.copyFrom(&y); 53 | 54 | // operators 55 | // 操作符 56 | 57 | // math operators 58 | // 数学符号 59 | 60 | // reset value 61 | // 重置值 62 | x = 10000; 63 | 64 | x++; 65 | x--; 66 | 67 | ++x; 68 | --x; 69 | 70 | uint64_t a = 0; 71 | 72 | a = x + y; 73 | a = x - y; 74 | a = x * y; 75 | a = x / y; 76 | a = x % y; 77 | 78 | a = x + 100; 79 | a = x - 100; 80 | a = x * 100; 81 | a = x / 100; 82 | a = x % 100; 83 | 84 | x = "Hello "; 85 | String b = x + String("world!"); 86 | String c = "o"; 87 | x += "world!"; 88 | 89 | // will remove all char 'o' 90 | // 会去除掉所有的'o' 91 | String c = x - c; 92 | 93 | x -= c; 94 | 95 | // reset value 96 | // 重置值 97 | x = 10000; 98 | 99 | x += 100; 100 | x -= 100; 101 | x *= 100; 102 | x /= 100; 103 | x %= 100; 104 | 105 | // reset value 106 | // 重置值 107 | x = 10000; 108 | 109 | // bit operators 110 | // 位符号 111 | a = ~x; 112 | 113 | a = x << 2; 114 | a = x >> 2; 115 | 116 | x <<= 2; 117 | x >>= 2; 118 | 119 | a = x & 0xffU; 120 | a = x | 0xffU; 121 | 122 | x &= 0xffU; 123 | x |= 0xffU; 124 | 125 | a = x ^ 0xffU; 126 | x ^= 0xffU; 127 | 128 | // reset value 129 | // 重置值 130 | x = 10000; 131 | 132 | // logical 133 | // 逻辑值 134 | Element z = 2; 135 | bool d = x && 100; 136 | int e = 100; 137 | 138 | d = x || 100; 139 | d = !x; 140 | 141 | d = x > z; 142 | d = x < z; 143 | 144 | d = x >= z; 145 | d = x <= z; 146 | 147 | d = x == z; 148 | 149 | d = x > e; 150 | d = x < e; 151 | 152 | d = x <= e; 153 | d = x >= e; 154 | 155 | x = "abc"; 156 | z = "cde"; 157 | 158 | d = x < z; 159 | d = x > z; 160 | //... 161 | 162 | // reset value 163 | // 重置值 164 | x(buf, 3); 165 | z = 2; 166 | 167 | // index 168 | // 索引 169 | uint8_t f = x[2]; 170 | f = x[z]; 171 | 172 | x = "abc"; 173 | char g = x[2]; 174 | 175 | // others functions 176 | // 其他功能 177 | x = "Hello world!"; 178 | const char *h = x.c_str(); 179 | f = x.indexOf("w"); 180 | f = x.lastIndexOf("o"); 181 | 182 | ElementType type = x.getType(); 183 | type = x.getType(true); 184 | 185 | // reset value 186 | // 重置值 187 | x = 10000; 188 | 189 | uint64_t i = x.getUint8(); 190 | i = x.getUint16(); 191 | i = x.getUint32(); 192 | i = x.getUint64(); 193 | i = x.getNumber(); 194 | 195 | x = "abc"; 196 | String j = x.getString(); 197 | 198 | // reset value 199 | // 重置值 200 | x(buf, 3); 201 | String k = x.getHex(); 202 | 203 | x = "0f3ab86d19f98891"; 204 | x.convertHexStringIntoUint8Array(); 205 | 206 | uint32_t m = 0; 207 | uint8_t *l = x.getUint8Array(&m); 208 | m = x.getRawBufferLength(); 209 | 210 | } 211 | 212 | void App::loop() 213 | { 214 | // put your code in this function 215 | // it will run repeatedly, attention attached 216 | 217 | // 把你的代码放在这个函数里,代码会重复的运行,请阅读注意事项 218 | } 219 | 220 | /** 221 | * @brief App instance defined here 222 | * 223 | * 类App实例在此实例化 224 | * 225 | */ 226 | App *app = new App(); -------------------------------------------------------------------------------- /examples/element_example/app/app.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | 4 | /** 5 | * @brief name of app 6 | * 7 | * app名称 8 | * 9 | */ 10 | #define APP_NAME "Class Element" 11 | 12 | /** 13 | * @brief version of app 14 | * 15 | * app版本 16 | * 17 | */ 18 | #define APP_VERSION 0 19 | 20 | /** 21 | * @brief default cpu frequency of current app 22 | * 23 | * 这个app默认的cpu频率 24 | * 25 | */ 26 | #define DEFAULT_CPU_FREQ 240 27 | 28 | 29 | /** 30 | * @brief indicate app has custom extra callback 31 | * for local websocket server 32 | * 33 | * 指示app是否有自定义的用于本地websocket服务器的回调函数 34 | * 35 | * 36 | * @note the function declared and defined as C pattern 37 | * not included in class App 38 | * 39 | * 函数以C语言风格声明和定义,不包含在类App内 40 | * 41 | */ 42 | //#define APP_HAS_EXTRA_LOCAL_WEBSOCKET_CALLBACK 43 | 44 | /** 45 | * @brief indicate app has custom extra callback 46 | * for remote websocket client 47 | * 48 | * 指示app是否有自定义的用于远程websocket客户端的回调函数 49 | * 50 | * 51 | * @note the function declared and defined as C pattern 52 | * not included in class App 53 | * 54 | * 函数以C语言风格声明和定义,不包含在类App内 55 | * 56 | */ 57 | //#define APP_HAS_EXTRA_REMOTE_WEBSOCKET_CALLBACK 58 | 59 | /** 60 | * @brief default app class 61 | * you could modify class name, attention attached 62 | * 63 | * 默认的app类 64 | * 你可以修改这个类的名字,请阅读注意事项 65 | * 66 | * @attention the name of instance must be "app" 67 | * refer to the tail of the file "app.cpp" 68 | * 69 | * 类实例的名称必须为 "app",查看 "app.app" 的尾部 70 | * 71 | * @note if you modified the name of he instance, 72 | * you should also modify the name in the file "main.cpp" 73 | * not recommended for beginner 74 | * 75 | * 如果你修改了类实例的名字,你也应该一同修改文件 "main.cpp" 76 | * 里的名字 77 | * 不推荐新手做此操作 78 | * 79 | */ 80 | class App 81 | { 82 | private: 83 | public: 84 | /** 85 | * @brief default constructor, attention attached! 86 | * 默认构造函数,请阅读注意事项! 87 | * 88 | * @attention if you declared and define other constructors 89 | * you should NOT do any actions except set default value 90 | * for variables. 91 | * Because the instance of class App will be define in the tail 92 | * of the app.cpp, and that is NOT included in the system 93 | * GlobalManager::makeSureNewFirmwareValid() measurement scope, 94 | * if any action in constructor cause ESP32 panic reset, 95 | * you will lost connection of this ESP32, then you have to 96 | * use hardware serial to upload new firmware. 97 | * 98 | * 如果你定义了其他构造函数,请确保在构造函数内 [不要] 做 除了给变量赋值 99 | * 以外的其他操作,因为类App的实例在 app.cpp 文件的尾部被实例化, 100 | * 而这不在系统 GlobalManager::makeSureNewFirmwareValid() 的 101 | * 监测范围内,如果构造函数内有任何操作导致ESP32复位,你会丢失与这个 102 | * ESP32的连接,此时你只能再次用硬件串口重新上传固件。 103 | * 104 | */ 105 | inline App(){}; 106 | inline ~App(){}; 107 | 108 | /** 109 | * @brief put your code in this function to run it once 110 | * 111 | * 把你的代码放在这个函数里,代码会被运行一次 112 | * 113 | */ 114 | void setup(); 115 | 116 | /** 117 | * @brief put your code in this function 118 | * it will run repeatedly, attention attached 119 | * 120 | * 把你的代码放在这个函数里,代码会重复的运行,请阅读注意事项 121 | * 122 | * @attention loop function will NOT run when OTA update 123 | * process running 124 | * 125 | * 在OTA升级期间,loop函数不会被运行 126 | * 127 | */ 128 | void loop(); 129 | }; 130 | 131 | extern App *app; -------------------------------------------------------------------------------- /examples/empty_example/README.md: -------------------------------------------------------------------------------- 1 | This is an empty example, only show code structure. 2 | 3 | 这是个空工程,仅用于展示代码结构。 -------------------------------------------------------------------------------- /examples/empty_example/app/app.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * @file app.cpp 3 | * @brief 4 | * 5 | * Code this file just like Arduino way 6 | * 7 | * 就像使用Arduino框架一样在这个文件中写代码就可以了 8 | * 9 | */ 10 | 11 | #include "app.h" 12 | 13 | void App::setup() 14 | { 15 | /** 16 | * ===English: 17 | * put your code in this function to run it once 18 | * 19 | * ===中文: 20 | * 把你的代码放在这个函数里,代码会被运行一次 21 | */ 22 | 23 | /** 24 | * ===English: 25 | * if you just did an OTA update 26 | * you could do anything here, don't worry any code bugs 27 | * if any error happened, and reboot times reach maximum valve 28 | * firmware will rollback to former version automatically 29 | * 30 | * you could modify the valve yourself 31 | * in file /lib/config/config.h 32 | * 33 | * ! Attention: ONLY when firmware updated by built-in OTA function 34 | * has this feature, not included update through serial lines 35 | * 36 | * ===中文: 37 | * 如果你刚才使用了OTA升级了这个固件 38 | * 你可以在这里写任何代码 39 | * 无须担心任何错误引起无限复位 40 | * 当复位次数超过设定阈值时 41 | * 固件会自动回滚上一个版本 42 | * 43 | * 阈值可以根据自己的需求调整 44 | * 在 /lib/config/config.h 45 | * 46 | * ! 注意: [ 仅 ] 当固件通过内置的OTA升级功能升级后才有此功能 47 | * 直接从串口升级的固件没有此功能 48 | */ 49 | } 50 | 51 | void App::loop() 52 | { 53 | // put your code in this function 54 | // it will run repeatedly, attention attached 55 | 56 | // 把你的代码放在这个函数里,代码会重复的运行,请阅读注意事项 57 | } 58 | 59 | /** 60 | * @brief App instance defined here 61 | * 62 | * 类App实例在此实例化 63 | * 64 | */ 65 | App *app = new App(); -------------------------------------------------------------------------------- /examples/empty_example/app/app.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | 4 | /** 5 | * @brief name of app 6 | * 7 | * app名称 8 | * 9 | */ 10 | #define APP_NAME "Abc" 11 | 12 | /** 13 | * @brief version of app 14 | * 15 | * app版本 16 | * 17 | */ 18 | #define APP_VERSION 0 19 | 20 | /** 21 | * @brief default cpu frequency of current app 22 | * 23 | * 这个app默认的cpu频率 24 | * 25 | */ 26 | #define DEFAULT_CPU_FREQ 240 27 | 28 | 29 | /** 30 | * @brief indicate app has custom extra callback 31 | * for local websocket server 32 | * 33 | * 指示app是否有自定义的用于本地websocket服务器的回调函数 34 | * 35 | * 36 | * @note the function declared and defined as C pattern 37 | * not included in class App 38 | * 39 | * 函数以C语言风格声明和定义,不包含在类App内 40 | * 41 | */ 42 | //#define APP_HAS_EXTRA_LOCAL_WEBSOCKET_CALLBACK 43 | 44 | /** 45 | * @brief indicate app has custom extra callback 46 | * for remote websocket client 47 | * 48 | * 指示app是否有自定义的用于远程websocket客户端的回调函数 49 | * 50 | * 51 | * @note the function declared and defined as C pattern 52 | * not included in class App 53 | * 54 | * 函数以C语言风格声明和定义,不包含在类App内 55 | * 56 | */ 57 | //#define APP_HAS_EXTRA_REMOTE_WEBSOCKET_CALLBACK 58 | 59 | /** 60 | * @brief default app class 61 | * you could modify class name, attention attached 62 | * 63 | * 默认的app类 64 | * 你可以修改这个类的名字,请阅读注意事项 65 | * 66 | * @attention the name of instance must be "app" 67 | * refer to the tail of the file "app.cpp" 68 | * 69 | * 类实例的名称必须为 "app",查看 "app.app" 的尾部 70 | * 71 | * @note if you modified the name of he instance, 72 | * you should also modify the name in the file "main.cpp" 73 | * not recommended for beginner 74 | * 75 | * 如果你修改了类实例的名字,你也应该一同修改文件 "main.cpp" 76 | * 里的名字 77 | * 不推荐新手做此操作 78 | * 79 | */ 80 | class App 81 | { 82 | private: 83 | public: 84 | /** 85 | * @brief default constructor, attention attached! 86 | * 默认构造函数,请阅读注意事项! 87 | * 88 | * @attention if you declared and define other constructors 89 | * you should NOT do any actions except set default value 90 | * for variables. 91 | * Because the instance of class App will be define in the tail 92 | * of the app.cpp, and that is NOT included in the system 93 | * GlobalManager::makeSureNewFirmwareValid() measurement scope, 94 | * if any action in constructor cause ESP32 panic reset, 95 | * you will lost connection of this ESP32, then you have to 96 | * use hardware serial to upload new firmware. 97 | * 98 | * 如果你定义了其他构造函数,请确保在构造函数内 [不要] 做 除了给变量赋值 99 | * 以外的其他操作,因为类App的实例在 app.cpp 文件的尾部被实例化, 100 | * 而这不在系统 GlobalManager::makeSureNewFirmwareValid() 的 101 | * 监测范围内,如果构造函数内有任何操作导致ESP32复位,你会丢失与这个 102 | * ESP32的连接,此时你只能再次用硬件串口重新上传固件。 103 | * 104 | */ 105 | inline App(){}; 106 | inline ~App(){}; 107 | 108 | /** 109 | * @brief put your code in this function to run it once 110 | * 111 | * 把你的代码放在这个函数里,代码会被运行一次 112 | * 113 | */ 114 | void setup(); 115 | 116 | /** 117 | * @brief put your code in this function 118 | * it will run repeatedly, attention attached 119 | * 120 | * 把你的代码放在这个函数里,代码会重复的运行,请阅读注意事项 121 | * 122 | * @attention loop function will NOT run when OTA update 123 | * process running 124 | * 125 | * 在OTA升级期间,loop函数不会被运行 126 | * 127 | */ 128 | void loop(); 129 | }; 130 | 131 | extern App *app; -------------------------------------------------------------------------------- /examples/ota_example/README.md: -------------------------------------------------------------------------------- 1 | ### English 2 | 3 | This example show OTA update function. 4 | 5 | Before you use this example, you need to do a little bit prepare: 6 | 7 | * 1. Install Node.js 16 on your PC, offical download address: 8 | 9 | ```console 10 | https://nodejs.org/ 11 | ``` 12 | 13 | * 2. Use cmd or terminal open the path /server/ 14 | 15 | * 3. Run 16 | 17 | ```console 18 | node iot.js 19 | ``` 20 | 21 | Or 22 | 23 | ```console 24 | sudo node iot.js 25 | ``` 26 | 27 | To start a local server. 28 | 29 | Don't forget let port pass in firewall. 30 | 31 | * 4. Modify the IP address in "app.h" to your PC IP address(Local Area Network IP), line 131. 32 | 33 | * 5. Compile and upload firmware 34 | 35 | * 6. Open your serial monitor, follow the guide in serial monitor 36 | 37 | (If you didn't see anything output from monitor, press reset button on your ESP32 dev board.) 38 | 39 | 40 | ### 中文 41 | 42 | 这个例子展示OTA升级功能。 43 | 44 | 在你使用这个例子之前,你需要做一点点准备工作: 45 | 46 | * 1. 在你的电脑上安装 Node.js, 官方下载地址: 47 | 48 | ```console 49 | https://nodejs.org/ 50 | ``` 51 | 52 | ```console 53 | http://nodejs.cn/ 54 | ``` 55 | 56 | * 2. 使用 cmd 或终端打开路径 /server/ 57 | 58 | * 3. 运行 59 | 60 | ```console 61 | node iot.js 62 | ``` 63 | 64 | 或者 65 | 66 | ```console 67 | sudo node iot.js 68 | ``` 69 | 70 | 来开启一个本地服务器。 71 | 72 | 别忘了在防火墙中放行相应端口. 73 | 74 | * 4. 修改文件 "app.h" 中的IP地址为你电脑所在的内网IP地址,131行 75 | 76 | * 5. 编译上传固件 77 | 78 | * 6. 打开你的串口监视器, 跟随串口监视器中的指引操作 79 | 80 | (如果你没有在串口监视器中看到任何信息,按复位按钮。) 81 | 82 | -------------------------------------------------------------------------------- /examples/ota_example/app/app.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * @file app.cpp 3 | * @brief 4 | * 5 | * Code this file just like Arduino way 6 | * 7 | * 就像使用Arduino框架一样在这个文件中写代码就可以了 8 | * 9 | */ 10 | 11 | #include "app.h" 12 | 13 | void App::setup() 14 | { 15 | Serial.println("1. Open your browser 打开你的浏览器\n"); 16 | 17 | Serial.println("2. Redirect to \"http://127.0.0.1:12345/\" or \"http://localhost:12345/\""); 18 | Serial.println("转到\"http://127.0.0.1:12345/\" 或者 \"http://localhost:12345/\""); 19 | 20 | Serial.println("3. Use \"abc12345678\" as user name and password"); 21 | Serial.println("使用 \"abc12345678\" 作为用户名和密码\n"); 22 | 23 | Serial.println("4. Login 登录\n"); 24 | 25 | Serial.println("5. You can see esp32 is online 你可以看见esp32在线\n"); 26 | 27 | Serial.println("6. Click \"@\" 点击 \"@\"\n"); 28 | 29 | Serial.println("7. Click \"Upload firmware\" 点击 \"上传固件\"\n"); 30 | 31 | Serial.println("8. Choose a firmware 选择一个固件\n"); 32 | 33 | Serial.println("9. Click OK 点击确定\n"); 34 | 35 | Serial.println("10. Click \"OTA Update\" 点击 \"更新固件\"\n"); 36 | 37 | Serial.println("Then you will see esp32 is updating firmware, it will reboot after finished"); 38 | Serial.println("然后你就可以看到esp32正在更新固件了, 更新完成后会自动重启"); 39 | } 40 | 41 | void App::loop() 42 | { 43 | // put your code in this function 44 | // it will run repeatedly, attention attached 45 | 46 | // 把你的代码放在这个函数里,代码会重复的运行,请阅读注意事项 47 | } 48 | 49 | /** 50 | * @brief App instance defined here 51 | * 52 | * 类App实例在此实例化 53 | * 54 | */ 55 | App *app = new App(); -------------------------------------------------------------------------------- /examples/ota_example/app/app.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | 4 | /** 5 | * @brief name of app 6 | * 7 | * app名称 8 | * 9 | */ 10 | #define APP_NAME "OTA Update" 11 | 12 | /** 13 | * @brief version of app 14 | * 15 | * app版本 16 | * 17 | */ 18 | #define APP_VERSION 0 19 | 20 | /** 21 | * @brief default cpu frequency of current app 22 | * 23 | * 这个app默认的cpu频率 24 | * 25 | */ 26 | #define DEFAULT_CPU_FREQ 240 27 | 28 | 29 | /** 30 | * @brief indicate app has custom extra callback 31 | * for local websocket server 32 | * 33 | * 指示app是否有自定义的用于本地websocket服务器的回调函数 34 | * 35 | * 36 | * @note the function declared and defined as C pattern 37 | * not included in class App 38 | * 39 | * 函数以C语言风格声明和定义,不包含在类App内 40 | * 41 | */ 42 | //#define APP_HAS_EXTRA_LOCAL_WEBSOCKET_CALLBACK 43 | 44 | /** 45 | * @brief indicate app has custom extra callback 46 | * for remote websocket client 47 | * 48 | * 指示app是否有自定义的用于远程websocket客户端的回调函数 49 | * 50 | * 51 | * @note the function declared and defined as C pattern 52 | * not included in class App 53 | * 54 | * 函数以C语言风格声明和定义,不包含在类App内 55 | * 56 | */ 57 | //#define APP_HAS_EXTRA_REMOTE_WEBSOCKET_CALLBACK 58 | 59 | #ifdef PRESET_WIFI_SSID 60 | #undef PRESET_WIFI_SSID 61 | #endif 62 | 63 | #ifdef PRESET_WIFI_PASSWORD 64 | #undef PRESET_WIFI_PASSWORD 65 | #endif 66 | 67 | #ifdef PRESET_ADMIN_USERNAME 68 | #undef PRESET_ADMIN_USERNAME 69 | #endif 70 | 71 | #ifdef PRESET_ADMIN_PASSWORD 72 | #undef PRESET_ADMIN_PASSWORD 73 | #endif 74 | 75 | #ifndef ENABLE_USE_PRESET_WEBSOCKET_INFORMATION 76 | #define ENABLE_USE_PRESET_WEBSOCKET_INFORMATION 77 | #endif 78 | 79 | #ifdef PRESET_WEBSOCKET_DOMAIN 80 | #undef PRESET_WEBSOCKET_DOMAIN 81 | #endif 82 | 83 | #ifdef PRESET_WEBSOCKET_PORT 84 | #undef PRESET_WEBSOCKET_PORT 85 | #endif 86 | 87 | #ifdef PRESET_WEBSOCKET_PATH 88 | #undef PRESET_WEBSOCKET_PATH 89 | #endif 90 | 91 | /** 92 | * @brief replace the strings 93 | * following 2 lines with 94 | * your wifi name and password 95 | * 96 | * 把下面两行里面的字符串替换成你的wifi名称 97 | * 和密码 98 | * 99 | */ 100 | #define PRESET_WIFI_SSID "Your WiFi Name" 101 | #define PRESET_WIFI_PASSWORD "Your WiFi password" 102 | 103 | /** 104 | * @brief replace IP with your local ip 105 | * of your computer, and run /server/start.(bat/command) 106 | * make sure you didn't change the default port 107 | * of the local server 108 | * 109 | * 把 IP 替换为你电脑的局域网IP地址 110 | * 然后运行 /server/start.(bat/command) 111 | * 确保你没有修改本地服务器的默认端口 112 | * 113 | */ 114 | #define PRESET_WEBSOCKET_DOMAIN "IP" 115 | #define PRESET_WEBSOCKET_PORT 12345 116 | #define PRESET_WEBSOCKET_PATH "/" 117 | 118 | #define PRESET_ADMIN_USERNAME "e06787aebebb1ce726260447b036fd36289a7e7133f61b1def05f18734300b1a" 119 | #define PRESET_ADMIN_PASSWORD "e06787aebebb1ce726260447b036fd36289a7e7133f61b1def05f18734300b1a" 120 | 121 | /** 122 | * @brief default app class 123 | * you could modify class name, attention attached 124 | * 125 | * 默认的app类 126 | * 你可以修改这个类的名字,请阅读注意事项 127 | * 128 | * @attention the name of instance must be "app" 129 | * refer to the tail of the file "app.cpp" 130 | * 131 | * 类实例的名称必须为 "app",查看 "app.app" 的尾部 132 | * 133 | * @note if you modified the name of he instance, 134 | * you should also modify the name in the file "main.cpp" 135 | * not recommended for beginner 136 | * 137 | * 如果你修改了类实例的名字,你也应该一同修改文件 "main.cpp" 138 | * 里的名字 139 | * 不推荐新手做此操作 140 | * 141 | */ 142 | class App 143 | { 144 | private: 145 | public: 146 | /** 147 | * @brief default constructor, attention attached! 148 | * 默认构造函数,请阅读注意事项! 149 | * 150 | * @attention if you declared and define other constructors 151 | * you should NOT do any actions except set default value 152 | * for variables. 153 | * Because the instance of class App will be define in the tail 154 | * of the app.cpp, and that is NOT included in the system 155 | * GlobalManager::makeSureNewFirmwareValid() measurement scope, 156 | * if any action in constructor cause ESP32 panic reset, 157 | * you will lost connection of this ESP32, then you have to 158 | * use hardware serial to upload new firmware. 159 | * 160 | * 如果你定义了其他构造函数,请确保在构造函数内 [不要] 做 除了给变量赋值 161 | * 以外的其他操作,因为类App的实例在 app.cpp 文件的尾部被实例化, 162 | * 而这不在系统 GlobalManager::makeSureNewFirmwareValid() 的 163 | * 监测范围内,如果构造函数内有任何操作导致ESP32复位,你会丢失与这个 164 | * ESP32的连接,此时你只能再次用硬件串口重新上传固件。 165 | * 166 | */ 167 | inline App(){}; 168 | inline ~App(){}; 169 | 170 | /** 171 | * @brief put your code in this function to run it once 172 | * 173 | * 把你的代码放在这个函数里,代码会被运行一次 174 | * 175 | */ 176 | void setup(); 177 | 178 | /** 179 | * @brief put your code in this function 180 | * it will run repeatedly, attention attached 181 | * 182 | * 把你的代码放在这个函数里,代码会重复的运行,请阅读注意事项 183 | * 184 | * @attention loop function will NOT run when OTA update 185 | * process running 186 | * 187 | * 在OTA升级期间,loop函数不会被运行 188 | * 189 | */ 190 | void loop(); 191 | }; 192 | 193 | extern App *app; -------------------------------------------------------------------------------- /examples/provider_example/README.md: -------------------------------------------------------------------------------- 1 | This is example shows you how to create unique provider for single app. 2 | 3 | 这个例子展示如何给 app 添加 单独的 provider。 -------------------------------------------------------------------------------- /examples/provider_example/app/app.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * @file app.cpp 3 | * @brief 4 | * 5 | * Code this file just like Arduino way 6 | * 7 | * 就像使用Arduino框架一样在这个文件中写代码就可以了 8 | * 9 | */ 10 | 11 | #include "app.h" 12 | 13 | void App::setup() 14 | { 15 | 16 | // callback for provider 17 | // you could return information or null pointer 18 | // provider 的回调函数 19 | // 你可以返回信息或空指针 20 | auto fnCallback = []( 21 | // arguments 参数 22 | ProviderArguments arguments) -> Element * 23 | { 24 | // do your things here with arguments 25 | // you could convert every argument to supported types 26 | // in web page, js file 27 | // or use string directly 28 | // 你可以在这用参数执行你的操作 29 | // 你可以提前在前端把参数转换为支持的类型 30 | // 或者直接使用字符串 31 | if (arguments->size()) 32 | { 33 | auto argument0 = arguments->at(0); 34 | 35 | if (argument0->available()) 36 | { 37 | // ... 38 | } 39 | } 40 | 41 | // return information, no need to delete object 42 | // 返回信息, 不需要手动释放对象 43 | return new Element("Hello world!"); 44 | // return new Element(0x10); 45 | 46 | /* 47 | * char buf[24] = {0}; 48 | * sprintf(buf, "Time: %llu", globalTime->getTime() ); 49 | * return new Element(buf); 50 | */ 51 | 52 | // ... 53 | 54 | // or return null pointer 55 | // 或者返回空指针 56 | // return nullptr; 57 | }; 58 | 59 | // create new provider 60 | // this provider only belongs to this app 61 | // 创建新的provider 62 | // 这个provider只属于当前app 63 | global->createProvider( 64 | 65 | // callback 回调函数 66 | fnCallback, 67 | 68 | // provider name, provider 名称 69 | "Provider 0", 70 | 71 | // provider settings, provider 选项 72 | (PROVIDER_ADMIN | PROVIDER_COMMON), 73 | 74 | // how many arguments this provider needs, for web page use, default is 0 75 | // 这个provider 需要多少个参数, 给前端用的, 默认为0 76 | 0, 77 | 78 | // custom id, for other purpose, optional 79 | // 其他用途的自定义id,可省略 80 | 12345ULL); 81 | 82 | /* 83 | if you added "PROVIDER_USER" in settings 84 | this provider will show it to any users 85 | this is especially for web page js using 86 | if you added "PROVIDER_QUESTION" in settings 87 | means this provider should question user 88 | to confirm action 89 | though anyone could send request to 90 | execute those providers only 91 | administrator can execute 92 | don't worry, the request will be confirmed 93 | at local, provider will NOT be executed 94 | if user role isn't administrator 95 | 96 | 如果你在选项中添加了 "PROVIDER_USER" 97 | 那么这个 provider 会显示给所有用户 98 | 这个是为了前端用的 99 | 如果你在选项添加了 "PROVIDER_QUESTION" 100 | 代表这个操作需要确认,典型的,比如双击或者 101 | 弹窗询问 102 | 尽管所有人都可以通过直接发送请求 103 | 比如直接使用控制台企图用普通用户执行 104 | 管理员才能执行的provider 105 | 但是无须担心 106 | 请求会在本地被确认 107 | 如果用户角色不是管理员 108 | 则请求会被丢弃 109 | */ 110 | global->createProvider( 111 | [](ProviderArguments arguments) -> Element * 112 | { 113 | // ... 114 | return new Element("OK"); 115 | }, 116 | "Provider 1", 117 | (PROVIDER_USER | PROVIDER_QUESTION), 118 | 2); 119 | 120 | /* 121 | if you added "PROVIDER_ENCRYPT" in settings 122 | both arguments and return value will be encrypted 123 | with AES-256-CBC before transfer to target 124 | I think this is safer than https 125 | 126 | 如果你在选项添加了 "PROVIDER_ENCRYPT" 127 | 那么参数和返回值都会使用 AES-256-CBC 加密后 128 | 再传输给对方 129 | 我认为这比https更安全 130 | */ 131 | global->createProvider( 132 | [](ProviderArguments arguments) -> Element * 133 | { 134 | // arguments had been decrypted before callback 135 | // so just use it is fine 136 | // 参数在回调函数执行前已经被解密 137 | // 直接使用就可以了 138 | // ... 139 | return new Element("OK"); 140 | }, 141 | "Provider 2", 142 | (PROVIDER_ADMIN | PROVIDER_COMMON | PROVIDER_ENCRYPT), 143 | 2); 144 | 145 | global->createProvider( 146 | [](ProviderArguments arguments) -> Element * 147 | { 148 | // ... 149 | return new Element("OK"); 150 | }, 151 | "Provider 3", 152 | PROVIDER_USER, 153 | 0, 154 | 155 | // custom id could use as many purpose 156 | // like execute provider by http get method 157 | // other purposes depend on your design 158 | // 自定义 id 可以用作许多用途 159 | // 比如通过 http get 方法执行provider 160 | // 其他用途取决于你的设计 161 | 8888ULL); 162 | } 163 | 164 | void App::loop() 165 | { 166 | // put your code in this function 167 | // it will run repeatedly, attention attached 168 | 169 | // 把你的代码放在这个函数里,代码会重复的运行,请阅读注意事项 170 | } 171 | 172 | /** 173 | * @brief App instance defined here 174 | * 175 | * 类App实例在此实例化 176 | * 177 | */ 178 | App *app = new App(); -------------------------------------------------------------------------------- /examples/provider_example/app/app.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | 4 | /** 5 | * @brief name of app 6 | * 7 | * app名称 8 | * 9 | */ 10 | #define APP_NAME "Provider" 11 | 12 | /** 13 | * @brief version of app 14 | * 15 | * app版本 16 | * 17 | */ 18 | #define APP_VERSION 0 19 | 20 | /** 21 | * @brief default cpu frequency of current app 22 | * 23 | * 这个app默认的cpu频率 24 | * 25 | */ 26 | #define DEFAULT_CPU_FREQ 240 27 | 28 | 29 | /** 30 | * @brief indicate app has custom extra callback 31 | * for local websocket server 32 | * 33 | * 指示app是否有自定义的用于本地websocket服务器的回调函数 34 | * 35 | * 36 | * @note the function declared and defined as C pattern 37 | * not included in class App 38 | * 39 | * 函数以C语言风格声明和定义,不包含在类App内 40 | * 41 | */ 42 | //#define APP_HAS_EXTRA_LOCAL_WEBSOCKET_CALLBACK 43 | 44 | /** 45 | * @brief indicate app has custom extra callback 46 | * for remote websocket client 47 | * 48 | * 指示app是否有自定义的用于远程websocket客户端的回调函数 49 | * 50 | * 51 | * @note the function declared and defined as C pattern 52 | * not included in class App 53 | * 54 | * 函数以C语言风格声明和定义,不包含在类App内 55 | * 56 | */ 57 | //#define APP_HAS_EXTRA_REMOTE_WEBSOCKET_CALLBACK 58 | 59 | /** 60 | * @brief default app class 61 | * you could modify class name, attention attached 62 | * 63 | * 默认的app类 64 | * 你可以修改这个类的名字,请阅读注意事项 65 | * 66 | * @attention the name of instance must be "app" 67 | * refer to the tail of the file "app.cpp" 68 | * 69 | * 类实例的名称必须为 "app",查看 "app.app" 的尾部 70 | * 71 | * @note if you modified the name of he instance, 72 | * you should also modify the name in the file "main.cpp" 73 | * not recommended for beginner 74 | * 75 | * 如果你修改了类实例的名字,你也应该一同修改文件 "main.cpp" 76 | * 里的名字 77 | * 不推荐新手做此操作 78 | * 79 | */ 80 | class App 81 | { 82 | private: 83 | public: 84 | /** 85 | * @brief default constructor, attention attached! 86 | * 默认构造函数,请阅读注意事项! 87 | * 88 | * @attention if you declared and define other constructors 89 | * you should NOT do any actions except set default value 90 | * for variables. 91 | * Because the instance of class App will be define in the tail 92 | * of the app.cpp, and that is NOT included in the system 93 | * GlobalManager::makeSureNewFirmwareValid() measurement scope, 94 | * if any action in constructor cause ESP32 panic reset, 95 | * you will lost connection of this ESP32, then you have to 96 | * use hardware serial to upload new firmware. 97 | * 98 | * 如果你定义了其他构造函数,请确保在构造函数内 [不要] 做 除了给变量赋值 99 | * 以外的其他操作,因为类App的实例在 app.cpp 文件的尾部被实例化, 100 | * 而这不在系统 GlobalManager::makeSureNewFirmwareValid() 的 101 | * 监测范围内,如果构造函数内有任何操作导致ESP32复位,你会丢失与这个 102 | * ESP32的连接,此时你只能再次用硬件串口重新上传固件。 103 | * 104 | */ 105 | inline App(){}; 106 | inline ~App(){}; 107 | 108 | /** 109 | * @brief put your code in this function to run it once 110 | * 111 | * 把你的代码放在这个函数里,代码会被运行一次 112 | * 113 | */ 114 | void setup(); 115 | 116 | /** 117 | * @brief put your code in this function 118 | * it will run repeatedly, attention attached 119 | * 120 | * 把你的代码放在这个函数里,代码会重复的运行,请阅读注意事项 121 | * 122 | * @attention loop function will NOT run when OTA update 123 | * process running 124 | * 125 | * 在OTA升级期间,loop函数不会被运行 126 | * 127 | */ 128 | void loop(); 129 | }; 130 | 131 | extern App *app; -------------------------------------------------------------------------------- /examples/sha_digest_example/README.md: -------------------------------------------------------------------------------- 1 | This example show you how to get SHA digest with one line code. 2 | 3 | 这个例子演示如何使用一行代码获取SHA数字摘要。 -------------------------------------------------------------------------------- /examples/sha_digest_example/app/app.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * @file app.cpp 3 | * @brief 4 | * 5 | * Code this file just like Arduino way 6 | * 7 | * 就像使用Arduino框架一样在这个文件中写代码就可以了 8 | * 9 | */ 10 | 11 | #include "app.h" 12 | 13 | void App::setup() 14 | { 15 | // SHA1 16 | String sha1Result = mycrypto::SHA::sha1("1234567890"); 17 | 18 | // SHA256 19 | String sha256Result = mycrypto::SHA::sha256("1234567890"); 20 | 21 | // print 22 | // 打印 23 | Serial.printf("SHA1 of \"1234567890\": %s\n", sha1Result.c_str()); 24 | Serial.printf("SHA256 of \"1234567890\": %s\n", sha256Result.c_str()); 25 | 26 | //and there are more way to get digest, refer to /lib/mycrypto/mycrypto.h 27 | //还有更多方法获取数字摘要,访问 /lib/mycrypto/mycrypto.h 28 | } 29 | 30 | void App::loop() 31 | { 32 | // put your code in this function 33 | // it will run repeatedly, attention attached 34 | 35 | // 把你的代码放在这个函数里,代码会重复的运行,请阅读注意事项 36 | } 37 | 38 | /** 39 | * @brief App instance defined here 40 | * 41 | * 类App实例在此实例化 42 | * 43 | */ 44 | App *app = new App(); -------------------------------------------------------------------------------- /examples/sha_digest_example/app/app.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | 4 | /** 5 | * @brief name of app 6 | * 7 | * app名称 8 | * 9 | */ 10 | #define APP_NAME "SHA Digest" 11 | 12 | /** 13 | * @brief version of app 14 | * 15 | * app版本 16 | * 17 | */ 18 | #define APP_VERSION 0 19 | 20 | /** 21 | * @brief default cpu frequency of current app 22 | * 23 | * 这个app默认的cpu频率 24 | * 25 | */ 26 | #define DEFAULT_CPU_FREQ 240 27 | 28 | 29 | /** 30 | * @brief indicate app has custom extra callback 31 | * for local websocket server 32 | * 33 | * 指示app是否有自定义的用于本地websocket服务器的回调函数 34 | * 35 | * 36 | * @note the function declared and defined as C pattern 37 | * not included in class App 38 | * 39 | * 函数以C语言风格声明和定义,不包含在类App内 40 | * 41 | */ 42 | //#define APP_HAS_EXTRA_LOCAL_WEBSOCKET_CALLBACK 43 | 44 | /** 45 | * @brief indicate app has custom extra callback 46 | * for remote websocket client 47 | * 48 | * 指示app是否有自定义的用于远程websocket客户端的回调函数 49 | * 50 | * 51 | * @note the function declared and defined as C pattern 52 | * not included in class App 53 | * 54 | * 函数以C语言风格声明和定义,不包含在类App内 55 | * 56 | */ 57 | //#define APP_HAS_EXTRA_REMOTE_WEBSOCKET_CALLBACK 58 | 59 | /** 60 | * @brief default app class 61 | * you could modify class name, attention attached 62 | * 63 | * 默认的app类 64 | * 你可以修改这个类的名字,请阅读注意事项 65 | * 66 | * @attention the name of instance must be "app" 67 | * refer to the tail of the file "app.cpp" 68 | * 69 | * 类实例的名称必须为 "app",查看 "app.app" 的尾部 70 | * 71 | * @note if you modified the name of he instance, 72 | * you should also modify the name in the file "main.cpp" 73 | * not recommended for beginner 74 | * 75 | * 如果你修改了类实例的名字,你也应该一同修改文件 "main.cpp" 76 | * 里的名字 77 | * 不推荐新手做此操作 78 | * 79 | */ 80 | class App 81 | { 82 | private: 83 | public: 84 | /** 85 | * @brief default constructor, attention attached! 86 | * 默认构造函数,请阅读注意事项! 87 | * 88 | * @attention if you declared and define other constructors 89 | * you should NOT do any actions except set default value 90 | * for variables. 91 | * Because the instance of class App will be define in the tail 92 | * of the app.cpp, and that is NOT included in the system 93 | * GlobalManager::makeSureNewFirmwareValid() measurement scope, 94 | * if any action in constructor cause ESP32 panic reset, 95 | * you will lost connection of this ESP32, then you have to 96 | * use hardware serial to upload new firmware. 97 | * 98 | * 如果你定义了其他构造函数,请确保在构造函数内 [不要] 做 除了给变量赋值 99 | * 以外的其他操作,因为类App的实例在 app.cpp 文件的尾部被实例化, 100 | * 而这不在系统 GlobalManager::makeSureNewFirmwareValid() 的 101 | * 监测范围内,如果构造函数内有任何操作导致ESP32复位,你会丢失与这个 102 | * ESP32的连接,此时你只能再次用硬件串口重新上传固件。 103 | * 104 | */ 105 | inline App(){}; 106 | inline ~App(){}; 107 | 108 | /** 109 | * @brief put your code in this function to run it once 110 | * 111 | * 把你的代码放在这个函数里,代码会被运行一次 112 | * 113 | */ 114 | void setup(); 115 | 116 | /** 117 | * @brief put your code in this function 118 | * it will run repeatedly, attention attached 119 | * 120 | * 把你的代码放在这个函数里,代码会重复的运行,请阅读注意事项 121 | * 122 | * @attention loop function will NOT run when OTA update 123 | * process running 124 | * 125 | * 在OTA升级期间,loop函数不会被运行 126 | * 127 | */ 128 | void loop(); 129 | }; 130 | 131 | extern App *app; -------------------------------------------------------------------------------- /examples/web_serial_example/README.md: -------------------------------------------------------------------------------- 1 | This is example shows you how to push message to administrator. 2 | 3 | 这个例子展示如何推送消息给管理员。 -------------------------------------------------------------------------------- /examples/web_serial_example/app/app.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * @file app.cpp 3 | * @brief 4 | * 5 | * Code this file just like Arduino way 6 | * 7 | * 就像使用Arduino框架一样在这个文件中写代码就可以了 8 | * 9 | */ 10 | 11 | #include "app.h" 12 | 13 | void App::setup() 14 | { 15 | 16 | app->push = dbApp("push")->getUint8() ? true : false; 17 | 18 | // create a provider for toggle message push 19 | // 创建一个 provider 用于切换消息推送 20 | global->createProvider( 21 | [](ProviderArguments arguments) -> Element * 22 | { 23 | app->push = !app->push; 24 | 25 | // you could store it with database 26 | // 你可以顺便把设置保存一下 27 | 28 | *dbApp("push") = app->push ? 1 : 0; 29 | dbApp.flush(); 30 | 31 | return new Element("OK"); 32 | }, 33 | "Toggle 开关", PROVIDER_USER); 34 | } 35 | 36 | void App::loop() 37 | { 38 | if (this->push) 39 | { 40 | uint64_t t = millis(); 41 | 42 | if (t - this->lastPushTime > 1000) 43 | { 44 | this->lastPushTime = t; 45 | global->sendMessageToClient(globalTime->getTime()); 46 | } 47 | } 48 | } 49 | 50 | /** 51 | * @brief App instance defined here 52 | * 53 | * 类App实例在此实例化 54 | * 55 | */ 56 | App *app = new App(); -------------------------------------------------------------------------------- /examples/web_serial_example/app/app.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | 4 | /** 5 | * @brief name of app 6 | * 7 | * app名称 8 | * 9 | */ 10 | #define APP_NAME "Web Serial" 11 | 12 | /** 13 | * @brief version of app 14 | * 15 | * app版本 16 | * 17 | */ 18 | #define APP_VERSION 0 19 | 20 | /** 21 | * @brief default cpu frequency of current app 22 | * 23 | * 这个app默认的cpu频率 24 | * 25 | */ 26 | #define DEFAULT_CPU_FREQ 240 27 | 28 | 29 | /** 30 | * @brief indicate app has custom extra callback 31 | * for local websocket server 32 | * 33 | * 指示app是否有自定义的用于本地websocket服务器的回调函数 34 | * 35 | * 36 | * @note the function declared and defined as C pattern 37 | * not included in class App 38 | * 39 | * 函数以C语言风格声明和定义,不包含在类App内 40 | * 41 | */ 42 | //#define APP_HAS_EXTRA_LOCAL_WEBSOCKET_CALLBACK 43 | 44 | /** 45 | * @brief indicate app has custom extra callback 46 | * for remote websocket client 47 | * 48 | * 指示app是否有自定义的用于远程websocket客户端的回调函数 49 | * 50 | * 51 | * @note the function declared and defined as C pattern 52 | * not included in class App 53 | * 54 | * 函数以C语言风格声明和定义,不包含在类App内 55 | * 56 | */ 57 | //#define APP_HAS_EXTRA_REMOTE_WEBSOCKET_CALLBACK 58 | 59 | /** 60 | * @brief default app class 61 | * you could modify class name, attention attached 62 | * 63 | * 默认的app类 64 | * 你可以修改这个类的名字,请阅读注意事项 65 | * 66 | * @attention the name of instance must be "app" 67 | * refer to the tail of the file "app.cpp" 68 | * 69 | * 类实例的名称必须为 "app",查看 "app.app" 的尾部 70 | * 71 | * @note if you modified the name of he instance, 72 | * you should also modify the name in the file "main.cpp" 73 | * not recommended for beginner 74 | * 75 | * 如果你修改了类实例的名字,你也应该一同修改文件 "main.cpp" 76 | * 里的名字 77 | * 不推荐新手做此操作 78 | * 79 | */ 80 | class App 81 | { 82 | private: 83 | public: 84 | bool push = false; 85 | uint64_t lastPushTime = 0; 86 | /** 87 | * @brief default constructor, attention attached! 88 | * 默认构造函数,请阅读注意事项! 89 | * 90 | * @attention if you declared and define other constructors 91 | * you should NOT do any actions except set default value 92 | * for variables. 93 | * Because the instance of class App will be define in the tail 94 | * of the app.cpp, and that is NOT included in the system 95 | * GlobalManager::makeSureNewFirmwareValid() measurement scope, 96 | * if any action in constructor cause ESP32 panic reset, 97 | * you will lost connection of this ESP32, then you have to 98 | * use hardware serial to upload new firmware. 99 | * 100 | * 如果你定义了其他构造函数,请确保在构造函数内 [不要] 做 除了给变量赋值 101 | * 以外的其他操作,因为类App的实例在 app.cpp 文件的尾部被实例化, 102 | * 而这不在系统 GlobalManager::makeSureNewFirmwareValid() 的 103 | * 监测范围内,如果构造函数内有任何操作导致ESP32复位,你会丢失与这个 104 | * ESP32的连接,此时你只能再次用硬件串口重新上传固件。 105 | * 106 | */ 107 | inline App(){}; 108 | inline ~App(){}; 109 | 110 | /** 111 | * @brief put your code in this function to run it once 112 | * 113 | * 把你的代码放在这个函数里,代码会被运行一次 114 | * 115 | */ 116 | void setup(); 117 | 118 | /** 119 | * @brief put your code in this function 120 | * it will run repeatedly, attention attached 121 | * 122 | * 把你的代码放在这个函数里,代码会重复的运行,请阅读注意事项 123 | * 124 | * @attention loop function will NOT run when OTA update 125 | * process running 126 | * 127 | * 在OTA升级期间,loop函数不会被运行 128 | * 129 | */ 130 | void loop(); 131 | }; 132 | 133 | extern App *app; -------------------------------------------------------------------------------- /firmware/README.md: -------------------------------------------------------------------------------- 1 | Complied firmware will be copied to here. 2 | 3 | 编译好的固件会被拷贝到这里。 -------------------------------------------------------------------------------- /lib/arraybuffer/README.md: -------------------------------------------------------------------------------- 1 | The class Element is the core of class MyDB and whole system, it allows you to store data in it. And you could use it for manu purpose. 2 | The class ArrayBuffer is a tool that generate buffer using Element vector, or decode buffer to Element vector. 3 | If you are a beginner, do not modify any codes. If you are master-hand of C++ and you have enhancement method, please submit issue or send email, thanks. 4 | There are full comments in header file, check arraybuffer.h for more information. 5 | 6 | 类Element是数据库和整个系统的核心,他可以用来存储数据。你可以把它用作许多用途。 7 | 类ArrayBuffer是一个可以将Element容器转换为buffer的工具,或者可以用来将buffer转换为Element容器。 8 | 如果你是新手,请不要改动任何代码。如果你是C++高手觉得某些地方可以改进,可以提交issue或发邮件,谢谢。 9 | 头文件中有完整的注释,更多信息请参阅arraybuffer.h。 10 | 11 | 2023/03/23 12 | Rebuilt class Element, now it supports all types of data. 13 | Change file structure from .h and .cpp to single .hpp. 14 | Almost all operators had been overloaded, it's very useful. 15 | More usage refer to /test/test.cpp. 16 | 17 | 18 | 重构了类Element,现在它支持所有数据类型。 19 | 修改了文件结构,由.hpp替代.h和.cpp。 20 | 几乎所有的符号都已被重载,这很实用。 21 | 更多用法参见/test/test.cpp。 -------------------------------------------------------------------------------- /lib/config/README.md: -------------------------------------------------------------------------------- 1 | Almost all configs in the config.h, you could change it yourself. 2 | 3 | 几乎所有的设置都在config.h中,你可以自己修改。 -------------------------------------------------------------------------------- /lib/esp32time/esp32time.cpp: -------------------------------------------------------------------------------- 1 | #include "esp32time.h" 2 | 3 | String Esp32Time::getTimeFormat(bool longFormat) 4 | { 5 | struct tm time = this->getStructure(); 6 | char s[61]; 7 | if (longFormat) 8 | { 9 | strftime(s, 60, "%H:%M:%S %A, %B %d %Y", &time); 10 | } 11 | else 12 | { 13 | strftime(s, 60, "%H:%M:%S %a, %b %d %Y", &time); 14 | } 15 | return String(s); 16 | } 17 | 18 | struct tm Esp32Time::getStructure() 19 | { 20 | time_t now; 21 | struct tm tms; 22 | time(&now); 23 | localtime_r(&now, &tms); 24 | return tms; 25 | } 26 | 27 | void Esp32Time::setTime(uint64_t time, uint16_t timeZone) 28 | { 29 | long second = time / 1000; 30 | int ms = time % 1000; 31 | 32 | struct timeval tv; 33 | tv.tv_sec = second; 34 | tv.tv_usec = ms; 35 | 36 | setenv("TZ", String(String("GMT-") + String(timeZone)).c_str(), 1); 37 | tzset(); 38 | 39 | settimeofday(&tv, NULL); 40 | } 41 | 42 | void Esp32Time::setTime(uint16_t year, uint8_t month, uint8_t date, uint8_t hour, uint8_t minute, uint8_t second, uint16_t ms) 43 | { 44 | struct tm time = {0}; 45 | time.tm_year = year - 1900; 46 | time.tm_mon = month - 1; 47 | time.tm_mday = date; 48 | time.tm_hour = hour; 49 | time.tm_min = minute; 50 | time.tm_sec = second; 51 | time_t t = mktime(&time); 52 | setTime((t * 1000) + ms); 53 | } 54 | 55 | String Esp32Time::getDate(bool longFormat) 56 | { 57 | struct tm time = this->getStructure(); 58 | 59 | char buf[61]; 60 | buf[60] = 0; 61 | 62 | if (longFormat) 63 | { 64 | strftime(buf, 60, "%A, %B %d %Y", &time); 65 | } 66 | else 67 | { 68 | strftime(buf, 60, "%a, %b %d %Y", &time); 69 | } 70 | 71 | return String(buf); 72 | } 73 | 74 | uint64_t Esp32Time::getSeconds() 75 | { 76 | struct timeval tv; 77 | gettimeofday(&tv, NULL); 78 | return tv.tv_sec; 79 | } 80 | 81 | uint16_t Esp32Time::getMilli() 82 | { 83 | return this->getMicro() / 1000; 84 | } 85 | 86 | uint32_t Esp32Time::getMicro() 87 | { 88 | struct timeval tv; 89 | gettimeofday(&tv, NULL); 90 | return tv.tv_usec; 91 | } 92 | 93 | Esp32Time *globalTime = new Esp32Time(); -------------------------------------------------------------------------------- /lib/esp32time/esp32time.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file esp32time.h 3 | * @author Vida Wang (support@vida.wang) 4 | * @brief This file provided functions related to time, reduced and modified 5 | * some functions from https://github.com/fbiego/ESP32Time, thanks to Felix Biego 6 | * 7 | * 这个文件提供了时间相关的功能,从 https://github.com/fbiego/ESP32Time 去掉了一些 8 | * 不常用功能,对有些功能做出了一些修改,感谢 Felix Biego 9 | * 10 | * @version 1.0.0 11 | * @date 2022-08-01 12 | * 13 | * @copyright Copyright (c) 2022 14 | * 15 | */ 16 | 17 | /* 18 | MIT License 19 | Copyright (c) 2021 Felix Biego 20 | Permission is hereby granted, free of charge, to any person obtaining a copy 21 | of this software and associated documentation files (the "Software"), to deal 22 | in the Software without restriction, including without limitation the rights 23 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 24 | copies of the Software, and to permit persons to whom the Software is 25 | furnished to do so, subject to the following conditions: 26 | The above copyright notice and this permission notice shall be included in all 27 | copies or substantial portions of the Software. 28 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 29 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 30 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 31 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 32 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 33 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 34 | SOFTWARE. 35 | */ 36 | 37 | #ifndef ESP32_TIME_H_ 38 | #define ESP32_TIME_H_ 39 | 40 | #include 41 | #include "time.h" 42 | #include 43 | 44 | class Esp32Time 45 | { 46 | private: 47 | struct tm getStructure(); 48 | 49 | public: 50 | /** 51 | * @brief set system time(unix epoch timestamp) 52 | * 设置系统时间(unix时间戳) 53 | * 54 | * @param time unix epoch timestamp, unix时间戳 55 | * @param timeZone time zone 时区 56 | */ 57 | void setTime(uint64_t time, uint16_t timeZone = 8); 58 | 59 | /** 60 | * @brief set system time 61 | * 设置系统时间 62 | * 63 | * @param year year 年 64 | * @param month month 月 65 | * @param date date 日 66 | * @param hour hour 时 67 | * @param minute minute 分 68 | * @param second second 秒 69 | * @param ms millisecond 毫秒 70 | */ 71 | void setTime(uint16_t year, uint8_t month, uint8_t date, uint8_t hour, uint8_t minute, uint8_t second, uint16_t ms = 0); 72 | 73 | /** 74 | * @brief get formatted time string 75 | * 获取格式化的时间 76 | * 77 | * @param longFormat use full name or not 是否使用完整名称 78 | * @return String formatted time 格式化的时间 79 | */ 80 | String getTimeFormat(bool longFormat = true); 81 | 82 | /** 83 | * @brief get day number in year 84 | * 获取在一年中的天数 85 | * 86 | * @return uint16_t number of day 天数 87 | */ 88 | inline uint16_t getDayInYear() 89 | { 90 | return this->getStructure().tm_yday; 91 | } 92 | 93 | /** 94 | * @brief get milliseconds from unix epoch 95 | * 获取unix时间戳 96 | * 97 | * @return uint64_t unix epoch timestamp, unix时间戳 98 | */ 99 | inline uint64_t getTime() 100 | { 101 | return ((uint64_t)((this->getSeconds() * 1000) + this->getMilli())); 102 | } 103 | 104 | /** 105 | * @brief get year 106 | * 获取年份 107 | * 108 | * @return uint16_t year 年份 109 | */ 110 | inline uint16_t getYear() 111 | { 112 | return this->getStructure().tm_year + 1900; 113 | } 114 | 115 | /** 116 | * @brief get month 117 | * 获取月份 118 | * 119 | * @return uint8_t month 月份 120 | */ 121 | inline uint8_t getMonth() 122 | { 123 | return this->getStructure().tm_mon; 124 | } 125 | 126 | /** 127 | * @brief get date(not included time) 128 | * 获取日期(不包括时间) 129 | * 130 | * @param longFormat use full name 是否使用完整名称 131 | * @return String formatted date 格式化的日期 132 | */ 133 | String getDate(bool longFormat = true); 134 | 135 | /** 136 | * @brief get hour 137 | * 获取小时 138 | * 139 | * @param a24hour use a24-hour or not 是否使用24小时制 140 | * @return uint8_t hour 小时 141 | */ 142 | inline uint8_t getHour(bool a24hour = true) 143 | { 144 | uint8_t hour = this->getStructure().tm_hour; 145 | return a24hour ? hour : (hour > 12 ? hour - 12 : hour); 146 | } 147 | 148 | /** 149 | * @brief get minute 150 | * 获取分钟 151 | * 152 | * @return uint8_t minute 分钟 153 | */ 154 | inline uint8_t getMinute() 155 | { 156 | return this->getStructure().tm_min; 157 | } 158 | 159 | /** 160 | * @brief get second 161 | * 获取秒数 162 | * 163 | * @return uint8_t second 秒数 164 | */ 165 | uint8_t getSecond() 166 | { 167 | return this->getStructure().tm_sec; 168 | } 169 | 170 | /** 171 | * @brief get seconds from unix epoch timestamp 172 | * 获取unix时间戳的秒数 173 | * 174 | * @return uint64_t seconds from unix epoch timestamp, unix时间戳的秒数 175 | */ 176 | uint64_t getSeconds(); 177 | 178 | /** 179 | * @brief get milliseconds 180 | * 获取毫秒数 181 | * 182 | * @return uint16_t milliseconds 毫秒数 183 | */ 184 | uint16_t getMilli(); 185 | 186 | /** 187 | * @brief get microseconds 188 | * 获取微秒数 189 | * 190 | * @return uint32_t microseconds 微秒数 191 | */ 192 | uint32_t getMicro(); 193 | 194 | /** 195 | * @brief get day in week 196 | * 获取在星期内的天数 197 | * 198 | * @return uint8_t day 天数 199 | */ 200 | inline uint8_t getDay() 201 | { 202 | return this->getStructure().tm_wday; 203 | } 204 | 205 | /** 206 | * @brief get formatted time string 207 | * 获取格式化的时间 208 | * 209 | * @param longFormat use full name or not 是否使用完整名称 210 | * @return String formatted time 格式化的时间 211 | */ 212 | inline String getDateTime(bool longFormat = true) 213 | { 214 | return this->getTimeFormat(longFormat); 215 | } 216 | 217 | inline Esp32Time() {} 218 | inline ~Esp32Time() {} 219 | }; 220 | 221 | extern Esp32Time *globalTime; 222 | 223 | #endif -------------------------------------------------------------------------------- /lib/languages/languages.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file languages.h 3 | * @author Vida Wang (support@vida.wang) 4 | * @brief This file include languages informations 5 | * 这个文件包含了语言信息 6 | * @version 1.0.0 7 | * @date 2022-08-16 8 | * 9 | * @copyright Copyright (c) 2022 10 | * 11 | */ 12 | 13 | #ifndef LANGUAGES_H_ 14 | #define LANGUAGES_H_ 15 | #include "config.h" 16 | 17 | #ifdef CHINESE_VERSION 18 | // PROVIDER_INFO 19 | 20 | #define PI_UNKNOWN "未知" 21 | #define PI_BOOT "开机" 22 | #define PI_ESP_RST_INT_WDT "中断看门狗" 23 | #define PI_ESP_RST_TASK_WDT "任务看门狗" 24 | #define PI_ESP_RST_WDT "其他看门狗" 25 | #define PI_ESP_RST_PANIC "软件异常" 26 | #define PI_ESP_RST_BROWNOUT "掉电保护器" 27 | #define PI_ESP_RST_DEEPSLEEP "深度睡眠" 28 | #define PI_ESP_RST_SW "软件复位" 29 | #define PI_GET_RESET_AND_ONLINE_TIME "获取复位信息和启动时间" 30 | 31 | #define PI_INVALID_LENGTH_OF_ARGUMENTS "参数个数不正确" 32 | #define PI_INVALID_LENGTH_OF_ARGUMENT "参数长度不正确" 33 | #define PI_INVALID_ARGUMENT "参数不正确" 34 | #define PI_INVALID_TYPE_OF_ARGUMENT "参数类型不正确" 35 | #define PI_SUCCESS "成功" 36 | #define PI_DELETED "已删除" 37 | 38 | #define PI_TOKEN_SET "token已设置" 39 | #define PI_MODIFY_TOKEN "修改token" 40 | 41 | #define PI_USER_DELETED "用户删除成功" 42 | #define PI_ADD_MODIFY_DELETE_USER "(添加/修改/删除)用户" 43 | 44 | #define PI_WEB_SERIAL "发送数据到串口" 45 | 46 | #define PI_DATABASE_UPDATED "数据库已更新" 47 | #define PI_QUERY_UPDATE_MAIN_DATABASE_OR_LIST_ALL_KEYS_IN_MAIN_DATABASE \ 48 | "((查询/更新)主数据库)/(列出所有主数据库中的所有键)" 49 | 50 | #define PI_REMOVE_VALUE_IN_MAIN_DATABASE "删除主数据库中的值" 51 | 52 | #define PI_WIFI_INFORMATION_REMOVED "WiFi信息已清除" 53 | #define PI_REMOVE_WIFI_INFORMATION "[清除WiFi信息]" 54 | 55 | #define PI_CAN_NOT_ROLLBACK "没有可供回滚的固件" 56 | #define PI_WILL_ROLLBACK_IN_TIMEOUT_SECONDS "会在3秒后回滚固件" 57 | #define PI_ROLLBACK "[回滚固件]" 58 | 59 | #define PI_FIRMWARE_PENDING_VERIFY "正在等待验证固件有效性" 60 | #define PI_FIRMWARE_VALID "固件有效" 61 | #define PI_NO_OTA_INFORMATION "没有在数据库中找到OTA升级信息" 62 | #define PI_FIRMWARE_STATUS "固件状态" 63 | 64 | #define PI_LAST_OTA_TIME "上次OTA更新时间" 65 | 66 | #define PI_MAC_ADDRESS "物理地址(MAC)" 67 | 68 | #define PI_WILL_INTO_DEEP_SLEEP_FOR_TIMEOUT_SECONDS_IN_SECONDS \ 69 | "将会睡眠%u秒, %u秒后执行" 70 | #define PI_DEEP_SLEEP "[睡眠]" 71 | 72 | #define PI_ALL_INFORMATION_REMOVED_REBOOT_IN_3_SECONDS \ 73 | "所有信息已被清除, 3秒后重启" 74 | 75 | #define PI_REMOVE_ALL_INFORMATION "[清除所有信息]" 76 | 77 | 78 | #define PI_NEW_SSID_OR_PASSWORD_HAS_BEEN_SET "新的%s: \"%s\"已经设置" 79 | #define PI_INVALID_SSID_PROVIDED "提供的SSID不正确" 80 | #define PI_MODIFY_SSID "[修改WiFi SSID]" 81 | 82 | #define PI_INVALID_WIFI_PASSWORD "提供了格式不正确的密码" 83 | #define PI_MODIFY_WIFI_PASSWORD "[修改WiFi密码]" 84 | 85 | #define PI_WILL_REBOOT_IN_TIMEOUT_SECONDS "将会在[%u]秒后重启" 86 | #define PI_REBOOT "[重启]" 87 | 88 | #define PI_HARDWARE_TIMESTAMP "硬件时间" 89 | 90 | #define PI_PREFIX_BYTE "字节" 91 | #define PI_FREE_HEAP "剩余堆内存" 92 | 93 | #define PI_FREE_SPACE "剩余存储空间" 94 | 95 | #define PI_FIRMWARE_ERROR_TIME "固件异常: %llu" 96 | 97 | 98 | #else 99 | 100 | #define PI_UNKNOWN "unknown" 101 | #define PI_BOOT "power on" 102 | #define PI_ESP_RST_INT_WDT "interrupt watchdog" 103 | #define PI_ESP_RST_TASK_WDT "task watchdog" 104 | #define PI_ESP_RST_WDT "other watchdogs" 105 | #define PI_ESP_RST_PANIC "software error" 106 | #define PI_ESP_RST_BROWNOUT "low voltage" 107 | #define PI_ESP_RST_DEEPSLEEP "deep sleep" 108 | #define PI_ESP_RST_SW "software reset" 109 | #define PI_GET_RESET_AND_ONLINE_TIME "Get last reset reason" 110 | 111 | #define PI_INVALID_LENGTH_OF_ARGUMENTS "Invalid length of arguments" 112 | #define PI_INVALID_LENGTH_OF_ARGUMENT "Invalid length of argument" 113 | #define PI_INVALID_ARGUMENT "Invalid argument" 114 | #define PI_INVALID_TYPE_OF_ARGUMENT "Invalid type of arguments" 115 | #define PI_SUCCESS "Success" 116 | #define PI_DELETED "Removed" 117 | 118 | #define PI_TOKEN_SET "Token has been set" 119 | #define PI_MODIFY_TOKEN "Modify token" 120 | 121 | #define PI_USER_DELETED "User removed" 122 | #define PI_ADD_MODIFY_DELETE_USER "(Add/Update/Remove)User" 123 | 124 | #define PI_WEB_SERIAL "Send data to serial" 125 | 126 | #define PI_DATABASE_UPDATED "Database updated" 127 | #define PI_QUERY_UPDATE_MAIN_DATABASE_OR_LIST_ALL_KEYS_IN_MAIN_DATABASE \ 128 | "((Query/Update)value in main database)/(List all keys in main database)" 129 | 130 | #define PI_REMOVE_VALUE_IN_MAIN_DATABASE "Remove value in main database" 131 | 132 | #define PI_WIFI_INFORMATION_REMOVED "WiFi information has been removed" 133 | #define PI_REMOVE_WIFI_INFORMATION "[RESET WiFi information]" 134 | 135 | #define PI_CAN_NOT_ROLLBACK "There isn't firmware could rollback" 136 | #define PI_WILL_ROLLBACK_IN_TIMEOUT_SECONDS "Will rollback firmware in 3 seconds" 137 | #define PI_ROLLBACK "[ROLLBACK FIRMWARE]" 138 | 139 | #define PI_FIRMWARE_PENDING_VERIFY "Pending verify" 140 | #define PI_FIRMWARE_VALID "Firmware OK" 141 | #define PI_NO_OTA_INFORMATION "Couldn't detect pendingOTA in database" 142 | #define PI_FIRMWARE_STATUS "Firmware status" 143 | 144 | #define PI_LAST_OTA_TIME "Last OTA update time" 145 | 146 | #define PI_MAC_ADDRESS "MAC Address" 147 | 148 | #define PI_WILL_INTO_DEEP_SLEEP_FOR_TIMEOUT_SECONDS_IN_SECONDS \ 149 | "Will into deep sleep for [%u] seconds in %u seconds" 150 | #define PI_DEEP_SLEEP "[SLEEP]" 151 | 152 | #define PI_ALL_INFORMATION_REMOVED_REBOOT_IN_3_SECONDS \ 153 | "All information has been removed, reboot in 3 seconds" 154 | 155 | #define PI_REMOVE_ALL_INFORMATION "[REMOVE ALL INFORMATION]" 156 | 157 | 158 | #define PI_NEW_SSID_OR_PASSWORD_HAS_BEEN_SET "New%s: \"%s\" set" 159 | #define PI_INVALID_SSID_PROVIDED "Invalid SSID provided" 160 | #define PI_MODIFY_SSID "[UPDATE WiFi SSID]" 161 | 162 | #define PI_INVALID_WIFI_PASSWORD "Invalid password provided" 163 | #define PI_MODIFY_WIFI_PASSWORD "[UPDATE WiFi password]" 164 | 165 | #define PI_WILL_REBOOT_IN_TIMEOUT_SECONDS "Will reboot in %u second(s)" 166 | #define PI_REBOOT "[REBOOT]" 167 | 168 | #define PI_HARDWARE_TIMESTAMP "Hardware timestamp" 169 | 170 | #define PI_PREFIX_BYTE "bytes" 171 | #define PI_FREE_HEAP "Free heap" 172 | 173 | #define PI_FREE_SPACE "Free space" 174 | 175 | #define PI_FIRMWARE_ERROR_TIME "Firmware error: %llu" 176 | 177 | 178 | #endif 179 | 180 | #endif -------------------------------------------------------------------------------- /lib/mycrypto/README.md: -------------------------------------------------------------------------------- 1 | The class WebSocketClient will use SHA1 and Base64 encode functions. Also provided AES-256-CBC encryption and decryption and SHA256. There are full comments in header file, check mycrypto.h for more information. 2 | 3 | 类WebSocketClient会使用SHA1和Base64编码功能,同时提供了AES-256-CBC和SHA256。头文件中有完整的注释,更多信息请参阅mycrypto.h。 -------------------------------------------------------------------------------- /lib/mydb/README.md: -------------------------------------------------------------------------------- 1 | ### English 2 | 3 | A key-value typed RAM database for access data in different types conveniently. 4 | There are full comments in header file, check mydb.h for more information. 5 | 6 | The file /lib/mydb/mydb.h and mydb.cpp declared and defined 3 instances of class MyDB, they are: 7 | 8 | - db 9 | - dbUser 10 | - dbApp 11 | 12 | The first one is main database for system use, you app could use this database to store data, but this is not recommended. 13 | 14 | The second one stored other users information, you **SHOULD NOT** modify any content by your way in this database. 15 | 16 | The last one is for user app, you could do any action to this instance. 17 | 18 | ### 中文 19 | 20 | 一个键值对型内存数据库,方便访问不同种类的数据。 21 | 头文件中有完整的注释,更多信息请参阅mydb.h。 22 | 23 | 文件 /lib/mydb/mydb.h 和 mydb.cpp 中声明和定义了3个数据库实例,它们是: 24 | 25 | - db 26 | - dbUser 27 | - dbApp 28 | 29 | 第一个是主数据库,给系统用的,你的app可以使用这个数据库,但是不建议。 30 | 31 | 第二个存储了其他用户信息,你 **不** 应该对这个数据库用你的方式进行任何操作。 32 | 33 | 第三个是给你的app用的,你可以随意使用。 -------------------------------------------------------------------------------- /lib/myfs/myfs.cpp: -------------------------------------------------------------------------------- 1 | #include "myfs.h" 2 | 3 | void MyFS::myfsInit() 4 | { 5 | if (!LittleFS.begin(FORMAT_LITTLEFS_IF_FAILED)) 6 | { 7 | delay(1000); 8 | ESP_LOGD("myfs", "formatted"); 9 | } 10 | } 11 | 12 | void MyFS::listFile(String path, fileElementList *list, String prefix) 13 | { 14 | File root = LittleFS.open(path); 15 | if (prefix != "") 16 | { 17 | prefix = "/" + prefix; 18 | } 19 | 20 | if (root) 21 | { 22 | if (root.isDirectory()) 23 | { 24 | File file = root.openNextFile(); 25 | 26 | while (file) 27 | { 28 | FileElement fileInfo; 29 | fileInfo.name = file.name(); 30 | if (file.isDirectory()) 31 | { 32 | fileInfo.isFile = false; 33 | fileInfo.size = 0; 34 | list->push_back(fileInfo); 35 | } 36 | else 37 | { 38 | if (prefix != "") 39 | { 40 | if (!fileInfo.name.startsWith(prefix)) 41 | { 42 | file = root.openNextFile(); 43 | continue; 44 | } 45 | } 46 | fileInfo.isFile = true; 47 | fileInfo.size = file.size(); 48 | list->push_back(fileInfo); 49 | } 50 | file = root.openNextFile(); 51 | } 52 | } 53 | } 54 | } 55 | 56 | bool MyFS::writeFile(const char *p, const char *data, bool base64Encode) 57 | { 58 | if (!base64Encode) 59 | { 60 | return writeFile(p, (uint8_t *)data, strlen(data)); 61 | } 62 | else 63 | { 64 | char *d = mycrypto::Base64::base64Encode((uint8_t *)data, strlen(data)); 65 | return writeFile(p, (uint8_t *)d, strlen(d)); 66 | } 67 | } 68 | 69 | bool MyFS::writeFile(const char *p, uint8_t *data, uint64_t length) 70 | { 71 | String path = p; 72 | if (path[0] != '/') 73 | { 74 | path = "/" + path; 75 | } 76 | 77 | File file = LittleFS.open(path, FILE_WRITE); 78 | if (!file) 79 | { 80 | return false; 81 | } 82 | 83 | bool value = file.write(data, length); 84 | file.flush(); 85 | file.close(); 86 | return value; 87 | } 88 | 89 | bool MyFS::writeFile(String path, String data, bool base64Encode) 90 | { 91 | if (path[0] != '/') 92 | { 93 | path = "/" + path; 94 | } 95 | 96 | File file = LittleFS.open(path, FILE_WRITE); 97 | if (!file) 98 | { 99 | return false; 100 | } 101 | 102 | if (base64Encode) 103 | { 104 | data = mycrypto::Base64::base64Encode(data); 105 | } 106 | 107 | bool value = file.print(data); 108 | file.close(); 109 | return value; 110 | } 111 | 112 | uint8_t *MyFS::readFile(const char *p, uint64_t *outLen) 113 | { 114 | String path = p; 115 | if (path[0] != '/') 116 | { 117 | path = "/" + path; 118 | } 119 | File file = LittleFS.open(path); 120 | 121 | if (!file || file.isDirectory()) 122 | { 123 | (*outLen) = 0; 124 | return nullptr; 125 | } 126 | 127 | uint64_t size = file.size(); 128 | uint8_t *data = new (std::nothrow) uint8_t[size]; 129 | 130 | if (!data) 131 | { 132 | (*outLen) = 0; 133 | return nullptr; 134 | } 135 | file.readBytes((char *)data, size); 136 | file.close(); 137 | 138 | (*outLen) = size; 139 | return data; 140 | } 141 | 142 | void MyFS::readFile(const char *p, std::function callback) 143 | { 144 | String path = p; 145 | if (path[0] != '/') 146 | { 147 | path = "/" + path; 148 | } 149 | File file = LittleFS.open(path); 150 | 151 | if (!file || file.isDirectory()) 152 | { 153 | callback(nullptr, 0); 154 | return; 155 | } 156 | uint64_t size = 0; 157 | 158 | uint8_t *data = MyFS::readFile(p, &size); 159 | 160 | callback(data, size); 161 | 162 | delete data; 163 | } 164 | 165 | String MyFS::readFile(String path, bool base64Decode) 166 | { 167 | if (path[0] != '/') 168 | { 169 | path = "/" + path; 170 | } 171 | 172 | File file = LittleFS.open(path); 173 | 174 | if (!file) 175 | { 176 | return String("error"); 177 | } 178 | 179 | if (file.isDirectory()) 180 | { 181 | return String("not file"); 182 | } 183 | 184 | uint32_t length = file.size(); 185 | uint8_t *data = new (std::nothrow) uint8_t[length + 1]; 186 | 187 | if (!data) 188 | { 189 | return String("memory full"); 190 | } 191 | 192 | file.readBytes((char *)data, length); 193 | file.close(); 194 | data[length] = 0; 195 | String output = String((char *)data); 196 | 197 | if (base64Decode) 198 | { 199 | output = mycrypto::Base64::base64Decode(output); 200 | } 201 | 202 | return output; 203 | } 204 | 205 | bool MyFS::appendFile(String path, String data) 206 | { 207 | if (path[0] != '/') 208 | { 209 | path = "/" + path; 210 | } 211 | 212 | File file = LittleFS.open(path, FILE_APPEND); 213 | 214 | if (!file) 215 | { 216 | return false; 217 | } 218 | 219 | bool value = file.print(data); 220 | file.close(); 221 | 222 | return value; 223 | } 224 | 225 | bool MyFS::deleteFile(String path) 226 | { 227 | 228 | if (path[0] != '/') 229 | { 230 | path = "/" + path; 231 | } 232 | return LittleFS.remove(path); 233 | } 234 | 235 | bool MyFS::renameFile(String path0, String path1) 236 | { 237 | if (path0[0] != '/') 238 | { 239 | path0 = "/" + path0; 240 | } 241 | if (path1[0] != '/') 242 | { 243 | path1 = "/" + path1; 244 | } 245 | 246 | return LittleFS.rename(path0, path1); 247 | } 248 | 249 | bool MyFS::fileExist(String path) 250 | { 251 | if (path[0] != '/') 252 | { 253 | path = "/" + path; 254 | } 255 | 256 | return LittleFS.exists(path); 257 | } 258 | 259 | void MyFS::formatSPIFFS() 260 | { 261 | LittleFS.format(); 262 | } 263 | 264 | size_t MyFS::getFreeSpace() 265 | { 266 | return LittleFS.totalBytes() - LittleFS.usedBytes(); 267 | } 268 | 269 | size_t MyFS::getUsedSpace() 270 | { 271 | return LittleFS.usedBytes(); 272 | } -------------------------------------------------------------------------------- /lib/myfs/myfs.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file myfs.h 3 | * @author Vida Wang (support@vida.wang) 4 | * @brief This file packed some functions to access flash. 5 | * User don't need to access these functions directly, using 6 | * functions in mydb.h to instead. 7 | * 8 | * 这个文件包装了一堆函数来使用flash。 9 | * 用户不需要直接访问这里的函数,使用mydb.h中的功能来替代。 10 | * 11 | * @version 1.0.0 12 | * @date 2022-08-16 13 | * 14 | * @copyright Copyright (c) 2022 15 | * 16 | */ 17 | #ifndef MYFS_H_ 18 | #define MYFS_H_ 19 | 20 | #include 21 | #include "FS.h" 22 | #include 23 | #include 24 | #include 25 | #include 26 | 27 | using namespace std; 28 | 29 | typedef struct 30 | { 31 | String name; 32 | int size; 33 | bool isFile; 34 | } FileElement; 35 | 36 | #define FORMAT_LITTLEFS_IF_FAILED true 37 | typedef std::vector fileElementList; 38 | 39 | class MyFS 40 | { 41 | public: 42 | static void myfsInit(); 43 | static void listFile(String path, fileElementList *list, String prefix = ""); 44 | static bool writeFile(String path, String data, bool base64Encode = true); 45 | static bool writeFile(const char *path, uint8_t *data, uint64_t length); 46 | static bool writeFile(const char *path, const char *data, bool base64Encode = true); 47 | static String readFile(String path, bool base64Decode = true); 48 | static void readFile(const char *p, std::function callback); 49 | static uint8_t *readFile(const char *p, uint64_t *outLen); 50 | static bool appendFile(String path, String data); 51 | static bool deleteFile(String path); 52 | static bool renameFile(String path0, String path1); 53 | static bool fileExist(String path); 54 | static void formatSPIFFS(); 55 | static size_t getFreeSpace(); 56 | static size_t getUsedSpace(); 57 | }; 58 | 59 | #endif -------------------------------------------------------------------------------- /lib/mynet/mynet.cpp: -------------------------------------------------------------------------------- 1 | #include "mynet.h" 2 | 3 | bool MyNet::startAP(const char *ssid, IPAddress *apIP, const char *pwd) 4 | { 5 | this->apIP = apIP; 6 | if (!ssid) 7 | { 8 | return false; 9 | } 10 | if (!strlen(ssid)) 11 | { 12 | return false; 13 | } 14 | 15 | WiFi.enableAP(true); 16 | 17 | WiFi.onEvent( 18 | [](arduino_event_t *event) -> void 19 | { 20 | WiFi.softAPConfig(*(myNet.apIP), *(myNet.apIP), IPAddress(255, 255, 255, 0)); 21 | }, 22 | ARDUINO_EVENT_WIFI_AP_START); 23 | if (pwd) 24 | { 25 | if (strlen(pwd)) 26 | { 27 | WiFi.softAP(ssid, pwd); 28 | } 29 | else 30 | { 31 | WiFi.softAP(ssid); 32 | } 33 | } 34 | else 35 | { 36 | WiFi.softAP(ssid); 37 | } 38 | return true; 39 | } 40 | MyNet myNet; -------------------------------------------------------------------------------- /lib/mynet/mynet.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file mynet.h 3 | * @author Vida Wang (support@vida.wang) 4 | * @brief This file packed soft ap method. 5 | * 这个文件把soft ap函数包装了一下 6 | * 7 | * @version 1.0.0 8 | * @date 2022-08-16 9 | * 10 | * @copyright Copyright (c) 2022 11 | * 12 | */ 13 | 14 | #ifndef MYNET_H_ 15 | #define MYNET_H_ 16 | 17 | #include 18 | #include 19 | #include 20 | #include 21 | 22 | class MyNet 23 | { 24 | private: 25 | IPAddress *apIP = nullptr; 26 | 27 | public: 28 | /** 29 | * @brief start soft access point 30 | * 开启热点 31 | * 32 | * @param ssid service set identifier 热点名称 33 | * @param apIP default ap gateway 默认ap网关 34 | * @param pwd password 密码 35 | * @return true success 成功 36 | * @return false failed 失败 37 | */ 38 | bool startAP(const char *ssid, IPAddress *apIP, const char *pwd = nullptr); 39 | }; 40 | extern MyNet myNet; 41 | 42 | #endif -------------------------------------------------------------------------------- /lib/mywebsocket/README.md: -------------------------------------------------------------------------------- 1 | This component is the core of the whole system, it will be used in transfer information to server. 2 | Currently the client part is very stable. 3 | If any error detected while using client, please check your server first. Errors with CombinedServer please submit an issue, thanks. 4 | There are full comments in header file, check mywebsocket.h for more information. 5 | 6 | 这个组件是整个系统的核心,它被用来和服务器之间传送数据。 7 | 如果你在使用客户端的过程中遇到错误,请先检查你的服务器。CombinedServer的错误请提交issue,谢谢。 8 | 目前它的客户端部分非常稳定。 9 | 头文件中有完整的注释,更多信息请参阅mywebsocket.h。 -------------------------------------------------------------------------------- /lib/ota/README.md: -------------------------------------------------------------------------------- 1 | This component enable the system update firmware over the air. 2 | More than 10000 times OTA update had been tested, it is very stable. 3 | If any error detected on your ESP32 while using OTA update, please check your remote server first. 4 | There are full comments in header file, check ota.h for more information. 5 | 6 | * If got low connection quality between your device and AP it connected, or between your server and your network, OTA update may stuck during it process, don't reboot your device or upload again, leave it, it will update successfully. 7 | 8 | 这个组件可以让系统空中升级固件。 9 | 已经经过了至少10000次的升级测试,它很稳定。 10 | 如果你在OTA升级过程中遇到了问题,请先检查你的服务器。 11 | 头文件中有完整的注释,更多信息请参阅ota.h。 12 | 13 | * 如果你的设备与它连接的AP连接质量不高或你的服务器和你的网络连接质量不高,在OTA升级的时候有时候会卡住,但是不用重启设备或重新上传固件,不用管它,它自己会更新成功的。 -------------------------------------------------------------------------------- /lib/ota/ota.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file ota.h 3 | * @author Vida Wang (support@vida.wang) 4 | * @brief This file implemented OTA update function using websocket client. 5 | * 这个文件实现了使用websocket执行OTA更新的功能 6 | * 7 | * @version 1.0.0 8 | * @date 2022-08-16 9 | * 10 | * @copyright Copyright (c) 2022 11 | * 12 | */ 13 | #ifndef OTA_H_ 14 | #define OTA_H_ 15 | #include 16 | #include 17 | #include "esp_ota_ops.h" 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | 24 | #define OTA_DEBUG_HEADER "OTAUpdate" 25 | 26 | using namespace myWebSocket; 27 | 28 | typedef std::function OTACallback; 29 | 30 | class WebsocketOTA 31 | { 32 | private: 33 | // ota update handle 34 | // ota 升级句柄 35 | esp_ota_handle_t handle = 0; 36 | 37 | // ota data block index 38 | // ota 数据块索引 39 | uint32_t index = 0; 40 | 41 | // ota update partition 42 | // ota升级分区 43 | const esp_partition_t *partition; 44 | 45 | // for request ota block from server 46 | // 用于向服务器请求ota数据块 47 | std::vector *request = nullptr; 48 | 49 | // callback for ota update aborted 50 | // ota升级中断的回调函数 51 | OTACallback abortCallback = nullptr; 52 | 53 | // callback for ota update started 54 | // ota升级开始的回调函数 55 | OTACallback startCallback = nullptr; 56 | 57 | // length of firmware 58 | // 固件长度 59 | uint32_t firmwareLength = 0; 60 | 61 | // length of single ota data block 62 | // 单个ota数据块长度 63 | uint32_t blockLength = 0; 64 | 65 | // for request ota data block from server 66 | // 用于向服务器请求ota数据块 67 | uint32_t bufferOutLen = 0; 68 | 69 | // offset of data written to ota partition 70 | // 向ota分区写入的偏移量 71 | uint64_t writeOffset = 0; 72 | 73 | /** 74 | * @brief request next ota block or resend last request 75 | * 请求下一个ota数据块或重复上次请求 76 | */ 77 | void fetchNext(); 78 | 79 | /** 80 | * @brief clear ota data block buffer 81 | * 清除ota数据块缓存 82 | * 83 | * @param output ota data block from server 服务器发来的ota数据块 84 | */ 85 | void clearBuffer(std::vector *output); 86 | 87 | public: 88 | // websocket client to server for ota update 89 | // 用于ota升级的websocket客户端 90 | WebSocketClient *client = nullptr; 91 | 92 | inline WebsocketOTA() {} 93 | 94 | // indicate if ota update ready 95 | // 指示ota升级是否准备就绪 96 | bool otaInitialized = false; 97 | 98 | // indicate if ota update has been aborted 99 | // 指示ota升级是否被中断 100 | bool aborted = false; 101 | 102 | /** 103 | * @brief default websocket ota constructor 104 | * 默认websocket ota升级构造函数 105 | * 106 | * @param startOTARequest websocket ota request sent from web page 由前端发来的ota升级请求 107 | * @param startCallback callback for ota update started 开始ota升级的回调函数 108 | * @param abortCallback callback for ota update aborted, ota升级中断的回调函数 109 | */ 110 | WebsocketOTA(std::vector *startOTARequest, OTACallback startCallback, OTACallback abortCallback); 111 | 112 | /** 113 | * @brief start ota update process 114 | * 开始ota升级 115 | * 116 | * @param universalID id of current device 当前设备id 117 | * @param domain domain for websocket ota update, websocket升级服务器域名 118 | * @param port port for websocket ota update, websocket升级服务器端口 119 | * @param path path for websocket ota update, websocket升级服务器路径 120 | */ 121 | void start(Element universalID, String domain, uint16_t port, String path); 122 | 123 | ~WebsocketOTA(); 124 | }; 125 | 126 | #endif -------------------------------------------------------------------------------- /lib/provider/provider.cpp: -------------------------------------------------------------------------------- 1 | #include "provider.h" 2 | 3 | Provider::Provider(uint16_t id, 4 | ProviderCallback cb, 5 | String name, 6 | uint8_t settings, 7 | uint8_t lengthOfArguments) : id(id), cb(cb), name(name), settings(settings) 8 | { 9 | if (settings & PROVIDER_ENCRYPT) 10 | { 11 | this->encrypt = true; 12 | } 13 | // length of arguments will took 3 bits, 8 arguments maximum 14 | this->settings |= (lengthOfArguments & (uint8_t)0b00000111); 15 | } 16 | 17 | uint8_t *Provider::getBuffer(uint32_t *outLen) 18 | { 19 | std::vector container; 20 | container.push_back(new Element(this->id)); 21 | container.push_back(new Element(this->settings)); 22 | container.push_back(new Element(this->name)); 23 | container.push_back(new Element(this->customID)); 24 | 25 | uint8_t *buffer = ArrayBuffer::createArrayBuffer(&container, outLen); 26 | 27 | for (uint32_t i = 0; i < container.size(); ++i) 28 | { 29 | delete container.at(i); 30 | } 31 | 32 | return buffer; 33 | } -------------------------------------------------------------------------------- /lib/provider/provider.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file provider.h 3 | * @author Vida Wang (support@vida.wang) 4 | * @brief This file implemented a type of method to store pre-built functions and provide 5 | * interface to web. 6 | * 7 | * 这个文件实现了一种方法来保存预先构建的功能和提供给前端的接口 8 | * 9 | * @version 1.0.0 10 | * @date 2022-08-16 11 | * 12 | * @copyright Copyright (c) 2022 13 | * 14 | */ 15 | #ifndef PROVIDER_H_ 16 | #define PROVIDER_H_ 17 | 18 | #include 19 | #include 20 | #include 21 | 22 | typedef Elements *ProviderArguments; 23 | 24 | typedef std::function ProviderCallback; 25 | 26 | typedef enum : uint8_t 27 | { 28 | // default show to administrator 29 | // 默认显示给管理员 30 | PROVIDER_COMMON = 0b10000000, 31 | 32 | // indicate current provider action should question user or not 33 | // 指示执行当前provider是否是需要确认 34 | PROVIDER_QUESTION = 0b01000000, 35 | 36 | // only administrator could execute 37 | // 仅开放给管理员执行 38 | PROVIDER_ADMIN = 0b00100000, 39 | 40 | // default show to any user ( included administrator ) 41 | // 指示当前provider显示给所有人 42 | PROVIDER_USER = 0b00010000, 43 | 44 | // both request and response should be encrypted 45 | // 执行provider时输入数据和返回数据是否需要加密 46 | PROVIDER_ENCRYPT = 0b00001000 47 | } ProviderType; 48 | 49 | class Provider 50 | { 51 | public: 52 | /** 53 | * @brief id of provider, automatically set by "createProvider" 54 | * user should not modify this id 55 | * 56 | * provider的ID,由createProvider函数设置,用户不应该修改此id 57 | */ 58 | uint16_t id = 0; 59 | 60 | /** 61 | * @brief id set by user for other purpose 62 | * 用于其他用途的由用户设置的id 63 | */ 64 | uint64_t customID = 0; 65 | 66 | /** 67 | * @brief name of provider, for human read 68 | * privoder的名字,给人看的 69 | */ 70 | String name = ""; 71 | 72 | /** 73 | * @brief provider callback 74 | * provider回调函数 75 | */ 76 | ProviderCallback cb = nullptr; 77 | 78 | /** 79 | * @brief part of generate buffer 80 | * 用来生成buffer的一部分 81 | */ 82 | uint8_t settings = 0x00; 83 | 84 | /** 85 | * @brief indicate data of current provider should encrypt or not 86 | * 指示当前provider的数据是否需要加密 87 | */ 88 | bool encrypt = false; 89 | 90 | /** 91 | * @brief indicate that current provider 92 | * is built-in or not 93 | * 94 | * 指示当前provider是内建的provider吗 95 | * 96 | */ 97 | bool isBuiltIn = false; 98 | 99 | /** 100 | * @brief default empty arguments constructor 101 | * 默认无参构造 102 | */ 103 | inline Provider() {} 104 | 105 | /** 106 | * @brief create a new provider 107 | * 创建一个新的provider 108 | * 109 | * @param id id set by createProvider 由createProvider自动填充的序列号 110 | * @param cb callback of provider, provider回调函数 111 | * @param name name of provider, for human read; provider的名字,给人看的 112 | * @param settings options of provider, provider的选项 113 | * @param lengthOfArguments length of arguments for js create inputs 给js用的创建参数输入框的参数长度 114 | */ 115 | Provider(uint16_t id, 116 | ProviderCallback cb, 117 | String name, 118 | uint8_t settings, 119 | uint8_t lengthOfArguments = 0); 120 | 121 | inline ~Provider() {} 122 | 123 | /** 124 | * @brief create a buffer of current provider 125 | * 为当前provider创建buffer 126 | * 127 | * @param outLen output length 输出buffer的长度 128 | * @return uint8_t* buffer 缓存 129 | */ 130 | uint8_t *getBuffer(uint32_t *outLen); 131 | }; 132 | 133 | #endif 134 | -------------------------------------------------------------------------------- /lib/softtimer/softtimer.h: -------------------------------------------------------------------------------- 1 | #ifndef SOFT_TIMER_H_ 2 | #define SOFT_TIMER_H_ 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | /** 9 | * @brief store timer handle, id and callback 10 | * 存储定时器句柄、id和回调函数 11 | */ 12 | typedef struct 13 | { 14 | // id 15 | uint32_t id = 0; 16 | 17 | //句柄 18 | TimerHandle_t handle = NULL; 19 | 20 | // callback 回调函数 21 | std::function fn = nullptr; 22 | } SoftTimerT; 23 | 24 | /** 25 | * @brief universal timer callback 26 | * not for user execute 27 | * 28 | * 全局timer回调函数 29 | * 用户不要执行此函数 30 | */ 31 | void _universalSoftTimerCallback(TimerHandle_t); 32 | 33 | uint32_t _createTimer(std::function fn, int32_t type = 0, uint32_t timeout = 1); 34 | 35 | bool _clearTimer(uint32_t id); 36 | 37 | /** 38 | * @brief create a new timer only execute once 39 | * 40 | * 创建一个一次性定时器 41 | * 42 | * @param fn callback 回调函数 43 | * @param timeout timeout in ms 超时时间,毫秒 44 | * @return uint32_t id of timer, timer的id号 45 | */ 46 | uint32_t setTimeout(std::function fn, uint32_t timeout); 47 | 48 | /** 49 | * @brief stop and delete a timer 50 | * 51 | * 停止并删除一个定时器 52 | * 53 | * @param id id of timer 定时器的id 54 | * @return true success 成功 55 | * @return false failed 失败 56 | */ 57 | bool clearTimeout(uint32_t id); 58 | 59 | /** 60 | * @brief create a timer run repeatedly 61 | * 62 | * 创建一个周期性运行的定时器 63 | * 64 | * @param fn callback 回调函数 65 | * @param interval period in ms 周期,毫秒 66 | * @return uint32_t id of timer, 定时器的id号 67 | */ 68 | uint32_t setInterval(std::function fn, uint32_t interval); 69 | 70 | /** 71 | * @brief stop and delete a timer 72 | * 73 | * 停止并删除一个定时器 74 | * 75 | * @param id id of timer 定时器的id 76 | * @return true success 成功 77 | * @return false failed 失败 78 | */ 79 | bool clearInterval(uint32_t id); 80 | 81 | /** 82 | * @brief suspend all timers 83 | * and NOT allow to create new timer 84 | * 85 | * 挂起所有定时器 86 | * 且不允许创建新的计时器 87 | * 88 | * @return true all timers suspended 所有定时器挂起成功 89 | * @return false NOT all timers suspended 部分定时器挂起成功 90 | */ 91 | bool suspendAllTimers(void); 92 | 93 | /** 94 | * @brief automtic id for timers when creating 95 | * 96 | * 自动设置的id 97 | * 98 | */ 99 | extern uint32_t _universalSoftTimerID; 100 | 101 | /** 102 | * @brief indicate allow create timer or not 103 | * 指示是否允许创建新的定时器 104 | * 105 | */ 106 | extern uint8_t _allowNewTimer; 107 | 108 | /** 109 | * @brief container for timers 110 | * 111 | * 定时器的容器 112 | * 113 | */ 114 | extern std::map _timerContainer; 115 | 116 | #endif -------------------------------------------------------------------------------- /lib/softtimer/softtimr.cpp: -------------------------------------------------------------------------------- 1 | #include "softtimer.h" 2 | 3 | void _universalSoftTimerCallback(TimerHandle_t handle) 4 | { 5 | if (!handle) 6 | { 7 | return; 8 | } 9 | 10 | uint32_t id = (uint32_t)pvTimerGetTimerID(handle); 11 | 12 | auto it = _timerContainer.find(id); 13 | if (it == _timerContainer.end()) 14 | { 15 | return; 16 | } 17 | 18 | if (it->second->fn) 19 | { 20 | it->second->fn(); 21 | } 22 | 23 | if (uxTimerGetReloadMode(handle) == pdFALSE) 24 | { 25 | delete it->second; 26 | 27 | xTimerDelete(handle, 0); 28 | 29 | _timerContainer.erase(it); 30 | } 31 | } 32 | 33 | bool suspendAllTimers(void) 34 | { 35 | _allowNewTimer = 0; 36 | auto it = _timerContainer.begin(); 37 | auto end = _timerContainer.end(); 38 | 39 | int count = 0; 40 | 41 | for (; it != end; ++it) 42 | { 43 | if (xTimerStop(it->second->handle, 0) == pdPASS) 44 | { 45 | count++; 46 | } 47 | } 48 | 49 | return count >= _timerContainer.size(); 50 | } 51 | 52 | uint32_t _createTimer(std::function fn, int32_t type, uint32_t timeout) 53 | { 54 | if (!_allowNewTimer) 55 | { 56 | return 0; 57 | } 58 | 59 | SoftTimerT *timer = new SoftTimerT(); 60 | 61 | timer->fn = fn; 62 | 63 | timer->handle = xTimerCreate("nb", 64 | (timeout / portTICK_PERIOD_MS), 65 | (type ? pdTRUE : pdFALSE), 66 | (void *)_universalSoftTimerID, 67 | _universalSoftTimerCallback); 68 | 69 | if (!timer->handle) 70 | { 71 | return 0; 72 | } 73 | 74 | if (xTimerStart(timer->handle, 0) != pdPASS) 75 | { 76 | return 0; 77 | } 78 | 79 | timer->id = _universalSoftTimerID; 80 | 81 | _universalSoftTimerID++; 82 | 83 | _timerContainer.insert( 84 | {timer->id, 85 | timer}); 86 | 87 | return timer->id; 88 | } 89 | 90 | uint32_t setTimeout(std::function fn, uint32_t timeout) 91 | { 92 | return _createTimer(fn, 0, timeout); 93 | } 94 | 95 | uint32_t setInterval(std::function fn, uint32_t interval) 96 | { 97 | return _createTimer(fn, 1, interval); 98 | } 99 | 100 | bool _clearTimer(uint32_t id) 101 | { 102 | auto it = _timerContainer.find(id); 103 | 104 | if (it != _timerContainer.end()) 105 | { 106 | delete it->second; 107 | xTimerDelete(it->second->handle, 0); 108 | _timerContainer.erase(it); 109 | std::map().swap(_timerContainer); 110 | return true; 111 | } 112 | return false; 113 | } 114 | 115 | bool clearTimeout(uint32_t id) 116 | { 117 | return _clearTimer(id); 118 | } 119 | 120 | bool clearInterval(uint32_t id) 121 | { 122 | return _clearTimer(id); 123 | } 124 | 125 | uint8_t _allowNewTimer = 0xffu; 126 | 127 | uint32_t _universalSoftTimerID = 1; 128 | 129 | std::map _timerContainer; -------------------------------------------------------------------------------- /partition/README.md: -------------------------------------------------------------------------------- 1 | Currently use 1.9MB app partition with OTA update, 190KB SPIFFS. 2 | You could change partition table if you like. 3 | 现在使用的是1.9MB应用分区带OTA更新,190KB SPIFFS。 4 | 你可以自己修改分区表。 -------------------------------------------------------------------------------- /partition/partition.csv: -------------------------------------------------------------------------------- 1 | # Name, Type, SubType, Offset, Size, Flags 2 | nvs, data, nvs, 0x9000, 0x5000, 3 | otadata, data, ota, 0xe000, 0x2000, 4 | app0, app, ota_0, 0x10000, 0x1E0000, 5 | app1, app, ota_1, 0x1F0000,0x1E0000, 6 | spiffs, data, spiffs, 0x3D0000,0x30000, 7 | -------------------------------------------------------------------------------- /platformio.ini: -------------------------------------------------------------------------------- 1 | ; PlatformIO Project Configuration File 2 | ; 3 | ; Build options: build flags, source filter 4 | ; Upload options: custom upload port, speed and extra flags 5 | ; Library options: dependencies, extra library storages 6 | ; Advanced options: extra scripting 7 | ; 8 | ; Please visit documentation for the other options and examples 9 | ; https://docs.platformio.org/page/projectconf.html 10 | 11 | [platformio] 12 | default_envs = esp32 13 | 14 | [env] 15 | framework = arduino 16 | 17 | [env:esp32] 18 | platform = espressif32 19 | board = esp32dev 20 | monitor_filters = esp32_exception_decoder 21 | monitor_speed = 115200 22 | upload_speed = 921600 23 | board_build.flash_mode = qio 24 | board_build.f_flash = 80000000L 25 | check_skip_packages = true 26 | extra_scripts = 27 | ./scripts/littlefsbuilder.py 28 | board_build.partitions = ./partition/partition.csv 29 | build_flags = 30 | -DCORE_DEBUG_LEVEL=0 31 | -DCONFIG_FREERTOS_USE_TRACE_FACILITY=1 32 | -DconfigUSE_TRACE_FACILITY=1 33 | ;lib_deps = peterus/INA226Lib@^1.1.3 34 | 35 | 36 | [env:esp32-s3-devkitc-1] 37 | 38 | platform = https://github.com/platformio/platform-espressif32/archive/refs/tags/v6.3.2.zip 39 | board = esp32-s3-devkitc-1 40 | framework = arduino 41 | 42 | board_build.flash_mode = qio 43 | board_build.f_flash = 80000000L 44 | board_build.f_cpu = 240000000L 45 | board_build.mcu = esp32s3 46 | board_build.partitions = default_8MB.csv 47 | 48 | monitor_speed = 115200 49 | upload_speed = 921600 50 | ;monitor_filters = 51 | ; default 52 | ;esp32_exception_decoder 53 | ; colorize 54 | 55 | build_flags = 56 | -DCORE_DEBUG_LEVEL=5 57 | -DARDUINO_USB_MODE=1 58 | -DARDUINO_USB_CDC_ON_BOOT=1 59 | -DCONFIG_FREERTOS_USE_TRACE_FACILITY=1 60 | ;-DBOARD_HAS_PSRAM 61 | ;-DUSE_STACK_HACK_RECYCLE_ALLOCATION_SINGLE_THREADED=1 62 | ;-mfix-esp32-psram-cache-issue 63 | ;-I src/ 64 | ;-I src/third_party/arduino-liblame 65 | ;-I src/third_party/snprintf 66 | ;-DLAME_NO_LOGGING 67 | ;-DLAME_LOGGING_ACTIVE=0 68 | 69 | debug_tool = esp-builtin 70 | debug_speed = 40000 71 | debug_load_mode = always 72 | 73 | check_skip_packages = true 74 | extra_scripts = 75 | ./scripts/littlefsbuilder.py 76 | 77 | ;upload_protocol = esp-builtin 78 | 79 | 80 | 81 | [env:esp32-c3-devkitm-1] 82 | platform = espressif32 83 | board = esp32-c3-devkitm-1 84 | framework = arduino 85 | 86 | monitor_speed = 115200 87 | upload_speed = 921600 88 | ;monitor_filters = 89 | ; default 90 | ;esp32_exception_decoder 91 | ; colorize 92 | 93 | build_flags = 94 | -std=c++2a 95 | -DCORE_DEBUG_LEVEL=5 96 | -DARDUINO_USB_MODE=1 97 | -DARDUINO_USB_CDC_ON_BOOT=1 98 | -DCONFIG_FREERTOS_USE_TRACE_FACILITY=1 99 | ;-DBOARD_HAS_PSRAM 100 | ;-DUSE_STACK_HACK_RECYCLE_ALLOCATION_SINGLE_THREADED=1 101 | ;-mfix-esp32-psram-cache-issue 102 | ;-I src/ 103 | ;-I src/third_party/arduino-liblame 104 | ;-I src/third_party/snprintf 105 | ;-DLAME_NO_LOGGING 106 | ;-DLAME_LOGGING_ACTIVE=0 107 | 108 | debug_tool = esp-builtin 109 | debug_speed = 40000 110 | debug_load_mode = always 111 | 112 | check_skip_packages = true 113 | extra_scripts = 114 | ./scripts/littlefsbuilder.py -------------------------------------------------------------------------------- /scripts/.htmlHash.txt: -------------------------------------------------------------------------------- 1 | 7864b065b5153d14460ffe001a2eea69e30412ca1013f6bc830a79289f51683e -------------------------------------------------------------------------------- /scripts/autoOTA.js: -------------------------------------------------------------------------------- 1 | let rootPath = process.argv[process.argv.length - 1]; 2 | let fs = require("fs"); 3 | let ws = require("ws"); 4 | let getHash = require("./hash"); 5 | let { createArrayBuffer, decodeArrayBuffer } = require("./ab"); 6 | 7 | let config = null; 8 | 9 | let timeout = 5000; 10 | let timerCheck = 0; 11 | let running = false; 12 | 13 | if (fs.existsSync("./autoOTAConfig.json")) { 14 | config = JSON.parse(fs.readFileSync("./autoOTAConfig.json").toString()); 15 | } else { 16 | config = { 17 | adminUserName: "", 18 | adminPassword: "", 19 | domain: "", 20 | port: 0, 21 | path: "", 22 | table: [ 23 | //{ 24 | // appName: appName1, 25 | // id: id1, 26 | // blockSize: 8 * 1024, 27 | // enable: true 28 | //}, 29 | //{ 30 | // appName: appName2, 31 | // id: id2, 32 | // blockSize: 1 * 1024, 33 | // enable: true 34 | //}, 35 | //{ 36 | // appName: appName3, 37 | // id: id3, 38 | // blockSize: 16 * 1024, 39 | // enable: true 40 | //}, 41 | //... 42 | ] 43 | }; 44 | fs.writeFileSync("./autoOTAConfig.json", JSON.stringify(config)); 45 | } 46 | 47 | if ( 48 | !config.adminUserName.length || 49 | !config.adminPassword.length || 50 | !config.domain.length || 51 | !config.port || 52 | !config.table.length 53 | ) { 54 | console.log("No OTA basic information"); 55 | return; 56 | } 57 | 58 | let firmwares = fs.readdirSync(rootPath + "firmware/"); 59 | 60 | let arr = []; 61 | 62 | for (let i of firmwares) { 63 | let json = { 64 | fileName: i, 65 | meta: /(?.+)_(?.+)_(?\d{2})\.(?\d{2})\.(?\d{2})/.exec(i) 66 | }; 67 | if (json.meta) { 68 | json.time = parseInt(fs.statSync(rootPath + "firmware/" + i).mtimeMs) 69 | json.appName = json.meta.groups.appName; 70 | arr.push(json); 71 | } 72 | } 73 | 74 | arr.sort((a, b) => { 75 | return b.time - a.time; 76 | }); 77 | 78 | let target = null; 79 | 80 | if(arr.length <1){ 81 | return; 82 | } 83 | 84 | target = config.table.find(e => { 85 | return e.appName === arr[0].appName; 86 | }); 87 | 88 | if (!target) { 89 | return; 90 | } 91 | 92 | if (!target.enable) { 93 | return; 94 | } 95 | 96 | let id = target.id; 97 | 98 | if (!id || id.length != 64) { 99 | return; 100 | } 101 | 102 | target.fileName = arr[0].fileName; 103 | 104 | console.log("Pending OTA firmware: ", target.fileName); 105 | 106 | function sendOTARequest(client) { 107 | let userName = config.adminUserName; 108 | let password = config.adminPassword; 109 | 110 | userName = getHash(userName); 111 | password = getHash(password); 112 | 113 | let t = new Date().getTime(); 114 | 115 | let hash = new Uint8Array(getHash(userName + password + t, !0)); 116 | 117 | let firmware = new Uint8Array(fs.readFileSync(rootPath + "firmware/" + target.fileName)); 118 | 119 | let firmwareHash = new Uint8Array(getHash(firmware, !0)); 120 | 121 | let ab = createArrayBuffer( 122 | [ 123 | 0xAB, 124 | id, 125 | userName, 126 | firmware, 127 | t, 128 | hash, 129 | target.blockSize, 130 | firmwareHash 131 | ] 132 | ); 133 | 134 | client.send(ab); 135 | }; 136 | 137 | function pushFirmware() { 138 | if (!config.path.startsWith("/")) 139 | config.path = "/" + config.path; 140 | 141 | let client = new ws.WebSocket("ws://" + config.domain + ":" + config.port + config.path); 142 | client.on("open", function () { 143 | client.binaryType = "arraybuffer"; 144 | console.log("Connected, uploading firmware..."); 145 | 146 | sendOTARequest(client); 147 | 148 | timerCheck = setInterval(() => { 149 | if (!running) { 150 | sendOTARequest(client); 151 | } else { 152 | clearInterval(timerCheck); 153 | } 154 | }, timeout); 155 | }); 156 | client.on("message", function (msg) { 157 | let arr = decodeArrayBuffer(msg); 158 | 159 | let command = arr[0]; 160 | 161 | switch (command) { 162 | case 0x0c: 163 | this.send(createArrayBuffer([0xc0])); 164 | break; 165 | case 0xae: 166 | console.log("progress: " + arr[2]); 167 | running = true; 168 | break; 169 | case 0xfb: 170 | if (arr[3] == 100) { 171 | console.log("OTA update finished!"); 172 | setTimeout(() => { 173 | process.exit(0); 174 | }, 100); 175 | } 176 | break; 177 | default: 178 | 179 | } 180 | }); 181 | client.on("error", function (err) { 182 | console.log("Error OTA update :", err.toString()); 183 | process.exit(1); 184 | }); 185 | }; 186 | 187 | pushFirmware(); -------------------------------------------------------------------------------- /scripts/copyFirmware.js: -------------------------------------------------------------------------------- 1 | let fs = require("fs"); 2 | let os = require("os"); 3 | console.log("copy firmware\n"); 4 | 5 | let slash = "/"; 6 | if (os.type() == "Windows_NT") { 7 | slash = "\\"; 8 | } 9 | 10 | Date.prototype.getFormat = function (format) { 11 | let b = this; 12 | format = format || "yyyy年mm月dd日 hh:MM:ss"; 13 | let year = b.getFullYear().toString(); 14 | let month = (b.getMonth() + 1).toString(); 15 | let date = b.getDate().toString(); 16 | let hour = b.getHours().toString(); 17 | let minute = b.getMinutes().toString(); 18 | let second = b.getSeconds().toString(); 19 | 20 | let fn = e => { return e.length == 1 ? "0" + e : e; }; 21 | 22 | month = fn(month); 23 | date = fn(date); 24 | hour = fn(hour); 25 | minute = fn(minute); 26 | second = fn(second); 27 | 28 | fn = (reg, obj) => { format = format.replace(reg, obj); }; 29 | 30 | fn(/yyyy/, year); 31 | fn(/mm/, month); 32 | fn(/dd/, date); 33 | fn(/hh/, hour); 34 | fn(/MM/, minute); 35 | fn(/ss/, second); 36 | 37 | return format; 38 | }; 39 | 40 | let bootloaderPath = process.argv[process.argv.length - 1]; 41 | let envPath = bootloaderPath.match(/.+\/\.pio/i);//.toString(); 42 | 43 | if (!envPath) { 44 | envPath = bootloaderPath.match(/.+\\\.pio/i); 45 | if (!envPath) { 46 | console.log("Unknown error"); 47 | return; 48 | } 49 | } 50 | envPath = envPath.toString(); 51 | 52 | envPath = envPath.substring(0, envPath.length - 4); 53 | 54 | let firmwarePath = bootloaderPath.match(/.+\//i); 55 | if (!firmwarePath) { 56 | firmwarePath = bootloaderPath.match(/.+\\/i); 57 | if (!firmwarePath) { 58 | console.log("Unknown error"); 59 | return; 60 | } 61 | } 62 | 63 | let app_h, appName, appVersion; 64 | 65 | if (fs.existsSync(envPath + "src/app/app.h")) { 66 | app_h = fs.readFileSync(envPath + "src/app/app.h").toString(); 67 | appName = /#define\s+?APP_NAME\s+?(?.+)(\s|\n)?/g.exec(app_h).groups.appName; 68 | appName = appName.replace(/[\\\/,:;'"?\{\}\[\]~`&\^\*\r\n]/g, ""); 69 | appVersion = /#define\s+APP_VERSION\s+?(?.+)(\s|\n)?/g.exec(app_h).groups.appVersion; 70 | } else { 71 | appName = "Abc"; 72 | appVersion = "10"; 73 | } 74 | 75 | if (!fs.existsSync(bootloaderPath + "firmware/")) { 76 | fs.mkdirSync(bootloaderPath + "firmware/"); 77 | } 78 | 79 | appVersion = appVersion.replace(/['"\\\/;:,\.]/gi,"_"); 80 | 81 | 82 | fs.copyFileSync(firmwarePath + 'firmware.bin', 83 | envPath + "firmware" + slash + appName + "_" + appVersion + "_" + new Date().getFormat("hh.MM.ss") + ".bin"); 84 | 85 | console.log("firmware copied\n"); -------------------------------------------------------------------------------- /scripts/hash.js: -------------------------------------------------------------------------------- 1 | (function () { 2 | let rotateRight = (n, x) => { return (x >>> n) | (x << (32 - n)) } 3 | 4 | let choice = (x, y, z) => { return (x & y) ^ (~x & z) } 5 | 6 | let majority = (x, y, z) => { return (x & y) ^ (x & z) ^ (y & z) } 7 | 8 | let sha256_Sigma0 = x => { return rotateRight(2, x) ^ rotateRight(13, x) ^ rotateRight(22, x) } 9 | 10 | let sha256_Sigma1 = x => { return rotateRight(6, x) ^ rotateRight(11, x) ^ rotateRight(25, x) } 11 | 12 | let sha256_sigma0 = x => { return rotateRight(7, x) ^ rotateRight(18, x) ^ (x >>> 3) } 13 | 14 | let sha256_sigma1 = x => { return rotateRight(17, x) ^ rotateRight(19, x) ^ (x >>> 10) } 15 | 16 | let sha256_expand = (W, j) => { return (W[j & 0x0f] += sha256_sigma1(W[(j + 14) & 0x0f]) + W[(j + 9) & 0x0f] + sha256_sigma0(W[(j + 1) & 0x0f])) } 17 | let K256 = new Array(0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5, 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174, 0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da, 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967, 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85, 0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070, 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3, 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2); 18 | let ihash, count, buffer; 19 | let sha256_hex_digits = "0123456789abcdef"; 20 | 21 | let safe_add = (x, y) => { let lsw = (x & 0xffff) + (y & 0xffff); let msw = (x >> 16) + (y >> 16) + (lsw >> 16); return (msw << 16) | (lsw & 0xffff) } 22 | 23 | let sha256_init = () => { 24 | ihash = new Array(8); 25 | count = new Array(2); 26 | buffer = new Array(64); 27 | count[0] = count[1] = 0; 28 | ihash[0] = 0x6a09e667; 29 | ihash[1] = 0xbb67ae85; 30 | ihash[2] = 0x3c6ef372; 31 | ihash[3] = 0xa54ff53a; 32 | ihash[4] = 0x510e527f; 33 | ihash[5] = 0x9b05688c; 34 | ihash[6] = 0x1f83d9ab; 35 | ihash[7] = 0x5be0cd19; 36 | } 37 | 38 | let sha256_transform = () => { 39 | let a, b, c, d, e, f, g, h, T1, T2; 40 | let W = new Array(16); 41 | a = ihash[0]; 42 | b = ihash[1]; 43 | c = ihash[2]; 44 | d = ihash[3]; 45 | e = ihash[4]; 46 | f = ihash[5]; 47 | g = ihash[6]; 48 | h = ihash[7]; 49 | for (let i = 0; i < 16; i++) W[i] = buffer[(i << 2) + 3] | (buffer[(i << 2) + 2] << 8) | (buffer[(i << 2) + 1] << 16) | (buffer[i << 2] << 24); 50 | for (let j = 0; j < 64; j++) { 51 | T1 = h + sha256_Sigma1(e) + choice(e, f, g) + K256[j]; 52 | if (j < 16) T1 += W[j]; 53 | else T1 += sha256_expand(W, j); 54 | T2 = sha256_Sigma0(a) + majority(a, b, c); 55 | h = g; 56 | g = f; 57 | f = e; 58 | e = safe_add(d, T1); 59 | d = c; 60 | c = b; 61 | b = a; 62 | a = safe_add(T1, T2); 63 | } 64 | ihash[0] += a; 65 | ihash[1] += b; 66 | ihash[2] += c; 67 | ihash[3] += d; 68 | ihash[4] += e; 69 | ihash[5] += f; 70 | ihash[6] += g; 71 | ihash[7] += h; 72 | } 73 | 74 | let sha256_update = (data, inputLen) => { 75 | let i, index, curpos = 0; 76 | index = (count[0] >> 3) & 0x3f; 77 | let remainder = inputLen & 0x3f; 78 | if ((count[0] += inputLen << 3) < inputLen << 3) count[1]++; 79 | count[1] += inputLen >> 29; 80 | if (typeof data == "string") { 81 | for (i = 0; i + 63 < inputLen; i += 64) { 82 | for (let j = index; j < 64; j++) buffer[j] = data.charCodeAt(curpos++); 83 | sha256_transform(); 84 | index = 0; 85 | } 86 | for (let j = 0; j < remainder; j++) buffer[j] = data.charCodeAt(curpos++); 87 | } 88 | else { 89 | for (i = 0; i + 63 < inputLen; i += 64) { 90 | for (let j = index; j < 64; j++) buffer[j] = data[curpos++]; 91 | sha256_transform(); 92 | index = 0; 93 | } 94 | for (let j = 0; j < remainder; j++) buffer[j] = data[curpos++]; 95 | } 96 | 97 | } 98 | 99 | let sha256_final = () => { 100 | let index = (count[0] >> 3) & 0x3f; 101 | buffer[index++] = 0x80; 102 | if (index <= 56) { for (let i = index; i < 56; i++) buffer[i] = 0 } else { 103 | for (let i = index; i < 64; i++) buffer[i] = 0; 104 | sha256_transform(); 105 | for (let i = 0; i < 56; i++) buffer[i] = 0; 106 | } 107 | buffer[56] = (count[1] >>> 24) & 0xff; 108 | buffer[57] = (count[1] >>> 16) & 0xff; 109 | buffer[58] = (count[1] >>> 8) & 0xff; 110 | buffer[59] = count[1] & 0xff; 111 | buffer[60] = (count[0] >>> 24) & 0xff; 112 | buffer[61] = (count[0] >>> 16) & 0xff; 113 | buffer[62] = (count[0] >>> 8) & 0xff; 114 | buffer[63] = count[0] & 0xff; 115 | sha256_transform(); 116 | } 117 | 118 | let sha256_encode_bytes = () => { 119 | let j = 0; 120 | let output = new Array(32); 121 | for (let i = 0; i < 8; i++) { 122 | output[j++] = (ihash[i] >>> 24) & 0xff; 123 | output[j++] = (ihash[i] >>> 16) & 0xff; 124 | output[j++] = (ihash[i] >>> 8) & 0xff; 125 | output[j++] = ihash[i] & 0xff; 126 | } 127 | return output; 128 | } 129 | 130 | let sha256_encode_hex = () => { let output = new String(); for (let i = 0; i < 8; i++) { for (let j = 28; j >= 0; j -= 4) output += sha256_hex_digits.charAt((ihash[i] >>> j) & 0x0f) } return output; } 131 | 132 | let hash = (data, bytes, gen) => { 133 | sha256_init(); 134 | sha256_update(data, data.length); 135 | sha256_final(); 136 | if (bytes) { 137 | return sha256_encode_bytes(); 138 | } else { 139 | return sha256_encode_hex(); 140 | } 141 | }; 142 | if (typeof window == "object") { 143 | window.getHash = hash; 144 | } else { 145 | module.exports = hash; 146 | } 147 | 148 | })(); -------------------------------------------------------------------------------- /scripts/littlefsbuilder.py: -------------------------------------------------------------------------------- 1 | Import("env", "projenv") 2 | import platform 3 | projectRootPath = env.get("PROJECT_DIR") + "/" 4 | 5 | bootloaderPath = env.get("FLASH_EXTRA_IMAGES")[0][1] 6 | 7 | osType = platform.system().lower() 8 | 9 | if osType == "darwin": 10 | print("\nOS: MacOS\n") 11 | env.Replace( MKSPIFFSTOOL = projectRootPath + '/tools/mklittlefs' ) 12 | elif osType == "windows": 13 | print("\nOS: Windows\n") 14 | env.Replace( MKSPIFFSTOOL = projectRootPath + '/tools/mklittlefs.exe' ) 15 | 16 | 17 | env.Execute("node " + projectRootPath + "ap/tools.js " + projectRootPath) 18 | env.Execute("node "+ projectRootPath + "scripts/replaceHtml.js " + projectRootPath) 19 | 20 | def copyFirmware(source, target, env): 21 | env.Execute("node "+ projectRootPath +"scripts/copyFirmware.js " + bootloaderPath) 22 | 23 | def OTA(source, target, env): 24 | env.Execute("node "+ projectRootPath +"scripts/autoOTA.js " + projectRootPath) 25 | 26 | env.AddPostAction("buildprog", copyFirmware) 27 | env.AddPostAction("upload", copyFirmware) 28 | 29 | env.AddPostAction("buildprog", OTA) -------------------------------------------------------------------------------- /scripts/replaceHtml.js: -------------------------------------------------------------------------------- 1 | let getHash = require("./hash"); 2 | let fs = require("fs"); 3 | 4 | let rootPath = process.argv[process.argv.length - 1]; 5 | 6 | console.log("replacing AP index.html\n"); 7 | 8 | let html = fs.readFileSync(rootPath + "ap/out.txt").toString(); 9 | 10 | let lastHash = ""; 11 | let currentHash = getHash(html); 12 | 13 | if (fs.existsSync("./.htmlHash.txt")) { 14 | lastHash = fs.readFileSync("./.htmlHash.txt").toString(); 15 | } 16 | fs.writeFileSync("./.htmlHash.txt", currentHash); 17 | 18 | let headerPath = rootPath + "lib/globalmanager/globalmanager.h"; 19 | 20 | 21 | function replace() { 22 | console.log("AP index.html modified, update header file"); 23 | 24 | let globalmanager_h = fs.readFileSync(headerPath).toString(); 25 | 26 | let date = new Date().toString(); 27 | 28 | globalmanager_h = globalmanager_h.replace(/\/\/\s*?apindex[\S\s]+\/\/\s*?apindex/g, 29 | `//apindex ${date}\r\nconst char* serverIndex = R"(${html})";\r\n//apindex`); 30 | 31 | 32 | 33 | fs.writeFileSync(headerPath, globalmanager_h); 34 | 35 | console.log("replace html OK\n"); 36 | }; 37 | 38 | function fillCompileTime() { 39 | let globalmanager_h = fs.readFileSync(headerPath).toString(); 40 | 41 | let date = new Date(); 42 | 43 | let M = (date.getMonth() + 1); 44 | M = M < 10 ? "0" + M : M; 45 | 46 | let d = date.getDate(); 47 | d = d < 10 ? "0" + d : d; 48 | 49 | let h = date.getHours(); 50 | h = h < 10 ? "0" + h : h; 51 | 52 | let m = date.getMinutes(); 53 | m = m < 10 ? "0" + m : m; 54 | 55 | let s = date.getSeconds(); 56 | s = s < 10 ? "0" + s : s; 57 | 58 | let dateString = `${date.getFullYear()}/${M}/${d}-${h}:${m}:${s}`; 59 | 60 | //replace firmware compile time 61 | globalmanager_h = globalmanager_h.replace(/#define\s*?firmware_compile_time\s*?.+\s*?"/gi, `#define FIRMWARE_COMPILE_TIME "@${dateString}"`); 62 | 63 | fs.writeFileSync(headerPath, globalmanager_h); 64 | }; 65 | 66 | 67 | if (lastHash.length) { 68 | if (lastHash != currentHash) { 69 | replace(); 70 | } else { 71 | console.log("html doesn't change"); 72 | } 73 | 74 | } else { 75 | replace(); 76 | } 77 | 78 | fillCompileTime(); -------------------------------------------------------------------------------- /server/README.md: -------------------------------------------------------------------------------- 1 | ### English 2 | 3 | * Project use Node.js implemented a simple server to transfer data. As we all know, Node.js is very fit in this purpose use, and coding with Node.js could speed up developing. 4 | 5 | * Server as could as posible doesn't store any information valuable, that ensure even your server under a attack, no one could get your personal information. 6 | 7 | * Note: the token in globalConfig.json could be empty. 8 | If you fill the token with content, then you must set same content in your device. 9 | 10 | * Currently, the web page only for administrator debug and very simple use, this is still under developing. 11 | 12 | ### 中文 13 | 14 | * 项目使用 Node.js 实现了一个简单的消息中转服务器,众所周知 Node.js 非常适合此类用途,而且使用 Node.js 开发也可以加快原型速度。 15 | 16 | * 服务器尽可能的不存储任何有价值的信息,这可以确保即使你的服务器遭受攻击,攻击者也不会获取到任何有价值的信息。 17 | 18 | * 注意: globalConfig.json文件中的token字段可以为空。 19 | 如果你填写了token,那么你的设备也必须填写一样的token。 20 | 21 | * 目前这个前端页面仅为了管理员调试和用户的简单实用,这是个很简陋的随便做的前端,这部分仍在开发当中。 -------------------------------------------------------------------------------- /server/ab.min.js: -------------------------------------------------------------------------------- 1 | (function(){function e(e){for(let t=0;t=0?n<256?(o.push(r),o=e({arr:o,byteLength:1,number:n,type:"8"})):n>255&&n<65536?(o.push(a),o=e({arr:o,byteLength:2,number:n,type:"16"})):n>65535&&n<4294967296?(o.push(g),o=e({arr:o,byteLength:4,number:n,type:"32"})):(o.push(h),o=e({arr:o,byteLength:8,number:n,type:"64"})):n>=-128?(o.push(b),o=e({arr:o,byteLength:1,number:n,type:"8"})):n<-128&&n>=-32768?(o.push(f),o=e({arr:o,byteLength:2,number:n,type:"16"})):n<-32768&&n>=-2147483648?(o.push(i),o=e({arr:o,byteLength:4,number:n,type:"32"})):(o.push(y),o=e({arr:o,byteLength:8,number:n,type:"64"}));else{let t=n.toString().length-1;t>5?(o.push(l),o=e({arr:o,byteLength:8,number:n,type:"d"})):(o.push(p),o=e({arr:o,byteLength:4,number:n,type:"f"}))}}else if(0<=typeName.indexOf("bigint"))o.push(h),o=e({arr:o,byteLength:8,number:t[L],type:"64"});else if(0<=typeName.indexOf("uint8array")){o.push(u),o=e({arr:o,byteLength:4,number:t[L].length,type:"32"});for(let e of t[L])o.push(e)}else if(0<=typeName.indexOf("string")){o.push(s);let n=(new TextEncoder).encode(t[L]);o=e({arr:o,byteLength:4,number:n.length+1,type:"32"});for(let e of n)o.push(e);o.push(0)}return new Uint8Array(o).buffer}function n(e,t,n,o,L){if(!e)return;if(!e.byteLength)return;let c,m=[],d=e=>n?{}:[];try{c=new DataView(e)}catch(e){return d()}let U=0;for(;U247&&(t-=256),m.length&&o)break;switch(t){case r:case b:if(++U,e.byteLength-U<1)return d();if(L?m.push({data:t==r?c.getUint8(U):c.getInt8(U),offset:U,length:1}):m.push(t==r?c.getUint8(U):c.getInt8(U)),++U,o)break;break;case a:case f:if(++U,e.byteLength-U<2)return d();L?m.push({data:t==a?c.getUint16(U,!0):c.getInt16(U,!0),offset:U,length:2}):m.push(t==a?c.getUint16(U,!0):c.getInt16(U,!0)),U+=2;break;case g:case i:if(++U,e.byteLength-U<4)return d();L?m.push({data:t==g?c.getUint32(U,!0):c.getInt32(U,!0),offset:U,length:4}):m.push(t==g?c.getUint32(U,!0):c.getInt32(U,!0)),U+=4;break;case h:case y:if(++U,e.byteLength-U<8)return d();L?m.push({data:t==h?c.getBigUint64(U,!0):c.getBigInt64(U,!0),offset:U,length:8}):m.push(t==h?c.getBigUint64(U,!0):c.getBigInt64(U,!0)),U+=8;break;case p:case l:if(++U,e.byteLength-U<(t==p?4:8))return d();L?m.push({data:m.push(t==p?c.getFloat32(U,!0):c.getFloat64(U,!0)),offset:U,length:t==p?4:8}):m.push(t==p?c.getFloat32(U,!0):c.getFloat64(U,!0)),U+=t==p?4:8;break;case u:if(++U,e.byteLength-U<4)return d();let n=c.getUint32(U,!0);if(U+=4,e.byteLength-Ue.byteLength){m.push({offset:U,length:n});break}let w=new Uint8Array(n);for(let e=0;em.length-1?w[n[e]]=null:w[n[e]]=m[e];return w}const r=1,a=2,g=4,h=8,u=10,s=9,b=-1,f=-2,i=-4,y=-8,p=6,l=7;"object"==typeof window?(window.createArrayBuffer=t,window.decodeArrayBuffer=n):module.exports={createArrayBuffer:t,decodeArrayBuffer:n}})(); -------------------------------------------------------------------------------- /server/blacklist.json: -------------------------------------------------------------------------------- 1 | [] -------------------------------------------------------------------------------- /server/create.js: -------------------------------------------------------------------------------- 1 | module.exports = function (blacklist, visitors, getType, print, fnProvider) { 2 | let url = require("url"); 3 | let http = require("http"); 4 | let fs = require("fs"); 5 | let zlib = require("zlib"); 6 | return http.createServer(function (req, res) { 7 | 8 | //parse url 9 | //解析url 10 | let urlObj = url.parse(req.url, true); 11 | let pathName = urlObj.pathname; 12 | 13 | //default page 14 | //默认页面 15 | if (pathName == "/") { 16 | pathName = "index.html"; 17 | } else { 18 | pathName = pathName.substring(1); 19 | } 20 | 21 | //this part is same as iot.js 22 | //这部分和iot.js相同 23 | let ip = /\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}/.exec(req.socket.remoteAddress); 24 | 25 | if (!ip) { 26 | res.destroy(); 27 | print("a hacker detected 0"); 28 | return; 29 | } 30 | 31 | ip = ip.toString(); 32 | 33 | if (getType(ip) != "string") { 34 | res.destroy(); 35 | print("a hacker detected 1"); 36 | return; 37 | } 38 | 39 | if (!ip.length) { 40 | res.destroy(); 41 | print("a hacker detected 2"); 42 | return; 43 | } 44 | 45 | let hacker = blacklist.find(e => { return e == ip; }); 46 | if (hacker) { 47 | res.destroy(); 48 | print("a hacker detected 3"); 49 | return; 50 | } 51 | 52 | let visitor = visitors.find(e => { return e.ip == ip; }); 53 | if (visitor) { 54 | if (visitor.times++ > 100) { 55 | res.destroy(); 56 | blacklist.push(ip); 57 | print("a hacker detected"); 58 | return; 59 | } 60 | } else { 61 | visitors.push({ 62 | ip: ip, 63 | times: 0 64 | }); 65 | } 66 | 67 | 68 | //这个是以前用的现在已经不用了,暂时先不删 69 | if (/showtime/.test(pathName)) { 70 | res.writeHeader(200); 71 | let t = new Date(); 72 | let hour = t.getHours().toString(); 73 | hour = hour.length == 1 ? "0" + hour : hour; 74 | let min = t.getMinutes().toString(); 75 | min = min.length == 1 ? "0" + min : min; 76 | res.end(hour + min); 77 | return; 78 | } 79 | 80 | //not allow request js, json and node_modules 81 | //不允许请求 js, json 和 node.js 的组件目录 82 | if (/\.js/.test(pathName)) { 83 | res.writeHead(404); 84 | res.end(); 85 | return; 86 | } 87 | if (/\.json/.test(pathName)) { 88 | res.writeHead(404); 89 | res.end(); 90 | return; 91 | } 92 | if (/node_modules/.test(pathName)) { 93 | res.writeHead(404); 94 | res.end(); 95 | return; 96 | } 97 | 98 | //when use http get method to execute provider 99 | //当使用http get方法请求执行provider时 100 | if (/exec_provider/.test(pathName)) { 101 | let fromID = fnProvider(urlObj.query, res); 102 | 103 | //if function return "" 104 | //means wait for response 105 | //如果函数返回空字符串 106 | //表示需要等待返回值 107 | if (fromID.length) 108 | res.end(fromID); 109 | return; 110 | } 111 | 112 | let stat = fs.statSync(pathName, { 113 | throwIfNoEntry: false 114 | }); 115 | if (!stat) { 116 | res.writeHead(404); 117 | res.end(); 118 | return; 119 | } 120 | let tFromLocal = new Date(stat.mtime); 121 | 122 | let sendFile = e => { 123 | res.setHeader("Last-Modified", tFromLocal.toGMTString()); 124 | res.writeHead(200, { 'Content-Encoding': 'gzip' }); 125 | res.end(zlib.gzipSync(fs.readFileSync(pathName))); 126 | }; 127 | 128 | 129 | if (req.headers["if-modified-since"]) { 130 | 131 | let tFromRemote = new Date(req.headers["if-modified-since"]); 132 | if (tFromRemote) { 133 | tFromRemote = tFromRemote.getTime() + 1000; 134 | } 135 | 136 | if (tFromRemote < tFromLocal.getTime()) { 137 | sendFile(); 138 | } else { 139 | res.writeHeader(304); 140 | res.write("Not Modified"); 141 | res.end(); 142 | } 143 | 144 | } else { 145 | sendFile(); 146 | } 147 | 148 | }); 149 | }; -------------------------------------------------------------------------------- /server/globalConfig.json: -------------------------------------------------------------------------------- 1 | {"port":12345,"sendHelloInterval":10000,"waitForClientResponseTimeout":3000,"proactiveDetectClientOnline":true,"maxFindDeviceTimes":100,"token":"","enableTokenAuthorize":false} -------------------------------------------------------------------------------- /server/hash.js: -------------------------------------------------------------------------------- 1 | (function () { 2 | let rotateRight = (n, x) => { return (x >>> n) | (x << (32 - n)) } 3 | 4 | let choice = (x, y, z) => { return (x & y) ^ (~x & z) } 5 | 6 | let majority = (x, y, z) => { return (x & y) ^ (x & z) ^ (y & z) } 7 | 8 | let sha256_Sigma0 = x => { return rotateRight(2, x) ^ rotateRight(13, x) ^ rotateRight(22, x) } 9 | 10 | let sha256_Sigma1 = x => { return rotateRight(6, x) ^ rotateRight(11, x) ^ rotateRight(25, x) } 11 | 12 | let sha256_sigma0 = x => { return rotateRight(7, x) ^ rotateRight(18, x) ^ (x >>> 3) } 13 | 14 | let sha256_sigma1 = x => { return rotateRight(17, x) ^ rotateRight(19, x) ^ (x >>> 10) } 15 | 16 | let sha256_expand = (W, j) => { return (W[j & 0x0f] += sha256_sigma1(W[(j + 14) & 0x0f]) + W[(j + 9) & 0x0f] + sha256_sigma0(W[(j + 1) & 0x0f])) } 17 | let K256 = new Array(0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5, 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174, 0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da, 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967, 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85, 0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070, 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3, 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2); 18 | let ihash, count, buffer; 19 | let sha256_hex_digits = "0123456789abcdef"; 20 | 21 | let safe_add = (x, y) => { let lsw = (x & 0xffff) + (y & 0xffff); let msw = (x >> 16) + (y >> 16) + (lsw >> 16); return (msw << 16) | (lsw & 0xffff) } 22 | 23 | let sha256_init = () => { 24 | ihash = new Array(8); 25 | count = new Array(2); 26 | buffer = new Array(64); 27 | count[0] = count[1] = 0; 28 | ihash[0] = 0x6a09e667; 29 | ihash[1] = 0xbb67ae85; 30 | ihash[2] = 0x3c6ef372; 31 | ihash[3] = 0xa54ff53a; 32 | ihash[4] = 0x510e527f; 33 | ihash[5] = 0x9b05688c; 34 | ihash[6] = 0x1f83d9ab; 35 | ihash[7] = 0x5be0cd19; 36 | } 37 | 38 | let sha256_transform = () => { 39 | let a, b, c, d, e, f, g, h, T1, T2; 40 | let W = new Array(16); 41 | a = ihash[0]; 42 | b = ihash[1]; 43 | c = ihash[2]; 44 | d = ihash[3]; 45 | e = ihash[4]; 46 | f = ihash[5]; 47 | g = ihash[6]; 48 | h = ihash[7]; 49 | for (let i = 0; i < 16; i++) W[i] = buffer[(i << 2) + 3] | (buffer[(i << 2) + 2] << 8) | (buffer[(i << 2) + 1] << 16) | (buffer[i << 2] << 24); 50 | for (let j = 0; j < 64; j++) { 51 | T1 = h + sha256_Sigma1(e) + choice(e, f, g) + K256[j]; 52 | if (j < 16) T1 += W[j]; 53 | else T1 += sha256_expand(W, j); 54 | T2 = sha256_Sigma0(a) + majority(a, b, c); 55 | h = g; 56 | g = f; 57 | f = e; 58 | e = safe_add(d, T1); 59 | d = c; 60 | c = b; 61 | b = a; 62 | a = safe_add(T1, T2); 63 | } 64 | ihash[0] += a; 65 | ihash[1] += b; 66 | ihash[2] += c; 67 | ihash[3] += d; 68 | ihash[4] += e; 69 | ihash[5] += f; 70 | ihash[6] += g; 71 | ihash[7] += h; 72 | } 73 | 74 | let sha256_update = (data, inputLen) => { 75 | let i, index, curpos = 0; 76 | index = (count[0] >> 3) & 0x3f; 77 | let remainder = inputLen & 0x3f; 78 | if ((count[0] += inputLen << 3) < inputLen << 3) count[1]++; 79 | count[1] += inputLen >> 29; 80 | if (typeof data == "string") { 81 | for (i = 0; i + 63 < inputLen; i += 64) { 82 | for (let j = index; j < 64; j++) buffer[j] = data.charCodeAt(curpos++); 83 | sha256_transform(); 84 | index = 0; 85 | } 86 | for (let j = 0; j < remainder; j++) buffer[j] = data.charCodeAt(curpos++); 87 | } 88 | else { 89 | for (i = 0; i + 63 < inputLen; i += 64) { 90 | for (let j = index; j < 64; j++) buffer[j] = data[curpos++]; 91 | sha256_transform(); 92 | index = 0; 93 | } 94 | for (let j = 0; j < remainder; j++) buffer[j] = data[curpos++]; 95 | } 96 | 97 | } 98 | 99 | let sha256_final = () => { 100 | let index = (count[0] >> 3) & 0x3f; 101 | buffer[index++] = 0x80; 102 | if (index <= 56) { for (let i = index; i < 56; i++) buffer[i] = 0 } else { 103 | for (let i = index; i < 64; i++) buffer[i] = 0; 104 | sha256_transform(); 105 | for (let i = 0; i < 56; i++) buffer[i] = 0; 106 | } 107 | buffer[56] = (count[1] >>> 24) & 0xff; 108 | buffer[57] = (count[1] >>> 16) & 0xff; 109 | buffer[58] = (count[1] >>> 8) & 0xff; 110 | buffer[59] = count[1] & 0xff; 111 | buffer[60] = (count[0] >>> 24) & 0xff; 112 | buffer[61] = (count[0] >>> 16) & 0xff; 113 | buffer[62] = (count[0] >>> 8) & 0xff; 114 | buffer[63] = count[0] & 0xff; 115 | sha256_transform(); 116 | } 117 | 118 | let sha256_encode_bytes = () => { 119 | let j = 0; 120 | let output = new Array(32); 121 | for (let i = 0; i < 8; i++) { 122 | output[j++] = (ihash[i] >>> 24) & 0xff; 123 | output[j++] = (ihash[i] >>> 16) & 0xff; 124 | output[j++] = (ihash[i] >>> 8) & 0xff; 125 | output[j++] = ihash[i] & 0xff; 126 | } 127 | return output; 128 | } 129 | 130 | let sha256_encode_hex = () => { let output = new String(); for (let i = 0; i < 8; i++) { for (let j = 28; j >= 0; j -= 4) output += sha256_hex_digits.charAt((ihash[i] >>> j) & 0x0f) } return output; } 131 | 132 | let hash = (data, bytes, gen) => { 133 | sha256_init(); 134 | sha256_update(data, data.length); 135 | sha256_final(); 136 | if (bytes) { 137 | return sha256_encode_bytes(); 138 | } else { 139 | return sha256_encode_hex(); 140 | } 141 | }; 142 | if (typeof window == "object") { 143 | window.getHash = hash; 144 | } else { 145 | module.exports = hash; 146 | } 147 | 148 | })(); -------------------------------------------------------------------------------- /server/hash.min.js: -------------------------------------------------------------------------------- 1 | (function(){let e,r,t,l=(e,r)=>r>>>e|r<<32-e,o=(e,r,t)=>e&r^~e&t,f=(e,r,t)=>e&r^e&t^r&t,n=e=>l(2,e)^l(13,e)^l(22,e),a=e=>l(6,e)^l(11,e)^l(25,e),w=e=>l(7,e)^l(18,e)^e>>>3,A=e=>l(17,e)^l(19,e)^e>>>10,y=(e,r)=>e[15&r]+=A(e[r+14&15])+e[r+9&15]+w(e[r+1&15]),i=new Array(1116352408,1899447441,3049323471,3921009573,961987163,1508970993,2453635748,2870763221,3624381080,310598401,607225278,1426881987,1925078388,2162078206,2614888103,3248222580,3835390401,4022224774,264347078,604807628,770255983,1249150122,1555081692,1996064986,2554220882,2821834349,2952996808,3210313671,3336571891,3584528711,113926993,338241895,666307205,773529912,1294757372,1396182291,1695183700,1986661051,2177026350,2456956037,2730485921,2820302411,3259730800,3345764771,3516065817,3600352804,4094571909,275423344,430227734,506948616,659060556,883997877,958139571,1322822218,1537002063,1747873779,1955562222,2024104815,2227730452,2361852424,2428436474,2756734187,3204031479,3329325298),c="0123456789abcdef",d=(e,r)=>{let t=(65535&e)+(65535&r),l=(e>>16)+(r>>16)+(t>>16);return l<<16|65535&t},h=()=>{e=new Array(8),r=new Array(2),t=new Array(64),r[0]=r[1]=0,e[0]=1779033703,e[1]=3144134277,e[2]=1013904242,e[3]=2773480762,e[4]=1359893119,e[5]=2600822924,e[6]=528734635,e[7]=1541459225},s=()=>{let r,l,w,A,c,h,s,u,g,p,b=new Array(16);r=e[0],l=e[1],w=e[2],A=e[3],c=e[4],h=e[5],s=e[6],u=e[7];for(let e=0;e<16;e++)b[e]=t[3+(e<<2)]|t[2+(e<<2)]<<8|t[1+(e<<2)]<<16|t[e<<2]<<24;for(let e=0;e<64;e++)g=u+a(c)+o(c,h,s)+i[e],g+=e<16?b[e]:y(b,e),p=n(r)+f(r,l,w),u=s,s=h,h=c,c=d(A,g),A=w,w=l,l=r,r=d(g,p);e[0]+=r,e[1]+=l,e[2]+=w,e[3]+=A,e[4]+=c,e[5]+=h,e[6]+=s,e[7]+=u},u=(e,l)=>{let o,f,n=0;f=r[0]>>3&63;let a=63&l;if((r[0]+=l<<3)>29,"string"==typeof e){for(o=0;o+63{let e=r[0]>>3&63;if(t[e++]=128,e<=56)for(let r=e;r<56;r++)t[r]=0;else{for(let r=e;r<64;r++)t[r]=0;s();for(let e=0;e<56;e++)t[e]=0}t[56]=r[1]>>>24&255,t[57]=r[1]>>>16&255,t[58]=r[1]>>>8&255,t[59]=255&r[1],t[60]=r[0]>>>24&255,t[61]=r[0]>>>16&255,t[62]=r[0]>>>8&255,t[63]=255&r[0],s()},p=()=>{let r=0,t=new Array(32);for(let l=0;l<8;l++)t[r++]=e[l]>>>24&255,t[r++]=e[l]>>>16&255,t[r++]=e[l]>>>8&255,t[r++]=255&e[l];return t},b=()=>{let r=new String;for(let t=0;t<8;t++)for(let l=28;l>=0;l-=4)r+=c.charAt(e[t]>>>l&15);return r},C=(e,r,t)=>(h(),u(e,e.length),g(),r?p():b());"object"==typeof window?window.getHash=C:module.exports=C})(); -------------------------------------------------------------------------------- /server/index.min.js: -------------------------------------------------------------------------------- 1 | (function(){let e=window;e.rememberPassword=!1;let t=Uint8Array;e.devices=[];let r=e.get=(e=>document.getElementById(e)),n=e.create=((e,t)=>{e=e||"div";let r=document.createElement(e);return t&&(r.style.display="inline-block"),r});e.getType=function(e,t){let r=Object.prototype.toString.call(e).toLowerCase().split(" ")[1];return r=r.substring(0,r.length-1),"array"==r?"a":"uint8array"==r?"u8a":"string"==r?r:"number"==r?t?e<0?r:e>=0&&e<255?"u8":e>=255&&e<65535?"u16":e>=65535&&e<4294967295?"u32":"u64":r:"bigint"==r?"b":"object"==r?t?e.constructor.name?e.constructor.name:"uo":"o":"arraybuffer"==r?"ab":"function"==r?"f":"date"==r?"d":"dataview"==r?"dv":r},t.prototype.toHex=function(){let e="";for(let t of this){let r;t?(r=t.toString(16),1==r.length&&(r="0"+r)):r="00",e+=r}return e},String.prototype.toU8a=function(){if(this.length%2)return;let e=this,r=new t(e.length/2),n=0;for(let t=0;t{if(!e)return"";t=CryptoJS.enc.Hex.parse(t),r=CryptoJS.enc.Hex.parse(r),e=CryptoJS.enc.Hex.parse(e),e=CryptoJS.enc.Base64.stringify(e);let s=CryptoJS.AES.decrypt(e,t,{iv:r,mode:CryptoJS.mode.CBC,padding:CryptoJS.pad.Pkcs7}),o=s.toString(CryptoJS.enc.Hex);return n?o.toU8a():o});e.encrypt=((e,t,r,n)=>{if(!e)return"";t=CryptoJS.enc.Hex.parse(t),r=CryptoJS.enc.Hex.parse(r);let s=CryptoJS.enc.Hex.parse(e),o=CryptoJS.AES.encrypt(s,t,{iv:r,mode:CryptoJS.mode.CBC,padding:CryptoJS.pad.Pkcs7}),a=o.ciphertext.toString();return n?a.toU8a():a}),(t=>{let r=localStorage,n=JSON;class s{constructor(e){if(this.key=e,this.arr=0,this.exists=!1,r[e]&&r[e].length)try{this.arr=n.parse(r[e]),this.exists=!0}catch(e){this.arr=[]}else this.arr=[]}update(){r[this.key]=n.stringify(this.arr)}find(e){return this.arr.find(e)}size(){return this.arr.length}filter(e){return this.arr.filter(e)}findIndex(e){return this.arr.findIndex(e)}push(e,t){this.arr.push(e),t||this.update()}splice(e,t,r){this.arr.splice(e,t||1),r||this.update()}spliceIfExists(e,t){let r=this.findIndex(e);r>=0&&this.splice(r,t)}pushIfNotExists(e,t,r){this.set(e,t,r)}get(e,t){let r=this,n=r.findIndex(e);if(n>=0)return t?{index:n,data:r.arr[n]}:r.arr[n]}set(e,t,r){let n=this,s=n.findIndex(e);s>=0?n.arr[s]=t:n.arr.push(t),r||n.update()}concat(e,t){let r=this;r.arr=e instanceof s?r.arr.concat(e.arr):r.arr.concat(e),t||r.update}forEach(e){for(let t of this.arr)e(t)}}e.LSAsArray=s})();let o=new LSAsArray("users");e.setEventEmitter=(e=>{e.on=((t,r,n,s,o)=>{if(e["@"+t]||(e["@"+t]=[]),!n)return void e["@"+t].push({id:n,fn:r,obj:s,disposable:o});let a=e["@"+t].find(e=>e.id==n);a||e["@"+t].push({id:n,fn:r,obj:s,disposable:o})}),e.emit=((t,r)=>{let n=e["@"+t];if(!n)return;for(let e=0;e{let n=r("divMsg");if(n.t)try{clearTimeout(n.t)}catch(e){}n.className="divMsg divMsgShow",n.innerHTML=e,n.t=setTimeout(()=>{n.className="divMsg"},t||3e3)});(t=>{setEventEmitter(e),e.on("connected",t=>{e.connected=!0,a("已建立连接")}),e.on("unknownData",e=>{a("接收到了未知消息")}),e.on("disconnected",t=>{e.connected=!1,a("已断开连接")}),e.on("connectionError",t=>{e.connected=!1,a("连接发生错误")}),e.on("unknownCommand",function(e){a("接收到了未知命令"+e.toString())}),e.on("newDevice",function(t){let n=e.devices.find(e=>e.id==t[7]);n?n.updateInfo(t):(n=new Device(t),e.devices.push(n),r("devices").appendChild(n.container))});let n=r("btnRemPwd"),s=r("remPwdDot");n.onclick=(t=>{e.rememberPassword?(e.rememberPassword=!1,s.className+="remPwdDot",e.readPwdFromCache&&a("是否清除保存的密码?",1e4)):(e.rememberPassword=!0,s.className+=" remPwdDotSelected")})})(),e.removeUserInfo=(e=>{delete localStorage.users,a("密码已清除"),r("remPwdDot").className="remPwdDot"}),e.generateHash=(t=>{let r=(new Date).getTime().toString();return{t:r,hash:getHash(e.userName+e.password+r)}});let i=t=>e.devices.find(e=>e.id==t),c=t=>{t=t.data,"ab"!=getType(t)&&e.emit("unknownData");let r=decodeArrayBuffer(t);if(r&&r.length){let t=r[0],n=getType(t,!0);if("b"===n){let e=BigInt(255);t&=e,t=Number(t)}else if("u8"!=n)return;switch(t){case 12:sendArrayBuffer([192]);break;case 250:e.emit("newDevice",r);break;case 174:{let e=i(r[1]);e&&e.otaProgress(r[2]);break}case 182:{let e=i(r[2]);e&&e.commandResponse(r[3]);break}case 251:{r[1]="u8a"==getType(r[1])?r[1].toHex():r[1];let t=i(r[1]);if(t)if(5==r.length){let n=s(r[3].toHex(),e.password,e.password.substring(0,32),!0);n&&n.length&&t.showMsg(decodeArrayBuffer(n.buffer))}else t.showMsg(r[3]);break}default:e.emit("unknownCommand",t)}}};e.resetWebsocket=function(){e.webSocket=new WebSocket("ws://"+e.location.host),webSocket.binaryType="arraybuffer",webSocket.onopen=function(){e.emit("connected")},webSocket.onclose=function(){e.emit("disconnected"),setTimeout(()=>{e.resetWebsocket()},1e3)},webSocket.onmessage=function(e){c(e)},webSocket.onerror=function(t){e.emit("connectionError")}},e.resetWebsocket(),e.sendArrayBuffer=(e=>{webSocket.send(createArrayBuffer(e))});let d=(t,n,s)=>{e.password=n=s?n:getHash(n),r("divLogin").className+=" divLoginHide",r("devices").className+=" devicesShow",e.rememberPassword&&t.length&&n.length&&(o.set(e=>e.userName==t,{userName:t,password:n}),a("密码已存储")),e.userName=t=getHash(t),window.android&&window.android.updateUserInfo(t+","+n),e.id=getHash(t+n+"admin");let i=(new Date).getTime(),c=new Uint8Array(getHash(t+n+i.toString(),!0)),d=BigInt("0xC0000000000000AF");sendArrayBuffer([d,e.id,e.userName,i,c])};o.size()&&(1==o.size()?e.on("connected",t=>{e.readPwdFromCache=!0,e.rememberPassword=!0,r("remPwdDot").className+=" remPwdDotSelected",setTimeout(()=>{let t=o.get(e=>!0);e.rememberPassword&&d(t.userName,t.password,!0)},1e3)}):o.forEach(t=>{let s=n("a");s.className="button",s.innerText=t.userName,s.onclick=function(){e.readPwdFromCache=!0,d(t.userName,t.password,!0)},r("btnContainer").appendChild(s)})),r("login").onclick=function(){d(r("userName").value,r("password").value,0)},(e=>{let t=document.getElementsByTagName("a");for(let e of t)e.className.indexOf("button")>=0&&!e.href&&(e.href="javascript:void(0);")})()})(); -------------------------------------------------------------------------------- /server/source/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Internal Of Things 8 | 173 | 174 | 175 | 176 |
177 |
178 |
179 |
180 | 181 |
182 |
183 | 184 |
185 |
186 | 187 |
188 | 登录 189 |
190 |
191 |
192 | 记住密码 193 |
194 |
195 | 196 |
197 |
198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | -------------------------------------------------------------------------------- /server/start.bat: -------------------------------------------------------------------------------- 1 | node iot.js -------------------------------------------------------------------------------- /server/start.command: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | cd $(dirname $0) 3 | node iot.js -------------------------------------------------------------------------------- /server/tools.bat: -------------------------------------------------------------------------------- 1 | node tools.js -------------------------------------------------------------------------------- /server/tools.command: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | cd $(dirname $0) 3 | node tools.js -------------------------------------------------------------------------------- /server/tools.js: -------------------------------------------------------------------------------- 1 | let fs = require('fs'); 2 | let lastUpdateTime = 0; 3 | 4 | function refresh() { 5 | let html = fs.readFileSync("./source/index.html").toString(); 6 | let hash = fs.readFileSync("./hash.min.js").toString(); 7 | let aes = fs.readFileSync("./aes.min.js").toString(); 8 | let ab = fs.readFileSync("./ab.min.js").toString(); 9 | let device = fs.readFileSync("./device.min.js").toString(); 10 | let index = fs.readFileSync("./index.min.js").toString(); 11 | 12 | 13 | html = html.replace(/\/\/hash\.min\.js\/\//g, hash); 14 | html = html.replace(/\/\/aes\.min\.js\/\//g, aes); 15 | html = html.replace(/\/\/ab\.min\.js\/\//g, ab); 16 | html = html.replace(/\/\/index\.min\.js\/\//g, index); 17 | html = html.replace(/\/\/device\.min\.js\/\//g, device); 18 | 19 | 20 | html = html.replace(/(\r|\n){1,}/g, ""); 21 | html = html.replace(/\s{2,}/g, " "); 22 | 23 | if (!fs.existsSync("./pro/")) { 24 | fs.mkdirSync("./pro/"); 25 | } 26 | 27 | fs.writeFileSync("./pro/index.html", html); 28 | fs.writeFileSync("./index.html", html); 29 | console.log("success"); 30 | lastUpdateTime = new Date().getTime(); 31 | }; 32 | 33 | fs.watch("./", function (event, filename) { 34 | if (new Date().getTime() - lastUpdateTime > 1000) 35 | refresh(); 36 | }); 37 | -------------------------------------------------------------------------------- /src/app/README.md: -------------------------------------------------------------------------------- 1 | ### English 2 | 3 | You could code from here, edit functions "setup" and "loop" in file "app.cpp" as arduino way. 4 | 5 | The file /lib/mydb/mydb.h and mydb.cpp declared and defined 3 instances of class MyDB, they are: 6 | 7 | - db 8 | - dbUser 9 | - dbApp 10 | 11 | The first one is main database for system use, you app could use this database to store data, but this is not recommended. 12 | 13 | The second one stored other users information, you **SHOULD NOT** modify any content by your way in this database. 14 | 15 | The last one is for user app, you could do any action to this instance. 16 | 17 | ### 中文 18 | 19 | 你可以从这里开始编写你的代码,编辑文件 "app.cpp" 中的 函数 "setup" 和 "loop",和使用arduino的方式一样。 20 | 21 | 文件 /lib/mydb/mydb.h 和 mydb.cpp 中声明和定义了3个数据库实例,它们是: 22 | 23 | - db 24 | - dbUser 25 | - dbApp 26 | 27 | 第一个是主数据库,给系统用的,你的app可以使用这个数据库,但是不建议。 28 | 29 | 第二个存储了其他用户信息,你 **不** 应该对这个数据库用你的方式进行任何操作。 30 | 31 | 第三个是给你的app用的,你可以随意使用。 -------------------------------------------------------------------------------- /src/app/app.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * @file app.cpp 3 | * @brief 4 | * 5 | * Code this file just like Arduino way 6 | * 7 | * 就像使用Arduino框架一样在这个文件中写代码就可以了 8 | * 9 | */ 10 | 11 | #include "app.h" 12 | 13 | void App::setup() 14 | { 15 | /** 16 | * ===English: 17 | * put your code in this function to run it once 18 | * 19 | * ===中文: 20 | * 把你的代码放在这个函数里,代码会被运行一次 21 | */ 22 | 23 | /** 24 | * ===English: 25 | * if you just did an OTA update 26 | * you could do anything here, don't worry any code bugs 27 | * if any error happened, and reboot times reach maximum valve 28 | * firmware will rollback to former version automatically 29 | * 30 | * you could modify the valve yourself 31 | * in file /lib/config/config.h 32 | * 33 | * ! Attention: ONLY when firmware updated by built-in OTA function 34 | * has this feature, not included update through serial lines 35 | * 36 | * ===中文: 37 | * 如果你刚才使用了OTA升级了这个固件 38 | * 你可以在这里写任何代码 39 | * 无须担心任何错误引起无限复位 40 | * 当复位次数超过设定阈值时 41 | * 固件会自动回滚上一个版本 42 | * 43 | * 阈值可以根据自己的需求调整 44 | * 在 /lib/config/config.h 45 | * 46 | * ! 注意: [ 仅 ] 当固件通过内置的OTA升级功能升级后才有此功能 47 | * 直接从串口升级的固件没有此功能 48 | */ 49 | } 50 | 51 | void App::loop() 52 | { 53 | // put your code in this function 54 | // it will run repeatedly, attention attached 55 | 56 | // 把你的代码放在这个函数里,代码会重复的运行,请阅读注意事项 57 | } 58 | 59 | /** 60 | * @brief App instance defined here 61 | * 62 | * 类App实例在此实例化 63 | * 64 | */ 65 | App *app = new App(); -------------------------------------------------------------------------------- /src/app/app.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | 4 | /** 5 | * @brief name of app 6 | * 7 | * app名称 8 | * 9 | */ 10 | #define APP_NAME "Abc" 11 | 12 | /** 13 | * @brief version of app 14 | * 15 | * app版本 16 | * 17 | */ 18 | #define APP_VERSION 0 19 | 20 | /** 21 | * @brief default cpu frequency of current app 22 | * 23 | * 这个app默认的cpu频率 24 | * 25 | */ 26 | #define DEFAULT_CPU_FREQ 240 27 | 28 | 29 | /** 30 | * @brief indicate app has custom extra callback 31 | * for local websocket server 32 | * 33 | * 指示app是否有自定义的用于本地websocket服务器的回调函数 34 | * 35 | * 36 | * @note the function declared and defined as C pattern 37 | * not included in class App 38 | * 39 | * 函数以C语言风格声明和定义,不包含在类App内 40 | * 41 | */ 42 | //#define APP_HAS_EXTRA_LOCAL_WEBSOCKET_CALLBACK 43 | 44 | /** 45 | * @brief indicate app has custom extra callback 46 | * for remote websocket client 47 | * 48 | * 指示app是否有自定义的用于远程websocket客户端的回调函数 49 | * 50 | * 51 | * @note the function declared and defined as C pattern 52 | * not included in class App 53 | * 54 | * 函数以C语言风格声明和定义,不包含在类App内 55 | * 56 | */ 57 | //#define APP_HAS_EXTRA_REMOTE_WEBSOCKET_CALLBACK 58 | 59 | /** 60 | * @brief default app class 61 | * you could modify class name, attention attached 62 | * 63 | * 默认的app类 64 | * 你可以修改这个类的名字,请阅读注意事项 65 | * 66 | * @attention the name of instance must be "app" 67 | * refer to the tail of the file "app.cpp" 68 | * 69 | * 类实例的名称必须为 "app",查看 "app.app" 的尾部 70 | * 71 | * @note if you modified the name of he instance, 72 | * you should also modify the name in the file "main.cpp" 73 | * not recommended for beginner 74 | * 75 | * 如果你修改了类实例的名字,你也应该一同修改文件 "main.cpp" 76 | * 里的名字 77 | * 不推荐新手做此操作 78 | * 79 | */ 80 | class App 81 | { 82 | private: 83 | public: 84 | /** 85 | * @brief default constructor, attention attached! 86 | * 默认构造函数,请阅读注意事项! 87 | * 88 | * @attention if you declared and define other constructors 89 | * you should NOT do any actions except set default value 90 | * for variables. 91 | * Because the instance of class App will be define in the tail 92 | * of the app.cpp, and that is NOT included in the system 93 | * GlobalManager::makeSureNewFirmwareValid() measurement scope, 94 | * if any action in constructor cause ESP32 panic reset, 95 | * you will lost connection of this ESP32, then you have to 96 | * use hardware serial to upload new firmware. 97 | * 98 | * 如果你定义了其他构造函数,请确保在构造函数内 [不要] 做 除了给变量赋值 99 | * 以外的其他操作,因为类App的实例在 app.cpp 文件的尾部被实例化, 100 | * 而这不在系统 GlobalManager::makeSureNewFirmwareValid() 的 101 | * 监测范围内,如果构造函数内有任何操作导致ESP32复位,你会丢失与这个 102 | * ESP32的连接,此时你只能再次用硬件串口重新上传固件。 103 | * 104 | */ 105 | inline App(){}; 106 | inline ~App(){}; 107 | 108 | /** 109 | * @brief put your code in this function to run it once 110 | * 111 | * 把你的代码放在这个函数里,代码会被运行一次 112 | * 113 | */ 114 | void setup(); 115 | 116 | /** 117 | * @brief put your code in this function 118 | * it will run repeatedly, attention attached 119 | * 120 | * 把你的代码放在这个函数里,代码会重复的运行,请阅读注意事项 121 | * 122 | * @attention loop function will NOT run when OTA update 123 | * process running 124 | * 125 | * 在OTA升级期间,loop函数不会被运行 126 | * 127 | */ 128 | void loop(); 129 | }; 130 | 131 | extern App *app; -------------------------------------------------------------------------------- /todo_list.txt: -------------------------------------------------------------------------------- 1 | Add ECDHE for AP mode setup. 2 | 增加面向新手的更方便的ESPNOW集成。 3 | ESPNOW数据路由到统一处理过程。 4 | 存储空间优化。 5 | 增加内置数据库存储大型二进制文件功能。 6 | C语言转换器,实现C语言转换到ULP宏,方便新手使用C语言编程ULP。 7 | 为Provider添加主动模式。 8 | 统一管理界面,同步本地(AP)和远程管理界面。 9 | 添加设备间快速配网功能。 -------------------------------------------------------------------------------- /tools/mklittlefs: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vidalouiswang/Abc/e382735f6003fe49349794d8421e23fcdb191b9a/tools/mklittlefs -------------------------------------------------------------------------------- /tools/mklittlefs.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vidalouiswang/Abc/e382735f6003fe49349794d8421e23fcdb191b9a/tools/mklittlefs.exe --------------------------------------------------------------------------------