├── .editorconfig ├── .gitignore ├── .gitmodules ├── LICENSE ├── README.md ├── converter ├── .dockerignore ├── .gitignore ├── Dockerfile ├── convert.sh ├── docker-compose.yml ├── epaper_7c_palette.png ├── epaper_7c_palette_captured.png ├── generate-index-bin.js ├── package-lock.json └── package.json ├── images └── smp7c_main.jpg ├── include └── README ├── kicad ├── .gitignore ├── smp7c.kicad_pcb ├── smp7c.kicad_pro └── smp7c.kicad_sch ├── lib └── README ├── openscad ├── .gitignore ├── Makefile ├── README.md ├── _preview_picture_frame_parameters.scad ├── _preview_smp7c_spacer.scad ├── images │ ├── models.png │ ├── picture_frame_parameters.png │ ├── smp7c_battery_spacer.png │ ├── smp7c_preview.png │ ├── smp7c_spacer_01_top_left.png │ ├── smp7c_spacer_02_top_right.png │ ├── smp7c_spacer_03_bottom_right.png │ ├── smp7c_spacer_04_bottom_left.png │ └── smp7c_stapler_wall_hook.png ├── modules │ ├── annotation.scad │ ├── epd_565in_acep.scad │ ├── picture_frame.scad │ └── smp7c_spacer.scad ├── smp7c_battery_spacer.scad ├── smp7c_spacer_01_top_left.scad ├── smp7c_spacer_02_top_right.scad ├── smp7c_spacer_03_bottom_right.scad ├── smp7c_spacer_04_bottom_left.scad ├── smp7c_stapler_wall_hook.scad └── stl │ ├── smp7c_battery_spacer.stl │ ├── smp7c_spacer_01_top_left.stl │ ├── smp7c_spacer_02_top_right.stl │ ├── smp7c_spacer_03_bottom_right.stl │ ├── smp7c_spacer_04_bottom_left.stl │ └── smp7c_stapler_wall_hook.stl ├── platformio.ini ├── src ├── GxEPD2_565c_mod.cpp ├── GxEPD2_565c_mod.h ├── GxEPD2_7C_mod.h ├── PngBlobParser.cpp ├── PngBlobParser.h ├── SMP7C.cpp ├── SMP7C.h ├── assets │ └── lowbattery.h ├── main.cpp ├── pm.cpp ├── pm.h ├── stash.cpp └── stash.h ├── test └── README └── ulp ├── .gitignore ├── ulp_common.h ├── ulp_main.h ├── ulp_powermanager.c └── ulp_powermanager.s /.editorconfig: -------------------------------------------------------------------------------- 1 | # top-most EditorConfig file 2 | root = true 3 | 4 | # Unix-style newlines with a newline ending every file 5 | [*] 6 | charset = utf-8 7 | end_of_line = lf 8 | insert_final_newline = true 9 | 10 | # 4 space indentation 11 | [*.py] 12 | indent_style = space 13 | indent_size = 4 14 | 15 | [*.scad] 16 | trim_trailing_whitespace = true 17 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .pio 2 | ### https://raw.github.com/github/gitignore/cdde85a36ecb48a162385829d198a7a5bf429a63/Global/Linux.gitignore 3 | 4 | *~ 5 | 6 | # temporary files which can be created if a process still has a handle open of a deleted file 7 | .fuse_hidden* 8 | 9 | # KDE directory preferences 10 | .directory 11 | 12 | # Linux trash folder which might appear on any partition or disk 13 | .Trash-* 14 | 15 | # .nfs files are created when an open file is removed but is still being accessed 16 | .nfs* 17 | 18 | 19 | ### https://raw.github.com/github/gitignore/cdde85a36ecb48a162385829d198a7a5bf429a63/Global/VisualStudioCode.gitignore 20 | 21 | .vscode/ 22 | !.vscode/settings.json 23 | !.vscode/tasks.json 24 | !.vscode/launch.json 25 | !.vscode/extensions.json 26 | !.vscode/*.code-snippets 27 | 28 | # Local History for Visual Studio Code 29 | .history/ 30 | 31 | # Built Visual Studio Code Extensions 32 | *.vsix 33 | 34 | 35 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "openscad/scad-utils"] 2 | path = openscad/scad-utils 3 | url = https://github.com/OskarLinde/scad-utils 4 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 likeablob 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

2 | 3 |

4 | From "Spirited Away (2001)" © 2001 Studio Ghibli・NDDTM. 5 |

6 |

7 | 8 | https://user-images.githubusercontent.com/46628917/185350349-dca5a5df-3bee-4c23-829b-23d41562c1f2.mp4 9 | 10 | # slow-movie-player-7c 11 | 12 | [![standard-readme compliant](https://img.shields.io/badge/standard--readme-OK-green.svg?style=flat-square)](https://github.com/RichardLitt/standard-readme) 13 | 14 | Yet another "Slow Movie Player" implementation with 7-color E-Paper module and ESP32. 15 | 16 | Features: 17 | 18 | - 7-color E-Paper 19 | - microSD (MMC_SD) as the main storage 20 | - Power management by the ESP32 ULP coprocessor 21 | - Battery operated (12 uA @ Deep Sleep. Runs 1.2 years on a 2000 mAh battery.) 22 | 23 | ## Table of Contents 24 | 25 | - [Background](#background) 26 | - [Install](#install) 27 | - [Usage](#usage) 28 | - [Maintainers](#maintainers) 29 | - [Contributing](#contributing) 30 | - [License](#license) 31 | 32 | ## Background 33 | 34 | A few years later from my previous EPD project [SHIHEN](https://github.com/likeablob/shihen), while being impressed on "Slow Movie" and forks ([[1]](https://hackaday.io/project/179122-slow-ish-movie-player), [[2]](https://github.com/TomWhitwell/SlowMovie)), I was wondering it would be fun if I could also have something bigger and more eye-catching than SHIHEN. One day I found a multi-color E-Paper module available and immediately made an impulse buy. At that time, in [Endless Endless Eight](https://github.com/likeablob/endless-endless-eight/) I had just started learning how powerful ESP32 ULP co-processor is, especially for battery-powered devices. So I decided to make a yet another variant of slow movie player by combining the two components. 35 | 36 | The 7-color EPD module has notoriously long refresh time (>= 30 sec), however by delegating BUSY signal handlings to the ULP co-processor, the ESP32 module itself can be stay in Deep Sleep at most of the time. 37 | 38 | Given the ESP32 module in the Deep Sleep mode only draws a dozen uA (Actually `12 uA` measured = `6 uA for the module` + `6 uA for peripherals`.), [theoretically the device can survive over a year](https://blc.vercel.app/?config=eyJiYXR0ZXJ5Q2FwYWNpdHkiOiIyMDAwIiwic2xlZXBDdXJyZW50TWlsaXMiOjAuMDEyLCJ3YWtlQ3VycmVudE1pbGlzIjoiMzAiLCJ3YWtldXBDb3VudCI6IjEyIiwid2FrZXVwVW5pdCI6ODY0MDAsIndha2V1cFNlYyI6IjMwIn0) under a 2000 mAh rated battery and 2 hours of wake interval. 39 | 40 | ## Install 41 | 42 | ### BOM 43 | 44 | _WIP_ 45 | 46 | - ESP32 (ESP-WROOM-32) 47 | - Waveshare ACeP 7-Color E-Paper Display Module (600x448 5.65inch) 48 | - SMD microSD slot 49 | - microSD card 50 | - M2\*5 screws \* 4 51 | - AAA batteries \* 3 52 | 53 | ### Wire components 54 | 55 | See [./kicad](./kicad). 56 | 57 | ### Print spacers 58 | 59 | See [./openscad](./openscad). 60 | 61 | 62 | 63 | ### Build firmware 64 | 65 | ```sh 66 | $ pio run || pio run # The very 1st run may fail due to ulptool-pio 67 | $ pio run -t upload 68 | ``` 69 | 70 | ## Usage 71 | 72 | ### Prepare video file 73 | 74 | Your video file must get converted to a special format before being placed on the microSD. 75 | Simply put `1. Dump video frames to a bunch of PNG images.` `2. Apply palette to the images.` `3. Concat the images into a single file.` 76 | 77 | You have several options as follows. After obtaining `index.bin` and `images.bin`, place them onto the root of your FAT32-formatted microSD. 78 | 79 | #### Option A: With Docker 80 | 81 | ```sh 82 | $ cd converter 83 | $ cp path/to/your_vid_file . 84 | $ docker-compose run --rm converter your_vid_file 85 | ``` 86 | 87 | #### Option B: With .sh 88 | 89 | - Note that you have to install ffmpeg, ImageMagick, node(v16) beforehand. 90 | 91 | ```sh 92 | $ cd converter 93 | $ ./convert.sh path/to/your_vid_file 94 | ``` 95 | 96 | #### Option C: By your hand 97 | 98 | 1. Dump video frames into .png files. 99 | 100 | ```sh 101 | $ cd converter 102 | $ mkdir -p vid 103 | 104 | # -r 1/2 == 0.5 FPS 105 | $ ffmpeg -i path/to/your_vid_file -r 1/2 -vf scale=-1:448,crop=600:448 vid/vid_%06d.png 106 | ``` 107 | 108 | As you may have noticed, you can use any kind of image sources here not only usual video files. 109 | 110 | 2. Reduce colors with the following 7-color palette. 111 | ![](./converter/epaper_7c_palette.png) 112 | 113 | ```sh 114 | $ mogrify -monitor -dither FloydSteinberg -remap epaper_7c_palette.png vid/* 115 | ``` 116 | 117 | 3. Generate `index.bin` and `images.bin`. 118 | 119 | ```sh 120 | $ npm ci 121 | $ node generate-index-bin.js vid/ 122 | ``` 123 | 124 | ### How it works 125 | 126 | _WIP_ 127 | 128 | ## Maintainers 129 | 130 | [@likeablob](https://github.com/likeablob) 131 | 132 | ## Contributing 133 | 134 | PRs accepted. 135 | 136 | Small note: If editing the README, please conform to the [standard-readme](https://github.com/RichardLitt/standard-readme) specification. 137 | 138 | ## License 139 | 140 | MIT 141 | -------------------------------------------------------------------------------- /converter/.dockerignore: -------------------------------------------------------------------------------- 1 | ### https://raw.github.com/github/gitignore/cdde85a36ecb48a162385829d198a7a5bf429a63/Node.gitignore 2 | 3 | # Logs 4 | logs 5 | *.log 6 | npm-debug.log* 7 | yarn-debug.log* 8 | yarn-error.log* 9 | lerna-debug.log* 10 | .pnpm-debug.log* 11 | 12 | # Diagnostic reports (https://nodejs.org/api/report.html) 13 | report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json 14 | 15 | # Runtime data 16 | pids 17 | *.pid 18 | *.seed 19 | *.pid.lock 20 | 21 | # Directory for instrumented libs generated by jscoverage/JSCover 22 | lib-cov 23 | 24 | # Coverage directory used by tools like istanbul 25 | coverage 26 | *.lcov 27 | 28 | # nyc test coverage 29 | .nyc_output 30 | 31 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) 32 | .grunt 33 | 34 | # Bower dependency directory (https://bower.io/) 35 | bower_components 36 | 37 | # node-waf configuration 38 | .lock-wscript 39 | 40 | # Compiled binary addons (https://nodejs.org/api/addons.html) 41 | build/Release 42 | 43 | # Dependency directories 44 | node_modules/ 45 | jspm_packages/ 46 | 47 | # Snowpack dependency directory (https://snowpack.dev/) 48 | web_modules/ 49 | 50 | # TypeScript cache 51 | *.tsbuildinfo 52 | 53 | # Optional npm cache directory 54 | .npm 55 | 56 | # Optional eslint cache 57 | .eslintcache 58 | 59 | # Optional stylelint cache 60 | .stylelintcache 61 | 62 | # Microbundle cache 63 | .rpt2_cache/ 64 | .rts2_cache_cjs/ 65 | .rts2_cache_es/ 66 | .rts2_cache_umd/ 67 | 68 | # Optional REPL history 69 | .node_repl_history 70 | 71 | # Output of 'npm pack' 72 | *.tgz 73 | 74 | # Yarn Integrity file 75 | .yarn-integrity 76 | 77 | # dotenv environment variable files 78 | .env 79 | .env.development.local 80 | .env.test.local 81 | .env.production.local 82 | .env.local 83 | 84 | # parcel-bundler cache (https://parceljs.org/) 85 | .cache 86 | .parcel-cache 87 | 88 | # Next.js build output 89 | .next 90 | out 91 | 92 | # Nuxt.js build / generate output 93 | .nuxt 94 | dist 95 | 96 | # Gatsby files 97 | .cache/ 98 | # Comment in the public line in if your project uses Gatsby and not Next.js 99 | # https://nextjs.org/blog/next-9-1#public-directory-support 100 | # public 101 | 102 | # vuepress build output 103 | .vuepress/dist 104 | 105 | # vuepress v2.x temp and cache directory 106 | .temp 107 | .cache 108 | 109 | # Serverless directories 110 | .serverless/ 111 | 112 | # FuseBox cache 113 | .fusebox/ 114 | 115 | # DynamoDB Local files 116 | .dynamodb/ 117 | 118 | # TernJS port file 119 | .tern-port 120 | 121 | # Stores VSCode versions used for testing VSCode extensions 122 | .vscode-test 123 | 124 | # yarn v2 125 | .yarn/cache 126 | .yarn/unplugged 127 | .yarn/build-state.yml 128 | .yarn/install-state.gz 129 | .pnp.* 130 | 131 | # Project local 132 | *.bin 133 | vid*/ 134 | -------------------------------------------------------------------------------- /converter/.gitignore: -------------------------------------------------------------------------------- 1 | ### https://raw.github.com/github/gitignore/cdde85a36ecb48a162385829d198a7a5bf429a63/Node.gitignore 2 | 3 | # Logs 4 | logs 5 | *.log 6 | npm-debug.log* 7 | yarn-debug.log* 8 | yarn-error.log* 9 | lerna-debug.log* 10 | .pnpm-debug.log* 11 | 12 | # Diagnostic reports (https://nodejs.org/api/report.html) 13 | report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json 14 | 15 | # Runtime data 16 | pids 17 | *.pid 18 | *.seed 19 | *.pid.lock 20 | 21 | # Directory for instrumented libs generated by jscoverage/JSCover 22 | lib-cov 23 | 24 | # Coverage directory used by tools like istanbul 25 | coverage 26 | *.lcov 27 | 28 | # nyc test coverage 29 | .nyc_output 30 | 31 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) 32 | .grunt 33 | 34 | # Bower dependency directory (https://bower.io/) 35 | bower_components 36 | 37 | # node-waf configuration 38 | .lock-wscript 39 | 40 | # Compiled binary addons (https://nodejs.org/api/addons.html) 41 | build/Release 42 | 43 | # Dependency directories 44 | node_modules/ 45 | jspm_packages/ 46 | 47 | # Snowpack dependency directory (https://snowpack.dev/) 48 | web_modules/ 49 | 50 | # TypeScript cache 51 | *.tsbuildinfo 52 | 53 | # Optional npm cache directory 54 | .npm 55 | 56 | # Optional eslint cache 57 | .eslintcache 58 | 59 | # Optional stylelint cache 60 | .stylelintcache 61 | 62 | # Microbundle cache 63 | .rpt2_cache/ 64 | .rts2_cache_cjs/ 65 | .rts2_cache_es/ 66 | .rts2_cache_umd/ 67 | 68 | # Optional REPL history 69 | .node_repl_history 70 | 71 | # Output of 'npm pack' 72 | *.tgz 73 | 74 | # Yarn Integrity file 75 | .yarn-integrity 76 | 77 | # dotenv environment variable files 78 | .env 79 | .env.development.local 80 | .env.test.local 81 | .env.production.local 82 | .env.local 83 | 84 | # parcel-bundler cache (https://parceljs.org/) 85 | .cache 86 | .parcel-cache 87 | 88 | # Next.js build output 89 | .next 90 | out 91 | 92 | # Nuxt.js build / generate output 93 | .nuxt 94 | dist 95 | 96 | # Gatsby files 97 | .cache/ 98 | # Comment in the public line in if your project uses Gatsby and not Next.js 99 | # https://nextjs.org/blog/next-9-1#public-directory-support 100 | # public 101 | 102 | # vuepress build output 103 | .vuepress/dist 104 | 105 | # vuepress v2.x temp and cache directory 106 | .temp 107 | .cache 108 | 109 | # Serverless directories 110 | .serverless/ 111 | 112 | # FuseBox cache 113 | .fusebox/ 114 | 115 | # DynamoDB Local files 116 | .dynamodb/ 117 | 118 | # TernJS port file 119 | .tern-port 120 | 121 | # Stores VSCode versions used for testing VSCode extensions 122 | .vscode-test 123 | 124 | # yarn v2 125 | .yarn/cache 126 | .yarn/unplugged 127 | .yarn/build-state.yml 128 | .yarn/install-state.gz 129 | .pnp.* 130 | 131 | # Project local 132 | *.bin 133 | vid*/ 134 | 135 | *.mp4 136 | -------------------------------------------------------------------------------- /converter/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM node:16-alpine3.15 2 | 3 | RUN apk --no-cache add ffmpeg imagemagick 4 | 5 | ENTRYPOINT [ "ash" ] 6 | -------------------------------------------------------------------------------- /converter/convert.sh: -------------------------------------------------------------------------------- 1 | #! /bin/sh 2 | set -e 3 | 4 | VID_FILE=$1 5 | VID_DIR=vid_$(date +%s) 6 | 7 | usage() { 8 | echo usage: $0 VID_FILE 9 | exit 1 10 | } 11 | 12 | echo VID_FILE=$VID_FILE 13 | 14 | [ -z $VID_FILE ] && usage 15 | [ ! -e $VID_FILE ] && { 16 | echo "VID_FILE not found." 17 | exit 1 18 | } 19 | 20 | command -v ffmpeg && command -v mogrify || { 21 | echo "Please install ffmpeg and ImageMagick." 22 | exit 1 23 | } 24 | 25 | mkdir $VID_DIR 26 | 27 | # Dump frames 28 | ffmpeg -i $VID_FILE -r 1/2 -vf scale=-1:448,crop=600:448 ${VID_DIR}/vid_%06d.png 29 | 30 | # Reduce colors with the palette 31 | mogrify -monitor -dither FloydSteinberg -remap epaper_7c_palette.png ${VID_DIR}/*png 32 | 33 | # Generate .bin files 34 | npm i 35 | node generate-index-bin.js ${VID_DIR} 36 | 37 | echo "Done. You can delete ./${VID_DIR}" 38 | -------------------------------------------------------------------------------- /converter/docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: "3.5" 2 | 3 | services: 4 | converter: 5 | build: . 6 | init: true 7 | volumes: 8 | - ".:/workdir" 9 | user: 1000:1000 10 | working_dir: /workdir 11 | entrypoint: sh ./convert.sh 12 | -------------------------------------------------------------------------------- /converter/epaper_7c_palette.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/likeablob/slow-movie-player-7c/2035753694703946fb785cb00832117055652b49/converter/epaper_7c_palette.png -------------------------------------------------------------------------------- /converter/epaper_7c_palette_captured.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/likeablob/slow-movie-player-7c/2035753694703946fb785cb00832117055652b49/converter/epaper_7c_palette_captured.png -------------------------------------------------------------------------------- /converter/generate-index-bin.js: -------------------------------------------------------------------------------- 1 | const fs = require('fs'); 2 | const _ = require('lodash'); 3 | const path = require("path") 4 | const dir = process.argv[2] || "./vid" 5 | 6 | // Find and sort files. 7 | const files = _.sortBy(fs.readdirSync(dir).filter(v => /.png$/.test(v)), (v) => { 8 | return parseFloat(v.replace(/[^0-9]/g, '')) 9 | }) 10 | 11 | console.log(`${files.length} files found.`); 12 | 13 | // Read all the files. 14 | const bins = files.map((file) => { 15 | console.log(file); 16 | const buff = fs.readFileSync(path.join(dir, file)) 17 | return buff 18 | }) 19 | 20 | // Calculate cumulative sum of file sizes 21 | const offsets = _( 22 | bins.reduce( 23 | (p, c) => { 24 | const pos = _.last(p) 25 | return p.concat(pos + c.length) 26 | }, 27 | [0] 28 | ) 29 | ) 30 | .drop() // Drop [0] 31 | .value() 32 | 33 | // Peak content 34 | console.log("index.bin", Uint32Array.from(offsets)); 35 | 36 | // Dump .bin blobs 37 | fs.writeFileSync("index.bin", Buffer.from(Uint32Array.from(offsets).buffer)); 38 | fs.writeFileSync("images.bin", Buffer.concat(bins)); 39 | -------------------------------------------------------------------------------- /converter/package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "eihen", 3 | "version": "1.0.0", 4 | "lockfileVersion": 2, 5 | "requires": true, 6 | "packages": { 7 | "": { 8 | "name": "eihen", 9 | "version": "1.0.0", 10 | "license": "MIT", 11 | "dependencies": { 12 | "lodash": "^4.17.11" 13 | } 14 | }, 15 | "node_modules/lodash": { 16 | "version": "4.17.21", 17 | "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", 18 | "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" 19 | } 20 | }, 21 | "dependencies": { 22 | "lodash": { 23 | "version": "4.17.21", 24 | "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", 25 | "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /converter/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "eihen", 3 | "version": "1.0.0", 4 | "main": "index.js", 5 | "license": "MIT", 6 | "dependencies": { 7 | "lodash": "^4.17.11" 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /images/smp7c_main.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/likeablob/slow-movie-player-7c/2035753694703946fb785cb00832117055652b49/images/smp7c_main.jpg -------------------------------------------------------------------------------- /include/README: -------------------------------------------------------------------------------- 1 | 2 | This directory is intended for project header files. 3 | 4 | A header file is a file containing C declarations and macro definitions 5 | to be shared between several project source files. You request the use of a 6 | header file in your project source file (C, C++, etc) located in `src` folder 7 | by including it, with the C preprocessing directive `#include'. 8 | 9 | ```src/main.c 10 | 11 | #include "header.h" 12 | 13 | int main (void) 14 | { 15 | ... 16 | } 17 | ``` 18 | 19 | Including a header file produces the same results as copying the header file 20 | into each source file that needs it. Such copying would be time-consuming 21 | and error-prone. With a header file, the related declarations appear 22 | in only one place. If they need to be changed, they can be changed in one 23 | place, and programs that include the header file will automatically use the 24 | new version when next recompiled. The header file eliminates the labor of 25 | finding and changing all the copies as well as the risk that a failure to 26 | find one copy will result in inconsistencies within a program. 27 | 28 | In C, the usual convention is to give header files names that end with `.h'. 29 | It is most portable to use only letters, digits, dashes, and underscores in 30 | header file names, and at most one dot. 31 | 32 | Read more about using header files in official GCC documentation: 33 | 34 | * Include Syntax 35 | * Include Operation 36 | * Once-Only Headers 37 | * Computed Includes 38 | 39 | https://gcc.gnu.org/onlinedocs/cpp/Header-Files.html 40 | -------------------------------------------------------------------------------- /kicad/.gitignore: -------------------------------------------------------------------------------- 1 | ### https://raw.github.com/github/gitignore/cdde85a36ecb48a162385829d198a7a5bf429a63/KiCad.gitignore 2 | 3 | # For PCBs designed using KiCad: https://www.kicad.org/ 4 | # Format documentation: https://kicad.org/help/file-formats/ 5 | 6 | # Temporary files 7 | *.000 8 | *.bak 9 | *.bck 10 | *.kicad_pcb-bak 11 | *.kicad_sch-bak 12 | *-backups 13 | *.kicad_prl 14 | *.sch-bak 15 | *~ 16 | _autosave-* 17 | *.tmp 18 | *-save.pro 19 | *-save.kicad_pcb 20 | fp-info-cache 21 | sym-lib-table 22 | 23 | # Netlist files (exported from Eeschema) 24 | *.net 25 | 26 | # Autorouter files (exported from Pcbnew) 27 | *.dsn 28 | *.ses 29 | 30 | # Exported BOM files 31 | *.xml 32 | *.csv 33 | -------------------------------------------------------------------------------- /kicad/smp7c.kicad_pro: -------------------------------------------------------------------------------- 1 | { 2 | "board": { 3 | "design_settings": { 4 | "defaults": { 5 | "board_outline_line_width": 0.1, 6 | "copper_line_width": 0.2, 7 | "copper_text_size_h": 1.5, 8 | "copper_text_size_v": 1.5, 9 | "copper_text_thickness": 0.3, 10 | "other_line_width": 0.15, 11 | "silk_line_width": 0.15, 12 | "silk_text_size_h": 1.0, 13 | "silk_text_size_v": 1.0, 14 | "silk_text_thickness": 0.15 15 | }, 16 | "diff_pair_dimensions": [], 17 | "drc_exclusions": [], 18 | "rules": { 19 | "min_copper_edge_clearance": 0.0, 20 | "solder_mask_clearance": 0.0, 21 | "solder_mask_min_width": 0.0 22 | }, 23 | "track_widths": [], 24 | "via_dimensions": [] 25 | }, 26 | "layer_presets": [] 27 | }, 28 | "boards": [], 29 | "cvpcb": { 30 | "equivalence_files": [] 31 | }, 32 | "erc": { 33 | "erc_exclusions": [], 34 | "meta": { 35 | "version": 0 36 | }, 37 | "pin_map": [ 38 | [ 39 | 0, 40 | 0, 41 | 0, 42 | 0, 43 | 0, 44 | 0, 45 | 1, 46 | 0, 47 | 0, 48 | 0, 49 | 0, 50 | 2 51 | ], 52 | [ 53 | 0, 54 | 2, 55 | 0, 56 | 1, 57 | 0, 58 | 0, 59 | 1, 60 | 0, 61 | 2, 62 | 2, 63 | 2, 64 | 2 65 | ], 66 | [ 67 | 0, 68 | 0, 69 | 0, 70 | 0, 71 | 0, 72 | 0, 73 | 1, 74 | 0, 75 | 1, 76 | 0, 77 | 1, 78 | 2 79 | ], 80 | [ 81 | 0, 82 | 1, 83 | 0, 84 | 0, 85 | 0, 86 | 0, 87 | 1, 88 | 1, 89 | 2, 90 | 1, 91 | 1, 92 | 2 93 | ], 94 | [ 95 | 0, 96 | 0, 97 | 0, 98 | 0, 99 | 0, 100 | 0, 101 | 1, 102 | 0, 103 | 0, 104 | 0, 105 | 0, 106 | 2 107 | ], 108 | [ 109 | 0, 110 | 0, 111 | 0, 112 | 0, 113 | 0, 114 | 0, 115 | 0, 116 | 0, 117 | 0, 118 | 0, 119 | 0, 120 | 2 121 | ], 122 | [ 123 | 1, 124 | 1, 125 | 1, 126 | 1, 127 | 1, 128 | 0, 129 | 1, 130 | 1, 131 | 1, 132 | 1, 133 | 1, 134 | 2 135 | ], 136 | [ 137 | 0, 138 | 0, 139 | 0, 140 | 1, 141 | 0, 142 | 0, 143 | 1, 144 | 0, 145 | 0, 146 | 0, 147 | 0, 148 | 2 149 | ], 150 | [ 151 | 0, 152 | 2, 153 | 1, 154 | 2, 155 | 0, 156 | 0, 157 | 1, 158 | 0, 159 | 2, 160 | 2, 161 | 2, 162 | 2 163 | ], 164 | [ 165 | 0, 166 | 2, 167 | 0, 168 | 1, 169 | 0, 170 | 0, 171 | 1, 172 | 0, 173 | 2, 174 | 0, 175 | 0, 176 | 2 177 | ], 178 | [ 179 | 0, 180 | 2, 181 | 1, 182 | 1, 183 | 0, 184 | 0, 185 | 1, 186 | 0, 187 | 2, 188 | 0, 189 | 0, 190 | 2 191 | ], 192 | [ 193 | 2, 194 | 2, 195 | 2, 196 | 2, 197 | 2, 198 | 2, 199 | 2, 200 | 2, 201 | 2, 202 | 2, 203 | 2, 204 | 2 205 | ] 206 | ], 207 | "rule_severities": { 208 | "bus_definition_conflict": "error", 209 | "bus_entry_needed": "error", 210 | "bus_label_syntax": "error", 211 | "bus_to_bus_conflict": "error", 212 | "bus_to_net_conflict": "error", 213 | "different_unit_footprint": "error", 214 | "different_unit_net": "error", 215 | "duplicate_reference": "error", 216 | "duplicate_sheet_names": "error", 217 | "extra_units": "error", 218 | "global_label_dangling": "warning", 219 | "hier_label_mismatch": "error", 220 | "label_dangling": "error", 221 | "lib_symbol_issues": "warning", 222 | "multiple_net_names": "warning", 223 | "net_not_bus_member": "warning", 224 | "no_connect_connected": "warning", 225 | "no_connect_dangling": "warning", 226 | "pin_not_connected": "error", 227 | "pin_not_driven": "error", 228 | "pin_to_pin": "warning", 229 | "power_pin_not_driven": "error", 230 | "similar_labels": "warning", 231 | "unannotated": "error", 232 | "unit_value_mismatch": "error", 233 | "unresolved_variable": "error", 234 | "wire_dangling": "error" 235 | } 236 | }, 237 | "libraries": { 238 | "pinned_footprint_libs": [], 239 | "pinned_symbol_libs": [] 240 | }, 241 | "meta": { 242 | "filename": "smp7c.kicad_pro", 243 | "version": 1 244 | }, 245 | "net_settings": { 246 | "classes": [ 247 | { 248 | "bus_width": 12.0, 249 | "clearance": 0.2, 250 | "diff_pair_gap": 0.25, 251 | "diff_pair_via_gap": 0.25, 252 | "diff_pair_width": 0.2, 253 | "line_style": 0, 254 | "microvia_diameter": 0.3, 255 | "microvia_drill": 0.1, 256 | "name": "Default", 257 | "pcb_color": "rgba(0, 0, 0, 0.000)", 258 | "schematic_color": "rgba(0, 0, 0, 0.000)", 259 | "track_width": 0.25, 260 | "via_diameter": 0.8, 261 | "via_drill": 0.4, 262 | "wire_width": 6.0 263 | } 264 | ], 265 | "meta": { 266 | "version": 2 267 | }, 268 | "net_colors": null 269 | }, 270 | "pcbnew": { 271 | "last_paths": { 272 | "gencad": "", 273 | "idf": "", 274 | "netlist": "", 275 | "specctra_dsn": "", 276 | "step": "", 277 | "vrml": "" 278 | }, 279 | "page_layout_descr_file": "" 280 | }, 281 | "schematic": { 282 | "annotate_start_num": 0, 283 | "drawing": { 284 | "default_line_thickness": 6.0, 285 | "default_text_size": 50.0, 286 | "field_names": [], 287 | "intersheets_ref_own_page": false, 288 | "intersheets_ref_prefix": "", 289 | "intersheets_ref_short": false, 290 | "intersheets_ref_show": false, 291 | "intersheets_ref_suffix": "", 292 | "junction_size_choice": 3, 293 | "label_size_ratio": 0.375, 294 | "pin_symbol_size": 25.0, 295 | "text_offset_ratio": 0.15 296 | }, 297 | "legacy_lib_dir": "", 298 | "legacy_lib_list": [], 299 | "meta": { 300 | "version": 1 301 | }, 302 | "net_format_name": "", 303 | "ngspice": { 304 | "fix_include_paths": true, 305 | "fix_passive_vals": false, 306 | "meta": { 307 | "version": 0 308 | }, 309 | "model_mode": 0, 310 | "workbook_filename": "" 311 | }, 312 | "page_layout_descr_file": "", 313 | "plot_directory": "", 314 | "spice_adjust_passive_values": false, 315 | "spice_external_command": "spice \"%I\"", 316 | "subpart_first_id": 65, 317 | "subpart_id_separator": 0 318 | }, 319 | "sheets": [ 320 | [ 321 | "e63e39d7-6ac0-4ffd-8aa3-1841a4541b55", 322 | "" 323 | ] 324 | ], 325 | "text_variables": {} 326 | } 327 | -------------------------------------------------------------------------------- /lib/README: -------------------------------------------------------------------------------- 1 | 2 | This directory is intended for project specific (private) libraries. 3 | PlatformIO will compile them to static libraries and link into executable file. 4 | 5 | The source code of each library should be placed in a an own separate directory 6 | ("lib/your_library_name/[here are source files]"). 7 | 8 | For example, see a structure of the following two libraries `Foo` and `Bar`: 9 | 10 | |--lib 11 | | | 12 | | |--Bar 13 | | | |--docs 14 | | | |--examples 15 | | | |--src 16 | | | |- Bar.c 17 | | | |- Bar.h 18 | | | |- library.json (optional, custom build options, etc) https://docs.platformio.org/page/librarymanager/config.html 19 | | | 20 | | |--Foo 21 | | | |- Foo.c 22 | | | |- Foo.h 23 | | | 24 | | |- README --> THIS FILE 25 | | 26 | |- platformio.ini 27 | |--src 28 | |- main.c 29 | 30 | and a contents of `src/main.c`: 31 | ``` 32 | #include 33 | #include 34 | 35 | int main (void) 36 | { 37 | ... 38 | } 39 | 40 | ``` 41 | 42 | PlatformIO Library Dependency Finder will find automatically dependent 43 | libraries scanning project source files. 44 | 45 | More information about PlatformIO Library Dependency Finder 46 | - https://docs.platformio.org/page/librarymanager/ldf.html 47 | -------------------------------------------------------------------------------- /openscad/.gitignore: -------------------------------------------------------------------------------- 1 | ### https://raw.github.com/github/gitignore/cdde85a36ecb48a162385829d198a7a5bf429a63/Global/Linux.gitignore 2 | 3 | *~ 4 | 5 | # temporary files which can be created if a process still has a handle open of a deleted file 6 | .fuse_hidden* 7 | 8 | # KDE directory preferences 9 | .directory 10 | 11 | # Linux trash folder which might appear on any partition or disk 12 | .Trash-* 13 | 14 | # .nfs files are created when an open file is removed but is still being accessed 15 | .nfs* 16 | 17 | ### https://raw.github.com/github/gitignore/cdde85a36ecb48a162385829d198a7a5bf429a63/Global/VisualStudioCode.gitignore 18 | 19 | .vscode/ 20 | !.vscode/settings.json 21 | !.vscode/tasks.json 22 | !.vscode/launch.json 23 | !.vscode/extensions.json 24 | !.vscode/*.code-snippets 25 | 26 | # Local History for Visual Studio Code 27 | .history/ 28 | 29 | # Built Visual Studio Code Extensions 30 | *.vsix 31 | -------------------------------------------------------------------------------- /openscad/Makefile: -------------------------------------------------------------------------------- 1 | OPENSCAD := openscad 2 | MONTAGE := montage 3 | targets := $(shell ls smp7c_*.scad) 4 | stl_dir := stl 5 | stls := $(targets:%.scad=${stl_dir}/%.stl) 6 | image_dir := images 7 | thumbnails := $(targets:%.scad=${image_dir}/%.png) 8 | img_models := ${image_dir}/models.png 9 | 10 | .PHONY: all clean clean_images images 11 | all: stls images 12 | @echo done 13 | 14 | stls: ${stls} 15 | echo stls succesfully generated 16 | 17 | ${stls}: ${stl_dir}/%.stl: %.scad 18 | @echo Building $@ from $< 19 | ${OPENSCAD} -o $@ $< 20 | 21 | images: $(thumbnails) ${img_models} 22 | @echo images succesfully generated 23 | 24 | $(thumbnails): ${image_dir}/%.png: %.scad 25 | @echo Generating $@ from $< 26 | ${OPENSCAD} -o $@ \ 27 | --imgsize=480,320 --colorscheme=Tomorrow \ 28 | --projection o --viewall --view axes $< 29 | 30 | ${img_models}: ${thumbnails} 31 | @echo Generating $@ from $^ 32 | ${MONTAGE} -label '%t' -pointsize 16 -geometry 480x320 $(sort $^) $@ 33 | 34 | clean: 35 | rm ${thumbnails} 36 | rm ${stls} 37 | 38 | clean_images: 39 | rm ${thumbnails} ${img_models} 40 | 41 | clean_stls: 42 | rm ${stls} 43 | -------------------------------------------------------------------------------- /openscad/README.md: -------------------------------------------------------------------------------- 1 | ![](./images/smp7c_preview.png) 2 | 3 | # SMP7C parts 4 | 5 | ```sh 6 | # Ensure you have submodules 7 | $ git submodule update --init 8 | 9 | # Convert *.scad to *.stl 10 | $ make stls 11 | 12 | # Generate thumbnails 13 | $ make images 14 | ``` 15 | 16 | All the `*.stl` files are available under [`./stl`](./stl). 17 | 18 | ![](./images/models.png) 19 | 20 | ## Recommended print configuration 21 | 22 | - PLA 23 | - Layer: 0.2 mm 24 | 25 | ## Parameters 26 | 27 | See [`_preview_picture_frame_parameters.scad`](./_preview_picture_frame_parameters.scad) for the details. 28 | 29 | ![](./images/picture_frame_parameters.png) 30 | -------------------------------------------------------------------------------- /openscad/_preview_picture_frame_parameters.scad: -------------------------------------------------------------------------------- 1 | include 2 | include 3 | include 4 | include 5 | 6 | $fn=50; 7 | 8 | module cut_half_yz() { 9 | intersection(){ 10 | children(); 11 | 12 | translate([-1000/2, 0, 0]) 13 | cube(size=[1000, 1000, 1000], center=true); 14 | } 15 | } 16 | 17 | // Models 18 | cut_half_yz() 19 | picture_frame_body(); 20 | 21 | translate([0.01, 0, 0]) 22 | color("red", 0.8) 23 | cut_half_yz() 24 | picture_frame_front_layers(); 25 | 26 | translate([0.02, 0, 0]) 27 | color("yellow", 0.8) 28 | cut_half_yz() 29 | picture_frame_back_panel(); 30 | 31 | // Annotations 32 | // on YZ plane 33 | rotate([90, 0, 90]){ 34 | color("black") 35 | translate([PICTURE_FRAME_BODY.y/2 - PICTURE_FRAME_WIDTH/2, 0, 1]){ 36 | translate([0, -2, 0]) 37 | arrow(length=PICTURE_FRAME_WIDTH ,label="PICTURE_FRAME_WIDTH", label_pos_reverse=false); 38 | 39 | translate([PICTURE_FRAME_WIDTH/2 + 2, PICTURE_FRAME_BODY.z/2, 0]) 40 | arrow(length=PICTURE_FRAME_BODY.z, vertical=true, label="PICTURE_FRAME_BODY.z", label_pos_reverse=false); 41 | 42 | translate([-PICTURE_FRAME_WIDTH/2 + PICTURE_FRAME_INNER_EDGE_WIDTH/2, PICTURE_FRAME_BODY.z - 1, 0]) 43 | arrow(length=PICTURE_FRAME_INNER_EDGE_WIDTH, label="PICTURE_FRAME_INNER_EDGE_WIDTH", label_pos_reverse=true); 44 | 45 | translate([-PICTURE_FRAME_WIDTH/2 - 2, PICTURE_FRAME_INNER_SPACE.z/2 + PICTURE_FRAME_WINDOW_Z, 0]) 46 | arrow(length=PICTURE_FRAME_INNER_SPACE.z, vertical=true, label="PICTURE_FRAME_INNER_SPACE.z", label_pos_reverse=true); 47 | } 48 | 49 | translate([2, PICTURE_FRAME_BODY.z + 1, 0]) 50 | color("yellow") 51 | text("BACK_PANEL", size=3); 52 | 53 | translate([2, PICTURE_FRAME_WINDOW_Z + 1, 0]) 54 | color("red") 55 | text("FRONT_LAYERS", size=3); 56 | 57 | color("black") 58 | translate([0, PICTURE_FRAME_BODY.z + 8, 0]) 59 | arrow(length=PICTURE_FRAME_INNER_SPACE.y, vertical=false, label="PICTURE_FRAME_INNER_SPACE.y", label_pos_reverse=true); 60 | } 61 | -------------------------------------------------------------------------------- /openscad/_preview_smp7c_spacer.scad: -------------------------------------------------------------------------------- 1 | include 2 | include 3 | include 4 | 5 | $fn=50; 6 | 7 | 8 | % 9 | translate_to_picture_frame_available_inner_space_zplane(as_origin_plane=true){ 10 | picture_frame_body(); 11 | picture_frame_front_layers(); 12 | // picture_frame_back_panel(); 13 | } 14 | 15 | 16 | translate([0, 0, EPD_MODULE_EPD.z]) 17 | translate_to_epd_visible_area_center(as_center=true) 18 | epd_module_body(); 19 | 20 | smp7c_spacer_body(with_battery_space=true); 21 | -------------------------------------------------------------------------------- /openscad/images/models.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/likeablob/slow-movie-player-7c/2035753694703946fb785cb00832117055652b49/openscad/images/models.png -------------------------------------------------------------------------------- /openscad/images/picture_frame_parameters.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/likeablob/slow-movie-player-7c/2035753694703946fb785cb00832117055652b49/openscad/images/picture_frame_parameters.png -------------------------------------------------------------------------------- /openscad/images/smp7c_battery_spacer.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/likeablob/slow-movie-player-7c/2035753694703946fb785cb00832117055652b49/openscad/images/smp7c_battery_spacer.png -------------------------------------------------------------------------------- /openscad/images/smp7c_preview.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/likeablob/slow-movie-player-7c/2035753694703946fb785cb00832117055652b49/openscad/images/smp7c_preview.png -------------------------------------------------------------------------------- /openscad/images/smp7c_spacer_01_top_left.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/likeablob/slow-movie-player-7c/2035753694703946fb785cb00832117055652b49/openscad/images/smp7c_spacer_01_top_left.png -------------------------------------------------------------------------------- /openscad/images/smp7c_spacer_02_top_right.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/likeablob/slow-movie-player-7c/2035753694703946fb785cb00832117055652b49/openscad/images/smp7c_spacer_02_top_right.png -------------------------------------------------------------------------------- /openscad/images/smp7c_spacer_03_bottom_right.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/likeablob/slow-movie-player-7c/2035753694703946fb785cb00832117055652b49/openscad/images/smp7c_spacer_03_bottom_right.png -------------------------------------------------------------------------------- /openscad/images/smp7c_spacer_04_bottom_left.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/likeablob/slow-movie-player-7c/2035753694703946fb785cb00832117055652b49/openscad/images/smp7c_spacer_04_bottom_left.png -------------------------------------------------------------------------------- /openscad/images/smp7c_stapler_wall_hook.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/likeablob/slow-movie-player-7c/2035753694703946fb785cb00832117055652b49/openscad/images/smp7c_stapler_wall_hook.png -------------------------------------------------------------------------------- /openscad/modules/annotation.scad: -------------------------------------------------------------------------------- 1 | include <../scad-utils/morphology.scad> 2 | 3 | module _triangle(h=6){ 4 | polygon(points=[[-h,0],[0,h/2],[0,-h/2]]); 5 | } 6 | 7 | module arrow(length=20, line_width=0.5, vertical=false, head_size=2, label="", label_pos_reverse=false) { 8 | rotate([0, 0, vertical ? 90 : 0]){ 9 | // head 10 | mirror_x() 11 | translate([-length/2+head_size, 0, 0]) 12 | _triangle(head_size); 13 | 14 | // line 15 | square(size=[length - head_size, line_width], center=true); 16 | } 17 | 18 | // label 19 | if(vertical){ 20 | label_halign=label_pos_reverse ? "right" : "left"; 21 | translate([2 * (label_pos_reverse ? -1 : 1), 0, 0]) 22 | text(label, size=2, valign="center", halign=label_halign); 23 | }else{ 24 | label_halign="center"; 25 | translate([0, 4 * (label_pos_reverse ? 1 : -1), 0]) 26 | text(label, size=2, valign="center", halign=label_halign); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /openscad/modules/epd_565in_acep.scad: -------------------------------------------------------------------------------- 1 | include <../scad-utils/morphology.scad> 2 | 3 | EPD_MODULE_BODY=[138.7, 100.5, 1.6]; 4 | EPD_MODULE_EPD=[114, 86, 1.4]; 5 | EPD_MODULE_EPD_BASE=[125.5, 100.5, EPD_MODULE_EPD.z]; 6 | EPD_MODULE_SPACE_Z=8; 7 | EPD_MODULE_TOTAL_Z=EPD_MODULE_SPACE_Z + EPD_MODULE_EPD.z; 8 | 9 | 10 | module epd_module_holes(d=3) { 11 | center_to_edge=(3/2+1); 12 | 13 | mirror_x() 14 | mirror_y() 15 | translate([EPD_MODULE_BODY.x/2-center_to_edge, EPD_MODULE_BODY.y/2-center_to_edge, 0]) 16 | circle(d=d); 17 | } 18 | 19 | module translate_to_epd_visible_area_center(as_center=false){ 20 | translate([0, (-(EPD_MODULE_BODY.y - EPD_MODULE_EPD.y)/2 + 5.5) * (as_center ? 1 : -1), 0]) 21 | children(0); 22 | } 23 | 24 | module epd_module_body(){ 25 | linear_extrude(height=EPD_MODULE_BODY.z, center=!true, convexity=10, twist=0) 26 | difference() { 27 | rounding(r=1) 28 | square(size=[EPD_MODULE_BODY.x, EPD_MODULE_BODY.y], center=true); 29 | 30 | epd_module_holes(); 31 | } 32 | 33 | // Space for connector, chips, etc. 34 | % 35 | color("orange", 0.3) 36 | linear_extrude(height=EPD_MODULE_SPACE_Z, center=!true, convexity=10, twist=0) 37 | rounding(r=1) 38 | square(size=[EPD_MODULE_BODY.x, EPD_MODULE_BODY.y], center=true); 39 | 40 | // EPD (visible area) 41 | color("lightgreen", 0.5) 42 | translate_to_epd_visible_area_center() 43 | translate([0, 0, -EPD_MODULE_EPD.z/2]) 44 | cube(size=EPD_MODULE_EPD, center=true); 45 | 46 | // EPD base substrate 47 | color("white", 0.5) 48 | translate([0, 0, -EPD_MODULE_EPD.z/2]) 49 | cube(size=EPD_MODULE_EPD_BASE, center=true); 50 | } 51 | -------------------------------------------------------------------------------- /openscad/modules/picture_frame.scad: -------------------------------------------------------------------------------- 1 | include <../scad-utils/morphology.scad> 2 | 3 | PICTURE_FRAME_WIDTH=20; 4 | PICTURE_FRAME_INNER_EDGE_WIDTH=7; 5 | PICTURE_FRAME_WALL_WIDTH=PICTURE_FRAME_WIDTH - PICTURE_FRAME_INNER_EDGE_WIDTH; 6 | 7 | PICTURE_FRAME_INNER_SPACE=[158, 130, 22]; 8 | PICTURE_FRAME_BODY=[PICTURE_FRAME_INNER_SPACE.x + PICTURE_FRAME_WALL_WIDTH*2, PICTURE_FRAME_INNER_SPACE.y + PICTURE_FRAME_WALL_WIDTH*2, 29]; 9 | 10 | PICTURE_FRAME_FRONT_LAYERS_Z=2 + 1.2; // Glass + Sheet 11 | PICTURE_FRAME_BACK_PANEL_Z=3; 12 | PICTURE_FRAME_SHEET_WINDOW=[114, 86]; 13 | 14 | PICTURE_FRAME_WINDOW_Z=PICTURE_FRAME_BODY.z - PICTURE_FRAME_INNER_SPACE.z; 15 | PICTURE_FRAME_INNER_SPACE_AVAILABLE_Z=PICTURE_FRAME_INNER_SPACE.z - (PICTURE_FRAME_FRONT_LAYERS_Z + PICTURE_FRAME_BACK_PANEL_Z); 16 | 17 | echo(str("PICTURE_FRAME_INNER_SPACE_AVAILABLE_Z = ", PICTURE_FRAME_INNER_SPACE_AVAILABLE_Z)); 18 | 19 | 20 | module translate_to_picture_frame_available_inner_space_zplane(as_origin_plane=false){ 21 | translate([0, 0, (PICTURE_FRAME_WINDOW_Z + PICTURE_FRAME_FRONT_LAYERS_Z) * (as_origin_plane ? -1 : 1) ]) 22 | children(); 23 | } 24 | 25 | module picture_frame_body() { 26 | difference(){ 27 | translate([0, 0, PICTURE_FRAME_BODY.z/2]) 28 | cube(size=PICTURE_FRAME_BODY, center=true); 29 | 30 | // window 31 | translate([0, 0, -0.01]) 32 | linear_extrude(height=10 + 0.01, center=!true, convexity=10, twist=0) 33 | inset(d=PICTURE_FRAME_WIDTH) 34 | square(size=[PICTURE_FRAME_BODY.x, PICTURE_FRAME_BODY.y], center=true); 35 | 36 | // inner space 37 | translate([0, 0, PICTURE_FRAME_WINDOW_Z + 0.01]) 38 | translate([0, 0, PICTURE_FRAME_INNER_SPACE.z/2]) 39 | cube(size=PICTURE_FRAME_INNER_SPACE, center=true); 40 | } 41 | } 42 | 43 | module picture_frame_front_layers(){ 44 | color("blue", 0.1) 45 | translate([0, 0, PICTURE_FRAME_WINDOW_Z]) 46 | linear_extrude(height=PICTURE_FRAME_FRONT_LAYERS_Z, center=!true, convexity=10, twist=0) 47 | difference(){ 48 | square(size=[PICTURE_FRAME_INNER_SPACE.x, PICTURE_FRAME_INNER_SPACE.y], center=true); 49 | 50 | // Sheet window 51 | square(size=[PICTURE_FRAME_SHEET_WINDOW.x, PICTURE_FRAME_SHEET_WINDOW.y], center=true); 52 | } 53 | } 54 | 55 | module picture_frame_back_panel(){ 56 | color("yellow", 0.3) 57 | translate([0, 0, PICTURE_FRAME_BODY.z - PICTURE_FRAME_BACK_PANEL_Z]) 58 | linear_extrude(height=PICTURE_FRAME_BACK_PANEL_Z, center=!true, convexity=10, twist=0) 59 | square(size=[PICTURE_FRAME_INNER_SPACE.x, PICTURE_FRAME_INNER_SPACE.y], center=true); 60 | } 61 | -------------------------------------------------------------------------------- /openscad/modules/smp7c_spacer.scad: -------------------------------------------------------------------------------- 1 | include <../scad-utils/morphology.scad> 2 | include <./epd_565in_acep.scad> 3 | include <./picture_frame.scad> 4 | 5 | $fn=50; 6 | 7 | SMP7C_SPACER_BODY=[40, 40, PICTURE_FRAME_INNER_SPACE_AVAILABLE_Z]; 8 | SMP7C_SPACER_EPD_MOUNT_EPD_Z=EPD_MODULE_EPD_BASE.z + EPD_MODULE_BODY.z + 0.6; 9 | SMP7C_SPACER_EPD_MOUNT=[20, 20, SMP7C_SPACER_BODY.z - SMP7C_SPACER_EPD_MOUNT_EPD_Z]; 10 | SMP7C_SPACER_BATTERY_HOLDER = [50 * 3 + 2, 14.5, SMP7C_SPACER_BODY.z+1]; 11 | 12 | 13 | module smp7c_spacer_body_base() { 14 | linear_extrude(height=PICTURE_FRAME_INNER_SPACE_AVAILABLE_Z, center=!true, convexity=10, twist=0) 15 | rounding(r=2) 16 | difference(){ 17 | // Fill inner space 18 | square(size=[PICTURE_FRAME_INNER_SPACE.x, PICTURE_FRAME_INNER_SPACE.y], center=true); 19 | 20 | // Split along x 21 | square(size=[PICTURE_FRAME_INNER_SPACE.x*1.2, PICTURE_FRAME_INNER_SPACE.y - SMP7C_SPACER_BODY.y * 2], center=true); 22 | 23 | // Split along y 24 | square(size=[PICTURE_FRAME_INNER_SPACE.x - SMP7C_SPACER_BODY.x * 2, PICTURE_FRAME_INNER_SPACE.y * 1.2], center=true); 25 | 26 | // Space for EPD 27 | translate_to_epd_visible_area_center(as_center=true) 28 | outset(d=0.5) 29 | square(size=[EPD_MODULE_BODY.x, EPD_MODULE_BODY.y], center=true); 30 | } 31 | } 32 | 33 | module smp7c_spacer_epd_mount_base(){ 34 | epd_mount_edge_width=5; 35 | 36 | rotate([0 , 0, 180]) // -x -y 37 | intersection(){ 38 | rounding(r=2) 39 | union(){ 40 | circle(r=epd_mount_edge_width*2); 41 | difference() { // L shape 42 | scale([2, 2, 1]) 43 | square(size=[SMP7C_SPACER_EPD_MOUNT.x, SMP7C_SPACER_EPD_MOUNT.y], center=true); 44 | 45 | translate([epd_mount_edge_width, epd_mount_edge_width, 0]) 46 | square(size=[SMP7C_SPACER_EPD_MOUNT.x, SMP7C_SPACER_EPD_MOUNT.y], center=false); 47 | } 48 | } 49 | 50 | // Remove unused 51 | translate([-1, -1, 0]) 52 | square(size=[SMP7C_SPACER_EPD_MOUNT.x + 1, SMP7C_SPACER_EPD_MOUNT.y + 1], center=!true); 53 | } 54 | } 55 | 56 | module smp7c_spacer_battery_holder_diff() { 57 | // Holder space 58 | translate([0, PICTURE_FRAME_INNER_SPACE.y/2 - SMP7C_SPACER_BATTERY_HOLDER.y/2 + 0.01, SMP7C_SPACER_BODY.z/2]) 59 | difference() { 60 | cube(size=SMP7C_SPACER_BATTERY_HOLDER, center=true); 61 | 62 | // Hold batteries at the edge 63 | holder_edge=[10, SMP7C_SPACER_BATTERY_HOLDER.y, 5]; 64 | 65 | mirror_x() 66 | translate([SMP7C_SPACER_BATTERY_HOLDER.x/2 - holder_edge.x/2, 0, 0]) 67 | difference() { 68 | translate([0, 0, SMP7C_SPACER_BODY.z/2 - holder_edge.z/2]) 69 | cube(size=[holder_edge.x, SMP7C_SPACER_BATTERY_HOLDER.y, holder_edge.z], center=true); 70 | 71 | rotate([0, 90, 0]) 72 | cylinder(d=14.5, h=holder_edge.x, center=true); 73 | } 74 | } 75 | 76 | // Slots for terminal 77 | mirror_x() 78 | translate([SMP7C_SPACER_BATTERY_HOLDER.x/2, 0, 0]) 79 | translate([0, PICTURE_FRAME_INNER_SPACE.y/2 - SMP7C_SPACER_BATTERY_HOLDER.y/2 , SMP7C_SPACER_BODY.z/2]){ 80 | translate([1, 0, 0]) 81 | cube(size=[0.8, 11, SMP7C_SPACER_BATTERY_HOLDER.z], center=true); 82 | 83 | translate([-1/2 + 1, 0, 0]) 84 | cube(size=[1 + 0.01, 6, SMP7C_SPACER_BATTERY_HOLDER.z], center=true); 85 | } 86 | 87 | // Shrink the slots 88 | mirror_x() 89 | translate([SMP7C_SPACER_BATTERY_HOLDER.x/2 - 5/2 + 1, 0, 5 - SMP7C_SPACER_BATTERY_HOLDER.z]) 90 | translate([0, PICTURE_FRAME_INNER_SPACE.y/2 - SMP7C_SPACER_BATTERY_HOLDER.y/2 , SMP7C_SPACER_BODY.z/2]) 91 | cube(size=[5, 10, SMP7C_SPACER_BATTERY_HOLDER.z], center=true); 92 | } 93 | 94 | module smp7c_spacer_body(with_battery_space=true) { 95 | difference(){ 96 | union(){ 97 | smp7c_spacer_body_base(); 98 | 99 | // EPD mount 100 | translate_to_epd_visible_area_center(as_center=true) 101 | mirror_x() 102 | mirror_y() 103 | translate([EPD_MODULE_BODY.x/2, EPD_MODULE_BODY.y/2, 0]) 104 | translate([0, 0, SMP7C_SPACER_EPD_MOUNT_EPD_Z]) 105 | linear_extrude(height=SMP7C_SPACER_EPD_MOUNT.z, center=!true, convexity=10, twist=0) 106 | smp7c_spacer_epd_mount_base(); 107 | } 108 | 109 | // AAA Battery x3 holder 110 | if(with_battery_space){ 111 | smp7c_spacer_battery_holder_diff(); 112 | } 113 | 114 | // Space in edges 115 | translate_to_epd_visible_area_center(as_center=true) 116 | mirror_x() 117 | mirror_y() 118 | translate([EPD_MODULE_BODY.x/2, EPD_MODULE_BODY.y/2, 0]) 119 | linear_extrude(height=SMP7C_SPACER_EPD_MOUNT_EPD_Z, center=!true, convexity=10, twist=0) 120 | circle(r=4); 121 | 122 | // Screw holes for EPD 123 | translate_to_epd_visible_area_center(as_center=true) 124 | mirror_x() 125 | mirror_y() 126 | linear_extrude(height=10, center=!true, convexity=10, twist=0) 127 | epd_module_holes(d=2); 128 | } 129 | } 130 | 131 | module smp7c_spacer_body_at(edge_pos=0) { 132 | // edge_pos: 0=top left, 1=top right, 2=bottom left, 3=bottom right 133 | intersection(){ 134 | smp7c_spacer_body(); 135 | 136 | rotate([0, 0, 90 * edge_pos]) 137 | scale([1, 1, 2]) 138 | cube(size=PICTURE_FRAME_BODY, center=!true); 139 | } 140 | } 141 | -------------------------------------------------------------------------------- /openscad/smp7c_battery_spacer.scad: -------------------------------------------------------------------------------- 1 | include 2 | 3 | $fn=50; 4 | 5 | 6 | module smp7c_battery_spacer_body() { 7 | difference(){ 8 | cube(size=[80, 14.5, PICTURE_FRAME_INNER_SPACE_AVAILABLE_Z], center=true); 9 | 10 | // Space for batteries 11 | rotate([0, 90, 0]) 12 | cylinder(d=14.5, h=80 + 1, center=true); 13 | 14 | // Slice it 15 | translate([0, 0, 5]) 16 | cube(size=[80 + 1, 14.5 + 1, PICTURE_FRAME_INNER_SPACE_AVAILABLE_Z], center=true); 17 | } 18 | } 19 | 20 | smp7c_battery_spacer_body(); 21 | -------------------------------------------------------------------------------- /openscad/smp7c_spacer_01_top_left.scad: -------------------------------------------------------------------------------- 1 | include 2 | 3 | $fn=50; 4 | 5 | EDGE_POS=0; 6 | 7 | rotate([180, 0, 180]) 8 | smp7c_spacer_body_at(edge_pos=EDGE_POS); 9 | -------------------------------------------------------------------------------- /openscad/smp7c_spacer_02_top_right.scad: -------------------------------------------------------------------------------- 1 | include <./smp7c_spacer_01_top_left.scad> 2 | 3 | EDGE_POS=1; 4 | -------------------------------------------------------------------------------- /openscad/smp7c_spacer_03_bottom_right.scad: -------------------------------------------------------------------------------- 1 | include <./smp7c_spacer_01_top_left.scad> 2 | 3 | EDGE_POS=2; 4 | -------------------------------------------------------------------------------- /openscad/smp7c_spacer_04_bottom_left.scad: -------------------------------------------------------------------------------- 1 | include <./smp7c_spacer_01_top_left.scad> 2 | 3 | EDGE_POS=3; 4 | -------------------------------------------------------------------------------- /openscad/smp7c_stapler_wall_hook.scad: -------------------------------------------------------------------------------- 1 | include 2 | 3 | $fn=50; 4 | 5 | STAPLER_SIZE=10; 6 | WALL_HOOK_BODY=[50, 20, 1]; 7 | 8 | 9 | module smp7c_stapler_wall_hook_stapler_holes() { 10 | mirror_y() 11 | translate([0, STAPLER_SIZE/2, 0]) 12 | circle(d=2); 13 | } 14 | 15 | module smp7c_stapler_wall_hook_body() { 16 | linear_extrude(height=WALL_HOOK_BODY.z, center=!true, convexity=10, twist=0) 17 | difference() { 18 | rounding(r=3) 19 | square(size=[WALL_HOOK_BODY.x, WALL_HOOK_BODY.y], center=true); 20 | 21 | // Stapler holes 22 | mirror_x() 23 | translate([WALL_HOOK_BODY.x/4 + 2, 0, 0]) 24 | smp7c_stapler_wall_hook_stapler_holes(); 25 | 26 | mirror_x() 27 | translate([WALL_HOOK_BODY.x/4 + 7, 0, 0]) 28 | smp7c_stapler_wall_hook_stapler_holes(); 29 | } 30 | 31 | // Hook 32 | wall_hook_width=WALL_HOOK_BODY.x/4; 33 | translate([0, 0, WALL_HOOK_BODY.z]) 34 | hull() { 35 | translate([0, WALL_HOOK_BODY.y/2, 0]) 36 | rotate([0, 90, 0]) 37 | cylinder(d=0.1, h=wall_hook_width, center=true); 38 | 39 | translate([0, WALL_HOOK_BODY.y/2 + 5, 5]) 40 | rotate([0, 90, 0]) 41 | cylinder(d=1, h=wall_hook_width, center=true); 42 | 43 | translate([0, -WALL_HOOK_BODY.y/2, 0]) 44 | rotate([0, 90, 0]) 45 | cylinder(d=0.1, h=wall_hook_width, center=true); 46 | 47 | } 48 | } 49 | 50 | smp7c_stapler_wall_hook_body(); 51 | -------------------------------------------------------------------------------- /openscad/stl/smp7c_battery_spacer.stl: -------------------------------------------------------------------------------- 1 | solid OpenSCAD_Model 2 | facet normal 1 0 0 3 | outer loop 4 | vertex 40 6.56 -3.0869 5 | vertex 40 7.25 -2.9 6 | vertex 40 6.63399 -2.9 7 | endloop 8 | endfacet 9 | facet normal 1 0 0 10 | outer loop 11 | vertex 40 6.12138 -3.88474 12 | vertex 40 7.25 -2.9 13 | vertex 40 6.56 -3.0869 14 | endloop 15 | endfacet 16 | facet normal 1 0 0 17 | outer loop 18 | vertex 40 7.25 -2.9 19 | vertex 40 6.12138 -3.88474 20 | vertex 40 7.25 -7.9 21 | endloop 22 | endfacet 23 | facet normal 1 -0 0 24 | outer loop 25 | vertex 40 5.58622 -4.62132 26 | vertex 40 7.25 -7.9 27 | vertex 40 6.12138 -3.88474 28 | endloop 29 | endfacet 30 | facet normal 1 -0 0 31 | outer loop 32 | vertex 40 4.96297 -5.28502 33 | vertex 40 7.25 -7.9 34 | vertex 40 5.58622 -4.62132 35 | endloop 36 | endfacet 37 | facet normal 1 -0 0 38 | outer loop 39 | vertex 40 4.26144 -5.86537 40 | vertex 40 7.25 -7.9 41 | vertex 40 4.96297 -5.28502 42 | endloop 43 | endfacet 44 | facet normal 1 -0 0 45 | outer loop 46 | vertex 40 3.49271 -6.35322 47 | vertex 40 7.25 -7.9 48 | vertex 40 4.26144 -5.86537 49 | endloop 50 | endfacet 51 | facet normal 1 -0 0 52 | outer loop 53 | vertex 40 2.6689 -6.74088 54 | vertex 40 7.25 -7.9 55 | vertex 40 3.49271 -6.35322 56 | endloop 57 | endfacet 58 | facet normal 1 -0 0 59 | outer loop 60 | vertex 40 1.803 -7.02223 61 | vertex 40 7.25 -7.9 62 | vertex 40 2.6689 -6.74088 63 | endloop 64 | endfacet 65 | facet normal 1 -0 0 66 | outer loop 67 | vertex 40 0.908666 -7.19283 68 | vertex 40 7.25 -7.9 69 | vertex 40 1.803 -7.02223 70 | endloop 71 | endfacet 72 | facet normal 1 -0 0 73 | outer loop 74 | vertex 40 0 -7.25 75 | vertex 40 7.25 -7.9 76 | vertex 40 0.908666 -7.19283 77 | endloop 78 | endfacet 79 | facet normal 1 0 0 80 | outer loop 81 | vertex 40 -7.25 -7.9 82 | vertex 40 0 -7.25 83 | vertex 40 -0.908666 -7.19283 84 | endloop 85 | endfacet 86 | facet normal 1 -0 0 87 | outer loop 88 | vertex 40 -7.25 -2.9 89 | vertex 40 -6.56 -3.0869 90 | vertex 40 -6.63399 -2.9 91 | endloop 92 | endfacet 93 | facet normal 1 0 0 94 | outer loop 95 | vertex 40 -7.25 -2.9 96 | vertex 40 -6.12138 -3.88474 97 | vertex 40 -6.56 -3.0869 98 | endloop 99 | endfacet 100 | facet normal 1 0 0 101 | outer loop 102 | vertex 40 -7.25 -7.9 103 | vertex 40 -6.12138 -3.88474 104 | vertex 40 -7.25 -2.9 105 | endloop 106 | endfacet 107 | facet normal 1 0 0 108 | outer loop 109 | vertex 40 -6.12138 -3.88474 110 | vertex 40 -7.25 -7.9 111 | vertex 40 -5.58622 -4.62132 112 | endloop 113 | endfacet 114 | facet normal 1 0 0 115 | outer loop 116 | vertex 40 0 -7.25 117 | vertex 40 -7.25 -7.9 118 | vertex 40 7.25 -7.9 119 | endloop 120 | endfacet 121 | facet normal 1 0 0 122 | outer loop 123 | vertex 40 -1.803 -7.02223 124 | vertex 40 -7.25 -7.9 125 | vertex 40 -0.908666 -7.19283 126 | endloop 127 | endfacet 128 | facet normal 1 0 0 129 | outer loop 130 | vertex 40 -2.6689 -6.74088 131 | vertex 40 -7.25 -7.9 132 | vertex 40 -1.803 -7.02223 133 | endloop 134 | endfacet 135 | facet normal 1 0 0 136 | outer loop 137 | vertex 40 -3.49271 -6.35322 138 | vertex 40 -7.25 -7.9 139 | vertex 40 -2.6689 -6.74088 140 | endloop 141 | endfacet 142 | facet normal 1 0 0 143 | outer loop 144 | vertex 40 -4.26144 -5.86537 145 | vertex 40 -7.25 -7.9 146 | vertex 40 -3.49271 -6.35322 147 | endloop 148 | endfacet 149 | facet normal 1 0 0 150 | outer loop 151 | vertex 40 -4.96297 -5.28502 152 | vertex 40 -7.25 -7.9 153 | vertex 40 -4.26144 -5.86537 154 | endloop 155 | endfacet 156 | facet normal 1 0 0 157 | outer loop 158 | vertex 40 -5.58622 -4.62132 159 | vertex 40 -7.25 -7.9 160 | vertex 40 -4.96297 -5.28502 161 | endloop 162 | endfacet 163 | facet normal 0 0 -1 164 | outer loop 165 | vertex -40 -7.25 -7.9 166 | vertex 40 7.25 -7.9 167 | vertex 40 -7.25 -7.9 168 | endloop 169 | endfacet 170 | facet normal -0 0 -1 171 | outer loop 172 | vertex 40 7.25 -7.9 173 | vertex -40 -7.25 -7.9 174 | vertex -40 7.25 -7.9 175 | endloop 176 | endfacet 177 | facet normal -1 0 0 178 | outer loop 179 | vertex -40 6.12138 -3.88474 180 | vertex -40 7.25 -2.9 181 | vertex -40 7.25 -7.9 182 | endloop 183 | endfacet 184 | facet normal -1 -0 0 185 | outer loop 186 | vertex -40 7.25 -2.9 187 | vertex -40 6.56 -3.0869 188 | vertex -40 6.63399 -2.9 189 | endloop 190 | endfacet 191 | facet normal -1 0 0 192 | outer loop 193 | vertex -40 7.25 -2.9 194 | vertex -40 6.12138 -3.88474 195 | vertex -40 6.56 -3.0869 196 | endloop 197 | endfacet 198 | facet normal -1 0 0 199 | outer loop 200 | vertex -40 7.25 -7.9 201 | vertex -40 5.58622 -4.62132 202 | vertex -40 6.12138 -3.88474 203 | endloop 204 | endfacet 205 | facet normal -1 0 0 206 | outer loop 207 | vertex -40 7.25 -7.9 208 | vertex -40 4.96297 -5.28502 209 | vertex -40 5.58622 -4.62132 210 | endloop 211 | endfacet 212 | facet normal -1 0 0 213 | outer loop 214 | vertex -40 7.25 -7.9 215 | vertex -40 4.26144 -5.86537 216 | vertex -40 4.96297 -5.28502 217 | endloop 218 | endfacet 219 | facet normal -1 0 0 220 | outer loop 221 | vertex -40 7.25 -7.9 222 | vertex -40 3.49271 -6.35322 223 | vertex -40 4.26144 -5.86537 224 | endloop 225 | endfacet 226 | facet normal -1 0 0 227 | outer loop 228 | vertex -40 7.25 -7.9 229 | vertex -40 2.6689 -6.74088 230 | vertex -40 3.49271 -6.35322 231 | endloop 232 | endfacet 233 | facet normal -1 0 0 234 | outer loop 235 | vertex -40 7.25 -7.9 236 | vertex -40 1.803 -7.02223 237 | vertex -40 2.6689 -6.74088 238 | endloop 239 | endfacet 240 | facet normal -1 0 0 241 | outer loop 242 | vertex -40 7.25 -7.9 243 | vertex -40 0.908666 -7.19283 244 | vertex -40 1.803 -7.02223 245 | endloop 246 | endfacet 247 | facet normal -1 0 0 248 | outer loop 249 | vertex -40 7.25 -7.9 250 | vertex -40 0 -7.25 251 | vertex -40 0.908666 -7.19283 252 | endloop 253 | endfacet 254 | facet normal -1 0 0 255 | outer loop 256 | vertex -40 -7.25 -7.9 257 | vertex -40 0 -7.25 258 | vertex -40 7.25 -7.9 259 | endloop 260 | endfacet 261 | facet normal -1 -0 0 262 | outer loop 263 | vertex -40 0 -7.25 264 | vertex -40 -7.25 -7.9 265 | vertex -40 -0.908666 -7.19283 266 | endloop 267 | endfacet 268 | facet normal -1 0 0 269 | outer loop 270 | vertex -40 -7.25 -7.9 271 | vertex -40 -1.803 -7.02223 272 | vertex -40 -0.908666 -7.19283 273 | endloop 274 | endfacet 275 | facet normal -1 0 0 276 | outer loop 277 | vertex -40 -7.25 -7.9 278 | vertex -40 -2.6689 -6.74088 279 | vertex -40 -1.803 -7.02223 280 | endloop 281 | endfacet 282 | facet normal -1 0 0 283 | outer loop 284 | vertex -40 -7.25 -7.9 285 | vertex -40 -3.49271 -6.35322 286 | vertex -40 -2.6689 -6.74088 287 | endloop 288 | endfacet 289 | facet normal -1 0 0 290 | outer loop 291 | vertex -40 -7.25 -7.9 292 | vertex -40 -4.26144 -5.86537 293 | vertex -40 -3.49271 -6.35322 294 | endloop 295 | endfacet 296 | facet normal -1 0 0 297 | outer loop 298 | vertex -40 -7.25 -7.9 299 | vertex -40 -4.96297 -5.28502 300 | vertex -40 -4.26144 -5.86537 301 | endloop 302 | endfacet 303 | facet normal -1 0 0 304 | outer loop 305 | vertex -40 -7.25 -7.9 306 | vertex -40 -5.58622 -4.62132 307 | vertex -40 -4.96297 -5.28502 308 | endloop 309 | endfacet 310 | facet normal -1 0 0 311 | outer loop 312 | vertex -40 -7.25 -7.9 313 | vertex -40 -6.12138 -3.88474 314 | vertex -40 -5.58622 -4.62132 315 | endloop 316 | endfacet 317 | facet normal -1 0 0 318 | outer loop 319 | vertex -40 -7.25 -2.9 320 | vertex -40 -6.12138 -3.88474 321 | vertex -40 -7.25 -7.9 322 | endloop 323 | endfacet 324 | facet normal -1 0 0 325 | outer loop 326 | vertex -40 -6.12138 -3.88474 327 | vertex -40 -7.25 -2.9 328 | vertex -40 -6.56 -3.0869 329 | endloop 330 | endfacet 331 | facet normal -1 0 0 332 | outer loop 333 | vertex -40 -6.56 -3.0869 334 | vertex -40 -7.25 -2.9 335 | vertex -40 -6.63399 -2.9 336 | endloop 337 | endfacet 338 | facet normal 0 1 -0 339 | outer loop 340 | vertex 40 7.25 -7.9 341 | vertex -40 7.25 -2.9 342 | vertex 40 7.25 -2.9 343 | endloop 344 | endfacet 345 | facet normal 0 1 0 346 | outer loop 347 | vertex -40 7.25 -2.9 348 | vertex 40 7.25 -7.9 349 | vertex -40 7.25 -7.9 350 | endloop 351 | endfacet 352 | facet normal 0 -1 0 353 | outer loop 354 | vertex -40 -7.25 -7.9 355 | vertex 40 -7.25 -2.9 356 | vertex -40 -7.25 -2.9 357 | endloop 358 | endfacet 359 | facet normal 0 -1 -0 360 | outer loop 361 | vertex 40 -7.25 -2.9 362 | vertex -40 -7.25 -7.9 363 | vertex 40 -7.25 -7.9 364 | endloop 365 | endfacet 366 | facet normal 0 0.876305 0.481757 367 | outer loop 368 | vertex 40 -6.12138 -3.88474 369 | vertex -40 -6.56 -3.0869 370 | vertex 40 -6.56 -3.0869 371 | endloop 372 | endfacet 373 | facet normal 0 0.876305 0.481757 374 | outer loop 375 | vertex -40 -6.56 -3.0869 376 | vertex 40 -6.12138 -3.88474 377 | vertex -40 -6.12138 -3.88474 378 | endloop 379 | endfacet 380 | facet normal 0 -0.309019 0.951056 381 | outer loop 382 | vertex -40 2.6689 -6.74088 383 | vertex 40 1.803 -7.02223 384 | vertex 40 2.6689 -6.74088 385 | endloop 386 | endfacet 387 | facet normal 0 -0.309019 0.951056 388 | outer loop 389 | vertex 40 1.803 -7.02223 390 | vertex -40 2.6689 -6.74088 391 | vertex -40 1.803 -7.02223 392 | endloop 393 | endfacet 394 | facet normal -0 0.0627922 0.998027 395 | outer loop 396 | vertex -40 0 -7.25 397 | vertex 40 -0.908666 -7.19283 398 | vertex 40 0 -7.25 399 | endloop 400 | endfacet 401 | facet normal 0 0.0627922 0.998027 402 | outer loop 403 | vertex 40 -0.908666 -7.19283 404 | vertex -40 0 -7.25 405 | vertex -40 -0.908666 -7.19283 406 | endloop 407 | endfacet 408 | facet normal 0 -0.929792 0.368086 409 | outer loop 410 | vertex -40 6.56 -3.0869 411 | vertex 40 6.63399 -2.9 412 | vertex -40 6.63399 -2.9 413 | endloop 414 | endfacet 415 | facet normal 0 -0.929792 0.368086 416 | outer loop 417 | vertex 40 6.63399 -2.9 418 | vertex -40 6.56 -3.0869 419 | vertex 40 6.56 -3.0869 420 | endloop 421 | endfacet 422 | facet normal 0 -0.425783 0.904825 423 | outer loop 424 | vertex -40 3.49271 -6.35322 425 | vertex 40 2.6689 -6.74088 426 | vertex 40 3.49271 -6.35322 427 | endloop 428 | endfacet 429 | facet normal 0 -0.425783 0.904825 430 | outer loop 431 | vertex 40 2.6689 -6.74088 432 | vertex -40 3.49271 -6.35322 433 | vertex -40 2.6689 -6.74088 434 | endloop 435 | endfacet 436 | facet normal 0 -0.0627922 0.998027 437 | outer loop 438 | vertex -40 0.908666 -7.19283 439 | vertex 40 0 -7.25 440 | vertex 40 0.908666 -7.19283 441 | endloop 442 | endfacet 443 | facet normal 0 -0.0627922 0.998027 444 | outer loop 445 | vertex 40 0 -7.25 446 | vertex -40 0.908666 -7.19283 447 | vertex -40 0 -7.25 448 | endloop 449 | endfacet 450 | facet normal 0 0.929792 0.368086 451 | outer loop 452 | vertex 40 -6.56 -3.0869 453 | vertex -40 -6.63399 -2.9 454 | vertex 40 -6.63399 -2.9 455 | endloop 456 | endfacet 457 | facet normal 0 0.929792 0.368086 458 | outer loop 459 | vertex -40 -6.63399 -2.9 460 | vertex 40 -6.56 -3.0869 461 | vertex -40 -6.56 -3.0869 462 | endloop 463 | endfacet 464 | facet normal -0 0.309019 0.951056 465 | outer loop 466 | vertex -40 -1.803 -7.02223 467 | vertex 40 -2.6689 -6.74088 468 | vertex 40 -1.803 -7.02223 469 | endloop 470 | endfacet 471 | facet normal 0 0.309019 0.951056 472 | outer loop 473 | vertex 40 -2.6689 -6.74088 474 | vertex -40 -1.803 -7.02223 475 | vertex -40 -2.6689 -6.74088 476 | endloop 477 | endfacet 478 | facet normal 0 -0.876305 0.481757 479 | outer loop 480 | vertex -40 6.12138 -3.88474 481 | vertex 40 6.56 -3.0869 482 | vertex -40 6.56 -3.0869 483 | endloop 484 | endfacet 485 | facet normal 0 -0.876305 0.481757 486 | outer loop 487 | vertex 40 6.56 -3.0869 488 | vertex -40 6.12138 -3.88474 489 | vertex 40 6.12138 -3.88474 490 | endloop 491 | endfacet 492 | facet normal -0 0.425783 0.904825 493 | outer loop 494 | vertex -40 -2.6689 -6.74088 495 | vertex 40 -3.49271 -6.35322 496 | vertex 40 -2.6689 -6.74088 497 | endloop 498 | endfacet 499 | facet normal 0 0.425783 0.904825 500 | outer loop 501 | vertex 40 -3.49271 -6.35322 502 | vertex -40 -2.6689 -6.74088 503 | vertex -40 -3.49271 -6.35322 504 | endloop 505 | endfacet 506 | facet normal -0 0.187378 0.982288 507 | outer loop 508 | vertex -40 -0.908666 -7.19283 509 | vertex 40 -1.803 -7.02223 510 | vertex 40 -0.908666 -7.19283 511 | endloop 512 | endfacet 513 | facet normal 0 0.187378 0.982288 514 | outer loop 515 | vertex 40 -1.803 -7.02223 516 | vertex -40 -0.908666 -7.19283 517 | vertex -40 -1.803 -7.02223 518 | endloop 519 | endfacet 520 | facet normal 0 -0.809015 0.587788 521 | outer loop 522 | vertex -40 5.58622 -4.62132 523 | vertex 40 6.12138 -3.88474 524 | vertex -40 6.12138 -3.88474 525 | endloop 526 | endfacet 527 | facet normal 0 -0.809015 0.587788 528 | outer loop 529 | vertex 40 6.12138 -3.88474 530 | vertex -40 5.58622 -4.62132 531 | vertex 40 5.58622 -4.62132 532 | endloop 533 | endfacet 534 | facet normal 0 -0.728972 0.684544 535 | outer loop 536 | vertex -40 4.96297 -5.28502 537 | vertex 40 5.58622 -4.62132 538 | vertex -40 5.58622 -4.62132 539 | endloop 540 | endfacet 541 | facet normal 0 -0.728972 0.684544 542 | outer loop 543 | vertex 40 5.58622 -4.62132 544 | vertex -40 4.96297 -5.28502 545 | vertex 40 4.96297 -5.28502 546 | endloop 547 | endfacet 548 | facet normal 0 -0.187378 0.982288 549 | outer loop 550 | vertex -40 1.803 -7.02223 551 | vertex 40 0.908666 -7.19283 552 | vertex 40 1.803 -7.02223 553 | endloop 554 | endfacet 555 | facet normal 0 -0.187378 0.982288 556 | outer loop 557 | vertex 40 0.908666 -7.19283 558 | vertex -40 1.803 -7.02223 559 | vertex -40 0.908666 -7.19283 560 | endloop 561 | endfacet 562 | facet normal -0 0.535826 0.844328 563 | outer loop 564 | vertex -40 -3.49271 -6.35322 565 | vertex 40 -4.26144 -5.86537 566 | vertex 40 -3.49271 -6.35322 567 | endloop 568 | endfacet 569 | facet normal 0 0.535826 0.844328 570 | outer loop 571 | vertex 40 -4.26144 -5.86537 572 | vertex -40 -3.49271 -6.35322 573 | vertex -40 -4.26144 -5.86537 574 | endloop 575 | endfacet 576 | facet normal -0 0.63742 0.770517 577 | outer loop 578 | vertex -40 -4.26144 -5.86537 579 | vertex 40 -4.96297 -5.28502 580 | vertex 40 -4.26144 -5.86537 581 | endloop 582 | endfacet 583 | facet normal 0 0.63742 0.770517 584 | outer loop 585 | vertex 40 -4.96297 -5.28502 586 | vertex -40 -4.26144 -5.86537 587 | vertex -40 -4.96297 -5.28502 588 | endloop 589 | endfacet 590 | facet normal 0 -0.535826 0.844328 591 | outer loop 592 | vertex -40 4.26144 -5.86537 593 | vertex 40 3.49271 -6.35322 594 | vertex 40 4.26144 -5.86537 595 | endloop 596 | endfacet 597 | facet normal 0 -0.535826 0.844328 598 | outer loop 599 | vertex 40 3.49271 -6.35322 600 | vertex -40 4.26144 -5.86537 601 | vertex -40 3.49271 -6.35322 602 | endloop 603 | endfacet 604 | facet normal 0 0.728972 0.684544 605 | outer loop 606 | vertex 40 -4.96297 -5.28502 607 | vertex -40 -5.58622 -4.62132 608 | vertex 40 -5.58622 -4.62132 609 | endloop 610 | endfacet 611 | facet normal 0 0.728972 0.684544 612 | outer loop 613 | vertex -40 -5.58622 -4.62132 614 | vertex 40 -4.96297 -5.28502 615 | vertex -40 -4.96297 -5.28502 616 | endloop 617 | endfacet 618 | facet normal 0 -0.63742 0.770517 619 | outer loop 620 | vertex -40 4.96297 -5.28502 621 | vertex 40 4.26144 -5.86537 622 | vertex 40 4.96297 -5.28502 623 | endloop 624 | endfacet 625 | facet normal 0 -0.63742 0.770517 626 | outer loop 627 | vertex 40 4.26144 -5.86537 628 | vertex -40 4.96297 -5.28502 629 | vertex -40 4.26144 -5.86537 630 | endloop 631 | endfacet 632 | facet normal 0 0.809015 0.587788 633 | outer loop 634 | vertex 40 -5.58622 -4.62132 635 | vertex -40 -6.12138 -3.88474 636 | vertex 40 -6.12138 -3.88474 637 | endloop 638 | endfacet 639 | facet normal 0 0.809015 0.587788 640 | outer loop 641 | vertex -40 -6.12138 -3.88474 642 | vertex 40 -5.58622 -4.62132 643 | vertex -40 -5.58622 -4.62132 644 | endloop 645 | endfacet 646 | facet normal -0 0 1 647 | outer loop 648 | vertex -40 7.25 -2.9 649 | vertex 40 6.63399 -2.9 650 | vertex 40 7.25 -2.9 651 | endloop 652 | endfacet 653 | facet normal 0 0 1 654 | outer loop 655 | vertex 40 6.63399 -2.9 656 | vertex -40 7.25 -2.9 657 | vertex -40 6.63399 -2.9 658 | endloop 659 | endfacet 660 | facet normal -0 0 1 661 | outer loop 662 | vertex -40 -6.63399 -2.9 663 | vertex 40 -7.25 -2.9 664 | vertex 40 -6.63399 -2.9 665 | endloop 666 | endfacet 667 | facet normal 0 0 1 668 | outer loop 669 | vertex 40 -7.25 -2.9 670 | vertex -40 -6.63399 -2.9 671 | vertex -40 -7.25 -2.9 672 | endloop 673 | endfacet 674 | endsolid OpenSCAD_Model 675 | -------------------------------------------------------------------------------- /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 | [env:default] 12 | platform = espressif32@3.5.0 13 | board = lolin32_lite 14 | framework = arduino 15 | upload_speed = 921600 16 | monitor_speed = 115200 17 | monitor_filters = esp32_exception_decoder 18 | ; board_build.flash_mode = qio 19 | ; board_build.f_flash = 80000000L 20 | ; build_flags = -DSMP7C_RANDOM_MODE 21 | lib_deps = 22 | https://github.com/adafruit/Adafruit_BusIO#1.1.0 23 | https://github.com/adafruit/Adafruit-GFX-Library#1.10.12 24 | https://github.com/ZinggJM/GxEPD2#1.4.5 25 | https://github.com/kikuchan/pngle#v1.0.0 26 | https://github.com/likeablob/ulptool-pio#pio/0.1.0 27 | 28 | extra_scripts = 29 | pre:/$PROJECT_LIBDEPS_DIR/$PIOENV/ulptool-pio/pre_extra_script_ulptool.py 30 | post:/$PROJECT_LIBDEPS_DIR/$PIOENV/ulptool-pio/post_extra_script_ulptool.py 31 | -------------------------------------------------------------------------------- /src/GxEPD2_565c_mod.cpp: -------------------------------------------------------------------------------- 1 | // Display Library for SPI e-paper panels from Dalian Good Display and boards 2 | // from Waveshare. Requires HW SPI and Adafruit_GFX. Caution: the e-paper panels 3 | // require 3.3V supply AND data lines! 4 | // 5 | // based on Demo Example from Good Display: 6 | // http://www.e-paper-display.com/download_detail/downloadsId=808.html 7 | // Panel: 5.65inch ACeP 7-Color E-Paper : 8 | // https://www.waveshare.com/product/displays/e-paper/5.65inch-e-paper-module-f.htm 9 | // Controller: unknonw 10 | // initcode extracted from Waveshare library file epd5in65f.cpp available here: 11 | // https://www.waveshare.com/wiki/5.65inch_e-Paper_Module_(F) 12 | // 13 | // Author: Jean-Marc Zingg 14 | // 15 | // Version: see library.properties 16 | // 17 | // Library: https://github.com/ZinggJM/GxEPD2 18 | // 19 | // Modified by likeablob for SMP7C. Removed busy wait handling. 20 | 21 | #include "GxEPD2_565c_mod.h" 22 | 23 | GxEPD2_565c::GxEPD2_565c(int16_t cs, int16_t dc, int16_t rst, int16_t busy) 24 | : GxEPD2_EPD(cs, dc, rst, busy, LOW, 25000000, WIDTH, HEIGHT, panel, 25 | hasColor, hasPartialUpdate, hasFastPartialUpdate) { 26 | _paged = false; 27 | } 28 | 29 | void GxEPD2_565c::clearScreen(uint8_t value) { clearScreen(value, 0xFF); } 30 | 31 | void GxEPD2_565c::clearScreen(uint8_t black_value, uint8_t color_value) { 32 | writeScreenBuffer(black_value, color_value); 33 | _Update_Full(); 34 | } 35 | 36 | void GxEPD2_565c::writeScreenBuffer(uint8_t value) { 37 | writeScreenBuffer(value, 0xFF); 38 | } 39 | 40 | void GxEPD2_565c::writeScreenBuffer(uint8_t black_value, uint8_t color_value) { 41 | _initial_write = false; // initial full screen buffer clean done 42 | _Init_Full(); 43 | _writeCommand(0x10); 44 | _startTransfer(); 45 | for(uint32_t i = 0; i < uint32_t(WIDTH) * uint32_t(HEIGHT) / 2; i++) { 46 | //_transfer(0x11); 47 | _transfer(0xFF == black_value ? 0x11 : black_value); 48 | } 49 | _endTransfer(); 50 | } 51 | 52 | void GxEPD2_565c::writeImage(const uint8_t bitmap[], int16_t x, int16_t y, 53 | int16_t w, int16_t h, bool invert, bool mirror_y, 54 | bool pgm) { 55 | // Serial.print("writeImage("); Serial.print(x); Serial.print(", "); 56 | // Serial.print(y); Serial.print(", "); Serial.print(w); Serial.print(", "); 57 | // Serial.print(h); Serial.println(")"); 58 | delay(1); // yield() to avoid WDT on ESP8266 and ESP32 59 | if(_paged && (x == 0) && (w == int16_t(WIDTH)) && (h < int16_t(HEIGHT))) { 60 | // Serial.println("paged"); 61 | _startTransfer(); 62 | for(uint32_t i = 0; i < uint32_t(WIDTH) * uint32_t(h) / 8; i++) { 63 | uint8_t data = bitmap[i]; 64 | for(int16_t k = 0; k < 4; k++) { 65 | uint8_t data2 = 66 | (data & 0x80 ? 0x10 : 0x00) | (data & 0x40 ? 0x01 : 0x00); 67 | data <<= 2; 68 | _transfer(data2); 69 | } 70 | } 71 | _endTransfer(); 72 | if(y + h == HEIGHT) // last page 73 | { 74 | // Serial.println("paged ended"); 75 | _paged = false; 76 | } 77 | } else { 78 | _paged = false; 79 | int16_t wb = (w + 7) / 8; // width bytes, bitmaps are padded 80 | x -= x % 8; // byte boundary 81 | w = wb * 8; // byte boundary 82 | if((w <= 0) || (h <= 0)) 83 | return; 84 | _Init_Full(); 85 | _writeCommand(0x10); 86 | _startTransfer(); 87 | for(int16_t i = 0; i < int16_t(HEIGHT); i++) { 88 | for(int16_t j = 0; j < int16_t(WIDTH); j += 8) { 89 | uint8_t data = 0xFF; 90 | if((j >= x) && (j <= x + w) && (i >= y) && (i < y + h)) { 91 | uint32_t idx = 92 | mirror_y 93 | ? (j - x) / 8 + uint32_t((h - 1 - (i - y))) * wb 94 | : (j - x) / 8 + uint32_t(i - y) * wb; 95 | if(pgm) { 96 | #if defined(__AVR) || defined(ESP8266) || defined(ESP32) 97 | data = pgm_read_byte(&bitmap[idx]); 98 | #else 99 | data = bitmap[idx]; 100 | #endif 101 | } else { 102 | data = bitmap[idx]; 103 | } 104 | if(invert) 105 | data = ~data; 106 | } 107 | for(int16_t k = 0; k < 4; k++) { 108 | uint8_t data2 = (data & 0x80 ? 0x10 : 0x00) | 109 | (data & 0x40 ? 0x01 : 0x00); 110 | data <<= 2; 111 | _transfer(data2); 112 | } 113 | } 114 | } 115 | _endTransfer(); 116 | } 117 | delay(1); // yield() to avoid WDT on ESP8266 and ESP32 118 | } 119 | 120 | void GxEPD2_565c::writeImage(const uint8_t *black, const uint8_t *color, 121 | int16_t x, int16_t y, int16_t w, int16_t h, 122 | bool invert, bool mirror_y, bool pgm) { 123 | if(!black && !color) 124 | return; 125 | if(!color) 126 | return writeImage(black, x, y, w, h, invert, mirror_y, pgm); 127 | // Serial.print("writeImage("); Serial.print(x); Serial.print(", "); 128 | // Serial.print(y); Serial.print(", "); Serial.print(w); Serial.print(", "); 129 | // Serial.print(h); Serial.println(")"); 130 | delay(1); // yield() to avoid WDT on ESP8266 and ESP32 131 | if(_paged && (x == 0) && (w == int16_t(WIDTH)) && (h < int16_t(HEIGHT))) { 132 | // Serial.println("paged"); 133 | _startTransfer(); 134 | for(uint32_t i = 0; i < uint32_t(WIDTH) * uint32_t(h) / 8; i++) { 135 | uint8_t black_data = black[i]; 136 | uint8_t color_data = color[i]; 137 | for(int16_t k = 0; k < 4; k++) { 138 | uint8_t out_data = 0x00; 139 | for(int16_t l = 0; l < 2; l++) { 140 | out_data <<= 4; 141 | if(!(color_data & 0x80)) 142 | out_data |= 0x04; 143 | else 144 | out_data |= black_data & 0x80 ? 0x01 : 0x00; 145 | black_data <<= 1; 146 | color_data <<= 1; 147 | } 148 | _transfer(out_data); 149 | } 150 | } 151 | _endTransfer(); 152 | if(y + h == HEIGHT) // last page 153 | { 154 | // Serial.println("paged ended"); 155 | _paged = false; 156 | } 157 | } else { 158 | _paged = false; 159 | int16_t wb = (w + 7) / 8; // width bytes, bitmaps are padded 160 | x -= x % 8; // byte boundary 161 | w = wb * 8; // byte boundary 162 | if((w <= 0) || (h <= 0)) 163 | return; 164 | _Init_Full(); 165 | _writeCommand(0x10); 166 | _startTransfer(); 167 | for(int16_t i = 0; i < int16_t(HEIGHT); i++) { 168 | for(int16_t j = 0; j < int16_t(WIDTH); j += 8) { 169 | uint8_t black_data = 0xFF, color_data = 0xFF; 170 | if((j >= x) && (j < x + w) && (i >= y) && (i < y + h)) { 171 | uint32_t idx = 172 | mirror_y 173 | ? (j - x) / 8 + uint32_t((h - 1 - (i - y))) * wb 174 | : (j - x) / 8 + uint32_t(i - y) * wb; 175 | if(pgm) { 176 | #if defined(__AVR) || defined(ESP8266) || defined(ESP32) 177 | black_data = pgm_read_byte(&black[idx]); 178 | color_data = pgm_read_byte(&color[idx]); 179 | #else 180 | black_data = black[idx]; 181 | color_data = color[idx]; 182 | #endif 183 | } else { 184 | black_data = black[idx]; 185 | color_data = color[idx]; 186 | } 187 | if(invert) { 188 | black_data = ~black_data; 189 | color_data = ~color_data; 190 | } 191 | } 192 | for(int16_t k = 0; k < 4; k++) { 193 | uint8_t out_data = 0x00; 194 | for(int16_t l = 0; l < 2; l++) { 195 | out_data <<= 4; 196 | if(!(color_data & 0x80)) 197 | out_data |= 0x04; 198 | else 199 | out_data |= black_data & 0x80 ? 0x01 : 0x00; 200 | black_data <<= 1; 201 | color_data <<= 1; 202 | } 203 | _transfer(out_data); 204 | } 205 | } 206 | } 207 | _endTransfer(); 208 | } 209 | delay(1); // yield() to avoid WDT on ESP8266 and ESP32 210 | } 211 | 212 | void GxEPD2_565c::writeImagePart(const uint8_t bitmap[], int16_t x_part, 213 | int16_t y_part, int16_t w_bitmap, 214 | int16_t h_bitmap, int16_t x, int16_t y, 215 | int16_t w, int16_t h, bool invert, 216 | bool mirror_y, bool pgm) { 217 | delay(1); // yield() to avoid WDT on ESP8266 and ESP32 218 | if((w_bitmap < 0) || (h_bitmap < 0) || (w < 0) || (h < 0)) 219 | return; 220 | if((x_part < 0) || (x_part >= w_bitmap)) 221 | return; 222 | if((y_part < 0) || (y_part >= h_bitmap)) 223 | return; 224 | int16_t wb_bitmap = (w_bitmap + 7) / 8; // width bytes, bitmaps are padded 225 | x_part -= x_part % 8; // byte boundary 226 | w = w_bitmap - x_part < w ? w_bitmap - x_part : w; // limit 227 | h = h_bitmap - y_part < h ? h_bitmap - y_part : h; // limit 228 | x -= x % 8; // byte boundary 229 | w = 8 * ((w + 7) / 8); // byte boundary, bitmaps are padded 230 | int16_t x1 = x < 0 ? 0 : x; // limit 231 | int16_t y1 = y < 0 ? 0 : y; // limit 232 | int16_t w1 = x + w < int16_t(WIDTH) ? w : int16_t(WIDTH) - x; // limit 233 | int16_t h1 = y + h < int16_t(HEIGHT) ? h : int16_t(HEIGHT) - y; // limit 234 | int16_t dx = x1 - x; 235 | int16_t dy = y1 - y; 236 | w1 -= dx; 237 | h1 -= dy; 238 | if((w1 <= 0) || (h1 <= 0)) 239 | return; 240 | _Init_Full(); 241 | _writeCommand(0x10); 242 | _startTransfer(); 243 | for(int16_t i = 0; i < int16_t(HEIGHT); i++) { 244 | for(int16_t j = 0; j < int16_t(WIDTH); j += 8) { 245 | uint8_t data = 0xFF; 246 | if((j >= x1) && (j < x1 + w) && (i >= y1) && (i < y1 + h)) { 247 | // use wb_bitmap, h_bitmap of bitmap for index! 248 | uint32_t idx = 249 | mirror_y 250 | ? (x_part + j - x1) / 8 + 251 | uint32_t((h_bitmap - 1 - (y_part + i - y1))) * 252 | wb_bitmap 253 | : (x_part + j - x1) / 8 + 254 | uint32_t(y_part + i - y1) * wb_bitmap; 255 | if(pgm) { 256 | #if defined(__AVR) || defined(ESP8266) || defined(ESP32) 257 | data = pgm_read_byte(&bitmap[idx]); 258 | #else 259 | data = bitmap[idx]; 260 | #endif 261 | } else { 262 | data = bitmap[idx]; 263 | } 264 | if(invert) 265 | data = ~data; 266 | } 267 | for(int16_t k = 0; k < 4; k++) { 268 | uint8_t data2 = 269 | (data & 0x80 ? 0x10 : 0x00) | (data & 0x40 ? 0x01 : 0x00); 270 | data <<= 2; 271 | _transfer(data2); 272 | } 273 | } 274 | } 275 | _endTransfer(); 276 | delay(1); // yield() to avoid WDT on ESP8266 and ESP32 277 | } 278 | 279 | void GxEPD2_565c::writeImagePart(const uint8_t *black, const uint8_t *color, 280 | int16_t x_part, int16_t y_part, 281 | int16_t w_bitmap, int16_t h_bitmap, int16_t x, 282 | int16_t y, int16_t w, int16_t h, bool invert, 283 | bool mirror_y, bool pgm) { 284 | // Serial.print("writeImagePart("); Serial.print(x_part); Serial.print(", 285 | // "); Serial.print(y_part); Serial.print(", "); Serial.print(w_bitmap); 286 | // Serial.print(", "); Serial.print(h_bitmap); Serial.print(", "); 287 | // Serial.print(x); Serial.print(", "); Serial.print(y); Serial.print(", "); 288 | // Serial.print(w); Serial.print(", "); Serial.print(h); 289 | // Serial.println(")"); 290 | if(!black && !color) 291 | return; 292 | if(!color) 293 | return writeImagePart(black, x_part, y_part, w_bitmap, h_bitmap, x, y, 294 | w, h, invert, mirror_y, pgm); 295 | delay(1); // yield() to avoid WDT on ESP8266 and ESP32 296 | if((w_bitmap < 0) || (h_bitmap < 0) || (w < 0) || (h < 0)) 297 | return; 298 | if((x_part < 0) || (x_part >= w_bitmap)) 299 | return; 300 | if((y_part < 0) || (y_part >= h_bitmap)) 301 | return; 302 | int16_t wb_bitmap = (w_bitmap + 7) / 8; // width bytes, bitmaps are padded 303 | x_part -= x_part % 8; // byte boundary 304 | w = w_bitmap - x_part < w ? w_bitmap - x_part : w; // limit 305 | h = h_bitmap - y_part < h ? h_bitmap - y_part : h; // limit 306 | x -= x % 8; // byte boundary 307 | w = 8 * ((w + 7) / 8); // byte boundary, bitmaps are padded 308 | int16_t x1 = x < 0 ? 0 : x; // limit 309 | int16_t y1 = y < 0 ? 0 : y; // limit 310 | int16_t w1 = x + w < int16_t(WIDTH) ? w : int16_t(WIDTH) - x; // limit 311 | int16_t h1 = y + h < int16_t(HEIGHT) ? h : int16_t(HEIGHT) - y; // limit 312 | int16_t dx = x1 - x; 313 | int16_t dy = y1 - y; 314 | w1 -= dx; 315 | h1 -= dy; 316 | if((w1 <= 0) || (h1 <= 0)) 317 | return; 318 | _Init_Full(); 319 | _writeCommand(0x10); 320 | _startTransfer(); 321 | for(int16_t i = 0; i < int16_t(HEIGHT); i++) { 322 | for(int16_t j = 0; j < int16_t(WIDTH); j += 8) { 323 | uint8_t black_data = 0xFF, color_data = 0xFF; 324 | if((j >= x1) && (j < x1 + w) && (i >= y1) && (i < y1 + h)) { 325 | // use wb_bitmap, h_bitmap of bitmap for index! 326 | uint32_t idx = 327 | mirror_y 328 | ? (x_part + j - x1) / 8 + 329 | uint32_t((h_bitmap - 1 - (y_part + i - y1))) * 330 | wb_bitmap 331 | : (x_part + j - x1) / 8 + 332 | uint32_t(y_part + i - y1) * wb_bitmap; 333 | if(pgm) { 334 | #if defined(__AVR) || defined(ESP8266) || defined(ESP32) 335 | black_data = pgm_read_byte(&black[idx]); 336 | color_data = pgm_read_byte(&color[idx]); 337 | #else 338 | black_data = black[idx]; 339 | color_data = color[idx]; 340 | #endif 341 | } else { 342 | black_data = black[idx]; 343 | color_data = color[idx]; 344 | } 345 | if(invert) { 346 | black_data = ~black_data; 347 | color_data = ~color_data; 348 | } 349 | } 350 | for(int16_t k = 0; k < 4; k++) { 351 | uint8_t out_data = 0x00; 352 | for(int16_t l = 0; l < 2; l++) { 353 | out_data <<= 4; 354 | if(!(color_data & 0x80)) 355 | out_data |= 0x04; 356 | else 357 | out_data |= black_data & 0x80 ? 0x01 : 0x00; 358 | black_data <<= 1; 359 | color_data <<= 1; 360 | } 361 | _transfer(out_data); 362 | } 363 | } 364 | } 365 | _endTransfer(); 366 | delay(1); // yield() to avoid WDT on ESP8266 and ESP32 367 | } 368 | 369 | void GxEPD2_565c::writeNative(const uint8_t *data1, const uint8_t *data2, 370 | int16_t x, int16_t y, int16_t w, int16_t h, 371 | bool invert, bool mirror_y, bool pgm) { 372 | if(data1) { 373 | // Serial.print("writeNative("); Serial.print(x); Serial.print(", "); 374 | // Serial.print(y); Serial.print(", "); Serial.print(w); Serial.print(", 375 | // "); Serial.print(h); Serial.println(")"); 376 | delay(1); // yield() to avoid WDT on ESP8266 and ESP32 377 | if(_paged && (x == 0) && (w == int16_t(WIDTH)) && 378 | (h < int16_t(HEIGHT))) { 379 | // Serial.println("paged"); 380 | _startTransfer(); 381 | for(uint32_t i = 0; i < uint32_t(WIDTH) * uint32_t(h) / 2; i++) { 382 | uint8_t data = data1[i]; 383 | _transfer(data); 384 | } 385 | _endTransfer(); 386 | if(y + h == HEIGHT) // last page 387 | { 388 | // Serial.println("paged ended"); 389 | _paged = false; 390 | } 391 | } else { 392 | _paged = false; 393 | int16_t wb = (w + 1) / 2; // width bytes, bitmaps are padded 394 | x -= x % 2; // byte boundary 395 | w = wb * 2; // byte boundary 396 | if((w <= 0) || (h <= 0)) 397 | return; 398 | _Init_Full(); 399 | _writeCommand(0x10); 400 | _startTransfer(); 401 | for(int16_t i = 0; i < int16_t(HEIGHT); i++) { 402 | for(int16_t j = 0; j < int16_t(WIDTH); j += 2) { 403 | uint8_t data = 0x11; 404 | if(data1) { 405 | if((j >= x) && (j < x + w) && (i >= y) && (i < y + h)) { 406 | uint32_t idx = 407 | mirror_y ? (j - x) / 2 + 408 | uint32_t((h - 1 - (i - y))) * wb 409 | : (j - x) / 2 + uint32_t(i - y) * wb; 410 | if(pgm) { 411 | #if defined(__AVR) || defined(ESP8266) || defined(ESP32) 412 | data = pgm_read_byte(&data1[idx]); 413 | #else 414 | data = data1[idx]; 415 | #endif 416 | } else { 417 | data = data1[idx]; 418 | } 419 | if(invert) 420 | data = ~data; 421 | } 422 | } 423 | _transfer(data); 424 | } 425 | } 426 | _endTransfer(); 427 | } 428 | delay(1); // yield() to avoid WDT on ESP8266 and ESP32 429 | } 430 | } 431 | 432 | void GxEPD2_565c::writeNativePart(const uint8_t *data1, const uint8_t *data2, 433 | int16_t x_part, int16_t y_part, 434 | int16_t w_bitmap, int16_t h_bitmap, int16_t x, 435 | int16_t y, int16_t w, int16_t h, bool invert, 436 | bool mirror_y, bool pgm) { 437 | // Serial.print("writeNativePart("); Serial.print(x_part); Serial.print(", 438 | // "); Serial.print(y_part); Serial.print(", "); Serial.print(w_bitmap); 439 | // Serial.print(", "); Serial.print(h_bitmap); Serial.print(", "); 440 | // Serial.print(x); Serial.print(", "); Serial.print(y); Serial.print(", "); 441 | // Serial.print(w); Serial.print(", "); Serial.print(h); 442 | // Serial.println(")"); 443 | if(!data1) 444 | return; 445 | delay(1); // yield() to avoid WDT on ESP8266 and ESP32 446 | if((w_bitmap < 0) || (h_bitmap < 0) || (w < 0) || (h < 0)) 447 | return; 448 | if((x_part < 0) || (x_part >= w_bitmap)) 449 | return; 450 | if((y_part < 0) || (y_part >= h_bitmap)) 451 | return; 452 | int16_t wb_bitmap = (w_bitmap + 1) / 2; // width bytes, bitmaps are padded 453 | x_part -= x_part % 2; // byte boundary 454 | w = w_bitmap - x_part < w ? w_bitmap - x_part : w; // limit 455 | h = h_bitmap - y_part < h ? h_bitmap - y_part : h; // limit 456 | x -= x % 2; // byte boundary 457 | w = 2 * ((w + 1) / 2); // byte boundary, bitmaps are padded 458 | int16_t x1 = x < 0 ? 0 : x; // limit 459 | int16_t y1 = y < 0 ? 0 : y; // limit 460 | int16_t w1 = x + w < int16_t(WIDTH) ? w : int16_t(WIDTH) - x; // limit 461 | int16_t h1 = y + h < int16_t(HEIGHT) ? h : int16_t(HEIGHT) - y; // limit 462 | int16_t dx = x1 - x; 463 | int16_t dy = y1 - y; 464 | w1 -= dx; 465 | h1 -= dy; 466 | if((w1 <= 0) || (h1 <= 0)) 467 | return; 468 | _Init_Full(); 469 | _writeCommand(0x10); 470 | _startTransfer(); 471 | for(int16_t i = 0; i < int16_t(HEIGHT); i++) { 472 | for(int16_t j = 0; j < int16_t(WIDTH); j += 2) { 473 | uint8_t data = 0x11; 474 | if((j >= x1) && (j < x1 + w) && (i >= y1) && (i < y1 + h)) { 475 | // use wb_bitmap, h_bitmap of bitmap for index! 476 | uint32_t idx = 477 | mirror_y 478 | ? (x_part + j - x1) / 2 + 479 | uint32_t((h_bitmap - 1 - (y_part + i - y1))) * 480 | wb_bitmap 481 | : (x_part + j - x1) / 2 + 482 | uint32_t(y_part + i - y1) * wb_bitmap; 483 | if(pgm) { 484 | #if defined(__AVR) || defined(ESP8266) || defined(ESP32) 485 | data = pgm_read_byte(&data1[idx]); 486 | #else 487 | data = data1[idx]; 488 | #endif 489 | } else { 490 | data = data1[idx]; 491 | } 492 | if(invert) 493 | data = ~data; 494 | } 495 | _transfer(data); 496 | } 497 | } 498 | _endTransfer(); 499 | delay(1); // yield() to avoid WDT on ESP8266 and ESP32 500 | } 501 | 502 | void GxEPD2_565c::drawImage(const uint8_t bitmap[], int16_t x, int16_t y, 503 | int16_t w, int16_t h, bool invert, bool mirror_y, 504 | bool pgm) { 505 | writeImage(bitmap, x, y, w, h, invert, mirror_y, pgm); 506 | refresh(x, y, w, h); 507 | } 508 | 509 | void GxEPD2_565c::drawImagePart(const uint8_t bitmap[], int16_t x_part, 510 | int16_t y_part, int16_t w_bitmap, 511 | int16_t h_bitmap, int16_t x, int16_t y, 512 | int16_t w, int16_t h, bool invert, 513 | bool mirror_y, bool pgm) { 514 | writeImagePart(bitmap, x_part, y_part, w_bitmap, h_bitmap, x, y, w, h, 515 | invert, mirror_y, pgm); 516 | refresh(x, y, w, h); 517 | } 518 | 519 | void GxEPD2_565c::drawImage(const uint8_t *black, const uint8_t *color, 520 | int16_t x, int16_t y, int16_t w, int16_t h, 521 | bool invert, bool mirror_y, bool pgm) { 522 | writeImage(black, color, x, y, w, h, invert, mirror_y, pgm); 523 | refresh(x, y, w, h); 524 | } 525 | 526 | void GxEPD2_565c::drawImagePart(const uint8_t *black, const uint8_t *color, 527 | int16_t x_part, int16_t y_part, 528 | int16_t w_bitmap, int16_t h_bitmap, int16_t x, 529 | int16_t y, int16_t w, int16_t h, bool invert, 530 | bool mirror_y, bool pgm) { 531 | writeImagePart(black, color, x_part, y_part, w_bitmap, h_bitmap, x, y, w, h, 532 | invert, mirror_y, pgm); 533 | refresh(x, y, w, h); 534 | } 535 | 536 | void GxEPD2_565c::drawNative(const uint8_t *data1, const uint8_t *data2, 537 | int16_t x, int16_t y, int16_t w, int16_t h, 538 | bool invert, bool mirror_y, bool pgm) { 539 | writeNative(data1, data2, x, y, w, h, invert, mirror_y, pgm); 540 | refresh(x, y, w, h); 541 | } 542 | 543 | void GxEPD2_565c::refresh(bool partial_update_mode) { 544 | if(partial_update_mode) 545 | refresh(0, 0, WIDTH, HEIGHT); 546 | else 547 | _Update_Full(); 548 | } 549 | 550 | void GxEPD2_565c::refresh(int16_t x, int16_t y, int16_t w, int16_t h) { 551 | _Update_Part(); 552 | } 553 | 554 | void GxEPD2_565c::powerOff() { _PowerOff(); } 555 | 556 | void GxEPD2_565c::hibernate() { 557 | _PowerOff(); 558 | if(_rst >= 0) { 559 | _writeCommand(0x07); // deep sleep 560 | _writeData(0xA5); // control code 561 | _hibernating = true; 562 | } 563 | } 564 | 565 | void GxEPD2_565c::setPaged() { 566 | _paged = true; 567 | _Init_Full(); 568 | _writeCommand(0x10); 569 | } 570 | 571 | void GxEPD2_565c::_PowerOn() { 572 | if(!_power_is_on) { 573 | _writeCommand(0x04); 574 | _waitWhileBusy("_PowerOn", power_on_time); 575 | } 576 | _power_is_on = true; 577 | } 578 | 579 | void GxEPD2_565c::_PowerOff() { 580 | if(_power_is_on) { 581 | _writeCommand(0x02); 582 | _waitWhileBusy("_PowerOff", power_off_time); 583 | } 584 | _power_is_on = false; 585 | _using_partial_mode = false; 586 | } 587 | 588 | void GxEPD2_565c::_InitDisplay() { 589 | if(_hibernating) 590 | _reset(); 591 | _writeCommand(0x00); // Panel Settings 592 | _writeData(0xEF); 593 | _writeData(0x08); 594 | _writeCommand(0x01); // Power Settings 595 | _writeData(0x37); 596 | _writeData(0x00); 597 | _writeData(0x23); 598 | _writeData(0x23); 599 | _writeCommand(0x03); // Power Off Sequence 600 | _writeData(0x00); 601 | _writeCommand(0x06); // Booster Soft Start 602 | _writeData(0xC7); 603 | _writeData(0xC7); 604 | _writeData(0x1D); 605 | _writeCommand(0x30); // PLL Control 606 | _writeData(0x3C); // 50 Hz 607 | _writeCommand(0x40); // Temperature Sensor Command 608 | _writeData(0x00); // ?? 609 | _writeCommand(0x50); // VCOM and Data Interval Setting 610 | _writeData(0x37); // white border 611 | _writeCommand(0x60); // undocumented 612 | _writeData(0x22); 613 | _writeCommand(0x61); // Resolution Setting 614 | _writeData(0x02); 615 | _writeData(0x58); 616 | _writeData(0x01); 617 | _writeData(0xC0); 618 | _writeCommand(0xE3); // undocumented 619 | _writeData(0xAA); 620 | delay(100); 621 | _writeCommand(0x50); // VCOM and Data Interval Setting 622 | _writeData(0x37); // white border 623 | } 624 | 625 | void GxEPD2_565c::_Init_Full() { 626 | _InitDisplay(); 627 | _PowerOn(); 628 | } 629 | 630 | void GxEPD2_565c::_Init_Part() { 631 | _InitDisplay(); 632 | _PowerOn(); 633 | } 634 | 635 | void GxEPD2_565c::_Update_Full() { 636 | _writeCommand(0x12); // Display Refresh 637 | // _waitWhileBusy("_Update_Full", full_refresh_time); 638 | } 639 | 640 | void GxEPD2_565c::_Update_Part() { 641 | _writeCommand(0x12); // Display Refresh 642 | _waitWhileBusy("_Update_Part", partial_refresh_time); 643 | } 644 | -------------------------------------------------------------------------------- /src/GxEPD2_565c_mod.h: -------------------------------------------------------------------------------- 1 | // Display Library for SPI e-paper panels from Dalian Good Display and boards 2 | // from Waveshare. Requires HW SPI and Adafruit_GFX. Caution: the e-paper panels 3 | // require 3.3V supply AND data lines! 4 | // 5 | // based on Demo Example from Good Display: 6 | // http://www.e-paper-display.com/download_detail/downloadsId=808.html 7 | // Panel: 5.65inch ACeP 7-Color E-Paper : 8 | // https://www.waveshare.com/product/displays/e-paper/5.65inch-e-paper-module-f.htm 9 | // Controller: unknonw 10 | // 11 | // Author: Jean-Marc Zingg 12 | // 13 | // Version: see library.properties 14 | // 15 | // Library: https://github.com/ZinggJM/GxEPD2 16 | // 17 | // Modified by likeablob for SMP7C. Removed busy wait handling. 18 | 19 | #ifndef _GxEPD2_565c_H_ 20 | #define _GxEPD2_565c_H_ 21 | 22 | #include "GxEPD2_EPD.h" 23 | 24 | class GxEPD2_565c : public GxEPD2_EPD { 25 | public: 26 | // attributes 27 | static const uint16_t WIDTH = 600; 28 | static const uint16_t WIDTH_VISIBLE = WIDTH; 29 | static const uint16_t HEIGHT = 448; 30 | static const GxEPD2::Panel panel = GxEPD2::ACeP565; 31 | static const bool hasColor = true; 32 | static const bool hasPartialUpdate = false; 33 | static const bool hasFastPartialUpdate = false; 34 | static const uint16_t power_on_time = 100; // ms, e.g. 96001us 35 | static const uint16_t power_off_time = 100; // ms, e.g. 60001us 36 | static const uint16_t full_refresh_time = 12000; // ms, e.g. 11354001us 37 | static const uint16_t partial_refresh_time = 12000; // ms, e.g. 11354001us 38 | // constructor 39 | GxEPD2_565c(int16_t cs, int16_t dc, int16_t rst, int16_t busy); 40 | // methods (virtual) 41 | // Support for Bitmaps (Sprites) to Controller Buffer and to Screen 42 | void 43 | clearScreen(uint8_t value = 44 | 0xFF); // init controller memory and screen (default white) 45 | void clearScreen(uint8_t black_value, 46 | uint8_t color_value); // init controller memory and screen 47 | void writeScreenBuffer( 48 | uint8_t value = 0xFF); // init controller memory (default white) 49 | void writeScreenBuffer(uint8_t black_value, 50 | uint8_t color_value); // init controller memory 51 | // write to controller memory, without screen refresh; x and w should be 52 | // multiple of 8 53 | void writeImage(const uint8_t bitmap[], int16_t x, int16_t y, int16_t w, 54 | int16_t h, bool invert = false, bool mirror_y = false, 55 | bool pgm = false); 56 | void writeImagePart(const uint8_t bitmap[], int16_t x_part, int16_t y_part, 57 | int16_t w_bitmap, int16_t h_bitmap, int16_t x, 58 | int16_t y, int16_t w, int16_t h, bool invert = false, 59 | bool mirror_y = false, bool pgm = false); 60 | void writeImage(const uint8_t *black, const uint8_t *color, int16_t x, 61 | int16_t y, int16_t w, int16_t h, bool invert = false, 62 | bool mirror_y = false, bool pgm = false); 63 | void writeImagePart(const uint8_t *black, const uint8_t *color, 64 | int16_t x_part, int16_t y_part, int16_t w_bitmap, 65 | int16_t h_bitmap, int16_t x, int16_t y, int16_t w, 66 | int16_t h, bool invert = false, bool mirror_y = false, 67 | bool pgm = false); 68 | // write sprite of native data to controller memory, without screen refresh; 69 | // x and w should be multiple of 8 70 | void writeNative(const uint8_t *data1, const uint8_t *data2, int16_t x, 71 | int16_t y, int16_t w, int16_t h, bool invert = false, 72 | bool mirror_y = false, bool pgm = false); 73 | void writeNativePart(const uint8_t *data1, const uint8_t *data2, 74 | int16_t x_part, int16_t y_part, int16_t w_bitmap, 75 | int16_t h_bitmap, int16_t x, int16_t y, int16_t w, 76 | int16_t h, bool invert = false, bool mirror_y = false, 77 | bool pgm = false); 78 | // write to controller memory, with screen refresh; x and w should be 79 | // multiple of 8 80 | void drawImage(const uint8_t bitmap[], int16_t x, int16_t y, int16_t w, 81 | int16_t h, bool invert = false, bool mirror_y = false, 82 | bool pgm = false); 83 | void drawImagePart(const uint8_t bitmap[], int16_t x_part, int16_t y_part, 84 | int16_t w_bitmap, int16_t h_bitmap, int16_t x, int16_t y, 85 | int16_t w, int16_t h, bool invert = false, 86 | bool mirror_y = false, bool pgm = false); 87 | void drawImage(const uint8_t *black, const uint8_t *color, int16_t x, 88 | int16_t y, int16_t w, int16_t h, bool invert = false, 89 | bool mirror_y = false, bool pgm = false); 90 | void drawImagePart(const uint8_t *black, const uint8_t *color, 91 | int16_t x_part, int16_t y_part, int16_t w_bitmap, 92 | int16_t h_bitmap, int16_t x, int16_t y, int16_t w, 93 | int16_t h, bool invert = false, bool mirror_y = false, 94 | bool pgm = false); 95 | // write sprite of native data to controller memory, with screen refresh; x 96 | // and w should be multiple of 8 97 | void drawNative(const uint8_t *data1, const uint8_t *data2, int16_t x, 98 | int16_t y, int16_t w, int16_t h, bool invert = false, 99 | bool mirror_y = false, bool pgm = false); 100 | void 101 | refresh(bool partial_update_mode = 102 | false); // screen refresh from controller memory to full screen 103 | void 104 | refresh(int16_t x, int16_t y, int16_t w, 105 | int16_t h); // screen refresh from controller memory, partial screen 106 | void powerOff(); // turns off generation of panel driving voltages, avoids 107 | // screen fading over time 108 | void hibernate(); // turns powerOff() and sets controller to deep sleep for 109 | // minimum power use, ONLY if wakeable by RST (rst >= 0) 110 | void setPaged(); // for GxEPD2_154c and GxEPD2_565c paged workaround 111 | private: 112 | void _PowerOn(); 113 | void _PowerOff(); 114 | void _InitDisplay(); 115 | void _Init_Full(); 116 | void _Init_Part(); 117 | void _Update_Full(); 118 | void _Update_Part(); 119 | 120 | private: 121 | bool _paged; 122 | }; 123 | 124 | #endif 125 | -------------------------------------------------------------------------------- /src/GxEPD2_7C_mod.h: -------------------------------------------------------------------------------- 1 | // Display Library for SPI e-paper panels from Dalian Good Display and boards 2 | // from Waveshare. Requires HW SPI and Adafruit_GFX. Caution: these e-papers 3 | // require 3.3V supply AND data lines! 4 | // 5 | // based on Demo Example from Good Display: 6 | // http://www.e-paper-display.com/download_list/downloadcategoryid=34&isMode=false.html 7 | // 8 | // Author: Jean-Marc Zingg 9 | // 10 | // Version: see library.properties 11 | // 12 | // Library: https://github.com/ZinggJM/GxEPD2 13 | // 14 | // Modified by likeablob for SMP7C. To have single paged operation. 15 | 16 | #ifndef _GxEPD2_7C_H_ 17 | #define _GxEPD2_7C_H_ 18 | // uncomment next line to use class GFX of library GFX_Root instead of 19 | // Adafruit_GFX 20 | //#include 21 | 22 | #ifndef ENABLE_GxEPD2_GFX 23 | // default is off 24 | #define ENABLE_GxEPD2_GFX 0 25 | #endif 26 | 27 | #if ENABLE_GxEPD2_GFX 28 | #include "GxEPD2_GFX.h" 29 | #define GxEPD2_GFX_BASE_CLASS GxEPD2_GFX 30 | #elif defined(_GFX_H_) 31 | #define GxEPD2_GFX_BASE_CLASS GFX 32 | #else 33 | #include 34 | #define GxEPD2_GFX_BASE_CLASS Adafruit_GFX 35 | #endif 36 | 37 | // Modify to have a pseudo page buffer which is actually splitted into two byte 38 | // buffers. This is because ESP32 doesn't allow allocating 131 KiB(600 * 448 / 39 | // 2) of a single contiguous buffer. 40 | #define PIXEL_BUFFER_LEN ((GxEPD2_Type::WIDTH / 2) * page_height / 2) 41 | #define PIXEL_BUFFER(i) \ 42 | ((i) < PIXEL_BUFFER_LEN ? (_pixel_buffer_a[(i)]) \ 43 | : (_pixel_buffer_b[(i)-PIXEL_BUFFER_LEN])) 44 | 45 | // Hidden 8th color? 46 | #define GxEPD_BEIGE 0x07 47 | 48 | #include "GxEPD2_565c_mod.h" 49 | #include "GxEPD2_EPD.h" 50 | 51 | template 52 | class GxEPD2_7C : public GxEPD2_GFX_BASE_CLASS { 53 | public: 54 | GxEPD2_Type epd2; 55 | #if ENABLE_GxEPD2_GFX 56 | GxEPD2_7C(GxEPD2_Type epd2_instance) 57 | : GxEPD2_GFX_BASE_CLASS(epd2, GxEPD2_Type::WIDTH, GxEPD2_Type::HEIGHT), 58 | epd2(epd2_instance) 59 | #else 60 | GxEPD2_7C(GxEPD2_Type epd2_instance) 61 | : GxEPD2_GFX_BASE_CLASS(GxEPD2_Type::WIDTH, GxEPD2_Type::HEIGHT), 62 | epd2(epd2_instance) 63 | #endif 64 | { 65 | _page_height = page_height; 66 | _pages = (HEIGHT / _page_height) + ((HEIGHT % _page_height) > 0); 67 | _mirror = false; 68 | _using_partial_mode = false; 69 | _current_page = 0; 70 | setFullWindow(); 71 | } 72 | 73 | uint16_t pages() { return _pages; } 74 | 75 | uint16_t pageHeight() { return _page_height; } 76 | 77 | bool mirror(bool m) { 78 | _swap_(_mirror, m); 79 | return m; 80 | } 81 | 82 | void drawPixel(int16_t x, int16_t y, uint16_t color) { 83 | if((x < 0) || (x >= width()) || (y < 0) || (y >= height())) 84 | return; 85 | if(_mirror) 86 | x = width() - x - 1; 87 | // check rotation, move pixel around if necessary 88 | switch(getRotation()) { 89 | case 1: 90 | _swap_(x, y); 91 | x = WIDTH - x - 1; 92 | break; 93 | case 2: 94 | x = WIDTH - x - 1; 95 | y = HEIGHT - y - 1; 96 | break; 97 | case 3: 98 | _swap_(x, y); 99 | y = HEIGHT - y - 1; 100 | break; 101 | } 102 | // transpose partial window to 0,0 103 | x -= _pw_x; 104 | y -= _pw_y; 105 | // clip to (partial) window 106 | if((x < 0) || (x >= int16_t(_pw_w)) || (y < 0) || (y >= int16_t(_pw_h))) 107 | return; 108 | // adjust for current page 109 | y -= _current_page * _page_height; 110 | // check if in current page 111 | if((y < 0) || (y >= int16_t(_page_height))) 112 | return; 113 | uint32_t i = x / 2 + uint32_t(y) * (_pw_w / 2); 114 | uint8_t pv = color7(color); 115 | if(x & 1) 116 | // _pixel_buffer_a[i] = (_pixel_buffer_a[i] & 0xF0) | pv; 117 | PIXEL_BUFFER(i) = (PIXEL_BUFFER(i) & 0xF0) | pv; 118 | else 119 | // _pixel_buffer_a[i] = (_pixel_buffer_a[i] & 0x0F) | (pv << 4); 120 | PIXEL_BUFFER(i) = (PIXEL_BUFFER(i) & 0x0F) | (pv << 4); 121 | } 122 | 123 | void init(uint32_t serial_diag_bitrate = 0) // = 0 : disabled 124 | { 125 | epd2.init(serial_diag_bitrate); 126 | _using_partial_mode = false; 127 | _current_page = 0; 128 | _pixel_buffer_a = (uint8_t *)malloc(PIXEL_BUFFER_LEN); 129 | _pixel_buffer_b = (uint8_t *)malloc(PIXEL_BUFFER_LEN); 130 | setFullWindow(); 131 | } 132 | 133 | // init method with additional parameters: 134 | // initial false for re-init after processor deep sleep wake up, if display 135 | // power supply was kept only relevant for b/w displays with fast partial 136 | // update reset_duration = 20 is default; a value of 2 may help with 137 | // "clever" reset circuit of newer boards from Waveshare pulldown_rst_mode 138 | // true for alternate RST handling to avoid feeding 5V through RST pin 139 | void init(uint32_t serial_diag_bitrate, bool initial, 140 | uint16_t reset_duration = 20, bool pulldown_rst_mode = false) { 141 | epd2.init(serial_diag_bitrate, initial, reset_duration, 142 | pulldown_rst_mode); 143 | _using_partial_mode = false; 144 | _current_page = 0; 145 | setFullWindow(); 146 | } 147 | 148 | // init method with additional parameters: 149 | // SPIClass& spi: either SPI or alternate HW SPI channel 150 | // SPISettings spi_settings: e.g. for higher SPI speed selection 151 | void init(uint32_t serial_diag_bitrate, bool initial, 152 | uint16_t reset_duration, bool pulldown_rst_mode, SPIClass &spi, 153 | SPISettings spi_settings) { 154 | epd2.selectSPI(spi, spi_settings); 155 | epd2.init(serial_diag_bitrate, initial, reset_duration, 156 | pulldown_rst_mode); 157 | _using_partial_mode = false; 158 | _current_page = 0; 159 | setFullWindow(); 160 | } 161 | 162 | void fillScreen(uint16_t color) { 163 | uint8_t pv = color7(color); 164 | uint8_t pv2 = pv | pv << 4; 165 | for(uint32_t x = 0; x < PIXEL_BUFFER_LEN; x++) { 166 | _pixel_buffer_a[x] = pv2; 167 | } 168 | for(uint32_t x = 0; x < PIXEL_BUFFER_LEN; x++) { 169 | _pixel_buffer_b[x] = pv2; 170 | } 171 | } 172 | 173 | // display buffer content to screen, useful for full screen buffer 174 | void display(bool partial_update_mode = false) { 175 | epd2.writeNative(_pixel_buffer_a, 0, 0, 0, WIDTH, _page_height / 2); 176 | epd2.writeNative(_pixel_buffer_b, 0, 0, _page_height / 2, WIDTH, 177 | _page_height / 2); 178 | epd2.refresh(partial_update_mode); 179 | if(!partial_update_mode) 180 | epd2.powerOff(); 181 | } 182 | 183 | // display part of buffer content to screen, useful for full screen buffer 184 | // displayWindow, use parameters according to actual rotation. 185 | // x and w should be multiple of 8, for rotation 0 or 2, 186 | // y and h should be multiple of 8, for rotation 1 or 3, 187 | // else window is increased as needed, 188 | // this is an addressing limitation of the e-paper controllers 189 | void displayWindow(uint16_t x, uint16_t y, uint16_t w, uint16_t h) { 190 | x = gx_uint16_min(x, width()); 191 | y = gx_uint16_min(y, height()); 192 | w = gx_uint16_min(w, width() - x); 193 | h = gx_uint16_min(h, height() - y); 194 | _rotate(x, y, w, h); 195 | epd2.writeNativePart(_pixel_buffer_a, 0, x, y, WIDTH, _page_height / 2, 196 | x, y, w, h); 197 | epd2.refresh(x, y, w, h); 198 | } 199 | 200 | void setFullWindow() { 201 | _using_partial_mode = false; 202 | _pw_x = 0; 203 | _pw_y = 0; 204 | _pw_w = WIDTH; 205 | _pw_h = HEIGHT; 206 | } 207 | 208 | // setPartialWindow, use parameters according to actual rotation. 209 | // x and w should be multiple of 8, for rotation 0 or 2, 210 | // y and h should be multiple of 8, for rotation 1 or 3, 211 | // else window is increased as needed, 212 | // this is an addressing limitation of the e-paper controllers 213 | void setPartialWindow(uint16_t x, uint16_t y, uint16_t w, uint16_t h) { 214 | if(!epd2.hasPartialUpdate) 215 | return; 216 | _pw_x = gx_uint16_min(x, width()); 217 | _pw_y = gx_uint16_min(y, height()); 218 | _pw_w = gx_uint16_min(w, width() - _pw_x); 219 | _pw_h = gx_uint16_min(h, height() - _pw_y); 220 | _rotate(_pw_x, _pw_y, _pw_w, _pw_h); 221 | _using_partial_mode = true; 222 | // make _pw_x, _pw_w multiple of 2 223 | _pw_w += _pw_x % 2; 224 | if(_pw_w % 2 > 0) 225 | _pw_w += 2 - _pw_w % 2; 226 | _pw_x -= _pw_x % 2; 227 | } 228 | 229 | void firstPage() { 230 | fillScreen(GxEPD_WHITE); 231 | _current_page = 0; 232 | _second_phase = false; 233 | epd2.setPaged(); // for GxEPD2_565c paged workaround 234 | } 235 | 236 | bool nextPage() { 237 | uint16_t page_ys = _current_page * _page_height; 238 | if(_using_partial_mode) { 239 | // Serial.print(" nextPage("); Serial.print(_pw_x); Serial.print(", 240 | // "); Serial.print(_pw_y); Serial.print(", "); Serial.print(_pw_w); 241 | // Serial.print(", "); Serial.print(_pw_h); Serial.print(") P"); 242 | // Serial.println(_current_page); 243 | uint16_t page_ye = _current_page < int16_t(_pages - 1) 244 | ? page_ys + _page_height 245 | : HEIGHT; 246 | uint16_t dest_ys = _pw_y + page_ys; // transposed 247 | uint16_t dest_ye = gx_uint16_min(_pw_y + _pw_h, _pw_y + page_ye); 248 | if(dest_ye > dest_ys) { 249 | // Serial.print("writeImage("); Serial.print(_pw_x); 250 | // Serial.print(", "); Serial.print(dest_ys); Serial.print(", 251 | // "); Serial.print(_pw_w); Serial.print(", "); 252 | // Serial.print(dest_ye - dest_ys); Serial.println(")"); 253 | epd2.writeNative(_pixel_buffer_a, 0, _pw_x, dest_ys, _pw_w, 254 | dest_ye - dest_ys); 255 | } else { 256 | // Serial.print("writeImage("); Serial.print(_pw_x); 257 | // Serial.print(", "); Serial.print(dest_ys); Serial.print(", 258 | // "); Serial.print(_pw_w); Serial.print(", "); 259 | // Serial.print(dest_ye - dest_ys); Serial.print(") skipped "); 260 | // Serial.print(dest_ys); Serial.print(".."); 261 | // Serial.println(dest_ye); 262 | } 263 | _current_page++; 264 | if(_current_page == int16_t(_pages)) { 265 | _current_page = 0; 266 | if(!_second_phase) { 267 | epd2.refresh(_pw_x, _pw_y, _pw_w, _pw_h); 268 | if(epd2.hasFastPartialUpdate) { 269 | _second_phase = true; 270 | return true; 271 | } 272 | } 273 | return false; 274 | } 275 | fillScreen(GxEPD_WHITE); 276 | return true; 277 | } else // full update 278 | { 279 | epd2.writeNative(_pixel_buffer_a, 0, 0, 0, WIDTH, page_height / 2); 280 | epd2.writeNative(_pixel_buffer_b, 0, 0, page_height / 2, WIDTH, 281 | page_height / 2); 282 | _current_page++; 283 | if(_current_page == int16_t(_pages)) { 284 | _current_page = 0; 285 | if((epd2.panel == GxEPD2::GDEW0154Z04) && (_pages > 1)) { 286 | if(!_second_phase) { 287 | epd2.refresh(false); // full update after first phase 288 | _second_phase = true; 289 | fillScreen(GxEPD_WHITE); 290 | return true; 291 | } else 292 | epd2.refresh(true); // partial update after second phase 293 | } else 294 | NOP(); 295 | // epd2.refresh(false); // full update after only phase 296 | // epd2.powerOff(); 297 | return false; 298 | } 299 | fillScreen(GxEPD_WHITE); 300 | return true; 301 | } 302 | } 303 | 304 | // GxEPD style paged drawing; drawCallback() is called as many times as 305 | // needed 306 | void drawPaged(void (*drawCallback)(const void *), const void *pv) { 307 | if(_using_partial_mode) { 308 | for(_current_page = 0; _current_page < _pages; _current_page++) { 309 | uint16_t page_ys = _current_page * _page_height; 310 | uint16_t page_ye = _current_page < (_pages - 1) 311 | ? page_ys + _page_height 312 | : HEIGHT; 313 | uint16_t dest_ys = _pw_y + page_ys; // transposed 314 | uint16_t dest_ye = 315 | gx_uint16_min(_pw_y + _pw_h, _pw_y + page_ye); 316 | if(dest_ye > dest_ys) { 317 | fillScreen(GxEPD_WHITE); 318 | drawCallback(pv); 319 | epd2.writeNative(_pixel_buffer_a, 0, _pw_x, dest_ys, _pw_w, 320 | dest_ye - dest_ys); 321 | } 322 | } 323 | epd2.refresh(_pw_x, _pw_y, _pw_w, _pw_h); 324 | } else // full update 325 | { 326 | epd2.setPaged(); // for GxEPD2_154c paged workaround 327 | for(_current_page = 0; _current_page < _pages; _current_page++) { 328 | uint16_t page_ys = _current_page * _page_height; 329 | fillScreen(GxEPD_WHITE); 330 | drawCallback(pv); 331 | epd2.writeNative(_pixel_buffer_a, 0, 0, 0, WIDTH, 332 | page_height / 2); 333 | epd2.writeNative(_pixel_buffer_b, 0, 0, page_height / 2, WIDTH, 334 | page_height / 2); 335 | } 336 | epd2.refresh(false); // full update 337 | epd2.powerOff(); 338 | } 339 | _current_page = 0; 340 | } 341 | 342 | void drawInvertedBitmap(int16_t x, int16_t y, const uint8_t bitmap[], 343 | int16_t w, int16_t h, uint16_t color) { 344 | // taken from Adafruit_GFX.cpp, modified 345 | int16_t byteWidth = (w + 7) / 8; // Bitmap scanline pad = whole byte 346 | uint8_t byte = 0; 347 | for(int16_t j = 0; j < h; j++) { 348 | for(int16_t i = 0; i < w; i++) { 349 | if(i & 7) 350 | byte <<= 1; 351 | else { 352 | #if defined(__AVR) || defined(ESP8266) || defined(ESP32) 353 | byte = pgm_read_byte(&bitmap[j * byteWidth + i / 8]); 354 | #else 355 | byte = bitmap[j * byteWidth + i / 8]; 356 | #endif 357 | } 358 | if(!(byte & 0x80)) { 359 | drawPixel(x + i, y + j, color); 360 | } 361 | } 362 | } 363 | } 364 | 365 | // Support for Bitmaps (Sprites) to Controller Buffer and to Screen 366 | void 367 | clearScreen(uint8_t value = 368 | 0xFF) // init controller memory and screen (default white) 369 | { 370 | epd2.clearScreen(value); 371 | } 372 | void writeScreenBuffer( 373 | uint8_t value = 0xFF) // init controller memory (default white) 374 | { 375 | epd2.writeScreenBuffer(value); 376 | } 377 | // write to controller memory, without screen refresh; x and w should be 378 | // multiple of 8 379 | void writeImage(const uint8_t bitmap[], int16_t x, int16_t y, int16_t w, 380 | int16_t h, bool invert = false, bool mirror_y = false, 381 | bool pgm = false) { 382 | epd2.writeImage(bitmap, x, y, w, h, invert, mirror_y, pgm); 383 | } 384 | void writeImagePart(const uint8_t bitmap[], int16_t x_part, int16_t y_part, 385 | int16_t w_bitmap, int16_t h_bitmap, int16_t x, 386 | int16_t y, int16_t w, int16_t h, bool invert = false, 387 | bool mirror_y = false, bool pgm = false) { 388 | epd2.writeImagePart(bitmap, x_part, y_part, w_bitmap, h_bitmap, x, y, w, 389 | h, invert, mirror_y, pgm); 390 | } 391 | void writeImage(const uint8_t *black, const uint8_t *color, int16_t x, 392 | int16_t y, int16_t w, int16_t h, bool invert, bool mirror_y, 393 | bool pgm) { 394 | epd2.writeImage(black, color, x, y, w, h, invert, mirror_y, pgm); 395 | } 396 | void writeImage(const uint8_t *black, const uint8_t *color, int16_t x, 397 | int16_t y, int16_t w, int16_t h) { 398 | epd2.writeImage(black, color, x, y, w, h, false, false, false); 399 | } 400 | void writeImagePart(const uint8_t *black, const uint8_t *color, 401 | int16_t x_part, int16_t y_part, int16_t w_bitmap, 402 | int16_t h_bitmap, int16_t x, int16_t y, int16_t w, 403 | int16_t h, bool invert, bool mirror_y, bool pgm) { 404 | epd2.writeImagePart(black, color, x_part, y_part, w_bitmap, h_bitmap, x, 405 | y, w, h, invert, mirror_y, pgm); 406 | } 407 | void writeImagePart(const uint8_t *black, const uint8_t *color, 408 | int16_t x_part, int16_t y_part, int16_t w_bitmap, 409 | int16_t h_bitmap, int16_t x, int16_t y, int16_t w, 410 | int16_t h) { 411 | epd2.writeImagePart(black, color, x_part, y_part, w_bitmap, h_bitmap, x, 412 | y, w, h, false, false, false); 413 | } 414 | // write sprite of native data to controller memory, without screen refresh; 415 | // x and w should be multiple of 8 416 | void writeNative(const uint8_t *data1, const uint8_t *data2, int16_t x, 417 | int16_t y, int16_t w, int16_t h, bool invert, 418 | bool mirror_y, bool pgm) { 419 | epd2.writeNative(data1, data2, x, y, w, h, invert, mirror_y, pgm); 420 | } 421 | // write to controller memory, with screen refresh; x and w should be 422 | // multiple of 8 423 | void drawImage(const uint8_t bitmap[], int16_t x, int16_t y, int16_t w, 424 | int16_t h, bool invert = false, bool mirror_y = false, 425 | bool pgm = false) { 426 | epd2.drawImage(bitmap, x, y, w, h, invert, mirror_y, pgm); 427 | } 428 | void drawImagePart(const uint8_t bitmap[], int16_t x_part, int16_t y_part, 429 | int16_t w_bitmap, int16_t h_bitmap, int16_t x, int16_t y, 430 | int16_t w, int16_t h, bool invert = false, 431 | bool mirror_y = false, bool pgm = false) { 432 | epd2.drawImagePart(bitmap, x_part, y_part, w_bitmap, h_bitmap, x, y, w, 433 | h, invert, mirror_y, pgm); 434 | } 435 | void drawImage(const uint8_t *black, const uint8_t *color, int16_t x, 436 | int16_t y, int16_t w, int16_t h, bool invert, bool mirror_y, 437 | bool pgm) { 438 | epd2.drawImage(black, color, x, y, w, h, invert, mirror_y, pgm); 439 | } 440 | void drawImage(const uint8_t *black, const uint8_t *color, int16_t x, 441 | int16_t y, int16_t w, int16_t h) { 442 | epd2.drawImage(black, color, x, y, w, h, false, false, false); 443 | } 444 | void drawImagePart(const uint8_t *black, const uint8_t *color, 445 | int16_t x_part, int16_t y_part, int16_t w_bitmap, 446 | int16_t h_bitmap, int16_t x, int16_t y, int16_t w, 447 | int16_t h, bool invert, bool mirror_y, bool pgm) { 448 | epd2.drawImagePart(black, color, x_part, y_part, w_bitmap, h_bitmap, x, 449 | y, w, h, invert, mirror_y, pgm); 450 | } 451 | void drawImagePart(const uint8_t *black, const uint8_t *color, 452 | int16_t x_part, int16_t y_part, int16_t w_bitmap, 453 | int16_t h_bitmap, int16_t x, int16_t y, int16_t w, 454 | int16_t h) { 455 | epd2.drawImagePart(black, color, x_part, y_part, w_bitmap, h_bitmap, x, 456 | y, w, h, false, false, false); 457 | } 458 | // write sprite of native data to controller memory, with screen refresh; x 459 | // and w should be multiple of 8 460 | void drawNative(const uint8_t *data1, const uint8_t *data2, int16_t x, 461 | int16_t y, int16_t w, int16_t h, bool invert, bool mirror_y, 462 | bool pgm) { 463 | epd2.drawNative(data1, data2, x, y, w, h, invert, mirror_y, pgm); 464 | } 465 | void 466 | refresh(bool partial_update_mode = 467 | false) // screen refresh from controller memory to full screen 468 | { 469 | epd2.refresh(partial_update_mode); 470 | if(!partial_update_mode) 471 | epd2.powerOff(); 472 | } 473 | void 474 | refresh(int16_t x, int16_t y, int16_t w, 475 | int16_t h) // screen refresh from controller memory, partial screen 476 | { 477 | epd2.refresh(x, y, w, h); 478 | } 479 | // turns off generation of panel driving voltages, avoids screen fading over 480 | // time 481 | void powerOff() { epd2.powerOff(); } 482 | // turns powerOff() and sets controller to deep sleep for minimum power use, 483 | // ONLY if wakeable by RST (rst >= 0) 484 | void hibernate() { epd2.hibernate(); } 485 | 486 | private: 487 | template static inline void _swap_(T &a, T &b) { 488 | T t = a; 489 | a = b; 490 | b = t; 491 | }; 492 | static inline uint16_t gx_uint16_min(uint16_t a, uint16_t b) { 493 | return (a < b ? a : b); 494 | }; 495 | static inline uint16_t gx_uint16_max(uint16_t a, uint16_t b) { 496 | return (a > b ? a : b); 497 | }; 498 | void _rotate(uint16_t &x, uint16_t &y, uint16_t &w, uint16_t &h) { 499 | switch(getRotation()) { 500 | case 1: 501 | _swap_(x, y); 502 | _swap_(w, h); 503 | x = WIDTH - x - w; 504 | break; 505 | case 2: 506 | x = WIDTH - x - w; 507 | y = HEIGHT - y - h; 508 | break; 509 | case 3: 510 | _swap_(x, y); 511 | _swap_(w, h); 512 | y = HEIGHT - y - h; 513 | break; 514 | } 515 | } 516 | uint8_t color7(uint16_t color) { 517 | static uint16_t _prev_color = GxEPD_BLACK; 518 | static uint8_t _prev_color7 = 0x00; // black 519 | if(color == _prev_color) 520 | return _prev_color7; 521 | uint8_t cv7 = 0x00; 522 | switch(color) { 523 | case GxEPD_BLACK: 524 | cv7 = 0x00; 525 | break; 526 | case GxEPD_WHITE: 527 | cv7 = 0x01; 528 | break; 529 | case GxEPD_GREEN: 530 | cv7 = 0x02; 531 | break; 532 | case GxEPD_BLUE: 533 | cv7 = 0x03; 534 | break; 535 | case GxEPD_RED: 536 | cv7 = 0x04; 537 | break; 538 | case GxEPD_YELLOW: 539 | cv7 = 0x05; 540 | break; 541 | case GxEPD_ORANGE: 542 | cv7 = 0x06; 543 | break; 544 | case GxEPD_BEIGE: 545 | cv7 = 0x07; 546 | break; 547 | default: { 548 | uint16_t red = color & 0xF800; 549 | uint16_t green = (color & 0x07E0) << 5; 550 | uint16_t blue = (color & 0x001F) << 11; 551 | if((red < 0x8000) && (green < 0x8000) && (blue < 0x8000)) 552 | cv7 = 0x00; // black 553 | else if((red >= 0x8000) && (green >= 0x8000) && (blue >= 0x8000)) 554 | cv7 = 0x01; // white 555 | else if((red >= 0x8000) && (blue >= 0x8000)) 556 | cv7 = red > blue ? 0x04 : 0x03; // red, blue 557 | else if((green >= 0x8000) && (blue >= 0x8000)) 558 | cv7 = green > blue ? 0x02 : 0x03; // green, blue 559 | else if((red >= 0x8000) && (green >= 0x8000)) { 560 | static const uint16_t y2o_lim = 561 | ((GxEPD_YELLOW - GxEPD_ORANGE) / 2 + 562 | (GxEPD_ORANGE & 0x07E0)) 563 | << 5; 564 | cv7 = green > y2o_lim ? 0x05 : 0x06; // yellow, orange 565 | } else if(red >= 0x8000) 566 | cv7 = 0x04; // red 567 | else if(green >= 0x8000) 568 | cv7 = 0x02; // green 569 | else 570 | cv7 = 0x03; // blue 571 | } 572 | } 573 | _prev_color = color; 574 | _prev_color7 = cv7; 575 | return cv7; 576 | } 577 | 578 | private: 579 | // uint8_t _pixel_buffer[(GxEPD2_Type::WIDTH / 2) * page_height]; 580 | // uint8_t _pixel_buffer_a[PIXEL_BUFFER_LEN]; 581 | // uint8_t _pixel_buffer_a[(GxEPD2_Type::WIDTH / 2) * page_height / 2]; 582 | // uint8_t _pixel_buffer_b[(GxEPD2_Type::WIDTH / 2) * page_height / 2]; 583 | uint8_t *_pixel_buffer_a; 584 | uint8_t *_pixel_buffer_b; 585 | bool _using_partial_mode, _second_phase, _mirror; 586 | uint16_t _width_bytes, _pixel_bytes; 587 | int16_t _current_page; 588 | uint16_t _pages, _page_height; 589 | uint16_t _pw_x, _pw_y, _pw_w, _pw_h; 590 | }; 591 | 592 | #endif 593 | -------------------------------------------------------------------------------- /src/PngBlobParser.cpp: -------------------------------------------------------------------------------- 1 | #include "PngBlobParser.h" 2 | 3 | PngBlobParser::PngBlobParser(){}; 4 | 5 | PngBlobParser::~PngBlobParser() { 6 | this->_indexFile.close(); 7 | this->_imagesFile.close(); 8 | } 9 | 10 | png_blob_parser_err_t PngBlobParser::init(fs::FS &fs, const char *indexFilePath, 11 | const char *imagesFilePath) { 12 | this->_indexFile.close(); 13 | this->_imagesFile.close(); 14 | 15 | if(!fs.exists(indexFilePath)) { 16 | return PNG_BLOB_PARSER_ERR_INDEX_FILE_NOT_FOUND; 17 | } 18 | if(!fs.exists(imagesFilePath)) { 19 | return PNG_BLOB_PARSER_ERR_IMAGES_FILE_NOT_FOUND; 20 | } 21 | 22 | fs::File indexFile = fs.open(indexFilePath, "r"); 23 | fs::File imagesFile = fs.open(imagesFilePath, "r"); 24 | 25 | this->_imagesFile = imagesFile; 26 | this->_indexFile = indexFile; 27 | 28 | return PNG_BLOB_PARSER_OK; 29 | } 30 | 31 | png_blob_parser_err_t PngBlobParser::readIndex(uint32_t ind, 32 | FrameIndexData &out) { 33 | fs::File file = (this->_indexFile); 34 | 35 | uint32_t prevIndexStartsFrom = 0; 36 | bool isFirstFrame = ind == 0; 37 | 38 | // The first index value at i = 0 is equal to the size of the first image. 39 | // So no need to get the previous file's index. 40 | // index.bin layout: 41 | // [SizeOf(1st frame), SizeOf(1st frame) + SizeOf(2nd frame), ...] 42 | // In other words: 43 | // [(2nd frame starts from), (3rd frame starts from), ...] 44 | if(!isFirstFrame) { 45 | prevIndexStartsFrom = (ind - 1) * sizeof(uint32_t); 46 | } 47 | 48 | // Seek to the index of the previous file. 49 | bool ok = file.seek(prevIndexStartsFrom, SeekMode::SeekSet); 50 | if(!ok) { 51 | return PNG_BLOB_PARSER_ERR_SEEK_FAILED; 52 | } 53 | 54 | uint32_t frameStartsFrom = 0; 55 | uint32_t nextFrameStartsFrom = 0; 56 | 57 | // Read two index sequenceally. 58 | if(!isFirstFrame) { 59 | // frameStartsFrom should be 0 at i=0. 60 | size_t readLen = 61 | file.readBytes((char *)&frameStartsFrom, sizeof(frameStartsFrom)); 62 | if(readLen != sizeof(frameStartsFrom)) { 63 | return PNG_BLOB_PARSER_ERR_READ_FAILED; 64 | } 65 | } 66 | file.readBytes((char *)&nextFrameStartsFrom, sizeof(nextFrameStartsFrom)); 67 | 68 | // Set results. 69 | out.frameOffset = frameStartsFrom; 70 | out.frameSize = nextFrameStartsFrom - frameStartsFrom; 71 | 72 | // When we reached to the end of the index file. 73 | if(-1 == file.peek()) { 74 | return PNG_BLOB_PARSER_LAST_FRAME; 75 | } 76 | 77 | return PNG_BLOB_PARSER_OK; 78 | } 79 | 80 | png_blob_parser_err_t PngBlobParser::readFrame(FrameIndexData &indexData, 81 | pngle_draw_callback_t callback) { 82 | fs::File file = (this->_imagesFile); 83 | 84 | // Seek to where the frame starts. 85 | bool ok = file.seek(indexData.frameOffset, SeekMode::SeekSet); 86 | if(!ok) { 87 | return PNG_BLOB_PARSER_ERR_SEEK_FAILED; 88 | } 89 | 90 | // Read and decode the frame. 91 | pngle_t *pngle = pngle_new(); 92 | pngle_set_draw_callback(pngle, callback); 93 | 94 | uint8_t buf[2048]; 95 | size_t remain = 0; 96 | size_t totalRead = 0; 97 | png_blob_parser_err_t err = PNG_BLOB_PARSER_OK; 98 | 99 | while(totalRead < indexData.frameSize) { 100 | size_t availableInFrame = indexData.frameSize - totalRead; 101 | size_t availableInBuf = sizeof(buf) - remain; 102 | size_t readLen = _min(availableInBuf, availableInFrame); 103 | 104 | size_t size = file.read(buf + remain, readLen); 105 | totalRead += size; 106 | 107 | // Here, buf may contain some remaining bytes plus the bytes just read 108 | // out above. 109 | size += remain; 110 | // Exit if there was nothing to be read. 111 | if(!size) { 112 | err = PNG_BLOB_PARSER_ERR_INVALID_FRAME; 113 | break; 114 | } 115 | 116 | // Decode some bytes. fed is the number of bytes decoded. 117 | int fed = pngle_feed(pngle, buf, size); 118 | if(fed < 0) { 119 | Serial.printf("PNGLE ERROR: %s\n", pngle_error(pngle)); 120 | err = PNG_BLOB_PARSER_ERR_READ_FAILED; 121 | break; 122 | } 123 | // Serial.printf("fed: %d\n", fed); 124 | 125 | remain = size - fed; 126 | if(remain) { 127 | // Move the remaining bytes to head. 128 | memmove(buf, buf + fed, remain); 129 | } 130 | } 131 | 132 | pngle_destroy(pngle); 133 | 134 | return err; 135 | } 136 | 137 | uint32_t PngBlobParser::getIndexSize() { 138 | return _indexFile.size() / sizeof(uint32_t) + 1; 139 | } 140 | -------------------------------------------------------------------------------- /src/PngBlobParser.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | #include 7 | 8 | typedef uint8_t png_blob_parser_err_t; 9 | 10 | #define PNG_BLOB_PARSER_OK 0x0 11 | #define PNG_BLOB_PARSER_LAST_FRAME 0x10 12 | #define PNG_BLOB_PARSER_ERR 0x20 13 | #define PNG_BLOB_PARSER_ERR_SEEK_FAILED 0x30 14 | #define PNG_BLOB_PARSER_ERR_READ_FAILED 0x40 15 | #define PNG_BLOB_PARSER_ERR_INVALID_FRAME 0x50 16 | #define PNG_BLOB_PARSER_ERR_INDEX_FILE_NOT_FOUND 0x60 17 | #define PNG_BLOB_PARSER_ERR_IMAGES_FILE_NOT_FOUND 0x61 18 | #define PNG_BLOB_PARSER_ERR_NOT_SUPPORTED 0x70 19 | 20 | typedef struct { 21 | uint32_t frameOffset; 22 | uint32_t frameSize; 23 | } FrameIndexData; 24 | 25 | class PngBlobParser { 26 | private: 27 | fs::File _indexFile; 28 | fs::File _imagesFile; 29 | 30 | public: 31 | PngBlobParser(); 32 | ~PngBlobParser(); 33 | 34 | png_blob_parser_err_t init(fs::FS &fs, const char *indexFilePath, 35 | const char *imagesFilePath); 36 | 37 | png_blob_parser_err_t readIndex(uint32_t ind, FrameIndexData &out); 38 | png_blob_parser_err_t readFrame(FrameIndexData &indexData, 39 | pngle_draw_callback_t callback); 40 | uint32_t getIndexSize(); 41 | }; 42 | -------------------------------------------------------------------------------- /src/SMP7C.cpp: -------------------------------------------------------------------------------- 1 | #include "SMP7C.h" 2 | #include "assets/lowbattery.h" 3 | 4 | const char *indexFilePath = "/index.bin"; 5 | const char *imagesFilePath = "/images.bin"; 6 | 7 | SMP7C_ &SMP7C_::getInstance() { 8 | static SMP7C_ instance; 9 | return instance; 10 | } 11 | 12 | SMP7C_ &SMP7C = SMP7C.getInstance(); 13 | 14 | void SMP7C_::_pngleCallback(pngle_t *pngle, uint32_t x, uint32_t y, uint32_t w, 15 | uint32_t h, uint8_t rgba[4]) { 16 | uint32_t color = (rgba[0] << 16) | (rgba[1] << 8) | rgba[2]; 17 | 18 | uint32_t epColor = GxEPD_WHITE; 19 | switch(color) { 20 | case 0x0: 21 | epColor = GxEPD_BLACK; 22 | break; 23 | case 0x781818: 24 | epColor = GxEPD_RED; 25 | break; 26 | case 0xa58a1a: 27 | epColor = GxEPD_YELLOW; 28 | break; 29 | case 0x123b19: 30 | epColor = GxEPD_GREEN; 31 | break; 32 | case 0x131d49: 33 | epColor = GxEPD_BLUE; 34 | break; 35 | case 0x823826: 36 | epColor = GxEPD_ORANGE; 37 | break; 38 | case 0xffffff: 39 | break; 40 | default: 41 | Serial.printf("rgba: %x,%x,%x\n", rgba[0], rgba[1], rgba[2]); 42 | Serial.printf("col: %x\n", color); 43 | Serial.println("An unexpected color found."); 44 | return; 45 | } 46 | SMP7C.display->drawPixel(x, y, epColor); 47 | } 48 | 49 | void SMP7C_::_resetToFirstFrame() { frameInd = 0; } 50 | 51 | bool SMP7C_::begin(uint32_t debugBaudrate) { 52 | uint32_t tInit = millis(); 53 | 54 | // Init SDMMC 55 | if(!SD_MMC.begin()) { 56 | Serial.println("Card Mount Failed"); 57 | return false; 58 | } 59 | 60 | uint8_t cardType = SD_MMC.cardType(); 61 | if(cardType == CARD_NONE) { 62 | Serial.println("No SD_MMC card attached"); 63 | return false; 64 | } 65 | 66 | // Init EPD 67 | SPI.begin(SMP7C_PIN_EPD_MOSI, SMP7C_PIN_EPD_MISO, SMP7C_PIN_EPD_CLK, 68 | SMP7C_PIN_EPD_CS); 69 | epdInstance = 70 | new GxEPD2_565c(/*CS=*/SMP7C_PIN_EPD_CS, /*DC=*/SMP7C_PIN_EPD_DC, 71 | /*RST=*/SMP7C_PIN_EPD_RST, /*BUSY=*/SMP7C_PIN_EPD_BUSY); 72 | display = new GxEPD2_7C(*epdInstance); 73 | display->init(debugBaudrate); 74 | // It seems no pullup register implemented on the EPD board... 75 | pinMode(SMP7C_PIN_EPD_BUSY, INPUT_PULLUP); 76 | 77 | png_blob_parser_err_t err = 78 | pngBlobParser.init(SD_MMC, indexFilePath, imagesFilePath); 79 | if(err) { 80 | Serial.printf("An error returned by pngBlobParser.init(): 0x%x\n", err); 81 | return false; 82 | } 83 | Serial.printf("tInit: %lu ms\r\n", millis() - tInit); 84 | 85 | return true; 86 | } 87 | 88 | bool SMP7C_::renderFrame() { 89 | // Init EPD screen buffer. 90 | display->setFullWindow(); 91 | display->setRotation(0); 92 | display->firstPage(); 93 | 94 | uint32_t tLoad = millis(); 95 | 96 | // Load index data 97 | FrameIndexData indexData; 98 | png_blob_parser_err_t err; 99 | 100 | #ifdef SMP7C_RANDOM_MODE 101 | // Note this implementation is deterministic 102 | uint32_t indexSize = pngBlobParser.getIndexSize(); 103 | randomSeed(frameInd); 104 | frameInd = random(0, indexSize); 105 | #endif 106 | 107 | err = pngBlobParser.readIndex(frameInd, indexData); 108 | Serial.printf("frameData: ind: %u, offset: %u, size: %u\n", frameInd, 109 | indexData.frameOffset, indexData.frameSize); 110 | if(err == PNG_BLOB_PARSER_LAST_FRAME) { 111 | Serial.printf("Reached to the end. frameInd: %u\n", frameInd); 112 | _resetToFirstFrame(); 113 | } else if(err != PNG_BLOB_PARSER_OK) { 114 | Serial.printf("An error returned by readIndex(): 0x%x\r\n", err); 115 | _resetToFirstFrame(); 116 | return false; 117 | } else if(err == PNG_BLOB_PARSER_OK) { 118 | frameInd++; 119 | } 120 | 121 | // Load frame data 122 | err = pngBlobParser.readFrame(indexData, SMP7C._pngleCallback); 123 | if(err) { 124 | Serial.printf("An error returned by readFrame(): 0x%x\r\n", err); 125 | _resetToFirstFrame(); 126 | return false; 127 | } 128 | Serial.printf("tLoad: %lu\n", millis() - tLoad); 129 | 130 | // Send the frame data to EPD. Note that it doesn't wait EPD for refreshing. 131 | // Workaround for pseudo paging. This inits data transmission. 132 | display->nextPage(); 133 | display->epd2.refresh(); 134 | 135 | return true; 136 | } 137 | 138 | void SMP7C_::renderLowVoltageCaution() { 139 | // Init EPD screen buffer. 140 | display->setFullWindow(); 141 | display->setRotation(0); 142 | display->firstPage(); 143 | 144 | display->drawXBitmap(display->width() / 2 - lowbattery_width / 2, 145 | display->height() / 2 - lowbattery_height / 2, 146 | lowbattery_bits, lowbattery_width, lowbattery_height, 147 | GxEPD_RED); 148 | 149 | display->nextPage(); 150 | display->epd2.refresh(); 151 | } 152 | -------------------------------------------------------------------------------- /src/SMP7C.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | 4 | #define FS_NO_GLOBALS 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #include 11 | #include // For Adafruit_GFX.h 12 | #include 13 | 14 | #include "PngBlobParser.h" 15 | 16 | #include "GxEPD2_7C_mod.h" 17 | 18 | #define SMP7C_PIN_EPD_MOSI 26 19 | #define SMP7C_PIN_EPD_MISO -1 20 | #define SMP7C_PIN_EPD_CLK 27 21 | #define SMP7C_PIN_EPD_CS 5 22 | #define SMP7C_PIN_EPD_DC 23 23 | #define SMP7C_PIN_EPD_RST 18 24 | #define SMP7C_PIN_EPD_BUSY 25 25 | 26 | class SMP7C_ { 27 | private: 28 | SMP7C_() = default; 29 | 30 | PngBlobParser pngBlobParser; 31 | 32 | GxEPD2_565c *epdInstance; 33 | GxEPD2_7C *display; 34 | 35 | static void _pngleCallback(pngle_t *pngle, uint32_t x, uint32_t y, 36 | uint32_t w, uint32_t h, uint8_t rgba[4]); 37 | 38 | void _resetToFirstFrame(); 39 | 40 | public: 41 | size_t frameInd = 0; 42 | 43 | static SMP7C_ &getInstance(); // Accessor for singleton instance 44 | 45 | SMP7C_(const SMP7C_ &) = delete; // Prohibit copying 46 | SMP7C_ &operator=(const SMP7C_ &) = delete; 47 | 48 | bool begin(uint32_t debugBaudrate = 115200); 49 | bool renderFrame(); 50 | 51 | void renderLowVoltageCaution(); 52 | }; 53 | 54 | extern SMP7C_ &SMP7C; 55 | -------------------------------------------------------------------------------- /src/assets/lowbattery.h: -------------------------------------------------------------------------------- 1 | #define lowbattery_width 359 2 | #define lowbattery_height 150 3 | static uint8_t lowbattery_bits[] = { 4 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 5 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x3F, 6 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 7 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 8 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 9 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFE, 0xFF, 0x7F, 0x00, 0x00, 0x00, 10 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 11 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 12 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 13 | 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x7F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 14 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 15 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 16 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 17 | 0xFE, 0xFF, 0x3F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 18 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 19 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 20 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x7F, 21 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 22 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 23 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 24 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFE, 0xFF, 0x3F, 0x00, 0x00, 0x00, 25 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 26 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 27 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 28 | 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x3F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 29 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 30 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 31 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF4, 32 | 0xFF, 0xFF, 0xFF, 0x37, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 33 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 34 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 35 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xFF, 0xFF, 0xFF, 0xFF, 36 | 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 37 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 38 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 39 | 0x00, 0x00, 0x00, 0x00, 0xC0, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x01, 0x00, 40 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 41 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 42 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 43 | 0x00, 0xC0, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x01, 0x00, 0x00, 0x00, 0x00, 44 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 45 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 46 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xC0, 0xFF, 47 | 0xFF, 0xFF, 0xFF, 0xFF, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 48 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 49 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 50 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xC0, 0xFF, 0xFF, 0xFF, 0xFF, 51 | 0xFF, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 52 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 53 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 54 | 0x00, 0x00, 0x00, 0x00, 0xE0, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x03, 0x00, 55 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 56 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 57 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 58 | 0x00, 0xC0, 0xFF, 0xDE, 0xBB, 0xBB, 0xFE, 0x03, 0x00, 0x00, 0x00, 0x00, 59 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 60 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 61 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE0, 0x1F, 62 | 0x00, 0x00, 0x00, 0xFC, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 63 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 64 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 65 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xC0, 0x1F, 0x00, 0x00, 0x00, 66 | 0xFC, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 67 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 68 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 69 | 0x00, 0x00, 0x00, 0x00, 0xE0, 0x1F, 0x00, 0x00, 0x00, 0xFC, 0x03, 0x00, 70 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 71 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 72 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 73 | 0x00, 0xC0, 0x1F, 0x00, 0x00, 0x00, 0xFC, 0x01, 0x00, 0x00, 0x00, 0x00, 74 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 75 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 76 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE0, 0x1F, 77 | 0x00, 0x00, 0x00, 0xFC, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 78 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 79 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 80 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xC0, 0x1F, 0x00, 0x00, 0x00, 81 | 0xFC, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 82 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 83 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 84 | 0x00, 0x00, 0x00, 0x00, 0xE0, 0x1F, 0x00, 0x00, 0x00, 0xFC, 0x03, 0x00, 85 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 86 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 87 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 88 | 0x00, 0xC0, 0x1F, 0x00, 0x00, 0x00, 0xFC, 0x01, 0x00, 0x00, 0x00, 0x00, 89 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 90 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 91 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE0, 0x1F, 92 | 0x00, 0x00, 0x00, 0xFC, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 93 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 94 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 95 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xC0, 0x1F, 0x00, 0x00, 0x00, 96 | 0xFC, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 97 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 98 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 99 | 0x00, 0x00, 0x00, 0x00, 0xE0, 0x1F, 0x00, 0x00, 0x00, 0xFC, 0x03, 0x00, 100 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 101 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 102 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 103 | 0x00, 0xC0, 0x1F, 0x00, 0x00, 0x00, 0xFC, 0x01, 0x00, 0x00, 0x00, 0x00, 104 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 105 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 106 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE0, 0x1F, 107 | 0x00, 0x00, 0x00, 0xFC, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 108 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 109 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 110 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xC0, 0x1F, 0x00, 0x00, 0x00, 111 | 0xFC, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 112 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 113 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 114 | 0x00, 0x00, 0x00, 0x00, 0xE0, 0x1F, 0x00, 0x00, 0x00, 0xFC, 0x01, 0x00, 115 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 116 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 117 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 118 | 0x00, 0xC0, 0x1F, 0x00, 0x00, 0x00, 0xFC, 0x01, 0x00, 0x00, 0x00, 0x00, 119 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 120 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 121 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE0, 0x1F, 122 | 0x00, 0x00, 0x00, 0xFC, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 123 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 124 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 125 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE0, 0x1F, 0x00, 0x00, 0x00, 126 | 0xFC, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 127 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 128 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 129 | 0x00, 0x00, 0x00, 0x00, 0xE0, 0x1F, 0x00, 0x00, 0x00, 0xFC, 0x03, 0x00, 130 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 131 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 132 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 133 | 0x00, 0xC0, 0x1F, 0x00, 0x00, 0x00, 0xFC, 0x03, 0x00, 0x00, 0x00, 0x00, 134 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 135 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 136 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE0, 0x1F, 137 | 0x00, 0x00, 0x00, 0xFC, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 138 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 139 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 140 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE0, 0x1F, 0x00, 0x00, 0x00, 141 | 0xFC, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 142 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 143 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 144 | 0x00, 0x00, 0x00, 0x00, 0xC0, 0x1F, 0x00, 0x00, 0x00, 0xFC, 0x03, 0x00, 145 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 146 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 147 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 148 | 0x00, 0xC0, 0x1F, 0x00, 0x00, 0x00, 0xFC, 0x01, 0x00, 0x00, 0x00, 0x00, 149 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 150 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 151 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE0, 0x1F, 152 | 0x00, 0x00, 0x00, 0xFC, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 153 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 154 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 155 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xC0, 0x1F, 0x00, 0x00, 0x00, 156 | 0xFC, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 157 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 158 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 159 | 0x00, 0x00, 0x00, 0x00, 0xE0, 0x1F, 0x00, 0x00, 0x00, 0xFC, 0x03, 0x00, 160 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 161 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 162 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 163 | 0x00, 0xE0, 0x1F, 0x00, 0x00, 0x00, 0xFC, 0x01, 0x00, 0x00, 0x00, 0x00, 164 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 165 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 166 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xC0, 0x1F, 167 | 0x00, 0x00, 0x00, 0xFC, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 168 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 169 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 170 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE0, 0x1F, 0x00, 0x00, 0x00, 171 | 0xFC, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 172 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 173 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 174 | 0x00, 0x00, 0x00, 0x00, 0xE0, 0x1F, 0x00, 0x00, 0x00, 0xFC, 0x03, 0x00, 175 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 176 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 177 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 178 | 0x00, 0xC0, 0x1F, 0x00, 0x00, 0x00, 0xFC, 0x01, 0x00, 0x00, 0x00, 0x00, 179 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 180 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 181 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE0, 0x1F, 182 | 0x00, 0x00, 0x00, 0xFC, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 183 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 184 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 185 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE0, 0x1F, 0x00, 0x00, 0x00, 186 | 0xFC, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 187 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 188 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 189 | 0x00, 0x00, 0x00, 0x00, 0xC0, 0x1F, 0x00, 0x00, 0x00, 0xFC, 0x01, 0x00, 190 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 191 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 192 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 193 | 0x00, 0xC0, 0x1F, 0x00, 0x00, 0x00, 0xFC, 0x01, 0x00, 0x00, 0x00, 0x00, 194 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 195 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 196 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE0, 0x1F, 197 | 0x00, 0x00, 0x00, 0xFC, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 198 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 199 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 200 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xC0, 0x1F, 0x00, 0x00, 0x00, 201 | 0xFC, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 202 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 203 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 204 | 0x00, 0x00, 0x00, 0x00, 0xE0, 0x1F, 0x00, 0x00, 0x00, 0xFC, 0x03, 0x00, 205 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 206 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 207 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 208 | 0x00, 0xE0, 0x1F, 0x00, 0x00, 0x00, 0xFC, 0x03, 0x00, 0x00, 0x00, 0x00, 209 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 210 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 211 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xC0, 0x1F, 212 | 0x00, 0x00, 0x00, 0xFC, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 213 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 214 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 215 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE0, 0x1F, 0x00, 0x00, 0x00, 216 | 0xFC, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 217 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 218 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 219 | 0x00, 0x00, 0x00, 0x00, 0xE0, 0x1F, 0x00, 0x00, 0x00, 0xFC, 0x01, 0x00, 220 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 221 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 222 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 223 | 0x00, 0xC0, 0x5F, 0x69, 0x69, 0x69, 0xFE, 0x01, 0x00, 0x00, 0x00, 0x00, 224 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 225 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 226 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE0, 0xFF, 227 | 0xFF, 0xFF, 0xFF, 0xFF, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 228 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 229 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 230 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE0, 0xFF, 0xFF, 0xFF, 0xFF, 231 | 0xFF, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 232 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 233 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 234 | 0x00, 0x00, 0x00, 0x00, 0xC0, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x03, 0x00, 235 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 236 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 237 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 238 | 0x00, 0xC0, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x03, 0x00, 0x00, 0x00, 0x00, 239 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 240 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 241 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE0, 0xFF, 242 | 0xFF, 0xFF, 0xFF, 0xFF, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 243 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 244 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 245 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE0, 0xFF, 0xFF, 0xFF, 0xFF, 246 | 0xFF, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 247 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 248 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 249 | 0x00, 0x00, 0x00, 0x00, 0xC0, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x01, 0x00, 250 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 251 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 252 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 253 | 0x00, 0xE0, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x01, 0x00, 0x00, 0x00, 0x00, 254 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 255 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 256 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xC0, 0xFF, 257 | 0xFF, 0xFF, 0xFF, 0xFF, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 258 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 259 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 260 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE0, 0xFF, 0xFF, 0xFF, 0xFF, 261 | 0xFF, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 262 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 263 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 264 | 0x00, 0x00, 0x00, 0x00, 0xE0, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x01, 0x00, 265 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 266 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 267 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 268 | 0x00, 0xC0, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x01, 0x00, 0x00, 0x00, 0x00, 269 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 270 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 271 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xFF, 272 | 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 273 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 274 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 275 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 276 | 0x7F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 277 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 278 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 279 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x04, 0x40, 0x00, 0x00, 0x00, 280 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 281 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 282 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 283 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 284 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 285 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 286 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 287 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 288 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 289 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 290 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 291 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 292 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 293 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 294 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 295 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 296 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 297 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 298 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 299 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 300 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 301 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 302 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 303 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 304 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 305 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 306 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 307 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 308 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 309 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 310 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 311 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 312 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 313 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 314 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 315 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 316 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 317 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 318 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 319 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 320 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 321 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 322 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 323 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 324 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 325 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 326 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 327 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 328 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 329 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 330 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 331 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 332 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 333 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 334 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 335 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 336 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 337 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 338 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 339 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 340 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 341 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 342 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 343 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 344 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 345 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 346 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 347 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 348 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 349 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 350 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 351 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 352 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 353 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 354 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 355 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 356 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 357 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 358 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 359 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 360 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 361 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 362 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 363 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 364 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 365 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 366 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 367 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 368 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 369 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 370 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 371 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 372 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 373 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 374 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 375 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 376 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 377 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 378 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 379 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 380 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 381 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 382 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 383 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 384 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 385 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 386 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 387 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 388 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 389 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 390 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 391 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 392 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 393 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 394 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 395 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 396 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 397 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 398 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 399 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 400 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 401 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 402 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 403 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 404 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 405 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 406 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 407 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 408 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 409 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 410 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 411 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 412 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x84, 0x00, 0x00, 413 | 0x00, 0x00, 0xFE, 0x01, 0x00, 0x80, 0x00, 0x00, 0x08, 0x00, 0x40, 0x00, 414 | 0x00, 0x10, 0x10, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x40, 0x18, 0x41, 415 | 0x18, 0x00, 0x10, 0x84, 0x41, 0x00, 0x84, 0x10, 0x10, 0x00, 0x18, 0x01, 416 | 0x00, 0x00, 0x10, 0x00, 0x00, 0x08, 0xFF, 0x00, 0x00, 0x00, 0xE0, 0xFF, 417 | 0x07, 0x00, 0xFC, 0x03, 0xC0, 0x1F, 0x00, 0xFE, 0x01, 0x00, 0xF8, 0xFF, 418 | 0x7F, 0x00, 0x00, 0xC0, 0xFF, 0x00, 0xFC, 0xFF, 0xFF, 0xFF, 0xF8, 0xFF, 419 | 0xFF, 0xFF, 0x01, 0xFF, 0xFF, 0xFF, 0x00, 0xFE, 0xFF, 0x1F, 0x00, 0xFF, 420 | 0x00, 0xC0, 0x7F, 0xFF, 0x00, 0x00, 0x00, 0xF8, 0xFF, 0x3F, 0x00, 0xFC, 421 | 0x03, 0xC0, 0x1F, 0x00, 0xFE, 0x00, 0x00, 0xF8, 0xFF, 0xFF, 0x01, 0x00, 422 | 0xC0, 0xFF, 0x01, 0xFC, 0xFF, 0xFF, 0xFF, 0xF8, 0xFF, 0xFF, 0xFF, 0x03, 423 | 0xFF, 0xFF, 0xFF, 0x00, 0xFE, 0xFF, 0x7F, 0x00, 0xFF, 0x00, 0xC0, 0x3F, 424 | 0xFF, 0x00, 0x00, 0x00, 0xFC, 0xFF, 0x7F, 0x00, 0xFC, 0x03, 0xC0, 0x3F, 425 | 0x00, 0xFE, 0x00, 0x00, 0xF8, 0xFF, 0xFF, 0x07, 0x00, 0xC0, 0xFF, 0x01, 426 | 0xFC, 0xFF, 0xFF, 0xFF, 0xF0, 0xFF, 0xFF, 0xFF, 0x01, 0xFF, 0xFF, 0xFF, 427 | 0x00, 0xFE, 0xFF, 0xFF, 0x01, 0xFE, 0x01, 0xC0, 0x3F, 0xFF, 0x00, 0x00, 428 | 0x00, 0xFE, 0xFF, 0xFF, 0x01, 0xF8, 0x07, 0xE0, 0x3F, 0x00, 0xFE, 0x00, 429 | 0x00, 0xF8, 0xFF, 0xFF, 0x0F, 0x00, 0xE0, 0xFF, 0x01, 0xFC, 0xFF, 0xFF, 430 | 0xFF, 0xF8, 0xFF, 0xFF, 0xFF, 0x03, 0xFF, 0xFF, 0xFF, 0x00, 0xFE, 0xFF, 431 | 0xFF, 0x03, 0xFE, 0x01, 0xE0, 0x1F, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0xFF, 432 | 0xFF, 0x01, 0xF8, 0x07, 0xE0, 0x3F, 0x00, 0xFE, 0x00, 0x00, 0xF8, 0xFF, 433 | 0xFF, 0x1F, 0x00, 0xE0, 0xFF, 0x03, 0xFC, 0xFF, 0xFF, 0xFF, 0xF8, 0xFF, 434 | 0xFF, 0xFF, 0x01, 0xFF, 0xFF, 0xFF, 0x00, 0xFE, 0xFF, 0xFF, 0x07, 0xFE, 435 | 0x03, 0xE0, 0x1F, 0xFF, 0x00, 0x00, 0x80, 0xFF, 0xFF, 0xFF, 0x03, 0xF8, 436 | 0x07, 0xE0, 0x3F, 0x00, 0xFF, 0x00, 0x00, 0xF8, 0xFF, 0xFF, 0x1F, 0x00, 437 | 0xE0, 0xFF, 0x03, 0xFC, 0xFF, 0xFF, 0xFF, 0xF8, 0xFF, 0xFF, 0xFF, 0x03, 438 | 0xFF, 0xFF, 0xFF, 0x01, 0xFF, 0xFF, 0xFF, 0x0F, 0xFC, 0x03, 0xF0, 0x0F, 439 | 0xFF, 0x00, 0x00, 0xC0, 0xFF, 0x03, 0xFF, 0x07, 0xF8, 0x07, 0xE0, 0x7F, 440 | 0x00, 0xFF, 0x00, 0x00, 0xFC, 0x07, 0xF8, 0x3F, 0x00, 0xF0, 0xFF, 0x03, 441 | 0xA8, 0xE5, 0x5F, 0xAA, 0xA8, 0xDA, 0x7F, 0xA5, 0x01, 0xFF, 0xAA, 0xAA, 442 | 0x00, 0xFE, 0x00, 0xFE, 0x0F, 0xF8, 0x03, 0xF0, 0x0F, 0xFF, 0x00, 0x00, 443 | 0xC0, 0xFF, 0x00, 0xFE, 0x07, 0xF0, 0x07, 0xF0, 0x7F, 0x00, 0x7F, 0x00, 444 | 0x00, 0xF8, 0x07, 0xE0, 0x3F, 0x00, 0xF0, 0xF7, 0x03, 0x00, 0xE0, 0x1F, 445 | 0x00, 0x00, 0xC0, 0x3F, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFE, 0x01, 446 | 0xF0, 0x1F, 0xF8, 0x07, 0xF0, 0x07, 0xFF, 0x00, 0x00, 0xE0, 0x3F, 0x00, 447 | 0xF8, 0x0F, 0xF8, 0x07, 0xF0, 0x7F, 0x00, 0x7F, 0x00, 0x00, 0xF8, 0x03, 448 | 0xC0, 0x3F, 0x00, 0xF0, 0xF3, 0x07, 0x00, 0xE0, 0x1F, 0x00, 0x00, 0xC0, 449 | 0x3F, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x01, 0xE0, 0x1F, 0xF8, 450 | 0x07, 0xF8, 0x07, 0xFF, 0x00, 0x00, 0xE0, 0x3F, 0x00, 0xF8, 0x0F, 0xF0, 451 | 0x0F, 0xF0, 0x7F, 0x00, 0x7F, 0x00, 0x00, 0xF8, 0x07, 0xC0, 0x3F, 0x00, 452 | 0xF0, 0xF3, 0x07, 0x00, 0xE0, 0x1F, 0x00, 0x00, 0x80, 0x7F, 0x00, 0x00, 453 | 0xFF, 0x00, 0x00, 0x00, 0xFE, 0x01, 0xE0, 0x1F, 0xF0, 0x0F, 0xF8, 0x03, 454 | 0xFF, 0x00, 0x00, 0xF0, 0x1F, 0x00, 0xF0, 0x1F, 0xF0, 0x0F, 0xF0, 0x7F, 455 | 0x00, 0x7F, 0x00, 0x00, 0xF8, 0x07, 0xC0, 0x3F, 0x00, 0xF8, 0xF3, 0x07, 456 | 0x00, 0xE0, 0x1F, 0x00, 0x00, 0x80, 0x7F, 0x00, 0x00, 0xFF, 0x00, 0x00, 457 | 0x00, 0xFE, 0x00, 0xE0, 0x3F, 0xF0, 0x0F, 0xFC, 0x03, 0xFF, 0x00, 0x00, 458 | 0xF0, 0x0F, 0x00, 0xE0, 0x1F, 0xF0, 0x0F, 0xF8, 0xFF, 0x80, 0x3F, 0x00, 459 | 0x00, 0xF8, 0x07, 0x80, 0x3F, 0x00, 0xF8, 0xE3, 0x0F, 0x00, 0xE0, 0x1F, 460 | 0x00, 0x00, 0xC0, 0x3F, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFE, 0x01, 461 | 0xC0, 0x1F, 0xE0, 0x1F, 0xFC, 0x03, 0xFF, 0x00, 0x00, 0xF0, 0x0F, 0x00, 462 | 0xE0, 0x1F, 0xF0, 0x0F, 0xF8, 0xFD, 0x80, 0x3F, 0x00, 0x00, 0xF8, 0x07, 463 | 0xC0, 0x3F, 0x00, 0xF8, 0xE1, 0x0F, 0x00, 0xE0, 0x1F, 0x00, 0x00, 0xC0, 464 | 0x3F, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFE, 0x01, 0xC0, 0x1F, 0xE0, 465 | 0x1F, 0xFE, 0x01, 0xFF, 0x00, 0x00, 0xF8, 0x0F, 0x00, 0xE0, 0x3F, 0xE0, 466 | 0x0F, 0xF8, 0xFD, 0x80, 0x3F, 0x00, 0x00, 0xFC, 0x03, 0xE0, 0x1F, 0x00, 467 | 0xFC, 0xE1, 0x1F, 0x00, 0xE0, 0x1F, 0x00, 0x00, 0x80, 0x3F, 0x00, 0x00, 468 | 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x01, 0xE0, 0x1F, 0xC0, 0x1F, 0xFE, 0x00, 469 | 0xFF, 0x00, 0x00, 0xF8, 0x0F, 0x00, 0xC0, 0x3F, 0xE0, 0x0F, 0xFC, 0xFC, 470 | 0x81, 0x3F, 0x00, 0x00, 0xF8, 0x07, 0xE0, 0x1F, 0x00, 0xFC, 0xE1, 0x1F, 471 | 0x00, 0xE0, 0x1F, 0x00, 0x00, 0xC0, 0x3F, 0x00, 0x00, 0xFF, 0x00, 0x00, 472 | 0x00, 0xFE, 0x00, 0xE0, 0x1F, 0xC0, 0x3F, 0xFE, 0x00, 0xFF, 0x00, 0x00, 473 | 0xF8, 0x07, 0x00, 0xC0, 0x3F, 0xE0, 0x1F, 0xF8, 0xFC, 0x81, 0x3F, 0x00, 474 | 0x00, 0xF8, 0x07, 0xFC, 0x0F, 0x00, 0xFC, 0xC1, 0x1F, 0x00, 0xE0, 0x1F, 475 | 0x00, 0x00, 0x80, 0x7F, 0x00, 0x00, 0xFF, 0x6A, 0x1A, 0x00, 0xFE, 0x01, 476 | 0xE0, 0x1F, 0x80, 0x3F, 0xFF, 0x00, 0xFF, 0x00, 0x00, 0xF8, 0x07, 0x00, 477 | 0xC0, 0x3F, 0xE0, 0x1F, 0xFC, 0xFC, 0xC1, 0x3F, 0x00, 0x00, 0xF8, 0xFF, 478 | 0xFF, 0x07, 0x00, 0xFE, 0xC0, 0x1F, 0x00, 0xE0, 0x1F, 0x00, 0x00, 0x80, 479 | 0x3F, 0x00, 0x00, 0xFF, 0xFF, 0x3F, 0x00, 0xFF, 0x01, 0xF0, 0x1F, 0x80, 480 | 0x7F, 0x7F, 0x00, 0xFF, 0x00, 0x00, 0xF8, 0x07, 0x00, 0xC0, 0x3F, 0xE0, 481 | 0x1F, 0xFC, 0xF8, 0x81, 0x1F, 0x00, 0x00, 0xF8, 0xFF, 0xFF, 0x03, 0x00, 482 | 0xFE, 0x80, 0x3F, 0x00, 0xE0, 0x1F, 0x00, 0x00, 0xC0, 0x3F, 0x00, 0x00, 483 | 0xFF, 0xFF, 0x1F, 0x00, 0xFE, 0x01, 0xFC, 0x0F, 0x00, 0x7F, 0x7F, 0x00, 484 | 0xFF, 0x00, 0x00, 0xF8, 0x07, 0x00, 0xC0, 0x3F, 0xE0, 0x1F, 0xFC, 0xF8, 485 | 0xC1, 0x1F, 0x00, 0x00, 0xF8, 0xFF, 0xFF, 0x01, 0x00, 0xFE, 0x80, 0x3F, 486 | 0x00, 0xE0, 0x1F, 0x00, 0x00, 0x80, 0x3F, 0x00, 0x00, 0xFF, 0xFF, 0x3F, 487 | 0x00, 0xFE, 0xFF, 0xFF, 0x0F, 0x00, 0xFF, 0x3F, 0x00, 0xFF, 0x00, 0x00, 488 | 0xF8, 0x07, 0x00, 0xC0, 0x3F, 0xC0, 0x1F, 0xFE, 0xF8, 0xC3, 0x1F, 0x00, 489 | 0x00, 0xF8, 0xFF, 0xFF, 0x0F, 0x00, 0xFF, 0x80, 0x3F, 0x00, 0xE0, 0x1F, 490 | 0x00, 0x00, 0xC0, 0x7F, 0x00, 0x00, 0xFF, 0xFF, 0x3F, 0x00, 0xFE, 0xFF, 491 | 0xFF, 0x07, 0x00, 0xFE, 0x3F, 0x00, 0xFF, 0x00, 0x00, 0xF8, 0x07, 0x00, 492 | 0xC0, 0x3F, 0xC0, 0x1F, 0x7E, 0xF8, 0xC3, 0x1F, 0x00, 0x00, 0xFC, 0xFF, 493 | 0xFF, 0x1F, 0x00, 0x7F, 0x80, 0x7F, 0x00, 0xE0, 0x1F, 0x00, 0x00, 0xC0, 494 | 0x7F, 0x00, 0x00, 0xFF, 0xFF, 0x1F, 0x00, 0xFE, 0xFF, 0xFF, 0x03, 0x00, 495 | 0xFE, 0x1F, 0x00, 0xFF, 0x00, 0x00, 0xF8, 0x07, 0x00, 0xC0, 0x3F, 0xC0, 496 | 0x3F, 0x7E, 0xF8, 0xC3, 0x1F, 0x00, 0x00, 0xF8, 0xBF, 0xFF, 0x3F, 0x00, 497 | 0x7F, 0x80, 0x7F, 0x00, 0xE0, 0x1F, 0x00, 0x00, 0x80, 0x3F, 0x00, 0x00, 498 | 0xFF, 0xFF, 0x3F, 0x00, 0xFF, 0xFF, 0xFF, 0x01, 0x00, 0xFC, 0x1F, 0x00, 499 | 0xFF, 0x00, 0x00, 0xF8, 0x07, 0x00, 0xC0, 0x3F, 0xC0, 0x1F, 0x7E, 0xF0, 500 | 0xE3, 0x1F, 0x00, 0x00, 0xF8, 0x07, 0xE0, 0x7F, 0x80, 0x7F, 0x00, 0x7F, 501 | 0x00, 0xE0, 0x1F, 0x00, 0x00, 0xC0, 0x3F, 0x00, 0x00, 0xFF, 0x02, 0x00, 502 | 0x00, 0xFE, 0xFF, 0x7F, 0x00, 0x00, 0xFC, 0x0F, 0x00, 0xFF, 0x00, 0x00, 503 | 0xF8, 0x07, 0x00, 0xC0, 0x3F, 0xC0, 0x3F, 0x7E, 0xF0, 0xC7, 0x0F, 0x00, 504 | 0x00, 0xF8, 0x07, 0x80, 0x7F, 0x80, 0xFF, 0xFF, 0xFF, 0x00, 0xE0, 0x1F, 505 | 0x00, 0x00, 0x80, 0x3F, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFE, 0xFF, 506 | 0x3F, 0x00, 0x00, 0xF8, 0x0F, 0x00, 0xFF, 0x00, 0x00, 0xF8, 0x0F, 0x00, 507 | 0xC0, 0x3F, 0x80, 0x3F, 0x7F, 0xF0, 0xE3, 0x0F, 0x00, 0x00, 0xF8, 0x07, 508 | 0x00, 0xFF, 0x80, 0xFF, 0xFF, 0xFF, 0x00, 0xE0, 0x1F, 0x00, 0x00, 0xC0, 509 | 0x3F, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0xE5, 0x7F, 0x00, 0x00, 510 | 0xF8, 0x07, 0x00, 0xFF, 0x00, 0x00, 0xF0, 0x0F, 0x00, 0xE0, 0x3F, 0x80, 511 | 0x3F, 0x3E, 0xF0, 0xE7, 0x0F, 0x00, 0x00, 0xF8, 0x03, 0x00, 0xFF, 0xC0, 512 | 0xFF, 0xFF, 0xFF, 0x00, 0xE0, 0x1F, 0x00, 0x00, 0x80, 0x7F, 0x00, 0x00, 513 | 0xFF, 0x00, 0x00, 0x00, 0xFE, 0x81, 0x7F, 0x00, 0x00, 0xF8, 0x07, 0x00, 514 | 0xFF, 0x00, 0x00, 0xF0, 0x0F, 0x00, 0xE0, 0x1F, 0x80, 0x3F, 0x3F, 0xF0, 515 | 0xE7, 0x0F, 0x00, 0x00, 0xFC, 0x07, 0x00, 0xFF, 0xC0, 0xFF, 0xFF, 0xFF, 516 | 0x01, 0xE0, 0x1F, 0x00, 0x00, 0xC0, 0x7F, 0x00, 0x00, 0xFF, 0x00, 0x00, 517 | 0x00, 0xFE, 0x80, 0xFF, 0x00, 0x00, 0xF8, 0x07, 0x00, 0xFF, 0x00, 0x00, 518 | 0xF0, 0x1F, 0x00, 0xE0, 0x1F, 0x80, 0x3F, 0x3F, 0xE0, 0xE7, 0x0F, 0x00, 519 | 0x00, 0xF8, 0x07, 0x00, 0xFE, 0xC0, 0xFF, 0xFF, 0xFF, 0x01, 0xE0, 0x1F, 520 | 0x00, 0x00, 0xC0, 0x3F, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFE, 0x01, 521 | 0xFF, 0x00, 0x00, 0xF8, 0x07, 0x00, 0xFF, 0x00, 0x00, 0xF0, 0x1F, 0x00, 522 | 0xF0, 0x1F, 0x80, 0x3F, 0x3F, 0xE0, 0xE7, 0x07, 0x00, 0x00, 0xF8, 0x03, 523 | 0x00, 0xFF, 0xE0, 0xFF, 0xFF, 0xFF, 0x01, 0xE0, 0x1F, 0x00, 0x00, 0xC0, 524 | 0x3F, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFE, 0x01, 0xFF, 0x01, 0x00, 525 | 0xF8, 0x07, 0x00, 0xFF, 0x00, 0x00, 0xE0, 0x3F, 0x00, 0xF8, 0x0F, 0x00, 526 | 0xFF, 0x1F, 0xE0, 0xFF, 0x07, 0x00, 0x00, 0xF8, 0x07, 0x00, 0xFF, 0xE0, 527 | 0x1F, 0x00, 0xFC, 0x01, 0xE0, 0x1F, 0x00, 0x00, 0x80, 0x3F, 0x00, 0x00, 528 | 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x01, 0xFE, 0x03, 0x00, 0xF8, 0x07, 0x00, 529 | 0xFF, 0x00, 0x00, 0xE0, 0x3F, 0x00, 0xF8, 0x0F, 0x00, 0xFF, 0x1F, 0xC0, 530 | 0xEF, 0x07, 0x00, 0x00, 0xF8, 0x03, 0x00, 0xFF, 0xE0, 0x1F, 0x00, 0xFC, 531 | 0x03, 0xE0, 0x1F, 0x00, 0x00, 0x80, 0x3F, 0x00, 0x00, 0xFF, 0x00, 0x00, 532 | 0x00, 0xFE, 0x00, 0xFE, 0x03, 0x00, 0xF8, 0x07, 0x00, 0xFF, 0x00, 0x00, 533 | 0xC0, 0xFF, 0x00, 0xFE, 0x07, 0x00, 0xFF, 0x1F, 0xC0, 0xFF, 0x07, 0x00, 534 | 0x00, 0xF8, 0x07, 0xC0, 0x7F, 0xF0, 0x0F, 0x00, 0xFC, 0x03, 0xE0, 0x1F, 535 | 0x00, 0x00, 0xC0, 0x7F, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFE, 0x01, 536 | 0xFC, 0x07, 0x00, 0xF8, 0x07, 0x00, 0xFF, 0x14, 0x8A, 0xC0, 0xFF, 0x01, 537 | 0xFF, 0x07, 0x00, 0xFF, 0x1F, 0xC0, 0xFF, 0x07, 0x00, 0x00, 0xF8, 0x07, 538 | 0xE0, 0x7F, 0xF0, 0x0F, 0x00, 0xFC, 0x07, 0xE0, 0x1F, 0x00, 0x00, 0xC0, 539 | 0x3F, 0x00, 0x00, 0xFF, 0x44, 0x44, 0x00, 0xFF, 0x01, 0xF8, 0x07, 0x00, 540 | 0xF8, 0x07, 0x00, 0xFF, 0xFF, 0xFF, 0x80, 0xFF, 0xFF, 0xFF, 0x03, 0x00, 541 | 0xFE, 0x0F, 0xC0, 0xFF, 0x03, 0x00, 0x00, 0xFC, 0xFF, 0xFF, 0x7F, 0xF0, 542 | 0x0F, 0x00, 0xF8, 0x07, 0xE0, 0x1F, 0x00, 0x00, 0x80, 0x3F, 0x00, 0x00, 543 | 0xFF, 0xFF, 0xFF, 0x01, 0xFE, 0x01, 0xF8, 0x0F, 0x00, 0xF8, 0x07, 0x00, 544 | 0xFF, 0xFF, 0xFF, 0x00, 0xFF, 0xFF, 0xFF, 0x01, 0x00, 0xFE, 0x0F, 0xC0, 545 | 0xFF, 0x03, 0x00, 0x00, 0xF8, 0xFF, 0xFF, 0x3F, 0xF8, 0x07, 0x00, 0xF8, 546 | 0x07, 0xE0, 0x1F, 0x00, 0x00, 0xC0, 0x3F, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 547 | 0x01, 0xFE, 0x01, 0xF0, 0x0F, 0x00, 0xF8, 0x07, 0x00, 0xFF, 0xFF, 0xFF, 548 | 0x00, 0xFE, 0xFF, 0xFF, 0x00, 0x00, 0xFE, 0x0F, 0x80, 0xFF, 0x03, 0x00, 549 | 0x00, 0xF8, 0xFF, 0xFF, 0x1F, 0xF8, 0x07, 0x00, 0xF8, 0x07, 0xE0, 0x1F, 550 | 0x00, 0x00, 0x80, 0x7F, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0x01, 0xFE, 0x01, 551 | 0xF0, 0x1F, 0x00, 0xF8, 0x07, 0x00, 0xFF, 0xFF, 0xFF, 0x00, 0xFC, 0xFF, 552 | 0x7F, 0x00, 0x00, 0xFE, 0x0F, 0x80, 0xFF, 0x03, 0x00, 0x00, 0xF8, 0xFF, 553 | 0xFF, 0x0F, 0xF8, 0x07, 0x00, 0xF0, 0x0F, 0xE0, 0x1F, 0x00, 0x00, 0x80, 554 | 0x3F, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0x01, 0xFE, 0x00, 0xE0, 0x3F, 0x00, 555 | 0xF8, 0x07, 0x00, 0xFF, 0xFF, 0xFF, 0x00, 0xF8, 0xFF, 0x3F, 0x00, 0x00, 556 | 0xFE, 0x07, 0x80, 0xFF, 0x03, 0x00, 0x00, 0xF8, 0xFF, 0xFF, 0x03, 0xF8, 557 | 0x07, 0x00, 0xF0, 0x0F, 0xE0, 0x1F, 0x00, 0x00, 0xC0, 0x3F, 0x00, 0x00, 558 | 0xFF, 0xFF, 0xFF, 0x01, 0xFE, 0x01, 0xE0, 0x3F, 0x00, 0xF8, 0x07, 0x00, 559 | 0xFF, 0xFF, 0xFF, 0x00, 0xE0, 0xFF, 0x0F, 0x00, 0x00, 0xFE, 0x0F, 0x80, 560 | 0xFF, 0x03, 0x00, 0x00, 0xFC, 0xFF, 0x7F, 0x00, 0xFC, 0x03, 0x00, 0xF0, 561 | 0x1F, 0xE0, 0x1F, 0x00, 0x00, 0xC0, 0x7F, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 562 | 0x01, 0xFE, 0x01, 0xC0, 0x7F, 0x00, 0xF8, 0x07, 0x00, 0x88, 0x08, 0x02, 563 | 0x00, 0x00, 0xFE, 0x01, 0x00, 0x00, 0x08, 0x00, 0x00, 0x88, 0x00, 0x00, 564 | 0x00, 0x20, 0x00, 0x04, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 565 | 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x88, 0x20, 0x20, 0x00, 0x02, 0x00, 566 | 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 567 | }; 568 | -------------------------------------------------------------------------------- /src/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | // Third-party libs 5 | #include 6 | 7 | // Special header generated by ulptool 8 | #include "ulp_main.h" 9 | 10 | // Local headers 11 | #include "SMP7C.h" 12 | #include "pm.h" 13 | #include "stash.h" 14 | #include "ulp_common.h" 15 | 16 | #define ULP_WAKEUP_PERIOD_US (1000 * 1000) // 1 sec 17 | #define DEBUG_SERIAL_BAUD 115200 18 | #define STASH_SAVE_PERIOD \ 19 | 10 // Save status to non-volatile storage for every N wake ups 20 | 21 | extern const uint8_t ulp_main_bin_start[] asm("_binary_ulp_main_bin_start"); 22 | extern const uint8_t ulp_main_bin_end[] asm("_binary_ulp_main_bin_end"); 23 | 24 | // RTC variables 25 | RTC_DATA_ATTR uint32_t RTC_SMP7C_frameInd = 0; 26 | RTC_DATA_ATTR uint32_t RTC_batV = 0; 27 | 28 | static void init_run_ulp(uint32_t usec) { 29 | ulp_set_wakeup_period(0, usec); 30 | esp_deep_sleep_disable_rom_logging(); // Suppress boot messages 31 | 32 | esp_err_t err = ulptool_load_binary( 33 | 0, ulp_main_bin_start, 34 | (ulp_main_bin_end - ulp_main_bin_start) / sizeof(uint32_t)); 35 | err = ulp_run((&ulp_entry - RTC_SLOW_MEM) / sizeof(uint32_t)); 36 | 37 | if(err) { 38 | Serial.println("Error Starting ULP Coprocessor"); 39 | } 40 | 41 | esp_sleep_enable_ulp_wakeup(); 42 | } 43 | 44 | void start_deep_sleep() { 45 | // Save variables into RTC SLOW MEM 46 | RTC_SMP7C_frameInd = SMP7C.frameInd; 47 | 48 | PM::prepareRtcPinsForDeepSleep(); 49 | esp_deep_sleep_start(); 50 | } 51 | 52 | void reboot_by_deep_sleep() { 53 | PM::disableBusPower(); 54 | init_run_ulp(ULP_WAKEUP_PERIOD_US); 55 | ulp__counterPeriodicTask = (ulp__counterPeriodicTask & 0xFFFF0000) | 56 | ((COUNTER_PERIODIC_TASK - 3) & UINT16_MAX); 57 | start_deep_sleep(); 58 | } 59 | 60 | void setup() { 61 | Serial.begin(DEBUG_SERIAL_BAUD); 62 | Serial.printf("started: %lu\n", millis()); 63 | 64 | esp_sleep_wakeup_cause_t cause = esp_sleep_get_wakeup_cause(); 65 | if(cause != ESP_SLEEP_WAKEUP_ULP) { 66 | Serial.println("Not ULP wakeup"); 67 | 68 | // Restore variables from non-volatile memory 69 | if(Stash::restore(SMP7C.frameInd)) { 70 | Serial.printf("Restored from Stash: frameInd: %u\n", 71 | SMP7C.frameInd); 72 | } else { 73 | Serial.printf("Failed to load from Stash\n"); 74 | } 75 | } else { 76 | Serial.printf("ULP wakeup\n"); 77 | PM::resetRtcPins(); 78 | 79 | // Restore variables from RTC SLOW MEM 80 | SMP7C.frameInd = RTC_SMP7C_frameInd; 81 | Serial.printf("Restored: frameInd: %u\n", SMP7C.frameInd); 82 | 83 | // Save variables to non-volatile memory 84 | if(!(SMP7C.frameInd % STASH_SAVE_PERIOD)) { 85 | Serial.println("Saving to Stash."); 86 | if(!Stash::save(SMP7C.frameInd)) { 87 | Serial.printf("Failed to save to Stash\n"); 88 | } 89 | } 90 | } 91 | 92 | // Turn on bus power for microSD and EPD. 93 | PM::enableBusPower(); 94 | 95 | // Init (Init FS, open files, etc.) 96 | if(!SMP7C.begin(DEBUG_SERIAL_BAUD)) { 97 | // Restart immediately if fails 98 | Serial.println("SMP7C.begin() failed. Rebooting."); 99 | reboot_by_deep_sleep(); 100 | return; 101 | } 102 | 103 | // Check battery level 104 | bool isLowBattery = PM::isLowBattery(); 105 | if(isLowBattery) { 106 | Serial.println("Low battery detected."); 107 | SMP7C.renderLowVoltageCaution(); 108 | } else { 109 | // Render (Load a frame, send it to EPD.) 110 | if(!SMP7C.renderFrame()) { 111 | Serial.println("SMP7C.renderFrame() failed. Rebooting."); 112 | reboot_by_deep_sleep(); 113 | return; 114 | } 115 | } 116 | 117 | // Prepare for deep sleep. 118 | init_run_ulp(ULP_WAKEUP_PERIOD_US); 119 | PM::setWaitBusyRequest(ulp_status); // EPD_BUSY will be handled by ULP. 120 | if(isLowBattery) { 121 | PM::setForeverSleepRequest(ulp_status); // Never wake up by ULP. 122 | } 123 | 124 | Serial.printf("Elapsed: %lu\n", millis()); 125 | start_deep_sleep(); 126 | } 127 | 128 | void loop() { 129 | // NOP 130 | } 131 | -------------------------------------------------------------------------------- /src/pm.cpp: -------------------------------------------------------------------------------- 1 | #include "pm.h" 2 | 3 | namespace PM { 4 | 5 | gpio_num_t mmcIoToShutdown[] = { 6 | GPIO_NUM_14, // SDMMC_CLK 7 | GPIO_NUM_15, // SDMMC_CMD 8 | }; 9 | 10 | void enableBusPower() { 11 | pinMode(PM_PIN_BUS_POWER_SW, OUTPUT); 12 | digitalWrite(PM_PIN_BUS_POWER_SW, HIGH); 13 | } 14 | 15 | void disableBusPower() { 16 | pinMode(PM_PIN_BUS_POWER_SW, OUTPUT); 17 | digitalWrite(PM_PIN_BUS_POWER_SW, LOW); 18 | } 19 | 20 | void prepareRtcPinsForDeepSleep() { 21 | // Allow ULP to read EPD_BUSY pin. 22 | rtc_gpio_init(PM_PIN_EPD_BUSY); 23 | rtc_gpio_set_direction(PM_PIN_EPD_BUSY, RTC_GPIO_MODE_INPUT_ONLY); 24 | rtc_gpio_pullup_en(PM_PIN_EPD_BUSY); 25 | 26 | // Allow ULP to handle BUS_POWER_SW pin. 27 | rtc_gpio_init(PM_PIN_BUS_POWER_SW); 28 | rtc_gpio_set_direction(PM_PIN_BUS_POWER_SW, RTC_GPIO_MODE_OUTPUT_ONLY); 29 | rtc_gpio_set_level(PM_PIN_BUS_POWER_SW, 1); 30 | 31 | // Set some of MMC_SD pins as INPUT to avoid current leakage. 32 | for(auto &&io : mmcIoToShutdown) { 33 | rtc_gpio_init(io); 34 | rtc_gpio_set_direction(io, RTC_GPIO_MODE_INPUT_ONLY); 35 | rtc_gpio_pullup_dis(io); 36 | } 37 | } 38 | 39 | void resetRtcPins() { 40 | for(auto &&io : mmcIoToShutdown) { 41 | rtc_gpio_deinit(io); 42 | } 43 | } 44 | 45 | void setWaitBusyRequest(uint32_t &status) { status |= PM_STATUS_WAIT_BUSY_REQ; } 46 | 47 | void setForeverSleepRequest(uint32_t &status) { 48 | status |= PM_STATUS_FOREVER_SLEEP_REQ; 49 | } 50 | 51 | uint16_t readBatteryVoltage() { 52 | analogSetAttenuation(ADC_0db); 53 | 54 | return analogReadMilliVolts(GPIO_NUM_34) * PM_BAT_V_FACTOR; 55 | } 56 | 57 | bool isLowBattery() { return readBatteryVoltage() <= PM_VBAT_TH_EMERGENCY; } 58 | 59 | } // namespace PM 60 | -------------------------------------------------------------------------------- /src/pm.h: -------------------------------------------------------------------------------- 1 | #include "ulp_common.h" 2 | #include 3 | #include 4 | #include 5 | 6 | #define PM_BAT_V_FACTOR 4.0 // 1k / (3k + 1k) 7 | 8 | #define PM_VBAT_TH_EMERGENCY 3500 // 3500 mV 9 | 10 | #define PM_PIN_EPD_BUSY GPIO_NUM_25 11 | #define PM_PIN_BUS_POWER_SW GPIO_NUM_32 12 | #define PM_PIN_BAT_V_MON GPIO_NUM_34 // ADC1_6 13 | 14 | namespace PM { 15 | 16 | void enableBusPower(); 17 | void disableBusPower(); 18 | 19 | void prepareRtcPinsForDeepSleep(); 20 | void resetRtcPins(); 21 | 22 | void setWaitBusyRequest(uint32_t &status); 23 | void setForeverSleepRequest(uint32_t &status); 24 | 25 | uint16_t readBatteryVoltage(); 26 | bool isLowBattery(); 27 | 28 | } // namespace PM 29 | -------------------------------------------------------------------------------- /src/stash.cpp: -------------------------------------------------------------------------------- 1 | #include "stash.h" 2 | 3 | Preferences preferences; 4 | 5 | namespace Stash { 6 | 7 | bool save(uint32_t frameInd) { 8 | if(!preferences.begin(STASH_PREFERENCE_NS, false)) { 9 | return false; 10 | } 11 | 12 | preferences.putUInt(STASH_KEY_FRAME_IND, frameInd); 13 | 14 | preferences.end(); 15 | } 16 | 17 | bool restore(uint32_t &frameInd) { 18 | if(!preferences.begin(STASH_PREFERENCE_NS, true)) { 19 | return false; 20 | } 21 | 22 | frameInd = preferences.getUInt(STASH_KEY_FRAME_IND, 0); 23 | 24 | preferences.end(); 25 | } 26 | 27 | } // namespace Stash 28 | -------------------------------------------------------------------------------- /src/stash.h: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #define STASH_PREFERENCE_NS "smp7c" 5 | #define STASH_KEY_FRAME_IND "frameInd" 6 | 7 | namespace Stash { 8 | 9 | bool save(uint32_t frameInd); 10 | bool restore(uint32_t &frameInd); 11 | 12 | } // namespace Stash 13 | -------------------------------------------------------------------------------- /test/README: -------------------------------------------------------------------------------- 1 | 2 | This directory is intended for PlatformIO Unit Testing and project tests. 3 | 4 | Unit Testing is a software testing method by which individual units of 5 | source code, sets of one or more MCU program modules together with associated 6 | control data, usage procedures, and operating procedures, are tested to 7 | determine whether they are fit for use. Unit testing finds problems early 8 | in the development cycle. 9 | 10 | More information about PlatformIO Unit Testing: 11 | - https://docs.platformio.org/page/plus/unit-testing.html 12 | -------------------------------------------------------------------------------- /ulp/.gitignore: -------------------------------------------------------------------------------- 1 | # Ignore build products 2 | *.o 3 | *.d 4 | *.ld 5 | *.elf 6 | *.map 7 | *.sym 8 | *.pS 9 | *.lst 10 | *.bin 11 | -------------------------------------------------------------------------------- /ulp/ulp_common.h: -------------------------------------------------------------------------------- 1 | // This header is shared between the main program and ULP. 2 | // Therefore it should only contain "plain" values. 3 | #define COUNTER_PERIODIC_TASK 3600 // Wake up period. 3600 sec = 1 hour 4 | 5 | #define PM_STATUS_WAIT_BUSY_REQ 0x1 6 | #define PM_STATUS_WAIT_BUSY_REQ_M 0xFFFE 7 | #define PM_STATUS_FOREVER_SLEEP_REQ 0x2 8 | -------------------------------------------------------------------------------- /ulp/ulp_main.h: -------------------------------------------------------------------------------- 1 | // Variable definitions for ESP32ULP 2 | // This file is generated automatically by esp32ulp_mapgen.py utility 3 | 4 | #pragma once 5 | 6 | extern uint32_t ulp__counterPeriodicTask; 7 | extern uint32_t ulp_entry; 8 | extern uint32_t ulp_epdBusyStatus; 9 | extern uint32_t ulp_status; 10 | -------------------------------------------------------------------------------- /ulp/ulp_powermanager.c: -------------------------------------------------------------------------------- 1 | #ifdef _ULPCC_ // do not add code above this line 2 | #include 3 | 4 | #include "ulp_common.h" 5 | 6 | // See 7 | // https://github.com/espressif/esp-idf/blob/98e15df7f6af8f7f26f895b31e18049198dcc938/components/driver/rtc_module.c#L46 8 | #define PIN_RTC_EPD_BUSY 6 // GPIO25 RTCIO6 9 | #define PIN_RTC_BUS_POWER_SW 9 // GPIO32 RTCIO9 10 | 11 | // Global variables 12 | unsigned _counterPeriodicTask = 0; 13 | unsigned status = 0; 14 | unsigned epdBusyStatus = 0; 15 | 16 | void entry() { 17 | // Un hold GPIO32 (BUS_POWER_SW) 18 | reg_wr(RTC_IO_XTAL_32K_PAD_REG, RTC_IO_X32P_HOLD_S, RTC_IO_X32P_HOLD_S, 0); 19 | 20 | if(status & PM_STATUS_WAIT_BUSY_REQ) { 21 | // Read EPD_BUSY H/L 22 | epdBusyStatus = 23 | reg_rd(RTC_GPIO_IN_REG, RTC_GPIO_IN_NEXT_S + PIN_RTC_EPD_BUSY, 24 | RTC_GPIO_IN_NEXT_S + PIN_RTC_EPD_BUSY); 25 | 26 | // If not busy (H) 27 | if(epdBusyStatus) { 28 | // Clear the flag. 29 | status &= PM_STATUS_WAIT_BUSY_REQ_M; 30 | // Disable Pull-up of GPIO25 (EPD_BUSY). Avoid current leakage. 31 | reg_wr(RTC_IO_PAD_DAC1_REG, RTC_IO_PDAC1_RUE_S, RTC_IO_PDAC1_RUE_S, 32 | 0); 33 | // Put GPIO32 (BUS_POWER_SW) to LOW. Power off EPD & SD bus. 34 | reg_wr(RTC_GPIO_OUT_W1TC_REG, RTC_GPIO_OUT_DATA_W1TC_S + 9, 35 | RTC_GPIO_OUT_DATA_W1TC_S + 9, 1); 36 | } 37 | } 38 | // Hold GPIO32 (BUS_POWER_SW). To keep Bus Power ON. 39 | reg_wr(RTC_IO_XTAL_32K_PAD_REG, RTC_IO_X32P_HOLD_S, RTC_IO_X32P_HOLD_S, 1); 40 | 41 | // If FOREVER_SLEEP_REQ is set and not processing WAIT_BUSY_REQ 42 | if((status & PM_STATUS_FOREVER_SLEEP_REQ) && 43 | !(status & PM_STATUS_WAIT_BUSY_REQ)) { 44 | halt(); 45 | } 46 | 47 | _counterPeriodicTask++; 48 | if(_counterPeriodicTask >= COUNTER_PERIODIC_TASK) { 49 | _counterPeriodicTask = 0; 50 | wake(); 51 | // Un hold GPIO32 (BUS_POWER_SW) 52 | reg_wr(RTC_IO_XTAL_32K_PAD_REG, RTC_IO_X32P_HOLD_S, RTC_IO_X32P_HOLD_S, 53 | 0); 54 | halt(); 55 | } 56 | } 57 | #endif // do not add code after here 58 | -------------------------------------------------------------------------------- /ulp/ulp_powermanager.s: -------------------------------------------------------------------------------- 1 | .data 2 | .global _counterPeriodicTask 3 | _counterPeriodicTask: 4 | .long 0x0 5 | .global status 6 | status: 7 | .long 0x0 8 | .global epdBusyStatus 9 | epdBusyStatus: 10 | .long 0x0 11 | .global entry 12 | .text 13 | entry: 14 | reg_wr 0x3ff4848c,24,24,0 15 | move r2,status 16 | ld r2,r2,0 17 | and r2,r2,1 18 | move r2,r2 #if r2 == 0 goto L.2 19 | jump L.2, eq 20 | reg_rd 0x3ff48424,20,20 21 | move r1,epdBusyStatus 22 | st r0,r1,0 23 | move r2,epdBusyStatus 24 | ld r2,r2,0 25 | move r2,r2 #if r2 == 0 goto L.4 26 | jump L.4, eq 27 | move r2,status 28 | ld r1,r2,0 29 | and r1,r1,0xfffe 30 | st r1,r2,0 31 | reg_wr 0x3ff48484,27,27,0 32 | reg_wr 0x3ff48408,23,23,1 33 | L.4: 34 | L.2: 35 | reg_wr 0x3ff4848c,24,24,1 36 | move r2,status 37 | ld r2,r2,0 38 | and r1,r2,2 39 | move r1,r1 #if r1 == 0 goto L.6 40 | jump L.6, eq 41 | and r2,r2,1 42 | move r2,r2 #{ if r2 goto L.6 43 | jump 1f, eq 44 | jump L.6 45 | 1: #} 46 | halt 47 | L.6: 48 | move r2,_counterPeriodicTask 49 | ld r1,r2,0 50 | add r1,r1,1 51 | st r1,r2,0 52 | ld r2,r2,0 53 | move r1,3600 54 | sub r2,r2,r1 #{ if r2 < r1 goto L.8 55 | add r2,r2,r1 56 | jump L.8, ov #} 57 | move r2,_counterPeriodicTask 58 | move r1,0 59 | st r1,r2,0 60 | wake 61 | reg_wr 0x3ff4848c,24,24,0 62 | halt 63 | L.8: 64 | L.1: 65 | 66 | halt 67 | --------------------------------------------------------------------------------