├── .gitignore ├── LICENSE ├── README.md ├── _README.md ├── data └── .gitignore ├── img ├── DXTracker.jpeg ├── Map_1.png ├── Map_2.png ├── Map_3.png ├── Menu.png ├── flash_1.png ├── flash_2.png └── screenshot.png ├── include └── README ├── lib └── README ├── platformio.ini ├── src ├── DXTracker.cpp ├── DXTracker.h ├── Web │ ├── LICENSE │ ├── README.md │ ├── bin2array.py │ ├── button.png │ ├── button_full.png │ ├── index.html │ ├── indexBasicAndGrey.html │ ├── indexCore2.html │ ├── m5.png │ ├── m5_basic.png │ ├── m5_basic_full.png │ ├── m5_core2.png │ ├── m5_core2_full.png │ ├── m5_full.png │ ├── screenshot.bmp │ └── screenshot.png ├── data.h ├── font.h ├── functions.h ├── map.h ├── menu.h ├── settings.h ├── tasks.h ├── tools.h └── webIndex.h └── test └── README /.gitignore: -------------------------------------------------------------------------------- 1 | .pio 2 | .vscode 3 | .DS_Store -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Armel FAUVEAU 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 | # DXTracker 2 | ![basic](https://img.shields.io/badge/M5Stack-BASIC-blue) 3 | ![grey](https://img.shields.io/badge/M5Stack-GREY-blue) 4 | ![fire](https://img.shields.io/badge/M5Stack-FIRE-orange) 5 | ![core2](https://img.shields.io/badge/M5Stack-CORE2-green) 6 | ![aws](https://img.shields.io/badge/M5Stack-AWS-orange) 7 | 8 | ![ATOM Lite](https://img.shields.io/badge/M5Stack-ATOM%20Lite-darkgrey) 9 | ![ATOM Echo Smart](https://img.shields.io/badge/M5Stack-ATOM%20Echo%20Smart-white) 10 | ![ATOM Matrix](https://img.shields.io/badge/M5Stack-ATOM%20Matrix-blue) 11 | 12 | ![licence](https://img.shields.io/github/license/armel/DXTracker) 13 | ![language](https://img.shields.io/github/languages/top/armel/DXTracker) 14 | ![size](https://img.shields.io/github/repo-size/armel/DXTracker) 15 | ![version](https://img.shields.io/github/v/release/armel/DXTracker) 16 | ![activity](https://img.shields.io/github/commit-activity/y/armel/DXTracker) 17 | 18 | This project will no longer be maintained. Please follow the new project [M5HamRadio](https://github.com/armel/M5HamRadio) to get the latest news and firmwares. 19 | 20 | # Credits 21 | 22 | Many thanks to all my [donors](#donations) and to [@HamRadioDude](https://www.youtube.com/watch?v=SCPEO7Eiy1E&ab_channel=HAMRADIODUDE), [@TechMindsYT](https://www.youtube.com/watch?v=_UbwZrT2O8w) and [@ManuelLausmann](https://www.youtube.com/watch?v=DrcMoVNwG_8) for their videos 🙏🏻 23 | 24 | Last but not least, many thanks to... 25 | 26 | | Project | Author | Link | 27 | |:------------------- | :---------------------------------------------------- | :------------------------------------------- | 28 | | M5Stack | [M5Stack](https://twitter.com/M5Stack) | https://github.com/m5stack/M5Stack | 29 | | M5Stack-SD-Updater | [Tobozo](https://twitter.com/TobozoTagada) | https://github.com/tobozo/M5Stack-SD-Updater | 30 | 31 | Icom and the Icom logo are registered trademarks of Icom Incorporated (Japan) in Japan, the United States, the United Kingdom, Germany, France, Spain, Russia, Australia, New Zealand, and/or other countries. 32 | 33 | # Donations 34 | 35 | Special thanks to Rolf Schroeder DL8BAG, Brian Garber WB8AM, Matt B-Wilkinson M6VWM, Robert Agnew KD0TVP, Meinhard Frank Günther DL0CN, Johan Hansson SM0TSC, Tadeusz Pater VA7CPM, Frederic Ulmer F4ESO, Joshua Murray M0JMO, Mark Hammond N8MH, Angel Mateu Muzzio EA4GIG, Hiroshi Sasaki JL7KGW, Robert John Williams VK3IE, Mark Bumstead M0IAX, Félix Symann F1VEO, Patrick Ruhl DG2YRP, Michael Beck DH5DAX, Philippe Nicolas F4IQP, Timothy Nustad KD9KHZ, Martin Blanz DL9SAD, Edmund Thompson AE4TQ, Gregory Kiyoi KN6RUQ, Patrick Samson F6GWE, George Kokolakis SV3QUP and Ambrose "Bo" Barry W4GHV for their donations. That’s so kind of them. Thanks so much 🙏🏻 36 | 37 | If you find this project fun and useful then [offer me a beer](https://www.paypal.me/F4HWN) :) 38 | 39 | You could use the code F4HWN in order to get 5% discount on the [M5Stack shop](https://shop.m5stack.com/?ref=LUxetaH4) 🎁 40 | 41 | By the way, you can follow me on [Twitter](https://twitter.com/F4HWN) and post pictures of your installation with your M5Stack. It always makes me happy ;) -------------------------------------------------------------------------------- /_README.md: -------------------------------------------------------------------------------- 1 | # DXTracker 2 | ![basic](https://img.shields.io/badge/M5Stack-BASIC-blue) 3 | ![grey](https://img.shields.io/badge/M5Stack-GREY-blue) 4 | ![fire](https://img.shields.io/badge/M5Stack-FIRE-orange) 5 | ![core2](https://img.shields.io/badge/M5Stack-CORE2-green) 6 | ![aws](https://img.shields.io/badge/M5Stack-AWS-orange) 7 | 8 | ![ATOM Lite](https://img.shields.io/badge/M5Stack-ATOM%20Lite-darkgrey) 9 | ![ATOM Echo Smart](https://img.shields.io/badge/M5Stack-ATOM%20Echo%20Smart-white) 10 | ![ATOM Matrix](https://img.shields.io/badge/M5Stack-ATOM%20Matrix-blue) 11 | 12 | ![licence](https://img.shields.io/github/license/armel/DXTracker) 13 | ![language](https://img.shields.io/github/languages/top/armel/DXTracker) 14 | ![size](https://img.shields.io/github/repo-size/armel/DXTracker) 15 | ![version](https://img.shields.io/github/v/release/armel/DXTracker) 16 | ![activity](https://img.shields.io/github/commit-activity/y/armel/DXTracker) 17 | 18 | **You could use the code F4HWN in order to get 5% discount on the [M5Stack shop](https://shop.m5stack.com/?ref=LUxetaH4) 🎁** 19 | 20 | **For Germans, take a look at this other excellent [video](https://www.youtube.com/watch?v=UR8D_PGrvio&ab_channel=ManuelLausmann) released by [@ManuelLausmann](https://twitter.com/ManuelLausmann).** 21 | 22 | **Many thanks to them and all my [donors](#donations)🙏🏻** 23 | 24 | The DXTracker project offers several functionalities to monitor the Sun's activity, the opening conditions and the traffic activity on the Amateur Radio bands, as well as the transit forecasts of the Amateur Radio satellites. 25 | 26 | For example, the DXTracker displays the following informations in real time: 27 | 28 | - Solar datas, 29 | - SFI, 30 | - Sunspots, 31 | - A-Index, 32 | - K-Index, 33 | - X-Ray, 34 | - Helium Line, 35 | - Proton Flux, 36 | - Electron Flux, 37 | - Aurora, 38 | - Solard Wind, 39 | - Magnetic Field, 40 | - Signal Noise. 41 | - Propagation conditions on VHF band, 42 | - Propagation conditions on HF band (dayly, nightly), 43 | - Greyline, 44 | - Satellite transit forecasts, 45 | - Cluster DX. 46 | 47 | ![Map1](https://github.com/armel/DXTracker/blob/main/img/Map_1.png) 48 | ![Map2](https://github.com/armel/DXTracker/blob/main/img/Map_2.png) 49 | ![Map3](https://github.com/armel/DXTracker/blob/main/img/Map_3.png) 50 | ![Menu](https://github.com/armel/DXTracker/blob/main/img/Menu.png) 51 | 52 | # Technical architecture 53 | 54 | ## Quick overview 55 | 56 | [M5Stack](https://m5stack.com/) is based on an ESP-32, dual core, which can be clocked up to 240 MHz. M5Stack has 16 MB of flash memory. Like all ESPs, Wi-Fi is of course integrated. The 2 inch IPS color display, based on the ILI9342C chipset, has a comfortable resolution of 320 x 240 pixels. It is very bright. The integrated battery is 110 mAh. It is possible to add an additional battery (700 or 800mAh) if needed. 57 | 58 | In terms of size and weight, it is very compact: 54 x 54 x 18mm for 47.2g. Can be carried in the pocket without any problem ;) 59 | 60 | ## Detailed technical specs 61 | 62 | Here are the detailed technical specs, for the curious: 63 | 64 | | Resources | Description | 65 | | --------- | ------------ | 66 | |ESP32| 240MHz dual core, 600 DMIPS, 520KB SRAM, Wi-Fi, dual mode Bluetooth 67 | Flash| Memory 16MB| 68 | |Power| Input 5V @ 500mA| 69 | |Port| TypeC x 1, GROVE(I2C+I/0+UART) x 1| 70 | |Core|Bottom Port PIN (G1,G2,G3,G16, G17, G18, G19, G21, G22, G23, G25, G26, G35, G36)| 71 | |IPS Screen| 2 inch, 320x240 Colorful TFT LCD, ILI9342C, max brightness 853nit| 72 | |Button| Custom button x 3| 73 | |Speaker| 1W-0928| 74 | |Battery| 110mAh @ 3.7V| 75 | |Antenna| 2.4G 3D Antenna| 76 | |Operating Temperature| 32°F to 104°F ( 0°C to 40°C )| 77 | |Net weight| 47.2g| 78 | |Gross weight| 93g| 79 | |Product Size| 54 x 54 x 18mm| 80 | |Package Size |95 x 65 x 25mm| 81 | |Case Material| Plastic ( PC )| 82 | 83 | ## In addition 84 | 85 | About the QSJ, count around 45€. You then have a complete development platform, totally autonomous, programmable in C and C++, MicroPython and UIFlow, from Linux, Windows or MacOS, all in a compact and ultra ergonomic box. 86 | 87 | # Installation 88 | 89 | ## Prepare the stack 90 | 91 | The easiest way is to install [PlateformIO for VSCode](https://platformio.org/install/ide?install=vscode) on your PC (Linux or Windows) or on your Mac (Intel or M1). It is a cross-platform and multilanguage development environment that is powerful and pleasant to use. 92 | 93 | Then, still on your PC or Mac, clone the DXTracker project via the command : 94 | 95 | `git clone https://github.com/armel/DXTracker.git` 96 | 97 | You can also download a [zip archive](https://github.com/armel/DXTracker/releases) of the project, if you prefer, and unzip it. 98 | 99 | As I said, an excellent [video](https://www.youtube.com/watch?v=SCPEO7Eiy1E&ab_channel=HAMRADIODUDE) was released by @HamRadioDude about the installation of the IC705SMeter project. It can help you ! 100 | 101 | ## Configuration 102 | 103 | Open the DXTracker project with PlateformIO for VSCode. 104 | 105 | ### File `src/settings.h` 106 | 107 | Edit the `src/settings.h` file to set : 108 | 109 | * your Wifi SSID, 110 | * your Wifi password, 111 | * your latitude (decimal format, for example 48.8482855), 112 | * your longitude (decimal format, for example 2.2708201). 113 | 114 | > It is possible to manage as many configurations as you wish! This allows you, for example, to pre-configure your settings for local use on the Wifi of your QRA and mobile configuration on the Wifi of your Smartphone. Such a configuration could look like this: 115 | 116 | ``` 117 | const char *config[] = { 118 | "Livebox", "mdp_Livebox", "48.8482855", "2.2708201", 119 | "iPhone8", "mdp_iphone", "48.8482855", "2.2708201" 120 | }; 121 | ``` 122 | 123 | ### File `src/DXTracker.h` 124 | 125 | #### Frequency Exclusion 126 | 127 | By default, FT8 frequencies are filtered out and will not be displayed by the DX Cluster. You are free to change this filter list and add or remove frequencies you wish. To do that, edit the table `frequencyExclude[]`, line 176. 128 | 129 | ### File `platformio.ini` (for ATOM Display only) 130 | 131 | If and only if __you are using the ATOM Display__, edit the `platformio.ini` file and change line 12, 132 | 133 | ``` 134 | default_envs = m5stack 135 | ``` 136 | 137 | By, 138 | 139 | ``` 140 | default_envs = atom 141 | ``` 142 | 143 | This is the same as changing the target platform. 144 | 145 | In addition, you can specify the resolution of your screen. Note that the DXTracker will still be displayed at 320 x 240, but will be centered on the screen. By default, the screen resolution is set to 320 x 240. But, for example, if you want to change it to 480 x 320, change line 41, 146 | 147 | ``` 148 | build_flags = ${env.build_flags} -D atom=1 -D displayWidth=320 -D displayHeight=240 149 | ``` 150 | 151 | By, 152 | 153 | ``` 154 | build_flags = ${env.build_flags} -D atom=1 -D displayWidth=480 -D displayHeight=320 155 | ``` 156 | 157 | # Compiling and flashing the M5Stack 158 | 159 | Compile and upload the project to your M5Stack. You are done ! 160 | 161 | # Usage 162 | 163 | Once launched, after wifi and initial data initialization, if you click on left button, if you click on the middle button, you enter the settings menu. Use the right and left buttons to select a menu option and confirm with the middle button. If necessary, use the right and left buttons again to select the sub-options and the middle button to confirm. 164 | 165 | The following options are available : 166 | 167 | | Settings | Value | 168 | | -------------------- |:--------------------------------------------:| 169 | | Maps. | Set Map (Classic, Sunmap or Nightmap) | 170 | | GMT Offset | Set GMT Offset (-14 ~ +14) | 171 | | Daylight Offset | Set Daylight Offset (0 ~ 1) | 172 | | Clock | Display Clock (OFF ~ ON) | 173 | | Brightness | Set Brightness (0 ~ 100%) | 174 | | Beep | Set Beep (0 ~ 100%) | 175 | | Screensaver | Set Screensaver TimeOut (1 ~ 60 min) | 176 | | IP Address | Display your IP | 177 | | Shutdown | Shutdown your M5Stack (even if in charge) | 178 | | Exit | Menu Exit | 179 | 180 | > Maps, GMT Offset, Daylight Offset, Clock, Brightness, Beep and Screensaver are preserved at the next restart. 181 | 182 | # Using the Bin Loader (power user only...) 183 | 184 | It's possible to store several applications on the SPI Flash File Storage of your M5Stack or on SD Card Storage. At startup, a procedure is provided to load a particular application. 185 | 186 | > The Bin Loader does not work with the ATOM Display. 187 | 188 | ## Preparation 189 | 190 | I will detail here the procedure to deploy ICSMeter and ICMultiMeter applications on the same M5Stack. 191 | 192 | ### Step 1 - Compile 193 | 194 | Start by compiling your applications, as you used to do. Nothing changes here. For example, start by compiling the ICSMeter application. Then do the same with the ICMultiMeter application. 195 | 196 | ### Step 2 - Collecting the binary files 197 | 198 | That's it, you have compiled the ICSMeter and ICMultiMeter application? It's perfect. 199 | 200 | Each compilation has produced a binary. It is this binary that is sent / flashed to your M5Stack, via the USB connection. 201 | 202 | Go to the root of the ICSMeter folder, which contains the whole project. And go to the directory `.pio/build/m5stack`. 203 | 204 | You will find a `firmware.bin` file there. Now, there are 2 solutions... 205 | 206 | ### Step 2.1 - SD Card Storage (simple) 207 | 208 | Format an SD Card as FAT32. 209 | 210 | Copy the `firmware.bin` at the root of the SD Card. And take the opportunity to rename it, for example, `ICSMeter.bin`. 211 | 212 | Do the same with the ICMultiMeter application. Of course rename it with a different name, for example, `ICMultiMeter.bin`. 213 | 214 | At this point, you should have 2 clearly identified binaries in the root of your SD Card : `ICSMeter.bin` and `ICMultiMeter.bin`. 215 | 216 | ### Step 2.2 - SPI Flash File Storage (more difficult) 217 | 218 | Copy `firmware.bin` in the `data` directory at the root of the ICSMeter folder. And take the opportunity to rename it, for example, `ICSMeter.bin`. 219 | 220 | > If the `data` folder does not exist, create it. 221 | 222 | Do the same with the ICMultiMeter application. Go to the root of the ICMultiMeter folder, which contains the whole project. And go to the directory `.pio/build/m5stack`. 223 | 224 | You will also find a `firmware.bin` file. Copy it, too, in the `data` directory at the __root of the ICSMeter folder__. And take the opportunity to rename it to, for example, `ICMultiMeter.bin`. 225 | 226 | > Important, the idea is to copy these 2 binaries in the same directory `data`** (located at the root of the ICSMeter folder). 227 | 228 | At this point, you should have 2 clearly identified binaries: `ICSMeter.bin` and `ICMultiMeter.bin` in the `data` directory at the root of the ICSMeter folder. 229 | 230 | So let's move on to what is probably the most complicated step. Open the ICSMeter project from Visual Studio Code, as you would compile it. 231 | 232 | ![Capture](https://github.com/armel/ICMultiMeter/blob/main/img/flash_1.png) 233 | 234 | Step 1, click on the Platformio icon (the icon with an ant's head...). Step 2, unroll the section `m5stack`. 235 | 236 | ![Capture](https://github.com/armel/ICMultiMeter/blob/main/img/flash_2.png) 237 | 238 | Finally, step 3, go to the `Platform` sub-section. And click on `Upload Filesystem Image`. 239 | 240 | Wait ! It's coffee time (or beer) :) The contents of the `data` directory will be written to the SPI Flash File Storage of your M5Stack. Are you done? You're there !!!! 241 | 242 | ## Usage 243 | 244 | Start your M5Stack. You should see a black screen with a QR code, followed by 1, 2 and 3 small dots at the top of the screen. This is the famous Bin Loader ;) 245 | 246 | As soon as the first little dot appears, you can : 247 | 248 | - either press the left or right button, to launch the default application. 249 | - or press the central button. In this case, the Bin Loader menu appears and offers you the list of available binaries in SPI Flash File Storage or SD Card. 250 | 251 | If you have followed the procedure perfectly, you should have a choice between `ICSMeter.bin` and `ICMultiMeter.bin`. 252 | 253 | The left and right buttons allow you to switch from one binary to another. And the middle button validates the selected binary. In this case, the selected application will be loaded ;) 254 | 255 | > The loading takes about 20 seconds... 256 | 257 | ## Limitation 258 | 259 | I think it is possible to have 3 or 4 applications cohabit in SPI Flash File Storage. If need more, use SD Card Storage. 260 | 261 | # Credits 262 | 263 | Many thanks to... 264 | 265 | | Project | Author | Link | 266 | |:------------------- | :---------------------------------------------------- | :------------------------------------------- | 267 | | M5Stack | [M5Stack](https://twitter.com/M5Stack) | https://github.com/m5stack/M5Stack | 268 | | M5Stack-SD-Updater | [Tobozo](https://twitter.com/TobozoTagada) | https://github.com/tobozo/M5Stack-SD-Updater | 269 | 270 | 271 | # Donations 272 | 273 | Special thanks to Rolf Schroeder DL8BAG, Brian Garber WB8AM, Matt B-Wilkinson M6VWM, Robert Agnew KD0TVP, Meinhard Frank Günther DL0CN, Johan Hansson SM0TSC, Tadeusz Pater VA7CPM, Frederic Ulmer F4ESO, Joshua Murray M0JMO, Mark Hammond N8MH, Angel Mateu Muzzio EA4GIG, Hiroshi Sasaki JL7KGW, Robert John Williams VK3IE, Mark Bumstead M0IAX, Félix Symann F1VEO, Patrick Ruhl DG2YRP, Michael Beck DH5DAX, Philippe Nicolas F4IQP, Timothy Nustad KD9KHZ, Martin Blanz DL9SAD, Edmund Thompson AE4TQ, Gregory Kiyoi KN6RUQ, Patrick Samson F6GWE, George Kokolakis SV3QUP and Ambrose "Bo" Barry W4GHV for their donations. That’s so kind of them. Thanks so much 🙏🏻 274 | 275 | If you find this project fun and useful then [offer me a beer](https://www.paypal.me/F4HWN) :) 276 | 277 | You could use the code F4HWN in order to get 5% discount on the [M5Stack shop](https://shop.m5stack.com/?ref=LUxetaH4) 🎁 278 | 279 | By the way, you can follow me on [Twitter](https://twitter.com/F4HWN) and post pictures of your installation with your M5Stack. It always makes me happy ;) 280 | -------------------------------------------------------------------------------- /data/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | */ 3 | !.gitignore -------------------------------------------------------------------------------- /img/DXTracker.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/armel/DXTracker/44031e77ed5ed422b876c1fd179f5de5b5e1c7ac/img/DXTracker.jpeg -------------------------------------------------------------------------------- /img/Map_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/armel/DXTracker/44031e77ed5ed422b876c1fd179f5de5b5e1c7ac/img/Map_1.png -------------------------------------------------------------------------------- /img/Map_2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/armel/DXTracker/44031e77ed5ed422b876c1fd179f5de5b5e1c7ac/img/Map_2.png -------------------------------------------------------------------------------- /img/Map_3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/armel/DXTracker/44031e77ed5ed422b876c1fd179f5de5b5e1c7ac/img/Map_3.png -------------------------------------------------------------------------------- /img/Menu.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/armel/DXTracker/44031e77ed5ed422b876c1fd179f5de5b5e1c7ac/img/Menu.png -------------------------------------------------------------------------------- /img/flash_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/armel/DXTracker/44031e77ed5ed422b876c1fd179f5de5b5e1c7ac/img/flash_1.png -------------------------------------------------------------------------------- /img/flash_2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/armel/DXTracker/44031e77ed5ed422b876c1fd179f5de5b5e1c7ac/img/flash_2.png -------------------------------------------------------------------------------- /img/screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/armel/DXTracker/44031e77ed5ed422b876c1fd179f5de5b5e1c7ac/img/screenshot.png -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /platformio.ini: -------------------------------------------------------------------------------- 1 | ; PlatformIO Project Configuration File 2 | ; 3 | ; Build options: build flags, source filter 4 | ; Upload options: custom upload port, speed and extra flags 5 | ; Library options: dependencies, extra library storages 6 | ; Advanced options: extra scripting 7 | ; 8 | ; Please visit documentation for the other options and examples 9 | ; https://docs.platformio.org/page/projectconf.html 10 | 11 | [platformio] 12 | default_envs = m5stack 13 | 14 | [env] 15 | framework = arduino 16 | 17 | platform = espressif32@^3.5.0 18 | platform_packages = framework-arduinoespressif32 @ https://github.com/espressif/arduino-esp32/archive/refs/tags/1.0.6.zip 19 | 20 | build_flags = -O2 -Wall 21 | 22 | monitor_speed = 115200 23 | monitor_filters = time, esp32_exception_decoder 24 | 25 | board_build.flash_mode = dio 26 | board_build.f_cpu = 240000000L 27 | 28 | lib_deps = 29 | m5stack/M5Unified@^0.0.7 30 | tobozo/M5Stack-SD-Updater@^1.1.8 31 | 32 | [env:m5stack] 33 | build_flags = ${env.build_flags} -D atom=0 34 | 35 | board = m5stack-core2 36 | board_build.f_flash = 80000000L 37 | board_build.partitions = large_spiffs_16MB.csv 38 | ;upload_port = /dev/cu.wchusbserial53230020071 39 | 40 | [env:atom] 41 | build_flags = ${env.build_flags} -D atom=1 -D displayWidth=320 -D displayHeight=240 42 | 43 | board = m5stack-atom 44 | board_build.f_flash = 40000000L 45 | board_build.partitions = huge_app.csv 46 | ;upload_port = /dev/cu.wchusbserial53230020071 47 | -------------------------------------------------------------------------------- /src/DXTracker.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) F4HWN Armel. All rights reserved. 2 | // Licensed under the MIT license. See LICENSE file in the project root for full license information. 3 | 4 | #include "settings.h" 5 | #include "DXTracker.h" 6 | #include "map.h" 7 | #include "font.h" 8 | #include "tools.h" 9 | #include "webIndex.h" 10 | #include "data.h" 11 | #include "functions.h" 12 | #include "menu.h" 13 | #include "tasks.h" 14 | 15 | // Setup 16 | void setup() 17 | { 18 | // Init M5 19 | auto cfg = M5.config(); 20 | M5.begin(cfg); 21 | 22 | pinMode(32, INPUT_PULLUP); 23 | pinMode(26, INPUT_PULLUP); 24 | 25 | // Init Display 26 | display.begin(); 27 | 28 | offsetX = (display.width() - 320) / 2; 29 | offsetY = (display.height() - 240) / 2; 30 | 31 | // Bin Loader 32 | binLoader(); 33 | 34 | // Preferences 35 | preferences.begin(NAME); 36 | maps = preferences.getUInt("maps", 0); 37 | gmt = preferences.getInt("gmt", 0); 38 | daylight = preferences.getUInt("daylight", 0); 39 | watch = preferences.getUInt("watch", 0); 40 | brightness = preferences.getUInt("brightness", 64); 41 | beep = preferences.getUInt("beep", 0); 42 | screensaver = preferences.getUInt("screensaver", 10); 43 | 44 | size_t n = sizeof(config) / sizeof(config[0]); 45 | n = (n / 4) - 1; 46 | 47 | //preferences.putUInt("config", 0); 48 | 49 | configCurrent = preferences.getUInt("config", 0); 50 | 51 | if(configCurrent > n) { 52 | configCurrent = 0; 53 | preferences.putUInt("config", configCurrent); 54 | } 55 | 56 | // LCD 57 | display.setBrightness(map(brightness, 1, 100, 1, 254)); 58 | display.fillScreen(TFT_BACK); 59 | 60 | // SPIFFS 61 | if(!SPIFFS.begin()) 62 | { 63 | Serial.println("SPIFFS Mount Failed"); 64 | return; 65 | } 66 | 67 | // Title 68 | display.setFont(&rounded_led_board10pt7b); 69 | display.setTextColor(TFT_WHITE, TFT_BACK); 70 | display.setTextDatum(CC_DATUM); 71 | display.drawString(String(NAME), 160 + offsetX, 20 + offsetY); 72 | display.setFont(0); 73 | display.drawString("Version " + String(VERSION) + " by F4HWN", 160 + offsetX, 50 + offsetY); 74 | 75 | // QRCode 76 | display.qrcode("https://github.com/armel/DXTracker", 90 + offsetX, 80 + offsetY, 140, 6); 77 | 78 | // We start by connecting to the WiFi network 79 | display.setTextPadding(320); 80 | 81 | while(true) 82 | { 83 | uint8_t attempt = 1; 84 | display.drawString(String(config[(configCurrent * 4)]), 160 + offsetX, 60 + offsetY); 85 | WiFi.begin(config[(configCurrent * 4)], config[(configCurrent * 4) + 1]); 86 | while (WiFi.status() != WL_CONNECTED) 87 | { 88 | delay(500); 89 | if(attempt % 2 == 0) 90 | { 91 | display.drawString("Connecting in progress", 160 + offsetX, 70 + offsetY); 92 | } 93 | else 94 | { 95 | display.drawString(" ", 160 + offsetX, 70 + offsetY); 96 | } 97 | attempt++; 98 | if(attempt > 10) { 99 | break; 100 | } 101 | } 102 | if(WiFi.status() != WL_CONNECTED) 103 | { 104 | configCurrent += 1; 105 | if(configCurrent > n) { 106 | configCurrent = 0; 107 | preferences.putUInt("config", configCurrent); 108 | } 109 | } 110 | else { 111 | break; 112 | } 113 | } 114 | 115 | display.drawString(String(WiFi.localIP().toString().c_str()), 160 + offsetX, 70 + offsetY); 116 | 117 | // Init and get time 118 | configTime(gmt * 60 * 60, daylight * 60 * 60, ntpServer); 119 | updateLocalTime(); 120 | 121 | // Scroll 122 | posA = 320; 123 | imgA.createSprite(posA, 20); 124 | 125 | posB = 320; 126 | imgB.createSprite(posB, 20); 127 | 128 | // Start server (for Web site Screen Capture) 129 | httpServer.begin(); 130 | 131 | // Let's go after temporisation 132 | delay(250); 133 | 134 | // Waiting for data 135 | while(hamQSLData == "" || hamQTHData == "" || satData == "") 136 | { 137 | display.drawString("Loading datas", 160 + offsetX, 70 + offsetY); 138 | 139 | if(hamQTHData == "") 140 | { 141 | getHamQTH(); 142 | if(hamQTHData != "") 143 | { 144 | display.drawString("Cluster Ok", 160 + offsetX, 70 + offsetY); 145 | } 146 | } 147 | if(satData == "") 148 | { 149 | getHamSat(); 150 | if(hamQTHData != "") 151 | { 152 | display.drawString("Sat Ok", 160 + offsetX, 70 + offsetY); 153 | } 154 | } 155 | if(hamQSLData == "") 156 | { 157 | getHamQSL(); 158 | if(hamQSLData != "") 159 | { 160 | display.drawString("Solar Ok", 160 + offsetX, 70 + offsetY); 161 | } 162 | } 163 | } 164 | 165 | delay(500); 166 | 167 | for (uint8_t i = 0; i <= 120; i++) 168 | { 169 | display.drawFastHLine(0 + offsetX, i + offsetY, 320, TFT_BLACK); 170 | display.drawFastHLine(0 + offsetX, 240 - i + offsetY, 320, TFT_BLACK); 171 | delay(5); 172 | } 173 | 174 | // Multitasking task for retreive propag data 175 | xTaskCreatePinnedToCore( 176 | hamdata, // Function to implement the task 177 | "hamdata", // Name of the task 178 | 16384, // Stack size in words 179 | NULL, // Task input parameter 180 | 2, // Priority of the task 181 | &hamdataHandle, // Task handle 182 | 1); // Core where the task should run 183 | 184 | // Multitasking task for retreive button 185 | xTaskCreatePinnedToCore( 186 | button, // Function to implement the task 187 | "button", // Name of the task 188 | 8192, // Stack size in words 189 | NULL, // Task input parameter 190 | 4, // Priority of the task 191 | &buttonHandle, // Task handle 192 | 1); // Core where the task should run 193 | 194 | // And clear 195 | screenRefresh = 1; 196 | 197 | // Init screensaver timer 198 | screensaverTimer = millis(); 199 | } 200 | 201 | // Main loop 202 | void loop() 203 | { 204 | // Get screenshot 205 | getScreenshot(); 206 | 207 | if(settingsMode == true) { 208 | settingLock = false; 209 | } 210 | else { 211 | settingLock = true; 212 | 213 | // Let's clean if necessary 214 | clear(); 215 | 216 | // View clock 217 | viewClock(); 218 | 219 | // View propag datas 220 | title(propagData(alternance)); 221 | 222 | // Prepare cluster and sat scroll message 223 | clusterAndSatMessage(); 224 | 225 | // Prepare propag scroll message 226 | propagMessage(); 227 | 228 | // Manage scroll 229 | scroll(); 230 | 231 | // Manage screensaver 232 | wakeAndSleep(); 233 | 234 | // Get screenshot 235 | getScreenshot(); 236 | 237 | // Manage alternance 238 | if(screenRefresh == 0 && millis() - temporisation > TIMEOUT_TEMPORISATION) { 239 | temporisation = millis(); 240 | alternance = (alternance++ > 10) ? 0 : alternance; 241 | if(alternance == 0) { 242 | messageCurrent = (messageCurrent++ < 3) ? messageCurrent : 0; 243 | reload = 0; 244 | Serial.println(String(ESP.getFreeHeap() / 1024) + " kb" + " / " + String(esp_get_minimum_free_heap_size() / 1024) + " kb"); 245 | } 246 | } 247 | } 248 | } 249 | -------------------------------------------------------------------------------- /src/DXTracker.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) F4HWN Armel. All rights reserved. 2 | // Licensed under the MIT license. See LICENSE file in the project root for full license information. 3 | 4 | #define VERSION "1.2.4" 5 | #define AUTHOR "F4HWN" 6 | #define NAME "DXTracker" 7 | 8 | #define DEBUG 0 9 | #define ATOM atom 10 | #define WIDTH displayWidth 11 | #define HEIGHT displayHeight 12 | 13 | #define TIMEOUT_BIN_LOADER 3 // 3 sec 14 | #define TIMEOUT_TEMPORISATION 5 * 1000 // 5 sec 15 | 16 | #define M5ATOMDISPLAY_LOGICAL_WIDTH WIDTH // width 17 | #define M5ATOMDISPLAY_LOGICAL_HEIGHT HEIGHT // height 18 | #define M5ATOMDISPLAY_REFRESH_RATE 60 // refresh rate 19 | 20 | #undef min 21 | 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | 28 | #if ATOM == 1 29 | #include 30 | #endif 31 | 32 | #include 33 | #include 34 | 35 | // Wifi 36 | WiFiClient client; 37 | WiFiClient httpClient; 38 | WiFiServer httpServer(80); 39 | HTTPClient http; 40 | 41 | // Web site Screen Capture stuff 42 | #define GET_unknown 0 43 | #define GET_index_page 1 44 | #define GET_screenshot 2 45 | 46 | // Display 47 | uint16_t offsetX = 0; 48 | uint16_t offsetY = 0; 49 | 50 | #if ATOM == 0 51 | M5GFX &display(M5.Lcd); 52 | #else 53 | M5AtomDisplay display(WIDTH, HEIGHT); 54 | #endif 55 | 56 | // Flags for button presses via Web site Screen Capture 57 | bool buttonLeftPressed = false; 58 | bool buttonCenterPressed = false; 59 | bool buttonRightPressed = false; 60 | 61 | // Preferences 62 | Preferences preferences; 63 | 64 | // Color 65 | typedef struct __attribute__((__packed__)) 66 | { 67 | uint8_t r; 68 | uint8_t g; 69 | uint8_t b; 70 | } colorType; 71 | 72 | #define TFT_BACK display.color565(48, 48, 48) 73 | #define TFT_GRAY display.color565(128, 128, 128) 74 | #define TFT_MENU_BORDER display.color565(115, 135, 159) 75 | #define TFT_MENU_BACK display.color565(24, 57, 92) 76 | #define TFT_MENU_SELECT display.color565(255, 255, 255) 77 | 78 | // Time 79 | const char* ntpServer = "pool.ntp.org"; 80 | char timeLocalBuff[9]; 81 | char dateLocalBuff[9]; 82 | 83 | struct tm timeinfo; 84 | 85 | // HTTP endpoint 86 | String endpointHamQSL = "http://www.hamqsl.com/solarxml.php"; 87 | String endpointSat = "http://rrf2.f5nlg.ovh:8080/cgi-bin/DXSat.py"; 88 | String endpointHamQTH = "http://rrf2.f5nlg.ovh:8080/cgi-bin/DXCluster.py"; 89 | 90 | String endpointGreyline[3] = { 91 | "http://rrf2.f5nlg.ovh:8080/greyline.jpg", 92 | "http://rrf2.f5nlg.ovh:8080/sunmap.jpg", 93 | "http://rrf2.f5nlg.ovh:8080/nightmap.jpg" 94 | }; 95 | 96 | // Scroll 97 | LGFX_Sprite imgA(&display); // Create Sprite object "img" with pointer to "tft" object 98 | String messageA = ""; 99 | int16_t posA; 100 | 101 | LGFX_Sprite imgB(&display); // Create Sprite object "img" with pointer to "tft" object 102 | String messageB = ""; 103 | int16_t posB; 104 | 105 | // Bin loader 106 | File root; 107 | String binFilename[128]; 108 | uint8_t binIndex = 0; 109 | 110 | // Propag data 111 | String solarData[] = { 112 | "SFI", "Sunspots", "A-Index", "K-Index", 113 | "X-Ray", "Helium Line", "Proton Flux", "Electron Flux", 114 | "Aurora", "Solar Wind", "Magnetic Field", "Signal Noise", 115 | "" 116 | }; 117 | 118 | String solarKey[] = { 119 | "solarflux", "sunspots", "aindex", "kindex", 120 | "xray", "heliumline", "protonflux", "electonflux", 121 | "aurora", "solarwind", "magneticfield", "signalnoise", 122 | "updated" 123 | }; 124 | 125 | String skipData[] = { 126 | "E-Skip North America", 127 | "E-Skip Europe", 128 | "E-Skip Europe 4m", 129 | "E-Skip Europe 6m", 130 | }; 131 | 132 | String skipKey[] = { 133 | "location=\"north_america\">", 134 | "location=\"europe\">", 135 | "location=\"europe_4m\">", 136 | "location=\"europe_6m\">" 137 | }; 138 | 139 | String propagKey[] = { 140 | "80m-40m\" time=\"day\">", 141 | "30m-20m\" time=\"day\">", 142 | "17m-15m\" time=\"day\">", 143 | "12m-10m\" time=\"day\">", 144 | "80m-40m\" time=\"night\">", 145 | "30m-20m\" time=\"night\">", 146 | "17m-15m\" time=\"night\">", 147 | "12m-10m\" time=\"night\">" 148 | }; 149 | 150 | String cluster, call, frequency, band, country; 151 | 152 | // Task Handle 153 | TaskHandle_t hamdataHandle; 154 | TaskHandle_t buttonHandle; 155 | 156 | // Miscellaneous 157 | String tmpString; 158 | String dateString; 159 | String greylineData = "", hamQSLData = "", hamQTHData = "", satData = ""; 160 | String greylineUrl = ""; 161 | String reloadState = ""; 162 | 163 | boolean reload = 0; 164 | boolean screensaverMode = 0; 165 | boolean greylineRefresh = 0; 166 | boolean settingsMode = false; 167 | boolean settingLock = true; 168 | 169 | uint8_t screenRefresh = 1; 170 | uint8_t htmlGetRequest; 171 | uint8_t alternance = 0; 172 | uint8_t configCurrent = 0; 173 | uint8_t brightness = 64; 174 | uint8_t messageCurrent = 0; 175 | 176 | int8_t beep = 0; 177 | int8_t maps = 0; 178 | int8_t watch = 0; 179 | int8_t screensaver = 0; 180 | int8_t gmt = 0; 181 | int16_t daylight = 0; 182 | 183 | int16_t parenthesisBegin = 0; 184 | int16_t parenthesisLast = 0; 185 | 186 | uint32_t temporisation; 187 | uint32_t screensaverTimer; 188 | uint32_t frequencyExclude[] = { 189 | 1840, 1842, 3573, 5357, 190 | 7056, 7071, 7074, 7078, 191 | 10130, 10132, 10133, 10136, 192 | 14071, 14074, 14078, 14090, 193 | 18100, 18104, 21074, 21078, 194 | 21091, 24915, 28074, 28078, 195 | 50310, 50313, 50328, 50323, 196 | 70100, 144174, 222065, 432065 197 | }; 198 | 199 | // Menu 200 | const char *settings[] = {"Maps", "GMT Offset", "Daylight Offset", "Clock", "Brightness", "Beep", "Screensaver", "IP Address", "Shutdown", "Exit"}; 201 | const char *choiceMaps[] = {"CLASSIC", "SUNMAP", "NIGHTMAP"}; 202 | const char *choiceGMT[] = {"GMT OFFSET"}; 203 | const char *choiceDaylight[] = {"DAYLIGHT OFFSET"}; 204 | const char *choiceBrightness[] = {"BRIGHTNESS"}; 205 | const char *choiceClock[] = {"OFF", "ON"}; 206 | const char *choiceBeep[] = {"BEEP LEVEL"}; 207 | const char *choiceScreensaver[] = {"TIMEOUT"}; -------------------------------------------------------------------------------- /src/Web/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 James Swineson 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. -------------------------------------------------------------------------------- /src/Web/README.md: -------------------------------------------------------------------------------- 1 | # bin2array 2 | 3 | Converts binary file to C-style array initializer. 4 | 5 | Ever wanted to embed a binary file in your program? Trying to serve images and executables from a tiny web server on Arduino or ESP8266? This utility is here to help. 6 | 7 | ## Requirements 8 | 9 | * Python 3 10 | 11 | ## Usage 12 | 13 | I guess it is self-explanatory. 14 | 15 | ``` 16 | usage: bin2array.py [-h] [-O OUTPUT] [-l LINEBREAK] [-L LINEBREAK_STRING] 17 | [-S SEPARATOR_STRING] [-H ELEMENT_PREFIX] 18 | [-T ELEMENT_SUFFIX] [-U] [-n] 19 | filename 20 | 21 | Convert binary file to C-style array initializer. 22 | 23 | positional arguments: 24 | filename the file to be converted 25 | 26 | optional arguments: 27 | -h, --help show this help message and exit 28 | -O OUTPUT, --output OUTPUT 29 | write output to a file 30 | -l LINEBREAK, --linebreak LINEBREAK 31 | add linebreak after every N element 32 | -L LINEBREAK_STRING, --linebreak-string LINEBREAK_STRING 33 | use what to break link, defaults to "\n" 34 | -S SEPARATOR_STRING, --separator-string SEPARATOR_STRING 35 | use what to separate elements, defaults to ", " 36 | -H ELEMENT_PREFIX, --element-prefix ELEMENT_PREFIX 37 | string to be added to the head of element, defaults to 38 | "0x" 39 | -T ELEMENT_SUFFIX, --element-suffix ELEMENT_SUFFIX 40 | string to be added to the tail of element, defaults to 41 | none 42 | -U, --force-uppercase 43 | force uppercase HEX representation 44 | -n, --newline add a newline on file end 45 | ``` 46 | 47 | ## Caveats 48 | 49 | ### Arduino IDE 50 | 51 | **Do not put large source code files in the root folder of your project.** Otherwise some of the following events will happen: 52 | 53 | * One of your CPU cores been eaten up by java 54 | * The splash screen shows up but never loads 55 | * 3rd World War 56 | 57 | Make a new folder inside project root, put the converted file (use `.h` as extension, otherwise may not be recognized) in, then use the following grammer to use it: 58 | 59 | ```C++ 60 | const char great_image[] PROGMEM = { 61 | #include "data/great_image.png.h" 62 | } 63 | ``` 64 | 65 | If you are using ESP8266WebServer to serve static binary files, you can use the following code: 66 | 67 | ```C++ 68 | #include 69 | 70 | // create server 71 | ESP8266WebServer server(80); 72 | 73 | // include the image data 74 | const char image[] PROGMEM = { 75 | #include "data/image.png.h" 76 | }; 77 | 78 | // statis binary file handler 79 | void handleImage() { 80 | server.sendHeader("Cache-Control", "max-age=31536000", false); 81 | server.send_P(200, "image/png", image, sizeof(image)); 82 | } 83 | 84 | void setup() { 85 | // do other things... 86 | // register image handler before server.begin() 87 | server.on("/image.png", handleImage); 88 | // do other things... 89 | server.begin(); 90 | } 91 | ``` 92 | 93 | ### Windows 94 | 95 | Do not use command line redirection (`python bin2array.py test.png > test.png.h`) since CMD will save the file using UTF-16 which is not recognized by some compiler. Use `-O` option to save output to file, or manually convert UTF-16 to UTF-8 for maximum compatibility. -------------------------------------------------------------------------------- /src/Web/bin2array.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | import binascii 3 | import sys 4 | import argparse 5 | 6 | parser = argparse.ArgumentParser(description='Convert binary file to C-style array initializer.') 7 | parser.add_argument("filename", help="the file to be converted") 8 | parser.add_argument("-O", "--output", help="write output to a file") 9 | parser.add_argument("-l", "--linebreak", type=int, help="add linebreak after every N element") 10 | parser.add_argument("-L", "--linebreak-string", default="\n", help="use what to break link, defaults to \"\\n\"") 11 | parser.add_argument("-S", "--separator-string", default=", ", help="use what to separate elements, defaults to \", \"") 12 | parser.add_argument("-H", "--element-prefix", default="0x", help="string to be added to the head of element, defaults to \"0x\"") 13 | parser.add_argument("-T", "--element-suffix", default="", help="string to be added to the tail of element, defaults to none") 14 | parser.add_argument("-U", "--force-uppercase", action='store_true', help="force uppercase HEX representation") 15 | parser.add_argument("-n", "--newline", action='store_true', help="add a newline on file end") 16 | args = parser.parse_args() 17 | 18 | def make_sublist_group(lst: list, grp: int) -> list: 19 | """ 20 | Group list elements into sublists. 21 | 22 | make_sublist_group([1, 2, 3, 4, 5, 6, 7], 3) = [[1, 2, 3], [4, 5, 6], 7] 23 | """ 24 | return [lst[i:i+grp] for i in range(0, len(lst), grp)] 25 | 26 | def do_convension(content: bytes, to_uppercase: bool=False) -> str: 27 | hexstr = binascii.hexlify(content).decode("UTF-8") 28 | if to_uppercase: 29 | hexstr = hexstr.upper() 30 | array = [args.element_prefix + hexstr[i:i + 2] + args.element_suffix for i in range(0, len(hexstr), 2)] 31 | if args.linebreak: 32 | array = make_sublist_group(array, args.linebreak) 33 | else: 34 | array = [array,] 35 | 36 | return args.linebreak_string.join([args.separator_string.join(e) + args.separator_string for e in array]) 37 | 38 | if __name__ == "__main__": 39 | with open(args.filename, 'rb') as f: 40 | file_content = f.read() 41 | ret = do_convension(file_content, to_uppercase=args.force_uppercase) 42 | if args.newline: 43 | ret = ret + args.linebreak_string 44 | if args.output: 45 | with open(args.output, 'w') as f: 46 | f.write(ret) 47 | else: 48 | print(ret) -------------------------------------------------------------------------------- /src/Web/button.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/armel/DXTracker/44031e77ed5ed422b876c1fd179f5de5b5e1c7ac/src/Web/button.png -------------------------------------------------------------------------------- /src/Web/button_full.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/armel/DXTracker/44031e77ed5ed422b876c1fd179f5de5b5e1c7ac/src/Web/button_full.png -------------------------------------------------------------------------------- /src/Web/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | DXTracker Screen Capture 6 | 53 | 54 | 55 |
56 | 57 | 58 | 63 | 64 | 65 | 72 | 73 |
59 | 60 | Screen 61 | 62 |
66 |
    67 |
  • 68 |
  • 69 |
  • 70 |
71 |
74 |
75 | 76 | 77 | -------------------------------------------------------------------------------- /src/Web/indexBasicAndGrey.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | DXTracker Screen Capture 6 | 53 | 54 | 55 |
56 | 57 | 58 | 63 | 64 | 65 | 72 | 73 |
59 | 60 | Screen 61 | 62 |
66 |
    67 |
  • 68 |
  • 69 |
  • 70 |
71 |
74 |
75 | 76 | 77 | -------------------------------------------------------------------------------- /src/Web/indexCore2.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | DXTracker Screen Capture 6 | 52 | 53 | 54 |
55 | 56 | 57 | 62 | 63 | 64 | 71 | 72 |
58 | 59 | Screen 60 | 61 |
65 |
    66 |
  • 67 |
  • 68 |
  • 69 |
70 |
73 |
74 | 75 | 76 | -------------------------------------------------------------------------------- /src/Web/m5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/armel/DXTracker/44031e77ed5ed422b876c1fd179f5de5b5e1c7ac/src/Web/m5.png -------------------------------------------------------------------------------- /src/Web/m5_basic.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/armel/DXTracker/44031e77ed5ed422b876c1fd179f5de5b5e1c7ac/src/Web/m5_basic.png -------------------------------------------------------------------------------- /src/Web/m5_basic_full.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/armel/DXTracker/44031e77ed5ed422b876c1fd179f5de5b5e1c7ac/src/Web/m5_basic_full.png -------------------------------------------------------------------------------- /src/Web/m5_core2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/armel/DXTracker/44031e77ed5ed422b876c1fd179f5de5b5e1c7ac/src/Web/m5_core2.png -------------------------------------------------------------------------------- /src/Web/m5_core2_full.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/armel/DXTracker/44031e77ed5ed422b876c1fd179f5de5b5e1c7ac/src/Web/m5_core2_full.png -------------------------------------------------------------------------------- /src/Web/m5_full.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/armel/DXTracker/44031e77ed5ed422b876c1fd179f5de5b5e1c7ac/src/Web/m5_full.png -------------------------------------------------------------------------------- /src/Web/screenshot.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/armel/DXTracker/44031e77ed5ed422b876c1fd179f5de5b5e1c7ac/src/Web/screenshot.bmp -------------------------------------------------------------------------------- /src/Web/screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/armel/DXTracker/44031e77ed5ed422b876c1fd179f5de5b5e1c7ac/src/Web/screenshot.png -------------------------------------------------------------------------------- /src/data.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) F4HWN Armel. All rights reserved. 2 | // Licensed under the MIT license. See LICENSE file in the project root for full license information. 3 | 4 | // get Greyline data 5 | void getGreyline() 6 | { 7 | reloadState = "Greyline"; 8 | if (DEBUG) Serial.println(reloadState); 9 | 10 | display.drawJpgUrl(endpointGreyline[maps], 0 + offsetX, 101 + offsetY, 320, 139, 0, 11); 11 | 12 | vTaskDelay(pdMS_TO_TICKS(20)); 13 | reloadState = ""; 14 | } 15 | 16 | // get Solar data 17 | void getHamQSL() 18 | { 19 | uint16_t httpCode; 20 | 21 | reloadState = "Solar"; 22 | if (DEBUG) Serial.println(reloadState); 23 | 24 | http.begin(client, endpointHamQSL); // Specify the URL 25 | http.addHeader("Content-Type", "text/plain"); // Specify content-type header 26 | http.setTimeout(1000); // Set Time Out 27 | httpCode = http.GET(); // Make the request 28 | if (httpCode == 200) // Check for the returning code 29 | { 30 | String tmpString = http.getString(); // Get data 31 | tmpString.trim(); 32 | 33 | if(tmpString != "") 34 | { 35 | hamQSLData = tmpString; 36 | } 37 | } 38 | http.end(); // Free the resources 39 | client.flush(); 40 | client.stop(); 41 | 42 | vTaskDelay(pdMS_TO_TICKS(20)); 43 | reloadState = ""; 44 | } 45 | 46 | // get Cluster data 47 | void getHamQTH() 48 | { 49 | uint16_t httpCode; 50 | 51 | reloadState = "Cluster"; 52 | if (DEBUG) Serial.println(reloadState); 53 | 54 | http.begin(client, endpointHamQTH); // Specify the URL 55 | http.addHeader("Content-Type", "text/plain"); // Specify content-type header 56 | http.setTimeout(1000); // Set Time Out 57 | httpCode = http.GET(); // Make the request 58 | if (httpCode == 200) // Check for the returning code 59 | { 60 | String tmpString = http.getString(); // Get data 61 | tmpString.trim(); 62 | tmpString.replace("\n", "|"); 63 | 64 | if(tmpString != "" && tmpString != hamQTHData) 65 | { 66 | hamQTHData = tmpString; 67 | } 68 | } 69 | http.end(); // Free the resources 70 | client.flush(); 71 | client.stop(); 72 | 73 | vTaskDelay(pdMS_TO_TICKS(20)); 74 | reloadState = ""; 75 | } 76 | 77 | // get Sat data 78 | void getHamSat() 79 | { 80 | uint16_t httpCode; 81 | 82 | reloadState = "Sat"; 83 | if (DEBUG) Serial.println(reloadState); 84 | 85 | http.begin(client, endpointSat + "?lat=" + config[(configCurrent * 4) + 2] + "&lng=" + config[(configCurrent * 4) + 3] + "&format=text"); // Specify the URL 86 | http.addHeader("Content-Type", "text/plain"); // Specify content-type header 87 | http.setTimeout(1000); // Set Time Out 88 | httpCode = http.GET(); // Make the request 89 | if (httpCode == 200) // Check for the returning code 90 | { 91 | String tmpString = http.getString(); // Get data 92 | tmpString.trim(); 93 | 94 | if(tmpString != "" && tmpString != satData) 95 | { 96 | satData = tmpString; 97 | } 98 | } 99 | http.end(); // Free the resources 100 | client.flush(); 101 | client.stop(); 102 | 103 | vTaskDelay(pdMS_TO_TICKS(20)); 104 | reloadState = ""; 105 | } -------------------------------------------------------------------------------- /src/functions.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) F4HWN Armel. All rights reserved. 2 | // Licensed under the MIT license. See LICENSE file in the project root for full license information. 3 | 4 | // Parse data 5 | String getValue(String data, char separator, uint16_t index) 6 | { 7 | int16_t strIndex[] = {0, -1}; 8 | int16_t found = 0; 9 | int16_t maxIndex = data.length() - 1; 10 | 11 | for (uint16_t i = 0; i <= maxIndex && found <= index; i++) 12 | { 13 | if (data.charAt(i) == separator || i == maxIndex) 14 | { 15 | found++; 16 | strIndex[0] = strIndex[1] + 1; 17 | strIndex[1] = (i == maxIndex) ? i + 1 : i; 18 | } 19 | } 20 | 21 | return found > index ? data.substring(strIndex[0], strIndex[1]) : ""; 22 | } 23 | 24 | // Get local time 25 | void updateLocalTime() 26 | { 27 | if(!getLocalTime(&timeinfo)){ 28 | Serial.println("Failed to obtain time"); 29 | return; 30 | } 31 | 32 | strftime(timeLocalBuff, sizeof(timeLocalBuff), "%H:%M:%S", &timeinfo); 33 | strftime(dateLocalBuff, sizeof(dateLocalBuff), "%d-%m-%y", &timeinfo); 34 | } 35 | 36 | // Clear screen 37 | void clear() 38 | { 39 | if(screenRefresh >= 1) { 40 | display.clear(); 41 | display.fillRect(0 + offsetX, 0 + offsetY, 320, 44, TFT_BACK); 42 | display.drawFastHLine( 0 + offsetX, 0 + offsetY, 320, TFT_WHITE); 43 | display.drawFastHLine( 0 + offsetX, 44 + offsetY, 320, TFT_WHITE); 44 | display.drawFastHLine( 0 + offsetX, 100 + offsetY, 320, TFT_WHITE); 45 | } 46 | if(screenRefresh == 2) 47 | { 48 | screenRefresh = 0; 49 | getGreyline(); 50 | } 51 | } 52 | 53 | // Manage message cycle 54 | String binarise() 55 | { 56 | switch(messageCurrent) 57 | { 58 | case 0: return "00"; break; 59 | case 1: return "01"; break; 60 | case 2: return "10"; break; 61 | case 3: return "11"; break; 62 | } 63 | 64 | return "00"; 65 | } 66 | 67 | // Build scroll A 68 | void buildScrollA() 69 | { 70 | int16_t h = 20; 71 | int16_t w; 72 | 73 | imgA.setFont(&FreeSans9pt7b); 74 | w = imgA.textWidth(messageA) + 80; 75 | // We could just use fillSprite(color) but lets be a bit more creative... 76 | while (h--) 77 | imgA.drawFastHLine(0, h, w, TFT_BLACK); 78 | 79 | // Now print text on top of the graphics 80 | imgA.setTextColor(TFT_WHITE); // White text, no background colour 81 | imgA.setTextWrap(false); // Turn of wrap so we can print past end of sprite 82 | 83 | // Need to print twice so text appears to wrap around at left and right edges 84 | imgA.drawString(messageA, posA, 2); 85 | imgA.drawString(messageA, posA - w, 2); 86 | } 87 | 88 | // Scroll A 89 | void scrollA(uint8_t pause) 90 | { 91 | // Sprite for scroll 92 | buildScrollA(); 93 | imgA.pushSprite(0 + offsetX, 52 + offsetY); 94 | 95 | posA -= 2; 96 | if (posA < 0) 97 | { 98 | //posA = display.width(); 99 | imgA.setFont(&FreeSans9pt7b); 100 | posA = imgA.textWidth(messageA) + 80; 101 | } 102 | 103 | vTaskDelay(pdMS_TO_TICKS(pause)); 104 | } 105 | 106 | // Build scroll B 107 | void buildScrollB() 108 | { 109 | int16_t h = 20; 110 | int16_t w; 111 | 112 | imgB.setTextSize(1); // Font size scaling is x1 113 | imgB.setTextFont(2); // Font 2 selected 114 | w = imgB.textWidth(messageB) + 80; 115 | // We could just use fillSprite(color) but lets be a bit more creative... 116 | while (h--) 117 | imgB.drawFastHLine(0, h, w, TFT_BLACK); 118 | 119 | // Now print text on top of the graphics 120 | imgB.setTextColor(TFT_GRAY); // Gray text, no background colour 121 | imgB.setTextWrap(false); // Turn of wrap so we can print past end of sprite 122 | 123 | // Need to print twice so text appears to wrap around at left and right edges 124 | imgB.drawString(messageB, posB, 2); 125 | imgB.drawString(messageB, posB - w, 2); 126 | } 127 | 128 | // Scroll B 129 | void scrollB(uint8_t pause) 130 | { 131 | // Sprite for scroll 132 | buildScrollB(); 133 | imgB.pushSprite(0 + offsetX, 74 + offsetY); 134 | 135 | posB -= 1; 136 | if (posB < 0) 137 | { 138 | //posB = display.width(); 139 | imgB.setTextSize(1); // Font size scaling is x1 140 | imgB.setTextFont(2); // Font 2 selected 141 | posB = imgB.textWidth(messageB) + 80; 142 | } 143 | 144 | vTaskDelay(pdMS_TO_TICKS(pause)); 145 | } 146 | 147 | // Manage scroll 148 | void scroll() 149 | { 150 | if(screenRefresh == 1) 151 | { 152 | return; 153 | } 154 | 155 | for(uint16_t i = 0; i < 10; i += 1) 156 | { 157 | scrollA(5); 158 | scrollB(5); 159 | } 160 | } 161 | 162 | // Get Propag Data 163 | String propagData(uint8_t index) 164 | { 165 | // Title 166 | solarData[index].toUpperCase(); 167 | 168 | // Current value 169 | tmpString = hamQSLData; 170 | 171 | tmpString.replace("<" + solarKey[index] + ">", "("); 172 | tmpString.replace("", ")"); 173 | 174 | parenthesisBegin = tmpString.indexOf("("); 175 | parenthesisLast = tmpString.indexOf(")"); 176 | if (parenthesisBegin > 0) 177 | { 178 | tmpString = tmpString.substring(parenthesisBegin + 1, parenthesisLast); 179 | } 180 | 181 | tmpString.trim(); 182 | 183 | return(solarData[index] + " " + tmpString); 184 | } 185 | 186 | // Draw title 187 | void title(String title) 188 | { 189 | static String titleOld; 190 | static String baselineOld; 191 | static String reloadStateOld; 192 | 193 | if(screenRefresh == 1 || screenRefresh == 2) { 194 | titleOld = ""; 195 | baselineOld = ""; 196 | reloadStateOld = ""; 197 | screenRefresh = 0; 198 | } 199 | 200 | // Title 201 | if(title != titleOld) { // Refresh 202 | titleOld = title; 203 | reloadStateOld = " "; 204 | 205 | display.setTextColor(TFT_WHITE, TFT_BACK); 206 | display.setFont(&dot15pt7b); 207 | display.setTextDatum(CC_DATUM); 208 | display.setTextPadding(320); 209 | display.drawString(title, 160 + offsetX, 16 + offsetY); 210 | } 211 | 212 | if(alternance % 2 == 0) 213 | { 214 | tmpString = propagData(12); 215 | tmpString = "Updated" + tmpString.substring(0, 7) + " " + tmpString.substring(10, 15) + ":" + tmpString.substring(15, 21); 216 | } 217 | else if(alternance == 5) 218 | { 219 | tmpString = String(WiFi.localIP().toString().c_str()); 220 | } 221 | else 222 | { 223 | tmpString = String(NAME) + " Version " + String(VERSION); 224 | } 225 | 226 | if(tmpString != baselineOld) { // Refresh 227 | baselineOld = tmpString; 228 | 229 | display.setTextColor(TFT_WHITE, TFT_BACK); 230 | display.setFont(0); 231 | display.setTextDatum(CC_DATUM); 232 | display.setTextPadding(320); 233 | display.drawString(tmpString, 160 + offsetX, 36 + offsetY); 234 | } 235 | 236 | // On right, view reload data 237 | tmpString = reloadState; 238 | 239 | if(tmpString != reloadStateOld) { // Refresh 240 | reloadStateOld = tmpString; 241 | 242 | if(tmpString != "") { 243 | display.drawFastHLine(2 + offsetX, 35 + offsetY, 10, TFT_GRAY); 244 | display.drawLine(12 + offsetX, 35 + offsetY, 8 + offsetX, 31 + offsetY, TFT_GRAY); 245 | display.drawLine(12 + offsetX, 35 + offsetY, 8 + offsetX, 39 + offsetY, TFT_GRAY); 246 | } 247 | else { 248 | display.drawFastHLine(2 + offsetX, 35 + offsetY, 10, TFT_BACK); 249 | display.drawLine(12 + offsetX, 35 + offsetY, 8 + offsetX, 31 + offsetY, TFT_BACK); 250 | display.drawLine(12 + offsetX, 35 + offsetY, 8 + offsetX, 39 + offsetY, TFT_BACK); 251 | } 252 | 253 | display.setTextColor(TFT_GRAY, TFT_BACK); 254 | display.setFont(0); 255 | display.setTextDatum(ML_DATUM); 256 | display.setTextPadding(60); 257 | display.drawString(tmpString, 18 + offsetX, 36 + offsetY); 258 | } 259 | 260 | // On left, view battery level 261 | if (M5.getBoard() != m5::board_t::board_M5ATOM && M5.getBoard() != m5::board_t::board_M5AtomPsram && M5.getBoard() != m5::board_t::board_M5AtomU) 262 | { 263 | uint8_t val = map(getBatteryLevel(1), 0, 100, 0, 16); 264 | 265 | display.drawRect(294 + offsetX, 30 + offsetY, 20, 12, TFT_GRAY); 266 | display.drawRect(313 + offsetX, 33 + offsetY, 4, 6, TFT_GRAY); 267 | display.fillRect(296 + offsetX, 32 + offsetY, val, 8, TFT_GRAY); 268 | 269 | if(isCharging()) { 270 | display.setTextColor(TFT_GRAY, TFT_BACK); 271 | display.setFont(0); 272 | display.setTextDatum(CC_DATUM); 273 | display.setTextPadding(0); 274 | display.drawString("+", 288 + offsetX, 37 + offsetY); 275 | } 276 | else { 277 | display.setTextColor(TFT_GRAY, TFT_BACK); 278 | display.setFont(0); 279 | display.setTextDatum(CC_DATUM); 280 | display.setTextPadding(0); 281 | display.drawString(" ", 288 + offsetX, 37 + offsetY); 282 | } 283 | } 284 | } 285 | 286 | // Format Propag Message 287 | void propagMessage() 288 | { 289 | if(binarise().charAt(1) == '0') 290 | { 291 | messageB = "VHF Conditions -- "; 292 | 293 | for (uint8_t i = 0; i < 4; i++) 294 | { 295 | // Current propagation 50 MHz 296 | tmpString = hamQSLData; 297 | tmpString.replace(skipKey[i], "("); 298 | tmpString.replace("", ")"); 299 | parenthesisBegin = tmpString.indexOf("("); 300 | parenthesisLast = tmpString.indexOf(")", parenthesisBegin); 301 | if (parenthesisBegin > 0) 302 | { 303 | tmpString = tmpString.substring(parenthesisBegin + 1, parenthesisLast); 304 | } 305 | 306 | tmpString.trim(); 307 | 308 | messageB += skipData[i] + " : " + tmpString; 309 | messageB += " -- "; 310 | } 311 | 312 | messageB = messageB.substring(0, messageB.length() - 4); 313 | } 314 | else if(binarise().charAt(1) == '1') 315 | { 316 | messageB = "HF Conditions -- "; 317 | 318 | // Day 319 | for(uint8_t i = 0; i <= 3; i += 1) 320 | { 321 | tmpString = hamQSLData; 322 | tmpString.replace(propagKey[i], "("); 323 | tmpString.replace("", ")"); 324 | parenthesisBegin = tmpString.indexOf("("); 325 | parenthesisLast = tmpString.indexOf(")", parenthesisBegin); 326 | if (parenthesisBegin > 0) 327 | { 328 | tmpString = tmpString.substring(parenthesisBegin + 1, parenthesisLast); 329 | } 330 | tmpString.trim(); 331 | //tmpString.toUpperCase(); 332 | 333 | messageB += propagKey[i].substring(0, 7) + " Day " + tmpString + " -- "; 334 | } 335 | 336 | // Night 337 | for(uint8_t i = 4; i <= 7; i += 1) 338 | { 339 | tmpString = hamQSLData; 340 | tmpString.replace(propagKey[i], "("); 341 | tmpString.replace("", ")"); 342 | parenthesisBegin = tmpString.indexOf("("); 343 | parenthesisLast = tmpString.indexOf(")", parenthesisBegin); 344 | if (parenthesisBegin > 0) 345 | { 346 | tmpString = tmpString.substring(parenthesisBegin + 1, parenthesisLast); 347 | } 348 | tmpString.trim(); 349 | //tmpString.toUpperCase(); 350 | 351 | messageB += propagKey[i].substring(0, 7) + " Night " + tmpString + " -- "; 352 | } 353 | messageB = messageB.substring(0, messageB.length() - 4); 354 | } 355 | } 356 | 357 | // Format Cluster Message 358 | void clusterAndSatMessage() 359 | { 360 | boolean exclude = 0; 361 | uint8_t next = 0; 362 | uint8_t counter = 0; 363 | static uint8_t messageOld = 64; 364 | long tmp = 0; 365 | 366 | if(messageOld != messageCurrent && reloadState == "") { 367 | messageOld = messageCurrent; 368 | 369 | if(binarise().charAt(0) == '0') 370 | { 371 | posA = 80; 372 | messageA = ""; 373 | 374 | size_t n = sizeof(frequencyExclude)/sizeof(frequencyExclude[0]); 375 | 376 | cluster = getValue(hamQTHData, '|', next); 377 | while(cluster != "") 378 | { 379 | frequency = getValue(cluster, '^', 1); 380 | tmp = frequency.toInt(); 381 | 382 | exclude = 0; 383 | 384 | for (uint8_t j = 0; j < n; j++) 385 | { 386 | if(abs(tmp - frequencyExclude[j]) <= 2 || tmp > 470000) 387 | { 388 | exclude = 1; 389 | break; 390 | } 391 | } 392 | 393 | if(exclude == 0) 394 | { 395 | messageA += getValue(cluster, '^', 0) + " " + getValue(cluster, '^', 8) + " " + frequency + " " + getValue(cluster, '^', 9) + " -- "; 396 | counter += 1; 397 | } 398 | 399 | if(counter == 20) 400 | { 401 | break; 402 | } 403 | 404 | next++; 405 | cluster = getValue(hamQTHData, '|', next); 406 | } 407 | if(messageA != "") 408 | { 409 | messageA = "DX Cluster -- " + messageA; 410 | messageA = messageA.substring(0, messageA.length() - 4); 411 | } 412 | else 413 | { 414 | messageA = "DX Cluster -- Data acquisition on the way, please wait..."; 415 | } 416 | } 417 | else if(binarise().charAt(0) == '1' && reloadState == "") 418 | { 419 | posA = 80; 420 | messageA = ""; 421 | 422 | if(satData.length() > 32) 423 | { 424 | messageA = satData.substring(15, satData.length() - 3); 425 | } 426 | if(messageA != "") 427 | { 428 | messageA = "Satellites Passes -- " + messageA; 429 | } 430 | else 431 | { 432 | messageA = "Satellites Passes -- Data acquisition on the way, please wait..."; 433 | } 434 | } 435 | } 436 | } 437 | 438 | // Manage screensaver 439 | void wakeAndSleep() 440 | { 441 | if (screensaverMode == 0 && millis() - screensaverTimer > screensaver * 60 * 1000) 442 | { 443 | for (int16_t i = map(brightness, 1, 100, 1, 254); i >= 1; i -= 4) 444 | { 445 | display.setBrightness(i); 446 | scrollA(0); 447 | scrollB(0); 448 | delay(10); 449 | } 450 | 451 | screensaverMode = 1; 452 | display.sleep(); 453 | screensaverTimer = millis(); 454 | } 455 | else if (screensaverMode == 1 && millis() - screensaverTimer > screensaver * 60 * 1000) 456 | { 457 | screensaverTimer = millis(); 458 | display.wakeup(); 459 | screensaverMode = 0; 460 | 461 | for (int16_t i = 1; i <= map(brightness, 1, 100, 1, 254); i += 4) 462 | { 463 | display.setBrightness(i); 464 | scrollA(0); 465 | scrollB(0); 466 | delay(10); 467 | } 468 | } 469 | } 470 | 471 | // M5Screen2bmp (dump the screen to a file) 472 | 473 | bool M5Screen2bmp(fs::FS &fs, const char * path){ 474 | uint16_t image_height = display.height(); 475 | uint16_t image_width = display.width(); 476 | const uint16_t pad = (4 - (3 * image_width) % 4) % 4; 477 | uint16_t filesize = 54 + (3 * image_width + pad) * image_height; 478 | unsigned char swap; 479 | unsigned char line_data[image_width * 3 + pad]; 480 | unsigned char header[54] = { 481 | 'B','M', // BMP signature (Windows 3.1x, 95, NT, …) 482 | 0,0,0,0, // image file size in bytes 483 | 0,0,0,0, // reserved 484 | 54,0,0,0, // start of pixel array 485 | 40,0,0,0, // info header size 486 | 0,0,0,0, // image width 487 | 0,0,0,0, // image height 488 | 1,0, // number of color planes 489 | 24,0, // bits per pixel 490 | 0,0,0,0, // compression 491 | 0,0,0,0, // image size (can be 0 for uncompressed images) 492 | 0,0,0,0, // horizontal resolution (dpm) 493 | 0,0,0,0, // vertical resolution (dpm) 494 | 0,0,0,0, // colors in color table (0 = none) 495 | 0,0,0,0 };// important color count (0 = all colors are important) 496 | 497 | // Open file for writing 498 | // The existing image file will be replaced 499 | File file = fs.open(path, FILE_WRITE); 500 | 501 | if(file){ 502 | // fill filesize, width and heigth in the header array 503 | for(uint8_t i = 0; i < 4; i++) { 504 | header[ 2 + i] = (char)((filesize>>(8 * i)) & 255); 505 | header[18 + i] = (char)((image_width >>(8 * i)) & 255); 506 | header[22 + i] = (char)((image_height >>(8 * i)) & 255); 507 | } 508 | // write the header to the file 509 | file.write(header, 54); 510 | 511 | // initialize padded pixel with 0 512 | for(uint16_t i = (image_width - 1) * 3; i < (image_width * 3 + pad); i++){ 513 | line_data[i]=0; 514 | } 515 | // The coordinate origin of a BMP image is at the bottom left. 516 | // Therefore, the image must be read from bottom to top. 517 | for(uint16_t y = image_height; y > 0; y--){ 518 | // get one line of the screen content 519 | display.readRectRGB(0, y - 1, image_width, 1, line_data); 520 | // BMP color order is: Blue, Green, Red 521 | // return values from readRectRGB is: Red, Green, Blue 522 | // therefore: R und B need to be swapped 523 | for(uint16_t x = 0; x < image_width; x++){ 524 | swap = line_data[x * 3]; 525 | line_data[x * 3] = line_data[x * 3 + 2]; 526 | line_data[x * 3 + 2] = swap; 527 | } 528 | // write the line to the file 529 | file.write(line_data, (image_width * 3) + pad); 530 | } 531 | file.close(); 532 | return true; 533 | } 534 | return false; 535 | } 536 | 537 | // M5Screen2bmp (dump the screen to a WiFi client) 538 | 539 | bool M5Screen2bmp(){ 540 | uint16_t image_height = display.height(); 541 | uint16_t image_width = display.width(); 542 | const uint16_t pad = (4 - (3 * image_width) % 4) % 4; 543 | uint16_t filesize = 54 + (3 * image_width + pad) * image_height; 544 | unsigned char swap; 545 | unsigned char line_data[image_width * 3 + pad]; 546 | unsigned char header[54] = { 547 | 'B','M', // BMP signature (Windows 3.1x, 95, NT, …) 548 | 0,0,0,0, // image file size in bytes 549 | 0,0,0,0, // reserved 550 | 54,0,0,0, // start of pixel array 551 | 40,0,0,0, // info header size 552 | 0,0,0,0, // image width 553 | 0,0,0,0, // image height 554 | 1,0, // number of color planes 555 | 24,0, // bits per pixel 556 | 0,0,0,0, // compression 557 | 0,0,0,0, // image size (can be 0 for uncompressed images) 558 | 0,0,0,0, // horizontal resolution (dpm) 559 | 0,0,0,0, // vertical resolution (dpm) 560 | 0,0,0,0, // colors in color table (0 = none) 561 | 0,0,0,0 };// important color count (0 = all colors are important) 562 | 563 | // fill filesize, width and heigth in the header array 564 | for(uint8_t i = 0; i < 4; i++) { 565 | header[ 2 + i] = (char)((filesize>>(8 * i)) & 255); 566 | header[18 + i] = (char)((image_width >> (8 * i)) & 255); 567 | header[22 + i] = (char)((image_height >> (8 * i)) & 255); 568 | } 569 | // write the header to the file 570 | httpClient.write(header, 54); 571 | 572 | // To keep the required memory low, the image is captured line by line 573 | // initialize padded pixel with 0 574 | for(uint16_t i = (image_width - 1) * 3; i < (image_width * 3 + pad); i++){ 575 | line_data[i]=0; 576 | } 577 | // The coordinate origin of a BMP image is at the bottom left. 578 | // Therefore, the image must be read from bottom to top. 579 | for(uint16_t y = image_height; y > 0; y--){ 580 | // get one line of the screen content 581 | display.readRectRGB(0, y - 1, image_width, 1, line_data); 582 | // BMP color order is: Blue, Green, Red 583 | // return values from readRectRGB is: Red, Green, Blue 584 | // therefore: R und B need to be swapped 585 | for(uint16_t x = 0; x < image_width; x++){ 586 | swap = line_data[x * 3]; 587 | line_data[x * 3] = line_data[x * 3 + 2]; 588 | line_data[x * 3 + 2] = swap; 589 | } 590 | // write the line to the file 591 | httpClient.write(line_data, (image_width * 3) + pad); 592 | } 593 | return true; 594 | } 595 | 596 | // Get screenshot 597 | 598 | void getScreenshot() 599 | { 600 | unsigned long timeout_millis = millis() + 2000; 601 | String currentLine = ""; 602 | 603 | httpClient = httpServer.available(); 604 | 605 | if(WiFi.status() == WL_CONNECTED){ 606 | //client.setNoDelay(1); 607 | if (httpClient) { 608 | // Force a disconnect after 2 seconds 609 | Serial.println("New Client."); 610 | // Loop while the client's connected 611 | while (httpClient.connected()) { 612 | // If the client is still connected after 2 seconds, 613 | // Something is wrong. So kill the connection 614 | if(millis() > timeout_millis){ 615 | Serial.println("Force Client stop!"); 616 | httpClient.stop(); 617 | } 618 | // If there's bytes to read from the client, 619 | if (httpClient.available()) { 620 | char c = httpClient.read(); 621 | Serial.write(c); 622 | // If the byte is a newline character 623 | if (c == '\n') { 624 | // Uwo newline characters in a row (empty line) are indicating 625 | // The end of the client HTTP request, so send a response: 626 | if (currentLine.length() == 0) { 627 | // HTTP headers always start with a response code (e.g. HTTP/1.1 200 OK) 628 | // and a content-type so the client knows what's coming, then a blank line, 629 | // followed by the content: 630 | 631 | screensaverTimer = millis(); // Screensaver update !!! 632 | 633 | switch (htmlGetRequest) 634 | { 635 | case GET_index_page: { 636 | httpClient.println("HTTP/1.1 200 OK"); 637 | httpClient.println("Content-type:text/html"); 638 | httpClient.println(); 639 | if(M5.getBoard() == m5::board_t::board_M5Stack) { 640 | httpClient.write_P(index_m5stack_html, sizeof(index_m5stack_html)); 641 | } 642 | else if(M5.getBoard() == m5::board_t::board_M5StackCore2) { 643 | httpClient.write_P(index_core2_html, sizeof(index_core2_html)); 644 | } 645 | break; 646 | } 647 | case GET_screenshot: { 648 | httpClient.println("HTTP/1.1 200 OK"); 649 | httpClient.println("Content-type:image/bmp"); 650 | httpClient.println(); 651 | M5Screen2bmp(); 652 | break; 653 | } 654 | default: 655 | httpClient.println("HTTP/1.1 404 Not Found"); 656 | httpClient.println("Content-type:text/html"); 657 | httpClient.println(); 658 | httpClient.print("404 Page not found.
"); 659 | break; 660 | } 661 | // The HTTP response ends with another blank line: 662 | httpClient.println(); 663 | // Break out of the while loop: 664 | break; 665 | } else { // if a newline is found 666 | // Analyze the currentLine: 667 | // detect the specific GET requests: 668 | if(currentLine.startsWith("GET /")){ 669 | htmlGetRequest = GET_unknown; 670 | // If no specific target is requested 671 | if(currentLine.startsWith("GET / ")){ 672 | htmlGetRequest = GET_index_page; 673 | } 674 | // If the screenshot image is requested 675 | if(currentLine.startsWith("GET /screenshot.bmp")){ 676 | htmlGetRequest = GET_screenshot; 677 | } 678 | // If the button left was pressed on the HTML page 679 | if(currentLine.startsWith("GET /buttonLeft")){ 680 | buttonLeftPressed = true; 681 | htmlGetRequest = GET_index_page; 682 | } 683 | // If the button center was pressed on the HTML page 684 | if(currentLine.startsWith("GET /buttonCenter")){ 685 | buttonCenterPressed = true; 686 | htmlGetRequest = GET_index_page; 687 | } 688 | // If the button right was pressed on the HTML page 689 | if(currentLine.startsWith("GET /buttonRight")){ 690 | buttonRightPressed = true; 691 | htmlGetRequest = GET_index_page; 692 | } 693 | } 694 | currentLine = ""; 695 | } 696 | } else if (c != '\r') { 697 | // Add anything else than a carriage return 698 | // character to the currentLine 699 | currentLine += c; 700 | } 701 | } 702 | } 703 | // Close the connection 704 | httpClient.stop(); 705 | Serial.println("Client Disconnected."); 706 | } 707 | } 708 | } 709 | 710 | // List files on SPIFFS or SD 711 | void getBinaryList(File dir, String type) 712 | { 713 | while (true) 714 | { 715 | File entry = dir.openNextFile(); 716 | if (!entry) 717 | { 718 | // no more files 719 | break; 720 | } 721 | 722 | if (strstr(entry.name(), "/.") == NULL && strstr(entry.name(), ".bin") != NULL) 723 | { 724 | //Serial.println(type + "_" + entry.name()); 725 | binFilename[binIndex] = type + "_" + entry.name(); 726 | binIndex++; 727 | } 728 | 729 | if (entry.isDirectory() && strstr(entry.name(), "/.") == NULL) 730 | { 731 | getBinaryList(entry, type); 732 | } 733 | 734 | entry.close(); 735 | } 736 | } 737 | 738 | // Bin Loader 739 | void binLoader() 740 | { 741 | boolean click = 0; 742 | int8_t cursor = 0; 743 | int8_t start = 0; 744 | int8_t stop = 0; 745 | int8_t limit = 8; 746 | int8_t change = 255; 747 | String tmpName; 748 | 749 | if (!SPIFFS.begin()) 750 | { 751 | Serial.println("SPIFFS Mount Failed"); 752 | 753 | display.setTextFont(1); 754 | display.setTextSize(2); 755 | 756 | display.setTextColor(TFT_WHITE, TFT_BLACK); 757 | display.setTextDatum(CC_DATUM); 758 | display.drawString("Flash File System", 160, 20); 759 | display.drawString("needs to be formated.", 160, 50); 760 | display.drawString("It takes around 4 minutes.", 160, 100); 761 | display.drawString("Please, wait until ", 160, 150); 762 | display.drawString("the application starts !", 160, 180); 763 | 764 | Serial.println("SPIFFS Formating..."); 765 | 766 | SPIFFS.format(); // Format SPIFFS... 767 | 768 | display.setTextFont(0); 769 | display.setTextSize(0); 770 | 771 | return; 772 | } 773 | 774 | root = SPIFFS.open("/"); 775 | getBinaryList(root, "SP"); 776 | 777 | if (SD.begin(GPIO_NUM_4, SPI, 25000000)) 778 | { 779 | root = SD.open("/"); 780 | getBinaryList(root, "SD"); 781 | } 782 | 783 | if (binIndex != 0) 784 | { 785 | // QRCode 786 | display.qrcode("https://github.com/armel/DXTracker", 90 + offsetX, 80 + offsetY, 140, 6); 787 | 788 | display.setTextFont(1); 789 | display.setTextSize(1); 790 | 791 | display.setTextColor(TFT_WHITE, TFT_BLACK); 792 | display.setTextDatum(CC_DATUM); 793 | 794 | for (uint8_t i = TIMEOUT_BIN_LOADER * 10; i > 0; i--) 795 | { 796 | getButton(); 797 | 798 | if (i % 10 == 0) 799 | { 800 | tmpName += "."; 801 | display.drawString(tmpName, 160, 20); 802 | } 803 | 804 | if (btnA || btnC) 805 | { 806 | SD.end(); // If not Bluetooth doesn't work !!! 807 | return; 808 | } 809 | else if (btnB) 810 | { 811 | click = 1; 812 | display.fillRect(0, 0, 320, 240, TFT_BLACK); 813 | break; 814 | } 815 | 816 | vTaskDelay(100); 817 | } 818 | } 819 | 820 | while (click == 1) 821 | { 822 | while (btnB != 0) 823 | { 824 | getButton(); 825 | vTaskDelay(100); 826 | } 827 | 828 | display.setTextFont(1); 829 | display.setTextSize(2); 830 | 831 | display.setTextColor(TFT_WHITE, TFT_BLACK); 832 | display.setTextDatum(CC_DATUM); 833 | display.drawString("Bin Loader V0.2", 160, 20); 834 | 835 | getButton(); 836 | 837 | if (btnA) 838 | { 839 | cursor--; 840 | } 841 | else if (btnC) 842 | { 843 | cursor++; 844 | } 845 | else if (btnB) 846 | { 847 | if(binFilename[cursor].substring(0, 4) == "SP_/") { 848 | updateFromFS(SPIFFS, binFilename[cursor].substring(3)); 849 | } 850 | else { 851 | updateFromFS(SD, binFilename[cursor].substring(3)); 852 | } 853 | ESP.restart(); 854 | } 855 | 856 | cursor = (cursor < 0) ? binIndex - 1 : cursor; 857 | cursor = (cursor > binIndex - 1) ? 0 : cursor; 858 | 859 | start = cursor / limit; 860 | 861 | stop = (start * limit) + limit; 862 | 863 | /* 864 | Serial.print(cursor); 865 | Serial.print("-"); 866 | Serial.print(start); 867 | Serial.print("-"); 868 | Serial.print(stop); 869 | Serial.println("----------"); 870 | */ 871 | 872 | if (change != cursor) 873 | { 874 | change = cursor; 875 | display.setTextPadding(320); 876 | 877 | uint8_t i = 0; 878 | for (uint8_t j = (start * limit); j < stop; j++) 879 | { 880 | tmpName = binFilename[j].substring(4); 881 | 882 | if (cursor == j) 883 | { 884 | tmpName = ">> " + tmpName + " <<"; 885 | 886 | if(binFilename[cursor].substring(0, 4) == "SP_/") { 887 | display.setTextSize(1); 888 | display.drawString("SPI Flash File Storage", 160, 50); 889 | } 890 | else { 891 | display.setTextSize(1); 892 | display.drawString("SD Card Storage", 160, 50); 893 | } 894 | } 895 | 896 | display.setTextSize(2); 897 | display.drawString(tmpName, 160, 80 + i * 20); 898 | i++; 899 | } 900 | } 901 | vTaskDelay(100); 902 | } 903 | SD.end(); // If not Bluetooth doesn't work !!! 904 | } 905 | 906 | // View Clock 907 | void viewClock() 908 | { 909 | if(watch == 1) 910 | { 911 | updateLocalTime(); 912 | 913 | if(maps == 0) 914 | { 915 | display.setTextColor(TFT_WHITE, TFT_BACK); 916 | } 917 | else 918 | { 919 | display.setTextColor(TFT_WHITE, display.color565(24, 57, 92)); 920 | } 921 | display.setFont(&YELLOWCRE8pt7b); 922 | display.setTextPadding(100); 923 | display.setTextDatum(CL_DATUM); 924 | display.drawString(timeLocalBuff, 60 + offsetX, 220 + offsetY); 925 | display.setTextDatum(CR_DATUM); 926 | display.drawString(dateLocalBuff, 260 + offsetX, 220 + offsetY); 927 | } 928 | } -------------------------------------------------------------------------------- /src/map.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) F4HWN Armel. All rights reserved. 2 | // Licensed under the MIT license. See LICENSE file in the project root for full license information. 3 | 4 | #include // PROGMEM support header 5 | 6 | PROGMEM const uint8_t map_sunmap[]={ 7 | 0xff, 0xd8, 0xff, 0xe0, 0x00, 0x10, 0x4a, 0x46, 0x49, 0x46, 0x00, 0x01, 0x01, 0x00, 0x00, 0x48, 8 | 0x00, 0x48, 0x00, 0x00, 0xff, 0xe1, 0x00, 0x8c, 0x45, 0x78, 0x69, 0x66, 0x00, 0x00, 0x4d, 0x4d, 9 | 0x00, 0x2a, 0x00, 0x00, 0x00, 0x08, 0x00, 0x05, 0x01, 0x12, 0x00, 0x03, 0x00, 0x00, 0x00, 0x01, 10 | 0x00, 0x01, 0x00, 0x00, 0x01, 0x1a, 0x00, 0x05, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x4a, 11 | 0x01, 0x1b, 0x00, 0x05, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x52, 0x01, 0x28, 0x00, 0x03, 12 | 0x00, 0x00, 0x00, 0x01, 0x00, 0x02, 0x00, 0x00, 0x87, 0x69, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 13 | 0x00, 0x00, 0x00, 0x5a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x48, 0x00, 0x00, 0x00, 0x01, 14 | 0x00, 0x00, 0x00, 0x48, 0x00, 0x00, 0x00, 0x01, 0x00, 0x03, 0xa0, 0x01, 0x00, 0x03, 0x00, 0x00, 15 | 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0xa0, 0x02, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 16 | 0x00, 0x78, 0xa0, 0x03, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x3c, 0x00, 0x00, 17 | 0x00, 0x00, 0xff, 0xed, 0x00, 0x38, 0x50, 0x68, 0x6f, 0x74, 0x6f, 0x73, 0x68, 0x6f, 0x70, 0x20, 18 | 0x33, 0x2e, 0x30, 0x00, 0x38, 0x42, 0x49, 0x4d, 0x04, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 19 | 0x38, 0x42, 0x49, 0x4d, 0x04, 0x25, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0xd4, 0x1d, 0x8c, 0xd9, 20 | 0x8f, 0x00, 0xb2, 0x04, 0xe9, 0x80, 0x09, 0x98, 0xec, 0xf8, 0x42, 0x7e, 0xff, 0xc0, 0x00, 0x11, 21 | 0x08, 0x00, 0x3c, 0x00, 0x78, 0x03, 0x01, 0x22, 0x00, 0x02, 0x11, 0x01, 0x03, 0x11, 0x01, 0xff, 22 | 0xc4, 0x00, 0x1f, 0x00, 0x00, 0x01, 0x05, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 23 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 24 | 0xff, 0xc4, 0x00, 0xb5, 0x10, 0x00, 0x02, 0x01, 0x03, 0x03, 0x02, 0x04, 0x03, 0x05, 0x05, 0x04, 25 | 0x04, 0x00, 0x00, 0x01, 0x7d, 0x01, 0x02, 0x03, 0x00, 0x04, 0x11, 0x05, 0x12, 0x21, 0x31, 0x41, 26 | 0x06, 0x13, 0x51, 0x61, 0x07, 0x22, 0x71, 0x14, 0x32, 0x81, 0x91, 0xa1, 0x08, 0x23, 0x42, 0xb1, 27 | 0xc1, 0x15, 0x52, 0xd1, 0xf0, 0x24, 0x33, 0x62, 0x72, 0x82, 0x09, 0x0a, 0x16, 0x17, 0x18, 0x19, 28 | 0x1a, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x43, 0x44, 29 | 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x63, 0x64, 30 | 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, 0x83, 0x84, 31 | 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, 0xa2, 32 | 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 33 | 0xba, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 34 | 0xd8, 0xd9, 0xda, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea, 0xf1, 0xf2, 0xf3, 35 | 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0xff, 0xc4, 0x00, 0x1f, 0x01, 0x00, 0x03, 0x01, 0x01, 36 | 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x03, 37 | 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0xff, 0xc4, 0x00, 0xb5, 0x11, 0x00, 0x02, 0x01, 38 | 0x02, 0x04, 0x04, 0x03, 0x04, 0x07, 0x05, 0x04, 0x04, 0x00, 0x01, 0x02, 0x77, 0x00, 0x01, 0x02, 39 | 0x03, 0x11, 0x04, 0x05, 0x21, 0x31, 0x06, 0x12, 0x41, 0x51, 0x07, 0x61, 0x71, 0x13, 0x22, 0x32, 40 | 0x81, 0x08, 0x14, 0x42, 0x91, 0xa1, 0xb1, 0xc1, 0x09, 0x23, 0x33, 0x52, 0xf0, 0x15, 0x62, 0x72, 41 | 0xd1, 0x0a, 0x16, 0x24, 0x34, 0xe1, 0x25, 0xf1, 0x17, 0x18, 0x19, 0x1a, 0x26, 0x27, 0x28, 0x29, 42 | 0x2a, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x53, 43 | 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x73, 44 | 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 45 | 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 46 | 0xa9, 0xaa, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 47 | 0xc7, 0xc8, 0xc9, 0xca, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xe2, 0xe3, 0xe4, 48 | 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0xff, 49 | 0xdb, 0x00, 0x43, 0x00, 0x0a, 0x07, 0x07, 0x08, 0x07, 0x06, 0x0a, 0x08, 0x08, 0x08, 0x0b, 0x0a, 50 | 0x0a, 0x0b, 0x0e, 0x18, 0x10, 0x0e, 0x0d, 0x0d, 0x0e, 0x1d, 0x15, 0x16, 0x11, 0x18, 0x23, 0x1f, 51 | 0x25, 0x24, 0x22, 0x1f, 0x22, 0x21, 0x26, 0x2b, 0x37, 0x2f, 0x26, 0x29, 0x34, 0x29, 0x21, 0x22, 52 | 0x30, 0x41, 0x31, 0x34, 0x39, 0x3b, 0x3e, 0x3e, 0x3e, 0x25, 0x2e, 0x44, 0x49, 0x43, 0x3c, 0x48, 53 | 0x37, 0x3d, 0x3e, 0x3b, 0xff, 0xdb, 0x00, 0x43, 0x01, 0x0a, 0x0b, 0x0b, 0x0e, 0x0d, 0x0e, 0x1c, 54 | 0x10, 0x10, 0x1c, 0x3b, 0x28, 0x22, 0x28, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 55 | 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 56 | 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 57 | 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0xff, 0xdd, 0x00, 0x04, 0x00, 0x08, 0xff, 58 | 0xda, 0x00, 0x0c, 0x03, 0x01, 0x00, 0x02, 0x11, 0x03, 0x11, 0x00, 0x3f, 0x00, 0xce, 0x87, 0x4b, 59 | 0x69, 0x56, 0x28, 0x90, 0x59, 0xcb, 0x91, 0x85, 0x57, 0xb5, 0xdc, 0x4f, 0x7f, 0x5e, 0xb5, 0x76, 60 | 0x2f, 0x0b, 0x4e, 0xc0, 0xaf, 0xd9, 0xed, 0x9b, 0x3d, 0x49, 0xb2, 0x3f, 0x2f, 0xeb, 0x53, 0xe9, 61 | 0x70, 0xbe, 0x61, 0x78, 0xb6, 0xac, 0x80, 0x0c, 0x31, 0x5c, 0xf3, 0xff, 0x00, 0xea, 0xae, 0xbe, 62 | 0xcd, 0x2e, 0xa1, 0xb5, 0x3e, 0x5d, 0xdc, 0x8a, 0xe4, 0x12, 0xac, 0xc0, 0x10, 0x0f, 0xae, 0x31, 63 | 0x5e, 0x8c, 0xa0, 0xd6, 0xc8, 0xe6, 0xe6, 0x5d, 0x59, 0xc1, 0x4b, 0xe0, 0x6b, 0xe8, 0xcc, 0x61, 64 | 0x01, 0x65, 0x6e, 0x0b, 0xbc, 0x4c, 0x80, 0x7e, 0x27, 0x8a, 0x82, 0x5f, 0x07, 0xea, 0x00, 0xc8, 65 | 0x11, 0x90, 0x04, 0x5c, 0x96, 0x28, 0x70, 0x39, 0xe3, 0x9e, 0x95, 0xec, 0xfa, 0x45, 0xe4, 0xb7, 66 | 0x76, 0xed, 0xe7, 0x42, 0xc9, 0x24, 0x6d, 0xb5, 0x8e, 0xec, 0xab, 0x9c, 0x67, 0x2a, 0x7a, 0xe3, 67 | 0xd8, 0xf2, 0x29, 0xba, 0xd4, 0x30, 0x49, 0x67, 0xe6, 0x5c, 0xb4, 0x82, 0x38, 0xce, 0x48, 0x54, 68 | 0xde, 0x0f, 0xd5, 0x7b, 0xd7, 0x23, 0x9b, 0xbd, 0x9e, 0x86, 0xdc, 0xaa, 0xd7, 0x47, 0x92, 0x41, 69 | 0xe0, 0x59, 0xa4, 0x08, 0x64, 0xd4, 0x62, 0x8c, 0x06, 0xdb, 0x27, 0xee, 0xf3, 0xcf, 0x60, 0x1b, 70 | 0xa7, 0x3e, 0xe0, 0x56, 0x9d, 0x9f, 0xc3, 0x68, 0x6f, 0xc4, 0xc6, 0x0d, 0x55, 0xb7, 0x5b, 0xbf, 71 | 0x97, 0x2a, 0x18, 0x40, 0x28, 0xd8, 0x07, 0x1d, 0x79, 0xe0, 0x83, 0x91, 0xeb, 0x5d, 0x74, 0x96, 72 | 0xba, 0x5c, 0x1a, 0x64, 0x73, 0xbb, 0x19, 0x0b, 0x49, 0x95, 0x6b, 0x5f, 0x98, 0x12, 0x0e, 0x17, 73 | 0x20, 0xf5, 0xc7, 0x1f, 0x4a, 0x6c, 0x24, 0xc9, 0x11, 0x2f, 0x1b, 0x46, 0x58, 0xfc, 0xe8, 0x78, 74 | 0x0c, 0x47, 0x42, 0x47, 0x7a, 0xd2, 0x31, 0xe7, 0x5a, 0x10, 0xe5, 0xcb, 0xb9, 0xe7, 0xba, 0x87, 75 | 0x82, 0x24, 0xb5, 0xbe, 0x5b, 0x68, 0x2f, 0x4c, 0xb8, 0xf9, 0xa4, 0x73, 0x16, 0x15, 0x47, 0x3f, 76 | 0x74, 0xff, 0x00, 0x11, 0xce, 0x38, 0xed, 0x55, 0xa5, 0xf0, 0xb3, 0x46, 0x32, 0x2e, 0xfa, 0x75, 77 | 0xcc, 0x7d, 0x3f, 0x5a, 0xf4, 0xd9, 0x21, 0x8e, 0x5c, 0x79, 0x91, 0xab, 0x60, 0x60, 0x12, 0x3a, 78 | 0x56, 0x05, 0xee, 0x9b, 0x71, 0x21, 0x99, 0x9e, 0x58, 0x91, 0x03, 0x0c, 0x3b, 0x72, 0xb8, 0x3d, 79 | 0x06, 0x3f, 0x8b, 0x9c, 0xe7, 0xa7, 0x6a, 0xdb, 0x96, 0x30, 0x5a, 0x91, 0xcc, 0xe4, 0xf4, 0x38, 80 | 0x19, 0xf4, 0x79, 0x61, 0x45, 0x6f, 0xb4, 0xab, 0x86, 0xe8, 0x56, 0x13, 0x8c, 0x7a, 0xe7, 0x35, 81 | 0x51, 0xf4, 0xf9, 0x3f, 0x86, 0xe8, 0x93, 0xe8, 0xa9, 0x9a, 0xea, 0x35, 0x24, 0x11, 0x4c, 0xb3, 82 | 0xcb, 0x70, 0x1c, 0x38, 0xc7, 0x03, 0x0b, 0xc7, 0x18, 0x15, 0x59, 0x64, 0x88, 0x2a, 0x85, 0x20, 83 | 0x02, 0x32, 0x00, 0xf4, 0xa1, 0x5a, 0xdb, 0x17, 0x63, 0x03, 0xfb, 0x2e, 0x6e, 0x86, 0x76, 0x2c, 84 | 0x7a, 0x00, 0xb5, 0x2f, 0xf6, 0x0d, 0xc9, 0xe7, 0xed, 0x4a, 0x06, 0x3a, 0x15, 0x24, 0xff, 0x00, 85 | 0x3a, 0xde, 0xce, 0x46, 0x68, 0xa2, 0xc8, 0x66, 0x0f, 0xf6, 0x25, 0xce, 0x39, 0x98, 0xfe, 0x1c, 86 | 0xff, 0x00, 0x5a, 0x6f, 0xf6, 0x3d, 0xd6, 0x7f, 0xd6, 0x3e, 0x07, 0x7c, 0x7f, 0x4c, 0xd7, 0x41, 87 | 0x9a, 0xa7, 0x25, 0xc3, 0xf9, 0xcc, 0x8b, 0x20, 0xc7, 0x6d, 0xa2, 0x9a, 0x8a, 0x62, 0x66, 0x68, 88 | 0xd3, 0x26, 0x53, 0xf3, 0x4a, 0xc7, 0xd8, 0xc6, 0x69, 0xdf, 0xd9, 0xed, 0xfd, 0xe6, 0xff, 0x00, 89 | 0xbf, 0x66, 0xae, 0x96, 0x72, 0x08, 0x2e, 0xc4, 0x1e, 0xc5, 0xb3, 0x4c, 0xd8, 0x3f, 0xbb, 0xfa, 90 | 0xd5, 0xfb, 0x3f, 0x22, 0x6e, 0x7f, 0xff, 0xd0, 0xd5, 0xd1, 0x6d, 0xc1, 0x65, 0xcf, 0x40, 0x3a, 91 | 0xd7, 0x4e, 0x31, 0x81, 0x8e, 0x9d, 0xab, 0x2b, 0x43, 0x87, 0x64, 0x45, 0xf1, 0xdb, 0x8a, 0xd6, 92 | 0xaf, 0x5d, 0x9c, 0x2c, 0x92, 0x1b, 0xe9, 0xec, 0x4e, 0xe8, 0x93, 0xcd, 0x46, 0x61, 0xbe, 0x3c, 93 | 0xf2, 0x47, 0x72, 0x3d, 0xff, 0x00, 0x9d, 0x68, 0x5e, 0x6a, 0xde, 0x50, 0x8c, 0xc4, 0x88, 0xe8, 94 | 0xea, 0x1b, 0x73, 0x12, 0x07, 0x5e, 0x98, 0xc7, 0x5a, 0xcb, 0xa6, 0x91, 0xd8, 0x64, 0x67, 0x8e, 95 | 0x2b, 0x9e, 0x74, 0x54, 0x9d, 0xcb, 0x8d, 0x46, 0x95, 0x86, 0x2d, 0xea, 0x5d, 0xcf, 0x23, 0x92, 96 | 0x12, 0x52, 0x70, 0x50, 0xf0, 0x54, 0x76, 0x00, 0x7a, 0x54, 0xb5, 0x6a, 0xc3, 0x4a, 0x17, 0x16, 97 | 0x8b, 0x24, 0xb3, 0x39, 0x76, 0xce, 0xd2, 0xa0, 0x01, 0x8e, 0xc7, 0x1c, 0xd4, 0x57, 0x16, 0xb3, 98 | 0x5a, 0xc8, 0x55, 0xd5, 0x99, 0x40, 0x07, 0xcc, 0x55, 0x3b, 0x7f, 0xfa, 0xd4, 0xe1, 0x52, 0x3f, 99 | 0x08, 0x4a, 0x12, 0xdc, 0x84, 0x06, 0x2c, 0x02, 0x0c, 0xbb, 0x10, 0x00, 0xce, 0x32, 0x6a, 0xed, 100 | 0xe6, 0x85, 0x04, 0xda, 0x76, 0xc9, 0xcb, 0xc8, 0xe8, 0x84, 0x0c, 0xb1, 0xc2, 0xe7, 0xae, 0x07, 101 | 0xf9, 0xe9, 0x54, 0xb8, 0xdc, 0xa7, 0x9c, 0x06, 0x04, 0xe3, 0xad, 0x5d, 0x4d, 0x49, 0xa3, 0x05, 102 | 0x3c, 0xb2, 0xf1, 0x67, 0xab, 0x36, 0x0e, 0x0d, 0x45, 0x65, 0x36, 0xd5, 0x8b, 0xa6, 0xe2, 0x96, 103 | 0xa7, 0x9a, 0xeb, 0xba, 0x47, 0xd9, 0x59, 0x5e, 0x29, 0x0e, 0xe0, 0x48, 0x1c, 0x7a, 0xff, 0x00, 104 | 0x5c, 0xe7, 0x9a, 0xcd, 0x66, 0x50, 0x12, 0x55, 0x8f, 0x72, 0x81, 0x9c, 0x83, 0x82, 0xb5, 0xd9, 105 | 0x78, 0xa2, 0xd4, 0x84, 0x90, 0x8e, 0x76, 0x9d, 0xc3, 0xdf, 0xfc, 0xf3, 0x5c, 0x3c, 0xdb, 0x37, 106 | 0x90, 0x99, 0xc6, 0x49, 0x3c, 0xf1, 0x9a, 0xba, 0x7e, 0xf1, 0x4f, 0x42, 0xcc, 0x72, 0xa4, 0x84, 107 | 0xed, 0xce, 0x47, 0x50, 0x69, 0x56, 0x54, 0x60, 0x48, 0x61, 0x85, 0xea, 0x6a, 0x96, 0x48, 0x07, 108 | 0x07, 0x19, 0x18, 0x34, 0x85, 0x99, 0x18, 0x32, 0xf0, 0x31, 0xcf, 0xd2, 0xb4, 0x70, 0xb0, 0xae, 109 | 0x49, 0x72, 0xe9, 0x33, 0x29, 0x8d, 0x89, 0xf9, 0x4f, 0x23, 0xa5, 0x46, 0xab, 0xb7, 0xb9, 0x24, 110 | 0xf5, 0x27, 0xbd, 0x47, 0x21, 0xdb, 0x2a, 0x1d, 0xc7, 0x00, 0x67, 0x0b, 0xcf, 0x1e, 0x86, 0xa4, 111 | 0xdc, 0x36, 0x86, 0xce, 0x01, 0xe8, 0x4f, 0x7a, 0xb8, 0xd8, 0x4e, 0xe2, 0xf5, 0x38, 0xa7, 0x79, 112 | 0x6f, 0xfd, 0xd3, 0x4f, 0xb5, 0x4f, 0x32, 0xe6, 0x35, 0xc7, 0x7c, 0x9a, 0xd7, 0xf2, 0x53, 0xfb, 113 | 0xb5, 0x64, 0x36, 0x7f, 0xff, 0xd1, 0xed, 0x2c, 0x62, 0xf2, 0xad, 0x54, 0x7a, 0xd5, 0x8a, 0x00, 114 | 0xc0, 0x03, 0x18, 0xc5, 0x2d, 0x7a, 0xc7, 0x00, 0x52, 0x52, 0xd1, 0x40, 0x17, 0x74, 0x49, 0x27, 115 | 0x59, 0x64, 0x81, 0xdb, 0x72, 0x60, 0xb2, 0xfb, 0x73, 0xff, 0x00, 0xd7, 0xab, 0x9a, 0x85, 0xea, 116 | 0xc3, 0x09, 0x89, 0x49, 0x13, 0x3a, 0xfc, 0xb8, 0x1f, 0x77, 0xb6, 0x6b, 0x22, 0xd7, 0x50, 0x5b, 117 | 0x59, 0x2e, 0x11, 0x4a, 0x79, 0xdb, 0x50, 0x20, 0x63, 0xce, 0x0e, 0x49, 0x38, 0xf4, 0x18, 0xfc, 118 | 0xe9, 0x9b, 0xb7, 0xfc, 0xe5, 0x8b, 0x96, 0xe7, 0x71, 0x39, 0xcd, 0x71, 0x42, 0x82, 0xe7, 0x76, 119 | 0xd9, 0x1d, 0x32, 0xaa, 0xf9, 0x75, 0xdc, 0x00, 0x00, 0x60, 0x74, 0x14, 0x1e, 0x41, 0x02, 0x96, 120 | 0x8a, 0xed, 0x39, 0x88, 0x35, 0xf8, 0x91, 0xac, 0x12, 0xe1, 0x47, 0xee, 0xfc, 0xa1, 0xfc, 0xba, 121 | 0x7e, 0x7c, 0x57, 0x98, 0xdc, 0x46, 0x62, 0x9d, 0x94, 0xf0, 0x72, 0x4e, 0x3b, 0x63, 0xb5, 0x7a, 122 | 0x6d, 0xec, 0x26, 0xe2, 0xd8, 0xc7, 0x96, 0x20, 0x1c, 0x85, 0xcf, 0x1f, 0x95, 0x71, 0xba, 0x9e, 123 | 0x98, 0x5c, 0x9e, 0x30, 0xc3, 0xa1, 0xff, 0x00, 0x3d, 0xaa, 0x29, 0x43, 0x94, 0xd5, 0xce, 0xe6, 124 | 0x05, 0x15, 0x24, 0xb0, 0x49, 0x0b, 0x61, 0xd0, 0x8f, 0x7e, 0xd5, 0x1d, 0x6e, 0x03, 0xe2, 0xb4, 125 | 0x59, 0x3e, 0x6f, 0xba, 0x3d, 0xba, 0xd2, 0xcb, 0x6b, 0xb1, 0x4b, 0x02, 0x48, 0x07, 0x38, 0xf4, 126 | 0xa6, 0x02, 0x4f, 0xca, 0x09, 0x3e, 0xc0, 0xd5, 0xbb, 0x6b, 0x2b, 0x89, 0xb0, 0x8f, 0x91, 0x19, 127 | 0xe4, 0x83, 0xf7, 0x8f, 0xf8, 0x0a, 0xcd, 0xc5, 0xde, 0xe3, 0xba, 0xb1, 0x36, 0x8d, 0x6a, 0xcd, 128 | 0x21, 0x70, 0xa0, 0x03, 0xc2, 0xe6, 0xb6, 0xfe, 0xc9, 0x73, 0xfd, 0xc4, 0xff, 0x00, 0xbe, 0x4f, 129 | 0xf8, 0xd4, 0xda, 0x5e, 0x9c, 0xe5, 0xe3, 0x08, 0x36, 0x28, 0xc1, 0xdd, 0xed, 0x5b, 0x7f, 0x61, 130 | 0xff, 0x00, 0xa6, 0xed, 0xff, 0x00, 0x7c, 0x8a, 0x6d, 0x99, 0xb3, 0xff, 0xd2, 0xef, 0x09, 0x00, 131 | 0x64, 0x9c, 0x50, 0xac, 0x1d, 0x8a, 0xa6, 0x5c, 0x8e, 0xa1, 0x41, 0x38, 0xfc, 0xab, 0x61, 0xf4, 132 | 0x4b, 0x46, 0x1c, 0x99, 0x0e, 0x0e, 0x79, 0x6c, 0xff, 0x00, 0x31, 0x53, 0xda, 0x58, 0x47, 0x66, 133 | 0x81, 0x22, 0x92, 0x4d, 0xa3, 0x92, 0x09, 0x1c, 0xfd, 0x78, 0xae, 0xd9, 0x62, 0x2d, 0xb2, 0x39, 134 | 0x95, 0x2e, 0xe6, 0x00, 0x0e, 0x58, 0x81, 0x14, 0xd9, 0x1f, 0xf4, 0xc9, 0xbf, 0xc2, 0x95, 0x12, 135 | 0x69, 0x0b, 0x04, 0xb6, 0x9c, 0x85, 0x38, 0xcf, 0x94, 0x40, 0x3f, 0x4a, 0xe9, 0xe9, 0x6b, 0x3f, 136 | 0xac, 0xcb, 0xb1, 0x7e, 0xc5, 0x1c, 0x74, 0xc6, 0x56, 0x72, 0x22, 0xb6, 0x5d, 0xc8, 0x0e, 0x26, 137 | 0x94, 0x0d, 0xa0, 0xfa, 0x0e, 0xe4, 0xfb, 0x71, 0x53, 0xa0, 0x60, 0x80, 0x3b, 0x6f, 0x6c, 0x72, 138 | 0x71, 0x8c, 0xfe, 0x15, 0xb7, 0x3e, 0x8d, 0xa6, 0xdc, 0x38, 0x79, 0x2c, 0xa2, 0x2c, 0xa4, 0x90, 139 | 0xc0, 0x6d, 0x39, 0x3d, 0x4f, 0x14, 0x26, 0x8d, 0xa6, 0xc6, 0xca, 0x56, 0xce, 0x2c, 0xae, 0x70, 140 | 0x48, 0xce, 0x33, 0xd6, 0x9a, 0xc4, 0x75, 0x62, 0x74, 0x7b, 0x18, 0xe8, 0x8f, 0x27, 0x11, 0x23, 141 | 0x3f, 0xfb, 0x8b, 0x9a, 0x99, 0x74, 0xfb, 0xc6, 0xff, 0x00, 0x97, 0x66, 0x1f, 0xef, 0x32, 0x8f, 142 | 0xeb, 0x5b, 0xd1, 0x45, 0x1c, 0x31, 0x88, 0xe2, 0x8d, 0x51, 0x07, 0x45, 0x51, 0x80, 0x29, 0xf4, 143 | 0x9e, 0x22, 0x5d, 0x06, 0xa8, 0xae, 0xa6, 0x07, 0xf6, 0x6d, 0xef, 0xfc, 0xf1, 0x1f, 0x8b, 0x8a, 144 | 0x86, 0x6f, 0x0c, 0xcb, 0x74, 0x32, 0xc6, 0x28, 0xc9, 0xe7, 0x04, 0x93, 0xcf, 0xe1, 0xd2, 0xba, 145 | 0x5a, 0x2a, 0x7d, 0xbc, 0xca, 0xf6, 0x51, 0x38, 0x59, 0xbc, 0x19, 0x78, 0x65, 0x2a, 0xab, 0x19, 146 | 0x5f, 0xef, 0xef, 0xe3, 0xfc, 0x6a, 0x16, 0xf0, 0x25, 0xc9, 0x3c, 0xf9, 0x5f, 0xf7, 0xc1, 0xaf, 147 | 0x40, 0xa2, 0x8f, 0xac, 0x54, 0xee, 0x1e, 0xce, 0x27, 0x05, 0x1f, 0x81, 0xae, 0x97, 0xa3, 0x44, 148 | 0x3f, 0xe0, 0x26, 0xae, 0xc5, 0xe0, 0xd9, 0xe2, 0xc1, 0xdf, 0x0b, 0xfb, 0x64, 0x8a, 0xec, 0x28, 149 | 0xa3, 0xdb, 0xd4, 0xee, 0x3f, 0x67, 0x13, 0x94, 0x10, 0x8b, 0x59, 0x9a, 0xd8, 0xed, 0xf3, 0x14, 150 | 0x7c, 0xd8, 0x60, 0x71, 0xe9, 0x9f, 0x4a, 0x75, 0x6e, 0xbe, 0x95, 0x69, 0x2c, 0x8f, 0x29, 0x8c, 151 | 0xab, 0xb9, 0xcb, 0x15, 0x62, 0x32, 0x69, 0xbf, 0xd8, 0xf6, 0xbf, 0xf4, 0xd3, 0xfe, 0xfb, 0x35, 152 | 0xac, 0x71, 0x0a, 0xda, 0x98, 0xba, 0x4e, 0xfa, 0x1f, 0xff, 0xd9, 153 | }; 154 | 155 | PROGMEM const uint8_t map_greyline[]={ 156 | 0xff, 0xd8, 0xff, 0xe0, 0x00, 0x10, 0x4a, 0x46, 0x49, 0x46, 0x00, 0x01, 0x01, 0x00, 0x00, 0x48, 157 | 0x00, 0x48, 0x00, 0x00, 0xff, 0xe1, 0x00, 0x8c, 0x45, 0x78, 0x69, 0x66, 0x00, 0x00, 0x4d, 0x4d, 158 | 0x00, 0x2a, 0x00, 0x00, 0x00, 0x08, 0x00, 0x05, 0x01, 0x12, 0x00, 0x03, 0x00, 0x00, 0x00, 0x01, 159 | 0x00, 0x01, 0x00, 0x00, 0x01, 0x1a, 0x00, 0x05, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x4a, 160 | 0x01, 0x1b, 0x00, 0x05, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x52, 0x01, 0x28, 0x00, 0x03, 161 | 0x00, 0x00, 0x00, 0x01, 0x00, 0x02, 0x00, 0x00, 0x87, 0x69, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 162 | 0x00, 0x00, 0x00, 0x5a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x48, 0x00, 0x00, 0x00, 0x01, 163 | 0x00, 0x00, 0x00, 0x48, 0x00, 0x00, 0x00, 0x01, 0x00, 0x03, 0xa0, 0x01, 0x00, 0x03, 0x00, 0x00, 164 | 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0xa0, 0x02, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 165 | 0x00, 0x78, 0xa0, 0x03, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x3c, 0x00, 0x00, 166 | 0x00, 0x00, 0xff, 0xed, 0x00, 0x38, 0x50, 0x68, 0x6f, 0x74, 0x6f, 0x73, 0x68, 0x6f, 0x70, 0x20, 167 | 0x33, 0x2e, 0x30, 0x00, 0x38, 0x42, 0x49, 0x4d, 0x04, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 168 | 0x38, 0x42, 0x49, 0x4d, 0x04, 0x25, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0xd4, 0x1d, 0x8c, 0xd9, 169 | 0x8f, 0x00, 0xb2, 0x04, 0xe9, 0x80, 0x09, 0x98, 0xec, 0xf8, 0x42, 0x7e, 0xff, 0xc0, 0x00, 0x11, 170 | 0x08, 0x00, 0x3c, 0x00, 0x78, 0x03, 0x01, 0x22, 0x00, 0x02, 0x11, 0x01, 0x03, 0x11, 0x01, 0xff, 171 | 0xc4, 0x00, 0x1f, 0x00, 0x00, 0x01, 0x05, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 172 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 173 | 0xff, 0xc4, 0x00, 0xb5, 0x10, 0x00, 0x02, 0x01, 0x03, 0x03, 0x02, 0x04, 0x03, 0x05, 0x05, 0x04, 174 | 0x04, 0x00, 0x00, 0x01, 0x7d, 0x01, 0x02, 0x03, 0x00, 0x04, 0x11, 0x05, 0x12, 0x21, 0x31, 0x41, 175 | 0x06, 0x13, 0x51, 0x61, 0x07, 0x22, 0x71, 0x14, 0x32, 0x81, 0x91, 0xa1, 0x08, 0x23, 0x42, 0xb1, 176 | 0xc1, 0x15, 0x52, 0xd1, 0xf0, 0x24, 0x33, 0x62, 0x72, 0x82, 0x09, 0x0a, 0x16, 0x17, 0x18, 0x19, 177 | 0x1a, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x43, 0x44, 178 | 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x63, 0x64, 179 | 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, 0x83, 0x84, 180 | 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, 0xa2, 181 | 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 182 | 0xba, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 183 | 0xd8, 0xd9, 0xda, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea, 0xf1, 0xf2, 0xf3, 184 | 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0xff, 0xc4, 0x00, 0x1f, 0x01, 0x00, 0x03, 0x01, 0x01, 185 | 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x03, 186 | 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0xff, 0xc4, 0x00, 0xb5, 0x11, 0x00, 0x02, 0x01, 187 | 0x02, 0x04, 0x04, 0x03, 0x04, 0x07, 0x05, 0x04, 0x04, 0x00, 0x01, 0x02, 0x77, 0x00, 0x01, 0x02, 188 | 0x03, 0x11, 0x04, 0x05, 0x21, 0x31, 0x06, 0x12, 0x41, 0x51, 0x07, 0x61, 0x71, 0x13, 0x22, 0x32, 189 | 0x81, 0x08, 0x14, 0x42, 0x91, 0xa1, 0xb1, 0xc1, 0x09, 0x23, 0x33, 0x52, 0xf0, 0x15, 0x62, 0x72, 190 | 0xd1, 0x0a, 0x16, 0x24, 0x34, 0xe1, 0x25, 0xf1, 0x17, 0x18, 0x19, 0x1a, 0x26, 0x27, 0x28, 0x29, 191 | 0x2a, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x53, 192 | 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x73, 193 | 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 194 | 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 195 | 0xa9, 0xaa, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 196 | 0xc7, 0xc8, 0xc9, 0xca, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xe2, 0xe3, 0xe4, 197 | 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0xff, 198 | 0xdb, 0x00, 0x43, 0x00, 0x0a, 0x07, 0x07, 0x08, 0x07, 0x06, 0x0a, 0x08, 0x08, 0x08, 0x0b, 0x0a, 199 | 0x0a, 0x0b, 0x0e, 0x18, 0x10, 0x0e, 0x0d, 0x0d, 0x0e, 0x1d, 0x15, 0x16, 0x11, 0x18, 0x23, 0x1f, 200 | 0x25, 0x24, 0x22, 0x1f, 0x22, 0x21, 0x26, 0x2b, 0x37, 0x2f, 0x26, 0x29, 0x34, 0x29, 0x21, 0x22, 201 | 0x30, 0x41, 0x31, 0x34, 0x39, 0x3b, 0x3e, 0x3e, 0x3e, 0x25, 0x2e, 0x44, 0x49, 0x43, 0x3c, 0x48, 202 | 0x37, 0x3d, 0x3e, 0x3b, 0xff, 0xdb, 0x00, 0x43, 0x01, 0x0a, 0x0b, 0x0b, 0x0e, 0x0d, 0x0e, 0x1c, 203 | 0x10, 0x10, 0x1c, 0x3b, 0x28, 0x22, 0x28, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 204 | 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 205 | 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 206 | 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0xff, 0xdd, 0x00, 0x04, 0x00, 0x08, 0xff, 207 | 0xda, 0x00, 0x0c, 0x03, 0x01, 0x00, 0x02, 0x11, 0x03, 0x11, 0x00, 0x3f, 0x00, 0xc8, 0x17, 0xf6, 208 | 0x30, 0x3e, 0xc9, 0x6e, 0xd0, 0xbe, 0x33, 0xb4, 0x73, 0x8f, 0xca, 0x96, 0x5d, 0x63, 0x4e, 0x8c, 209 | 0x64, 0xcc, 0xac, 0x7d, 0x02, 0x93, 0xfd, 0x2b, 0x85, 0x59, 0xc8, 0xe0, 0x73, 0x57, 0xac, 0xa4, 210 | 0x62, 0x77, 0xc8, 0x88, 0xe9, 0xd9, 0x0a, 0xf1, 0x57, 0xca, 0xde, 0xc6, 0x4e, 0xa2, 0x4a, 0xec, 211 | 0xea, 0xad, 0x35, 0x28, 0x2f, 0x32, 0x61, 0x81, 0xb1, 0xea, 0xfc, 0x0a, 0xd1, 0x36, 0x17, 0xb2, 212 | 0x2a, 0x3c, 0x4b, 0x66, 0x04, 0x88, 0x5d, 0x49, 0x98, 0x12, 0x47, 0xd3, 0xd7, 0xda, 0xb3, 0xf4, 213 | 0xc5, 0xb7, 0xbb, 0x00, 0xac, 0x2c, 0x4f, 0xf2, 0x3f, 0xe7, 0xbd, 0x68, 0xde, 0x59, 0xc9, 0x2d, 214 | 0xb2, 0xef, 0x12, 0xa2, 0x28, 0x18, 0x92, 0x21, 0x82, 0x3b, 0x60, 0xf1, 0xfa, 0x57, 0x34, 0xaa, 215 | 0xb5, 0x2e, 0x56, 0x69, 0xcc, 0xad, 0x72, 0xba, 0xda, 0x6a, 0xb2, 0xdc, 0x08, 0xd2, 0xc1, 0x64, 216 | 0x04, 0x65, 0x44, 0x72, 0x28, 0x66, 0xe3, 0xb0, 0x6c, 0x7f, 0x3a, 0xb5, 0xa6, 0x68, 0xd7, 0x5a, 217 | 0xcd, 0x89, 0xbc, 0xb1, 0x85, 0xe5, 0x8c, 0x1c, 0x32, 0x87, 0x01, 0xd4, 0xfa, 0x10, 0x6a, 0xb8, 218 | 0xb7, 0xb7, 0xb2, 0xd3, 0x97, 0xed, 0x33, 0x4a, 0x58, 0x86, 0xf9, 0xe2, 0x90, 0xa3, 0x1c, 0xe7, 219 | 0xbe, 0x47, 0x22, 0xb0, 0x25, 0xd4, 0x2f, 0x34, 0xfb, 0x7b, 0x9d, 0x36, 0xcb, 0x54, 0x77, 0xb0, 220 | 0xb8, 0x20, 0xba, 0x46, 0xc0, 0x07, 0xe9, 0xd7, 0x8c, 0x83, 0xc6, 0x08, 0x07, 0x1f, 0x5a, 0xde, 221 | 0x93, 0x55, 0x2f, 0x63, 0x29, 0x54, 0xe5, 0xb1, 0x36, 0xbd, 0x74, 0xfa, 0x63, 0xf9, 0x51, 0x4e, 222 | 0xdf, 0x68, 0x56, 0xc3, 0x46, 0x40, 0x6c, 0x7a, 0xe6, 0xb1, 0xc7, 0x88, 0xb5, 0x01, 0xc6, 0x62, 223 | 0x3e, 0xe5, 0x39, 0xfe, 0x74, 0xef, 0xe0, 0xda, 0x41, 0x0a, 0x47, 0x45, 0x38, 0xa6, 0xfd, 0x8c, 224 | 0xdb, 0xc9, 0xe6, 0x27, 0xdc, 0x23, 0x0a, 0xd2, 0x27, 0xde, 0x07, 0xd3, 0x3c, 0x66, 0xb5, 0x92, 225 | 0x51, 0x5a, 0x91, 0x1a, 0x8e, 0x6c, 0x9d, 0xb5, 0xdd, 0x49, 0x06, 0x76, 0x22, 0x82, 0x3f, 0x89, 226 | 0x3f, 0xfa, 0xf4, 0xc1, 0xaf, 0x6a, 0x2c, 0x7a, 0xc7, 0xff, 0x00, 0x7e, 0xff, 0x00, 0xfa, 0xf5, 227 | 0x1b, 0xc3, 0x1c, 0x50, 0x99, 0x9e, 0x4c, 0x96, 0x3c, 0x73, 0xde, 0x9a, 0xb1, 0x3c, 0x85, 0x36, 228 | 0x26, 0xe0, 0x57, 0x76, 0x41, 0xed, 0x58, 0x7b, 0x4f, 0x23, 0x5b, 0x3b, 0xee, 0x58, 0x1a, 0xfd, 229 | 0xf8, 0x5c, 0x15, 0x87, 0x3e, 0xa5, 0x08, 0xfd, 0x33, 0x4a, 0x35, 0xeb, 0xec, 0x65, 0x84, 0x44, 230 | 0x7b, 0x29, 0x1f, 0xd6, 0xab, 0xcb, 0x6e, 0x52, 0x5d, 0x8d, 0x1e, 0x3b, 0xf5, 0xa9, 0x24, 0xd3, 231 | 0xdc, 0x44, 0xa5, 0x70, 0x73, 0xd1, 0x41, 0xe6, 0xa7, 0x9d, 0x94, 0xa3, 0x22, 0xda, 0x6b, 0xc8, 232 | 0xcb, 0xfb, 0xcb, 0x57, 0x67, 0xf5, 0x8d, 0xc6, 0x0f, 0xe7, 0x53, 0x47, 0xae, 0xdb, 0x1d, 0xbb, 233 | 0xa0, 0x90, 0x67, 0xaf, 0x19, 0x22, 0xb3, 0x52, 0xca, 0x65, 0x6f, 0x9a, 0x16, 0x5f, 0x5c, 0xd5, 234 | 0x5b, 0x89, 0xda, 0x39, 0x08, 0x56, 0x1b, 0x41, 0xc1, 0xc0, 0xaa, 0x8c, 0xdc, 0x9d, 0x90, 0x9b, 235 | 0x68, 0xe9, 0x17, 0x56, 0xd3, 0xe4, 0x70, 0x37, 0x3a, 0x93, 0xc6, 0x5e, 0x36, 0x03, 0xf3, 0xa9, 236 | 0xbe, 0xd3, 0x67, 0xff, 0x00, 0x3f, 0x30, 0xff, 0x00, 0xdf, 0x55, 0xc6, 0x49, 0x76, 0xee, 0x48, 237 | 0x05, 0x95, 0x7f, 0x5a, 0x67, 0x9a, 0x7f, 0xbc, 0xd5, 0xa5, 0xa4, 0xf7, 0x17, 0x33, 0x3f, 0xff, 238 | 0xd0, 0xf3, 0x14, 0x80, 0xc8, 0xea, 0x8b, 0xd5, 0x8e, 0x2b, 0x48, 0x46, 0x10, 0x6d, 0xe9, 0x8e, 239 | 0x2a, 0x1d, 0x3e, 0x10, 0xf3, 0x96, 0x39, 0xc2, 0x8f, 0xd6, 0xb4, 0x7c, 0xb5, 0x06, 0xbb, 0xa1, 240 | 0x1d, 0x0f, 0x3a, 0xa4, 0xb5, 0xb1, 0x77, 0xc3, 0xfa, 0xb3, 0xe9, 0x93, 0x90, 0x55, 0x5a, 0x36, 241 | 0xe7, 0x91, 0xd0, 0xf6, 0xfc, 0x2b, 0xa5, 0xbf, 0xd6, 0xe4, 0xf2, 0x2d, 0x85, 0xb4, 0x6c, 0xc9, 242 | 0xb0, 0xbb, 0x1c, 0xf0, 0x14, 0x63, 0xa9, 0xf5, 0xae, 0x3d, 0x40, 0x5e, 0x00, 0xa9, 0xe1, 0xbb, 243 | 0x96, 0xdc, 0xe5, 0x24, 0x2a, 0xbd, 0xd7, 0xa8, 0x3f, 0x85, 0x63, 0x57, 0x0b, 0x09, 0xbe, 0x66, 244 | 0x0a, 0xb4, 0x94, 0x79, 0x45, 0xd5, 0x64, 0x9e, 0xe6, 0x6f, 0x31, 0xf2, 0xf1, 0xe3, 0x0a, 0x7a, 245 | 0x8f, 0xff, 0x00, 0x5d, 0x67, 0x88, 0xf0, 0x0b, 0x11, 0x9c, 0x57, 0x63, 0x63, 0xa3, 0x58, 0x49, 246 | 0x6d, 0x0c, 0x97, 0x28, 0xc6, 0x59, 0x00, 0x91, 0x80, 0x94, 0x8c, 0x02, 0x3b, 0x1f, 0xc4, 0x71, 247 | 0x59, 0x37, 0xba, 0x0c, 0xd1, 0xb9, 0x36, 0x89, 0x2d, 0xc4, 0x78, 0xcb, 0x00, 0x9f, 0x30, 0xfc, 248 | 0x07, 0x5f, 0xc2, 0x95, 0x3a, 0xf4, 0x6f, 0xec, 0xd3, 0xb5, 0x89, 0x94, 0x24, 0xb5, 0x33, 0x74, 249 | 0xfb, 0x29, 0xb5, 0x5d, 0x42, 0x0b, 0x28, 0x40, 0x12, 0x4c, 0xdb, 0x57, 0x3d, 0x07, 0x04, 0xff, 250 | 0x00, 0x20, 0x6b, 0xb9, 0xd5, 0xfc, 0x13, 0x6f, 0x07, 0x86, 0xc3, 0x5c, 0x2c, 0x97, 0x17, 0x36, 251 | 0xc3, 0x2a, 0xc4, 0xe0, 0x2a, 0xf7, 0x00, 0x7a, 0x7b, 0xfb, 0x57, 0x0f, 0x6d, 0x2b, 0xd9, 0x5d, 252 | 0x24, 0xf0, 0x3b, 0x2c, 0xb1, 0x9c, 0xa9, 0xee, 0x0f, 0x4f, 0xeb, 0x5d, 0x2c, 0x7e, 0x34, 0xba, 253 | 0x31, 0xfd, 0x98, 0x99, 0x36, 0xba, 0x6c, 0x26, 0x46, 0x0d, 0xc9, 0x18, 0xfc, 0xa9, 0x62, 0x23, 254 | 0x37, 0x67, 0x1d, 0x8e, 0x8c, 0x34, 0xe9, 0xab, 0xf3, 0xee, 0x73, 0x8d, 0xa3, 0x08, 0x54, 0xcb, 255 | 0x1d, 0xcc, 0xbb, 0xd0, 0x9e, 0x30, 0xa7, 0xf0, 0xe9, 0x53, 0xc8, 0x82, 0x7b, 0x65, 0x9a, 0x24, 256 | 0xc0, 0x55, 0x24, 0x11, 0xc6, 0xd3, 0xfe, 0x15, 0x5d, 0xaf, 0x2e, 0x5b, 0x74, 0x52, 0x4b, 0x90, 257 | 0x09, 0x5c, 0xe3, 0x9e, 0x2b, 0x3f, 0x50, 0xb9, 0x42, 0xbf, 0x67, 0x89, 0x98, 0xfc, 0xdb, 0x9f, 258 | 0xe6, 0x3b, 0x7e, 0x98, 0xae, 0x68, 0xfb, 0xec, 0xeb, 0x6d, 0x46, 0x37, 0x45, 0xf8, 0x6e, 0x60, 259 | 0x95, 0xe3, 0x91, 0x24, 0x53, 0x21, 0x3b, 0x48, 0x64, 0xf9, 0xbf, 0x3a, 0x97, 0xcf, 0xf3, 0x24, 260 | 0x60, 0x1e, 0x36, 0x55, 0xe0, 0x11, 0x83, 0xf5, 0x19, 0xac, 0x04, 0x90, 0xc6, 0x1b, 0x1f, 0xc4, 261 | 0x30, 0x6a, 0xc5, 0x8c, 0xac, 0x85, 0xf6, 0x36, 0x1c, 0x0c, 0x80, 0x47, 0x06, 0xae, 0x54, 0x2d, 262 | 0x7b, 0x33, 0x35, 0x59, 0x96, 0x35, 0x49, 0xe2, 0xca, 0xa4, 0x45, 0x4e, 0xdc, 0x87, 0xda, 0xd9, 263 | 0x07, 0x35, 0x91, 0x22, 0xee, 0xc1, 0xda, 0x06, 0x06, 0x30, 0x05, 0x5e, 0x9a, 0x20, 0xb1, 0xe0, 264 | 0x32, 0xb6, 0xfe, 0x49, 0xcf, 0x43, 0xff, 0x00, 0xea, 0xa8, 0x5a, 0x09, 0x15, 0x41, 0x28, 0xc0, 265 | 0x1e, 0x87, 0xb5, 0x6d, 0x08, 0xa4, 0xac, 0x8c, 0xa5, 0x26, 0xdd, 0xd9, 0x57, 0x67, 0x3d, 0x29, 266 | 0x7c, 0xbf, 0x6a, 0x95, 0x93, 0x1c, 0x53, 0x76, 0x1f, 0x5a, 0xbb, 0x13, 0x73, 0xff, 0xd1, 0xe0, 267 | 0xec, 0x98, 0x24, 0x64, 0xff, 0x00, 0x78, 0xd5, 0xb1, 0x2a, 0x11, 0xc5, 0x51, 0xf9, 0x54, 0x05, 268 | 0x4c, 0xe0, 0x71, 0xcd, 0x19, 0x35, 0xde, 0x9d, 0x91, 0xe6, 0x35, 0x77, 0x72, 0xfe, 0xf5, 0x3d, 269 | 0x39, 0x35, 0x13, 0x3b, 0xe3, 0x19, 0x15, 0x02, 0xb3, 0x0e, 0x73, 0x8a, 0x43, 0x21, 0xcd, 0x3b, 270 | 0x8a, 0xc7, 0x4f, 0xa1, 0x5e, 0xbc, 0x96, 0x13, 0x23, 0x44, 0x58, 0xd9, 0xaa, 0xec, 0x90, 0x13, 271 | 0x92, 0xa4, 0x91, 0xb4, 0x8f, 0x6c, 0x75, 0xab, 0xd7, 0x1a, 0xbb, 0x69, 0x2e, 0xcd, 0x1b, 0x15, 272 | 0x99, 0x94, 0xed, 0x3f, 0xdd, 0x38, 0xac, 0x3d, 0x1a, 0xf5, 0x6c, 0x2c, 0xe6, 0x99, 0x5b, 0x74, 273 | 0x8c, 0xe3, 0x11, 0x1e, 0x8d, 0x81, 0xc1, 0x3f, 0x99, 0x15, 0x9b, 0x7d, 0x79, 0x75, 0x7b, 0x76, 274 | 0xf3, 0x4e, 0x72, 0xc7, 0xa0, 0x1d, 0x00, 0xec, 0x00, 0xaf, 0x39, 0xe1, 0x63, 0x3a, 0xce, 0x4d, 275 | 0x68, 0x75, 0x73, 0xa8, 0xc1, 0x5b, 0x71, 0xdb, 0xc9, 0x94, 0xb1, 0x62, 0xc4, 0xf2, 0x49, 0xea, 276 | 0x4d, 0x30, 0xc8, 0x77, 0xee, 0x39, 0xc0, 0xeb, 0x8a, 0x80, 0x48, 0x41, 0xe6, 0x82, 0x5a, 0xbd, 277 | 0x0b, 0x9c, 0xbc, 0xa6, 0xc6, 0xb5, 0x04, 0x56, 0xd7, 0x09, 0x32, 0xb8, 0xf2, 0xae, 0x23, 0x12, 278 | 0x21, 0x1f, 0x4c, 0x1f, 0xf3, 0xef, 0x5c, 0xf4, 0xb2, 0x46, 0xca, 0x24, 0x18, 0xde, 0xcc, 0x72, 279 | 0x3d, 0x05, 0x4b, 0x77, 0x24, 0xb2, 0xc0, 0x88, 0x4b, 0x11, 0x17, 0xdd, 0x19, 0xe8, 0x3d, 0x2a, 280 | 0x81, 0x3c, 0x57, 0x24, 0x69, 0xf2, 0x33, 0xb5, 0xd4, 0xe7, 0x5a, 0x22, 0x42, 0xdd, 0xc1, 0xa4, 281 | 0xde, 0x73, 0xfd, 0x69, 0x9f, 0xc3, 0x9a, 0x4c, 0xe4, 0x56, 0x97, 0x22, 0xc6, 0xce, 0x9f, 0x66, 282 | 0x93, 0xc7, 0xe6, 0xcf, 0x31, 0xda, 0xcb, 0x95, 0x03, 0x9f, 0xce, 0x9d, 0x79, 0x6b, 0xe4, 0xc1, 283 | 0xf2, 0x48, 0x7b, 0xb6, 0xd3, 0xd3, 0x15, 0x8d, 0xe7, 0xca, 0xaa, 0x11, 0x64, 0x60, 0x07, 0x60, 284 | 0x71, 0x52, 0x9b, 0xc9, 0xda, 0xdc, 0xc4, 0xcd, 0x95, 0xf5, 0x3d, 0x6b, 0x17, 0x19, 0xf3, 0x5e, 285 | 0xe6, 0xa9, 0xc6, 0xd6, 0xb0, 0xe2, 0xc3, 0x20, 0x92, 0x4f, 0xad, 0x2e, 0xf8, 0xfd, 0x1b, 0xf3, 286 | 0x15, 0x1c, 0x23, 0xcc, 0xe0, 0xf5, 0x3c, 0x0f, 0x6a, 0x9b, 0xec, 0xa3, 0xfe, 0x7a, 0xa5, 0x6d, 287 | 0xa9, 0x8e, 0x88, 0xff, 0xd2, 0xe0, 0xfc, 0x99, 0x4b, 0x6c, 0x58, 0x25, 0x66, 0xf4, 0x58, 0xd8, 288 | 0x9f, 0xc8, 0x0a, 0xbb, 0x6f, 0xe1, 0xed, 0x7a, 0xf5, 0x37, 0xdb, 0x68, 0xd7, 0xd2, 0x2f, 0xaf, 289 | 0x92, 0x50, 0x7f, 0xe3, 0xd8, 0xaf, 0x7a, 0x30, 0x28, 0xe8, 0xcd, 0x4d, 0x58, 0x14, 0x12, 0x72, 290 | 0xd9, 0xfa, 0xd7, 0x61, 0xc6, 0xa0, 0xba, 0x9e, 0x1e, 0x7c, 0x17, 0xe2, 0x7e, 0x33, 0xa2, 0xdd, 291 | 0x0d, 0xdd, 0x39, 0x8f, 0xff, 0x00, 0x8a, 0xa9, 0x57, 0xc0, 0x3e, 0x26, 0x78, 0x16, 0x61, 0xa7, 292 | 0x12, 0x1c, 0x67, 0x67, 0x9c, 0xa1, 0xc7, 0xb1, 0x04, 0x8c, 0x1f, 0xc4, 0xd7, 0xb6, 0x98, 0x87, 293 | 0x4c, 0x9a, 0x5f, 0x25, 0x36, 0xe3, 0x9a, 0x43, 0xe4, 0x47, 0x93, 0xe8, 0xbf, 0x0d, 0x6f, 0xee, 294 | 0xa3, 0x32, 0xea, 0x73, 0xb5, 0x8c, 0x63, 0x39, 0x85, 0x40, 0x32, 0x9c, 0x74, 0xe7, 0x25, 0x46, 295 | 0x7f, 0x1a, 0xe5, 0x6e, 0x2c, 0x6e, 0xa0, 0x94, 0x86, 0xb3, 0x9e, 0x1c, 0x7f, 0x0b, 0xc6, 0xd9, 296 | 0x1f, 0x8e, 0x39, 0xfa, 0xd7, 0xbf, 0x1b, 0x58, 0xc9, 0xe7, 0x3f, 0x9d, 0x38, 0x5b, 0x44, 0x46, 297 | 0x08, 0x27, 0xf1, 0xa7, 0xa0, 0x9c, 0x2e, 0x78, 0x3e, 0x9d, 0xe1, 0xbd, 0x6f, 0x57, 0x6c, 0x59, 298 | 0xe9, 0xd3, 0x38, 0xef, 0x23, 0xaf, 0x96, 0xa3, 0xf1, 0x6c, 0x67, 0xf0, 0xcd, 0x6d, 0x2f, 0xc2, 299 | 0xff, 0x00, 0x12, 0x30, 0xf9, 0xda, 0xc1, 0x7d, 0xbe, 0xd4, 0xc7, 0xff, 0x00, 0x64, 0xaf, 0x5f, 300 | 0x16, 0xf1, 0xe7, 0xbf, 0xe7, 0x4e, 0xf2, 0x57, 0xfb, 0xcd, 0xf9, 0xd2, 0x29, 0x45, 0x1e, 0x44, 301 | 0xbf, 0x0a, 0xb5, 0xe3, 0x28, 0x56, 0x9e, 0xc9, 0x53, 0x19, 0x2e, 0x1d, 0x8e, 0x3f, 0x0c, 0x7f, 302 | 0x5a, 0x98, 0x7c, 0x1c, 0xba, 0x78, 0x99, 0xe4, 0xd6, 0x6d, 0xa3, 0x93, 0x19, 0x08, 0xb6, 0xec, 303 | 0xc3, 0x3e, 0xe7, 0x77, 0xf2, 0xaf, 0x54, 0x30, 0x8f, 0xef, 0x37, 0xe7, 0x40, 0x81, 0x7f, 0xbc, 304 | 0xc7, 0xf1, 0xa3, 0x95, 0x31, 0xa5, 0x63, 0xc5, 0xdf, 0xe1, 0x66, 0xbb, 0x14, 0xbb, 0x41, 0x8a, 305 | 0x54, 0x1d, 0x5a, 0x3f, 0xe8, 0x09, 0x14, 0x9f, 0xf0, 0xad, 0xef, 0xd4, 0x95, 0x78, 0xee, 0xb3, 306 | 0x8e, 0x31, 0x18, 0xe2, 0xbd, 0xa7, 0xc8, 0x8f, 0xae, 0x32, 0x7d, 0xe9, 0x7c, 0x95, 0x1d, 0x09, 307 | 0x15, 0x2e, 0x28, 0x67, 0x8b, 0x47, 0xf0, 0xc7, 0x55, 0x93, 0x07, 0xcb, 0xb9, 0x0b, 0x8e, 0x7f, 308 | 0x76, 0xa3, 0xf9, 0x9a, 0xb9, 0x07, 0xc2, 0x2d, 0x52, 0x61, 0xb9, 0xaf, 0xa0, 0x81, 0x71, 0xd2, 309 | 0x65, 0xdc, 0x7f, 0x25, 0x35, 0xeb, 0x9e, 0x50, 0xcf, 0x56, 0x3f, 0x53, 0x4b, 0xe5, 0xaf, 0x4a, 310 | 0x5c, 0xa8, 0x67, 0x87, 0xea, 0xbe, 0x07, 0xd4, 0x34, 0x08, 0xbc, 0xc9, 0x64, 0xb5, 0x98, 0x64, 311 | 0x63, 0xc9, 0x91, 0x8c, 0x8d, 0xce, 0x32, 0x13, 0x6e, 0x71, 0xf8, 0xd6, 0x57, 0xd8, 0xae, 0x7f, 312 | 0xe7, 0xde, 0x6f, 0xfb, 0xf4, 0xff, 0x00, 0xe1, 0x5f, 0x42, 0xad, 0xba, 0x81, 0xc3, 0x38, 0x1e, 313 | 0x80, 0xf1, 0x47, 0x92, 0x3f, 0xbe, 0xff, 0x00, 0x9d, 0x5d, 0x91, 0x93, 0x8a, 0x67, 0xff, 0xd9, 314 | }; -------------------------------------------------------------------------------- /src/menu.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) F4HWN Armel. All rights reserved. 2 | // Licensed under the MIT license. See LICENSE file in the project root for full license information. 3 | 4 | void viewMenu(uint16_t x, uint16_t y, uint16_t w, uint16_t h) { 5 | display.fillRoundRect(x, y, w, h, 8, TFT_MENU_BACK); 6 | display.drawRoundRect(x, y, w, h, 8, TFT_MENU_BORDER); 7 | 8 | display.setTextDatum(CC_DATUM); 9 | display.setFont(&YELLOWCRE8pt7b); 10 | display.setTextPadding(w - 2); 11 | display.setTextColor(TFT_MENU_SELECT); 12 | display.drawString("SETTINGS", 160 + offsetX, 14 + y); 13 | 14 | display.setTextDatum(CC_DATUM); 15 | display.setFont(0); 16 | display.setTextPadding(w - 2); 17 | display.setTextColor(TFT_MENU_SELECT); 18 | display.drawString(String(NAME) + " V" + String(VERSION) + " by " + String(AUTHOR), 160 + offsetX, 28 + y); 19 | 20 | display.drawFastHLine(120 + offsetX, 3 + offsetY, 80, TFT_BLACK); 21 | display.drawFastHLine(x + 1, 36 + y, w - 2, TFT_MENU_SELECT); 22 | display.drawFastHLine(x + 1, (y + h) - 24, w - 2, TFT_MENU_SELECT); 23 | } 24 | 25 | void viewOption(int8_t settingsChoice, boolean settingsSelect, uint16_t x, uint16_t y, uint16_t w) { 26 | uint8_t start = 0; 27 | uint8_t i, j; 28 | 29 | display.setTextDatum(CC_DATUM); 30 | display.setFont(&tahoma8pt7b); 31 | display.setTextPadding(w - 2); 32 | display.setTextColor(TFT_MENU_SELECT, TFT_MENU_BACK); 33 | 34 | size_t stop = sizeof(settings) / sizeof(settings[0]); 35 | 36 | if(settingsChoice > 6) 37 | { 38 | start = settingsChoice - 6; 39 | } 40 | else { 41 | start = 0; 42 | } 43 | 44 | stop = start + 7; 45 | 46 | j = 0; 47 | for(i = start; i < stop; i++) { 48 | if(settingsChoice == i && settingsSelect == false) { 49 | display.setTextColor(TFT_BLACK, TFT_MENU_SELECT); 50 | display.drawString(settings[i], 160 + offsetX, 45 + y + (j * 18)); 51 | } 52 | else if(settingsChoice == i && settingsSelect == true) { 53 | display.setTextColor(TFT_BLACK, TFT_MENU_SELECT); 54 | display.drawString(settings[i], 160 + offsetX, 45 + y + (j * 18)); 55 | } 56 | else { 57 | display.setTextColor(TFT_MENU_SELECT, TFT_MENU_BACK); 58 | display.drawString(settings[i], 160 + offsetX, 45 + y + (j * 18)); 59 | } 60 | j++; 61 | if(j > 7) { 62 | j = 7; 63 | } 64 | } 65 | } -------------------------------------------------------------------------------- /src/settings.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) F4HWN Armel. All rights reserved. 2 | // Licensed under the MIT license. See LICENSE file in the project root for full license information. 3 | 4 | // Config 5 | 6 | const char *config[] = { 7 | "WIFI_SSID", "WIFI_PASSWORD", "LATITUDE", "LONGITUDE" 8 | }; 9 | 10 | 11 | /* 12 | Note that it's possible to configure many configurations, like this 13 | const char *config[] = { 14 | "WIFI_SSID_1", "WIFI_PASSWORD_1", "LATITUDE_1", "LONGITUDE_1", 15 | "WIFI_SSID_2", "WIFI_PASSWORD_2", "LATITUDE_2", "LONGITUDE_2", 16 | "WIFI_SSID_3", "WIFI_PASSWORD_3", "LATITUDE_3", "LONGITUDE_3" 17 | }; 18 | */ -------------------------------------------------------------------------------- /src/tasks.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) F4HWN Armel. All rights reserved. 2 | // Licensed under the MIT license. See LICENSE file in the project root for full license information. 3 | 4 | // Multitasking task for retreive propag data and greyline 5 | void hamdata(void *pvParameters) 6 | { 7 | for (;;) 8 | { 9 | if(alternance == 0 && reload == 0) 10 | { 11 | switch (messageCurrent) 12 | { 13 | case 0: getGreyline(); break; 14 | case 1: getHamSat(); getHamQTH(); break; 15 | case 2: getGreyline(); break; 16 | case 3: getHamQSL(); break; 17 | } 18 | reload = 1; 19 | } 20 | vTaskDelay(pdMS_TO_TICKS(20)); 21 | } 22 | } 23 | 24 | // Get Button 25 | void button(void *pvParameters) 26 | { 27 | int8_t change; 28 | 29 | int8_t mapsOld = 0; 30 | int8_t watchOld = 0; 31 | int8_t beepOld = 0; 32 | int8_t screensaverOld = 0; 33 | int8_t gmtOld = 0; 34 | int8_t daylightOld = 0; 35 | uint8_t brightnessOld = 64; 36 | 37 | static int8_t settingsChoice = 0; 38 | static boolean settingsSelect = false; 39 | 40 | uint16_t x = 44 + offsetX; 41 | uint16_t y = 4 + offsetY; 42 | uint16_t w = display.width() - (x * 2); 43 | uint16_t h = 185; 44 | 45 | for (;;) 46 | { 47 | // Get button 48 | getButton(); 49 | 50 | if(btnA || btnB || btnC) { 51 | if(screensaverMode == 1) 52 | { 53 | Serial.println("Wake up"); 54 | screensaverTimer = millis() - (screensaver * 60 * 1000); 55 | btnA = 0; 56 | btnB = 0; 57 | btnC = 0; 58 | } 59 | else { 60 | if (M5.Speaker.isEnabled() && beep > 0) 61 | { 62 | // set master volume (0~255) 63 | M5.Speaker.setVolume(beep); 64 | if(btnA || btnC) 65 | { 66 | // play beep sound 1000Hz 100msec (background task) 67 | M5.Speaker.tone(1000, 50); 68 | } 69 | else if(btnB) 70 | { 71 | // play beep sound 2000Hz 100msec (background task) 72 | M5.Speaker.tone(2000, 50); 73 | } 74 | // wait done 75 | while (M5.Speaker.isPlaying()) { vTaskDelay(1); } 76 | } 77 | 78 | screensaverTimer = millis(); 79 | mapsOld = preferences.getUInt("maps"); 80 | gmtOld = preferences.getInt("gmt"); 81 | daylightOld = preferences.getUInt("daylight"); 82 | watchOld = preferences.getUInt("watch"); 83 | brightnessOld = preferences.getUInt("brightness"); 84 | screensaverOld = preferences.getUInt("screensaver"); 85 | } 86 | } 87 | 88 | if(settingsMode == false) 89 | { 90 | if(btnA || btnC) 91 | { 92 | change = alternance; 93 | if(btnA) 94 | { 95 | change = (change++ < 11) ? change : 0; 96 | } 97 | else if(btnC) 98 | { 99 | messageCurrent = (messageCurrent++ < 3) ? messageCurrent : 0; 100 | posA = 80; 101 | posB = 80; 102 | } 103 | 104 | if (change != alternance) 105 | { 106 | alternance = change; 107 | } 108 | } 109 | // Enter settings 110 | if(btnB) { 111 | settingsMode = true; 112 | while(settingLock == true) { 113 | vTaskDelay(10); 114 | } 115 | viewMenu(x, y, w, h); 116 | viewOption(settingsChoice, settingsSelect, x, y, w); 117 | vTaskDelay(300); 118 | } 119 | vTaskDelay(100); 120 | } 121 | // Select settings 122 | else if(settingsMode == true) 123 | { 124 | if(settingsSelect == false) { 125 | if(btnA || btnC) { 126 | if(btnA) { 127 | settingsChoice--; 128 | } 129 | else if(btnC) { 130 | settingsChoice++; 131 | } 132 | 133 | size_t stop = sizeof(settings) / sizeof(settings[0]); 134 | stop--; 135 | 136 | settingsChoice = (settingsChoice < 0) ? stop : settingsChoice; 137 | settingsChoice = (settingsChoice > stop) ? 0 : settingsChoice; 138 | 139 | viewOption(settingsChoice, settingsSelect, x, y, w); 140 | } 141 | else if(btnB) { 142 | settingsSelect = true; 143 | viewOption(settingsChoice, settingsSelect, x, y, w); 144 | 145 | String settingsString = String(settings[settingsChoice]); 146 | if(settingsString == "Shutdown") { 147 | shutdown(); 148 | } 149 | else if(settingsString == "Exit") { 150 | screenRefresh = 2; 151 | clear(); 152 | settingsSelect = false; 153 | settingsMode = false; 154 | vTaskDelay(pdMS_TO_TICKS(150)); 155 | } 156 | } 157 | vTaskDelay(pdMS_TO_TICKS(150)); 158 | } 159 | // Manage settings 160 | else if(settingsSelect == true) { 161 | String settingsString = String(settings[settingsChoice]); 162 | 163 | display.setTextDatum(CC_DATUM); 164 | display.setFont(&YELLOWCRE8pt7b); 165 | display.setTextPadding(w - 2); 166 | display.setTextColor(TFT_MENU_SELECT, TFT_MENU_BACK); 167 | 168 | // Maps 169 | if(settingsString == "Maps") 170 | { 171 | display.drawString(String(choiceMaps[maps]), 160 + offsetX, h - 6 + offsetY); 172 | 173 | if(btnA || btnC) { 174 | if(btnA == 1) { 175 | maps -= 1; 176 | if(maps < 0) { 177 | maps = 2; 178 | } 179 | } 180 | else if(btnC == 1) { 181 | maps += 1; 182 | if(maps > 2) { 183 | maps = 0; 184 | } 185 | } 186 | } 187 | else if(btnB == 1) { 188 | if(mapsOld != maps) 189 | preferences.putUInt("maps", maps); 190 | screenRefresh = 2; 191 | settingsSelect = false; 192 | settingsMode = false; 193 | vTaskDelay(pdMS_TO_TICKS(150)); 194 | } 195 | vTaskDelay(pdMS_TO_TICKS(150)); 196 | } 197 | 198 | // GMT Offset 199 | else if(settingsString == "GMT Offset") 200 | { 201 | if(gmt > 0) 202 | { 203 | display.drawString(String(choiceGMT[0]) + " +" + String(gmt), 160 + offsetX, h - 6 + offsetY); 204 | } 205 | else 206 | { 207 | display.drawString(String(choiceGMT[0]) + " " + String(gmt), 160 + offsetX, h - 6 + offsetY); 208 | } 209 | 210 | if(btnA || btnC) { 211 | if(btnA == 1) { 212 | gmt -= 1; 213 | if(gmt < -14) { 214 | gmt = -14; 215 | } 216 | } 217 | else if(btnC == 1) { 218 | gmt += 1; 219 | if(gmt > 14) { 220 | gmt = 14; 221 | } 222 | } 223 | } 224 | else if(btnB == 1) { 225 | if(gmtOld != gmt) 226 | { 227 | configTime(gmt * 60 * 60, daylight * 60 * 60, ntpServer); 228 | preferences.putInt("gmt", gmt); 229 | } 230 | screenRefresh = 2; 231 | settingsSelect = false; 232 | settingsMode = false; 233 | vTaskDelay(pdMS_TO_TICKS(150)); 234 | } 235 | vTaskDelay(pdMS_TO_TICKS(25)); 236 | } 237 | 238 | // Daylight Offset 239 | else if(settingsString == "Daylight Offset") 240 | { 241 | display.drawString(String(choiceDaylight[0]) + " " + String(daylight), 160 + offsetX, h - 6 + offsetY); 242 | 243 | if(btnA || btnC) { 244 | if(btnA == 1) { 245 | daylight -= 1; 246 | if(daylight < 0) { 247 | daylight = 0; 248 | } 249 | } 250 | else if(btnC == 1) { 251 | daylight += 1; 252 | if(daylight > 1) { 253 | daylight = 1; 254 | } 255 | } 256 | } 257 | else if(btnB == 1) { 258 | if(daylightOld != daylight) 259 | { 260 | configTime(gmt * 60 * 60, daylight * 60 * 60, ntpServer); 261 | preferences.putUInt("daylight", daylight); 262 | } 263 | screenRefresh = 2; 264 | settingsSelect = false; 265 | settingsMode = false; 266 | vTaskDelay(pdMS_TO_TICKS(150)); 267 | } 268 | vTaskDelay(pdMS_TO_TICKS(25)); 269 | } 270 | 271 | // watch 272 | if(settingsString == "Clock") 273 | { 274 | display.drawString(String(choiceClock[watch]), 160 + offsetX, h - 6 + offsetY); 275 | 276 | if(btnA || btnC) { 277 | if(btnA == 1) { 278 | watch -= 1; 279 | if(watch < 0) { 280 | watch = 1; 281 | } 282 | } 283 | else if(btnC == 1) { 284 | watch += 1; 285 | if(watch > 1) { 286 | watch = 0; 287 | } 288 | } 289 | } 290 | else if(btnB == 1) { 291 | if(watchOld != watch) 292 | preferences.putUInt("watch", watch); 293 | screenRefresh = 2; 294 | settingsSelect = false; 295 | settingsMode = false; 296 | vTaskDelay(pdMS_TO_TICKS(150)); 297 | } 298 | vTaskDelay(pdMS_TO_TICKS(150)); 299 | } 300 | 301 | // Brightness 302 | if(settingsString == "Brightness") 303 | { 304 | display.drawString(String(choiceBrightness[0]) + " " + String(brightness) + "%", 160 + offsetX, h - 6 + offsetY); 305 | 306 | if(btnA || btnC) { 307 | if(btnA == 1) { 308 | brightness -= 1; 309 | if(brightness < 1) { 310 | brightness = 1; 311 | } 312 | } 313 | else if(btnC == 1) { 314 | brightness += 1; 315 | if(brightness > 100) { 316 | brightness = 100; 317 | } 318 | } 319 | } 320 | else if(btnB == 1) { 321 | if(brightnessOld != brightness) 322 | preferences.putUInt("brightness", brightness); 323 | screenRefresh = 2; 324 | settingsSelect = false; 325 | settingsMode = false; 326 | vTaskDelay(pdMS_TO_TICKS(150)); 327 | } 328 | display.setBrightness(map(brightness, 1, 100, 1, 254)); 329 | vTaskDelay(pdMS_TO_TICKS(25)); 330 | } 331 | // Beep 332 | else if(settingsString == "Beep") 333 | { 334 | display.drawString(String(choiceBeep[0]) + " " + String(beep) + "%", 160 + offsetX, h - 6 + offsetY); 335 | 336 | if(btnA || btnC) { 337 | if(btnA == 1) { 338 | beep -= 1; 339 | if(beep < 0) { 340 | beep = 0; 341 | } 342 | } 343 | else if(btnC == 1) { 344 | beep += 1; 345 | if(beep > 100) { 346 | beep = 100; 347 | } 348 | } 349 | } 350 | else if(btnB == 1) { 351 | if(beepOld != beep) 352 | preferences.putUInt("beep", beep); 353 | screenRefresh = 2; 354 | settingsSelect = false; 355 | settingsMode = false; 356 | vTaskDelay(pdMS_TO_TICKS(150)); 357 | } 358 | vTaskDelay(pdMS_TO_TICKS(25)); 359 | } 360 | 361 | // Screensaver 362 | else if(settingsString == "Screensaver") 363 | { 364 | display.drawString(String(choiceScreensaver[0]) + " " + String(screensaver) + " MIN", 160 + offsetX, h - 6 + offsetY); 365 | 366 | if(btnA || btnC) { 367 | if(btnA == 1) { 368 | screensaver -= 1; 369 | if(screensaver < 1) { 370 | screensaver = 1; 371 | } 372 | } 373 | else if(btnC == 1) { 374 | screensaver += 1; 375 | if(screensaver > 60) { 376 | screensaver = 60; 377 | } 378 | } 379 | } 380 | else if(btnB == 1) { 381 | if(screensaverOld != screensaver) 382 | preferences.putUInt("screensaver", screensaver); 383 | screenRefresh = 2; 384 | settingsSelect = false; 385 | settingsMode = false; 386 | vTaskDelay(pdMS_TO_TICKS(150)); 387 | } 388 | vTaskDelay(pdMS_TO_TICKS(25)); 389 | } 390 | 391 | // IP Address 392 | else if(settingsString == "IP Address") 393 | { 394 | display.drawString(String(WiFi.localIP().toString().c_str()), 160 + offsetX, h - 6 + offsetY); 395 | 396 | if(btnB == 1) { 397 | screenRefresh = 2; 398 | settingsSelect = false; 399 | settingsMode = false; 400 | vTaskDelay(pdMS_TO_TICKS(150)); 401 | } 402 | vTaskDelay(pdMS_TO_TICKS(150)); 403 | } 404 | } 405 | } 406 | vTaskDelay(pdMS_TO_TICKS(10)); 407 | } 408 | } -------------------------------------------------------------------------------- /src/tools.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) F4HWN Armel. All rights reserved. 2 | // Licensed under the MIT license. See LICENSE file in the project root for full license information. 3 | 4 | int btnA, btnB, btnC; 5 | 6 | // Get Battery level 7 | int8_t getBatteryLevel(bool type) 8 | { 9 | return M5.Power.getBatteryLevel(); 10 | } 11 | 12 | // Control if charging 13 | bool isCharging() 14 | { 15 | return M5.Power.isCharging(); 16 | } 17 | 18 | // Get button 19 | void getButton() 20 | { 21 | if (M5.getBoard() == m5::board_t::board_M5ATOM || M5.getBoard() == m5::board_t::board_M5AtomPsram || M5.getBoard() == m5::board_t::board_M5AtomU) 22 | { 23 | M5.update(); 24 | btnB = M5.BtnA.isPressed(); 25 | btnA = 0; 26 | btnC = 0; 27 | if(digitalRead(32) == 0) btnA = 1; 28 | else if(digitalRead(26) == 0) btnC = 1; 29 | } 30 | else 31 | { 32 | M5.update(); 33 | 34 | btnA = M5.BtnA.isPressed(); 35 | btnB = M5.BtnB.isPressed(); 36 | btnC = M5.BtnC.isPressed(); 37 | } 38 | 39 | //Serial.printf("%d %d %d\n", btnA, btnB, btnC); 40 | } 41 | 42 | // Shutdown 43 | void shutdown() 44 | { 45 | M5.Power.powerOff(); 46 | } -------------------------------------------------------------------------------- /src/webIndex.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) F4HWN Armel. All rights reserved. 2 | // Licensed under the MIT license. See LICENSE file in the project root for full license information. 3 | 4 | #include // PROGMEM support header 5 | 6 | const char index_m5stack_html[] = R"HTML( 7 | 8 | 9 | 10 | 11 | 12 | DXTracker Screen Capture 13 | 60 | 61 | 62 |
63 | 64 | 65 | 70 | 71 | 72 | 79 | 80 |
66 | 67 | Screen 68 | 69 |
73 |
    74 |
  • 75 |
  • 76 |
  • 77 |
78 |
81 |
82 | 83 | 84 | 85 | )HTML"; 86 | 87 | const char index_core2_html[] = R"HTML( 88 | 89 | 90 | 91 | 92 | 93 | DXTracker Screen Capture 94 | 140 | 141 | 142 |
143 | 144 | 145 | 150 | 151 | 152 | 159 | 160 |
146 | 147 | Screen 148 | 149 |
153 |
    154 |
  • 155 |
  • 156 |
  • 157 |
158 |
161 |
162 | 163 | 164 | 165 | )HTML"; -------------------------------------------------------------------------------- /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 | --------------------------------------------------------------------------------