1
0
mirror of https://github.com/thooge/esp32-nmea2000-obp60.git synced 2025-12-16 15:33:05 +01:00

308 Commits

Author SHA1 Message Date
Norbert Walter
470c0e5f4d Merge pull request #208 from thooge/fonts
Added small 8x8px font mainly for use with graphs
2025-10-06 18:25:44 +02:00
Norbert Walter
9a792b49db Merge pull request #206 from TobiasE-github/master
disabe mode x in PageWind
2025-10-06 18:23:56 +02:00
8f851a4b61 Added small 8x8px font mainly for use with graphs
Page skyview improved with the new font as example usage
2025-10-06 13:19:42 +02:00
Norbert Walter
f46a43d7fd Merge pull request #207 from thooge/configfix
Config file fixes and  generation script update
2025-10-06 10:44:58 +02:00
84e99365f7 Config file fixes and generation script update 2025-09-29 14:31:28 +02:00
TobiasE-github
e5950f95fd disabe mode x in PageWind 2025-09-27 20:30:00 +02:00
Norbert Walter
d0076f336d Merge pull request #205 from TobiasE-github/master
use a smaller font on long names in WindRoseFlex
2025-09-25 23:59:28 +02:00
Tobias E
d94c4bbbdb optimize font size 2025-09-20 11:16:17 +00:00
TobiasE-github
6ef7681a40 use a smaller font on long names in WindRoseFlex 2025-09-14 21:00:02 +02:00
norbert-walter
34a289048f Fix HDOP and more delay for page refresh after new page 2025-09-10 18:48:24 +02:00
Norbert Walter
df1bd498ae Merge pull request #204 from Scorgan01/PSRAM
Data History Buffer: Moved buffers to PSRAM; extended wind buffer sizes to 1920 values
2025-09-10 18:17:00 +02:00
norbert-walter
e5eee37b59 Code cleaning 2025-08-29 17:02:25 +02:00
norbert-walter
28b3cfba0b Change design PageSkyView 2025-08-29 16:58:15 +02:00
norbert-walter
674a78b03c Fix PageSkyView 2025-08-29 12:17:03 +02:00
Scorgan01
de448974d9 Delete serial_output.txt 2025-08-27 23:21:21 +02:00
Scorgan01
6b91400cfc Merge branch 'norbert-walter:master' into PSRAM 2025-08-27 23:17:18 +02:00
Norbert Walter
be946440d3 Merge pull request #203 from Scorgan01/PageWindPlot
History Buffer + Wind Calculation CleanUp
2025-08-27 23:15:15 +02:00
Norbert Walter
ac86bfb304 Merge pull request #202 from TobiasE-github/master
fix error in label position
2025-08-27 23:14:37 +02:00
Norbert Walter
d719c7260e Merge pull request #201 from thooge/skyview
Created new page SkyView. Additionally some graphics improvements.
2025-08-27 23:14:04 +02:00
Ulrich Meine
1abcb158ec Moved history buffers to PSRAM; extended buffer to 1920 values each (32 min.) 2025-08-26 23:21:36 +02:00
Tobias E
00ea413411 fix error in label position 2025-08-23 18:55:33 +00:00
Ulrich Meine
851149bae6 Convert invalid marker of ringbuffer to MAX_VAL -> required for unsigned types 2025-08-23 13:43:02 +02:00
Ulrich Meine
c6c2ad537a Merge remote-tracking branch 'upstream/master' into PageWindPlot 2025-08-23 11:58:14 +02:00
3eb2c8093e Created new page SkyView. Additionally some graphics improvements. 2025-08-23 09:53:26 +02:00
Ulrich Meine
636b1596f5 Code cleanup: moved buffer + wind calc to OBPDataOperations; <BoatValueList> header to obp60task.h; tws 3 decimals 2025-08-23 01:41:39 +02:00
Norbert Walter
a21ce00260 Merge pull request #200 from thooge/voltage
Improve and speedup undervoltage detection code
2025-08-22 10:26:00 +02:00
norbert-walter
794cbf1c4f New links for docu, rename new tab 2025-08-22 10:23:53 +02:00
4f6079f418 Improve and speedup undervoltage detection code 2025-08-22 10:14:38 +02:00
norbert-walter
a8f3fbb34d Fix for XTE page 2025-08-18 10:22:09 +02:00
Norbert Walter
748867682c Merge pull request #199 from Scorgan01/PageWindPlot
PageWindPlot: add simulation data and AWD data option; COG validity check for true wind calculation
2025-08-18 00:29:58 +02:00
Norbert Walter
5b5e003836 Merge pull request #198 from TobiasE-github/WindRoseFlex
button in WindRoseFlex to switch true/apparent + 4 user-defined values
2025-08-18 00:29:26 +02:00
Ulrich Meine
07200ad701 Merge branch 'PageWindPlot' of https://github.com/Scorgan01/esp32-nmea2000-obp60 into PageWindPlot 2025-08-17 23:50:24 +02:00
Ulrich Meine
371816f946 PageWindPlot: add simulation data, switch TWD/AWD; diff. setup for OBP40; delete showTWS option 2025-08-17 23:50:19 +02:00
Scorgan01
c8a7f14773 Merge branch 'norbert-walter:master' into PageWindPlot 2025-08-17 23:47:38 +02:00
TobiasE-github
4a97768d0b button in WindRoseFlex to switch true/apparent + 4 user-defined values 2025-08-17 16:34:52 +02:00
Norbert Walter
e19bd0898d Merge pull request #197 from TobiasE-github/Webinterface
New tab in webinterface with a screenshot button
2025-08-16 19:16:00 +02:00
Norbert Walter
d130f7ff78 Merge pull request #196 from thooge/master
Enhancements: leavePage() and displayNew() for system page
2025-08-16 19:14:54 +02:00
Tobias Edler
7c14577bbc Typo 2025-08-16 18:47:45 +02:00
Tobias Edler
ba94fddb80 Add a page to the web interface with a screenshot button 2025-08-16 18:44:55 +02:00
Ulrich Meine
8faead0a1a add simulation data for TWD, TWS history data 2025-08-16 16:49:17 +02:00
bc9d139d19 Enhancement for future use: displayNew() for system page and generic leavePage() method 2025-08-15 09:07:49 +02:00
Norbert Walter
a74ce9e553 Merge pull request #195 from thooge/sdcard
Fixed and finished SD card code. Added uptime feature to system page.
2025-08-14 14:59:33 +02:00
779f557d47 Fixed and finished SD card code. Added uptime feature to system page. 2025-08-14 10:19:15 +02:00
norbert-walter
4a273d2c93 Add hibernate in full page refresh 2025-08-12 15:37:22 +02:00
Norbert Walter
9be1b864f4 Merge pull request #192 from thooge/scripts
Automate gen_set.py with page detection and command line parameters
2025-08-12 15:32:51 +02:00
Norbert Walter
bfc4337417 Merge pull request #191 from thooge/precision
Added config option for display precision and formatter code improvement
2025-08-12 15:31:28 +02:00
Ulrich Meine
398b8e0d02 another wndCenter fix; TWD calc with HDM and no VAR; COG valid check; dflt range 60° 2025-08-11 20:49:39 +02:00
28a7e58e27 Automate gen_set.py with page detection and command line parameters 2025-08-01 11:01:23 +02:00
eb51092b23 Added config option for display precision and formatter code improvements 2025-07-31 12:31:57 +02:00
norbert-walter
fb3af0bf83 Fix for page RollPitch 2025-07-31 11:58:04 +02:00
Norbert Walter
539500e088 Merge pull request #190 from thooge/master
Fix typo: Formated -> Formatted
2025-07-31 11:52:13 +02:00
Norbert Walter
229106b04c Merge pull request #189 from thooge/cleanup
Minor code cleanup: fixing comments and formatting
2025-07-31 11:51:54 +02:00
Norbert Walter
eefa59a7c2 Merge pull request #188 from Scorgan01/PageWindPlot
Page WindPlot: small fixes for TWS name display and data availabity checks for true wind calculation
2025-07-31 11:51:36 +02:00
588008e370 Fix typo: Formated -> Formatted 2025-07-29 19:33:31 +02:00
caf5572459 Minor code cleanup: fixing comments and formatting 2025-07-28 09:54:20 +02:00
Ulrich Meine
05f8b3ec65 fix TWS name not displayed; improve check for chart center adjustment; debug code changes 2025-07-27 20:54:35 +02:00
Ulrich Meine
351ef5d9fe fix true wind input check; fix TWS not calculated with SOG only or w/o COG 2025-07-27 20:51:11 +02:00
Norbert Walter
e93193c3e0 Merge pull request #187 from thooge/master
Change xbm file header to fix strange accesspoint behaviour
2025-07-26 21:21:46 +02:00
e367d15568 Change xbm file header to fix strange accesspoint behaviour 2025-07-26 20:22:07 +02:00
Norbert Walter
2773685db3 Merge pull request #186 from Scorgan01/PageWindPlot
Page WindPlot v1, history buffer and true wind calculation
2025-07-26 18:36:58 +02:00
Norbert Walter
9953165dfe Merge pull request #185 from thooge/master
Fix OBP40-Logo for WhitePage, use xbm image format
2025-07-26 18:35:25 +02:00
Ulrich Meine
da451bee70 removed test comment from wind function call 2025-07-26 09:27:35 +02:00
Ulrich Meine
f79124eed3 Revert speed change in platformio.ini 2025-07-25 19:54:01 +02:00
Ulrich Meine
33b5776421 Merge branch 'PageWindPlot' of https://github.com/Scorgan01/esp32-nmea2000-obp60 into PageWindPlot 2025-07-25 19:50:47 +02:00
Ulrich Meine
2954a9a58b adjust page call to new standard; clean debug code; fix TWS print alignment 2025-07-25 19:50:42 +02:00
Scorgan01
e6add8e6fc Merge branch 'norbert-walter:master' into PageWindPlot 2025-07-25 19:44:02 +02:00
norbert-walter
15bcd53350 Add display infos in platformio.ini 2025-07-25 14:29:54 +02:00
Ulrich Meine
938b566bfc Merge branch 'PageWindPlot' of https://github.com/Scorgan01/esp32-nmea2000-obp60 into PageWindPlot 2025-07-25 08:43:01 +02:00
Ulrich Meine
fe2223839f added calibration to buffer; separated buffer and wind code in opb60task; prepared simulation; getMin/Max fix for ringbuffer for invalid data; fix for chart center; cleanup code 2025-07-25 08:42:43 +02:00
Scorgan01
c888804aef Merge branch 'norbert-walter:master' into PageWindPlot 2025-07-25 08:19:24 +02:00
d963153b35 Fix OBP40-Logo for WhitePage, use xbm image format 2025-07-24 19:51:38 +02:00
Norbert Walter
4934fb1346 Merge pull request #184 from thooge/master
Fix missing config data for OBP40
2025-07-24 16:22:34 +02:00
norbert-walter
8150947cd6 Fix contrast lost for GDEY042T81 display 2025-07-24 14:28:56 +02:00
1e7368db4d Additional bug fix in JSON-configuration 2025-07-24 10:35:01 +02:00
238e0fc79d Fix missing config data for OBP40 2025-07-24 09:50:16 +02:00
Norbert Walter
34801d1e0e Merge pull request #183 from thooge/alarm
Preparation for upcoming alarm functionality
2025-07-23 18:50:30 +02:00
0afe629b38 Preparation for upcoming alarm functionality 2025-07-23 14:00:06 +02:00
norbert-walter
ccc0d2b6c1 Use fix lib for FRAM 2025-07-22 22:50:53 +02:00
norbert-walter
791fa10b01 Use fix lib for FRAM 2025-07-21 10:25:50 +02:00
Ulrich Meine
c48c6a2e48 Move buffer handling to obp60task; reset OBPSensorTask; add true wind calculation 2025-07-19 00:26:37 +02:00
Norbert Walter
4110e3f812 Merge pull request #182 from thooge/system
Add display library version info to page system
2025-07-15 13:08:21 +02:00
Ulrich Meine
bb99978177 no buffer writes for invalid data; fix ringbuffer index 2025-07-14 21:17:17 +02:00
norbert-walter
85dee2a622 Fix page refresh if using keypad 2025-07-14 14:27:33 +02:00
norbert-walter
f2e069b768 Merge remote-tracking branch 'origin/master' 2025-07-14 14:14:56 +02:00
norbert-walter
71946248e2 Fix voltage sensor for OBP40 2025-07-14 14:14:50 +02:00
Ulrich Meine
91a3ac081f Merge branch 'PageWindPlot' of https://github.com/Scorgan01/esp32-nmea2000-obp60 into PageWindPlot 2025-07-13 00:26:24 +02:00
Ulrich Meine
59cf52b5d2 Semaphore + chart fixes; added simulation data 2025-07-13 00:26:16 +02:00
Scorgan01
60193fa3be Merge branch 'norbert-walter:master' into PageWindPlot 2025-07-12 01:49:20 +02:00
norbert-walter
7f954e702d Add debugging info 2025-07-11 18:37:12 +02:00
7ca0a4d09a Fix variable name in extra_script.py.new 2025-07-09 16:23:11 +02:00
13abfe4c14 Merge branch 'master' into system 2025-07-09 16:15:30 +02:00
8e34fa936d Add display library version info to page system 2025-07-09 16:13:32 +02:00
Norbert Walter
6f4e9b625d Merge pull request #181 from thooge/fonts
Reorganized fonts and changed Ubuntu-font to 8bit version
2025-07-08 21:43:52 +02:00
4aefc99212 Add home location to system page 2025-07-08 19:41:18 +02:00
fa4b563ff0 Reorganized fonts and changed Ubuntu-font to 8bit version 2025-07-08 15:19:07 +02:00
Norbert Walter
ae7130b86c Merge pull request #180 from TobiasE-github/master
Fix error message in  lib/obp60task/OBPSensorTask.cpp
2025-07-07 16:15:04 +02:00
TobiasE-github
7f9beb064a Merge pull request #69 from norbert-walter/master
Delete obp60task.cpp in root
2025-07-07 15:29:00 +02:00
norbert-walter
2095efbb01 Delete obp60task.cpp in root 2025-07-07 15:04:02 +02:00
TobiasE-github
d0e2fc1eac Fix error message in lib/obp60task/OBPSensorTask.cpp 2025-07-06 15:41:18 +02:00
norbert-walter
d1c620a858 Fix for compass page 2025-07-04 14:11:22 +02:00
norbert-walter
07a1b2388e Fix for voltage sensor OBP40 2025-07-03 22:41:12 +02:00
norbert-walter
c614cae472 Change to actual extra_script.py 2025-07-03 19:41:27 +02:00
Norbert Walter
ec7e1a35e7 Merge branch 'wellenvogel:master' into master 2025-07-03 19:39:16 +02:00
norbert-walter
b82e94d716 Change back to old extra_script.py for sync with wellenvogel 2025-07-03 19:36:47 +02:00
Norbert Walter
a2c1c1042d Merge pull request #177 from thooge/obp40
Apply new OBP60 settings also to the OBP40
2025-07-03 16:42:18 +02:00
Norbert Walter
77872fea4a Merge pull request #175 from ManfredRad/master
Page Compass
2025-07-03 16:38:01 +02:00
Norbert Walter
8a069b9445 Merge branch 'master' into master 2025-07-03 16:37:39 +02:00
Norbert Walter
b3a9b9990f Merge pull request #174 from Scorgan01/PageSixValues
PageSixValues: Drop top and bottom line to adjust to standard page design
2025-07-03 16:34:00 +02:00
Norbert Walter
dc8b9e48d7 Merge pull request #173 from Scorgan01/CalibrationDataV2
Improve and extend data calibration and smoothing
2025-07-03 16:33:27 +02:00
Ulrich Meine
72ddeb3cfb Pointer correction -> no data copy; conc. access issues 2025-07-01 01:27:41 +02:00
Ulrich Meine
2729ef9cb6 Implement v1 history data storage at OBPSensorTask 2025-06-25 23:14:09 +02:00
Ulrich Meine
1f90cefbd6 Implement OBPRingBuffer class and adjust PageWindPlot accordingly 2025-06-24 00:05:15 +02:00
Ulrich Meine
9ada5be7cb Handling of missing data 2025-06-22 14:29:03 +02:00
Ulrich Meine
03d8339170 fix interval, border cross, TWS flip, range; add config_obp40; adjust axis legend 2025-06-22 00:11:54 +02:00
Ulrich Meine
73656e7d14 Buffer and interval stuff 2025-06-18 23:40:57 +02:00
c5a1244519 Some config.json improvements 2025-06-15 10:19:59 +02:00
99c3470a22 Some work on system page and SD-card integration 2025-06-14 10:09:48 +02:00
Ulrich Meine
bd9741d851 buffer extension; still some errors 2025-06-14 02:19:52 +02:00
Ulrich Meine
13c85adad2 completed config.json; modified TWS flipping; almost fully fixed chart rng overflow 2025-06-13 17:43:23 +02:00
f5cf292804 Typo in platformio.ini fixed 2025-06-13 10:32:18 +02:00
77c05de4fc More work on system page 2025-06-13 09:21:41 +02:00
Ulrich Meine
9b504469bc Fixes for TWS flip, scale calculation, chart range overflow; add axis lines 2025-06-12 23:41:15 +02:00
1bac5d8b16 System page improvements 2025-06-12 15:23:21 +02:00
c7eafbf9b8 Apply new OBP60 settings also to the OBP40 2025-06-12 11:43:21 +02:00
Ulrich Meine
f0aba89301 Simulation data; ext. chart area; flexible TWS position 2025-06-09 22:32:34 +02:00
Ulrich Meine
fe095a9716 Y Axis label; some interval bug fixing 2025-06-09 17:58:57 +02:00
Ulrich Meine
235188dfb2 Added update interval + no sens data msg; corrected rounding 2025-06-09 13:35:54 +02:00
Ulrich Meine
aa70c34a96 Fix getMin/Max + wndCenter rounding; 2025-06-08 13:45:20 +02:00
Ulrich Meine
62aef176d3 Chart + plotshift working 2025-06-06 23:06:04 +02:00
Scorgan01
9f79a7d4bc Switch to TWD 2025-06-06 10:17:11 +02:00
Ulrich Meine
bf4dff45b4 Compact config reading code 2025-06-05 23:42:36 +02:00
Ulrich Meine
f153d82825 PlotShift 2025-06-05 22:51:25 +02:00
Manfred Radmacher
4b5aa7ff4b Pagecompass update
obp60rtask.cpp updated to handle pagecompass
2025-06-05 16:05:33 +02:00
Manfred Radmacher
ebf6e62389 PageCompass
A new page to show heading data or course over ground in a layout inspoired by an analog compass. The compass signal shown can be altered by the left key. In addition a second signal can be shown as well from the following choice: HDM, HDT, COG, STW, SOG, DBS. This signal can also be changed by the second key.
2025-06-05 15:57:06 +02:00
Ulrich Meine
da06f3e791 Automatic scale adjustment + plot shift 2025-06-05 01:04:07 +02:00
Ulrich Meine
7d66ec91da Principle working; several bugs incl. 2025-06-03 22:52:06 +02:00
Ulrich Meine
29706df6cd Drop top and bottom line to adjust to standard page design 2025-05-25 20:18:09 +02:00
Ulrich Meine
d6e3c7ad48 Calibration for data types COG, SOG added 2025-05-25 17:42:50 +02:00
Ulrich Meine
02712263d3 Changed array for calibration data into a hash map; reduced unnecessary method parameters 2025-05-25 13:53:50 +02:00
Ulrich Meine
0c4fce0e25 No smoothing when boatDataValue is invalid 2025-05-24 00:40:10 +02:00
Ulrich Meine
4e6d52d197 added "condition" to calibration settings in config.json 2025-05-22 23:28:33 +02:00
Ulrich Meine
f9cf73ae04 adjust code position of calibration method call, where not applied yet 2025-05-22 22:45:20 +02:00
Ulrich Meine
bf59cfbae8 tiny code adjustments 2025-05-22 21:48:32 +02:00
Ulrich Meine
214c10ff93 removed static definition of methods 2025-05-21 21:47:21 +02:00
Norbert Walter
e72ece8452 Merge pull request #172 from Scorgan01/Smoothing
Smoothing of data value; extension of calibration of data types (PR #171)
2025-05-18 21:51:02 +02:00
Ulrich Meine
fca7a47728 smoothing factor adjustments 2025-05-17 12:18:21 +02:00
Ulrich Meine
10951d7f13 Basis working 2025-05-17 10:58:31 +02:00
Ulrich Meine
6dbbd13ead Improve code readability 2025-05-16 20:04:20 +02:00
Ulrich Meine
fb89dca3be Fix 'value1 = 0" in PageRudderPosition 2025-05-16 19:45:58 +02:00
Ulrich Meine
3d31fcf4ed add empty type as default; add calib for types PRPOS,RPOS, and for PageRudderPosition 2025-05-16 19:43:27 +02:00
Ulrich Meine
2c348ca7fb Revert changes in PageOneValue except of calibration. 2025-05-16 00:15:12 +02:00
Scorgan01
6ca40aaa8f Merge branch 'norbert-walter:master' into CalibrationData_v0-1 2025-05-15 14:29:26 -07:00
Ulrich Meine
7ced07d2d9 TWA, TWS, TWD + Pages Wind, WindRose, WindRoseFlex added 2025-05-15 23:24:40 +02:00
Ulrich Meine
583fbd0db8 DBT implemented 2025-05-15 23:13:16 +02:00
Norbert Walter
1862bdc6ae Merge pull request #169 from Scorgan01/Fix_obpFormatter
Fix temperature conversion to Fahrenheit
2025-05-12 10:03:47 +08:00
Norbert Walter
c0f36ecdf4 Merge pull request #167 from TobiasE-github/master
Update PageWindRoseFlex, value 1 as wind pointer, other 5 values as n…
2025-05-12 09:56:15 +08:00
Ulrich Meine
2fb59fb118 Implemented for few std. types - DBT missing 2025-05-11 21:57:22 +02:00
Ulrich Meine
13d6091ae8 Fix temperature conversion to Fahrenheit 2025-05-10 02:16:10 +02:00
Ulrich Meine
a5494ccee4 Initial commit - principle working 2025-05-10 01:59:19 +02:00
Tobias E
e2270d8ed2 Update PageWindRoseFlex, Value 1 as wind pointer, other 5 values as numbers. 2025-05-07 18:00:03 +00:00
Norbert Walter
309d55cdb4 Merge pull request #165 from ManfredRad/master
PageSixValues: compiler directive to include OPB40 support added
2025-05-07 21:57:16 +08:00
norbert-walter
0ec2f93915 Merge remote-tracking branch 'origin/master' 2025-05-07 16:51:00 +08:00
norbert-walter
f8b86e67df Rename file 2025-05-07 16:50:41 +08:00
ManfredRad
0f0a096219 Add files via upload 2025-05-06 21:43:42 +02:00
Norbert Walter
d094bfc32a Merge pull request #162 from ManfredRad/PageSixValues
PageSixValues
2025-05-06 22:18:40 +08:00
Norbert Walter
0044fdd3b6 Update platformio.ini
Chang back to old version
2025-05-06 22:18:03 +08:00
ManfredRad
57684f77db Add files via upload 2025-05-06 07:49:53 +02:00
wellenvogel
d228f38461 update link to the newer version of the web installer 2025-05-02 17:58:26 +02:00
wellenvogel
417fe3db08 update link to the newer version of the web installer 2025-05-02 17:57:26 +02:00
andreas
44c9e998c9 rename dbs to dbt in convertDBKx for more code clearity 2025-04-28 18:42:16 +02:00
andreas
c02122f253 #107: update doc 2025-04-22 19:43:23 +02:00
andreas
30ade0c07a Merge branch 'master' of github.com:wellenvogel/esp32-nmea2000 2025-04-22 19:39:15 +02:00
andreas
38d370dcfb #107: correctly handle DBT,DBS,DBK 2025-04-22 19:35:57 +02:00
wellenvogel
52a376c43a release docs for 20250305 2025-03-05 20:56:51 +01:00
wellenvogel
8c035c4ba1 #100: allow to set custom_config, custom_js, custom_css 2025-03-05 20:48:29 +01:00
wellenvogel
036add6feb #100: introduce extra_config, extra_js, extra_css as options in the environment 2025-03-05 19:49:46 +01:00
rowa-hooge
a1f00dde82 Fix warning extra_script.py 2025-03-05 19:03:34 +01:00
rowa-hooge
756064a362 Fix warning NMEA0183DataToN2K.cpp
Also switch to more readable function names
2025-03-05 19:01:45 +01:00
rowa-hooge
b640d1adda Fix warning GwSpiSensor.h 2025-03-05 19:00:24 +01:00
rowa-hooge
c5cadce268 Fix deprecated warning in main.cpp 2025-03-05 18:59:09 +01:00
norbert-walter
293b362ee4 Fix compiling problem for OBP40 2025-03-05 09:48:45 +01:00
Norbert Walter
98c8d44d2f Merge branch 'wellenvogel:master' into master 2025-03-04 21:38:32 +01:00
norbert-walter
24af837148 Changes 2025-03-04 21:37:55 +01:00
Norbert Walter
7c9b615526 Merge pull request #158 from thooge/fonts
8bit font for Ubuntu Bold
2025-03-04 21:09:42 +01:00
Andreas Vogel
c55bcc1f94 Merge pull request #105 from free-x/VWR
VWR
2025-03-04 20:49:44 +01:00
andreas
a1b8f06959 #101: avoid logging errors for all unset config items 2025-03-04 20:48:12 +01:00
andreas
437b76897a #101: avoid logging errors for all unset config items 2025-03-04 20:48:02 +01:00
andreas
2ab6a00883 #102: only reconnect wifi every 40s 2025-03-04 20:47:47 +01:00
5192e77fab Changed 20pt Ubuntu font to 8bit in value pages 2025-03-04 19:24:08 +01:00
8439dcc18a New 8bit font to make use of degree sign 2025-03-04 19:17:31 +01:00
free-x
5e41c64dd3 #103: fix units logic 2025-02-27 19:44:19 +01:00
free-x
6a2c623ea0 #103: using formatters 2025-02-25 18:54:00 +01:00
free-x
d0256fd37c #103: Initial VWR 2025-02-24 19:01:13 +01:00
Norbert Walter
7cf1b0e6af Merge pull request #156 from thooge/clock
Clock improvements: datetime, RTC, GPS
2025-02-13 21:20:38 +01:00
1aa18f3ec0 Fix config description text 2025-02-10 13:14:47 +01:00
00a8aff8ca Fix OBP60 build and add sunrise/sunset feature for stationary OBP40 2025-02-10 13:09:43 +01:00
acf75495df Fix pointer display and header layout improved 2025-02-08 16:15:37 +01:00
2a4c351c58 Add support for internal RTC and NTP for OBP40 2025-02-06 13:05:56 +01:00
e398c7bdce More work on clock page and datetime handling 2025-02-05 18:17:30 +01:00
eb3a0d5fc0 Clock improvements: source selectable, RTC or GPS 2025-02-04 16:19:29 +01:00
Norbert Walter
3412da8e18 Merge pull request #155 from thooge/simulation
Simulation for more pages
2025-02-03 22:45:17 +01:00
norbert-walter
9909bfdb4c Fix undervoltage detection 2025-01-30 21:59:34 +01:00
6f78fa5434 Merge branch 'simulation' of github.com:thooge/esp32-nmea2000-obp60 into simulation 2025-01-27 18:02:36 +01:00
thooge
3395a63ba1 Merge branch 'norbert-walter:master' into simulation 2025-01-27 18:00:34 +01:00
775a22393d Simulation for page clock 2025-01-27 17:59:32 +01:00
31c968d0a8 Add simulation feature to more pages 2025-01-27 12:31:14 +01:00
norbert-walter
175f525bcd Fix undervoltage detection, format error 2025-01-26 22:11:55 +01:00
Norbert Walter
ba6c1038af Merge pull request #153 from thooge/heartbeat
Integration of heartbeat and page number
2025-01-26 15:19:29 +01:00
Norbert Walter
cbe06f65be Merge pull request #154 from TobiasE-github/master
Dont't display ILUM on Min Power
2025-01-26 15:07:52 +01:00
norbert-walter
e7d5ada610 Fix undervoltage detection 2025-01-26 15:01:47 +01:00
Tobias E
38f1d5de44 Dont't display ILUM on Min Power 2025-01-26 13:09:35 +00:00
Tobias E
93402841cb Merge branch 'master' of https://github.com/TobiasE-github/esp32-nmea2000-obp60 2025-01-26 13:00:48 +00:00
f0cc5d9966 Integration of heartbeat and page number 2025-01-25 19:11:16 +01:00
norbert-walter
9dc857056b Fix LiPo battery level and sensor pad 2025-01-25 18:04:58 +01:00
Norbert Walter
c202554c5c Merge pull request #152 from thooge/battery
Battery symbols for OBP40
2025-01-25 16:20:13 +01:00
norbert-walter
df81e6e443 Fix charge status, add to PageVoltage 2025-01-25 16:19:10 +01:00
097111c270 Added code for new OBP40 battery symbols 2025-01-25 10:21:34 +01:00
bdfaf3c886 Added battery symbols 2025-01-25 09:39:12 +01:00
norbert-walter
26e551c616 Typo 2025-01-24 22:29:12 +01:00
norbert-walter
0cfaf1e793 Merge remote-tracking branch 'origin/master' 2025-01-24 22:19:50 +01:00
norbert-walter
ea9a2ff9c4 Fix for config.json, dependencies to other ode 2025-01-24 22:19:39 +01:00
norbert-walter
f116e41964 Fix for config.json, depensencies to other ode 2025-01-24 22:18:38 +01:00
Norbert Walter
cf7ef8d849 Merge pull request #151 from thooge/master
System page for OBP40 and deep sleep improvements
2025-01-24 18:49:03 +01:00
Norbert Walter
752388a6e7 Merge branch 'master' into master 2025-01-24 18:48:44 +01:00
norbert-walter
0b7863cb86 Typo 2025-01-24 18:41:48 +01:00
norbert-walter
e9ee49a6ef Add undervoltage for LiPo accu for OBP40 2025-01-24 17:33:34 +01:00
norbert-walter
bba24ac2fc Merge remote-tracking branch 'origin/master' 2025-01-24 15:28:09 +01:00
norbert-walter
7afcb86404 OBP40 Battery voltage measuring and capacity calculation 2025-01-24 15:23:02 +01:00
7be102127e Add simulation feature to more pages 2025-01-24 15:14:38 +01:00
Norbert Walter
fc851093a6 Merge pull request #150 from free-x/obp60
extend CI for OBP boards
2025-01-24 12:52:21 +01:00
1174622b4a System page for OBP40 and deep sleep improvements 2025-01-24 12:44:10 +01:00
free-x
28e4fc0643 extend CI for OBP boards 2025-01-24 12:22:26 +01:00
Norbert Walter
a9525676b2 Merge pull request #149 from thooge/master
Fix for OBP40 in page system and deepSleep
2025-01-24 10:07:24 +01:00
46af8916e7 Fix for OBP40 in page system and deepSleep 2025-01-24 08:06:26 +01:00
norbert-walter
b4ebec872d Merge remote-tracking branch 'origin/master' 2025-01-23 22:55:47 +01:00
norbert-walter
78b5861da4 Typo 2025-01-23 22:55:35 +01:00
Norbert Walter
d6a7323600 Merge pull request #148 from thooge/system
Deep sleep for OBP60 and small fix for BMP180
2025-01-23 22:51:52 +01:00
Norbert Walter
db4547ac3f Merge pull request #146 from thooge/refresh
Improved page refresh possibilities and page white
2025-01-23 22:47:30 +01:00
1ff0de5d24 Deep sleep for OBP60 and small fix for BMP180 2025-01-23 19:49:44 +01:00
a42d31ff49 System page inprovements, e.g. soft reset 2025-01-22 20:14:55 +01:00
TobiasE-github
c85e575378 Merge branch 'norbert-walter:master' into master 2025-01-22 14:12:20 +01:00
44cb8d35ce Improved page refresh possibilities and page white 2025-01-22 12:07:00 +01:00
Norbert Walter
988e7ccbc7 Merge pull request #145 from thooge/master
Fix warning and FRAM code on system page
2025-01-21 22:43:35 +01:00
norbert-walter
535f1cd7c4 Modify power mode fpr OBP40 2025-01-21 22:41:47 +01:00
norbert-walter
fa2cfcccca Merge remote-tracking branch 'origin/master' 2025-01-21 22:08:22 +01:00
norbert-walter
27d23c9d16 Add sleep mode for OBP40 and cleanup code 2025-01-21 22:08:02 +01:00
TobiasE-github
922247ed4a Merge pull request #12 from thooge/master
Sync
2025-01-21 19:26:27 +01:00
norbert-walter
bea4c8298e Modify files for Gitpod 2025-01-21 16:32:40 +01:00
464d6879a3 Fix broken white page and add logo feature 2025-01-21 11:26:49 +01:00
b9356c9ae8 Fix warning and FRAM code on system page 2025-01-21 09:28:53 +01:00
norbert-walter
569273519c Correct splitting between OBP60 and OBP40 hardware 2025-01-20 21:21:58 +01:00
Norbert Walter
4c8ffae0b6 Merge pull request #144 from thooge/system
New system page
2025-01-20 20:22:01 +01:00
a73f50ba74 First working system page 2025-01-20 19:20:37 +01:00
Norbert Walter
f377ab135d Merge pull request #143 from thooge/master
OBP40 integration code and improvements
2025-01-20 16:13:07 +01:00
Norbert Walter
e348e40ab4 Merge pull request #142 from TobiasE-github/master
modified gen_set.py, as PageSixValues does not exist (yet)
2025-01-20 16:10:32 +01:00
1545855326 OBP40 integration code and improvements 2025-01-20 12:49:30 +01:00
9d395c719a More work on system page 2025-01-20 06:33:51 +01:00
322ae30858 Merge branch 'master' into system 2025-01-19 15:09:29 +01:00
Tobias E
27b916c079 modified gen_set.py, as PageSixValues does not exist (yet) 2025-01-19 08:36:13 +00:00
norbert-walter
87a7a79358 Change back to plaformio.ini OBP60 2025-01-19 00:23:22 +01:00
Norbert Walter
e7216e6d2b Merge branch 'wellenvogel:master' into master 2025-01-18 19:12:24 +01:00
Norbert Walter
e7f3430ed6 Merge pull request #141 from thooge/keyboard
Key label improvements: footer function
2025-01-18 19:10:33 +01:00
Norbert Walter
2c2d21535b Merge pull request #139 from TobiasE-github/master
Add SixValues and fix Fluid in gen_set.py
2025-01-18 19:06:40 +01:00
Tobias E
27b02c4860 removed obsolete perl script gen_set.pl 2025-01-18 13:59:37 +00:00
e917a7fc76 Modification of footer layout and new swipe icon 2025-01-17 21:23:41 +01:00
Tobias E
10552763fb add explanation to gen_set.py 2025-01-17 19:24:16 +00:00
Tobias E
24386d4d42 add explanation to gen_set.py 2025-01-17 19:23:26 +00:00
norbert-walter
49be7f117a Modify pad detectionfor OBP60 Light 2025-01-17 18:00:32 +01:00
f690cf3a34 Hide key labels in keylock mode, improved swipe icon 2025-01-16 19:39:35 +01:00
46da1142c8 Merge branch 'master' into keyboard 2025-01-15 18:27:13 +01:00
519af68bef Footer function for improved key labels, icons implemented 2025-01-15 18:08:39 +01:00
TobiasE-github
e70660c981 Merge branch 'norbert-walter:master' into master 2025-01-14 20:39:25 +01:00
norbert-walter
47db247471 Fix backup platformio.ini 2025-01-14 20:28:41 +01:00
norbert-walter
6ffa974354 Fix platformio.ini 2025-01-14 20:25:36 +01:00
norbert-walter
d2ee05a353 Merge remote-tracking branch 'origin/master' 2025-01-14 20:20:04 +01:00
norbert-walter
574deac6f8 Fix for obptask.cpp 2025-01-14 20:19:31 +01:00
TobiasE-github
4395c623ea Merge branch 'norbert-walter:master' into master 2025-01-13 06:30:15 +01:00
Norbert Walter
bbdbe47972 Merge pull request #137 from thooge/master
Fix missing data bindings for page fluid
2025-01-12 23:56:02 +01:00
norbert-walter
9fc090a5a8 Fix flag error in platformio.ini 2025-01-12 23:50:38 +01:00
Tobias E
dfc79c80dc Add SixValues and fix Fluid in gen_set.py 2025-01-12 21:40:39 +00:00
norbert-walter
71673e78bd First version for OBP 60 Light 2025-01-12 18:22:40 +01:00
970d05191c Fix missing data bindings for page fluid 2025-01-12 18:01:54 +01:00
e7db5038fb Merge branch 'master' into keyboard: new page wind 2025-01-10 19:53:26 +01:00
e4af7cf731 Move page number, swipe and lock indicator to header 2025-01-10 19:41:55 +01:00
Norbert Walter
695013cb88 Merge pull request #135 from thooge/wind
Changed ApparentWind to Wind with modes and lens function
2025-01-10 16:40:42 +01:00
81a6c05b55 Merge branch 'master' into wind 2025-01-09 09:32:07 +01:00
Norbert Walter
bedb8338ab Merge pull request #134 from TobiasE-github/master
PageRollPitch now accepts 2 config options, converted perl script to python
2025-01-08 22:39:38 +01:00
8d43189140 Fix zoom keycodes 2025-01-08 14:52:44 +01:00
be48929c40 Changed ApparentWind to Wind with modes and lens function 2025-01-08 14:26:52 +01:00
TobiasE-github
1a43fe29e9 Merge branch 'norbert-walter:master' into master 2025-01-06 15:45:00 +01:00
Tobias E
9930769073 PageRollPitch now accepts 2 config options
Fixed conversion issue in PageWindRoseFlex
gen_set.pl is now obsolete, new version in python.
modified config.json (mostly reformatting due to gen_set.py), added 2 config fields for PageRollPitch
2025-01-06 14:36:51 +00:00
norbert-walter
83f3e6f24b Fix error 2025-01-04 22:32:19 +01:00
norbert-walter
a8d1080429 Delete FastLED includes 2025-01-04 22:26:15 +01:00
norbert-walter
b142470c1d Docu 2025-01-04 22:24:33 +01:00
Norbert Walter
b91f345d25 Merge pull request #129 from thooge/master
Some small typo fixes
2025-01-04 17:31:22 +01:00
6637b80400 Some small typo fixes 2025-01-02 19:55:39 +01:00
Norbert Walter
707ac9b84f Merge pull request #128 from thooge/fluid
Fix page fluid data and fluidtype selection
2025-01-02 16:16:25 +01:00
Norbert Walter
2808c56a37 Merge pull request #127 from thooge/voltage
Voltage analog display
2025-01-02 16:15:30 +01:00
319f3f3e68 Improve FRAM icon 2024-12-30 19:46:54 +01:00
6e256e136a Fix page fluid data and fluidtype selection 2024-12-30 12:16:46 +01:00
daaefc7eba Use I2C FRAM module if available to store page voltage state 2024-12-25 19:23:44 +01:00
3bc0082bb8 Fix pointer base display 2024-12-24 20:31:45 +01:00
0274b5d755 Added analog voltage display 2024-12-24 20:24:48 +01:00
Norbert Walter
df5ff1c91a Merge pull request #126 from thooge/screenshot
Screenshot feature
2024-12-24 00:06:28 +01:00
c7b6bafde8 Added missing imglib files 2024-12-22 14:10:46 +01:00
21ae96c8d7 Added screenshot feature 2024-12-22 14:07:18 +01:00
wellenvogel
a129d865c9 warn the user if page translation is enabled for cibuild 2024-12-10 19:16:11 +01:00
57e194e39d Add new system page 2024-12-07 11:11:07 +01:00
184 changed files with 27786 additions and 35148 deletions

View File

@@ -68,12 +68,12 @@ Initial Flash
__Browser__ __Browser__
If you run a system with a modern Chrome or Edge Browser you can directly flash your device from within the browser. If you run a system with a modern Chrome or Edge Browser you can directly flash your device from within the browser.
Just go to the [Flash Page](https://wellenvogel.github.io/esp32-nmea2000/install.html) and select the "Initial" flash for your Hardware. This will install the most current software to your device. Just go to the [Flash Page](https://wellenvogel.de/software/esp32/install.html) and select the "Initial" flash for your Hardware. This will install the most current software to your device. If you are using a forked project (like OBP60) refer to the documentation of the fork. You can just install any flash binary from your local computer with the browser based installation using the "upload" button.<br>
If you are on Windows you will need to have the correct driver installed before (see below at [windows users](#windows) - only install the driver, not the flashtool). If you are on Windows you will need to have the correct driver installed before (see below at [windows users](#windows) - only install the driver, not the flashtool).
You can also install an update from the flash page but normally it is easier to do this from the Web Gui of the device (see [below](#update)). You can also install an update from the flash page but normally it is easier to do this from the Web Gui of the device (see [below](#update)).
The [Flash Page](https://wellenvogel.github.io/esp32-nmea2000/install.html) will also allow you to open a console window to your ESP32. The [Flash Page](https://wellenvogel.de/software/esp32/install.html) will also allow you to open a console window to your ESP32.
__Tool based__ __Tool based__
@@ -170,6 +170,12 @@ For details refer to the [example description](lib/exampletask/Readme.md).
Changelog Changelog
--------- ---------
[20250305](../../releases/tag/20250305)
*********
* better handling for reconnect to a raspberry pi after reset [#102](../../issues/102)
* introduce _custom_config_, _custom_js_, _custom_css_, refer to [extending the core](lib/exampletask/Readme.md) [#100](../../pull/100)
* create VWR [#103](../../issues/103)
[20241128](../../releases/tag/20241128) [20241128](../../releases/tag/20241128)
********* *********
* additional correction for: USB connection on S3 stops [#81](../../issues/81) * additional correction for: USB connection on S3 stops [#81](../../issues/81)

56
boards/obp40_s3_n8r8.json Normal file
View File

@@ -0,0 +1,56 @@
{
"build": {
"arduino":{
"ldscript": "esp32s3_out.ld",
"partitions": "default_8MB.csv",
"memory_type": "qio_opi"
},
"core": "esp32",
"extra_flags": [
"-DBOARD_HAS_PSRAM",
"-DARDUINO_ESP32S3_DEV",
"-DARDUINO_USB_MODE=1",
"-DARDUINO_USB_CDC_ON_BOOT=0",
"-DARDUINO_RUNNING_CORE=1",
"-DARDUINO_EVENT_RUNNING_CORE=1"
],
"f_cpu": "240000000L",
"f_flash": "80000000L",
"flash_mode": "qio",
"hwids": [
[
"0x1A86",
"0x7523"
]
],
"mcu": "esp32s3",
"variant": "obp40s3"
},
"connectivity": [
"bluetooth",
"wifi"
],
"debug": {
"default_tool": "esp-builtin",
"onboard_tools": [
"esp-builtin"
],
"openocd_target": "esp32s3.cfg"
},
"frameworks": [
"arduino",
"espidf"
],
"name": "OBP40 ESP32-S3-N8R8 (8 MB QD, 8 MB PSRAM)",
"upload": {
"flash_size": "8MB",
"maximum_ram_size": 327680,
"maximum_size": 8388608,
"use_1200bps_touch": true,
"wait_for_upload_port": true,
"require_upload_port": true,
"speed": 460800
},
"url": "https://open-boat-projects.org/en/diy-multifunktionsdisplay-obp-60/",
"vendor": "Open Boat Projects"
}

Binary file not shown.

Binary file not shown.

View File

@@ -10,7 +10,7 @@ from datetime import datetime
import re import re
import pprint import pprint
from platformio.project.config import ProjectConfig from platformio.project.config import ProjectConfig
from platformio.project.exception import InvalidProjectConfError
Import("env") Import("env")
#print(env.Dump()) #print(env.Dump())
@@ -104,7 +104,17 @@ def writeFileIfChanged(fileName,data):
return True return True
def mergeConfig(base,other): def mergeConfig(base,other):
try:
customconfig = env.GetProjectOption("custom_config")
except InvalidProjectConfError:
customconfig = None
for bdir in other: for bdir in other:
if customconfig and os.path.exists(os.path.join(bdir,customconfig)):
cname=os.path.join(bdir,customconfig)
print("merge custom config {}".format(cname))
with open(cname,'rb') as ah:
base += json.load(ah)
continue
cname=os.path.join(bdir,"config.json") cname=os.path.join(bdir,"config.json")
if os.path.exists(cname): if os.path.exists(cname):
print("merge config %s"%cname) print("merge config %s"%cname)
@@ -274,9 +284,9 @@ class Grove:
def _ss(self,z=False): def _ss(self,z=False):
if z: if z:
return self.name return self.name
return self.name if self.name is not 'Z' else '' return self.name if self.name != 'Z' else ''
def _suffix(self): def _suffix(self):
return '_'+self.name if self.name is not 'Z' else '' return '_'+self.name if self.name != 'Z' else ''
def replace(self,line): def replace(self,line):
if line is None: if line is None:
return line return line
@@ -516,3 +526,17 @@ env.Append(
) )
#script does not run on clean yet - maybe in the future #script does not run on clean yet - maybe in the future
env.AddPostAction("clean",cleangenerated) env.AddPostAction("clean",cleangenerated)
#look for extra task scripts and include them here
for taskdir in userTaskDirs:
script = os.path.join(taskdir, "extra_task.py")
if os.path.isfile(script):
taskname = os.path.basename(os.path.normpath(taskdir))
print("#extra task script for '{}'".format(taskname))
with open(script) as fh:
try:
code = compile(fh.read(), taskname, 'exec')
except SyntaxError:
print("#ERROR: script does not compile")
continue
exec(code)

542
extra_script.py.new Normal file
View File

@@ -0,0 +1,542 @@
print("running extra...")
import gzip
import shutil
import os
import sys
import inspect
import json
import glob
from datetime import datetime
import re
import pprint
from platformio.project.config import ProjectConfig
from platformio.project.exception import InvalidProjectConfError
Import("env")
#print(env.Dump())
OWN_FILE="extra_script.py"
GEN_DIR='lib/generated'
CFG_FILE='web/config.json'
XDR_FILE='web/xdrconfig.json'
INDEXJS="index.js"
INDEXCSS="index.css"
CFG_INCLUDE='GwConfigDefinitions.h'
CFG_INCLUDE_IMPL='GwConfigDefImpl.h'
XDR_INCLUDE='GwXdrTypeMappings.h'
TASK_INCLUDE='GwUserTasks.h'
GROVE_CONFIG="GwM5GroveGen.h"
GROVE_CONFIG_IN="lib/hardware/GwM5Grove.in"
EMBEDDED_INCLUDE="GwEmbeddedFiles.h"
def getEmbeddedFiles(env):
rt=[]
efiles=env.GetProjectOption("board_build.embed_files")
for f in efiles.split("\n"):
if f == '':
continue
rt.append(f)
return rt
def basePath():
#see: https://stackoverflow.com/questions/16771894/python-nameerror-global-name-file-is-not-defined
return os.path.dirname(inspect.getfile(lambda: None))
def outPath():
return os.path.join(basePath(),GEN_DIR)
def checkDir():
dn=outPath()
if not os.path.exists(dn):
os.makedirs(dn)
if not os.path.isdir(dn):
print("unable to create %s"%dn)
return False
return True
def isCurrent(infile,outfile):
if os.path.exists(outfile):
otime=os.path.getmtime(outfile)
itime=os.path.getmtime(infile)
if (otime >= itime):
own=os.path.join(basePath(),OWN_FILE)
if os.path.exists(own):
owntime=os.path.getmtime(own)
if owntime > otime:
return False
print("%s is newer then %s, no need to recreate"%(outfile,infile))
return True
return False
def compressFile(inFile,outfile):
if isCurrent(inFile,outfile):
return
print("compressing %s"%inFile)
with open(inFile, 'rb') as f_in:
with gzip.open(outfile, 'wb') as f_out:
shutil.copyfileobj(f_in, f_out)
def generateFile(infile,outfile,callback,inMode='rb',outMode='w'):
if isCurrent(infile,outfile):
return
print("creating %s"%outfile)
oh=None
with open(infile,inMode) as ch:
with open(outfile,outMode) as oh:
try:
callback(ch,oh,inFile=infile)
oh.close()
except Exception as e:
try:
oh.close()
except:
pass
os.unlink(outfile)
raise
def writeFileIfChanged(fileName,data):
if os.path.exists(fileName):
with open(fileName,"r") as ih:
old=ih.read()
ih.close()
if old == data:
return False
print("#generating %s"%fileName)
with open(fileName,"w") as oh:
oh.write(data)
return True
def mergeConfig(base,other):
try:
customconfig = env.GetProjectOption("custom_config")
except InvalidProjectConfError:
customconfig = None
for bdir in other:
if customconfig and os.path.exists(os.path.join(bdir,customconfig)):
cname=os.path.join(bdir,customconfig)
print("merge custom config {}".format(cname))
with open(cname,'rb') as ah:
base += json.load(ah)
continue
cname=os.path.join(bdir,"config.json")
if os.path.exists(cname):
print("merge config %s"%cname)
with open(cname,'rb') as ah:
merge=json.load(ah)
base=base+merge
return base
def replaceTexts(data,replacements):
if replacements is None:
return data
if isinstance(data,str):
for k,v in replacements.items():
data=data.replace("$"+k,str(v))
return data
if isinstance(data,list):
rt=[]
for e in data:
rt.append(replaceTexts(e,replacements))
return rt
if isinstance(data,dict):
rt={}
for k,v in data.items():
rt[replaceTexts(k,replacements)]=replaceTexts(v,replacements)
return rt
return data
def expandConfig(config):
rt=[]
for item in config:
type=item.get('type')
if type != 'array':
rt.append(item)
continue
replacements=item.get('replace')
children=item.get('children')
name=item.get('name')
if name is None:
name="#unknown#"
if not isinstance(replacements,list):
raise Exception("missing replacements at array %s"%name)
for replace in replacements:
if children is not None:
for c in children:
rt.append(replaceTexts(c,replace))
return rt
def generateMergedConfig(inFile,outFile,addDirs=[]):
if not os.path.exists(inFile):
raise Exception("unable to read cfg file %s"%inFile)
data=""
with open(inFile,'rb') as ch:
config=json.load(ch)
config=mergeConfig(config,addDirs)
config=expandConfig(config)
data=json.dumps(config,indent=2)
writeFileIfChanged(outFile,data)
def generateCfg(inFile,outFile,impl):
if not os.path.exists(inFile):
raise Exception("unable to read cfg file %s"%inFile)
data=""
with open(inFile,'rb') as ch:
config=json.load(ch)
data+="//generated from %s\n"%inFile
l=len(config)
idx=0
if not impl:
data+='#include "GwConfigItem.h"\n'
data+='class GwConfigDefinitions{\n'
data+=' public:\n'
data+=' int getNumConfig() const{return %d;}\n'%(l)
for item in config:
n=item.get('name')
if n is None:
continue
if len(n) > 15:
raise Exception("%s: config names must be max 15 caracters"%n)
data+=' static constexpr const char* %s="%s";\n'%(n,n)
data+="};\n"
else:
data+='void GwConfigHandler::populateConfigs(GwConfigInterface **config){\n'
for item in config:
name=item.get('name')
if name is None:
continue
data+=' configs[%d]='%(idx)
idx+=1
secret="false";
if item.get('type') == 'password':
secret="true"
data+=" new GwConfigInterface(%s,\"%s\",%s);\n"%(name,item.get('default'),secret)
data+='}\n'
writeFileIfChanged(outFile,data)
def labelFilter(label):
return re.sub("[^a-zA-Z0-9]","",re.sub("\([0-9]*\)","",label))
def generateXdrMappings(fp,oh,inFile=''):
jdoc=json.load(fp)
oh.write("static GwXDRTypeMapping* typeMappings[]={\n")
first=True
for cat in jdoc:
item=jdoc[cat]
cid=item.get('id')
if cid is None:
continue
tc=item.get('type')
if tc is not None:
if first:
first=False
else:
oh.write(",\n")
oh.write(" new GwXDRTypeMapping(%d,0,%d) /*%s*/"%(cid,tc,cat))
fields=item.get('fields')
if fields is None:
continue
idx=0
for fe in fields:
if not isinstance(fe,dict):
continue
tc=fe.get('t')
id=fe.get('v')
if id is None:
id=idx
idx+=1
l=fe.get('l') or ''
if tc is None or id is None:
continue
if first:
first=False
else:
oh.write(",\n")
oh.write(" new GwXDRTypeMapping(%d,%d,%d) /*%s:%s*/"%(cid,id,tc,cat,l))
oh.write("\n")
oh.write("};\n")
for cat in jdoc:
item=jdoc[cat]
cid=item.get('id')
if cid is None:
continue
selectors=item.get('selector')
if selectors is not None:
for selector in selectors:
label=selector.get('l')
value=selector.get('v')
if label is not None and value is not None:
label=labelFilter(label)
define=("GWXDRSEL_%s_%s"%(cat,label)).upper()
oh.write(" #define %s %s\n"%(define,value))
fields=item.get('fields')
if fields is not None:
idx=0
for field in fields:
v=field.get('v')
if v is None:
v=idx
else:
v=int(v)
label=field.get('l')
if v is not None and label is not None:
define=("GWXDRFIELD_%s_%s"%(cat,labelFilter(label))).upper();
oh.write(" #define %s %s\n"%(define,str(v)))
idx+=1
class Grove:
def __init__(self,name) -> None:
self.name=name
def _ss(self,z=False):
if z:
return self.name
return self.name if self.name != 'Z' else ''
def _suffix(self):
return '_'+self.name if self.name != 'Z' else ''
def replace(self,line):
if line is None:
return line
return line.replace('$G$',self._ss()).replace('$Z$',self._ss(True)).replace('$GS$',self._suffix())
def generateGroveDefs(inh,outh,inFile=''):
GROVES=[Grove('Z'),Grove('A'),Grove('B'),Grove('C')]
definition=[]
started=False
def writeConfig():
for grove in GROVES:
for cl in definition:
outh.write(grove.replace(cl))
for line in inh:
if re.match(" *#GROVE",line):
started=True
if len(definition) > 0:
writeConfig()
definition=[]
continue
if started:
definition.append(line)
if len(definition) > 0:
writeConfig()
userTaskDirs=[]
def getUserTaskDirs():
rt=[]
taskdirs=glob.glob(os.path.join( basePath(),'lib','*task*'))
for task in taskdirs:
rt.append(task)
return rt
def checkAndAdd(file,names,ilist):
if not file.endswith('.h'):
return
match=False
for cmp in names:
#print("##check %s<->%s"%(f.lower(),cmp))
if file.lower() == cmp:
match=True
if not match:
return
ilist.append(file)
def genereateUserTasks(outfile):
includes=[]
for task in userTaskDirs:
#print("##taskdir=%s"%task)
base=os.path.basename(task)
includeNames=[base.lower()+".h",'gw'+base.lower()+'.h']
for f in os.listdir(task):
checkAndAdd(f,includeNames,includes)
includeData=""
for i in includes:
print("#task include %s"%i)
includeData+="#include <%s>\n"%i
writeFileIfChanged(outfile,includeData)
def generateEmbedded(elist,outFile):
content=""
for entry in elist:
content+="EMBED_GZ_FILE(\"%s\",%s,\"%s\");\n"%entry
writeFileIfChanged(outFile,content)
def getContentType(fn):
if (fn.endswith('.gz')):
fn=fn[0:-3]
if (fn.endswith('html')):
return "text/html"
if (fn.endswith('json')):
return "application/json"
if (fn.endswith('js')):
return "text/javascript"
if (fn.endswith('css')):
return "text/css"
return "application/octet-stream"
def getLibs():
base=os.path.join(basePath(),"lib")
rt=[]
for sd in os.listdir(base):
if sd == '..':
continue
if sd == '.':
continue
fn=os.path.join(base,sd)
if os.path.isdir(fn):
rt.append(sd)
EXTRAS=['generated']
for e in EXTRAS:
if not e in rt:
rt.append(e)
return rt
def joinFiles(target,pattern,dirlist):
flist=[]
for dir in dirlist:
fn=os.path.join(dir,pattern)
if os.path.exists(fn):
flist.append(fn)
current=False
if os.path.exists(target):
current=True
for f in flist:
if not isCurrent(f,target):
current=False
break
if current:
print("%s is up to date"%target)
return
print("creating %s"%target)
with gzip.open(target,"wb") as oh:
for fn in flist:
print("adding %s to %s"%(fn,target))
with open(fn,"rb") as rh:
shutil.copyfileobj(rh,oh)
OWNLIBS=getLibs()+["FS","WiFi"]
GLOBAL_INCLUDES=[]
def handleDeps(env):
#overwrite the GetProjectConfig
#to inject all our libs
oldGetProjectConfig=env.GetProjectConfig
def GetProjectConfigX(env):
rt=oldGetProjectConfig()
cenv="env:"+env['PIOENV']
libs=[]
for section,options in rt.as_tuple():
if section == cenv:
for key,values in options:
if key == 'lib_deps':
libs=values
mustUpdate=False
for lib in OWNLIBS:
if not lib in libs:
libs.append(lib)
mustUpdate=True
if mustUpdate:
update=[(cenv,[('lib_deps',libs)])]
rt.update(update)
return rt
env.AddMethod(GetProjectConfigX,"GetProjectConfig")
#store the list of all includes after we resolved
#the dependencies for our main project
#we will use them for all compilations afterwards
oldLibBuilder=env.ConfigureProjectLibBuilder
def ConfigureProjectLibBuilderX(env):
global GLOBAL_INCLUDES
project=oldLibBuilder()
#print("##ConfigureProjectLibBuilderX")
#pprint.pprint(project)
if project.depbuilders:
#print("##depbuilders %s"%",".join(map(lambda x: x.path,project.depbuilders)))
for db in project.depbuilders:
idirs=db.get_include_dirs()
for id in idirs:
if not id in GLOBAL_INCLUDES:
GLOBAL_INCLUDES.append(id)
return project
env.AddMethod(ConfigureProjectLibBuilderX,"ConfigureProjectLibBuilder")
def injectIncludes(env,node):
return env.Object(
node,
CPPPATH=env["CPPPATH"]+GLOBAL_INCLUDES
)
env.AddBuildMiddleware(injectIncludes)
def prebuild(env):
global userTaskDirs
print("#prebuild running")
if not checkDir():
sys.exit(1)
ldf_mode=env.GetProjectOption("lib_ldf_mode")
if ldf_mode == 'off':
print("##ldf off - own dependency handling")
handleDeps(env)
userTaskDirs=getUserTaskDirs()
mergedConfig=os.path.join(outPath(),os.path.basename(CFG_FILE))
generateMergedConfig(os.path.join(basePath(),CFG_FILE),mergedConfig,userTaskDirs)
compressFile(mergedConfig,mergedConfig+".gz")
generateCfg(mergedConfig,os.path.join(outPath(),CFG_INCLUDE),False)
generateCfg(mergedConfig,os.path.join(outPath(),CFG_INCLUDE_IMPL),True)
joinFiles(os.path.join(outPath(),INDEXJS+".gz"),INDEXJS,["web"]+userTaskDirs)
joinFiles(os.path.join(outPath(),INDEXCSS+".gz"),INDEXCSS,["web"]+userTaskDirs)
embedded=getEmbeddedFiles(env)
filedefs=[]
for ef in embedded:
print("#checking embedded file %s"%ef)
(dn,fn)=os.path.split(ef)
pureName=fn
if pureName.endswith('.gz'):
pureName=pureName[0:-3]
ct=getContentType(pureName)
usname=ef.replace('/','_').replace('.','_')
filedefs.append((pureName,usname,ct))
inFile=os.path.join(basePath(),"web",pureName)
if os.path.exists(inFile):
compressFile(inFile,ef)
else:
print("#WARNING: infile %s for %s not found"%(inFile,ef))
generateEmbedded(filedefs,os.path.join(outPath(),EMBEDDED_INCLUDE))
genereateUserTasks(os.path.join(outPath(), TASK_INCLUDE))
generateFile(os.path.join(basePath(),XDR_FILE),os.path.join(outPath(),XDR_INCLUDE),generateXdrMappings)
generateFile(os.path.join(basePath(),GROVE_CONFIG_IN),os.path.join(outPath(),GROVE_CONFIG),generateGroveDefs,inMode='r')
version="dev"+datetime.now().strftime("%Y%m%d")
env.Append(CPPDEFINES=[('GWDEVVERSION',version)])
def cleangenerated(source, target, env):
od=outPath()
if os.path.isdir(od):
print("#cleaning up %s"%od)
for f in os.listdir(od):
if f == "." or f == "..":
continue
fn=os.path.join(od,f)
os.unlink(f)
print("#prescript...")
prebuild(env)
board="PLATFORM_BOARD_%s"%env["BOARD"].replace("-","_").upper()
print("Board=#%s#"%board)
print("BuildFlags=%s"%(" ".join(env["BUILD_FLAGS"])))
env.Append(
LINKFLAGS=[ "-u", "custom_app_desc" ],
CPPDEFINES=[(board,"1")]
)
#script does not run on clean yet - maybe in the future
env.AddPostAction("clean",cleangenerated)
#look for extra task scripts and include them here
for taskdir in userTaskDirs:
script = os.path.join(taskdir, "extra_task.py")
if os.path.isfile(script):
taskname = os.path.basename(os.path.normpath(taskdir))
print("#extra task script for '{}'".format(taskname))
with open(script) as fh:
try:
code = compile(fh.read(), taskname, 'exec')
except SyntaxError:
print("#ERROR: script does not compile")
continue
exec(code)

518
extra_script.py.old Normal file
View File

@@ -0,0 +1,518 @@
print("running extra...")
import gzip
import shutil
import os
import sys
import inspect
import json
import glob
from datetime import datetime
import re
import pprint
from platformio.project.config import ProjectConfig
Import("env")
#print(env.Dump())
OWN_FILE="extra_script.py"
GEN_DIR='lib/generated'
CFG_FILE='web/config.json'
XDR_FILE='web/xdrconfig.json'
INDEXJS="index.js"
INDEXCSS="index.css"
CFG_INCLUDE='GwConfigDefinitions.h'
CFG_INCLUDE_IMPL='GwConfigDefImpl.h'
XDR_INCLUDE='GwXdrTypeMappings.h'
TASK_INCLUDE='GwUserTasks.h'
GROVE_CONFIG="GwM5GroveGen.h"
GROVE_CONFIG_IN="lib/hardware/GwM5Grove.in"
EMBEDDED_INCLUDE="GwEmbeddedFiles.h"
def getEmbeddedFiles(env):
rt=[]
efiles=env.GetProjectOption("board_build.embed_files")
for f in efiles.split("\n"):
if f == '':
continue
rt.append(f)
return rt
def basePath():
#see: https://stackoverflow.com/questions/16771894/python-nameerror-global-name-file-is-not-defined
return os.path.dirname(inspect.getfile(lambda: None))
def outPath():
return os.path.join(basePath(),GEN_DIR)
def checkDir():
dn=outPath()
if not os.path.exists(dn):
os.makedirs(dn)
if not os.path.isdir(dn):
print("unable to create %s"%dn)
return False
return True
def isCurrent(infile,outfile):
if os.path.exists(outfile):
otime=os.path.getmtime(outfile)
itime=os.path.getmtime(infile)
if (otime >= itime):
own=os.path.join(basePath(),OWN_FILE)
if os.path.exists(own):
owntime=os.path.getmtime(own)
if owntime > otime:
return False
print("%s is newer then %s, no need to recreate"%(outfile,infile))
return True
return False
def compressFile(inFile,outfile):
if isCurrent(inFile,outfile):
return
print("compressing %s"%inFile)
with open(inFile, 'rb') as f_in:
with gzip.open(outfile, 'wb') as f_out:
shutil.copyfileobj(f_in, f_out)
def generateFile(infile,outfile,callback,inMode='rb',outMode='w'):
if isCurrent(infile,outfile):
return
print("creating %s"%outfile)
oh=None
with open(infile,inMode) as ch:
with open(outfile,outMode) as oh:
try:
callback(ch,oh,inFile=infile)
oh.close()
except Exception as e:
try:
oh.close()
except:
pass
os.unlink(outfile)
raise
def writeFileIfChanged(fileName,data):
if os.path.exists(fileName):
with open(fileName,"r") as ih:
old=ih.read()
ih.close()
if old == data:
return False
print("#generating %s"%fileName)
with open(fileName,"w") as oh:
oh.write(data)
return True
def mergeConfig(base,other):
for bdir in other:
cname=os.path.join(bdir,"config.json")
if os.path.exists(cname):
print("merge config %s"%cname)
with open(cname,'rb') as ah:
merge=json.load(ah)
base=base+merge
return base
def replaceTexts(data,replacements):
if replacements is None:
return data
if isinstance(data,str):
for k,v in replacements.items():
data=data.replace("$"+k,str(v))
return data
if isinstance(data,list):
rt=[]
for e in data:
rt.append(replaceTexts(e,replacements))
return rt
if isinstance(data,dict):
rt={}
for k,v in data.items():
rt[replaceTexts(k,replacements)]=replaceTexts(v,replacements)
return rt
return data
def expandConfig(config):
rt=[]
for item in config:
type=item.get('type')
if type != 'array':
rt.append(item)
continue
replacements=item.get('replace')
children=item.get('children')
name=item.get('name')
if name is None:
name="#unknown#"
if not isinstance(replacements,list):
raise Exception("missing replacements at array %s"%name)
for replace in replacements:
if children is not None:
for c in children:
rt.append(replaceTexts(c,replace))
return rt
def generateMergedConfig(inFile,outFile,addDirs=[]):
if not os.path.exists(inFile):
raise Exception("unable to read cfg file %s"%inFile)
data=""
with open(inFile,'rb') as ch:
config=json.load(ch)
config=mergeConfig(config,addDirs)
config=expandConfig(config)
data=json.dumps(config,indent=2)
writeFileIfChanged(outFile,data)
def generateCfg(inFile,outFile,impl):
if not os.path.exists(inFile):
raise Exception("unable to read cfg file %s"%inFile)
data=""
with open(inFile,'rb') as ch:
config=json.load(ch)
data+="//generated from %s\n"%inFile
l=len(config)
idx=0
if not impl:
data+='#include "GwConfigItem.h"\n'
data+='class GwConfigDefinitions{\n'
data+=' public:\n'
data+=' int getNumConfig() const{return %d;}\n'%(l)
for item in config:
n=item.get('name')
if n is None:
continue
if len(n) > 15:
raise Exception("%s: config names must be max 15 caracters"%n)
data+=' static constexpr const char* %s="%s";\n'%(n,n)
data+="};\n"
else:
data+='void GwConfigHandler::populateConfigs(GwConfigInterface **config){\n'
for item in config:
name=item.get('name')
if name is None:
continue
data+=' configs[%d]='%(idx)
idx+=1
secret="false";
if item.get('type') == 'password':
secret="true"
data+=" new GwConfigInterface(%s,\"%s\",%s);\n"%(name,item.get('default'),secret)
data+='}\n'
writeFileIfChanged(outFile,data)
def labelFilter(label):
return re.sub("[^a-zA-Z0-9]","",re.sub("\([0-9]*\)","",label))
def generateXdrMappings(fp,oh,inFile=''):
jdoc=json.load(fp)
oh.write("static GwXDRTypeMapping* typeMappings[]={\n")
first=True
for cat in jdoc:
item=jdoc[cat]
cid=item.get('id')
if cid is None:
continue
tc=item.get('type')
if tc is not None:
if first:
first=False
else:
oh.write(",\n")
oh.write(" new GwXDRTypeMapping(%d,0,%d) /*%s*/"%(cid,tc,cat))
fields=item.get('fields')
if fields is None:
continue
idx=0
for fe in fields:
if not isinstance(fe,dict):
continue
tc=fe.get('t')
id=fe.get('v')
if id is None:
id=idx
idx+=1
l=fe.get('l') or ''
if tc is None or id is None:
continue
if first:
first=False
else:
oh.write(",\n")
oh.write(" new GwXDRTypeMapping(%d,%d,%d) /*%s:%s*/"%(cid,id,tc,cat,l))
oh.write("\n")
oh.write("};\n")
for cat in jdoc:
item=jdoc[cat]
cid=item.get('id')
if cid is None:
continue
selectors=item.get('selector')
if selectors is not None:
for selector in selectors:
label=selector.get('l')
value=selector.get('v')
if label is not None and value is not None:
label=labelFilter(label)
define=("GWXDRSEL_%s_%s"%(cat,label)).upper()
oh.write(" #define %s %s\n"%(define,value))
fields=item.get('fields')
if fields is not None:
idx=0
for field in fields:
v=field.get('v')
if v is None:
v=idx
else:
v=int(v)
label=field.get('l')
if v is not None and label is not None:
define=("GWXDRFIELD_%s_%s"%(cat,labelFilter(label))).upper();
oh.write(" #define %s %s\n"%(define,str(v)))
idx+=1
class Grove:
def __init__(self,name) -> None:
self.name=name
def _ss(self,z=False):
if z:
return self.name
return self.name if self.name is not 'Z' else ''
def _suffix(self):
return '_'+self.name if self.name is not 'Z' else ''
def replace(self,line):
if line is None:
return line
return line.replace('$G$',self._ss()).replace('$Z$',self._ss(True)).replace('$GS$',self._suffix())
def generateGroveDefs(inh,outh,inFile=''):
GROVES=[Grove('Z'),Grove('A'),Grove('B'),Grove('C')]
definition=[]
started=False
def writeConfig():
for grove in GROVES:
for cl in definition:
outh.write(grove.replace(cl))
for line in inh:
if re.match(" *#GROVE",line):
started=True
if len(definition) > 0:
writeConfig()
definition=[]
continue
if started:
definition.append(line)
if len(definition) > 0:
writeConfig()
userTaskDirs=[]
def getUserTaskDirs():
rt=[]
taskdirs=glob.glob(os.path.join( basePath(),'lib','*task*'))
for task in taskdirs:
rt.append(task)
return rt
def checkAndAdd(file,names,ilist):
if not file.endswith('.h'):
return
match=False
for cmp in names:
#print("##check %s<->%s"%(f.lower(),cmp))
if file.lower() == cmp:
match=True
if not match:
return
ilist.append(file)
def genereateUserTasks(outfile):
includes=[]
for task in userTaskDirs:
#print("##taskdir=%s"%task)
base=os.path.basename(task)
includeNames=[base.lower()+".h",'gw'+base.lower()+'.h']
for f in os.listdir(task):
checkAndAdd(f,includeNames,includes)
includeData=""
for i in includes:
print("#task include %s"%i)
includeData+="#include <%s>\n"%i
writeFileIfChanged(outfile,includeData)
def generateEmbedded(elist,outFile):
content=""
for entry in elist:
content+="EMBED_GZ_FILE(\"%s\",%s,\"%s\");\n"%entry
writeFileIfChanged(outFile,content)
def getContentType(fn):
if (fn.endswith('.gz')):
fn=fn[0:-3]
if (fn.endswith('html')):
return "text/html"
if (fn.endswith('json')):
return "application/json"
if (fn.endswith('js')):
return "text/javascript"
if (fn.endswith('css')):
return "text/css"
return "application/octet-stream"
def getLibs():
base=os.path.join(basePath(),"lib")
rt=[]
for sd in os.listdir(base):
if sd == '..':
continue
if sd == '.':
continue
fn=os.path.join(base,sd)
if os.path.isdir(fn):
rt.append(sd)
EXTRAS=['generated']
for e in EXTRAS:
if not e in rt:
rt.append(e)
return rt
def joinFiles(target,pattern,dirlist):
flist=[]
for dir in dirlist:
fn=os.path.join(dir,pattern)
if os.path.exists(fn):
flist.append(fn)
current=False
if os.path.exists(target):
current=True
for f in flist:
if not isCurrent(f,target):
current=False
break
if current:
print("%s is up to date"%target)
return
print("creating %s"%target)
with gzip.open(target,"wb") as oh:
for fn in flist:
print("adding %s to %s"%(fn,target))
with open(fn,"rb") as rh:
shutil.copyfileobj(rh,oh)
OWNLIBS=getLibs()+["FS","WiFi"]
GLOBAL_INCLUDES=[]
def handleDeps(env):
#overwrite the GetProjectConfig
#to inject all our libs
oldGetProjectConfig=env.GetProjectConfig
def GetProjectConfigX(env):
rt=oldGetProjectConfig()
cenv="env:"+env['PIOENV']
libs=[]
for section,options in rt.as_tuple():
if section == cenv:
for key,values in options:
if key == 'lib_deps':
libs=values
mustUpdate=False
for lib in OWNLIBS:
if not lib in libs:
libs.append(lib)
mustUpdate=True
if mustUpdate:
update=[(cenv,[('lib_deps',libs)])]
rt.update(update)
return rt
env.AddMethod(GetProjectConfigX,"GetProjectConfig")
#store the list of all includes after we resolved
#the dependencies for our main project
#we will use them for all compilations afterwards
oldLibBuilder=env.ConfigureProjectLibBuilder
def ConfigureProjectLibBuilderX(env):
global GLOBAL_INCLUDES
project=oldLibBuilder()
#print("##ConfigureProjectLibBuilderX")
#pprint.pprint(project)
if project.depbuilders:
#print("##depbuilders %s"%",".join(map(lambda x: x.path,project.depbuilders)))
for db in project.depbuilders:
idirs=db.get_include_dirs()
for id in idirs:
if not id in GLOBAL_INCLUDES:
GLOBAL_INCLUDES.append(id)
return project
env.AddMethod(ConfigureProjectLibBuilderX,"ConfigureProjectLibBuilder")
def injectIncludes(env,node):
return env.Object(
node,
CPPPATH=env["CPPPATH"]+GLOBAL_INCLUDES
)
env.AddBuildMiddleware(injectIncludes)
def prebuild(env):
global userTaskDirs
print("#prebuild running")
if not checkDir():
sys.exit(1)
ldf_mode=env.GetProjectOption("lib_ldf_mode")
if ldf_mode == 'off':
print("##ldf off - own dependency handling")
handleDeps(env)
userTaskDirs=getUserTaskDirs()
mergedConfig=os.path.join(outPath(),os.path.basename(CFG_FILE))
generateMergedConfig(os.path.join(basePath(),CFG_FILE),mergedConfig,userTaskDirs)
compressFile(mergedConfig,mergedConfig+".gz")
generateCfg(mergedConfig,os.path.join(outPath(),CFG_INCLUDE),False)
generateCfg(mergedConfig,os.path.join(outPath(),CFG_INCLUDE_IMPL),True)
joinFiles(os.path.join(outPath(),INDEXJS+".gz"),INDEXJS,["web"]+userTaskDirs)
joinFiles(os.path.join(outPath(),INDEXCSS+".gz"),INDEXCSS,["web"]+userTaskDirs)
embedded=getEmbeddedFiles(env)
filedefs=[]
for ef in embedded:
print("#checking embedded file %s"%ef)
(dn,fn)=os.path.split(ef)
pureName=fn
if pureName.endswith('.gz'):
pureName=pureName[0:-3]
ct=getContentType(pureName)
usname=ef.replace('/','_').replace('.','_')
filedefs.append((pureName,usname,ct))
inFile=os.path.join(basePath(),"web",pureName)
if os.path.exists(inFile):
compressFile(inFile,ef)
else:
print("#WARNING: infile %s for %s not found"%(inFile,ef))
generateEmbedded(filedefs,os.path.join(outPath(),EMBEDDED_INCLUDE))
genereateUserTasks(os.path.join(outPath(), TASK_INCLUDE))
generateFile(os.path.join(basePath(),XDR_FILE),os.path.join(outPath(),XDR_INCLUDE),generateXdrMappings)
generateFile(os.path.join(basePath(),GROVE_CONFIG_IN),os.path.join(outPath(),GROVE_CONFIG),generateGroveDefs,inMode='r')
version="dev"+datetime.now().strftime("%Y%m%d")
env.Append(CPPDEFINES=[('GWDEVVERSION',version)])
def cleangenerated(source, target, env):
od=outPath()
if os.path.isdir(od):
print("#cleaning up %s"%od)
for f in os.listdir(od):
if f == "." or f == "..":
continue
fn=os.path.join(od,f)
os.unlink(f)
print("#prescript...")
prebuild(env)
board="PLATFORM_BOARD_%s"%env["BOARD"].replace("-","_").upper()
print("Board=#%s#"%board)
print("BuildFlags=%s"%(" ".join(env["BUILD_FLAGS"])))
env.Append(
LINKFLAGS=[ "-u", "custom_app_desc" ],
CPPDEFINES=[(board,"1")]
)
#script does not run on clean yet - maybe in the future
env.AddPostAction("clean",cleangenerated)

View File

@@ -493,6 +493,11 @@ double formatKnots(double cv)
return cv * 3600.0 / 1852.0; return cv * 3600.0 / 1852.0;
} }
double formatKmh(double cv)
{
return cv *3600.0 / 1000.0;
}
uint32_t mtr2nm(uint32_t m) uint32_t mtr2nm(uint32_t m)
{ {
return m / 1852; return m / 1852;
@@ -523,4 +528,4 @@ public:
}; };
static XWriter xwriter; static XWriter xwriter;
ARDUINOJSON_NAMESPACE::TextFormatter<XWriter> testWriter(xwriter); ARDUINOJSON_NAMESPACE::TextFormatter<XWriter> testWriter(xwriter);
#endif #endif

View File

@@ -129,6 +129,7 @@ double formatCourse(double cv);
double formatDegToRad(double deg); double formatDegToRad(double deg);
double formatWind(double cv); double formatWind(double cv);
double formatKnots(double cv); double formatKnots(double cv);
double formatKmh(double cv);
uint32_t mtr2nm(uint32_t m); uint32_t mtr2nm(uint32_t m);
double mtr2nm(double m); double mtr2nm(double m);
@@ -251,4 +252,4 @@ class GwBoatData{
}; };
#endif #endif

View File

@@ -27,18 +27,19 @@ class DummyConfig : public GwConfigInterface{
}; };
DummyConfig dummyConfig; DummyConfig dummyConfig;
String GwConfigHandler::toString() const{ void GwConfigHandler::logConfig(int level) const
String rt; {
rt+="Config: "; if (!logger->isActive(level))
for (int i=0;i<getNumConfig();i++){ return;
rt+=configs[i]->getName(); for (int i = 0; i < getNumConfig(); i++)
rt+="="; {
rt+=configs[i]->asString(); String v=configs[i]->asString();
rt+=", "; bool isChanged=v != configs[i]->getDefault();
} logger->logDebug(level, "Config[%s]%s='%s'", configs[i]->getName().c_str(),isChanged?"*":"", configs[i]->isSecret() ? "***" : configs[i]->asString().c_str());
return rt; if ((i%20) == 19) logger->flush();
} }
logger->flush();
}
String GwConfigHandler::toJson() const{ String GwConfigHandler::toJson() const{
String rt; String rt;
int num=getNumConfig(); int num=getNumConfig();
@@ -80,6 +81,9 @@ GwConfigHandler::~GwConfigHandler(){
bool GwConfigHandler::loadConfig(){ bool GwConfigHandler::loadConfig(){
prefs->begin(PREF_NAME,true); prefs->begin(PREF_NAME,true);
for (int i=0;i<getNumConfig();i++){ for (int i=0;i<getNumConfig();i++){
if (!prefs->isKey(configs[i]->getName().c_str())) {
continue;
}
String v=prefs->getString(configs[i]->getName().c_str(),configs[i]->getDefault()); String v=prefs->getString(configs[i]->getName().c_str(),configs[i]->getDefault());
configs[i]->value=v; configs[i]->value=v;
} }

View File

@@ -22,7 +22,7 @@ class GwConfigHandler: public GwConfigDefinitions{
void stopChanges(); void stopChanges();
bool updateValue(String name, String value); bool updateValue(String name, String value);
bool reset(); bool reset();
String toString() const; void logConfig(int level) const;
String toJson() const; String toJson() const;
String getString(const String name,const String defaultv="") const; String getString(const String name,const String defaultv="") const;
bool getBool(const String name,bool defaultv=false) const ; bool getBool(const String name,bool defaultv=false) const ;

View File

@@ -14,7 +14,7 @@ Files
* [platformio.ini](platformio.ini)<br> * [platformio.ini](platformio.ini)<br>
This file is completely optional. This file is completely optional.
You only need this if you want to You only need this if you want to
extend the base configuration - we add a dummy library here and define one additional build environment (board) extend the base configuration - we add a dummy library here and define additional build environments (boards)
* [GwExampleTask.h](GwExampleTask.h) the name of this include must match the name of the directory (ignoring case) with a "gw" in front. This file includes our special hardware definitions and registers our task at the core.<br> * [GwExampleTask.h](GwExampleTask.h) the name of this include must match the name of the directory (ignoring case) with a "gw" in front. This file includes our special hardware definitions and registers our task at the core.<br>
This registration can be done statically using [DECLARE_USERTASK](https://github.com/wellenvogel/esp32-nmea2000/blob/9b955d135d74937a60f2926e8bfb9395585ff8cd/lib/api/GwApi.h#L202) in the header file. <br> This registration can be done statically using [DECLARE_USERTASK](https://github.com/wellenvogel/esp32-nmea2000/blob/9b955d135d74937a60f2926e8bfb9395585ff8cd/lib/api/GwApi.h#L202) in the header file. <br>
As an alternative we just only register an [initialization function](https://github.com/wellenvogel/esp32-nmea2000/blob/9b955d135d74937a60f2926e8bfb9395585ff8cd/lib/exampletask/GwExampleTask.h#L19) using DECLARE_INITFUNCTION and later on register the task function itself via the [API](https://github.com/wellenvogel/esp32-nmea2000/blob/9b955d135d74937a60f2926e8bfb9395585ff8cd/lib/exampletask/GwExampleTask.cpp#L32).<br> As an alternative we just only register an [initialization function](https://github.com/wellenvogel/esp32-nmea2000/blob/9b955d135d74937a60f2926e8bfb9395585ff8cd/lib/exampletask/GwExampleTask.h#L19) using DECLARE_INITFUNCTION and later on register the task function itself via the [API](https://github.com/wellenvogel/esp32-nmea2000/blob/9b955d135d74937a60f2926e8bfb9395585ff8cd/lib/exampletask/GwExampleTask.cpp#L32).<br>
@@ -28,11 +28,13 @@ Files
* [GwExampleTaks.cpp](GwExampleTask.cpp) includes the implementation of our task. This tasks runs in an own thread - see the comments in the code. * [GwExampleTaks.cpp](GwExampleTask.cpp) includes the implementation of our task. This tasks runs in an own thread - see the comments in the code.
We can have as many cpp (and header files) as we need to structure our code. We can have as many cpp (and header files) as we need to structure our code.
* [config.json](config.json)<br> * [config.json](exampleConfig.json)<br>
This file allows to add some config definitions that are needed for our task. For the possible options have a look at the global [config.json](../../web/config.json). Be careful not to overwrite config defitions from the global file. A good practice wood be to prefix the names of definitions with parts of the library name. Always put them in a separate category so that they do not interfere with the system ones. This file allows to add some config definitions that are needed for our task. For the possible options have a look at the global [config.json](../../web/config.json). Be careful not to overwrite config defitions from the global file. A good practice wood be to prefix the names of definitions with parts of the library name. Always put them in a separate category so that they do not interfere with the system ones.
The defined config items can later be accessed in the code (see the example in [GwExampleTask.cpp](GwExampleTask.cpp)). The defined config items can later be accessed in the code (see the example in [GwExampleTask.cpp](GwExampleTask.cpp)).<br>
Starting from Version 20250305 you should normally not use this file name any more as those configs would be added for all build environments. Instead define a parameter _custom_config_ in your [platformio.ini](platformio.ini) for the environments you would like to add some configurations for. This parameter accepts a list of file names (relative to the project root, separated by ,).
* [index.js](index.js)<br> * [index.js](example.js)<br>
You can add javascript code that will contribute to the UI of the system. The WebUI provides a small API that allows you to "hook" into some functions to include your own parts of the UI. This includes adding new tabs, modifying/replacing the data display items, modifying the status display or accessing the config items. You can add javascript code that will contribute to the UI of the system. The WebUI provides a small API that allows you to "hook" into some functions to include your own parts of the UI. This includes adding new tabs, modifying/replacing the data display items, modifying the status display or accessing the config items.
For the API refer to [../../web/index.js](../../web/index.js#L2001). For the API refer to [../../web/index.js](../../web/index.js#L2001).
To start interacting just register for some events like api.EVENTS.init. You can check the capabilities you have defined to see if your task is active. To start interacting just register for some events like api.EVENTS.init. You can check the capabilities you have defined to see if your task is active.
@@ -46,10 +48,14 @@ Files
tools/testServer.py nnn http://x.x.x.x/api tools/testServer.py nnn http://x.x.x.x/api
``` ```
with nnn being the local port and x.x.x.x the address of a running system. Open `http://localhost:nnn` in your browser.<br> with nnn being the local port and x.x.x.x the address of a running system. Open `http://localhost:nnn` in your browser.<br>
After a change just start the compilation and reload the page. After a change just start the compilation and reload the page.<br>
Starting from Version 20250305 you should normally not use this file name any more as those js code would be added for all build environments. Instead define a parameter _custom_js_ in your [platformio.ini](platformio.ini) for the environments you would like to add the js code for. This parameter accepts a list of file names (relative to the project root, separated by ,). This will also allow you to skip the check for capabilities in your code.
* [index.css](index.css)<br> * [index.css](index.css)<br>
You can add own css to influence the styling of the display. You can add own css to influence the styling of the display.<br>
Starting from Version 20250305 you should normally not use this file name any more as those styles would be added for all build environments. Instead define a parameter _custom_css_ in your [platformio.ini](platformio.ini) for the environments you would like to add some styles for. This parameter accepts a list of file names (relative to the project root, separated by , or as multi line entry)
Interfaces Interfaces

View File

@@ -10,5 +10,9 @@ lib_deps =
build_flags= build_flags=
-D BOARD_TEST -D BOARD_TEST
${env.build_flags} ${env.build_flags}
custom_config=
lib/exampletask/exampleConfig.json
custom_js=lib/exampletask/example.js
custom_css=lib/exampletask/example.css
upload_port = /dev/esp32 upload_port = /dev/esp32
upload_protocol = esptool upload_protocol = esptool

View File

@@ -85,6 +85,7 @@ bool GwWifi::connectInternal(){
if (wifiClient->asBoolean()){ if (wifiClient->asBoolean()){
clientIsConnected=false; clientIsConnected=false;
LOG_DEBUG(GwLog::LOG,"creating wifiClient ssid=%s",wifiSSID->asString().c_str()); LOG_DEBUG(GwLog::LOG,"creating wifiClient ssid=%s",wifiSSID->asString().c_str());
WiFi.setAutoReconnect(false); //#102
wl_status_t rt=WiFi.begin(wifiSSID->asCString(),wifiPass->asCString()); wl_status_t rt=WiFi.begin(wifiSSID->asCString(),wifiPass->asCString());
LOG_DEBUG(GwLog::LOG,"wifiClient connect returns %d",(int)rt); LOG_DEBUG(GwLog::LOG,"wifiClient connect returns %d",(int)rt);
lastConnectStart=millis(); lastConnectStart=millis();
@@ -92,7 +93,8 @@ bool GwWifi::connectInternal(){
} }
return false; return false;
} }
#define RETRY_MILLIS 20000 //#102: we should have a wifi connect retry being > 30s - with some headroom
#define RETRY_MILLIS 40000
void GwWifi::loop(){ void GwWifi::loop(){
if (wifiClient->asBoolean()) if (wifiClient->asBoolean())
{ {

View File

@@ -351,8 +351,8 @@ private:
rmb.vmg rmb.vmg
); );
send(n2kMsg,msg.sourceId); send(n2kMsg,msg.sourceId);
SetN2kPGN129285(n2kMsg,sourceId,1,1,true,true,"default"); SetN2kRouteWPInfo(n2kMsg,sourceId,1,1,N2kdir_forward,"default");
AppendN2kPGN129285(n2kMsg,destinationId,rmb.destID,rmb.latitude,rmb.longitude); AppendN2kRouteWPInfo(n2kMsg,destinationId,rmb.destID,rmb.latitude,rmb.longitude);
send(n2kMsg,msg.sourceId); send(n2kMsg,msg.sourceId);
} }
} }
@@ -638,8 +638,8 @@ private:
for (int i=0;i< 3;i++){ for (int i=0;i< 3;i++){
if (msg.FieldLen(0)>0){ if (msg.FieldLen(0)>0){
Depth=atof(msg.Field(0)); Depth=atof(msg.Field(0));
char dt=msg.Field(i+1)[0]; char du=msg.Field(i+1)[0];
switch(dt){ switch(du){
case 'f': case 'f':
Depth=Depth/mToFeet; Depth=Depth/mToFeet;
break; break;
@@ -662,8 +662,9 @@ private:
//we can only send if we have a valid depth beloww tranducer //we can only send if we have a valid depth beloww tranducer
//to compute the offset //to compute the offset
if (! boatData->DBT->isValid()) return; if (! boatData->DBT->isValid()) return;
double offset=Depth-boatData->DBT->getData(); double dbt=boatData->DBT->getData();
if (offset >= 0 && dt == DBT){ double offset=Depth-dbt;
if (offset >= 0 && dt == DBK){
logger->logDebug(GwLog::DEBUG, "strange DBK - more depth then transducer %s", msg.line); logger->logDebug(GwLog::DEBUG, "strange DBK - more depth then transducer %s", msg.line);
return; return;
} }
@@ -675,8 +676,8 @@ private:
if (! boatData->DBS->update(Depth,msg.sourceId)) return; if (! boatData->DBS->update(Depth,msg.sourceId)) return;
} }
tN2kMsg n2kMsg; tN2kMsg n2kMsg;
SetN2kWaterDepth(n2kMsg,1,Depth,offset); SetN2kWaterDepth(n2kMsg,1,dbt,offset); //on the N2K side we always have depth below transducer
send(n2kMsg,msg.sourceId,(n2kMsg.PGN)+String((offset != N2kDoubleNA)?1:0)); send(n2kMsg,msg.sourceId,(n2kMsg.PGN)+String((offset >=0)?1:0));
} }
} }
} }

View File

@@ -267,21 +267,29 @@ private:
double DepthBelowTransducer; double DepthBelowTransducer;
double Offset; double Offset;
double Range; double Range;
double WaterDepth;
if (ParseN2kWaterDepth(N2kMsg, SID, DepthBelowTransducer, Offset, Range)) if (ParseN2kWaterDepth(N2kMsg, SID, DepthBelowTransducer, Offset, Range))
{ {
if (updateDouble(boatData->DBT, DepthBelowTransducer))
WaterDepth = DepthBelowTransducer + Offset;
updateDouble(boatData->DBS, WaterDepth);
updateDouble(boatData->DBT,DepthBelowTransducer);
tNMEA0183Msg NMEA0183Msg;
if (NMEA0183SetDPT(NMEA0183Msg, DepthBelowTransducer, Offset,talkerId))
{ {
SendMessage(NMEA0183Msg); tNMEA0183Msg NMEA0183Msg;
} bool offsetValid=true;
if (NMEA0183SetDBx(NMEA0183Msg, DepthBelowTransducer, Offset,talkerId)) if (N2kIsNA(Offset)) {
{ Offset=NMEA0183DoubleNA;
SendMessage(NMEA0183Msg); offsetValid=false;
}
if (NMEA0183SetDPT(NMEA0183Msg, DepthBelowTransducer, Offset, talkerId))
{
SendMessage(NMEA0183Msg);
}
if (offsetValid)
{
double WaterDepth = DepthBelowTransducer + Offset;
updateDouble(boatData->DBS, WaterDepth);
}
if (NMEA0183SetDBx(NMEA0183Msg, DepthBelowTransducer, Offset, talkerId))
{
SendMessage(NMEA0183Msg);
}
} }
} }
} }
@@ -528,6 +536,31 @@ private:
{ {
SendMessage(NMEA0183Msg); SendMessage(NMEA0183Msg);
} }
if (shouldSend && NMEA0183Reference == NMEA0183Wind_Apparent)
{
double wa = formatCourse(WindAngle);
if (!NMEA0183Msg.Init("VWR", talkerId))
return;
if (!NMEA0183Msg.AddDoubleField(( wa > 180 ) ? 360-wa : wa))
return;
if (!NMEA0183Msg.AddStrField(( wa >= 0 && wa <= 180) ? 'R' : 'L'))
return;
if (!NMEA0183Msg.AddDoubleField(formatKnots(WindSpeed)))
return;
if (!NMEA0183Msg.AddStrField("N"))
return;
if (!NMEA0183Msg.AddDoubleField(WindSpeed))
return;
if (!NMEA0183Msg.AddStrField("M"))
return;
if (!NMEA0183Msg.AddDoubleField(formatKmh(WindSpeed)))
return;
if (!NMEA0183Msg.AddStrField("K"))
return;
SendMessage(NMEA0183Msg);
}
} }
/* if (WindReference == N2kWind_Apparent && boatData->SOG->isValid()) /* if (WindReference == N2kWind_Apparent && boatData->SOG->isValid())

View File

@@ -1,4 +1,4 @@
#ifdef BOARD_OBP60S3 #if defined BOARD_OBP60S3 || defined BOARD_OBP40S3
/**************************************************** /****************************************************
AMS 5600 class for Arduino platform AMS 5600 class for Arduino platform

View File

@@ -0,0 +1,190 @@
#if defined BOARD_OBP60S3 || defined BOARD_OBP40S3
#include "BoatDataCalibration.h"
#include <cmath>
#include <math.h>
#include <unordered_map>
CalibrationDataList calibrationData;
std::unordered_map<std::string, TypeCalibData> CalibrationDataList::calibMap; // list of calibration data instances
void CalibrationDataList::readConfig(GwConfigHandler* config, GwLog* logger)
// Initial load of calibration data into internal list
// This method is called once at init phase of <obp60task> to read the configuration values
{
std::string instance;
double offset;
double slope;
double smooth;
String calInstance = "";
String calOffset = "";
String calSlope = "";
String calSmooth = "";
// Load user format configuration values
String lengthFormat = config->getString(config->lengthFormat); // [m|ft]
String distanceFormat = config->getString(config->distanceFormat); // [m|km|nm]
String speedFormat = config->getString(config->speedFormat); // [m/s|km/h|kn]
String windspeedFormat = config->getString(config->windspeedFormat); // [m/s|km/h|kn|bft]
String tempFormat = config->getString(config->tempFormat); // [K|C|F]
// Read calibration settings for data instances
for (int i = 0; i < MAX_CALIBRATION_DATA; i++) {
calInstance = "calInstance" + String(i + 1);
calOffset = "calOffset" + String(i + 1);
calSlope = "calSlope" + String(i + 1);
calSmooth = "calSmooth" + String(i + 1);
instance = std::string(config->getString(calInstance, "---").c_str());
if (instance == "---") {
LOG_DEBUG(GwLog::LOG, "no calibration data for instance no. %d", i + 1);
continue;
}
calibMap[instance] = { 0.0f, 1.0f, 1.0f, 0.0f, false };
offset = (config->getString(calOffset, "")).toFloat();
slope = (config->getString(calSlope, "")).toFloat();
smooth = (config->getString(calSmooth, "")).toInt(); // user input is int; further math is done with double
// Convert calibration values to internal standard formats
if (instance == "AWS" || instance == "TWS") {
if (windspeedFormat == "m/s") {
// No conversion needed
} else if (windspeedFormat == "km/h") {
offset /= 3.6; // Convert km/h to m/s
} else if (windspeedFormat == "kn") {
offset /= 1.94384; // Convert kn to m/s
} else if (windspeedFormat == "bft") {
offset *= 2 + (offset / 2); // Convert Bft to m/s (approx) -> to be improved
}
} else if (instance == "AWA" || instance == "COG" || instance == "TWA" || instance == "TWD" || instance == "HDM" || instance == "PRPOS" || instance == "RPOS") {
offset *= M_PI / 180; // Convert deg to rad
} else if (instance == "DBT") {
if (lengthFormat == "m") {
// No conversion needed
} else if (lengthFormat == "ft") {
offset /= 3.28084; // Convert ft to m
}
} else if (instance == "SOG" || instance == "STW") {
if (speedFormat == "m/s") {
// No conversion needed
} else if (speedFormat == "km/h") {
offset /= 3.6; // Convert km/h to m/s
} else if (speedFormat == "kn") {
offset /= 1.94384; // Convert kn to m/s
}
} else if (instance == "WTemp") {
if (tempFormat == "K" || tempFormat == "C") {
// No conversion needed
} else if (tempFormat == "F") {
offset *= 9.0 / 5.0; // Convert °F to K
slope *= 9.0 / 5.0; // Convert °F to K
}
}
// transform smoothing factor from {0.01..10} to {0.3..0.95} and invert for exponential smoothing formula
if (smooth <= 0) {
smooth = 0;
} else {
if (smooth > 10) {
smooth = 10;
}
smooth = 0.3 + ((smooth - 0.01) * (0.95 - 0.3) / (10 - 0.01));
}
smooth = 1 - smooth;
calibMap[instance].offset = offset;
calibMap[instance].slope = slope;
calibMap[instance].smooth = smooth;
calibMap[instance].isCalibrated = false;
LOG_DEBUG(GwLog::LOG, "calibration data: %s, offset: %f, slope: %f, smoothing: %f", instance.c_str(),
calibMap[instance].offset, calibMap[instance].slope, calibMap[instance].smooth);
}
LOG_DEBUG(GwLog::LOG, "all calibration data read");
}
void CalibrationDataList::calibrateInstance(GwApi::BoatValue* boatDataValue, GwLog* logger)
// Method to calibrate the boat data value
{
std::string instance = boatDataValue->getName().c_str();
double offset = 0;
double slope = 1.0;
double dataValue = 0;
std::string format = "";
if (calibMap.find(instance) == calibMap.end()) {
LOG_DEBUG(GwLog::DEBUG, "BoatDataCalibration: %s not in calibration list", instance.c_str());
return;
} else if (!boatDataValue->valid) { // no valid boat data value, so we don't want to apply calibration data
calibMap[instance].isCalibrated = false;
return;
} else {
offset = calibMap[instance].offset;
slope = calibMap[instance].slope;
dataValue = boatDataValue->value;
format = boatDataValue->getFormat().c_str();
LOG_DEBUG(GwLog::DEBUG, "BoatDataCalibration: %s: value: %f, format: %s", instance.c_str(), dataValue, format.c_str());
if (format == "formatWind") { // instance is of type angle
dataValue = (dataValue * slope) + offset;
dataValue = fmod(dataValue, 2 * M_PI);
if (dataValue > (M_PI)) {
dataValue -= (2 * M_PI);
} else if (dataValue < (M_PI * -1)) {
dataValue += (2 * M_PI);
}
} else if (format == "formatCourse") { // instance is of type direction
dataValue = (dataValue * slope) + offset;
dataValue = fmod(dataValue, 2 * M_PI);
if (dataValue < 0) {
dataValue += (2 * M_PI);
}
} else if (format == "kelvinToC") { // instance is of type temperature
dataValue = ((dataValue - 273.15) * slope) + offset + 273.15;
} else {
dataValue = (dataValue * slope) + offset;
}
calibMap[instance].isCalibrated = true;
boatDataValue->value = dataValue;
calibrationData.smoothInstance(boatDataValue, logger); // smooth the boat data value
calibMap[instance].value = boatDataValue->value; // store the calibrated + smoothed value in the list
LOG_DEBUG(GwLog::DEBUG, "BoatDataCalibration: %s: Offset: %f, Slope: %f, Result: %f", instance.c_str(), offset, slope, calibMap[instance].value);
}
}
void CalibrationDataList::smoothInstance(GwApi::BoatValue* boatDataValue, GwLog* logger)
// Method to smoothen the boat data value
{
static std::unordered_map<std::string, double> lastValue; // array for last values of smoothed boat data values
std::string instance = boatDataValue->getName().c_str();
double oldValue = 0;
double dataValue = boatDataValue->value;
double smoothFactor = 0;
if (!boatDataValue->valid) { // no valid boat data value, so we don't want to smoothen value
return;
} else if (calibMap.find(instance) == calibMap.end()) {
LOG_DEBUG(GwLog::DEBUG, "BoatDataCalibration: smooth factor for %s not found in calibration list", instance.c_str());
return;
} else {
smoothFactor = calibMap[instance].smooth;
if (lastValue.find(instance) != lastValue.end()) {
oldValue = lastValue[instance];
dataValue = oldValue + (smoothFactor * (dataValue - oldValue)); // exponential smoothing algorithm
}
lastValue[instance] = dataValue; // store the new value for next cycle; first time, store only the current value and return
boatDataValue->value = dataValue; // set the smoothed value to the boat data value
}
}
#endif

View File

@@ -0,0 +1,34 @@
// Functions lib for data instance calibration
#ifndef _BOATDATACALIBRATION_H
#define _BOATDATACALIBRATION_H
// #include "Pagedata.h"
#include "GwApi.h"
#include <string>
#include <unordered_map>
#define MAX_CALIBRATION_DATA 3 // maximum number of calibration data instances
typedef struct {
double offset; // calibration offset
double slope; // calibration slope
double smooth; // smoothing factor
double value; // calibrated data value
bool isCalibrated; // is data instance value calibrated?
} TypeCalibData;
class CalibrationDataList {
public:
static std::unordered_map<std::string, TypeCalibData> calibMap; // list of calibration data instances
void readConfig(GwConfigHandler* config, GwLog* logger);
void calibrateInstance(GwApi::BoatValue* boatDataValue, GwLog* logger);
void smoothInstance(GwApi::BoatValue* boatDataValue, GwLog* logger);
private:
};
extern CalibrationDataList calibrationData; // this list holds all calibration data
#endif

View File

@@ -2,5 +2,5 @@ Craete new page for OBP60
1. Create page under /lib/obp60task/PageXXXX.cpp 1. Create page under /lib/obp60task/PageXXXX.cpp
2. Set page name in PageXXXX.cpp on file name 2. Set page name in PageXXXX.cpp on file name
3. Register new page in /lib/obp60task/obp60task.cpp line 242 (registerAllPages) 3. Register new page in /lib/obp60task/obp60task.cpp line 242 (registerAllPages)
4. Add new page in /lib/obp60task/config.json for each page type or add new page to gen_set.pl and run it to auto-generate the relevant section of config.json 4. Add new page in /lib/obp60task/config.json for each page type or add new page to gen_set.py and run it to auto-generate the relevant section of config.json

View File

@@ -0,0 +1,25 @@
/*
Generic graphics functions
*/
#include <math.h>
#include "Graphics.h"
Point rotatePoint(const Point& origin, const Point& p, double angle) {
// rotate poind around origin by degrees
Point rotated;
double phi = angle * M_PI / 180.0;
double dx = p.x - origin.x;
double dy = p.y - origin.y;
rotated.x = origin.x + cos(phi) * dx - sin(phi) * dy;
rotated.y = origin.y + sin(phi) * dx + cos(phi) * dy;
return rotated;
}
std::vector<Point> rotatePoints(const Point& origin, const std::vector<Point>& pts, double angle) {
std::vector<Point> rotatedPoints;
for (const auto& p : pts) {
rotatedPoints.push_back(rotatePoint(origin, p, angle));
}
return rotatedPoints;
}

17
lib/obp60task/Graphics.h Normal file
View File

@@ -0,0 +1,17 @@
#pragma once
#include <vector>
struct Point {
double x;
double y;
};
struct Rect {
double x;
double y;
double w;
double h;
};
Point rotatePoint(const Point& origin, const Point& p, double angle);
std::vector<Point> rotatePoints(const Point& origin, const std::vector<Point>& pts, double angle);

View File

@@ -4,7 +4,6 @@
#include "GwApi.h" #include "GwApi.h"
#include "OBP60Hardware.h" #include "OBP60Hardware.h"
class Color{ class Color{
public: public:
uint8_t r; uint8_t r;
@@ -33,6 +32,7 @@ static Color COLOR_BLACK=Color(0,0,0);
Color setBrightness(const Color &color,uint8_t brightness); Color setBrightness(const Color &color,uint8_t brightness);
enum BacklightMode {OFF, ON, SUN, BUS, TIME, KEY};
class LedInterface { class LedInterface {
private: private:
@@ -92,5 +92,4 @@ class LedTaskData{
//task function //task function
void createSpiLedTask(LedTaskData *param); void createSpiLedTask(LedTaskData *param);
#endif
#endif

View File

@@ -1,9 +1,6 @@
#ifdef BOARD_OBP60S3 #if defined BOARD_OBP60S3 || defined BOARD_OBP40S3
#include <Arduino.h> #include <Arduino.h>
#define FASTLED_ALL_PINS_HARDWARE_SPI
#define FASTLED_ESP32_SPI_BUS FSPI
#define FASTLED_ESP32_FLASH_LOCK 1
#include <PCF8574.h> // Driver for PCF8574 output modul from Horter #include <PCF8574.h> // Driver for PCF8574 output modul from Horter
#include <Wire.h> // I2C #include <Wire.h> // I2C
#include <RTClib.h> // Driver for DS1388 RTC #include <RTClib.h> // Driver for DS1388 RTC
@@ -11,19 +8,23 @@
#include "Pagedata.h" #include "Pagedata.h"
#include "OBP60Hardware.h" #include "OBP60Hardware.h"
#include "OBP60Extensions.h" #include "OBP60Extensions.h"
#include "imglib.h"
// Character sets // Character sets
#include "Ubuntu_Bold8pt7b.h" #include "fonts/DSEG7Classic-BoldItalic16pt7b.h"
#include "Ubuntu_Bold10pt7b.h" #include "fonts/DSEG7Classic-BoldItalic20pt7b.h"
#include "Ubuntu_Bold12pt7b.h" #include "fonts/DSEG7Classic-BoldItalic26pt7b.h"
#include "Ubuntu_Bold16pt7b.h" #include "fonts/DSEG7Classic-BoldItalic30pt7b.h"
#include "Ubuntu_Bold20pt7b.h" #include "fonts/DSEG7Classic-BoldItalic42pt7b.h"
#include "Ubuntu_Bold32pt7b.h" #include "fonts/DSEG7Classic-BoldItalic60pt7b.h"
#include "DSEG7Classic-BoldItalic16pt7b.h" #include "fonts/Ubuntu_Bold8pt8b.h"
#include "DSEG7Classic-BoldItalic20pt7b.h" #include "fonts/Ubuntu_Bold10pt8b.h"
#include "DSEG7Classic-BoldItalic30pt7b.h" #include "fonts/Ubuntu_Bold12pt8b.h"
#include "DSEG7Classic-BoldItalic42pt7b.h" #include "fonts/Ubuntu_Bold16pt8b.h"
#include "DSEG7Classic-BoldItalic60pt7b.h" #include "fonts/Ubuntu_Bold20pt8b.h"
#include "fonts/Ubuntu_Bold32pt8b.h"
#include "fonts/Atari16px8b.h" // Key label font
#include "fonts/IBM8x8px.h"
// E-Ink Display // E-Ink Display
#define GxEPD_WIDTH 400 // Display width #define GxEPD_WIDTH 400 // Display width
@@ -60,6 +61,16 @@ GxEPD2_BW<GxEPD2_420_SE0420NQ04, GxEPD2_420_SE0420NQ04::HEIGHT> & getdisplay(){r
// Horter I2C moduls // Horter I2C moduls
PCF8574 pcf8574_Out(PCF8574_I2C_ADDR1); // First digital output modul PCF8574 from Horter PCF8574 pcf8574_Out(PCF8574_I2C_ADDR1); // First digital output modul PCF8574 from Horter
// FRAM
Adafruit_FRAM_I2C fram;
bool hasFRAM = false;
// SD Card
#ifdef BOARD_OBP40S3
sdmmc_card_t *sdcard;
#endif
bool hasSDCard = false;
// Global vars // Global vars
bool blinkingLED = false; // Enable / disable blinking flash LED bool blinkingLED = false; // Enable / disable blinking flash LED
bool statusLED = false; // Actual status of flash LED on/off bool statusLED = false; // Actual status of flash LED on/off
@@ -67,22 +78,120 @@ bool statusBacklightLED = false;// Actual status of flash LED on/off
int uvDuration = 0; // Under voltage duration in n x 100ms int uvDuration = 0; // Under voltage duration in n x 100ms
RTC_DATA_ATTR uint8_t RTC_lastpage; // Remember last page while deep sleeping
LedTaskData *ledTaskData=nullptr; LedTaskData *ledTaskData=nullptr;
void hardwareInit() void hardwareInit(GwApi *api)
{ {
GwLog *logger = api->getLogger();
GwConfigHandler *config = api->getConfig();
Wire.begin(); Wire.begin();
// Init PCF8574 digital outputs // Init PCF8574 digital outputs
Wire.setClock(I2C_SPEED); // Set I2C clock on 10 kHz Wire.setClock(I2C_SPEED); // Set I2C clock on 10 kHz
if(pcf8574_Out.begin()){ // Initialize PCF8574 if(pcf8574_Out.begin()){ // Initialize PCF8574
pcf8574_Out.write8(255); // Clear all outputs pcf8574_Out.write8(255); // Clear all outputs
} }
fram = Adafruit_FRAM_I2C();
if (esp_reset_reason() == ESP_RST_POWERON) {
// help initialize FRAM
logger->logDebug(GwLog::LOG, "Delaying I2C init for 250ms due to cold boot");
delay(250);
}
// FRAM (e.g. MB85RC256V)
if (fram.begin(FRAM_I2C_ADDR)) {
hasFRAM = true;
uint16_t manufacturerID;
uint16_t productID;
fram.getDeviceID(&manufacturerID, &productID);
// Boot counter
uint8_t framcounter = fram.read(0x0000);
fram.write(0x0000, framcounter+1);
logger->logDebug(GwLog::LOG, "FRAM detected: 0x%04x/0x%04x (counter=%d)", manufacturerID, productID, framcounter);
}
else {
hasFRAM = false;
logger->logDebug(GwLog::LOG, "NO FRAM detected");
}
// SD Card
hasSDCard = false;
#ifdef BOARD_OBP40S3
if (config->getBool(config->useSDCard)) {
esp_err_t ret;
sdmmc_host_t host = SDSPI_HOST_DEFAULT();
host.slot = SPI3_HOST;
logger->logDebug(GwLog::DEBUG, "SDSPI_HOST: max_freq_khz=%d" , host.max_freq_khz);
spi_bus_config_t bus_cfg = {
.mosi_io_num = SD_SPI_MOSI,
.miso_io_num = SD_SPI_MISO,
.sclk_io_num = SD_SPI_CLK,
.quadwp_io_num = -1,
.quadhd_io_num = -1,
.max_transfer_sz = 4000,
};
ret = spi_bus_initialize((spi_host_device_t) host.slot, &bus_cfg, SDSPI_DEFAULT_DMA);
if (ret != ESP_OK) {
logger->logDebug(GwLog::ERROR, "Failed to initialize SPI bus for SD card");
} else {
sdspi_device_config_t slot_config = SDSPI_DEVICE_CONFIG_DEFAULT();
slot_config.gpio_cs = SD_SPI_CS;
slot_config.host_id = (spi_host_device_t) host.slot;
esp_vfs_fat_sdmmc_mount_config_t mount_config = {
.format_if_mount_failed = false,
.max_files = 5,
.allocation_unit_size = 16 * 1024
};
ret = esp_vfs_fat_sdspi_mount(MOUNT_POINT, &host, &slot_config, &mount_config, &sdcard);
if (ret != ESP_OK) {
if (ret == ESP_FAIL) {
logger->logDebug(GwLog::ERROR, "Failed to mount SD card filesystem");
} else {
// ret == 263 could be not powered up yet
logger->logDebug(GwLog::ERROR, "Failed to initialize SD card (error #%d)", ret);
}
} else {
logger->logDebug(GwLog::LOG, "SD card filesystem mounted at '%s'", MOUNT_POINT);
hasSDCard = true;
}
}
if (hasSDCard) {
// read some stats
String features = "";
if (sdcard->is_mem) features += "MEM "; // Memory card
if (sdcard->is_sdio) features += "IO "; // IO Card
if (sdcard->is_mmc) features += "MMC "; // MMC Card
if (sdcard->is_ddr) features += "DDR ";
// if (sdcard->is_uhs1) features += "UHS-1 ";
// ext_csd. Extended information
// uint8_t rev, uint8_t power_class
logger->logDebug(GwLog::LOG, "SD card features: %s", features);
logger->logDebug(GwLog::LOG, "SD card size: %lluMB", ((uint64_t) sdcard->csd.capacity) * sdcard->csd.sector_size / (1024 * 1024));
}
}
#endif
} }
void startLedTask(GwApi *api){ void powerInit(String powermode) {
ledTaskData=new LedTaskData(api); // Max Power | Only 5.0V | Min Power
createSpiLedTask(ledTaskData); if (powermode == "Max Power" || powermode == "Only 5.0V") {
#ifdef HARDWARE_V21
setPortPin(OBP_POWER_50, true); // Power on 5.0V rail
#endif
#ifdef BOARD_OBP40S3
setPortPin(OBP_POWER_EPD, true);// Power on ePaper display
setPortPin(OBP_POWER_SD, true); // Power on SD card
#endif
} else { // Min Power
#ifdef HARDWARE_V21
setPortPin(OBP_POWER_50, false); // Power off 5.0V rail
#endif
#ifdef BOARD_OBP40S3
setPortPin(OBP_POWER_EPD, false);// Power off ePaper display
setPortPin(OBP_POWER_SD, false); // Power off SD card
#endif
}
} }
void setPortPin(uint pin, bool value){ void setPortPin(uint pin, bool value){
@@ -95,6 +204,66 @@ void togglePortPin(uint pin){
digitalWrite(pin, !digitalRead(pin)); digitalWrite(pin, !digitalRead(pin));
} }
void startLedTask(GwApi *api){
ledTaskData=new LedTaskData(api);
createSpiLedTask(ledTaskData);
}
uint8_t getLastPage() {
return RTC_lastpage;
}
#ifdef BOARD_OBP60S3
void deepSleep(CommonData &common){
RTC_lastpage = common.data.actpage - 1;
// Switch off all power lines
setPortPin(OBP_BACKLIGHT_LED, false); // Backlight Off
setFlashLED(false); // Flash LED Off
buzzer(TONE4, 20); // Buzzer tone 4kHz 20ms
// Shutdown EInk display
getdisplay().setFullWindow(); // Set full Refresh
getdisplay().fillScreen(common.bgcolor); // Clear screen
getdisplay().setTextColor(common.fgcolor);
getdisplay().setFont(&Ubuntu_Bold20pt8b);
getdisplay().setCursor(85, 150);
getdisplay().print("Sleep Mode");
getdisplay().setFont(&Ubuntu_Bold8pt8b);
getdisplay().setCursor(65, 175);
getdisplay().print("To wake up press key and wait 5s");
getdisplay().nextPage(); // Update display contents
getdisplay().powerOff(); // Display power off
setPortPin(OBP_POWER_50, false); // Power off ePaper display
// Stop system
esp_deep_sleep_start(); // Deep Sleep with weakup via touch pin
}
#endif
#ifdef BOARD_OBP40S3
// Deep sleep funktion
void deepSleep(CommonData &common){
RTC_lastpage = common.data.actpage - 1;
// Switch off all power lines
setPortPin(OBP_BACKLIGHT_LED, false); // Backlight Off
setFlashLED(false); // Flash LED Off
// Shutdown EInk display
getdisplay().setFullWindow(); // Set full Refresh
//getdisplay().setPartialWindow(0, 0, getdisplay().width(), getdisplay().height()); // Set partial update
getdisplay().fillScreen(common.bgcolor); // Clear screen
getdisplay().setTextColor(common.fgcolor);
getdisplay().setFont(&Ubuntu_Bold20pt8b);
getdisplay().setCursor(85, 150);
getdisplay().print("Sleep Mode");
getdisplay().setFont(&Ubuntu_Bold8pt8b);
getdisplay().setCursor(65, 175);
getdisplay().print("To wake up press wheel and wait 5s");
getdisplay().nextPage(); // Partial update
getdisplay().powerOff(); // Display power off
setPortPin(OBP_POWER_EPD, false); // Power off ePaper display
setPortPin(OBP_POWER_SD, false); // Power off SD card
// Stop system
esp_deep_sleep_start(); // Deep Sleep with weakup via GPIO pin
}
#endif
// Valid colors see hue // Valid colors see hue
Color colorMapping(const String &colorString){ Color colorMapping(const String &colorString){
Color color = COLOR_RED; Color color = COLOR_RED;
@@ -108,6 +277,21 @@ Color colorMapping(const String &colorString){
return color; return color;
} }
BacklightMode backlightMapping(const String &backlightString) {
static std::map<String, BacklightMode> const table = {
{"Off", BacklightMode::OFF},
{"Control by Bus", BacklightMode::BUS},
{"Control by Time", BacklightMode::TIME},
{"Control by Key", BacklightMode::KEY},
{"On", BacklightMode::ON},
};
auto it = table.find(backlightString);
if (it != table.end()) {
return it->second;
}
return BacklightMode::OFF;
}
// All defined colors see pixeltypes.h in FastLED lib // All defined colors see pixeltypes.h in FastLED lib
void setBacklightLED(uint brightness, const Color &color){ void setBacklightLED(uint brightness, const Color &color){
if (ledTaskData == nullptr) return; if (ledTaskData == nullptr) return;
@@ -179,6 +363,99 @@ String xdrDelete(String input){
return input; return input;
} }
void fillPoly4(const std::vector<Point>& p4, uint16_t color) {
getdisplay().fillTriangle(p4[0].x, p4[0].y, p4[1].x, p4[1].y, p4[2].x, p4[2].y, color);
getdisplay().fillTriangle(p4[0].x, p4[0].y, p4[2].x, p4[2].y, p4[3].x, p4[3].y, color);
}
void drawPoly(const std::vector<Point>& points, uint16_t color) {
size_t polysize = points.size();
for (size_t i = 0; i < polysize - 1; i++) {
getdisplay().drawLine(points[i].x, points[i].y, points[i+1].x, points[i+1].y, color);
}
// close path
getdisplay().drawLine(points[polysize-1].x, points[polysize-1].y, points[0].x, points[0].y, color);
}
// Split string into words, whitespace separated
std::vector<String> split(const String &s) {
std::vector<String> words;
String word = "";
for (size_t i = 0; i < s.length(); i++) {
if (s[i] == ' ' || s[i] == '\t' || s[i] == '\r' || s[i] == '\n') {
if (word.length() > 0) {
words.push_back(word);
word = "";
}
} else {
word += s[i];
}
}
if (word.length() > 0) {
words.push_back(word);
}
return words;
}
// Wordwrap single line, monospaced font
std::vector<String> wordwrap(String &line, uint16_t maxwidth) {
std::vector<String> lines;
std::vector<String> words = split(line);
String currentLine = "";
for (const auto& word : words) {
if (currentLine.length() + word.length() + 1 > maxwidth) {
if (currentLine.length() > 0) {
lines.push_back(currentLine);
currentLine = "";
}
}
if (currentLine.length() > 0) {
currentLine += " ";
}
currentLine += word;
}
if (currentLine.length() > 0) {
lines.push_back(currentLine);
}
return lines;
}
// Draw centered text
void drawTextCenter(int16_t cx, int16_t cy, String text) {
int16_t x1, y1;
uint16_t w, h;
getdisplay().getTextBounds(text, 0, 150, &x1, &y1, &w, &h);
getdisplay().setCursor(cx - w / 2, cy + h / 2);
getdisplay().print(text);
}
// Draw right aligned text
void drawTextRalign(int16_t x, int16_t y, String text) {
int16_t x1, y1;
uint16_t w, h;
getdisplay().getTextBounds(text, 0, 150, &x1, &y1, &w, &h);
getdisplay().setCursor(x - w, y);
getdisplay().print(text);
}
// Draw text inside box, normal or inverted
void drawTextBoxed(Rect box, String text, uint16_t fg, uint16_t bg, bool inverted, bool border) {
if (inverted) {
getdisplay().fillRect(box.x, box.y, box.w, box.h, fg);
getdisplay().setTextColor(bg);
} else {
if (border) {
getdisplay().fillRect(box.x + 1, box.y + 1, box.w - 2, box.h - 2, bg);
getdisplay().drawRect(box.x, box.y, box.w, box.h, fg);
}
getdisplay().setTextColor(fg);
}
uint16_t border_offset = box.h / 4; // 25% of box height
getdisplay().setCursor(box.x + border_offset, box.y + box.h - border_offset);
getdisplay().print(text);
getdisplay().setTextColor(fg);
}
// Show a triangle for trend direction high (x, y is the left edge) // Show a triangle for trend direction high (x, y is the left edge)
void displayTrendHigh(int16_t x, int16_t y, uint16_t size, uint16_t color){ void displayTrendHigh(int16_t x, int16_t y, uint16_t size, uint16_t color){
getdisplay().fillTriangle(x, y, x+size*2, y, x+size, y-size*2, color); getdisplay().fillTriangle(x, y, x+size*2, y, x+size, y-size*2, color);
@@ -203,20 +480,12 @@ void displayHeader(CommonData &commonData, GwApi::BoatValue *date, GwApi::BoatVa
static unsigned long tcpClTxOld = 0; static unsigned long tcpClTxOld = 0;
static unsigned long n2kRxOld = 0; static unsigned long n2kRxOld = 0;
static unsigned long n2kTxOld = 0; static unsigned long n2kTxOld = 0;
int textcolor = GxEPD_BLACK;
if(commonData.config->getBool(commonData.config->statusLine) == true){ if(commonData.config->getBool(commonData.config->statusLine) == true){
if(commonData.config->getString(commonData.config->displaycolor) == "Normal"){
textcolor = GxEPD_BLACK;
}
else{
textcolor = GxEPD_WHITE;
}
// Show status info // Show status info
getdisplay().setTextColor(textcolor); getdisplay().setTextColor(commonData.fgcolor);
getdisplay().setFont(&Ubuntu_Bold8pt7b); getdisplay().setFont(&Ubuntu_Bold8pt8b);
getdisplay().setCursor(0, 15); getdisplay().setCursor(0, 15);
if(commonData.status.wifiApOn){ if(commonData.status.wifiApOn){
getdisplay().print(" AP "); getdisplay().print(" AP ");
@@ -250,52 +519,213 @@ void displayHeader(CommonData &commonData, GwApi::BoatValue *date, GwApi::BoatVa
usbRxOld = commonData.status.usbRx; usbRxOld = commonData.status.usbRx;
usbTxOld = commonData.status.usbTx; usbTxOld = commonData.status.usbTx;
// Heartbeat as dot #ifdef HARDWARE_V21
getdisplay().setTextColor(textcolor); // Display key lock status
getdisplay().setFont(&Ubuntu_Bold32pt7b); if (commonData.keylock) {
getdisplay().setCursor(205, 14); getdisplay().drawXBitmap(170, 1, lock_bits, icon_width, icon_height, commonData.fgcolor);
if(heartbeat == true){ } else {
getdisplay().print("."); getdisplay().drawXBitmap(166, 1, swipe_bits, swipe_width, swipe_height, commonData.fgcolor);
} }
else{ #endif
getdisplay().print(" "); #ifdef LIPO_ACCU_1200
if (commonData.data.BatteryChargeStatus == 1) {
getdisplay().drawXBitmap(170, 1, battery_loading_bits, battery_width, battery_height, commonData.fgcolor);
} else {
#ifdef VOLTAGE_SENSOR
if (commonData.data.batteryLevelLiPo < 10) {
getdisplay().drawXBitmap(170, 1, battery_0_bits, battery_width, battery_height, commonData.fgcolor);
} else if (commonData.data.batteryLevelLiPo < 25) {
getdisplay().drawXBitmap(170, 1, battery_25_bits, battery_width, battery_height, commonData.fgcolor);
} else if (commonData.data.batteryLevelLiPo < 50) {
getdisplay().drawXBitmap(170, 1, battery_50_bits, battery_width, battery_height, commonData.fgcolor);
} else if (commonData.data.batteryLevelLiPo < 75) {
getdisplay().drawXBitmap(170, 1, battery_75_bits, battery_width, battery_height, commonData.fgcolor);
} else {
getdisplay().drawXBitmap(170, 1, battery_100_bits, battery_width, battery_height, commonData.fgcolor);
}
#endif // VOLTAGE_SENSOR
} }
heartbeat = !heartbeat; #endif // LIPO_ACCU_1200
// Heartbeat as page number
if (heartbeat) {
getdisplay().setTextColor(commonData.bgcolor);
getdisplay().fillRect(201, 0, 23, 19, commonData.fgcolor);
} else {
getdisplay().setTextColor(commonData.fgcolor);
getdisplay().drawRect(201, 0, 23, 19, commonData.fgcolor);
}
getdisplay().setFont(&Ubuntu_Bold8pt8b);
drawTextCenter(211, 9, String(commonData.data.actpage));
heartbeat = !heartbeat;
// Date and time // Date and time
getdisplay().setTextColor(textcolor); String fmttype = commonData.config->getString(commonData.config->dateFormat);
getdisplay().setFont(&Ubuntu_Bold8pt7b); String timesource = commonData.config->getString(commonData.config->timeSource);
double tz = commonData.config->getString(commonData.config->timeZone).toDouble();
getdisplay().setTextColor(commonData.fgcolor);
getdisplay().setFont(&Ubuntu_Bold8pt8b);
getdisplay().setCursor(230, 15); getdisplay().setCursor(230, 15);
// Show date and time if date present if (timesource == "RTC" or timesource == "iRTC") {
if(date->valid == true){ // TODO take DST into account
String acttime = formatValue(time, commonData).svalue; if (commonData.data.rtcValid) {
acttime = acttime.substring(0, 5); time_t tv = mktime(&commonData.data.rtcTime) + (int)(tz * 3600);
String actdate = formatValue(date, commonData).svalue; struct tm *local_tm = localtime(&tv);
getdisplay().print(acttime); getdisplay().print(formatTime('m', local_tm->tm_hour, local_tm->tm_min, 0));
getdisplay().print(" "); getdisplay().print(" ");
getdisplay().print(actdate); getdisplay().print(formatDate(fmttype, local_tm->tm_year + 1900, local_tm->tm_mon + 1, local_tm->tm_mday));
getdisplay().print(" "); getdisplay().print(" ");
if(commonData.config->getInt(commonData.config->timeZone) == 0){ getdisplay().print(tz == 0 ? "UTC" : "LOT");
getdisplay().print("UTC"); } else {
} drawTextRalign(396, 15, "RTC invalid");
else{
getdisplay().print("LOT");
} }
} }
else{ else if (timesource == "GPS") {
if(commonData.config->getBool(commonData.config->useSimuData) == true){ // Show date and time if date present
getdisplay().print("12:00 01.01.2024 LOT"); if(date->valid == true){
String acttime = formatValue(time, commonData).svalue;
acttime = acttime.substring(0, 5);
String actdate = formatValue(date, commonData).svalue;
getdisplay().print(acttime);
getdisplay().print(" ");
getdisplay().print(actdate);
getdisplay().print(" ");
getdisplay().print(tz == 0 ? "UTC" : "LOT");
} }
else{ else{
getdisplay().print("No GPS data"); if(commonData.config->getBool(commonData.config->useSimuData) == true){
getdisplay().print("12:00 01.01.2024 LOT");
}
else{
drawTextRalign(396, 15, "No GPS data");
}
} }
} // timesource == "GPS"
else {
getdisplay().print("No time source");
} }
} }
} }
void displayFooter(CommonData &commonData) {
getdisplay().setFont(&Atari16px);
getdisplay().setTextColor(commonData.fgcolor);
#ifdef HARDWARE_V21
// Frame around key icon area
if (! commonData.keylock) {
// horizontal elements
const uint16_t top = 280;
const uint16_t bottom = 299;
// horizontal stub lines
getdisplay().drawLine(commonData.keydata[0].x, top, commonData.keydata[0].x+10, top, commonData.fgcolor);
for (int i = 1; i <= 5; i++) {
getdisplay().drawLine(commonData.keydata[i].x-10, top, commonData.keydata[i].x+10, top, commonData.fgcolor);
}
getdisplay().drawLine(commonData.keydata[5].x + commonData.keydata[5].w - 10, top, commonData.keydata[5].x + commonData.keydata[5].w + 1, top, commonData.fgcolor);
// vertical key separators
for (int i = 0; i <= 4; i++) {
getdisplay().drawLine(commonData.keydata[i].x + commonData.keydata[i].w, top, commonData.keydata[i].x + commonData.keydata[i].w, bottom, commonData.fgcolor);
}
for (int i = 0; i < 6; i++) {
uint16_t x, y;
if (commonData.keydata[i].label.length() > 0) {
// check if icon is enabled
String icon_name = commonData.keydata[i].label.substring(1);
if (commonData.keydata[i].label[0] == '#') {
if (iconmap.find(icon_name) != iconmap.end()) {
x = commonData.keydata[i].x + (commonData.keydata[i].w - icon_width) / 2;
y = commonData.keydata[i].y + (commonData.keydata[i].h - icon_height) / 2;
getdisplay().drawXBitmap(x, y, iconmap[icon_name], icon_width, icon_height, commonData.fgcolor);
} else {
// icon is missing, use name instead
x = commonData.keydata[i].x + commonData.keydata[i].w / 2;
y = commonData.keydata[i].y + commonData.keydata[i].h / 2;
drawTextCenter(x, y, icon_name);
}
} else {
x = commonData.keydata[i].x + commonData.keydata[i].w / 2;
y = commonData.keydata[i].y + commonData.keydata[i].h / 2;
drawTextCenter(x, y, commonData.keydata[i].label);
}
}
}
} else {
getdisplay().setCursor(65, 295);
getdisplay().print("Press 1 and 6 fast to unlock keys");
}
#endif
#ifdef BOARD_OBP40S3
// grapical page indicator
static const uint16_t r = 5;
static const uint16_t space = 4;
uint16_t w = commonData.data.maxpage * r * 2 + (commonData.data.maxpage - 1) * space;
uint16_t x0 = (GxEPD_WIDTH - w) / 2 + r * 2;
for (int i = 0; i < commonData.data.maxpage; i++) {
if (i == (commonData.data.actpage - 1)) {
getdisplay().fillCircle(x0 + i * (r * 2 + space), 290, r, commonData.fgcolor);
} else {
getdisplay().drawCircle(x0 + i * (r * 2 + space), 290, r, commonData.fgcolor);
}
}
// key indicators
// left side = top key "menu"
getdisplay().drawLine(0, 280, 10, 280, commonData.fgcolor);
getdisplay().drawLine(55, 280, 65, 280, commonData.fgcolor);
getdisplay().drawLine(65, 280, 65, 299, commonData.fgcolor);
drawTextCenter(33, 291, commonData.keydata[0].label);
// right side = bottom key "exit"
getdisplay().drawLine(390, 280, 399, 280, commonData.fgcolor);
getdisplay().drawLine(335, 280, 345, 280, commonData.fgcolor);
getdisplay().drawLine(335, 280, 335, 399, commonData.fgcolor);
drawTextCenter(366, 291, commonData.keydata[1].label);
#endif
}
// Alarm overlay, to be drawn as very last draw operation
void displayAlarm(CommonData &commonData) {
const uint16_t x = 50; // overlay area
const uint16_t y = 100;
const uint16_t w = 300;
const uint16_t h = 150;
getdisplay().setFont(&Atari16px);
getdisplay().setTextColor(commonData.fgcolor);
// overlay
getdisplay().drawRect(x, y, w, h, commonData.fgcolor);
getdisplay().fillRect(x + 1, y + 1, w - 1, h - 1, commonData.bgcolor);
getdisplay().drawRect(x + 3, y + 3, w - 5, h - 5, commonData.fgcolor);
// exclamation icon in left top corner
getdisplay().drawXBitmap(x + 16, y + 16, exclamation_bits, exclamation_width, exclamation_height, commonData.fgcolor);
// title
getdisplay().setCursor(x + 64, y + 30);
getdisplay().print("A L A R M");
getdisplay().setCursor(x + 64, y + 48);
getdisplay().print("#" + commonData.alarm.id);
getdisplay().print(" from ");
getdisplay().print(commonData.alarm.source);
// message, but maximum 4 lines
std::vector<String> lines = wordwrap (commonData.alarm.message, w - 16 - 8 / 8);
int n = 0;
for (const auto& l : lines) {
getdisplay().setCursor(x + 16, y + 80 + n);
getdisplay().print(l);
n += 16;
if (n > 64) {
break;
}
}
drawTextCenter(x + w / 2, y + h - 16, "Press button 1 to dismiss alarm");
}
// Sunset und sunrise calculation // Sunset und sunrise calculation
SunData calcSunsetSunrise(GwApi *api, double time, double date, double latitude, double longitude, double timezone){ SunData calcSunsetSunrise(double time, double date, double latitude, double longitude, float timezone){
GwLog *logger=api->getLogger();
SunData returnset; SunData returnset;
SunRise sr; SunRise sr;
int secPerHour = 3600; int secPerHour = 3600;
@@ -309,8 +739,7 @@ SunData calcSunsetSunrise(GwApi *api, double time, double date, double latitude,
if (!isnan(time) && !isnan(date) && !isnan(latitude) && !isnan(longitude) && !isnan(timezone)) { if (!isnan(time) && !isnan(date) && !isnan(latitude) && !isnan(longitude) && !isnan(timezone)) {
// Calculate local epoch // Calculate local epoch
t = (date * secPerYear) + time; t = (date * secPerYear) + time;
// api->getLogger()->logDebug(GwLog::DEBUG,"... calcSun: Lat %f, Lon %f, at: %d ", latitude, longitude, t);
sr.calculate(latitude, longitude, t); // LAT, LON, EPOCH sr.calculate(latitude, longitude, t); // LAT, LON, EPOCH
// Sunrise // Sunrise
if (sr.hasRise) { if (sr.hasRise) {
@@ -333,6 +762,37 @@ SunData calcSunsetSunrise(GwApi *api, double time, double date, double latitude,
return returnset; return returnset;
} }
SunData calcSunsetSunriseRTC(struct tm *rtctime, double latitude, double longitude, float timezone) {
SunData returnset;
SunRise sr;
const int secPerHour = 3600;
const int secPerYear = 86400;
sr.hasRise = false;
sr.hasSet = false;
time_t t = mktime(rtctime) + timezone * 3600;;
time_t sunR = 0;
time_t sunS = 0;
sr.calculate(latitude, longitude, t); // LAT, LON, EPOCH
// Sunrise
if (sr.hasRise) {
sunR = (sr.riseTime + int(timezone * secPerHour) + 30) % secPerYear; // add 30 seconds: round to minutes
returnset.sunriseHour = int (sunR / secPerHour);
returnset.sunriseMinute = int((sunR - returnset.sunriseHour * secPerHour) / 60);
}
// Sunset
if (sr.hasSet) {
sunS = (sr.setTime + int(timezone * secPerHour) + 30) % secPerYear; // add 30 seconds: round to minutes
returnset.sunsetHour = int (sunS / secPerHour);
returnset.sunsetMinute = int((sunS - returnset.sunsetHour * secPerHour) / 60);
}
// Sun control (return value by sun on sky = false, sun down = true)
if ((t >= sr.riseTime) && (t <= sr.setTime))
returnset.sunDown = false;
else returnset.sunDown = true;
return returnset;
}
// Battery graphic with fill level // Battery graphic with fill level
void batteryGraphic(uint x, uint y, float percent, int pcolor, int bcolor){ void batteryGraphic(uint x, uint y, float percent, int pcolor, int bcolor){
// Show battery // Show battery
@@ -399,9 +859,50 @@ void generatorGraphic(uint x, uint y, int pcolor, int bcolor){
getdisplay().fillCircle(xb, yb, 41, bcolor); getdisplay().fillCircle(xb, yb, 41, bcolor);
// Insert G // Insert G
getdisplay().setTextColor(pcolor); getdisplay().setTextColor(pcolor);
getdisplay().setFont(&Ubuntu_Bold32pt7b); getdisplay().setFont(&Ubuntu_Bold32pt8b);
getdisplay().setCursor(xb-22, yb+20); getdisplay().setCursor(xb-22, yb+20);
getdisplay().print("G"); getdisplay().print("G");
} }
#endif // Function to handle HTTP image request
// http://192.168.15.1/api/user/OBP60Task/screenshot
void doImageRequest(GwApi *api, int *pageno, const PageStruct pages[MAX_PAGE_NUMBER], AsyncWebServerRequest *request) {
GwLog *logger = api->getLogger();
String imgformat = api->getConfig()->getConfigItem(api->getConfig()->imageFormat,true)->asString();
imgformat.toLowerCase();
String filename = "Page" + String(*pageno) + "_" + pages[*pageno].description->pageName + "." + imgformat;
logger->logDebug(GwLog::LOG,"handle image request [%s]: %s", imgformat, filename);
uint8_t *fb = getdisplay().getBuffer(); // EPD framebuffer
std::vector<uint8_t> imageBuffer; // image in webserver transferbuffer
String mimetype;
if (imgformat == "gif") {
// GIF is commpressed with LZW, so small
mimetype = "image/gif";
if (!createGIF(fb, &imageBuffer, GxEPD_WIDTH, GxEPD_HEIGHT)) {
logger->logDebug(GwLog::LOG,"GIF creation failed: Hashtable init error!");
return;
}
}
else if (imgformat == "bmp") {
// Microsoft BMP bitmap
mimetype = "image/bmp";
createBMP(fb, &imageBuffer, GxEPD_WIDTH, GxEPD_HEIGHT);
}
else {
// PBM simple portable bitmap
mimetype = "image/x-portable-bitmap";
createPBM(fb, &imageBuffer, GxEPD_WIDTH, GxEPD_HEIGHT);
}
AsyncWebServerResponse *response = request->beginResponse_P(200, mimetype, (const uint8_t*)imageBuffer.data(), imageBuffer.size());
response->addHeader("Content-Disposition", "inline; filename=" + filename);
request->send(response);
imageBuffer.clear();
}
#endif

View File

@@ -3,26 +3,57 @@
#include <Arduino.h> #include <Arduino.h>
#include "OBP60Hardware.h" #include "OBP60Hardware.h"
#define FASTLED_ALL_PINS_HARDWARE_SPI
#define FASTLED_ESP32_SPI_BUS FSPI
#define FASTLED_ESP32_FLASH_LOCK 1
#include "LedSpiTask.h" #include "LedSpiTask.h"
#include "Graphics.h"
#include <GxEPD2_BW.h> // E-paper lib V2 #include <GxEPD2_BW.h> // E-paper lib V2
#include <Adafruit_FRAM_I2C.h> // I2C FRAM
// Fonts declarations for display (#inclues see OBP60Extensions.cpp) #ifdef BOARD_OBP40S3
extern const GFXfont Ubuntu_Bold8pt7b; #include "esp_vfs_fat.h"
extern const GFXfont Ubuntu_Bold10pt7b; #include "sdmmc_cmd.h"
extern const GFXfont Ubuntu_Bold12pt7b; #define MOUNT_POINT "/sdcard"
extern const GFXfont Ubuntu_Bold16pt7b; #endif
extern const GFXfont Ubuntu_Bold20pt7b;
extern const GFXfont Ubuntu_Bold32pt7b; // FRAM address reservations 32kB: 0x0000 - 0x7FFF
// 0x0000 - 0x03ff: single variables
#define FRAM_PAGE_NO 0x0002
#define FRAM_SYSTEM_MODE 0x009
// Voltage page
#define FRAM_VOLTAGE_AVG 0x000A
#define FRAM_VOLTAGE_TREND 0x000B
#define FRAM_VOLTAGE_MODE 0x000C
// Wind page
#define FRAM_WIND_SIZE 0x000D
#define FRAM_WIND_SRC 0x000E
#define FRAM_WIND_MODE 0x000F
// Barograph history data
#define FRAM_BAROGRAPH_START 0x0400
#define FRAM_BAROGRAPH_END 0x13FF
extern Adafruit_FRAM_I2C fram;
extern bool hasFRAM;
extern bool hasSDCard;
#ifdef BOARD_OBP40S3
extern sdmmc_card_t *sdcard;
#endif
// Fonts declarations for display (#includes see OBP60Extensions.cpp)
extern const GFXfont DSEG7Classic_BoldItalic16pt7b; extern const GFXfont DSEG7Classic_BoldItalic16pt7b;
extern const GFXfont DSEG7Classic_BoldItalic20pt7b; extern const GFXfont DSEG7Classic_BoldItalic20pt7b;
extern const GFXfont DSEG7Classic_BoldItalic26pt7b;
extern const GFXfont DSEG7Classic_BoldItalic30pt7b; extern const GFXfont DSEG7Classic_BoldItalic30pt7b;
extern const GFXfont DSEG7Classic_BoldItalic42pt7b; extern const GFXfont DSEG7Classic_BoldItalic42pt7b;
extern const GFXfont DSEG7Classic_BoldItalic60pt7b; extern const GFXfont DSEG7Classic_BoldItalic60pt7b;
extern const GFXfont Ubuntu_Bold8pt8b;
extern const GFXfont Ubuntu_Bold10pt8b;
extern const GFXfont Ubuntu_Bold12pt8b;
extern const GFXfont Ubuntu_Bold16pt8b;
extern const GFXfont Ubuntu_Bold20pt8b;
extern const GFXfont Ubuntu_Bold32pt8b;
extern const GFXfont Atari16px;
extern const GFXfont IBM8x8px;
// Gloabl functions // Global functions
#ifdef DISPLAY_GDEW042T2 #ifdef DISPLAY_GDEW042T2
GxEPD2_BW<GxEPD2_420, GxEPD2_420::HEIGHT> & getdisplay(); GxEPD2_BW<GxEPD2_420, GxEPD2_420::HEIGHT> & getdisplay();
#endif #endif
@@ -39,7 +70,20 @@ GxEPD2_BW<GxEPD2_420_GYE042A87, GxEPD2_420_GYE042A87::HEIGHT> & getdisplay();
GxEPD2_BW<GxEPD2_420_SE0420NQ04, GxEPD2_420_SE0420NQ04::HEIGHT> & getdisplay(); GxEPD2_BW<GxEPD2_420_SE0420NQ04, GxEPD2_420_SE0420NQ04::HEIGHT> & getdisplay();
#endif #endif
void hardwareInit(); // Page display return values
#define PAGE_OK 0 // all ok, do nothing
#define PAGE_UPDATE 1 // page wants display to update
#define PAGE_HIBERNATE 2 // page wants displey to hibernate
void fillPoly4(const std::vector<Point>& p4, uint16_t color);
void drawPoly(const std::vector<Point>& points, uint16_t color);
void deepSleep(CommonData &common);
uint8_t getLastPage();
void hardwareInit(GwApi *api);
void powerInit(String powermode);
void setPortPin(uint pin, bool value); // Set port pin for extension port void setPortPin(uint pin, bool value); // Set port pin for extension port
@@ -48,6 +92,7 @@ void togglePortPin(uint pin); // Toggle extension port pin
Color colorMapping(const String &colorString); // Color mapping string to CHSV colors Color colorMapping(const String &colorString); // Color mapping string to CHSV colors
void setBacklightLED(uint brightness, const Color &color);// Set backlight LEDs void setBacklightLED(uint brightness, const Color &color);// Set backlight LEDs
void toggleBacklightLED(uint brightness,const Color &color);// Toggle backlight LEDs void toggleBacklightLED(uint brightness,const Color &color);// Toggle backlight LEDs
BacklightMode backlightMapping(const String &backlightString);// Configuration string to value
void setFlashLED(bool status); // Set flash LED void setFlashLED(bool status); // Set flash LED
void blinkingFlashLED(); // Blinking function for flash LED void blinkingFlashLED(); // Blinking function for flash LED
@@ -58,16 +103,143 @@ void setBuzzerPower(uint power); // Set buzzer power
String xdrDelete(String input); // Delete xdr prefix from string String xdrDelete(String input); // Delete xdr prefix from string
void drawTextCenter(int16_t cx, int16_t cy, String text);
void drawTextRalign(int16_t x, int16_t y, String text);
void drawTextBoxed(Rect box, String text, uint16_t fg, uint16_t bg, bool inverted, bool border);
void displayTrendHigh(int16_t x, int16_t y, uint16_t size, uint16_t color); void displayTrendHigh(int16_t x, int16_t y, uint16_t size, uint16_t color);
void displayTrendLow(int16_t x, int16_t y, uint16_t size, uint16_t color); void displayTrendLow(int16_t x, int16_t y, uint16_t size, uint16_t color);
void displayHeader(CommonData &commonData, GwApi::BoatValue *date, GwApi::BoatValue *time, GwApi::BoatValue *hdop); // Draw display header void displayHeader(CommonData &commonData, GwApi::BoatValue *date, GwApi::BoatValue *time, GwApi::BoatValue *hdop); // Draw display header
void displayFooter(CommonData &commonData);
void displayAlarm(CommonData &commonData);
SunData calcSunsetSunrise(GwApi *api, double time, double date, double latitude, double longitude, double timezone); // Calulate sunset and sunrise SunData calcSunsetSunrise(double time, double date, double latitude, double longitude, float timezone); // Calulate sunset and sunrise
SunData calcSunsetSunriseRTC(struct tm *rtctime, double latitude, double longitude, float timezone);
void batteryGraphic(uint x, uint y, float percent, int pcolor, int bcolor); // Battery graphic with fill level void batteryGraphic(uint x, uint y, float percent, int pcolor, int bcolor); // Battery graphic with fill level
void solarGraphic(uint x, uint y, int pcolor, int bcolor); // Solar graphic with fill level void solarGraphic(uint x, uint y, int pcolor, int bcolor); // Solar graphic
void generatorGraphic(uint x, uint y, int pcolor, int bcolor); // Generator graphic with fill level void generatorGraphic(uint x, uint y, int pcolor, int bcolor); // Generator graphic
void startLedTask(GwApi *api); void startLedTask(GwApi *api);
#endif void doImageRequest(GwApi *api, int *pageno, const PageStruct pages[MAX_PAGE_NUMBER], AsyncWebServerRequest *request);
// Icons
#define icon_width 16
#define icon_height 16
static unsigned char left_bits[] PROGMEM = {
0x00, 0x00, 0xc0, 0x01, 0xe0, 0x01, 0xf0, 0x01, 0xf8, 0x01, 0xfc, 0x7f,
0xfe, 0x7f, 0xff, 0x7f, 0xff, 0x7f, 0xfe, 0x7f, 0xfc, 0x7f, 0xf8, 0x01,
0xf0, 0x01, 0xe0, 0x01, 0xc0, 0x01, 0x00, 0x00 };
static unsigned char right_bits[] PROGMEM = {
0x00, 0x00, 0x80, 0x03, 0x80, 0x07, 0x80, 0x0f, 0x80, 0x1f, 0xfe, 0x3f,
0xfe, 0x7f, 0xfe, 0xff, 0xfe, 0xff, 0xfe, 0x7f, 0xfe, 0x3f, 0x80, 0x1f,
0x80, 0x0f, 0x80, 0x07, 0x80, 0x03, 0x00, 0x00 };
static unsigned char lock_bits[] PROGMEM = {
0xc0, 0x03, 0x60, 0x06, 0x30, 0x0c, 0x10, 0x08, 0x10, 0x08, 0x10, 0x08,
0xfc, 0x3f, 0x04, 0x20, 0x04, 0x20, 0x84, 0x21, 0x84, 0x21, 0x84, 0x21,
0x04, 0x20, 0x04, 0x20, 0x04, 0x20, 0xfc, 0x3f };
static unsigned char plus_bits[] PROGMEM = {
0x00, 0x00, 0xe0, 0x01, 0x18, 0x06, 0x04, 0x08, 0xc4, 0x08, 0xc2, 0x10,
0xf2, 0x13, 0xf2, 0x13, 0xc2, 0x10, 0xc4, 0x08, 0x04, 0x0c, 0x18, 0x1e,
0xe0, 0x39, 0x00, 0x70, 0x00, 0xe0, 0x00, 0xc0 };
static unsigned char minus_bits[] PROGMEM = {
0x00, 0x00, 0xe0, 0x01, 0x18, 0x06, 0x04, 0x08, 0x04, 0x08, 0x02, 0x10,
0xf2, 0x13, 0xf2, 0x13, 0x02, 0x10, 0x04, 0x08, 0x04, 0x0c, 0x18, 0x1e,
0xe0, 0x39, 0x00, 0x70, 0x00, 0xe0, 0x00, 0xc0 };
static unsigned char fram_bits[] PROGMEM = {
0xf8, 0x1f, 0xff, 0xff, 0x9f, 0xff, 0x98, 0x1f, 0xf8, 0x1f, 0xff, 0xff,
0xff, 0xff, 0xf8, 0x1f, 0xf8, 0x1f, 0xff, 0xff, 0xff, 0xff, 0xf8, 0x1f,
0xf8, 0x1f, 0xff, 0xff, 0xff, 0xff, 0xf8, 0x1f };
static unsigned char ap_bits[] PROGMEM = {
0xe0, 0x03, 0x18, 0x0c, 0x04, 0x10, 0xc2, 0x21, 0x30, 0x06, 0x08, 0x08,
0xc0, 0x01, 0x20, 0x02, 0x00, 0x00, 0x80, 0x00, 0xc0, 0x01, 0xc0, 0x01,
0x80, 0x00, 0x80, 0x00, 0x80, 0x00, 0x80, 0x00 };
static unsigned char dish_bits[] PROGMEM = {
0x3c, 0x00, 0x42, 0x18, 0xfa, 0x1b, 0x02, 0x04, 0x02, 0x0a, 0x02, 0x09,
0x82, 0x08, 0x06, 0x0a, 0x0e, 0x1b, 0x9c, 0x2b, 0x38, 0x2b, 0x74, 0x20,
0xec, 0x1f, 0x1c, 0x00, 0xf4, 0x00, 0xfe, 0x03 };
static std::map<String, unsigned char *> iconmap = {
{"LEFT", left_bits},
{"RIGHT", right_bits},
{"LOCK", lock_bits},
{"PLUS", plus_bits},
{"MINUS", minus_bits},
{"DISH", dish_bits},
{"AP", ap_bits}
};
// Battery
#define battery_width 24
#define battery_height 16
static unsigned char battery_0_bits[] = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0xff, 0x0f, 0xff, 0xff, 0x1f,
0x03, 0x00, 0x18, 0x03, 0x00, 0x78, 0x03, 0x00, 0xf8, 0x03, 0x00, 0xd8,
0x03, 0x00, 0xd8, 0x03, 0x00, 0xd8, 0x03, 0x00, 0xf8, 0x03, 0x00, 0x78,
0x03, 0x00, 0x18, 0xff, 0xff, 0x1f, 0xfe, 0xff, 0x0f, 0x00, 0x00, 0x00 };
static unsigned char battery_25_bits[] = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0xff, 0x0f, 0xff, 0xff, 0x1f,
0x03, 0x00, 0x18, 0x3b, 0x00, 0x78, 0x3b, 0x00, 0xf8, 0x3b, 0x00, 0xd8,
0x3b, 0x00, 0xd8, 0x3b, 0x00, 0xd8, 0x3b, 0x00, 0xf8, 0x3b, 0x00, 0x78,
0x03, 0x00, 0x18, 0xff, 0xff, 0x1f, 0xfe, 0xff, 0x0f, 0x00, 0x00, 0x00 };
static unsigned char battery_50_bits[] = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0xff, 0x0f, 0xff, 0xff, 0x1f,
0x03, 0x00, 0x18, 0xbb, 0x03, 0x78, 0xbb, 0x03, 0xf8, 0xbb, 0x03, 0xd8,
0xbb, 0x03, 0xd8, 0xbb, 0x03, 0xd8, 0xbb, 0x03, 0xf8, 0xbb, 0x03, 0x78,
0x03, 0x00, 0x18, 0xff, 0xff, 0x1f, 0xfe, 0xff, 0x0f, 0x00, 0x00, 0x00 };
static unsigned char battery_75_bits[] = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0xff, 0x0f, 0xff, 0xff, 0x1f,
0x03, 0x00, 0x18, 0xbb, 0x3b, 0x78, 0xbb, 0x3b, 0xf8, 0xbb, 0x3b, 0xd8,
0xbb, 0x3b, 0xd8, 0xbb, 0x3b, 0xd8, 0xbb, 0x3b, 0xf8, 0xbb, 0x3b, 0x78,
0x03, 0x00, 0x18, 0xff, 0xff, 0x1f, 0xfe, 0xff, 0x0f, 0x00, 0x00, 0x00 };
static unsigned char battery_100_bits[] = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0xff, 0x0f, 0xff, 0xff, 0x1f,
0x03, 0x00, 0x18, 0xbb, 0xbb, 0x7b, 0xbb, 0xbb, 0xfb, 0xbb, 0xbb, 0xdb,
0xbb, 0xbb, 0xdb, 0xbb, 0xbb, 0xdb, 0xbb, 0xbb, 0xfb, 0xbb, 0xbb, 0x7b,
0x03, 0x00, 0x18, 0xff, 0xff, 0x1f, 0xfe, 0xff, 0x0f, 0x00, 0x00, 0x00 };
static unsigned char battery_loading_bits[] = {
0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0xfe, 0xe4, 0x0f, 0xff, 0xec, 0x1f,
0x03, 0x08, 0x18, 0x03, 0x18, 0x78, 0x03, 0x30, 0xf8, 0x83, 0x3f, 0xd8,
0x03, 0x7f, 0xd8, 0x03, 0x03, 0xd8, 0x03, 0x06, 0xf8, 0x03, 0x04, 0x78,
0x03, 0x0c, 0x18, 0xff, 0xcb, 0x1f, 0xfe, 0xd3, 0x0f, 0x00, 0x10, 0x00 };
// Other symbols
#define swipe_width 24
#define swipe_height 16
static unsigned char swipe_bits[] PROGMEM = {
0x00, 0x06, 0x00, 0x24, 0x09, 0x24, 0x12, 0x09, 0x48, 0x7f, 0x09, 0xfe,
0x12, 0xb9, 0x48, 0x24, 0xc9, 0x25, 0x40, 0x49, 0x02, 0xa0, 0x49, 0x06,
0x20, 0x01, 0x0a, 0x20, 0x00, 0x08, 0x40, 0x00, 0x08, 0x40, 0x00, 0x08,
0x80, 0x00, 0x04, 0x00, 0x01, 0x04, 0x00, 0x02, 0x02, 0x00, 0xfc, 0x01 };
#define exclamation_width 32
#define exclamation_height 32
static unsigned char exclamation_bits[] PROGMEM = {
0x00, 0xc0, 0x03, 0x00, 0x00, 0x60, 0x06, 0x00, 0x00, 0xb0, 0x0d, 0x00,
0x00, 0xd8, 0x1b, 0x00, 0x00, 0xec, 0x37, 0x00, 0x00, 0xf6, 0x6f, 0x00,
0x00, 0x3b, 0xdc, 0x00, 0x80, 0x3d, 0xbc, 0x01, 0xc0, 0x3e, 0x7c, 0x03,
0x60, 0x3f, 0xfc, 0x06, 0xb0, 0x3f, 0xfc, 0x0d, 0xd8, 0x3f, 0xfc, 0x1b,
0xec, 0x3f, 0xfc, 0x37, 0xf6, 0x3f, 0xfc, 0x6f, 0xfb, 0x3f, 0xfc, 0xdf,
0xfd, 0x3f, 0xfc, 0xbf, 0xfd, 0x3f, 0xfc, 0xbf, 0xfb, 0x3f, 0xfc, 0xdf,
0xf6, 0x3f, 0xfc, 0x6f, 0xec, 0x3f, 0xfc, 0x37, 0xd8, 0xff, 0xff, 0x1b,
0xb0, 0xff, 0xff, 0x0d, 0x60, 0x3f, 0xfc, 0x06, 0xc0, 0x3e, 0x7c, 0x03,
0x80, 0x3d, 0xbc, 0x01, 0x00, 0x3b, 0xdc, 0x00, 0x00, 0xf6, 0x6f, 0x00,
0x00, 0xec, 0x37, 0x00, 0x00, 0xd8, 0x1b, 0x00, 0x00, 0xb0, 0x0d, 0x00,
0x00, 0x60, 0x06, 0x00, 0x00, 0xc0, 0x03, 0x00 };
#endif

View File

@@ -1,6 +1,7 @@
// General hardware definitions // General hardware definitions
// CAN and RS485 bus pin definitions see obp60task.h // CAN and RS485 bus pin definitions see obp60task.h
#ifdef HARDWARE_V21
// Direction pin for RS485 NMEA0183 // Direction pin for RS485 NMEA0183
#define OBP_DIRECTION_PIN 18 #define OBP_DIRECTION_PIN 18
// I2C // I2C
@@ -30,6 +31,8 @@
#define INA226_I2C_ADDR3 0x45 // Addr. 0x45 (fix A0 = 5V, A1 = 5V) for generator #define INA226_I2C_ADDR3 0x45 // Addr. 0x45 (fix A0 = 5V, A1 = 5V) for generator
// Horter modules // Horter modules
#define PCF8574_I2C_ADDR1 0x20 // First digital out module #define PCF8574_I2C_ADDR1 0x20 // First digital out module
// FRAM (e.g. MB85RC256V)
#define FRAM_I2C_ADDR 0x50
// SPI (E-Ink display, Extern Bus) // SPI (E-Ink display, Extern Bus)
#define OBP_SPI_CS 39 #define OBP_SPI_CS 39
#define OBP_SPI_DC 40 #define OBP_SPI_DC 40
@@ -39,11 +42,6 @@
#define OBP_SPI_DIN 48 #define OBP_SPI_DIN 48
#define SHOW_TIME 6000 // Show time in [ms] for logo and WiFi QR code #define SHOW_TIME 6000 // Show time in [ms] for logo and WiFi QR code
#define FULL_REFRESH_TIME 600 // Refresh cycle time in [s][600...3600] for full display update (very important healcy function) #define FULL_REFRESH_TIME 600 // Refresh cycle time in [s][600...3600] for full display update (very important healcy function)
#define MAX_PAGE_NUMBER 10 // Max number of pages for show data
#define FONT1 "Ubuntu_Bold8pt7b"
#define FONT2 "Ubuntu_Bold24pt7b"
#define FONT3 "Ubuntu_Bold32pt7b"
#define FONT4 "DSEG7Classic_BoldItalic80pt7b"
// GPS (NEO-6M, NEO-M8N, ATGM336H) // GPS (NEO-6M, NEO-M8N, ATGM336H)
#define OBP_GPS_RX 2 #define OBP_GPS_RX 2
@@ -57,7 +55,7 @@
#define TONE3 3500 // 3500Hz #define TONE3 3500 // 3500Hz
#define TONE4 4000 // 4000Hz #define TONE4 4000 // 4000Hz
// Analog Input // Analog Input
#define OBP_ANALOG0 4 // Analog input for voltage power supplay #define OBP_ANALOG0 4 // Analog input for voltage power supply
#define MIN_VOLTAGE 10.0 // Min voltage for under voltage detection (then goto deep sleep) #define MIN_VOLTAGE 10.0 // Min voltage for under voltage detection (then goto deep sleep)
#define POWER_FAIL_TIME 2 // in [ms] Accept min voltage until 2 x 1ms (for under voltage gaps by engine start) #define POWER_FAIL_TIME 2 // in [ms] Accept min voltage until 2 x 1ms (for under voltage gaps by engine start)
// Touch buttons // Touch buttons
@@ -72,9 +70,96 @@
#define NUM_FLASH_LED 1 // Number of flash LED #define NUM_FLASH_LED 1 // Number of flash LED
#define OBP_FLASH_LED 7 // GPIO port #define OBP_FLASH_LED 7 // GPIO port
// Backlight LEDs (6x WS2812B) // Backlight LEDs (6x WS2812B)
#define NUM_BACKLIGHT_LED 6 // Numebr of Backlight LEDs #define NUM_BACKLIGHT_LED 6 // Number of Backlight LEDs
#define OBP_BACKLIGHT_LED 15 // GPIO port #define OBP_BACKLIGHT_LED 15 // GPIO port
// Power Rail // Power Rail
#define OBP_POWER_50 5 // 5.0V power rail #define OBP_POWER_50 5 // 5.0V power rail
#endif
// Hardware configuration for OBP40
#ifdef BOARD_OBP40S3
// Direction pin for RS485 NMEA0183
#define OBP_DIRECTION_PIN 8
// I2C
#define I2C_SPEED 100000UL // 100kHz clock speed on I2C bus
#define OBP_I2C_SDA 21
#define OBP_I2C_SCL 38
// DS1388 RTC
#define DS1388_I2C_ADDR 0x68 // Addr. 0x68
// BME280
#define BME280_I2C_ADDR 0x76 // Addr. 0x76 (0x77)
// BMP280
#define BMP280_I2C_ADDR 0x77 // Addr. 0x77 (0x76) Attention: Pull up resistor
// BMP085 / BMP180
#define BMP180_I2C_ADDR 0x77 // Addr. 0x77 (fix)
// SHT21 / HUT21
#define SHT21_I2C_ADDR 0x40 // Addr. 0x40 (fix)
// AS5600
#define AS5600_I2C_ADDR 0x36 // Addr. 0x36 (fix)
// INA219
#define SHUNT_VOLTAGE 0.075 // Shunt voltage in V by max. current (75mV)
#define INA219_I2C_ADDR1 0x40 // Addr. 0x41 (fix A0 = 5V, A1 = GND) for battery
#define INA219_I2C_ADDR2 0x41 // Addr. 0x44 (fix A0 = GND, A1 = 5V) for solar panels
#define INA219_I2C_ADDR3 0x45 // Addr. 0x45 (fix A0 = 5V, A1 = 5V) for generator
// INA226
#define INA226_I2C_ADDR1 0x41 // Addr. 0x41 (fix A0 = 5V, A1 = GND) for battery
#define INA226_I2C_ADDR2 0x44 // Addr. 0x44 (fix A0 = GND, A1 = 5V) for solar panels
#define INA226_I2C_ADDR3 0x45 // Addr. 0x45 (fix A0 = 5V, A1 = 5V) for generator
// Horter modules
#define PCF8574_I2C_ADDR1 0x20 // First digital out module
// FRAM (e.g. MB85RC256V)
#define FRAM_I2C_ADDR 0x50
// SPI (E-Ink display, Extern Bus)
#define OBP_SPI_CS 45
#define OBP_SPI_DC 46
#define OBP_SPI_RST 47
#define OBP_SPI_BUSY 48
#define OBP_SPI_CLK 12
#define OBP_SPI_DIN 11
#define SHOW_TIME 6000 // Show time in [ms] for logo and WiFi QR code
#define FULL_REFRESH_TIME 600 // Refresh cycle time in [s][600...3600] for full display update (very important healcy function)
// SPI SD-Card
#define SD_SPI_CS GPIO_NUM_10
#define SD_SPI_MOSI GPIO_NUM_40
#define SD_SPI_CLK GPIO_NUM_39
#define SD_SPI_MISO GPIO_NUM_13
// GPS (NEO-6M, NEO-M8N, ATGM336H)
#define OBP_GPS_RX 19
#define OBP_GPS_TX 20
// 1Wire (DS18B20)
#define OBP_1WIRE 17 // External 1Wire
// Buzzer
#define OBP_BUZZER 18
#define TONE1 1500 // 1500Hz
#define TONE2 2500 // 2500Hz
#define TONE3 3500 // 3500Hz
#define TONE4 4000 // 4000Hz
// Analog Input
#define OBP_ANALOG0 3 // Analog input for voltage power supply
#define MIN_VOLTAGE 10.0 // Min voltage for under voltage detection (then goto deep sleep)
#define POWER_FAIL_TIME 2 // in [ms] Accept min voltage until 2 x 1ms (for under voltage gaps by engine start)
// Buttons
#define UP 6 // Wheel up
#define DOWN 4 // Wheel down
#define CONF 5 // Wheel press
#define MENUE 2 // Button top
#define EXIT 1 // Button bottom
// Flash LED (1x WS2812B)
#define NUM_FLASH_LED 1 // Number of flash LED
#define OBP_FLASH_LED 41 // GPIO port (power LED)
// Backlight LEDs (6x WS2812B)
#define NUM_BACKLIGHT_LED 6 // Number of Backlight LEDs
#define OBP_BACKLIGHT_LED 41 // GPIO port (power LED)
// Power Rail
#define OBP_POWER_50 41 // Power LED
#define OBP_POWER_EPD 7 // ePaper power
#define OBP_POWER_SD 42 // SD card power
// Deep sleep wakeup
#define OBP_WAKEUP_LEVEL 0 // //1 = High, 0 = Low, depends on switch
#define OBP_WAKEWUP_PIN GPIO_NUM_5// Wakeup pin, same as CONF (wheel press)
// Must define as GPIO_NUM_X
#endif

View File

@@ -14,167 +14,290 @@ int keycode = 0; // Keycode of pressed key [0...8], 0 = nothing touched
int keycode2 = 0; // Keycode of very short pressed key [0...8], 0 = nothing touched int keycode2 = 0; // Keycode of very short pressed key [0...8], 0 = nothing touched
int keycodeold = 0; // Old keycode int keycodeold = 0; // Old keycode
int keycodeold2 = 0; // Old keycode for short pressed key int keycodeold2 = 0; // Old keycode for short pressed key
int keystatus = 0; // Status of key [0...11]
bool keyoff = false; // Disable all keys bool keyoff = false; // Disable all keys
int keydelay = 250; // Delay after key pressed in [ms] int keydelay = 250; // Delay after key pressed in [ms]
bool keylock = false; // Key lock after pressed key is valid (repeat protection by conginous pressing) bool keylock = false; // Key lock after pressed key is valid (repeat protection by conginous pressing)
long starttime = 0; // Start time point for pressed key long starttime = 0; // Start time point for pressed key
void initKeys(CommonData &commonData) {
// coordinates for virtual keyboard keys
int readKeypad(uint thSensitivity) { static uint16_t top = 281;
static uint16_t width = 65;
// Touch sensor values static uint16_t height = 18;
// 35000 - Not touched
// 50000 - Light toched with fingertip
// 70000 - Touched
// 170000 - Strong touched
uint32_t touchthreshold = (thSensitivity * -1200) + 170000; // thSensitivity 0...100%
int keystatus = 0; // Status of key [0...11], 0 = processed, 1...8 = key 1..8, 9 = right swipe , 10 = left swipe, 11 keys disabled commonData.keydata[0].x = 0;
keycode = 0; commonData.keydata[0].y = top;
commonData.keydata[0].w = width + 1;
commonData.keydata[0].h = height;
// Read key code commonData.keydata[1].x = commonData.keydata[0].x + commonData.keydata[0].w + 1;
if(touchRead(14) > touchthreshold){ // Touch pad 1 commonData.keydata[1].y = top;
keypad[1] = 1; commonData.keydata[1].w = width;
} commonData.keydata[1].h = height;
else{
keypad[1] = 0;
}
if(touchRead(13) > touchthreshold){ // Touch pad 2
keypad[2] = 1;
}
else{
keypad[2] = 0;
}
if(touchRead(12) > touchthreshold){ // Touch pad 3
keypad[3] = 1;
}
else{
keypad[3] = 0;
}
if(touchRead(11) > touchthreshold){ // Touch pad 4
keypad[4] = 1;
}
else{
keypad[4] = 0;
}
if(touchRead(10) > touchthreshold){ // Touch pad 5
keypad[5] = 1;
}
else{
keypad[5] = 0;
}
if(touchRead(9) > touchthreshold){ // Touch pad 6
keypad[6] = 1;
}
else{
keypad[6] = 0;
}
// Nothing touched
if(keypad[1] == 0 && keypad[2] == 0 && keypad[3] == 0 && keypad[4] == 0 && keypad[5] == 0 && keypad[6] == 0){
keypad[0] = 1;
}
else{
keypad[0] = 0;
}
for (int i = 0; i < 9; i++) { commonData.keydata[2].x = commonData.keydata[1].x + commonData.keydata[1].w + 1;
if(i > 0){ commonData.keydata[2].y = top;
// Convert keypad to keycode commonData.keydata[2].w = width;
if(keypad[i] == 1){ commonData.keydata[2].h = height;
key = 1;
}
else{
key = 0;
}
keycode += key * i;
}
}
// Detect short keynumber commonData.keydata[3].x = commonData.keydata[2].x + commonData.keydata[2].w + 1;
if (keycode > 0 ){ commonData.keydata[3].y = top;
if(keylock == false){ commonData.keydata[3].w = width;
starttime = millis(); commonData.keydata[3].h = height;
keylock = true;
commonData.keydata[4].x = commonData.keydata[3].x + commonData.keydata[3].w + 1;
commonData.keydata[4].y = top;
commonData.keydata[4].w = width;
commonData.keydata[4].h = height;
commonData.keydata[5].x = commonData.keydata[4].x + commonData.keydata[4].w + 1;
commonData.keydata[5].y = top;
commonData.keydata[5].w = width;
commonData.keydata[5].h = height;
}
#ifdef HARDWARE_V21
// Keypad functions for original OBP60 hardware
int readKeypad(GwLog* logger, uint thSensitivity, bool use_syspage) {
// Touch sensor values
// 35000 - Not touched
// 50000 - Light toched with fingertip
// 70000 - Touched
// 170000 - Strong touched
uint32_t touchthreshold = (thSensitivity * -1200) + 170000; // thSensitivity 0...100%
keystatus = 0; // Status of key [0...11], 0 = processed, 1...8 = key 1..8, 9 = right swipe , 10 = left swipe, 11 keys disabled
keycode = 0;
// Read key code
if(touchRead(14) > touchthreshold){ // Touch pad 1
keypad[1] = 1;
} }
if (keycode != keycodeold){ else{
keylock = false; keypad[1] = 0;
} }
// Detect a very short keynumber (10ms) if(touchRead(13) > touchthreshold){ // Touch pad 2
if (millis() > starttime + 10 && keycode == keycodeold && keylock == true) { keypad[2] = 1;
// Process only valid keys }
if(keycode == 1 || keycode == 6){ else{
keycode2 = keycode; keypad[2] = 0;
}
if(touchRead(12) > touchthreshold){ // Touch pad 3
keypad[3] = 1;
}
else{
keypad[3] = 0;
}
if(touchRead(11) > touchthreshold){ // Touch pad 4
keypad[4] = 1;
}
else{
keypad[4] = 0;
}
if(touchRead(10) > touchthreshold){ // Touch pad 5
keypad[5] = 1;
}
else{
keypad[5] = 0;
}
if(touchRead(9) > touchthreshold){ // Touch pad 6
keypad[6] = 1;
}
else{
keypad[6] = 0;
}
// Nothing touched
/* if(keypad[1] == 0 && keypad[2] == 0 && keypad[3] == 0 && keypad[4] == 0 && keypad[5] == 0 && keypad[6] == 0){
keypad[0] = 1;
}
else{
keypad[0] = 0;
} */
for (int i = 1; i <= 6; i++) {
if(i > 0){
// Convert keypad to keycode
if(keypad[i] == 1){
key = 1;
}
else{
key = 0;
}
keycode += key * i;
}
}
// Detect short keynumber
if (keycode > 0 ){
if(keylock == false){
starttime = millis();
keylock = true;
} }
// Clear by unvalid keys if (keycode != keycodeold){
else{ keylock = false;
}
// Detect a very short keynumber (10ms)
if (millis() > starttime + 10 && keycode == keycodeold && keylock == true) {
logger->logDebug(GwLog::LOG,"Very short 20ms key touch: %d", keycode);
// Process only valid keys
if(keycode == 1 || keycode == 4 || keycode == 5 || keycode == 6){
keycode2 = keycode;
}
// Clear by invalid keys
else{
keycode2 = 0;
keycodeold2 = 0;
}
}
// Timeout for very short pressed key
if(millis() > starttime + 200){
keycode2 = 0;
}
// Detect a short keynumber (200ms)
if (keyoff == false && millis() > starttime + 200 && keycode == keycodeold && keylock == true) {
logger->logDebug(GwLog::LOG,"Short 200ms key touch: %d", keycode);
keystatus = keycode;
keycode = 0;
keycodeold = 0;
keycode2 = 0; keycode2 = 0;
keycodeold2 = 0; keycodeold2 = 0;
buzzer(TONE4, 100);
keylock = false;
delay(keydelay);
} }
} }
// Timeout for very short pressed key
if(millis() > starttime + 200){ // System page with key 5 and 4 in fast series
keycode2 = 0; if (keycode2 == 5 && keycodeold2 == 4) {
} logger->logDebug(GwLog::LOG,"Keycode for system page");
// Detect a short keynumber (200ms)
if (keyoff == false && millis() > starttime + 200 && keycode == keycodeold && keylock == true) {
keystatus = keycode;
keycode = 0; keycode = 0;
keycodeold = 0; keycodeold = 0;
keycode2 = 0; keycode2 = 0;
keycodeold2 = 0; keycodeold2 = 0;
buzzer(TONE4, 100); keystatus = 12;
buzzer(TONE4, 50);
delay(30);
buzzer(TONE4, 50);
delay(30);
buzzer(TONE4, 50);
}
// Key lock with key 1 and 6 or 6 and 1 in fast series
if((keycode2 == 1 && keycodeold2 == 6) || (keycode2 == 6 && keycodeold2 == 1)) {
keycode = 0;
keycodeold = 0;
keycode2 = 0;
keycodeold2 = 0;
buzzer(TONE4, 1000);
keylock = false; keylock = false;
delay(keydelay); delay(keydelay);
keyoff = !keyoff;
keystatus = 11;
} }
// Detect swipe right
if (keyoff == false && keycode > 0 && keycodeold > 0 && keycode > keycodeold && !((keycode == 1 && keycodeold == 6) || (keycode == 6 && keycodeold == 1))){
//if (keycode > 0 && keycodeold > 0 && keycode > keycodeold){
keycode = 0;
keycodeold = 0;
keycode2 = 0;
keycodeold2 = 0;
keystatus = 9;
buzzer(TONE3, 150);
buzzer(TONE4, 150);
}
// Detect swipe left
if (keyoff == false && keycode > 0 && keycodeold > 0 && keycode < keycodeold && !((keycode == 1 && keycodeold == 6) || (keycode == 6 && keycodeold == 1))){
//if (keycode > 0 && keycodeold > 0 && keycode < keycodeold){
keycode = 0;
keycodeold = 0;
keycode2 = 0;
keycodeold2 = 0;
keystatus = 10;
buzzer(TONE4, 150);
buzzer(TONE3, 150);
}
// Reset keylock after release
if (keycode == 0){
keylock = false;
}
// Copy keycode
keycodeold = keycode;
keycodeold2 = keycode2;
return keystatus;
} }
#endif
// Key lock with key 1 and 6 or 6 and 1 in fast series #ifdef BOARD_OBP40S3
if((keycode2 == 1 && keycodeold2 == 6) || (keycode2 == 6 && keycodeold2 == 1)) { int readSensorpads(){
keycode = 0; // Read key code
keycodeold = 0; if(digitalRead(UP) == LOW){
keycode2 = 0; keycode = 10; // Left swipe
keycodeold2 = 0; }
buzzer(TONE4, 1000); else if(digitalRead(DOWN) == LOW){
keylock = false; keycode = 9; // Right swipe
delay(keydelay); }
keyoff = !keyoff; else if(digitalRead(CONF) == LOW){
keystatus = 11; keycode = 3; // Key 3
}
else if(digitalRead(MENUE) == LOW){
keycode = 1; // Key 1
}
else if(digitalRead(EXIT) == LOW){
keycode = 2; // Key 2
}
else{
keycode = 0; // No key activ
}
return keycode;
}
// Keypad functions for OBP60 clone (thSensitivity is inactiv)
int readKeypad(GwLog* logger, uint thSensitivity, bool use_syspage) {
pinMode(UP, INPUT);
pinMode(DOWN, INPUT);
pinMode(CONF, INPUT);
pinMode(MENUE, INPUT);
pinMode(EXIT, INPUT);
// Read pad values
readSensorpads();
// Detect key
if (keycode > 0 ){
if(keycode != keycodeold){
starttime = millis(); // Start key pressed
keycodeold = keycode;
}
// If key pressed longer than 100ms
if(millis() > starttime + 100 && keycode == keycodeold) {
if (use_syspage and keycode == 3) {
keystatus = 12;
} else {
keystatus = keycode;
}
// Copy keycode
keycodeold = keycode;
while(readSensorpads() > 0){} // Wait for pad release
delay(keydelay);
}
}
else{
keycode = 0;
keycodeold = 0;
keystatus = 0;
}
return keystatus;
} }
#endif
// Detect swipe right #endif
if (keyoff == false && keycode > 0 && keycodeold > 0 && keycode > keycodeold && !((keycode == 1 && keycodeold == 6) || (keycode == 6 && keycodeold == 1))){
//if (keycode > 0 && keycodeold > 0 && keycode > keycodeold){
keycode = 0;
keycodeold = 0;
keycode2 = 0;
keycodeold2 = 0;
keystatus = 9;
buzzer(TONE3, 150);
buzzer(TONE4, 150);
}
// Detect swipe left
if (keyoff == false && keycode > 0 && keycodeold > 0 && keycode < keycodeold && !((keycode == 1 && keycodeold == 6) || (keycode == 6 && keycodeold == 1))){
//if (keycode > 0 && keycodeold > 0 && keycode < keycodeold){
keycode = 0;
keycodeold = 0;
keycode2 = 0;
keycodeold2 = 0;
keystatus = 10;
buzzer(TONE4, 150);
buzzer(TONE3, 150);
}
// Reset keylock after release
if (keycode == 0){
keylock = false;
}
// Copy keycode
keycodeold = keycode;
keycodeold2 = keycode2;
return keystatus;
}
#endif

View File

@@ -35,7 +35,7 @@ void qrWiFi(String ssid, String passwd, uint16_t fgcolor, uint16_t bgcolor){
box_y = box_y + box_s; box_y = box_y + box_s;
box_x = init_x; box_x = init_x;
} }
getdisplay().setFont(&Ubuntu_Bold32pt7b); getdisplay().setFont(&Ubuntu_Bold32pt8b);
getdisplay().setTextColor(fgcolor); getdisplay().setTextColor(fgcolor);
getdisplay().setCursor(140, 285); getdisplay().setCursor(140, 285);
getdisplay().print("WiFi"); getdisplay().print("WiFi");

View File

@@ -0,0 +1,322 @@
#include "OBPDataOperations.h"
// --- Class HstryBuf ---------------
// Init history buffers for selected boat data
void HstryBuf::init(BoatValueList* boatValues, GwLog *log) {
logger = log;
int hstryUpdFreq = 1000; // Update frequency for history buffers in ms
int hstryMinVal = 0; // Minimum value for these history buffers
twdHstryMax = 6283; // Max value for wind direction (TWD, AWD) in rad [0...2*PI], shifted by 1000 for 3 decimals
twsHstryMax = 65000; // Max value for wind speed (TWS, AWS) in m/s [0..65], shifted by 1000 for 3 decimals
awdHstryMax = twdHstryMax;
awsHstryMax = twsHstryMax;
twdHstryMin = hstryMinVal;
twsHstryMin = hstryMinVal;
awdHstryMin = hstryMinVal;
awsHstryMin = hstryMinVal;
const double DBL_MAX = std::numeric_limits<double>::max();
// Initialize history buffers with meta data
hstryBufList.twdHstry->setMetaData("TWD", "formatCourse", hstryUpdFreq, hstryMinVal, twdHstryMax);
hstryBufList.twsHstry->setMetaData("TWS", "formatKnots", hstryUpdFreq, hstryMinVal, twsHstryMax);
hstryBufList.awdHstry->setMetaData("AWD", "formatCourse", hstryUpdFreq, hstryMinVal, twdHstryMax);
hstryBufList.awsHstry->setMetaData("AWS", "formatKnots", hstryUpdFreq, hstryMinVal, twsHstryMax);
// create boat values for history data types, if they don't exist yet
twdBVal = boatValues->findValueOrCreate(hstryBufList.twdHstry->getName());
twsBVal = boatValues->findValueOrCreate(hstryBufList.twsHstry->getName());
twaBVal = boatValues->findValueOrCreate("TWA");
awdBVal = boatValues->findValueOrCreate(hstryBufList.awdHstry->getName());
awsBVal = boatValues->findValueOrCreate(hstryBufList.awsHstry->getName());
if (!awdBVal->valid) { // AWD usually does not exist
awdBVal->setFormat(hstryBufList.awdHstry->getFormat());
awdBVal->value = DBL_MAX;
}
// collect boat values for true wind calculation
awaBVal = boatValues->findValueOrCreate("AWA");
hdtBVal = boatValues->findValueOrCreate("HDT");
hdmBVal = boatValues->findValueOrCreate("HDM");
varBVal = boatValues->findValueOrCreate("VAR");
cogBVal = boatValues->findValueOrCreate("COG");
sogBVal = boatValues->findValueOrCreate("SOG");
}
// Handle history buffers for TWD, TWS, AWD, AWS
//void HstryBuf::handleHstryBuf(GwApi* api, BoatValueList* boatValues, bool useSimuData) {
void HstryBuf::handleHstryBuf(bool useSimuData) {
static int16_t twd = 20; //initial value only relevant if we use simulation data
static uint16_t tws = 20; //initial value only relevant if we use simulation data
static double awd, aws, hdt = 20; //initial value only relevant if we use simulation data
GwApi::BoatValue *calBVal; // temp variable just for data calibration -> we don't want to calibrate the original data here
LOG_DEBUG(GwLog::DEBUG,"obp60task handleHstryBuf: TWD_isValid? %d, twdBVal: %.1f, twaBVal: %.1f, twsBVal: %.1f", twdBVal->valid, twdBVal->value * RAD_TO_DEG,
twaBVal->value * RAD_TO_DEG, twsBVal->value * 3.6 / 1.852);
if (twdBVal->valid) {
calBVal = new GwApi::BoatValue("TWD"); // temporary solution for calibration of history buffer values
calBVal->setFormat(twdBVal->getFormat());
calBVal->value = twdBVal->value;
calBVal->valid = twdBVal->valid;
calibrationData.calibrateInstance(calBVal, logger); // Check if boat data value is to be calibrated
twd = static_cast<int16_t>(std::round(calBVal->value * 1000.0));
if (twd >= twdHstryMin && twd <= twdHstryMax) {
hstryBufList.twdHstry->add(twd);
}
delete calBVal;
calBVal = nullptr;
} else if (useSimuData) {
twd += random(-20, 20);
twd = WindUtils::to360(twd);
hstryBufList.twdHstry->add(static_cast<int16_t>(DegToRad(twd) * 1000.0));
}
if (twsBVal->valid) {
calBVal = new GwApi::BoatValue("TWS"); // temporary solution for calibration of history buffer values
calBVal->setFormat(twsBVal->getFormat());
calBVal->value = twsBVal->value;
calBVal->valid = twsBVal->valid;
calibrationData.calibrateInstance(calBVal, logger); // Check if boat data value is to be calibrated
tws = static_cast<uint16_t>(std::round(calBVal->value * 1000));
if (tws >= twsHstryMin && tws <= twsHstryMax) {
hstryBufList.twsHstry->add(tws);
}
delete calBVal;
calBVal = nullptr;
} else if (useSimuData) {
tws += random(-5000, 5000); // TWS value in m/s; expands to 3 decimals
tws = constrain(tws, 0, 25000); // Limit TWS to [0..25] m/s
hstryBufList.twsHstry->add(tws);
}
if (awaBVal->valid) {
if (hdtBVal->valid) {
hdt = hdtBVal->value; // Use HDT if available
} else {
hdt = WindUtils::calcHDT(&hdmBVal->value, &varBVal->value, &cogBVal->value, &sogBVal->value);
}
awd = awaBVal->value + hdt;
awd = WindUtils::to2PI(awd);
calBVal = new GwApi::BoatValue("AWD"); // temporary solution for calibration of history buffer values
calBVal->value = awd;
calBVal->setFormat(awdBVal->getFormat());
calBVal->valid = true;
calibrationData.calibrateInstance(calBVal, logger); // Check if boat data value is to be calibrated
awdBVal->value = calBVal->value;
awdBVal->valid = true;
awd = std::round(calBVal->value * 1000.0);
if (awd >= awdHstryMin && awd <= awdHstryMax) {
hstryBufList.awdHstry->add(static_cast<int16_t>(awd));
}
delete calBVal;
calBVal = nullptr;
} else if (useSimuData) {
awd += random(-20, 20);
awd = WindUtils::to360(awd);
hstryBufList.awdHstry->add(static_cast<int16_t>(DegToRad(awd) * 1000.0));
}
if (awsBVal->valid) {
calBVal = new GwApi::BoatValue("AWS"); // temporary solution for calibration of history buffer values
calBVal->setFormat(awsBVal->getFormat());
calBVal->value = awsBVal->value;
calBVal->valid = awsBVal->valid;
calibrationData.calibrateInstance(calBVal, logger); // Check if boat data value is to be calibrated
aws = std::round(calBVal->value * 1000);
if (aws >= awsHstryMin && aws <= awsHstryMax) {
hstryBufList.awsHstry->add(static_cast<uint16_t>(aws));
}
delete calBVal;
calBVal = nullptr;
} else if (useSimuData) {
aws += random(-5000, 5000); // TWS value in m/s; expands to 1 decimal
aws = constrain(aws, 0, 25000); // Limit TWS to [0..25] m/s
hstryBufList.awsHstry->add(aws);
}
}
// --- Class HstryBuf ---------------
// --- Class WindUtils --------------
double WindUtils::to2PI(double a)
{
a = fmod(a, 2 * M_PI);
if (a < 0.0) {
a += 2 * M_PI;
}
return a;
}
double WindUtils::toPI(double a)
{
a += M_PI;
a = to2PI(a);
a -= M_PI;
return a;
}
double WindUtils::to360(double a)
{
a = fmod(a, 360);
if (a < 0.0) {
a += 360;
}
return a;
}
double WindUtils::to180(double a)
{
a += 180;
a = to360(a);
a -= 180;
return a;
}
void WindUtils::toCart(const double* phi, const double* r, double* x, double* y)
{
*x = *r * sin(*phi);
*y = *r * cos(*phi);
}
void WindUtils::toPol(const double* x, const double* y, double* phi, double* r)
{
*phi = (M_PI / 2) - atan2(*y, *x);
*phi = to2PI(*phi);
*r = sqrt(*x * *x + *y * *y);
}
void WindUtils::addPolar(const double* phi1, const double* r1,
const double* phi2, const double* r2,
double* phi, double* r)
{
double x1, y1, x2, y2;
toCart(phi1, r1, &x1, &y1);
toCart(phi2, r2, &x2, &y2);
x1 += x2;
y1 += y2;
toPol(&x1, &y1, phi, r);
}
void WindUtils::calcTwdSA(const double* AWA, const double* AWS,
const double* CTW, const double* STW, const double* HDT,
double* TWD, double* TWS, double* TWA)
{
double awd = *AWA + *HDT;
awd = to2PI(awd);
double stw = -*STW;
addPolar(&awd, AWS, CTW, &stw, TWD, TWS);
// Normalize TWD and TWA to 0-360°
*TWD = to2PI(*TWD);
*TWA = toPI(*TWD - *HDT);
}
double WindUtils::calcHDT(const double* hdmVal, const double* varVal, const double* cogVal, const double* sogVal)
{
double hdt;
double minSogVal = 0.1; // SOG below this value (m/s) is assumed to be data noise from GPS sensor
if (*hdmVal != DBL_MAX) {
hdt = *hdmVal + (*varVal != DBL_MAX ? *varVal : 0.0); // Use corrected HDM if HDT is not available (or just HDM if VAR is not available)
hdt = to2PI(hdt);
} else if (*cogVal != DBL_MAX && *sogVal >= minSogVal) {
hdt = *cogVal; // Use COG as fallback if HDT and HDM are not available, and SOG is not data noise
} else {
hdt = DBL_MAX; // Cannot calculate HDT without valid HDM or HDM+VAR or COG
}
return hdt;
}
bool WindUtils::calcTrueWind(const double* awaVal, const double* awsVal,
const double* cogVal, const double* stwVal, const double* sogVal, const double* hdtVal,
const double* hdmVal, const double* varVal, double* twdVal, double* twsVal, double* twaVal)
{
double stw, hdt, ctw;
double twd, tws, twa;
double minSogVal = 0.1; // SOG below this value (m/s) is assumed to be data noise from GPS sensor
if (*hdtVal != DBL_MAX) {
hdt = *hdtVal; // Use HDT if available
} else {
hdt = calcHDT(hdmVal, varVal, cogVal, sogVal);
}
if (*cogVal != DBL_MAX && *sogVal >= minSogVal) { // if SOG is data noise, we don't trust COG
ctw = *cogVal; // Use COG for CTW if available
} else {
ctw = hdt; // 2nd approximation for CTW; hdt must exist if we reach this part of the code
}
if (*stwVal != DBL_MAX) {
stw = *stwVal; // Use STW if available
} else if (*sogVal != DBL_MAX) {
stw = *sogVal;
} else {
// If STW and SOG are not available, we cannot calculate true wind
return false;
}
// Serial.println("\ncalcTrueWind: HDT: " + String(hdt) + ", CTW: " + String(ctw) + ", STW: " + String(stw));
if ((*awaVal == DBL_MAX) || (*awsVal == DBL_MAX)) {
// Cannot calculate true wind without valid AWA, AWS; other checks are done earlier
return false;
} else {
calcTwdSA(awaVal, awsVal, &ctw, &stw, &hdt, &twd, &tws, &twa);
*twdVal = twd;
*twsVal = tws;
*twaVal = twa;
return true;
}
}
// Calculate true wind data and add to obp60task boat data list
bool WindUtils::addTrueWind(GwApi* api, BoatValueList* boatValues, GwLog* log) {
GwLog* logger = log;
double awaVal, awsVal, cogVal, stwVal, sogVal, hdtVal, hdmVal, varVal;
double twd, tws, twa;
bool isCalculated = false;
awaVal = awaBVal->valid ? awaBVal->value : DBL_MAX;
awsVal = awsBVal->valid ? awsBVal->value : DBL_MAX;
cogVal = cogBVal->valid ? cogBVal->value : DBL_MAX;
stwVal = stwBVal->valid ? stwBVal->value : DBL_MAX;
sogVal = sogBVal->valid ? sogBVal->value : DBL_MAX;
hdtVal = hdtBVal->valid ? hdtBVal->value : DBL_MAX;
hdmVal = hdmBVal->valid ? hdmBVal->value : DBL_MAX;
varVal = varBVal->valid ? varBVal->value : DBL_MAX;
LOG_DEBUG(GwLog::DEBUG,"obp60task addTrueWind: AWA %.1f, AWS %.1f, COG %.1f, STW %.1f, SOG %.2f, HDT %.1f, HDM %.1f, VAR %.1f", awaBVal->value * RAD_TO_DEG, awsBVal->value * 3.6 / 1.852,
cogBVal->value * RAD_TO_DEG, stwBVal->value * 3.6 / 1.852, sogBVal->value * 3.6 / 1.852, hdtBVal->value * RAD_TO_DEG, hdmBVal->value * RAD_TO_DEG, varBVal->value * RAD_TO_DEG);
isCalculated = calcTrueWind(&awaVal, &awsVal, &cogVal, &stwVal, &sogVal, &hdtVal, &hdmVal, &varVal, &twd, &tws, &twa);
if (isCalculated) { // Replace values only, if successfully calculated and not already available
if (!twdBVal->valid) {
twdBVal->value = twd;
twdBVal->valid = true;
}
if (!twsBVal->valid) {
twsBVal->value = tws;
twsBVal->valid = true;
}
if (!twaBVal->valid) {
twaBVal->value = twa;
twaBVal->valid = true;
}
}
LOG_DEBUG(GwLog::DEBUG,"obp60task addTrueWind: isCalculated %d, TWD %.1f, TWA %.1f, TWS %.1f", isCalculated, twdBVal->value * RAD_TO_DEG,
twaBVal->value * RAD_TO_DEG, twsBVal->value * 3.6 / 1.852);
return isCalculated;
}
// --- Class WindUtils --------------

View File

@@ -0,0 +1,91 @@
#pragma once
#include <N2kMessages.h>
#include "OBPRingBuffer.h"
#include "BoatDataCalibration.h" // Functions lib for data instance calibration
#include "obp60task.h"
#include <math.h>
typedef struct {
RingBuffer<int16_t>* twdHstry;
RingBuffer<uint16_t>* twsHstry;
RingBuffer<int16_t>* awdHstry;
RingBuffer<uint16_t>* awsHstry;
} tBoatHstryData; // Holds pointers to all history buffers for boat data
class HstryBuf {
private:
GwLog *logger;
RingBuffer<int16_t> twdHstry; // Circular buffer to store true wind direction values
RingBuffer<uint16_t> twsHstry; // Circular buffer to store true wind speed values (TWS)
RingBuffer<int16_t> awdHstry; // Circular buffer to store apparant wind direction values
RingBuffer<uint16_t> awsHstry; // Circular buffer to store apparant xwind speed values (AWS)
int16_t twdHstryMin; // Min value for wind direction (TWD) in history buffer
int16_t twdHstryMax; // Max value for wind direction (TWD) in history buffer
uint16_t twsHstryMin;
uint16_t twsHstryMax;
int16_t awdHstryMin;
int16_t awdHstryMax;
uint16_t awsHstryMin;
uint16_t awsHstryMax;
// boat values for buffers and for true wind calculation
GwApi::BoatValue *twdBVal, *twsBVal, *twaBVal, *awdBVal, *awsBVal;
GwApi::BoatValue *awaBVal, *hdtBVal, *hdmBVal, *varBVal, *cogBVal, *sogBVal;
public:
tBoatHstryData hstryBufList;
HstryBuf(){
hstryBufList = {&twdHstry, &twsHstry, &awdHstry, &awsHstry}; // Generate history buffers of zero size
};
HstryBuf(int size) {
hstryBufList = {&twdHstry, &twsHstry, &awdHstry, &awsHstry};
hstryBufList.twdHstry->resize(size); // store <size> xWD values for <size>/60 minutes history
hstryBufList.twsHstry->resize(size);
hstryBufList.awdHstry->resize(size);
hstryBufList.awsHstry->resize(size);
};
void init(BoatValueList* boatValues, GwLog *log);
void handleHstryBuf(bool useSimuData);
};
class WindUtils {
private:
GwApi::BoatValue *twdBVal, *twsBVal, *twaBVal;
GwApi::BoatValue *awaBVal, *awsBVal, *cogBVal, *stwBVal, *sogBVal, *hdtBVal, *hdmBVal, *varBVal;
static constexpr double DBL_MAX = std::numeric_limits<double>::max();
public:
WindUtils(BoatValueList* boatValues){
twdBVal = boatValues->findValueOrCreate("TWD");
twsBVal = boatValues->findValueOrCreate("TWS");
twaBVal = boatValues->findValueOrCreate("TWA");
awaBVal = boatValues->findValueOrCreate("AWA");
awsBVal = boatValues->findValueOrCreate("AWS");
cogBVal = boatValues->findValueOrCreate("COG");
stwBVal = boatValues->findValueOrCreate("STW");
sogBVal = boatValues->findValueOrCreate("SOG");
hdtBVal = boatValues->findValueOrCreate("HDT");
hdmBVal = boatValues->findValueOrCreate("HDM");
varBVal = boatValues->findValueOrCreate("VAR");
};
static double to2PI(double a);
static double toPI(double a);
static double to360(double a);
static double to180(double a);
void toCart(const double* phi, const double* r, double* x, double* y);
void toPol(const double* x, const double* y, double* phi, double* r);
void addPolar(const double* phi1, const double* r1,
const double* phi2, const double* r2,
double* phi, double* r);
void calcTwdSA(const double* AWA, const double* AWS,
const double* CTW, const double* STW, const double* HDT,
double* TWD, double* TWS, double* TWA);
static double calcHDT(const double* hdmVal, const double* varVal, const double* cogVal, const double* sogVal);
bool calcTrueWind(const double* awaVal, const double* awsVal,
const double* cogVal, const double* stwVal, const double* sogVal, const double* hdtVal,
const double* hdmVal, const double* varVal, double* twdVal, double* twsVal, double* twaVal);
bool addTrueWind(GwApi* api, BoatValueList* boatValues, GwLog *log);
};

View File

@@ -0,0 +1,99 @@
#pragma once
#include "GwSynchronized.h"
#include "WString.h"
#include "esp_heap_caps.h"
#include <algorithm>
#include <limits>
#include <stdexcept>
#include <vector>
template <typename T>
struct PSRAMAllocator {
using value_type = T;
PSRAMAllocator() = default;
template <class U>
constexpr PSRAMAllocator(const PSRAMAllocator<U>&) noexcept { }
T* allocate(std::size_t n)
{
void* ptr = heap_caps_malloc(n * sizeof(T), MALLOC_CAP_SPIRAM);
if (!ptr) {
return nullptr;
} else {
return static_cast<T*>(ptr);
}
}
void deallocate(T* p, std::size_t) noexcept
{
heap_caps_free(p);
}
};
template <class T, class U>
bool operator==(const PSRAMAllocator<T>&, const PSRAMAllocator<U>&) { return true; }
template <class T, class U>
bool operator!=(const PSRAMAllocator<T>&, const PSRAMAllocator<U>&) { return false; }
template <typename T>
class RingBuffer {
private:
// std::vector<T> buffer; // THE buffer vector
std::vector<T, PSRAMAllocator<T>> buffer; // THE buffer vector, allocated in PSRAM
size_t capacity;
size_t head; // Points to the next insertion position
size_t first; // Points to the first (oldest) valid element
size_t last; // Points to the last (newest) valid element
size_t count; // Number of valid elements currently in buffer
bool is_Full; // Indicates that all buffer elements are used and ringing is in use
T MIN_VAL; // lowest possible value of buffer of type <T>
T MAX_VAL; // highest possible value of buffer of type <T> -> indicates invalid value in buffer
mutable SemaphoreHandle_t bufLocker;
// metadata for buffer
String dataName; // Name of boat data in buffer
String dataFmt; // Format of boat data in buffer
int updFreq; // Update frequency in milliseconds
T smallest; // Value range of buffer: smallest value; needs to be => MIN_VAL
T largest; // Value range of buffer: biggest value; needs to be < MAX_VAL, since MAX_VAL indicates invalid entries
void initCommon();
public:
RingBuffer();
RingBuffer(size_t size);
void setMetaData(String name, String format, int updateFrequency, T minValue, T maxValue); // Set meta data for buffer
bool getMetaData(String& name, String& format, int& updateFrequency, T& minValue, T& maxValue); // Get meta data of buffer
bool getMetaData(String& name, String& format);
String getName() const; // Get buffer name
String getFormat() const; // Get buffer data format
void add(const T& value); // Add a new value to buffer
T get(size_t index) const; // Get value at specific position (0-based index from oldest to newest)
T getFirst() const; // Get the first (oldest) value in buffer
T getLast() const; // Get the last (newest) value in buffer
T getMin() const; // Get the lowest value in buffer
T getMin(size_t amount) const; // Get minimum value of the last <amount> values of buffer
T getMax() const; // Get the highest value in buffer
T getMax(size_t amount) const; // Get maximum value of the last <amount> values of buffer
T getMid() const; // Get mid value between <min> and <max> value in buffer
T getMid(size_t amount) const; // Get mid value between <min> and <max> value of the last <amount> values of buffer
T getMedian() const; // Get the median value in buffer
T getMedian(size_t amount) const; // Get the median value of the last <amount> values of buffer
size_t getCapacity() const; // Get the buffer capacity (maximum size)
size_t getCurrentSize() const; // Get the current number of elements in buffer
size_t getFirstIdx() const; // Get the index of oldest value in buffer
size_t getLastIdx() const; // Get the index of newest value in buffer
bool isEmpty() const; // Check if buffer is empty
bool isFull() const; // Check if buffer is full
T getMinVal() const; // Get lowest possible value for buffer
T getMaxVal() const; // Get highest possible value for buffer; used for unset/invalid buffer data
void clear(); // Clear buffer
void resize(size_t size); // Delete buffer and set new size
T operator[](size_t index) const; // Operator[] for convenient access (same as get())
std::vector<T> getAllValues() const; // Get all current values as a vector
};
#include "OBPRingBuffer.tpp"

View File

@@ -0,0 +1,426 @@
#include "OBPRingBuffer.h"
template <typename T>
void RingBuffer<T>::initCommon() {
MIN_VAL = std::numeric_limits<T>::lowest();
MAX_VAL = std::numeric_limits<T>::max();
dataName = "";
dataFmt = "";
updFreq = -1;
smallest = MIN_VAL;
largest = MAX_VAL;
bufLocker = xSemaphoreCreateMutex();
}
template <typename T>
RingBuffer<T>::RingBuffer()
: capacity(0)
, head(0)
, first(0)
, last(0)
, count(0)
, is_Full(false)
{
initCommon();
// <buffer> stays empty
}
template <typename T>
RingBuffer<T>::RingBuffer(size_t size)
: capacity(size)
, head(0)
, first(0)
, last(0)
, count(0)
, is_Full(false)
{
initCommon();
buffer.reserve(size);
buffer.resize(size, MAX_VAL); // MAX_VAL indicate invalid values
}
// Specify meta data of buffer content
template <typename T>
void RingBuffer<T>::setMetaData(String name, String format, int updateFrequency, T minValue, T maxValue)
{
GWSYNCHRONIZED(&bufLocker);
dataName = name;
dataFmt = format;
updFreq = updateFrequency;
smallest = std::max(MIN_VAL, minValue);
largest = std::min(MAX_VAL, maxValue);
}
// Get meta data of buffer content
template <typename T>
bool RingBuffer<T>::getMetaData(String& name, String& format, int& updateFrequency, T& minValue, T& maxValue)
{
if (dataName == "" || dataFmt == "" || updFreq == -1) {
return false; // Meta data not set
}
GWSYNCHRONIZED(&bufLocker);
name = dataName;
format = dataFmt;
updateFrequency = updFreq;
minValue = smallest;
maxValue = largest;
return true;
}
// Get meta data of buffer content
template <typename T>
bool RingBuffer<T>::getMetaData(String& name, String& format)
{
if (dataName == "" || dataFmt == "") {
return false; // Meta data not set
}
GWSYNCHRONIZED(&bufLocker);
name = dataName;
format = dataFmt;
return true;
}
// Get buffer name
template <typename T>
String RingBuffer<T>::getName() const
{
return dataName;
}
// Get buffer data format
template <typename T>
String RingBuffer<T>::getFormat() const
{
return dataFmt;
}
// Add a new value to buffer
template <typename T>
void RingBuffer<T>::add(const T& value)
{
GWSYNCHRONIZED(&bufLocker);
if (value < smallest || value > largest) {
buffer[head] = MAX_VAL; // Store MAX_VAL if value is out of range
} else {
buffer[head] = value;
}
last = head;
if (is_Full) {
first = (first + 1) % capacity; // Move pointer to oldest element when overwriting
} else {
count++;
if (count == capacity) {
is_Full = true;
}
}
head = (head + 1) % capacity;
}
// Get value at specific position (0-based index from oldest to newest)
template <typename T>
T RingBuffer<T>::get(size_t index) const
{
GWSYNCHRONIZED(&bufLocker);
if (isEmpty() || index < 0 || index >= count) {
return MAX_VAL;
}
size_t realIndex = (first + index) % capacity;
return buffer[realIndex];
}
// Operator[] for convenient access (same as get())
template <typename T>
T RingBuffer<T>::operator[](size_t index) const
{
return get(index);
}
// Get the first (oldest) value in the buffer
template <typename T>
T RingBuffer<T>::getFirst() const
{
if (isEmpty()) {
return MAX_VAL;
}
return get(0);
}
// Get the last (newest) value in the buffer
template <typename T>
T RingBuffer<T>::getLast() const
{
if (isEmpty()) {
return MAX_VAL;
}
return get(count - 1);
}
// Get the lowest value in the buffer
template <typename T>
T RingBuffer<T>::getMin() const
{
if (isEmpty()) {
return MAX_VAL;
}
T minVal = MAX_VAL;
T value;
for (size_t i = 0; i < count; i++) {
value = get(i);
if (value < minVal && value != MAX_VAL) {
minVal = value;
}
}
return minVal;
}
// Get minimum value of the last <amount> values of buffer
template <typename T>
T RingBuffer<T>::getMin(size_t amount) const
{
if (isEmpty() || amount <= 0) {
return MAX_VAL;
}
if (amount > count)
amount = count;
T minVal = MAX_VAL;
T value;
for (size_t i = 0; i < amount; i++) {
value = get(count - 1 - i);
if (value < minVal && value != MAX_VAL) {
minVal = value;
}
}
return minVal;
}
// Get the highest value in the buffer
template <typename T>
T RingBuffer<T>::getMax() const
{
if (isEmpty()) {
return MAX_VAL;
}
T maxVal = MIN_VAL;
T value;
for (size_t i = 0; i < count; i++) {
value = get(i);
if (value > maxVal && value != MAX_VAL) {
maxVal = value;
}
}
return maxVal;
}
// Get maximum value of the last <amount> values of buffer
template <typename T>
T RingBuffer<T>::getMax(size_t amount) const
{
if (isEmpty() || amount <= 0) {
return MAX_VAL;
}
if (amount > count)
amount = count;
T maxVal = MIN_VAL;
T value;
for (size_t i = 0; i < amount; i++) {
value = get(count - 1 - i);
if (value > maxVal && value != MAX_VAL) {
maxVal = value;
}
}
return maxVal;
}
// Get mid value between <min> and <max> value in the buffer
template <typename T>
T RingBuffer<T>::getMid() const
{
if (isEmpty()) {
return MAX_VAL;
}
return (getMin() + getMax()) / static_cast<T>(2);
}
// Get mid value between <min> and <max> value of the last <amount> values of buffer
template <typename T>
T RingBuffer<T>::getMid(size_t amount) const
{
if (isEmpty() || amount <= 0) {
return MAX_VAL;
}
if (amount > count)
amount = count;
return (getMin(amount) + getMax(amount)) / static_cast<T>(2);
}
// Get the median value in the buffer
template <typename T>
T RingBuffer<T>::getMedian() const
{
if (isEmpty()) {
return MAX_VAL;
}
// Create a temporary vector with current valid elements
std::vector<T> temp;
temp.reserve(count);
for (size_t i = 0; i < count; i++) {
temp.push_back(get(i));
}
// Sort to find median
std::sort(temp.begin(), temp.end());
if (count % 2 == 1) {
// Odd number of elements
return temp[count / 2];
} else {
// Even number of elements - return average of middle two
// Note: For integer types, this truncates. For floating point, it's exact.
return (temp[count / 2 - 1] + temp[count / 2]) / 2;
}
}
// Get the median value of the last <amount> values of buffer
template <typename T>
T RingBuffer<T>::getMedian(size_t amount) const
{
if (isEmpty() || amount <= 0) {
return MAX_VAL;
}
if (amount > count)
amount = count;
// Create a temporary vector with current valid elements
std::vector<T> temp;
temp.reserve(amount);
for (size_t i = 0; i < amount; i++) {
temp.push_back(get(i));
}
// Sort to find median
std::sort(temp.begin(), temp.end());
if (amount % 2 == 1) {
// Odd number of elements
return temp[amount / 2];
} else {
// Even number of elements - return average of middle two
// Note: For integer types, this truncates. For floating point, it's exact.
return (temp[amount / 2 - 1] + temp[amount / 2]) / 2;
}
}
// Get the buffer capacity (maximum size)
template <typename T>
size_t RingBuffer<T>::getCapacity() const
{
return capacity;
}
// Get the current number of elements in the buffer
template <typename T>
size_t RingBuffer<T>::getCurrentSize() const
{
return count;
}
// Get the first index of buffer
template <typename T>
size_t RingBuffer<T>::getFirstIdx() const
{
return first;
}
// Get the last index of buffer
template <typename T>
size_t RingBuffer<T>::getLastIdx() const
{
return last;
}
// Check if buffer is empty
template <typename T>
bool RingBuffer<T>::isEmpty() const
{
return count == 0;
}
// Check if buffer is full
template <typename T>
bool RingBuffer<T>::isFull() const
{
return is_Full;
}
// Get lowest possible value for buffer
template <typename T>
T RingBuffer<T>::getMinVal() const
{
return MIN_VAL;
}
// Get highest possible value for buffer; used for unset/invalid buffer data
template <typename T>
T RingBuffer<T>::getMaxVal() const
{
return MAX_VAL;
}
// Clear buffer
template <typename T>
void RingBuffer<T>::clear()
{
GWSYNCHRONIZED(&bufLocker);
head = 0;
first = 0;
last = 0;
count = 0;
is_Full = false;
}
// Delete buffer and set new size
template <typename T>
void RingBuffer<T>::resize(size_t newSize)
{
GWSYNCHRONIZED(&bufLocker);
capacity = newSize;
head = 0;
first = 0;
last = 0;
count = 0;
is_Full = false;
buffer.clear();
buffer.reserve(newSize);
buffer.resize(newSize, MAX_VAL);
}
// Get all current values as a vector
template <typename T>
std::vector<T> RingBuffer<T>::getAllValues() const
{
std::vector<T> result;
result.reserve(count);
for (size_t i = 0; i < count; i++) {
result.push_back(get(i));
}
return result;
}

View File

@@ -1,4 +1,4 @@
#ifdef BOARD_OBP60S3 #if defined BOARD_OBP60S3 || defined BOARD_OBP40S3
#include <Adafruit_Sensor.h> // Adafruit Lib for sensors #include <Adafruit_Sensor.h> // Adafruit Lib for sensors
#include <Adafruit_BME280.h> // Adafruit Lib for BME280 #include <Adafruit_BME280.h> // Adafruit Lib for BME280
#include <Adafruit_BMP280.h> // Adafruit Lib for BMP280 #include <Adafruit_BMP280.h> // Adafruit Lib for BMP280
@@ -17,9 +17,11 @@
#include "ObpNmea0183.h" // Check NMEA0183 sentence for uncorrect content #include "ObpNmea0183.h" // Check NMEA0183 sentence for uncorrect content
#include "OBP60Extensions.h" // Lib for hardware extensions #include "OBP60Extensions.h" // Lib for hardware extensions
#include "movingAvg.h" // Lib for moving average building #include "movingAvg.h" // Lib for moving average building
#include "time.h" // For getting NTP time
#include <ESP32Time.h> // Internal ESP32 RTC clock
// Timer for hardware functions // Timer for hardware functions
Ticker Timer1(blinkingFlashLED, 500); // Satrt Timer1 for flash LED all 500ms Ticker Timer1(blinkingFlashLED, 500); // Start Timer1 for flash LED all 500ms
// Initialization for all sensors (RS232, I2C, 1Wire, IOs) // Initialization for all sensors (RS232, I2C, 1Wire, IOs)
//#################################################################################### //####################################################################################
@@ -88,8 +90,16 @@ void sensorTask(void *param){
double voffset = (api->getConfig()->getConfigItem(api->getConfig()->vOffset,true)->asString()).toFloat(); double voffset = (api->getConfig()->getConfigItem(api->getConfig()->vOffset,true)->asString()).toFloat();
double vslope = (api->getConfig()->getConfigItem(api->getConfig()->vSlope,true)->asString()).toFloat(); double vslope = (api->getConfig()->getConfigItem(api->getConfig()->vSlope,true)->asString()).toFloat();
if(String(powsensor1) == "off"){ if(String(powsensor1) == "off"){
sensors.batteryVoltage = (float(analogRead(OBP_ANALOG0)) * 3.3 / 4096 + 0.17) * 20; // Vin = 1/20 #ifdef VOLTAGE_SENSOR
sensors.batteryVoltage = sensors.batteryVoltage * vslope + voffset; // Calibration float rawVoltage = (float(analogRead(OBP_ANALOG0)) * 3.3 / 4096 + 0.53) * 2; // Vin = 1/2 for OBP40
#else
float rawVoltage = (float(analogRead(OBP_ANALOG0)) * 3.3 / 4096 + 0.17) * 20; // Vin = 1/20 for OBP60
#endif
sensors.batteryVoltage = rawVoltage * vslope + voffset; // Calibration
#ifdef LIPO_ACCU_1200
sensors.BatteryChargeStatus = 0; // Set to discharging
sensors.batteryLevelLiPo = 0; // Level 0...100%
#endif
sensors.batteryCurrent = 0; sensors.batteryCurrent = 0;
sensors.batteryPower = 0; sensors.batteryPower = 0;
// Fill average arrays with start values // Fill average arrays with start values
@@ -142,6 +152,7 @@ void sensorTask(void *param){
// ds1388.adjust(DateTime(__DATE__, __TIME__)); // Set date and time from PC file time // ds1388.adjust(DateTime(__DATE__, __TIME__)); // Set date and time from PC file time
} }
RTC_ready = true; RTC_ready = true;
sensors.rtcValid = true;
} }
} }
@@ -358,6 +369,28 @@ void sensorTask(void *param){
GwApi::BoatValue *hdop=new GwApi::BoatValue(GwBoatData::_HDOP); GwApi::BoatValue *hdop=new GwApi::BoatValue(GwBoatData::_HDOP);
GwApi::BoatValue *valueList[]={gpsdays, gpsseconds, hdop}; GwApi::BoatValue *valueList[]={gpsdays, gpsseconds, hdop};
// Internal RTC with NTP init
ESP32Time rtc(0);
if (api->getConfig()->getString(api->getConfig()->timeSource) == "iRTC") {
GwApi::Status status;
api->getStatus(status);
if (status.wifiClientConnected) {
const char *ntpServer = api->getConfig()->getCString(api->getConfig()->timeServer);
api->getLogger()->logDebug(GwLog::LOG,"Fetching date and time from NTP server '%s'.", ntpServer);
configTime(0, 0, ntpServer); // get time in UTC
struct tm timeinfo;
if (getLocalTime(&timeinfo)) {
api->getLogger()->logDebug(GwLog::LOG,"NTP time: %04d-%02d-%02d %02d:%02d:%02d UTC", timeinfo.tm_year+1900, timeinfo.tm_mon+1, timeinfo.tm_mday, timeinfo.tm_hour, timeinfo.tm_min, timeinfo.tm_sec);
rtc.setTimeStruct(timeinfo);
sensors.rtcValid = true;
} else {
api->getLogger()->logDebug(GwLog::LOG,"NTP time fetch failed!");
}
} else {
api->getLogger()->logDebug(GwLog::LOG,"Wifi client not connected, NTP not available.");
}
}
// Sensor task loop runs with 100ms // Sensor task loop runs with 100ms
//#################################################################################### //####################################################################################
@@ -420,58 +453,116 @@ void sensorTask(void *param){
loopCounter++; loopCounter++;
} }
// If GPS not ready or installed then send RTC time on bus all 500ms // Get current RTC date and time all 500ms
if(millis() > starttime12 + 500){ if (millis() > starttime12 + 500) {
starttime12 = millis(); starttime12 = millis();
if((rtcOn == "DS1388" && RTC_ready == true && GPS_ready == false) || (rtcOn == "DS1388" && RTC_ready == true && GPS_ready == true && hdop->valid == false)){ if (rtcOn == "DS1388" && RTC_ready) {
// Convert RTC time to Unix system time DateTime dt = ds1388.now();
// https://de.wikipedia.org/wiki/Unixzeit sensors.rtcTime.tm_year = dt.year() - 1900; // Save values in SensorData
const short daysOfYear[12] = {0,31,59,90,120,151,181,212,243,273,304,334}; sensors.rtcTime.tm_mon = dt.month() - 1;
long unixtime = ds1388.now().get(); sensors.rtcTime.tm_mday = dt.day();
uint16_t year = ds1388.now().year(); sensors.rtcTime.tm_hour = dt.hour();
uint8_t month = ds1388.now().month(); sensors.rtcTime.tm_min = dt.minute();
uint8_t hour = ds1388.now().hour(); sensors.rtcTime.tm_sec = dt.second();
uint8_t minute = ds1388.now().minute(); sensors.rtcTime.tm_isdst = 0; // Not considering daylight saving time
uint8_t second = ds1388.now().second();
uint8_t day = ds1388.now().day(); // If GPS not ready or installed then send RTC time on bus
uint16_t switchYear = ((year-1)-1968)/4 - ((year-1)-1900)/100 + ((year-1)-1600)/400; // TODO If there are other time sources on the bus there should
long daysAt1970 = (year-1970)*365 + switchYear + daysOfYear[month-1] + day-1; // be a logic not to send or to send with lower frequency
// If switch year then add one day // or something totally different
if ( (month>2) && (year%4==0 && (year%100!=0 || year%400==0)) ){ if ((GPS_ready == false) || (GPS_ready == true && hdop->valid == false)) {
daysAt1970 += 1; // TODO implement daysAt1970 and sysTime as methods of DateTime
} const short daysOfYear[12] = {0,31,59,90,120,151,181,212,243,273,304,334};
double sysTime = (hour * 3600) + (minute * 60) + second; uint16_t switchYear = ((dt.year()-1)-1968)/4 - ((dt.year()-1)-1900)/100 + ((dt.year()-1)-1600)/400;
if(!isnan(daysAt1970) && !isnan(sysTime)){ long daysAt1970 = (dt.year()-1970)*365 + switchYear + daysOfYear[dt.month()-1] + dt.day()-1;
sensors.rtcYear = year; // Save values in SensorData // If switch year then add one day
sensors.rtcMonth = month; if ((dt.month() > 2) && (dt.year() % 4 == 0 && (dt.year() % 100 != 0 || dt.year() % 400 == 0))) {
sensors.rtcDay = day; daysAt1970 += 1;
sensors.rtcHour = hour; }
sensors.rtcMinute = minute; // N2K sysTime is double in n2klib
sensors.rtcSecond = second; double sysTime = (dt.hour() * 3600) + (dt.minute() * 60) + dt.second();
// api->getLogger()->logDebug(GwLog::LOG,"RTC time: %04d/%02d/%02d %02d:%02d:%02d",year, month, day, hour, minute, second); // WHY? isnan should always fail here
// api->getLogger()->logDebug(GwLog::LOG,"Send PGN126992: %10d %10d",daysAt1970, (uint16_t)sysTime); //if(!isnan(daysAt1970) && !isnan(sysTime)){
SetN2kPGN126992(N2kMsg,0,daysAt1970,sysTime,N2ktimes_LocalCrystalClock); //api->getLogger()->logDebug(GwLog::LOG,"RTC time: %04d/%02d/%02d %02d:%02d:%02d",sensors.rtcTime.tm_year+1900,sensors.rtcTime.tm_mon, sensors.rtcTime.tm_mday, sensors.rtcTime.tm_hour, sensors.rtcTime.tm_min, sensors.rtcTime.tm_sec);
api->sendN2kMessage(N2kMsg); //api->getLogger()->logDebug(GwLog::LOG,"Send PGN126992: %10d %10d",daysAt1970, (uint16_t)sysTime);
SetN2kPGN126992(N2kMsg,0,daysAt1970,sysTime,N2ktimes_LocalCrystalClock);
api->sendN2kMessage(N2kMsg);
// }
} }
} else if (sensors.rtcValid) {
// use internal rtc feature
sensors.rtcTime = rtc.getTimeStruct();
} }
} }
// Send supplay voltage value all 1s // Send supply voltage value all 1s
if(millis() > starttime5 + 1000 && String(powsensor1) == "off"){ if(millis() > starttime5 + 1000 && String(powsensor1) == "off"){
starttime5 = millis(); starttime5 = millis();
sensors.batteryVoltage = (float(analogRead(OBP_ANALOG0)) * 3.3 / 4096 + 0.17) * 20; // Vin = 1/20 float rawVoltage = 0; // Default value
sensors.batteryVoltage = sensors.batteryVoltage * vslope + voffset; // Calibration #ifdef BOARD_OBP40S3
sensors.batteryVoltage = 0; // If no sensor then zero voltage
#endif
#if defined(BOARD_OBP40S3) && defined(VOLTAGE_SENSOR)
rawVoltage = (float(analogRead(OBP_ANALOG0)) * 3.3 / 4096 + 0.53) * 2; // Vin = 1/2 for OBP40
sensors.batteryVoltage = rawVoltage * vslope + voffset; // Calibration
#endif
#ifdef BOARD_OBP60S3
rawVoltage = (float(analogRead(OBP_ANALOG0)) * 3.3 / 4096 + 0.17) * 20; // Vin = 1/20 for OBP60
sensors.batteryVoltage = rawVoltage * vslope + voffset; // Calibration
#endif
// Save new data in average array // Save new data in average array
batV.reading(int(sensors.batteryVoltage * 100)); batV.reading(int(sensors.batteryVoltage * 100));
// Calculate the average values for different time lines from integer values // Calculate the average values for different time lines from integer values
sensors.batteryVoltage10 = batV.getAvg(10) / 100.0; sensors.batteryVoltage10 = batV.getAvg(10) / 100.0;
sensors.batteryVoltage60 = batV.getAvg(60) / 100.0; sensors.batteryVoltage60 = batV.getAvg(60) / 100.0;
sensors.batteryVoltage300 = batV.getAvg(300) / 100.0; sensors.batteryVoltage300 = batV.getAvg(300) / 100.0;
#if BOARD_OBP40S3 && defined LIPO_ACCU_1200 && defined VOLTAGE_SENSOR
// Polynomfit for LiPo capacity calculation for 3,7V LiPo accus, 0...100%
sensors.batteryLevelLiPo = sensors.batteryVoltage60 * 203.8312 -738.1635;
// Limiter
if(sensors.batteryLevelLiPo > 100){
sensors.batteryLevelLiPo = 100;
}
if(sensors.batteryLevelLiPo < 0){
sensors.batteryLevelLiPo = 0;
}
// Charging detection
float deltaV = sensors.batteryVoltage - sensors.batteryVoltage10;
// Higher limits for lower voltages
if(sensors.batteryVoltage10 < 4.0){
if(deltaV > 0.045){
sensors.BatteryChargeStatus = 1; // Charging active
}
if(deltaV < -0.04){
sensors.BatteryChargeStatus = 0; // Discharging
}
}
// Lower limits for higher voltages
else{
if(deltaV > 0.03){
sensors.BatteryChargeStatus = 1; // Charging active
}
if(deltaV < -0.03){
sensors.BatteryChargeStatus = 0; // Discharging
}
}
// Charging stops by grater than 4,15V
if(sensors.batteryVoltage10 > 4.15){
sensors.BatteryChargeStatus = 0; // Discharging
}
// Send to NMEA200 bus as instance 10
if(!isnan(sensors.batteryVoltage)){
SetN2kDCBatStatus(N2kMsg, 10, sensors.batteryVoltage, N2kDoubleNA, N2kDoubleNA, 0);
api->sendN2kMessage(N2kMsg);
}
#endif
#ifdef BOARD_OBP60S3
// Send to NMEA200 bus // Send to NMEA200 bus
if(!isnan(sensors.batteryVoltage)){ if(!isnan(sensors.batteryVoltage)){
SetN2kDCBatStatus(N2kMsg, 0, sensors.batteryVoltage, N2kDoubleNA, N2kDoubleNA, 1); SetN2kDCBatStatus(N2kMsg, 0, sensors.batteryVoltage, N2kDoubleNA, N2kDoubleNA, 1);
api->sendN2kMessage(N2kMsg); api->sendN2kMessage(N2kMsg);
} }
#endif
} }
// Send data from environment sensor all 2s // Send data from environment sensor all 2s

View File

@@ -1,192 +0,0 @@
#ifdef BOARD_OBP60S3
#include "Pagedata.h"
#include "OBP60Extensions.h"
class PageApparentWind : public Page
{
bool keylock = false; // Keylock
int16_t lp = 80; // Pointer length
public:
PageApparentWind(CommonData &common){
common.logger->logDebug(GwLog::LOG,"Show PageApparentWind");
}
// Key functions
virtual int handleKey(int key){
// Reduce instrument size
if(key == 2){ // Code for reduce
lp = lp - 10;
if(lp < 10){
lp = 10;
}
return 0; // Commit the key
}
// Enlarge instrument size
if(key == 5){ // Code for enlarge
lp = lp + 10;
if(lp > 80){
lp = 80;
}
return 0; // Commit the key
}
// Keylock function
if(key == 11){ // Code for keylock
keylock = !keylock; // Toggle keylock
return 0; // Commit the key
}
return key;
}
virtual void displayPage(CommonData &commonData, PageData &pageData)
{
GwConfigHandler *config = commonData.config;
GwLog *logger=commonData.logger;
static String svalue1old = "";
static String unit1old = "";
static String svalue2old = "";
static String unit2old = "";
// Get config data
String lengthformat = config->getString(config->lengthFormat);
// bool simulation = config->getBool(config->useSimuData);
bool holdvalues = config->getBool(config->holdvalues);
String flashLED = config->getString(config->flashLED);
String backlightMode = config->getString(config->backlight);
// Get boat values for AWS
GwApi::BoatValue *bvalue1 = pageData.values[0]; // First element in list (only one value by PageOneValue)
String name1 = bvalue1->getName().c_str(); // Value name
name1 = name1.substring(0, 6); // String length limit for value name
double value1 = bvalue1->value; // Value as double in SI unit
// bool valid1 = bvalue1->valid; // Valid information
String svalue1 = formatValue(bvalue1, commonData).svalue; // Formatted value as string including unit conversion and switching decimal places
String unit1 = formatValue(bvalue1, commonData).unit; // Unit of value
// Get boat values for AWD
GwApi::BoatValue *bvalue2 = pageData.values[1]; // First element in list (only one value by PageOneValue)
String name2 = bvalue2->getName().c_str(); // Value name
name2 = name2.substring(0, 6); // String length limit for value name
double value2 = bvalue2->value; // Value as double in SI unit
// bool valid2 = bvalue2->valid; // Valid information
String svalue2 = formatValue(bvalue2, commonData).svalue; // Formatted value as string including unit conversion and switching decimal places
String unit2 = formatValue(bvalue2, commonData).unit; // Unit of value
// Optical warning by limit violation (unused)
if(String(flashLED) == "Limit Violation"){
setBlinkingLED(false);
setFlashLED(false);
}
// Logging boat values
if (bvalue1 == NULL) return;
LOG_DEBUG(GwLog::LOG,"Drawing at PageApparentWind, %s:%f, %s:%f", name1.c_str(), value1, name2.c_str(), value2);
// Draw page
//***********************************************************
// Set display in partial refresh mode
getdisplay().setPartialWindow(0, 0, getdisplay().width(), getdisplay().height()); // Set partial update
getdisplay().setTextColor(commonData.fgcolor);
// Show values AWS
getdisplay().setFont(&Ubuntu_Bold20pt7b);
getdisplay().setCursor(20, 50);
if(holdvalues == false){
getdisplay().print(name1); // Value name
getdisplay().print(": ");
getdisplay().print(svalue1); // Value
getdisplay().print(" ");
getdisplay().print(unit1); // Unit
}
else{
getdisplay().print(name1); // Value name
getdisplay().print(": ");
getdisplay().print(svalue1old); // Value old
getdisplay().print(" ");
getdisplay().print(unit1old); // Unit old
}
// Show values AWD
getdisplay().setFont(&Ubuntu_Bold20pt7b);
getdisplay().setCursor(20, 260);
if(holdvalues == false){
getdisplay().print(name2); // Value name
getdisplay().print(": ");
getdisplay().print(svalue2); // Value
getdisplay().print(" ");
getdisplay().print(unit2); // Unit
}
else{
getdisplay().print(name2); // Value name
getdisplay().print(": ");
getdisplay().print(svalue2old); // Value old
getdisplay().print(" ");
getdisplay().print(unit2old); // Unit old
}
// Draw wind pointer
static int16_t x0 = 200; // Center point
static int16_t y0 = 145;
static int16_t x1 = x0; // Start point for pointer
static int16_t y1 = y0;
static int16_t x2 = x0; // End point for pointer
static int16_t y2 = y0;
//Draw instrument
getdisplay().fillCircle(x0, y0, lp + 5, commonData.fgcolor);
getdisplay().fillCircle(x0, y0, lp + 1, commonData.bgcolor);
// Calculation end point of pointer
value2 = value2 - 3.14 / 2;
x1 = x0 + cos(value2) * lp * 0.6;
y1 = y0 + sin(value2) * lp * 0.6;
x2 = x0 + cos(value2) * lp;
y2 = y0 + sin(value2) * lp;
getdisplay().drawLine(x1, y1, x2, y2, commonData.fgcolor);
// Key Layout
getdisplay().setFont(&Ubuntu_Bold8pt7b);
if(keylock == false){
getdisplay().setCursor(130, 290);
getdisplay().print("[ <<<< " + String(commonData.data.actpage) + "/" + String(commonData.data.maxpage) + " >>>> ]");
if(String(backlightMode) == "Control by Key"){ // Key for illumination
getdisplay().setCursor(343, 290);
getdisplay().print("[ILUM]");
}
}
else{
getdisplay().setCursor(130, 290);
getdisplay().print(" [ Keylock active ]");
}
// Update display
getdisplay().nextPage(); // Partial update (fast)
};
};
static Page *createPage(CommonData &common){
return new PageApparentWind(common);
}
/**
* with the code below we make this page known to the PageTask
* we give it a type (name) that can be selected in the config
* we define which function is to be called
* and we provide the number of user parameters we expect (0 here)
* and will will provide the names of the fixed values we need
*/
PageDescription registerPageApparentWind(
"ApparentWind", // Page name
createPage, // Action
0, // Number of bus values depends on selection in Web configuration
{"AWS","AWA"}, // Bus values we need in the page
true // Show display header on/off
);
#endif

View File

@@ -1,28 +1,28 @@
#ifdef BOARD_OBP60S3 #if defined BOARD_OBP60S3 || defined BOARD_OBP40S3
#include "Pagedata.h" #include "Pagedata.h"
#include "OBP60Extensions.h" #include "OBP60Extensions.h"
class PageBME280 : public Page class PageBME280 : public Page
{ {
bool keylock = false; // Keylock
public: public:
PageBME280(CommonData &common){ PageBME280(CommonData &common){
common.logger->logDebug(GwLog::LOG,"Show PageBME280"); commonData = &common;
common.logger->logDebug(GwLog::LOG,"Instantiate PageBME280");
} }
virtual int handleKey(int key){ virtual int handleKey(int key){
if(key == 11){ // Code for keylock // Code for keylock
keylock = !keylock; // Toggle keylock if(key == 11){
commonData->keylock = !commonData->keylock;
return 0; // Commit the key return 0; // Commit the key
} }
return key; return key;
} }
virtual void displayPage(CommonData &commonData, PageData &pageData){ int displayPage(PageData &pageData){
GwConfigHandler *config = commonData.config; GwConfigHandler *config = commonData->config;
GwLog *logger=commonData.logger; GwLog *logger = commonData->logger;
double value1 = 0; double value1 = 0;
double value2 = 0; double value2 = 0;
@@ -42,13 +42,13 @@ class PageBME280 : public Page
String name1 = "Temp"; // Value name String name1 = "Temp"; // Value name
name1 = name1.substring(0, 6); // String length limit for value name name1 = name1.substring(0, 6); // String length limit for value name
if(simulation == false){ if(simulation == false){
value1 = commonData.data.airTemperature; // Value as double in SI unit value1 = commonData->data.airTemperature; // Value as double in SI unit
} }
else{ else{
value1 = 23.0 + float(random(0, 10)) / 10.0; value1 = 23.0 + float(random(0, 10)) / 10.0;
} }
// Display data when sensor activated // Display data when sensor activated
if((String(useenvsensor) == "BME280") or (String(useenvsensor) == "BMP280")){ if((useenvsensor == "BME280") or (useenvsensor == "BMP280") or (useenvsensor == "BMP180")){
svalue1 = String(value1, 1); // Formatted value as string including unit conversion and switching decimal places svalue1 = String(value1, 1); // Formatted value as string including unit conversion and switching decimal places
} }
else{ else{
@@ -60,13 +60,13 @@ class PageBME280 : public Page
String name2 = "Humid"; // Value name String name2 = "Humid"; // Value name
name2 = name2.substring(0, 6); // String length limit for value name name2 = name2.substring(0, 6); // String length limit for value name
if(simulation == false){ if(simulation == false){
value2 = commonData.data.airHumidity; // Value as double in SI unit value2 = commonData->data.airHumidity; // Value as double in SI unit
} }
else{ else{
value2 = 43 + float(random(0, 4)); value2 = 43 + float(random(0, 4));
} }
// Display data when sensor activated // Display data when sensor activated
if(String(useenvsensor) == "BME280"){ if(useenvsensor == "BME280"){
svalue2 = String(value2, 0); // Formatted value as string including unit conversion and switching decimal places svalue2 = String(value2, 0); // Formatted value as string including unit conversion and switching decimal places
} }
else{ else{
@@ -78,13 +78,13 @@ class PageBME280 : public Page
String name3 = "Press"; // Value name String name3 = "Press"; // Value name
name3 = name3.substring(0, 6); // String length limit for value name name3 = name3.substring(0, 6); // String length limit for value name
if(simulation == false){ if(simulation == false){
value3 = commonData.data.airPressure; // Value as double in SI unit value3 = commonData->data.airPressure; // Value as double in SI unit
} }
else{ else{
value3 = 1006 + float(random(0, 5)); value3 = 1006 + float(random(0, 5));
} }
// Display data when sensor activated // Display data when sensor activated
if((String(useenvsensor) == "BME280") or (String(useenvsensor) == "BMP280")){ if((useenvsensor == "BME280") or (useenvsensor == "BMP280") or (useenvsensor == "BMP180")){
svalue3 = String(value3 / 100, 1); // Formatted value as string including unit conversion and switching decimal places svalue3 = String(value3 / 100, 1); // Formatted value as string including unit conversion and switching decimal places
} }
else{ else{
@@ -107,17 +107,17 @@ class PageBME280 : public Page
// Set display in partial refresh mode // Set display in partial refresh mode
getdisplay().setPartialWindow(0, 0, getdisplay().width(), getdisplay().height()); // Set partial update getdisplay().setPartialWindow(0, 0, getdisplay().width(), getdisplay().height()); // Set partial update
getdisplay().setTextColor(commonData.fgcolor); getdisplay().setTextColor(commonData->fgcolor);
// ############### Value 1 ################ // ############### Value 1 ################
// Show name // Show name
getdisplay().setFont(&Ubuntu_Bold20pt7b); getdisplay().setFont(&Ubuntu_Bold20pt8b);
getdisplay().setCursor(20, 55); getdisplay().setCursor(20, 55);
getdisplay().print(name1); // Page name getdisplay().print(name1); // Page name
// Show unit // Show unit
getdisplay().setFont(&Ubuntu_Bold12pt7b); getdisplay().setFont(&Ubuntu_Bold12pt8b);
getdisplay().setCursor(20, 90); getdisplay().setCursor(20, 90);
getdisplay().print(unit1); // Unit getdisplay().print(unit1); // Unit
@@ -131,17 +131,17 @@ class PageBME280 : public Page
// ############### Horizontal Line ################ // ############### Horizontal Line ################
// Horizontal line 3 pix // Horizontal line 3 pix
getdisplay().fillRect(0, 105, 400, 3, commonData.fgcolor); getdisplay().fillRect(0, 105, 400, 3, commonData->fgcolor);
// ############### Value 2 ################ // ############### Value 2 ################
// Show name // Show name
getdisplay().setFont(&Ubuntu_Bold20pt7b); getdisplay().setFont(&Ubuntu_Bold20pt8b);
getdisplay().setCursor(20, 145); getdisplay().setCursor(20, 145);
getdisplay().print(name2); // Page name getdisplay().print(name2); // Page name
// Show unit // Show unit
getdisplay().setFont(&Ubuntu_Bold12pt7b); getdisplay().setFont(&Ubuntu_Bold12pt8b);
getdisplay().setCursor(20, 180); getdisplay().setCursor(20, 180);
getdisplay().print(unit2); // Unit getdisplay().print(unit2); // Unit
@@ -155,17 +155,17 @@ class PageBME280 : public Page
// ############### Horizontal Line ################ // ############### Horizontal Line ################
// Horizontal line 3 pix // Horizontal line 3 pix
getdisplay().fillRect(0, 195, 400, 3, commonData.fgcolor); getdisplay().fillRect(0, 195, 400, 3, commonData->fgcolor);
// ############### Value 3 ################ // ############### Value 3 ################
// Show name // Show name
getdisplay().setFont(&Ubuntu_Bold20pt7b); getdisplay().setFont(&Ubuntu_Bold20pt8b);
getdisplay().setCursor(20, 235); getdisplay().setCursor(20, 235);
getdisplay().print(name3); // Page name getdisplay().print(name3); // Page name
// Show unit // Show unit
getdisplay().setFont(&Ubuntu_Bold12pt7b); getdisplay().setFont(&Ubuntu_Bold12pt8b);
getdisplay().setCursor(20, 270); getdisplay().setCursor(20, 270);
getdisplay().print(unit3); // Unit getdisplay().print(unit3); // Unit
@@ -176,26 +176,7 @@ class PageBME280 : public Page
// Show bus data // Show bus data
getdisplay().print(svalue3); // Real value as formated string getdisplay().print(svalue3); // Real value as formated string
// ############### Key Layout ################ return PAGE_UPDATE;
// Key Layout
getdisplay().setFont(&Ubuntu_Bold8pt7b);
if(keylock == false){
getdisplay().setCursor(130, 290);
getdisplay().print("[ <<<< " + String(commonData.data.actpage) + "/" + String(commonData.data.maxpage) + " >>>> ]");
if(String(backlightMode) == "Control by Key"){ // Key for illumination
getdisplay().setCursor(343, 290);
getdisplay().print("[ILUM]");
}
}
else{
getdisplay().setCursor(130, 290);
getdisplay().print(" [ Keylock active ]");
}
// Update display
getdisplay().nextPage(); // Partial update (fast)
}; };
}; };

View File

@@ -1,16 +1,21 @@
#ifdef BOARD_OBP60S3 #if defined BOARD_OBP60S3 || defined BOARD_OBP40S3
#include "Pagedata.h" #include "Pagedata.h"
#include "OBP60Extensions.h" #include "OBP60Extensions.h"
class PageBattery : public Page class PageBattery : public Page
{ {
bool keylock = false; // Keylock int average = 0; // Average type [0...3], 0=off, 1=10s, 2=60s, 3=300s
int average = 0; // Average type [0...3], 0=off, 1=10s, 2=60s, 3=300s
public: public:
PageBattery(CommonData &common){ PageBattery(CommonData &common){
common.logger->logDebug(GwLog::LOG,"Show PageBattery"); commonData = &common;
common.logger->logDebug(GwLog::LOG,"Instantiate PageBattery");
}
virtual void setupKeys(){
Page::setupKeys();
commonData->keydata[0].label = "AVG";
} }
virtual int handleKey(int key){ virtual int handleKey(int key){
@@ -23,15 +28,15 @@ class PageBattery : public Page
// Code for keylock // Code for keylock
if(key == 11){ if(key == 11){
keylock = !keylock; // Toggle keylock commonData->keylock = !commonData->keylock;
return 0; // Commit the key return 0; // Commit the key
} }
return key; return key;
} }
virtual void displayPage(CommonData &commonData, PageData &pageData){ int displayPage(PageData &pageData){
GwConfigHandler *config = commonData.config; GwConfigHandler *config = commonData->config;
GwLog *logger=commonData.logger; GwLog *logger = commonData->logger;
// Old values for hold function // Old values for hold function
double value1 = 0; double value1 = 0;
@@ -58,19 +63,19 @@ class PageBattery : public Page
// Switch average values // Switch average values
switch (average) { switch (average) {
case 0: case 0:
value1 = commonData.data.batteryVoltage; // Live data value1 = commonData->data.batteryVoltage; // Live data
break; break;
case 1: case 1:
value1 = commonData.data.batteryVoltage10; // Average 10s value1 = commonData->data.batteryVoltage10; // Average 10s
break; break;
case 2: case 2:
value1 = commonData.data.batteryVoltage60; // Average 60s value1 = commonData->data.batteryVoltage60; // Average 60s
break; break;
case 3: case 3:
value1 = commonData.data.batteryVoltage300; // Average 300s value1 = commonData->data.batteryVoltage300; // Average 300s
break; break;
default: default:
value1 = commonData.data.batteryVoltage; // Default value1 = commonData->data.batteryVoltage; // Default
break; break;
} }
} }
@@ -87,19 +92,19 @@ class PageBattery : public Page
if(String(powsensor1) == "INA219" || String(powsensor1) == "INA226"){ if(String(powsensor1) == "INA219" || String(powsensor1) == "INA226"){
switch (average) { switch (average) {
case 0: case 0:
value2 = commonData.data.batteryCurrent; // Live data value2 = commonData->data.batteryCurrent; // Live data
break; break;
case 1: case 1:
value2 = commonData.data.batteryCurrent10; // Average 10s value2 = commonData->data.batteryCurrent10; // Average 10s
break; break;
case 2: case 2:
value2 = commonData.data.batteryCurrent60; // Average 60s value2 = commonData->data.batteryCurrent60; // Average 60s
break; break;
case 3: case 3:
value2 = commonData.data.batteryCurrent300; // Average 300s value2 = commonData->data.batteryCurrent300; // Average 300s
break; break;
default: default:
value2 = commonData.data.batteryCurrent; // Default value2 = commonData->data.batteryCurrent; // Default
break; break;
} }
} }
@@ -116,19 +121,19 @@ class PageBattery : public Page
if(String(powsensor1) == "INA219" || String(powsensor1) == "INA226"){ if(String(powsensor1) == "INA219" || String(powsensor1) == "INA226"){
switch (average) { switch (average) {
case 0: case 0:
value3 = commonData.data.batteryPower; // Live data value3 = commonData->data.batteryPower; // Live data
break; break;
case 1: case 1:
value3 = commonData.data.batteryPower10; // Average 10s value3 = commonData->data.batteryPower10; // Average 10s
break; break;
case 2: case 2:
value3 = commonData.data.batteryPower60; // Average 60s value3 = commonData->data.batteryPower60; // Average 60s
break; break;
case 3: case 3:
value3 = commonData.data.batteryPower300; // Average 300s value3 = commonData->data.batteryPower300; // Average 300s
break; break;
default: default:
value3 = commonData.data.batteryPower; // Default value3 = commonData->data.batteryPower; // Default
break; break;
} }
} }
@@ -156,8 +161,8 @@ class PageBattery : public Page
getdisplay().setPartialWindow(0, 0, getdisplay().width(), getdisplay().height()); // Set partial update getdisplay().setPartialWindow(0, 0, getdisplay().width(), getdisplay().height()); // Set partial update
// Show average settings // Show average settings
getdisplay().setTextColor(commonData.fgcolor); getdisplay().setTextColor(commonData->fgcolor);
getdisplay().setFont(&Ubuntu_Bold8pt7b); getdisplay().setFont(&Ubuntu_Bold8pt8b);
switch (average) { switch (average) {
case 0: case 0:
getdisplay().setCursor(60, 90); getdisplay().setCursor(60, 90);
@@ -204,12 +209,12 @@ class PageBattery : public Page
// ############### Value 1 ################ // ############### Value 1 ################
// Show name // Show name
getdisplay().setFont(&Ubuntu_Bold20pt7b); getdisplay().setFont(&Ubuntu_Bold20pt8b);
getdisplay().setCursor(20, 55); getdisplay().setCursor(20, 55);
getdisplay().print(name1); // Value name getdisplay().print(name1); // Value name
// Show unit // Show unit
getdisplay().setFont(&Ubuntu_Bold12pt7b); getdisplay().setFont(&Ubuntu_Bold12pt8b);
getdisplay().setCursor(20, 90); getdisplay().setCursor(20, 90);
getdisplay().print(unit1); // Unit getdisplay().print(unit1); // Unit
@@ -228,17 +233,17 @@ class PageBattery : public Page
// ############### Horizontal Line ################ // ############### Horizontal Line ################
// Horizontal line 3 pix // Horizontal line 3 pix
getdisplay().fillRect(0, 105, 400, 3, commonData.fgcolor); getdisplay().fillRect(0, 105, 400, 3, commonData->fgcolor);
// ############### Value 2 ################ // ############### Value 2 ################
// Show name // Show name
getdisplay().setFont(&Ubuntu_Bold20pt7b); getdisplay().setFont(&Ubuntu_Bold20pt8b);
getdisplay().setCursor(20, 145); getdisplay().setCursor(20, 145);
getdisplay().print(name2); // Value name getdisplay().print(name2); // Value name
// Show unit // Show unit
getdisplay().setFont(&Ubuntu_Bold12pt7b); getdisplay().setFont(&Ubuntu_Bold12pt8b);
getdisplay().setCursor(20, 180); getdisplay().setCursor(20, 180);
getdisplay().print(unit2); // Unit getdisplay().print(unit2); // Unit
@@ -257,17 +262,17 @@ class PageBattery : public Page
// ############### Horizontal Line ################ // ############### Horizontal Line ################
// Horizontal line 3 pix // Horizontal line 3 pix
getdisplay().fillRect(0, 195, 400, 3, commonData.fgcolor); getdisplay().fillRect(0, 195, 400, 3, commonData->fgcolor);
// ############### Value 3 ################ // ############### Value 3 ################
// Show name // Show name
getdisplay().setFont(&Ubuntu_Bold20pt7b); getdisplay().setFont(&Ubuntu_Bold20pt8b);
getdisplay().setCursor(20, 235); getdisplay().setCursor(20, 235);
getdisplay().print(name3); // Value name getdisplay().print(name3); // Value name
// Show unit // Show unit
getdisplay().setFont(&Ubuntu_Bold12pt7b); getdisplay().setFont(&Ubuntu_Bold12pt8b);
getdisplay().setCursor(20, 270); getdisplay().setCursor(20, 270);
getdisplay().print(unit3); // Unit getdisplay().print(unit3); // Unit
@@ -283,29 +288,7 @@ class PageBattery : public Page
getdisplay().print("---"); // No sensor data (sensor is off) getdisplay().print("---"); // No sensor data (sensor is off)
} }
return PAGE_UPDATE;
// ############### Key Layout ################
// Key Layout
getdisplay().setFont(&Ubuntu_Bold8pt7b);
if(keylock == false){
getdisplay().setCursor(10, 290);
getdisplay().print("[AVG]");
getdisplay().setCursor(130, 290);
getdisplay().print("[ <<<< " + String(commonData.data.actpage) + "/" + String(commonData.data.maxpage) + " >>>> ]");
if(String(backlightMode) == "Control by Key"){ // Key for illumination
getdisplay().setCursor(343, 290);
getdisplay().print("[ILUM]");
}
}
else{
getdisplay().setCursor(130, 290);
getdisplay().print(" [ Keylock active ]");
}
// Update display
getdisplay().nextPage(); // Partial update (fast)
}; };
}; };

View File

@@ -1,4 +1,4 @@
#ifdef BOARD_OBP60S3 #if defined BOARD_OBP60S3 || defined BOARD_OBP40S3
#include "Pagedata.h" #include "Pagedata.h"
#include "OBP60Extensions.h" #include "OBP60Extensions.h"
@@ -7,15 +7,21 @@
class PageBattery2 : public Page class PageBattery2 : public Page
{ {
bool init = false; // Marker for init done bool init = false; // Marker for init done
bool keylock = false; // Keylock
int average = 0; // Average type [0...3], 0=off, 1=10s, 2=60s, 3=300s int average = 0; // Average type [0...3], 0=off, 1=10s, 2=60s, 3=300s
bool trend = true; // Trend indicator [0|1], 0=off, 1=on bool trend = true; // Trend indicator [0|1], 0=off, 1=on
double raw = 0; double raw = 0;
public: public:
PageBattery2(CommonData &common){ PageBattery2(CommonData &common){
common.logger->logDebug(GwLog::LOG,"Show PageBattery2"); commonData = &common;
common.logger->logDebug(GwLog::LOG,"Instantiate PageBattery2");
} }
virtual void setupKeys(){
Page::setupKeys();
commonData->keydata[0].label = "AVG";
}
virtual int handleKey(int key){ virtual int handleKey(int key){
// Change average // Change average
if(key == 1){ if(key == 1){
@@ -32,16 +38,16 @@ public:
// Code for keylock // Code for keylock
if(key == 11){ if(key == 11){
keylock = !keylock; // Toggle keylock commonData->keylock = !commonData->keylock;
return 0; // Commit the key return 0; // Commit the key
} }
return key; return key;
} }
virtual void displayPage(CommonData &commonData, PageData &pageData) int displayPage(PageData &pageData)
{ {
GwConfigHandler *config = commonData.config; GwConfigHandler *config = commonData->config;
GwLog *logger=commonData.logger; GwLog *logger = commonData->logger;
// Polynominal coefficients second order for battery energy level calculation // Polynominal coefficients second order for battery energy level calculation
// index 0 = Pb, 1 = Gel, 2 = AGM, 3 = LiFePo4 // index 0 = Pb, 1 = Gel, 2 = AGM, 3 = LiFePo4
@@ -71,42 +77,42 @@ public:
// Create trend value // Create trend value
if(init == false){ // Load start values for first page run if(init == false){ // Load start values for first page run
valueTrend = commonData.data.batteryVoltage10; valueTrend = commonData->data.batteryVoltage10;
init = true; init = true;
} }
else{ // Reading trend value else{ // Reading trend value
valueTrend = commonData.data.batteryVoltage10; valueTrend = commonData->data.batteryVoltage10;
} }
// Get raw value for trend indicator // Get raw value for trend indicator
raw = commonData.data.batteryVoltage; // Live data raw = commonData->data.batteryVoltage; // Live data
// Switch average values // Switch average values
switch (average) { switch (average) {
case 0: case 0:
value1 = commonData.data.batteryVoltage; // Live data value1 = commonData->data.batteryVoltage; // Live data
value2 = commonData.data.batteryCurrent; value2 = commonData->data.batteryCurrent;
value3 = commonData.data.batteryPower; value3 = commonData->data.batteryPower;
break; break;
case 1: case 1:
value1 = commonData.data.batteryVoltage10; // Average 10s value1 = commonData->data.batteryVoltage10; // Average 10s
value2 = commonData.data.batteryCurrent10; value2 = commonData->data.batteryCurrent10;
value3 = commonData.data.batteryPower10; value3 = commonData->data.batteryPower10;
break; break;
case 2: case 2:
value1 = commonData.data.batteryVoltage60; // Average 60s value1 = commonData->data.batteryVoltage60; // Average 60s
value2 = commonData.data.batteryCurrent60; value2 = commonData->data.batteryCurrent60;
value3 = commonData.data.batteryPower60; value3 = commonData->data.batteryPower60;
break; break;
case 3: case 3:
value1 = commonData.data.batteryVoltage300; // Average 300s value1 = commonData->data.batteryVoltage300; // Average 300s
value2 = commonData.data.batteryCurrent300; value2 = commonData->data.batteryCurrent300;
value3 = commonData.data.batteryPower300; value3 = commonData->data.batteryPower300;
break; break;
default: default:
value1 = commonData.data.batteryVoltage; // Default value1 = commonData->data.batteryVoltage; // Default
value2 = commonData.data.batteryCurrent; value2 = commonData->data.batteryCurrent;
value3 = commonData.data.batteryPower; value3 = commonData->data.batteryPower;
break; break;
} }
bool valid1 = true; bool valid1 = true;
@@ -180,15 +186,15 @@ public:
// Set display in partial refresh mode // Set display in partial refresh mode
getdisplay().setPartialWindow(0, 0, getdisplay().width(), getdisplay().height()); // Set partial update getdisplay().setPartialWindow(0, 0, getdisplay().width(), getdisplay().height()); // Set partial update
getdisplay().setTextColor(commonData.fgcolor); getdisplay().setTextColor(commonData->fgcolor);
// Show name // Show name
getdisplay().setFont(&Ubuntu_Bold20pt7b); getdisplay().setFont(&Ubuntu_Bold20pt8b);
getdisplay().setCursor(10, 65); getdisplay().setCursor(10, 65);
getdisplay().print("Bat."); getdisplay().print("Bat.");
// Show battery type // Show battery type
getdisplay().setFont(&Ubuntu_Bold8pt7b); getdisplay().setFont(&Ubuntu_Bold8pt8b);
getdisplay().setCursor(90, 65); getdisplay().setCursor(90, 65);
getdisplay().print(batType); getdisplay().print(batType);
@@ -199,7 +205,7 @@ public:
if(String(batVoltage) == "12V") bvoltage = 12; if(String(batVoltage) == "12V") bvoltage = 12;
else bvoltage = 24; else bvoltage = 24;
getdisplay().print(bvoltage); getdisplay().print(bvoltage);
getdisplay().setFont(&Ubuntu_Bold16pt7b); getdisplay().setFont(&Ubuntu_Bold16pt8b);
getdisplay().print("V"); getdisplay().print("V");
// Show battery capacity // Show battery capacity
@@ -207,22 +213,22 @@ public:
getdisplay().setCursor(10, 200); getdisplay().setCursor(10, 200);
if(batCapacity <= 999) getdisplay().print(batCapacity, 0); if(batCapacity <= 999) getdisplay().print(batCapacity, 0);
if(batCapacity > 999) getdisplay().print(float(batCapacity/1000.0), 1); if(batCapacity > 999) getdisplay().print(float(batCapacity/1000.0), 1);
getdisplay().setFont(&Ubuntu_Bold16pt7b); getdisplay().setFont(&Ubuntu_Bold16pt8b);
if(batCapacity <= 999) getdisplay().print("Ah"); if(batCapacity <= 999) getdisplay().print("Ah");
if(batCapacity > 999) getdisplay().print("kAh"); if(batCapacity > 999) getdisplay().print("kAh");
// Show info // Show info
getdisplay().setFont(&Ubuntu_Bold8pt7b); getdisplay().setFont(&Ubuntu_Bold8pt8b);
getdisplay().setCursor(10, 235); getdisplay().setCursor(10, 235);
getdisplay().print("Installed"); getdisplay().print("Installed");
getdisplay().setCursor(10, 255); getdisplay().setCursor(10, 255);
getdisplay().print("Battery Type"); getdisplay().print("Battery Type");
// Show battery with fill level // Show battery with fill level
batteryGraphic(150, 45, batPercentage, commonData.fgcolor, commonData.bgcolor); batteryGraphic(150, 45, batPercentage, commonData->fgcolor, commonData->bgcolor);
// Show average settings // Show average settings
getdisplay().setFont(&Ubuntu_Bold8pt7b); getdisplay().setFont(&Ubuntu_Bold8pt8b);
getdisplay().setCursor(150, 145); getdisplay().setCursor(150, 145);
switch (average) { switch (average) {
case 0: case 0:
@@ -246,7 +252,7 @@ public:
getdisplay().setFont(&DSEG7Classic_BoldItalic20pt7b); getdisplay().setFont(&DSEG7Classic_BoldItalic20pt7b);
getdisplay().setCursor(150, 200); getdisplay().setCursor(150, 200);
getdisplay().print(batPercentage); getdisplay().print(batPercentage);
getdisplay().setFont(&Ubuntu_Bold16pt7b); getdisplay().setFont(&Ubuntu_Bold16pt8b);
getdisplay().print("%"); getdisplay().print("%");
// Show time to full discharge // Show time to full discharge
@@ -257,12 +263,12 @@ public:
else getdisplay().print(batRange, 0); else getdisplay().print(batRange, 0);
} }
else getdisplay().print("--"); else getdisplay().print("--");
getdisplay().setFont(&Ubuntu_Bold16pt7b); getdisplay().setFont(&Ubuntu_Bold16pt8b);
getdisplay().print("h"); getdisplay().print("h");
// Show sensor type info // Show sensor type info
String i2cAddr = ""; String i2cAddr = "";
getdisplay().setFont(&Ubuntu_Bold8pt7b); getdisplay().setFont(&Ubuntu_Bold8pt8b);
getdisplay().setCursor(270, 60); getdisplay().setCursor(270, 60);
if(powerSensor == "off") getdisplay().print("Internal"); if(powerSensor == "off") getdisplay().print("Internal");
if(powerSensor == "INA219"){ if(powerSensor == "INA219"){
@@ -301,7 +307,7 @@ public:
getdisplay().print("---"); // Missing bus data getdisplay().print("---"); // Missing bus data
} }
} }
getdisplay().setFont(&Ubuntu_Bold16pt7b); getdisplay().setFont(&Ubuntu_Bold16pt8b);
getdisplay().print("V"); getdisplay().print("V");
// Show actual current in A // Show actual current in A
@@ -313,7 +319,7 @@ public:
if(value2 > 99.9) getdisplay().print(value2, 0); if(value2 > 99.9) getdisplay().print(value2, 0);
} }
else getdisplay().print("---"); else getdisplay().print("---");
getdisplay().setFont(&Ubuntu_Bold16pt7b); getdisplay().setFont(&Ubuntu_Bold16pt8b);
getdisplay().print("A"); getdisplay().print("A");
// Show actual consumption in W // Show actual consumption in W
@@ -325,28 +331,10 @@ public:
if(value3 > 99.9) getdisplay().print(value3, 0); if(value3 > 99.9) getdisplay().print(value3, 0);
} }
else getdisplay().print("---"); else getdisplay().print("---");
getdisplay().setFont(&Ubuntu_Bold16pt7b); getdisplay().setFont(&Ubuntu_Bold16pt8b);
getdisplay().print("W"); getdisplay().print("W");
// Key Layout return PAGE_UPDATE;
getdisplay().setFont(&Ubuntu_Bold8pt7b);
if(keylock == false){
getdisplay().setCursor(10, 290);
getdisplay().print("[AVG]");
getdisplay().setCursor(130, 290);
getdisplay().print("[ <<<< " + String(commonData.data.actpage) + "/" + String(commonData.data.maxpage) + " >>>> ]");
if(String(backlightMode) == "Control by Key"){ // Key for illumination
getdisplay().setCursor(343, 290);
getdisplay().print("[ILUM]");
}
}
else{
getdisplay().setCursor(130, 290);
getdisplay().print(" [ Keylock active ]");
}
// Update display
getdisplay().nextPage(); // Partial update (fast)
}; };
}; };

View File

@@ -1,19 +1,82 @@
#ifdef BOARD_OBP60S3 #if defined BOARD_OBP60S3 || defined BOARD_OBP40S3
#include "Pagedata.h" #include "Pagedata.h"
#include "OBP60Extensions.h" #include "OBP60Extensions.h"
/*
* TODO mode: race timer: keys
* - prepare: set countdown to 5min
* reset: abort current countdown and start over with 5min preparation
* - 5min: key press
* - 4min: key press to sync
* - 1min: buzzer signal
* - start: buzzer signal for start
*
*/
class PageClock : public Page class PageClock : public Page
{ {
bool keylock = false; // Keylock bool simulation = false;
int simtime;
bool keylock = false;
char source = 'R'; // time source (R)TC | (G)PS | (N)TP
char mode = 'A'; // display mode (A)nalog | (D)igital | race (T)imer
char tz = 'L'; // time zone (L)ocal | (U)TC
double timezone = 0; // there are timezones with non int offsets, e.g. 5.5 or 5.75
double homelat;
double homelon;
bool homevalid = false; // homelat and homelon are valid
public: public:
PageClock(CommonData &common){ PageClock(CommonData &common){
common.logger->logDebug(GwLog::LOG,"Show PageClock"); commonData = &common;
common.logger->logDebug(GwLog::LOG,"Instantiate PageClock");
simulation = common.config->getBool(common.config->useSimuData);
timezone = common.config->getString(common.config->timeZone).toDouble();
homelat = common.config->getString(common.config->homeLAT).toDouble();
homelon = common.config->getString(common.config->homeLON).toDouble();
homevalid = homelat >= -180.0 and homelat <= 180 and homelon >= -90.0 and homelon <= 90.0;
simtime = 38160; // time value 11:36
}
virtual void setupKeys(){
Page::setupKeys();
commonData->keydata[0].label = "SRC";
commonData->keydata[1].label = "MODE";
commonData->keydata[4].label = "TZ";
} }
// Key functions // Key functions
virtual int handleKey(int key){ virtual int handleKey(int key){
// Time source
if (key == 1) {
if (source == 'G') {
source = 'R';
} else {
source = 'G';
}
return 0;
}
if (key == 2) {
if (mode == 'A') {
mode = 'D';
} else if (mode == 'D') {
mode = 'T';
} else {
mode = 'A';
}
return 0;
}
// Time zone: Local / UTC
if (key == 5) {
if (tz == 'L') {
tz = 'U';
} else {
tz = 'L';
}
return 0;
}
// Keylock function // Keylock function
if(key == 11){ // Code for keylock if(key == 11){ // Code for keylock
keylock = !keylock; // Toggle keylock keylock = !keylock; // Toggle keylock
@@ -22,10 +85,10 @@ public:
return key; return key;
} }
virtual void displayPage(CommonData &commonData, PageData &pageData) int displayPage(PageData &pageData)
{ {
GwConfigHandler *config = commonData.config; GwConfigHandler *config = commonData->config;
GwLog *logger=commonData.logger; GwLog *logger = commonData->logger;
static String svalue1old = ""; static String svalue1old = "";
static String unit1old = ""; static String unit1old = "";
@@ -43,12 +106,10 @@ public:
// Get config data // Get config data
String lengthformat = config->getString(config->lengthFormat); String lengthformat = config->getString(config->lengthFormat);
bool simulation = config->getBool(config->useSimuData); String dateformat = config->getString(config->dateFormat);
bool holdvalues = config->getBool(config->holdvalues); bool holdvalues = config->getBool(config->holdvalues);
String flashLED = config->getString(config->flashLED); String flashLED = config->getString(config->flashLED);
String backlightMode = config->getString(config->backlight); String backlightMode = config->getString(config->backlight);
String stimezone = config->getString(config->timeZone);
double timezone = stimezone.toDouble();
// Get boat values for GPS time // Get boat values for GPS time
GwApi::BoatValue *bvalue1 = pageData.values[0]; // First element in list (only one value by PageOneValue) GwApi::BoatValue *bvalue1 = pageData.values[0]; // First element in list (only one value by PageOneValue)
@@ -58,13 +119,13 @@ public:
value1 = bvalue1->value; // Value as double in SI unit value1 = bvalue1->value; // Value as double in SI unit
} }
else{ else{
value1 = 38160; // Simulation data for time value 11:36 in seconds value1 = simtime++; // Simulation data for time value 11:36 in seconds
} // Other simulation data see OBP60Formater.cpp } // Other simulation data see OBP60Formatter.cpp
bool valid1 = bvalue1->valid; // Valid information bool valid1 = bvalue1->valid; // Valid information
String svalue1 = formatValue(bvalue1, commonData).svalue; // Formatted value as string including unit conversion and switching decimal places String svalue1 = formatValue(bvalue1, *commonData).svalue; // Formatted value as string including unit conversion and switching decimal places
String unit1 = formatValue(bvalue1, commonData).unit; // Unit of value String unit1 = formatValue(bvalue1, *commonData).unit; // Unit of value
if(valid1 == true){ if(valid1 == true){
svalue1old = svalue1; // Save old value svalue1old = svalue1; // Save old value
unit1old = unit1; // Save old unit unit1old = unit1; // Save old unit
} }
@@ -74,10 +135,10 @@ public:
name2 = name2.substring(0, 6); // String length limit for value name name2 = name2.substring(0, 6); // String length limit for value name
value2 = bvalue2->value; // Value as double in SI unit value2 = bvalue2->value; // Value as double in SI unit
bool valid2 = bvalue2->valid; // Valid information bool valid2 = bvalue2->valid; // Valid information
String svalue2 = formatValue(bvalue2, commonData).svalue; // Formatted value as string including unit conversion and switching decimal places String svalue2 = formatValue(bvalue2, *commonData).svalue; // Formatted value as string including unit conversion and switching decimal places
String unit2 = formatValue(bvalue2, commonData).unit; // Unit of value String unit2 = formatValue(bvalue2, *commonData).unit; // Unit of value
if(valid2 == true){ if(valid2 == true){
svalue2old = svalue2; // Save old value svalue2old = svalue2; // Save old value
unit2old = unit2; // Save old unit unit2old = unit2; // Save old unit
} }
@@ -87,21 +148,21 @@ public:
name3 = name3.substring(0, 6); // String length limit for value name name3 = name3.substring(0, 6); // String length limit for value name
value3 = bvalue3->value; // Value as double in SI unit value3 = bvalue3->value; // Value as double in SI unit
bool valid3 = bvalue3->valid; // Valid information bool valid3 = bvalue3->valid; // Valid information
String svalue3 = formatValue(bvalue3, commonData).svalue; // Formatted value as string including unit conversion and switching decimal places String svalue3 = formatValue(bvalue3, *commonData).svalue; // Formatted value as string including unit conversion and switching decimal places
String unit3 = formatValue(bvalue3, commonData).unit; // Unit of value String unit3 = formatValue(bvalue3, *commonData).unit; // Unit of value
if(valid3 == true){ if(valid3 == true){
svalue3old = svalue3; // Save old value svalue3old = svalue3; // Save old value
unit3old = unit3; // Save old unit unit3old = unit3; // Save old unit
} }
// Optical warning by limit violation (unused) // Optical warning by limit violation (unused)
if(String(flashLED) == "Limit Violation"){ if(String(flashLED) == "Limit Violation"){
setBlinkingLED(false); setBlinkingLED(false);
setFlashLED(false); setFlashLED(false);
} }
// Logging boat values // Logging boat values
if (bvalue1 == NULL) return; if (bvalue1 == NULL) return PAGE_OK; // WTF why this statement?
LOG_DEBUG(GwLog::LOG,"Drawing at PageClock, %s:%f, %s:%f", name1.c_str(), value1, name2.c_str(), value2); LOG_DEBUG(GwLog::LOG,"Drawing at PageClock, %s:%f, %s:%f", name1.c_str(), value1, name2.c_str(), value2);
// Draw page // Draw page
@@ -110,70 +171,109 @@ public:
// Set display in partial refresh mode // Set display in partial refresh mode
getdisplay().setPartialWindow(0, 0, getdisplay().width(), getdisplay().height()); // Set partial update getdisplay().setPartialWindow(0, 0, getdisplay().width(), getdisplay().height()); // Set partial update
getdisplay().setTextColor(commonData.fgcolor); getdisplay().setTextColor(commonData->fgcolor);
time_t tv = mktime(&commonData->data.rtcTime) + timezone * 3600;
struct tm *local_tm = localtime(&tv);
// Show values GPS date // Show values GPS date
getdisplay().setFont(&Ubuntu_Bold8pt7b); getdisplay().setFont(&Ubuntu_Bold8pt8b);
getdisplay().setCursor(10, 65); getdisplay().setCursor(10, 65);
if(holdvalues == false) getdisplay().print(svalue2); // Value if (holdvalues == false) {
else getdisplay().print(svalue2old); if (source == 'G') {
getdisplay().setFont(&Ubuntu_Bold12pt7b); // GPS value
getdisplay().print(svalue2);
} else if (commonData->data.rtcValid) {
// RTC value
if (tz == 'L') {
getdisplay().print(formatDate(dateformat, local_tm->tm_year + 1900, local_tm->tm_mon + 1, local_tm->tm_mday));
}
else {
getdisplay().print(formatDate(dateformat, commonData->data.rtcTime.tm_year + 1900, commonData->data.rtcTime.tm_mon + 1, commonData->data.rtcTime.tm_mday));
}
} else {
getdisplay().print("---");
}
} else {
getdisplay().print(svalue2old);
}
getdisplay().setFont(&Ubuntu_Bold12pt8b);
getdisplay().setCursor(10, 95); getdisplay().setCursor(10, 95);
getdisplay().print("Date"); // Name getdisplay().print("Date"); // Name
// Horizintal separator left // Horizintal separator left
getdisplay().fillRect(0, 149, 60, 3, commonData.fgcolor); getdisplay().fillRect(0, 149, 60, 3, commonData->fgcolor);
// Show values GPS time // Show values GPS time
getdisplay().setFont(&Ubuntu_Bold8pt7b); getdisplay().setFont(&Ubuntu_Bold8pt8b);
getdisplay().setCursor(10, 250); getdisplay().setCursor(10, 250);
if(holdvalues == false) getdisplay().print(svalue1); // Value if (holdvalues == false) {
else getdisplay().print(svalue1old); if (source == 'G') {
getdisplay().setFont(&Ubuntu_Bold12pt7b); getdisplay().print(svalue1); // Value
}
else if (commonData->data.rtcValid) {
if (tz == 'L') {
getdisplay().print(formatTime('s', local_tm->tm_hour, local_tm->tm_min, local_tm->tm_sec));
}
else {
getdisplay().print(formatTime('s', commonData->data.rtcTime.tm_hour, commonData->data.rtcTime.tm_min, commonData->data.rtcTime.tm_sec));
}
} else {
getdisplay().print("---");
}
}
else {
getdisplay().print(svalue1old);
}
getdisplay().setFont(&Ubuntu_Bold12pt8b);
getdisplay().setCursor(10, 220); getdisplay().setCursor(10, 220);
getdisplay().print("Time"); // Name getdisplay().print("Time"); // Name
// Show values sunrise // Show values sunrise
String sunrise = "---"; String sunrise = "---";
if(valid1 == true && valid2 == true && valid3 == true){ if ((valid1 and valid2 and valid3 == true) or (homevalid and commonData->data.rtcValid)) {
sunrise = String(commonData.sundata.sunriseHour) + ":" + String(commonData.sundata.sunriseMinute + 100).substring(1); sunrise = String(commonData->sundata.sunriseHour) + ":" + String(commonData->sundata.sunriseMinute + 100).substring(1);
svalue5old = sunrise; svalue5old = sunrise;
} else if (simulation) {
sunrise = String("06:42");
} }
getdisplay().setFont(&Ubuntu_Bold8pt7b); getdisplay().setFont(&Ubuntu_Bold8pt8b);
getdisplay().setCursor(335, 65); getdisplay().setCursor(335, 65);
if(holdvalues == false) getdisplay().print(sunrise); // Value if(holdvalues == false) getdisplay().print(sunrise); // Value
else getdisplay().print(svalue5old); else getdisplay().print(svalue5old);
getdisplay().setFont(&Ubuntu_Bold12pt7b); getdisplay().setFont(&Ubuntu_Bold12pt8b);
getdisplay().setCursor(335, 95); getdisplay().setCursor(335, 95);
getdisplay().print("SunR"); // Name getdisplay().print("SunR"); // Name
// Horizintal separator right // Horizintal separator right
getdisplay().fillRect(340, 149, 80, 3, commonData.fgcolor); getdisplay().fillRect(340, 149, 80, 3, commonData->fgcolor);
// Show values sunset // Show values sunset
String sunset = "---"; String sunset = "---";
if(valid1 == true && valid2 == true && valid3 == true){ if ((valid1 and valid2 and valid3 == true) or (homevalid and commonData->data.rtcValid)) {
sunset = String(commonData.sundata.sunsetHour) + ":" + String(commonData.sundata.sunsetMinute + 100).substring(1); sunset = String(commonData->sundata.sunsetHour) + ":" + String(commonData->sundata.sunsetMinute + 100).substring(1);
svalue6old = sunset; svalue6old = sunset;
} else if (simulation) {
sunset = String("21:03");
} }
getdisplay().setFont(&Ubuntu_Bold8pt7b); getdisplay().setFont(&Ubuntu_Bold8pt8b);
getdisplay().setCursor(335, 250); getdisplay().setCursor(335, 250);
if(holdvalues == false) getdisplay().print(sunset); // Value if(holdvalues == false) getdisplay().print(sunset); // Value
else getdisplay().print(svalue6old); else getdisplay().print(svalue6old);
getdisplay().setFont(&Ubuntu_Bold12pt7b); getdisplay().setFont(&Ubuntu_Bold12pt8b);
getdisplay().setCursor(335, 220); getdisplay().setCursor(335, 220);
getdisplay().print("SunS"); // Name getdisplay().print("SunS"); // Name
//******************************************************************************************* //*******************************************************************************************
// Draw clock // Draw clock
int rInstrument = 110; // Radius of clock int rInstrument = 110; // Radius of clock
float pi = 3.141592; float pi = 3.141592;
getdisplay().fillCircle(200, 150, rInstrument + 10, commonData.fgcolor); // Outer circle getdisplay().fillCircle(200, 150, rInstrument + 10, commonData->fgcolor); // Outer circle
getdisplay().fillCircle(200, 150, rInstrument + 7, commonData.bgcolor); // Outer circle getdisplay().fillCircle(200, 150, rInstrument + 7, commonData->bgcolor); // Outer circle
for(int i=0; i<360; i=i+1) for(int i=0; i<360; i=i+1)
{ {
@@ -204,7 +304,7 @@ public:
getdisplay().getTextBounds(ii, int(x), int(y), &x1, &y1, &w, &h); // Calc width of new string getdisplay().getTextBounds(ii, int(x), int(y), &x1, &y1, &w, &h); // Calc width of new string
getdisplay().setCursor(x-w/2, y+h/2); getdisplay().setCursor(x-w/2, y+h/2);
if(i % 30 == 0){ if(i % 30 == 0){
getdisplay().setFont(&Ubuntu_Bold12pt7b); getdisplay().setFont(&Ubuntu_Bold12pt8b);
getdisplay().print(ii); getdisplay().print(ii);
} }
@@ -214,7 +314,7 @@ public:
if(i % 6 == 0){ if(i % 6 == 0){
float x1c = 200 + rInstrument*sin(i/180.0*pi); float x1c = 200 + rInstrument*sin(i/180.0*pi);
float y1c = 150 - rInstrument*cos(i/180.0*pi); float y1c = 150 - rInstrument*cos(i/180.0*pi);
getdisplay().fillCircle((int)x1c, (int)y1c, 2, commonData.fgcolor); getdisplay().fillCircle((int)x1c, (int)y1c, 2, commonData->fgcolor);
sinx=sin(i/180.0*pi); sinx=sin(i/180.0*pi);
cosx=cos(i/180.0*pi); cosx=cos(i/180.0*pi);
} }
@@ -228,38 +328,63 @@ public:
float yy2 = -(rInstrument+10); float yy2 = -(rInstrument+10);
getdisplay().fillTriangle(200+(int)(cosx*xx1-sinx*yy1),150+(int)(sinx*xx1+cosx*yy1), getdisplay().fillTriangle(200+(int)(cosx*xx1-sinx*yy1),150+(int)(sinx*xx1+cosx*yy1),
200+(int)(cosx*xx2-sinx*yy1),150+(int)(sinx*xx2+cosx*yy1), 200+(int)(cosx*xx2-sinx*yy1),150+(int)(sinx*xx2+cosx*yy1),
200+(int)(cosx*xx1-sinx*yy2),150+(int)(sinx*xx1+cosx*yy2),commonData.fgcolor); 200+(int)(cosx*xx1-sinx*yy2),150+(int)(sinx*xx1+cosx*yy2),commonData->fgcolor);
getdisplay().fillTriangle(200+(int)(cosx*xx2-sinx*yy1),150+(int)(sinx*xx2+cosx*yy1), getdisplay().fillTriangle(200+(int)(cosx*xx2-sinx*yy1),150+(int)(sinx*xx2+cosx*yy1),
200+(int)(cosx*xx1-sinx*yy2),150+(int)(sinx*xx1+cosx*yy2), 200+(int)(cosx*xx1-sinx*yy2),150+(int)(sinx*xx1+cosx*yy2),
200+(int)(cosx*xx2-sinx*yy2),150+(int)(sinx*xx2+cosx*yy2),commonData.fgcolor); 200+(int)(cosx*xx2-sinx*yy2),150+(int)(sinx*xx2+cosx*yy2),commonData->fgcolor);
} }
} }
// Print Unit in clock // Print Unit in clock
getdisplay().setFont(&Ubuntu_Bold12pt7b); getdisplay().setFont(&Ubuntu_Bold12pt8b);
getdisplay().setCursor(175, 110); getdisplay().setCursor(175, 110);
if(holdvalues == false){ if(holdvalues == false){
getdisplay().print(unit2); // Unit getdisplay().print(tz == 'L' ? "LOT" : "UTC");
} }
else{ else{
getdisplay().print(unit2old); // Unit getdisplay().print(unit2old); // date unit
}
getdisplay().setFont(&Ubuntu_Bold8pt8b);
getdisplay().setCursor(185, 190);
if (source == 'G') {
getdisplay().print("GPS");
} else {
getdisplay().print("RTC");
} }
// Clock values // Clock values
double hour = 0; double hour = 0;
double minute = 0; double minute = 0;
value1 = value1 + int(timezone*3600); if (source == 'R') {
if (value1 > 86400) {value1 = value1 - 86400;} if (tz == 'L') {
if (value1 < 0) {value1 = value1 + 86400;} time_t tv = mktime(&commonData->data.rtcTime) + timezone * 3600;
hour = (value1 / 3600.0); struct tm *local_tm = localtime(&tv);
if(hour > 12) hour = hour - 12.0; minute = local_tm->tm_min;
// minute = (hour - int(hour)) * 3600.0 / 60.0; // Analog minute pointer smooth moving hour = local_tm->tm_hour;
minute = int((hour - int(hour)) * 3600.0 / 60.0); // Jumping minute pointer from minute to minute } else {
minute = commonData->data.rtcTime.tm_min;
hour = commonData->data.rtcTime.tm_hour;
}
hour += minute / 60;
} else {
if (tz == 'L') {
value1 += timezone * 3600;
}
if (value1 > 86400) {value1 -= 86400;}
if (value1 < 0) {value1 += 86400;}
hour = (value1 / 3600.0);
// minute = (hour - int(hour)) * 3600.0 / 60.0; // Analog minute pointer smooth moving
minute = int((hour - int(hour)) * 3600.0 / 60.0); // Jumping minute pointer from minute to minute
}
if (hour > 12) {
hour -= 12.0;
}
LOG_DEBUG(GwLog::DEBUG,"... PageClock, value1: %f hour: %f minute:%f", value1, hour, minute); LOG_DEBUG(GwLog::DEBUG,"... PageClock, value1: %f hour: %f minute:%f", value1, hour, minute);
// Draw hour pointer // Draw hour pointer
float startwidth = 8; // Start width of pointer float startwidth = 8; // Start width of pointer
if(valid1 == true || holdvalues == true || simulation == true){ if(valid1 == true || (source == 'R' && commonData->data.rtcValid) || holdvalues == true || simulation == true){
float sinx=sin(hour * 30.0 * pi / 180); // Hour float sinx=sin(hour * 30.0 * pi / 180); // Hour
float cosx=cos(hour * 30.0 * pi / 180); float cosx=cos(hour * 30.0 * pi / 180);
// Normal pointer // Normal pointer
@@ -267,10 +392,10 @@ public:
float xx1 = -startwidth; float xx1 = -startwidth;
float xx2 = startwidth; float xx2 = startwidth;
float yy1 = -startwidth; float yy1 = -startwidth;
float yy2 = -(rInstrument * 0.5); float yy2 = -(rInstrument * 0.5);
getdisplay().fillTriangle(200+(int)(cosx*xx1-sinx*yy1),150+(int)(sinx*xx1+cosx*yy1), getdisplay().fillTriangle(200+(int)(cosx*xx1-sinx*yy1),150+(int)(sinx*xx1+cosx*yy1),
200+(int)(cosx*xx2-sinx*yy1),150+(int)(sinx*xx2+cosx*yy1), 200+(int)(cosx*xx2-sinx*yy1),150+(int)(sinx*xx2+cosx*yy1),
200+(int)(cosx*0-sinx*yy2),150+(int)(sinx*0+cosx*yy2),commonData.fgcolor); 200+(int)(cosx*0-sinx*yy2),150+(int)(sinx*0+cosx*yy2),commonData->fgcolor);
// Inverted pointer // Inverted pointer
// Pointer as triangle with center base 2*width // Pointer as triangle with center base 2*width
float endwidth = 2; // End width of pointer float endwidth = 2; // End width of pointer
@@ -280,12 +405,12 @@ public:
float iy2 = -endwidth; float iy2 = -endwidth;
getdisplay().fillTriangle(200+(int)(cosx*ix1-sinx*iy1),150+(int)(sinx*ix1+cosx*iy1), getdisplay().fillTriangle(200+(int)(cosx*ix1-sinx*iy1),150+(int)(sinx*ix1+cosx*iy1),
200+(int)(cosx*ix2-sinx*iy1),150+(int)(sinx*ix2+cosx*iy1), 200+(int)(cosx*ix2-sinx*iy1),150+(int)(sinx*ix2+cosx*iy1),
200+(int)(cosx*0-sinx*iy2),150+(int)(sinx*0+cosx*iy2),commonData.fgcolor); 200+(int)(cosx*0-sinx*iy2),150+(int)(sinx*0+cosx*iy2),commonData->fgcolor);
} }
// Draw minute pointer // Draw minute pointer
startwidth = 8; // Start width of pointer startwidth = 8; // Start width of pointer
if(valid1 == true || holdvalues == true || simulation == true){ if(valid1 == true || (source == 'R' && commonData->data.rtcValid) || holdvalues == true || simulation == true){
float sinx=sin(minute * 6.0 * pi / 180); // Minute float sinx=sin(minute * 6.0 * pi / 180); // Minute
float cosx=cos(minute * 6.0 * pi / 180); float cosx=cos(minute * 6.0 * pi / 180);
// Normal pointer // Normal pointer
@@ -293,10 +418,10 @@ public:
float xx1 = -startwidth; float xx1 = -startwidth;
float xx2 = startwidth; float xx2 = startwidth;
float yy1 = -startwidth; float yy1 = -startwidth;
float yy2 = -(rInstrument - 15); float yy2 = -(rInstrument - 15);
getdisplay().fillTriangle(200+(int)(cosx*xx1-sinx*yy1),150+(int)(sinx*xx1+cosx*yy1), getdisplay().fillTriangle(200+(int)(cosx*xx1-sinx*yy1),150+(int)(sinx*xx1+cosx*yy1),
200+(int)(cosx*xx2-sinx*yy1),150+(int)(sinx*xx2+cosx*yy1), 200+(int)(cosx*xx2-sinx*yy1),150+(int)(sinx*xx2+cosx*yy1),
200+(int)(cosx*0-sinx*yy2),150+(int)(sinx*0+cosx*yy2),commonData.fgcolor); 200+(int)(cosx*0-sinx*yy2),150+(int)(sinx*0+cosx*yy2),commonData->fgcolor);
// Inverted pointer // Inverted pointer
// Pointer as triangle with center base 2*width // Pointer as triangle with center base 2*width
float endwidth = 2; // End width of pointer float endwidth = 2; // End width of pointer
@@ -306,32 +431,14 @@ public:
float iy2 = -endwidth; float iy2 = -endwidth;
getdisplay().fillTriangle(200+(int)(cosx*ix1-sinx*iy1),150+(int)(sinx*ix1+cosx*iy1), getdisplay().fillTriangle(200+(int)(cosx*ix1-sinx*iy1),150+(int)(sinx*ix1+cosx*iy1),
200+(int)(cosx*ix2-sinx*iy1),150+(int)(sinx*ix2+cosx*iy1), 200+(int)(cosx*ix2-sinx*iy1),150+(int)(sinx*ix2+cosx*iy1),
200+(int)(cosx*0-sinx*iy2),150+(int)(sinx*0+cosx*iy2),commonData.fgcolor); 200+(int)(cosx*0-sinx*iy2),150+(int)(sinx*0+cosx*iy2),commonData->fgcolor);
} }
// Center circle // Center circle
getdisplay().fillCircle(200, 150, startwidth + 6, commonData.bgcolor); getdisplay().fillCircle(200, 150, startwidth + 6, commonData->bgcolor);
getdisplay().fillCircle(200, 150, startwidth + 4, commonData.fgcolor); getdisplay().fillCircle(200, 150, startwidth + 4, commonData->fgcolor);
//*******************************************************************************************
// Key Layout
getdisplay().setFont(&Ubuntu_Bold8pt7b);
if(keylock == false){
getdisplay().setCursor(130, 290);
getdisplay().print("[ <<<< " + String(commonData.data.actpage) + "/" + String(commonData.data.maxpage) + " >>>> ]");
if(String(backlightMode) == "Control by Key"){ // Key for illumination
getdisplay().setCursor(343, 290);
getdisplay().print("[ILUM]");
}
}
else{
getdisplay().setCursor(130, 290);
getdisplay().print(" [ Keylock active ]");
}
// Update display
getdisplay().nextPage(); // Partial update (fast)
return PAGE_UPDATE;
}; };
}; };

View File

@@ -0,0 +1,260 @@
#if defined BOARD_OBP60S3 || defined BOARD_OBP40S3
#include "Pagedata.h"
#include "OBP60Extensions.h"
// these constants have to match the declaration below in :
// PageDescription registerPageCompass(
// {"COG","HDT", "HDM"}, // Bus values we need in the page
const int HowManyValues = 6;
const int AverageValues = 4;
const int ShowHDM = 0;
const int ShowHDT = 1;
const int ShowCOG = 2;
const int ShowSTW = 3;
const int ShowSOG = 4;
const int ShowDBS = 5;
const int Compass_X0 = 200; // center point of compass band
const int Compass_Y0 = 220; // position of compass lines
const int Compass_LineLength = 22; // length of compass lines
const float Compass_LineDelta = 8.0;// compass band: 1deg = 5 Pixels, 10deg = 50 Pixels
class PageCompass : public Page
{
int WhichDataCompass = ShowHDM;
int WhichDataDisplay = ShowHDM;
public:
PageCompass(CommonData &common){
commonData = &common;
common.logger->logDebug(GwLog::LOG,"Instantiate PageCompass");
}
virtual void setupKeys(){
Page::setupKeys();
commonData->keydata[0].label = "CMP";
commonData->keydata[1].label = "SRC";
}
virtual int handleKey(int key){
// Code for keylock
if ( key == 1 ) {
WhichDataCompass += 1;
if ( WhichDataCompass > ShowCOG)
WhichDataCompass = ShowHDM;
return 0;
}
if ( key == 2 ) {
WhichDataDisplay += 1;
if ( WhichDataDisplay > ShowDBS)
WhichDataDisplay = ShowHDM;
}
if(key == 11){
commonData->keylock = !commonData->keylock;
return 0; // Commit the key
}
return key;
}
int displayPage(PageData &pageData){
GwConfigHandler *config = commonData->config;
GwLog *logger = commonData->logger;
// Old values for hold function
static String OldDataText[HowManyValues] = {"", "", "","", "", ""};
static String OldDataUnits[HowManyValues] = {"", "", "","", "", ""};
// Get config data
String lengthformat = config->getString(config->lengthFormat);
// bool simulation = config->getBool(config->useSimuData);
bool holdvalues = config->getBool(config->holdvalues);
String flashLED = config->getString(config->flashLED);
String backlightMode = config->getString(config->backlight);
GwApi::BoatValue *bvalue;
String DataName[HowManyValues];
double DataValue[HowManyValues];
bool DataValid[HowManyValues];
String DataText[HowManyValues];
String DataUnits[HowManyValues];
String DataFormat[HowManyValues];
FormattedData TheFormattedData;
for (int i = 0; i < HowManyValues; i++){
bvalue = pageData.values[i];
TheFormattedData = formatValue(bvalue, *commonData);
DataName[i] = xdrDelete(bvalue->getName());
DataName[i] = DataName[i].substring(0, 6); // String length limit for value name
DataUnits[i] = formatValue(bvalue, *commonData).unit;
DataText[i] = TheFormattedData.svalue; // Formatted value as string including unit conversion and switching decimal places
DataValue[i] = TheFormattedData.value; // Value as double in SI unit
DataValid[i] = bvalue->valid;
DataFormat[i] = bvalue->getFormat(); // Unit of value
LOG_DEBUG(GwLog::LOG,"Drawing at PageCompass: %d %s %f %s %s", i, DataName[i], DataValue[i], DataFormat[i], DataText[i] );
}
// Optical warning by limit violation (unused)
if(String(flashLED) == "Limit Violation"){
setBlinkingLED(false);
setFlashLED(false);
}
if (bvalue == NULL) return PAGE_OK; // WTF why this statement?
//***********************************************************
// Set display in partial refresh mode
getdisplay().setPartialWindow(0, 0, getdisplay().width(), getdisplay().height()); // Set partial update
getdisplay().setTextColor(commonData->fgcolor);
// Horizontal line 2 pix top & bottom
// Print data on top half
getdisplay().fillRect(0, 130, 400, 2, commonData->fgcolor);
getdisplay().setFont(&Ubuntu_Bold20pt8b);
getdisplay().setCursor(10, 70);
getdisplay().print(DataName[WhichDataDisplay]); // Page name
// Show unit
getdisplay().setFont(&Ubuntu_Bold12pt8b);
getdisplay().setCursor(10, 120);
getdisplay().print(DataUnits[WhichDataDisplay]);
getdisplay().setCursor(190, 120);
getdisplay().setFont(&DSEG7Classic_BoldItalic42pt7b);
if(holdvalues == false){
getdisplay().print(DataText[WhichDataDisplay]); // Real value as formated string
}
else{
getdisplay().print(OldDataText[WhichDataDisplay]); // Old value as formated string
}
if(DataValid[WhichDataDisplay] == true){
OldDataText[WhichDataDisplay] = DataText[WhichDataDisplay]; // Save the old value
OldDataUnits[WhichDataDisplay] = DataUnits[WhichDataDisplay]; // Save the old unit
}
// Now draw compass band
// Get the data
double TheAngle = DataValue[WhichDataCompass];
static double AvgAngle = 0;
AvgAngle = ( AvgAngle * AverageValues + TheAngle ) / (AverageValues + 1 );
int TheTrend = round( ( TheAngle - AvgAngle) * 180.0 / M_PI );
static const int bsize = 30;
char buffer[bsize+1];
buffer[0]=0;
getdisplay().setFont(&Ubuntu_Bold16pt8b);
getdisplay().setCursor(10, Compass_Y0-60);
getdisplay().print(DataName[WhichDataCompass]); // Page name
// Draw compass base line and pointer
getdisplay().fillRect(0, Compass_Y0, 400, 3, commonData->fgcolor);
getdisplay().fillTriangle(Compass_X0,Compass_Y0-40,Compass_X0-10,Compass_Y0-80,Compass_X0+10,Compass_Y0-80,commonData->fgcolor);
// Draw trendlines
for ( int i = 1; i < abs(TheTrend) / 2; i++){
int x1;
if ( TheTrend < 0 )
x1 = Compass_X0 + 20 * i;
else
x1 = Compass_X0 - 20 * ( i + 1 );
getdisplay().fillRect(x1, Compass_Y0 -55, 10, 6, commonData->fgcolor);
}
// Central line + satellite lines
double NextSector = round(TheAngle / ( M_PI / 9 )) * ( M_PI / 9 ); // Get the next 20degree value
double Offset = - ( NextSector - TheAngle); // Offest of the center line compared to TheAngle in Radian
int Delta_X = int ( Offset * 180.0 / M_PI * Compass_LineDelta );
for ( int i = 0; i <=4; i++ ){
int x0;
x0 = Compass_X0 + Delta_X + 2 * i * 5 * Compass_LineDelta;
getdisplay().fillRect(x0-2, Compass_Y0 - 2 * Compass_LineLength, 5, 2 * Compass_LineLength, commonData->fgcolor);
x0 = Compass_X0 + Delta_X + ( 2 * i + 1 ) * 5 * Compass_LineDelta;
getdisplay().fillRect(x0-1, Compass_Y0 - Compass_LineLength, 3, Compass_LineLength, commonData->fgcolor);
x0 = Compass_X0 + Delta_X - 2 * i * 5 * Compass_LineDelta;
getdisplay().fillRect(x0-2, Compass_Y0 - 2 * Compass_LineLength, 5, 2 * Compass_LineLength, commonData->fgcolor);
x0 = Compass_X0 + Delta_X - ( 2 * i + 1 ) * 5 * Compass_LineDelta;
getdisplay().fillRect(x0-1, Compass_Y0 - Compass_LineLength, 3, Compass_LineLength, commonData->fgcolor);
}
getdisplay().fillRect(0, Compass_Y0, 400, 3, commonData->fgcolor);
// Add the numbers to the compass band
int x0;
float AngleToDisplay = NextSector * 180.0 / M_PI;
x0 = Compass_X0 + Delta_X;
getdisplay().setFont(&DSEG7Classic_BoldItalic16pt7b);
do {
getdisplay().setCursor(x0 - 40, Compass_Y0 + 40);
snprintf(buffer,bsize,"%03.0f", AngleToDisplay);
getdisplay().print(buffer);
AngleToDisplay += 20;
if ( AngleToDisplay >= 360.0 )
AngleToDisplay -= 360.0;
x0 -= 4 * 5 * Compass_LineDelta;
} while ( x0 >= 0 - 60 );
AngleToDisplay = NextSector * 180.0 / M_PI - 20;
if ( AngleToDisplay < 0 )
AngleToDisplay += 360.0;
x0 = Compass_X0 + Delta_X + 4 * 5 * Compass_LineDelta;
do {
getdisplay().setCursor(x0 - 40, Compass_Y0 + 40);
snprintf(buffer,bsize,"%03.0f", AngleToDisplay);
// Quick and dirty way to prevent wrapping text in next line
if ( ( x0 - 40 ) > 380 )
buffer[0] = 0;
else if ( ( x0 - 40 ) > 355 )
buffer[1] = 0;
else if ( ( x0 - 40 ) > 325 )
buffer[2] = 0;
getdisplay().print(buffer);
AngleToDisplay -= 20;
if ( AngleToDisplay < 0 )
AngleToDisplay += 360.0;
x0 += 4 * 5 * Compass_LineDelta;
} while (x0 < ( 400 - 20 -40 ) );
// static int x_test = 320;
// x_test += 2;
// snprintf(buffer,bsize,"%03d", x_test);
// getdisplay().setCursor(x_test, Compass_Y0 - 60);
// getdisplay().print(buffer);
// if ( x_test > 390)
// x_test = 320;
return PAGE_UPDATE;
};
};
static Page *createPage(CommonData &common){
return new PageCompass(common);
}/**
* with the code below we make this page known to the PageTask
* we give it a type (name) that can be selected in the config
* we define which function is to be called
* and we provide the number of user parameters we expect
* this will be number of BoatValue pointers in pageData.values
*/
PageDescription registerPageCompass(
"Compass", // Page name
createPage, // Action
0, // Number of bus values depends on selection in Web configuration
{"HDM","HDT", "COG", "STW", "SOG", "DBS"}, // Bus values we need in the page
true // Show display header on/off
);
#endif

View File

@@ -1,28 +1,28 @@
#ifdef BOARD_OBP60S3 #if defined BOARD_OBP60S3 || defined BOARD_OBP40S3
#include "Pagedata.h" #include "Pagedata.h"
#include "OBP60Extensions.h" #include "OBP60Extensions.h"
class PageDST810 : public Page class PageDST810 : public Page
{ {
bool keylock = false; // Keylock public:
public:
PageDST810(CommonData &common){ PageDST810(CommonData &common){
common.logger->logDebug(GwLog::LOG,"Show PageDST810"); commonData = &common;
common.logger->logDebug(GwLog::LOG,"Instantiate PageDST810");
} }
virtual int handleKey(int key){ virtual int handleKey(int key){
if(key == 11){ // Code for keylock // Code for keylock
keylock = !keylock; // Toggle keylock if(key == 11){
commonData->keylock = !commonData->keylock;
return 0; // Commit the key return 0; // Commit the key
} }
return key; return key;
} }
virtual void displayPage(CommonData &commonData, PageData &pageData){ int displayPage(PageData &pageData){
GwConfigHandler *config = commonData.config; GwConfigHandler *config = commonData->config;
GwLog *logger=commonData.logger; GwLog *logger = commonData->logger;
// Old values for hold function // Old values for hold function
static String svalue1old = ""; static String svalue1old = "";
@@ -47,8 +47,8 @@ class PageDST810 : public Page
name1 = name1.substring(0, 6); // String length limit for value name name1 = name1.substring(0, 6); // String length limit for value name
double value1 = bvalue1->value; // Value as double in SI unit double value1 = bvalue1->value; // Value as double in SI unit
bool valid1 = bvalue1->valid; // Valid information bool valid1 = bvalue1->valid; // Valid information
String svalue1 = formatValue(bvalue1, commonData).svalue; // Formatted value as string including unit conversion and switching decimal places String svalue1 = formatValue(bvalue1, *commonData).svalue; // Formatted value as string including unit conversion and switching decimal places
String unit1 = formatValue(bvalue1, commonData).unit; // Unit of value String unit1 = formatValue(bvalue1, *commonData).unit; // Unit of value
// Get boat values #2 // Get boat values #2
GwApi::BoatValue *bvalue2 = pageData.values[1]; // Second element in list (only one value by PageOneValue) GwApi::BoatValue *bvalue2 = pageData.values[1]; // Second element in list (only one value by PageOneValue)
@@ -56,8 +56,8 @@ class PageDST810 : public Page
name2 = name2.substring(0, 6); // String length limit for value name name2 = name2.substring(0, 6); // String length limit for value name
double value2 = bvalue2->value; // Value as double in SI unit double value2 = bvalue2->value; // Value as double in SI unit
bool valid2 = bvalue2->valid; // Valid information bool valid2 = bvalue2->valid; // Valid information
String svalue2 = formatValue(bvalue2, commonData).svalue; // Formatted value as string including unit conversion and switching decimal places String svalue2 = formatValue(bvalue2, *commonData).svalue; // Formatted value as string including unit conversion and switching decimal places
String unit2 = formatValue(bvalue2, commonData).unit; // Unit of value String unit2 = formatValue(bvalue2, *commonData).unit; // Unit of value
// Get boat values #3 // Get boat values #3
GwApi::BoatValue *bvalue3 = pageData.values[2]; // Second element in list (only one value by PageOneValue) GwApi::BoatValue *bvalue3 = pageData.values[2]; // Second element in list (only one value by PageOneValue)
@@ -65,8 +65,8 @@ class PageDST810 : public Page
name3 = name3.substring(0, 6); // String length limit for value name name3 = name3.substring(0, 6); // String length limit for value name
double value3 = bvalue3->value; // Value as double in SI unit double value3 = bvalue3->value; // Value as double in SI unit
bool valid3 = bvalue3->valid; // Valid information bool valid3 = bvalue3->valid; // Valid information
String svalue3 = formatValue(bvalue3, commonData).svalue; // Formatted value as string including unit conversion and switching decimal places String svalue3 = formatValue(bvalue3, *commonData).svalue; // Formatted value as string including unit conversion and switching decimal places
String unit3 = formatValue(bvalue3, commonData).unit; // Unit of value String unit3 = formatValue(bvalue3, *commonData).unit; // Unit of value
// Get boat values #4 // Get boat values #4
GwApi::BoatValue *bvalue4 = pageData.values[3]; // Second element in list (only one value by PageOneValue) GwApi::BoatValue *bvalue4 = pageData.values[3]; // Second element in list (only one value by PageOneValue)
@@ -74,8 +74,8 @@ class PageDST810 : public Page
name4 = name4.substring(0, 6); // String length limit for value name name4 = name4.substring(0, 6); // String length limit for value name
double value4 = bvalue4->value; // Value as double in SI unit double value4 = bvalue4->value; // Value as double in SI unit
bool valid4 = bvalue4->valid; // Valid information bool valid4 = bvalue4->valid; // Valid information
String svalue4 = formatValue(bvalue4, commonData).svalue; // Formatted value as string including unit conversion and switching decimal places String svalue4 = formatValue(bvalue4, *commonData).svalue; // Formatted value as string including unit conversion and switching decimal places
String unit4 = formatValue(bvalue4, commonData).unit; // Unit of value String unit4 = formatValue(bvalue4, *commonData).unit; // Unit of value
// Optical warning by limit violation (unused) // Optical warning by limit violation (unused)
if(String(flashLED) == "Limit Violation"){ if(String(flashLED) == "Limit Violation"){
@@ -84,7 +84,7 @@ class PageDST810 : public Page
} }
// Logging boat values // Logging boat values
if (bvalue1 == NULL) return; if (bvalue1 == NULL) return PAGE_OK; // WTF why this statement?
LOG_DEBUG(GwLog::LOG,"Drawing at PageDST810, %s: %f, %s: %f, %s: %f, %s: %f", name1.c_str(), value1, name2.c_str(), value2, name3.c_str(), value3, name4.c_str(), value4); LOG_DEBUG(GwLog::LOG,"Drawing at PageDST810, %s: %f, %s: %f, %s: %f, %s: %f", name1.c_str(), value1, name2.c_str(), value2, name3.c_str(), value3, name4.c_str(), value4);
// Draw page // Draw page
@@ -93,17 +93,17 @@ class PageDST810 : public Page
// Set display in partial refresh mode // Set display in partial refresh mode
getdisplay().setPartialWindow(0, 0, getdisplay().width(), getdisplay().height()); // Set partial update getdisplay().setPartialWindow(0, 0, getdisplay().width(), getdisplay().height()); // Set partial update
getdisplay().setTextColor(commonData.fgcolor); getdisplay().setTextColor(commonData->fgcolor);
// ############### Value 1 ################ // ############### Value 1 ################
// Show name // Show name
getdisplay().setFont(&Ubuntu_Bold20pt7b); getdisplay().setFont(&Ubuntu_Bold20pt8b);
getdisplay().setCursor(20, 55); getdisplay().setCursor(20, 55);
getdisplay().print("Depth"); // Page name getdisplay().print("Depth"); // Page name
// Show unit // Show unit
getdisplay().setFont(&Ubuntu_Bold12pt7b); getdisplay().setFont(&Ubuntu_Bold12pt8b);
getdisplay().setCursor(20, 90); getdisplay().setCursor(20, 90);
if(holdvalues == false){ if(holdvalues == false){
getdisplay().print(unit1); // Unit getdisplay().print(unit1); // Unit
@@ -131,17 +131,17 @@ class PageDST810 : public Page
// ############### Horizontal Line ################ // ############### Horizontal Line ################
// Horizontal line 3 pix // Horizontal line 3 pix
getdisplay().fillRect(0, 105, 400, 3, commonData.fgcolor); getdisplay().fillRect(0, 105, 400, 3, commonData->fgcolor);
// ############### Value 2 ################ // ############### Value 2 ################
// Show name // Show name
getdisplay().setFont(&Ubuntu_Bold20pt7b); getdisplay().setFont(&Ubuntu_Bold20pt8b);
getdisplay().setCursor(20, 145); getdisplay().setCursor(20, 145);
getdisplay().print("Speed"); // Page name getdisplay().print("Speed"); // Page name
// Show unit // Show unit
getdisplay().setFont(&Ubuntu_Bold12pt7b); getdisplay().setFont(&Ubuntu_Bold12pt8b);
getdisplay().setCursor(20, 180); getdisplay().setCursor(20, 180);
if(holdvalues == false){ if(holdvalues == false){
getdisplay().print(unit2); // Unit getdisplay().print(unit2); // Unit
@@ -169,17 +169,17 @@ class PageDST810 : public Page
// ############### Horizontal Line ################ // ############### Horizontal Line ################
// Horizontal line 3 pix // Horizontal line 3 pix
getdisplay().fillRect(0, 195, 400, 3, commonData.fgcolor); getdisplay().fillRect(0, 195, 400, 3, commonData->fgcolor);
// ############### Value 3 ################ // ############### Value 3 ################
// Show name // Show name
getdisplay().setFont(&Ubuntu_Bold12pt7b); getdisplay().setFont(&Ubuntu_Bold12pt8b);
getdisplay().setCursor(20, 220); getdisplay().setCursor(20, 220);
getdisplay().print("Log"); // Page name getdisplay().print("Log"); // Page name
// Show unit // Show unit
getdisplay().setFont(&Ubuntu_Bold8pt7b); getdisplay().setFont(&Ubuntu_Bold8pt8b);
getdisplay().setCursor(20, 240); getdisplay().setCursor(20, 240);
if(holdvalues == false){ if(holdvalues == false){
getdisplay().print(unit3); // Unit getdisplay().print(unit3); // Unit
@@ -207,17 +207,17 @@ class PageDST810 : public Page
// ############### Vertical Line ################ // ############### Vertical Line ################
// Vertical line 3 pix // Vertical line 3 pix
getdisplay().fillRect(200, 195, 3, 75, commonData.fgcolor); getdisplay().fillRect(200, 195, 3, 75, commonData->fgcolor);
// ############### Value 4 ################ // ############### Value 4 ################
// Show name // Show name
getdisplay().setFont(&Ubuntu_Bold12pt7b); getdisplay().setFont(&Ubuntu_Bold12pt8b);
getdisplay().setCursor(220, 220); getdisplay().setCursor(220, 220);
getdisplay().print("Temp"); // Page name getdisplay().print("Temp"); // Page name
// Show unit // Show unit
getdisplay().setFont(&Ubuntu_Bold8pt7b); getdisplay().setFont(&Ubuntu_Bold8pt8b);
getdisplay().setCursor(220, 240); getdisplay().setCursor(220, 240);
if(holdvalues == false){ if(holdvalues == false){
getdisplay().print(unit4); // Unit getdisplay().print(unit4); // Unit
@@ -242,27 +242,7 @@ class PageDST810 : public Page
unit4old = unit4; // Save the old unit unit4old = unit4; // Save the old unit
} }
return PAGE_UPDATE;
// ############### Key Layout ################
// Key Layout
getdisplay().setFont(&Ubuntu_Bold8pt7b);
if(keylock == false){
getdisplay().setCursor(130, 290);
getdisplay().print("[ <<<< " + String(commonData.data.actpage) + "/" + String(commonData.data.maxpage) + " >>>> ]");
if(String(backlightMode) == "Control by Key"){ // Key for illumination
getdisplay().setCursor(343, 290);
getdisplay().print("[ILUM]");
}
}
else{
getdisplay().setCursor(130, 290);
getdisplay().print(" [ Keylock active ]");
}
// Update display
getdisplay().nextPage(); // Partial update (fast)
}; };
}; };

View File

@@ -1,4 +1,4 @@
#ifdef BOARD_OBP60S3 #if defined BOARD_OBP60S3 || defined BOARD_OBP40S3
#include "Pagedata.h" #include "Pagedata.h"
#include "OBP60Extensions.h" #include "OBP60Extensions.h"
@@ -22,43 +22,6 @@ TODO
*/ */
struct Point {
double x;
double y;
};
Point rotatePoint(const Point& origin, const Point& p, double angle) {
// rotate poind around origin by degrees
Point rotated;
double phi = angle * M_PI / 180.0;
double dx = p.x - origin.x;
double dy = p.y - origin.y;
rotated.x = origin.x + cos(phi) * dx - sin(phi) * dy;
rotated.y = origin.y + sin(phi) * dx + cos(phi) * dy;
return rotated;
}
std::vector<Point> rotatePoints(const Point& origin, const std::vector<Point>& pts, double angle) {
std::vector<Point> rotatedPoints;
for (const auto& p : pts) {
rotatedPoints.push_back(rotatePoint(origin, p, angle));
}
return rotatedPoints;
}
void fillPoly4(const std::vector<Point>& p4, uint16_t color) {
getdisplay().fillTriangle(p4[0].x, p4[0].y, p4[1].x, p4[1].y, p4[2].x, p4[2].y, color);
getdisplay().fillTriangle(p4[0].x, p4[0].y, p4[2].x, p4[2].y, p4[3].x, p4[3].y, color);
}
void drawTextCentered(int16_t tx, int16_t ty, String text) {
int16_t x, y;
uint16_t w, h;
getdisplay().getTextBounds(text, 0, 0, &x, &y, &w, &h);
getdisplay().setCursor(tx - w / 2, ty + h / 2);
getdisplay().print(text);
}
#define fuel_width 16 #define fuel_width 16
#define fuel_height 16 #define fuel_height 16
static unsigned char fuel_bits[] = { static unsigned char fuel_bits[] = {
@@ -94,27 +57,53 @@ static unsigned char gasoline_bits[] = {
0x98, 0xcf, 0x38, 0xe7, 0x78, 0xf0, 0xf8, 0xfa, 0xf8, 0xfa, 0x78, 0xf0, 0x98, 0xcf, 0x38, 0xe7, 0x78, 0xf0, 0xf8, 0xfa, 0xf8, 0xfa, 0x78, 0xf0,
0x38, 0xe7, 0x98, 0xcf, 0xf8, 0xff, 0xf0, 0x7f }; 0x38, 0xe7, 0x98, 0xcf, 0xf8, 0xff, 0xf0, 0x7f };
class PageFluid : public Page{ #define fish_width 16
bool keylock = false; // Keylock #define fish_height 16
static unsigned char fish_bits[] = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe0, 0x01, 0xf0, 0x03, 0xf8, 0x37,
0xfc, 0x7f, 0xfc, 0x7f, 0xec, 0x3f, 0xfc, 0x7f, 0xfc, 0x7f, 0xf8, 0x37,
0xf0, 0x03, 0xe0, 0x01, 0x00, 0x00, 0x00, 0x00 };
class PageFluid : public Page
{
bool simulation = false;
double simgoto;
double simval;
double simstep;
bool holdvalues = false;
int fluidtype; int fluidtype;
public: public:
PageFluid(CommonData &common){ PageFluid(CommonData &common){
common.logger->logDebug(GwLog::LOG,"Show PageFluid"); commonData = &common;
fluidtype = common.config->getInt("page" + String(common.data.actpage) + "fluid", 0); common.logger->logDebug(GwLog::LOG,"Instantiate PageFluid");
simulation = common.config->getBool(common.config->useSimuData);
holdvalues = common.config->getBool(common.config->holdvalues);
simval = double(random(0, 100));
simgoto = double(random(0, 100));
simstep = (simgoto - simval) / 20.0;
} }
virtual int handleKey(int key){ virtual int handleKey(int key){
if(key == 11){ // Code for keylock // Code for keylock
keylock = !keylock; // Toggle keylock if(key == 11){
commonData->keylock = !commonData->keylock;
return 0; // Commit the key return 0; // Commit the key
} }
return key; return key;
} }
virtual void displayPage(CommonData &commonData, PageData &pageData){ virtual void displayNew(PageData &pageData){
GwConfigHandler *config = commonData.config; fluidtype = commonData->config->getInt("page" + String(pageData.pageNumber) + "fluid", 0);
GwLog *logger=commonData.logger; commonData->logger->logDebug(GwLog::LOG,"New PageFluid: fluidtype=%d", fluidtype);
}
int displayPage(PageData &pageData){
GwConfigHandler *config = commonData->config;
GwLog *logger = commonData->logger;
// Old values for hold function
static double value1old;
// Get config data // Get config data
String flashLED = config->getString(config->flashLED); String flashLED = config->getString(config->flashLED);
@@ -126,13 +115,24 @@ class PageFluid : public Page{
setFlashLED(false); setFlashLED(false);
} }
// Logging boat values
LOG_DEBUG(GwLog::LOG,"Drawing at PageFluid");
GwApi::BoatValue *bvalue1 = pageData.values[0]; GwApi::BoatValue *bvalue1 = pageData.values[0];
String name1 = bvalue1->getName(); String name1 = bvalue1->getName();
double value1 = bvalue1->value; double fluidlevel = bvalue1->value;
bool valid1 = bvalue1->valid; if (!simulation) {
if (holdvalues and bvalue1->valid) {
value1old = bvalue1->value;
}
} else {
fluidlevel = simval;
simval += simstep;
if ((simgoto - simval) < 1.5 * simstep) {
simgoto = double(random(0, 100));
simstep = (simgoto - simval) / 20.0;
}
}
// Logging boat values
LOG_DEBUG(GwLog::LOG,"Drawing at PageFluid: value=%f", bvalue1->value);
// Draw page // Draw page
//*********************************************************** //***********************************************************
@@ -140,10 +140,10 @@ class PageFluid : public Page{
// Set display in partial refresh mode // Set display in partial refresh mode
getdisplay().setPartialWindow(0, 0, getdisplay().width(), getdisplay().height()); getdisplay().setPartialWindow(0, 0, getdisplay().width(), getdisplay().height());
getdisplay().setTextColor(commonData.fgcolor); getdisplay().setTextColor(commonData->fgcolor);
// descriptions // descriptions
getdisplay().setFont(&Ubuntu_Bold12pt7b); getdisplay().setFont(&Ubuntu_Bold12pt8b);
getdisplay().setCursor(20, 60); getdisplay().setCursor(20, 60);
getdisplay().print("Fluid"); getdisplay().print("Fluid");
@@ -158,57 +158,63 @@ class PageFluid : public Page{
uint8_t r = 110; uint8_t r = 110;
// circular frame // circular frame
getdisplay().drawCircle(c.x, c.y, r+5, commonData.fgcolor); getdisplay().drawCircle(c.x, c.y, r+5, commonData->fgcolor);
getdisplay().fillCircle(c.x, c.y, r+2, commonData.fgcolor); getdisplay().fillCircle(c.x, c.y, r+2, commonData->fgcolor);
getdisplay().fillCircle(c.x, c.y, r-1, commonData.bgcolor); getdisplay().fillCircle(c.x, c.y, r-1, commonData->bgcolor);
// center of pointer as dot // center of pointer as dot
getdisplay().fillCircle(c.x, c.y, 8, commonData.fgcolor); getdisplay().fillCircle(c.x, c.y, 8, commonData->fgcolor);
// value down centered // value down centered
char buffer[6]; char buffer[6];
if (bvalue1->valid) { if (bvalue1->valid or simulation) {
snprintf(buffer, 6, "%3.0f%%", bvalue1->value); snprintf(buffer, 6, "%3.0f%%", fluidlevel);
} else { } else {
strcpy(buffer, "---"); strcpy(buffer, "---");
} }
drawTextCentered(c.x, c.y + r - 20, String(buffer)); drawTextCenter(c.x, c.y + r - 20, String(buffer));
// draw symbol (as bitmap) // draw symbol (as bitmap)
switch (fluidtype) { switch (fluidtype) {
case 0: case 0:
getdisplay().drawXBitmap(c.x-8, c.y-50, fuel_bits, fuel_width, fuel_height, commonData.fgcolor); getdisplay().drawXBitmap(c.x-8, c.y-50, fuel_bits, fuel_width, fuel_height, commonData->fgcolor);
break; break;
case 1: case 1:
getdisplay().drawXBitmap(c.x-8, c.y-50, water_bits, water_width, water_height, commonData.fgcolor); getdisplay().drawXBitmap(c.x-8, c.y-50, water_bits, water_width, water_height, commonData->fgcolor);
break;
case 2: // gray water no symbol yet
// getdisplay().drawXBitmap(c.x-8, c.y-50, gray_bits, gray_width, gray_height, commonData->fgcolor);
break;
case 3:
getdisplay().drawXBitmap(c.x-8, c.y-50, fish_bits, fish_width, fish_height, commonData->fgcolor);
break; break;
case 4: case 4:
getdisplay().drawXBitmap(c.x-8, c.y-50, oil_bits, oil_width, oil_height, commonData.fgcolor); getdisplay().drawXBitmap(c.x-8, c.y-50, oil_bits, oil_width, oil_height, commonData->fgcolor);
break; break;
case 5: case 5:
getdisplay().drawXBitmap(c.x-8, c.y-50, waste_bits, waste_width, waste_height, commonData.fgcolor); getdisplay().drawXBitmap(c.x-8, c.y-50, waste_bits, waste_width, waste_height, commonData->fgcolor);
break; break;
case 6: case 6:
getdisplay().drawXBitmap(c.x-8, c.y-50, gasoline_bits, gasoline_width, gasoline_height, commonData.fgcolor); getdisplay().drawXBitmap(c.x-8, c.y-50, gasoline_bits, gasoline_width, gasoline_height, commonData->fgcolor);
break; break;
} }
Point p, pr; Point p, pr;
// scale texts // scale texts
getdisplay().setFont(&Ubuntu_Bold8pt7b); getdisplay().setFont(&Ubuntu_Bold8pt8b);
p = {c.x, c.y - r + 30}; p = {c.x, c.y - r + 30};
drawTextCentered(p.x, p.y, "1/2"); drawTextCenter(p.x, p.y, "1/2");
pr = rotatePoint(c, p, -60); pr = rotatePoint(c, p, -60);
drawTextCentered(pr.x, pr.y, "1/4"); drawTextCenter(pr.x, pr.y, "1/4");
pr = rotatePoint(c, p, 60); pr = rotatePoint(c, p, 60);
drawTextCentered(pr.x, pr.y, "3/4"); drawTextCenter(pr.x, pr.y, "3/4");
// empty and full // empty and full
getdisplay().setFont(&Ubuntu_Bold12pt7b); getdisplay().setFont(&Ubuntu_Bold12pt8b);
p = rotatePoint(c, {c.x, c.y - r + 30}, -130); p = rotatePoint(c, {c.x, c.y - r + 30}, -130);
drawTextCentered(p.x, p.y, "E"); drawTextCenter(p.x, p.y, "E");
p = rotatePoint(c, {c.x, c.y - r + 30}, 130); p = rotatePoint(c, {c.x, c.y - r + 30}, 130);
drawTextCentered(p.x, p.y, "F"); drawTextCenter(p.x, p.y, "F");
// lines // lines
std::vector<Point> pts = { std::vector<Point> pts = {
@@ -217,11 +223,11 @@ class PageFluid : public Page{
{c.x + 2, c.y - (r - 16)}, {c.x + 2, c.y - (r - 16)},
{c.x - 2, c.y - (r - 16)} {c.x - 2, c.y - (r - 16)}
}; };
fillPoly4(rotatePoints(c, pts, -120), commonData.fgcolor); fillPoly4(rotatePoints(c, pts, -120), commonData->fgcolor);
fillPoly4(rotatePoints(c, pts, -60), commonData.fgcolor); fillPoly4(rotatePoints(c, pts, -60), commonData->fgcolor);
fillPoly4(rotatePoints(c, pts, 0), commonData.fgcolor); fillPoly4(rotatePoints(c, pts, 0), commonData->fgcolor);
fillPoly4(rotatePoints(c, pts, 60), commonData.fgcolor); fillPoly4(rotatePoints(c, pts, 60), commonData->fgcolor);
fillPoly4(rotatePoints(c, pts, 120), commonData.fgcolor); fillPoly4(rotatePoints(c, pts, 120), commonData->fgcolor);
// dots // dots
// rotate 0 to 360 in 12 degree steps // rotate 0 to 360 in 12 degree steps
@@ -230,40 +236,23 @@ class PageFluid : public Page{
continue; continue;
} }
p = rotatePoint(c, {c.x, c.y - r + 10}, angle); p = rotatePoint(c, {c.x, c.y - r + 10}, angle);
getdisplay().fillCircle(p.x, p.y, 3, commonData.fgcolor); getdisplay().fillCircle(p.x, p.y, 3, commonData->fgcolor);
} }
// pointer // pointer
if (bvalue1->valid) { if (bvalue1->valid or simulation) {
pts = { pts = {
{c.x - 1, c.y - (r - 20)}, {c.x - 1, c.y - (r - 20)},
{c.x + 1, c.y - (r - 20)}, {c.x + 1, c.y - (r - 20)},
{c.x + 6, c.y + 15}, {c.x + 6, c.y + 15},
{c.x - 6, c.y + 15} {c.x - 6, c.y + 15}
}; };
fillPoly4(rotatePoints(c, pts, -120 + bvalue1->value * 2.4), commonData.fgcolor); fillPoly4(rotatePoints(c, pts, -120 + fluidlevel * 2.4), commonData->fgcolor);
// Pointer axis is white // Pointer axis is white
getdisplay().fillCircle(c.x, c.y, 6, commonData.bgcolor); getdisplay().fillCircle(c.x, c.y, 6, commonData->bgcolor);
} }
// Key Layout return PAGE_UPDATE;
getdisplay().setFont(&Ubuntu_Bold8pt7b);
if(keylock == false){
getdisplay().setCursor(130, 296);
getdisplay().print("[ <<<< " + String(commonData.data.actpage) + "/" + String(commonData.data.maxpage) + " >>>> ]");
if(String(backlightMode) == "Control by Key"){ // Key for illumination
getdisplay().setCursor(343, 296);
getdisplay().print("[ILUM]");
}
}
else{
getdisplay().setCursor(130, 296);
getdisplay().print(" [ Keylock active ]");
}
// Update display
getdisplay().nextPage(); // Partial update (fast)
}; };
}; };

View File

@@ -1,28 +1,29 @@
#ifdef BOARD_OBP60S3 #if defined BOARD_OBP60S3 || defined BOARD_OBP40S3
#include "Pagedata.h" #include "Pagedata.h"
#include "OBP60Extensions.h" #include "OBP60Extensions.h"
#include "BoatDataCalibration.h"
class PageFourValues : public Page class PageFourValues : public Page
{ {
bool keylock = false; // Keylock
public: public:
PageFourValues(CommonData &common){ PageFourValues(CommonData &common){
common.logger->logDebug(GwLog::LOG,"Show PageFourValues"); commonData = &common;
common.logger->logDebug(GwLog::LOG,"Instantiate PageFourValues");
} }
virtual int handleKey(int key){ virtual int handleKey(int key){
if(key == 11){ // Code for keylock // Code for keylock
keylock = !keylock; // Toggle keylock if(key == 11){
commonData->keylock = !commonData->keylock;
return 0; // Commit the key return 0; // Commit the key
} }
return key; return key;
} }
virtual void displayPage(CommonData &commonData, PageData &pageData){ int displayPage(PageData &pageData){
GwConfigHandler *config = commonData.config; GwConfigHandler *config = commonData->config;
GwLog *logger=commonData.logger; GwLog *logger = commonData->logger;
// Old values for hold function // Old values for hold function
static String svalue1old = ""; static String svalue1old = "";
@@ -45,37 +46,41 @@ class PageFourValues : public Page
GwApi::BoatValue *bvalue1 = pageData.values[0]; // First element in list (only one value by PageOneValue) GwApi::BoatValue *bvalue1 = pageData.values[0]; // First element in list (only one value by PageOneValue)
String name1 = xdrDelete(bvalue1->getName()); // Value name String name1 = xdrDelete(bvalue1->getName()); // Value name
name1 = name1.substring(0, 6); // String length limit for value name name1 = name1.substring(0, 6); // String length limit for value name
calibrationData.calibrateInstance(bvalue1, logger); // Check if boat data value is to be calibrated
double value1 = bvalue1->value; // Value as double in SI unit double value1 = bvalue1->value; // Value as double in SI unit
bool valid1 = bvalue1->valid; // Valid information bool valid1 = bvalue1->valid; // Valid information
String svalue1 = formatValue(bvalue1, commonData).svalue; // Formatted value as string including unit conversion and switching decimal places String svalue1 = formatValue(bvalue1, *commonData).svalue; // Formatted value as string including unit conversion and switching decimal places
String unit1 = formatValue(bvalue1, commonData).unit; // Unit of value String unit1 = formatValue(bvalue1, *commonData).unit; // Unit of value
// Get boat values #2 // Get boat values #2
GwApi::BoatValue *bvalue2 = pageData.values[1]; // Second element in list (only one value by PageOneValue) GwApi::BoatValue *bvalue2 = pageData.values[1]; // Second element in list
String name2 = xdrDelete(bvalue2->getName()); // Value name String name2 = xdrDelete(bvalue2->getName()); // Value name
name2 = name2.substring(0, 6); // String length limit for value name name2 = name2.substring(0, 6); // String length limit for value name
calibrationData.calibrateInstance(bvalue2, logger); // Check if boat data value is to be calibrated
double value2 = bvalue2->value; // Value as double in SI unit double value2 = bvalue2->value; // Value as double in SI unit
bool valid2 = bvalue2->valid; // Valid information bool valid2 = bvalue2->valid; // Valid information
String svalue2 = formatValue(bvalue2, commonData).svalue; // Formatted value as string including unit conversion and switching decimal places String svalue2 = formatValue(bvalue2, *commonData).svalue; // Formatted value as string including unit conversion and switching decimal places
String unit2 = formatValue(bvalue2, commonData).unit; // Unit of value String unit2 = formatValue(bvalue2, *commonData).unit; // Unit of value
// Get boat values #3 // Get boat values #3
GwApi::BoatValue *bvalue3 = pageData.values[2]; // Second element in list (only one value by PageOneValue) GwApi::BoatValue *bvalue3 = pageData.values[2]; // Third element in list
String name3 = xdrDelete(bvalue3->getName()); // Value name String name3 = xdrDelete(bvalue3->getName()); // Value name
name3 = name3.substring(0, 6); // String length limit for value name name3 = name3.substring(0, 6); // String length limit for value name
calibrationData.calibrateInstance(bvalue3, logger); // Check if boat data value is to be calibrated
double value3 = bvalue3->value; // Value as double in SI unit double value3 = bvalue3->value; // Value as double in SI unit
bool valid3 = bvalue3->valid; // Valid information bool valid3 = bvalue3->valid; // Valid information
String svalue3 = formatValue(bvalue3, commonData).svalue; // Formatted value as string including unit conversion and switching decimal places String svalue3 = formatValue(bvalue3, *commonData).svalue; // Formatted value as string including unit conversion and switching decimal places
String unit3 = formatValue(bvalue3, commonData).unit; // Unit of value String unit3 = formatValue(bvalue3, *commonData).unit; // Unit of value
// Get boat values #4 // Get boat values #4
GwApi::BoatValue *bvalue4 = pageData.values[3]; // Second element in list (only one value by PageOneValue) GwApi::BoatValue *bvalue4 = pageData.values[3]; // Fourth element in list
String name4 = xdrDelete(bvalue4->getName()); // Value name String name4 = xdrDelete(bvalue4->getName()); // Value name
name4 = name4.substring(0, 6); // String length limit for value name name4 = name4.substring(0, 6); // String length limit for value name
calibrationData.calibrateInstance(bvalue4, logger); // Check if boat data value is to be calibrated
double value4 = bvalue4->value; // Value as double in SI unit double value4 = bvalue4->value; // Value as double in SI unit
bool valid4 = bvalue4->valid; // Valid information bool valid4 = bvalue4->valid; // Valid information
String svalue4 = formatValue(bvalue4, commonData).svalue; // Formatted value as string including unit conversion and switching decimal places String svalue4 = formatValue(bvalue4, *commonData).svalue; // Formatted value as string including unit conversion and switching decimal places
String unit4 = formatValue(bvalue4, commonData).unit; // Unit of value String unit4 = formatValue(bvalue4, *commonData).unit; // Unit of value
// Optical warning by limit violation (unused) // Optical warning by limit violation (unused)
if(String(flashLED) == "Limit Violation"){ if(String(flashLED) == "Limit Violation"){
@@ -84,7 +89,7 @@ class PageFourValues : public Page
} }
// Logging boat values // Logging boat values
if (bvalue1 == NULL) return; if (bvalue1 == NULL) return PAGE_OK; // WTF why this statement?
LOG_DEBUG(GwLog::LOG,"Drawing at PageFourValues, %s: %f, %s: %f, %s: %f, %s: %f", name1.c_str(), value1, name2.c_str(), value2, name3.c_str(), value3, name4.c_str(), value4); LOG_DEBUG(GwLog::LOG,"Drawing at PageFourValues, %s: %f, %s: %f, %s: %f, %s: %f", name1.c_str(), value1, name2.c_str(), value2, name3.c_str(), value3, name4.c_str(), value4);
// Draw page // Draw page
@@ -93,17 +98,17 @@ class PageFourValues : public Page
// Set display in partial refresh mode // Set display in partial refresh mode
getdisplay().setPartialWindow(0, 0, getdisplay().width(), getdisplay().height()); // Set partial update getdisplay().setPartialWindow(0, 0, getdisplay().width(), getdisplay().height()); // Set partial update
getdisplay().setTextColor(commonData.fgcolor); getdisplay().setTextColor(commonData->fgcolor);
// ############### Value 1 ################ // ############### Value 1 ################
// Show name // Show name
getdisplay().setFont(&Ubuntu_Bold16pt7b); getdisplay().setFont(&Ubuntu_Bold16pt8b);
getdisplay().setCursor(20, 45); getdisplay().setCursor(20, 45);
getdisplay().print(name1); // Page name getdisplay().print(name1); // Page name
// Show unit // Show unit
getdisplay().setFont(&Ubuntu_Bold8pt7b); getdisplay().setFont(&Ubuntu_Bold8pt8b);
getdisplay().setCursor(20, 65); getdisplay().setCursor(20, 65);
if(holdvalues == false){ if(holdvalues == false){
getdisplay().print(unit1); // Unit getdisplay().print(unit1); // Unit
@@ -114,11 +119,11 @@ class PageFourValues : public Page
// Switch font if format for any values // Switch font if format for any values
if(bvalue1->getFormat() == "formatLatitude" || bvalue1->getFormat() == "formatLongitude"){ if(bvalue1->getFormat() == "formatLatitude" || bvalue1->getFormat() == "formatLongitude"){
getdisplay().setFont(&Ubuntu_Bold12pt7b); getdisplay().setFont(&Ubuntu_Bold12pt8b);
getdisplay().setCursor(120, 55); getdisplay().setCursor(120, 55);
} }
else if(bvalue1->getFormat() == "formatTime" || bvalue1->getFormat() == "formatDate"){ else if(bvalue1->getFormat() == "formatTime" || bvalue1->getFormat() == "formatDate"){
getdisplay().setFont(&Ubuntu_Bold12pt7b); getdisplay().setFont(&Ubuntu_Bold12pt8b);
getdisplay().setCursor(150, 58); getdisplay().setCursor(150, 58);
} }
else{ else{
@@ -141,17 +146,17 @@ class PageFourValues : public Page
// ############### Horizontal Line ################ // ############### Horizontal Line ################
// Horizontal line 3 pix // Horizontal line 3 pix
getdisplay().fillRect(0, 80, 400, 3, commonData.fgcolor); getdisplay().fillRect(0, 80, 400, 3, commonData->fgcolor);
// ############### Value 2 ################ // ############### Value 2 ################
// Show name // Show name
getdisplay().setFont(&Ubuntu_Bold16pt7b); getdisplay().setFont(&Ubuntu_Bold16pt8b);
getdisplay().setCursor(20, 113); getdisplay().setCursor(20, 113);
getdisplay().print(name2); // Page name getdisplay().print(name2); // Page name
// Show unit // Show unit
getdisplay().setFont(&Ubuntu_Bold8pt7b); getdisplay().setFont(&Ubuntu_Bold8pt8b);
getdisplay().setCursor(20, 133); getdisplay().setCursor(20, 133);
if(holdvalues == false){ if(holdvalues == false){
getdisplay().print(unit2); // Unit getdisplay().print(unit2); // Unit
@@ -162,11 +167,11 @@ class PageFourValues : public Page
// Switch font if format for any values // Switch font if format for any values
if(bvalue2->getFormat() == "formatLatitude" || bvalue2->getFormat() == "formatLongitude"){ if(bvalue2->getFormat() == "formatLatitude" || bvalue2->getFormat() == "formatLongitude"){
getdisplay().setFont(&Ubuntu_Bold12pt7b); getdisplay().setFont(&Ubuntu_Bold12pt8b);
getdisplay().setCursor(120, 123); getdisplay().setCursor(120, 123);
} }
else if(bvalue2->getFormat() == "formatTime" || bvalue2->getFormat() == "formatDate"){ else if(bvalue2->getFormat() == "formatTime" || bvalue2->getFormat() == "formatDate"){
getdisplay().setFont(&Ubuntu_Bold12pt7b); getdisplay().setFont(&Ubuntu_Bold12pt8b);
getdisplay().setCursor(150, 123); getdisplay().setCursor(150, 123);
} }
else{ else{
@@ -189,17 +194,17 @@ class PageFourValues : public Page
// ############### Horizontal Line ################ // ############### Horizontal Line ################
// Horizontal line 3 pix // Horizontal line 3 pix
getdisplay().fillRect(0, 146, 400, 3, commonData.fgcolor); getdisplay().fillRect(0, 146, 400, 3, commonData->fgcolor);
// ############### Value 3 ################ // ############### Value 3 ################
// Show name // Show name
getdisplay().setFont(&Ubuntu_Bold16pt7b); getdisplay().setFont(&Ubuntu_Bold16pt8b);
getdisplay().setCursor(20, 181); getdisplay().setCursor(20, 181);
getdisplay().print(name3); // Page name getdisplay().print(name3); // Page name
// Show unit // Show unit
getdisplay().setFont(&Ubuntu_Bold8pt7b); getdisplay().setFont(&Ubuntu_Bold8pt8b);
getdisplay().setCursor(20, 201); getdisplay().setCursor(20, 201);
if(holdvalues == false){ if(holdvalues == false){
getdisplay().print(unit3); // Unit getdisplay().print(unit3); // Unit
@@ -210,11 +215,11 @@ class PageFourValues : public Page
// Switch font if format for any values // Switch font if format for any values
if(bvalue3->getFormat() == "formatLatitude" || bvalue3->getFormat() == "formatLongitude"){ if(bvalue3->getFormat() == "formatLatitude" || bvalue3->getFormat() == "formatLongitude"){
getdisplay().setFont(&Ubuntu_Bold12pt7b); getdisplay().setFont(&Ubuntu_Bold12pt8b);
getdisplay().setCursor(120, 191); getdisplay().setCursor(120, 191);
} }
else if(bvalue3->getFormat() == "formatTime" || bvalue3->getFormat() == "formatDate"){ else if(bvalue3->getFormat() == "formatTime" || bvalue3->getFormat() == "formatDate"){
getdisplay().setFont(&Ubuntu_Bold12pt7b); getdisplay().setFont(&Ubuntu_Bold12pt8b);
getdisplay().setCursor(150, 191); getdisplay().setCursor(150, 191);
} }
else{ else{
@@ -237,17 +242,17 @@ class PageFourValues : public Page
// ############### Horizontal Line ################ // ############### Horizontal Line ################
// Horizontal line 3 pix // Horizontal line 3 pix
getdisplay().fillRect(0, 214, 400, 3, commonData.fgcolor); getdisplay().fillRect(0, 214, 400, 3, commonData->fgcolor);
// ############### Value 4 ################ // ############### Value 4 ################
// Show name // Show name
getdisplay().setFont(&Ubuntu_Bold16pt7b); getdisplay().setFont(&Ubuntu_Bold16pt8b);
getdisplay().setCursor(20, 249); getdisplay().setCursor(20, 249);
getdisplay().print(name4); // Page name getdisplay().print(name4); // Page name
// Show unit // Show unit
getdisplay().setFont(&Ubuntu_Bold8pt7b); getdisplay().setFont(&Ubuntu_Bold8pt8b);
getdisplay().setCursor(20, 269); getdisplay().setCursor(20, 269);
if(holdvalues == false){ if(holdvalues == false){
getdisplay().print(unit4); // Unit getdisplay().print(unit4); // Unit
@@ -258,11 +263,11 @@ class PageFourValues : public Page
// Switch font if format for any values // Switch font if format for any values
if(bvalue4->getFormat() == "formatLatitude" || bvalue4->getFormat() == "formatLongitude"){ if(bvalue4->getFormat() == "formatLatitude" || bvalue4->getFormat() == "formatLongitude"){
getdisplay().setFont(&Ubuntu_Bold12pt7b); getdisplay().setFont(&Ubuntu_Bold12pt8b);
getdisplay().setCursor(120, 259); getdisplay().setCursor(120, 259);
} }
else if(bvalue4->getFormat() == "formatTime" || bvalue4->getFormat() == "formatDate"){ else if(bvalue4->getFormat() == "formatTime" || bvalue4->getFormat() == "formatDate"){
getdisplay().setFont(&Ubuntu_Bold12pt7b); getdisplay().setFont(&Ubuntu_Bold12pt8b);
getdisplay().setCursor(150, 259); getdisplay().setCursor(150, 259);
} }
else{ else{
@@ -282,27 +287,7 @@ class PageFourValues : public Page
unit4old = unit4; // Save the old unit unit4old = unit4; // Save the old unit
} }
return PAGE_UPDATE;
// ############### Key Layout ################
// Key Layout
getdisplay().setFont(&Ubuntu_Bold8pt7b);
if(keylock == false){
getdisplay().setCursor(130, 290);
getdisplay().print("[ <<<< " + String(commonData.data.actpage) + "/" + String(commonData.data.maxpage) + " >>>> ]");
if(String(backlightMode) == "Control by Key"){ // Key for illumination
getdisplay().setCursor(343, 290);
getdisplay().print("[ILUM]");
}
}
else{
getdisplay().setCursor(130, 290);
getdisplay().print(" [ Keylock active ]");
}
// Update display
getdisplay().nextPage(); // Partial update (fast)
}; };
}; };
@@ -316,7 +301,7 @@ static Page *createPage(CommonData &common){
* this will be number of BoatValue pointers in pageData.values * this will be number of BoatValue pointers in pageData.values
*/ */
PageDescription registerPageFourValues( PageDescription registerPageFourValues(
"FourValues", // Page name "FourValues", // Page name
createPage, // Action createPage, // Action
4, // Number of bus values depends on selection in Web configuration 4, // Number of bus values depends on selection in Web configuration
true // Show display header on/off true // Show display header on/off

View File

@@ -1,28 +1,29 @@
#ifdef BOARD_OBP60S3 #if defined BOARD_OBP60S3 || defined BOARD_OBP40S3
#include "Pagedata.h" #include "Pagedata.h"
#include "OBP60Extensions.h" #include "OBP60Extensions.h"
#include "BoatDataCalibration.h"
class PageFourValues2 : public Page class PageFourValues2 : public Page
{ {
bool keylock = false; // Keylock
public: public:
PageFourValues2(CommonData &common){ PageFourValues2(CommonData &common){
common.logger->logDebug(GwLog::LOG,"Show PageFourValues2"); commonData = &common;
common.logger->logDebug(GwLog::LOG,"Instantiate PageFourValues2");
} }
virtual int handleKey(int key){ virtual int handleKey(int key){
if(key == 11){ // Code for keylock // Code for keylock
keylock = !keylock; // Toggle keylock if(key == 11){
commonData->keylock = !commonData->keylock; // Toggle keylock
return 0; // Commit the key return 0; // Commit the key
} }
return key; return key;
} }
virtual void displayPage(CommonData &commonData, PageData &pageData){ int displayPage(PageData &pageData){
GwConfigHandler *config = commonData.config; GwConfigHandler *config = commonData->config;
GwLog *logger=commonData.logger; GwLog *logger = commonData->logger;
// Old values for hold function // Old values for hold function
static String svalue1old = ""; static String svalue1old = "";
@@ -45,37 +46,41 @@ class PageFourValues2 : public Page
GwApi::BoatValue *bvalue1 = pageData.values[0]; // First element in list (only one value by PageOneValue) GwApi::BoatValue *bvalue1 = pageData.values[0]; // First element in list (only one value by PageOneValue)
String name1 = xdrDelete(bvalue1->getName()); // Value name String name1 = xdrDelete(bvalue1->getName()); // Value name
name1 = name1.substring(0, 6); // String length limit for value name name1 = name1.substring(0, 6); // String length limit for value name
calibrationData.calibrateInstance(bvalue1, logger); // Check if boat data value is to be calibrated
double value1 = bvalue1->value; // Value as double in SI unit double value1 = bvalue1->value; // Value as double in SI unit
bool valid1 = bvalue1->valid; // Valid information bool valid1 = bvalue1->valid; // Valid information
String svalue1 = formatValue(bvalue1, commonData).svalue; // Formatted value as string including unit conversion and switching decimal places String svalue1 = formatValue(bvalue1, *commonData).svalue; // Formatted value as string including unit conversion and switching decimal places
String unit1 = formatValue(bvalue1, commonData).unit; // Unit of value String unit1 = formatValue(bvalue1, *commonData).unit; // Unit of value
// Get boat values #2 // Get boat values #2
GwApi::BoatValue *bvalue2 = pageData.values[1]; // Second element in list (only one value by PageOneValue) GwApi::BoatValue *bvalue2 = pageData.values[1]; // Second element in list (only one value by PageOneValue)
String name2 = xdrDelete(bvalue2->getName()); // Value name String name2 = xdrDelete(bvalue2->getName()); // Value name
name2 = name2.substring(0, 6); // String length limit for value name name2 = name2.substring(0, 6); // String length limit for value name
calibrationData.calibrateInstance(bvalue2, logger); // Check if boat data value is to be calibrated
double value2 = bvalue2->value; // Value as double in SI unit double value2 = bvalue2->value; // Value as double in SI unit
bool valid2 = bvalue2->valid; // Valid information bool valid2 = bvalue2->valid; // Valid information
String svalue2 = formatValue(bvalue2, commonData).svalue; // Formatted value as string including unit conversion and switching decimal places String svalue2 = formatValue(bvalue2, *commonData).svalue; // Formatted value as string including unit conversion and switching decimal places
String unit2 = formatValue(bvalue2, commonData).unit; // Unit of value String unit2 = formatValue(bvalue2, *commonData).unit; // Unit of value
// Get boat values #3 // Get boat values #3
GwApi::BoatValue *bvalue3 = pageData.values[2]; // Second element in list (only one value by PageOneValue) GwApi::BoatValue *bvalue3 = pageData.values[2]; // Second element in list (only one value by PageOneValue)
String name3 = xdrDelete(bvalue3->getName()); // Value name String name3 = xdrDelete(bvalue3->getName()); // Value name
name3 = name3.substring(0, 6); // String length limit for value name name3 = name3.substring(0, 6); // String length limit for value name
calibrationData.calibrateInstance(bvalue3, logger); // Check if boat data value is to be calibrated
double value3 = bvalue3->value; // Value as double in SI unit double value3 = bvalue3->value; // Value as double in SI unit
bool valid3 = bvalue3->valid; // Valid information bool valid3 = bvalue3->valid; // Valid information
String svalue3 = formatValue(bvalue3, commonData).svalue; // Formatted value as string including unit conversion and switching decimal places String svalue3 = formatValue(bvalue3, *commonData).svalue; // Formatted value as string including unit conversion and switching decimal places
String unit3 = formatValue(bvalue3, commonData).unit; // Unit of value String unit3 = formatValue(bvalue3, *commonData).unit; // Unit of value
// Get boat values #4 // Get boat values #4
GwApi::BoatValue *bvalue4 = pageData.values[3]; // Second element in list (only one value by PageOneValue) GwApi::BoatValue *bvalue4 = pageData.values[3]; // Second element in list (only one value by PageOneValue)
String name4 = xdrDelete(bvalue4->getName()); // Value name String name4 = xdrDelete(bvalue4->getName()); // Value name
name4 = name4.substring(0, 6); // String length limit for value name name4 = name4.substring(0, 6); // String length limit for value name
calibrationData.calibrateInstance(bvalue4, logger); // Check if boat data value is to be calibrated
double value4 = bvalue4->value; // Value as double in SI unit double value4 = bvalue4->value; // Value as double in SI unit
bool valid4 = bvalue4->valid; // Valid information bool valid4 = bvalue4->valid; // Valid information
String svalue4 = formatValue(bvalue4, commonData).svalue; // Formatted value as string including unit conversion and switching decimal places String svalue4 = formatValue(bvalue4, *commonData).svalue; // Formatted value as string including unit conversion and switching decimal places
String unit4 = formatValue(bvalue4, commonData).unit; // Unit of value String unit4 = formatValue(bvalue4, *commonData).unit; // Unit of value
// Optical warning by limit violation (unused) // Optical warning by limit violation (unused)
if(String(flashLED) == "Limit Violation"){ if(String(flashLED) == "Limit Violation"){
@@ -84,7 +89,7 @@ class PageFourValues2 : public Page
} }
// Logging boat values // Logging boat values
if (bvalue1 == NULL) return; if (bvalue1 == NULL) return PAGE_OK; // WTF why this statement?
LOG_DEBUG(GwLog::LOG,"Drawing at PageFourValues2, %s: %f, %s: %f, %s: %f, %s: %f", name1.c_str(), value1, name2.c_str(), value2, name3.c_str(), value3, name4.c_str(), value4); LOG_DEBUG(GwLog::LOG,"Drawing at PageFourValues2, %s: %f, %s: %f, %s: %f, %s: %f", name1.c_str(), value1, name2.c_str(), value2, name3.c_str(), value3, name4.c_str(), value4);
// Draw page // Draw page
@@ -93,17 +98,17 @@ class PageFourValues2 : public Page
// Set display in partial refresh mode // Set display in partial refresh mode
getdisplay().setPartialWindow(0, 0, getdisplay().width(), getdisplay().height()); // Set partial update getdisplay().setPartialWindow(0, 0, getdisplay().width(), getdisplay().height()); // Set partial update
getdisplay().setTextColor(commonData.fgcolor); getdisplay().setTextColor(commonData->fgcolor);
// ############### Value 1 ################ // ############### Value 1 ################
// Show name // Show name
getdisplay().setFont(&Ubuntu_Bold20pt7b); getdisplay().setFont(&Ubuntu_Bold20pt8b);
getdisplay().setCursor(20, 55); getdisplay().setCursor(20, 55);
getdisplay().print(name1); // Page name getdisplay().print(name1); // Page name
// Show unit // Show unit
getdisplay().setFont(&Ubuntu_Bold12pt7b); getdisplay().setFont(&Ubuntu_Bold12pt8b);
getdisplay().setCursor(20, 90); getdisplay().setCursor(20, 90);
if(holdvalues == false){ if(holdvalues == false){
getdisplay().print(unit1); // Unit getdisplay().print(unit1); // Unit
@@ -114,11 +119,11 @@ class PageFourValues2 : public Page
// Switch font if format for any values // Switch font if format for any values
if(bvalue1->getFormat() == "formatLatitude" || bvalue1->getFormat() == "formatLongitude"){ if(bvalue1->getFormat() == "formatLatitude" || bvalue1->getFormat() == "formatLongitude"){
getdisplay().setFont(&Ubuntu_Bold12pt7b); getdisplay().setFont(&Ubuntu_Bold12pt8b);
getdisplay().setCursor(100, 90); getdisplay().setCursor(100, 90);
} }
else if(bvalue1->getFormat() == "formatTime" || bvalue1->getFormat() == "formatDate"){ else if(bvalue1->getFormat() == "formatTime" || bvalue1->getFormat() == "formatDate"){
getdisplay().setFont(&Ubuntu_Bold12pt7b); getdisplay().setFont(&Ubuntu_Bold12pt8b);
getdisplay().setCursor(180, 77); getdisplay().setCursor(180, 77);
} }
else{ else{
@@ -141,17 +146,17 @@ class PageFourValues2 : public Page
// ############### Horizontal Line ################ // ############### Horizontal Line ################
// Horizontal line 3 pix // Horizontal line 3 pix
getdisplay().fillRect(0, 105, 400, 3, commonData.fgcolor); getdisplay().fillRect(0, 105, 400, 3, commonData->fgcolor);
// ############### Value 2 ################ // ############### Value 2 ################
// Show name // Show name
getdisplay().setFont(&Ubuntu_Bold20pt7b); getdisplay().setFont(&Ubuntu_Bold20pt8b);
getdisplay().setCursor(20, 145); getdisplay().setCursor(20, 145);
getdisplay().print(name2); // Page name getdisplay().print(name2); // Page name
// Show unit // Show unit
getdisplay().setFont(&Ubuntu_Bold12pt7b); getdisplay().setFont(&Ubuntu_Bold12pt8b);
getdisplay().setCursor(20, 180); getdisplay().setCursor(20, 180);
if(holdvalues == false){ if(holdvalues == false){
getdisplay().print(unit2); // Unit getdisplay().print(unit2); // Unit
@@ -162,11 +167,11 @@ class PageFourValues2 : public Page
// Switch font if format for any values // Switch font if format for any values
if(bvalue2->getFormat() == "formatLatitude" || bvalue2->getFormat() == "formatLongitude"){ if(bvalue2->getFormat() == "formatLatitude" || bvalue2->getFormat() == "formatLongitude"){
getdisplay().setFont(&Ubuntu_Bold12pt7b); getdisplay().setFont(&Ubuntu_Bold12pt8b);
getdisplay().setCursor(100, 180); getdisplay().setCursor(100, 180);
} }
else if(bvalue2->getFormat() == "formatTime" || bvalue2->getFormat() == "formatDate"){ else if(bvalue2->getFormat() == "formatTime" || bvalue2->getFormat() == "formatDate"){
getdisplay().setFont(&Ubuntu_Bold12pt7b); getdisplay().setFont(&Ubuntu_Bold12pt8b);
getdisplay().setCursor(180, 158); getdisplay().setCursor(180, 158);
} }
else{ else{
@@ -189,17 +194,17 @@ class PageFourValues2 : public Page
// ############### Horizontal Line ################ // ############### Horizontal Line ################
// Horizontal line 3 pix // Horizontal line 3 pix
getdisplay().fillRect(0, 195, 400, 3, commonData.fgcolor); getdisplay().fillRect(0, 195, 400, 3, commonData->fgcolor);
// ############### Value 3 ################ // ############### Value 3 ################
// Show name // Show name
getdisplay().setFont(&Ubuntu_Bold12pt7b); getdisplay().setFont(&Ubuntu_Bold12pt8b);
getdisplay().setCursor(20, 220); getdisplay().setCursor(20, 220);
getdisplay().print(name3); // Page name getdisplay().print(name3); // Page name
// Show unit // Show unit
getdisplay().setFont(&Ubuntu_Bold8pt7b); getdisplay().setFont(&Ubuntu_Bold8pt8b);
getdisplay().setCursor(20, 240); getdisplay().setCursor(20, 240);
if(holdvalues == false){ if(holdvalues == false){
getdisplay().print(unit3); // Unit getdisplay().print(unit3); // Unit
@@ -210,11 +215,11 @@ class PageFourValues2 : public Page
// Switch font if format for any values // Switch font if format for any values
if(bvalue3->getFormat() == "formatLatitude" || bvalue3->getFormat() == "formatLongitude"){ if(bvalue3->getFormat() == "formatLatitude" || bvalue3->getFormat() == "formatLongitude"){
getdisplay().setFont(&Ubuntu_Bold8pt7b); getdisplay().setFont(&Ubuntu_Bold8pt8b);
getdisplay().setCursor(50, 240); getdisplay().setCursor(50, 240);
} }
else if(bvalue3->getFormat() == "formatTime" || bvalue3->getFormat() == "formatDate"){ else if(bvalue3->getFormat() == "formatTime" || bvalue3->getFormat() == "formatDate"){
getdisplay().setFont(&Ubuntu_Bold8pt7b); getdisplay().setFont(&Ubuntu_Bold8pt8b);
getdisplay().setCursor(100, 240); getdisplay().setCursor(100, 240);
} }
else{ else{
@@ -237,17 +242,17 @@ class PageFourValues2 : public Page
// ############### Vertical Line ################ // ############### Vertical Line ################
// Vertical line 3 pix // Vertical line 3 pix
getdisplay().fillRect(200, 195, 3, 75, commonData.fgcolor); getdisplay().fillRect(200, 195, 3, 75, commonData->fgcolor);
// ############### Value 4 ################ // ############### Value 4 ################
// Show name // Show name
getdisplay().setFont(&Ubuntu_Bold12pt7b); getdisplay().setFont(&Ubuntu_Bold12pt8b);
getdisplay().setCursor(220, 220); getdisplay().setCursor(220, 220);
getdisplay().print(name4); // Page name getdisplay().print(name4); // Page name
// Show unit // Show unit
getdisplay().setFont(&Ubuntu_Bold8pt7b); getdisplay().setFont(&Ubuntu_Bold8pt8b);
getdisplay().setCursor(220, 240); getdisplay().setCursor(220, 240);
if(holdvalues == false){ if(holdvalues == false){
getdisplay().print(unit4); // Unit getdisplay().print(unit4); // Unit
@@ -258,11 +263,11 @@ class PageFourValues2 : public Page
// Switch font if format for any values // Switch font if format for any values
if(bvalue4->getFormat() == "formatLatitude" || bvalue4->getFormat() == "formatLongitude"){ if(bvalue4->getFormat() == "formatLatitude" || bvalue4->getFormat() == "formatLongitude"){
getdisplay().setFont(&Ubuntu_Bold8pt7b); getdisplay().setFont(&Ubuntu_Bold8pt8b);
getdisplay().setCursor(250, 240); getdisplay().setCursor(250, 240);
} }
else if(bvalue4->getFormat() == "formatTime" || bvalue4->getFormat() == "formatDate"){ else if(bvalue4->getFormat() == "formatTime" || bvalue4->getFormat() == "formatDate"){
getdisplay().setFont(&Ubuntu_Bold8pt7b); getdisplay().setFont(&Ubuntu_Bold8pt8b);
getdisplay().setCursor(300, 240); getdisplay().setCursor(300, 240);
} }
else{ else{
@@ -282,27 +287,7 @@ class PageFourValues2 : public Page
unit4old = unit4; // Save the old unit unit4old = unit4; // Save the old unit
} }
return PAGE_UPDATE;
// ############### Key Layout ################
// Key Layout
getdisplay().setFont(&Ubuntu_Bold8pt7b);
if(keylock == false){
getdisplay().setCursor(130, 290);
getdisplay().print("[ <<<< " + String(commonData.data.actpage) + "/" + String(commonData.data.maxpage) + " >>>> ]");
if(String(backlightMode) == "Control by Key"){ // Key for illumination
getdisplay().setCursor(343, 290);
getdisplay().print("[ILUM]");
}
}
else{
getdisplay().setCursor(130, 290);
getdisplay().print(" [ Keylock active ]");
}
// Update display
getdisplay().nextPage(); // Partial update (fast)
}; };
}; };

View File

@@ -1,4 +1,4 @@
#ifdef BOARD_OBP60S3 #if defined BOARD_OBP60S3 || defined BOARD_OBP40S3
#include "Pagedata.h" #include "Pagedata.h"
#include "OBP60Extensions.h" #include "OBP60Extensions.h"
@@ -6,26 +6,24 @@
class PageGenerator : public Page class PageGenerator : public Page
{ {
bool init = false; // Marker for init done
bool keylock = false; // Keylock
public: public:
PageGenerator(CommonData &common){ PageGenerator(CommonData &common){
common.logger->logDebug(GwLog::LOG,"Show PageGenerator"); commonData = &common;
common.logger->logDebug(GwLog::LOG,"Instantiate PageGenerator");
} }
virtual int handleKey(int key){ virtual int handleKey(int key){
// Code for keylock // Code for keylock
if(key == 11){ if(key == 11){
keylock = !keylock; // Toggle keylock commonData->keylock = !commonData->keylock;
return 0; // Commit the key return 0; // Commit the key
} }
return key; return key;
} }
virtual void displayPage(CommonData &commonData, PageData &pageData) int displayPage(PageData &pageData)
{ {
GwConfigHandler *config = commonData.config; GwConfigHandler *config = commonData->config;
GwLog *logger=commonData.logger; GwLog *logger = commonData->logger;
// Get config data // Get config data
bool simulation = config->getBool(config->useSimuData); bool simulation = config->getBool(config->useSimuData);
@@ -47,13 +45,13 @@ public:
// Get raw value for trend indicator // Get raw value for trend indicator
if(powerSensor != "off"){ if(powerSensor != "off"){
value1 = commonData.data.generatorVoltage; // Use voltage from external sensor value1 = commonData->data.generatorVoltage; // Use voltage from external sensor
} }
else{ else{
value1 = commonData.data.batteryVoltage; // Use internal voltage sensor value1 = commonData->data.batteryVoltage; // Use internal voltage sensor
} }
value2 = commonData.data.generatorCurrent; value2 = commonData->data.generatorCurrent;
value3 = commonData.data.generatorPower; value3 = commonData->data.generatorPower;
genPercentage = value3 * 100 / (double)genPower; // Load value genPercentage = value3 * 100 / (double)genPower; // Load value
// Limits for battery level // Limits for battery level
if(genPercentage < 0) genPercentage = 0; if(genPercentage < 0) genPercentage = 0;
@@ -87,13 +85,13 @@ public:
// Set display in partial refresh mode // Set display in partial refresh mode
getdisplay().setPartialWindow(0, 0, getdisplay().width(), getdisplay().height()); // Set partial update getdisplay().setPartialWindow(0, 0, getdisplay().width(), getdisplay().height()); // Set partial update
getdisplay().setTextColor(commonData.fgcolor); getdisplay().setTextColor(commonData->fgcolor);
// Show name // Show name
getdisplay().setFont(&Ubuntu_Bold20pt7b); getdisplay().setFont(&Ubuntu_Bold20pt8b);
getdisplay().setCursor(10, 65); getdisplay().setCursor(10, 65);
getdisplay().print("Power"); getdisplay().print("Power");
getdisplay().setFont(&Ubuntu_Bold8pt7b); getdisplay().setFont(&Ubuntu_Bold8pt8b);
getdisplay().setCursor(12, 82); getdisplay().setCursor(12, 82);
getdisplay().print("Generator"); getdisplay().print("Generator");
@@ -104,7 +102,7 @@ public:
if(String(batVoltage) == "12V") bvoltage = 12; if(String(batVoltage) == "12V") bvoltage = 12;
else bvoltage = 24; else bvoltage = 24;
getdisplay().print(bvoltage); getdisplay().print(bvoltage);
getdisplay().setFont(&Ubuntu_Bold16pt7b); getdisplay().setFont(&Ubuntu_Bold16pt8b);
getdisplay().print("V"); getdisplay().print("V");
// Show solar power // Show solar power
@@ -112,33 +110,33 @@ public:
getdisplay().setCursor(10, 200); getdisplay().setCursor(10, 200);
if(genPower <= 999) getdisplay().print(genPower, 0); if(genPower <= 999) getdisplay().print(genPower, 0);
if(genPower > 999) getdisplay().print(float(genPower/1000.0), 1); if(genPower > 999) getdisplay().print(float(genPower/1000.0), 1);
getdisplay().setFont(&Ubuntu_Bold16pt7b); getdisplay().setFont(&Ubuntu_Bold16pt8b);
if(genPower <= 999) getdisplay().print("W"); if(genPower <= 999) getdisplay().print("W");
if(genPower > 999) getdisplay().print("kW"); if(genPower > 999) getdisplay().print("kW");
// Show info // Show info
getdisplay().setFont(&Ubuntu_Bold8pt7b); getdisplay().setFont(&Ubuntu_Bold8pt8b);
getdisplay().setCursor(10, 235); getdisplay().setCursor(10, 235);
getdisplay().print("Installed"); getdisplay().print("Installed");
getdisplay().setCursor(10, 255); getdisplay().setCursor(10, 255);
getdisplay().print("Power Modul"); getdisplay().print("Power Modul");
// Show generator // Show generator
generatorGraphic(200, 95, commonData.fgcolor, commonData.bgcolor); generatorGraphic(200, 95, commonData->fgcolor, commonData->bgcolor);
// Show load level in percent // Show load level in percent
getdisplay().setFont(&DSEG7Classic_BoldItalic20pt7b); getdisplay().setFont(&DSEG7Classic_BoldItalic20pt7b);
getdisplay().setCursor(150, 200); getdisplay().setCursor(150, 200);
getdisplay().print(genPercentage); getdisplay().print(genPercentage);
getdisplay().setFont(&Ubuntu_Bold16pt7b); getdisplay().setFont(&Ubuntu_Bold16pt8b);
getdisplay().print("%"); getdisplay().print("%");
getdisplay().setFont(&Ubuntu_Bold8pt7b); getdisplay().setFont(&Ubuntu_Bold8pt8b);
getdisplay().setCursor(150, 235); getdisplay().setCursor(150, 235);
getdisplay().print("Load"); getdisplay().print("Load");
// Show sensor type info // Show sensor type info
String i2cAddr = ""; String i2cAddr = "";
getdisplay().setFont(&Ubuntu_Bold8pt7b); getdisplay().setFont(&Ubuntu_Bold8pt8b);
getdisplay().setCursor(270, 60); getdisplay().setCursor(270, 60);
if(powerSensor == "off") getdisplay().print("Internal"); if(powerSensor == "off") getdisplay().print("Internal");
if(powerSensor == "INA219"){ if(powerSensor == "INA219"){
@@ -178,7 +176,7 @@ public:
getdisplay().print("---"); // Missing bus data getdisplay().print("---"); // Missing bus data
} }
} }
getdisplay().setFont(&Ubuntu_Bold16pt7b); getdisplay().setFont(&Ubuntu_Bold16pt8b);
getdisplay().print("V"); getdisplay().print("V");
// Show actual current in A // Show actual current in A
@@ -190,7 +188,7 @@ public:
if(value2 > 99.9) getdisplay().print(value2, 0); if(value2 > 99.9) getdisplay().print(value2, 0);
} }
else getdisplay().print("---"); else getdisplay().print("---");
getdisplay().setFont(&Ubuntu_Bold16pt7b); getdisplay().setFont(&Ubuntu_Bold16pt8b);
getdisplay().print("A"); getdisplay().print("A");
// Show actual consumption in W // Show actual consumption in W
@@ -202,26 +200,10 @@ public:
if(value3 > 99.9) getdisplay().print(value3, 0); if(value3 > 99.9) getdisplay().print(value3, 0);
} }
else getdisplay().print("---"); else getdisplay().print("---");
getdisplay().setFont(&Ubuntu_Bold16pt7b); getdisplay().setFont(&Ubuntu_Bold16pt8b);
getdisplay().print("W"); getdisplay().print("W");
// Key Layout return PAGE_UPDATE;
getdisplay().setFont(&Ubuntu_Bold8pt7b);
if(keylock == false){
getdisplay().setCursor(130, 290);
getdisplay().print("[ <<<< " + String(commonData.data.actpage) + "/" + String(commonData.data.maxpage) + " >>>> ]");
if(String(backlightMode) == "Control by Key"){ // Key for illumination
getdisplay().setCursor(343, 290);
getdisplay().print("[ILUM]");
}
}
else{
getdisplay().setCursor(130, 290);
getdisplay().print(" [ Keylock active ]");
}
// Update display
getdisplay().nextPage(); // Partial update (fast)
}; };
}; };

View File

@@ -1,31 +1,30 @@
#ifdef BOARD_OBP60S3 #if defined BOARD_OBP60S3 || defined BOARD_OBP40S3
#include "Pagedata.h" #include "Pagedata.h"
#include "OBP60Extensions.h" #include "OBP60Extensions.h"
class PageKeelPosition : public Page class PageKeelPosition : public Page
{ {
bool keylock = false; // Keylock
public: public:
PageKeelPosition(CommonData &common){ PageKeelPosition(CommonData &common){
common.logger->logDebug(GwLog::LOG,"Show PageKeelPosition"); commonData = &common;
common.logger->logDebug(GwLog::LOG,"Instantiate PageKeelPosition");
} }
// Key functions // Key functions
virtual int handleKey(int key){ virtual int handleKey(int key){
// Keylock function // Code for keylock
if(key == 11){ // Code for keylock if(key == 11){
keylock = !keylock; // Toggle keylock commonData->keylock = !commonData->keylock;
return 0; // Commit the key return 0; // Commit the key
} }
return key; return key;
} }
virtual void displayPage(CommonData &commonData, PageData &pageData) int displayPage(PageData &pageData)
{ {
GwConfigHandler *config = commonData.config; GwConfigHandler *config = commonData->config;
GwLog *logger=commonData.logger; GwLog *logger = commonData->logger;
double value1 = 0; double value1 = 0;
double value1old = 0; double value1old = 0;
@@ -40,9 +39,9 @@ public:
String rotfunction = config->getString(config->rotFunction); String rotfunction = config->getString(config->rotFunction);
// Get boat values for Keel position // Get boat values for Keel position
bool valid1 = commonData.data.validRotAngle; // Valid information bool valid1 = commonData->data.validRotAngle; // Valid information
if(simulation == false && rotsensor == "AS5600" && rotfunction == "Keel"){ if(simulation == false && rotsensor == "AS5600" && rotfunction == "Keel"){
value1 = commonData.data.rotationAngle; // Raw value without unit convertion value1 = commonData->data.rotationAngle; // Raw value without unit convertion
} }
else{ else{
value1 = 0; value1 = 0;
@@ -77,9 +76,9 @@ public:
int rInstrument = 110; // Radius of KeelPosition int rInstrument = 110; // Radius of KeelPosition
float pi = 3.141592; float pi = 3.141592;
getdisplay().fillCircle(200, 150, rInstrument + 10, commonData.fgcolor); // Outer circle getdisplay().fillCircle(200, 150, rInstrument + 10, commonData->fgcolor); // Outer circle
getdisplay().fillCircle(200, 150, rInstrument + 7, commonData.bgcolor); // Outer circle getdisplay().fillCircle(200, 150, rInstrument + 7, commonData->bgcolor); // Outer circle
getdisplay().fillRect(0, 30, 400, 122, commonData.bgcolor); // Delete half top circle getdisplay().fillRect(0, 30, 400, 122, commonData->bgcolor); // Delete half top circle
for(int i=90; i<=270; i=i+10) for(int i=90; i<=270; i=i+10)
{ {
@@ -87,21 +86,20 @@ public:
float x = 200 + (rInstrument-30)*sin(i/180.0*pi); // x-coordinate dots float x = 200 + (rInstrument-30)*sin(i/180.0*pi); // x-coordinate dots
float y = 150 - (rInstrument-30)*cos(i/180.0*pi); // y-coordinate cots float y = 150 - (rInstrument-30)*cos(i/180.0*pi); // y-coordinate cots
const char *ii = " "; const char *ii = " ";
switch (i) switch (i) {
{ case 0: ii=" "; break; // Use a blank for a empty scale value
case 0: ii=" "; break; // Use a blank for a empty scale value case 30 : ii=" "; break;
case 30 : ii=" "; break; case 60 : ii=" "; break;
case 60 : ii=" "; break; case 90 : ii="45"; break;
case 90 : ii="45"; break; case 120 : ii="30"; break;
case 120 : ii="30"; break; case 150 : ii="15"; break;
case 150 : ii="15"; break; case 180 : ii="0"; break;
case 180 : ii="0"; break; case 210 : ii="15"; break;
case 210 : ii="15"; break; case 240 : ii="30"; break;
case 240 : ii="30"; break; case 270 : ii="45"; break;
case 270 : ii="45"; break; case 300 : ii=" "; break;
case 300 : ii=" "; break; case 330 : ii=" "; break;
case 330 : ii=" "; break; default: break;
default: break;
} }
// Print text centered on position x, y // Print text centered on position x, y
@@ -110,14 +108,14 @@ public:
getdisplay().getTextBounds(ii, int(x), int(y), &x1, &y1, &w, &h); // Calc width of new string getdisplay().getTextBounds(ii, int(x), int(y), &x1, &y1, &w, &h); // Calc width of new string
getdisplay().setCursor(x-w/2, y+h/2); getdisplay().setCursor(x-w/2, y+h/2);
if(i % 30 == 0){ if(i % 30 == 0){
getdisplay().setFont(&Ubuntu_Bold8pt7b); getdisplay().setFont(&Ubuntu_Bold8pt8b);
getdisplay().print(ii); getdisplay().print(ii);
} }
// Draw sub scale with dots // Draw sub scale with dots
float x1c = 200 + rInstrument*sin(i/180.0*pi); float x1c = 200 + rInstrument*sin(i/180.0*pi);
float y1c = 150 - rInstrument*cos(i/180.0*pi); float y1c = 150 - rInstrument*cos(i/180.0*pi);
getdisplay().fillCircle((int)x1c, (int)y1c, 2, commonData.fgcolor); getdisplay().fillCircle((int)x1c, (int)y1c, 2, commonData->fgcolor);
float sinx=sin(i/180.0*pi); float sinx=sin(i/180.0*pi);
float cosx=cos(i/180.0*pi); float cosx=cos(i/180.0*pi);
@@ -130,10 +128,10 @@ public:
float yy2 = -(rInstrument+10); float yy2 = -(rInstrument+10);
getdisplay().fillTriangle(200+(int)(cosx*xx1-sinx*yy1),150+(int)(sinx*xx1+cosx*yy1), getdisplay().fillTriangle(200+(int)(cosx*xx1-sinx*yy1),150+(int)(sinx*xx1+cosx*yy1),
200+(int)(cosx*xx2-sinx*yy1),150+(int)(sinx*xx2+cosx*yy1), 200+(int)(cosx*xx2-sinx*yy1),150+(int)(sinx*xx2+cosx*yy1),
200+(int)(cosx*xx1-sinx*yy2),150+(int)(sinx*xx1+cosx*yy2),commonData.fgcolor); 200+(int)(cosx*xx1-sinx*yy2),150+(int)(sinx*xx1+cosx*yy2),commonData->fgcolor);
getdisplay().fillTriangle(200+(int)(cosx*xx2-sinx*yy1),150+(int)(sinx*xx2+cosx*yy1), getdisplay().fillTriangle(200+(int)(cosx*xx2-sinx*yy1),150+(int)(sinx*xx2+cosx*yy1),
200+(int)(cosx*xx1-sinx*yy2),150+(int)(sinx*xx1+cosx*yy2), 200+(int)(cosx*xx1-sinx*yy2),150+(int)(sinx*xx1+cosx*yy2),
200+(int)(cosx*xx2-sinx*yy2),150+(int)(sinx*xx2+cosx*yy2),commonData.fgcolor); 200+(int)(cosx*xx2-sinx*yy2),150+(int)(sinx*xx2+cosx*yy2),commonData->fgcolor);
} }
} }
@@ -167,7 +165,7 @@ public:
float yy2 = -(rInstrument * 0.6); float yy2 = -(rInstrument * 0.6);
getdisplay().fillTriangle(200+(int)(cosx*xx1-sinx*yy1),150+(int)(sinx*xx1+cosx*yy1), getdisplay().fillTriangle(200+(int)(cosx*xx1-sinx*yy1),150+(int)(sinx*xx1+cosx*yy1),
200+(int)(cosx*xx2-sinx*yy1),150+(int)(sinx*xx2+cosx*yy1), 200+(int)(cosx*xx2-sinx*yy1),150+(int)(sinx*xx2+cosx*yy1),
200+(int)(cosx*0-sinx*yy2),150+(int)(sinx*0+cosx*yy2),commonData.fgcolor); 200+(int)(cosx*0-sinx*yy2),150+(int)(sinx*0+cosx*yy2),commonData->fgcolor);
// Inverted pointer // Inverted pointer
// Pointer as triangle with center base 2*width // Pointer as triangle with center base 2*width
float endwidth = 2; // End width of pointer float endwidth = 2; // End width of pointer
@@ -177,55 +175,37 @@ public:
float iy2 = -endwidth; float iy2 = -endwidth;
getdisplay().fillTriangle(200+(int)(cosx*ix1-sinx*iy1),150+(int)(sinx*ix1+cosx*iy1), getdisplay().fillTriangle(200+(int)(cosx*ix1-sinx*iy1),150+(int)(sinx*ix1+cosx*iy1),
200+(int)(cosx*ix2-sinx*iy1),150+(int)(sinx*ix2+cosx*iy1), 200+(int)(cosx*ix2-sinx*iy1),150+(int)(sinx*ix2+cosx*iy1),
200+(int)(cosx*0-sinx*iy2),150+(int)(sinx*0+cosx*iy2),commonData.fgcolor); 200+(int)(cosx*0-sinx*iy2),150+(int)(sinx*0+cosx*iy2),commonData->fgcolor);
// Draw counterweight // Draw counterweight
getdisplay().fillCircle(200+(int)(cosx*0-sinx*yy2),150+(int)(sinx*0+cosx*yy2), 5, commonData.fgcolor); getdisplay().fillCircle(200+(int)(cosx*0-sinx*yy2),150+(int)(sinx*0+cosx*yy2), 5, commonData->fgcolor);
} }
// Center circle // Center circle
getdisplay().fillCircle(200, 140, startwidth + 22, commonData.bgcolor); getdisplay().fillCircle(200, 140, startwidth + 22, commonData->bgcolor);
getdisplay().fillCircle(200, 140, startwidth + 20, commonData.fgcolor); // Boat circle getdisplay().fillCircle(200, 140, startwidth + 20, commonData->fgcolor); // Boat circle
getdisplay().fillRect(200 - 30, 140 - 30, 2 * 30, 30, commonData.bgcolor); // Delete half top of boat circle getdisplay().fillRect(200 - 30, 140 - 30, 2 * 30, 30, commonData->bgcolor); // Delete half top of boat circle
getdisplay().fillRect(150, 150, 100, 4, commonData.fgcolor); // Water line getdisplay().fillRect(150, 150, 100, 4, commonData->fgcolor); // Water line
// Print label // Print label
getdisplay().setFont(&Ubuntu_Bold16pt7b); getdisplay().setFont(&Ubuntu_Bold16pt8b);
getdisplay().setCursor(100, 70); getdisplay().setCursor(100, 70);
getdisplay().print("Keel Position"); // Label getdisplay().print("Keel Position"); // Label
if((rotsensor == "AS5600" && rotfunction == "Keel" && (valid1 == true || holdvalues == true)) || simulation == true){ if((rotsensor == "AS5600" && rotfunction == "Keel" && (valid1 == true || holdvalues == true)) || simulation == true){
// Print Unit of keel position // Print Unit of keel position
getdisplay().setFont(&Ubuntu_Bold12pt7b); getdisplay().setFont(&Ubuntu_Bold12pt8b);
getdisplay().setCursor(175, 110); getdisplay().setCursor(175, 110);
getdisplay().print(unit1); // Unit getdisplay().print(unit1); // Unit
} }
else{ else{
// Print Unit of keel position // Print Unit of keel position
getdisplay().setFont(&Ubuntu_Bold8pt7b); getdisplay().setFont(&Ubuntu_Bold8pt8b);
getdisplay().setCursor(145, 110); getdisplay().setCursor(145, 110);
getdisplay().print("No sensor data"); // Info missing sensor getdisplay().print("No sensor data"); // Info missing sensor
} }
//******************************************************************************************* return PAGE_UPDATE;
// Key Layout
getdisplay().setFont(&Ubuntu_Bold8pt7b);
if(keylock == false){
getdisplay().setCursor(130, 290);
getdisplay().print("[ <<<< " + String(commonData.data.actpage) + "/" + String(commonData.data.maxpage) + " >>>> ]");
if(String(backlightMode) == "Control by Key"){ // Key for illumination
getdisplay().setCursor(343, 290);
getdisplay().print("[ILUM]");
}
}
else{
getdisplay().setCursor(130, 290);
getdisplay().print(" [ Keylock active ]");
}
// Update display
getdisplay().nextPage(); // Partial update (fast)
}; };
}; };

View File

@@ -1,27 +1,29 @@
#ifdef BOARD_OBP60S3 #if defined BOARD_OBP60S3 || defined BOARD_OBP40S3
#include "Pagedata.h" #include "Pagedata.h"
#include "OBP60Extensions.h" #include "OBP60Extensions.h"
#include "BoatDataCalibration.h"
class PageOneValue : public Page{ class PageOneValue : public Page
bool keylock = false; // Keylock {
public: public:
PageOneValue(CommonData &common){ PageOneValue(CommonData &common){
common.logger->logDebug(GwLog::LOG,"Show PageOneValue"); commonData = &common;
common.logger->logDebug(GwLog::LOG,"Instantiate PageOneValue");
} }
virtual int handleKey(int key){ virtual int handleKey(int key){
if(key == 11){ // Code for keylock // Code for keylock
keylock = !keylock; // Toggle keylock if(key == 11){
commonData->keylock = !commonData->keylock;
return 0; // Commit the key return 0; // Commit the key
} }
return key; return key;
} }
virtual void displayPage(CommonData &commonData, PageData &pageData){ int displayPage(PageData &pageData){
GwConfigHandler *config = commonData.config; GwConfigHandler *config = commonData->config;
GwLog *logger=commonData.logger; GwLog *logger = commonData->logger;
// Old values for hold function // Old values for hold function
static String svalue1old = ""; static String svalue1old = "";
@@ -38,10 +40,11 @@ class PageOneValue : public Page{
GwApi::BoatValue *bvalue1 = pageData.values[0]; // First element in list (only one value by PageOneValue) GwApi::BoatValue *bvalue1 = pageData.values[0]; // First element in list (only one value by PageOneValue)
String name1 = xdrDelete(bvalue1->getName()); // Value name String name1 = xdrDelete(bvalue1->getName()); // Value name
name1 = name1.substring(0, 6); // String length limit for value name name1 = name1.substring(0, 6); // String length limit for value name
calibrationData.calibrateInstance(bvalue1, logger); // Check if boat data value is to be calibrated
double value1 = bvalue1->value; // Value as double in SI unit double value1 = bvalue1->value; // Value as double in SI unit
bool valid1 = bvalue1->valid; // Valid information bool valid1 = bvalue1->valid; // Valid information
String svalue1 = formatValue(bvalue1, commonData).svalue; // Formatted value as string including unit conversion and switching decimal places String svalue1 = formatValue(bvalue1, *commonData).svalue; // Formatted value as string including unit conversion and switching decimal places
String unit1 = formatValue(bvalue1, commonData).unit; // Unit of value String unit1 = formatValue(bvalue1, *commonData).unit; // Unit of value
// Optical warning by limit violation (unused) // Optical warning by limit violation (unused)
if(String(flashLED) == "Limit Violation"){ if(String(flashLED) == "Limit Violation"){
@@ -50,7 +53,7 @@ class PageOneValue : public Page{
} }
// Logging boat values // Logging boat values
if (bvalue1 == NULL) return; if (bvalue1 == NULL) return PAGE_OK; // WTF why this statement?
LOG_DEBUG(GwLog::LOG,"Drawing at PageOneValue, %s: %f", name1.c_str(), value1); LOG_DEBUG(GwLog::LOG,"Drawing at PageOneValue, %s: %f", name1.c_str(), value1);
// Draw page // Draw page
@@ -60,13 +63,13 @@ class PageOneValue : public Page{
getdisplay().setPartialWindow(0, 0, getdisplay().width(), getdisplay().height()); // Set partial update getdisplay().setPartialWindow(0, 0, getdisplay().width(), getdisplay().height()); // Set partial update
// Show name // Show name
getdisplay().setTextColor(commonData.fgcolor); getdisplay().setTextColor(commonData->fgcolor);
getdisplay().setFont(&Ubuntu_Bold32pt7b); getdisplay().setFont(&Ubuntu_Bold32pt8b);
getdisplay().setCursor(20, 100); getdisplay().setCursor(20, 100);
getdisplay().print(name1); // Page name getdisplay().print(name1); // Page name
// Show unit // Show unit
getdisplay().setFont(&Ubuntu_Bold20pt7b); getdisplay().setFont(&Ubuntu_Bold20pt8b);
getdisplay().setCursor(270, 100); getdisplay().setCursor(270, 100);
if(holdvalues == false){ if(holdvalues == false){
getdisplay().print(unit1); // Unit getdisplay().print(unit1); // Unit
@@ -77,11 +80,11 @@ class PageOneValue : public Page{
// Switch font if format for any values // Switch font if format for any values
if(bvalue1->getFormat() == "formatLatitude" || bvalue1->getFormat() == "formatLongitude"){ if(bvalue1->getFormat() == "formatLatitude" || bvalue1->getFormat() == "formatLongitude"){
getdisplay().setFont(&Ubuntu_Bold20pt7b); getdisplay().setFont(&Ubuntu_Bold20pt8b);
getdisplay().setCursor(20, 180); getdisplay().setCursor(20, 180);
} }
else if(bvalue1->getFormat() == "formatTime" || bvalue1->getFormat() == "formatDate"){ else if(bvalue1->getFormat() == "formatTime" || bvalue1->getFormat() == "formatDate"){
getdisplay().setFont(&Ubuntu_Bold32pt7b); getdisplay().setFont(&Ubuntu_Bold32pt8b);
getdisplay().setCursor(20, 200); getdisplay().setCursor(20, 200);
} }
else{ else{
@@ -101,24 +104,7 @@ class PageOneValue : public Page{
unit1old = unit1; // Save the old unit unit1old = unit1; // Save the old unit
} }
// Key Layout return PAGE_UPDATE;
getdisplay().setFont(&Ubuntu_Bold8pt7b);
if(keylock == false){
getdisplay().setCursor(130, 290);
getdisplay().print("[ <<<< " + String(commonData.data.actpage) + "/" + String(commonData.data.maxpage) + " >>>> ]");
if(String(backlightMode) == "Control by Key"){ // Key for illumination
getdisplay().setCursor(343, 290);
getdisplay().print("[ILUM]");
}
}
else{
getdisplay().setCursor(130, 290);
getdisplay().print(" [ Keylock active ]");
}
// Update display
getdisplay().nextPage(); // Partial update (fast)
}; };
}; };

View File

@@ -1,31 +1,29 @@
#ifdef BOARD_OBP60S3 #if defined BOARD_OBP60S3 || defined BOARD_OBP40S3
#include "Pagedata.h" #include "Pagedata.h"
#include "OBP60Extensions.h" #include "OBP60Extensions.h"
class PageRollPitch : public Page class PageRollPitch : public Page
{ {
bool keylock = false; // Keylock
public: public:
PageRollPitch(CommonData &common){ PageRollPitch(CommonData &common){
common.logger->logDebug(GwLog::LOG,"Show PageRollPitch"); commonData = &common;
common.logger->logDebug(GwLog::LOG,"Instantiate PageRollPitch");
} }
// Key functions // Key functions
virtual int handleKey(int key){ virtual int handleKey(int key){
// Keylock function // Code for keylock
if(key == 11){ // Code for keylock if(key == 11){
keylock = !keylock; // Toggle keylock commonData->keylock = !commonData->keylock;
return 0; // Commit the key return 0; // Commit the key
} }
return key; return key;
} }
virtual void displayPage(CommonData &commonData, PageData &pageData) int displayPage(PageData &pageData){
{ GwConfigHandler *config = commonData->config;
GwConfigHandler *config = commonData.config; GwLog *logger = commonData->logger;
GwLog *logger=commonData.logger;
double value1 = 0; double value1 = 0;
double value2 = 0; double value2 = 0;
@@ -43,9 +41,9 @@ public:
String backlightMode = config->getString(config->backlight); String backlightMode = config->getString(config->backlight);
int rolllimit = config->getInt(config->rollLimit); int rolllimit = config->getInt(config->rollLimit);
String roffset = config->getString(config->rollOffset); String roffset = config->getString(config->rollOffset);
double rolloffset = roffset.toFloat()/360*(2*PI); double rolloffset = roffset.toFloat()/360*(2*M_PI);
String poffset = config->getString(config->pitchOffset); String poffset = config->getString(config->pitchOffset);
double pitchoffset = poffset.toFloat()/360*(2*PI); double pitchoffset = poffset.toFloat()/360*(2*M_PI);
// Get boat values for roll // Get boat values for roll
GwApi::BoatValue *bvalue1 = pageData.values[0]; // First element in list (xdrRoll) GwApi::BoatValue *bvalue1 = pageData.values[0]; // First element in list (xdrRoll)
@@ -57,17 +55,17 @@ public:
} }
else{ else{
if(simulation == true){ if(simulation == true){
value1 = (20 + float(random(0, 50)) / 10.0)/360*2*PI; value1 = (20 + float(random(0, 50)) / 10.0)/360*2*M_PI;
} }
else{ else{
value1 = 0; value1 = 0;
} }
} }
if(value1/(2*PI)*360 > -10 && value1/(2*PI)*360 < 10){ if(value1/(2*M_PI)*360 > -10 && value1/(2*M_PI)*360 < 10){
svalue1 = String(value1/(2*PI)*360,1); // Convert raw value to string svalue1 = String(value1/(2*M_PI)*360,1); // Convert raw value to string
} }
else{ else{
svalue1 = String(value1/(2*PI)*360,0); svalue1 = String(value1/(2*M_PI)*360,0);
} }
if(valid1 == true){ if(valid1 == true){
svalue1old = svalue1; // Save the old value svalue1old = svalue1; // Save the old value
@@ -82,17 +80,17 @@ public:
} }
else{ else{
if(simulation == true){ if(simulation == true){
value2 = (float(random(-5, 5)))/360*2*PI; value2 = (float(random(-5, 5)))/360*2*M_PI;
} }
else{ else{
value2 = 0; value2 = 0;
} }
} }
if(value2/(2*PI)*360 > -10 && value2/(2*PI)*360 < 10){ if(value2/(2*PI)*360 > -10 && value2/(2*M_PI)*360 < 10){
svalue2 = String(value2/(2*PI)*360,1); // Convert raw value to string svalue2 = String(value2/(2*M_PI)*360,1); // Convert raw value to string
} }
else{ else{
svalue2 = String(value2/(2*PI)*360,0); svalue2 = String(value2/(2*M_PI)*360,0);
} }
if(valid2 == true){ if(valid2 == true){
svalue2old = svalue2; // Save the old value svalue2old = svalue2; // Save the old value
@@ -101,7 +99,7 @@ public:
// Optical warning by limit violation // Optical warning by limit violation
if(String(flashLED) == "Limit Violation"){ if(String(flashLED) == "Limit Violation"){
// Limits for roll // Limits for roll
if(value1*360/(2*PI) >= -1*rolllimit && value1*360/(2*PI) <= rolllimit){ if(value1*360/(2*M_PI) >= -1*rolllimit && value1*360/(2*M_PI) <= rolllimit){
setBlinkingLED(false); setBlinkingLED(false);
setFlashLED(false); setFlashLED(false);
} }
@@ -111,7 +109,7 @@ public:
} }
// Logging boat values // Logging boat values
if (bvalue1 == NULL) return; if (bvalue1 == NULL) return PAGE_OK; // WTF why this statement?
LOG_DEBUG(GwLog::LOG,"Drawing at PageRollPitch, %s:%f, %s:%f", name1.c_str(), value1, name2.c_str(), value2); LOG_DEBUG(GwLog::LOG,"Drawing at PageRollPitch, %s:%f, %s:%f", name1.c_str(), value1, name2.c_str(), value2);
// Draw page // Draw page
@@ -120,7 +118,7 @@ public:
// Set display in partial refresh mode // Set display in partial refresh mode
getdisplay().setPartialWindow(0, 0, getdisplay().width(), getdisplay().height()); // Set partial update getdisplay().setPartialWindow(0, 0, getdisplay().width(), getdisplay().height()); // Set partial update
getdisplay().setTextColor(commonData.fgcolor); getdisplay().setTextColor(commonData->fgcolor);
// Show roll limit // Show roll limit
getdisplay().setFont(&DSEG7Classic_BoldItalic20pt7b); getdisplay().setFont(&DSEG7Classic_BoldItalic20pt7b);
@@ -128,70 +126,69 @@ public:
getdisplay().print(rolllimit); // Value getdisplay().print(rolllimit); // Value
//getdisplay().print(svalue1); // Value //getdisplay().print(svalue1); // Value
getdisplay().setFont(&Ubuntu_Bold12pt7b); getdisplay().setFont(&Ubuntu_Bold12pt8b);
getdisplay().setCursor(10, 95); getdisplay().setCursor(10, 95);
getdisplay().print("Limit"); // Name getdisplay().print("Limit"); // Name
getdisplay().setFont(&Ubuntu_Bold8pt7b); getdisplay().setFont(&Ubuntu_Bold8pt8b);
getdisplay().setCursor(10, 115); getdisplay().setCursor(10, 115);
getdisplay().print("DEG"); getdisplay().print("DEG");
// Horizintal separator left // Horizintal separator left
getdisplay().fillRect(0, 149, 60, 3, commonData.fgcolor); getdisplay().fillRect(0, 149, 60, 3, commonData->fgcolor);
// Show roll value // Show roll value
getdisplay().setFont(&DSEG7Classic_BoldItalic20pt7b); getdisplay().setFont(&DSEG7Classic_BoldItalic20pt7b);
getdisplay().setCursor(10, 270); getdisplay().setCursor(10, 270);
if(holdvalues == false) getdisplay().print(svalue1); // Value if(holdvalues == false) getdisplay().print(svalue1); // Value
else getdisplay().print(svalue1old); else getdisplay().print(svalue1old);
getdisplay().setFont(&Ubuntu_Bold12pt7b); getdisplay().setFont(&Ubuntu_Bold12pt8b);
getdisplay().setCursor(10, 220); getdisplay().setCursor(10, 220);
getdisplay().print(name1); // Name getdisplay().print(name1); // Name
getdisplay().setFont(&Ubuntu_Bold8pt7b); getdisplay().setFont(&Ubuntu_Bold8pt8b);
getdisplay().setCursor(10, 190); getdisplay().setCursor(10, 190);
getdisplay().print("Deg"); getdisplay().print("Deg");
// Horizintal separator right // Horizintal separator right
getdisplay().fillRect(340, 149, 80, 3, commonData.fgcolor); getdisplay().fillRect(340, 149, 80, 3, commonData->fgcolor);
// Show pitch value // Show pitch value
getdisplay().setFont(&DSEG7Classic_BoldItalic20pt7b); getdisplay().setFont(&DSEG7Classic_BoldItalic20pt7b);
getdisplay().setCursor(295, 270); getdisplay().setCursor(295, 270);
if(holdvalues == false) getdisplay().print(svalue2); // Value if(holdvalues == false) getdisplay().print(svalue2); // Value
else getdisplay().print(svalue2old); else getdisplay().print(svalue2old);
getdisplay().setFont(&Ubuntu_Bold12pt7b); getdisplay().setFont(&Ubuntu_Bold12pt8b);
getdisplay().setCursor(335, 220); getdisplay().setCursor(335, 220);
getdisplay().print(name2); // Name getdisplay().print(name2); // Name
getdisplay().setFont(&Ubuntu_Bold8pt7b); getdisplay().setFont(&Ubuntu_Bold8pt8b);
getdisplay().setCursor(335, 190); getdisplay().setCursor(335, 190);
getdisplay().print("Deg"); getdisplay().print("Deg");
//******************************************************************************************* //*******************************************************************************************
// Draw instrument // Draw instrument
int rInstrument = 100; // Radius of instrument int rInstrument = 100; // Radius of instrument
float pi = 3.141592; float pi = 3.141592;
getdisplay().fillCircle(200, 150, rInstrument + 10, commonData.fgcolor); // Outer circle getdisplay().fillCircle(200, 150, rInstrument + 10, commonData->fgcolor); // Outer circle
getdisplay().fillCircle(200, 150, rInstrument + 7, commonData.bgcolor); // Outer circle getdisplay().fillCircle(200, 150, rInstrument + 7, commonData->bgcolor); // Outer circle
for(int i=0; i<360; i=i+10) for(int i=0; i<360; i=i+10)
{ {
// Only scaling +/- 60 degrees // Only scaling +/- 60 degrees
if((i >= 0 && i <= 60) || (i >= 300 && i <= 360)){ if((i >= 0 && i <= 60) || (i >= 300 && i <= 360)){
// Scaling values // Scaling values
float x = 200 + (rInstrument+25)*sin(i/180.0*pi); // x-coordinate dots float x = 200 + (rInstrument+25)*sin(i/180.0*M_PI); // x-coordinate dots
float y = 150 - (rInstrument+25)*cos(i/180.0*pi); // y-coordinate cots float y = 150 - (rInstrument+25)*cos(i/180.0*M_PI); // y-coordinate cots
const char *ii = ""; const char *ii = "";
switch (i) switch (i) {
{ case 0: ii="0"; break;
case 0: ii="0"; break; case 20 : ii="20"; break;
case 20 : ii="20"; break; case 40 : ii="40"; break;
case 40 : ii="40"; break; case 60 : ii="60"; break;
case 60 : ii="60"; break; case 300 : ii="60"; break;
case 300 : ii="60"; break; case 320 : ii="40"; break;
case 320 : ii="40"; break; case 340 : ii="20"; break;
case 340 : ii="20"; break; default: break;
default: break;
} }
// Print text centered on position x, y // Print text centered on position x, y
@@ -200,16 +197,16 @@ public:
getdisplay().getTextBounds(ii, int(x), int(y), &x1, &y1, &w, &h); // Calc width of new string getdisplay().getTextBounds(ii, int(x), int(y), &x1, &y1, &w, &h); // Calc width of new string
getdisplay().setCursor(x-w/2, y+h/2); getdisplay().setCursor(x-w/2, y+h/2);
if(i % 20 == 0){ if(i % 20 == 0){
getdisplay().setFont(&Ubuntu_Bold8pt7b); getdisplay().setFont(&Ubuntu_Bold8pt8b);
getdisplay().print(ii); getdisplay().print(ii);
} }
// Draw sub scale with dots // Draw sub scale with dots
float x1c = 200 + rInstrument*sin(i/180.0*pi); float x1c = 200 + rInstrument*sin(i/180.0*M_PI);
float y1c = 150 - rInstrument*cos(i/180.0*pi); float y1c = 150 - rInstrument*cos(i/180.0*M_PI);
getdisplay().fillCircle((int)x1c, (int)y1c, 2, commonData.fgcolor); getdisplay().fillCircle((int)x1c, (int)y1c, 2, commonData->fgcolor);
float sinx=sin(i/180.0*pi); float sinx=sin(i/180.0*M_PI);
float cosx=cos(i/180.0*pi); float cosx=cos(i/180.0*M_PI);
// Draw sub scale with lines (two triangles) // Draw sub scale with lines (two triangles)
if(i % 20 == 0){ if(i % 20 == 0){
@@ -220,10 +217,10 @@ public:
float yy2 = -(rInstrument+10); float yy2 = -(rInstrument+10);
getdisplay().fillTriangle(200+(int)(cosx*xx1-sinx*yy1),150+(int)(sinx*xx1+cosx*yy1), getdisplay().fillTriangle(200+(int)(cosx*xx1-sinx*yy1),150+(int)(sinx*xx1+cosx*yy1),
200+(int)(cosx*xx2-sinx*yy1),150+(int)(sinx*xx2+cosx*yy1), 200+(int)(cosx*xx2-sinx*yy1),150+(int)(sinx*xx2+cosx*yy1),
200+(int)(cosx*xx1-sinx*yy2),150+(int)(sinx*xx1+cosx*yy2),commonData.fgcolor); 200+(int)(cosx*xx1-sinx*yy2),150+(int)(sinx*xx1+cosx*yy2),commonData->fgcolor);
getdisplay().fillTriangle(200+(int)(cosx*xx2-sinx*yy1),150+(int)(sinx*xx2+cosx*yy1), getdisplay().fillTriangle(200+(int)(cosx*xx2-sinx*yy1),150+(int)(sinx*xx2+cosx*yy1),
200+(int)(cosx*xx1-sinx*yy2),150+(int)(sinx*xx1+cosx*yy2), 200+(int)(cosx*xx1-sinx*yy2),150+(int)(sinx*xx1+cosx*yy2),
200+(int)(cosx*xx2-sinx*yy2),150+(int)(sinx*xx2+cosx*yy2),commonData.fgcolor); 200+(int)(cosx*xx2-sinx*yy2),150+(int)(sinx*xx2+cosx*yy2),commonData->fgcolor);
} }
} }
} }
@@ -231,20 +228,20 @@ public:
// Draw mast position pointer // Draw mast position pointer
float startwidth = 8; // Start width of pointer float startwidth = 8; // Start width of pointer
// value1 = (2 * pi ) - value1; // Mirror coordiante system for pointer, keel and boat // value1 = (2 * M_PI ) - value1; // Mirror coordiante system for pointer, keel and boat
if(valid1 == true || holdvalues == true || simulation == true){ if(valid1 == true || holdvalues == true || simulation == true){
float sinx=sin(value1 + pi); float sinx=sin(value1 + M_PI);
float cosx=cos(value1 + pi); float cosx=cos(value1 + M_PI);
// Normal pointer // Normal pointer
// Pointer as triangle with center base 2*width // Pointer as triangle with center base 2*width
float xx1 = -startwidth; float xx1 = -startwidth;
float xx2 = startwidth; float xx2 = startwidth;
float yy1 = -startwidth; float yy1 = -startwidth;
float yy2 = -(rInstrument * 0.7); float yy2 = -(rInstrument * 0.7);
getdisplay().fillTriangle(200+(int)(cosx*xx1-sinx*yy1),150+(int)(sinx*xx1+cosx*yy1), getdisplay().fillTriangle(200+(int)(cosx*xx1-sinx*yy1),150+(int)(sinx*xx1+cosx*yy1),
200+(int)(cosx*xx2-sinx*yy1),150+(int)(sinx*xx2+cosx*yy1), 200+(int)(cosx*xx2-sinx*yy1),150+(int)(sinx*xx2+cosx*yy1),
200+(int)(cosx*0-sinx*yy2),150+(int)(sinx*0+cosx*yy2),commonData.fgcolor); 200+(int)(cosx*0-sinx*yy2),150+(int)(sinx*0+cosx*yy2),commonData->fgcolor);
// Inverted pointer // Inverted pointer
// Pointer as triangle with center base 2*width // Pointer as triangle with center base 2*width
float endwidth = 2; // End width of pointer float endwidth = 2; // End width of pointer
@@ -254,26 +251,26 @@ public:
float iy2 = -endwidth; float iy2 = -endwidth;
getdisplay().fillTriangle(200+(int)(cosx*ix1-sinx*iy1),150+(int)(sinx*ix1+cosx*iy1), getdisplay().fillTriangle(200+(int)(cosx*ix1-sinx*iy1),150+(int)(sinx*ix1+cosx*iy1),
200+(int)(cosx*ix2-sinx*iy1),150+(int)(sinx*ix2+cosx*iy1), 200+(int)(cosx*ix2-sinx*iy1),150+(int)(sinx*ix2+cosx*iy1),
200+(int)(cosx*0-sinx*iy2),150+(int)(sinx*0+cosx*iy2),commonData.fgcolor); 200+(int)(cosx*0-sinx*iy2),150+(int)(sinx*0+cosx*iy2),commonData->fgcolor);
// Draw counterweight // Draw counterweight
getdisplay().fillCircle(200+(int)(cosx*0-sinx*yy2),150+(int)(sinx*0+cosx*yy2), 5, commonData.fgcolor); getdisplay().fillCircle(200+(int)(cosx*0-sinx*yy2),150+(int)(sinx*0+cosx*yy2), 5, commonData->fgcolor);
} }
// Center circle // Center circle
getdisplay().fillCircle(200, 150, startwidth + 22, commonData.bgcolor); getdisplay().fillCircle(200, 150, startwidth + 22, commonData->bgcolor);
getdisplay().fillCircle(200, 150, startwidth + 20, commonData.fgcolor); // Boat circle getdisplay().fillCircle(200, 150, startwidth + 20, commonData->fgcolor); // Boat circle
int x0 = 200; int x0 = 200;
int y0 = 150; int y0 = 150;
int x1 = x0 + 50*cos(value1); int x1 = x0 + 50*cos(value1);
int y1 = y0 + 50*sin(value1); int y1 = y0 + 50*sin(value1);
int x2 = x0 + 50*cos(value1 - pi/2); int x2 = x0 + 50*cos(value1 - pi/2);
int y2 = y0 + 50*sin(value1 - pi/2); int y2 = y0 + 50*sin(value1 - pi/2);
getdisplay().fillTriangle(x0, y0, x1, y1, x2, y2, commonData.bgcolor); // Clear half top side of boat circle (right triangle) getdisplay().fillTriangle(x0, y0, x1, y1, x2, y2, commonData->bgcolor); // Clear half top side of boat circle (right triangle)
x1 = x0 + 50*cos(value1 + pi); x1 = x0 + 50*cos(value1 + pi);
y1 = y0 + 50*sin(value1 + pi); y1 = y0 + 50*sin(value1 + pi);
getdisplay().fillTriangle(x0, y0, x1, y1, x2, y2, commonData.bgcolor); // Clear half top side of boat circle (left triangle) getdisplay().fillTriangle(x0, y0, x1, y1, x2, y2, commonData->bgcolor); // Clear half top side of boat circle (left triangle)
getdisplay().fillRect(150, 160, 100, 4, commonData.fgcolor); // Water line getdisplay().fillRect(150, 160, 100, 4, commonData->fgcolor); // Water line
// Draw roll pointer // Draw roll pointer
startwidth = 4; // Start width of pointer startwidth = 4; // Start width of pointer
@@ -285,10 +282,10 @@ public:
float xx1 = -startwidth; float xx1 = -startwidth;
float xx2 = startwidth; float xx2 = startwidth;
float yy1 = -startwidth; float yy1 = -startwidth;
float yy2 = -(rInstrument - 15); float yy2 = -(rInstrument - 15);
getdisplay().fillTriangle(200+(int)(cosx*xx1-sinx*yy1),150+(int)(sinx*xx1+cosx*yy1), getdisplay().fillTriangle(200+(int)(cosx*xx1-sinx*yy1),150+(int)(sinx*xx1+cosx*yy1),
200+(int)(cosx*xx2-sinx*yy1),150+(int)(sinx*xx2+cosx*yy1), 200+(int)(cosx*xx2-sinx*yy1),150+(int)(sinx*xx2+cosx*yy1),
200+(int)(cosx*0-sinx*yy2),150+(int)(sinx*0+cosx*yy2),commonData.fgcolor); 200+(int)(cosx*0-sinx*yy2),150+(int)(sinx*0+cosx*yy2),commonData->fgcolor);
// Inverted pointer // Inverted pointer
// Pointer as triangle with center base 2*width // Pointer as triangle with center base 2*width
float endwidth = 2; // End width of pointer float endwidth = 2; // End width of pointer
@@ -298,34 +295,16 @@ public:
float iy2 = -endwidth; float iy2 = -endwidth;
getdisplay().fillTriangle(200+(int)(cosx*ix1-sinx*iy1),150+(int)(sinx*ix1+cosx*iy1), getdisplay().fillTriangle(200+(int)(cosx*ix1-sinx*iy1),150+(int)(sinx*ix1+cosx*iy1),
200+(int)(cosx*ix2-sinx*iy1),150+(int)(sinx*ix2+cosx*iy1), 200+(int)(cosx*ix2-sinx*iy1),150+(int)(sinx*ix2+cosx*iy1),
200+(int)(cosx*0-sinx*iy2),150+(int)(sinx*0+cosx*iy2),commonData.fgcolor); 200+(int)(cosx*0-sinx*iy2),150+(int)(sinx*0+cosx*iy2),commonData->fgcolor);
} }
else{ else{
// Print sensor info // Print sensor info
getdisplay().setFont(&Ubuntu_Bold8pt7b); getdisplay().setFont(&Ubuntu_Bold8pt8b);
getdisplay().setCursor(145, 200); getdisplay().setCursor(145, 200);
getdisplay().print("No sensor data"); // Info missing sensor getdisplay().print("No sensor data"); // Info missing sensor
} }
//******************************************************************************************* return PAGE_UPDATE;
// Key Layout
getdisplay().setFont(&Ubuntu_Bold8pt7b);
if(keylock == false){
getdisplay().setCursor(130, 290);
getdisplay().print("[ <<<< " + String(commonData.data.actpage) + "/" + String(commonData.data.maxpage) + " >>>> ]");
if(String(backlightMode) == "Control by Key"){ // Key for illumination
getdisplay().setCursor(343, 290);
getdisplay().print("[ILUM]");
}
}
else{
getdisplay().setCursor(130, 290);
getdisplay().print(" [ Keylock active ]");
}
// Update display
getdisplay().nextPage(); // Partial update (fast)
}; };
}; };
@@ -342,9 +321,9 @@ static Page *createPage(CommonData &common){
PageDescription registerPageRollPitch( PageDescription registerPageRollPitch(
"RollPitch", // Page name "RollPitch", // Page name
createPage, // Action createPage, // Action
0, // Number of bus values depends on selection in Web configuration 2, // Number of bus values depends on selection in Web configuration
// {"xdrROLL", "xdrPTCH"},// Bus values we need in the page // {"xdrROLL", "xdrPTCH"},// Bus values we need in the page
{"xdrRoll", "xdrPitch"},// Bus values we need in the page //{"xdrRoll", "xdrPitch"},// Bus values we need in the page
true // Show display header on/off true // Show display header on/off
); );

View File

@@ -1,31 +1,30 @@
#ifdef BOARD_OBP60S3 #if defined BOARD_OBP60S3 || defined BOARD_OBP40S3
#include "Pagedata.h" #include "Pagedata.h"
#include "OBP60Extensions.h" #include "OBP60Extensions.h"
#include "BoatDataCalibration.h"
class PageRudderPosition : public Page class PageRudderPosition : public Page
{ {
bool keylock = false; // Keylock
public: public:
PageRudderPosition(CommonData &common){ PageRudderPosition(CommonData &common){
commonData = &common;
common.logger->logDebug(GwLog::LOG,"Show PageRudderPosition"); common.logger->logDebug(GwLog::LOG,"Show PageRudderPosition");
} }
// Key functions // Key functions
virtual int handleKey(int key){ virtual int handleKey(int key){
// Keylock function // Code for keylock
if(key == 11){ // Code for keylock if(key == 11){
keylock = !keylock; // Toggle keylock commonData->keylock = !commonData->keylock;
return 0; // Commit the key return 0; // Commit the key
} }
return key; return key;
} }
virtual void displayPage(CommonData &commonData, PageData &pageData) int displayPage(PageData &pageData){
{ GwConfigHandler *config = commonData->config;
GwConfigHandler *config = commonData.config; GwLog *logger = commonData->logger;
GwLog *logger=commonData.logger;
static String unit1old = ""; static String unit1old = "";
double value1 = 0.1; double value1 = 0.1;
@@ -42,24 +41,25 @@ public:
GwApi::BoatValue *bvalue1 = pageData.values[0]; // First element in list GwApi::BoatValue *bvalue1 = pageData.values[0]; // First element in list
String name1 = bvalue1->getName().c_str(); // Value name String name1 = bvalue1->getName().c_str(); // Value name
name1 = name1.substring(0, 6); // String length limit for value name name1 = name1.substring(0, 6); // String length limit for value name
calibrationData.calibrateInstance(bvalue1, logger); // Check if boat data value is to be calibrated
value1 = bvalue1->value; // Raw value without unit convertion value1 = bvalue1->value; // Raw value without unit convertion
bool valid1 = bvalue1->valid; // Valid information bool valid1 = bvalue1->valid; // Valid information
String svalue1 = formatValue(bvalue1, commonData).svalue; // Formatted value as string including unit conversion and switching decimal places String svalue1 = formatValue(bvalue1, *commonData).svalue; // Formatted value as string including unit conversion and switching decimal places
String unit1 = formatValue(bvalue1, commonData).unit; // Unit of value String unit1 = formatValue(bvalue1, *commonData).unit; // Unit of value
if(valid1 == true){ if(valid1 == true){
value1old = value1; // Save old value value1old = value1; // Save old value
unit1old = unit1; // Save old unit unit1old = unit1; // Save old unit
} else {
if(simulation == true){
value1 = (3 + float(random(0, 50)) / 10.0)/360*2*PI;
unit1 = "Deg";
}
else{
value1 = 0;
}
} }
if(simulation == true){
value1 = (3 + float(random(0, 50)) / 10.0)/360*2*PI;
unit1 = "Deg";
}
else{
value1 = 0;
}
// Optical warning by limit violation (unused) // Optical warning by limit violation (unused)
if(String(flashLED) == "Limit Violation"){ if(String(flashLED) == "Limit Violation"){
setBlinkingLED(false); setBlinkingLED(false);
@@ -67,7 +67,7 @@ public:
} }
// Logging boat values // Logging boat values
if (bvalue1 == NULL) return; if (bvalue1 == NULL) return PAGE_OK; // WTF why this statement?
LOG_DEBUG(GwLog::LOG,"Drawing at PageRudderPosition, %s:%f", name1.c_str(), value1); LOG_DEBUG(GwLog::LOG,"Drawing at PageRudderPosition, %s:%f", name1.c_str(), value1);
// Draw page // Draw page
@@ -82,9 +82,9 @@ public:
int rInstrument = 110; // Radius of RudderPosition int rInstrument = 110; // Radius of RudderPosition
float pi = 3.141592; float pi = 3.141592;
getdisplay().fillCircle(200, 150, rInstrument + 10, commonData.fgcolor); // Outer circle getdisplay().fillCircle(200, 150, rInstrument + 10, commonData->fgcolor); // Outer circle
getdisplay().fillCircle(200, 150, rInstrument + 7, commonData.bgcolor); // Outer circle getdisplay().fillCircle(200, 150, rInstrument + 7, commonData->bgcolor); // Outer circle
getdisplay().fillRect(0, 30, 400, 122, commonData.bgcolor); // Delete half top circle getdisplay().fillRect(0, 30, 400, 122, commonData->bgcolor); // Delete half top circle
for(int i=90; i<=270; i=i+10) for(int i=90; i<=270; i=i+10)
{ {
@@ -115,14 +115,14 @@ public:
getdisplay().getTextBounds(ii, int(x), int(y), &x1, &y1, &w, &h); // Calc width of new string getdisplay().getTextBounds(ii, int(x), int(y), &x1, &y1, &w, &h); // Calc width of new string
getdisplay().setCursor(x-w/2, y+h/2); getdisplay().setCursor(x-w/2, y+h/2);
if(i % 30 == 0){ if(i % 30 == 0){
getdisplay().setFont(&Ubuntu_Bold8pt7b); getdisplay().setFont(&Ubuntu_Bold8pt8b);
getdisplay().print(ii); getdisplay().print(ii);
} }
// Draw sub scale with dots // Draw sub scale with dots
float x1c = 200 + rInstrument*sin(i/180.0*pi); float x1c = 200 + rInstrument*sin(i/180.0*pi);
float y1c = 150 - rInstrument*cos(i/180.0*pi); float y1c = 150 - rInstrument*cos(i/180.0*pi);
getdisplay().fillCircle((int)x1c, (int)y1c, 2, commonData.fgcolor); getdisplay().fillCircle((int)x1c, (int)y1c, 2, commonData->fgcolor);
float sinx=sin(i/180.0*pi); float sinx=sin(i/180.0*pi);
float cosx=cos(i/180.0*pi); float cosx=cos(i/180.0*pi);
@@ -135,35 +135,35 @@ public:
float yy2 = -(rInstrument+10); float yy2 = -(rInstrument+10);
getdisplay().fillTriangle(200+(int)(cosx*xx1-sinx*yy1),150+(int)(sinx*xx1+cosx*yy1), getdisplay().fillTriangle(200+(int)(cosx*xx1-sinx*yy1),150+(int)(sinx*xx1+cosx*yy1),
200+(int)(cosx*xx2-sinx*yy1),150+(int)(sinx*xx2+cosx*yy1), 200+(int)(cosx*xx2-sinx*yy1),150+(int)(sinx*xx2+cosx*yy1),
200+(int)(cosx*xx1-sinx*yy2),150+(int)(sinx*xx1+cosx*yy2),commonData.fgcolor); 200+(int)(cosx*xx1-sinx*yy2),150+(int)(sinx*xx1+cosx*yy2),commonData->fgcolor);
getdisplay().fillTriangle(200+(int)(cosx*xx2-sinx*yy1),150+(int)(sinx*xx2+cosx*yy1), getdisplay().fillTriangle(200+(int)(cosx*xx2-sinx*yy1),150+(int)(sinx*xx2+cosx*yy1),
200+(int)(cosx*xx1-sinx*yy2),150+(int)(sinx*xx1+cosx*yy2), 200+(int)(cosx*xx1-sinx*yy2),150+(int)(sinx*xx1+cosx*yy2),
200+(int)(cosx*xx2-sinx*yy2),150+(int)(sinx*xx2+cosx*yy2),commonData.fgcolor); 200+(int)(cosx*xx2-sinx*yy2),150+(int)(sinx*xx2+cosx*yy2),commonData->fgcolor);
} }
} }
// Print label // Print label
getdisplay().setFont(&Ubuntu_Bold16pt7b); getdisplay().setFont(&Ubuntu_Bold16pt8b);
getdisplay().setCursor(80, 70); getdisplay().setCursor(80, 70);
getdisplay().print("Rudder Position"); // Label getdisplay().print("Rudder Position"); // Label
// Print Unit in RudderPosition // Print Unit in RudderPosition
if(valid1 == true || simulation == true){ if(valid1 == true || simulation == true){
if(holdvalues == false){ if(holdvalues == false){
getdisplay().setFont(&Ubuntu_Bold12pt7b); getdisplay().setFont(&Ubuntu_Bold12pt8b);
getdisplay().setCursor(175, 110); getdisplay().setCursor(175, 110);
getdisplay().print(unit1); // Unit getdisplay().print(unit1); // Unit
} }
else{ else{
getdisplay().setFont(&Ubuntu_Bold12pt7b); getdisplay().setFont(&Ubuntu_Bold12pt8b);
getdisplay().setCursor(175, 110); getdisplay().setCursor(175, 110);
getdisplay().print(unit1old); // Unit getdisplay().print(unit1old); // Unit
} }
} }
else{ else{
// Print Unit of keel position // Print Unit of keel position
getdisplay().setFont(&Ubuntu_Bold8pt7b); getdisplay().setFont(&Ubuntu_Bold8pt8b);
getdisplay().setCursor(145, 110); getdisplay().setCursor(145, 110);
getdisplay().print("No sensor data"); // Info missing sensor getdisplay().print("No sensor data"); // Info missing sensor
} }
@@ -190,7 +190,7 @@ public:
float yy2 = -(rInstrument * 0.5); float yy2 = -(rInstrument * 0.5);
getdisplay().fillTriangle(200+(int)(cosx*xx1-sinx*yy1),150+(int)(sinx*xx1+cosx*yy1), getdisplay().fillTriangle(200+(int)(cosx*xx1-sinx*yy1),150+(int)(sinx*xx1+cosx*yy1),
200+(int)(cosx*xx2-sinx*yy1),150+(int)(sinx*xx2+cosx*yy1), 200+(int)(cosx*xx2-sinx*yy1),150+(int)(sinx*xx2+cosx*yy1),
200+(int)(cosx*0-sinx*yy2),150+(int)(sinx*0+cosx*yy2),commonData.fgcolor); 200+(int)(cosx*0-sinx*yy2),150+(int)(sinx*0+cosx*yy2),commonData->fgcolor);
// Inverted pointer // Inverted pointer
// Pointer as triangle with center base 2*width // Pointer as triangle with center base 2*width
float endwidth = 2; // End width of pointer float endwidth = 2; // End width of pointer
@@ -200,31 +200,14 @@ public:
float iy2 = -endwidth; float iy2 = -endwidth;
getdisplay().fillTriangle(200+(int)(cosx*ix1-sinx*iy1),150+(int)(sinx*ix1+cosx*iy1), getdisplay().fillTriangle(200+(int)(cosx*ix1-sinx*iy1),150+(int)(sinx*ix1+cosx*iy1),
200+(int)(cosx*ix2-sinx*iy1),150+(int)(sinx*ix2+cosx*iy1), 200+(int)(cosx*ix2-sinx*iy1),150+(int)(sinx*ix2+cosx*iy1),
200+(int)(cosx*0-sinx*iy2),150+(int)(sinx*0+cosx*iy2),commonData.fgcolor); 200+(int)(cosx*0-sinx*iy2),150+(int)(sinx*0+cosx*iy2),commonData->fgcolor);
} }
// Center circle // Center circle
getdisplay().fillCircle(200, 150, startwidth + 6, commonData.bgcolor); getdisplay().fillCircle(200, 150, startwidth + 6, commonData->bgcolor);
getdisplay().fillCircle(200, 150, startwidth + 4, commonData.fgcolor); getdisplay().fillCircle(200, 150, startwidth + 4, commonData->fgcolor);
//******************************************************************************************* return PAGE_UPDATE;
// Key Layout
getdisplay().setFont(&Ubuntu_Bold8pt7b);
if(keylock == false){
getdisplay().setCursor(130, 290);
getdisplay().print("[ <<<< " + String(commonData.data.actpage) + "/" + String(commonData.data.maxpage) + " >>>> ]");
if(String(backlightMode) == "Control by Key"){ // Key for illumination
getdisplay().setCursor(343, 290);
getdisplay().print("[ILUM]");
}
}
else{
getdisplay().setCursor(130, 290);
getdisplay().print(" [ Keylock active ]");
}
// Update display
getdisplay().nextPage(); // Partial update (fast)
}; };
}; };

View File

@@ -0,0 +1,172 @@
#if defined BOARD_OBP60S3 || defined BOARD_OBP40S3
#include "Pagedata.h"
#include "OBP60Extensions.h"
#include "BoatDataCalibration.h"
const int SixValues_x1 = 5;
const int SixValues_DeltaX = 200;
const int SixValues_y1 = 23;
const int SixValues_DeltaY = 83;
const int HowManyValues = 6;
class PageSixValues : public Page
{
public:
PageSixValues(CommonData &common){
commonData = &common;
common.logger->logDebug(GwLog::LOG,"Instantiate PageSixValues");
}
virtual int handleKey(int key){
// Code for keylock
if(key == 11){
commonData->keylock = !commonData->keylock;
return 0; // Commit the key
}
return key;
}
int displayPage(PageData &pageData){
GwConfigHandler *config = commonData->config;
GwLog *logger = commonData->logger;
// Old values for hold function
static String OldDataText[HowManyValues] = {"", "", "", "", "", ""};
static String OldDataUnits[HowManyValues] = {"", "", "", "", "", ""};
// Get config data
String lengthformat = config->getString(config->lengthFormat);
// bool simulation = config->getBool(config->useSimuData);
bool holdvalues = config->getBool(config->holdvalues);
String flashLED = config->getString(config->flashLED);
String backlightMode = config->getString(config->backlight);
GwApi::BoatValue *bvalue;
String DataName[HowManyValues];
double DataValue[HowManyValues];
bool DataValid[HowManyValues];
String DataText[HowManyValues];
String DataUnits[HowManyValues];
String DataFormat[HowManyValues];
for (int i = 0; i < HowManyValues; i++){
bvalue = pageData.values[i];
DataName[i] = xdrDelete(bvalue->getName());
DataName[i] = DataName[i].substring(0, 6); // String length limit for value name
calibrationData.calibrateInstance(bvalue, logger); // Check if boat data value is to be calibrated
DataValue[i] = bvalue->value; // Value as double in SI unit
DataValid[i] = bvalue->valid;
DataText[i] = formatValue(bvalue, *commonData).svalue; // Formatted value as string including unit conversion and switching decimal places
DataUnits[i] = formatValue(bvalue, *commonData).unit;
DataFormat[i] = bvalue->getFormat(); // Unit of value
}
// Optical warning by limit violation (unused)
if(String(flashLED) == "Limit Violation"){
setBlinkingLED(false);
setFlashLED(false);
}
if (bvalue == NULL) return PAGE_OK; // WTF why this statement?
// Draw page
//***********************************************************
// Set display in partial refresh mode
getdisplay().setPartialWindow(0, 0, getdisplay().width(), getdisplay().height()); // Set partial update
getdisplay().setTextColor(commonData->fgcolor);
for (int i = 0; i < ( HowManyValues / 2 ); i++){
if (i < (HowManyValues / 2) - 1) { // Don't draw horizontal line after last line of values -> standard design
// Horizontal line 3 pix
getdisplay().fillRect(0, SixValues_y1+(i+1)*SixValues_DeltaY, 400, 3, commonData->fgcolor);
}
for (int j = 0; j < 2; j++){
int ValueIndex = i * 2 + j;
int x0 = SixValues_x1 + j * SixValues_DeltaX;
int y0 = SixValues_y1 + i * SixValues_DeltaY;
LOG_DEBUG(GwLog::LOG,"Drawing at PageSixValue: %d %s %f %s", ValueIndex, DataName[ValueIndex], DataValue[ValueIndex], DataFormat[ValueIndex] );
// Show name
getdisplay().setFont(&Ubuntu_Bold12pt8b);
getdisplay().setCursor(x0, y0+25);
getdisplay().print(DataName[ValueIndex]); // Page name
// Show unit
getdisplay().setFont(&Ubuntu_Bold8pt8b);
getdisplay().setCursor(x0, y0+72);
if(holdvalues == false){
getdisplay().print(DataUnits[ValueIndex]); // Unit
}
else{
getdisplay().print(OldDataUnits[ValueIndex]);
}
// Switch font if format for any values
if(DataFormat[ValueIndex] == "formatLatitude" || DataFormat[ValueIndex] == "formatLongitude"){
getdisplay().setFont(&Ubuntu_Bold12pt8b);
getdisplay().setCursor(x0+10, y0+60);
}
else if(DataFormat[ValueIndex] == "formatTime" || DataFormat[ValueIndex] == "formatDate"){
getdisplay().setFont(&Ubuntu_Bold16pt8b);
getdisplay().setCursor(x0+20,y0+55);
}
// pressure in hPa
else if(DataFormat[ValueIndex] == "formatXdr:P:P"){
getdisplay().setFont(&DSEG7Classic_BoldItalic26pt7b);
getdisplay().setCursor(x0+5, y0+70);
}
// RPM
else if(DataFormat[ValueIndex] == "formatXdr:T:R"){
getdisplay().setFont(&DSEG7Classic_BoldItalic16pt7b);
getdisplay().setCursor(x0+25, y0+70);
}
else{
getdisplay().setFont(&DSEG7Classic_BoldItalic26pt7b);
if ( DataText[ValueIndex][0] == '-' )
getdisplay().setCursor(x0+25, y0+70);
else
getdisplay().setCursor(x0+65, y0+70);
}
// Show bus data
if(holdvalues == false){
getdisplay().print(DataText[ValueIndex]); // Real value as formated string
}
else{
getdisplay().print(OldDataText[ValueIndex]); // Old value as formated string
}
if(DataValid[ValueIndex] == true){
OldDataText[ValueIndex] = DataText[ValueIndex]; // Save the old value
OldDataUnits[ValueIndex] = DataUnits[ValueIndex]; // Save the old unit
}
}
// Vertical line 3 pix
getdisplay().fillRect(SixValues_x1+SixValues_DeltaX-8, SixValues_y1+i*SixValues_DeltaY, 3, SixValues_DeltaY, commonData->fgcolor);
}
return PAGE_UPDATE;
};
};
static Page *createPage(CommonData &common){
return new PageSixValues(common);
}/**
* with the code below we make this page known to the PageTask
* we give it a type (name) that can be selected in the config
* we define which function is to be called
* and we provide the number of user parameters we expect
* this will be number of BoatValue pointers in pageData.values
*/
PageDescription registerPageSixValues(
"SixValues", // Page name
createPage, // Action
6, // Number of bus values depends on selection in Web configuration
true // Show display header on/off
);
#endif

View File

@@ -0,0 +1,204 @@
// SPDX-License-Identifier: GPL-2.0-or-later
#if defined BOARD_OBP60S3 || defined BOARD_OBP40S3
#include "Pagedata.h"
#include "OBP60Extensions.h"
#include <vector>
#include <algorithm> // for vector sorting
/*
* SkyView / Satellites
*/
class PageSkyView : public Page
{
private:
String flashLED;
GwBoatData *bd;
public:
PageSkyView(CommonData &common)
{
commonData = &common;
// task name access is for example purpose only
TaskHandle_t currentTaskHandle = xTaskGetCurrentTaskHandle();
const char* taskName = pcTaskGetName(currentTaskHandle);
common.logger->logDebug(GwLog::LOG, "Instantiate PageSkyView in task '%s'", taskName);
flashLED = common.config->getString(common.config->flashLED);
}
int handleKey(int key) {
// return 0 to mark the key handled completely
// return the key to allow further action
if (key == 11) {
commonData->keylock = !commonData->keylock;
return 0;
}
return key;
}
void displayNew(PageData &pageData) {
#ifdef BOARD_OBP60S3
// Clear optical warning
if (flashLED == "Limit Violation") {
setBlinkingLED(false);
setFlashLED(false);
}
#endif
bd = pageData.api->getBoatData();
};
// Comparator function to sort by SNR
static bool compareBySNR(const GwSatInfo& a, const GwSatInfo& b) {
return a.SNR > b.SNR; // Sort in descending order
}
int displayPage(PageData &pageData) {
GwLog *logger = commonData->logger;
std::vector<GwSatInfo> sats;
int nSat = bd->SatInfo->getNumSats();
logger->logDebug(GwLog::LOG, "Drawing at PageSkyView, %d satellites", nSat);
for (int i = 0; i < nSat; i++) {
sats.push_back(*bd->SatInfo->getAt(i));
}
std::sort(sats.begin(), sats.end(), compareBySNR);
// Draw page
//***********************************************************
// Set display in partial refresh mode
getdisplay().setPartialWindow(0, 0, getdisplay().width(), getdisplay().height()); // Set partial update
// current position
getdisplay().setFont(&Ubuntu_Bold8pt8b);
// sky view
Point c = {130, 148};
uint16_t r = 120;
uint16_t r1 = r / 2;
getdisplay().fillCircle(c.x, c.y, r + 2, commonData->fgcolor);
getdisplay().fillCircle(c.x, c.y, r - 1, commonData->bgcolor);
getdisplay().drawCircle(c.x, c.y, r1, commonData->fgcolor);
// separation lines
getdisplay().drawLine(c.x - r, c.y, c.x + r, c.y, commonData->fgcolor);
getdisplay().drawLine(c.x, c.y - r, c.x, c.y + r, commonData->fgcolor);
Point p = {c.x, c.y - r};
Point p1, p2;
p1 = rotatePoint(c, p, 45);
p2 = rotatePoint(c, p, 45 + 180);
getdisplay().drawLine(p1.x, p1.y, p2.x, p2.y, commonData->fgcolor);
p1 = rotatePoint(c, p, -45);
p2 = rotatePoint(c, p, -45 + 180);
getdisplay().drawLine(p1.x, p1.y, p2.x, p2.y, commonData->fgcolor);
// directions
int16_t x1, y1;
uint16_t w, h;
getdisplay().setFont(&Ubuntu_Bold12pt8b);
getdisplay().getTextBounds("N", 0, 150, &x1, &y1, &w, &h);
getdisplay().setCursor(c.x - w / 2, c.y - r + h + 3);
getdisplay().print("N");
getdisplay().getTextBounds("S", 0, 150, &x1, &y1, &w, &h);
getdisplay().setCursor(c.x - w / 2, c.y + r - 3);
getdisplay().print("S");
getdisplay().getTextBounds("E", 0, 150, &x1, &y1, &w, &h);
getdisplay().setCursor(c.x + r - w - 3, c.y + h / 2);
getdisplay().print("E");
getdisplay().getTextBounds("W", 0, 150, &x1, &y1, &w, &h);
getdisplay().setCursor(c.x - r + 3 , c.y + h / 2);
getdisplay().print("W");
// show satellites in "map"
getdisplay().setFont(&IBM8x8px);
for (int i = 0; i < nSat; i++) {
float arad = (sats[i].Azimut * M_PI / 180.0) + M_PI;
float erad = sats[i].Elevation * M_PI / 180.0;
uint16_t x = c.x + sin(arad) * erad * r1;
uint16_t y = c.y + cos(arad) * erad * r1;
getdisplay().fillRect(x-4, y-4, 8, 8, commonData->fgcolor);
getdisplay().setCursor(x-7, y+12);
getdisplay().printf("%02d", static_cast<int>(sats[i].PRN));
}
// Signal / Noise bars
getdisplay().setFont(&Ubuntu_Bold8pt8b);
getdisplay().setCursor(325, 34);
getdisplay().print("SNR");
// getdisplay().drawRect(270, 20, 125, 257, commonData->fgcolor);
int maxsat = std::min(nSat, 12);
for (int i = 0; i < maxsat; i++) {
uint16_t y = 29 + (i + 1) * 20;
getdisplay().setCursor(276, y);
char buffer[3];
snprintf(buffer, 3, "%02d", static_cast<int>(sats[i].PRN));
getdisplay().print(String(buffer));
getdisplay().drawRect(305, y-12, 85, 14, commonData->fgcolor);
getdisplay().setCursor(315, y);
// TODO SNR as number or as bar via mode key?
if (sats[i].SNR <= 100) {
// getdisplay().print(sats[i].SNR);
getdisplay().fillRect(307, y-10, int(81 * sats[i].SNR / 100.0), 10, commonData->fgcolor);
} else {
getdisplay().print("n/a");
}
}
// Show SatInfo and HDOP
getdisplay().setFont(&Ubuntu_Bold8pt8b);
getdisplay().setCursor(220, 34);
getdisplay().print("Sat:");
GwApi::BoatValue *bv_satinfo = pageData.values[0]; // SatInfo
String sval_satinfo = formatValue(bv_satinfo, *commonData).svalue;
getdisplay().setCursor(220, 49);
getdisplay().print(sval_satinfo);
getdisplay().setCursor(220, 254);
getdisplay().print("HDOP:");
GwApi::BoatValue *bv_hdop = pageData.values[1]; // HDOP
double hdop = formatValue(bv_hdop, *commonData).value * 4; // 4 is factor for UERE (translation in meter)
char sval_hdop[20];
dtostrf(hdop, 0, 1, sval_hdop); // Only one prefix
strcat(sval_hdop, "m");
getdisplay().setCursor(220, 269);
getdisplay().print(sval_hdop);
return PAGE_UPDATE;
};
};
static Page* createPage(CommonData &common){
return new PageSkyView(common);
}
/**
* with the code below we make this page known to the PageTask
* we give it a type (name) that can be selected in the config
* we define which function is to be called
* and we provide the number of user parameters we expect
* this will be number of BoatValue pointers in pageData.values
*/
PageDescription registerPageSkyView(
"SkyView", // Page name
createPage, // Action
0, // Number of bus values depends on selection in Web configuration
{"SatInfo", "HDOP"}, // Bus values we need in the page
true // Show display header on/off
);
#endif

View File

@@ -1,4 +1,4 @@
#ifdef BOARD_OBP60S3 #if defined BOARD_OBP60S3 || defined BOARD_OBP40S3
#include "Pagedata.h" #include "Pagedata.h"
#include "OBP60Extensions.h" #include "OBP60Extensions.h"
@@ -6,26 +6,23 @@
class PageSolar : public Page class PageSolar : public Page
{ {
bool init = false; // Marker for init done
bool keylock = false; // Keylock
public: public:
PageSolar(CommonData &common){ PageSolar(CommonData &common){
common.logger->logDebug(GwLog::LOG,"Show PageSolar"); commonData = &common;
common.logger->logDebug(GwLog::LOG,"Instantiate PageSolar");
} }
virtual int handleKey(int key){ virtual int handleKey(int key){
// Code for keylock // Code for keylock
if(key == 11){ if(key == 11){
keylock = !keylock; // Toggle keylock commonData->keylock = !commonData->keylock;
return 0; // Commit the key return 0; // Commit the key
} }
return key; return key;
} }
virtual void displayPage(CommonData &commonData, PageData &pageData) int displayPage(PageData &pageData){
{ GwConfigHandler *config = commonData->config;
GwConfigHandler *config = commonData.config; GwLog *logger = commonData->logger;
GwLog *logger=commonData.logger;
// Get config data // Get config data
bool simulation = config->getBool(config->useSimuData); bool simulation = config->getBool(config->useSimuData);
@@ -47,13 +44,13 @@ public:
// Get raw value for trend indicator // Get raw value for trend indicator
if(powerSensor != "off"){ if(powerSensor != "off"){
value1 = commonData.data.solarVoltage; // Use voltage from external sensor value1 = commonData->data.solarVoltage; // Use voltage from external sensor
} }
else{ else{
value1 = commonData.data.batteryVoltage; // Use internal voltage sensor value1 = commonData->data.batteryVoltage; // Use internal voltage sensor
} }
value2 = commonData.data.solarCurrent; value2 = commonData->data.solarCurrent;
value3 = commonData.data.solarPower; value3 = commonData->data.solarPower;
solPercentage = value3 * 100 / (double)solPower; // Load value solPercentage = value3 * 100 / (double)solPower; // Load value
// Limits for battery level // Limits for battery level
if(solPercentage < 0) solPercentage = 0; if(solPercentage < 0) solPercentage = 0;
@@ -87,10 +84,10 @@ public:
// Set display in partial refresh mode // Set display in partial refresh mode
getdisplay().setPartialWindow(0, 0, getdisplay().width(), getdisplay().height()); // Set partial update getdisplay().setPartialWindow(0, 0, getdisplay().width(), getdisplay().height()); // Set partial update
getdisplay().setTextColor(commonData.fgcolor); getdisplay().setTextColor(commonData->fgcolor);
// Show name // Show name
getdisplay().setFont(&Ubuntu_Bold20pt7b); getdisplay().setFont(&Ubuntu_Bold20pt8b);
getdisplay().setCursor(10, 65); getdisplay().setCursor(10, 65);
getdisplay().print("Solar"); getdisplay().print("Solar");
@@ -101,7 +98,7 @@ public:
if(String(batVoltage) == "12V") bvoltage = 12; if(String(batVoltage) == "12V") bvoltage = 12;
else bvoltage = 24; else bvoltage = 24;
getdisplay().print(bvoltage); getdisplay().print(bvoltage);
getdisplay().setFont(&Ubuntu_Bold16pt7b); getdisplay().setFont(&Ubuntu_Bold16pt8b);
getdisplay().print("V"); getdisplay().print("V");
// Show solar power // Show solar power
@@ -109,33 +106,33 @@ public:
getdisplay().setCursor(10, 200); getdisplay().setCursor(10, 200);
if(solPower <= 999) getdisplay().print(solPower, 0); if(solPower <= 999) getdisplay().print(solPower, 0);
if(solPower > 999) getdisplay().print(float(solPower/1000.0), 1); if(solPower > 999) getdisplay().print(float(solPower/1000.0), 1);
getdisplay().setFont(&Ubuntu_Bold16pt7b); getdisplay().setFont(&Ubuntu_Bold16pt8b);
if(solPower <= 999) getdisplay().print("W"); if(solPower <= 999) getdisplay().print("W");
if(solPower > 999) getdisplay().print("kW"); if(solPower > 999) getdisplay().print("kW");
// Show info // Show info
getdisplay().setFont(&Ubuntu_Bold8pt7b); getdisplay().setFont(&Ubuntu_Bold8pt8b);
getdisplay().setCursor(10, 235); getdisplay().setCursor(10, 235);
getdisplay().print("Installed"); getdisplay().print("Installed");
getdisplay().setCursor(10, 255); getdisplay().setCursor(10, 255);
getdisplay().print("Solar Modul"); getdisplay().print("Solar Modul");
// Show solar panel // Show solar panel
solarGraphic(150, 45, commonData.fgcolor, commonData.bgcolor); solarGraphic(150, 45, commonData->fgcolor, commonData->bgcolor);
// Show load level in percent // Show load level in percent
getdisplay().setFont(&DSEG7Classic_BoldItalic20pt7b); getdisplay().setFont(&DSEG7Classic_BoldItalic20pt7b);
getdisplay().setCursor(150, 200); getdisplay().setCursor(150, 200);
getdisplay().print(solPercentage); getdisplay().print(solPercentage);
getdisplay().setFont(&Ubuntu_Bold16pt7b); getdisplay().setFont(&Ubuntu_Bold16pt8b);
getdisplay().print("%"); getdisplay().print("%");
getdisplay().setFont(&Ubuntu_Bold8pt7b); getdisplay().setFont(&Ubuntu_Bold8pt8b);
getdisplay().setCursor(150, 235); getdisplay().setCursor(150, 235);
getdisplay().print("Load"); getdisplay().print("Load");
// Show sensor type info // Show sensor type info
String i2cAddr = ""; String i2cAddr = "";
getdisplay().setFont(&Ubuntu_Bold8pt7b); getdisplay().setFont(&Ubuntu_Bold8pt8b);
getdisplay().setCursor(270, 60); getdisplay().setCursor(270, 60);
if(powerSensor == "off") getdisplay().print("Internal"); if(powerSensor == "off") getdisplay().print("Internal");
if(powerSensor == "INA219"){ if(powerSensor == "INA219"){
@@ -175,7 +172,7 @@ public:
getdisplay().print("---"); // Missing bus data getdisplay().print("---"); // Missing bus data
} }
} }
getdisplay().setFont(&Ubuntu_Bold16pt7b); getdisplay().setFont(&Ubuntu_Bold16pt8b);
getdisplay().print("V"); getdisplay().print("V");
// Show actual current in A // Show actual current in A
@@ -187,7 +184,7 @@ public:
if(value2 > 99.9) getdisplay().print(value2, 0); if(value2 > 99.9) getdisplay().print(value2, 0);
} }
else getdisplay().print("---"); else getdisplay().print("---");
getdisplay().setFont(&Ubuntu_Bold16pt7b); getdisplay().setFont(&Ubuntu_Bold16pt8b);
getdisplay().print("A"); getdisplay().print("A");
// Show actual consumption in W // Show actual consumption in W
@@ -199,26 +196,10 @@ public:
if(value3 > 99.9) getdisplay().print(value3, 0); if(value3 > 99.9) getdisplay().print(value3, 0);
} }
else getdisplay().print("---"); else getdisplay().print("---");
getdisplay().setFont(&Ubuntu_Bold16pt7b); getdisplay().setFont(&Ubuntu_Bold16pt8b);
getdisplay().print("W"); getdisplay().print("W");
// Key Layout return PAGE_UPDATE;
getdisplay().setFont(&Ubuntu_Bold8pt7b);
if(keylock == false){
getdisplay().setCursor(130, 290);
getdisplay().print("[ <<<< " + String(commonData.data.actpage) + "/" + String(commonData.data.maxpage) + " >>>> ]");
if(String(backlightMode) == "Control by Key"){ // Key for illumination
getdisplay().setCursor(343, 290);
getdisplay().print("[ILUM]");
}
}
else{
getdisplay().setCursor(130, 290);
getdisplay().print(" [ Keylock active ]");
}
// Update display
getdisplay().nextPage(); // Partial update (fast)
}; };
}; };

View File

@@ -0,0 +1,496 @@
#if defined BOARD_OBP60S3 || defined BOARD_OBP40S3
/*
* Special system page, called directly with fast key sequence 5,4
* Out of normal page order.
* Consists of some sub-pages with following content:
* 1. Hard and software information
* 2. System settings
* 3. NMEA2000 device list
* 4. SD Card information if available
*/
#include "Pagedata.h"
#include "OBP60Extensions.h"
#include "images/logo64.xbm"
#include <esp32/clk.h>
#include "qrcode.h"
#ifdef BOARD_OBP40S3
#include "dirent.h"
#endif
#define STRINGIZE_IMPL(x) #x
#define STRINGIZE(x) STRINGIZE_IMPL(x)
#define VERSINFO STRINGIZE(GWDEVVERSION)
#define BOARDINFO STRINGIZE(BOARD)
#define PCBINFO STRINGIZE(PCBVERS)
#define DISPLAYINFO STRINGIZE(EPDTYPE)
#define GXEPD2INFO STRINGIZE(GXEPD2VERS)
class PageSystem : public Page
{
private:
uint64_t chipid;
bool simulation;
bool use_sdcard;
String buzzer_mode;
uint8_t buzzer_power;
String cpuspeed;
String rtc_module;
String gps_module;
String env_module;
String batt_sensor;
String solar_sensor;
String gen_sensor;
String rot_sensor;
double homelat;
double homelon;
char mode = 'N'; // (N)ormal, (S)ettings, (D)evice list, (C)ard
public:
PageSystem(CommonData &common){
commonData = &common;
common.logger->logDebug(GwLog::LOG,"Instantiate PageSystem");
if (hasFRAM) {
mode = fram.read(FRAM_SYSTEM_MODE);
common.logger->logDebug(GwLog::DEBUG, "Loaded mode '%c' from FRAM", mode);
}
chipid = ESP.getEfuseMac();
simulation = common.config->getBool(common.config->useSimuData);
#ifdef BOARD_OBP40S3
use_sdcard = common.config->getBool(common.config->useSDCard);
#endif
buzzer_mode = common.config->getString(common.config->buzzerMode);
buzzer_mode.toLowerCase();
buzzer_power = common.config->getInt(common.config->buzzerPower);
cpuspeed = common.config->getString(common.config->cpuSpeed);
env_module = common.config->getString(common.config->useEnvSensor);
rtc_module = common.config->getString(common.config->useRTC);
gps_module = common.config->getString(common.config->useGPS);
batt_sensor = common.config->getString(common.config->usePowSensor1);
solar_sensor = common.config->getString(common.config->usePowSensor2);
gen_sensor = common.config->getString(common.config->usePowSensor3);
rot_sensor = common.config->getString(common.config->useRotSensor);
homelat = common.config->getString(common.config->homeLAT).toDouble();
homelon = common.config->getString(common.config->homeLON).toDouble();
}
void setupKeys() {
commonData->keydata[0].label = "EXIT";
commonData->keydata[1].label = "MODE";
commonData->keydata[2].label = "";
commonData->keydata[3].label = "RST";
commonData->keydata[4].label = "STBY";
commonData->keydata[5].label = "ILUM";
}
int handleKey(int key) {
// do *NOT* handle key #1 this handled by obp60task as exit
// Switch display mode
commonData->logger->logDebug(GwLog::LOG, "System keyboard handler");
if (key == 2) {
if (mode == 'N') {
mode = 'S';
} else if (mode == 'S') {
mode = 'D';
} else if (mode == 'D') {
if (hasSDCard) {
mode = 'C';
} else {
mode = 'N';
}
} else {
mode = 'N';
}
if (hasFRAM) fram.write(FRAM_SYSTEM_MODE, mode);
return 0;
}
#ifdef BOARD_OBP60S3
// grab cursor key to disable page navigation
if (key == 3) {
return 0;
}
// soft reset
if (key == 4) {
ESP.restart();
}
// standby / deep sleep
if (key == 5) {
commonData->logger->logDebug(GwLog::LOG, "System going into deep sleep mode...");
deepSleep(*commonData);
}
// Code for keylock
if (key == 11) {
commonData->keylock = !commonData->keylock;
return 0;
}
#endif
#ifdef BOARD_OBP40S3
// grab cursor keys to disable page navigation
if (key == 9 or key == 10) {
return 0;
}
// standby / deep sleep
if (key == 12) {
commonData->logger->logDebug(GwLog::LOG, "System going into deep sleep mode...");
deepSleep(*commonData);
}
#endif
return key;
}
void displayBarcode(String serialno, uint16_t x, uint16_t y, uint16_t s) {
// Barcode with serial number
// x, y is top left corner
// s is pixel size of a single box
QRCode qrcode;
uint8_t qrcodeData[qrcode_getBufferSize(4)];
#ifdef BOARD_OBP40S3
String prefix = "OBP40:SN:";
#endif
#ifdef BOARD_OBP60S3
String prefix = "OBP60:SN:";
#endif
qrcode_initText(&qrcode, qrcodeData, 4, 0, (prefix + serialno).c_str());
int16_t x0 = x;
for (uint8_t j = 0; j < qrcode.size; j++) {
for (uint8_t i = 0; i < qrcode.size; i++) {
if (qrcode_getModule(&qrcode, i, j)) {
getdisplay().fillRect(x, y, s, s, commonData->fgcolor);
}
x += s;
}
y += s;
x = x0;
}
}
int displayPage(PageData &pageData){
GwConfigHandler *config = commonData->config;
GwLog *logger = commonData->logger;
// Get config data
String flashLED = config->getString(config->flashLED);
// Optical warning by limit violation (unused)
if(String(flashLED) == "Limit Violation"){
setBlinkingLED(false);
setFlashLED(false);
}
// Logging boat values
logger->logDebug(GwLog::LOG, "Drawing at PageSystem, Mode=%c", mode);
// Draw page
//***********************************************************
uint16_t x0 = 8; // left column
uint16_t y0 = 48; // data table starts here
// Set display in partial refresh mode
getdisplay().setPartialWindow(0, 0, getdisplay().width(), getdisplay().height()); // Set partial update
if (mode == 'N') {
getdisplay().setFont(&Ubuntu_Bold12pt8b);
getdisplay().setCursor(8, 48);
getdisplay().print("System Information");
getdisplay().drawXBitmap(320, 25, logo64_bits, logo64_width, logo64_height, commonData->fgcolor);
getdisplay().setFont(&Ubuntu_Bold8pt8b);
y0 = 155;
char ssid[13];
snprintf(ssid, 13, "%04X%08X", (uint16_t)(chipid >> 32), (uint32_t)chipid);
displayBarcode(String(ssid), 320, 200, 2);
getdisplay().setCursor(8, 70);
getdisplay().print(String("MCUDEVICE-") + String(ssid));
getdisplay().setCursor(8, 95);
getdisplay().print("Firmware version: ");
getdisplay().setCursor(150, 95);
getdisplay().print(VERSINFO);
getdisplay().setCursor(8, 113);
getdisplay().print("Board version: ");
getdisplay().setCursor(150, 113);
getdisplay().print(BOARDINFO);
getdisplay().print(String(" HW ") + String(PCBINFO));
getdisplay().setCursor(8, 131);
getdisplay().print("Display version: ");
getdisplay().setCursor(150, 131);
getdisplay().print(DISPLAYINFO);
getdisplay().print("; GxEPD2 v");
getdisplay().print(GXEPD2INFO);
getdisplay().setCursor(8, 265);
#ifdef BOARD_OBP60S3
getdisplay().print("Press STBY to enter deep sleep mode");
#endif
#ifdef BOARD_OBP40S3
getdisplay().print("Press wheel to enter deep sleep mode");
#endif
// Flash memory size
uint32_t flash_size = ESP.getFlashChipSize();
getdisplay().setCursor(8, y0);
getdisplay().print("FLASH:");
getdisplay().setCursor(90, y0);
getdisplay().print(String(flash_size / 1024) + String(" kB"));
// PSRAM memory size
uint32_t psram_size = ESP.getPsramSize();
getdisplay().setCursor(8, y0 + 16);
getdisplay().print("PSRAM:");
getdisplay().setCursor(90, y0 + 16);
getdisplay().print(String(psram_size / 1024) + String(" kB"));
// FRAM available / status
getdisplay().setCursor(8, y0 + 32);
getdisplay().print("FRAM:");
getdisplay().setCursor(90, y0 + 32);
getdisplay().print(hasFRAM ? "available" : "not found");
#ifdef BOARD_OBP40S3
// SD-Card
getdisplay().setCursor(8, y0 + 48);
getdisplay().print("SD-Card:");
getdisplay().setCursor(90, y0 + 48);
if (hasSDCard) {
uint64_t cardsize = ((uint64_t) sdcard->csd.capacity) * sdcard->csd.sector_size / (1024 * 1024);
getdisplay().printf("%llu MB", cardsize);
} else {
getdisplay().print("off");
}
#endif
// Uptime
int64_t uptime = esp_timer_get_time() / 1000000;
String uptime_unit;
if (uptime < 120) {
uptime_unit = " seconds";
} else {
if (uptime < 2 * 3600) {
uptime /= 60;
uptime_unit = " minutes";
} else if (uptime < 2 * 3600 * 24) {
uptime /= 3600;
uptime_unit = " hours";
} else {
uptime /= 86400;
uptime_unit = " days";
}
}
getdisplay().setCursor(8, y0 + 80);
getdisplay().print("Uptime:");
getdisplay().setCursor(90, y0 + 80);
getdisplay().print(uptime);
getdisplay().print(uptime_unit);
// CPU speed config / active
getdisplay().setCursor(202, y0);
getdisplay().print("CPU speed:");
getdisplay().setCursor(300, y0);
getdisplay().print(cpuspeed);
getdisplay().print(" / ");
int cpu_freq = esp_clk_cpu_freq() / 1000000;
getdisplay().print(String(cpu_freq));
// total RAM free
int Heap_free = esp_get_free_heap_size();
getdisplay().setCursor(202, y0 + 16);
getdisplay().print("Total free:");
getdisplay().setCursor(300, y0 + 16);
getdisplay().print(String(Heap_free));
// RAM free for task
int RAM_free = uxTaskGetStackHighWaterMark(NULL);
getdisplay().setCursor(202, y0 + 32);
getdisplay().print("Task free:");
getdisplay().setCursor(300, y0 + 32);
getdisplay().print(String(RAM_free));
} else if (mode == 'S') {
// Settings
getdisplay().setFont(&Ubuntu_Bold12pt8b);
getdisplay().setCursor(x0, 48);
getdisplay().print("System settings");
getdisplay().setFont(&Ubuntu_Bold8pt8b);
x0 = 8;
y0 = 72;
// left column
getdisplay().setCursor(x0, y0);
getdisplay().print("Simulation:");
getdisplay().setCursor(120, y0);
getdisplay().print(simulation ? "on" : "off");
getdisplay().setCursor(x0, y0 + 16);
getdisplay().print("Environment:");
getdisplay().setCursor(120, y0 + 16);
getdisplay().print(env_module);
getdisplay().setCursor(x0, y0 + 32);
getdisplay().print("Buzzer:");
getdisplay().setCursor(120, y0 + 32);
getdisplay().print(buzzer_mode);
getdisplay().setCursor(x0, y0 + 64);
getdisplay().print("GPS:");
getdisplay().setCursor(120, y0 + 64);
getdisplay().print(gps_module);
getdisplay().setCursor(x0, y0 + 80);
getdisplay().print("RTC:");
getdisplay().setCursor(120, y0 + 80);
getdisplay().print(rtc_module);
getdisplay().setCursor(x0, y0 + 96);
getdisplay().print("Wifi:");
getdisplay().setCursor(120, y0 + 96);
getdisplay().print(commonData->status.wifiApOn ? "on" : "off");
// Home location
getdisplay().setCursor(x0, y0 + 128);
getdisplay().print("Home Lat.:");
getdisplay().setCursor(120, y0 + 128);
getdisplay().print(formatLatitude(homelat));
getdisplay().setCursor(x0, y0 + 144);
getdisplay().print("Home Lon.:");
getdisplay().setCursor(120, y0 + 144);
getdisplay().print(formatLongitude(homelon));
// right column
getdisplay().setCursor(202, y0);
getdisplay().print("Batt. sensor:");
getdisplay().setCursor(320, y0);
getdisplay().print(batt_sensor);
// Solar sensor
getdisplay().setCursor(202, y0 + 16);
getdisplay().print("Solar sensor:");
getdisplay().setCursor(320, y0 + 16);
getdisplay().print(solar_sensor);
// Generator sensor
getdisplay().setCursor(202, y0 + 32);
getdisplay().print("Gen. sensor:");
getdisplay().setCursor(320, y0 + 32);
getdisplay().print(gen_sensor);
// Gyro sensor
} else if (mode == 'C') {
// Card info
getdisplay().setFont(&Ubuntu_Bold12pt8b);
getdisplay().setCursor(8, 48);
getdisplay().print("SD Card info");
getdisplay().setFont(&Ubuntu_Bold8pt8b);
x0 = 20;
y0 = 72;
getdisplay().setCursor(x0, y0);
#ifdef BOARD_OBP60S3
// This mode should not be callable by devices without card hardware
// In case of accidential reaching this, display a friendly message
getdisplay().print("This mode is not indended to be reached!\n");
getdisplay().print("There's nothing to see here. Move on.");
#endif
#ifdef BOARD_OBP40S3
getdisplay().print("Work in progress...");
/* TODO
this code should go somewhere else. only for testing purposes here
identify card as OBP-Card:
magic.dat
version.dat
readme.txt
IMAGES/
CHARTS/
LOGS/
DATA/
hint: file access with fopen, fgets, fread, fclose
*/
// Simple test for magic file in root
getdisplay().setCursor(x0, y0 + 32);
String file_magic = MOUNT_POINT "/magic.dat";
logger->logDebug(GwLog::LOG, "Test magicfile: %s", file_magic.c_str());
struct stat st;
if (stat(file_magic.c_str(), &st) == 0) {
getdisplay().printf("File %s exists", file_magic.c_str());
} else {
getdisplay().printf("File %s not found", file_magic.c_str());
}
// Root directory check
DIR* dir = opendir(MOUNT_POINT);
int dy = 0;
if (dir != NULL) {
logger->logDebug(GwLog::LOG, "Root directory: %s", MOUNT_POINT);
struct dirent* entry;
while (((entry = readdir(dir)) != NULL) and (dy < 140)) {
getdisplay().setCursor(x0, y0 + 64 + dy);
getdisplay().print(entry->d_name);
// type 1 is file, type 2 is dir
if (entry->d_type == 2) {
getdisplay().print("/");
}
dy += 20;
logger->logDebug(GwLog::DEBUG, " %s type %d", entry->d_name, entry->d_type);
}
closedir(dir);
} else {
logger->logDebug(GwLog::LOG, "Failed to open root directory");
}
#endif
} else {
// NMEA2000 device list
getdisplay().setFont(&Ubuntu_Bold12pt8b);
getdisplay().setCursor(8, 48);
getdisplay().print("NMEA2000 device list");
getdisplay().setFont(&Ubuntu_Bold8pt8b);
getdisplay().setCursor(20, 80);
getdisplay().print("RxD: ");
getdisplay().print(String(commonData->status.n2kRx));
getdisplay().setCursor(20, 100);
getdisplay().print("TxD: ");
getdisplay().print(String(commonData->status.n2kTx));
}
// Update display
getdisplay().nextPage(); // Partial update (fast)
return PAGE_OK;
};
};
static Page* createPage(CommonData &common){
return new PageSystem(common);
}
/**
* with the code below we make this page known to the PageTask
* we give it a type (name) that can be selected in the config
* we define which function is to be called
* and we provide the number of user parameters we expect
* this will be number of BoatValue pointers in pageData.values
*/
PageDescription registerPageSystem(
"System", // Page name
createPage, // Action
0, // No bus values
true // Headers are anabled so far
);
#endif

View File

@@ -1,28 +1,29 @@
#ifdef BOARD_OBP60S3 #if defined BOARD_OBP60S3 || defined BOARD_OBP40S3
#include "Pagedata.h" #include "Pagedata.h"
#include "OBP60Extensions.h" #include "OBP60Extensions.h"
#include "BoatDataCalibration.h"
class PageThreeValues : public Page class PageThreeValues : public Page
{ {
bool keylock = false; // Keylock
public: public:
PageThreeValues(CommonData &common){ PageThreeValues(CommonData &common){
common.logger->logDebug(GwLog::LOG,"Show PageThreeValue"); commonData = &common;
common.logger->logDebug(GwLog::LOG,"Instantiate PageThreeValue");
} }
virtual int handleKey(int key){ virtual int handleKey(int key){
if(key == 11){ // Code for keylock // Code for keylock
keylock = !keylock; // Toggle keylock if(key == 11){
commonData->keylock = !commonData->keylock;
return 0; // Commit the key return 0; // Commit the key
} }
return key; return key;
} }
virtual void displayPage(CommonData &commonData, PageData &pageData){ int displayPage(PageData &pageData){
GwConfigHandler *config = commonData.config; GwConfigHandler *config = commonData->config;
GwLog *logger=commonData.logger; GwLog *logger = commonData->logger;
// Old values for hold function // Old values for hold function
static String svalue1old = ""; static String svalue1old = "";
@@ -43,28 +44,31 @@ class PageThreeValues : public Page
GwApi::BoatValue *bvalue1 = pageData.values[0]; // First element in list (only one value by PageOneValue) GwApi::BoatValue *bvalue1 = pageData.values[0]; // First element in list (only one value by PageOneValue)
String name1 = xdrDelete(bvalue1->getName()); // Value name String name1 = xdrDelete(bvalue1->getName()); // Value name
name1 = name1.substring(0, 6); // String length limit for value name name1 = name1.substring(0, 6); // String length limit for value name
calibrationData.calibrateInstance(bvalue1, logger); // Check if boat data value is to be calibrated
double value1 = bvalue1->value; // Value as double in SI unit double value1 = bvalue1->value; // Value as double in SI unit
bool valid1 = bvalue1->valid; // Valid information bool valid1 = bvalue1->valid; // Valid information
String svalue1 = formatValue(bvalue1, commonData).svalue; // Formatted value as string including unit conversion and switching decimal places String svalue1 = formatValue(bvalue1, *commonData).svalue; // Formatted value as string including unit conversion and switching decimal places
String unit1 = formatValue(bvalue1, commonData).unit; // Unit of value String unit1 = formatValue(bvalue1, *commonData).unit; // Unit of value
// Get boat values #2 // Get boat values #2
GwApi::BoatValue *bvalue2 = pageData.values[1]; // Second element in list (only one value by PageOneValue) GwApi::BoatValue *bvalue2 = pageData.values[1]; // Second element in list
String name2 = xdrDelete(bvalue2->getName()); // Value name String name2 = xdrDelete(bvalue2->getName()); // Value name
name2 = name2.substring(0, 6); // String length limit for value name name2 = name2.substring(0, 6); // String length limit for value name
calibrationData.calibrateInstance(bvalue2, logger); // Check if boat data value is to be calibrated
double value2 = bvalue2->value; // Value as double in SI unit double value2 = bvalue2->value; // Value as double in SI unit
bool valid2 = bvalue2->valid; // Valid information bool valid2 = bvalue2->valid; // Valid information
String svalue2 = formatValue(bvalue2, commonData).svalue; // Formatted value as string including unit conversion and switching decimal places String svalue2 = formatValue(bvalue2, *commonData).svalue; // Formatted value as string including unit conversion and switching decimal places
String unit2 = formatValue(bvalue2, commonData).unit; // Unit of value String unit2 = formatValue(bvalue2, *commonData).unit; // Unit of value
// Get boat values #3 // Get boat values #3
GwApi::BoatValue *bvalue3 = pageData.values[2]; // Second element in list (only one value by PageOneValue) GwApi::BoatValue *bvalue3 = pageData.values[2]; // Third element in list
String name3 = xdrDelete(bvalue3->getName()); // Value name String name3 = xdrDelete(bvalue3->getName()); // Value name
name3 = name3.substring(0, 6); // String length limit for value name name3 = name3.substring(0, 6); // String length limit for value name
calibrationData.calibrateInstance(bvalue3, logger); // Check if boat data value is to be calibrated
double value3 = bvalue3->value; // Value as double in SI unit double value3 = bvalue3->value; // Value as double in SI unit
bool valid3 = bvalue3->valid; // Valid information bool valid3 = bvalue3->valid; // Valid information
String svalue3 = formatValue(bvalue3, commonData).svalue; // Formatted value as string including unit conversion and switching decimal places String svalue3 = formatValue(bvalue3, *commonData).svalue; // Formatted value as string including unit conversion and switching decimal places
String unit3 = formatValue(bvalue3, commonData).unit; // Unit of value String unit3 = formatValue(bvalue3, *commonData).unit; // Unit of value
// Optical warning by limit violation (unused) // Optical warning by limit violation (unused)
if(String(flashLED) == "Limit Violation"){ if(String(flashLED) == "Limit Violation"){
@@ -73,7 +77,7 @@ class PageThreeValues : public Page
} }
// Logging boat values // Logging boat values
if (bvalue1 == NULL) return; if (bvalue1 == NULL) return PAGE_OK; // WTF why this statement?
LOG_DEBUG(GwLog::LOG,"Drawing at PageThreeValues, %s: %f, %s: %f, %s: %f", name1.c_str(), value1, name2.c_str(), value2, name3.c_str(), value3); LOG_DEBUG(GwLog::LOG,"Drawing at PageThreeValues, %s: %f, %s: %f, %s: %f", name1.c_str(), value1, name2.c_str(), value2, name3.c_str(), value3);
// Draw page // Draw page
@@ -85,13 +89,13 @@ class PageThreeValues : public Page
// ############### Value 1 ################ // ############### Value 1 ################
// Show name // Show name
getdisplay().setTextColor(commonData.fgcolor); getdisplay().setTextColor(commonData->fgcolor);
getdisplay().setFont(&Ubuntu_Bold20pt7b); getdisplay().setFont(&Ubuntu_Bold20pt8b);
getdisplay().setCursor(20, 55); getdisplay().setCursor(20, 55);
getdisplay().print(name1); // Page name getdisplay().print(name1); // Page name
// Show unit // Show unit
getdisplay().setFont(&Ubuntu_Bold12pt7b); getdisplay().setFont(&Ubuntu_Bold12pt8b);
getdisplay().setCursor(20, 90); getdisplay().setCursor(20, 90);
if(holdvalues == false){ if(holdvalues == false){
getdisplay().print(unit1); // Unit getdisplay().print(unit1); // Unit
@@ -102,11 +106,11 @@ class PageThreeValues : public Page
// Switch font if format for any values // Switch font if format for any values
if(bvalue1->getFormat() == "formatLatitude" || bvalue1->getFormat() == "formatLongitude"){ if(bvalue1->getFormat() == "formatLatitude" || bvalue1->getFormat() == "formatLongitude"){
getdisplay().setFont(&Ubuntu_Bold20pt7b); getdisplay().setFont(&Ubuntu_Bold20pt8b);
getdisplay().setCursor(50, 90); getdisplay().setCursor(50, 90);
} }
else if(bvalue1->getFormat() == "formatTime" || bvalue1->getFormat() == "formatDate"){ else if(bvalue1->getFormat() == "formatTime" || bvalue1->getFormat() == "formatDate"){
getdisplay().setFont(&Ubuntu_Bold20pt7b); getdisplay().setFont(&Ubuntu_Bold20pt8b);
getdisplay().setCursor(170, 68); getdisplay().setCursor(170, 68);
} }
else{ else{
@@ -129,17 +133,17 @@ class PageThreeValues : public Page
// ############### Horizontal Line ################ // ############### Horizontal Line ################
// Horizontal line 3 pix // Horizontal line 3 pix
getdisplay().fillRect(0, 105, 400, 3, commonData.fgcolor); getdisplay().fillRect(0, 105, 400, 3, commonData->fgcolor);
// ############### Value 2 ################ // ############### Value 2 ################
// Show name // Show name
getdisplay().setFont(&Ubuntu_Bold20pt7b); getdisplay().setFont(&Ubuntu_Bold20pt8b);
getdisplay().setCursor(20, 145); getdisplay().setCursor(20, 145);
getdisplay().print(name2); // Page name getdisplay().print(name2); // Page name
// Show unit // Show unit
getdisplay().setFont(&Ubuntu_Bold12pt7b); getdisplay().setFont(&Ubuntu_Bold12pt8b);
getdisplay().setCursor(20, 180); getdisplay().setCursor(20, 180);
if(holdvalues == false){ if(holdvalues == false){
getdisplay().print(unit2); // Unit getdisplay().print(unit2); // Unit
@@ -150,11 +154,11 @@ class PageThreeValues : public Page
// Switch font if format for any values // Switch font if format for any values
if(bvalue2->getFormat() == "formatLatitude" || bvalue2->getFormat() == "formatLongitude"){ if(bvalue2->getFormat() == "formatLatitude" || bvalue2->getFormat() == "formatLongitude"){
getdisplay().setFont(&Ubuntu_Bold20pt7b); getdisplay().setFont(&Ubuntu_Bold20pt8b);
getdisplay().setCursor(50, 180); getdisplay().setCursor(50, 180);
} }
else if(bvalue2->getFormat() == "formatTime" || bvalue2->getFormat() == "formatDate"){ else if(bvalue2->getFormat() == "formatTime" || bvalue2->getFormat() == "formatDate"){
getdisplay().setFont(&Ubuntu_Bold20pt7b); getdisplay().setFont(&Ubuntu_Bold20pt8b);
getdisplay().setCursor(170, 158); getdisplay().setCursor(170, 158);
} }
else{ else{
@@ -177,17 +181,17 @@ class PageThreeValues : public Page
// ############### Horizontal Line ################ // ############### Horizontal Line ################
// Horizontal line 3 pix // Horizontal line 3 pix
getdisplay().fillRect(0, 195, 400, 3, commonData.fgcolor); getdisplay().fillRect(0, 195, 400, 3, commonData->fgcolor);
// ############### Value 3 ################ // ############### Value 3 ################
// Show name // Show name
getdisplay().setFont(&Ubuntu_Bold20pt7b); getdisplay().setFont(&Ubuntu_Bold20pt8b);
getdisplay().setCursor(20, 235); getdisplay().setCursor(20, 235);
getdisplay().print(name3); // Page name getdisplay().print(name3); // Page name
// Show unit // Show unit
getdisplay().setFont(&Ubuntu_Bold12pt7b); getdisplay().setFont(&Ubuntu_Bold12pt8b);
getdisplay().setCursor(20, 270); getdisplay().setCursor(20, 270);
if(holdvalues == false){ if(holdvalues == false){
getdisplay().print(unit3); // Unit getdisplay().print(unit3); // Unit
@@ -198,11 +202,11 @@ class PageThreeValues : public Page
// Switch font if format for any values // Switch font if format for any values
if(bvalue3->getFormat() == "formatLatitude" || bvalue3->getFormat() == "formatLongitude"){ if(bvalue3->getFormat() == "formatLatitude" || bvalue3->getFormat() == "formatLongitude"){
getdisplay().setFont(&Ubuntu_Bold20pt7b); getdisplay().setFont(&Ubuntu_Bold20pt8b);
getdisplay().setCursor(50, 270); getdisplay().setCursor(50, 270);
} }
else if(bvalue3->getFormat() == "formatTime" || bvalue3->getFormat() == "formatDate"){ else if(bvalue3->getFormat() == "formatTime" || bvalue3->getFormat() == "formatDate"){
getdisplay().setFont(&Ubuntu_Bold20pt7b); getdisplay().setFont(&Ubuntu_Bold20pt8b);
getdisplay().setCursor(170, 248); getdisplay().setCursor(170, 248);
} }
else{ else{
@@ -222,26 +226,7 @@ class PageThreeValues : public Page
unit3old = unit3; // Save the old unit unit3old = unit3; // Save the old unit
} }
return PAGE_UPDATE;
// ############### Key Layout ################
// Key Layout
getdisplay().setFont(&Ubuntu_Bold8pt7b);
if(keylock == false){
getdisplay().setCursor(130, 290);
getdisplay().print("[ <<<< " + String(commonData.data.actpage) + "/" + String(commonData.data.maxpage) + " >>>> ]");
if(String(backlightMode) == "Control by Key"){ // Key for illumination
getdisplay().setCursor(343, 290);
getdisplay().print("[ILUM]");
}
}
else{
getdisplay().setCursor(130, 290);
getdisplay().print(" [ Keylock active ]");
}
// Update display
getdisplay().nextPage(); // Partial update (fast)
}; };
}; };

View File

@@ -1,28 +1,29 @@
#ifdef BOARD_OBP60S3 #if defined BOARD_OBP60S3 || defined BOARD_OBP40S3
#include "Pagedata.h" #include "Pagedata.h"
#include "OBP60Extensions.h" #include "OBP60Extensions.h"
#include "BoatDataCalibration.h"
class PageTwoValues : public Page class PageTwoValues : public Page
{ {
bool keylock = false; // Keylock
public: public:
PageTwoValues(CommonData &common){ PageTwoValues(CommonData &common){
common.logger->logDebug(GwLog::LOG,"Show PageTwoValue"); commonData = &common;
common.logger->logDebug(GwLog::LOG,"Instantiate PageTwoValue");
} }
virtual int handleKey(int key){ virtual int handleKey(int key){
if(key == 11){ // Code for keylock // Code for keylock
keylock = !keylock; // Toggle keylock if(key == 11){
commonData->keylock = !commonData->keylock;
return 0; // Commit the key return 0; // Commit the key
} }
return key; return key;
} }
virtual void displayPage(CommonData &commonData, PageData &pageData){ int displayPage(PageData &pageData){
GwConfigHandler *config = commonData.config; GwConfigHandler *config = commonData->config;
GwLog *logger=commonData.logger; GwLog *logger = commonData->logger;
// Old values for hold function // Old values for hold function
static String svalue1old = ""; static String svalue1old = "";
@@ -41,19 +42,21 @@ class PageTwoValues : public Page
GwApi::BoatValue *bvalue1 = pageData.values[0]; // First element in list (only one value by PageOneValue) GwApi::BoatValue *bvalue1 = pageData.values[0]; // First element in list (only one value by PageOneValue)
String name1 = xdrDelete(bvalue1->getName()); // Value name String name1 = xdrDelete(bvalue1->getName()); // Value name
name1 = name1.substring(0, 6); // String length limit for value name name1 = name1.substring(0, 6); // String length limit for value name
calibrationData.calibrateInstance(bvalue1, logger); // Check if boat data value is to be calibrated
double value1 = bvalue1->value; // Value as double in SI unit double value1 = bvalue1->value; // Value as double in SI unit
bool valid1 = bvalue1->valid; // Valid information bool valid1 = bvalue1->valid; // Valid information
String svalue1 = formatValue(bvalue1, commonData).svalue; // Formatted value as string including unit conversion and switching decimal places String svalue1 = formatValue(bvalue1, *commonData).svalue; // Formatted value as string including unit conversion and switching decimal places
String unit1 = formatValue(bvalue1, commonData).unit; // Unit of value String unit1 = formatValue(bvalue1, *commonData).unit; // Unit of value
// Get boat values #2 // Get boat values #2
GwApi::BoatValue *bvalue2 = pageData.values[1]; // Second element in list (only one value by PageOneValue) GwApi::BoatValue *bvalue2 = pageData.values[1]; // Second element in list
String name2 = xdrDelete(bvalue2->getName()); // Value name String name2 = xdrDelete(bvalue2->getName()); // Value name
name2 = name2.substring(0, 6); // String length limit for value name name2 = name2.substring(0, 6); // String length limit for value name
calibrationData.calibrateInstance(bvalue2, logger); // Check if boat data value is to be calibrated
double value2 = bvalue2->value; // Value as double in SI unit double value2 = bvalue2->value; // Value as double in SI unit
bool valid2 = bvalue2->valid; // Valid information bool valid2 = bvalue2->valid; // Valid information
String svalue2 = formatValue(bvalue2, commonData).svalue; // Formatted value as string including unit conversion and switching decimal places String svalue2 = formatValue(bvalue2, *commonData).svalue; // Formatted value as string including unit conversion and switching decimal places
String unit2 = formatValue(bvalue2, commonData).unit; // Unit of value String unit2 = formatValue(bvalue2, *commonData).unit; // Unit of value
// Optical warning by limit violation (unused) // Optical warning by limit violation (unused)
if(String(flashLED) == "Limit Violation"){ if(String(flashLED) == "Limit Violation"){
@@ -62,7 +65,7 @@ class PageTwoValues : public Page
} }
// Logging boat values // Logging boat values
if (bvalue1 == NULL) return; if (bvalue1 == NULL) return PAGE_OK; // WTF why this statement?
LOG_DEBUG(GwLog::LOG,"Drawing at PageTwoValues, %s: %f, %s: %f", name1.c_str(), value1, name2.c_str(), value2); LOG_DEBUG(GwLog::LOG,"Drawing at PageTwoValues, %s: %f, %s: %f", name1.c_str(), value1, name2.c_str(), value2);
// Draw page // Draw page
@@ -74,13 +77,13 @@ class PageTwoValues : public Page
// ############### Value 1 ################ // ############### Value 1 ################
// Show name // Show name
getdisplay().setTextColor(commonData.fgcolor); getdisplay().setTextColor(commonData->fgcolor);
getdisplay().setFont(&Ubuntu_Bold20pt7b); getdisplay().setFont(&Ubuntu_Bold20pt8b);
getdisplay().setCursor(20, 80); getdisplay().setCursor(20, 80);
getdisplay().print(name1); // Page name getdisplay().print(name1); // Page name
// Show unit // Show unit
getdisplay().setFont(&Ubuntu_Bold12pt7b); getdisplay().setFont(&Ubuntu_Bold12pt8b);
getdisplay().setCursor(20, 130); getdisplay().setCursor(20, 130);
if(holdvalues == false){ if(holdvalues == false){
getdisplay().print(unit1); // Unit getdisplay().print(unit1); // Unit
@@ -91,11 +94,11 @@ class PageTwoValues : public Page
// Switch font if format for any values // Switch font if format for any values
if(bvalue1->getFormat() == "formatLatitude" || bvalue1->getFormat() == "formatLongitude"){ if(bvalue1->getFormat() == "formatLatitude" || bvalue1->getFormat() == "formatLongitude"){
getdisplay().setFont(&Ubuntu_Bold20pt7b); getdisplay().setFont(&Ubuntu_Bold20pt8b);
getdisplay().setCursor(50, 130); getdisplay().setCursor(50, 130);
} }
else if(bvalue1->getFormat() == "formatTime" || bvalue1->getFormat() == "formatDate"){ else if(bvalue1->getFormat() == "formatTime" || bvalue1->getFormat() == "formatDate"){
getdisplay().setFont(&Ubuntu_Bold20pt7b); getdisplay().setFont(&Ubuntu_Bold20pt8b);
getdisplay().setCursor(170, 105); getdisplay().setCursor(170, 105);
} }
else{ else{
@@ -118,17 +121,17 @@ class PageTwoValues : public Page
// ############### Horizontal Line ################ // ############### Horizontal Line ################
// Horizontal line 3 pix // Horizontal line 3 pix
getdisplay().fillRect(0, 145, 400, 3, commonData.fgcolor); getdisplay().fillRect(0, 145, 400, 3, commonData->fgcolor);
// ############### Value 2 ################ // ############### Value 2 ################
// Show name // Show name
getdisplay().setFont(&Ubuntu_Bold20pt7b); getdisplay().setFont(&Ubuntu_Bold20pt8b);
getdisplay().setCursor(20, 190); getdisplay().setCursor(20, 190);
getdisplay().print(name2); // Page name getdisplay().print(name2); // Page name
// Show unit // Show unit
getdisplay().setFont(&Ubuntu_Bold12pt7b); getdisplay().setFont(&Ubuntu_Bold12pt8b);
getdisplay().setCursor(20, 240); getdisplay().setCursor(20, 240);
if(holdvalues == false){ if(holdvalues == false){
getdisplay().print(unit2); // Unit getdisplay().print(unit2); // Unit
@@ -139,11 +142,11 @@ class PageTwoValues : public Page
// Switch font if format for any values // Switch font if format for any values
if(bvalue2->getFormat() == "formatLatitude" || bvalue2->getFormat() == "formatLongitude"){ if(bvalue2->getFormat() == "formatLatitude" || bvalue2->getFormat() == "formatLongitude"){
getdisplay().setFont(&Ubuntu_Bold20pt7b); getdisplay().setFont(&Ubuntu_Bold20pt8b);
getdisplay().setCursor(50, 240); getdisplay().setCursor(50, 240);
} }
else if(bvalue2->getFormat() == "formatTime" || bvalue2->getFormat() == "formatDate"){ else if(bvalue2->getFormat() == "formatTime" || bvalue2->getFormat() == "formatDate"){
getdisplay().setFont(&Ubuntu_Bold20pt7b); getdisplay().setFont(&Ubuntu_Bold20pt8b);
getdisplay().setCursor(170, 215); getdisplay().setCursor(170, 215);
} }
else{ else{
@@ -163,26 +166,7 @@ class PageTwoValues : public Page
unit2old = unit2; // Save the old unit unit2old = unit2; // Save the old unit
} }
return PAGE_UPDATE;
// ############### Key Layout ################
// Key Layout
getdisplay().setFont(&Ubuntu_Bold8pt7b);
if(keylock == false){
getdisplay().setCursor(130, 290);
getdisplay().print("[ <<<< " + String(commonData.data.actpage) + "/" + String(commonData.data.maxpage) + " >>>> ]");
if(String(backlightMode) == "Control by Key"){ // Key for illumination
getdisplay().setCursor(343, 290);
getdisplay().print("[ILUM]");
}
}
else{
getdisplay().setCursor(130, 290);
getdisplay().print(" [ Keylock active ]");
}
// Update display
getdisplay().nextPage(); // Partial update (fast)
}; };
}; };

View File

@@ -1,4 +1,4 @@
#ifdef BOARD_OBP60S3 #if defined BOARD_OBP60S3 || defined BOARD_OBP40S3
#include "Pagedata.h" #include "Pagedata.h"
#include "OBP60Extensions.h" #include "OBP60Extensions.h"
@@ -7,41 +7,102 @@
class PageVoltage : public Page class PageVoltage : public Page
{ {
bool init = false; // Marker for init done bool init = false; // Marker for init done
bool keylock = false; // Keylock uint8_t average = 0; // Average type [0...3], 0=off, 1=10s, 2=60s, 3=300s
int average = 0; // Average type [0...3], 0=off, 1=10s, 2=60s, 3=300s
bool trend = true; // Trend indicator [0|1], 0=off, 1=on bool trend = true; // Trend indicator [0|1], 0=off, 1=on
double raw = 0; double raw = 0;
char mode = 'D'; // display mode (A)nalog | (D)igital
public: public:
PageVoltage(CommonData &common){ PageVoltage(CommonData &common){
common.logger->logDebug(GwLog::LOG,"Show PageVoltage"); commonData = &common;
common.logger->logDebug(GwLog::LOG,"Instantiate PageVoltage");
if (hasFRAM) {
average = fram.read(FRAM_VOLTAGE_AVG);
trend = fram.read(FRAM_VOLTAGE_TREND);
mode = fram.read(FRAM_VOLTAGE_MODE);
}
} }
virtual void setupKeys(){
Page::setupKeys();
commonData->keydata[0].label = "AVG";
commonData->keydata[1].label = "MODE";
commonData->keydata[4].label = "TRD";
}
virtual int handleKey(int key){ virtual int handleKey(int key){
// Change average // Change average
if(key == 1){ if(key == 1){
average ++; average ++;
average = average % 4; // Modulo 4 average = average % 4; // Modulo 4
if (hasFRAM) fram.write(FRAM_VOLTAGE_AVG, average);
return 0; // Commit the key return 0; // Commit the key
} }
// Switch display mode
if (key == 2) {
if (mode == 'A') {
mode = 'D';
} else {
mode = 'A';
}
if (hasFRAM) fram.write(FRAM_VOLTAGE_MODE, mode);
return 0;
}
// Trend indicator // Trend indicator
if(key == 5){ if(key == 5){
trend = !trend; trend = !trend;
if (hasFRAM) fram.write(FRAM_VOLTAGE_TREND, trend);
return 0; // Commit the key return 0; // Commit the key
} }
// Code for keylock // Code for keylock
if(key == 11){ if(key == 11){
keylock = !keylock; // Toggle keylock commonData->keylock = !commonData->keylock;
return 0; // Commit the key return 0; // Commit the key
} }
return key; return key;
} }
virtual void displayPage(CommonData &commonData, PageData &pageData) void printAvg(int avg, uint16_t x, uint16_t y, bool prefix) {
{ getdisplay().setFont(&Ubuntu_Bold8pt8b);
GwConfigHandler *config = commonData.config; getdisplay().setCursor(x, y);
GwLog *logger=commonData.logger; if (prefix) {
getdisplay().print("Avg: ");
}
switch (average) {
case 0:
getdisplay().print("1s");
break;
case 1:
getdisplay().print("10s");
break;
case 2:
getdisplay().print("60s");
break;
case 3:
getdisplay().print("300s");
break;
default:
getdisplay().print("1s");
break;
}
}
void printVoltageSymbol(uint16_t x, uint16_t y, uint16_t color) {
getdisplay().setFont(&Ubuntu_Bold16pt8b);
getdisplay().setCursor(x, y);
getdisplay().print("V");
getdisplay().fillRect(x, y + 6, 22, 3, color);
getdisplay().fillRect(x, y + 11, 6, 3, color);
getdisplay().fillRect(x + 8, y + 11, 6, 3, color);
getdisplay().fillRect(x + 16, y + 11, 6, 3, color);
}
int displayPage(PageData &pageData){
GwConfigHandler *config = commonData->config;
GwLog *logger = commonData->logger;
// Get config data // Get config data
bool simulation = config->getBool(config->useSimuData); bool simulation = config->getBool(config->useSimuData);
@@ -59,32 +120,32 @@ public:
// Create trend value // Create trend value
if(init == false){ // Load start values for first page run if(init == false){ // Load start values for first page run
valueTrend = commonData.data.batteryVoltage10; valueTrend = commonData->data.batteryVoltage10;
init = true; init = true;
} }
else{ // Reading trend value else{ // Reading trend value
valueTrend = commonData.data.batteryVoltage10; valueTrend = commonData->data.batteryVoltage10;
} }
// Get raw value for trend indicator // Get raw value for trend indicator
raw = commonData.data.batteryVoltage; // Live data raw = commonData->data.batteryVoltage; // Live data
// Switch average values // Switch average values
switch (average) { switch (average) {
case 0: case 0:
value1 = commonData.data.batteryVoltage; // Live data value1 = commonData->data.batteryVoltage; // Live data
break; break;
case 1: case 1:
value1 = commonData.data.batteryVoltage10; // Average 10s value1 = commonData->data.batteryVoltage10; // Average 10s
break; break;
case 2: case 2:
value1 = commonData.data.batteryVoltage60; // Average 60s value1 = commonData->data.batteryVoltage60; // Average 60s
break; break;
case 3: case 3:
value1 = commonData.data.batteryVoltage300; // Average 300s value1 = commonData->data.batteryVoltage300; // Average 300s
break; break;
default: default:
value1 = commonData.data.batteryVoltage; // Default value1 = commonData->data.batteryVoltage; // Default
break; break;
} }
bool valid1 = true; bool valid1 = true;
@@ -126,7 +187,6 @@ public:
} }
// Logging voltage value // Logging voltage value
if (raw == 0) return;
LOG_DEBUG(GwLog::LOG,"Drawing at PageVoltage, Type:%s %s:=%f", batType, name1.c_str(), raw); LOG_DEBUG(GwLog::LOG,"Drawing at PageVoltage, Type:%s %s:=%f", batType, name1.c_str(), raw);
// Draw page // Draw page
@@ -135,115 +195,195 @@ public:
// Set display in partial refresh mode // Set display in partial refresh mode
getdisplay().setPartialWindow(0, 0, getdisplay().width(), getdisplay().height()); // Set partial update getdisplay().setPartialWindow(0, 0, getdisplay().width(), getdisplay().height()); // Set partial update
// Show name if (mode == 'D') {
getdisplay().setTextColor(commonData.fgcolor); // Display mode digital
getdisplay().setFont(&Ubuntu_Bold32pt7b);
getdisplay().setCursor(20, 100);
getdisplay().print(name1); // Value name
// Show unit // Show name
getdisplay().setFont(&Ubuntu_Bold20pt7b); getdisplay().setTextColor(commonData->fgcolor);
getdisplay().setCursor(270, 100); getdisplay().setFont(&Ubuntu_Bold32pt8b);
getdisplay().print("V"); getdisplay().setCursor(20, 100);
getdisplay().print(name1); // Value name
// Show battery type #if defined BOARD_OBP40S3 && defined LIPO_ACCU_1200 && defined VOLTAGE_SENSOR
getdisplay().setFont(&Ubuntu_Bold8pt7b); // Show charge status
getdisplay().setCursor(295, 100); getdisplay().setFont(&Ubuntu_Bold8pt8b);
getdisplay().print(batType); getdisplay().setCursor(185, 100);
if(commonData->data.BatteryChargeStatus == true){
// Show average settings getdisplay().print("Charge");
getdisplay().setFont(&Ubuntu_Bold8pt7b);
getdisplay().setCursor(320, 84);
switch (average) {
case 0:
getdisplay().print("Avg: 1s");
break;
case 1:
getdisplay().print("Avg: 10s");
break;
case 2:
getdisplay().print("Avg: 60s");
break;
case 3:
getdisplay().print("Avg: 300s");
break;
default:
getdisplay().print("Avg: 1s");
break;
}
// Reading bus data or using simulation data
getdisplay().setFont(&DSEG7Classic_BoldItalic60pt7b);
getdisplay().setCursor(20, 240);
if(simulation == true){
if(batVoltage == "12V"){
value1 = 12.0;
}
if(batVoltage == "24V"){
value1 = 24.0;
}
value1 += float(random(0, 5)) / 10; // Simulation data
getdisplay().print(value1,1);
}
else{
// Check for valid real data, display also if hold values activated
if(valid1 == true || holdvalues == true){
// Resolution switching
if(value1 < 10){
getdisplay().print(value1,2);
}
if(value1 >= 10 && value1 < 100){
getdisplay().print(value1,1);
}
if(value1 >= 100){
getdisplay().print(value1,0);
}
} }
else{ else{
getdisplay().print("---"); // Missing bus data getdisplay().print("Discharge");
}
}
// Trend indicator
// Show trend indicator
if(trend == true){
getdisplay().fillRect(310, 240, 40, 120, commonData.bgcolor); // Clear area
getdisplay().fillRect(315, 183, 35, 4, commonData.fgcolor); // Draw separator
if(int(raw * 10) > int(valueTrend * 10)){
displayTrendHigh(320, 174, 11, commonData.fgcolor); // Show high indicator
} }
if(int(raw * 10) < int(valueTrend * 10)){ #endif
displayTrendLow(320, 195, 11, commonData.fgcolor); // Show low indicator
// Show unit
getdisplay().setFont(&Ubuntu_Bold20pt8b);
getdisplay().setCursor(270, 100);
getdisplay().print("V");
// Show battery type
getdisplay().setFont(&Ubuntu_Bold8pt8b);
getdisplay().setCursor(295, 100);
#ifdef BOARD_OBP60S3
getdisplay().print(batType);
#endif
#if defined BOARD_OBP40S3 && defined LIPO_ACCU_1200 && defined VOLTAGE_SENSOR
getdisplay().print("LiPo");
#endif
// Show average settings
printAvg(average, 320, 84, true);
// Reading bus data or using simulation data
getdisplay().setFont(&DSEG7Classic_BoldItalic60pt7b);
getdisplay().setCursor(20, 240);
if(simulation == true){
if(batVoltage == "12V"){
value1 = 12.0;
}
if(batVoltage == "24V"){
value1 = 24.0;
}
value1 += float(random(0, 5)) / 10; // Simulation data
getdisplay().print(value1,1);
} }
} else{
// No trend indicator // Check for valid real data, display also if hold values activated
else{ if(valid1 == true || holdvalues == true){
getdisplay().fillRect(310, 240, 40, 120, commonData.bgcolor); // Clear area // Resolution switching
} if(value1 < 10){
getdisplay().print(value1,2);
}
// Key Layout if(value1 >= 10 && value1 < 100){
getdisplay().setTextColor(commonData.fgcolor); getdisplay().print(value1,1);
getdisplay().setFont(&Ubuntu_Bold8pt7b); }
if(keylock == false){ if(value1 >= 100){
getdisplay().setCursor(10, 290); getdisplay().print(value1,0);
getdisplay().print("[AVG]"); }
getdisplay().setCursor(130, 290); }
getdisplay().print("[ <<<< " + String(commonData.data.actpage) + "/" + String(commonData.data.maxpage) + " >>>> ]"); else{
getdisplay().setCursor(293, 290); getdisplay().print("---"); // Missing bus data
getdisplay().print("[TRD]"); }
if(String(backlightMode) == "Control by Key"){ // Key for illumination
getdisplay().setCursor(343, 290);
getdisplay().print("[ILUM]");
} }
// Show trend indicator
if(trend == true){
getdisplay().fillRect(315, 183, 35, 4, commonData->fgcolor); // Draw separator
if(int(raw * 10) > int(valueTrend * 10)){
displayTrendHigh(320, 174, 11, commonData->fgcolor); // Show high indicator
}
if(int(raw * 10) < int(valueTrend * 10)){
displayTrendLow(320, 195, 11, commonData->fgcolor); // Show low indicator
}
}
} }
else{ else {
getdisplay().setCursor(130, 290); // Display mode analog
getdisplay().print(" [ Keylock active ]");
// center
Point c = {260, 270};
uint8_t r = 240;
Point p1, p2;
std::vector<Point> pts;
// Instrument
getdisplay().drawCircleHelper(c.x, c.y, r + 2, 0x01, commonData->fgcolor);
getdisplay().drawCircleHelper(c.x, c.y, r + 1, 0x01, commonData->fgcolor);
getdisplay().drawCircleHelper(c.x, c.y, r , 0x01, commonData->fgcolor);
// Scale
// angle to voltage scale mapping
std::map<int, String> mapping = {
{15, "10"}, {30, "11"}, {45, "12"}, {60, "13"}, {75, "14"}
};
pts = {
{c.x - r, c.y - 1},
{c.x - r + 12, c.y - 1},
{c.x - r + 12, c.y + 1},
{c.x - r, c.y + 1}
};
getdisplay().setFont(&Ubuntu_Bold10pt8b);
for (int angle = 3; angle < 90; angle += 3) {
if (angle % 15 == 0) {
fillPoly4(rotatePoints(c, pts, angle), commonData->fgcolor);
p1 = rotatePoint(c, {c.x - r + 30, c.y}, angle);
drawTextCenter(p1.x, p1.y, mapping[angle]);
}
else {
p1 = rotatePoint(c, {c.x - r, c.y}, angle);
p2 = rotatePoint(c, {c.x - r + 6, c.y}, angle);
getdisplay().drawLine(p1.x, p1.y, p2.x, p2.y, commonData->fgcolor);
}
}
// Pointer rotation and limits
double angle;
if (not valid1) {
angle = -0.5;
}
else {
if (value1 > 15.0) {
angle = 91;
}
else if (value1 <= 9) {
angle = -0.5;
}
else {
angle = (value1 - 9) * 15;
}
}
// Pointer
// thick part
pts = {
{c.x - 2, c.y + 3},
{c.x - r + 38, c.y + 2},
{c.x - r + 38, c.y - 2},
{c.x - 2, c.y - 3}
};
fillPoly4(rotatePoints(c, pts, angle), commonData->fgcolor);
// thin part
pts = {
{c.x - r + 40, c.y + 1},
{c.x - r + 5, c.y + 1},
{c.x - r + 5, c.y -1},
{c.x - r + 40, c.y - 1},
};
fillPoly4(rotatePoints(c, pts, angle), commonData->fgcolor);
// base
getdisplay().fillCircle(c.x, c.y, 7, commonData->fgcolor);
getdisplay().fillCircle(c.x, c.y, 4, commonData->bgcolor);
// Symbol
printVoltageSymbol(40, 60, commonData->fgcolor);
// Additional information at right side
getdisplay().setFont(&Ubuntu_Bold8pt8b);
getdisplay().setCursor(300, 60);
getdisplay().print("Source:");
getdisplay().setCursor(300, 80);
getdisplay().print(name1);
getdisplay().setCursor(300, 110);
getdisplay().print("Type:");
getdisplay().setCursor(300, 130);
getdisplay().print(batType);
getdisplay().setCursor(300, 160);
getdisplay().print("Avg:");
printAvg(average, 300, 180, false);
// FRAM indicator
if (hasFRAM) {
getdisplay().drawXBitmap(300, 240, fram_bits, icon_width, icon_height, commonData->fgcolor);
}
} }
// Update display return PAGE_UPDATE;
getdisplay().nextPage(); // Partial update (fast)
}; };
}; };
@@ -258,11 +398,11 @@ static Page *createPage(CommonData &common){
* and will will provide the names of the fixed values we need * and will will provide the names of the fixed values we need
*/ */
PageDescription registerPageVoltage( PageDescription registerPageVoltage(
"Voltage", // Name of page "Voltage", // Name of page
createPage, // Action createPage, // Action
0, // Number of bus values depends on selection in Web configuration 0, // Number of bus values depends on selection in Web configuration
{}, // Names of bus values undepends on selection in Web configuration (refer GwBoatData.h) {}, // Names of bus values undepends on selection in Web configuration (refer GwBoatData.h)
true // Show display header on/off true // Show display header on/off
); );
#endif #endif

View File

@@ -1,19 +1,45 @@
#ifdef BOARD_OBP60S3 #if defined BOARD_OBP60S3 || defined BOARD_OBP40S3
#include "Pagedata.h" #include "Pagedata.h"
#include "OBP60Extensions.h" #include "OBP60Extensions.h"
class PageWhite : public Page{ #include "images/OBP_400x300.xbm" // OBP Logo
bool keylock = false; // Keylock #ifdef BOARD_OBP60S3
#include "images/OBP60_400x300.xbm" // MFD with logo
#endif
#ifdef BOARD_OBP40S3
#include "images/OBP40_400x300.xbm" // MFD with logo
#endif
public: class PageWhite : public Page
{
char mode = 'W'; // display mode (W)hite | (L)ogo | (M)FD logo
public:
PageWhite(CommonData &common){ PageWhite(CommonData &common){
common.logger->logDebug(GwLog::LOG,"Show PageWhite"); commonData = &common;
common.logger->logDebug(GwLog::LOG,"Instantiate PageWhite");
refreshtime = 15000;
} }
virtual void displayPage(CommonData &commonData, PageData &pageData){ virtual int handleKey(int key) {
GwConfigHandler *config = commonData.config; // Change display mode
GwLog *logger=commonData.logger; if (key == 1) {
if (mode == 'W') {
mode = 'L';
} else if (mode == 'L') {
mode = 'M';
} else {
mode = 'W';
}
return 0;
}
return key;
}
int displayPage(PageData &pageData){
GwConfigHandler *config = commonData->config;
GwLog *logger = commonData->logger;
// Get config data // Get config data
String flashLED = config->getString(config->flashLED); String flashLED = config->getString(config->flashLED);
@@ -34,11 +60,28 @@ class PageWhite : public Page{
int bgcolor = GxEPD_WHITE; int bgcolor = GxEPD_WHITE;
// Set display in partial refresh mode // Set display in partial refresh mode
getdisplay().setPartialWindow(0, 0, getdisplay().width(), getdisplay().height()); // Set partial update if (mode == 'W') {
getdisplay().setFullWindow();
} else {
getdisplay().setPartialWindow(0, 0, getdisplay().width(), getdisplay().height()); // Set partial update
}
// Update display if (mode == 'L') {
getdisplay().nextPage(); // Partial update (fast) getdisplay().drawXBitmap(0, 0, OBP_400x300_bits, OBP_400x300_width, OBP_400x300_height, commonData->fgcolor);
} else if (mode == 'M') {
#ifdef BOARD_OBP60S3
getdisplay().drawXBitmap(0, 0, OBP60_400x300_bits, OBP60_400x300_width, OBP60_400x300_height, commonData->fgcolor);
#endif
#ifdef BOARD_OBP40S3
getdisplay().drawXBitmap(0, 0, OBP40_400x300_bits, OBP40_400x300_width, OBP40_400x300_height, commonData->fgcolor);
#endif
}
int ret = PAGE_UPDATE;
if (mode == 'W') {
ret |= PAGE_HIBERNATE;
}
return ret;
}; };
}; };
@@ -60,4 +103,4 @@ PageDescription registerPageWhite(
false // Show display header on/off false // Show display header on/off
); );
#endif #endif

643
lib/obp60task/PageWind.cpp Normal file
View File

@@ -0,0 +1,643 @@
#if defined BOARD_OBP60S3 || defined BOARD_OBP40S3
#include "Pagedata.h"
#include "OBP60Extensions.h"
#include "N2kMessages.h"
#include "BoatDataCalibration.h"
#define front_width 120
#define front_height 162
static unsigned char front_bits[] PROGMEM = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x1c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7f, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x80, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0xc0, 0xff, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0xff, 0x01, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe0, 0xff, 0x03,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0xf0, 0xff, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0xf0, 0xff, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0xff, 0x0f, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfc, 0xff, 0x1f,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0xfc, 0xff, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0xfe, 0xff, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x7f, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xf7, 0x7f,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80,
0xff, 0xf3, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0xc0, 0xff, 0xe1, 0xff, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0xff, 0xc1, 0xff, 0x01, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe0, 0xff, 0x80, 0xff,
0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe0,
0x7f, 0x80, 0xff, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0xf0, 0x7f, 0x00, 0xff, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0x3f, 0x00, 0xfe, 0x0f, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0x1f, 0x00, 0xfe,
0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfc,
0x1f, 0x00, 0xfc, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0xfc, 0x0f, 0x00, 0xf8, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0x0f, 0x00, 0xf8, 0x3f, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x07, 0x00, 0xf0,
0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff,
0x03, 0x00, 0xe0, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x80, 0xff, 0x03, 0x00, 0xe0, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x80, 0xff, 0x01, 0x00, 0xc0, 0xff, 0x01, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0xff, 0x00, 0x00, 0x80,
0xff, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe0, 0xff,
0x00, 0x00, 0x80, 0xff, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0xe0, 0x7f, 0x00, 0x00, 0x00, 0xff, 0x03, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0xf0, 0x7f, 0x00, 0x00, 0x00, 0xfe, 0x07, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf0, 0x3f, 0x00, 0x00, 0x00,
0xfe, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0x1f,
0x00, 0x00, 0x00, 0xfc, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0xf8, 0x1f, 0x00, 0x00, 0x00, 0xf8, 0x1f, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0xfc, 0x0f, 0x00, 0x00, 0x00, 0xf8, 0x1f, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfc, 0x07, 0x00, 0x00, 0x00,
0xf0, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0x07,
0x00, 0x00, 0x00, 0xe0, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0xfe, 0x03, 0x00, 0x00, 0x00, 0xe0, 0x7f, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0xff, 0x03, 0x00, 0x00, 0x00, 0xc0, 0xff, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x01, 0x00, 0x00, 0x00,
0xc0, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xff, 0x00,
0x00, 0x00, 0x00, 0x80, 0xff, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0xc0, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x01, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0xc0, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x03,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe0, 0x7f, 0x00, 0x00, 0x00, 0x00,
0x00, 0xfe, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe0, 0x3f, 0x00,
0x00, 0x00, 0x00, 0x00, 0xfe, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0xf0, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfc, 0x07, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0xf0, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0x0f,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf0, 0x0f, 0x00, 0x00, 0x00, 0x00,
0x00, 0xf8, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0x0f, 0x00,
0x00, 0x00, 0x00, 0x00, 0xf0, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0xf8, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf0, 0x1f, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0xfc, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe0, 0x3f,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfc, 0x03, 0x00, 0x00, 0x00, 0x00,
0x00, 0xc0, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0x01, 0x00,
0x00, 0x00, 0x00, 0x00, 0xc0, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0xfe, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x7f, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xff,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x7f, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x01, 0x00, 0x00, 0x00, 0x00, 0x80,
0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0x01, 0x00, 0x00,
0x00, 0x00, 0xc0, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfc,
0x03, 0x00, 0x00, 0x00, 0x00, 0xc0, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0xfc, 0x03, 0x00, 0x00, 0x00, 0x00, 0xc0, 0x1f, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0x07, 0x00, 0x00, 0x00, 0x00, 0xe0,
0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0x07, 0x00, 0x00,
0x00, 0x00, 0xe0, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf0,
0x07, 0x00, 0x00, 0x00, 0x00, 0xf0, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0xf0, 0x0f, 0x00, 0x00, 0x00, 0x00, 0xf0, 0x07, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0xe0, 0x0f, 0x00, 0x00, 0x00, 0x00, 0xf0,
0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe0, 0x1f, 0x00, 0x00,
0x00, 0x00, 0xf8, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0,
0x1f, 0x00, 0x00, 0x00, 0x00, 0xf8, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0xc0, 0x3f, 0x00, 0x00, 0x00, 0x00, 0xfc, 0x01, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x3f, 0x00, 0x00, 0x00, 0x00, 0xfc,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x3f, 0x00, 0x00,
0x00, 0x00, 0xfc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x7f, 0x00, 0x00, 0x00, 0x00, 0x7e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x7e, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0x00, 0x00, 0x00, 0x00, 0x3f,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0x00, 0x00,
0x00, 0x00, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0xfc, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0xfc, 0x01, 0x00, 0x00, 0x80, 0x1f, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0x01, 0x00, 0x00, 0x80, 0x0f,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0x01, 0x00,
0x00, 0x80, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0xf0, 0x03, 0x00, 0x00, 0xc0, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0xf0, 0x03, 0x00, 0x00, 0xc0, 0x07, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe0, 0x03, 0x00, 0x00, 0xc0, 0x03,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe0, 0x07, 0x00,
0x00, 0xe0, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0xe0, 0x07, 0x00, 0x00, 0xe0, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0xc0, 0x07, 0x00, 0x00, 0xe0, 0x01, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0x0f, 0x00, 0x00, 0xf0, 0x01,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x0f, 0x00,
0x00, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x80, 0x0f, 0x00, 0x00, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x00, 0xf8, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x00, 0x78, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00,
0x00, 0x78, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x3e, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x00, 0x3c, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x00, 0x3c, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0x00,
0x00, 0x1c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x7c, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x7c, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x78, 0x00, 0x00, 0x0e, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0x00,
0x00, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0xf8, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0xf0, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf0, 0x00, 0x00, 0x07, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf0, 0x01,
0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0xe0, 0x01, 0x80, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0xe0, 0x01, 0x80, 0x03, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe0, 0x01, 0x80, 0x03, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0x03,
0x80, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0xc0, 0x03, 0xc0, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0xc0, 0x03, 0xc0, 0x01, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0x03, 0xc0, 0x01, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x07,
0xc0, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x80, 0x07, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x80, 0x07, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x07, 0xe0, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07,
0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x0f, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x70, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x70, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e,
0x70, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x0e, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1e, 0x30, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1c,
0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x1c, 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x1c, 0x38, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1c, 0x18, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1c,
0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x3c, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0x1c, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0x1c, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38,
0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x38, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x0c, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x70,
0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x70, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x70, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x70, 0x0e, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x70,
0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x60, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x60, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x60, 0x06, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x60,
0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x60, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x60, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x60, 0x06, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe0,
0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0xc0, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0x03, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0,
0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0xc0, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0x03, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0,
0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0xc0, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0xc0 };
class PageWind : public Page
{
bool keylock = false; // Keylock
int8_t lp = 80; // Pointer length
char mode = 'N'; // page mode (N)ormal | (L)ens | e(X)ample
char source = 'A'; // data source (A)pparent | (T)rue
public:
PageWind(CommonData &common){
commonData = &common;
common.logger->logDebug(GwLog::LOG,"Instantiate PageWind");
if (hasFRAM) {
lp = fram.read(FRAM_WIND_SIZE);
source = fram.read(FRAM_WIND_SRC);
mode = fram.read(FRAM_WIND_MODE);
}
}
virtual void setupKeys(){
Page::setupKeys();
commonData->keydata[0].label = "MODE";
if (mode == 'X') {
commonData->keydata[1].label = "#MINUS";
commonData->keydata[4].label = "#PLUS";
} else {
commonData->keydata[1].label = "SRC";
}
}
// Key functions
virtual int handleKey(int key){
if(key == 1){ // Mode switch
if(mode == 'N'){
mode = 'L';
// } else if (mode == 'L') {
// mode = 'X';
} else {
mode = 'N';
}
if (hasFRAM) fram.write(FRAM_WIND_MODE, mode);
setupKeys();
return 0; // Commit the key
}
// Set source or reduce instrument size
if(key == 2){
if(mode == 'X'){
// Code for reduce
lp = lp - 10;
if(lp < 10){
lp = 10;
}
if (hasFRAM) fram.write(FRAM_WIND_SIZE, lp);
} else {
// Code for set source
if(source == 'A'){
source = 'T';
} else {
source = 'A';
}
if (hasFRAM) fram.write(FRAM_WIND_SRC, source);
}
return 0; // Commit the key
}
// Enlarge instrument size
if(key == 5 && mode == 'X'){ // Code for enlarge
lp = lp + 10;
if(lp > 80){
lp = 80;
}
if (hasFRAM) fram.write(FRAM_WIND_SIZE, lp);
return 0; // Commit the key
}
// Keylock function
if(key == 11){ // Code for keylock
commonData->keylock = !commonData->keylock;
return 0; // Commit the key
}
return key;
}
int displayPage(PageData &pageData)
{
GwConfigHandler *config = commonData->config;
GwLog *logger = commonData->logger;
static String svalue1old = "";
static String unit1old = "";
static String svalue2old = "";
static String unit2old = "";
// Get config data
String lengthformat = config->getString(config->lengthFormat);
bool simulation = config->getBool(config->useSimuData);
bool holdvalues = config->getBool(config->holdvalues);
String flashLED = config->getString(config->flashLED);
String backlightMode = config->getString(config->backlight);
GwApi::BoatValue *bvalue1; // Value 1 for speed on top
GwApi::BoatValue *bvalue2; // Value 2 for angle on bottom
// Get boat values for speed (AWS/TWS)
if (source == 'A') {
bvalue1 = pageData.values[0];
} else {
bvalue1 = pageData.values[2];
}
String name1 = bvalue1->getName().c_str(); // Value name
name1 = name1.substring(0, 6); // String length limit for value name
calibrationData.calibrateInstance(bvalue1, logger); // Check if boat data value is to be calibrated
double value1 = bvalue1->value; // Value as double in SI unit
// bool valid1 = bvalue1->valid; // Valid information
String svalue1 = formatValue(bvalue1, *commonData).svalue; // Formatted value as string including unit conversion and switching decimal places
String unit1 = formatValue(bvalue1, *commonData).unit; // Unit of value
// Get boat values for angle (AWD/TWD)
if (source == 'A') {
bvalue2 = pageData.values[1];
} else {
bvalue2 = pageData.values[3];
}
String name2 = bvalue2->getName().c_str(); // Value name
name2 = name2.substring(0, 6); // String length limit for value name
calibrationData.calibrateInstance(bvalue2, logger); // Check if boat data value is to be calibrated
double value2 = bvalue2->value; // Value as double in SI unit
// bool valid2 = bvalue2->valid; // Valid information
if (simulation) {
value2 = 0.62731; // some random value
}
String svalue2 = formatValue(bvalue2, *commonData).svalue; // Formatted value as string including unit conversion and switching decimal places
String unit2 = formatValue(bvalue2, *commonData).unit; // Unit of value
// Optical warning by limit violation (unused)
if(String(flashLED) == "Limit Violation"){
setBlinkingLED(false);
setFlashLED(false);
}
// Logging boat values
if (bvalue1 == NULL) return PAGE_OK; // WTF why this statement?
LOG_DEBUG(GwLog::LOG,"Drawing at PageWind, %s:%f, %s:%f", name1.c_str(), value1, name2.c_str(), value2);
// Draw page
//***********************************************************
// Set display in partial refresh mode
getdisplay().setPartialWindow(0, 0, getdisplay().width(), getdisplay().height()); // Set partial update
getdisplay().setTextColor(commonData->fgcolor);
if (mode == 'X') {
// Original example code with scaling circle
// Show values AWS/TWS
getdisplay().setFont(&Ubuntu_Bold20pt8b);
getdisplay().setCursor(20, 50);
getdisplay().print(name1); // Value name
getdisplay().print(": ");
if(holdvalues == false){
getdisplay().print(svalue1); // Value
getdisplay().print(" ");
getdisplay().print(unit1); // Unit
}
else{
getdisplay().print(svalue1old); // Value old
getdisplay().print(" ");
getdisplay().print(unit1old); // Unit old
}
// Show values AWD/TWD
getdisplay().setFont(&Ubuntu_Bold20pt8b);
getdisplay().setCursor(20, 260);
getdisplay().print(name2); // Value name
getdisplay().print(": ");
if(holdvalues == false){
getdisplay().print(svalue2); // Value
getdisplay().print(" ");
getdisplay().print(unit2); // Unit
}
else{
getdisplay().print(svalue2old); // Value old
getdisplay().print(" ");
getdisplay().print(unit2old); // Unit old
}
Point c = {200, 145};
// Draw instrument
getdisplay().fillCircle(c.x, c.y, lp + 5, commonData->fgcolor);
getdisplay().fillCircle(c.x, c.y, lp + 1, commonData->bgcolor);
// Wind pointer
if (bvalue2->valid or simulation) {
uint8_t lp0 = lp * 0.6; // effective pointer outside size
uint8_t lp1 = lp * 0.4; // effective pointer inside size
// zero position
std::vector<Point> pts = {
{c.x, c.y - lp},
{c.x - 7, c.y - lp + lp0},
{c.x, c.y - lp + lp1},
{c.x + 7, c.y - lp + lp0}
};
fillPoly4(rotatePoints(c, pts, RadToDeg(value2)), commonData->fgcolor);
} else {
getdisplay().setFont(&Ubuntu_Bold12pt8b);
drawTextCenter(c.x, c.y, "no data");
}
} else if (mode == 'L') { // Mode (L)ens
Point c = {200, 155};
uint16_t r = 150;
Point p;
std::vector<Point> pts = { // polygon lines
{c.x - 2, c.y - r},
{c.x + 2, c.y - r},
{c.x + 2, c.y - (r - 16)},
{c.x - 2, c.y - (r - 16)}
};
int angle;
getdisplay().setFont(&Ubuntu_Bold12pt8b);
// starbord
// text with line
angle = 20;
for (int i = 30; i < 150; i += 30) {
p = rotatePoint(c, {c.x, c.y - r + 40}, i);
drawTextCenter(p.x, p.y, String(angle));
angle += 10;
fillPoly4(rotatePoints(c, pts, i), commonData->fgcolor);
}
// dots
for (int i = 30; i < 138; i += 6) {
if (i % 15 != 0) {
p = rotatePoint(c, {c.x, c.y - r + 5}, i);
getdisplay().fillCircle(p.x, p.y, 2, commonData->fgcolor);
}
}
// port
angle = 50;
// text with line
for (int i = 240; i <= 330; i += 30) {
p = rotatePoint(c, {c.x, c.y - r + 40}, i);
drawTextCenter(p.x, p.y, String(angle));
angle -= 10;
fillPoly4(rotatePoints(c, pts, i), commonData->fgcolor);
}
// dots
for (int i = 228; i < 330; i += 6) {
if (i % 15 != 0) {
p = rotatePoint(c, {c.x, c.y - r + 5}, i);
getdisplay().fillCircle(p.x, p.y, 2, commonData->fgcolor);
}
}
// data source
getdisplay().setFont(&Ubuntu_Bold12pt8b);
getdisplay().setCursor(8, 50);
if (source == 'A') {
getdisplay().print("APP");
} else {
getdisplay().print("TRUE");
}
// Wind pointer (angle)
if (bvalue2->valid or simulation) {
float alpha = RadToDeg(value2);
bool port = (alpha > 180);
if (port) {
alpha = 360 - alpha;
}
if (alpha < 15) {
alpha = 15; // stop at start of scale
} else if (alpha > 55) {
alpha = 55; // stop at end of scale
}
alpha = 3 * alpha - 30; // convert to lens scale
if (port) {
alpha *= -1;
}
getdisplay().fillCircle(c.x, c.y, 8, commonData->fgcolor);
pts = {
{c.x - 1, c.y - (r - 20)},
{c.x + 1, c.y - (r - 20)},
{c.x + 6, c.y + 15},
{c.x - 6, c.y + 15}
};
fillPoly4(rotatePoints(c, pts, alpha), commonData->fgcolor);
getdisplay().fillCircle(c.x, c.y, 6, commonData->bgcolor);
} else {
getdisplay().setFont(&Ubuntu_Bold12pt8b);
drawTextCenter(c.x, c.y, "no data");
}
// Wind speed as decimal number
getdisplay().setFont(&DSEG7Classic_BoldItalic20pt7b);
getdisplay().setCursor(150, 250);
if (holdvalues == false) {
getdisplay().print(svalue1);
} else {
getdisplay().print(svalue1old);
}
// unit
getdisplay().setFont(&Ubuntu_Bold8pt8b);
getdisplay().setCursor(220, 265);
getdisplay().print("kts");
}
else {
// Normal mode
// data source
getdisplay().setFont(&Ubuntu_Bold12pt8b);
getdisplay().setCursor(8, 50);
if (source == 'A') {
getdisplay().print("APP");
} else {
getdisplay().print("TRUE");
}
// draw ship front symbol (as bitmap)
getdisplay().drawXBitmap(140, 30, front_bits, front_width, front_height, commonData->fgcolor);
Point c = {200, 155};
uint16_t r = 150;
Point p;
std::vector<Point> pts = { // polygon lines
{c.x - 2, c.y - r},
{c.x + 2, c.y - r},
{c.x + 2, c.y - (r - 16)},
{c.x - 2, c.y - (r - 16)}
};
int angle;
// starbord
// text with line
for (int i = 30; i < 150; i += 30) {
p = rotatePoint(c, {c.x, c.y - r + 40}, i);
drawTextCenter(p.x, p.y, String(i));
fillPoly4(rotatePoints(c, pts, i), commonData->fgcolor);
}
// dots
for (int i = 30; i < 150; i += 10) {
if (i % 30 != 0) {
p = rotatePoint(c, {c.x, c.y - r + 5}, i);
getdisplay().fillCircle(p.x, p.y, 3, commonData->fgcolor);
}
}
// port
// text with line
angle = 120;
for (int i = 240; i <= 330; i += 30) {
p = rotatePoint(c, {c.x, c.y - r + 40}, i);
drawTextCenter(p.x, p.y, String(angle));
angle -= 30;
fillPoly4(rotatePoints(c, pts, i), commonData->fgcolor);
}
// dots
for (int i = 210; i < 340; i += 10) {
if (i % 30 != 0) {
p = rotatePoint(c, {c.x, c.y - r + 5}, i);
getdisplay().fillCircle(p.x, p.y, 3, commonData->fgcolor);
}
}
// Wind speed as decimal number
getdisplay().setFont(&DSEG7Classic_BoldItalic20pt7b);
getdisplay().setCursor(150, 250);
if (holdvalues == false) {
getdisplay().print(svalue1);
} else {
getdisplay().print(svalue1old);
}
// unit
getdisplay().setFont(&Ubuntu_Bold8pt8b);
getdisplay().setCursor(220, 265);
getdisplay().print("kts");
// Wind pointer (angle)
if (bvalue2->valid or simulation) {
float alpha = RadToDeg(value2);
getdisplay().fillCircle(c.x, c.y, 8, commonData->fgcolor);
pts = {
{c.x - 1, c.y - (r - 20)},
{c.x + 1, c.y - (r - 20)},
{c.x + 6, c.y + 15},
{c.x - 6, c.y + 15}
};
fillPoly4(rotatePoints(c, pts, alpha), commonData->fgcolor);
getdisplay().fillCircle(c.x, c.y, 6, commonData->bgcolor);
} else {
getdisplay().setFont(&Ubuntu_Bold12pt8b);
drawTextCenter(c.x, c.y, "no data");
}
}
return PAGE_UPDATE;
};
};
static Page *createPage(CommonData &common){
return new PageWind(common);
}
/**
* with the code below we make this page known to the PageTask
* we give it a type (name) that can be selected in the config
* we define which function is to be called
* and we provide the number of user parameters we expect (0 here)
* and will will provide the names of the fixed values we need
*/
PageDescription registerPageWind(
"Wind", // Page name
createPage, // Action
0, // Number of bus values depends on selection in Web configuration
{"AWS","AWA", "TWS", "TWA"}, // Bus values we need in the page
true // Show display header on/off
);
#endif

View File

@@ -0,0 +1,530 @@
#if defined BOARD_OBP60S3 || defined BOARD_OBP40S3
#include "Pagedata.h"
#include "OBP60Extensions.h"
#include "OBPRingBuffer.h"
#include "OBPDataOperations.h"
#include "BoatDataCalibration.h"
#include <vector>
static const double radToDeg = 180.0 / M_PI; // Conversion factor from radians to degrees
// Get maximum difference of last <amount> of TWD ringbuffer values to center chart; returns "0" if data is not valid
int getCntr(const RingBuffer<int16_t>& windDirHstry, size_t amount)
{
const int MAX_VAL = windDirHstry.getMaxVal();
size_t count = windDirHstry.getCurrentSize();
if (windDirHstry.isEmpty() || amount <= 0) {
return 0;
}
if (amount > count)
amount = count;
uint16_t midWndDir, minWndDir, maxWndDir = 0;
int wndCenter = 0;
midWndDir = windDirHstry.getMid(amount);
if (midWndDir != MAX_VAL) {
midWndDir = midWndDir / 1000.0 * radToDeg;
wndCenter = int((midWndDir + (midWndDir >= 0 ? 5 : -5)) / 10) * 10; // Set new center value; round to nearest 10 degree value
minWndDir = windDirHstry.getMin(amount) / 1000.0 * radToDeg;
maxWndDir = windDirHstry.getMax(amount) / 1000.0 * radToDeg;
if ((maxWndDir - minWndDir) > 180 && !(minWndDir > maxWndDir)) { // if wind range is > 180 and no 0° crossover, adjust wndCenter to smaller wind range end
wndCenter = WindUtils::to360(wndCenter + 180);
}
}
return wndCenter;
}
// Get maximum difference of last <amount> of TWD ringbuffer values to center chart
int getRng(const RingBuffer<int16_t>& windDirHstry, int center, size_t amount)
{
int minVal = windDirHstry.getMinVal();
const int MAX_VAL = windDirHstry.getMaxVal();
size_t count = windDirHstry.getCurrentSize();
if (windDirHstry.isEmpty() || amount <= 0) {
return MAX_VAL;
}
if (amount > count)
amount = count;
int value = 0;
int rng = 0;
int maxRng = minVal;
// Start from the newest value (last) and go backwards x times
for (size_t i = 0; i < amount; i++) {
value = windDirHstry.get(count - 1 - i);
if (value == MAX_VAL) {
continue; // ignore invalid values
}
value = value / 1000.0 * radToDeg;
rng = abs(((value - center + 540) % 360) - 180);
if (rng > maxRng)
maxRng = rng;
}
if (maxRng > 180) {
maxRng = 180;
}
return (maxRng != minVal ? maxRng : MAX_VAL);
}
// ****************************************************************
class PageWindPlot : public Page {
bool keylock = false; // Keylock
char chrtMode = 'D'; // Chart mode: 'D' for TWD, 'S' for TWS, 'B' for both
bool showTruW = true; // Show true wind or apparant wind in chart area
bool oldShowTruW = false; // remember recent user selection of wind data type
int dataIntv = 1; // Update interval for wind history chart:
// (1)|(2)|(3)|(4)|(8) x 240 seconds for 4, 8, 12, 16, 32 min. history chart
bool useSimuData;
String flashLED;
String backlightMode;
public:
PageWindPlot(CommonData& common)
{
commonData = &common;
common.logger->logDebug(GwLog::LOG, "Instantiate PageWindPlot");
// Get config data
useSimuData = common.config->getBool(common.config->useSimuData);
// holdValues = common.config->getBool(common.config->holdvalues);
flashLED = common.config->getString(common.config->flashLED);
backlightMode = common.config->getString(common.config->backlight);
}
virtual void setupKeys()
{
Page::setupKeys();
// commonData->keydata[0].label = "MODE";
#if defined BOARD_OBP60S3
commonData->keydata[1].label = "SRC";
commonData->keydata[4].label = "INTV";
#elif defined BOARD_OBP40S3
commonData->keydata[1].label = "INTV";
#endif
}
// Key functions
virtual int handleKey(int key)
{
// Set chart mode TWD | TWS -> to be implemented
if (key == 1) {
if (chrtMode == 'D') {
chrtMode = 'S';
} else if (chrtMode == 'S') {
chrtMode = 'B';
} else {
chrtMode = 'D';
}
return 0; // Commit the key
}
#if defined BOARD_OBP60S3
// Set data source TRUE | APP
if (key == 2) {
showTruW = !showTruW;
return 0; // Commit the key
}
// Set interval for wind history chart update time (interval)
if (key == 5) {
#elif defined BOARD_OBP40S3
if (key == 2) {
#endif
if (dataIntv == 1) {
dataIntv = 2;
} else if (dataIntv == 2) {
dataIntv = 3;
} else if (dataIntv == 3) {
dataIntv = 4;
} else if (dataIntv == 4) {
dataIntv = 8;
} else {
dataIntv = 1;
}
return 0; // Commit the key
}
// Keylock function
if (key == 11) { // Code for keylock
commonData->keylock = !commonData->keylock;
return 0; // Commit the key
}
return key;
}
virtual void displayNew(PageData &pageData){
#ifdef BOARD_OBP40S3
String wndSrc; // Wind source true/apparant wind - preselection for OBP40
wndSrc = commonData->config->getString("page" + String(pageData.pageNumber) + "wndsrc");
if (wndSrc =="True wind") {
showTruW = true;
} else {
showTruW = false; // Wind source is apparant wind
}
commonData->logger->logDebug(GwLog::LOG,"New PageWindPlot: wind source=%s", wndSrc);
#endif
oldShowTruW = !showTruW; // makes wind source being initialized at initial page call
}
int displayPage(PageData& pageData)
{
GwConfigHandler* config = commonData->config;
GwLog* logger = commonData->logger;
static RingBuffer<int16_t>* wdHstry; // Wind direction data buffer
static RingBuffer<uint16_t>* wsHstry; // Wind speed data buffer
static String wdName, wdFormat; // Wind direction name and format
static String wsName, wsFormat; // Wind speed name and format
static int16_t wdMAX_VAL; // Max. value of wd history buffer, indicating invalid values
float wsValue; // Wind speed value in chart area
String wsUnit; // Wind speed unit in chart area
static GwApi::BoatValue* wsBVal = new GwApi::BoatValue("TWS"); // temp BoatValue for wind speed unit identification; required by OBP60Formater
// current boat data values; TWD/AWD only for validation test
const int numBoatData = 2;
GwApi::BoatValue* bvalue;
bool BDataValid[numBoatData];
static bool isInitialized = false; // Flag to indicate that page is initialized
static bool wndDataValid = false; // Flag to indicate if wind data is valid
static int numNoData; // Counter for multiple invalid data values in a row
static int width; // Screen width
static int height; // Screen height
static int xCenter; // Center of screen in x direction
static const int yOffset = 48; // Offset for y coordinates of chart area
static int cHeight; // height of chart area
static int bufSize; // History buffer size: 1.920 values for 32 min. history chart
static int intvBufSize; // Buffer size used for currently selected time interval
int count; // current size of buffer
static int numWndVals; // number of wind values available for current interval selection
static int bufStart; // 1st data value in buffer to show
int numAddedBufVals; // Number of values added to buffer since last display
size_t currIdx; // Current index in TWD history buffer
static size_t lastIdx; // Last index of TWD history buffer
static size_t lastAddedIdx = 0; // Last index of TWD history buffer when new data was added
static int oldDataIntv; // remember recent user selection of data interval
static int wndCenter; // chart wind center value position
static int wndLeft; // chart wind left value position
static int wndRight; // chart wind right value position
static int chrtRng; // Range of wind values from mid wind value to min/max wind value in degrees
int diffRng; // Difference between mid and current wind value
static const int dfltRng = 60; // Default range for chart
int midWndDir; // New value for wndCenter after chart start / shift
int x, y; // x and y coordinates for drawing
static int prevX, prevY; // Last x and y coordinates for drawing
static float chrtScl; // Scale for wind values in pixels per degree
int chrtVal; // Current wind value
static int chrtPrevVal; // Last wind value in chart area for check if value crosses 180 degree line
LOG_DEBUG(GwLog::LOG, "Display PageWindPlot");
ulong timer = millis();
if (!isInitialized) {
width = getdisplay().width();
height = getdisplay().height();
xCenter = width / 2;
cHeight = height - yOffset - 22;
numNoData = 0;
bufStart = 0;
oldDataIntv = 0;
wsValue = 0;
numAddedBufVals, currIdx, lastIdx = 0;
wndCenter = INT_MAX;
midWndDir = 0;
diffRng = dfltRng;
chrtRng = dfltRng;
isInitialized = true; // Set flag to indicate that page is now initialized
}
// read boat data values; TWD only for validation test, TWS for display of current value
for (int i = 0; i < numBoatData; i++) {
bvalue = pageData.values[i];
BDataValid[i] = bvalue->valid;
}
// Optical warning by limit violation (unused)
if (String(flashLED) == "Limit Violation") {
setBlinkingLED(false);
setFlashLED(false);
}
if (showTruW != oldShowTruW) {
if (showTruW) {
wdHstry = pageData.boatHstry->hstryBufList.twdHstry;
wsHstry = pageData.boatHstry->hstryBufList.twsHstry;
} else {
wdHstry = pageData.boatHstry->hstryBufList.awdHstry;
wsHstry = pageData.boatHstry->hstryBufList.awsHstry;
}
wdHstry->getMetaData(wdName, wdFormat);
wsHstry->getMetaData(wsName, wsFormat);
wdMAX_VAL = wdHstry->getMaxVal();
bufSize = wdHstry->getCapacity();
wsBVal->setFormat(wsHstry->getFormat());
lastAddedIdx = wdHstry->getLastIdx();
oldShowTruW = showTruW;
}
// Identify buffer size and buffer start position for chart
count = wdHstry->getCurrentSize();
currIdx = wdHstry->getLastIdx();
numAddedBufVals = (currIdx - lastAddedIdx + bufSize) % bufSize; // Number of values added to buffer since last display
if (dataIntv != oldDataIntv || count == 1) {
// new data interval selected by user; this is only x * 230 values instead of 240 seconds (4 minutes) per interval step
intvBufSize = cHeight * dataIntv;
numWndVals = min(count, (cHeight - 60) * dataIntv);
bufStart = max(0, count - numWndVals);
lastAddedIdx = currIdx;
oldDataIntv = dataIntv;
} else {
numWndVals = numWndVals + numAddedBufVals;
lastAddedIdx = currIdx;
if (count == bufSize) {
bufStart = max(0, bufStart - numAddedBufVals);
}
}
// LOG_DEBUG(GwLog::DEBUG,"PSRAM Size: %d kByte; free: %d Byte", ESP.getPsramSize()/1024, ESP.getFreePsram());
LOG_DEBUG(GwLog::DEBUG, "PageWindPlot Dataset: count: %d, xWD: %.1f, xWS: %.2f, xWD_valid? %d, intvBufSize: %d, numWndVals: %d, bufStart: %d, numAddedBufVals: %d, lastIdx: %d, wind source: %s",
count, wdHstry->getLast() / 1000.0 * radToDeg, wsHstry->getLast() / 1000.0 * 1.94384, BDataValid[0], intvBufSize, numWndVals, bufStart, numAddedBufVals, wdHstry->getLastIdx(),
showTruW ? "True" : "App");
// Set wndCenter from 1st real buffer value
if (wndCenter == INT_MAX || (wndCenter == 0 && count == 1)) {
wndCenter = getCntr(*wdHstry, numWndVals);
LOG_DEBUG(GwLog::DEBUG, "PageWindPlot Range Init: count: %d, xWD: %.1f, wndCenter: %d, diffRng: %d, chrtRng: %d, Min: %.0f, Max: %.0f", count, wdHstry->getLast() / 1000.0 * radToDeg,
wndCenter, diffRng, chrtRng, wdHstry->getMin(numWndVals) / 1000.0 * radToDeg, wdHstry->getMax(numWndVals) / 1000.0 * radToDeg);
} else {
// check and adjust range between left, center, and right chart limit
diffRng = getRng(*wdHstry, wndCenter, numWndVals);
diffRng = (diffRng == wdMAX_VAL ? 0 : diffRng);
if (diffRng > chrtRng) {
chrtRng = int((diffRng + (diffRng >= 0 ? 9 : -1)) / 10) * 10; // Round up to next 10 degree value
} else if (diffRng + 10 < chrtRng) { // Reduce chart range for higher resolution if possible
chrtRng = max(dfltRng, int((diffRng + (diffRng >= 0 ? 9 : -1)) / 10) * 10);
LOG_DEBUG(GwLog::DEBUG, "PageWindPlot Range adjust: wndCenter: %d, diffRng: %d, chrtRng: %d, Min: %.0f, Max: %.0f", wndCenter, diffRng, chrtRng,
wdHstry->getMin(numWndVals) / 1000.0 * radToDeg, wdHstry->getMax(numWndVals) / 1000.0 * radToDeg);
}
}
chrtScl = float(width) / float(chrtRng) / 2.0; // Chart scale: pixels per degree
wndLeft = wndCenter - chrtRng;
if (wndLeft < 0)
wndLeft += 360;
wndRight = (chrtRng < 180 ? wndCenter + chrtRng : wndCenter + chrtRng - 1);
if (wndRight >= 360)
wndRight -= 360;
// Draw page
//***********************************************************************
// Set display in partial refresh mode
getdisplay().setPartialWindow(0, 0, width, height); // Set partial update
getdisplay().setTextColor(commonData->fgcolor);
// chart lines
getdisplay().fillRect(0, yOffset, width, 2, commonData->fgcolor);
getdisplay().fillRect(xCenter, yOffset, 1, cHeight, commonData->fgcolor);
// chart labels
char sWndLbl[4]; // char buffer for Wind angle label
getdisplay().setFont(&Ubuntu_Bold12pt8b);
getdisplay().setCursor(xCenter - 88, yOffset - 3);
getdisplay().print(wdName); // Wind data name
snprintf(sWndLbl, 4, "%03d", (wndCenter < 0) ? (wndCenter + 360) : wndCenter);
drawTextCenter(xCenter, yOffset - 11, sWndLbl);
getdisplay().drawCircle(xCenter + 25, yOffset - 17, 2, commonData->fgcolor); // <degree> symbol
getdisplay().drawCircle(xCenter + 25, yOffset - 17, 3, commonData->fgcolor); // <degree> symbol
getdisplay().setCursor(1, yOffset - 3);
snprintf(sWndLbl, 4, "%03d", (wndLeft < 0) ? (wndLeft + 360) : wndLeft);
getdisplay().print(sWndLbl); // Wind left value
getdisplay().drawCircle(46, yOffset - 17, 2, commonData->fgcolor); // <degree> symbol
getdisplay().drawCircle(46, yOffset - 17, 3, commonData->fgcolor); // <degree> symbol
getdisplay().setCursor(width - 50, yOffset - 3);
snprintf(sWndLbl, 4, "%03d", (wndRight < 0) ? (wndRight + 360) : wndRight);
getdisplay().print(sWndLbl); // Wind right value
getdisplay().drawCircle(width - 5, yOffset - 17, 2, commonData->fgcolor); // <degree> symbol
getdisplay().drawCircle(width - 5, yOffset - 17, 3, commonData->fgcolor); // <degree> symbol
if (wdHstry->getMax() == wdMAX_VAL) {
// only <MAX_VAL> values in buffer -> no valid wind data available
wndDataValid = false;
} else if (!BDataValid[0] && !useSimuData) {
// currently no valid xWD data available and no simulation mode
numNoData++;
wndDataValid = true;
if (numNoData > 3) {
// If more than 4 invalid values in a row, send message
wndDataValid = false;
}
} else {
numNoData = 0; // reset data error counter
wndDataValid = true; // At least some wind data available
}
// Draw wind values in chart
//***********************************************************************
if (wndDataValid) {
for (int i = 0; i < (numWndVals / dataIntv); i++) {
chrtVal = static_cast<int>(wdHstry->get(bufStart + (i * dataIntv))); // show the latest wind values in buffer; keep 1st value constant in a rolling buffer
if (chrtVal == wdMAX_VAL) {
chrtPrevVal = wdMAX_VAL;
} else {
chrtVal = static_cast<int>((chrtVal / 1000.0 * radToDeg) + 0.5); // Convert to degrees and round
x = ((chrtVal - wndLeft + 360) % 360) * chrtScl;
y = yOffset + cHeight - i; // Position in chart area
if (i >= (numWndVals / dataIntv) - 1) // log chart data of 1 line (adjust for test purposes)
LOG_DEBUG(GwLog::DEBUG, "PageWindPlot Chart: i: %d, chrtVal: %d, bufStart: %d, count: %d, linesToShow: %d", i, chrtVal, bufStart, count, (numWndVals / dataIntv));
if ((i == 0) || (chrtPrevVal == wdMAX_VAL)) {
// just a dot for 1st chart point or after some invalid values
prevX = x;
prevY = y;
} else {
// cross borders check; shift values to [-180..0..180]; when crossing borders, range is 2x 180 degrees
int wndLeftDlt = -180 - ((wndLeft >= 180) ? (wndLeft - 360) : wndLeft);
int chrtVal180 = ((chrtVal + wndLeftDlt + 180) % 360 + 360) % 360 - 180;
int chrtPrevVal180 = ((chrtPrevVal + wndLeftDlt + 180) % 360 + 360) % 360 - 180;
if (((chrtPrevVal180 >= -180) && (chrtPrevVal180 < -90) && (chrtVal180 > 90)) || ((chrtPrevVal180 <= 179) && (chrtPrevVal180 > 90) && chrtVal180 <= -90)) {
// If current value crosses chart borders compared to previous value, split line
int xSplit = (((chrtPrevVal180 > 0 ? wndRight : wndLeft) - wndLeft + 360) % 360) * chrtScl;
getdisplay().drawLine(prevX, prevY, xSplit, y, commonData->fgcolor);
getdisplay().drawLine(prevX, prevY - 1, ((xSplit != prevX) ? xSplit : xSplit - 1), ((xSplit != prevX) ? y - 1 : y), commonData->fgcolor);
prevX = (((chrtVal180 > 0 ? wndRight : wndLeft) - wndLeft + 360) % 360) * chrtScl;
}
}
// Draw line with 2 pixels width + make sure vertical line are drawn correctly
getdisplay().drawLine(prevX, prevY, x, y, commonData->fgcolor);
getdisplay().drawLine(prevX, prevY - 1, ((x != prevX) ? x : x - 1), ((x != prevX) ? y - 1 : y), commonData->fgcolor);
chrtPrevVal = chrtVal;
prevX = x;
prevY = y;
}
// Reaching chart area top end
if (i >= (cHeight - 1)) {
oldDataIntv = 0; // force reset of buffer start and number of values to show in next display loop
int minWndDir = wdHstry->getMin(numWndVals) / 1000.0 * radToDeg;
int maxWndDir = wdHstry->getMax(numWndVals) / 1000.0 * radToDeg;
LOG_DEBUG(GwLog::DEBUG, "PageWindPlot FreeTop: Minimum: %d, Maximum: %d, OldwndCenter: %d", minWndDir, maxWndDir, wndCenter);
// if (((minWndDir - wndCenter >= 0) && (minWndDir - wndCenter < 180)) || ((maxWndDir - wndCenter <= 0) && (maxWndDir - wndCenter >=180))) {
if ((wndRight > wndCenter && (minWndDir >= wndCenter && minWndDir <= wndRight)) || (wndRight <= wndCenter && (minWndDir >= wndCenter || minWndDir <= wndRight)) || (wndLeft < wndCenter && (maxWndDir <= wndCenter && maxWndDir >= wndLeft)) || (wndLeft >= wndCenter && (maxWndDir <= wndCenter || maxWndDir >= wndLeft))) {
// Check if all wind value are left or right of center value -> optimize chart center
wndCenter = getCntr(*wdHstry, numWndVals);
}
LOG_DEBUG(GwLog::DEBUG, "PageWindPlot FreeTop: cHeight: %d, bufStart: %d, numWndVals: %d, wndCenter: %d", cHeight, bufStart, numWndVals, wndCenter);
break;
}
}
// Print wind speed value
int currentZone;
static int lastZone = 0;
static bool flipTws = false;
int xPosTws;
static const int yPosTws = yOffset + 40;
xPosTws = flipTws ? 20 : width - 145;
currentZone = (y >= yPosTws - 38) && (y <= yPosTws + 6) && (x >= xPosTws - 4) && (x <= xPosTws + 146) ? 1 : 0; // Define current zone for TWS value
if (currentZone != lastZone) {
// Only flip when x moves to a different zone
if ((y >= yPosTws - 38) && (y <= yPosTws + 6) && (x >= xPosTws - 4) && (x <= xPosTws + 146)) {
flipTws = !flipTws;
xPosTws = flipTws ? 20 : width - 145;
}
}
lastZone = currentZone;
wsValue = wsHstry->getLast();
wsBVal->value = wsValue / 1000.0; // temp variable to retreive data unit from OBP60Formater
wsBVal->valid = (static_cast<uint16_t>(wsValue) != wsHstry->getMinVal());
String swsValue = formatValue(wsBVal, *commonData).svalue; // value (string)
wsUnit = formatValue(wsBVal, *commonData).unit; // Unit of value
getdisplay().fillRect(xPosTws - 4, yPosTws - 38, 142, 44, commonData->bgcolor); // Clear area for TWS value
getdisplay().setFont(&DSEG7Classic_BoldItalic16pt7b);
getdisplay().setCursor(xPosTws, yPosTws);
getdisplay().print(swsValue); // Value
/* if (!wsBVal->valid) {
getdisplay().print("--.-");
} else {
wsValue = wsValue / 1000.0 * 1.94384; // Wind speed value in knots
if (wsValue < 10.0) {
getdisplay().printf("!%3.1f", wsValue); // Value, round to 1 decimal
} else {
getdisplay().printf("%4.1f", wsValue); // Value, round to 1 decimal
}
} */
getdisplay().setFont(&Ubuntu_Bold12pt8b);
getdisplay().setCursor(xPosTws + 82, yPosTws - 14);
getdisplay().print(wsName); // Name
getdisplay().setFont(&Ubuntu_Bold8pt8b);
getdisplay().setCursor(xPosTws + 82, yPosTws + 1);
getdisplay().print(wsUnit); // Unit
} else {
// No valid data available
LOG_DEBUG(GwLog::LOG, "PageWindPlot: No valid data available");
getdisplay().setFont(&Ubuntu_Bold10pt8b);
getdisplay().fillRect(xCenter - 33, height / 2 - 20, 66, 24, commonData->bgcolor); // Clear area for message
drawTextCenter(xCenter, height / 2 - 10, "No data");
}
// chart Y axis labels; print at last to overwrite potential chart lines in label area
int yPos;
int chrtLbl;
getdisplay().setFont(&Ubuntu_Bold8pt8b);
for (int i = 1; i <= 3; i++) {
yPos = yOffset + (i * 60);
getdisplay().fillRect(0, yPos, width, 1, commonData->fgcolor);
getdisplay().fillRect(0, yPos - 8, 24, 16, commonData->bgcolor); // Clear small area to remove potential chart lines
getdisplay().setCursor(1, yPos + 4);
if (count >= intvBufSize) {
// Calculate minute value for label
chrtLbl = ((i - 1 + (prevY < yOffset + 30)) * dataIntv) * -1; // change label if last data point is more than 30 lines (= seconds) from chart line
} else {
int j = 3 - i;
chrtLbl = (int((((numWndVals / dataIntv) - 50) * dataIntv / 60) + 1) - (j * dataIntv)) * -1; // 50 lines left below last chart line
}
getdisplay().printf("%3d", chrtLbl); // Wind value label
}
LOG_DEBUG(GwLog::DEBUG, "PageWindPlot time: %ld", millis() - timer);
return PAGE_UPDATE;
};
};
static Page* createPage(CommonData& common)
{
return new PageWindPlot(common);
}
/* with the code below we make this page known to the PageTask
* we give it a type (name) that can be selected in the config
* we define which function is to be called
* and we provide the number of user parameters we expect (0 here)
* and will will provide the names of the fixed values we need */
PageDescription registerPageWindPlot(
"WindPlot", // Page name
createPage, // Action
0, // Number of bus values depends on selection in Web configuration
{ "TWD", "AWD" }, // Bus values we need in the page
true // Show display header on/off
);
#endif

View File

@@ -1,32 +1,32 @@
#ifdef BOARD_OBP60S3 #if defined BOARD_OBP60S3 || defined BOARD_OBP40S3
#include "Pagedata.h" #include "Pagedata.h"
#include "OBP60Extensions.h" #include "OBP60Extensions.h"
#include "BoatDataCalibration.h"
class PageWindRose : public Page class PageWindRose : public Page
{ {
bool keylock = false; // Keylock
int16_t lp = 80; // Pointer length int16_t lp = 80; // Pointer length
public: public:
PageWindRose(CommonData &common){ PageWindRose(CommonData &common){
common.logger->logDebug(GwLog::LOG,"Show PageWindRose"); commonData = &common;
common.logger->logDebug(GwLog::LOG,"Instantiate PageWindRose");
} }
// Key functions // Key functions
virtual int handleKey(int key){ virtual int handleKey(int key){
// Keylock function // Code for keylock
if(key == 11){ // Code for keylock if(key == 11){
keylock = !keylock; // Toggle keylock commonData->keylock = !commonData->keylock;
return 0; // Commit the key return 0; // Commit the key
} }
return key; return key;
} }
virtual void displayPage(CommonData &commonData, PageData &pageData) int displayPage(PageData &pageData){
{ GwConfigHandler *config = commonData->config;
GwConfigHandler *config = commonData.config; GwLog *logger = commonData->logger;
GwLog *logger=commonData.logger;
static String svalue1old = ""; static String svalue1old = "";
static String unit1old = ""; static String unit1old = "";
@@ -48,80 +48,86 @@ public:
String flashLED = config->getString(config->flashLED); String flashLED = config->getString(config->flashLED);
String backlightMode = config->getString(config->backlight); String backlightMode = config->getString(config->backlight);
// Get boat values for AWA // Get boat value for AWA
GwApi::BoatValue *bvalue1 = pageData.values[0]; // First element in list (only one value by PageOneValue) GwApi::BoatValue *bvalue1 = pageData.values[0]; // First element in list (only one value by PageOneValue)
String name1 = xdrDelete(bvalue1->getName()); // Value name String name1 = xdrDelete(bvalue1->getName()); // Value name
name1 = name1.substring(0, 6); // String length limit for value name name1 = name1.substring(0, 6); // String length limit for value name
calibrationData.calibrateInstance(bvalue1, logger); // Check if boat data value is to be calibrated
double value1 = bvalue1->value; // Value as double in SI unit double value1 = bvalue1->value; // Value as double in SI unit
bool valid1 = bvalue1->valid; // Valid information bool valid1 = bvalue1->valid; // Valid information
value1 = formatValue(bvalue1, commonData).value;// Format only nesaccery for simulation data for pointer value1 = formatValue(bvalue1, *commonData).value;// Format only nesaccery for simulation data for pointer
String svalue1 = formatValue(bvalue1, commonData).svalue; // Formatted value as string including unit conversion and switching decimal places String svalue1 = formatValue(bvalue1, *commonData).svalue; // Formatted value as string including unit conversion and switching decimal places
String unit1 = formatValue(bvalue1, commonData).unit; // Unit of value String unit1 = formatValue(bvalue1, *commonData).unit; // Unit of value
if(valid1 == true){ if(valid1 == true){
svalue1old = svalue1; // Save old value svalue1old = svalue1; // Save old value
unit1old = unit1; // Save old unit unit1old = unit1; // Save old unit
} }
// Get boat values for AWS // Get boat value for AWS
GwApi::BoatValue *bvalue2 = pageData.values[1]; // First element in list (only one value by PageOneValue) GwApi::BoatValue *bvalue2 = pageData.values[1]; // Second element in list
String name2 = xdrDelete(bvalue2->getName()); // Value name String name2 = xdrDelete(bvalue2->getName()); // Value name
name2 = name2.substring(0, 6); // String length limit for value name name2 = name2.substring(0, 6); // String length limit for value name
calibrationData.calibrateInstance(bvalue2, logger); // Check if boat data value is to be calibrated
double value2 = bvalue2->value; // Value as double in SI unit double value2 = bvalue2->value; // Value as double in SI unit
bool valid2 = bvalue2->valid; // Valid information bool valid2 = bvalue2->valid; // Valid information
String svalue2 = formatValue(bvalue2, commonData).svalue; // Formatted value as string including unit conversion and switching decimal places String svalue2 = formatValue(bvalue2, *commonData).svalue; // Formatted value as string including unit conversion and switching decimal places
String unit2 = formatValue(bvalue2, commonData).unit; // Unit of value String unit2 = formatValue(bvalue2, *commonData).unit; // Unit of value
if(valid2 == true){ if(valid2 == true){
svalue2old = svalue2; // Save old value svalue2old = svalue2; // Save old value
unit2old = unit2; // Save old unit unit2old = unit2; // Save old unit
} }
// Get boat values TWD // Get boat value for TWD
GwApi::BoatValue *bvalue3 = pageData.values[2]; // Second element in list (only one value by PageOneValue) GwApi::BoatValue *bvalue3 = pageData.values[2]; // Third element in list
String name3 = xdrDelete(bvalue3->getName()); // Value name String name3 = xdrDelete(bvalue3->getName()); // Value name
name3 = name3.substring(0, 6); // String length limit for value name name3 = name3.substring(0, 6); // String length limit for value name
calibrationData.calibrateInstance(bvalue3, logger); // Check if boat data value is to be calibrated
double value3 = bvalue3->value; // Value as double in SI unit double value3 = bvalue3->value; // Value as double in SI unit
bool valid3 = bvalue3->valid; // Valid information bool valid3 = bvalue3->valid; // Valid information
String svalue3 = formatValue(bvalue3, commonData).svalue; // Formatted value as string including unit conversion and switching decimal places String svalue3 = formatValue(bvalue3, *commonData).svalue; // Formatted value as string including unit conversion and switching decimal places
String unit3 = formatValue(bvalue3, commonData).unit; // Unit of value String unit3 = formatValue(bvalue3, *commonData).unit; // Unit of value
if(valid3 == true){ if(valid3 == true){
svalue3old = svalue3; // Save old value svalue3old = svalue3; // Save old value
unit3old = unit3; // Save old unit unit3old = unit3; // Save old unit
} }
// Get boat values TWS // Get boat value for TWS
GwApi::BoatValue *bvalue4 = pageData.values[3]; // Second element in list (only one value by PageOneValue) GwApi::BoatValue *bvalue4 = pageData.values[3]; // Fourth element in list
String name4 = xdrDelete(bvalue4->getName()); // Value name String name4 = xdrDelete(bvalue4->getName()); // Value name
name4 = name4.substring(0, 6); // String length limit for value name name4 = name4.substring(0, 6); // String length limit for value name
calibrationData.calibrateInstance(bvalue4, logger); // Check if boat data value is to be calibrated
double value4 = bvalue4->value; // Value as double in SI unit double value4 = bvalue4->value; // Value as double in SI unit
bool valid4 = bvalue4->valid; // Valid information bool valid4 = bvalue4->valid; // Valid information
String svalue4 = formatValue(bvalue4, commonData).svalue; // Formatted value as string including unit conversion and switching decimal places String svalue4 = formatValue(bvalue4, *commonData).svalue; // Formatted value as string including unit conversion and switching decimal places
String unit4 = formatValue(bvalue4, commonData).unit; // Unit of value String unit4 = formatValue(bvalue4, *commonData).unit; // Unit of value
if(valid4 == true){ if(valid4 == true){
svalue4old = svalue4; // Save old value svalue4old = svalue4; // Save old value
unit4old = unit4; // Save old unit unit4old = unit4; // Save old unit
} }
// Get boat values DBT // Get boat value for DBT
GwApi::BoatValue *bvalue5 = pageData.values[4]; // Second element in list (only one value by PageOneValue) GwApi::BoatValue *bvalue5 = pageData.values[4]; // Fifth element in list
String name5 = xdrDelete(bvalue5->getName()); // Value name String name5 = xdrDelete(bvalue5->getName()); // Value name
name5 = name5.substring(0, 6); // String length limit for value name name5 = name5.substring(0, 6); // String length limit for value name
calibrationData.calibrateInstance(bvalue5, logger); // Check if boat data value is to be calibrated
double value5 = bvalue5->value; // Value as double in SI unit double value5 = bvalue5->value; // Value as double in SI unit
bool valid5 = bvalue5->valid; // Valid information bool valid5 = bvalue5->valid; // Valid information
String svalue5 = formatValue(bvalue5, commonData).svalue; // Formatted value as string including unit conversion and switching decimal places String svalue5 = formatValue(bvalue5, *commonData).svalue; // Formatted value as string including unit conversion and switching decimal places
String unit5 = formatValue(bvalue5, commonData).unit; // Unit of value String unit5 = formatValue(bvalue5, *commonData).unit; // Unit of value
if(valid5 == true){ if(valid5 == true){
svalue5old = svalue5; // Save old value svalue5old = svalue5; // Save old value
unit5old = unit5; // Save old unit unit5old = unit5; // Save old unit
} }
// Get boat values STW // Get boat value for STW
GwApi::BoatValue *bvalue6 = pageData.values[5]; // Second element in list (only one value by PageOneValue) GwApi::BoatValue *bvalue6 = pageData.values[5]; // Sixth element in list
String name6 = xdrDelete(bvalue6->getName()); // Value name String name6 = xdrDelete(bvalue6->getName()); // Value name
name6 = name6.substring(0, 6); // String length limit for value name name6 = name6.substring(0, 6); // String length limit for value name
calibrationData.calibrateInstance(bvalue6, logger); // Check if boat data value is to be calibrated
double value6 = bvalue6->value; // Value as double in SI unit double value6 = bvalue6->value; // Value as double in SI unit
bool valid6 = bvalue6->valid; // Valid information bool valid6 = bvalue6->valid; // Valid information
String svalue6 = formatValue(bvalue6, commonData).svalue; // Formatted value as string including unit conversion and switching decimal places String svalue6 = formatValue(bvalue6, *commonData).svalue; // Formatted value as string including unit conversion and switching decimal places
String unit6 = formatValue(bvalue6, commonData).unit; // Unit of value String unit6 = formatValue(bvalue6, *commonData).unit; // Unit of value
if(valid6 == true){ if(valid6 == true){
svalue6old = svalue6; // Save old value svalue6old = svalue6; // Save old value
unit6old = unit6; // Save old unit unit6old = unit6; // Save old unit
@@ -134,7 +140,7 @@ public:
} }
// Logging boat values // Logging boat values
if (bvalue1 == NULL) return; if (bvalue1 == NULL) return PAGE_OK; // WTF why this statement?
LOG_DEBUG(GwLog::LOG,"Drawing at PageWindRose, %s:%f, %s:%f, %s:%f, %s:%f, %s:%f, %s:%f", name1.c_str(), value1, name2.c_str(), value2, name3.c_str(), value3, name4.c_str(), value4, name5.c_str(), value5, name6.c_str(), value6); LOG_DEBUG(GwLog::LOG,"Drawing at PageWindRose, %s:%f, %s:%f, %s:%f, %s:%f, %s:%f, %s:%f", name1.c_str(), value1, name2.c_str(), value2, name3.c_str(), value3, name4.c_str(), value4, name5.c_str(), value5, name6.c_str(), value6);
// Draw page // Draw page
@@ -143,16 +149,16 @@ public:
// Set display in partial refresh mode // Set display in partial refresh mode
getdisplay().setPartialWindow(0, 0, getdisplay().width(), getdisplay().height()); // Set partial update getdisplay().setPartialWindow(0, 0, getdisplay().width(), getdisplay().height()); // Set partial update
getdisplay().setTextColor(commonData.fgcolor); getdisplay().setTextColor(commonData->fgcolor);
// Show values AWA // Show values AWA
getdisplay().setFont(&DSEG7Classic_BoldItalic20pt7b); getdisplay().setFont(&DSEG7Classic_BoldItalic20pt7b);
getdisplay().setCursor(10, 65); getdisplay().setCursor(10, 65);
getdisplay().print(svalue1); // Value getdisplay().print(svalue1); // Value
getdisplay().setFont(&Ubuntu_Bold12pt7b); getdisplay().setFont(&Ubuntu_Bold12pt8b);
getdisplay().setCursor(10, 95); getdisplay().setCursor(10, 95);
getdisplay().print(name1); // Name getdisplay().print(name1); // Name
getdisplay().setFont(&Ubuntu_Bold8pt7b); getdisplay().setFont(&Ubuntu_Bold8pt8b);
getdisplay().setCursor(10, 115); getdisplay().setCursor(10, 115);
getdisplay().print(" "); getdisplay().print(" ");
if(holdvalues == false){ if(holdvalues == false){
@@ -163,16 +169,16 @@ public:
} }
// Horizintal separator left // Horizintal separator left
getdisplay().fillRect(0, 149, 60, 3, commonData.fgcolor); getdisplay().fillRect(0, 149, 60, 3, commonData->fgcolor);
// Show values AWS // Show values AWS
getdisplay().setFont(&DSEG7Classic_BoldItalic20pt7b); getdisplay().setFont(&DSEG7Classic_BoldItalic20pt7b);
getdisplay().setCursor(10, 270); getdisplay().setCursor(10, 270);
getdisplay().print(svalue2); // Value getdisplay().print(svalue2); // Value
getdisplay().setFont(&Ubuntu_Bold12pt7b); getdisplay().setFont(&Ubuntu_Bold12pt8b);
getdisplay().setCursor(10, 220); getdisplay().setCursor(10, 220);
getdisplay().print(name2); // Name getdisplay().print(name2); // Name
getdisplay().setFont(&Ubuntu_Bold8pt7b); getdisplay().setFont(&Ubuntu_Bold8pt8b);
getdisplay().setCursor(10, 190); getdisplay().setCursor(10, 190);
getdisplay().print(" "); getdisplay().print(" ");
if(holdvalues == false){ if(holdvalues == false){
@@ -191,10 +197,10 @@ public:
else{ else{
getdisplay().print("---"); // Value getdisplay().print("---"); // Value
} }
getdisplay().setFont(&Ubuntu_Bold12pt7b); getdisplay().setFont(&Ubuntu_Bold12pt8b);
getdisplay().setCursor(335, 95); getdisplay().setCursor(335, 95);
getdisplay().print(name3); // Name getdisplay().print(name3); // Name
getdisplay().setFont(&Ubuntu_Bold8pt7b); getdisplay().setFont(&Ubuntu_Bold8pt8b);
getdisplay().setCursor(335, 115); getdisplay().setCursor(335, 115);
getdisplay().print(" "); getdisplay().print(" ");
if(holdvalues == false){ if(holdvalues == false){
@@ -205,16 +211,16 @@ public:
} }
// Horizintal separator right // Horizintal separator right
getdisplay().fillRect(340, 149, 80, 3, commonData.fgcolor); getdisplay().fillRect(340, 149, 80, 3, commonData->fgcolor);
// Show values TWS // Show values TWS
getdisplay().setFont(&DSEG7Classic_BoldItalic20pt7b); getdisplay().setFont(&DSEG7Classic_BoldItalic20pt7b);
getdisplay().setCursor(295, 270); getdisplay().setCursor(295, 270);
getdisplay().print(svalue4); // Value getdisplay().print(svalue4); // Value
getdisplay().setFont(&Ubuntu_Bold12pt7b); getdisplay().setFont(&Ubuntu_Bold12pt8b);
getdisplay().setCursor(335, 220); getdisplay().setCursor(335, 220);
getdisplay().print(name4); // Name getdisplay().print(name4); // Name
getdisplay().setFont(&Ubuntu_Bold8pt7b); getdisplay().setFont(&Ubuntu_Bold8pt8b);
getdisplay().setCursor(335, 190); getdisplay().setCursor(335, 190);
getdisplay().print(" "); getdisplay().print(" ");
if(holdvalues == false){ if(holdvalues == false){
@@ -230,10 +236,10 @@ public:
int rInstrument = 110; // Radius of grafic instrument int rInstrument = 110; // Radius of grafic instrument
float pi = 3.141592; float pi = 3.141592;
getdisplay().fillCircle(200, 150, rInstrument + 10, commonData.fgcolor); // Outer circle getdisplay().fillCircle(200, 150, rInstrument + 10, commonData->fgcolor); // Outer circle
getdisplay().fillCircle(200, 150, rInstrument + 7, commonData.bgcolor); // Outer circle getdisplay().fillCircle(200, 150, rInstrument + 7, commonData->bgcolor); // Outer circle
getdisplay().fillCircle(200, 150, rInstrument - 10, commonData.fgcolor); // Inner circle getdisplay().fillCircle(200, 150, rInstrument - 10, commonData->fgcolor); // Inner circle
getdisplay().fillCircle(200, 150, rInstrument - 13, commonData.bgcolor); // Inner circle getdisplay().fillCircle(200, 150, rInstrument - 13, commonData->bgcolor); // Inner circle
for(int i=0; i<360; i=i+10) for(int i=0; i<360; i=i+10)
{ {
@@ -242,7 +248,7 @@ public:
float y = 150 - (rInstrument-30)*cos(i/180.0*pi); // y-coordinate cots float y = 150 - (rInstrument-30)*cos(i/180.0*pi); // y-coordinate cots
const char *ii = ""; const char *ii = "";
switch (i) switch (i)
{ {
case 0: ii="0"; break; case 0: ii="0"; break;
case 30 : ii="30"; break; case 30 : ii="30"; break;
case 60 : ii="60"; break; case 60 : ii="60"; break;
@@ -264,14 +270,14 @@ public:
getdisplay().getTextBounds(ii, int(x), int(y), &x1, &y1, &w, &h); // Calc width of new string getdisplay().getTextBounds(ii, int(x), int(y), &x1, &y1, &w, &h); // Calc width of new string
getdisplay().setCursor(x-w/2, y+h/2); getdisplay().setCursor(x-w/2, y+h/2);
if(i % 30 == 0){ if(i % 30 == 0){
getdisplay().setFont(&Ubuntu_Bold8pt7b); getdisplay().setFont(&Ubuntu_Bold8pt8b);
getdisplay().print(ii); getdisplay().print(ii);
} }
// Draw sub scale with dots // Draw sub scale with dots
float x1c = 200 + rInstrument*sin(i/180.0*pi); float x1c = 200 + rInstrument*sin(i/180.0*pi);
float y1c = 150 - rInstrument*cos(i/180.0*pi); float y1c = 150 - rInstrument*cos(i/180.0*pi);
getdisplay().fillCircle((int)x1c, (int)y1c, 2, commonData.fgcolor); getdisplay().fillCircle((int)x1c, (int)y1c, 2, commonData->fgcolor);
float sinx=sin(i/180.0*pi); float sinx=sin(i/180.0*pi);
float cosx=cos(i/180.0*pi); float cosx=cos(i/180.0*pi);
@@ -284,10 +290,10 @@ public:
float yy2 = -(rInstrument+10); float yy2 = -(rInstrument+10);
getdisplay().fillTriangle(200+(int)(cosx*xx1-sinx*yy1),150+(int)(sinx*xx1+cosx*yy1), getdisplay().fillTriangle(200+(int)(cosx*xx1-sinx*yy1),150+(int)(sinx*xx1+cosx*yy1),
200+(int)(cosx*xx2-sinx*yy1),150+(int)(sinx*xx2+cosx*yy1), 200+(int)(cosx*xx2-sinx*yy1),150+(int)(sinx*xx2+cosx*yy1),
200+(int)(cosx*xx1-sinx*yy2),150+(int)(sinx*xx1+cosx*yy2),commonData.fgcolor); 200+(int)(cosx*xx1-sinx*yy2),150+(int)(sinx*xx1+cosx*yy2),commonData->fgcolor);
getdisplay().fillTriangle(200+(int)(cosx*xx2-sinx*yy1),150+(int)(sinx*xx2+cosx*yy1), getdisplay().fillTriangle(200+(int)(cosx*xx2-sinx*yy1),150+(int)(sinx*xx2+cosx*yy1),
200+(int)(cosx*xx1-sinx*yy2),150+(int)(sinx*xx1+cosx*yy2), 200+(int)(cosx*xx1-sinx*yy2),150+(int)(sinx*xx1+cosx*yy2),
200+(int)(cosx*xx2-sinx*yy2),150+(int)(sinx*xx2+cosx*yy2),commonData.fgcolor); 200+(int)(cosx*xx2-sinx*yy2),150+(int)(sinx*xx2+cosx*yy2),commonData->fgcolor);
} }
} }
@@ -304,7 +310,7 @@ public:
float yy2 = -(rInstrument-15); float yy2 = -(rInstrument-15);
getdisplay().fillTriangle(200+(int)(cosx*xx1-sinx*yy1),150+(int)(sinx*xx1+cosx*yy1), getdisplay().fillTriangle(200+(int)(cosx*xx1-sinx*yy1),150+(int)(sinx*xx1+cosx*yy1),
200+(int)(cosx*xx2-sinx*yy1),150+(int)(sinx*xx2+cosx*yy1), 200+(int)(cosx*xx2-sinx*yy1),150+(int)(sinx*xx2+cosx*yy1),
200+(int)(cosx*0-sinx*yy2),150+(int)(sinx*0+cosx*yy2),commonData.fgcolor); 200+(int)(cosx*0-sinx*yy2),150+(int)(sinx*0+cosx*yy2),commonData->fgcolor);
// Inverted pointer // Inverted pointer
// Pointer as triangle with center base 2*width // Pointer as triangle with center base 2*width
float endwidth = 2; // End width of pointer float endwidth = 2; // End width of pointer
@@ -314,12 +320,12 @@ public:
float iy2 = -endwidth; float iy2 = -endwidth;
getdisplay().fillTriangle(200+(int)(cosx*ix1-sinx*iy1),150+(int)(sinx*ix1+cosx*iy1), getdisplay().fillTriangle(200+(int)(cosx*ix1-sinx*iy1),150+(int)(sinx*ix1+cosx*iy1),
200+(int)(cosx*ix2-sinx*iy1),150+(int)(sinx*ix2+cosx*iy1), 200+(int)(cosx*ix2-sinx*iy1),150+(int)(sinx*ix2+cosx*iy1),
200+(int)(cosx*0-sinx*iy2),150+(int)(sinx*0+cosx*iy2),commonData.fgcolor); 200+(int)(cosx*0-sinx*iy2),150+(int)(sinx*0+cosx*iy2),commonData->fgcolor);
} }
// Center circle // Center circle
getdisplay().fillCircle(200, 150, startwidth + 6, commonData.bgcolor); getdisplay().fillCircle(200, 150, startwidth + 6, commonData->bgcolor);
getdisplay().fillCircle(200, 150, startwidth + 4, commonData.fgcolor); getdisplay().fillCircle(200, 150, startwidth + 4, commonData->fgcolor);
//******************************************************************************************* //*******************************************************************************************
@@ -327,7 +333,7 @@ public:
getdisplay().setFont(&DSEG7Classic_BoldItalic16pt7b); getdisplay().setFont(&DSEG7Classic_BoldItalic16pt7b);
getdisplay().setCursor(160, 200); getdisplay().setCursor(160, 200);
getdisplay().print(svalue5); // Value getdisplay().print(svalue5); // Value
getdisplay().setFont(&Ubuntu_Bold8pt7b); getdisplay().setFont(&Ubuntu_Bold8pt8b);
getdisplay().setCursor(190, 215); getdisplay().setCursor(190, 215);
getdisplay().print(" "); getdisplay().print(" ");
if(holdvalues == false){ if(holdvalues == false){
@@ -341,7 +347,7 @@ public:
getdisplay().setFont(&DSEG7Classic_BoldItalic16pt7b); getdisplay().setFont(&DSEG7Classic_BoldItalic16pt7b);
getdisplay().setCursor(160, 130); getdisplay().setCursor(160, 130);
getdisplay().print(svalue6); // Value getdisplay().print(svalue6); // Value
getdisplay().setFont(&Ubuntu_Bold8pt7b); getdisplay().setFont(&Ubuntu_Bold8pt8b);
getdisplay().setCursor(190, 90); getdisplay().setCursor(190, 90);
getdisplay().print(" "); getdisplay().print(" ");
if(holdvalues == false){ if(holdvalues == false){
@@ -351,23 +357,7 @@ public:
getdisplay().print(unit6old); // Unit getdisplay().print(unit6old); // Unit
} }
// Key Layout return PAGE_UPDATE;
getdisplay().setFont(&Ubuntu_Bold8pt7b);
if(keylock == false){
getdisplay().setCursor(130, 290);
getdisplay().print("[ <<<< " + String(commonData.data.actpage) + "/" + String(commonData.data.maxpage) + " >>>> ]");
if(String(backlightMode) == "Control by Key"){ // Key for illumination
getdisplay().setCursor(343, 290);
getdisplay().print("[ILUM]");
}
}
else{
getdisplay().setCursor(130, 290);
getdisplay().print(" [ Keylock active ]");
}
// Update display
getdisplay().nextPage(); // Partial update (fast)
}; };
}; };
@@ -382,11 +372,11 @@ static Page *createPage(CommonData &common){
* and will will provide the names of the fixed values we need * and will will provide the names of the fixed values we need
*/ */
PageDescription registerPageWindRose( PageDescription registerPageWindRose(
"WindRose", // Page name "WindRose", // Page name
createPage, // Action createPage, // Action
0, // Number of bus values depends on selection in Web configuration 0, // Number of bus values depends on selection in Web configuration
{"AWA", "AWS", "TWD", "TWS", "DBT", "STW"}, // Bus values we need in the page {"AWA", "AWS", "TWD", "TWS", "DBT", "STW"}, // Bus values we need in the page
true // Show display header on/off true // Show display header on/off
); );
#endif #endif

View File

@@ -1,32 +1,50 @@
#ifdef BOARD_OBP60S3 #if defined BOARD_OBP60S3 || defined BOARD_OBP40S3
#include "Pagedata.h" #include "Pagedata.h"
#include "OBP60Extensions.h" #include "OBP60Extensions.h"
#include "BoatDataCalibration.h"
class PageWindRoseFlex : public Page class PageWindRoseFlex : public Page
{ {
bool keylock = false; // Keylock
int16_t lp = 80; // Pointer length int16_t lp = 80; // Pointer length
char source = 'A'; // data source (A)pparent | (T)rue
String ssource="App."; // String for Data Source
public: public:
PageWindRoseFlex(CommonData &common){ PageWindRoseFlex(CommonData &common){
common.logger->logDebug(GwLog::LOG,"Show PageWindRoseFlex"); commonData = &common;
common.logger->logDebug(GwLog::LOG,"Instantiate PageWindRoseFlex");
}
virtual void setupKeys(){
Page::setupKeys();
commonData->keydata[1].label = "SRC";
} }
// Key functions // Key functions
virtual int handleKey(int key){ virtual int handleKey(int key){
// Keylock function if(key == 2){
if(key == 11){ // Code for keylock // Code for set source
keylock = !keylock; // Toggle keylock if(source == 'A'){
source = 'T';
ssource = "True"; // String to display
} else {
source = 'A';
ssource = "App."; // String to display
}
}
return key; // Commit the key
// Code for keylock
if(key == 11){
commonData->keylock = !commonData->keylock;
return 0; // Commit the key return 0; // Commit the key
} }
return key; return key;
} }
virtual void displayPage(CommonData &commonData, PageData &pageData) int displayPage(PageData &pageData){
{ GwConfigHandler *config = commonData->config;
GwConfigHandler *config = commonData.config; GwLog *logger = commonData->logger;
GwLog *logger=commonData.logger;
static String svalue1old = ""; static String svalue1old = "";
static String unit1old = ""; static String unit1old = "";
@@ -40,6 +58,11 @@ public:
static String unit5old = ""; static String unit5old = "";
static String svalue6old = ""; static String svalue6old = "";
static String unit6old = ""; static String unit6old = "";
static GFXfont name3font;
static GFXfont name4font;
static GFXfont name5font;
static GFXfont name6font;
// Get config data // Get config data
String lengthformat = config->getString(config->lengthFormat); String lengthformat = config->getString(config->lengthFormat);
@@ -48,93 +71,139 @@ public:
String flashLED = config->getString(config->flashLED); String flashLED = config->getString(config->flashLED);
String backlightMode = config->getString(config->backlight); String backlightMode = config->getString(config->backlight);
// Get boat values for AWA GwApi::BoatValue *bvalue1; // Value 1 for angle
GwApi::BoatValue *bvalue1 = pageData.values[0]; // First element in list (only one value by PageOneValue) GwApi::BoatValue *bvalue2; // Value 2 for speed
String name1 = xdrDelete(bvalue1->getName()); // Value name
// Get boat value for wind angle (AWA/TWA), shown by pointer
if (source == 'A') {
bvalue1 = pageData.values[4];
} else {
bvalue1 = pageData.values[6];
}
String name1 = bvalue1->getName().c_str(); // Value name
name1 = name1.substring(0, 6); // String length limit for value name name1 = name1.substring(0, 6); // String length limit for value name
calibrationData.calibrateInstance(bvalue1, logger); // Check if boat data value is to be calibrated
double value1 = bvalue1->value; // Value as double in SI unit double value1 = bvalue1->value; // Value as double in SI unit
bool valid1 = bvalue1->valid; // Valid information bool valid1 = bvalue1->valid; // Valid information
value1 = formatValue(bvalue1, commonData).value;// Format only nesaccery for simulation data for pointer String svalue1 = formatValue(bvalue1, *commonData).svalue; // Formatted value as string including unit conversion and switching decimal places
String svalue1 = formatValue(bvalue1, commonData).svalue; // Formatted value as string including unit conversion and switching decimal places String unit1 = formatValue(bvalue1, *commonData).unit; // Unit of value
String unit1 = formatValue(bvalue1, commonData).unit; // Unit of value if(valid1 == true){
if(valid1 == true){
svalue1old = svalue1; // Save old value svalue1old = svalue1; // Save old value
unit1old = unit1; // Save old unit unit1old = unit1; // Save old unit
} }
// Get boat values for AWS // Get boat value for wind speed (AWS/TWS), shown in top left corner
GwApi::BoatValue *bvalue2 = pageData.values[1]; // First element in list (only one value by PageOneValue) if (source == 'A') {
String name2 = xdrDelete(bvalue2->getName()); // Value name bvalue2 =pageData.values[5];
} else {
bvalue2 = pageData.values[7];
}
String name2 = bvalue2->getName().c_str(); // Value name
name2 = name2.substring(0, 6); // String length limit for value name name2 = name2.substring(0, 6); // String length limit for value name
calibrationData.calibrateInstance(bvalue2, logger); // Check if boat data value is to be calibrated
double value2 = bvalue2->value; // Value as double in SI unit double value2 = bvalue2->value; // Value as double in SI unit
bool valid2 = bvalue2->valid; // Valid information bool valid2 = bvalue2->valid; // Valid information
String svalue2 = formatValue(bvalue2, commonData).svalue; // Formatted value as string including unit conversion and switching decimal places if (simulation) {
String unit2 = formatValue(bvalue2, commonData).unit; // Unit of value value2 = 0.62731; // some random value
if(valid2 == true){ }
String svalue2 = formatValue(bvalue2, *commonData).svalue; // Formatted value as string including unit conversion and switching decimal places
String unit2 = formatValue(bvalue2, *commonData).unit; // Unit of value
if(valid2 == true){
svalue2old = svalue2; // Save old value svalue2old = svalue2; // Save old value
unit2old = unit2; // Save old unit unit2old = unit2; // Save old unit
} }
// Get boat values TWD
GwApi::BoatValue *bvalue3 = pageData.values[2]; // Second element in list (only one value by PageOneValue)
// Get boat value for bottom left corner
GwApi::BoatValue *bvalue3 = pageData.values[0];
String name3 = xdrDelete(bvalue3->getName()); // Value name String name3 = xdrDelete(bvalue3->getName()); // Value name
name3 = name3.substring(0, 6); // String length limit for value name name3 = name3.substring(0, 6); // String length limit for value name
if (name3.length()>3){
name3font=Ubuntu_Bold8pt8b;
}
else{
name3font=Ubuntu_Bold12pt8b;
}
calibrationData.calibrateInstance(bvalue3, logger); // Check if boat data value is to be calibrated
double value3 = bvalue3->value; // Value as double in SI unit double value3 = bvalue3->value; // Value as double in SI unit
bool valid3 = bvalue3->valid; // Valid information bool valid3 = bvalue3->valid; // Valid information
String svalue3 = formatValue(bvalue3, commonData).svalue; // Formatted value as string including unit conversion and switching decimal places String svalue3 = formatValue(bvalue3, *commonData).svalue; // Formatted value as string including unit conversion and switching decimal places
String unit3 = formatValue(bvalue3, commonData).unit; // Unit of value String unit3 = formatValue(bvalue3, *commonData).unit; // Unit of value
if(valid3 == true){ if(valid3 == true){
svalue3old = svalue3; // Save old value svalue3old = svalue3; // Save old value
unit3old = unit3; // Save old unit unit3old = unit3; // Save old unit
} }
// Get boat values TWS // Get boat value for top right corner
GwApi::BoatValue *bvalue4 = pageData.values[3]; // Second element in list (only one value by PageOneValue) GwApi::BoatValue *bvalue4 = pageData.values[1];
String name4 = xdrDelete(bvalue4->getName()); // Value name String name4 = xdrDelete(bvalue4->getName()); // Value name
name4 = name4.substring(0, 6); // String length limit for value name name4 = name4.substring(0, 6); // String length limit for value name
if (name4.length()>3){
name4font=Ubuntu_Bold8pt8b;
}
else{
name4font=Ubuntu_Bold12pt8b;
}
calibrationData.calibrateInstance(bvalue4, logger); // Check if boat data value is to be calibrated
double value4 = bvalue4->value; // Value as double in SI unit double value4 = bvalue4->value; // Value as double in SI unit
bool valid4 = bvalue4->valid; // Valid information bool valid4 = bvalue4->valid; // Valid information
String svalue4 = formatValue(bvalue4, commonData).svalue; // Formatted value as string including unit conversion and switching decimal places String svalue4 = formatValue(bvalue4, *commonData).svalue; // Formatted value as string including unit conversion and switching decimal places
String unit4 = formatValue(bvalue4, commonData).unit; // Unit of value String unit4 = formatValue(bvalue4, *commonData).unit; // Unit of value
if(valid4 == true){ if(valid4 == true){
svalue4old = svalue4; // Save old value svalue4old = svalue4; // Save old value
unit4old = unit4; // Save old unit unit4old = unit4; // Save old unit
} }
// Get boat values DBT // Get boat value bottom right corner
GwApi::BoatValue *bvalue5 = pageData.values[4]; // Second element in list (only one value by PageOneValue) GwApi::BoatValue *bvalue5 = pageData.values[2];
String name5 = xdrDelete(bvalue5->getName()); // Value name String name5 = xdrDelete(bvalue5->getName()); // Value name
name5 = name5.substring(0, 6); // String length limit for value name name5 = name5.substring(0, 6); // String length limit for value name
if (name5.length()>3){
name5font=Ubuntu_Bold8pt8b;
}
else{
name5font=Ubuntu_Bold12pt8b;
}
calibrationData.calibrateInstance(bvalue5, logger); // Check if boat data value is to be calibrated
double value5 = bvalue5->value; // Value as double in SI unit double value5 = bvalue5->value; // Value as double in SI unit
bool valid5 = bvalue5->valid; // Valid information bool valid5 = bvalue5->valid; // Valid information
String svalue5 = formatValue(bvalue5, commonData).svalue; // Formatted value as string including unit conversion and switching decimal places String svalue5 = formatValue(bvalue5, *commonData).svalue; // Formatted value as string including unit conversion and switching decimal places
String unit5 = formatValue(bvalue5, commonData).unit; // Unit of value String unit5 = formatValue(bvalue5, *commonData).unit; // Unit of value
if(valid5 == true){ if(valid5 == true){
svalue5old = svalue5; // Save old value svalue5old = svalue5; // Save old value
unit5old = unit5; // Save old unit unit5old = unit5; // Save old unit
} }
// Get boat values STW // Get boat value for center (name is not displayed)
GwApi::BoatValue *bvalue6 = pageData.values[5]; // Second element in list (only one value by PageOneValue) GwApi::BoatValue *bvalue6 = pageData.values[3];
String name6 = xdrDelete(bvalue6->getName()); // Value name String name6 = xdrDelete(bvalue6->getName()); // Value name
name6 = name6.substring(0, 6); // String length limit for value name name6 = name6.substring(0, 6); // String length limit for value name
if (name6.length()>3){
name6font=Ubuntu_Bold8pt8b;
}
else{
name6font=Ubuntu_Bold8pt8b;
}
calibrationData.calibrateInstance(bvalue6, logger); // Check if boat data value is to be calibrated
double value6 = bvalue6->value; // Value as double in SI unit double value6 = bvalue6->value; // Value as double in SI unit
bool valid6 = bvalue6->valid; // Valid information bool valid6 = bvalue6->valid; // Valid information
String svalue6 = formatValue(bvalue6, commonData).svalue; // Formatted value as string including unit conversion and switching decimal places String svalue6 = formatValue(bvalue6, *commonData).svalue; // Formatted value as string including unit conversion and switching decimal places
String unit6 = formatValue(bvalue6, commonData).unit; // Unit of value String unit6 = formatValue(bvalue6, *commonData).unit; // Unit of value
if(valid6 == true){ if(valid6 == true){
svalue6old = svalue6; // Save old value svalue6old = svalue6; // Save old value
unit6old = unit6; // Save old unit unit6old = unit6; // Save old unit
} }
// Optical warning by limit violation (unused) // Optical warning by limit violation (unused)
if(String(flashLED) == "Limit Violation"){ if(String(flashLED) == "Limit Violation"){
setBlinkingLED(false); setBlinkingLED(false);
setFlashLED(false); setFlashLED(false);
} }
// Logging boat values // Logging boat values
if (bvalue1 == NULL) return; if (bvalue1 == NULL) return PAGE_OK; // WTF why this statement?
LOG_DEBUG(GwLog::LOG,"Drawing at PageWindRoseFlex, %s:%f, %s:%f, %s:%f, %s:%f, %s:%f, %s:%f", name1.c_str(), value1, name2.c_str(), value2, name3.c_str(), value3, name4.c_str(), value4, name5.c_str(), value5, name6.c_str(), value6); LOG_DEBUG(GwLog::LOG,"Drawing at PageWindRoseFlex, %s:%f, %s:%f, %s:%f, %s:%f, %s:%f, %s:%f", name1.c_str(), value1, name2.c_str(), value2, name3.c_str(), value3, name4.c_str(), value4, name5.c_str(), value5, name6.c_str(), value6);
// Draw page // Draw page
@@ -143,37 +212,17 @@ public:
// Set display in partial refresh mode // Set display in partial refresh mode
getdisplay().setPartialWindow(0, 0, getdisplay().width(), getdisplay().height()); // Set partial update getdisplay().setPartialWindow(0, 0, getdisplay().width(), getdisplay().height()); // Set partial update
getdisplay().setTextColor(commonData.fgcolor); getdisplay().setTextColor(commonData->fgcolor);
// Show values AWA // Show AWS or TWS top left
getdisplay().setFont(&DSEG7Classic_BoldItalic20pt7b); getdisplay().setFont(&DSEG7Classic_BoldItalic20pt7b);
getdisplay().setCursor(10, 65); getdisplay().setCursor(10, 65);
getdisplay().print(svalue1); // Value
getdisplay().setFont(&Ubuntu_Bold12pt7b);
getdisplay().setCursor(10, 95);
getdisplay().print(name1); // Name
getdisplay().setFont(&Ubuntu_Bold8pt7b);
getdisplay().setCursor(10, 115);
getdisplay().print(" ");
if(holdvalues == false){
getdisplay().print(unit1); // Unit
}
else{
getdisplay().print(unit1old); // Unit
}
// Horizintal separator left
getdisplay().fillRect(0, 149, 60, 3, commonData.fgcolor);
// Show values AWS
getdisplay().setFont(&DSEG7Classic_BoldItalic20pt7b);
getdisplay().setCursor(10, 270);
getdisplay().print(svalue2); // Value getdisplay().print(svalue2); // Value
getdisplay().setFont(&Ubuntu_Bold12pt7b); getdisplay().setFont(&Ubuntu_Bold12pt8b);
getdisplay().setCursor(10, 220); getdisplay().setCursor(10, 95);
getdisplay().print(name2); // Name getdisplay().print(name2); // Name
getdisplay().setFont(&Ubuntu_Bold8pt7b); getdisplay().setFont(&Ubuntu_Bold8pt8b);
getdisplay().setCursor(10, 190); getdisplay().setCursor(10, 115);
getdisplay().print(" "); getdisplay().print(" ");
if(holdvalues == false){ if(holdvalues == false){
getdisplay().print(unit2); // Unit getdisplay().print(unit2); // Unit
@@ -182,20 +231,18 @@ public:
getdisplay().print(unit2old); // Unit getdisplay().print(unit2old); // Unit
} }
// Show values TWD // Horizintal separator left
getdisplay().fillRect(0, 149, 60, 3, commonData->fgcolor);
// Show value 3 (=first user-configured parameter) at bottom left
getdisplay().setFont(&DSEG7Classic_BoldItalic20pt7b); getdisplay().setFont(&DSEG7Classic_BoldItalic20pt7b);
getdisplay().setCursor(295, 65); getdisplay().setCursor(10, 270);
if(valid3 == true){ getdisplay().print(svalue3); // Value
getdisplay().print(abs(value3 * 180 / PI), 0); // Value getdisplay().setFont(&name3font);
} getdisplay().setCursor(10, 220);
else{
getdisplay().print("---"); // Value
}
getdisplay().setFont(&Ubuntu_Bold12pt7b);
getdisplay().setCursor(335, 95);
getdisplay().print(name3); // Name getdisplay().print(name3); // Name
getdisplay().setFont(&Ubuntu_Bold8pt7b); getdisplay().setFont(&Ubuntu_Bold8pt8b);
getdisplay().setCursor(335, 115); getdisplay().setCursor(10, 190);
getdisplay().print(" "); getdisplay().print(" ");
if(holdvalues == false){ if(holdvalues == false){
getdisplay().print(unit3); // Unit getdisplay().print(unit3); // Unit
@@ -204,58 +251,74 @@ public:
getdisplay().print(unit3old); // Unit getdisplay().print(unit3old); // Unit
} }
// Horizintal separator right // Show value 4 (=second user-configured parameter) at top right
getdisplay().fillRect(340, 149, 80, 3, commonData.fgcolor);
// Show values TWS
getdisplay().setFont(&DSEG7Classic_BoldItalic20pt7b); getdisplay().setFont(&DSEG7Classic_BoldItalic20pt7b);
getdisplay().setCursor(295, 270); getdisplay().setCursor(295, 65);
getdisplay().print(svalue4); // Value getdisplay().print(svalue4); // Value
getdisplay().setFont(&Ubuntu_Bold12pt7b); getdisplay().setFont(&name4font);
getdisplay().setCursor(335, 220); getdisplay().setCursor(325, 95);
getdisplay().print(name4); // Name getdisplay().print(name4); // Name
getdisplay().setFont(&Ubuntu_Bold8pt7b); getdisplay().setFont(&Ubuntu_Bold8pt8b);
getdisplay().setCursor(335, 190); getdisplay().setCursor(325, 115);
getdisplay().print(" "); getdisplay().print(" ");
if(holdvalues == false){ if(holdvalues == false){
getdisplay().print(unit4); // Unit getdisplay().print(unit4); // Unit
} }
else{ else{
getdisplay().print(unit4old); // Unit getdisplay().print(unit4old); // Unit
} }
// Horizintal separator right
getdisplay().fillRect(340, 149, 80, 3, commonData->fgcolor);
// Show value 5 (=third user-configured parameter) at bottom right
getdisplay().setFont(&DSEG7Classic_BoldItalic20pt7b);
getdisplay().setCursor(295, 270);
getdisplay().print(svalue5); // Value
getdisplay().setFont(&name5font);
getdisplay().setCursor(325, 220);
getdisplay().print(name5); // Name
getdisplay().setFont(&Ubuntu_Bold8pt8b);
getdisplay().setCursor(325, 190);
getdisplay().print(" ");
if(holdvalues == false){
getdisplay().print(unit5); // Unit
}
else{
getdisplay().print(unit5old); // Unit
}
//******************************************************************************************* //*******************************************************************************************
// Draw wind rose // Draw wind rose
int rInstrument = 110; // Radius of grafic instrument int rInstrument = 110; // Radius of grafic instrument
float pi = 3.141592;
getdisplay().fillCircle(200, 150, rInstrument + 10, commonData.fgcolor); // Outer circle getdisplay().fillCircle(200, 150, rInstrument + 10, commonData->fgcolor); // Outer circle
getdisplay().fillCircle(200, 150, rInstrument + 7, commonData.bgcolor); // Outer circle getdisplay().fillCircle(200, 150, rInstrument + 7, commonData->bgcolor); // Outer circle
getdisplay().fillCircle(200, 150, rInstrument - 10, commonData.fgcolor); // Inner circle getdisplay().fillCircle(200, 150, rInstrument - 10, commonData->fgcolor); // Inner circle
getdisplay().fillCircle(200, 150, rInstrument - 13, commonData.bgcolor); // Inner circle getdisplay().fillCircle(200, 150, rInstrument - 13, commonData->bgcolor); // Inner circle
for(int i=0; i<360; i=i+10) for(int i=0; i<360; i=i+10)
{ {
// Scaling values // Scaling values
float x = 200 + (rInstrument-30)*sin(i/180.0*pi); // x-coordinate dots float x = 200 + (rInstrument-30)*sin(i/180.0*M_PI); // x-coordinate dots
float y = 150 - (rInstrument-30)*cos(i/180.0*pi); // y-coordinate dots float y = 150 - (rInstrument-30)*cos(i/180.0*M_PI); // y-coordinate dots
const char *ii = ""; const char *ii = "";
switch (i) switch (i) {
{ case 0: ii="0"; break;
case 0: ii="0"; break; case 30 : ii="30"; break;
case 30 : ii="30"; break; case 60 : ii="60"; break;
case 60 : ii="60"; break; case 90 : ii="90"; break;
case 90 : ii="90"; break; case 120 : ii="120"; break;
case 120 : ii="120"; break; case 150 : ii="150"; break;
case 150 : ii="150"; break; case 180 : ii="180"; break;
case 180 : ii="180"; break; case 210 : ii="210"; break;
case 210 : ii="210"; break; case 240 : ii="240"; break;
case 240 : ii="240"; break; case 270 : ii="270"; break;
case 270 : ii="270"; break; case 300 : ii="300"; break;
case 300 : ii="300"; break; case 330 : ii="330"; break;
case 330 : ii="330"; break; default: break;
default: break;
} }
// Print text centered on position x, y // Print text centered on position x, y
@@ -264,16 +327,16 @@ public:
getdisplay().getTextBounds(ii, int(x), int(y), &x1, &y1, &w, &h); // Calc width of new string getdisplay().getTextBounds(ii, int(x), int(y), &x1, &y1, &w, &h); // Calc width of new string
getdisplay().setCursor(x-w/2, y+h/2); getdisplay().setCursor(x-w/2, y+h/2);
if(i % 30 == 0){ if(i % 30 == 0){
getdisplay().setFont(&Ubuntu_Bold8pt7b); getdisplay().setFont(&Ubuntu_Bold8pt8b);
getdisplay().print(ii); getdisplay().print(ii);
} }
// Draw sub scale with dots // Draw sub scale with dots
float x1c = 200 + rInstrument*sin(i/180.0*pi); float x1c = 200 + rInstrument*sin(i/180.0*M_PI);
float y1c = 150 - rInstrument*cos(i/180.0*pi); float y1c = 150 - rInstrument*cos(i/180.0*M_PI);
getdisplay().fillCircle((int)x1c, (int)y1c, 2, commonData.fgcolor); getdisplay().fillCircle((int)x1c, (int)y1c, 2, commonData->fgcolor);
float sinx=sin(i/180.0*pi); float sinx=sin(i/180.0*M_PI);
float cosx=cos(i/180.0*pi); float cosx=cos(i/180.0*M_PI);
// Draw sub scale with lines (two triangles) // Draw sub scale with lines (two triangles)
if(i % 30 == 0){ if(i % 30 == 0){
@@ -284,10 +347,10 @@ public:
float yy2 = -(rInstrument+10); float yy2 = -(rInstrument+10);
getdisplay().fillTriangle(200+(int)(cosx*xx1-sinx*yy1),150+(int)(sinx*xx1+cosx*yy1), getdisplay().fillTriangle(200+(int)(cosx*xx1-sinx*yy1),150+(int)(sinx*xx1+cosx*yy1),
200+(int)(cosx*xx2-sinx*yy1),150+(int)(sinx*xx2+cosx*yy1), 200+(int)(cosx*xx2-sinx*yy1),150+(int)(sinx*xx2+cosx*yy1),
200+(int)(cosx*xx1-sinx*yy2),150+(int)(sinx*xx1+cosx*yy2),commonData.fgcolor); 200+(int)(cosx*xx1-sinx*yy2),150+(int)(sinx*xx1+cosx*yy2),commonData->fgcolor);
getdisplay().fillTriangle(200+(int)(cosx*xx2-sinx*yy1),150+(int)(sinx*xx2+cosx*yy1), getdisplay().fillTriangle(200+(int)(cosx*xx2-sinx*yy1),150+(int)(sinx*xx2+cosx*yy1),
200+(int)(cosx*xx1-sinx*yy2),150+(int)(sinx*xx1+cosx*yy2), 200+(int)(cosx*xx1-sinx*yy2),150+(int)(sinx*xx1+cosx*yy2),
200+(int)(cosx*xx2-sinx*yy2),150+(int)(sinx*xx2+cosx*yy2),commonData.fgcolor); 200+(int)(cosx*xx2-sinx*yy2),150+(int)(sinx*xx2+cosx*yy2),commonData->fgcolor);
} }
} }
@@ -301,10 +364,10 @@ public:
float xx1 = -startwidth; float xx1 = -startwidth;
float xx2 = startwidth; float xx2 = startwidth;
float yy1 = -startwidth; float yy1 = -startwidth;
float yy2 = -(rInstrument-15); float yy2 = -(rInstrument-15);
getdisplay().fillTriangle(200+(int)(cosx*xx1-sinx*yy1),150+(int)(sinx*xx1+cosx*yy1), getdisplay().fillTriangle(200+(int)(cosx*xx1-sinx*yy1),150+(int)(sinx*xx1+cosx*yy1),
200+(int)(cosx*xx2-sinx*yy1),150+(int)(sinx*xx2+cosx*yy1), 200+(int)(cosx*xx2-sinx*yy1),150+(int)(sinx*xx2+cosx*yy1),
200+(int)(cosx*0-sinx*yy2),150+(int)(sinx*0+cosx*yy2),commonData.fgcolor); 200+(int)(cosx*0-sinx*yy2),150+(int)(sinx*0+cosx*yy2),commonData->fgcolor);
// Inverted pointer // Inverted pointer
// Pointer as triangle with center base 2*width // Pointer as triangle with center base 2*width
float endwidth = 2; // End width of pointer float endwidth = 2; // End width of pointer
@@ -314,60 +377,62 @@ public:
float iy2 = -endwidth; float iy2 = -endwidth;
getdisplay().fillTriangle(200+(int)(cosx*ix1-sinx*iy1),150+(int)(sinx*ix1+cosx*iy1), getdisplay().fillTriangle(200+(int)(cosx*ix1-sinx*iy1),150+(int)(sinx*ix1+cosx*iy1),
200+(int)(cosx*ix2-sinx*iy1),150+(int)(sinx*ix2+cosx*iy1), 200+(int)(cosx*ix2-sinx*iy1),150+(int)(sinx*ix2+cosx*iy1),
200+(int)(cosx*0-sinx*iy2),150+(int)(sinx*0+cosx*iy2),commonData.fgcolor); 200+(int)(cosx*0-sinx*iy2),150+(int)(sinx*0+cosx*iy2),commonData->fgcolor);
} }
// Center circle // Center circle
getdisplay().fillCircle(200, 150, startwidth + 6, commonData.bgcolor); getdisplay().fillCircle(200, 150, startwidth + 6, commonData->bgcolor);
getdisplay().fillCircle(200, 150, startwidth + 4, commonData.fgcolor); getdisplay().fillCircle(200, 150, startwidth + 4, commonData->fgcolor);
//******************************************************************************************* //*******************************************************************************************
// Show values DBT // Show value6 (=fourth user-configured parameter) and ssource, so that they do not collide with the wind pointer
getdisplay().setFont(&DSEG7Classic_BoldItalic16pt7b); if ( cos(value1) > 0){
getdisplay().setCursor(160, 200); //pointer points upwards
getdisplay().print(svalue5); // Value getdisplay().setFont(&DSEG7Classic_BoldItalic16pt7b);
getdisplay().setFont(&Ubuntu_Bold8pt7b); getdisplay().setCursor(160, 200);
getdisplay().setCursor(190, 215); getdisplay().print(svalue6); // Value
getdisplay().print(" "); getdisplay().setFont(&Ubuntu_Bold8pt8b);
if(holdvalues == false){ getdisplay().setCursor(190, 215);
getdisplay().print(unit5); // Unit getdisplay().print(" ");
} if(holdvalues == false){
else{ getdisplay().print(unit6); // Unit
getdisplay().print(unit5old); // Unit }
} else{
getdisplay().print(unit6old); // Unit
}
if (sin(value1)>0){
getdisplay().setCursor(160, 130);
}
else{
getdisplay().setCursor(220, 130);
}
getdisplay().print(ssource); // true or app.
}
else{
// pointer points downwards
getdisplay().setFont(&DSEG7Classic_BoldItalic16pt7b);
getdisplay().setCursor(160, 130);
getdisplay().print(svalue6); // Value
getdisplay().setFont(&Ubuntu_Bold8pt8b);
getdisplay().setCursor(190, 90);
getdisplay().print(" ");
if(holdvalues == false){
getdisplay().print(unit6); // Unit
}
else{
getdisplay().print(unit6old); // Unit
}
if (sin(value1)>0){
getdisplay().setCursor(160, 200);
}
else{
getdisplay().setCursor(220, 200);
}
getdisplay().print(ssource); //true or app.
}
// Show values STW return PAGE_UPDATE;
getdisplay().setFont(&DSEG7Classic_BoldItalic16pt7b);
getdisplay().setCursor(160, 130);
getdisplay().print(svalue6); // Value
getdisplay().setFont(&Ubuntu_Bold8pt7b);
getdisplay().setCursor(190, 90);
getdisplay().print(" ");
if(holdvalues == false){
getdisplay().print(unit6); // Unit
}
else{
getdisplay().print(unit6old); // Unit
}
// Key Layout
getdisplay().setFont(&Ubuntu_Bold8pt7b);
if(keylock == false){
getdisplay().setCursor(130, 290);
getdisplay().print("[ <<<< " + String(commonData.data.actpage) + "/" + String(commonData.data.maxpage) + " >>>> ]");
if(String(backlightMode) == "Control by Key"){ // Key for illumination
getdisplay().setCursor(343, 290);
getdisplay().print("[ILUM]");
}
}
else{
getdisplay().setCursor(130, 290);
getdisplay().print(" [ Keylock active ]");
}
// Update display
getdisplay().nextPage(); // Partial update (fast)
}; };
}; };
@@ -378,15 +443,15 @@ static Page *createPage(CommonData &common){
* with the code below we make this page known to the PageTask * with the code below we make this page known to the PageTask
* we give it a type (name) that can be selected in the config * we give it a type (name) that can be selected in the config
* we define which function is to be called * we define which function is to be called
* and we provide the number of user parameters we expect (0 here) * and we provide the number of user parameters we expect (4 here)
* and will will provide the names of the fixed values we need * and will will provide the names of the fixed values we need
*/ */
PageDescription registerPageWindRoseFlex( PageDescription registerPageWindRoseFlex(
"WindRoseFlex", // Page name "WindRoseFlex", // Page name
createPage, // Action createPage, // Action
6, // Number of bus values depends on selection in Web configuration; was zero 4, // Number of bus values depends on selection in Web configuration
//{"AWA", "AWS", "COG", "SOG", "TWD", "TWS"}, // Bus values we need in the page, modified for WindRose2 {"AWA", "AWS", "TWA", "TWS"}, // fixed values we need in the page. They are inserted AFTER the web-configured values.
true // Show display header on/off true // Show display header on/off
); );
#endif #endif

View File

@@ -1,4 +1,4 @@
#ifdef BOARD_OBP60S3 #if defined BOARD_OBP60S3 || defined BOARD_OBP40S3
#include "Pagedata.h" #include "Pagedata.h"
#include "OBP60Extensions.h" #include "OBP60Extensions.h"
@@ -26,12 +26,17 @@ static unsigned char ship_bits[] PROGMEM = {
0x00, 0xf8, 0x3f, 0x00, 0x00, 0xf8, 0x3f, 0x00, 0x00, 0xf8, 0x3f, 0x00, 0x00, 0xf8, 0x3f, 0x00, 0x00, 0xf8, 0x3f, 0x00, 0x00, 0xf8, 0x3f, 0x00,
0x00, 0xf0, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00 }; 0x00, 0xf0, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00 };
class PageXTETrack : public Page{ class PageXTETrack : public Page
bool keylock = false; // Keylock {
bool simulation = false;
bool holdvalues = false;
public: public:
PageXTETrack(CommonData &common){ PageXTETrack(CommonData &common){
common.logger->logDebug(GwLog::LOG,"Show PageXTETrack"); commonData = &common;
common.logger->logDebug(GwLog::LOG,"Instantiate PageXTETrack");
simulation = common.config->getBool(common.config->useSimuData);
holdvalues = common.config->getBool(common.config->holdvalues);
} }
void drawSegment(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1, void drawSegment(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1,
@@ -52,16 +57,17 @@ class PageXTETrack : public Page{
} }
virtual int handleKey(int key){ virtual int handleKey(int key){
if(key == 11){ // Code for keylock // Code for keylock
keylock = !keylock; // Toggle keylock if(key == 11){
commonData->keylock = !commonData->keylock;
return 0; // Commit the key return 0; // Commit the key
} }
return key; return key;
} }
virtual void displayPage(CommonData &commonData, PageData &pageData){ int displayPage(PageData &pageData){
GwConfigHandler *config = commonData.config; GwConfigHandler *config = commonData->config;
GwLog *logger=commonData.logger; GwLog *logger = commonData->logger;
// Get config data // Get config data
String flashLED = config->getString(config->flashLED); String flashLED = config->getString(config->flashLED);
@@ -85,10 +91,10 @@ class PageXTETrack : public Page{
// Set display in partial refresh mode // Set display in partial refresh mode
getdisplay().setPartialWindow(0, 0, getdisplay().width(), getdisplay().height()); // Set partial update getdisplay().setPartialWindow(0, 0, getdisplay().width(), getdisplay().height()); // Set partial update
getdisplay().setTextColor(commonData.fgcolor); getdisplay().setTextColor(commonData->fgcolor);
// descriptions // descriptions
getdisplay().setFont(&Ubuntu_Bold8pt7b); getdisplay().setFont(&Ubuntu_Bold8pt8b);
getdisplay().setCursor(50, 188); getdisplay().setCursor(50, 188);
getdisplay().print("Cross-track error"); getdisplay().print("Cross-track error");
getdisplay().setCursor(270, 188); getdisplay().setCursor(270, 188);
@@ -105,25 +111,25 @@ class PageXTETrack : public Page{
uint16_t w, h; uint16_t w, h;
GwApi::BoatValue *bv_xte = pageData.values[0]; // XTE GwApi::BoatValue *bv_xte = pageData.values[0]; // XTE
String sval_xte = formatValue(bv_xte, commonData).svalue; String sval_xte = formatValue(bv_xte, *commonData).svalue;
getdisplay().getTextBounds(sval_xte, 0, 0, &x, &y, &w, &h); getdisplay().getTextBounds(sval_xte, 0, 0, &x, &y, &w, &h);
getdisplay().setCursor(160-w, 170); getdisplay().setCursor(160-w, 170);
getdisplay().print(sval_xte); getdisplay().print(sval_xte);
GwApi::BoatValue *bv_cog = pageData.values[1]; // COG GwApi::BoatValue *bv_cog = pageData.values[1]; // COG
String sval_cog = formatValue(bv_cog, commonData).svalue; String sval_cog = formatValue(bv_cog, *commonData).svalue;
getdisplay().getTextBounds(sval_cog, 0, 0, &x, &y, &w, &h); getdisplay().getTextBounds(sval_cog, 0, 0, &x, &y, &w, &h);
getdisplay().setCursor(360-w, 170); getdisplay().setCursor(360-w, 170);
getdisplay().print(sval_cog); getdisplay().print(sval_cog);
GwApi::BoatValue *bv_dtw = pageData.values[2]; // DTW GwApi::BoatValue *bv_dtw = pageData.values[2]; // DTW
String sval_dtw = formatValue(bv_dtw, commonData).svalue; String sval_dtw = formatValue(bv_dtw, *commonData).svalue;
getdisplay().getTextBounds(sval_dtw, 0, 0, &x, &y, &w, &h); getdisplay().getTextBounds(sval_dtw, 0, 0, &x, &y, &w, &h);
getdisplay().setCursor(160-w, 257); getdisplay().setCursor(160-w, 257);
getdisplay().print(sval_dtw); getdisplay().print(sval_dtw);
GwApi::BoatValue *bv_btw = pageData.values[3]; // BTW GwApi::BoatValue *bv_btw = pageData.values[3]; // BTW
String sval_btw = formatValue(bv_btw, commonData).svalue; String sval_btw = formatValue(bv_btw, *commonData).svalue;
getdisplay().getTextBounds(sval_btw, 0, 0, &x, &y, &w, &h); getdisplay().getTextBounds(sval_btw, 0, 0, &x, &y, &w, &h);
getdisplay().setCursor(360-w, 257); getdisplay().setCursor(360-w, 257);
getdisplay().print(sval_btw); getdisplay().print(sval_btw);
@@ -133,16 +139,16 @@ class PageXTETrack : public Page{
// XTETrack view // XTETrack view
// draw ship symbol (as bitmap) // draw ship symbol (as bitmap)
getdisplay().drawXBitmap(184, 68, ship_bits, ship_width, ship_height, commonData.fgcolor); getdisplay().drawXBitmap(184, 68, ship_bits, ship_width, ship_height, commonData->fgcolor);
// draw next waypoint name // draw next waypoint name
String sval_wpname = "no data"; String sval_wpname = "no data";
if (valid) { if (valid) {
sval_wpname = "Tonne 122"; sval_wpname = "Tonne 122";
} }
getdisplay().setFont(&Ubuntu_Bold10pt7b); getdisplay().setFont(&Ubuntu_Bold10pt8b);
getdisplay().getTextBounds(sval_wpname, 0, 150, &x, &y, &w, &h); getdisplay().getTextBounds(sval_wpname, 0, 150, &x, &y, &w, &h);
// TODO if text don't fix use smaller font size. // TODO if text don't fix use smaller font size.
// if smallest size does not fit use 2 lines // if smallest size does not fit use 2 lines
@@ -152,7 +158,12 @@ class PageXTETrack : public Page{
// draw course segments // draw course segments
double diff = bv_cog->value - bv_btw->value; double diff;
if (!simulation) {
diff = bv_cog->value - bv_btw->value;
} else {
diff = 7.0;
}
if (diff < -180) { if (diff < -180) {
diff += 360; diff += 360;
} else if (diff > 180) { } else if (diff > 180) {
@@ -188,32 +199,15 @@ class PageXTETrack : public Page{
} }
// left segments // left segments
drawSegment(0, 54, 46, 24, 75, 24, 0, 90, commonData.fgcolor, seg[2]); drawSegment(0, 54, 46, 24, 75, 24, 0, 90, commonData->fgcolor, seg[2]);
drawSegment(0, 100, 82, 24, 112, 24, 50, 100, commonData.fgcolor, seg[1]); drawSegment(0, 100, 82, 24, 112, 24, 50, 100, commonData->fgcolor, seg[1]);
drawSegment(60, 100, 117, 24, 147, 24, 110, 100, commonData.fgcolor,seg[0]); drawSegment(60, 100, 117, 24, 147, 24, 110, 100, commonData->fgcolor,seg[0]);
// right segments // right segments
drawSegment(340, 100, 283, 24, 253, 24, 290, 100, commonData.fgcolor, seg[3]); drawSegment(340, 100, 283, 24, 253, 24, 290, 100, commonData->fgcolor, seg[3]);
drawSegment(399, 100, 318, 24, 289, 24, 350, 100, commonData.fgcolor, seg[4]); drawSegment(399, 100, 318, 24, 289, 24, 350, 100, commonData->fgcolor, seg[4]);
drawSegment(399, 54, 354, 24, 325, 24, 399, 90, commonData.fgcolor, seg[5]); drawSegment(399, 54, 354, 24, 325, 24, 399, 90, commonData->fgcolor, seg[5]);
// Key Layout
getdisplay().setFont(&Ubuntu_Bold8pt7b);
if(keylock == false){
getdisplay().setCursor(130, 296);
getdisplay().print("[ <<<< " + String(commonData.data.actpage) + "/" + String(commonData.data.maxpage) + " >>>> ]");
if(String(backlightMode) == "Control by Key"){ // Key for illumination
getdisplay().setCursor(343, 296);
getdisplay().print("[ILUM]");
}
}
else{
getdisplay().setCursor(130, 296);
getdisplay().print(" [ Keylock active ]");
}
// Update display
getdisplay().nextPage(); // Partial update (fast)
return PAGE_UPDATE;
}; };
}; };
@@ -229,11 +223,11 @@ static Page* createPage(CommonData &common){
* this will be number of BoatValue pointers in pageData.values * this will be number of BoatValue pointers in pageData.values
*/ */
PageDescription registerPageXTETrack( PageDescription registerPageXTETrack(
"XTETrack", // Page name "XTETrack", // Page name
createPage, // Action createPage, // Action
0, // Number of bus values depends on selection in Web configuration 0, // Number of bus values depends on selection in Web configuration
{"XTE", "COG", "DTW", "BTW"}, // Bus values we need in the page {"XTE", "COG", "DTW", "BTW"}, // Bus values we need in the page
true // Show display header on/off true // Show display header on/off
); );
#endif #endif

View File

@@ -3,12 +3,20 @@
#include "GwApi.h" #include "GwApi.h"
#include <functional> #include <functional>
#include <vector> #include <vector>
#include "LedSpiTask.h"
#include "OBPDataOperations.h"
#define MAX_PAGE_NUMBER 10 // Max number of pages for show data
typedef std::vector<GwApi::BoatValue *> ValueList; typedef std::vector<GwApi::BoatValue *> ValueList;
typedef struct{ typedef struct{
GwApi *api;
String pageName; String pageName;
uint8_t pageNumber; // page number in sequence of visible pages
//the values will always contain the user defined values first //the values will always contain the user defined values first
ValueList values; ValueList values;
HstryBuf* boatHstry;
} PageData; } PageData;
// Sensor data structure (only for extended sensors, not for NMEA bus sensors) // Sensor data structure (only for extended sensors, not for NMEA bus sensors)
@@ -27,6 +35,8 @@ typedef struct{
double batteryVoltage300 = 0; // Sliding average over 300 values double batteryVoltage300 = 0; // Sliding average over 300 values
double batteryCurrent300 = 0; double batteryCurrent300 = 0;
double batteryPower300 = 0; double batteryPower300 = 0;
double batteryLevelLiPo = 0; // Battery level for OBP40 LiPo accu
int BatteryChargeStatus = 0; // LiPo charge status: 0 = discharge, 1 = loading activ
double solarVoltage = 0; double solarVoltage = 0;
double solarCurrent = 0; double solarCurrent = 0;
double solarPower = 0; double solarPower = 0;
@@ -37,14 +47,10 @@ typedef struct{
double airHumidity = 0; double airHumidity = 0;
double airPressure = 0; double airPressure = 0;
double onewireTemp[8] = {0,0,0,0,0,0,0,0}; double onewireTemp[8] = {0,0,0,0,0,0,0,0};
double rotationAngle = 0; // Rotation angle in radiant double rotationAngle = 0; // Rotation angle in radiant
bool validRotAngle = false; // Valid flag magnet present for rotation sensor bool validRotAngle = false; // Valid flag magnet present for rotation sensor
int rtcYear = 0; // UTC time struct tm rtcTime; // UTC time from internal RTC
int rtcMonth = 0; bool rtcValid = false;
int rtcDay = 0;
int rtcHour = 0;
int rtcMinute = 0;
int rtcSecond = 0;
int sunsetHour = 0; int sunsetHour = 0;
int sunsetMinute = 0; int sunsetMinute = 0;
int sunriseHour = 0; int sunriseHour = 0;
@@ -61,22 +67,82 @@ typedef struct{
} SunData; } SunData;
typedef struct{ typedef struct{
GwApi::Status status; String label = "";
GwLog *logger=NULL; bool selected = false; // for virtual keyboard function
GwConfigHandler *config=NULL; uint16_t x;
SensorData data; uint16_t y;
SunData sundata; uint16_t w;
GwApi::BoatValue *time=NULL; uint16_t h;
GwApi::BoatValue *date=NULL; } TouchKeyData;
uint16_t fgcolor;
uint16_t bgcolor; typedef struct{
Color color; // red, orange, yellow, green, blue, aqua, violet, white
BacklightMode mode; // off, on, sun, bus, time, key
uint8_t brightness; // 0% (off), user setting from 20% to 100% full power
bool on; // fast on/off detector
} BacklightData;
enum AlarmSource {
Alarm_Generic,
Alarm_Local,
Alarm_NMEA0183,
Alarm_NMEA2000
};
typedef struct{
uint8_t id; // alarm-id e.g. 01..99 from NMEA0183
AlarmSource source;
String message; // single line of plain text
bool active = false;
uint8_t signal; // how to signal MESSAGE | LED | BUZZER
uint8_t length_sec; // seconds until alarm disappeares without user interaction
} AlarmData;
typedef struct{
GwApi::Status status;
GwLog *logger = nullptr;
GwConfigHandler *config = nullptr;
SensorData data;
SunData sundata;
TouchKeyData keydata[6];
BacklightData backlight;
AlarmData alarm;
GwApi::BoatValue *time = nullptr;
GwApi::BoatValue *date = nullptr;
uint16_t fgcolor;
uint16_t bgcolor;
bool keylock = false;
String powermode;
} CommonData; } CommonData;
//a base class that all pages must inherit from //a base class that all pages must inherit from
class Page{ class Page{
protected:
CommonData *commonData;
public: public:
virtual void displayPage(CommonData &commonData, PageData &pageData)=0; int refreshtime = 1000;
virtual void displayNew(CommonData &commonData, PageData &pageData){} virtual int displayPage(PageData &pageData)=0;
virtual void displayNew(PageData &pageData){}
virtual void leavePage(PageData &pageData){}
virtual void setupKeys() {
#ifdef HARDWARE_V21
commonData->keydata[0].label = "";
commonData->keydata[1].label = "";
commonData->keydata[2].label = "#LEFT";
commonData->keydata[3].label = "#RIGHT";
commonData->keydata[4].label = "";
if ((commonData->backlight.mode == KEY) && !(commonData->powermode == "Min Power")) {
commonData->keydata[5].label = "ILUM";
} else {
commonData->keydata[5].label = "";
}
#endif
#ifdef BOARD_OBP40S3
commonData->keydata[0].label = "";
commonData->keydata[1].label = "";
#endif
}
//return -1 if handled by the page //return -1 if handled by the page
virtual int handleKey(int key){return key;} virtual int handleKey(int key){return key;}
}; };
@@ -114,13 +180,25 @@ class PageDescription{
} }
}; };
// Structure for formated boat values class PageStruct{
public:
Page *page = nullptr;
PageData parameters;
PageDescription *description = nullptr;
};
// Standard format functions without overhead
String formatDate(String fmttype, uint16_t year, uint8_t month, uint8_t day);
String formatTime(char fmttype, uint8_t hour, uint8_t minute, uint8_t second);
String formatLatitude(double lat);
String formatLongitude(double lon);
// Structure for formatted boat values
typedef struct{ typedef struct{
double value; double value;
String svalue; String svalue;
String unit; String unit;
} FormatedData; } FormattedData;
// Formatter for boat values
// Formater for boat values FormattedData formatValue(GwApi::BoatValue *value, CommonData &commondata);
FormatedData formatValue(GwApi::BoatValue *value, CommonData &commondata);

View File

@@ -1,232 +0,0 @@
const uint8_t Ubuntu_Bold10pt7bBitmaps[] PROGMEM = {
0xFF, 0xFF, 0xFF, 0xE3, 0xFF, 0xC0, 0xEF, 0xDF, 0xBF, 0x7E, 0xFD, 0xC0,
0x0E, 0xE0, 0xEE, 0x1D, 0xCF, 0xFF, 0xFF, 0xF1, 0xDC, 0x1D, 0xC3, 0xB8,
0xFF, 0xFF, 0xFF, 0x3B, 0x83, 0xB8, 0x77, 0x07, 0x70, 0x1C, 0x0E, 0x0F,
0xCF, 0xEE, 0x07, 0x03, 0x81, 0xFC, 0x7F, 0x0F, 0xC0, 0xE0, 0x74, 0x3F,
0xF9, 0xF8, 0x38, 0x1C, 0x00, 0x38, 0x38, 0x7C, 0x70, 0xC6, 0x70, 0xC6,
0xE0, 0xC6, 0xE0, 0xC7, 0xC0, 0x7D, 0xDC, 0x3B, 0xBE, 0x03, 0xE3, 0x07,
0x63, 0x07, 0x63, 0x0E, 0x63, 0x0E, 0x3E, 0x1C, 0x1C, 0x1E, 0x01, 0xF8,
0x1F, 0xE0, 0xE7, 0x07, 0x38, 0x1F, 0x80, 0xF8, 0x0F, 0xCE, 0xEF, 0x77,
0x3F, 0x38, 0xF9, 0xFF, 0xC7, 0xFF, 0x1F, 0xBC, 0xFF, 0xFF, 0xC0, 0x08,
0x73, 0x8E, 0x71, 0xCE, 0x38, 0xE3, 0x8E, 0x38, 0xE3, 0x87, 0x1C, 0x38,
0xE1, 0xC2, 0x43, 0x87, 0x1C, 0x38, 0xE1, 0xC7, 0x1C, 0x71, 0xC7, 0x1C,
0x73, 0x8E, 0x71, 0xCE, 0x10, 0x1C, 0x0E, 0x17, 0x5F, 0xFF, 0xF9, 0xB1,
0xDC, 0x6C, 0x0E, 0x01, 0xC0, 0x38, 0x07, 0x0F, 0xFF, 0xFF, 0xFF, 0xF8,
0x70, 0x0E, 0x01, 0xC0, 0x38, 0x00, 0x77, 0x77, 0xEE, 0xFF, 0xFF, 0xC0,
0x6F, 0xF6, 0x01, 0xC0, 0xE0, 0x38, 0x0E, 0x07, 0x01, 0xC0, 0x70, 0x38,
0x0E, 0x03, 0x81, 0xC0, 0x70, 0x1C, 0x0E, 0x03, 0x80, 0xE0, 0x70, 0x1C,
0x07, 0x03, 0x80, 0x1C, 0x3F, 0x9F, 0xDE, 0xFE, 0x3F, 0x1F, 0x8F, 0xC7,
0xE3, 0xF1, 0xFD, 0xEF, 0xE7, 0xF0, 0xE0, 0x0E, 0x3D, 0xFF, 0xF6, 0xE1,
0xC3, 0x87, 0x0E, 0x1C, 0x38, 0x70, 0xE1, 0xC0, 0x3E, 0x7F, 0xBF, 0xEC,
0x70, 0x38, 0x1C, 0x1C, 0x1C, 0x1C, 0x1C, 0x1C, 0x1F, 0xFF, 0xFF, 0xFC,
0x3C, 0x7F, 0x3F, 0xC8, 0xE0, 0x70, 0x30, 0xF0, 0x7E, 0x07, 0x81, 0xE0,
0xFF, 0xFF, 0xF3, 0xF0, 0x07, 0x07, 0x87, 0xC3, 0xE3, 0xF3, 0xB9, 0x9D,
0xCE, 0xFF, 0xFF, 0xFF, 0xE0, 0xE0, 0x70, 0x38, 0x3F, 0x1F, 0x8F, 0xC7,
0x03, 0x83, 0xF1, 0xFC, 0xFF, 0x07, 0x81, 0xC0, 0xFF, 0xFF, 0xF3, 0xE0,
0x07, 0x0F, 0x8F, 0xCF, 0x07, 0x07, 0xF3, 0xFD, 0xFF, 0xE3, 0xF1, 0xF8,
0xEF, 0xF7, 0xF1, 0xF0, 0xFF, 0xFF, 0xFF, 0xE0, 0xE0, 0xE0, 0x70, 0x70,
0x38, 0x38, 0x1C, 0x0E, 0x0E, 0x07, 0x03, 0x80, 0x3E, 0x3F, 0xBF, 0xFC,
0x7E, 0x3F, 0xB9, 0xF8, 0xFE, 0xE7, 0xF1, 0xF8, 0xFF, 0xF7, 0xF1, 0xF0,
0x3E, 0x3F, 0xBF, 0xDC, 0x7E, 0x3F, 0x1F, 0xFE, 0xFF, 0x3F, 0x83, 0xC3,
0xCF, 0xC7, 0xC3, 0x80, 0x6F, 0xF6, 0x00, 0x06, 0xFF, 0x60, 0x33, 0xDE,
0x60, 0x00, 0x00, 0x73, 0x9C, 0xEE, 0x70, 0x01, 0x87, 0xEF, 0xFF, 0xF8,
0xE0, 0x3F, 0x8F, 0xFC, 0x7E, 0x01, 0x80, 0xFF, 0xFF, 0xFF, 0xE0, 0x00,
0x07, 0xFF, 0xFF, 0xFF, 0x60, 0x3E, 0x3F, 0xE3, 0xF0, 0x38, 0xFF, 0xFE,
0xF8, 0x60, 0x00, 0x7C, 0xFE, 0xFF, 0x07, 0x07, 0x07, 0x0E, 0x1E, 0x3C,
0x38, 0x38, 0x00, 0x30, 0x78, 0x78, 0x30, 0x03, 0xF0, 0x07, 0xFE, 0x0F,
0x03, 0x86, 0x00, 0xE6, 0x1F, 0xB7, 0x3F, 0xCF, 0x3C, 0xE7, 0x9C, 0x73,
0xCE, 0x39, 0xE7, 0x1C, 0xF3, 0xCE, 0x5C, 0xFF, 0xE6, 0x3F, 0xE3, 0x80,
0x00, 0xF0, 0x00, 0x3F, 0xE0, 0x07, 0xF8, 0x00, 0x03, 0x80, 0x0F, 0x80,
0x1F, 0x00, 0x77, 0x00, 0xEE, 0x03, 0xDE, 0x07, 0x1C, 0x1E, 0x3C, 0x3F,
0xF8, 0x7F, 0xF1, 0xFF, 0xF3, 0x80, 0xE7, 0x01, 0xDC, 0x01, 0xC0, 0xFE,
0x3F, 0xCF, 0xFB, 0x8E, 0xE3, 0xBF, 0xCF, 0xF3, 0xFE, 0xE1, 0xF8, 0x7E,
0x1F, 0xFF, 0xFF, 0xBF, 0x80, 0x0F, 0xC7, 0xFD, 0xFF, 0xBC, 0x2F, 0x01,
0xC0, 0x38, 0x07, 0x00, 0xE0, 0x1E, 0x01, 0xE0, 0x3F, 0xF3, 0xFE, 0x1F,
0x80, 0xFF, 0x0F, 0xFC, 0xFF, 0xEE, 0x1E, 0xE0, 0xFE, 0x07, 0xE0, 0x7E,
0x07, 0xE0, 0x7E, 0x0F, 0xE1, 0xEF, 0xFE, 0xFF, 0xCF, 0xF0, 0xFF, 0xFF,
0xFF, 0xFC, 0x0E, 0x07, 0xFB, 0xFD, 0xFE, 0xE0, 0x70, 0x38, 0x1F, 0xFF,
0xFF, 0xFC, 0xFF, 0xFF, 0xFF, 0xE0, 0xE0, 0xFE, 0xFE, 0xFE, 0xE0, 0xE0,
0xE0, 0xE0, 0xE0, 0xE0, 0x0F, 0xC7, 0xFD, 0xFF, 0xBC, 0x2F, 0x01, 0xC0,
0x38, 0x07, 0x07, 0xE0, 0xFE, 0x1D, 0xE3, 0xBF, 0xF3, 0xFE, 0x1F, 0x80,
0xE0, 0xFC, 0x1F, 0x83, 0xF0, 0x7E, 0x0F, 0xFF, 0xFF, 0xFF, 0xFF, 0xE0,
0xFC, 0x1F, 0x83, 0xF0, 0x7E, 0x0F, 0xC1, 0xC0, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xC0, 0x03, 0x81, 0xC0, 0xE0, 0x70, 0x38, 0x1C, 0x0E, 0x07, 0x03,
0x81, 0xD1, 0xEF, 0xFF, 0xF3, 0xF0, 0xE0, 0xFE, 0x1E, 0xE3, 0xCE, 0x78,
0xEF, 0x0F, 0xE0, 0xFC, 0x0F, 0xC0, 0xEE, 0x0E, 0x70, 0xE7, 0x8E, 0x3C,
0xE1, 0xEE, 0x0F, 0xE0, 0x70, 0x38, 0x1C, 0x0E, 0x07, 0x03, 0x81, 0xC0,
0xE0, 0x70, 0x38, 0x1F, 0xFF, 0xFF, 0xFC, 0x70, 0x1C, 0xF0, 0x79, 0xE0,
0xF3, 0xE3, 0xE7, 0xC7, 0xDD, 0x8D, 0xFB, 0xBB, 0xF3, 0x67, 0xE7, 0xCF,
0xCF, 0x9F, 0x8E, 0x3F, 0x1C, 0x7E, 0x00, 0xFC, 0x01, 0xC0, 0xE0, 0xFE,
0x1F, 0xC3, 0xFC, 0x7F, 0xCF, 0xD9, 0xFB, 0xBF, 0x3F, 0xE7, 0xFC, 0x7F,
0x87, 0xF0, 0xFE, 0x0F, 0xC1, 0xC0, 0x0F, 0xC0, 0xFF, 0xC7, 0xFF, 0x9E,
0x1E, 0xF0, 0x3F, 0x80, 0x7E, 0x01, 0xF8, 0x07, 0xE0, 0x1F, 0xC0, 0xF7,
0x87, 0x9F, 0xFE, 0x3F, 0xF0, 0x3F, 0x00, 0xFE, 0x3F, 0xEF, 0xFF, 0x87,
0xE1, 0xF8, 0x7F, 0xFF, 0xFE, 0xFE, 0x38, 0x0E, 0x03, 0x80, 0xE0, 0x38,
0x00, 0x0F, 0xC0, 0xFF, 0xC7, 0xFF, 0x9E, 0x1E, 0xF0, 0x3F, 0x80, 0x7E,
0x01, 0xF8, 0x07, 0xE0, 0x1F, 0xC0, 0xF7, 0x87, 0x9F, 0xFE, 0x3F, 0xF0,
0x3F, 0x00, 0x3C, 0x00, 0x7E, 0x00, 0x78, 0xFE, 0x1F, 0xF3, 0xFF, 0x70,
0xEE, 0x1D, 0xC3, 0xBF, 0xF7, 0xFC, 0xFF, 0x1C, 0xF3, 0x8E, 0x70, 0xEE,
0x1D, 0xC1, 0xC0, 0x3F, 0x1F, 0xEF, 0xFB, 0x80, 0xE0, 0x3E, 0x07, 0xF0,
0x7E, 0x03, 0xC0, 0x74, 0x1F, 0xFF, 0xFF, 0x9F, 0xC0, 0xFF, 0xFF, 0xFF,
0xFF, 0x87, 0x00, 0xE0, 0x1C, 0x03, 0x80, 0x70, 0x0E, 0x01, 0xC0, 0x38,
0x07, 0x00, 0xE0, 0x1C, 0x00, 0xE0, 0xFC, 0x1F, 0x83, 0xF0, 0x7E, 0x0F,
0xC1, 0xF8, 0x3F, 0x07, 0xE0, 0xFC, 0x1F, 0xC7, 0xBF, 0xE7, 0xFC, 0x3E,
0x00, 0xE0, 0x0E, 0xE0, 0x39, 0xC0, 0x71, 0xC1, 0xC3, 0x83, 0x87, 0x07,
0x07, 0x1C, 0x0E, 0x38, 0x0E, 0xE0, 0x1D, 0xC0, 0x1F, 0x00, 0x3E, 0x00,
0x7C, 0x00, 0x70, 0x00, 0xE0, 0x00, 0xFC, 0x1C, 0x1D, 0xC3, 0x87, 0x38,
0x78, 0xE7, 0x1F, 0x1C, 0xE3, 0x63, 0x8E, 0x6C, 0xE1, 0xDD, 0xDC, 0x3B,
0xBB, 0x83, 0x63, 0x60, 0x7C, 0x7C, 0x0F, 0x8F, 0x81, 0xE0, 0xF0, 0x1C,
0x1C, 0x00, 0xF0, 0x3D, 0xE1, 0xE3, 0x87, 0x07, 0x38, 0x1F, 0xE0, 0x3F,
0x00, 0x78, 0x01, 0xE0, 0x0F, 0xC0, 0x7F, 0x81, 0xCE, 0x0E, 0x1C, 0x78,
0x7B, 0xC0, 0xF0, 0xE0, 0x3B, 0x83, 0x9C, 0x1C, 0x71, 0xC3, 0xDE, 0x0E,
0xE0, 0x3E, 0x01, 0xF0, 0x07, 0x00, 0x38, 0x01, 0xC0, 0x0E, 0x00, 0x70,
0x03, 0x80, 0xFF, 0xFF, 0xFF, 0xFC, 0x0E, 0x07, 0x03, 0x80, 0xE0, 0x70,
0x38, 0x1E, 0x07, 0x03, 0xFF, 0xFF, 0xFF, 0xF0, 0xFF, 0xFF, 0xCE, 0x73,
0x9C, 0xE7, 0x39, 0xCE, 0x73, 0x9C, 0xE7, 0xFF, 0xF0, 0xE0, 0x1C, 0x07,
0x01, 0xC0, 0x38, 0x0E, 0x03, 0x80, 0x70, 0x1C, 0x07, 0x00, 0xE0, 0x38,
0x0E, 0x01, 0xC0, 0x70, 0x1C, 0x03, 0x80, 0xE0, 0x38, 0x07, 0xFF, 0xFE,
0x73, 0x9C, 0xE7, 0x39, 0xCE, 0x73, 0x9C, 0xE7, 0x3F, 0xFF, 0xF0, 0x0E,
0x03, 0xE0, 0x7C, 0x1D, 0xC7, 0xBC, 0xE3, 0xB8, 0x3B, 0x06, 0xFF, 0xFF,
0xF0, 0x47, 0x1E, 0x20, 0x7E, 0x3F, 0x9F, 0xE0, 0x73, 0xFB, 0xFF, 0x8F,
0xC7, 0xFF, 0xBF, 0xCF, 0xE0, 0xE0, 0x38, 0x0E, 0x03, 0x80, 0xE0, 0x3F,
0xCF, 0xFB, 0xFE, 0xE3, 0xF8, 0x7E, 0x1F, 0x87, 0xE3, 0xFF, 0xEF, 0xFB,
0xF8, 0x1F, 0x3F, 0x7F, 0xF0, 0xE0, 0xE0, 0xE0, 0xF0, 0x7F, 0x7F, 0x1F,
0x01, 0xC0, 0x70, 0x1C, 0x07, 0x01, 0xC7, 0xF7, 0xFD, 0xFF, 0xF1, 0xF8,
0x7E, 0x1F, 0x87, 0xF1, 0xDF, 0xF7, 0xFC, 0x7F, 0x1F, 0x1F, 0xE7, 0xFF,
0x87, 0xFF, 0xFF, 0xFE, 0x03, 0xC0, 0x7F, 0x9F, 0xE1, 0xF8, 0x3F, 0x7E,
0xFE, 0xE0, 0xE0, 0xFE, 0xFE, 0xFE, 0xE0, 0xE0, 0xE0, 0xE0, 0xE0, 0xE0,
0xE0, 0xE0, 0x1F, 0xDF, 0xF7, 0xFF, 0xC7, 0xE1, 0xF8, 0x7F, 0x1F, 0xFF,
0x7F, 0xCF, 0xF0, 0x1C, 0x0F, 0x7F, 0x9F, 0xE7, 0xE0, 0xE0, 0x38, 0x0E,
0x03, 0x80, 0xE0, 0x3F, 0xCF, 0xFB, 0xFF, 0xE3, 0xF8, 0x7E, 0x1F, 0x87,
0xE1, 0xF8, 0x7E, 0x1F, 0x87, 0xFF, 0xF1, 0xFF, 0xFF, 0xFF, 0xFF, 0x1C,
0x71, 0xC7, 0x00, 0x71, 0xC7, 0x1C, 0x71, 0xC7, 0x1C, 0x71, 0xC7, 0x1F,
0xFF, 0xBC, 0xE0, 0x1C, 0x03, 0x80, 0x70, 0x0E, 0x01, 0xC3, 0xB8, 0xE7,
0x38, 0xEE, 0x1F, 0x83, 0xF8, 0x77, 0x8E, 0x79, 0xC7, 0x38, 0x77, 0x07,
0xE7, 0x39, 0xCE, 0x73, 0x9C, 0xE7, 0x39, 0xCE, 0x7F, 0xE7, 0xFE, 0xF9,
0xFF, 0xFB, 0xFF, 0xFF, 0x1C, 0x7E, 0x38, 0xFC, 0x71, 0xF8, 0xE3, 0xF1,
0xC7, 0xE3, 0x8F, 0xC7, 0x1F, 0x8E, 0x38, 0xFF, 0x3F, 0xEF, 0xFF, 0x8F,
0xE1, 0xF8, 0x7E, 0x1F, 0x87, 0xE1, 0xF8, 0x7E, 0x1C, 0x1E, 0x1F, 0xE7,
0xFB, 0xCF, 0xE1, 0xF8, 0x7E, 0x1F, 0xCF, 0x7F, 0x9F, 0xE1, 0xE0, 0xFE,
0x3F, 0xEF, 0xFB, 0x8F, 0xE1, 0xF8, 0x7E, 0x1F, 0x8F, 0xFF, 0xBF, 0xEF,
0xF3, 0x80, 0xE0, 0x38, 0x0E, 0x00, 0x1F, 0xDF, 0xF7, 0xFF, 0xC7, 0xE1,
0xF8, 0x7E, 0x1F, 0xC7, 0x7F, 0xDF, 0xF3, 0xFC, 0x07, 0x01, 0xC0, 0x70,
0x1C, 0x7F, 0xFF, 0xFF, 0x0E, 0x1C, 0x38, 0x70, 0xE1, 0xC3, 0x80, 0x3E,
0xFE, 0xE0, 0xE0, 0xF8, 0x7E, 0x1F, 0x07, 0x87, 0xFF, 0xFC, 0xE1, 0xC3,
0x87, 0xFF, 0xFF, 0xF8, 0x70, 0xE1, 0xC3, 0x87, 0xF7, 0xE7, 0xC0, 0xE1,
0xF8, 0x7E, 0x1F, 0x87, 0xE1, 0xF8, 0x7E, 0x1F, 0xC7, 0xFF, 0xDF, 0xF3,
0xFC, 0xE0, 0xFE, 0x1D, 0xC7, 0x38, 0xE7, 0xBC, 0x77, 0x0E, 0xE1, 0xFC,
0x1F, 0x03, 0xE0, 0x38, 0x00, 0xE3, 0x8F, 0xC7, 0x1D, 0x8E, 0x33, 0x9E,
0xE7, 0x7D, 0xCE, 0xDB, 0x8D, 0xB6, 0x1F, 0x7C, 0x3C, 0x78, 0x38, 0xE0,
0x71, 0xC0, 0xF1, 0xEF, 0x78, 0xEE, 0x1F, 0xC1, 0xF0, 0x1C, 0x07, 0xC1,
0xFC, 0x3B, 0x8F, 0x7B, 0xC7, 0x80, 0xE0, 0xFC, 0x1D, 0xC7, 0x38, 0xE7,
0x1C, 0x77, 0x0E, 0xE1, 0xFC, 0x1F, 0x03, 0xE0, 0x7C, 0x0F, 0x0F, 0xE1,
0xF8, 0x3E, 0x00, 0xFF, 0xFF, 0xFF, 0x0E, 0x1C, 0x38, 0x38, 0x70, 0xFF,
0xFF, 0xFF, 0x0E, 0x3C, 0xF9, 0xC3, 0x87, 0x0E, 0x1C, 0x79, 0xE3, 0xC7,
0xC3, 0x87, 0x0E, 0x1C, 0x38, 0x7C, 0xF8, 0x70, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xF0, 0xE1, 0xE3, 0xE1, 0xC3, 0x87, 0x0E, 0x1C, 0x3C,
0x3C, 0x79, 0xF3, 0x87, 0x0E, 0x1C, 0x39, 0xF3, 0xE7, 0x00, 0x30, 0x9F,
0x3F, 0xFF, 0x3E, 0x43, 0x00 };
const GFXglyph Ubuntu_Bold10pt7bGlyphs[] PROGMEM = {
{ 0, 0, 0, 5, 0, 1 }, // 0x20 ' '
{ 0, 3, 14, 5, 1, -13 }, // 0x21 '!'
{ 6, 7, 6, 9, 1, -14 }, // 0x22 '"'
{ 12, 12, 14, 14, 1, -13 }, // 0x23 '#'
{ 33, 9, 17, 11, 1, -14 }, // 0x24 '$'
{ 53, 16, 14, 18, 1, -13 }, // 0x25 '%'
{ 81, 13, 14, 14, 1, -13 }, // 0x26 '&'
{ 104, 3, 6, 5, 1, -14 }, // 0x27 '''
{ 107, 6, 20, 7, 1, -15 }, // 0x28 '('
{ 122, 6, 20, 7, 0, -15 }, // 0x29 ')'
{ 137, 9, 8, 10, 1, -13 }, // 0x2A '*'
{ 146, 11, 11, 13, 1, -11 }, // 0x2B '+'
{ 162, 4, 6, 5, 0, -2 }, // 0x2C ','
{ 165, 6, 3, 8, 1, -7 }, // 0x2D '-'
{ 168, 4, 4, 6, 1, -3 }, // 0x2E '.'
{ 170, 10, 20, 9, -1, -15 }, // 0x2F '/'
{ 195, 9, 14, 11, 1, -13 }, // 0x30 '0'
{ 211, 7, 14, 11, 1, -13 }, // 0x31 '1'
{ 224, 9, 14, 11, 1, -13 }, // 0x32 '2'
{ 240, 9, 14, 11, 1, -13 }, // 0x33 '3'
{ 256, 9, 14, 11, 1, -13 }, // 0x34 '4'
{ 272, 9, 14, 11, 1, -13 }, // 0x35 '5'
{ 288, 9, 14, 11, 1, -13 }, // 0x36 '6'
{ 304, 9, 14, 11, 1, -13 }, // 0x37 '7'
{ 320, 9, 14, 11, 1, -13 }, // 0x38 '8'
{ 336, 9, 14, 11, 1, -13 }, // 0x39 '9'
{ 352, 4, 11, 6, 1, -10 }, // 0x3A ':'
{ 358, 5, 14, 6, 0, -10 }, // 0x3B ';'
{ 367, 10, 9, 11, 1, -9 }, // 0x3C '<'
{ 379, 9, 8, 11, 1, -9 }, // 0x3D '='
{ 388, 9, 9, 11, 1, -9 }, // 0x3E '>'
{ 399, 8, 16, 9, 0, -15 }, // 0x3F '?'
{ 415, 17, 17, 19, 1, -13 }, // 0x40 '@'
{ 452, 15, 14, 15, 0, -13 }, // 0x41 'A'
{ 479, 10, 14, 13, 2, -13 }, // 0x42 'B'
{ 497, 11, 14, 13, 1, -13 }, // 0x43 'C'
{ 517, 12, 14, 15, 2, -13 }, // 0x44 'D'
{ 538, 9, 14, 12, 2, -13 }, // 0x45 'E'
{ 554, 8, 14, 11, 2, -13 }, // 0x46 'F'
{ 568, 11, 14, 14, 1, -13 }, // 0x47 'G'
{ 588, 11, 14, 15, 2, -13 }, // 0x48 'H'
{ 608, 3, 14, 7, 2, -13 }, // 0x49 'I'
{ 614, 9, 14, 11, 0, -13 }, // 0x4A 'J'
{ 630, 12, 14, 14, 2, -13 }, // 0x4B 'K'
{ 651, 9, 14, 11, 2, -13 }, // 0x4C 'L'
{ 667, 15, 14, 17, 1, -13 }, // 0x4D 'M'
{ 694, 11, 14, 15, 2, -13 }, // 0x4E 'N'
{ 714, 14, 14, 16, 1, -13 }, // 0x4F 'O'
{ 739, 10, 14, 13, 2, -13 }, // 0x50 'P'
{ 757, 14, 17, 16, 1, -13 }, // 0x51 'Q'
{ 787, 11, 14, 13, 2, -13 }, // 0x52 'R'
{ 807, 10, 14, 12, 1, -13 }, // 0x53 'S'
{ 825, 11, 14, 11, 0, -13 }, // 0x54 'T'
{ 845, 11, 14, 15, 2, -13 }, // 0x55 'U'
{ 865, 15, 14, 15, 0, -13 }, // 0x56 'V'
{ 892, 19, 14, 19, 0, -13 }, // 0x57 'W'
{ 926, 14, 14, 14, 0, -13 }, // 0x58 'X'
{ 951, 13, 14, 13, 0, -13 }, // 0x59 'Y'
{ 974, 10, 14, 12, 1, -13 }, // 0x5A 'Z'
{ 992, 5, 20, 7, 2, -15 }, // 0x5B '['
{ 1005, 10, 20, 9, -1, -15 }, // 0x5C '\'
{ 1030, 5, 20, 7, 0, -15 }, // 0x5D ']'
{ 1043, 11, 8, 11, 0, -13 }, // 0x5E '^'
{ 1054, 10, 2, 10, 0, 3 }, // 0x5F '_'
{ 1057, 5, 4, 6, 1, -15 }, // 0x60 '`'
{ 1060, 9, 11, 11, 1, -10 }, // 0x61 'a'
{ 1073, 10, 16, 12, 1, -15 }, // 0x62 'b'
{ 1093, 8, 11, 10, 1, -10 }, // 0x63 'c'
{ 1104, 10, 16, 12, 1, -15 }, // 0x64 'd'
{ 1124, 10, 11, 12, 1, -10 }, // 0x65 'e'
{ 1138, 8, 16, 8, 1, -15 }, // 0x66 'f'
{ 1154, 10, 15, 12, 1, -10 }, // 0x67 'g'
{ 1173, 10, 16, 12, 1, -15 }, // 0x68 'h'
{ 1193, 3, 16, 5, 1, -15 }, // 0x69 'i'
{ 1199, 6, 20, 5, -2, -15 }, // 0x6A 'j'
{ 1214, 11, 16, 12, 1, -15 }, // 0x6B 'k'
{ 1236, 5, 16, 6, 1, -15 }, // 0x6C 'l'
{ 1246, 15, 11, 17, 1, -10 }, // 0x6D 'm'
{ 1267, 10, 11, 12, 1, -10 }, // 0x6E 'n'
{ 1281, 10, 11, 12, 1, -10 }, // 0x6F 'o'
{ 1295, 10, 15, 12, 1, -10 }, // 0x70 'p'
{ 1314, 10, 15, 12, 1, -10 }, // 0x71 'q'
{ 1333, 7, 11, 8, 1, -10 }, // 0x72 'r'
{ 1343, 8, 11, 10, 1, -10 }, // 0x73 's'
{ 1354, 7, 14, 9, 1, -13 }, // 0x74 't'
{ 1367, 10, 11, 12, 1, -10 }, // 0x75 'u'
{ 1381, 11, 11, 11, 0, -10 }, // 0x76 'v'
{ 1397, 15, 11, 15, 0, -10 }, // 0x77 'w'
{ 1418, 11, 11, 11, 0, -10 }, // 0x78 'x'
{ 1434, 11, 15, 11, 0, -10 }, // 0x79 'y'
{ 1455, 8, 11, 10, 1, -10 }, // 0x7A 'z'
{ 1466, 7, 20, 8, 1, -15 }, // 0x7B '{'
{ 1484, 3, 20, 7, 2, -15 }, // 0x7C '|'
{ 1492, 7, 20, 8, 0, -15 }, // 0x7D '}'
{ 1510, 10, 5, 11, 1, -8 } }; // 0x7E '~'
const GFXfont Ubuntu_Bold10pt7b PROGMEM = {
(uint8_t *)Ubuntu_Bold10pt7bBitmaps,
(GFXglyph *)Ubuntu_Bold10pt7bGlyphs,
0x20, 0x7E, 23 };
// Approx. 2189 bytes

View File

@@ -1,290 +0,0 @@
const uint8_t Ubuntu_Bold12pt7bBitmaps[] PROGMEM = {
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x06, 0xFF, 0x60, 0xF7, 0xFB, 0xFD,
0xFE, 0xFF, 0x7F, 0xBF, 0x9C, 0x0F, 0x3C, 0x1E, 0x78, 0x3C, 0xF0, 0xF3,
0xCF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x8F, 0x3C, 0x3E, 0xF8, 0x79, 0xE3,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xE7, 0x9E, 0x1E, 0x78, 0x3C, 0xF0, 0x79,
0xE0, 0x0E, 0x00, 0xE0, 0x0E, 0x01, 0xFC, 0x7F, 0xE7, 0xFE, 0xF0, 0x4F,
0x00, 0xF0, 0x0F, 0xF0, 0x7F, 0xC3, 0xFE, 0x07, 0xF0, 0x1F, 0x00, 0xF6,
0x0F, 0xFF, 0xEF, 0xFC, 0x3F, 0x80, 0xE0, 0x0E, 0x00, 0xE0, 0x3E, 0x0F,
0x07, 0xF0, 0xE0, 0xF7, 0x9E, 0x0E, 0x39, 0xC0, 0xE3, 0xBC, 0x0E, 0x3B,
0x80, 0xF7, 0xF8, 0x07, 0xF7, 0x00, 0x3E, 0xF7, 0xC0, 0x0E, 0xFE, 0x01,
0xFE, 0xF0, 0x1D, 0xC7, 0x03, 0xDC, 0x70, 0x39, 0xC7, 0x07, 0x9E, 0xF0,
0x70, 0xFE, 0x0F, 0x07, 0xC0, 0x0F, 0x80, 0x1F, 0xC0, 0x3F, 0xE0, 0x38,
0xE0, 0x38, 0xE0, 0x3D, 0xE0, 0x1F, 0xC0, 0x1F, 0x80, 0x3F, 0x9E, 0x7F,
0x9E, 0xF3, 0xDC, 0xF1, 0xFC, 0xF0, 0xF8, 0xF8, 0xF8, 0xFF, 0xFC, 0x7F,
0xFE, 0x1F, 0x9F, 0xFF, 0xFF, 0xFF, 0xE0, 0x08, 0x3C, 0xF1, 0xE7, 0x8F,
0x1E, 0x78, 0xF1, 0xE3, 0xC7, 0x8F, 0x1E, 0x3C, 0x78, 0x78, 0xF1, 0xE1,
0xE3, 0xC3, 0xC2, 0x00, 0x21, 0xE1, 0xE3, 0xC3, 0xC7, 0x8F, 0x0F, 0x1E,
0x3C, 0x78, 0xF1, 0xE3, 0xC7, 0x8F, 0x3C, 0x78, 0xF3, 0xC7, 0x9E, 0x08,
0x00, 0x0E, 0x01, 0xC1, 0x39, 0x7A, 0xFF, 0xFE, 0x1C, 0x06, 0xC1, 0xDC,
0x7B, 0xC2, 0x20, 0x0E, 0x01, 0xC0, 0x38, 0x07, 0x0F, 0xFF, 0xFF, 0xFF,
0xF8, 0x70, 0x0E, 0x01, 0xC0, 0x38, 0x00, 0x7B, 0xDE, 0xF7, 0xBB, 0xCC,
0xFF, 0xFF, 0xF8, 0x6F, 0xF6, 0x00, 0xF0, 0x1F, 0x01, 0xE0, 0x1E, 0x03,
0xC0, 0x3C, 0x03, 0xC0, 0x78, 0x07, 0x80, 0x78, 0x0F, 0x00, 0xF0, 0x0F,
0x01, 0xE0, 0x1E, 0x01, 0xE0, 0x3C, 0x03, 0xC0, 0x3C, 0x07, 0x80, 0x78,
0x0F, 0x80, 0xF0, 0x00, 0x0F, 0x03, 0xFC, 0x7F, 0xE7, 0x9E, 0x70, 0xEF,
0x0F, 0xF0, 0xFF, 0x0F, 0xF0, 0xFF, 0x0F, 0xF0, 0xFF, 0x0F, 0xF0, 0xE7,
0x9E, 0x7F, 0xE3, 0xFC, 0x0F, 0x00, 0x07, 0x1F, 0x7F, 0xFF, 0xEF, 0x4F,
0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x1F,
0x07, 0xFC, 0xFF, 0xE6, 0x3E, 0x01, 0xE0, 0x1E, 0x03, 0xE0, 0x3C, 0x07,
0x80, 0xF8, 0x3F, 0x03, 0xE0, 0x7C, 0x0F, 0x80, 0xFF, 0xFF, 0xFF, 0xFF,
0xF0, 0x3E, 0x1F, 0xF3, 0xFF, 0x21, 0xE0, 0x3C, 0x07, 0x87, 0xE0, 0xF8,
0x1F, 0xC0, 0x78, 0x07, 0x80, 0xF0, 0x1E, 0x07, 0xFF, 0xF7, 0xFC, 0x7F,
0x00, 0x03, 0xC0, 0x7C, 0x0F, 0xC0, 0xFC, 0x1F, 0xC3, 0xBC, 0x3B, 0xC7,
0x3C, 0x73, 0xCF, 0x3C, 0xFF, 0xFF, 0xFF, 0xFF, 0xF0, 0x3C, 0x03, 0xC0,
0x3C, 0x03, 0xC0, 0x3F, 0xE3, 0xFE, 0x3F, 0xE3, 0x80, 0x38, 0x07, 0x80,
0x7F, 0x07, 0xFC, 0x7F, 0xE0, 0x3F, 0x00, 0xF0, 0x0F, 0x00, 0xF0, 0x1F,
0xFF, 0xEF, 0xFC, 0x7F, 0x00, 0x01, 0xE0, 0xFE, 0x1F, 0xE3, 0xF0, 0x7C,
0x07, 0x80, 0xFF, 0x8F, 0xFE, 0xFF, 0xEF, 0x1F, 0xF0, 0xFF, 0x0F, 0xF0,
0xF7, 0x9F, 0x7F, 0xE3, 0xFC, 0x1F, 0x80, 0xFF, 0xFF, 0xFF, 0xFF, 0xF0,
0x1E, 0x03, 0xE0, 0x7C, 0x07, 0x80, 0x78, 0x0F, 0x00, 0xF0, 0x1F, 0x01,
0xE0, 0x1E, 0x01, 0xE0, 0x3C, 0x03, 0xC0, 0x3C, 0x00, 0x1F, 0x83, 0xFE,
0x7F, 0xEF, 0x9F, 0xF0, 0xFF, 0x0F, 0xF9, 0xF7, 0xFE, 0x3F, 0xC7, 0xFE,
0xF9, 0xFF, 0x0F, 0xF0, 0xFF, 0x8F, 0xFF, 0xE7, 0xFE, 0x1F, 0x80, 0x1F,
0x03, 0xFC, 0x7F, 0xEF, 0x9E, 0xF0, 0xFF, 0x0F, 0xF0, 0xFF, 0x8F, 0x7F,
0xF7, 0xFF, 0x1F, 0xF0, 0x1E, 0x03, 0xE0, 0x7C, 0x7F, 0x87, 0xF0, 0x78,
0x00, 0x6F, 0xF6, 0x00, 0x00, 0x06, 0xFF, 0x60, 0x33, 0xDE, 0x60, 0x00,
0x00, 0x03, 0xDE, 0xF7, 0xBD, 0xDE, 0x60, 0x00, 0x60, 0x3E, 0x1F, 0xFF,
0xFF, 0xFF, 0x8F, 0x00, 0xFF, 0x8F, 0xFF, 0x1F, 0xF0, 0x3E, 0x00, 0x60,
0xFF, 0xFF, 0xFF, 0xFF, 0x80, 0x00, 0x01, 0xFF, 0xFF, 0xFF, 0xFF, 0x60,
0x07, 0xC0, 0xFF, 0x8F, 0xFF, 0x1F, 0xF0, 0x0F, 0x1F, 0xFF, 0xFF, 0xFF,
0x87, 0xC0, 0x60, 0x00, 0x3E, 0x3F, 0xEF, 0xF9, 0x0F, 0x03, 0xC0, 0xF0,
0x7C, 0x1E, 0x0F, 0x83, 0xC1, 0xE0, 0x78, 0x1C, 0x07, 0x00, 0x00, 0x30,
0x1E, 0x07, 0x80, 0xC0, 0x01, 0xFC, 0x00, 0x3F, 0xFC, 0x03, 0xFF, 0xF0,
0x7E, 0x07, 0xC3, 0xC0, 0x0F, 0x3C, 0x7E, 0x39, 0xC7, 0xF8, 0xFE, 0x7F,
0xC7, 0xE7, 0x8E, 0x3F, 0x38, 0x71, 0xF9, 0xC3, 0x8F, 0xCE, 0x1C, 0x7E,
0x78, 0xE7, 0x71, 0xFF, 0xF9, 0xCF, 0xFF, 0x8F, 0x1F, 0xF8, 0x3C, 0x00,
0x01, 0xF8, 0x00, 0x07, 0xFF, 0x80, 0x0F, 0xFE, 0x00, 0x1F, 0xE0, 0x00,
0x03, 0xE0, 0x01, 0xF0, 0x01, 0xFC, 0x00, 0xFE, 0x00, 0x77, 0x00, 0x7B,
0xC0, 0x3D, 0xE0, 0x3C, 0x78, 0x1E, 0x3C, 0x0F, 0x1E, 0x0F, 0xFF, 0x87,
0xFF, 0xC3, 0xFF, 0xE3, 0xC0, 0x79, 0xE0, 0x3D, 0xF0, 0x1E, 0xF0, 0x07,
0x80, 0xFF, 0x87, 0xFF, 0x3F, 0xF9, 0xE3, 0xEF, 0x0F, 0x78, 0x7B, 0xC7,
0xDF, 0xFC, 0xFF, 0xE7, 0xFF, 0xBC, 0x3F, 0xE0, 0xFF, 0x07, 0xF8, 0x7F,
0xFF, 0xDF, 0xFC, 0xFF, 0x80, 0x07, 0xF0, 0x7F, 0xF3, 0xFF, 0xDF, 0x82,
0x78, 0x03, 0xE0, 0x0F, 0x00, 0x3C, 0x00, 0xF0, 0x03, 0xC0, 0x0F, 0x00,
0x3E, 0x00, 0x78, 0x01, 0xF0, 0x23, 0xFF, 0xC7, 0xFF, 0x07, 0xF0, 0xFF,
0x81, 0xFF, 0xE3, 0xFF, 0xE7, 0x87, 0xEF, 0x03, 0xDE, 0x07, 0xFC, 0x07,
0xF8, 0x0F, 0xF0, 0x1F, 0xE0, 0x3F, 0xC0, 0x7F, 0x81, 0xFF, 0x03, 0xDE,
0x1F, 0xBF, 0xFE, 0x7F, 0xF8, 0xFF, 0x80, 0xFF, 0xEF, 0xFE, 0xFF, 0xEF,
0x00, 0xF0, 0x0F, 0x00, 0xF0, 0x0F, 0xFE, 0xFF, 0xEF, 0xFE, 0xF0, 0x0F,
0x00, 0xF0, 0x0F, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xF0, 0xFF, 0xFF, 0xFF,
0xFF, 0xF8, 0x0F, 0x01, 0xE0, 0x3C, 0x07, 0xFE, 0xFF, 0xDF, 0xFB, 0xC0,
0x78, 0x0F, 0x01, 0xE0, 0x3C, 0x07, 0x80, 0xF0, 0x00, 0x07, 0xF0, 0x7F,
0xF3, 0xFF, 0xDF, 0x02, 0x78, 0x03, 0xE0, 0x0F, 0x00, 0x3C, 0x00, 0xF0,
0x3F, 0xC0, 0xFF, 0x03, 0xFE, 0x0F, 0x78, 0x3D, 0xF0, 0xF3, 0xFF, 0xC7,
0xFF, 0x07, 0xF8, 0xF0, 0x3F, 0xC0, 0xFF, 0x03, 0xFC, 0x0F, 0xF0, 0x3F,
0xC0, 0xFF, 0x03, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x03, 0xFC, 0x0F,
0xF0, 0x3F, 0xC0, 0xFF, 0x03, 0xFC, 0x0F, 0xF0, 0x3C, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF0, 0x01, 0xE0, 0x3C, 0x07, 0x80, 0xF0,
0x1E, 0x03, 0xC0, 0x78, 0x0F, 0x01, 0xE0, 0x3C, 0x07, 0x80, 0xF0, 0x1E,
0x87, 0xDF, 0xF7, 0xFE, 0x3F, 0x00, 0xF0, 0xFF, 0xC7, 0xEF, 0x1F, 0x3C,
0xF8, 0xF7, 0xC3, 0xFE, 0x0F, 0xF0, 0x3F, 0x80, 0xFF, 0x03, 0xFC, 0x0F,
0x78, 0x3D, 0xF0, 0xF3, 0xE3, 0xC7, 0x8F, 0x1F, 0x3C, 0x3E, 0xF0, 0x7C,
0xF0, 0x0F, 0x00, 0xF0, 0x0F, 0x00, 0xF0, 0x0F, 0x00, 0xF0, 0x0F, 0x00,
0xF0, 0x0F, 0x00, 0xF0, 0x0F, 0x00, 0xF0, 0x0F, 0x00, 0xFF, 0xFF, 0xFF,
0xFF, 0xF0, 0x78, 0x03, 0xCF, 0x80, 0xF9, 0xF0, 0x1F, 0x3F, 0x07, 0xE7,
0xE0, 0xFC, 0xFE, 0x3F, 0xBD, 0xC7, 0xFF, 0xBC, 0xEF, 0xF3, 0xB9, 0xFE,
0x77, 0x3F, 0xCF, 0xE7, 0xF8, 0xF8, 0xFF, 0x1F, 0x1F, 0xE1, 0xC3, 0xFC,
0x38, 0x7F, 0x80, 0x0F, 0xF0, 0x01, 0xE0, 0xF0, 0x3F, 0xE0, 0xFF, 0x83,
0xFF, 0x0F, 0xFE, 0x3F, 0xF8, 0xFF, 0x73, 0xFD, 0xCF, 0xF3, 0xBF, 0xCF,
0xFF, 0x1F, 0xFC, 0x3F, 0xF0, 0xFF, 0xC1, 0xFF, 0x07, 0xFC, 0x0F, 0xF0,
0x3C, 0x07, 0xF0, 0x0F, 0xFE, 0x0F, 0xFF, 0x8F, 0x83, 0xE7, 0x80, 0xF7,
0xC0, 0x7F, 0xC0, 0x1F, 0xE0, 0x0F, 0xF0, 0x07, 0xF8, 0x03, 0xFC, 0x01,
0xFF, 0x01, 0xF7, 0x80, 0xF3, 0xE0, 0xF8, 0xFF, 0xF8, 0x3F, 0xF8, 0x07,
0xF0, 0x00, 0xFF, 0x0F, 0xFC, 0xFF, 0xEF, 0x1F, 0xF0, 0xFF, 0x0F, 0xF0,
0xFF, 0x1F, 0xFF, 0xEF, 0xFC, 0xFF, 0x8F, 0x00, 0xF0, 0x0F, 0x00, 0xF0,
0x0F, 0x00, 0xF0, 0x00, 0x07, 0xF0, 0x0F, 0xFE, 0x0F, 0xFF, 0x8F, 0x83,
0xE7, 0x80, 0xF7, 0xC0, 0x7F, 0xC0, 0x1F, 0xE0, 0x0F, 0xF0, 0x07, 0xF8,
0x03, 0xFC, 0x01, 0xFF, 0x01, 0xF7, 0x80, 0xF3, 0xE0, 0xF8, 0xFF, 0xF8,
0x3F, 0xF8, 0x07, 0xF0, 0x00, 0xF0, 0x00, 0x3E, 0x00, 0x1F, 0xE0, 0x07,
0xF0, 0x00, 0x70, 0xFF, 0x83, 0xFF, 0x8F, 0xFF, 0x3C, 0x3E, 0xF0, 0x7B,
0xC1, 0xEF, 0x07, 0xBC, 0x3E, 0xFF, 0xF3, 0xFF, 0x8F, 0xFC, 0x3C, 0xF8,
0xF1, 0xF3, 0xC3, 0xCF, 0x0F, 0xBC, 0x1E, 0xF0, 0x7C, 0x1F, 0x87, 0xFE,
0x7F, 0xCF, 0x04, 0xF0, 0x0F, 0x00, 0xFC, 0x07, 0xF8, 0x3F, 0xC0, 0xFE,
0x01, 0xF0, 0x0F, 0x00, 0xF4, 0x1F, 0xFF, 0xEF, 0xFE, 0x3F, 0x80, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xC1, 0xE0, 0x07, 0x80, 0x1E, 0x00, 0x78, 0x01,
0xE0, 0x07, 0x80, 0x1E, 0x00, 0x78, 0x01, 0xE0, 0x07, 0x80, 0x1E, 0x00,
0x78, 0x01, 0xE0, 0x07, 0x80, 0xF0, 0x7F, 0x83, 0xFC, 0x1F, 0xE0, 0xFF,
0x07, 0xF8, 0x3F, 0xC1, 0xFE, 0x0F, 0xF0, 0x7F, 0x83, 0xFC, 0x1F, 0xE0,
0xFF, 0x07, 0xBC, 0x79, 0xFF, 0xC7, 0xFC, 0x0F, 0x80, 0xF0, 0x07, 0xFC,
0x07, 0x9E, 0x03, 0xCF, 0x01, 0xE7, 0xC1, 0xE1, 0xE0, 0xF0, 0xF0, 0x78,
0x3C, 0x78, 0x1E, 0x3C, 0x0F, 0x1E, 0x03, 0xDE, 0x01, 0xEF, 0x00, 0xF7,
0x80, 0x3F, 0x80, 0x1F, 0xC0, 0x0F, 0xE0, 0x03, 0xE0, 0x00, 0xF0, 0x00,
0x1F, 0xE0, 0x00, 0x3F, 0xE0, 0xE0, 0xFB, 0xC3, 0xE1, 0xE7, 0x87, 0xC3,
0xCF, 0x0F, 0x87, 0x9E, 0x1F, 0x8F, 0x3C, 0x77, 0x3E, 0x7C, 0xEE, 0x7C,
0x7B, 0xDE, 0xF0, 0xF7, 0x1D, 0xE1, 0xEE, 0x3B, 0xC3, 0xFC, 0x7F, 0x83,
0xF0, 0x7E, 0x07, 0xE0, 0xFC, 0x0F, 0xC1, 0xF8, 0x1F, 0x01, 0xF0, 0xF8,
0x1F, 0x7C, 0x3E, 0x3C, 0x3C, 0x3E, 0x7C, 0x1F, 0xF8, 0x0F, 0xF0, 0x0F,
0xF0, 0x07, 0xE0, 0x03, 0xC0, 0x07, 0xE0, 0x0F, 0xF0, 0x0F, 0xF0, 0x1F,
0xF8, 0x3E, 0x7C, 0x3C, 0x3C, 0x7C, 0x3E, 0xF8, 0x1F, 0xF8, 0x1F, 0x78,
0x1E, 0x7C, 0x3E, 0x3C, 0x3C, 0x3E, 0x7C, 0x1E, 0xF8, 0x0F, 0xF0, 0x0F,
0xF0, 0x07, 0xE0, 0x07, 0xE0, 0x03, 0xC0, 0x03, 0xC0, 0x03, 0xC0, 0x03,
0xC0, 0x03, 0xC0, 0x03, 0xC0, 0x03, 0xC0, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE,
0x03, 0xE0, 0x1E, 0x01, 0xF0, 0x1F, 0x00, 0xF0, 0x0F, 0x80, 0xF8, 0x07,
0x80, 0x7C, 0x07, 0xC0, 0x3E, 0x03, 0xFF, 0xFF, 0xFF, 0xFF, 0xF8, 0xFF,
0xFF, 0xFF, 0x8F, 0x1E, 0x3C, 0x78, 0xF1, 0xE3, 0xC7, 0x8F, 0x1E, 0x3C,
0x78, 0xF1, 0xE3, 0xC7, 0x8F, 0xFF, 0xFF, 0x80, 0xF0, 0x07, 0x80, 0x78,
0x07, 0x80, 0x3C, 0x03, 0xC0, 0x3C, 0x01, 0xE0, 0x1E, 0x01, 0xE0, 0x0F,
0x00, 0xF0, 0x0F, 0x00, 0x78, 0x07, 0x80, 0x78, 0x03, 0xC0, 0x3C, 0x03,
0xC0, 0x1E, 0x01, 0xE0, 0x1E, 0x00, 0xF0, 0xFF, 0xFF, 0xF8, 0xF1, 0xE3,
0xC7, 0x8F, 0x1E, 0x3C, 0x78, 0xF1, 0xE3, 0xC7, 0x8F, 0x1E, 0x3C, 0x78,
0xFF, 0xFF, 0xFF, 0x80, 0x07, 0x00, 0x7C, 0x03, 0xE0, 0x3F, 0x83, 0xDE,
0x1E, 0xF1, 0xE3, 0xCE, 0x0E, 0xF0, 0x79, 0x01, 0x00, 0xFF, 0xFF, 0xFF,
0xFF, 0xF0, 0x23, 0xC7, 0x8F, 0x08, 0x7F, 0x1F, 0xE7, 0xFC, 0x0F, 0x03,
0xCF, 0xF7, 0xFF, 0xCF, 0xF3, 0xFC, 0xFF, 0xFD, 0xFF, 0x3F, 0x80, 0x70,
0x0F, 0x00, 0xF0, 0x0F, 0x00, 0xF0, 0x0F, 0x00, 0xFF, 0x8F, 0xFC, 0xFF,
0xEF, 0x1F, 0xF0, 0xFF, 0x0F, 0xF0, 0xFF, 0x0F, 0xF0, 0xFF, 0x1E, 0xFF,
0xEF, 0xFC, 0x7F, 0x00, 0x0F, 0x8F, 0xF7, 0xFD, 0xE0, 0xF0, 0x3C, 0x0F,
0x03, 0xC0, 0xF0, 0x1E, 0x17, 0xFC, 0xFF, 0x0F, 0x80, 0x00, 0x70, 0x0F,
0x00, 0xF0, 0x0F, 0x00, 0xF0, 0x0F, 0x1F, 0xF3, 0xFF, 0x7F, 0xFF, 0x8F,
0xF0, 0xFF, 0x0F, 0xF0, 0xFF, 0x0F, 0xF0, 0xF7, 0x8F, 0x7F, 0xF3, 0xFF,
0x0F, 0xE0, 0x0F, 0x83, 0xFC, 0x7F, 0xE7, 0x9E, 0xF0, 0xFF, 0x0F, 0xFF,
0xFF, 0xFF, 0xF0, 0x07, 0x82, 0x7F, 0xE3, 0xFE, 0x0F, 0xC0, 0x1F, 0x3F,
0x9F, 0xDF, 0x0F, 0x07, 0x83, 0xFD, 0xFE, 0xFF, 0x78, 0x3C, 0x1E, 0x0F,
0x07, 0x83, 0xC1, 0xE0, 0xF0, 0x78, 0x3C, 0x00, 0x1F, 0xC7, 0xFD, 0xFF,
0xFC, 0xFF, 0x1F, 0xE3, 0xFC, 0x7F, 0x8F, 0xF9, 0xEF, 0xFD, 0xFF, 0x9F,
0xF0, 0x1E, 0x87, 0xDF, 0xF3, 0xFE, 0x7F, 0x00, 0x70, 0x1E, 0x03, 0xC0,
0x78, 0x0F, 0x01, 0xE0, 0x3F, 0xE7, 0xFE, 0xFF, 0xDE, 0x7F, 0xC7, 0xF8,
0xFF, 0x1F, 0xE3, 0xFC, 0x7F, 0x8F, 0xF1, 0xFE, 0x3F, 0xC7, 0x80, 0xFF,
0xFF, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF0, 0x1E, 0x3C, 0x78,
0xF0, 0x00, 0x07, 0x8F, 0x1E, 0x3C, 0x78, 0xF1, 0xE3, 0xC7, 0x8F, 0x1E,
0x3C, 0x78, 0xFF, 0xFF, 0xBE, 0x00, 0x70, 0x0F, 0x00, 0xF0, 0x0F, 0x00,
0xF0, 0x0F, 0x00, 0xF1, 0xFF, 0x3E, 0xF7, 0xCF, 0xF8, 0xFF, 0x0F, 0xE0,
0xFF, 0x0F, 0xF8, 0xF7, 0x8F, 0x7C, 0xF3, 0xEF, 0x1E, 0xF1, 0xF0, 0x73,
0xCF, 0x3C, 0xF3, 0xCF, 0x3C, 0xF3, 0xCF, 0x3C, 0xF3, 0xCF, 0x3E, 0xFD,
0xF3, 0xC0, 0x7F, 0x3F, 0x3F, 0xFF, 0xEF, 0xFF, 0xFB, 0xCF, 0x9F, 0xF1,
0xE3, 0xFC, 0x78, 0xFF, 0x1E, 0x3F, 0xC7, 0x8F, 0xF1, 0xE3, 0xFC, 0x78,
0xFF, 0x1E, 0x3F, 0xC7, 0x8F, 0xF1, 0xE3, 0xC0, 0x7F, 0x1F, 0xFB, 0xFF,
0x79, 0xFF, 0x1F, 0xE3, 0xFC, 0x7F, 0x8F, 0xF1, 0xFE, 0x3F, 0xC7, 0xF8,
0xFF, 0x1E, 0x0F, 0x81, 0xFF, 0x1F, 0xFC, 0xF1, 0xEF, 0x07, 0xF8, 0x3F,
0xC1, 0xFE, 0x0F, 0xF0, 0x7B, 0xC7, 0x9F, 0xFC, 0x7F, 0xC0, 0xF8, 0x00,
0x7F, 0x0F, 0xFC, 0xFF, 0xEF, 0x1E, 0xF0, 0xFF, 0x0F, 0xF0, 0xFF, 0x0F,
0xF0, 0xFF, 0x1F, 0xFF, 0xEF, 0xFC, 0xFF, 0x8F, 0x00, 0xF0, 0x0F, 0x00,
0xF0, 0x00, 0x0F, 0xF1, 0xFF, 0xDF, 0xFE, 0xF0, 0xFF, 0x07, 0xF8, 0x3F,
0xC1, 0xFE, 0x0F, 0xF0, 0x7F, 0xC3, 0xDF, 0xFE, 0x7F, 0xF1, 0xFF, 0x80,
0x3C, 0x01, 0xE0, 0x0F, 0x00, 0x78, 0x3F, 0xFF, 0xFF, 0xFE, 0x0F, 0x07,
0x83, 0xC1, 0xE0, 0xF0, 0x78, 0x3C, 0x1E, 0x0F, 0x00, 0x1F, 0x9F, 0xEF,
0xFB, 0xC0, 0xF0, 0x3F, 0xE7, 0xFC, 0xFF, 0x03, 0xE0, 0xFF, 0xFF, 0xFE,
0x7E, 0x00, 0x70, 0x78, 0x3C, 0x1E, 0x0F, 0xF7, 0xFB, 0xFD, 0xE0, 0xF0,
0x78, 0x3C, 0x1E, 0x0F, 0x07, 0x83, 0xFC, 0xFE, 0x3F, 0x00, 0xF1, 0xFE,
0x3F, 0xC7, 0xF8, 0xFF, 0x1F, 0xE3, 0xFC, 0x7F, 0x8F, 0xF1, 0xFF, 0x3D,
0xFF, 0xBF, 0xF1, 0xFC, 0xF0, 0x7F, 0xC7, 0xDE, 0x3C, 0xF1, 0xE7, 0x8F,
0x1C, 0xF0, 0xF7, 0x87, 0xBC, 0x3D, 0xC0, 0xFE, 0x07, 0xF0, 0x1F, 0x00,
0xF8, 0x00, 0xF0, 0xE1, 0xFE, 0x1C, 0x3D, 0xC7, 0xC7, 0x3C, 0xF9, 0xE7,
0x9F, 0x3C, 0xF7, 0xE7, 0x8E, 0xEE, 0xE1, 0xDD, 0xDC, 0x3F, 0x3F, 0x83,
0xE3, 0xE0, 0x7C, 0x7C, 0x0F, 0x07, 0x80, 0xE0, 0xE0, 0xF8, 0xFB, 0xC7,
0x8F, 0x78, 0x7B, 0xC1, 0xFC, 0x07, 0xC0, 0x1C, 0x01, 0xF0, 0x1F, 0xC1,
0xEF, 0x0F, 0x78, 0xFB, 0xEF, 0x8F, 0x80, 0xF0, 0x7F, 0xC7, 0xDE, 0x3C,
0xF1, 0xE7, 0x8F, 0x1E, 0xF0, 0xF7, 0x87, 0xBC, 0x1D, 0xC0, 0xFE, 0x07,
0xF0, 0x1F, 0x00, 0xF8, 0x0F, 0x83, 0xFC, 0x1F, 0xC0, 0xFC, 0x00, 0xFF,
0xFF, 0xFF, 0xFC, 0x3E, 0x0F, 0x87, 0xC3, 0xE0, 0xF8, 0x7C, 0x1F, 0x0F,
0xFF, 0xFF, 0xFF, 0xC0, 0x0F, 0x1F, 0x3F, 0x3C, 0x3C, 0x3C, 0x3C, 0x3C,
0x3C, 0x7C, 0xF8, 0xF0, 0xF8, 0x7C, 0x3C, 0x3C, 0x3C, 0x3C, 0x3C, 0x3C,
0x3F, 0x1F, 0x0F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF8,
0xF0, 0xF8, 0xFC, 0x3C, 0x3C, 0x3C, 0x3C, 0x3C, 0x3C, 0x3E, 0x1F, 0x0F,
0x1F, 0x3E, 0x3C, 0x3C, 0x3C, 0x3C, 0x3C, 0x3C, 0xFC, 0xF8, 0xF0, 0x38,
0x27, 0xE7, 0xFF, 0xFE, 0x7E, 0x41, 0xC0 };
const GFXglyph Ubuntu_Bold12pt7bGlyphs[] PROGMEM = {
{ 0, 0, 0, 6, 0, 1 }, // 0x20 ' '
{ 0, 4, 17, 8, 2, -16 }, // 0x21 '!'
{ 9, 9, 7, 11, 1, -17 }, // 0x22 '"'
{ 17, 15, 17, 17, 1, -16 }, // 0x23 '#'
{ 49, 12, 22, 14, 1, -18 }, // 0x24 '$'
{ 82, 20, 17, 22, 1, -16 }, // 0x25 '%'
{ 125, 16, 17, 17, 1, -16 }, // 0x26 '&'
{ 159, 4, 7, 6, 1, -17 }, // 0x27 '''
{ 163, 7, 23, 9, 2, -18 }, // 0x28 '('
{ 184, 7, 23, 9, 0, -18 }, // 0x29 ')'
{ 205, 11, 10, 12, 1, -16 }, // 0x2A '*'
{ 219, 11, 11, 13, 1, -12 }, // 0x2B '+'
{ 235, 5, 8, 6, 0, -3 }, // 0x2C ','
{ 240, 7, 3, 9, 1, -8 }, // 0x2D '-'
{ 243, 4, 4, 6, 1, -3 }, // 0x2E '.'
{ 245, 12, 23, 10, -1, -18 }, // 0x2F '/'
{ 280, 12, 17, 14, 1, -16 }, // 0x30 '0'
{ 306, 8, 17, 14, 2, -16 }, // 0x31 '1'
{ 323, 12, 17, 14, 1, -16 }, // 0x32 '2'
{ 349, 11, 17, 14, 1, -16 }, // 0x33 '3'
{ 373, 12, 17, 14, 1, -16 }, // 0x34 '4'
{ 399, 12, 17, 14, 1, -16 }, // 0x35 '5'
{ 425, 12, 17, 14, 1, -16 }, // 0x36 '6'
{ 451, 12, 17, 14, 1, -16 }, // 0x37 '7'
{ 477, 12, 17, 14, 1, -16 }, // 0x38 '8'
{ 503, 12, 17, 14, 1, -16 }, // 0x39 '9'
{ 529, 4, 13, 6, 1, -12 }, // 0x3A ':'
{ 536, 5, 17, 6, 0, -12 }, // 0x3B ';'
{ 547, 12, 11, 14, 1, -12 }, // 0x3C '<'
{ 564, 11, 8, 14, 2, -10 }, // 0x3D '='
{ 575, 12, 11, 14, 1, -12 }, // 0x3E '>'
{ 592, 10, 19, 11, 0, -18 }, // 0x3F '?'
{ 616, 21, 21, 23, 1, -16 }, // 0x40 '@'
{ 672, 17, 17, 17, 0, -16 }, // 0x41 'A'
{ 709, 13, 17, 16, 2, -16 }, // 0x42 'B'
{ 737, 14, 17, 16, 1, -16 }, // 0x43 'C'
{ 767, 15, 17, 18, 2, -16 }, // 0x44 'D'
{ 799, 12, 17, 15, 2, -16 }, // 0x45 'E'
{ 825, 11, 17, 14, 2, -16 }, // 0x46 'F'
{ 849, 14, 17, 17, 1, -16 }, // 0x47 'G'
{ 879, 14, 17, 18, 2, -16 }, // 0x48 'H'
{ 909, 4, 17, 8, 2, -16 }, // 0x49 'I'
{ 918, 11, 17, 13, 0, -16 }, // 0x4A 'J'
{ 942, 14, 17, 16, 2, -16 }, // 0x4B 'K'
{ 972, 12, 17, 14, 2, -16 }, // 0x4C 'L'
{ 998, 19, 17, 21, 1, -16 }, // 0x4D 'M'
{ 1039, 14, 17, 18, 2, -16 }, // 0x4E 'N'
{ 1069, 17, 17, 19, 1, -16 }, // 0x4F 'O'
{ 1106, 12, 17, 15, 2, -16 }, // 0x50 'P'
{ 1132, 17, 22, 19, 1, -16 }, // 0x51 'Q'
{ 1179, 14, 17, 16, 2, -16 }, // 0x52 'R'
{ 1209, 12, 17, 14, 1, -16 }, // 0x53 'S'
{ 1235, 14, 17, 14, 0, -16 }, // 0x54 'T'
{ 1265, 13, 17, 17, 2, -16 }, // 0x55 'U'
{ 1293, 17, 17, 17, 0, -16 }, // 0x56 'V'
{ 1330, 23, 17, 23, 0, -16 }, // 0x57 'W'
{ 1379, 16, 17, 16, 0, -16 }, // 0x58 'X'
{ 1413, 16, 17, 16, 0, -16 }, // 0x59 'Y'
{ 1447, 13, 17, 15, 1, -16 }, // 0x5A 'Z'
{ 1475, 7, 23, 9, 2, -18 }, // 0x5B '['
{ 1496, 12, 23, 10, -1, -18 }, // 0x5C '\'
{ 1531, 7, 23, 9, 0, -18 }, // 0x5D ']'
{ 1552, 13, 10, 13, 0, -16 }, // 0x5E '^'
{ 1569, 12, 3, 12, 0, 2 }, // 0x5F '_'
{ 1574, 6, 5, 7, 1, -18 }, // 0x60 '`'
{ 1578, 10, 13, 13, 1, -12 }, // 0x61 'a'
{ 1595, 12, 19, 15, 2, -18 }, // 0x62 'b'
{ 1624, 10, 13, 12, 1, -12 }, // 0x63 'c'
{ 1641, 12, 19, 15, 1, -18 }, // 0x64 'd'
{ 1670, 12, 13, 14, 1, -12 }, // 0x65 'e'
{ 1690, 9, 19, 10, 2, -18 }, // 0x66 'f'
{ 1712, 11, 17, 14, 1, -12 }, // 0x67 'g'
{ 1736, 11, 19, 15, 2, -18 }, // 0x68 'h'
{ 1763, 4, 19, 8, 2, -18 }, // 0x69 'i'
{ 1773, 7, 23, 6, -2, -18 }, // 0x6A 'j'
{ 1794, 12, 19, 14, 2, -18 }, // 0x6B 'k'
{ 1823, 6, 19, 8, 2, -18 }, // 0x6C 'l'
{ 1838, 18, 13, 22, 2, -12 }, // 0x6D 'm'
{ 1868, 11, 13, 15, 2, -12 }, // 0x6E 'n'
{ 1886, 13, 13, 15, 1, -12 }, // 0x6F 'o'
{ 1908, 12, 17, 15, 2, -12 }, // 0x70 'p'
{ 1934, 13, 17, 15, 1, -12 }, // 0x71 'q'
{ 1962, 9, 13, 11, 2, -12 }, // 0x72 'r'
{ 1977, 10, 13, 12, 1, -12 }, // 0x73 's'
{ 1994, 9, 17, 11, 2, -16 }, // 0x74 't'
{ 2014, 11, 13, 15, 2, -12 }, // 0x75 'u'
{ 2032, 13, 13, 13, 0, -12 }, // 0x76 'v'
{ 2054, 19, 13, 19, 0, -12 }, // 0x77 'w'
{ 2085, 13, 13, 13, 0, -12 }, // 0x78 'x'
{ 2107, 13, 17, 13, 0, -12 }, // 0x79 'y'
{ 2135, 10, 13, 12, 1, -12 }, // 0x7A 'z'
{ 2152, 8, 23, 9, 1, -18 }, // 0x7B '{'
{ 2175, 3, 23, 7, 2, -18 }, // 0x7C '|'
{ 2184, 8, 23, 9, 0, -18 }, // 0x7D '}'
{ 2207, 12, 5, 14, 1, -9 } }; // 0x7E '~'
const GFXfont Ubuntu_Bold12pt7b PROGMEM = {
(uint8_t *)Ubuntu_Bold12pt7bBitmaps,
(GFXglyph *)Ubuntu_Bold12pt7bGlyphs,
0x20, 0x7E, 28 };
// Approx. 2887 bytes

View File

@@ -1,402 +0,0 @@
const uint8_t Ubuntu_Bold16pt7bBitmaps[] PROGMEM = {
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF7, 0x00, 0x0E, 0xFF, 0xFF,
0xF7, 0x00, 0xF9, 0xFF, 0x9F, 0xF9, 0xFF, 0x9F, 0xF9, 0xFF, 0x9F, 0xF9,
0xFF, 0x1E, 0x70, 0xE0, 0x07, 0xCF, 0x81, 0xF3, 0xE0, 0x7D, 0xF8, 0x3E,
0x7C, 0x0F, 0x9F, 0x3F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xC7, 0xCF, 0x81, 0xF3, 0xE0, 0x7C, 0xF8, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0x3E, 0x7C, 0x0F, 0x9F, 0x07, 0xEF, 0x81, 0xF3,
0xE0, 0x7C, 0xF8, 0x00, 0x03, 0xC0, 0x03, 0xC0, 0x03, 0xC0, 0x07, 0xF8,
0x1F, 0xFE, 0x3F, 0xFE, 0x3F, 0xFC, 0x7E, 0x0C, 0x7C, 0x00, 0x7C, 0x00,
0x7E, 0x00, 0x7F, 0xE0, 0x3F, 0xF8, 0x1F, 0xFC, 0x0F, 0xFE, 0x01, 0xFF,
0x00, 0x3F, 0x00, 0x1F, 0x00, 0x1F, 0x70, 0x3F, 0x7F, 0xFE, 0xFF, 0xFE,
0xFF, 0xFC, 0x1F, 0xF0, 0x03, 0xC0, 0x03, 0xC0, 0x03, 0xC0, 0x03, 0xC0,
0x1F, 0x80, 0xF8, 0x0F, 0xF0, 0x3C, 0x07, 0xFE, 0x1F, 0x03, 0xE7, 0xC7,
0x80, 0xF0, 0xF3, 0xE0, 0x3C, 0x3C, 0xF0, 0x0F, 0x0F, 0x78, 0x03, 0xC3,
0xFE, 0x00, 0xF9, 0xFF, 0x00, 0x1F, 0xFF, 0xDF, 0x83, 0xFD, 0xEF, 0xF0,
0x7E, 0xFF, 0xFE, 0x00, 0x3F, 0xE7, 0xC0, 0x1F, 0xF0, 0xF0, 0x07, 0xBC,
0x3C, 0x03, 0xCF, 0x0F, 0x01, 0xF3, 0xC3, 0xC0, 0x78, 0xF9, 0xF0, 0x3E,
0x1F, 0xF8, 0x0F, 0x03, 0xFC, 0x07, 0xC0, 0x7E, 0x00, 0x07, 0xF0, 0x00,
0x7F, 0xE0, 0x07, 0xFF, 0x00, 0x7F, 0xFC, 0x03, 0xE3, 0xE0, 0x1F, 0x1F,
0x00, 0xFD, 0xF0, 0x03, 0xFF, 0x80, 0x1F, 0xF8, 0x00, 0xFF, 0x00, 0x0F,
0xFC, 0xF8, 0xFF, 0xF7, 0xC7, 0xDF, 0xBE, 0x7C, 0x7F, 0xE3, 0xE1, 0xFF,
0x1F, 0x07, 0xF0, 0xFC, 0x1F, 0x87, 0xFF, 0xFE, 0x1F, 0xFF, 0xF8, 0x7F,
0xFF, 0xE0, 0xFE, 0x3F, 0x80, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0x70, 0x04,
0x07, 0x87, 0xE3, 0xE3, 0xF1, 0xF1, 0xF8, 0xF8, 0x7C, 0x3E, 0x3E, 0x1F,
0x0F, 0x87, 0xC3, 0xE1, 0xF0, 0xF8, 0x7C, 0x3E, 0x1F, 0x07, 0xC3, 0xE1,
0xF0, 0xFC, 0x3E, 0x1F, 0x87, 0xC3, 0xF0, 0xF0, 0x20, 0x10, 0x3C, 0x3F,
0x0F, 0x87, 0xE1, 0xF0, 0xFC, 0x3E, 0x1F, 0x0F, 0x83, 0xE1, 0xF0, 0xF8,
0x7C, 0x3E, 0x1F, 0x0F, 0x87, 0xC3, 0xE1, 0xF1, 0xF0, 0xF8, 0x7C, 0x7E,
0x3E, 0x3F, 0x1F, 0x1F, 0x87, 0x80, 0x80, 0x0F, 0x80, 0x7C, 0x1B, 0xCC,
0xEE, 0xEF, 0xFF, 0xFF, 0xFC, 0x1C, 0x03, 0xF8, 0x3D, 0xE3, 0xEF, 0x8E,
0x38, 0x11, 0x00, 0x07, 0x80, 0x1E, 0x00, 0x78, 0x01, 0xE0, 0x07, 0x83,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFC, 0x1E, 0x00, 0x78, 0x01, 0xE0,
0x07, 0x80, 0x1E, 0x00, 0x7D, 0xF7, 0xDF, 0x7D, 0xE7, 0xBE, 0xF0, 0xC0,
0xFF, 0xFF, 0xFF, 0xFF, 0xF0, 0x7B, 0xFF, 0xFF, 0xFD, 0xE0, 0x00, 0x3E,
0x00, 0xFC, 0x01, 0xF0, 0x03, 0xE0, 0x0F, 0xC0, 0x1F, 0x00, 0x3E, 0x00,
0xFC, 0x01, 0xF0, 0x03, 0xE0, 0x0F, 0xC0, 0x1F, 0x00, 0x3E, 0x00, 0xFC,
0x01, 0xF0, 0x03, 0xE0, 0x0F, 0xC0, 0x1F, 0x00, 0x3E, 0x00, 0xFC, 0x01,
0xF0, 0x03, 0xE0, 0x0F, 0xC0, 0x1F, 0x00, 0x3E, 0x00, 0xFC, 0x01, 0xF0,
0x03, 0xE0, 0x0F, 0xC0, 0x1F, 0x00, 0x00, 0x07, 0xE0, 0x1F, 0xF8, 0x3F,
0xFC, 0x3F, 0xFC, 0x7E, 0x7E, 0x7C, 0x3E, 0xF8, 0x1F, 0xF8, 0x1F, 0xF8,
0x1F, 0xF8, 0x1F, 0xF8, 0x1F, 0xF8, 0x1F, 0xF8, 0x1F, 0xF8, 0x1F, 0xF8,
0x1F, 0x7C, 0x3E, 0x7E, 0x7E, 0x3F, 0xFC, 0x3F, 0xFC, 0x1F, 0xF8, 0x07,
0xE0, 0x03, 0xC1, 0xF1, 0xFD, 0xFF, 0xFF, 0xFF, 0xF7, 0x7D, 0x1F, 0x07,
0xC1, 0xF0, 0x7C, 0x1F, 0x07, 0xC1, 0xF0, 0x7C, 0x1F, 0x07, 0xC1, 0xF0,
0x7C, 0x1F, 0x07, 0xC0, 0x0F, 0xC0, 0x7F, 0xE3, 0xFF, 0xE7, 0xFF, 0xE7,
0x8F, 0xC4, 0x0F, 0x80, 0x1F, 0x00, 0x3E, 0x00, 0xF8, 0x03, 0xF0, 0x0F,
0xC0, 0x3F, 0x00, 0xFE, 0x01, 0xF8, 0x07, 0xE0, 0x1F, 0x80, 0x3E, 0x00,
0xFF, 0xFD, 0xFF, 0xFB, 0xFF, 0xF7, 0xFF, 0xE0, 0x1F, 0xC0, 0xFF, 0xE3,
0xFF, 0xE3, 0xFF, 0xE6, 0x0F, 0xC0, 0x0F, 0x80, 0x1F, 0x00, 0x7C, 0x1F,
0xF8, 0x3F, 0xC0, 0x7F, 0xE0, 0xFF, 0xE0, 0x0F, 0xE0, 0x07, 0xC0, 0x0F,
0x80, 0x1F, 0x60, 0x7F, 0xFF, 0xFB, 0xFF, 0xF7, 0xFF, 0xC3, 0xFC, 0x00,
0x00, 0x7C, 0x00, 0xFC, 0x01, 0xFC, 0x03, 0xFC, 0x07, 0xFC, 0x0F, 0xFC,
0x0F, 0x7C, 0x1F, 0x7C, 0x3E, 0x7C, 0x3C, 0x7C, 0x78, 0x7C, 0xF8, 0x7C,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x7C, 0x00, 0x7C,
0x00, 0x7C, 0x00, 0x7C, 0x00, 0x7C, 0x3F, 0xFC, 0x7F, 0xF8, 0xFF, 0xF1,
0xFF, 0xE3, 0xC0, 0x07, 0x80, 0x1F, 0x00, 0x3F, 0xC0, 0x7F, 0xF0, 0xFF,
0xF1, 0xFF, 0xF0, 0x0F, 0xF0, 0x07, 0xE0, 0x07, 0xC0, 0x0F, 0x80, 0x1F,
0x40, 0x7F, 0xFF, 0xFB, 0xFF, 0xE7, 0xFF, 0x83, 0xFC, 0x00, 0x00, 0x3E,
0x01, 0xFE, 0x07, 0xFE, 0x0F, 0xFE, 0x1F, 0xE0, 0x3F, 0x00, 0x7E, 0x00,
0x7C, 0x00, 0xFF, 0xF0, 0xFF, 0xFC, 0xFF, 0xFE, 0xFF, 0xFE, 0xF8, 0x3F,
0xF8, 0x1F, 0xF8, 0x1F, 0xF8, 0x1F, 0x7C, 0x3F, 0x7F, 0xFE, 0x3F, 0xFC,
0x1F, 0xF8, 0x07, 0xE0, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0x00, 0x3E, 0x00, 0x7C, 0x00, 0xFC, 0x00, 0xF8, 0x01, 0xF8, 0x01, 0xF0,
0x01, 0xF0, 0x03, 0xE0, 0x03, 0xE0, 0x03, 0xE0, 0x07, 0xC0, 0x07, 0xC0,
0x07, 0xC0, 0x0F, 0x80, 0x0F, 0x80, 0x0F, 0x80, 0x0F, 0x80, 0x0F, 0xE0,
0x3F, 0xF8, 0x7F, 0xFC, 0x7F, 0xFE, 0xFC, 0x7E, 0xF8, 0x3E, 0xF8, 0x3E,
0xFC, 0x7C, 0x7F, 0xFC, 0x3F, 0xF0, 0x3F, 0xFC, 0x7F, 0xFE, 0xFC, 0x3E,
0xF8, 0x1F, 0xF8, 0x1F, 0xF8, 0x1F, 0xFC, 0x3F, 0x7F, 0xFE, 0x7F, 0xFE,
0x3F, 0xFC, 0x07, 0xF0, 0x07, 0xE0, 0x1F, 0xF8, 0x3F, 0xFC, 0x7F, 0xFE,
0xFC, 0x3E, 0xF8, 0x3F, 0xF8, 0x1F, 0xF8, 0x1F, 0xFC, 0x1F, 0x7F, 0xFF,
0x7F, 0xFF, 0x3F, 0xFF, 0x0F, 0xDF, 0x00, 0x3E, 0x00, 0x7E, 0x00, 0xFE,
0x03, 0xFC, 0x3F, 0xF8, 0x3F, 0xF0, 0x3F, 0xC0, 0x3E, 0x00, 0x7B, 0xFF,
0xFF, 0xFD, 0xE0, 0x00, 0x00, 0x00, 0x1E, 0xFF, 0xFF, 0xFF, 0x78, 0x7B,
0xFF, 0xFF, 0xFD, 0xE0, 0x00, 0x00, 0x00, 0x00, 0x7D, 0xF7, 0xDF, 0x7D,
0xE7, 0xBE, 0xF0, 0xC0, 0x00, 0x0C, 0x01, 0xF8, 0x3F, 0xFB, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0x3F, 0xE0, 0x7C, 0x00, 0xFF, 0x81, 0xFF, 0xF3, 0xFF,
0xFB, 0xFF, 0xF0, 0xFF, 0xE0, 0x1F, 0x80, 0x03, 0x00, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7F, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF8, 0x60, 0x01, 0xF0, 0x0F, 0xFC, 0x3F,
0xFE, 0xFF, 0xFC, 0xFF, 0xF0, 0x3F, 0xC0, 0x1F, 0x03, 0xFC, 0xFF, 0xFF,
0xFF, 0xFF, 0xFE, 0xFF, 0xC1, 0xF0, 0x06, 0x00, 0x00, 0x3F, 0x8F, 0xFC,
0xFF, 0xE7, 0xFF, 0x63, 0xF0, 0x1F, 0x01, 0xF0, 0x1F, 0x03, 0xE0, 0x7E,
0x0F, 0xC0, 0xF8, 0x1F, 0x01, 0xE0, 0x1E, 0x01, 0xE0, 0x00, 0x00, 0x00,
0x1E, 0x03, 0xF0, 0x3F, 0x03, 0xF0, 0x3F, 0x01, 0xE0, 0x00, 0x7F, 0x80,
0x00, 0x7F, 0xFC, 0x00, 0x7F, 0xFF, 0xC0, 0x3F, 0x03, 0xF8, 0x1F, 0x00,
0x3F, 0x0F, 0x80, 0x03, 0xC3, 0xC1, 0xF8, 0x79, 0xE1, 0xFF, 0x1E, 0x78,
0xFF, 0xC7, 0xFE, 0x3C, 0xF0, 0xFF, 0x1E, 0x3C, 0x3F, 0xC7, 0x8F, 0x0F,
0xF1, 0xE3, 0xC3, 0xFC, 0x78, 0xF0, 0xFF, 0x1E, 0x3C, 0x3F, 0xC7, 0x8F,
0x0F, 0xF1, 0xE3, 0xC7, 0xBE, 0x3C, 0xF1, 0xE7, 0x8F, 0xFF, 0xF1, 0xE1,
0xFF, 0xF8, 0x3C, 0x3E, 0xF8, 0x0F, 0x80, 0x00, 0x01, 0xF0, 0x00, 0x00,
0x3F, 0x80, 0x40, 0x07, 0xFF, 0xF0, 0x00, 0x7F, 0xFC, 0x00, 0x03, 0xFE,
0x00, 0x00, 0xFC, 0x00, 0x0F, 0xF0, 0x00, 0x7F, 0x80, 0x07, 0xFC, 0x00,
0x3F, 0xF0, 0x03, 0xEF, 0x80, 0x1F, 0x7C, 0x00, 0xFB, 0xF0, 0x0F, 0x8F,
0x80, 0x7C, 0x7E, 0x03, 0xE3, 0xF0, 0x3E, 0x0F, 0x81, 0xFF, 0xFC, 0x1F,
0xFF, 0xF0, 0xFF, 0xFF, 0x87, 0xFF, 0xFC, 0x7E, 0x03, 0xF3, 0xE0, 0x0F,
0x9F, 0x00, 0x7D, 0xF8, 0x03, 0xFF, 0x80, 0x0F, 0x80, 0xFF, 0xE0, 0x7F,
0xFE, 0x3F, 0xFF, 0x9F, 0xFF, 0xEF, 0x83, 0xF7, 0xC0, 0xFB, 0xE0, 0x7D,
0xF0, 0x7E, 0xFF, 0xFE, 0x7F, 0xFE, 0x3F, 0xFF, 0x9F, 0xFF, 0xEF, 0x81,
0xFF, 0xC0, 0x7F, 0xE0, 0x3F, 0xF0, 0x1F, 0xF8, 0x1F, 0xFF, 0xFF, 0xBF,
0xFF, 0xDF, 0xFF, 0x87, 0xFF, 0x00, 0x01, 0xFC, 0x03, 0xFF, 0xC7, 0xFF,
0xE7, 0xFF, 0xE3, 0xF0, 0x33, 0xF0, 0x01, 0xF0, 0x01, 0xF0, 0x00, 0xF8,
0x00, 0x7C, 0x00, 0x3E, 0x00, 0x1F, 0x00, 0x0F, 0x80, 0x07, 0xC0, 0x03,
0xF0, 0x00, 0xFC, 0x00, 0x7F, 0x03, 0x1F, 0xFF, 0x87, 0xFF, 0xE1, 0xFF,
0xF0, 0x3F, 0xC0, 0xFF, 0xE0, 0x1F, 0xFF, 0x83, 0xFF, 0xFC, 0x7F, 0xFF,
0xCF, 0x83, 0xF9, 0xF0, 0x1F, 0xBE, 0x01, 0xF7, 0xC0, 0x3F, 0xF8, 0x03,
0xFF, 0x00, 0x7F, 0xE0, 0x0F, 0xFC, 0x01, 0xFF, 0x80, 0x3F, 0xF0, 0x0F,
0xFE, 0x01, 0xF7, 0xC0, 0x7E, 0xF8, 0x3F, 0x9F, 0xFF, 0xF3, 0xFF, 0xFC,
0x7F, 0xFE, 0x0F, 0xFE, 0x00, 0xFF, 0xFE, 0xFF, 0xFE, 0xFF, 0xFE, 0xFF,
0xFE, 0xF8, 0x00, 0xF8, 0x00, 0xF8, 0x00, 0xF8, 0x00, 0xFF, 0xFC, 0xFF,
0xFC, 0xFF, 0xFC, 0xFF, 0xFC, 0xF8, 0x00, 0xF8, 0x00, 0xF8, 0x00, 0xF8,
0x00, 0xF8, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x80, 0x1F, 0x00, 0x3E, 0x00,
0x7C, 0x00, 0xFF, 0xFD, 0xFF, 0xFB, 0xFF, 0xF7, 0xFF, 0xEF, 0x80, 0x1F,
0x00, 0x3E, 0x00, 0x7C, 0x00, 0xF8, 0x01, 0xF0, 0x03, 0xE0, 0x07, 0xC0,
0x0F, 0x80, 0x00, 0x01, 0xFE, 0x01, 0xFF, 0xF1, 0xFF, 0xFC, 0xFF, 0xFE,
0x3F, 0x01, 0x9F, 0x80, 0x07, 0xC0, 0x03, 0xE0, 0x00, 0xF8, 0x00, 0x3E,
0x00, 0x0F, 0x80, 0x7F, 0xE0, 0x1F, 0xF8, 0x07, 0xFE, 0x01, 0xF7, 0xC0,
0x7D, 0xF8, 0x1F, 0x7F, 0x07, 0xCF, 0xFF, 0xF1, 0xFF, 0xFC, 0x3F, 0xFF,
0x01, 0xFF, 0x00, 0xF8, 0x03, 0xFF, 0x00, 0x7F, 0xE0, 0x0F, 0xFC, 0x01,
0xFF, 0x80, 0x3F, 0xF0, 0x07, 0xFE, 0x00, 0xFF, 0xC0, 0x1F, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x80, 0x3F, 0xF0, 0x07,
0xFE, 0x00, 0xFF, 0xC0, 0x1F, 0xF8, 0x03, 0xFF, 0x00, 0x7F, 0xE0, 0x0F,
0xFC, 0x01, 0xFF, 0x80, 0x3E, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x80, 0x00, 0x7C, 0x01, 0xF0, 0x07,
0xC0, 0x1F, 0x00, 0x7C, 0x01, 0xF0, 0x07, 0xC0, 0x1F, 0x00, 0x7C, 0x01,
0xF0, 0x07, 0xC0, 0x1F, 0x00, 0x7C, 0x01, 0xF0, 0x07, 0xC0, 0x1F, 0x60,
0xFD, 0xFF, 0xEF, 0xFF, 0xBF, 0xFC, 0x1F, 0xC0, 0xF8, 0x1F, 0xDF, 0x07,
0xF3, 0xE1, 0xFC, 0x7C, 0x7F, 0x0F, 0x9F, 0xC1, 0xF7, 0xF0, 0x3F, 0xFC,
0x07, 0xFF, 0x00, 0xFF, 0xC0, 0x1F, 0xF0, 0x03, 0xFF, 0x00, 0x7F, 0xF0,
0x0F, 0xFF, 0x01, 0xF7, 0xE0, 0x3E, 0x7E, 0x07, 0xC7, 0xE0, 0xF8, 0x7E,
0x1F, 0x07, 0xE3, 0xE0, 0x7E, 0x7C, 0x0F, 0xEF, 0x80, 0xFE, 0xF8, 0x03,
0xE0, 0x0F, 0x80, 0x3E, 0x00, 0xF8, 0x03, 0xE0, 0x0F, 0x80, 0x3E, 0x00,
0xF8, 0x03, 0xE0, 0x0F, 0x80, 0x3E, 0x00, 0xF8, 0x03, 0xE0, 0x0F, 0x80,
0x3E, 0x00, 0xF8, 0x03, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFC, 0x3E,
0x00, 0x7C, 0x3E, 0x00, 0x7C, 0x3F, 0x00, 0xFC, 0x7F, 0x00, 0xFE, 0x7F,
0x80, 0xFE, 0x7F, 0x81, 0xFE, 0x7F, 0xC1, 0xFE, 0x7F, 0xC3, 0xFE, 0x7F,
0xE3, 0xFE, 0x7D, 0xE7, 0xBE, 0x79, 0xF7, 0x9E, 0x78, 0xF7, 0x9E, 0x78,
0xFF, 0x1E, 0xF8, 0x7F, 0x1E, 0xF8, 0x7E, 0x1F, 0xF8, 0x3E, 0x1F, 0xF8,
0x3E, 0x1F, 0xF8, 0x3C, 0x1F, 0xF8, 0x00, 0x1F, 0xF8, 0x00, 0x1F, 0xF8,
0x00, 0x1F, 0xF8, 0x03, 0xFF, 0x80, 0x7F, 0xF8, 0x0F, 0xFF, 0x01, 0xFF,
0xF0, 0x3F, 0xFF, 0x07, 0xFF, 0xF0, 0xFF, 0xFF, 0x1F, 0xFB, 0xE3, 0xFF,
0x3E, 0x7F, 0xE3, 0xEF, 0xFC, 0x7D, 0xFF, 0x87, 0xFF, 0xF0, 0x7F, 0xFE,
0x07, 0xFF, 0xC0, 0xFF, 0xF8, 0x0F, 0xFF, 0x00, 0xFF, 0xE0, 0x1F, 0xFC,
0x01, 0xFF, 0x80, 0x3E, 0x01, 0xF8, 0x00, 0xFF, 0xF0, 0x1F, 0xFF, 0x83,
0xFF, 0xFC, 0x3F, 0x0F, 0xC7, 0xE0, 0x7E, 0x7C, 0x03, 0xEF, 0x80, 0x1F,
0xF8, 0x01, 0xFF, 0x80, 0x1F, 0xF8, 0x01, 0xFF, 0x80, 0x1F, 0xF8, 0x01,
0xFF, 0x80, 0x1F, 0x7C, 0x03, 0xE7, 0xE0, 0x7E, 0x7F, 0x0F, 0xE3, 0xFF,
0xFC, 0x1F, 0xFF, 0x80, 0xFF, 0xF0, 0x01, 0xF8, 0x00, 0xFF, 0xE0, 0x7F,
0xFE, 0x3F, 0xFF, 0x9F, 0xFF, 0xEF, 0x83, 0xFF, 0xC0, 0x7F, 0xE0, 0x3F,
0xF0, 0x1F, 0xF8, 0x0F, 0xFC, 0x1F, 0xFF, 0xFF, 0xDF, 0xFF, 0xCF, 0xFF,
0xC7, 0xFF, 0x03, 0xE0, 0x01, 0xF0, 0x00, 0xF8, 0x00, 0x7C, 0x00, 0x3E,
0x00, 0x1F, 0x00, 0x0F, 0x80, 0x00, 0x01, 0xF8, 0x00, 0xFF, 0xF0, 0x1F,
0xFF, 0x83, 0xFF, 0xFC, 0x3F, 0x0F, 0xC7, 0xE0, 0x7E, 0x7C, 0x03, 0xEF,
0x80, 0x1F, 0xF8, 0x01, 0xFF, 0x80, 0x1F, 0xF8, 0x01, 0xFF, 0x80, 0x1F,
0xF8, 0x01, 0xFF, 0x80, 0x1F, 0x7C, 0x03, 0xF7, 0xE0, 0x7E, 0x3F, 0x0F,
0xE3, 0xFF, 0xFC, 0x1F, 0xFF, 0xC0, 0xFF, 0xF0, 0x01, 0xFE, 0x00, 0x07,
0xC0, 0x00, 0x7F, 0x00, 0x03, 0xFE, 0x00, 0x1F, 0xE0, 0x00, 0xFC, 0x00,
0x03, 0xC0, 0xFF, 0xE0, 0x3F, 0xFF, 0x0F, 0xFF, 0xE3, 0xFF, 0xFC, 0xF8,
0x3F, 0xBE, 0x03, 0xEF, 0x80, 0xFB, 0xE0, 0x3E, 0xF8, 0x0F, 0xBE, 0x0F,
0xEF, 0xFF, 0xF3, 0xFF, 0xF8, 0xFF, 0xFC, 0x3F, 0xFE, 0x0F, 0x9F, 0xC3,
0xE3, 0xF0, 0xF8, 0x7E, 0x3E, 0x0F, 0xCF, 0x83, 0xF3, 0xE0, 0x7E, 0xF8,
0x0F, 0xC0, 0x07, 0xF0, 0x3F, 0xFE, 0x7F, 0xFE, 0x7F, 0xFC, 0xFC, 0x0C,
0xF8, 0x00, 0xF8, 0x00, 0xFC, 0x00, 0x7F, 0x80, 0x7F, 0xF0, 0x1F, 0xFC,
0x07, 0xFE, 0x00, 0xFF, 0x00, 0x3F, 0x00, 0x1F, 0x00, 0x1F, 0x70, 0x3F,
0x7F, 0xFF, 0xFF, 0xFE, 0xFF, 0xFC, 0x1F, 0xF0, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xF0, 0x3E, 0x00, 0x1F, 0x00, 0x0F, 0x80, 0x07,
0xC0, 0x03, 0xE0, 0x01, 0xF0, 0x00, 0xF8, 0x00, 0x7C, 0x00, 0x3E, 0x00,
0x1F, 0x00, 0x0F, 0x80, 0x07, 0xC0, 0x03, 0xE0, 0x01, 0xF0, 0x00, 0xF8,
0x00, 0x7C, 0x00, 0x3E, 0x00, 0xF8, 0x07, 0xFE, 0x01, 0xFF, 0x80, 0x7F,
0xE0, 0x1F, 0xF8, 0x07, 0xFE, 0x01, 0xFF, 0x80, 0x7F, 0xE0, 0x1F, 0xF8,
0x07, 0xFE, 0x01, 0xFF, 0x80, 0x7F, 0xE0, 0x1F, 0xF8, 0x07, 0xFE, 0x01,
0xFF, 0x80, 0x7F, 0xF0, 0x3F, 0x7E, 0x1F, 0x9F, 0xFF, 0xE3, 0xFF, 0xF0,
0x7F, 0xF8, 0x07, 0xF8, 0x00, 0xF8, 0x00, 0xFF, 0xE0, 0x0F, 0x9F, 0x00,
0x7C, 0xF8, 0x03, 0xE7, 0xE0, 0x3E, 0x1F, 0x01, 0xF0, 0xF8, 0x1F, 0x87,
0xE0, 0xF8, 0x1F, 0x07, 0xC0, 0xF8, 0x7E, 0x07, 0xE3, 0xE0, 0x1F, 0x1F,
0x00, 0xFD, 0xF8, 0x07, 0xEF, 0x80, 0x1F, 0x7C, 0x00, 0xFF, 0xE0, 0x03,
0xFE, 0x00, 0x1F, 0xF0, 0x00, 0xFF, 0x80, 0x03, 0xF8, 0x00, 0x1F, 0xC0,
0x00, 0xF8, 0x00, 0x00, 0xFF, 0xC0, 0x00, 0x0F, 0xDF, 0x03, 0xE0, 0x7C,
0xF8, 0x1F, 0x03, 0xE7, 0xC1, 0xFC, 0x1F, 0x3E, 0x0F, 0xE0, 0xF8, 0xF0,
0x7F, 0x0F, 0x87, 0xC3, 0xF8, 0x7C, 0x3E, 0x3D, 0xE3, 0xE1, 0xF1, 0xEF,
0x1F, 0x07, 0x8F, 0x78, 0xF0, 0x3E, 0xFB, 0xEF, 0x81, 0xF7, 0x8F, 0x7C,
0x0F, 0xBC, 0x7B, 0xE0, 0x3D, 0xE3, 0xFE, 0x01, 0xFF, 0x1F, 0xF0, 0x0F,
0xF0, 0x7F, 0x80, 0x3F, 0x83, 0xF8, 0x01, 0xFC, 0x1F, 0xC0, 0x0F, 0xC0,
0x7E, 0x00, 0x3E, 0x03, 0xE0, 0x00, 0xFC, 0x01, 0xFB, 0xF0, 0x1F, 0x8F,
0xC1, 0xF8, 0x3E, 0x0F, 0x81, 0xF8, 0xFC, 0x07, 0xEF, 0xC0, 0x1F, 0xFC,
0x00, 0xFF, 0xE0, 0x03, 0xFE, 0x00, 0x0F, 0xE0, 0x00, 0x3E, 0x00, 0x03,
0xF8, 0x00, 0x3F, 0xE0, 0x03, 0xFF, 0x80, 0x1F, 0xFC, 0x01, 0xFB, 0xF0,
0x1F, 0x8F, 0xC1, 0xF8, 0x3E, 0x0F, 0xC1, 0xF8, 0xFC, 0x07, 0xEF, 0xC0,
0x1F, 0x80, 0xFC, 0x01, 0xFB, 0xE0, 0x1F, 0x9F, 0x80, 0xFC, 0x7E, 0x0F,
0xC1, 0xF0, 0x7C, 0x0F, 0xC7, 0xE0, 0x3F, 0x7E, 0x01, 0xFB, 0xF0, 0x07,
0xFF, 0x00, 0x1F, 0xF0, 0x00, 0xFF, 0x80, 0x03, 0xF8, 0x00, 0x0F, 0x80,
0x00, 0x7C, 0x00, 0x03, 0xE0, 0x00, 0x1F, 0x00, 0x00, 0xF8, 0x00, 0x07,
0xC0, 0x00, 0x3E, 0x00, 0x01, 0xF0, 0x00, 0x0F, 0x80, 0x00, 0x7F, 0xFF,
0xBF, 0xFF, 0xDF, 0xFF, 0xEF, 0xFF, 0xF0, 0x03, 0xF0, 0x03, 0xF0, 0x03,
0xF0, 0x03, 0xF8, 0x01, 0xF8, 0x01, 0xF8, 0x01, 0xF8, 0x01, 0xFC, 0x00,
0xFC, 0x00, 0xFC, 0x00, 0xFE, 0x00, 0x7E, 0x00, 0x7E, 0x00, 0x7F, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF8, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xF8, 0x3E, 0x0F, 0x83, 0xE0, 0xF8, 0x3E, 0x0F, 0x83, 0xE0, 0xF8, 0x3E,
0x0F, 0x83, 0xE0, 0xF8, 0x3E, 0x0F, 0x83, 0xE0, 0xF8, 0x3E, 0x0F, 0x83,
0xE0, 0xF8, 0x3E, 0x0F, 0xFF, 0xFF, 0xFF, 0xFF, 0xF0, 0xF8, 0x01, 0xF8,
0x01, 0xF0, 0x03, 0xE0, 0x07, 0xE0, 0x07, 0xC0, 0x0F, 0x80, 0x1F, 0x80,
0x1F, 0x00, 0x3E, 0x00, 0x7E, 0x00, 0x7C, 0x00, 0xF8, 0x01, 0xF8, 0x01,
0xF0, 0x03, 0xE0, 0x07, 0xE0, 0x07, 0xC0, 0x0F, 0x80, 0x1F, 0x80, 0x1F,
0x00, 0x3E, 0x00, 0x7E, 0x00, 0x7C, 0x00, 0xF8, 0x01, 0xF8, 0x01, 0xF0,
0x03, 0xE0, 0x07, 0xE0, 0x07, 0xC0, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x07,
0xC1, 0xF0, 0x7C, 0x1F, 0x07, 0xC1, 0xF0, 0x7C, 0x1F, 0x07, 0xC1, 0xF0,
0x7C, 0x1F, 0x07, 0xC1, 0xF0, 0x7C, 0x1F, 0x07, 0xC1, 0xF0, 0x7C, 0x1F,
0x07, 0xC1, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF0, 0x01, 0xC0, 0x01, 0xF0,
0x01, 0xFC, 0x00, 0xFE, 0x00, 0xFF, 0x80, 0x7B, 0xC0, 0x7D, 0xF0, 0x7C,
0x7C, 0x3C, 0x1E, 0x3E, 0x0F, 0xBE, 0x03, 0xEF, 0x01, 0xE1, 0x00, 0x40,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x20, 0xE3, 0xE3, 0xE3,
0xE3, 0x82, 0x00, 0x1F, 0xE0, 0xFF, 0xC3, 0xFF, 0x8F, 0xFF, 0x00, 0xFC,
0x01, 0xF1, 0xFF, 0xDF, 0xFF, 0x7F, 0xFF, 0xF1, 0xFF, 0x87, 0xFE, 0x1F,
0xFF, 0xFD, 0xFF, 0xF7, 0xFF, 0xC7, 0xFC, 0x38, 0x00, 0xF8, 0x00, 0xF8,
0x00, 0xF8, 0x00, 0xF8, 0x00, 0xF8, 0x00, 0xF8, 0x00, 0xF8, 0x00, 0xFB,
0xE0, 0xFF, 0xF8, 0xFF, 0xFC, 0xFF, 0xFE, 0xF8, 0x3E, 0xF8, 0x3F, 0xF8,
0x1F, 0xF8, 0x1F, 0xF8, 0x1F, 0xF8, 0x1F, 0xF8, 0x3F, 0xF8, 0x7E, 0xFF,
0xFE, 0xFF, 0xFC, 0xFF, 0xF8, 0x3F, 0xE0, 0x03, 0xF8, 0x3F, 0xF3, 0xFF,
0xDF, 0xFE, 0x7E, 0x03, 0xF0, 0x0F, 0x80, 0x3E, 0x00, 0xF8, 0x03, 0xE0,
0x0F, 0xC0, 0x1F, 0x80, 0x7F, 0xFC, 0xFF, 0xF1, 0xFF, 0xC1, 0xFE, 0x00,
0x07, 0x00, 0x1F, 0x00, 0x1F, 0x00, 0x1F, 0x00, 0x1F, 0x00, 0x1F, 0x00,
0x1F, 0x00, 0x1F, 0x07, 0xDF, 0x1F, 0xFF, 0x3F, 0xFF, 0x7F, 0xFF, 0x7C,
0x1F, 0xFC, 0x1F, 0xF8, 0x1F, 0xF8, 0x1F, 0xF8, 0x1F, 0xF8, 0x1F, 0xFC,
0x1F, 0x7E, 0x1F, 0x7F, 0xFF, 0x3F, 0xFF, 0x1F, 0xFF, 0x07, 0xFC, 0x07,
0xE0, 0x1F, 0xF8, 0x3F, 0xFC, 0x7F, 0xFE, 0x7C, 0x3F, 0xF8, 0x1F, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF8, 0x00, 0xFC, 0x00, 0x7E, 0x04, 0x7F,
0xFE, 0x3F, 0xFE, 0x1F, 0xFE, 0x07, 0xF8, 0x0F, 0xE3, 0xFF, 0x7F, 0xE7,
0xFE, 0xFC, 0x0F, 0x80, 0xF8, 0x0F, 0x80, 0xFF, 0xEF, 0xFE, 0xFF, 0xEF,
0xFE, 0xF8, 0x0F, 0x80, 0xF8, 0x0F, 0x80, 0xF8, 0x0F, 0x80, 0xF8, 0x0F,
0x80, 0xF8, 0x0F, 0x80, 0xF8, 0x0F, 0x80, 0x07, 0xF8, 0x3F, 0xFC, 0xFF,
0xFB, 0xFF, 0xF7, 0xC3, 0xFF, 0x87, 0xFE, 0x0F, 0xFC, 0x1F, 0xF8, 0x3F,
0xF0, 0x7F, 0xF0, 0xFB, 0xFF, 0xF7, 0xFF, 0xE7, 0xFF, 0xC3, 0xFF, 0x80,
0x1F, 0x00, 0x3E, 0x40, 0xFD, 0xFF, 0xF3, 0xFF, 0xE7, 0xFF, 0x03, 0xF8,
0x00, 0x38, 0x03, 0xE0, 0x0F, 0x80, 0x3E, 0x00, 0xF8, 0x03, 0xE0, 0x0F,
0x80, 0x3E, 0x00, 0xFF, 0xE3, 0xFF, 0xCF, 0xFF, 0xBF, 0xFE, 0xF8, 0xFF,
0xE1, 0xFF, 0x87, 0xFE, 0x1F, 0xF8, 0x7F, 0xE1, 0xFF, 0x87, 0xFE, 0x1F,
0xF8, 0x7F, 0xE1, 0xFF, 0x87, 0xFE, 0x1F, 0x77, 0xFF, 0xF7, 0x00, 0x00,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x07, 0x07,
0xC3, 0xE1, 0xF0, 0x70, 0x00, 0x00, 0x00, 0x0F, 0x87, 0xC3, 0xE1, 0xF0,
0xF8, 0x7C, 0x3E, 0x1F, 0x0F, 0x87, 0xC3, 0xE1, 0xF0, 0xF8, 0x7C, 0x3E,
0x1F, 0x0F, 0x8F, 0xFF, 0xFF, 0xEF, 0xE7, 0xE0, 0x38, 0x01, 0xF0, 0x03,
0xE0, 0x07, 0xC0, 0x0F, 0x80, 0x1F, 0x00, 0x3E, 0x00, 0x7C, 0x00, 0xF8,
0xFF, 0xF1, 0xFB, 0xE7, 0xE7, 0xDF, 0x8F, 0xFF, 0x1F, 0xFC, 0x3F, 0xF0,
0x7F, 0xC0, 0xFF, 0xC1, 0xFF, 0xC3, 0xFF, 0xC7, 0xDF, 0x8F, 0x9F, 0x9F,
0x1F, 0xBE, 0x3F, 0x7C, 0x3F, 0x38, 0xF8, 0xF8, 0xF8, 0xF8, 0xF8, 0xF8,
0xF8, 0xF8, 0xF8, 0xF8, 0xF8, 0xF8, 0xF8, 0xF8, 0xF8, 0xF8, 0xF8, 0xF8,
0xFC, 0xFF, 0x7F, 0x7F, 0x1E, 0x3F, 0xC3, 0xF1, 0xFF, 0xFF, 0xF3, 0xFF,
0xFF, 0xF7, 0xFF, 0xFF, 0xEF, 0x8F, 0xC7, 0xFF, 0x0F, 0x87, 0xFE, 0x1F,
0x0F, 0xFC, 0x3E, 0x1F, 0xF8, 0x7C, 0x3F, 0xF0, 0xF8, 0x7F, 0xE1, 0xF0,
0xFF, 0xC3, 0xE1, 0xFF, 0x87, 0xC3, 0xFF, 0x0F, 0x87, 0xFE, 0x1F, 0x0F,
0xFC, 0x3E, 0x1F, 0x3F, 0xC3, 0xFF, 0xCF, 0xFF, 0xBF, 0xFE, 0xF8, 0xFF,
0xE1, 0xFF, 0x87, 0xFE, 0x1F, 0xF8, 0x7F, 0xE1, 0xFF, 0x87, 0xFE, 0x1F,
0xF8, 0x7F, 0xE1, 0xFF, 0x87, 0xFE, 0x1F, 0x07, 0xF0, 0x0F, 0xFE, 0x0F,
0xFF, 0x8F, 0xFF, 0xE7, 0xE3, 0xF7, 0xE0, 0xFF, 0xE0, 0x3F, 0xF0, 0x1F,
0xF8, 0x0F, 0xFC, 0x07, 0xFF, 0x07, 0xEF, 0xC7, 0xE7, 0xFF, 0xF1, 0xFF,
0xF0, 0x7F, 0xF0, 0x0F, 0xE0, 0x3F, 0xC0, 0xFF, 0xF8, 0xFF, 0xFC, 0xFF,
0xFE, 0xF8, 0x7E, 0xF8, 0x3F, 0xF8, 0x1F, 0xF8, 0x1F, 0xF8, 0x1F, 0xF8,
0x1F, 0xF8, 0x3F, 0xF8, 0x3E, 0xFF, 0xFE, 0xFF, 0xFC, 0xFF, 0xF8, 0xFB,
0xF0, 0xF8, 0x00, 0xF8, 0x00, 0xF8, 0x00, 0xF8, 0x00, 0xF8, 0x00, 0xF8,
0x00, 0x03, 0xFE, 0x0F, 0xFF, 0xCF, 0xFF, 0xEF, 0xFF, 0xF7, 0xE0, 0xFF,
0xE0, 0x7F, 0xE0, 0x3F, 0xF0, 0x1F, 0xF8, 0x0F, 0xFC, 0x07, 0xFF, 0x03,
0xEF, 0xC1, 0xF7, 0xFF, 0xF9, 0xFF, 0xFC, 0x7F, 0xFE, 0x0F, 0xDF, 0x00,
0x0F, 0x80, 0x07, 0xC0, 0x03, 0xE0, 0x01, 0xF0, 0x00, 0xF8, 0x00, 0x7C,
0x1F, 0xFF, 0xFF, 0xFF, 0xFF, 0xEF, 0x81, 0xF0, 0x3E, 0x07, 0xC0, 0xF8,
0x1F, 0x03, 0xE0, 0x7C, 0x0F, 0x81, 0xF0, 0x3E, 0x07, 0xC0, 0x0F, 0xE1,
0xFF, 0x9F, 0xFD, 0xFF, 0xEF, 0x81, 0x7C, 0x03, 0xFF, 0xDF, 0xFE, 0x7F,
0xF9, 0xFF, 0xC0, 0x3F, 0x81, 0xFF, 0xFF, 0xFF, 0xFB, 0xFF, 0x8F, 0xF0,
0x38, 0x0F, 0x80, 0xF8, 0x0F, 0x80, 0xF8, 0x0F, 0xFE, 0xFF, 0xEF, 0xFE,
0xFF, 0xEF, 0x80, 0xF8, 0x0F, 0x80, 0xF8, 0x0F, 0x80, 0xF8, 0x0F, 0x80,
0xFC, 0x0F, 0xFE, 0x7F, 0xE3, 0xFF, 0x0F, 0xE0, 0xF8, 0x7F, 0xE1, 0xFF,
0x87, 0xFE, 0x1F, 0xF8, 0x7F, 0xE1, 0xFF, 0x87, 0xFE, 0x1F, 0xF8, 0x7F,
0xE1, 0xFF, 0x87, 0xFF, 0x1F, 0x7F, 0xFD, 0xFF, 0xF3, 0xFF, 0xC3, 0xFC,
0xF8, 0x0F, 0xFE, 0x0F, 0xDF, 0x07, 0xCF, 0x83, 0xE7, 0xE3, 0xF1, 0xF1,
0xF0, 0xF8, 0xF8, 0x7E, 0xFC, 0x1F, 0x7C, 0x0F, 0xBE, 0x03, 0xFE, 0x01,
0xFF, 0x00, 0xFF, 0x00, 0x3F, 0x80, 0x1F, 0xC0, 0x07, 0xC0, 0xF8, 0x38,
0x1F, 0xF8, 0x3C, 0x1F, 0x7C, 0x7C, 0x3E, 0x7C, 0x7C, 0x3E, 0x7C, 0x7E,
0x3E, 0x3C, 0x7E, 0x3C, 0x3C, 0xFE, 0x3C, 0x3E, 0xEE, 0x7C, 0x1E, 0xEF,
0x78, 0x1E, 0xEF, 0x78, 0x1F, 0xE7, 0xF8, 0x0F, 0xC7, 0xF0, 0x0F, 0xC3,
0xF0, 0x0F, 0xC3, 0xF0, 0x07, 0x83, 0xE0, 0x07, 0x81, 0xE0, 0xFC, 0x1F,
0xBF, 0x1F, 0x8F, 0xDF, 0x87, 0xEF, 0xC1, 0xFF, 0xC0, 0x7F, 0xC0, 0x3F,
0xE0, 0x0F, 0xE0, 0x07, 0xF0, 0x07, 0xF8, 0x03, 0xFE, 0x03, 0xFF, 0x83,
0xF7, 0xE1, 0xFB, 0xF1, 0xF8, 0xFD, 0xF8, 0x3F, 0xF8, 0x0F, 0xFE, 0x0F,
0xDF, 0x07, 0xCF, 0x83, 0xE7, 0xC3, 0xF1, 0xF1, 0xF0, 0xF8, 0xF8, 0x7C,
0x78, 0x1F, 0x7C, 0x0F, 0xBE, 0x03, 0xDE, 0x01, 0xFF, 0x00, 0x7F, 0x00,
0x3F, 0x80, 0x0F, 0xC0, 0x07, 0xC0, 0x03, 0xE0, 0x03, 0xE0, 0x1F, 0xF0,
0x1F, 0xF0, 0x0F, 0xF0, 0x03, 0xE0, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0x03, 0xF8, 0x0F, 0xC0, 0x7E, 0x03, 0xF8, 0x1F, 0xC0, 0xFE,
0x03, 0xF0, 0x1F, 0xC0, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x03,
0xE1, 0xFC, 0x3F, 0x8F, 0xF1, 0xF8, 0x3E, 0x07, 0xC0, 0xF8, 0x1F, 0x03,
0xE0, 0x7C, 0x0F, 0x83, 0xF1, 0xFC, 0x3F, 0x87, 0xE0, 0xFE, 0x07, 0xE0,
0x7C, 0x0F, 0x81, 0xF0, 0x3E, 0x07, 0xC0, 0xF8, 0x1F, 0x03, 0xF0, 0x7F,
0x87, 0xF0, 0x7E, 0x07, 0xC0, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF8, 0x1F, 0x83, 0xF8,
0x7F, 0x83, 0xF0, 0x3E, 0x07, 0xC0, 0xF8, 0x1F, 0x03, 0xE0, 0x7C, 0x0F,
0x81, 0xF8, 0x1F, 0xC3, 0xF8, 0x7F, 0x0F, 0xE3, 0xF0, 0x7C, 0x0F, 0x81,
0xF0, 0x3E, 0x07, 0xC0, 0xF8, 0x1F, 0x07, 0xE3, 0xFC, 0x7F, 0x0F, 0xE1,
0xF0, 0x00, 0x1E, 0x06, 0x3F, 0x8F, 0x7F, 0xFF, 0xFF, 0xFE, 0xF1, 0xFC,
0x60, 0x78 };
const GFXglyph Ubuntu_Bold16pt7bGlyphs[] PROGMEM = {
{ 0, 0, 0, 7, 0, 1 }, // 0x20 ' '
{ 0, 5, 21, 9, 2, -20 }, // 0x21 '!'
{ 14, 12, 9, 14, 2, -23 }, // 0x22 '"'
{ 28, 18, 21, 22, 2, -20 }, // 0x23 '#'
{ 76, 16, 28, 18, 1, -23 }, // 0x24 '$'
{ 132, 26, 21, 28, 1, -20 }, // 0x25 '%'
{ 201, 21, 21, 22, 1, -20 }, // 0x26 '&'
{ 257, 5, 9, 9, 2, -23 }, // 0x27 '''
{ 263, 9, 30, 11, 2, -23 }, // 0x28 '('
{ 297, 9, 30, 11, 0, -23 }, // 0x29 ')'
{ 331, 13, 12, 16, 2, -20 }, // 0x2A '*'
{ 351, 14, 14, 18, 2, -15 }, // 0x2B '+'
{ 376, 6, 10, 8, 1, -4 }, // 0x2C ','
{ 384, 9, 4, 11, 1, -10 }, // 0x2D '-'
{ 389, 6, 6, 8, 1, -5 }, // 0x2E '.'
{ 394, 15, 30, 14, -1, -23 }, // 0x2F '/'
{ 451, 16, 21, 18, 1, -20 }, // 0x30 '0'
{ 493, 10, 21, 18, 3, -20 }, // 0x31 '1'
{ 520, 15, 21, 18, 1, -20 }, // 0x32 '2'
{ 560, 15, 21, 18, 1, -20 }, // 0x33 '3'
{ 600, 16, 21, 18, 1, -20 }, // 0x34 '4'
{ 642, 15, 21, 18, 1, -20 }, // 0x35 '5'
{ 682, 16, 21, 18, 1, -20 }, // 0x36 '6'
{ 724, 16, 21, 18, 1, -20 }, // 0x37 '7'
{ 766, 16, 21, 18, 1, -20 }, // 0x38 '8'
{ 808, 16, 21, 18, 1, -20 }, // 0x39 '9'
{ 850, 6, 17, 8, 1, -16 }, // 0x3A ':'
{ 863, 6, 22, 8, 1, -16 }, // 0x3B ';'
{ 880, 15, 15, 18, 1, -16 }, // 0x3C '<'
{ 909, 15, 11, 18, 1, -14 }, // 0x3D '='
{ 930, 14, 15, 18, 2, -16 }, // 0x3E '>'
{ 957, 12, 24, 14, 1, -23 }, // 0x3F '?'
{ 993, 26, 27, 30, 2, -21 }, // 0x40 '@'
{ 1081, 21, 21, 21, 0, -20 }, // 0x41 'A'
{ 1137, 17, 21, 21, 2, -20 }, // 0x42 'B'
{ 1182, 17, 21, 20, 2, -20 }, // 0x43 'C'
{ 1227, 19, 21, 23, 2, -20 }, // 0x44 'D'
{ 1277, 16, 21, 19, 2, -20 }, // 0x45 'E'
{ 1319, 15, 21, 18, 2, -20 }, // 0x46 'F'
{ 1359, 18, 21, 22, 2, -20 }, // 0x47 'G'
{ 1407, 19, 21, 23, 2, -20 }, // 0x48 'H'
{ 1457, 5, 21, 9, 2, -20 }, // 0x49 'I'
{ 1471, 14, 21, 16, 0, -20 }, // 0x4A 'J'
{ 1508, 19, 21, 21, 2, -20 }, // 0x4B 'K'
{ 1558, 14, 21, 17, 2, -20 }, // 0x4C 'L'
{ 1595, 24, 21, 28, 2, -20 }, // 0x4D 'M'
{ 1658, 19, 21, 23, 2, -20 }, // 0x4E 'N'
{ 1708, 20, 21, 24, 2, -20 }, // 0x4F 'O'
{ 1761, 17, 21, 20, 2, -20 }, // 0x50 'P'
{ 1806, 20, 27, 24, 2, -20 }, // 0x51 'Q'
{ 1874, 18, 21, 21, 2, -20 }, // 0x52 'R'
{ 1922, 16, 21, 18, 1, -20 }, // 0x53 'S'
{ 1964, 17, 21, 19, 1, -20 }, // 0x54 'T'
{ 2009, 18, 21, 22, 2, -20 }, // 0x55 'U'
{ 2057, 21, 21, 21, 0, -20 }, // 0x56 'V'
{ 2113, 29, 21, 31, 1, -20 }, // 0x57 'W'
{ 2190, 21, 21, 21, 0, -20 }, // 0x58 'X'
{ 2246, 21, 21, 21, 0, -20 }, // 0x59 'Y'
{ 2302, 17, 21, 19, 1, -20 }, // 0x5A 'Z'
{ 2347, 10, 30, 12, 2, -23 }, // 0x5B '['
{ 2385, 15, 30, 14, -1, -23 }, // 0x5C '\'
{ 2442, 10, 30, 12, 0, -23 }, // 0x5D ']'
{ 2480, 17, 13, 19, 1, -21 }, // 0x5E '^'
{ 2508, 16, 4, 16, 0, 3 }, // 0x5F '_'
{ 2516, 7, 7, 9, 1, -24 }, // 0x60 '`'
{ 2523, 14, 16, 17, 1, -15 }, // 0x61 'a'
{ 2551, 16, 24, 19, 2, -23 }, // 0x62 'b'
{ 2599, 14, 16, 16, 1, -15 }, // 0x63 'c'
{ 2627, 16, 24, 19, 1, -23 }, // 0x64 'd'
{ 2675, 16, 16, 18, 1, -15 }, // 0x65 'e'
{ 2707, 12, 24, 14, 2, -23 }, // 0x66 'f'
{ 2743, 15, 22, 18, 1, -15 }, // 0x67 'g'
{ 2785, 14, 24, 18, 2, -23 }, // 0x68 'h'
{ 2827, 5, 24, 9, 2, -23 }, // 0x69 'i'
{ 2842, 9, 30, 9, -2, -23 }, // 0x6A 'j'
{ 2876, 15, 24, 18, 2, -23 }, // 0x6B 'k'
{ 2921, 8, 24, 10, 2, -23 }, // 0x6C 'l'
{ 2945, 23, 16, 27, 2, -15 }, // 0x6D 'm'
{ 2991, 14, 16, 18, 2, -15 }, // 0x6E 'n'
{ 3019, 17, 16, 19, 1, -15 }, // 0x6F 'o'
{ 3053, 16, 22, 19, 2, -15 }, // 0x70 'p'
{ 3097, 17, 22, 19, 1, -15 }, // 0x71 'q'
{ 3144, 11, 16, 13, 2, -15 }, // 0x72 'r'
{ 3166, 13, 16, 15, 1, -15 }, // 0x73 's'
{ 3192, 12, 21, 15, 2, -20 }, // 0x74 't'
{ 3224, 14, 16, 18, 2, -15 }, // 0x75 'u'
{ 3252, 17, 16, 17, 0, -15 }, // 0x76 'v'
{ 3286, 24, 16, 24, 0, -15 }, // 0x77 'w'
{ 3334, 17, 16, 17, 0, -15 }, // 0x78 'x'
{ 3368, 17, 22, 17, 0, -15 }, // 0x79 'y'
{ 3415, 14, 16, 16, 1, -15 }, // 0x7A 'z'
{ 3443, 11, 30, 12, 1, -23 }, // 0x7B '{'
{ 3485, 4, 30, 10, 3, -23 }, // 0x7C '|'
{ 3500, 11, 30, 12, 0, -23 }, // 0x7D '}'
{ 3542, 16, 6, 18, 1, -11 } }; // 0x7E '~'
const GFXfont Ubuntu_Bold16pt7b PROGMEM = {
(uint8_t *)Ubuntu_Bold16pt7bBitmaps,
(GFXglyph *)Ubuntu_Bold16pt7bGlyphs,
0x20, 0x7E, 36 };
// Approx. 4226 bytes

View File

@@ -1,578 +0,0 @@
const uint8_t Ubuntu_Bold20pt7bBitmaps[] PROGMEM = {
0x7E, 0x7E, 0x7E, 0x7E, 0x7E, 0x7E, 0x7E, 0x7E, 0x7E, 0x7E, 0x7E, 0x7E,
0x7E, 0x7E, 0x7E, 0x3C, 0x3C, 0x3C, 0x00, 0x00, 0x3C, 0x7E, 0xFF, 0xFF,
0xFF, 0x7E, 0x3C, 0xFC, 0x7F, 0xF8, 0xFF, 0xF1, 0xFF, 0xE3, 0xFF, 0xC7,
0xFF, 0x8F, 0xFF, 0x1F, 0xFE, 0x3F, 0xF8, 0x7C, 0xF0, 0x79, 0xE0, 0xF0,
0x01, 0xF9, 0xF8, 0x03, 0xF3, 0xF0, 0x07, 0xEF, 0xE0, 0x1F, 0x9F, 0x80,
0x3F, 0x3F, 0x00, 0x7E, 0x7E, 0x00, 0xFC, 0xFC, 0x7F, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF8, 0x3F, 0x3F, 0x00, 0x7E,
0x7E, 0x01, 0xFD, 0xFC, 0x03, 0xF3, 0xF0, 0x07, 0xE7, 0xE0, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF1, 0xF9, 0xF8,
0x03, 0xF3, 0xF0, 0x07, 0xE7, 0xE0, 0x0F, 0xCF, 0xC0, 0x3F, 0xBF, 0x00,
0x7E, 0x7E, 0x00, 0xFC, 0xFC, 0x00, 0x00, 0xF8, 0x00, 0x1F, 0x00, 0x03,
0xE0, 0x00, 0x7C, 0x00, 0x1F, 0xF0, 0x1F, 0xFF, 0x87, 0xFF, 0xF1, 0xFF,
0xFE, 0x3F, 0xFF, 0x8F, 0xE0, 0x71, 0xF8, 0x00, 0x3F, 0x00, 0x07, 0xF0,
0x00, 0xFF, 0x80, 0x1F, 0xFE, 0x01, 0xFF, 0xF0, 0x1F, 0xFF, 0x81, 0xFF,
0xF8, 0x0F, 0xFF, 0x00, 0x3F, 0xF0, 0x00, 0xFE, 0x00, 0x0F, 0xC0, 0x01,
0xF8, 0x00, 0x3F, 0x78, 0x0F, 0xEF, 0xFF, 0xF9, 0xFF, 0xFF, 0x7F, 0xFF,
0xC7, 0xFF, 0xF0, 0x1F, 0xF8, 0x00, 0x3E, 0x00, 0x07, 0xC0, 0x00, 0xF8,
0x00, 0x1F, 0x00, 0x03, 0xE0, 0x00, 0x0F, 0x80, 0x03, 0xF0, 0x0F, 0xF8,
0x01, 0xF8, 0x07, 0xFF, 0x00, 0x7E, 0x01, 0xFF, 0xC0, 0x3F, 0x00, 0xF8,
0xF8, 0x0F, 0x80, 0x3C, 0x1E, 0x07, 0xE0, 0x0F, 0x07, 0x83, 0xF0, 0x03,
0xC1, 0xE0, 0xF8, 0x00, 0xF0, 0x78, 0x7E, 0x00, 0x3C, 0x1E, 0x1F, 0x00,
0x0F, 0x07, 0x8F, 0xC0, 0x03, 0xE3, 0xE7, 0xE1, 0xF0, 0x7F, 0xF1, 0xF1,
0xFF, 0x1F, 0xFC, 0xFC, 0xFF, 0xE3, 0xFE, 0x3E, 0x3F, 0xF8, 0x3E, 0x1F,
0x9F, 0x1F, 0x00, 0x0F, 0xC7, 0x83, 0xC0, 0x03, 0xE1, 0xE0, 0xF0, 0x01,
0xF8, 0x78, 0x3C, 0x00, 0x7C, 0x1E, 0x0F, 0x00, 0x3F, 0x07, 0x83, 0xC0,
0x1F, 0x81, 0xE0, 0xF0, 0x07, 0xC0, 0x7C, 0x7C, 0x03, 0xF0, 0x0F, 0xFE,
0x01, 0xF8, 0x03, 0xFF, 0x80, 0x7E, 0x00, 0x7F, 0xC0, 0x3F, 0x00, 0x07,
0xC0, 0x00, 0xFE, 0x00, 0x00, 0x7F, 0xF0, 0x00, 0x3F, 0xFF, 0x00, 0x07,
0xFF, 0xF0, 0x01, 0xFF, 0xFE, 0x00, 0x3F, 0x8F, 0xC0, 0x07, 0xE1, 0xF8,
0x00, 0xFC, 0x3F, 0x00, 0x1F, 0xCF, 0xC0, 0x01, 0xFF, 0xF8, 0x00, 0x3F,
0xFE, 0x00, 0x03, 0xFF, 0x80, 0x00, 0xFF, 0xC0, 0x00, 0x3F, 0xFC, 0x3E,
0x0F, 0xFF, 0xC7, 0xE3, 0xFB, 0xFC, 0xFC, 0x7E, 0x3F, 0xFF, 0x1F, 0x83,
0xFF, 0xE3, 0xF0, 0x3F, 0xF8, 0x7E, 0x07, 0xFF, 0x0F, 0xC0, 0x7F, 0xC1,
0xFE, 0x07, 0xFC, 0x1F, 0xFF, 0xFF, 0xC3, 0xFF, 0xFF, 0xFC, 0x3F, 0xFF,
0xFF, 0x83, 0xFF, 0xF7, 0xF8, 0x0F, 0xF0, 0x7F, 0x80, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xF9, 0xE7, 0x80, 0x01, 0x00, 0x70, 0x1F, 0x87, 0xE0,
0xFC, 0x3F, 0x07, 0xE1, 0xF8, 0x3F, 0x07, 0xE1, 0xF8, 0x3F, 0x07, 0xE1,
0xFC, 0x3F, 0x07, 0xE0, 0xFC, 0x1F, 0x83, 0xF0, 0x7E, 0x0F, 0xC1, 0xF8,
0x3F, 0x07, 0xE0, 0xFC, 0x0F, 0xC1, 0xF8, 0x3F, 0x03, 0xF0, 0x7E, 0x0F,
0xC0, 0xFC, 0x1F, 0x81, 0xF8, 0x3F, 0x03, 0xF0, 0x38, 0x02, 0x00, 0x10,
0x07, 0x03, 0xF0, 0x3F, 0x07, 0xE0, 0x7E, 0x0F, 0xC0, 0xFC, 0x1F, 0x83,
0xF0, 0x3F, 0x07, 0xE0, 0xFC, 0x1F, 0x81, 0xF8, 0x3F, 0x07, 0xE0, 0xFC,
0x1F, 0x83, 0xF0, 0x7E, 0x0F, 0xC1, 0xF8, 0x3F, 0x0F, 0xE1, 0xF8, 0x3F,
0x07, 0xE1, 0xF8, 0x3F, 0x07, 0xE1, 0xF8, 0x3F, 0x0F, 0xC1, 0xF8, 0x7E,
0x03, 0x80, 0x20, 0x00, 0x03, 0xE0, 0x01, 0xF0, 0x00, 0xF8, 0x0C, 0x7C,
0x67, 0x9C, 0xF3, 0xEE, 0xFB, 0xFF, 0xFF, 0xFF, 0xFF, 0x1F, 0xFC, 0x03,
0xF8, 0x03, 0xDE, 0x03, 0xEF, 0x83, 0xF7, 0xE0, 0xF1, 0xE0, 0x38, 0xE0,
0x08, 0x20, 0x01, 0xF0, 0x00, 0x3E, 0x00, 0x07, 0xC0, 0x00, 0xF8, 0x00,
0x1F, 0x00, 0x03, 0xE0, 0x00, 0x7C, 0x07, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF0, 0x1F, 0x00, 0x03, 0xE0, 0x00,
0x7C, 0x00, 0x0F, 0x80, 0x01, 0xF0, 0x00, 0x3E, 0x00, 0x07, 0xC0, 0x00,
0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x7E, 0x7E, 0x7E, 0x7C, 0xFC, 0x18,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0x38, 0xFB, 0xFF, 0xFF, 0xEF,
0x8E, 0x00, 0x00, 0x07, 0xE0, 0x01, 0xFC, 0x00, 0x3F, 0x00, 0x07, 0xE0,
0x01, 0xFC, 0x00, 0x3F, 0x00, 0x07, 0xE0, 0x01, 0xFC, 0x00, 0x3F, 0x00,
0x07, 0xE0, 0x01, 0xF8, 0x00, 0x3F, 0x00, 0x07, 0xE0, 0x01, 0xF8, 0x00,
0x3F, 0x00, 0x07, 0xE0, 0x01, 0xF8, 0x00, 0x3F, 0x00, 0x07, 0xE0, 0x01,
0xF8, 0x00, 0x3F, 0x00, 0x07, 0xE0, 0x01, 0xF8, 0x00, 0x3F, 0x00, 0x07,
0xE0, 0x01, 0xF8, 0x00, 0x3F, 0x00, 0x07, 0xE0, 0x01, 0xF8, 0x00, 0x3F,
0x00, 0x0F, 0xE0, 0x01, 0xF8, 0x00, 0x3F, 0x00, 0x0F, 0xE0, 0x01, 0xF8,
0x00, 0x3F, 0x00, 0x0F, 0xE0, 0x01, 0xF8, 0x00, 0x00, 0x01, 0xF8, 0x00,
0x7F, 0xE0, 0x0F, 0xFF, 0x01, 0xFF, 0xF8, 0x3F, 0xFF, 0xC3, 0xF0, 0xFE,
0x7E, 0x07, 0xE7, 0xE0, 0x7E, 0xFE, 0x07, 0xEF, 0xC0, 0x3F, 0xFC, 0x03,
0xFF, 0xC0, 0x3F, 0xFC, 0x03, 0xFF, 0xC0, 0x3F, 0xFC, 0x03, 0xFF, 0xC0,
0x3F, 0xFC, 0x03, 0xFF, 0xC0, 0x3F, 0xFC, 0x07, 0xF7, 0xE0, 0x7E, 0x7E,
0x07, 0xE7, 0xF0, 0xFE, 0x3F, 0xFF, 0xC1, 0xFF, 0xF8, 0x1F, 0xFF, 0x00,
0x7F, 0xE0, 0x01, 0xF8, 0x00, 0x00, 0xF8, 0x0F, 0xC0, 0xFE, 0x0F, 0xF1,
0xFF, 0xBF, 0xFF, 0xFF, 0xEF, 0xBF, 0x79, 0xF9, 0x0F, 0xC0, 0x7E, 0x03,
0xF0, 0x1F, 0x80, 0xFC, 0x07, 0xE0, 0x3F, 0x01, 0xF8, 0x0F, 0xC0, 0x7E,
0x03, 0xF0, 0x1F, 0x80, 0xFC, 0x07, 0xE0, 0x3F, 0x01, 0xF8, 0x0F, 0xC0,
0x7E, 0x07, 0xF8, 0x03, 0xFF, 0xC1, 0xFF, 0xFC, 0x7F, 0xFF, 0xC7, 0xFF,
0xFC, 0xF8, 0x3F, 0x8C, 0x03, 0xF0, 0x00, 0x7E, 0x00, 0x0F, 0xC0, 0x01,
0xF8, 0x00, 0x7E, 0x00, 0x1F, 0xC0, 0x07, 0xF0, 0x01, 0xFE, 0x00, 0x7F,
0x80, 0x1F, 0xE0, 0x07, 0xF8, 0x01, 0xFE, 0x00, 0x7F, 0x80, 0x0F, 0xE0,
0x03, 0xF8, 0x00, 0x7E, 0x00, 0x1F, 0xFF, 0xFB, 0xFF, 0xFF, 0x7F, 0xFF,
0xEF, 0xFF, 0xFD, 0xFF, 0xFF, 0x80, 0x07, 0xF0, 0x07, 0xFF, 0xC3, 0xFF,
0xFC, 0x3F, 0xFF, 0xC7, 0xFF, 0xF8, 0x70, 0x3F, 0x88, 0x03, 0xF0, 0x00,
0x7E, 0x00, 0x0F, 0xC0, 0x07, 0xF8, 0x3F, 0xFE, 0x07, 0xFF, 0x80, 0xFF,
0xE0, 0x1F, 0xFE, 0x03, 0xFF, 0xF0, 0x01, 0xFE, 0x00, 0x0F, 0xE0, 0x00,
0xFC, 0x00, 0x1F, 0x80, 0x03, 0xF0, 0x00, 0xFE, 0xE0, 0x3F, 0xDF, 0xFF,
0xF7, 0xFF, 0xFE, 0xFF, 0xFF, 0x9F, 0xFF, 0xC0, 0x3F, 0xC0, 0x00, 0x00,
0x1F, 0x80, 0x03, 0xF8, 0x00, 0x7F, 0x80, 0x0F, 0xF8, 0x01, 0xFF, 0x80,
0x1F, 0xF8, 0x03, 0xFF, 0x80, 0x7F, 0xF8, 0x07, 0xDF, 0x80, 0xF9, 0xF8,
0x1F, 0x9F, 0x81, 0xF1, 0xF8, 0x3E, 0x1F, 0x83, 0xE1, 0xF8, 0x7C, 0x1F,
0x8F, 0xC1, 0xF8, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xF0, 0x01, 0xF8, 0x00, 0x1F, 0x80, 0x01, 0xF8, 0x00,
0x1F, 0x80, 0x01, 0xF8, 0x00, 0x1F, 0x80, 0x1F, 0xFF, 0xC3, 0xFF, 0xF8,
0x7F, 0xFF, 0x0F, 0xFF, 0xE1, 0xFF, 0xFC, 0x3E, 0x00, 0x07, 0xC0, 0x00,
0xF8, 0x00, 0x3F, 0x00, 0x07, 0xE0, 0x00, 0xFF, 0x80, 0x1F, 0xFE, 0x03,
0xFF, 0xF0, 0x7F, 0xFF, 0x0F, 0xFF, 0xF0, 0x03, 0xFE, 0x00, 0x0F, 0xE0,
0x00, 0xFC, 0x00, 0x1F, 0x80, 0x03, 0xF0, 0x00, 0xFE, 0xC0, 0x3F, 0xDF,
0xFF, 0xF7, 0xFF, 0xFC, 0xFF, 0xFF, 0x1F, 0xFF, 0xC0, 0x7F, 0xC0, 0x00,
0x00, 0x07, 0xC0, 0x07, 0xFC, 0x01, 0xFF, 0xC0, 0x3F, 0xFC, 0x0F, 0xFF,
0xC1, 0xFF, 0xC0, 0x1F, 0xE0, 0x03, 0xF8, 0x00, 0x7F, 0x00, 0x07, 0xE0,
0x00, 0x7F, 0xFC, 0x0F, 0xFF, 0xF8, 0xFF, 0xFF, 0xCF, 0xFF, 0xFE, 0xFF,
0xFF, 0xEF, 0xC0, 0xFF, 0xFC, 0x07, 0xFF, 0xC0, 0x3F, 0xFC, 0x03, 0xFF,
0xC0, 0x3F, 0x7E, 0x07, 0xF7, 0xF0, 0xFE, 0x3F, 0xFF, 0xE3, 0xFF, 0xFC,
0x1F, 0xFF, 0x80, 0xFF, 0xF0, 0x01, 0xFC, 0x00, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xC0, 0x07, 0xE0, 0x03, 0xF0,
0x01, 0xFC, 0x00, 0x7E, 0x00, 0x3F, 0x00, 0x0F, 0xC0, 0x07, 0xE0, 0x01,
0xF8, 0x00, 0xFC, 0x00, 0x3F, 0x00, 0x0F, 0xC0, 0x07, 0xE0, 0x01, 0xF8,
0x00, 0x7E, 0x00, 0x3F, 0x00, 0x0F, 0xC0, 0x03, 0xF0, 0x00, 0xFC, 0x00,
0x7E, 0x00, 0x1F, 0x80, 0x07, 0xE0, 0x01, 0xF8, 0x00, 0x03, 0xF8, 0x01,
0xFF, 0xC0, 0x7F, 0xFC, 0x1F, 0xFF, 0xC3, 0xFF, 0xFC, 0xFE, 0x3F, 0x9F,
0x83, 0xF3, 0xF0, 0x7E, 0x7E, 0x0F, 0xCF, 0xE3, 0xF0, 0xFE, 0x7E, 0x0F,
0xFF, 0x80, 0xFF, 0xE0, 0x1F, 0xFE, 0x07, 0xFF, 0xE1, 0xF9, 0xFE, 0x7E,
0x0F, 0xDF, 0x80, 0xFF, 0xF0, 0x1F, 0xFE, 0x03, 0xFF, 0xC0, 0x7F, 0xFC,
0x1F, 0xDF, 0xFF, 0xF3, 0xFF, 0xFE, 0x3F, 0xFF, 0x83, 0xFF, 0xE0, 0x0F,
0xE0, 0x00, 0x03, 0xF8, 0x01, 0xFF, 0xC0, 0x7F, 0xFC, 0x1F, 0xFF, 0xC7,
0xFF, 0xF8, 0xFE, 0x3F, 0xBF, 0x83, 0xF7, 0xE0, 0x3F, 0xFC, 0x07, 0xFF,
0x80, 0xFF, 0xF0, 0x1F, 0xFF, 0x03, 0xF7, 0xFF, 0xFE, 0xFF, 0xFF, 0xCF,
0xFF, 0xF8, 0xFF, 0xFF, 0x07, 0xFF, 0xE0, 0x01, 0xF8, 0x00, 0x7F, 0x00,
0x0F, 0xC0, 0x07, 0xF8, 0x07, 0xFE, 0x0F, 0xFF, 0x81, 0xFF, 0xE0, 0x3F,
0xF8, 0x07, 0xFC, 0x00, 0xF8, 0x00, 0x00, 0x38, 0xFB, 0xFF, 0xFF, 0xEF,
0x8E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0E, 0x3E, 0xFF, 0xFF, 0xFB,
0xE3, 0x80, 0x1C, 0x3E, 0x7F, 0x7F, 0x7F, 0x3E, 0x1C, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x7E, 0x7E,
0x7E, 0x7C, 0xFC, 0x18, 0x00, 0x01, 0x80, 0x01, 0xF0, 0x01, 0xFF, 0x03,
0xFF, 0xE3, 0xFF, 0xFD, 0xFF, 0xFF, 0xBF, 0xFF, 0xC7, 0xFF, 0xC0, 0xFF,
0x80, 0x1F, 0x80, 0x03, 0xFE, 0x00, 0x7F, 0xFC, 0x0F, 0xFF, 0xF1, 0xFF,
0xFF, 0x8F, 0xFF, 0xF0, 0x3F, 0xFE, 0x00, 0x7F, 0xC0, 0x01, 0xF0, 0x00,
0x06, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3F, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF0, 0x30, 0x00,
0x07, 0xC0, 0x01, 0xFF, 0x00, 0x3F, 0xFE, 0x07, 0xFF, 0xF8, 0xFF, 0xFF,
0xC7, 0xFF, 0xF8, 0x1F, 0xFF, 0x00, 0x3F, 0xE0, 0x00, 0xFC, 0x00, 0xFF,
0x81, 0xFF, 0xF1, 0xFF, 0xFE, 0xFF, 0xFF, 0xDF, 0xFF, 0xE3, 0xFF, 0xE0,
0x7F, 0xC0, 0x07, 0xC0, 0x00, 0xC0, 0x00, 0x00, 0x0F, 0xE0, 0x7F, 0xF8,
0xFF, 0xFC, 0x7F, 0xFE, 0x7F, 0xFF, 0x70, 0x7F, 0x00, 0x3F, 0x00, 0x3F,
0x00, 0x3F, 0x00, 0x7E, 0x00, 0xFE, 0x01, 0xFC, 0x01, 0xF8, 0x03, 0xF8,
0x03, 0xF0, 0x07, 0xE0, 0x07, 0xC0, 0x07, 0xC0, 0x07, 0xC0, 0x00, 0x00,
0x00, 0x00, 0x03, 0x80, 0x07, 0xC0, 0x0F, 0xE0, 0x0F, 0xE0, 0x0F, 0xE0,
0x07, 0xC0, 0x03, 0x80, 0x00, 0x07, 0xFC, 0x00, 0x00, 0x0F, 0xFF, 0xE0,
0x00, 0x0F, 0xFF, 0xFE, 0x00, 0x0F, 0xFF, 0xFF, 0xE0, 0x07, 0xFC, 0x07,
0xFC, 0x03, 0xF8, 0x00, 0x3F, 0x81, 0xFC, 0x00, 0x07, 0xF0, 0x7C, 0x00,
0x00, 0xFC, 0x3F, 0x01, 0xFC, 0x1F, 0x8F, 0x81, 0xFF, 0xC3, 0xE7, 0xC1,
0xFF, 0xF0, 0xF9, 0xF0, 0x7F, 0xFC, 0x1F, 0xFC, 0x3F, 0x1F, 0x07, 0xFE,
0x0F, 0x87, 0xC1, 0xFF, 0x87, 0xC1, 0xF0, 0x7F, 0xE1, 0xF0, 0x7C, 0x1F,
0xF8, 0x7C, 0x1F, 0x07, 0xFE, 0x1F, 0x07, 0xC1, 0xFF, 0x87, 0xC1, 0xF0,
0x7F, 0xE1, 0xF0, 0x7C, 0x3E, 0xF8, 0x7E, 0x1F, 0x0F, 0xBF, 0x0F, 0xC7,
0xC7, 0xE7, 0xC3, 0xFF, 0xFF, 0xF1, 0xF0, 0x7F, 0xFF, 0xF8, 0x7E, 0x0F,
0xFF, 0xFC, 0x0F, 0x80, 0xF8, 0xFC, 0x03, 0xF0, 0x00, 0x00, 0x00, 0x7E,
0x00, 0x00, 0x00, 0x0F, 0xE0, 0x00, 0x00, 0x01, 0xFF, 0x00, 0x20, 0x00,
0x3F, 0xFF, 0xF8, 0x00, 0x03, 0xFF, 0xFE, 0x00, 0x00, 0x3F, 0xFF, 0xC0,
0x00, 0x01, 0xFF, 0x80, 0x00, 0x00, 0x3F, 0x80, 0x00, 0x07, 0xF0, 0x00,
0x01, 0xFF, 0x00, 0x00, 0x3F, 0xE0, 0x00, 0x0F, 0xFE, 0x00, 0x01, 0xFF,
0xC0, 0x00, 0x7E, 0xF8, 0x00, 0x0F, 0xDF, 0x80, 0x01, 0xFB, 0xF0, 0x00,
0x7E, 0x3F, 0x00, 0x0F, 0xC7, 0xE0, 0x03, 0xF8, 0xFC, 0x00, 0x7E, 0x0F,
0xC0, 0x0F, 0xC1, 0xF8, 0x03, 0xF8, 0x3F, 0x80, 0x7E, 0x03, 0xF0, 0x0F,
0xFF, 0xFE, 0x03, 0xFF, 0xFF, 0xE0, 0x7F, 0xFF, 0xFC, 0x0F, 0xFF, 0xFF,
0x83, 0xFF, 0xFF, 0xF8, 0x7E, 0x00, 0x3F, 0x1F, 0xC0, 0x07, 0xE3, 0xF8,
0x00, 0xFE, 0x7E, 0x00, 0x0F, 0xDF, 0xC0, 0x01, 0xFB, 0xF8, 0x00, 0x3F,
0x80, 0x7F, 0xFC, 0x07, 0xFF, 0xFC, 0x3F, 0xFF, 0xF1, 0xFF, 0xFF, 0xCF,
0xFF, 0xFF, 0x7E, 0x03, 0xFB, 0xF0, 0x0F, 0xDF, 0x80, 0x7E, 0xFC, 0x03,
0xF7, 0xE0, 0x7F, 0x3F, 0xFF, 0xF9, 0xFF, 0xFF, 0x8F, 0xFF, 0xFC, 0x7F,
0xFF, 0xF3, 0xFF, 0xFF, 0xDF, 0x80, 0xFF, 0xFC, 0x03, 0xFF, 0xE0, 0x0F,
0xFF, 0x00, 0x7F, 0xF8, 0x03, 0xFF, 0xC0, 0x3F, 0xFE, 0x03, 0xFF, 0xFF,
0xFF, 0xDF, 0xFF, 0xFC, 0xFF, 0xFF, 0xC7, 0xFF, 0xFC, 0x0F, 0xFF, 0x00,
0x00, 0x3F, 0xC0, 0x07, 0xFF, 0xE0, 0x7F, 0xFF, 0xC3, 0xFF, 0xFE, 0x1F,
0xFF, 0xF8, 0xFF, 0x81, 0xE3, 0xF8, 0x00, 0x1F, 0xC0, 0x00, 0x7E, 0x00,
0x03, 0xF8, 0x00, 0x0F, 0xC0, 0x00, 0x3F, 0x00, 0x00, 0xFC, 0x00, 0x03,
0xF0, 0x00, 0x0F, 0xC0, 0x00, 0x3F, 0x00, 0x00, 0xFC, 0x00, 0x03, 0xF8,
0x00, 0x07, 0xE0, 0x00, 0x1F, 0xC0, 0x00, 0x7F, 0x80, 0x00, 0xFF, 0x00,
0xE1, 0xFF, 0xFF, 0x87, 0xFF, 0xFE, 0x0F, 0xFF, 0xFC, 0x0F, 0xFF, 0xE0,
0x07, 0xFC, 0x00, 0x7F, 0xFC, 0x00, 0xFF, 0xFF, 0x80, 0xFF, 0xFF, 0xE0,
0xFF, 0xFF, 0xF0, 0xFF, 0xFF, 0xF8, 0xFC, 0x07, 0xFC, 0xFC, 0x01, 0xFC,
0xFC, 0x00, 0xFE, 0xFC, 0x00, 0x7E, 0xFC, 0x00, 0x7F, 0xFC, 0x00, 0x3F,
0xFC, 0x00, 0x3F, 0xFC, 0x00, 0x3F, 0xFC, 0x00, 0x3F, 0xFC, 0x00, 0x3F,
0xFC, 0x00, 0x3F, 0xFC, 0x00, 0x3F, 0xFC, 0x00, 0x7F, 0xFC, 0x00, 0x7E,
0xFC, 0x00, 0xFE, 0xFC, 0x01, 0xFC, 0xFC, 0x0F, 0xFC, 0xFF, 0xFF, 0xF8,
0xFF, 0xFF, 0xF0, 0xFF, 0xFF, 0xE0, 0xFF, 0xFF, 0x80, 0x7F, 0xF8, 0x00,
0xFF, 0xFF, 0xEF, 0xFF, 0xFE, 0xFF, 0xFF, 0xEF, 0xFF, 0xFE, 0xFF, 0xFF,
0xEF, 0xC0, 0x00, 0xFC, 0x00, 0x0F, 0xC0, 0x00, 0xFC, 0x00, 0x0F, 0xC0,
0x00, 0xFF, 0xFF, 0xCF, 0xFF, 0xFC, 0xFF, 0xFF, 0xCF, 0xFF, 0xFC, 0xFF,
0xFF, 0xCF, 0xC0, 0x00, 0xFC, 0x00, 0x0F, 0xC0, 0x00, 0xFC, 0x00, 0x0F,
0xC0, 0x00, 0xFC, 0x00, 0x0F, 0xC0, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF0, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x0F, 0xC0, 0x03,
0xF0, 0x00, 0xFC, 0x00, 0x3F, 0x00, 0x0F, 0xC0, 0x03, 0xFF, 0xFE, 0xFF,
0xFF, 0xBF, 0xFF, 0xEF, 0xFF, 0xFB, 0xFF, 0xFE, 0xFC, 0x00, 0x3F, 0x00,
0x0F, 0xC0, 0x03, 0xF0, 0x00, 0xFC, 0x00, 0x3F, 0x00, 0x0F, 0xC0, 0x03,
0xF0, 0x00, 0xFC, 0x00, 0x3F, 0x00, 0x0F, 0xC0, 0x00, 0x00, 0x7F, 0xC0,
0x07, 0xFF, 0xE0, 0x7F, 0xFF, 0xC3, 0xFF, 0xFE, 0x1F, 0xFF, 0xF8, 0xFF,
0x81, 0xE3, 0xF8, 0x00, 0x1F, 0xC0, 0x00, 0x7E, 0x00, 0x03, 0xF8, 0x00,
0x0F, 0xC0, 0x00, 0x3F, 0x00, 0x00, 0xFC, 0x00, 0x03, 0xF0, 0x03, 0xFF,
0xC0, 0x0F, 0xFF, 0x00, 0x3F, 0xFC, 0x00, 0xFF, 0xF8, 0x03, 0xF7, 0xE0,
0x0F, 0xDF, 0xC0, 0x3F, 0x7F, 0x80, 0xFC, 0xFF, 0x03, 0xF1, 0xFF, 0xFF,
0xC3, 0xFF, 0xFF, 0x07, 0xFF, 0xFC, 0x0F, 0xFF, 0xF0, 0x07, 0xFE, 0x00,
0xFC, 0x00, 0x7F, 0xF8, 0x00, 0xFF, 0xF0, 0x01, 0xFF, 0xE0, 0x03, 0xFF,
0xC0, 0x07, 0xFF, 0x80, 0x0F, 0xFF, 0x00, 0x1F, 0xFE, 0x00, 0x3F, 0xFC,
0x00, 0x7F, 0xF8, 0x00, 0xFF, 0xF0, 0x01, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFC, 0x00,
0x7F, 0xF8, 0x00, 0xFF, 0xF0, 0x01, 0xFF, 0xE0, 0x03, 0xFF, 0xC0, 0x07,
0xFF, 0x80, 0x0F, 0xFF, 0x00, 0x1F, 0xFE, 0x00, 0x3F, 0xFC, 0x00, 0x7F,
0xF8, 0x00, 0xFF, 0xF0, 0x01, 0xF8, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xC0, 0x00, 0x0F, 0xC0, 0x03, 0xF0, 0x00, 0xFC, 0x00, 0x3F,
0x00, 0x0F, 0xC0, 0x03, 0xF0, 0x00, 0xFC, 0x00, 0x3F, 0x00, 0x0F, 0xC0,
0x03, 0xF0, 0x00, 0xFC, 0x00, 0x3F, 0x00, 0x0F, 0xC0, 0x03, 0xF0, 0x00,
0xFC, 0x00, 0x3F, 0x00, 0x0F, 0xC0, 0x03, 0xF0, 0x00, 0xFC, 0x00, 0x3F,
0x00, 0x1F, 0xDE, 0x0F, 0xE7, 0xFF, 0xF9, 0xFF, 0xFE, 0xFF, 0xFF, 0x1F,
0xFF, 0x00, 0xFF, 0x00, 0xFC, 0x01, 0xFE, 0xFC, 0x03, 0xFC, 0xFC, 0x07,
0xF8, 0xFC, 0x0F, 0xF0, 0xFC, 0x1F, 0xE0, 0xFC, 0x3F, 0xC0, 0xFC, 0x7F,
0x80, 0xFC, 0xFF, 0x00, 0xFD, 0xFE, 0x00, 0xFF, 0xFC, 0x00, 0xFF, 0xF8,
0x00, 0xFF, 0xF0, 0x00, 0xFF, 0xE0, 0x00, 0xFF, 0xF0, 0x00, 0xFF, 0xF8,
0x00, 0xFF, 0xFC, 0x00, 0xFD, 0xFE, 0x00, 0xFC, 0xFE, 0x00, 0xFC, 0x7F,
0x00, 0xFC, 0x3F, 0x80, 0xFC, 0x3F, 0xC0, 0xFC, 0x1F, 0xE0, 0xFC, 0x0F,
0xF0, 0xFC, 0x07, 0xF8, 0xFC, 0x03, 0xFC, 0xFC, 0x01, 0xFE, 0xFC, 0x00,
0xFF, 0xFC, 0x00, 0x3F, 0x00, 0x0F, 0xC0, 0x03, 0xF0, 0x00, 0xFC, 0x00,
0x3F, 0x00, 0x0F, 0xC0, 0x03, 0xF0, 0x00, 0xFC, 0x00, 0x3F, 0x00, 0x0F,
0xC0, 0x03, 0xF0, 0x00, 0xFC, 0x00, 0x3F, 0x00, 0x0F, 0xC0, 0x03, 0xF0,
0x00, 0xFC, 0x00, 0x3F, 0x00, 0x0F, 0xC0, 0x03, 0xF0, 0x00, 0xFC, 0x00,
0x3F, 0x00, 0x0F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFC, 0x3F, 0x00, 0x01, 0xF8, 0x7F, 0x00, 0x07, 0xF0, 0xFE, 0x00,
0x0F, 0xE1, 0xFE, 0x00, 0x3F, 0xC7, 0xFC, 0x00, 0x7F, 0xCF, 0xFC, 0x01,
0xFF, 0x9F, 0xF8, 0x03, 0xFF, 0x3F, 0xF8, 0x07, 0xFE, 0x7F, 0xF0, 0x1F,
0xFC, 0xFF, 0xE0, 0x3F, 0xF9, 0xFF, 0xE0, 0xFB, 0xF3, 0xF7, 0xC1, 0xF7,
0xE7, 0xEF, 0xC7, 0xEF, 0xCF, 0xCF, 0x8F, 0x9F, 0x9F, 0x9F, 0x1F, 0x3F,
0x3E, 0x1F, 0x7C, 0x3E, 0x7C, 0x3E, 0xF8, 0x7D, 0xF8, 0x7F, 0xE0, 0xFB,
0xF0, 0x7F, 0xC1, 0xFF, 0xE0, 0xFF, 0x03, 0xFF, 0xC0, 0xFE, 0x07, 0xFF,
0x81, 0xFC, 0x0F, 0xFF, 0x01, 0xF0, 0x1F, 0xFE, 0x03, 0xE0, 0x3F, 0xFC,
0x00, 0x00, 0x7F, 0xF8, 0x00, 0x00, 0xFF, 0xF0, 0x00, 0x01, 0xF8, 0xF8,
0x00, 0x7F, 0xF8, 0x00, 0xFF, 0xF8, 0x01, 0xFF, 0xF8, 0x03, 0xFF, 0xF8,
0x07, 0xFF, 0xF8, 0x0F, 0xFF, 0xF0, 0x1F, 0xFF, 0xF0, 0x3F, 0xFF, 0xF0,
0x7F, 0xFF, 0xF0, 0xFF, 0xF7, 0xE1, 0xFF, 0xE7, 0xE3, 0xFF, 0xCF, 0xE7,
0xFF, 0x8F, 0xCF, 0xFF, 0x0F, 0xDF, 0xFE, 0x0F, 0xFF, 0xFC, 0x1F, 0xFF,
0xF8, 0x1F, 0xFF, 0xF0, 0x1F, 0xFF, 0xE0, 0x3F, 0xFF, 0xC0, 0x3F, 0xFF,
0x80, 0x3F, 0xFF, 0x00, 0x7F, 0xFE, 0x00, 0x7F, 0xFC, 0x00, 0xFF, 0xF8,
0x00, 0xFF, 0xF0, 0x00, 0xF8, 0x00, 0x3F, 0x80, 0x00, 0x3F, 0xFE, 0x00,
0x1F, 0xFF, 0xF0, 0x07, 0xFF, 0xFF, 0x01, 0xFF, 0xFF, 0xF0, 0x7F, 0xC1,
0xFF, 0x0F, 0xE0, 0x0F, 0xE3, 0xF8, 0x00, 0xFE, 0x7E, 0x00, 0x0F, 0xDF,
0xC0, 0x01, 0xFF, 0xF0, 0x00, 0x1F, 0xFE, 0x00, 0x03, 0xFF, 0xC0, 0x00,
0x7F, 0xF8, 0x00, 0x0F, 0xFF, 0x00, 0x01, 0xFF, 0xE0, 0x00, 0x3F, 0xFC,
0x00, 0x07, 0xFF, 0xC0, 0x01, 0xFD, 0xF8, 0x00, 0x3F, 0x3F, 0x80, 0x0F,
0xE3, 0xF8, 0x03, 0xF8, 0x7F, 0xC1, 0xFF, 0x07, 0xFF, 0xFF, 0xC0, 0x7F,
0xFF, 0xF0, 0x07, 0xFF, 0xFC, 0x00, 0x3F, 0xFE, 0x00, 0x01, 0xFF, 0x00,
0x00, 0x7F, 0xF8, 0x0F, 0xFF, 0xF0, 0xFF, 0xFF, 0x8F, 0xFF, 0xFC, 0xFF,
0xFF, 0xEF, 0xC0, 0xFE, 0xFC, 0x07, 0xFF, 0xC0, 0x3F, 0xFC, 0x03, 0xFF,
0xC0, 0x3F, 0xFC, 0x03, 0xFF, 0xC0, 0x7F, 0xFC, 0x0F, 0xEF, 0xFF, 0xFE,
0xFF, 0xFF, 0xCF, 0xFF, 0xF8, 0xFF, 0xFF, 0x0F, 0xFF, 0x80, 0xFC, 0x00,
0x0F, 0xC0, 0x00, 0xFC, 0x00, 0x0F, 0xC0, 0x00, 0xFC, 0x00, 0x0F, 0xC0,
0x00, 0xFC, 0x00, 0x0F, 0xC0, 0x00, 0xFC, 0x00, 0x00, 0x00, 0x3F, 0x80,
0x00, 0x3F, 0xFE, 0x00, 0x1F, 0xFF, 0xF0, 0x07, 0xFF, 0xFF, 0x01, 0xFF,
0xFF, 0xF0, 0x7F, 0xC1, 0xFF, 0x0F, 0xE0, 0x0F, 0xE3, 0xF8, 0x00, 0xFE,
0x7E, 0x00, 0x0F, 0xDF, 0xC0, 0x01, 0xFB, 0xF0, 0x00, 0x1F, 0xFE, 0x00,
0x03, 0xFF, 0xC0, 0x00, 0x7F, 0xF8, 0x00, 0x0F, 0xFF, 0x00, 0x01, 0xFF,
0xE0, 0x00, 0x3F, 0xFC, 0x00, 0x07, 0xFF, 0xC0, 0x01, 0xFD, 0xF8, 0x00,
0x3F, 0x3F, 0x80, 0x0F, 0xE7, 0xF8, 0x03, 0xF8, 0x7F, 0xC1, 0xFF, 0x07,
0xFF, 0xFF, 0xC0, 0x7F, 0xFF, 0xF0, 0x07, 0xFF, 0xFC, 0x00, 0x7F, 0xFE,
0x00, 0x01, 0xFF, 0x00, 0x00, 0x0F, 0xC0, 0x00, 0x00, 0xFC, 0x00, 0x00,
0x1F, 0xF0, 0x00, 0x01, 0xFF, 0xE0, 0x00, 0x1F, 0xFC, 0x00, 0x00, 0xFF,
0x00, 0x00, 0x03, 0xE0, 0x7F, 0xF8, 0x01, 0xFF, 0xFE, 0x03, 0xFF, 0xFF,
0x07, 0xFF, 0xFF, 0x0F, 0xFF, 0xFF, 0x1F, 0x81, 0xFE, 0x3F, 0x00, 0xFE,
0x7E, 0x00, 0xFC, 0xFC, 0x01, 0xF9, 0xF8, 0x03, 0xF3, 0xF0, 0x0F, 0xE7,
0xE0, 0x3F, 0x8F, 0xFF, 0xFF, 0x1F, 0xFF, 0xFC, 0x3F, 0xFF, 0xF0, 0x7F,
0xFF, 0x80, 0xFF, 0xFF, 0x01, 0xF8, 0xFF, 0x03, 0xF0, 0xFE, 0x07, 0xE0,
0xFE, 0x0F, 0xC0, 0xFE, 0x1F, 0x81, 0xFE, 0x3F, 0x01, 0xFC, 0x7E, 0x01,
0xFC, 0xFC, 0x03, 0xFD, 0xF8, 0x03, 0xFB, 0xF0, 0x03, 0xF8, 0x03, 0xFE,
0x00, 0xFF, 0xFC, 0x3F, 0xFF, 0xE7, 0xFF, 0xFC, 0x7F, 0xFF, 0xCF, 0xE0,
0x1C, 0xFC, 0x00, 0x0F, 0xC0, 0x00, 0xFC, 0x00, 0x0F, 0xF0, 0x00, 0x7F,
0xC0, 0x07, 0xFF, 0x80, 0x3F, 0xFF, 0x01, 0xFF, 0xF8, 0x07, 0xFF, 0xC0,
0x0F, 0xFE, 0x00, 0x1F, 0xF0, 0x00, 0x7F, 0x00, 0x03, 0xF0, 0x00, 0x3F,
0x00, 0x03, 0xF7, 0x80, 0x7F, 0x7F, 0xFF, 0xE7, 0xFF, 0xFE, 0xFF, 0xFF,
0xC7, 0xFF, 0xF8, 0x0F, 0xFC, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFC, 0x03, 0xF0, 0x00, 0x0F,
0xC0, 0x00, 0x3F, 0x00, 0x00, 0xFC, 0x00, 0x03, 0xF0, 0x00, 0x0F, 0xC0,
0x00, 0x3F, 0x00, 0x00, 0xFC, 0x00, 0x03, 0xF0, 0x00, 0x0F, 0xC0, 0x00,
0x3F, 0x00, 0x00, 0xFC, 0x00, 0x03, 0xF0, 0x00, 0x0F, 0xC0, 0x00, 0x3F,
0x00, 0x00, 0xFC, 0x00, 0x03, 0xF0, 0x00, 0x0F, 0xC0, 0x00, 0x3F, 0x00,
0x00, 0xFC, 0x00, 0x03, 0xF0, 0x00, 0x0F, 0xC0, 0x00, 0xFC, 0x00, 0xFF,
0xF0, 0x03, 0xFF, 0xC0, 0x0F, 0xFF, 0x00, 0x3F, 0xFC, 0x00, 0xFF, 0xF0,
0x03, 0xFF, 0xC0, 0x0F, 0xFF, 0x00, 0x3F, 0xFC, 0x00, 0xFF, 0xF0, 0x03,
0xFF, 0xC0, 0x0F, 0xFF, 0x00, 0x3F, 0xFC, 0x00, 0xFF, 0xF0, 0x03, 0xFF,
0xC0, 0x0F, 0xFF, 0x00, 0x3F, 0xFC, 0x00, 0xFF, 0xF0, 0x03, 0xFF, 0xC0,
0x0F, 0xFF, 0x80, 0x7F, 0x7E, 0x01, 0xF9, 0xFE, 0x1F, 0xE7, 0xFF, 0xFF,
0x8F, 0xFF, 0xFC, 0x1F, 0xFF, 0xE0, 0x3F, 0xFF, 0x00, 0x1F, 0xE0, 0x00,
0xFE, 0x00, 0x0F, 0xEF, 0xC0, 0x01, 0xF9, 0xF8, 0x00, 0x3F, 0x3F, 0x80,
0x0F, 0xE3, 0xF0, 0x01, 0xF8, 0x7E, 0x00, 0x3F, 0x07, 0xE0, 0x0F, 0xC0,
0xFC, 0x01, 0xF8, 0x1F, 0x80, 0x3F, 0x01, 0xF8, 0x0F, 0xC0, 0x3F, 0x01,
0xF8, 0x07, 0xE0, 0x7E, 0x00, 0x7E, 0x0F, 0xC0, 0x0F, 0xC1, 0xF8, 0x00,
0xFC, 0x7E, 0x00, 0x1F, 0x8F, 0xC0, 0x03, 0xF1, 0xF8, 0x00, 0x3F, 0x7E,
0x00, 0x07, 0xEF, 0xC0, 0x00, 0x7D, 0xF0, 0x00, 0x0F, 0xFE, 0x00, 0x01,
0xFF, 0x80, 0x00, 0x1F, 0xF0, 0x00, 0x03, 0xFE, 0x00, 0x00, 0x3F, 0x80,
0x00, 0x07, 0xF0, 0x00, 0x00, 0x7C, 0x00, 0x00, 0xFC, 0x00, 0x00, 0x03,
0xFF, 0xC0, 0x00, 0x00, 0x7F, 0x7E, 0x00, 0x00, 0x07, 0xE7, 0xE0, 0x1F,
0x00, 0x7E, 0x7E, 0x03, 0xF8, 0x07, 0xE7, 0xE0, 0x3F, 0x80, 0x7E, 0x7E,
0x03, 0xF8, 0x0F, 0xE3, 0xF0, 0x3F, 0xC0, 0xFC, 0x3F, 0x07, 0xFC, 0x0F,
0xC3, 0xF0, 0x7F, 0xC0, 0xFC, 0x3F, 0x07, 0xFE, 0x0F, 0xC1, 0xF8, 0x7B,
0xE1, 0xF8, 0x1F, 0x8F, 0xBE, 0x1F, 0x81, 0xF8, 0xFB, 0xF1, 0xF8, 0x1F,
0x8F, 0x9F, 0x1F, 0x80, 0xFC, 0xF1, 0xF3, 0xF0, 0x0F, 0xDF, 0x1F, 0xBF,
0x00, 0xFD, 0xF0, 0xFB, 0xF0, 0x07, 0xDF, 0x0F, 0xBE, 0x00, 0x7F, 0xE0,
0xFF, 0xE0, 0x07, 0xFE, 0x07, 0xFE, 0x00, 0x7F, 0xE0, 0x7F, 0xE0, 0x03,
0xFC, 0x07, 0xFC, 0x00, 0x3F, 0xC0, 0x3F, 0xC0, 0x03, 0xFC, 0x03, 0xFC,
0x00, 0x1F, 0xC0, 0x3F, 0x80, 0x01, 0xF8, 0x01, 0xF8, 0x00, 0xFE, 0x00,
0x1F, 0xDF, 0xC0, 0x0F, 0xE3, 0xF8, 0x07, 0xF0, 0xFF, 0x03, 0xFC, 0x1F,
0xC0, 0xFE, 0x03, 0xF8, 0x7F, 0x00, 0x7F, 0x3F, 0x80, 0x1F, 0xCF, 0xE0,
0x03, 0xFF, 0xF0, 0x00, 0x7F, 0xF8, 0x00, 0x1F, 0xFE, 0x00, 0x03, 0xFF,
0x00, 0x00, 0x7F, 0x80, 0x00, 0x1F, 0xE0, 0x00, 0x07, 0xF8, 0x00, 0x03,
0xFF, 0x00, 0x01, 0xFF, 0xE0, 0x00, 0x7F, 0xF8, 0x00, 0x3F, 0xFF, 0x00,
0x1F, 0xCF, 0xE0, 0x0F, 0xE3, 0xF8, 0x03, 0xF8, 0x7F, 0x01, 0xFC, 0x0F,
0xE0, 0xFE, 0x01, 0xFC, 0x3F, 0x80, 0x7F, 0x1F, 0xC0, 0x0F, 0xEF, 0xE0,
0x01, 0xFC, 0xFE, 0x00, 0x1F, 0xDF, 0xC0, 0x0F, 0xE7, 0xF0, 0x03, 0xF8,
0xFE, 0x01, 0xFC, 0x3F, 0x80, 0x7F, 0x07, 0xF0, 0x3F, 0x80, 0xFC, 0x1F,
0xC0, 0x3F, 0x87, 0xF0, 0x07, 0xF3, 0xF8, 0x01, 0xFC, 0xFE, 0x00, 0x3F,
0xFF, 0x00, 0x07, 0xFF, 0x80, 0x01, 0xFF, 0xE0, 0x00, 0x3F, 0xF0, 0x00,
0x0F, 0xF8, 0x00, 0x01, 0xFE, 0x00, 0x00, 0x3F, 0x00, 0x00, 0x0F, 0xC0,
0x00, 0x03, 0xF0, 0x00, 0x00, 0xFC, 0x00, 0x00, 0x3F, 0x00, 0x00, 0x0F,
0xC0, 0x00, 0x03, 0xF0, 0x00, 0x00, 0xFC, 0x00, 0x00, 0x3F, 0x00, 0x00,
0x0F, 0xC0, 0x00, 0x03, 0xF0, 0x00, 0x7F, 0xFF, 0xFD, 0xFF, 0xFF, 0xF7,
0xFF, 0xFF, 0xDF, 0xFF, 0xFF, 0x7F, 0xFF, 0xFC, 0x00, 0x1F, 0xE0, 0x00,
0x7F, 0x00, 0x03, 0xF8, 0x00, 0x1F, 0xC0, 0x00, 0xFE, 0x00, 0x07, 0xF8,
0x00, 0x3F, 0xC0, 0x00, 0xFE, 0x00, 0x07, 0xF0, 0x00, 0x3F, 0xC0, 0x01,
0xFE, 0x00, 0x07, 0xF0, 0x00, 0x3F, 0x80, 0x01, 0xFE, 0x00, 0x07, 0xF0,
0x00, 0x3F, 0x80, 0x01, 0xFE, 0x00, 0x07, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xC0, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xF8, 0x3F, 0x07, 0xE0, 0xFC, 0x1F, 0x83, 0xF0,
0x7E, 0x0F, 0xC1, 0xF8, 0x3F, 0x07, 0xE0, 0xFC, 0x1F, 0x83, 0xF0, 0x7E,
0x0F, 0xC1, 0xF8, 0x3F, 0x07, 0xE0, 0xFC, 0x1F, 0x83, 0xF0, 0x7E, 0x0F,
0xC1, 0xF8, 0x3F, 0x07, 0xE0, 0xFC, 0x1F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xC0, 0xFC, 0x00, 0x1F, 0xC0, 0x01, 0xF8, 0x00, 0x3F, 0x00, 0x03,
0xF0, 0x00, 0x7E, 0x00, 0x0F, 0xC0, 0x00, 0xFC, 0x00, 0x1F, 0x80, 0x03,
0xF0, 0x00, 0x3F, 0x00, 0x07, 0xE0, 0x00, 0xFC, 0x00, 0x0F, 0xC0, 0x01,
0xF8, 0x00, 0x3F, 0x00, 0x03, 0xF0, 0x00, 0x7E, 0x00, 0x0F, 0xC0, 0x00,
0xFC, 0x00, 0x1F, 0x80, 0x03, 0xF0, 0x00, 0x3F, 0x00, 0x07, 0xE0, 0x00,
0xFC, 0x00, 0x0F, 0xC0, 0x01, 0xF8, 0x00, 0x3F, 0x00, 0x03, 0xF0, 0x00,
0x7E, 0x00, 0x0F, 0xC0, 0x00, 0xFC, 0x00, 0x1F, 0x80, 0x03, 0xF0, 0x00,
0x3F, 0x00, 0x07, 0xE0, 0x00, 0xFE, 0x00, 0x0F, 0xC0, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFE, 0x0F, 0xC1, 0xF8, 0x3F, 0x07, 0xE0, 0xFC, 0x1F,
0x83, 0xF0, 0x7E, 0x0F, 0xC1, 0xF8, 0x3F, 0x07, 0xE0, 0xFC, 0x1F, 0x83,
0xF0, 0x7E, 0x0F, 0xC1, 0xF8, 0x3F, 0x07, 0xE0, 0xFC, 0x1F, 0x83, 0xF0,
0x7E, 0x0F, 0xC1, 0xF8, 0x3F, 0x07, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xC0, 0x00, 0xF8, 0x00, 0x0F, 0xE0, 0x00, 0x7F, 0x00, 0x07, 0xFC,
0x00, 0x7F, 0xF0, 0x03, 0xFF, 0x80, 0x3F, 0x7E, 0x01, 0xFB, 0xF0, 0x1F,
0x8F, 0xC1, 0xFC, 0x7F, 0x0F, 0xC1, 0xF8, 0xFC, 0x07, 0xE7, 0xE0, 0x3F,
0x7E, 0x00, 0xFC, 0xF0, 0x07, 0x81, 0x00, 0x10, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF0, 0x10, 0x1C, 0x1E,
0x1F, 0x83, 0xE0, 0xF8, 0x3E, 0x0E, 0x02, 0x00, 0x0F, 0xF0, 0x1F, 0xFE,
0x0F, 0xFF, 0x87, 0xFF, 0xE1, 0xFF, 0xF0, 0x81, 0xFC, 0x00, 0x7E, 0x00,
0x3F, 0x0F, 0xFF, 0x9F, 0xFF, 0xDF, 0xFF, 0xEF, 0xFF, 0xFF, 0xE1, 0xFF,
0xE0, 0xFF, 0xF0, 0x7F, 0xFC, 0x3F, 0xFF, 0xFF, 0xBF, 0xFF, 0xDF, 0xFF,
0xE7, 0xFF, 0xF0, 0xFF, 0xC0, 0x1C, 0x00, 0x1F, 0x80, 0x03, 0xF0, 0x00,
0x7E, 0x00, 0x0F, 0xC0, 0x01, 0xF8, 0x00, 0x3F, 0x00, 0x07, 0xE0, 0x00,
0xFC, 0x00, 0x1F, 0xBF, 0x03, 0xFF, 0xF8, 0x7F, 0xFF, 0x8F, 0xFF, 0xF9,
0xFF, 0xFF, 0xBF, 0x87, 0xF7, 0xE0, 0x7F, 0xFC, 0x07, 0xFF, 0x80, 0xFF,
0xF0, 0x1F, 0xFE, 0x03, 0xFF, 0xC0, 0x7F, 0xF8, 0x0F, 0xFF, 0x03, 0xFF,
0xE0, 0x7E, 0xFC, 0x1F, 0xDF, 0xFF, 0xF3, 0xFF, 0xFE, 0x7F, 0xFF, 0x8F,
0xFF, 0xC0, 0x3F, 0xE0, 0x00, 0x01, 0xFE, 0x03, 0xFF, 0xC3, 0xFF, 0xE3,
0xFF, 0xE3, 0xFF, 0xF3, 0xFC, 0x09, 0xF8, 0x01, 0xFC, 0x00, 0xFC, 0x00,
0x7E, 0x00, 0x3F, 0x00, 0x1F, 0x80, 0x0F, 0xC0, 0x07, 0xF0, 0x01, 0xF8,
0x00, 0xFF, 0x02, 0x7F, 0xFF, 0x1F, 0xFF, 0xC7, 0xFF, 0xE1, 0xFF, 0xF0,
0x1F, 0xE0, 0x00, 0x00, 0xE0, 0x00, 0xFC, 0x00, 0x1F, 0x80, 0x03, 0xF0,
0x00, 0x7E, 0x00, 0x0F, 0xC0, 0x01, 0xF8, 0x00, 0x3F, 0x00, 0x07, 0xE0,
0x7E, 0xFC, 0x3F, 0xFF, 0x8F, 0xFF, 0xF3, 0xFF, 0xFE, 0xFF, 0xFF, 0xDF,
0xC3, 0xFB, 0xF0, 0x3F, 0xFC, 0x07, 0xFF, 0x80, 0xFF, 0xF0, 0x1F, 0xFE,
0x03, 0xFF, 0xC0, 0x7F, 0xF8, 0x0F, 0xFF, 0x81, 0xFB, 0xF0, 0x3F, 0x7F,
0x07, 0xEF, 0xFF, 0xFC, 0xFF, 0xFF, 0x8F, 0xFF, 0xF0, 0xFF, 0xFE, 0x03,
0xFE, 0x00, 0x01, 0xF8, 0x01, 0xFF, 0xC0, 0x7F, 0xFC, 0x1F, 0xFF, 0xC3,
0xFF, 0xFC, 0xFE, 0x1F, 0x9F, 0x81, 0xFF, 0xE0, 0x3F, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xC0, 0x01, 0xF8, 0x00, 0x1F,
0x80, 0x03, 0xFC, 0x04, 0x7F, 0xFF, 0x87, 0xFF, 0xF8, 0x7F, 0xFF, 0x07,
0xFF, 0xE0, 0x1F, 0xE0, 0x07, 0xF0, 0x7F, 0xF3, 0xFF, 0xDF, 0xFE, 0x7F,
0xFB, 0xF8, 0x2F, 0xC0, 0x3F, 0x00, 0xFC, 0x03, 0xFF, 0xEF, 0xFF, 0xBF,
0xFE, 0xFF, 0xFB, 0xFF, 0xEF, 0xC0, 0x3F, 0x00, 0xFC, 0x03, 0xF0, 0x0F,
0xC0, 0x3F, 0x00, 0xFC, 0x03, 0xF0, 0x0F, 0xC0, 0x3F, 0x00, 0xFC, 0x03,
0xF0, 0x0F, 0xC0, 0x3F, 0x00, 0xFC, 0x03, 0xF0, 0x00, 0x03, 0xFE, 0x03,
0xFF, 0xF1, 0xFF, 0xFC, 0xFF, 0xFF, 0x7F, 0xFF, 0xDF, 0xC3, 0xFF, 0xE0,
0xFF, 0xF0, 0x3F, 0xFC, 0x0F, 0xFF, 0x03, 0xFF, 0xC0, 0xFF, 0xF0, 0x3F,
0xFE, 0x0F, 0xFF, 0x87, 0xF7, 0xFF, 0xFD, 0xFF, 0xFF, 0x3F, 0xFF, 0xC7,
0xFF, 0xF0, 0x7E, 0xFC, 0x00, 0x3F, 0x00, 0x0F, 0xC0, 0x07, 0xF3, 0x03,
0xF8, 0xFF, 0xFE, 0x7F, 0xFF, 0x1F, 0xFF, 0xC7, 0xFF, 0xC0, 0x3F, 0xC0,
0x1C, 0x00, 0x7E, 0x00, 0x3F, 0x00, 0x1F, 0x80, 0x0F, 0xC0, 0x07, 0xE0,
0x03, 0xF0, 0x01, 0xF8, 0x00, 0xFC, 0x00, 0x7F, 0xF8, 0x3F, 0xFF, 0x1F,
0xFF, 0xCF, 0xFF, 0xF7, 0xFF, 0xFB, 0xF0, 0xFF, 0xF8, 0x3F, 0xFC, 0x1F,
0xFE, 0x0F, 0xFF, 0x07, 0xFF, 0x83, 0xFF, 0xC1, 0xFF, 0xE0, 0xFF, 0xF0,
0x7F, 0xF8, 0x3F, 0xFC, 0x1F, 0xFE, 0x0F, 0xFF, 0x07, 0xFF, 0x83, 0xFF,
0xC1, 0xFF, 0xE0, 0xFC, 0x3C, 0x7E, 0x7E, 0xFF, 0x7E, 0x7E, 0x3C, 0x00,
0x00, 0x7E, 0x7E, 0x7E, 0x7E, 0x7E, 0x7E, 0x7E, 0x7E, 0x7E, 0x7E, 0x7E,
0x7E, 0x7E, 0x7E, 0x7E, 0x7E, 0x7E, 0x7E, 0x7E, 0x7E, 0x7E, 0x01, 0xE0,
0x1F, 0x80, 0xFC, 0x0F, 0xF0, 0x3F, 0x01, 0xF8, 0x07, 0x80, 0x00, 0x00,
0x00, 0x1F, 0x80, 0xFC, 0x07, 0xE0, 0x3F, 0x01, 0xF8, 0x0F, 0xC0, 0x7E,
0x03, 0xF0, 0x1F, 0x80, 0xFC, 0x07, 0xE0, 0x3F, 0x01, 0xF8, 0x0F, 0xC0,
0x7E, 0x03, 0xF0, 0x1F, 0x80, 0xFC, 0x07, 0xE0, 0x3F, 0x01, 0xF8, 0x0F,
0xC0, 0xFE, 0x7F, 0xE3, 0xFF, 0x3F, 0xF1, 0xFF, 0x0F, 0xE0, 0x00, 0x1C,
0x00, 0x1F, 0x80, 0x03, 0xF0, 0x00, 0x7E, 0x00, 0x0F, 0xC0, 0x01, 0xF8,
0x00, 0x3F, 0x00, 0x07, 0xE0, 0x00, 0xFC, 0x00, 0x1F, 0x83, 0xFF, 0xF0,
0xFF, 0x7E, 0x3F, 0xCF, 0xC7, 0xF1, 0xF9, 0xFC, 0x3F, 0x7F, 0x87, 0xFF,
0xE0, 0xFF, 0xF8, 0x1F, 0xFE, 0x03, 0xFF, 0x80, 0x7F, 0xF8, 0x0F, 0xFF,
0x81, 0xFF, 0xF8, 0x3F, 0x7F, 0x07, 0xE7, 0xF0, 0xFC, 0x7F, 0x1F, 0x8F,
0xE3, 0xF0, 0xFE, 0x7E, 0x1F, 0xEF, 0xC1, 0xFD, 0xF8, 0x1F, 0xC0, 0x1C,
0x7E, 0x3F, 0x1F, 0x8F, 0xC7, 0xE3, 0xF1, 0xF8, 0xFC, 0x7E, 0x3F, 0x1F,
0x8F, 0xC7, 0xE3, 0xF1, 0xF8, 0xFC, 0x7E, 0x3F, 0x1F, 0x8F, 0xC7, 0xE3,
0xF1, 0xF8, 0xFE, 0x7F, 0xDF, 0xEF, 0xF3, 0xF0, 0x78, 0x1F, 0xF0, 0x7E,
0x0F, 0xFF, 0xDF, 0xF8, 0xFF, 0xFF, 0xFF, 0xCF, 0xFF, 0xFF, 0xFE, 0xFF,
0xFF, 0xFF, 0xEF, 0xC3, 0xF8, 0x7F, 0xFC, 0x3F, 0x87, 0xFF, 0xC1, 0xF8,
0x3F, 0xFC, 0x1F, 0x83, 0xFF, 0xC1, 0xF8, 0x3F, 0xFC, 0x1F, 0x83, 0xFF,
0xC1, 0xF8, 0x3F, 0xFC, 0x1F, 0x83, 0xFF, 0xC1, 0xF8, 0x3F, 0xFC, 0x1F,
0x83, 0xFF, 0xC1, 0xF8, 0x3F, 0xFC, 0x1F, 0x83, 0xFF, 0xC1, 0xF8, 0x3F,
0xFC, 0x1F, 0x83, 0xFF, 0xC1, 0xF8, 0x3F, 0xFC, 0x1F, 0x83, 0xF0, 0x1F,
0xF0, 0x7F, 0xFE, 0x3F, 0xFF, 0x9F, 0xFF, 0xEF, 0xFF, 0xF7, 0xE1, 0xFF,
0xF0, 0x7F, 0xF8, 0x3F, 0xFC, 0x1F, 0xFE, 0x0F, 0xFF, 0x07, 0xFF, 0x83,
0xFF, 0xC1, 0xFF, 0xE0, 0xFF, 0xF0, 0x7F, 0xF8, 0x3F, 0xFC, 0x1F, 0xFE,
0x0F, 0xFF, 0x07, 0xFF, 0x83, 0xFF, 0xC1, 0xF8, 0x01, 0xF8, 0x00, 0x7F,
0xE0, 0x1F, 0xFF, 0x83, 0xFF, 0xFC, 0x3F, 0xFF, 0xC7, 0xF0, 0xFE, 0x7E,
0x07, 0xEF, 0xE0, 0x7F, 0xFC, 0x03, 0xFF, 0xC0, 0x3F, 0xFC, 0x03, 0xFF,
0xC0, 0x3F, 0xFC, 0x03, 0xFF, 0xC0, 0x7F, 0x7E, 0x07, 0xE7, 0xF0, 0xFE,
0x3F, 0xFF, 0xE3, 0xFF, 0xFC, 0x1F, 0xFF, 0x80, 0xFF, 0xF0, 0x01, 0xF8,
0x00, 0x1F, 0xF0, 0x1F, 0xFF, 0x83, 0xFF, 0xFC, 0x7F, 0xFF, 0xCF, 0xFF,
0xF9, 0xF8, 0x3F, 0xBF, 0x03, 0xF7, 0xE0, 0x7F, 0xFC, 0x07, 0xFF, 0x80,
0xFF, 0xF0, 0x1F, 0xFE, 0x03, 0xFF, 0xC0, 0x7F, 0xF8, 0x0F, 0xFF, 0x03,
0xFF, 0xF0, 0xFE, 0xFF, 0xFF, 0xDF, 0xFF, 0xF3, 0xFF, 0xFC, 0x7F, 0xFF,
0x0F, 0xDF, 0x81, 0xF8, 0x00, 0x3F, 0x00, 0x07, 0xE0, 0x00, 0xFC, 0x00,
0x1F, 0x80, 0x03, 0xF0, 0x00, 0x7E, 0x00, 0x00, 0x01, 0xFF, 0x80, 0x7F,
0xFF, 0x1F, 0xFF, 0xF3, 0xFF, 0xFF, 0x3F, 0xFF, 0xF7, 0xF8, 0x3F, 0x7E,
0x03, 0xFF, 0xE0, 0x3F, 0xFC, 0x03, 0xFF, 0xC0, 0x3F, 0xFC, 0x03, 0xFF,
0xC0, 0x3F, 0xFC, 0x03, 0xFF, 0xC0, 0x3F, 0xFE, 0x03, 0xF7, 0xF0, 0x7F,
0x7F, 0xFF, 0xF3, 0xFF, 0xFF, 0x1F, 0xFF, 0xF0, 0xFF, 0xFF, 0x03, 0xFB,
0xF0, 0x00, 0x3F, 0x00, 0x03, 0xF0, 0x00, 0x3F, 0x00, 0x03, 0xF0, 0x00,
0x3F, 0x00, 0x03, 0xF0, 0x00, 0x3F, 0x0F, 0xFB, 0xFF, 0xFF, 0xFF, 0xFF,
0xFE, 0xFF, 0xFB, 0xF0, 0x2F, 0xC0, 0x3F, 0x00, 0xFC, 0x03, 0xF0, 0x0F,
0xC0, 0x3F, 0x00, 0xFC, 0x03, 0xF0, 0x0F, 0xC0, 0x3F, 0x00, 0xFC, 0x03,
0xF0, 0x0F, 0xC0, 0x3F, 0x00, 0xFC, 0x00, 0x07, 0xF8, 0x0F, 0xFE, 0x3F,
0xFE, 0x3F, 0xFE, 0x7F, 0xFC, 0x7E, 0x04, 0x7E, 0x00, 0x7F, 0x00, 0x7F,
0xF8, 0x7F, 0xFE, 0x3F, 0xFE, 0x1F, 0xFF, 0x07, 0xFF, 0x00, 0x7F, 0x00,
0x3F, 0x70, 0x3F, 0x7F, 0xFF, 0x7F, 0xFE, 0x7F, 0xFE, 0xFF, 0xF8, 0x1F,
0xE0, 0x1C, 0x03, 0xF0, 0x0F, 0xC0, 0x3F, 0x00, 0xFC, 0x03, 0xF0, 0x0F,
0xFF, 0xBF, 0xFE, 0xFF, 0xFB, 0xFF, 0xEF, 0xFF, 0xBF, 0x00, 0xFC, 0x03,
0xF0, 0x0F, 0xC0, 0x3F, 0x00, 0xFC, 0x03, 0xF0, 0x0F, 0xC0, 0x3F, 0x00,
0xFC, 0x03, 0xF8, 0x27, 0xFF, 0x9F, 0xFE, 0x3F, 0xFC, 0x7F, 0xF0, 0x7F,
0x00, 0xFC, 0x1F, 0xFE, 0x0F, 0xFF, 0x07, 0xFF, 0x83, 0xFF, 0xC1, 0xFF,
0xE0, 0xFF, 0xF0, 0x7F, 0xF8, 0x3F, 0xFC, 0x1F, 0xFE, 0x0F, 0xFF, 0x07,
0xFF, 0x83, 0xFF, 0xC1, 0xFF, 0xE0, 0xFF, 0xF0, 0x7F, 0xFC, 0x3F, 0x7F,
0xFF, 0xBF, 0xFF, 0xCF, 0xFF, 0xE3, 0xFF, 0xF0, 0x7F, 0xC0, 0xFC, 0x01,
0xFF, 0xF0, 0x1F, 0xDF, 0x80, 0xFC, 0xFC, 0x07, 0xE7, 0xF0, 0x7F, 0x1F,
0x83, 0xF0, 0xFC, 0x1F, 0x87, 0xE0, 0xFC, 0x3F, 0x8F, 0xC0, 0xFC, 0x7E,
0x07, 0xE3, 0xF0, 0x3F, 0xBF, 0x00, 0xFD, 0xF8, 0x07, 0xEF, 0xC0, 0x1F,
0xFC, 0x00, 0xFF, 0xE0, 0x07, 0xFF, 0x00, 0x1F, 0xF0, 0x00, 0xFF, 0x80,
0x03, 0xF8, 0x00, 0x1F, 0xC0, 0x00, 0xFC, 0x0F, 0x80, 0xFF, 0xF0, 0x3E,
0x03, 0xF7, 0xE0, 0xF8, 0x1F, 0x9F, 0x83, 0xF0, 0x7E, 0x7E, 0x0F, 0xC1,
0xF8, 0xF8, 0x7F, 0x07, 0xC3, 0xF1, 0xFC, 0x3F, 0x0F, 0xC7, 0xF8, 0xFC,
0x1F, 0x1F, 0xE3, 0xE0, 0x7C, 0xF7, 0x8F, 0x81, 0xFB, 0xDF, 0x7E, 0x07,
0xEF, 0x7D, 0xF0, 0x0F, 0xBC, 0xF7, 0xC0, 0x3E, 0xE3, 0xDF, 0x00, 0xFF,
0x8F, 0xF8, 0x01, 0xFE, 0x1F, 0xE0, 0x07, 0xF8, 0x7F, 0x80, 0x0F, 0xC1,
0xFC, 0x00, 0x3F, 0x03, 0xF0, 0x00, 0xFC, 0x0F, 0xC0, 0x01, 0xF0, 0x3E,
0x00, 0xFF, 0x03, 0xFD, 0xFC, 0x0F, 0xE3, 0xF8, 0x7F, 0x0F, 0xF3, 0xFC,
0x1F, 0xCF, 0xE0, 0x3F, 0xFF, 0x00, 0x7F, 0xF8, 0x01, 0xFF, 0xE0, 0x03,
0xFF, 0x00, 0x07, 0xF8, 0x00, 0x1F, 0xE0, 0x00, 0x7F, 0x80, 0x03, 0xFF,
0x00, 0x1F, 0xFE, 0x00, 0x7F, 0xF8, 0x03, 0xFF, 0xF0, 0x1F, 0xCF, 0xE0,
0xFF, 0x3F, 0x83, 0xF8, 0x7F, 0x1F, 0xC0, 0xFE, 0xFF, 0x03, 0xFC, 0xFC,
0x01, 0xFF, 0xF0, 0x1F, 0xDF, 0x80, 0xFC, 0xFC, 0x07, 0xE7, 0xF0, 0x7F,
0x1F, 0x83, 0xF0, 0xFC, 0x1F, 0x87, 0xE0, 0xFC, 0x3F, 0x8F, 0xC0, 0xFC,
0x7E, 0x07, 0xE3, 0xF0, 0x1F, 0xBF, 0x00, 0xFD, 0xF8, 0x07, 0xEF, 0xC0,
0x1F, 0xFC, 0x00, 0xFF, 0xE0, 0x07, 0xFE, 0x00, 0x1F, 0xF0, 0x00, 0xFF,
0x80, 0x03, 0xF8, 0x00, 0x1F, 0xC0, 0x01, 0xFC, 0x00, 0x1F, 0xE0, 0x0F,
0xFE, 0x00, 0x7F, 0xF0, 0x07, 0xFF, 0x00, 0x3F, 0xF0, 0x00, 0xFE, 0x00,
0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xC0, 0x1F, 0xE0, 0x0F, 0xF0, 0x07, 0xF8, 0x01, 0xFC, 0x00, 0xFF, 0x00,
0x7F, 0x80, 0x3F, 0xC0, 0x1F, 0xE0, 0x07, 0xF8, 0x03, 0xFC, 0x01, 0xFE,
0x00, 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xC0, 0x00, 0xF8, 0x3F, 0xC3, 0xFE, 0x1F, 0xF1, 0xFF, 0x8F, 0xE0, 0x7E,
0x03, 0xF0, 0x1F, 0x80, 0xFC, 0x07, 0xE0, 0x3F, 0x01, 0xF8, 0x0F, 0xC0,
0x7E, 0x03, 0xF0, 0x3F, 0x87, 0xF8, 0x3F, 0x81, 0xFC, 0x0F, 0xF0, 0x7F,
0x80, 0xFE, 0x03, 0xF0, 0x1F, 0x80, 0xFC, 0x07, 0xE0, 0x3F, 0x01, 0xF8,
0x0F, 0xC0, 0x7E, 0x03, 0xF0, 0x1F, 0xC0, 0xFF, 0xC3, 0xFE, 0x1F, 0xF0,
0x7F, 0x80, 0x7C, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF0, 0xF8, 0x07, 0xF8, 0x3F,
0xE1, 0xFF, 0x0F, 0xFC, 0x0F, 0xE0, 0x3F, 0x01, 0xF8, 0x0F, 0xC0, 0x7E,
0x03, 0xF0, 0x1F, 0x80, 0xFC, 0x07, 0xE0, 0x3F, 0x01, 0xF8, 0x0F, 0xE0,
0x3F, 0xC0, 0xFE, 0x07, 0xF0, 0x7F, 0x87, 0xFC, 0x3F, 0x81, 0xF8, 0x0F,
0xC0, 0x7E, 0x03, 0xF0, 0x1F, 0x80, 0xFC, 0x07, 0xE0, 0x3F, 0x01, 0xF8,
0x1F, 0xC7, 0xFE, 0x3F, 0xE1, 0xFF, 0x0F, 0xF0, 0x7C, 0x00, 0x0F, 0x00,
0xC3, 0xFC, 0x0F, 0x3F, 0xF1, 0xF7, 0xFF, 0xFE, 0x7F, 0xFF, 0xEF, 0x8F,
0xFC, 0xF0, 0x3F, 0xC3, 0x00, 0xF0 };
const GFXglyph Ubuntu_Bold20pt7bGlyphs[] PROGMEM = {
{ 0, 0, 0, 9, 0, 1 }, // 0x20 ' '
{ 0, 8, 27, 11, 2, -26 }, // 0x21 '!'
{ 27, 15, 11, 18, 2, -29 }, // 0x22 '"'
{ 48, 23, 27, 27, 2, -26 }, // 0x23 '#'
{ 126, 19, 35, 22, 1, -29 }, // 0x24 '$'
{ 210, 34, 27, 36, 1, -26 }, // 0x25 '%'
{ 325, 27, 27, 28, 1, -26 }, // 0x26 '&'
{ 417, 6, 11, 10, 2, -29 }, // 0x27 '''
{ 426, 11, 38, 14, 3, -30 }, // 0x28 '('
{ 479, 11, 38, 14, 0, -30 }, // 0x29 ')'
{ 532, 17, 16, 20, 2, -26 }, // 0x2A '*'
{ 566, 19, 19, 23, 2, -20 }, // 0x2B '+'
{ 612, 8, 12, 9, 0, -5 }, // 0x2C ','
{ 624, 11, 5, 13, 1, -13 }, // 0x2D '-'
{ 631, 7, 7, 9, 1, -5 }, // 0x2E '.'
{ 638, 19, 38, 17, -1, -30 }, // 0x2F '/'
{ 729, 20, 27, 22, 1, -26 }, // 0x30 '0'
{ 797, 13, 27, 22, 3, -26 }, // 0x31 '1'
{ 841, 19, 27, 22, 1, -26 }, // 0x32 '2'
{ 906, 19, 27, 22, 1, -26 }, // 0x33 '3'
{ 971, 20, 27, 22, 1, -26 }, // 0x34 '4'
{ 1039, 19, 27, 22, 1, -26 }, // 0x35 '5'
{ 1104, 20, 27, 22, 1, -26 }, // 0x36 '6'
{ 1172, 18, 27, 22, 2, -26 }, // 0x37 '7'
{ 1233, 19, 27, 22, 2, -26 }, // 0x38 '8'
{ 1298, 19, 27, 22, 1, -26 }, // 0x39 '9'
{ 1363, 7, 21, 9, 1, -19 }, // 0x3A ':'
{ 1382, 8, 26, 9, 0, -19 }, // 0x3B ';'
{ 1408, 19, 19, 22, 2, -20 }, // 0x3C '<'
{ 1454, 18, 14, 22, 2, -17 }, // 0x3D '='
{ 1486, 19, 19, 22, 1, -20 }, // 0x3E '>'
{ 1532, 16, 28, 18, 1, -27 }, // 0x3F '?'
{ 1588, 34, 34, 38, 2, -27 }, // 0x40 '@'
{ 1733, 27, 27, 27, 0, -26 }, // 0x41 'A'
{ 1825, 21, 27, 26, 3, -26 }, // 0x42 'B'
{ 1896, 22, 27, 25, 2, -26 }, // 0x43 'C'
{ 1971, 24, 27, 29, 3, -26 }, // 0x44 'D'
{ 2052, 20, 27, 24, 3, -26 }, // 0x45 'E'
{ 2120, 18, 27, 22, 3, -26 }, // 0x46 'F'
{ 2181, 22, 27, 27, 2, -26 }, // 0x47 'G'
{ 2256, 23, 27, 29, 3, -26 }, // 0x48 'H'
{ 2334, 6, 27, 12, 3, -26 }, // 0x49 'I'
{ 2355, 18, 27, 21, 0, -26 }, // 0x4A 'J'
{ 2416, 24, 27, 27, 3, -26 }, // 0x4B 'K'
{ 2497, 18, 27, 22, 3, -26 }, // 0x4C 'L'
{ 2558, 31, 27, 35, 2, -26 }, // 0x4D 'M'
{ 2663, 23, 27, 29, 3, -26 }, // 0x4E 'N'
{ 2741, 27, 27, 31, 2, -26 }, // 0x4F 'O'
{ 2833, 20, 27, 25, 3, -26 }, // 0x50 'P'
{ 2901, 27, 34, 31, 2, -26 }, // 0x51 'Q'
{ 3016, 23, 27, 26, 3, -26 }, // 0x52 'R'
{ 3094, 20, 27, 23, 1, -26 }, // 0x53 'S'
{ 3162, 22, 27, 24, 1, -26 }, // 0x54 'T'
{ 3237, 22, 27, 28, 3, -26 }, // 0x55 'U'
{ 3312, 27, 27, 27, 0, -26 }, // 0x56 'V'
{ 3404, 36, 27, 38, 1, -26 }, // 0x57 'W'
{ 3526, 26, 27, 26, 0, -26 }, // 0x58 'X'
{ 3614, 26, 27, 26, 0, -26 }, // 0x59 'Y'
{ 3702, 22, 27, 24, 1, -26 }, // 0x5A 'Z'
{ 3777, 11, 38, 14, 3, -30 }, // 0x5B '['
{ 3830, 19, 38, 17, -1, -30 }, // 0x5C '\'
{ 3921, 11, 38, 14, 0, -30 }, // 0x5D ']'
{ 3974, 21, 16, 23, 1, -26 }, // 0x5E '^'
{ 4016, 20, 5, 20, 0, 3 }, // 0x5F '_'
{ 4029, 9, 9, 11, 1, -30 }, // 0x60 '`'
{ 4040, 17, 21, 22, 2, -20 }, // 0x61 'a'
{ 4085, 19, 30, 24, 3, -29 }, // 0x62 'b'
{ 4157, 17, 21, 20, 2, -20 }, // 0x63 'c'
{ 4202, 19, 30, 24, 2, -29 }, // 0x64 'd'
{ 4274, 19, 21, 23, 2, -20 }, // 0x65 'e'
{ 4324, 14, 30, 17, 3, -29 }, // 0x66 'f'
{ 4377, 18, 28, 23, 2, -20 }, // 0x67 'g'
{ 4440, 17, 30, 23, 3, -29 }, // 0x68 'h'
{ 4504, 8, 30, 12, 2, -29 }, // 0x69 'i'
{ 4534, 13, 37, 10, -4, -29 }, // 0x6A 'j'
{ 4595, 19, 30, 23, 3, -29 }, // 0x6B 'k'
{ 4667, 9, 30, 13, 3, -29 }, // 0x6C 'l'
{ 4701, 28, 21, 34, 3, -20 }, // 0x6D 'm'
{ 4775, 17, 21, 23, 3, -20 }, // 0x6E 'n'
{ 4820, 20, 21, 24, 2, -20 }, // 0x6F 'o'
{ 4873, 19, 28, 24, 3, -20 }, // 0x70 'p'
{ 4940, 20, 28, 24, 2, -20 }, // 0x71 'q'
{ 5010, 14, 21, 17, 3, -20 }, // 0x72 'r'
{ 5047, 16, 21, 19, 1, -20 }, // 0x73 's'
{ 5089, 14, 27, 18, 3, -26 }, // 0x74 't'
{ 5137, 17, 21, 23, 3, -20 }, // 0x75 'u'
{ 5182, 21, 21, 21, 0, -20 }, // 0x76 'v'
{ 5238, 30, 21, 30, 0, -20 }, // 0x77 'w'
{ 5317, 22, 21, 22, 0, -20 }, // 0x78 'x'
{ 5375, 21, 28, 21, 0, -20 }, // 0x79 'y'
{ 5449, 18, 21, 20, 1, -20 }, // 0x7A 'z'
{ 5497, 13, 38, 14, 1, -30 }, // 0x7B '{'
{ 5559, 6, 38, 14, 4, -30 }, // 0x7C '|'
{ 5588, 13, 38, 14, 0, -30 }, // 0x7D '}'
{ 5650, 20, 8, 22, 1, -15 } }; // 0x7E '~'
const GFXfont Ubuntu_Bold20pt7b PROGMEM = {
(uint8_t *)Ubuntu_Bold20pt7bBitmaps,
(GFXglyph *)Ubuntu_Bold20pt7bGlyphs,
0x20, 0x7E, 45 };
// Approx. 6342 bytes

File diff suppressed because it is too large Load Diff

View File

@@ -1,182 +0,0 @@
const uint8_t Ubuntu_Bold8pt7bBitmaps[] PROGMEM = {
0xFF, 0xFC, 0xFC, 0xDE, 0xF7, 0xBD, 0x80, 0x1B, 0x0D, 0x87, 0xDF, 0xFF,
0xF9, 0xB3, 0xFF, 0xFF, 0x6C, 0x36, 0x1B, 0x00, 0x18, 0x30, 0xFF, 0xFC,
0x38, 0x1C, 0x1E, 0x0E, 0x0C, 0x1F, 0xF7, 0xC3, 0x06, 0x00, 0x78, 0x63,
0xF3, 0x0C, 0xCC, 0x33, 0x60, 0xCF, 0xFB, 0xFF, 0xF7, 0xFC, 0xC1, 0xB3,
0x0C, 0xCC, 0x33, 0xF1, 0x87, 0x80, 0x3C, 0x1F, 0x86, 0x61, 0x98, 0x3C,
0x1E, 0x6C, 0xDB, 0x1C, 0xC7, 0x3F, 0xE7, 0xDC, 0xFF, 0xC0, 0x13, 0x66,
0xCC, 0xCC, 0xCC, 0xCC, 0x66, 0x31, 0x8C, 0x66, 0x33, 0x33, 0x33, 0x33,
0x66, 0xC8, 0x39, 0xFF, 0xF8, 0x86, 0xDD, 0xD1, 0x00, 0x18, 0x18, 0x18,
0xFF, 0xFF, 0x18, 0x18, 0x18, 0x6D, 0xEC, 0xFF, 0xC0, 0xFF, 0x80, 0x06,
0x1C, 0x30, 0x60, 0xC3, 0x06, 0x0C, 0x30, 0x60, 0xC3, 0x06, 0x0C, 0x38,
0x60, 0x38, 0xFB, 0xBE, 0x3C, 0x78, 0xF1, 0xE3, 0xEE, 0xF8, 0xE0, 0x1B,
0xFE, 0xB1, 0x8C, 0x63, 0x18, 0xC6, 0x7D, 0xFD, 0x18, 0x30, 0xE3, 0x8E,
0x38, 0xE1, 0xFF, 0xF8, 0x7D, 0xFC, 0x18, 0x33, 0xE7, 0x81, 0x83, 0x87,
0xFF, 0xE0, 0x06, 0x0E, 0x1E, 0x3E, 0x36, 0x66, 0xE6, 0xFF, 0xFF, 0x06,
0x06, 0x7E, 0xFD, 0x83, 0x0F, 0x9F, 0x83, 0x83, 0x07, 0xFB, 0xE0, 0x1C,
0x79, 0xC7, 0x0F, 0xDF, 0xF1, 0xE3, 0xC6, 0xF8, 0xE0, 0xFF, 0xFC, 0x30,
0xE1, 0x87, 0x0C, 0x18, 0x60, 0xC1, 0x80, 0x3C, 0xFF, 0x1E, 0x3E, 0xEF,
0xBF, 0xE3, 0xC7, 0xFD, 0xF0, 0x38, 0xFB, 0x1E, 0x3C, 0x7F, 0xDF, 0x87,
0x1C, 0xF1, 0xC0, 0xFF, 0x80, 0x3F, 0xE0, 0x77, 0x70, 0x00, 0x06, 0x66,
0xCC, 0x06, 0x3E, 0xF8, 0xC0, 0xF8, 0x3F, 0x06, 0xFF, 0xFF, 0x00, 0x00,
0xFF, 0xFF, 0x60, 0x7C, 0x1F, 0x03, 0x1F, 0xFC, 0x60, 0x7D, 0xFC, 0x18,
0x30, 0xC3, 0x0C, 0x18, 0x00, 0x70, 0xE1, 0xC0, 0x0F, 0xC0, 0x7F, 0xC3,
0x83, 0x98, 0x06, 0xE7, 0xCF, 0x3F, 0x3D, 0x8C, 0xF6, 0x33, 0xD8, 0xCF,
0xBF, 0xE6, 0x7F, 0x1E, 0x00, 0x3F, 0xC0, 0x3F, 0x00, 0x0E, 0x01, 0xC0,
0x7C, 0x0D, 0x83, 0xB8, 0x63, 0x0C, 0x63, 0xFE, 0x7F, 0xCC, 0x1B, 0x01,
0x80, 0xFC, 0x7F, 0xB0, 0xD8, 0x6F, 0xE7, 0xFB, 0x07, 0x83, 0xC3, 0xFF,
0xBF, 0x80, 0x1F, 0x1F, 0xD8, 0x18, 0x0C, 0x06, 0x03, 0x01, 0x80, 0x60,
0x3F, 0xC7, 0xC0, 0xFE, 0x3F, 0xCC, 0x3B, 0x07, 0xC0, 0xF0, 0x3C, 0x0F,
0x07, 0xC3, 0xBF, 0xCF, 0xE0, 0xFF, 0xFF, 0xC0, 0xC0, 0xFE, 0xFE, 0xC0,
0xC0, 0xC0, 0xFF, 0xFF, 0xFF, 0xFF, 0xC0, 0xC0, 0xFE, 0xFE, 0xC0, 0xC0,
0xC0, 0xC0, 0xC0, 0x1F, 0x1F, 0xD8, 0x18, 0x0C, 0x06, 0x0F, 0x07, 0x83,
0x71, 0xBF, 0xC7, 0xE0, 0xC1, 0xE0, 0xF0, 0x78, 0x3F, 0xFF, 0xFF, 0x07,
0x83, 0xC1, 0xE0, 0xF0, 0x60, 0xFF, 0xFF, 0xFC, 0x06, 0x0C, 0x18, 0x30,
0x60, 0xC1, 0x83, 0x07, 0xF9, 0xE0, 0xC1, 0xB0, 0xCC, 0x63, 0x30, 0xF8,
0x3C, 0x0D, 0x83, 0x30, 0xC6, 0x30, 0xCC, 0x18, 0xC0, 0xC0, 0xC0, 0xC0,
0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xFF, 0xFF, 0x60, 0x33, 0x83, 0x9C, 0x1C,
0xF1, 0xED, 0x8D, 0xEE, 0xEF, 0x36, 0x79, 0xF3, 0xC7, 0x1E, 0x38, 0xF0,
0x06, 0xC0, 0xF8, 0x3F, 0x0F, 0xE3, 0xDC, 0xF3, 0xBC, 0x7F, 0x0F, 0xC1,
0xF0, 0x7C, 0x0C, 0x1F, 0x07, 0xF1, 0xC7, 0x70, 0x7C, 0x07, 0x80, 0xF0,
0x1F, 0x07, 0x71, 0xCF, 0xF8, 0x7C, 0x00, 0xFC, 0xFE, 0xC3, 0xC3, 0xC3,
0xFE, 0xFC, 0xC0, 0xC0, 0xC0, 0xC0, 0x1F, 0x07, 0xF1, 0xC7, 0x70, 0x7C,
0x07, 0x80, 0xF0, 0x1F, 0x07, 0x71, 0xCF, 0xF0, 0xF8, 0x07, 0x00, 0x7C,
0x07, 0x80, 0xFC, 0x7F, 0x30, 0xD8, 0x6C, 0x37, 0xF3, 0xE1, 0x98, 0xC6,
0x63, 0xB0, 0xE0, 0x3D, 0xFF, 0x06, 0x0F, 0x0F, 0x87, 0x83, 0x07, 0xFD,
0xF0, 0xFF, 0xFF, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
0xC1, 0xE0, 0xF0, 0x78, 0x3C, 0x1E, 0x0F, 0x07, 0x83, 0xE3, 0xBF, 0x8F,
0x80, 0xC0, 0x6C, 0x19, 0x83, 0x38, 0xE3, 0x18, 0x63, 0x06, 0xC0, 0xD8,
0x0E, 0x01, 0xC0, 0x38, 0x00, 0xC0, 0x07, 0x87, 0x0D, 0x8E, 0x33, 0x1C,
0x66, 0x6C, 0xC6, 0xDB, 0x0D, 0xB6, 0x1B, 0x6C, 0x1C, 0x70, 0x38, 0xE0,
0x71, 0xC0, 0xE0, 0xEC, 0x18, 0xC6, 0x0D, 0x80, 0xE0, 0x1C, 0x03, 0x80,
0xD8, 0x31, 0x8C, 0x1B, 0x83, 0x80, 0xC0, 0xD8, 0x67, 0x38, 0xCC, 0x1E,
0x07, 0x80, 0xC0, 0x30, 0x0C, 0x03, 0x00, 0xC0, 0xFF, 0xFF, 0x06, 0x0C,
0x1C, 0x18, 0x30, 0x70, 0x60, 0xFF, 0xFF, 0xFF, 0xF1, 0x8C, 0x63, 0x18,
0xC6, 0x31, 0x8C, 0x63, 0xFF, 0xC1, 0xC1, 0x83, 0x06, 0x06, 0x0C, 0x18,
0x18, 0x30, 0x60, 0x60, 0xC1, 0x83, 0x83, 0xFF, 0xC6, 0x31, 0x8C, 0x63,
0x18, 0xC6, 0x31, 0x8F, 0xFF, 0x08, 0x0E, 0x0F, 0x86, 0xC7, 0x77, 0x1D,
0x04, 0xFF, 0xFF, 0x4E, 0x72, 0x7C, 0xFC, 0x1B, 0xFF, 0xF8, 0xFF, 0xBF,
0xC1, 0x83, 0x06, 0x0F, 0x9F, 0xB3, 0xE3, 0xC7, 0x9F, 0xF7, 0xC0, 0x3D,
0xFE, 0x30, 0xC3, 0x87, 0xCF, 0x06, 0x0C, 0x18, 0x33, 0xEF, 0xF9, 0xE3,
0xC7, 0xCD, 0xF9, 0xF0, 0x38, 0xFB, 0x1F, 0xFF, 0xF8, 0x1F, 0x1E, 0x7F,
0xF1, 0x8F, 0xFF, 0x18, 0xC6, 0x31, 0x80, 0x3E, 0xFF, 0x9E, 0x3C, 0x7C,
0xDF, 0x9F, 0x06, 0xFB, 0xE0, 0xC1, 0x83, 0x06, 0x0F, 0xDF, 0xF1, 0xE3,
0xC7, 0x8F, 0x1E, 0x30, 0xFC, 0xFF, 0xFF, 0x33, 0x30, 0x33, 0x33, 0x33,
0x33, 0x3F, 0xE0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC6, 0xCC, 0xD8, 0xF0, 0xD8,
0xCC, 0xC6, 0xC7, 0xDB, 0x6D, 0xB6, 0xDB, 0xB0, 0xFD, 0xEF, 0xFF, 0xC6,
0x3C, 0x63, 0xC6, 0x3C, 0x63, 0xC6, 0x3C, 0x63, 0xFD, 0xFF, 0x1E, 0x3C,
0x78, 0xF1, 0xE3, 0x3C, 0x7E, 0xE7, 0xC3, 0xC3, 0xE7, 0x7E, 0x3C, 0xF9,
0xFB, 0x3E, 0x3C, 0x79, 0xFF, 0x7C, 0xC1, 0x83, 0x00, 0x3E, 0xFF, 0x9E,
0x3C, 0x7C, 0xDF, 0xBF, 0x06, 0x0C, 0x18, 0xFF, 0xF1, 0x8C, 0x63, 0x18,
0x7F, 0xEC, 0x3E, 0x7C, 0x3F, 0xFE, 0xC6, 0x3F, 0xFC, 0x63, 0x18, 0xFB,
0xC0, 0xC7, 0x8F, 0x1E, 0x3C, 0x78, 0xFF, 0xBF, 0xC1, 0xB1, 0x98, 0xCE,
0xE3, 0x61, 0xF0, 0x70, 0x38, 0xC6, 0x79, 0xCD, 0xBB, 0x35, 0x66, 0xAC,
0x77, 0x0E, 0xE1, 0x8C, 0xE3, 0xBB, 0x8D, 0x83, 0x81, 0xC1, 0xB1, 0xDD,
0xC7, 0xE3, 0xB1, 0x98, 0xCE, 0xE3, 0x61, 0xF0, 0x70, 0x38, 0x18, 0x7C,
0x3C, 0x00, 0xFF, 0xF1, 0x8E, 0x71, 0x8F, 0xFF, 0x3B, 0xD8, 0xC6, 0x31,
0x98, 0xC3, 0x18, 0xC6, 0x31, 0xE7, 0xFF, 0xFF, 0xFF, 0xFF, 0xE7, 0x8C,
0x63, 0x18, 0xC3, 0x19, 0x8C, 0x63, 0x1B, 0xDC, 0x73, 0xFF, 0xCE };
const GFXglyph Ubuntu_Bold8pt7bGlyphs[] PROGMEM = {
{ 0, 0, 0, 4, 0, 1 }, // 0x20 ' '
{ 0, 2, 11, 4, 1, -10 }, // 0x21 '!'
{ 3, 5, 5, 7, 1, -11 }, // 0x22 '"'
{ 7, 9, 11, 11, 1, -10 }, // 0x23 '#'
{ 20, 7, 15, 9, 1, -12 }, // 0x24 '$'
{ 34, 14, 11, 16, 1, -10 }, // 0x25 '%'
{ 54, 10, 11, 11, 1, -10 }, // 0x26 '&'
{ 68, 2, 5, 4, 1, -11 }, // 0x27 '''
{ 70, 4, 16, 6, 1, -12 }, // 0x28 '('
{ 78, 4, 16, 6, 1, -12 }, // 0x29 ')'
{ 86, 7, 7, 8, 1, -10 }, // 0x2A '*'
{ 93, 8, 8, 10, 1, -8 }, // 0x2B '+'
{ 101, 3, 5, 4, 0, -1 }, // 0x2C ','
{ 103, 5, 2, 7, 1, -5 }, // 0x2D '-'
{ 105, 3, 3, 5, 1, -2 }, // 0x2E '.'
{ 107, 7, 16, 7, 0, -12 }, // 0x2F '/'
{ 121, 7, 11, 9, 1, -10 }, // 0x30 '0'
{ 131, 5, 11, 9, 1, -10 }, // 0x31 '1'
{ 138, 7, 11, 9, 1, -10 }, // 0x32 '2'
{ 148, 7, 11, 9, 1, -10 }, // 0x33 '3'
{ 158, 8, 11, 9, 1, -10 }, // 0x34 '4'
{ 169, 7, 11, 9, 1, -10 }, // 0x35 '5'
{ 179, 7, 11, 9, 1, -10 }, // 0x36 '6'
{ 189, 7, 11, 9, 1, -10 }, // 0x37 '7'
{ 199, 7, 11, 9, 1, -10 }, // 0x38 '8'
{ 209, 7, 11, 9, 1, -10 }, // 0x39 '9'
{ 219, 3, 9, 5, 1, -8 }, // 0x3A ':'
{ 223, 4, 12, 5, 0, -8 }, // 0x3B ';'
{ 229, 8, 7, 9, 1, -7 }, // 0x3C '<'
{ 236, 8, 6, 10, 1, -7 }, // 0x3D '='
{ 242, 8, 7, 9, 0, -7 }, // 0x3E '>'
{ 249, 7, 12, 7, 0, -11 }, // 0x3F '?'
{ 260, 14, 14, 16, 1, -10 }, // 0x40 '@'
{ 285, 11, 11, 11, 0, -10 }, // 0x41 'A'
{ 301, 9, 11, 11, 1, -10 }, // 0x42 'B'
{ 314, 9, 11, 11, 1, -10 }, // 0x43 'C'
{ 327, 10, 11, 12, 1, -10 }, // 0x44 'D'
{ 341, 8, 11, 10, 1, -10 }, // 0x45 'E'
{ 352, 8, 11, 9, 1, -10 }, // 0x46 'F'
{ 363, 9, 11, 11, 1, -10 }, // 0x47 'G'
{ 376, 9, 11, 11, 1, -10 }, // 0x48 'H'
{ 389, 2, 11, 4, 1, -10 }, // 0x49 'I'
{ 392, 7, 11, 8, 0, -10 }, // 0x4A 'J'
{ 402, 10, 11, 11, 1, -10 }, // 0x4B 'K'
{ 416, 8, 11, 9, 1, -10 }, // 0x4C 'L'
{ 427, 13, 11, 15, 1, -10 }, // 0x4D 'M'
{ 445, 10, 11, 12, 1, -10 }, // 0x4E 'N'
{ 459, 11, 11, 13, 1, -10 }, // 0x4F 'O'
{ 475, 8, 11, 10, 1, -10 }, // 0x50 'P'
{ 486, 11, 14, 13, 1, -10 }, // 0x51 'Q'
{ 506, 9, 11, 10, 1, -10 }, // 0x52 'R'
{ 519, 7, 11, 9, 1, -10 }, // 0x53 'S'
{ 529, 8, 11, 8, 0, -10 }, // 0x54 'T'
{ 540, 9, 11, 11, 1, -10 }, // 0x55 'U'
{ 553, 11, 11, 11, 0, -10 }, // 0x56 'V'
{ 569, 15, 11, 15, 0, -10 }, // 0x57 'W'
{ 590, 11, 11, 11, 0, -10 }, // 0x58 'X'
{ 606, 10, 11, 10, 0, -10 }, // 0x59 'Y'
{ 620, 8, 11, 10, 1, -10 }, // 0x5A 'Z'
{ 631, 5, 16, 6, 1, -12 }, // 0x5B '['
{ 641, 7, 16, 7, 0, -12 }, // 0x5C '\'
{ 655, 5, 16, 6, 0, -12 }, // 0x5D ']'
{ 665, 9, 7, 9, 0, -10 }, // 0x5E '^'
{ 673, 8, 2, 8, 0, 2 }, // 0x5F '_'
{ 675, 4, 4, 5, 1, -12 }, // 0x60 '`'
{ 677, 7, 8, 9, 1, -7 }, // 0x61 'a'
{ 684, 7, 12, 9, 1, -11 }, // 0x62 'b'
{ 695, 6, 8, 8, 1, -7 }, // 0x63 'c'
{ 701, 7, 12, 9, 1, -11 }, // 0x64 'd'
{ 712, 7, 8, 9, 1, -7 }, // 0x65 'e'
{ 719, 5, 12, 6, 1, -11 }, // 0x66 'f'
{ 727, 7, 11, 9, 1, -7 }, // 0x67 'g'
{ 737, 7, 12, 9, 1, -11 }, // 0x68 'h'
{ 748, 2, 12, 4, 1, -11 }, // 0x69 'i'
{ 751, 4, 15, 4, -1, -11 }, // 0x6A 'j'
{ 759, 8, 12, 9, 1, -11 }, // 0x6B 'k'
{ 771, 3, 12, 4, 1, -11 }, // 0x6C 'l'
{ 776, 12, 8, 14, 1, -7 }, // 0x6D 'm'
{ 788, 7, 8, 9, 1, -7 }, // 0x6E 'n'
{ 795, 8, 8, 10, 1, -7 }, // 0x6F 'o'
{ 803, 7, 11, 9, 1, -7 }, // 0x70 'p'
{ 813, 7, 11, 9, 1, -7 }, // 0x71 'q'
{ 823, 5, 8, 6, 1, -7 }, // 0x72 'r'
{ 828, 6, 8, 8, 1, -7 }, // 0x73 's'
{ 834, 5, 10, 7, 1, -9 }, // 0x74 't'
{ 841, 7, 8, 9, 1, -7 }, // 0x75 'u'
{ 848, 9, 8, 9, 0, -7 }, // 0x76 'v'
{ 857, 11, 8, 11, 0, -7 }, // 0x77 'w'
{ 868, 9, 8, 9, 0, -7 }, // 0x78 'x'
{ 877, 9, 11, 9, 0, -7 }, // 0x79 'y'
{ 890, 6, 8, 8, 1, -7 }, // 0x7A 'z'
{ 896, 5, 16, 5, 0, -12 }, // 0x7B '{'
{ 906, 2, 16, 4, 1, -12 }, // 0x7C '|'
{ 910, 5, 16, 5, 0, -12 }, // 0x7D '}'
{ 920, 8, 3, 9, 1, -5 } }; // 0x7E '~'
const GFXfont Ubuntu_Bold8pt7b PROGMEM = {
(uint8_t *)Ubuntu_Bold8pt7bBitmaps,
(GFXglyph *)Ubuntu_Bold8pt7bGlyphs,
0x20, 0x7E, 18 };
// Approx. 1595 bytes

View File

@@ -6,10 +6,12 @@ https://gitpod.io/#https://github.com/norbert-walter/esp32-nmea2000-obp60/tree/m
Input in terminal: Input in terminal:
cd /workspace/esp32-nmea2000-obp60 cd /workspace/esp32-nmea2000-obp60
bash /workspace/esp32-nmea2000-obp60/lib/obp60task/run bash /workspace/esp32-nmea2000-obp60/lib/obp60task/run_installing_tools
bash /workspace/esp32-nmea2000-obp60/lib/obp60task/run_obp60_s3
bash /workspace/esp32-nmea2000-obp60/lib/obp60task/run_obp40_s3
Compile result in: Compile result for OBP60
/workspace/esp32-nmea2000-obp60/.pio/build/obp60_s3/obp60_s3-all.bin, ready to flash to offset 0x0000 ########################
/workspace/esp32-nmea2000-obp60/.pio/build/obp60_s3/bootloader.bin /workspace/esp32-nmea2000-obp60/.pio/build/obp60_s3/bootloader.bin
/workspace/esp32-nmea2000-obp60/.pio/build/obp60_s3/firmware.bin /workspace/esp32-nmea2000-obp60/.pio/build/obp60_s3/firmware.bin
@@ -18,3 +20,19 @@ Compile result in:
/workspace/esp32-nmea2000-obp60/.pio/build/obp60_s3/obp60_s3-all.bin /workspace/esp32-nmea2000-obp60/.pio/build/obp60_s3/obp60_s3-all.bin
/workspace/esp32-nmea2000-obp60/.pio/build/obp60_s3/obp60_s3-dev20231220-all.bin /workspace/esp32-nmea2000-obp60/.pio/build/obp60_s3/obp60_s3-dev20231220-all.bin
/workspace/esp32-nmea2000-obp60/.pio/build/obp60_s3/obp60_s3-dev20231220-update.bin /workspace/esp32-nmea2000-obp60/.pio/build/obp60_s3/obp60_s3-dev20231220-update.bin
/workspace/esp32-nmea2000-obp60/.pio/build/obp60_s3/obp60_s3-all.bin, ready to flash to offset 0x0000
Compile result for OBP40 (CrowPanel 4.2)
########################################
/workspace/esp32-nmea2000-obp60/.pio/build/obp40_s3/bootloader.bin
/workspace/esp32-nmea2000-obp60/.pio/build/obp40_s3/firmware.bin
/workspace/esp32-nmea2000-obp60/.pio/build/obp40_s3/partitions.bin
/workspace/esp32-nmea2000-obp60/.pio/build/obp40_s3/obp40_s3-all.bin
/workspace/esp32-nmea2000-obp60/.pio/build/obp40_s3/obp40_s3-dev20231220-all.bin
/workspace/esp32-nmea2000-obp60/.pio/build/obp40_s3/obp40_s3-dev20231220-update.bin
/workspace/esp32-nmea2000-obp60/.pio/build/obp40_s3/obp40_s3-all.bin, ready to flash to offset 0x0000

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,6 @@
Debugging tool
##############
log.txt = text file with error messages from terminal console
tools/decoder.py -p ESP32S3 -t ~/.platformio/packages/toolchain-xtensa-esp32s3/ -e .pio/build/obp60_s3/firmware.elf log.txt

View File

@@ -0,0 +1,30 @@
# PlatformIO extra script for obp60task
epdtype = "unknown"
pcbvers = "unknown"
for x in env["BUILD_FLAGS"]:
if x.startswith("-D HARDWARE_"):
pcbvers = x.split('_')[1]
if x.startswith("-D DISPLAY_"):
epdtype = x.split('_')[1]
propfilename = os.path.join(env["PROJECT_LIBDEPS_DIR"], env["PIOENV"], "GxEPD2/library.properties")
properties = {}
with open(propfilename, 'r') as file:
for line in file:
match = re.match(r'^([^=]+)=(.*)$', line)
if match:
key = match.group(1).strip()
value = match.group(2).strip()
properties[key] = value
gxepd2vers = "unknown"
try:
if properties["name"] == "GxEPD2":
gxepd2vers = properties["version"]
except:
pass
env["CPPDEFINES"].extend([("BOARD", env["BOARD"]), ("EPDTYPE", epdtype), ("PCBVERS", pcbvers), ("GXEPD2VERS", gxepd2vers)])
print("added hardware info to CPPDEFINES")

View File

@@ -0,0 +1,338 @@
const uint8_t Atari16pxBitmaps[] PROGMEM = {
0x00, 0xFF, 0xFF, 0x0F, 0xCF, 0x3C, 0xF3, 0xCF, 0x30, 0x66, 0x66, 0xFF,
0xFF, 0x66, 0x66, 0xFF, 0xFF, 0x66, 0x66, 0x30, 0xC7, 0xFF, 0xC3, 0x0F,
0x9F, 0x0C, 0x3F, 0xFE, 0x30, 0xC0, 0xCF, 0x3D, 0x86, 0x30, 0xC6, 0x1B,
0xCF, 0x30, 0x38, 0xF9, 0xB3, 0x63, 0x87, 0x1C, 0x38, 0xDF, 0xBF, 0x36,
0x6F, 0xEE, 0xC0, 0xFF, 0xF0, 0x36, 0xEC, 0xCC, 0xCC, 0xCE, 0x63, 0xC6,
0x73, 0x33, 0x33, 0x37, 0x6C, 0x66, 0x66, 0x3C, 0x3C, 0xFF, 0xFF, 0x3C,
0x3C, 0x66, 0x66, 0x30, 0xC3, 0x3F, 0xFC, 0xC3, 0x0C, 0x6D, 0xBD, 0x00,
0xFF, 0xF0, 0xFF, 0x0C, 0x30, 0xC6, 0x18, 0xC3, 0x18, 0x63, 0x0C, 0x30,
0x7B, 0xFC, 0xF3, 0xCF, 0x7E, 0xF3, 0xCF, 0x3F, 0xDE, 0x30, 0xC7, 0x1C,
0x30, 0xC3, 0x0C, 0x30, 0xCF, 0xFF, 0x7B, 0xFC, 0xF3, 0x18, 0x63, 0x0C,
0x61, 0x8F, 0xFF, 0xFF, 0xF1, 0x86, 0x30, 0xC1, 0x86, 0xCF, 0x3F, 0xDE,
0x18, 0x63, 0x8E, 0x79, 0xED, 0xB6, 0xFF, 0xF1, 0x86, 0xFF, 0xFC, 0x30,
0xFB, 0xF0, 0xC3, 0x0F, 0x3F, 0xDE, 0x39, 0xEE, 0x30, 0xC3, 0xEF, 0xF3,
0xCF, 0x3F, 0xDE, 0xFF, 0xF0, 0xC3, 0x18, 0x63, 0x0C, 0x61, 0x86, 0x18,
0x7B, 0xFC, 0xF3, 0x79, 0xEC, 0xF3, 0xCF, 0x3F, 0xDE, 0x7B, 0xFC, 0xF3,
0xFD, 0xF0, 0xC3, 0x0C, 0x77, 0x9C, 0xFF, 0x0F, 0xF0, 0x6D, 0xB0, 0x1B,
0x6F, 0x40, 0x0E, 0x38, 0xE3, 0x8E, 0x0E, 0x0E, 0x0E, 0x0E, 0xFF, 0xF0,
0x00, 0xFF, 0xF0, 0xE0, 0xE0, 0xE0, 0xE0, 0xE3, 0x8E, 0x38, 0xE0, 0x7B,
0xFC, 0xF3, 0x18, 0x63, 0x0C, 0x30, 0x03, 0x0C, 0x38, 0xFB, 0x9E, 0x1D,
0xBA, 0xF5, 0xEE, 0xC1, 0xC5, 0xF9, 0xE0, 0x31, 0xEF, 0xF3, 0xCF, 0x3F,
0xFF, 0xCF, 0x3C, 0xF3, 0xFB, 0xFC, 0xF3, 0xFF, 0xEC, 0xF3, 0xCF, 0x3F,
0xFE, 0x7B, 0xFC, 0xF3, 0xC3, 0x0C, 0x30, 0xCF, 0x3F, 0xDE, 0xF3, 0xED,
0xF3, 0xCF, 0x3C, 0xF3, 0xCF, 0x7F, 0xBC, 0xFF, 0xFC, 0x30, 0xFB, 0xEC,
0x30, 0xC3, 0x0F, 0xFF, 0xFF, 0xFC, 0x30, 0xFB, 0xEC, 0x30, 0xC3, 0x0C,
0x30, 0x7F, 0xFC, 0x30, 0xDF, 0x7C, 0xF3, 0xCF, 0x3F, 0xDE, 0xCF, 0x3C,
0xF3, 0xFF, 0xFC, 0xF3, 0xCF, 0x3C, 0xF3, 0xFF, 0xF3, 0x0C, 0x30, 0xC3,
0x0C, 0x30, 0xCF, 0xFF, 0x0C, 0x30, 0xC3, 0x0C, 0x30, 0xC3, 0xCF, 0x3F,
0xDE, 0xCD, 0x9B, 0x66, 0xCF, 0x1E, 0x36, 0x6C, 0xCD, 0x9B, 0x1E, 0x30,
0xC3, 0x0C, 0x30, 0xC3, 0x0C, 0x30, 0xC3, 0x0F, 0xFF, 0xC7, 0x8F, 0xBF,
0x7F, 0xFA, 0xF5, 0xE3, 0xC7, 0x8F, 0x1E, 0x30, 0xCF, 0x3C, 0xFB, 0xEF,
0xFF, 0xF7, 0xDF, 0x3C, 0xF3, 0x7B, 0xFC, 0xF3, 0xCF, 0x3C, 0xF3, 0xCF,
0x3F, 0xDE, 0xFB, 0xFC, 0xF3, 0xCF, 0x3F, 0xFE, 0xC3, 0x0C, 0x30, 0x7B,
0xFC, 0xF3, 0xCF, 0x3C, 0xF3, 0xCF, 0x5F, 0x9B, 0xF9, 0xFB, 0x36, 0x6C,
0xDF, 0xBE, 0x6C, 0xCD, 0x9B, 0x1E, 0x30, 0x7F, 0xFC, 0x30, 0xE1, 0xC3,
0x87, 0x0C, 0x3F, 0xFE, 0xFF, 0xF3, 0x0C, 0x30, 0xC3, 0x0C, 0x30, 0xC3,
0x0C, 0xCF, 0x3C, 0xF3, 0xCF, 0x3C, 0xF3, 0xCF, 0x3F, 0xDE, 0xCF, 0x3C,
0xF3, 0xCF, 0x3C, 0xF3, 0x79, 0xE3, 0x0C, 0xC7, 0x8F, 0x1E, 0x3C, 0x7A,
0xF5, 0xFF, 0xFF, 0xDF, 0x1C, 0x10, 0xCF, 0x3C, 0xDE, 0x78, 0xC3, 0x1E,
0x7B, 0x3C, 0xF3, 0xCF, 0x3C, 0xF3, 0x79, 0xE3, 0x0C, 0x30, 0xC3, 0x0C,
0xFF, 0xF1, 0x86, 0x30, 0xC6, 0x18, 0xC3, 0x0F, 0xFF, 0xFF, 0xCC, 0xCC,
0xCC, 0xCC, 0xFF, 0xC3, 0x0C, 0x18, 0x60, 0xC3, 0x06, 0x18, 0x30, 0xC3,
0xFF, 0x33, 0x33, 0x33, 0x33, 0xFF, 0x10, 0x20, 0xE1, 0xC6, 0xCD, 0xB1,
0xE3, 0xFF, 0xFC, 0x86, 0x38, 0xE3, 0x8C, 0x20, 0x79, 0xF0, 0xDF, 0xFF,
0x3C, 0xFF, 0x7C, 0xC3, 0x0C, 0x3E, 0xFF, 0x3C, 0xF3, 0xCF, 0x3F, 0xFE,
0x7B, 0xEC, 0x30, 0xC3, 0x0C, 0x3F, 0x7C, 0x0C, 0x30, 0xDF, 0xFF, 0x3C,
0xF3, 0xCF, 0x3F, 0xDF, 0x7B, 0xFC, 0xF3, 0xFF, 0x0C, 0x3F, 0x7C, 0x1C,
0xF3, 0x0C, 0xFF, 0xF3, 0x0C, 0x30, 0xC3, 0x0C, 0x7F, 0xFC, 0xF3, 0xCF,
0x3F, 0xDF, 0x0F, 0xFF, 0x80, 0xC3, 0x0C, 0x3E, 0xFF, 0x3C, 0xF3, 0xCF,
0x3C, 0xF3, 0x66, 0x0E, 0xE6, 0x66, 0x66, 0xFF, 0x18, 0xC0, 0x31, 0x8C,
0x63, 0x18, 0xC6, 0x3F, 0xF8, 0xC1, 0x83, 0x06, 0x6D, 0xDF, 0x3C, 0x7C,
0xD9, 0x9B, 0x3E, 0x30, 0xEE, 0x66, 0x66, 0x66, 0x66, 0xFF, 0x6D, 0xFF,
0xFE, 0xBD, 0x7A, 0xF1, 0xE3, 0xC6, 0x7B, 0xFC, 0xF3, 0xCF, 0x3C, 0xF3,
0xCC, 0x7B, 0xFC, 0xF3, 0xCF, 0x3C, 0xFF, 0x78, 0xFB, 0xFC, 0xF3, 0xCF,
0x3C, 0xFF, 0xFB, 0x0C, 0x00, 0x7F, 0xFC, 0xF3, 0xCF, 0x3C, 0xFF, 0x7C,
0x30, 0xC0, 0xFB, 0xFC, 0xF0, 0xC3, 0x0C, 0x30, 0xC0, 0x7F, 0xFC, 0x38,
0x78, 0x70, 0xFF, 0xF8, 0x30, 0xCF, 0xFF, 0x30, 0xC3, 0x0C, 0x30, 0xF1,
0xC0, 0xCF, 0x3C, 0xF3, 0xCF, 0x3C, 0xFF, 0x7C, 0xCF, 0x3C, 0xF3, 0xCD,
0xE7, 0x8C, 0x30, 0xC7, 0x8F, 0x5E, 0xBF, 0xFF, 0xFB, 0xE3, 0x82, 0xCF,
0x37, 0x9E, 0x31, 0xE7, 0xB3, 0xCC, 0xCF, 0x3C, 0xF3, 0xCF, 0x3F, 0xDF,
0x0F, 0xFF, 0x80, 0xFF, 0xF1, 0x8C, 0x31, 0x86, 0x3F, 0xFC, 0x0E, 0x30,
0x60, 0xC1, 0x87, 0x3C, 0x78, 0x38, 0x30, 0x60, 0xC1, 0x81, 0xC0, 0xFF,
0xFF, 0xFF, 0xF0, 0xE0, 0x60, 0xC1, 0x83, 0x07, 0x07, 0x8F, 0x38, 0x60,
0xC1, 0x83, 0x1C, 0x00, 0x63, 0xE6, 0xFC, 0xE0, 0xFC, 0x63, 0x18, 0xC6,
0x31, 0x8C, 0x7E, 0x00, 0xF3, 0xFF, 0xFF, 0x30, 0xC7, 0xBF, 0xCF, 0x0C,
0x33, 0xFD, 0xE3, 0x0C, 0x0E, 0x1E, 0x38, 0x30, 0x30, 0x30, 0x30, 0xFE,
0x30, 0x30, 0x30, 0x7F, 0xFF, 0xFC, 0x63, 0x18, 0xC6, 0x31, 0x8C, 0x7E,
0xCF, 0x3C, 0xF3, 0xFD, 0xE3, 0x3F, 0x30, 0xC3, 0x0C, 0xFC, 0x63, 0x18,
0xC6, 0x31, 0x8C, 0x7E, 0x39, 0xB6, 0x4C, 0x7B, 0x3C, 0xDE, 0x32, 0x6D,
0x9C, 0xFC, 0x63, 0x18, 0xC6, 0x31, 0x8C, 0x7E, 0x7D, 0x8E, 0x0D, 0xDA,
0x34, 0x68, 0xDD, 0x83, 0x8D, 0xF0, 0x79, 0xF0, 0xDF, 0xFF, 0x3C, 0xFF,
0x7C, 0x0F, 0xC0, 0x1A, 0x6D, 0xB6, 0xC6, 0xC6, 0xC6, 0x80, 0xFF, 0xF0,
0xC3, 0x0C, 0xFF, 0x7D, 0x8E, 0x0D, 0xDA, 0xB6, 0x6E, 0xD5, 0x83, 0x8D,
0xF0, 0xFF, 0xFC, 0x76, 0xE3, 0xB7, 0x00, 0x30, 0xC3, 0x3F, 0xFC, 0xC3,
0x0C, 0x03, 0xFF, 0xC0, 0x69, 0x36, 0xCF, 0xF3, 0x63, 0x96, 0xFC, 0x63,
0x18, 0xC6, 0x31, 0x8C, 0x7E, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x7F,
0x5D, 0xC0, 0x80, 0x7B, 0x97, 0x2E, 0x5C, 0xB9, 0x5E, 0x85, 0x0A, 0x14,
0x28, 0x50, 0x6F, 0xF6, 0xFC, 0x63, 0x18, 0xC6, 0x31, 0x8C, 0x7E, 0xD5,
0x50, 0x7B, 0xFC, 0xF3, 0xCF, 0x3C, 0xFF, 0x78, 0x0F, 0xC0, 0xB1, 0xB1,
0xB1, 0xB6, 0xDB, 0x2C, 0x00, 0xFC, 0x63, 0x18, 0xC6, 0x31, 0x8C, 0x7E,
0xFC, 0x63, 0x18, 0xC6, 0x31, 0x8C, 0x7E, 0xFC, 0x63, 0x18, 0xC6, 0x31,
0x8C, 0x7E, 0x30, 0xC0, 0x0C, 0x30, 0xC6, 0x18, 0xCF, 0x3F, 0xDE, 0xC1,
0x83, 0x00, 0x7B, 0xFC, 0xF3, 0xFF, 0xFC, 0xF3, 0xCF, 0x30, 0x0C, 0x63,
0x00, 0x7B, 0xFC, 0xF3, 0xFF, 0xFC, 0xF3, 0xCF, 0x30, 0x31, 0xEC, 0xC0,
0x7B, 0xFC, 0xF3, 0xFF, 0xFC, 0xF3, 0xCF, 0x30, 0x67, 0xD9, 0x80, 0x7B,
0xFC, 0xF3, 0xFF, 0xFC, 0xF3, 0xCF, 0x30, 0xCF, 0x33, 0x1E, 0xFF, 0x3C,
0xFF, 0xFF, 0x3C, 0xF3, 0xCC, 0x7B, 0x37, 0x8C, 0x7B, 0xFC, 0xF3, 0xFF,
0xFC, 0xF3, 0xCF, 0x30, 0x3E, 0xFF, 0xE6, 0xCD, 0x9B, 0xF7, 0xFC, 0xF9,
0xB3, 0x66, 0xFD, 0xE0, 0x7B, 0xFC, 0xF3, 0xC3, 0x0C, 0x30, 0xCF, 0x3F,
0xDE, 0x19, 0xC0, 0xC1, 0x83, 0x00, 0xFF, 0xFC, 0x3E, 0xFB, 0x0C, 0x30,
0xFF, 0xF0, 0x0C, 0x63, 0x00, 0xFF, 0xFC, 0x3E, 0xFB, 0x0C, 0x30, 0xFF,
0xF0, 0x31, 0xEC, 0xC0, 0xFF, 0xFC, 0x3E, 0xFB, 0x0C, 0x30, 0xFF, 0xF0,
0xCF, 0x30, 0x3F, 0xFF, 0x0F, 0xBE, 0xC3, 0x0C, 0x3F, 0xFC, 0xC1, 0x83,
0x00, 0xFF, 0xF3, 0x0C, 0x30, 0xC3, 0x0C, 0xFF, 0xF0, 0x0C, 0x63, 0x00,
0xFF, 0xF3, 0x0C, 0x30, 0xC3, 0x0C, 0xFF, 0xF0, 0x31, 0xEC, 0xC0, 0xFF,
0xF3, 0x0C, 0x30, 0xC3, 0x0C, 0xFF, 0xF0, 0xCF, 0x30, 0x3F, 0xFC, 0xC3,
0x0C, 0x30, 0xC3, 0x3F, 0xFC, 0x78, 0xF9, 0xBB, 0x36, 0x7E, 0xFD, 0xB3,
0x66, 0xDD, 0xF3, 0xC0, 0x67, 0xD9, 0x80, 0xCF, 0x3E, 0xFF, 0xFF, 0x7C,
0xF3, 0xCC, 0xC1, 0x83, 0x00, 0x7B, 0xFC, 0xF3, 0xCF, 0x3C, 0xF3, 0xFD,
0xE0, 0x0C, 0x63, 0x00, 0x7B, 0xFC, 0xF3, 0xCF, 0x3C, 0xF3, 0xFD, 0xE0,
0x31, 0xEC, 0xC0, 0x7B, 0xFC, 0xF3, 0xCF, 0x3C, 0xF3, 0xFD, 0xE0, 0x67,
0xD9, 0x80, 0x7B, 0xFC, 0xF3, 0xCF, 0x3C, 0xF3, 0xFD, 0xE0, 0xCF, 0x30,
0x1E, 0xFF, 0x3C, 0xF3, 0xCF, 0x3C, 0xFF, 0x78, 0xCD, 0xE3, 0x1E, 0xCC,
0x01, 0x3D, 0x7E, 0x66, 0x66, 0x6E, 0x6E, 0x76, 0x76, 0x66, 0x66, 0x7E,
0xBC, 0x80, 0xC1, 0x83, 0x00, 0xCF, 0x3C, 0xF3, 0xCF, 0x3C, 0xF3, 0xFD,
0xE0, 0x0C, 0x63, 0x00, 0xCF, 0x3C, 0xF3, 0xCF, 0x3C, 0xF3, 0xFD, 0xE0,
0x31, 0xEC, 0xC0, 0xCF, 0x3C, 0xF3, 0xCF, 0x3C, 0xF3, 0xFD, 0xE0, 0xCF,
0x30, 0x33, 0xCF, 0x3C, 0xF3, 0xCF, 0x3C, 0xFF, 0x78, 0x0C, 0x63, 0x00,
0xCF, 0x3C, 0xF3, 0x79, 0xE3, 0x0C, 0x30, 0xC0, 0xC3, 0x0F, 0xBF, 0xCF,
0x3C, 0xFF, 0xFB, 0x0C, 0x30, 0x31, 0xEC, 0xF3, 0xCF, 0xEC, 0xF3, 0xCF,
0xED, 0xB0, 0x80, 0xC1, 0x83, 0x00, 0x79, 0xF0, 0xDF, 0xFF, 0x3C, 0xFF,
0x7C, 0x0C, 0x63, 0x00, 0x79, 0xF0, 0xDF, 0xFF, 0x3C, 0xFF, 0x7C, 0x31,
0xEC, 0xC0, 0x79, 0xF0, 0xDF, 0xFF, 0x3C, 0xFF, 0x7C, 0x67, 0xD9, 0x80,
0x79, 0xF0, 0xDF, 0xFF, 0x3C, 0xFF, 0x7C, 0xCF, 0x30, 0x1E, 0x7C, 0x37,
0xFF, 0xCF, 0x3F, 0xDF, 0x39, 0xB3, 0x80, 0x79, 0xF0, 0xDF, 0xFF, 0x3C,
0xFF, 0x7C, 0x76, 0x7F, 0x1B, 0x7B, 0xFF, 0xD8, 0xD8, 0xFF, 0x7F, 0x7B,
0xEC, 0x30, 0xC3, 0x0C, 0x3F, 0x7C, 0x67, 0x00, 0xC1, 0x83, 0x00, 0x7B,
0xFC, 0xF3, 0xFF, 0x0C, 0x3F, 0x7C, 0x0C, 0x63, 0x00, 0x7B, 0xFC, 0xF3,
0xFF, 0x0C, 0x3F, 0x7C, 0x31, 0xEC, 0xC0, 0x7B, 0xFC, 0xF3, 0xFF, 0x0C,
0x3F, 0x7C, 0xCF, 0x30, 0x1E, 0xFF, 0x3C, 0xFF, 0xC3, 0x0F, 0xDF, 0xC3,
0x0C, 0x07, 0x38, 0xC6, 0x31, 0x8C, 0xF7, 0x80, 0x19, 0x98, 0x0E, 0x71,
0x8C, 0x63, 0x19, 0xEF, 0x00, 0x31, 0xEC, 0xC0, 0x71, 0xC3, 0x0C, 0x30,
0xC3, 0x1E, 0x78, 0xCF, 0x30, 0x1C, 0x70, 0xC3, 0x0C, 0x30, 0xC7, 0x9E,
0x78, 0xC7, 0x86, 0x7F, 0xFC, 0xF3, 0xCF, 0x3C, 0xFF, 0x78, 0x67, 0xD9,
0x80, 0x7B, 0xFC, 0xF3, 0xCF, 0x3C, 0xF3, 0xCC, 0xC1, 0x83, 0x00, 0x7B,
0xFC, 0xF3, 0xCF, 0x3C, 0xFF, 0x78, 0x0C, 0x63, 0x00, 0x7B, 0xFC, 0xF3,
0xCF, 0x3C, 0xFF, 0x78, 0x31, 0xEC, 0xC0, 0x7B, 0xFC, 0xF3, 0xCF, 0x3C,
0xFF, 0x78, 0x67, 0xD9, 0x80, 0x7B, 0xFC, 0xF3, 0xCF, 0x3C, 0xFF, 0x78,
0xCF, 0x30, 0x1E, 0xFF, 0x3C, 0xF3, 0xCF, 0x3F, 0xDE, 0x30, 0xC0, 0x3F,
0xFC, 0x03, 0x0C, 0x01, 0x3D, 0x7E, 0x66, 0x6E, 0x7E, 0x76, 0x66, 0x7E,
0xBC, 0x80, 0xC1, 0x83, 0x00, 0xCF, 0x3C, 0xF3, 0xCF, 0x3C, 0xFF, 0x7C,
0x0C, 0x63, 0x00, 0xCF, 0x3C, 0xF3, 0xCF, 0x3C, 0xFF, 0x7C, 0x31, 0xEC,
0xC0, 0xCF, 0x3C, 0xF3, 0xCF, 0x3C, 0xFF, 0x7C, 0xCF, 0x30, 0x33, 0xCF,
0x3C, 0xF3, 0xCF, 0x3F, 0xDF, 0x0C, 0x63, 0x00, 0xCF, 0x3C, 0xF3, 0xCF,
0x3F, 0xDF, 0x0F, 0xFF, 0x80, 0xC3, 0x0C, 0x3E, 0xFF, 0x3C, 0xF3, 0xCF,
0x3F, 0xFE, 0xC3, 0x00, 0xCF, 0x30, 0x33, 0xCF, 0x3C, 0xF3, 0xCF, 0xF7,
0xC3, 0xFF, 0xE0 };
const GFXglyph Atari16pxGlyphs[] PROGMEM = {
{ 0, 1, 1, 8, 0, 0 }, // 0x20 ' ' U+0020
{ 1, 2, 12, 8, 3, -11 }, // 0x21 '!' U+0021
{ 4, 6, 6, 8, 1, -11 }, // 0x22 '"' U+0022
{ 9, 8, 10, 8, 0, -11 }, // 0x23 '#' U+0023
{ 19, 6, 14, 8, 1, -13 }, // 0x24 '$' U+0024
{ 30, 6, 10, 8, 1, -11 }, // 0x25 '%' U+0025
{ 38, 7, 14, 8, 0, -13 }, // 0x26 '&' U+0026
{ 51, 2, 6, 8, 3, -11 }, // 0x27 ''' U+0027
{ 53, 4, 12, 8, 3, -11 }, // 0x28 '(' U+0028
{ 59, 4, 12, 8, 1, -11 }, // 0x29 ')' U+0029
{ 65, 8, 10, 8, 0, -11 }, // 0x2a '*' U+002A
{ 75, 6, 8, 8, 1, -10 }, // 0x2b '+' U+002B
{ 81, 3, 6, 8, 2, -3 }, // 0x2c ',' U+002C
{ 84, 6, 2, 8, 1, -7 }, // 0x2d '-' U+002D
{ 86, 2, 4, 8, 3, -3 }, // 0x2e '.' U+002E
{ 87, 6, 12, 8, 1, -11 }, // 0x2f '/' U+002F
{ 96, 6, 12, 8, 1, -11 }, // 0x30 '0' U+0030
{ 105, 6, 12, 8, 1, -11 }, // 0x31 '1' U+0031
{ 114, 6, 12, 8, 1, -11 }, // 0x32 '2' U+0032
{ 123, 6, 12, 8, 1, -11 }, // 0x33 '3' U+0033
{ 132, 6, 12, 8, 1, -11 }, // 0x34 '4' U+0034
{ 141, 6, 12, 8, 1, -11 }, // 0x35 '5' U+0035
{ 150, 6, 12, 8, 1, -11 }, // 0x36 '6' U+0036
{ 159, 6, 12, 8, 1, -11 }, // 0x37 '7' U+0037
{ 168, 6, 12, 8, 1, -11 }, // 0x38 '8' U+0038
{ 177, 6, 12, 8, 1, -11 }, // 0x39 '9' U+0039
{ 186, 2, 10, 8, 3, -9 }, // 0x3a ':' U+003A
{ 189, 3, 12, 8, 2, -9 }, // 0x3b ';' U+003B
{ 194, 7, 9, 8, 0, -10 }, // 0x3c '<' U+003C
{ 202, 6, 6, 8, 1, -9 }, // 0x3d '=' U+003D
{ 207, 7, 9, 8, 0, -10 }, // 0x3e '>' U+003E
{ 215, 6, 12, 8, 1, -11 }, // 0x3f '?' U+003F
{ 224, 7, 12, 8, 0, -11 }, // 0x40 '@' U+0040
{ 235, 6, 12, 8, 1, -11 }, // 0x41 'A' U+0041
{ 244, 6, 12, 8, 1, -11 }, // 0x42 'B' U+0042
{ 253, 6, 12, 8, 1, -11 }, // 0x43 'C' U+0043
{ 262, 6, 12, 8, 1, -11 }, // 0x44 'D' U+0044
{ 271, 6, 12, 8, 1, -11 }, // 0x45 'E' U+0045
{ 280, 6, 12, 8, 1, -11 }, // 0x46 'F' U+0046
{ 289, 6, 12, 8, 1, -11 }, // 0x47 'G' U+0047
{ 298, 6, 12, 8, 1, -11 }, // 0x48 'H' U+0048
{ 307, 6, 12, 8, 1, -11 }, // 0x49 'I' U+0049
{ 316, 6, 12, 8, 1, -11 }, // 0x4a 'J' U+004A
{ 325, 7, 12, 8, 0, -11 }, // 0x4b 'K' U+004B
{ 336, 6, 12, 8, 1, -11 }, // 0x4c 'L' U+004C
{ 345, 7, 12, 8, 0, -11 }, // 0x4d 'M' U+004D
{ 356, 6, 12, 8, 1, -11 }, // 0x4e 'N' U+004E
{ 365, 6, 12, 8, 1, -11 }, // 0x4f 'O' U+004F
{ 374, 6, 12, 8, 1, -11 }, // 0x50 'P' U+0050
{ 383, 6, 12, 8, 1, -11 }, // 0x51 'Q' U+0051
{ 392, 7, 12, 8, 0, -11 }, // 0x52 'R' U+0052
{ 403, 6, 12, 8, 1, -11 }, // 0x53 'S' U+0053
{ 412, 6, 12, 8, 1, -11 }, // 0x54 'T' U+0054
{ 421, 6, 12, 8, 1, -11 }, // 0x55 'U' U+0055
{ 430, 6, 12, 8, 1, -11 }, // 0x56 'V' U+0056
{ 439, 7, 12, 8, 0, -11 }, // 0x57 'W' U+0057
{ 450, 6, 12, 8, 1, -11 }, // 0x58 'X' U+0058
{ 459, 6, 12, 8, 1, -11 }, // 0x59 'Y' U+0059
{ 468, 6, 12, 8, 1, -11 }, // 0x5a 'Z' U+005A
{ 477, 4, 12, 8, 3, -11 }, // 0x5b '[' U+005B
{ 483, 6, 12, 8, 1, -11 }, // 0x5c '\' U+005C
{ 492, 4, 12, 8, 1, -11 }, // 0x5d ']' U+005D
{ 498, 7, 8, 8, 0, -12 }, // 0x5e '^' U+005E
{ 505, 7, 2, 8, 0, -1 }, // 0x5f '_' U+005F
{ 507, 5, 7, 8, 1, -13 }, // 0x60 '`' U+0060
{ 512, 6, 9, 8, 1, -8 }, // 0x61 'a' U+0061
{ 519, 6, 12, 8, 1, -11 }, // 0x62 'b' U+0062
{ 528, 6, 9, 8, 1, -8 }, // 0x63 'c' U+0063
{ 535, 6, 12, 8, 1, -11 }, // 0x64 'd' U+0064
{ 544, 6, 9, 8, 1, -8 }, // 0x65 'e' U+0065
{ 551, 6, 12, 8, 1, -11 }, // 0x66 'f' U+0066
{ 560, 6, 11, 8, 1, -8 }, // 0x67 'g' U+0067
{ 569, 6, 12, 8, 1, -11 }, // 0x68 'h' U+0068
{ 578, 4, 12, 8, 2, -11 }, // 0x69 'i' U+0069
{ 584, 5, 14, 8, 1, -11 }, // 0x6a 'j' U+006A
{ 593, 7, 12, 8, 0, -11 }, // 0x6b 'k' U+006B
{ 604, 4, 12, 8, 2, -11 }, // 0x6c 'l' U+006C
{ 610, 7, 9, 8, 0, -8 }, // 0x6d 'm' U+006D
{ 618, 6, 9, 8, 1, -8 }, // 0x6e 'n' U+006E
{ 625, 6, 9, 8, 1, -8 }, // 0x6f 'o' U+006F
{ 632, 6, 11, 8, 1, -8 }, // 0x70 'p' U+0070
{ 641, 6, 11, 8, 1, -8 }, // 0x71 'q' U+0071
{ 650, 6, 9, 8, 1, -8 }, // 0x72 'r' U+0072
{ 657, 6, 9, 8, 1, -8 }, // 0x73 's' U+0073
{ 664, 6, 11, 8, 1, -10 }, // 0x74 't' U+0074
{ 673, 6, 9, 8, 1, -8 }, // 0x75 'u' U+0075
{ 680, 6, 9, 8, 1, -8 }, // 0x76 'v' U+0076
{ 687, 7, 9, 8, 0, -8 }, // 0x77 'w' U+0077
{ 695, 6, 9, 8, 1, -8 }, // 0x78 'x' U+0078
{ 702, 6, 11, 8, 1, -8 }, // 0x79 'y' U+0079
{ 711, 6, 9, 8, 1, -8 }, // 0x7a 'z' U+007A
{ 718, 7, 14, 8, 0, -12 }, // 0x7b '{' U+007B
{ 731, 2, 14, 8, 3, -12 }, // 0x7c '|' U+007C
{ 735, 7, 14, 8, 0, -12 }, // 0x7d '}' U+007D
{ 748, 7, 4, 8, 0, -8 }, // 0x7e '~' U+007E
{ 752, 5, 11, 8, 1, -10 }, // 0x7f 'REPLACEMENT CHARACTER *' U+2370
{ 759, 1, 1, 8, 0, 0 }, // 0x80 'NO-BREAK SPACE' U+00A0
{ 760, 2, 12, 8, 3, -10 }, // 0x81 'INVERTED EXCLAMATION MARK' U+00A1
{ 763, 6, 12, 8, 1, -11 }, // 0x82 'CENT SIGN' U+00A2
{ 772, 8, 13, 8, 0, -12 }, // 0x83 'POUND SIGN' U+00A3
{ 785, 5, 11, 8, 1, -10 }, // 0x84 'CURRENCY SIGN' U+00A4
{ 792, 6, 12, 8, 1, -11 }, // 0x85 'YEN SIGN' U+00A5
{ 801, 5, 11, 8, 1, -10 }, // 0x86 'BROKEN BAR' U+00A6
{ 808, 6, 12, 8, 1, -11 }, // 0x87 'SECTION SIGN' U+00A7
{ 817, 5, 11, 8, 1, -10 }, // 0x88 'DIAERESIS' U+00A8
{ 824, 7, 11, 8, 0, -11 }, // 0x89 'COPYRIGHT SIGN' U+00A9
{ 834, 6, 11, 8, 1, -9 }, // 0x8a 'FEMININE ORDINAL INDICATOR' U+00AA
{ 843, 7, 7, 8, 0, -7 }, // 0x8b 'LEFT-POINTING DOUBLE ANGLE QUOTATION MARK' U+00AB
{ 850, 6, 5, 8, 1, -4 }, // 0x8c 'NOT SIGN' U+00AC
{ 854, 4, 2, 8, 2, -4 }, // 0x8d 'SOFT HYPHEN' U+00AD
{ 855, 7, 11, 8, 0, -11 }, // 0x8e 'REGISTERED SIGN' U+00AE
{ 865, 7, 2, 8, 0, -12 }, // 0x8f 'MACRON' U+00AF
{ 867, 5, 5, 8, 1, -12 }, // 0x90 'DEGREE SIGN' U+00B0
{ 871, 6, 11, 8, 1, -10 }, // 0x91 'PLUS-MINUS SIGN' U+00B1
{ 880, 4, 6, 8, 1, -11 }, // 0x92 'SUPERSCRIPT TWO' U+00B2
{ 883, 4, 6, 8, 1, -11 }, // 0x93 'SUPERSCRIPT THREE' U+00B3
{ 886, 5, 11, 8, 1, -10 }, // 0x94 'ACUTE ACCENT' U+00B4
{ 893, 8, 10, 8, 0, -8 }, // 0x95 'MICRO SIGN' U+00B5
{ 903, 7, 12, 8, 0, -11 }, // 0x96 'PILCROW SIGN' U+00B6
{ 914, 4, 4, 8, 2, -6 }, // 0x97 'MIDDLE DOT' U+00B7
{ 916, 5, 11, 8, 1, -10 }, // 0x98 'CEDILLA' U+00B8
{ 923, 2, 6, 8, 1, -11 }, // 0x99 'SUPERSCRIPT ONE' U+00B9
{ 925, 6, 11, 8, 1, -9 }, // 0x9a 'MASCULINE ORDINAL INDICATOR' U+00BA
{ 934, 7, 7, 8, 0, -7 }, // 0x9b 'RIGHT-POINTING DOUBLE ANGLE QUOTATION MARK' U+00BB
{ 941, 5, 11, 8, 1, -10 }, // 0x9c 'VULGAR FRACTION ONE QUARTER' U+00BC
{ 948, 5, 11, 8, 1, -10 }, // 0x9d 'VULGAR FRACTION ONE HALF' U+00BD
{ 955, 5, 11, 8, 1, -10 }, // 0x9e 'VULGAR FRACTION THREE QUARTERS' U+00BE
{ 962, 6, 12, 8, 1, -11 }, // 0x9f 'INVERTED QUESTION MARK' U+00BF
{ 971, 6, 14, 8, 1, -13 }, // 0xa0 'LATIN CAPITAL LETTER A WITH GRAVE' U+00C0
{ 982, 6, 14, 8, 1, -13 }, // 0xa1 'LATIN CAPITAL LETTER A WITH ACUTE' U+00C1
{ 993, 6, 14, 8, 1, -13 }, // 0xa2 'LATIN CAPITAL LETTER A WITH CIRCUMFLEX' U+00C2
{ 1004, 6, 14, 8, 1, -13 }, // 0xa3 'LATIN CAPITAL LETTER A WITH TILDE' U+00C3
{ 1015, 6, 13, 8, 1, -12 }, // 0xa4 'LATIN CAPITAL LETTER A WITH DIAERESIS' U+00C4
{ 1025, 6, 14, 8, 1, -13 }, // 0xa5 'LATIN CAPITAL LETTER A WITH RING ABOVE' U+00C5
{ 1036, 7, 13, 8, 0, -12 }, // 0xa6 'LATIN CAPITAL LETTER AE' U+00C6
{ 1048, 6, 14, 8, 1, -11 }, // 0xa7 'LATIN CAPITAL LETTER C WITH CEDILLA' U+00C7
{ 1059, 6, 14, 8, 1, -13 }, // 0xa8 'LATIN CAPITAL LETTER E WITH GRAVE' U+00C8
{ 1070, 6, 14, 8, 1, -13 }, // 0xa9 'LATIN CAPITAL LETTER E WITH ACUTE' U+00C9
{ 1081, 6, 14, 8, 1, -13 }, // 0xaa 'LATIN CAPITAL LETTER E WITH CIRCUMFLEX' U+00CA
{ 1092, 6, 13, 8, 1, -12 }, // 0xab 'LATIN CAPITAL LETTER E WITH DIAERESIS' U+00CB
{ 1102, 6, 14, 8, 1, -13 }, // 0xac 'LATIN CAPITAL LETTER I WITH GRAVE' U+00CC
{ 1113, 6, 14, 8, 1, -13 }, // 0xad 'LATIN CAPITAL LETTER I WITH ACUTE' U+00CD
{ 1124, 6, 14, 8, 1, -13 }, // 0xae 'LATIN CAPITAL LETTER I WITH CIRCUMFLEX' U+00CE
{ 1135, 6, 13, 8, 1, -12 }, // 0xaf 'LATIN CAPITAL LETTER I WITH DIAERESIS' U+00CF
{ 1145, 7, 12, 8, 0, -11 }, // 0xb0 'LATIN CAPITAL LETTER ETH' U+00D0
{ 1156, 6, 13, 8, 1, -12 }, // 0xb1 'LATIN CAPITAL LETTER N WITH TILDE' U+00D1
{ 1166, 6, 14, 8, 1, -13 }, // 0xb2 'LATIN CAPITAL LETTER O WITH GRAVE' U+00D2
{ 1177, 6, 14, 8, 1, -13 }, // 0xb3 'LATIN CAPITAL LETTER O WITH ACUTE' U+00D3
{ 1188, 6, 14, 8, 1, -13 }, // 0xb4 'LATIN CAPITAL LETTER O WITH CIRCUMFLEX' U+00D4
{ 1199, 6, 14, 8, 1, -13 }, // 0xb5 'LATIN CAPITAL LETTER O WITH TILDE' U+00D5
{ 1210, 6, 13, 8, 1, -12 }, // 0xb6 'LATIN CAPITAL LETTER O WITH DIAERESIS' U+00D6
{ 1220, 6, 5, 8, 1, -6 }, // 0xb7 'MULTIPLICATION SIGN' U+00D7
{ 1224, 8, 14, 8, 0, -12 }, // 0xb8 'LATIN CAPITAL LETTER O WITH STROKE' U+00D8
{ 1238, 6, 14, 8, 1, -13 }, // 0xb9 'LATIN CAPITAL LETTER U WITH GRAVE' U+00D9
{ 1249, 6, 14, 8, 1, -13 }, // 0xba 'LATIN CAPITAL LETTER U WITH ACUTE' U+00DA
{ 1260, 6, 14, 8, 1, -13 }, // 0xbb 'LATIN CAPITAL LETTER U WITH CIRCUMFLEX' U+00DB
{ 1271, 6, 13, 8, 1, -12 }, // 0xbc 'LATIN CAPITAL LETTER U WITH DIAERESIS' U+00DC
{ 1281, 6, 14, 8, 1, -13 }, // 0xbd 'LATIN CAPITAL LETTER Y WITH ACUTE' U+00DD
{ 1292, 6, 12, 8, 1, -11 }, // 0xbe 'LATIN CAPITAL LETTER THORN' U+00DE
{ 1301, 6, 13, 8, 1, -11 }, // 0xbf 'LATIN SMALL LETTER SHARP S' U+00DF
{ 1311, 6, 13, 8, 1, -12 }, // 0xc0 'LATIN SMALL LETTER A WITH GRAVE' U+00E0
{ 1321, 6, 13, 8, 1, -12 }, // 0xc1 'LATIN SMALL LETTER A WITH ACUTE' U+00E1
{ 1331, 6, 13, 8, 1, -12 }, // 0xc2 'LATIN SMALL LETTER A WITH CIRCUMFLEX' U+00E2
{ 1341, 6, 13, 8, 1, -12 }, // 0xc3 'LATIN SMALL LETTER A WITH TILDE' U+00E3
{ 1351, 6, 12, 8, 1, -11 }, // 0xc4 'LATIN SMALL LETTER A WITH DIAERESIS' U+00E4
{ 1360, 6, 13, 8, 1, -12 }, // 0xc5 'LATIN SMALL LETTER A WITH RING ABOVE' U+00E5
{ 1370, 8, 9, 8, 0, -8 }, // 0xc6 'LATIN SMALL LETTER AE' U+00E6
{ 1379, 6, 11, 8, 1, -8 }, // 0xc7 'LATIN SMALL LETTER C WITH CEDILLA' U+00E7
{ 1388, 6, 13, 8, 1, -12 }, // 0xc8 'LATIN SMALL LETTER E WITH GRAVE' U+00E8
{ 1398, 6, 13, 8, 1, -12 }, // 0xc9 'LATIN SMALL LETTER E WITH ACUTE' U+00E9
{ 1408, 6, 13, 8, 1, -12 }, // 0xca 'LATIN SMALL LETTER E WITH CIRCUMFLEX' U+00EA
{ 1418, 6, 12, 8, 1, -11 }, // 0xcb 'LATIN SMALL LETTER E WITH DIAERESIS' U+00EB
{ 1427, 5, 13, 8, 1, -12 }, // 0xcc 'LATIN SMALL LETTER I WITH GRAVE' U+00EC
{ 1436, 5, 13, 8, 2, -12 }, // 0xcd 'LATIN SMALL LETTER I WITH ACUTE' U+00ED
{ 1445, 6, 13, 8, 1, -12 }, // 0xce 'LATIN SMALL LETTER I WITH CIRCUMFLEX' U+00EE
{ 1455, 6, 12, 8, 1, -11 }, // 0xcf 'LATIN SMALL LETTER I WITH DIAERESIS' U+00EF
{ 1464, 6, 13, 8, 1, -12 }, // 0xd0 'LATIN SMALL LETTER ETH' U+00F0
{ 1474, 6, 13, 8, 1, -12 }, // 0xd1 'LATIN SMALL LETTER N WITH TILDE' U+00F1
{ 1484, 6, 13, 8, 1, -12 }, // 0xd2 'LATIN SMALL LETTER O WITH GRAVE' U+00F2
{ 1494, 6, 13, 8, 1, -12 }, // 0xd3 'LATIN SMALL LETTER O WITH ACUTE' U+00F3
{ 1504, 6, 13, 8, 1, -12 }, // 0xd4 'LATIN SMALL LETTER O WITH CIRCUMFLEX' U+00F4
{ 1514, 6, 13, 8, 1, -12 }, // 0xd5 'LATIN SMALL LETTER O WITH TILDE' U+00F5
{ 1524, 6, 12, 8, 1, -11 }, // 0xd6 'LATIN SMALL LETTER O WITH DIAERESIS' U+00F6
{ 1533, 6, 8, 8, 1, -10 }, // 0xd7 'DIVISION SIGN' U+00F7
{ 1539, 8, 11, 8, 0, -9 }, // 0xd8 'LATIN SMALL LETTER O WITH STROKE' U+00F8
{ 1550, 6, 13, 8, 1, -12 }, // 0xd9 'LATIN SMALL LETTER U WITH GRAVE' U+00F9
{ 1560, 6, 13, 8, 1, -12 }, // 0xda 'LATIN SMALL LETTER U WITH ACUTE' U+00FA
{ 1570, 6, 13, 8, 1, -12 }, // 0xdb 'LATIN SMALL LETTER U WITH CIRCUMFLEX' U+00FB
{ 1580, 6, 12, 8, 1, -11 }, // 0xdc 'LATIN SMALL LETTER U WITH DIAERESIS' U+00FC
{ 1589, 6, 15, 8, 1, -12 }, // 0xdd 'LATIN SMALL LETTER Y WITH ACUTE' U+00FD
{ 1601, 6, 14, 8, 1, -11 }, // 0xde 'LATIN SMALL LETTER THORN' U+00FE
{ 1612, 6, 14, 8, 1, -11 } }; // 0xdf 'LATIN SMALL LETTER Y WITH DIAERESIS' U+000FF
const GFXfont Atari16px PROGMEM = {
(uint8_t *)Atari16pxBitmaps,
(GFXglyph *)Atari16pxGlyphs,
0x20, 0xDF, 16 };
// Approx. 2974 bytes

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,202 @@
const uint8_t IBM8x8pxBitmaps[] PROGMEM = {
0x00, /* 0x20 space */
0x6F, 0xF6, 0x60, 0x60, /* 0x21 exclam */
0xDE, 0xF6, /* 0x22 quotedbl */
0x6C, 0xDB, 0xFB, 0x6F, 0xED, 0x9B, 0x00, /* 0x23 numbersign */
0x31, 0xFC, 0x1E, 0x0F, 0xE3, 0x00, /* 0x24 dollar */
0xC7, 0x98, 0x61, 0x86, 0x78, 0xC0, /* 0x25 percent */
0x38, 0xD8, 0xE3, 0xBD, 0xD9, 0x9D, 0x80, /* 0x26 ampersand */
0x6F, 0x00, /* 0x27 quotesingle */
0x36, 0xCC, 0xC6, 0x30, /* 0x28 parenleft */
0xC6, 0x33, 0x36, 0xC0, /* 0x29 parenright */
0x66, 0x3C, 0xFF, 0x3C, 0x66, /* 0x2A asterisk */
0x30, 0xCF, 0xCC, 0x30, /* 0x2B plus */
0x6F, 0x00, /* 0x2C comma */
0xFC, /* 0x2D hyphen */
0xF0, /* 0x2E period */
0x06, 0x18, 0x61, 0x86, 0x18, 0x20, 0x00, /* 0x2F slash */
0x7D, 0x8F, 0x3E, 0xFF, 0x7C, 0xDF, 0x00, /* 0x30 zero */
0x31, 0xC3, 0x0C, 0x30, 0xCF, 0xC0, /* 0x31 one */
0x7B, 0x30, 0xCE, 0x63, 0x1F, 0xC0, /* 0x32 two */
0x7B, 0x30, 0xCE, 0x0F, 0x37, 0x80, /* 0x33 three */
0x1C, 0x79, 0xB6, 0x6F, 0xE1, 0x87, 0x80, /* 0x34 four */
0xFF, 0x0F, 0x83, 0x0F, 0x37, 0x80, /* 0x35 five */
0x39, 0x8C, 0x3E, 0xCF, 0x37, 0x80, /* 0x36 six */
0xFF, 0x30, 0xC6, 0x30, 0xC3, 0x00, /* 0x37 seven */
0x7B, 0x3C, 0xDE, 0xCF, 0x37, 0x80, /* 0x38 eight */
0x7B, 0x3C, 0xDF, 0x0C, 0x67, 0x00, /* 0x39 nine */
0xF0, 0xF0, /* 0x3A colon */
0x6C, 0x37, 0x80, /* 0x3B semicolon */
0x19, 0x99, 0x86, 0x18, 0x60, /* 0x3C less */
0xFC, 0x00, 0x3F, /* 0x3D equal */
0xC3, 0x0C, 0x33, 0x33, 0x00, /* 0x3E greater */
0x7B, 0x30, 0xC6, 0x30, 0x03, 0x00, /* 0x3F question */
0x7D, 0x8F, 0x7E, 0xFD, 0xF8, 0x1E, 0x00, /* 0x40 at */
0x31, 0xEC, 0xF3, 0xFF, 0x3C, 0xC0, /* 0x41 A */
0xFC, 0xCD, 0x9B, 0xE6, 0x6C, 0xFF, 0x00, /* 0x42 B */
0x3C, 0xCF, 0x06, 0x0C, 0x0C, 0xCF, 0x00, /* 0x43 C */
0xF8, 0xD9, 0x9B, 0x36, 0x6D, 0xBE, 0x00, /* 0x44 D */
0xFE, 0xC5, 0xA3, 0xC6, 0x8C, 0x7F, 0x80, /* 0x45 E */
0xFE, 0xC5, 0xA3, 0xC6, 0x8C, 0x3C, 0x00, /* 0x46 F */
0x3C, 0xCF, 0x06, 0x0C, 0xEC, 0xCF, 0x80, /* 0x47 G */
0xCF, 0x3C, 0xFF, 0xCF, 0x3C, 0xC0, /* 0x48 H */
0xF6, 0x66, 0x66, 0xF0, /* 0x49 I */
0x1E, 0x18, 0x30, 0x6C, 0xD9, 0x9E, 0x00, /* 0x4A J */
0xE6, 0xCD, 0xB3, 0xC6, 0xCC, 0xF9, 0x80, /* 0x4B K */
0xF0, 0xC1, 0x83, 0x06, 0x2C, 0xFF, 0x80, /* 0x4C L */
0xC7, 0xDF, 0xFF, 0xFD, 0x78, 0xF1, 0x80, /* 0x4D M */
0xC7, 0xCF, 0xDE, 0xFC, 0xF8, 0xF1, 0x80, /* 0x4E N */
0x38, 0xDB, 0x1E, 0x3C, 0x6D, 0x8E, 0x00, /* 0x4F O */
0xFC, 0xCD, 0x9B, 0xE6, 0x0C, 0x3C, 0x00, /* 0x50 P */
0x7B, 0x3C, 0xF3, 0xDD, 0xE1, 0xC0, /* 0x51 Q */
0xFC, 0xCD, 0x9B, 0xE6, 0xCC, 0xF9, 0x80, /* 0x52 R */
0x7B, 0x3E, 0x1C, 0x1F, 0x37, 0x80, /* 0x53 S */
0xFE, 0xD3, 0x0C, 0x30, 0xC7, 0x80, /* 0x54 T */
0xCF, 0x3C, 0xF3, 0xCF, 0x3F, 0xC0, /* 0x55 U */
0xCF, 0x3C, 0xF3, 0xCD, 0xE3, 0x00, /* 0x56 V */
0xC7, 0x8F, 0x1E, 0xBF, 0xFD, 0xF1, 0x80, /* 0x57 W */
0xC7, 0x8D, 0xB1, 0xC3, 0x8D, 0xB1, 0x80, /* 0x58 X */
0xCF, 0x3C, 0xDE, 0x30, 0xC7, 0x80, /* 0x59 Y */
0xFF, 0x8E, 0x30, 0xC3, 0x2C, 0xFF, 0x80, /* 0x5A Z */
0xFC, 0xCC, 0xCC, 0xF0, /* 0x5B bracketleft */
0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0x80, /* 0x5C backslash */
0xF3, 0x33, 0x33, 0xF0, /* 0x5D bracketright */
0x10, 0x71, 0xB6, 0x30, /* 0x5E asciicircum */
0xFF, /* 0x5F underscore */
0xD9, 0x80, /* 0x60 grave */
0x78, 0x19, 0xF6, 0x67, 0x60, /* 0x61 a */
0xE0, 0xC1, 0x83, 0xE6, 0x6C, 0xF7, 0x00, /* 0x62 b */
0x7B, 0x3C, 0x33, 0x78, /* 0x63 c */
0x1C, 0x18, 0x33, 0xEC, 0xD9, 0x9D, 0x80, /* 0x64 d */
0x7B, 0x3F, 0xF0, 0x78, /* 0x65 e */
0x39, 0xB6, 0x3C, 0x61, 0x8F, 0x00, /* 0x66 f */
0x77, 0x9B, 0x33, 0xE0, 0xDF, 0x00, /* 0x67 g */
0xE0, 0xC1, 0xB3, 0xB6, 0x6C, 0xF9, 0x80, /* 0x68 h */
0x60, 0xE6, 0x66, 0xF0, /* 0x69 i */
0x0C, 0x00, 0xC3, 0x0F, 0x3C, 0xDE, /* 0x6A j */
0xE0, 0xC1, 0x9B, 0x67, 0x8D, 0xB9, 0x80, /* 0x6B k */
0xE6, 0x66, 0x66, 0xF0, /* 0x6C l */
0xCD, 0xFF, 0xFE, 0xBC, 0x60, /* 0x6D m */
0xFB, 0x3C, 0xF3, 0xCC, /* 0x6E n */
0x7B, 0x3C, 0xF3, 0x78, /* 0x6F o */
0xDC, 0xCD, 0x9B, 0xE6, 0x1E, 0x00, /* 0x70 p */
0x77, 0x9B, 0x33, 0xE0, 0xC3, 0xC0, /* 0x71 q */
0xDC, 0xED, 0x9B, 0x0F, 0x00, /* 0x72 r */
0x7F, 0x07, 0x83, 0xF8, /* 0x73 s */
0x23, 0x3E, 0xC6, 0x34, 0xC0, /* 0x74 t */
0xCD, 0x9B, 0x36, 0x67, 0x60, /* 0x75 u */
0xCF, 0x3C, 0xDE, 0x30, /* 0x76 v */
0xC7, 0xAF, 0xFF, 0xF6, 0xC0, /* 0x77 w */
0xC6, 0xD8, 0xE3, 0x6C, 0x60, /* 0x78 x */
0xCF, 0x3C, 0xDF, 0x0F, 0xE0, /* 0x79 y */
0xFE, 0x63, 0x19, 0xFC, /* 0x7A z */
0x1C, 0xC3, 0x38, 0x30, 0xC1, 0xC0, /* 0x7B braceleft */
0xFC, 0xFC, /* 0x7C bar */
0xE0, 0xC3, 0x07, 0x30, 0xCE, 0x00, /* 0x7D braceright */
0x77, 0xB8, /* 0x7E asciitilde */
0x10, 0x71, 0xB6, 0x3C, 0x7F, 0xC0 /* 0x7F uni007F */
};
const GFXglyph IBM8x8pxGlyphs[] PROGMEM = {
{ 0, 1, 1, 2, 0, -1 }, /* 0x20 space */
{ 1, 4, 7, 5, 0, -7 }, /* 0x21 exclam */
{ 5, 5, 3, 6, 0, -7 }, /* 0x22 quotedbl */
{ 7, 7, 7, 8, 0, -7 }, /* 0x23 numbersign */
{ 14, 6, 7, 7, 0, -7 }, /* 0x24 dollar */
{ 20, 7, 6, 8, 0, -6 }, /* 0x25 percent */
{ 26, 7, 7, 8, 0, -7 }, /* 0x26 ampersand */
{ 33, 3, 3, 4, 0, -7 }, /* 0x27 quotesingle */
{ 35, 4, 7, 5, 0, -7 }, /* 0x28 parenleft */
{ 39, 4, 7, 5, 0, -7 }, /* 0x29 parenright */
{ 43, 8, 5, 9, 0, -6 }, /* 0x2A asterisk */
{ 48, 6, 5, 7, 0, -6 }, /* 0x2B plus */
{ 52, 3, 3, 4, 0, -2 }, /* 0x2C comma */
{ 54, 6, 1, 7, 0, -4 }, /* 0x2D hyphen */
{ 55, 2, 2, 3, 0, -2 }, /* 0x2E period */
{ 56, 7, 7, 8, 0, -7 }, /* 0x2F slash */
{ 63, 7, 7, 8, 0, -7 }, /* 0x30 zero */
{ 70, 6, 7, 7, 0, -7 }, /* 0x31 one */
{ 76, 6, 7, 7, 0, -7 }, /* 0x32 two */
{ 82, 6, 7, 7, 0, -7 }, /* 0x33 three */
{ 88, 7, 7, 8, 0, -7 }, /* 0x34 four */
{ 95, 6, 7, 7, 0, -7 }, /* 0x35 five */
{ 101, 6, 7, 7, 0, -7 }, /* 0x36 six */
{ 107, 6, 7, 7, 0, -7 }, /* 0x37 seven */
{ 113, 6, 7, 7, 0, -7 }, /* 0x38 eight */
{ 119, 6, 7, 7, 0, -7 }, /* 0x39 nine */
{ 125, 2, 6, 3, 0, -6 }, /* 0x3A colon */
{ 127, 3, 6, 4, 0, -6 }, /* 0x3B semicolon */
{ 130, 5, 7, 6, 0, -7 }, /* 0x3C less */
{ 135, 6, 4, 7, 0, -5 }, /* 0x3D equal */
{ 138, 5, 7, 6, 0, -7 }, /* 0x3E greater */
{ 143, 6, 7, 7, 0, -7 }, /* 0x3F question */
{ 149, 7, 7, 8, 0, -7 }, /* 0x40 at */
{ 156, 6, 7, 7, 0, -7 }, /* 0x41 A */
{ 162, 7, 7, 8, 0, -7 }, /* 0x42 B */
{ 169, 7, 7, 8, 0, -7 }, /* 0x43 C */
{ 176, 7, 7, 8, 0, -7 }, /* 0x44 D */
{ 183, 7, 7, 8, 0, -7 }, /* 0x45 E */
{ 190, 7, 7, 8, 0, -7 }, /* 0x46 F */
{ 197, 7, 7, 8, 0, -7 }, /* 0x47 G */
{ 204, 6, 7, 7, 0, -7 }, /* 0x48 H */
{ 210, 4, 7, 5, 0, -7 }, /* 0x49 I */
{ 214, 7, 7, 8, 0, -7 }, /* 0x4A J */
{ 221, 7, 7, 8, 0, -7 }, /* 0x4B K */
{ 228, 7, 7, 8, 0, -7 }, /* 0x4C L */
{ 235, 7, 7, 8, 0, -7 }, /* 0x4D M */
{ 242, 7, 7, 8, 0, -7 }, /* 0x4E N */
{ 249, 7, 7, 8, 0, -7 }, /* 0x4F O */
{ 256, 7, 7, 8, 0, -7 }, /* 0x50 P */
{ 263, 6, 7, 7, 0, -7 }, /* 0x51 Q */
{ 269, 7, 7, 8, 0, -7 }, /* 0x52 R */
{ 276, 6, 7, 7, 0, -7 }, /* 0x53 S */
{ 282, 6, 7, 7, 0, -7 }, /* 0x54 T */
{ 288, 6, 7, 7, 0, -7 }, /* 0x55 U */
{ 294, 6, 7, 7, 0, -7 }, /* 0x56 V */
{ 300, 7, 7, 8, 0, -7 }, /* 0x57 W */
{ 307, 7, 7, 8, 0, -7 }, /* 0x58 X */
{ 314, 6, 7, 7, 0, -7 }, /* 0x59 Y */
{ 320, 7, 7, 8, 0, -7 }, /* 0x5A Z */
{ 327, 4, 7, 5, 0, -7 }, /* 0x5B bracketleft */
{ 331, 7, 7, 8, 0, -7 }, /* 0x5C backslash */
{ 338, 4, 7, 5, 0, -7 }, /* 0x5D bracketright */
{ 342, 7, 4, 8, 0, -7 }, /* 0x5E asciicircum */
{ 346, 8, 1, 9, 0, 0 }, /* 0x5F underscore */
{ 347, 3, 3, 4, 0, -7 }, /* 0x60 grave */
{ 349, 7, 5, 8, 0, -5 }, /* 0x61 a */
{ 354, 7, 7, 8, 0, -7 }, /* 0x62 b */
{ 361, 6, 5, 7, 0, -5 }, /* 0x63 c */
{ 365, 7, 7, 8, 0, -7 }, /* 0x64 d */
{ 372, 6, 5, 7, 0, -5 }, /* 0x65 e */
{ 376, 6, 7, 7, 0, -7 }, /* 0x66 f */
{ 382, 7, 6, 8, 0, -5 }, /* 0x67 g */
{ 388, 7, 7, 8, 0, -7 }, /* 0x68 h */
{ 395, 4, 7, 5, 0, -7 }, /* 0x69 i */
{ 399, 6, 8, 7, 0, -7 }, /* 0x6A j */
{ 405, 7, 7, 8, 0, -7 }, /* 0x6B k */
{ 412, 4, 7, 5, 0, -7 }, /* 0x6C l */
{ 416, 7, 5, 8, 0, -5 }, /* 0x6D m */
{ 421, 6, 5, 7, 0, -5 }, /* 0x6E n */
{ 425, 6, 5, 7, 0, -5 }, /* 0x6F o */
{ 429, 7, 6, 8, 0, -5 }, /* 0x70 p */
{ 435, 7, 6, 8, 0, -5 }, /* 0x71 q */
{ 441, 7, 5, 8, 0, -5 }, /* 0x72 r */
{ 446, 6, 5, 7, 0, -5 }, /* 0x73 s */
{ 450, 5, 7, 6, 0, -7 }, /* 0x74 t */
{ 455, 7, 5, 8, 0, -5 }, /* 0x75 u */
{ 460, 6, 5, 7, 0, -5 }, /* 0x76 v */
{ 464, 7, 5, 8, 0, -5 }, /* 0x77 w */
{ 469, 7, 5, 8, 0, -5 }, /* 0x78 x */
{ 474, 6, 6, 7, 0, -5 }, /* 0x79 y */
{ 479, 6, 5, 7, 0, -5 }, /* 0x7A z */
{ 483, 6, 7, 7, 0, -7 }, /* 0x7B braceleft */
{ 489, 2, 7, 3, 0, -7 }, /* 0x7C bar */
{ 491, 6, 7, 7, 0, -7 }, /* 0x7D braceright */
{ 497, 7, 2, 8, 0, -7 }, /* 0x7E asciitilde */
{ 499, 7, 6, 8, 0, -6 } /* 0x7F uni007F */
};
const GFXfont IBM8x8px PROGMEM = {
(uint8_t *)IBM8x8pxBitmaps,
(GFXglyph *)IBM8x8pxGlyphs,
0x20, 0x7F, 8 };

View File

@@ -0,0 +1,485 @@
const uint8_t Ubuntu_Bold10pt8bBitmaps[] PROGMEM = {
0x00, 0xFF, 0xFF, 0xFF, 0xE3, 0xFF, 0xC0, 0xEF, 0xDF, 0xBF, 0x7E, 0xFD,
0xC0, 0x0E, 0xE0, 0xEE, 0x1D, 0xCF, 0xFF, 0xFF, 0xF1, 0xDC, 0x1D, 0xC3,
0xB8, 0xFF, 0xFF, 0xFF, 0x3B, 0x83, 0xB8, 0x77, 0x07, 0x70, 0x1C, 0x0E,
0x0F, 0xCF, 0xEE, 0x07, 0x03, 0x81, 0xFC, 0x7F, 0x0F, 0xC0, 0xE0, 0x74,
0x3F, 0xF9, 0xF8, 0x38, 0x1C, 0x00, 0x38, 0x38, 0x7C, 0x70, 0xC6, 0x70,
0xC6, 0xE0, 0xC6, 0xE0, 0xC7, 0xC0, 0x7D, 0xDC, 0x3B, 0xBE, 0x03, 0xE3,
0x07, 0x63, 0x07, 0x63, 0x0E, 0x63, 0x0E, 0x3E, 0x1C, 0x1C, 0x1E, 0x01,
0xF8, 0x1F, 0xE0, 0xE7, 0x07, 0x38, 0x1F, 0x80, 0xF8, 0x0F, 0xCE, 0xEF,
0x77, 0x3F, 0x38, 0xF9, 0xFF, 0xC7, 0xFF, 0x1F, 0xBC, 0xFF, 0xFF, 0xC0,
0x08, 0x73, 0x8E, 0x71, 0xCE, 0x38, 0xE3, 0x8E, 0x38, 0xE3, 0x87, 0x1C,
0x38, 0xE1, 0xC2, 0x43, 0x87, 0x1C, 0x38, 0xE1, 0xC7, 0x1C, 0x71, 0xC7,
0x1C, 0x73, 0x8E, 0x71, 0xCE, 0x10, 0x1C, 0x0E, 0x17, 0x5F, 0xFF, 0xF9,
0xB1, 0xDC, 0x6C, 0x0E, 0x01, 0xC0, 0x38, 0x07, 0x0F, 0xFF, 0xFF, 0xFF,
0xF8, 0x70, 0x0E, 0x01, 0xC0, 0x38, 0x00, 0x77, 0x77, 0xEE, 0xFF, 0xFF,
0xC0, 0x6F, 0xF6, 0x01, 0xC0, 0xE0, 0x38, 0x0E, 0x07, 0x01, 0xC0, 0x70,
0x38, 0x0E, 0x03, 0x81, 0xC0, 0x70, 0x1C, 0x0E, 0x03, 0x80, 0xE0, 0x70,
0x1C, 0x07, 0x03, 0x80, 0x1C, 0x3F, 0x9F, 0xDE, 0xFE, 0x3F, 0x1F, 0x8F,
0xC7, 0xE3, 0xF1, 0xFD, 0xEF, 0xE7, 0xF0, 0xE0, 0x0E, 0x3D, 0xFF, 0xF6,
0xE1, 0xC3, 0x87, 0x0E, 0x1C, 0x38, 0x70, 0xE1, 0xC0, 0x3E, 0x7F, 0xBF,
0xEC, 0x70, 0x38, 0x1C, 0x1C, 0x1C, 0x1C, 0x1C, 0x1C, 0x1F, 0xFF, 0xFF,
0xFC, 0x3C, 0x7F, 0x3F, 0xC8, 0xE0, 0x70, 0x30, 0xF0, 0x7E, 0x07, 0x81,
0xE0, 0xFF, 0xFF, 0xF3, 0xF0, 0x07, 0x07, 0x87, 0xC3, 0xE3, 0xF3, 0xB9,
0x9D, 0xCE, 0xFF, 0xFF, 0xFF, 0xE0, 0xE0, 0x70, 0x38, 0x3F, 0x1F, 0x8F,
0xC7, 0x03, 0x83, 0xF1, 0xFC, 0xFF, 0x07, 0x81, 0xC0, 0xFF, 0xFF, 0xF3,
0xE0, 0x07, 0x0F, 0x8F, 0xCF, 0x07, 0x07, 0xF3, 0xFD, 0xFF, 0xE3, 0xF1,
0xF8, 0xEF, 0xF7, 0xF1, 0xF0, 0xFF, 0xFF, 0xFF, 0xE0, 0xE0, 0xE0, 0x70,
0x70, 0x38, 0x38, 0x1C, 0x0E, 0x0E, 0x07, 0x03, 0x80, 0x3E, 0x3F, 0xBF,
0xFC, 0x7E, 0x3F, 0xB9, 0xF8, 0xFE, 0xE7, 0xF1, 0xF8, 0xFF, 0xF7, 0xF1,
0xF0, 0x3E, 0x3F, 0xBF, 0xDC, 0x7E, 0x3F, 0x1F, 0xFE, 0xFF, 0x3F, 0x83,
0xC3, 0xCF, 0xC7, 0xC3, 0x80, 0x6F, 0xF6, 0x00, 0x06, 0xFF, 0x60, 0x33,
0xDE, 0x60, 0x00, 0x00, 0x73, 0x9C, 0xEE, 0x70, 0x01, 0x87, 0xEF, 0xFF,
0xF8, 0xE0, 0x3F, 0x8F, 0xFC, 0x7E, 0x01, 0x80, 0xFF, 0xFF, 0xFF, 0xE0,
0x00, 0x07, 0xFF, 0xFF, 0xFF, 0x60, 0x3E, 0x3F, 0xE3, 0xF0, 0x38, 0xFF,
0xFE, 0xF8, 0x60, 0x00, 0x7C, 0xFE, 0xFF, 0x07, 0x07, 0x07, 0x0E, 0x1E,
0x3C, 0x38, 0x38, 0x00, 0x30, 0x78, 0x78, 0x30, 0x03, 0xF0, 0x07, 0xFE,
0x0F, 0x03, 0x86, 0x00, 0xE6, 0x1F, 0xB7, 0x3F, 0xCF, 0x3C, 0xE7, 0x9C,
0x73, 0xCE, 0x39, 0xE7, 0x1C, 0xF3, 0xCE, 0x5C, 0xFF, 0xE6, 0x3F, 0xE3,
0x80, 0x00, 0xF0, 0x00, 0x3F, 0xE0, 0x07, 0xF8, 0x00, 0x03, 0x80, 0x0F,
0x80, 0x1F, 0x00, 0x77, 0x00, 0xEE, 0x03, 0xDE, 0x07, 0x1C, 0x1E, 0x3C,
0x3F, 0xF8, 0x7F, 0xF1, 0xFF, 0xF3, 0x80, 0xE7, 0x01, 0xDC, 0x01, 0xC0,
0xFE, 0x3F, 0xCF, 0xFB, 0x8E, 0xE3, 0xBF, 0xCF, 0xF3, 0xFE, 0xE1, 0xF8,
0x7E, 0x1F, 0xFF, 0xFF, 0xBF, 0x80, 0x0F, 0xC7, 0xFD, 0xFF, 0xBC, 0x2F,
0x01, 0xC0, 0x38, 0x07, 0x00, 0xE0, 0x1E, 0x01, 0xE0, 0x3F, 0xF3, 0xFE,
0x1F, 0x80, 0xFF, 0x0F, 0xFC, 0xFF, 0xEE, 0x1E, 0xE0, 0xFE, 0x07, 0xE0,
0x7E, 0x07, 0xE0, 0x7E, 0x0F, 0xE1, 0xEF, 0xFE, 0xFF, 0xCF, 0xF0, 0xFF,
0xFF, 0xFF, 0xFC, 0x0E, 0x07, 0xFB, 0xFD, 0xFE, 0xE0, 0x70, 0x38, 0x1F,
0xFF, 0xFF, 0xFC, 0xFF, 0xFF, 0xFF, 0xE0, 0xE0, 0xFE, 0xFE, 0xFE, 0xE0,
0xE0, 0xE0, 0xE0, 0xE0, 0xE0, 0x0F, 0xC7, 0xFD, 0xFF, 0xBC, 0x2F, 0x01,
0xC0, 0x38, 0x07, 0x07, 0xE0, 0xFE, 0x1D, 0xE3, 0xBF, 0xF3, 0xFE, 0x1F,
0x80, 0xE0, 0xFC, 0x1F, 0x83, 0xF0, 0x7E, 0x0F, 0xFF, 0xFF, 0xFF, 0xFF,
0xE0, 0xFC, 0x1F, 0x83, 0xF0, 0x7E, 0x0F, 0xC1, 0xC0, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xC0, 0x03, 0x81, 0xC0, 0xE0, 0x70, 0x38, 0x1C, 0x0E, 0x07,
0x03, 0x81, 0xD1, 0xEF, 0xFF, 0xF3, 0xF0, 0xE0, 0xFE, 0x1E, 0xE3, 0xCE,
0x78, 0xEF, 0x0F, 0xE0, 0xFC, 0x0F, 0xC0, 0xEE, 0x0E, 0x70, 0xE7, 0x8E,
0x3C, 0xE1, 0xEE, 0x0F, 0xE0, 0x70, 0x38, 0x1C, 0x0E, 0x07, 0x03, 0x81,
0xC0, 0xE0, 0x70, 0x38, 0x1F, 0xFF, 0xFF, 0xFC, 0x70, 0x1C, 0xF0, 0x79,
0xE0, 0xF3, 0xE3, 0xE7, 0xC7, 0xDD, 0x8D, 0xFB, 0xBB, 0xF3, 0x67, 0xE7,
0xCF, 0xCF, 0x9F, 0x8E, 0x3F, 0x1C, 0x7E, 0x00, 0xFC, 0x01, 0xC0, 0xE0,
0xFE, 0x1F, 0xC3, 0xFC, 0x7F, 0xCF, 0xD9, 0xFB, 0xBF, 0x3F, 0xE7, 0xFC,
0x7F, 0x87, 0xF0, 0xFE, 0x0F, 0xC1, 0xC0, 0x0F, 0xC0, 0xFF, 0xC7, 0xFF,
0x9E, 0x1E, 0xF0, 0x3F, 0x80, 0x7E, 0x01, 0xF8, 0x07, 0xE0, 0x1F, 0xC0,
0xF7, 0x87, 0x9F, 0xFE, 0x3F, 0xF0, 0x3F, 0x00, 0xFE, 0x3F, 0xEF, 0xFF,
0x87, 0xE1, 0xF8, 0x7F, 0xFF, 0xFE, 0xFE, 0x38, 0x0E, 0x03, 0x80, 0xE0,
0x38, 0x00, 0x0F, 0xC0, 0xFF, 0xC7, 0xFF, 0x9E, 0x1E, 0xF0, 0x3F, 0x80,
0x7E, 0x01, 0xF8, 0x07, 0xE0, 0x1F, 0xC0, 0xF7, 0x87, 0x9F, 0xFE, 0x3F,
0xF0, 0x3F, 0x00, 0x3C, 0x00, 0x7E, 0x00, 0x78, 0xFE, 0x1F, 0xF3, 0xFF,
0x70, 0xEE, 0x1D, 0xC3, 0xBF, 0xF7, 0xFC, 0xFF, 0x1C, 0xF3, 0x8E, 0x70,
0xEE, 0x1D, 0xC1, 0xC0, 0x3F, 0x1F, 0xEF, 0xFB, 0x80, 0xE0, 0x3E, 0x07,
0xF0, 0x7E, 0x03, 0xC0, 0x74, 0x1F, 0xFF, 0xFF, 0x9F, 0xC0, 0xFF, 0xFF,
0xFF, 0xFF, 0x87, 0x00, 0xE0, 0x1C, 0x03, 0x80, 0x70, 0x0E, 0x01, 0xC0,
0x38, 0x07, 0x00, 0xE0, 0x1C, 0x00, 0xE0, 0xFC, 0x1F, 0x83, 0xF0, 0x7E,
0x0F, 0xC1, 0xF8, 0x3F, 0x07, 0xE0, 0xFC, 0x1F, 0xC7, 0xBF, 0xE7, 0xFC,
0x3E, 0x00, 0xE0, 0x0E, 0xE0, 0x39, 0xC0, 0x71, 0xC1, 0xC3, 0x83, 0x87,
0x07, 0x07, 0x1C, 0x0E, 0x38, 0x0E, 0xE0, 0x1D, 0xC0, 0x1F, 0x00, 0x3E,
0x00, 0x7C, 0x00, 0x70, 0x00, 0xE0, 0x00, 0xFC, 0x1C, 0x1D, 0xC3, 0x87,
0x38, 0x78, 0xE7, 0x1F, 0x1C, 0xE3, 0x63, 0x8E, 0x6C, 0xE1, 0xDD, 0xDC,
0x3B, 0xBB, 0x83, 0x63, 0x60, 0x7C, 0x7C, 0x0F, 0x8F, 0x81, 0xE0, 0xF0,
0x1C, 0x1C, 0x00, 0xF0, 0x3D, 0xE1, 0xE3, 0x87, 0x07, 0x38, 0x1F, 0xE0,
0x3F, 0x00, 0x78, 0x01, 0xE0, 0x0F, 0xC0, 0x7F, 0x81, 0xCE, 0x0E, 0x1C,
0x78, 0x7B, 0xC0, 0xF0, 0xE0, 0x3B, 0x83, 0x9C, 0x1C, 0x71, 0xC3, 0xDE,
0x0E, 0xE0, 0x3E, 0x01, 0xF0, 0x07, 0x00, 0x38, 0x01, 0xC0, 0x0E, 0x00,
0x70, 0x03, 0x80, 0xFF, 0xFF, 0xFF, 0xFC, 0x0E, 0x07, 0x03, 0x80, 0xE0,
0x70, 0x38, 0x1E, 0x07, 0x03, 0xFF, 0xFF, 0xFF, 0xF0, 0xFF, 0xFF, 0xCE,
0x73, 0x9C, 0xE7, 0x39, 0xCE, 0x73, 0x9C, 0xE7, 0xFF, 0xF0, 0xE0, 0x1C,
0x07, 0x01, 0xC0, 0x38, 0x0E, 0x03, 0x80, 0x70, 0x1C, 0x07, 0x00, 0xE0,
0x38, 0x0E, 0x01, 0xC0, 0x70, 0x1C, 0x03, 0x80, 0xE0, 0x38, 0x07, 0xFF,
0xFE, 0x73, 0x9C, 0xE7, 0x39, 0xCE, 0x73, 0x9C, 0xE7, 0x3F, 0xFF, 0xF0,
0x0E, 0x03, 0xE0, 0x7C, 0x1D, 0xC7, 0xBC, 0xE3, 0xB8, 0x3B, 0x06, 0xFF,
0xFF, 0xF0, 0x47, 0x1E, 0x20, 0x7E, 0x3F, 0x9F, 0xE0, 0x73, 0xFB, 0xFF,
0x8F, 0xC7, 0xFF, 0xBF, 0xCF, 0xE0, 0xE0, 0x38, 0x0E, 0x03, 0x80, 0xE0,
0x3F, 0xCF, 0xFB, 0xFE, 0xE3, 0xF8, 0x7E, 0x1F, 0x87, 0xE3, 0xFF, 0xEF,
0xFB, 0xF8, 0x1F, 0x3F, 0x7F, 0xF0, 0xE0, 0xE0, 0xE0, 0xF0, 0x7F, 0x7F,
0x1F, 0x01, 0xC0, 0x70, 0x1C, 0x07, 0x01, 0xC7, 0xF7, 0xFD, 0xFF, 0xF1,
0xF8, 0x7E, 0x1F, 0x87, 0xF1, 0xDF, 0xF7, 0xFC, 0x7F, 0x1F, 0x1F, 0xE7,
0xFF, 0x87, 0xFF, 0xFF, 0xFE, 0x03, 0xC0, 0x7F, 0x9F, 0xE1, 0xF8, 0x3F,
0x7E, 0xFE, 0xE0, 0xE0, 0xFE, 0xFE, 0xFE, 0xE0, 0xE0, 0xE0, 0xE0, 0xE0,
0xE0, 0xE0, 0xE0, 0x1F, 0xDF, 0xF7, 0xFF, 0xC7, 0xE1, 0xF8, 0x7F, 0x1F,
0xFF, 0x7F, 0xCF, 0xF0, 0x1C, 0x0F, 0x7F, 0x9F, 0xE7, 0xE0, 0xE0, 0x38,
0x0E, 0x03, 0x80, 0xE0, 0x3F, 0xCF, 0xFB, 0xFF, 0xE3, 0xF8, 0x7E, 0x1F,
0x87, 0xE1, 0xF8, 0x7E, 0x1F, 0x87, 0xFF, 0xF1, 0xFF, 0xFF, 0xFF, 0xFF,
0x1C, 0x71, 0xC7, 0x00, 0x71, 0xC7, 0x1C, 0x71, 0xC7, 0x1C, 0x71, 0xC7,
0x1F, 0xFF, 0xBC, 0xE0, 0x1C, 0x03, 0x80, 0x70, 0x0E, 0x01, 0xC3, 0xB8,
0xE7, 0x38, 0xEE, 0x1F, 0x83, 0xF8, 0x77, 0x8E, 0x79, 0xC7, 0x38, 0x77,
0x07, 0xE7, 0x39, 0xCE, 0x73, 0x9C, 0xE7, 0x39, 0xCE, 0x7F, 0xE7, 0xFE,
0xF9, 0xFF, 0xFB, 0xFF, 0xFF, 0x1C, 0x7E, 0x38, 0xFC, 0x71, 0xF8, 0xE3,
0xF1, 0xC7, 0xE3, 0x8F, 0xC7, 0x1F, 0x8E, 0x38, 0xFF, 0x3F, 0xEF, 0xFF,
0x8F, 0xE1, 0xF8, 0x7E, 0x1F, 0x87, 0xE1, 0xF8, 0x7E, 0x1C, 0x1E, 0x1F,
0xE7, 0xFB, 0xCF, 0xE1, 0xF8, 0x7E, 0x1F, 0xCF, 0x7F, 0x9F, 0xE1, 0xE0,
0xFE, 0x3F, 0xEF, 0xFB, 0x8F, 0xE1, 0xF8, 0x7E, 0x1F, 0x8F, 0xFF, 0xBF,
0xEF, 0xF3, 0x80, 0xE0, 0x38, 0x0E, 0x00, 0x1F, 0xDF, 0xF7, 0xFF, 0xC7,
0xE1, 0xF8, 0x7E, 0x1F, 0xC7, 0x7F, 0xDF, 0xF3, 0xFC, 0x07, 0x01, 0xC0,
0x70, 0x1C, 0x7F, 0xFF, 0xFF, 0x0E, 0x1C, 0x38, 0x70, 0xE1, 0xC3, 0x80,
0x3E, 0xFE, 0xE0, 0xE0, 0xF8, 0x7E, 0x1F, 0x07, 0x87, 0xFF, 0xFC, 0xE1,
0xC3, 0x87, 0xFF, 0xFF, 0xF8, 0x70, 0xE1, 0xC3, 0x87, 0xF7, 0xE7, 0xC0,
0xE1, 0xF8, 0x7E, 0x1F, 0x87, 0xE1, 0xF8, 0x7E, 0x1F, 0xC7, 0xFF, 0xDF,
0xF3, 0xFC, 0xE0, 0xFE, 0x1D, 0xC7, 0x38, 0xE7, 0xBC, 0x77, 0x0E, 0xE1,
0xFC, 0x1F, 0x03, 0xE0, 0x38, 0x00, 0xE3, 0x8F, 0xC7, 0x1D, 0x8E, 0x33,
0x9E, 0xE7, 0x7D, 0xCE, 0xDB, 0x8D, 0xB6, 0x1F, 0x7C, 0x3C, 0x78, 0x38,
0xE0, 0x71, 0xC0, 0xF1, 0xEF, 0x78, 0xEE, 0x1F, 0xC1, 0xF0, 0x1C, 0x07,
0xC1, 0xFC, 0x3B, 0x8F, 0x7B, 0xC7, 0x80, 0xE0, 0xFC, 0x1D, 0xC7, 0x38,
0xE7, 0x1C, 0x77, 0x0E, 0xE1, 0xFC, 0x1F, 0x03, 0xE0, 0x7C, 0x0F, 0x0F,
0xE1, 0xF8, 0x3E, 0x00, 0xFF, 0xFF, 0xFF, 0x0E, 0x1C, 0x38, 0x38, 0x70,
0xFF, 0xFF, 0xFF, 0x0E, 0x3C, 0xF9, 0xC3, 0x87, 0x0E, 0x1C, 0x79, 0xE3,
0xC7, 0xC3, 0x87, 0x0E, 0x1C, 0x38, 0x7C, 0xF8, 0x70, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xF0, 0xE1, 0xE3, 0xE1, 0xC3, 0x87, 0x0E, 0x1C,
0x3C, 0x3C, 0x79, 0xF3, 0x87, 0x0E, 0x1C, 0x39, 0xF3, 0xE7, 0x00, 0x30,
0x9F, 0x3F, 0xFF, 0x3E, 0x43, 0x00, 0xFF, 0x81, 0x81, 0x81, 0x81, 0x81,
0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0xFF, 0x00, 0xFF, 0xF1,
0xFF, 0xFF, 0xFF, 0xF8, 0x1C, 0x1C, 0x1C, 0x3F, 0x7F, 0x7F, 0xF0, 0xE0,
0xE0, 0xF0, 0xFF, 0x7F, 0x3F, 0x1C, 0x1C, 0x1C, 0x0F, 0x87, 0xF3, 0xFC,
0xF0, 0x38, 0x0E, 0x0F, 0xF3, 0xFC, 0xFF, 0x0E, 0x03, 0x80, 0xFF, 0x3F,
0xDF, 0xF0, 0x40, 0xBF, 0xF7, 0xF9, 0xCE, 0x61, 0x98, 0x67, 0x39, 0xFE,
0xFF, 0xD0, 0x20, 0xE0, 0xEE, 0x39, 0xC7, 0x1D, 0xC3, 0xB8, 0x3E, 0x1F,
0xF3, 0xFE, 0x0E, 0x01, 0xC1, 0xFF, 0x3F, 0xE0, 0xE0, 0x1C, 0x00, 0xFF,
0xFF, 0xFF, 0x00, 0x0F, 0xFF, 0xFF, 0xF0, 0x3E, 0x7F, 0xFF, 0xE0, 0xE0,
0x7C, 0xFE, 0xEF, 0xE7, 0xE7, 0xF7, 0x7E, 0x1E, 0x07, 0x07, 0xFF, 0xFF,
0x7C, 0xEF, 0xDF, 0xB8, 0x0F, 0xC0, 0x7F, 0x83, 0x87, 0x18, 0x06, 0xE7,
0xDF, 0x3F, 0x3C, 0xC0, 0xF3, 0x03, 0xCF, 0xCF, 0x9F, 0x76, 0x01, 0x8E,
0x1C, 0x1F, 0xE0, 0x3F, 0x00, 0x7C, 0xFC, 0x19, 0xFF, 0xF8, 0xFF, 0xBF,
0x10, 0x8E, 0x77, 0xBD, 0xCE, 0xF7, 0x9C, 0xE7, 0xBC, 0xE7, 0x10, 0x80,
0xFF, 0xFF, 0xFF, 0xE0, 0x70, 0x38, 0x1C, 0x0E, 0x07, 0xFF, 0xFF, 0xC0,
0x0F, 0xC0, 0x7F, 0x83, 0x87, 0x18, 0x06, 0xEF, 0x9F, 0x33, 0x3C, 0xFC,
0xF3, 0xE3, 0xCD, 0xCF, 0xB3, 0x76, 0x01, 0x8E, 0x1C, 0x1F, 0xE0, 0x3F,
0x00, 0xFF, 0xF0, 0x7B, 0xFC, 0xF3, 0xFD, 0xE0, 0x0E, 0x01, 0xC0, 0x38,
0x07, 0x0F, 0xFF, 0xFF, 0xFF, 0xF8, 0x70, 0x0E, 0x01, 0xC0, 0x38, 0x00,
0x0F, 0xFF, 0xFF, 0xFF, 0xF8, 0x7B, 0xF0, 0xC7, 0x31, 0x8F, 0xFF, 0x7B,
0xF0, 0xCF, 0x3C, 0x3F, 0xFE, 0x11, 0xFC, 0x80, 0xE1, 0xF8, 0x7E, 0x1F,
0x87, 0xE1, 0xF8, 0x7E, 0x1F, 0xC7, 0xFF, 0xFF, 0xFF, 0xFF, 0x80, 0xE0,
0x38, 0x0E, 0x00, 0x1F, 0xF7, 0xFF, 0xFE, 0x7F, 0xE7, 0xFE, 0x7F, 0xE7,
0xFE, 0x77, 0xE7, 0x1E, 0x70, 0xE7, 0x0E, 0x70, 0xE7, 0x0E, 0x70, 0xE7,
0x0E, 0x70, 0xE7, 0x0E, 0x70, 0xE7, 0x6F, 0xF6, 0x63, 0xBF, 0x37, 0xFB,
0x33, 0x33, 0x3C, 0x7E, 0xE7, 0xC3, 0xC3, 0xE7, 0x7E, 0x3C, 0x42, 0x39,
0xCF, 0x79, 0xCE, 0x7B, 0xDC, 0xEF, 0x7B, 0x9C, 0x42, 0x00, 0x30, 0x30,
0x70, 0x70, 0xF0, 0x60, 0xB0, 0xE0, 0x31, 0xC0, 0x31, 0xC0, 0x33, 0x8E,
0x33, 0x9E, 0x07, 0x36, 0x07, 0x26, 0x0E, 0x7F, 0x0E, 0x7F, 0x1C, 0x06,
0x1C, 0x06, 0x30, 0x30, 0xE0, 0xE3, 0xC1, 0x85, 0x87, 0x03, 0x1C, 0x06,
0x38, 0x0C, 0xEF, 0x19, 0xFF, 0x07, 0x06, 0x0E, 0x1C, 0x38, 0x60, 0x71,
0x81, 0xC7, 0xE3, 0x8F, 0xC0, 0x78, 0x0C, 0x7E, 0x0E, 0x03, 0x06, 0x07,
0x87, 0x03, 0xC7, 0x00, 0x63, 0x83, 0xF3, 0x9D, 0xF1, 0xDE, 0x01, 0xDB,
0x00, 0xE9, 0x80, 0xEF, 0xE0, 0x77, 0xF0, 0x70, 0x30, 0x38, 0x18, 0x0C,
0x1E, 0x1E, 0x0C, 0x00, 0x1C, 0x1C, 0x3C, 0x78, 0x70, 0xE0, 0xE0, 0xFF,
0x7F, 0x3E, 0x02, 0x00, 0x0E, 0x00, 0x0F, 0x00, 0x04, 0x00, 0x00, 0x00,
0x70, 0x01, 0xF0, 0x03, 0xE0, 0x0E, 0xE0, 0x1D, 0xC0, 0x7B, 0xC0, 0xE3,
0x83, 0xC7, 0x87, 0xFF, 0x0F, 0xFE, 0x3F, 0xFE, 0x70, 0x1C, 0xE0, 0x3B,
0x80, 0x38, 0x00, 0x80, 0x03, 0x80, 0x1E, 0x00, 0x10, 0x00, 0x00, 0x00,
0x70, 0x01, 0xF0, 0x03, 0xE0, 0x0E, 0xE0, 0x1D, 0xC0, 0x7B, 0xC0, 0xE3,
0x83, 0xC7, 0x87, 0xFF, 0x0F, 0xFE, 0x3F, 0xFE, 0x70, 0x1C, 0xE0, 0x3B,
0x80, 0x38, 0x01, 0x00, 0x07, 0x00, 0x1F, 0x00, 0x77, 0x00, 0x44, 0x00,
0x00, 0x00, 0xE0, 0x03, 0xE0, 0x07, 0xC0, 0x1D, 0xC0, 0x3B, 0x80, 0xF7,
0x81, 0xC7, 0x07, 0x8F, 0x0F, 0xFE, 0x1F, 0xFC, 0x7F, 0xFC, 0xE0, 0x39,
0xC0, 0x77, 0x00, 0x70, 0x06, 0x40, 0x1F, 0xC0, 0x13, 0x00, 0x00, 0x00,
0x38, 0x00, 0xF8, 0x01, 0xF0, 0x07, 0x70, 0x0E, 0xE0, 0x3D, 0xE0, 0x71,
0xC1, 0xE3, 0xC3, 0xFF, 0x87, 0xFF, 0x1F, 0xFF, 0x38, 0x0E, 0x70, 0x1D,
0xC0, 0x1C, 0x0E, 0xE0, 0x1D, 0xC0, 0x3B, 0x80, 0x00, 0x00, 0x38, 0x00,
0xF8, 0x01, 0xF0, 0x07, 0x70, 0x0E, 0xE0, 0x3D, 0xE0, 0x71, 0xC1, 0xE3,
0xC3, 0xFF, 0x87, 0xFF, 0x1F, 0xFF, 0x38, 0x0E, 0x70, 0x1D, 0xC0, 0x1C,
0x03, 0x80, 0x0D, 0x80, 0x1B, 0x00, 0x3E, 0x00, 0x7C, 0x00, 0xF8, 0x03,
0xF8, 0x07, 0x70, 0x0E, 0xE0, 0x38, 0xE0, 0x71, 0xC1, 0xE3, 0xC3, 0xFF,
0x87, 0xFF, 0x1F, 0xFF, 0x38, 0x0E, 0x70, 0x1D, 0xC0, 0x1C, 0x00, 0x7F,
0xE0, 0x1F, 0xFC, 0x07, 0xFF, 0x80, 0xFC, 0x00, 0x3B, 0x80, 0x0E, 0x7F,
0x81, 0xCF, 0xF0, 0x71, 0xFE, 0x1F, 0xF8, 0x03, 0xFF, 0x00, 0xFF, 0xE0,
0x1C, 0x1F, 0xF7, 0x03, 0xFF, 0xC0, 0x7F, 0xC0, 0x0F, 0xC7, 0xFD, 0xFF,
0xBC, 0x2F, 0x01, 0xC0, 0x38, 0x07, 0x00, 0xE0, 0x1E, 0x01, 0xE0, 0x3F,
0xF3, 0xFE, 0x1F, 0x81, 0x80, 0x18, 0x0F, 0x01, 0xC0, 0x10, 0x1C, 0x07,
0x80, 0x80, 0x07, 0xFF, 0xFF, 0xFF, 0xE0, 0x70, 0x3F, 0xDF, 0xEF, 0xF7,
0x03, 0x81, 0xC0, 0xFF, 0xFF, 0xFF, 0xE0, 0x04, 0x07, 0x0F, 0x02, 0x00,
0x07, 0xFF, 0xFF, 0xFF, 0xE0, 0x70, 0x3F, 0xDF, 0xEF, 0xF7, 0x03, 0x81,
0xC0, 0xFF, 0xFF, 0xFF, 0xE0, 0x08, 0x0E, 0x0F, 0x8E, 0xE2, 0x20, 0x03,
0xFF, 0xFF, 0xFF, 0xF0, 0x38, 0x1F, 0xEF, 0xF7, 0xFB, 0x81, 0xC0, 0xE0,
0x7F, 0xFF, 0xFF, 0xF0, 0x77, 0x3B, 0x9D, 0xC0, 0x0F, 0xFF, 0xFF, 0xFF,
0xC0, 0xE0, 0x7F, 0xBF, 0xDF, 0xEE, 0x07, 0x03, 0x81, 0xFF, 0xFF, 0xFF,
0xC0, 0x47, 0x1E, 0x20, 0x39, 0xCE, 0x73, 0x9C, 0xE7, 0x39, 0xCE, 0x73,
0x9C, 0x11, 0xFC, 0x80, 0x39, 0xCE, 0x73, 0x9C, 0xE7, 0x39, 0xCE, 0x73,
0x9C, 0x10, 0x71, 0xF7, 0x74, 0x40, 0x0E, 0x1C, 0x38, 0x70, 0xE1, 0xC3,
0x87, 0x0E, 0x1C, 0x38, 0x70, 0xE1, 0xC0, 0xEF, 0xDF, 0xB8, 0x03, 0x87,
0x0E, 0x1C, 0x38, 0x70, 0xE1, 0xC3, 0x87, 0x0E, 0x1C, 0x38, 0x70, 0x3F,
0xC0, 0xFF, 0xC3, 0xFF, 0x8E, 0x1E, 0x38, 0x3C, 0xE0, 0x7F, 0xF1, 0xFF,
0xC7, 0x38, 0x1C, 0xE0, 0xF3, 0x87, 0x8F, 0xFE, 0x3F, 0xF0, 0xFF, 0x00,
0x19, 0x07, 0xF0, 0x4C, 0x00, 0x0E, 0x0F, 0xE1, 0xFC, 0x3F, 0xC7, 0xFC,
0xFD, 0x9F, 0xBB, 0xF3, 0xFE, 0x7F, 0xC7, 0xF8, 0x7F, 0x0F, 0xE0, 0xFC,
0x1C, 0x04, 0x00, 0x38, 0x00, 0x78, 0x00, 0x40, 0x00, 0x00, 0x3F, 0x03,
0xFF, 0x1F, 0xFE, 0x78, 0x7B, 0xC0, 0xFE, 0x01, 0xF8, 0x07, 0xE0, 0x1F,
0x80, 0x7F, 0x03, 0xDE, 0x1E, 0x7F, 0xF8, 0xFF, 0xC0, 0xFC, 0x00, 0x00,
0x80, 0x07, 0x00, 0x78, 0x00, 0x80, 0x00, 0x00, 0x3F, 0x03, 0xFF, 0x1F,
0xFE, 0x78, 0x7B, 0xC0, 0xFE, 0x01, 0xF8, 0x07, 0xE0, 0x1F, 0x80, 0x7F,
0x03, 0xDE, 0x1E, 0x7F, 0xF8, 0xFF, 0xC0, 0xFC, 0x00, 0x01, 0x00, 0x0E,
0x00, 0x7C, 0x03, 0xB8, 0x04, 0x40, 0x00, 0x00, 0xFC, 0x0F, 0xFC, 0x7F,
0xF9, 0xE1, 0xEF, 0x03, 0xF8, 0x07, 0xE0, 0x1F, 0x80, 0x7E, 0x01, 0xFC,
0x0F, 0x78, 0x79, 0xFF, 0xE3, 0xFF, 0x03, 0xF0, 0x06, 0x40, 0x3F, 0x80,
0x4C, 0x00, 0x00, 0x0F, 0xC0, 0xFF, 0xC7, 0xFF, 0x9E, 0x1E, 0xF0, 0x3F,
0x80, 0x7E, 0x01, 0xF8, 0x07, 0xE0, 0x1F, 0xC0, 0xF7, 0x87, 0x9F, 0xFE,
0x3F, 0xF0, 0x3F, 0x00, 0x1D, 0xC0, 0x77, 0x01, 0xDC, 0x00, 0x00, 0x0F,
0xC0, 0xFF, 0xC7, 0xFF, 0x9E, 0x1E, 0xF0, 0x3F, 0x80, 0x7E, 0x01, 0xF8,
0x07, 0xE0, 0x1F, 0xC0, 0xF7, 0x87, 0x9F, 0xFE, 0x3F, 0xF0, 0x3F, 0x00,
0x41, 0x71, 0xDD, 0xC7, 0xC1, 0xC1, 0xF1, 0xDD, 0xC7, 0x41, 0x00, 0x00,
0x00, 0x3F, 0x63, 0xFF, 0x9F, 0xFE, 0x78, 0xFB, 0xC3, 0xFE, 0x1D, 0xF8,
0xE7, 0xE7, 0x1F, 0xB8, 0x7F, 0xC3, 0xDE, 0x1E, 0x7F, 0xF9, 0xFF, 0xC6,
0xFC, 0x00, 0x00, 0x10, 0x07, 0x00, 0x78, 0x02, 0x00, 0x01, 0xC1, 0xF8,
0x3F, 0x07, 0xE0, 0xFC, 0x1F, 0x83, 0xF0, 0x7E, 0x0F, 0xC1, 0xF8, 0x3F,
0x8F, 0x7F, 0xCF, 0xF8, 0x7C, 0x00, 0x02, 0x00, 0xE0, 0x78, 0x04, 0x00,
0x01, 0xC1, 0xF8, 0x3F, 0x07, 0xE0, 0xFC, 0x1F, 0x83, 0xF0, 0x7E, 0x0F,
0xC1, 0xF8, 0x3F, 0x8F, 0x7F, 0xCF, 0xF8, 0x7C, 0x00, 0x04, 0x01, 0xC0,
0x7C, 0x1D, 0xC1, 0x10, 0x00, 0x38, 0x3F, 0x07, 0xE0, 0xFC, 0x1F, 0x83,
0xF0, 0x7E, 0x0F, 0xC1, 0xF8, 0x3F, 0x07, 0xF1, 0xEF, 0xF9, 0xFF, 0x0F,
0x80, 0x3B, 0x87, 0x70, 0xEE, 0x00, 0x0E, 0x0F, 0xC1, 0xF8, 0x3F, 0x07,
0xE0, 0xFC, 0x1F, 0x83, 0xF0, 0x7E, 0x0F, 0xC1, 0xFC, 0x7B, 0xFE, 0x7F,
0xC3, 0xE0, 0x01, 0x00, 0x1C, 0x03, 0xC0, 0x08, 0x00, 0x00, 0x70, 0x1D,
0xC1, 0xCE, 0x0E, 0x38, 0xE1, 0xEF, 0x07, 0x70, 0x1F, 0x00, 0xF8, 0x03,
0x80, 0x1C, 0x00, 0xE0, 0x07, 0x00, 0x38, 0x01, 0xC0, 0xE0, 0x38, 0x0F,
0xE3, 0xFE, 0xFF, 0xF8, 0x7E, 0x1F, 0x87, 0xFF, 0xFF, 0xEF, 0xE3, 0x80,
0xE0, 0x38, 0x00, 0x3E, 0x0F, 0xE3, 0xFE, 0x71, 0xCE, 0x39, 0xCF, 0x39,
0xC7, 0x70, 0xEF, 0x1D, 0xF3, 0x8F, 0x70, 0x7E, 0x0F, 0xDF, 0xFB, 0xF7,
0x7C, 0x10, 0x1C, 0x07, 0x80, 0x80, 0x03, 0xF1, 0xFC, 0xFF, 0x03, 0x9F,
0xDF, 0xFC, 0x7E, 0x3F, 0xFD, 0xFE, 0x7F, 0x04, 0x07, 0x0F, 0x02, 0x00,
0x03, 0xF1, 0xFC, 0xFF, 0x03, 0x9F, 0xDF, 0xFC, 0x7E, 0x3F, 0xFD, 0xFE,
0x7F, 0x08, 0x0E, 0x0F, 0x8E, 0xE2, 0x20, 0x01, 0xF8, 0xFE, 0x7F, 0x81,
0xCF, 0xEF, 0xFE, 0x3F, 0x1F, 0xFE, 0xFF, 0x3F, 0x80, 0x32, 0x3F, 0x89,
0x80, 0x07, 0xE3, 0xF9, 0xFE, 0x07, 0x3F, 0xBF, 0xF8, 0xFC, 0x7F, 0xFB,
0xFC, 0xFE, 0x77, 0x3B, 0x9D, 0xC0, 0x07, 0xE3, 0xF9, 0xFE, 0x07, 0x3F,
0xBF, 0xF8, 0xFC, 0x7F, 0xFB, 0xFC, 0xFE, 0x1E, 0x1F, 0x8C, 0xC6, 0x63,
0xF0, 0xF1, 0xF8, 0xFE, 0x7F, 0x81, 0xCF, 0xEF, 0xFE, 0x3F, 0x1F, 0xFE,
0xFF, 0x3F, 0x80, 0x7E, 0x78, 0xFF, 0xF9, 0xFF, 0xF8, 0x1C, 0x73, 0xFF,
0xEF, 0xFF, 0xF8, 0xE0, 0x71, 0xE0, 0xFF, 0xFC, 0xFF, 0xF8, 0xF9, 0xF0,
0x1E, 0x7F, 0x7E, 0xF0, 0xE0, 0xE0, 0xE0, 0xF0, 0x7F, 0x3F, 0x0F, 0x0C,
0x0C, 0x3C, 0x38, 0x10, 0x0E, 0x01, 0xE0, 0x10, 0x00, 0x07, 0xC7, 0xF9,
0xFF, 0xE1, 0xFF, 0xFF, 0xFF, 0x80, 0xF0, 0x1F, 0xE7, 0xF8, 0x7E, 0x02,
0x01, 0xC1, 0xE0, 0x20, 0x00, 0x07, 0xC7, 0xF9, 0xFF, 0xE1, 0xFF, 0xFF,
0xFF, 0x80, 0xF0, 0x1F, 0xE7, 0xF8, 0x7E, 0x08, 0x07, 0x03, 0xE1, 0xDC,
0x22, 0x00, 0x01, 0xF1, 0xFE, 0x7F, 0xF8, 0x7F, 0xFF, 0xFF, 0xE0, 0x3C,
0x07, 0xF9, 0xFE, 0x1F, 0x80, 0x3B, 0x8E, 0xE3, 0xB8, 0x00, 0x1F, 0x1F,
0xE7, 0xFF, 0x87, 0xFF, 0xFF, 0xFE, 0x03, 0xC0, 0x7F, 0x9F, 0xE1, 0xF8,
0x47, 0x1E, 0x20, 0x39, 0xCE, 0x73, 0x9C, 0xE7, 0x39, 0xCE, 0x11, 0xFC,
0x80, 0x39, 0xCE, 0x73, 0x9C, 0xE7, 0x39, 0xCE, 0x10, 0x71, 0xF7, 0x74,
0x40, 0x0E, 0x1C, 0x38, 0x70, 0xE1, 0xC3, 0x87, 0x0E, 0x1C, 0x38, 0xEF,
0xDF, 0xB8, 0x03, 0x87, 0x0E, 0x1C, 0x38, 0x70, 0xE1, 0xC3, 0x87, 0x0E,
0x00, 0x0C, 0x07, 0xB0, 0x7C, 0x7E, 0x0B, 0x80, 0x73, 0xFD, 0xFF, 0xFF,
0xFC, 0x7E, 0x1F, 0x87, 0xE3, 0x9F, 0xE7, 0xF0, 0x78, 0x19, 0x0F, 0xE1,
0x30, 0x00, 0xFF, 0x3F, 0xEF, 0xFF, 0x8F, 0xE1, 0xF8, 0x7E, 0x1F, 0x87,
0xE1, 0xF8, 0x7E, 0x1C, 0x10, 0x0E, 0x01, 0xE0, 0x10, 0x00, 0x07, 0x87,
0xF9, 0xFE, 0xF3, 0xF8, 0x7E, 0x1F, 0x87, 0xF3, 0xDF, 0xE7, 0xF8, 0x78,
0x02, 0x01, 0xC1, 0xE0, 0x20, 0x00, 0x07, 0x87, 0xF9, 0xFE, 0xF3, 0xF8,
0x7E, 0x1F, 0x87, 0xF3, 0xDF, 0xE7, 0xF8, 0x78, 0x08, 0x07, 0x03, 0xE1,
0xDC, 0x22, 0x00, 0x01, 0xE1, 0xFE, 0x7F, 0xBC, 0xFE, 0x1F, 0x87, 0xE1,
0xFC, 0xF7, 0xF9, 0xFE, 0x1E, 0x00, 0x19, 0x0F, 0xE1, 0x30, 0x00, 0x1E,
0x1F, 0xE7, 0xFB, 0xCF, 0xE1, 0xF8, 0x7E, 0x1F, 0xCF, 0x7F, 0x9F, 0xE1,
0xE0, 0x77, 0x1D, 0xC7, 0x70, 0x00, 0x1E, 0x1F, 0xE7, 0xFB, 0xCF, 0xE1,
0xF8, 0x7E, 0x1F, 0xCF, 0x7F, 0x9F, 0xE1, 0xE0, 0x1C, 0x0E, 0x07, 0x00,
0x0F, 0xFF, 0xFF, 0xFE, 0x00, 0x1C, 0x0E, 0x07, 0x00, 0x00, 0x07, 0xB7,
0xF9, 0xFE, 0xF7, 0xF9, 0xFE, 0xDF, 0xE7, 0xFB, 0xDF, 0xE7, 0xFB, 0x78,
0x00, 0x00, 0x10, 0x0E, 0x01, 0xE0, 0x10, 0x00, 0x38, 0x7E, 0x1F, 0x87,
0xE1, 0xF8, 0x7E, 0x1F, 0x87, 0xF1, 0xFF, 0xF7, 0xFC, 0xFF, 0x02, 0x01,
0xC1, 0xE0, 0x20, 0x00, 0x38, 0x7E, 0x1F, 0x87, 0xE1, 0xF8, 0x7E, 0x1F,
0x87, 0xF1, 0xFF, 0xF7, 0xFC, 0xFF, 0x04, 0x03, 0x81, 0xF0, 0xEE, 0x11,
0x00, 0x0E, 0x1F, 0x87, 0xE1, 0xF8, 0x7E, 0x1F, 0x87, 0xE1, 0xFC, 0x7F,
0xFD, 0xFF, 0x3F, 0xC0, 0x77, 0x1D, 0xC7, 0x70, 0x00, 0xE1, 0xF8, 0x7E,
0x1F, 0x87, 0xE1, 0xF8, 0x7E, 0x1F, 0xC7, 0xFF, 0xDF, 0xF3, 0xFC, 0x01,
0x00, 0x70, 0x3C, 0x02, 0x00, 0x01, 0xC1, 0xF8, 0x3B, 0x8E, 0x71, 0xCE,
0x38, 0xEE, 0x1D, 0xC3, 0xF8, 0x3E, 0x07, 0xC0, 0xF8, 0x1E, 0x1F, 0xC3,
0xF0, 0x7C, 0x00, 0xE0, 0x38, 0x0E, 0x03, 0x80, 0xE0, 0x3F, 0x8F, 0xFB,
0xFE, 0xE3, 0xF8, 0x7E, 0x1F, 0x87, 0xE3, 0xFF, 0xEF, 0xFB, 0xFC, 0xE0,
0x38, 0x0E, 0x03, 0x80, 0x3B, 0x87, 0x70, 0xEE, 0x00, 0x0E, 0x0F, 0xC1,
0xDC, 0x73, 0x8E, 0x71, 0xC7, 0x70, 0xEE, 0x1F, 0xC1, 0xF0, 0x3E, 0x07,
0xC0, 0xF0, 0xFE, 0x1F, 0x83, 0xE0, 0x00 };
const GFXglyph Ubuntu_Bold10pt8bGlyphs[] PROGMEM = {
{ 0, 1, 1, 5, 0, 0 }, // 0x20 ' ' U+0020
{ 1, 3, 14, 5, 1, -13 }, // 0x21 '!' U+0021
{ 7, 7, 6, 9, 1, -14 }, // 0x22 '"' U+0022
{ 13, 12, 14, 14, 1, -13 }, // 0x23 '#' U+0023
{ 34, 9, 17, 11, 1, -14 }, // 0x24 '$' U+0024
{ 54, 16, 14, 18, 1, -13 }, // 0x25 '%' U+0025
{ 82, 13, 14, 14, 1, -13 }, // 0x26 '&' U+0026
{ 105, 3, 6, 5, 1, -14 }, // 0x27 ''' U+0027
{ 108, 6, 20, 7, 1, -15 }, // 0x28 '(' U+0028
{ 123, 6, 20, 7, 0, -15 }, // 0x29 ')' U+0029
{ 138, 9, 8, 10, 1, -13 }, // 0x2a '*' U+002A
{ 147, 11, 11, 13, 1, -11 }, // 0x2b '+' U+002B
{ 163, 4, 6, 5, 0, -2 }, // 0x2c ',' U+002C
{ 166, 6, 3, 8, 1, -7 }, // 0x2d '-' U+002D
{ 169, 4, 4, 6, 1, -3 }, // 0x2e '.' U+002E
{ 171, 10, 20, 9, -1, -15 }, // 0x2f '/' U+002F
{ 196, 9, 14, 11, 1, -13 }, // 0x30 '0' U+0030
{ 212, 7, 14, 11, 1, -13 }, // 0x31 '1' U+0031
{ 225, 9, 14, 11, 1, -13 }, // 0x32 '2' U+0032
{ 241, 9, 14, 11, 1, -13 }, // 0x33 '3' U+0033
{ 257, 9, 14, 11, 1, -13 }, // 0x34 '4' U+0034
{ 273, 9, 14, 11, 1, -13 }, // 0x35 '5' U+0035
{ 289, 9, 14, 11, 1, -13 }, // 0x36 '6' U+0036
{ 305, 9, 14, 11, 1, -13 }, // 0x37 '7' U+0037
{ 321, 9, 14, 11, 1, -13 }, // 0x38 '8' U+0038
{ 337, 9, 14, 11, 1, -13 }, // 0x39 '9' U+0039
{ 353, 4, 11, 6, 1, -10 }, // 0x3a ':' U+003A
{ 359, 5, 14, 6, 0, -10 }, // 0x3b ';' U+003B
{ 368, 10, 9, 11, 1, -9 }, // 0x3c '<' U+003C
{ 380, 9, 8, 11, 1, -9 }, // 0x3d '=' U+003D
{ 389, 9, 9, 11, 1, -9 }, // 0x3e '>' U+003E
{ 400, 8, 16, 9, 0, -15 }, // 0x3f '?' U+003F
{ 416, 17, 17, 19, 1, -13 }, // 0x40 '@' U+0040
{ 453, 15, 14, 15, 0, -13 }, // 0x41 'A' U+0041
{ 480, 10, 14, 13, 2, -13 }, // 0x42 'B' U+0042
{ 498, 11, 14, 13, 1, -13 }, // 0x43 'C' U+0043
{ 518, 12, 14, 15, 2, -13 }, // 0x44 'D' U+0044
{ 539, 9, 14, 12, 2, -13 }, // 0x45 'E' U+0045
{ 555, 8, 14, 11, 2, -13 }, // 0x46 'F' U+0046
{ 569, 11, 14, 14, 1, -13 }, // 0x47 'G' U+0047
{ 589, 11, 14, 15, 2, -13 }, // 0x48 'H' U+0048
{ 609, 3, 14, 7, 2, -13 }, // 0x49 'I' U+0049
{ 615, 9, 14, 11, 0, -13 }, // 0x4a 'J' U+004A
{ 631, 12, 14, 14, 2, -13 }, // 0x4b 'K' U+004B
{ 652, 9, 14, 11, 2, -13 }, // 0x4c 'L' U+004C
{ 668, 15, 14, 17, 1, -13 }, // 0x4d 'M' U+004D
{ 695, 11, 14, 15, 2, -13 }, // 0x4e 'N' U+004E
{ 715, 14, 14, 16, 1, -13 }, // 0x4f 'O' U+004F
{ 740, 10, 14, 13, 2, -13 }, // 0x50 'P' U+0050
{ 758, 14, 17, 16, 1, -13 }, // 0x51 'Q' U+0051
{ 788, 11, 14, 13, 2, -13 }, // 0x52 'R' U+0052
{ 808, 10, 14, 12, 1, -13 }, // 0x53 'S' U+0053
{ 826, 11, 14, 11, 0, -13 }, // 0x54 'T' U+0054
{ 846, 11, 14, 15, 2, -13 }, // 0x55 'U' U+0055
{ 866, 15, 14, 15, 0, -13 }, // 0x56 'V' U+0056
{ 893, 19, 14, 19, 0, -13 }, // 0x57 'W' U+0057
{ 927, 14, 14, 14, 0, -13 }, // 0x58 'X' U+0058
{ 952, 13, 14, 13, 0, -13 }, // 0x59 'Y' U+0059
{ 975, 10, 14, 12, 1, -13 }, // 0x5a 'Z' U+005A
{ 993, 5, 20, 7, 2, -15 }, // 0x5b '[' U+005B
{ 1006, 10, 20, 9, -1, -15 }, // 0x5c '\' U+005C
{ 1031, 5, 20, 7, 0, -15 }, // 0x5d ']' U+005D
{ 1044, 11, 8, 11, 0, -13 }, // 0x5e '^' U+005E
{ 1055, 10, 2, 10, 0, 3 }, // 0x5f '_' U+005F
{ 1058, 5, 4, 6, 1, -15 }, // 0x60 '`' U+0060
{ 1061, 9, 11, 11, 1, -10 }, // 0x61 'a' U+0061
{ 1074, 10, 16, 12, 1, -15 }, // 0x62 'b' U+0062
{ 1094, 8, 11, 10, 1, -10 }, // 0x63 'c' U+0063
{ 1105, 10, 16, 12, 1, -15 }, // 0x64 'd' U+0064
{ 1125, 10, 11, 12, 1, -10 }, // 0x65 'e' U+0065
{ 1139, 8, 16, 8, 1, -15 }, // 0x66 'f' U+0066
{ 1155, 10, 15, 12, 1, -10 }, // 0x67 'g' U+0067
{ 1174, 10, 16, 12, 1, -15 }, // 0x68 'h' U+0068
{ 1194, 3, 16, 5, 1, -15 }, // 0x69 'i' U+0069
{ 1200, 6, 20, 5, -2, -15 }, // 0x6a 'j' U+006A
{ 1215, 11, 16, 12, 1, -15 }, // 0x6b 'k' U+006B
{ 1237, 5, 16, 6, 1, -15 }, // 0x6c 'l' U+006C
{ 1247, 15, 11, 17, 1, -10 }, // 0x6d 'm' U+006D
{ 1268, 10, 11, 12, 1, -10 }, // 0x6e 'n' U+006E
{ 1282, 10, 11, 12, 1, -10 }, // 0x6f 'o' U+006F
{ 1296, 10, 15, 12, 1, -10 }, // 0x70 'p' U+0070
{ 1315, 10, 15, 12, 1, -10 }, // 0x71 'q' U+0071
{ 1334, 7, 11, 8, 1, -10 }, // 0x72 'r' U+0072
{ 1344, 8, 11, 10, 1, -10 }, // 0x73 's' U+0073
{ 1355, 7, 14, 9, 1, -13 }, // 0x74 't' U+0074
{ 1368, 10, 11, 12, 1, -10 }, // 0x75 'u' U+0075
{ 1382, 11, 11, 11, 0, -10 }, // 0x76 'v' U+0076
{ 1398, 15, 11, 15, 0, -10 }, // 0x77 'w' U+0077
{ 1419, 11, 11, 11, 0, -10 }, // 0x78 'x' U+0078
{ 1435, 11, 15, 11, 0, -10 }, // 0x79 'y' U+0079
{ 1456, 8, 11, 10, 1, -10 }, // 0x7a 'z' U+007A
{ 1467, 7, 20, 8, 1, -15 }, // 0x7b '{' U+007B
{ 1485, 3, 20, 7, 2, -15 }, // 0x7c '|' U+007C
{ 1493, 7, 20, 8, 0, -15 }, // 0x7d '}' U+007D
{ 1511, 10, 5, 11, 1, -8 }, // 0x7e '~' U+007E
{ 1518, 8, 15, 10, 1, -14 }, // 0x7f 'REPLACEMENT CHARACTER *' U+2370
{ 1533, 1, 1, 5, 0, 0 }, // 0x80 'NO-BREAK SPACE' U+00A0
{ 1534, 3, 15, 5, 1, -10 }, // 0x81 'INVERTED EXCLAMATION MARK' U+00A1
{ 1540, 8, 16, 11, 1, -13 }, // 0x82 'CENT SIGN' U+00A2
{ 1556, 10, 14, 11, 0, -13 }, // 0x83 'POUND SIGN' U+00A3
{ 1574, 10, 10, 11, 1, -11 }, // 0x84 'CURRENCY SIGN' U+00A4
{ 1587, 11, 14, 11, 0, -13 }, // 0x85 'YEN SIGN' U+00A5
{ 1607, 3, 20, 6, 2, -15 }, // 0x86 'BROKEN BAR' U+00A6
{ 1615, 8, 18, 10, 1, -13 }, // 0x87 'SECTION SIGN' U+00A7
{ 1633, 7, 3, 11, 2, -14 }, // 0x88 'DIAERESIS' U+00A8
{ 1636, 14, 14, 16, 1, -13 }, // 0x89 'COPYRIGHT SIGN' U+00A9
{ 1661, 7, 8, 8, 0, -13 }, // 0x8a 'FEMININE ORDINAL INDICATOR' U+00AA
{ 1668, 10, 9, 12, 0, -9 }, // 0x8b 'LEFT-POINTING DOUBLE ANGLE QUOTATION MARK' U+00AB
{ 1680, 9, 8, 11, 1, -8 }, // 0x8c 'NOT SIGN' U+00AC
{ 1689, 6, 3, 8, 1, -7 }, // 0x8d 'SOFT HYPHEN' U+00AD
{ 1692, 14, 14, 16, 1, -13 }, // 0x8e 'REGISTERED SIGN' U+00AE
{ 1717, 6, 2, 8, 1, -13 }, // 0x8f 'MACRON' U+00AF
{ 1719, 6, 6, 6, 0, -15 }, // 0x90 'DEGREE SIGN' U+00B0
{ 1724, 11, 15, 13, 1, -14 }, // 0x91 'PLUS-MINUS SIGN' U+00B1
{ 1745, 6, 8, 7, 0, -13 }, // 0x92 'SUPERSCRIPT TWO' U+00B2
{ 1751, 6, 8, 7, 0, -13 }, // 0x93 'SUPERSCRIPT THREE' U+00B3
{ 1757, 5, 4, 6, 0, -15 }, // 0x94 'ACUTE ACCENT' U+00B4
{ 1760, 10, 15, 12, 1, -10 }, // 0x95 'MICRO SIGN' U+00B5
{ 1779, 12, 18, 14, 1, -13 }, // 0x96 'PILCROW SIGN' U+00B6
{ 1806, 4, 4, 6, 1, -7 }, // 0x97 'MIDDLE DOT' U+00B7
{ 1808, 4, 4, 7, 1, 1 }, // 0x98 'CEDILLA' U+00B8
{ 1810, 4, 8, 7, 1, -13 }, // 0x99 'SUPERSCRIPT ONE' U+00B9
{ 1814, 8, 8, 10, 1, -13 }, // 0x9a 'MASCULINE ORDINAL INDICATOR' U+00BA
{ 1822, 10, 9, 12, 2, -9 }, // 0x9b 'RIGHT-POINTING DOUBLE ANGLE QUOTATION MARK' U+00BB
{ 1834, 16, 14, 18, 1, -13 }, // 0x9c 'VULGAR FRACTION ONE QUARTER' U+00BC
{ 1862, 15, 14, 18, 1, -13 }, // 0x9d 'VULGAR FRACTION ONE HALF' U+00BD
{ 1889, 17, 14, 18, 0, -13 }, // 0x9e 'VULGAR FRACTION THREE QUARTERS' U+00BE
{ 1919, 8, 15, 9, 1, -10 }, // 0x9f 'INVERTED QUESTION MARK' U+00BF
{ 1934, 15, 19, 15, 0, -18 }, // 0xa0 'LATIN CAPITAL LETTER A WITH GRAVE' U+00C0
{ 1970, 15, 19, 15, 0, -18 }, // 0xa1 'LATIN CAPITAL LETTER A WITH ACUTE' U+00C1
{ 2006, 15, 20, 15, 0, -19 }, // 0xa2 'LATIN CAPITAL LETTER A WITH CIRCUMFLEX' U+00C2
{ 2044, 15, 18, 15, 0, -17 }, // 0xa3 'LATIN CAPITAL LETTER A WITH TILDE' U+00C3
{ 2078, 15, 18, 15, 0, -17 }, // 0xa4 'LATIN CAPITAL LETTER A WITH DIAERESIS' U+00C4
{ 2112, 15, 18, 15, 0, -17 }, // 0xa5 'LATIN CAPITAL LETTER A WITH RING ABOVE' U+00C5
{ 2146, 19, 14, 20, 0, -13 }, // 0xa6 'LATIN CAPITAL LETTER AE' U+00C6
{ 2180, 11, 18, 13, 1, -13 }, // 0xa7 'LATIN CAPITAL LETTER C WITH CEDILLA' U+00C7
{ 2205, 9, 19, 12, 2, -18 }, // 0xa8 'LATIN CAPITAL LETTER E WITH GRAVE' U+00C8
{ 2227, 9, 19, 12, 2, -18 }, // 0xa9 'LATIN CAPITAL LETTER E WITH ACUTE' U+00C9
{ 2249, 9, 20, 12, 2, -19 }, // 0xaa 'LATIN CAPITAL LETTER E WITH CIRCUMFLEX' U+00CA
{ 2272, 9, 18, 12, 2, -17 }, // 0xab 'LATIN CAPITAL LETTER E WITH DIAERESIS' U+00CB
{ 2293, 5, 19, 7, 1, -18 }, // 0xac 'LATIN CAPITAL LETTER I WITH GRAVE' U+00CC
{ 2305, 5, 19, 7, 1, -18 }, // 0xad 'LATIN CAPITAL LETTER I WITH ACUTE' U+00CD
{ 2317, 7, 20, 7, 0, -19 }, // 0xae 'LATIN CAPITAL LETTER I WITH CIRCUMFLEX' U+00CE
{ 2335, 7, 18, 7, 0, -17 }, // 0xaf 'LATIN CAPITAL LETTER I WITH DIAERESIS' U+00CF
{ 2351, 14, 14, 15, 0, -13 }, // 0xb0 'LATIN CAPITAL LETTER ETH' U+00D0
{ 2376, 11, 18, 15, 2, -17 }, // 0xb1 'LATIN CAPITAL LETTER N WITH TILDE' U+00D1
{ 2401, 14, 19, 16, 1, -18 }, // 0xb2 'LATIN CAPITAL LETTER O WITH GRAVE' U+00D2
{ 2435, 14, 19, 16, 1, -18 }, // 0xb3 'LATIN CAPITAL LETTER O WITH ACUTE' U+00D3
{ 2469, 14, 20, 16, 1, -19 }, // 0xb4 'LATIN CAPITAL LETTER O WITH CIRCUMFLEX' U+00D4
{ 2504, 14, 18, 16, 1, -17 }, // 0xb5 'LATIN CAPITAL LETTER O WITH TILDE' U+00D5
{ 2536, 14, 18, 16, 1, -17 }, // 0xb6 'LATIN CAPITAL LETTER O WITH DIAERESIS' U+00D6
{ 2568, 9, 9, 11, 1, -10 }, // 0xb7 'MULTIPLICATION SIGN' U+00D7
{ 2579, 14, 16, 16, 1, -14 }, // 0xb8 'LATIN CAPITAL LETTER O WITH STROKE' U+00D8
{ 2607, 11, 19, 15, 2, -18 }, // 0xb9 'LATIN CAPITAL LETTER U WITH GRAVE' U+00D9
{ 2634, 11, 19, 15, 2, -18 }, // 0xba 'LATIN CAPITAL LETTER U WITH ACUTE' U+00DA
{ 2661, 11, 20, 15, 2, -19 }, // 0xbb 'LATIN CAPITAL LETTER U WITH CIRCUMFLEX' U+00DB
{ 2689, 11, 18, 15, 2, -17 }, // 0xbc 'LATIN CAPITAL LETTER U WITH DIAERESIS' U+00DC
{ 2714, 13, 19, 13, 0, -18 }, // 0xbd 'LATIN CAPITAL LETTER Y WITH ACUTE' U+00DD
{ 2745, 10, 14, 13, 2, -13 }, // 0xbe 'LATIN CAPITAL LETTER THORN' U+00DE
{ 2763, 11, 16, 13, 1, -15 }, // 0xbf 'LATIN SMALL LETTER SHARP S' U+00DF
{ 2785, 9, 16, 11, 1, -15 }, // 0xc0 'LATIN SMALL LETTER A WITH GRAVE' U+00E0
{ 2803, 9, 16, 11, 1, -15 }, // 0xc1 'LATIN SMALL LETTER A WITH ACUTE' U+00E1
{ 2821, 9, 17, 11, 1, -16 }, // 0xc2 'LATIN SMALL LETTER A WITH CIRCUMFLEX' U+00E2
{ 2841, 9, 15, 11, 1, -14 }, // 0xc3 'LATIN SMALL LETTER A WITH TILDE' U+00E3
{ 2858, 9, 15, 11, 1, -14 }, // 0xc4 'LATIN SMALL LETTER A WITH DIAERESIS' U+00E4
{ 2875, 9, 17, 11, 1, -16 }, // 0xc5 'LATIN SMALL LETTER A WITH RING ABOVE' U+00E5
{ 2895, 15, 11, 17, 1, -10 }, // 0xc6 'LATIN SMALL LETTER AE' U+00E6
{ 2916, 8, 15, 10, 1, -10 }, // 0xc7 'LATIN SMALL LETTER C WITH CEDILLA' U+00E7
{ 2931, 10, 16, 12, 1, -15 }, // 0xc8 'LATIN SMALL LETTER E WITH GRAVE' U+00E8
{ 2951, 10, 16, 12, 1, -15 }, // 0xc9 'LATIN SMALL LETTER E WITH ACUTE' U+00E9
{ 2971, 10, 17, 12, 1, -16 }, // 0xca 'LATIN SMALL LETTER E WITH CIRCUMFLEX' U+00EA
{ 2993, 10, 15, 12, 1, -14 }, // 0xcb 'LATIN SMALL LETTER E WITH DIAERESIS' U+00EB
{ 3012, 5, 16, 5, 0, -15 }, // 0xcc 'LATIN SMALL LETTER I WITH GRAVE' U+00EC
{ 3022, 5, 16, 5, 0, -15 }, // 0xcd 'LATIN SMALL LETTER I WITH ACUTE' U+00ED
{ 3032, 7, 17, 5, -1, -16 }, // 0xce 'LATIN SMALL LETTER I WITH CIRCUMFLEX' U+00EE
{ 3047, 7, 15, 5, -1, -14 }, // 0xcf 'LATIN SMALL LETTER I WITH DIAERESIS' U+00EF
{ 3061, 10, 16, 12, 1, -15 }, // 0xd0 'LATIN SMALL LETTER ETH' U+00F0
{ 3081, 10, 15, 12, 1, -14 }, // 0xd1 'LATIN SMALL LETTER N WITH TILDE' U+00F1
{ 3100, 10, 16, 12, 1, -15 }, // 0xd2 'LATIN SMALL LETTER O WITH GRAVE' U+00F2
{ 3120, 10, 16, 12, 1, -15 }, // 0xd3 'LATIN SMALL LETTER O WITH ACUTE' U+00F3
{ 3140, 10, 17, 12, 1, -16 }, // 0xd4 'LATIN SMALL LETTER O WITH CIRCUMFLEX' U+00F4
{ 3162, 10, 15, 12, 1, -14 }, // 0xd5 'LATIN SMALL LETTER O WITH TILDE' U+00F5
{ 3181, 10, 15, 12, 1, -14 }, // 0xd6 'LATIN SMALL LETTER O WITH DIAERESIS' U+00F6
{ 3200, 9, 11, 11, 1, -10 }, // 0xd7 'DIVISION SIGN' U+00F7
{ 3213, 10, 13, 12, 1, -11 }, // 0xd8 'LATIN SMALL LETTER O WITH STROKE' U+00F8
{ 3230, 10, 16, 12, 1, -15 }, // 0xd9 'LATIN SMALL LETTER U WITH GRAVE' U+00F9
{ 3250, 10, 16, 12, 1, -15 }, // 0xda 'LATIN SMALL LETTER U WITH ACUTE' U+00FA
{ 3270, 10, 17, 12, 1, -16 }, // 0xdb 'LATIN SMALL LETTER U WITH CIRCUMFLEX' U+00FB
{ 3292, 10, 15, 12, 1, -14 }, // 0xdc 'LATIN SMALL LETTER U WITH DIAERESIS' U+00FC
{ 3311, 11, 20, 11, 0, -15 }, // 0xdd 'LATIN SMALL LETTER Y WITH ACUTE' U+00FD
{ 3339, 10, 20, 12, 1, -15 }, // 0xde 'LATIN SMALL LETTER THORN' U+00FE
{ 3364, 11, 19, 11, 0, -14 } }; // 0xdf 'LATIN SMALL LETTER Y WITH DIAERESIS' U+000FF
const GFXfont Ubuntu_Bold10pt8b PROGMEM = {
(uint8_t *)Ubuntu_Bold10pt8bBitmaps,
(GFXglyph *)Ubuntu_Bold10pt8bGlyphs,
0x20, 0xDF, 23 };
// Approx. 4742 bytes

View File

@@ -0,0 +1,611 @@
const uint8_t Ubuntu_Bold12pt8bBitmaps[] PROGMEM = {
0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x06, 0xFF, 0x60, 0xF7, 0xFB,
0xFD, 0xFE, 0xFF, 0x7F, 0xBF, 0xDE, 0x0F, 0x3C, 0x1E, 0x78, 0x3C, 0xF0,
0xF3, 0xCF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x8F, 0x3C, 0x3E, 0xF8, 0x79,
0xE3, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xE7, 0x9E, 0x1E, 0x78, 0x3C, 0xF0,
0x79, 0xE0, 0x0E, 0x00, 0xE0, 0x0E, 0x01, 0xFC, 0x7F, 0xE7, 0xFE, 0xF0,
0x4F, 0x00, 0xF0, 0x0F, 0xF0, 0x7F, 0xC3, 0xFE, 0x07, 0xF0, 0x1F, 0x00,
0xF6, 0x0F, 0xFF, 0xEF, 0xFC, 0x7F, 0x80, 0xE0, 0x0E, 0x00, 0xE0, 0x3E,
0x0F, 0x07, 0xF0, 0xE0, 0xF7, 0x9E, 0x0E, 0x39, 0xC0, 0xE3, 0xBC, 0x0E,
0x3B, 0x80, 0xF7, 0xF8, 0x07, 0xF7, 0x00, 0x3E, 0xF7, 0xC0, 0x0E, 0xFE,
0x01, 0xFE, 0xF0, 0x1D, 0xC7, 0x03, 0xDC, 0x70, 0x39, 0xC7, 0x07, 0x9E,
0xF0, 0x70, 0xFE, 0x0F, 0x07, 0xC0, 0x0F, 0x80, 0x1F, 0xC0, 0x3F, 0xE0,
0x38, 0xE0, 0x38, 0xE0, 0x3D, 0xE0, 0x1F, 0xC0, 0x1F, 0x80, 0x3F, 0x9E,
0x7F, 0x9E, 0xF3, 0xDC, 0xF1, 0xFC, 0xF0, 0xF8, 0xF8, 0xF8, 0xFF, 0xFC,
0x7F, 0xFE, 0x1F, 0x9F, 0xFF, 0xFF, 0xFF, 0xF0, 0x08, 0x3C, 0xF1, 0xE7,
0x8F, 0x1E, 0x78, 0xF1, 0xE3, 0xC7, 0x8F, 0x1E, 0x3C, 0x78, 0x78, 0xF1,
0xE1, 0xE3, 0xC3, 0xC2, 0x00, 0x21, 0xE1, 0xE3, 0xC3, 0xC7, 0x8F, 0x0F,
0x1E, 0x3C, 0x78, 0xF1, 0xE3, 0xC7, 0x8F, 0x3C, 0x78, 0xF3, 0xC7, 0x9E,
0x08, 0x00, 0x0E, 0x01, 0xC1, 0x39, 0x7A, 0xFF, 0xFE, 0x1C, 0x06, 0xC1,
0xDC, 0x7B, 0xC2, 0x20, 0x0E, 0x01, 0xC0, 0x38, 0x07, 0x0F, 0xFF, 0xFF,
0xFF, 0xF8, 0x70, 0x0E, 0x01, 0xC0, 0x38, 0x00, 0x7B, 0xDE, 0xF7, 0xFB,
0xCC, 0xFF, 0xFF, 0xF8, 0x6F, 0xF6, 0x00, 0xF0, 0x1F, 0x01, 0xE0, 0x1E,
0x03, 0xC0, 0x3C, 0x03, 0xC0, 0x78, 0x07, 0x80, 0x78, 0x0F, 0x00, 0xF0,
0x0F, 0x01, 0xE0, 0x1E, 0x01, 0xE0, 0x3C, 0x03, 0xC0, 0x3C, 0x07, 0x80,
0x78, 0x0F, 0x80, 0xF0, 0x00, 0x0F, 0x03, 0xFC, 0x7F, 0xE7, 0x9E, 0x70,
0xEF, 0x0F, 0xF0, 0xFF, 0x0F, 0xF0, 0xFF, 0x0F, 0xF0, 0xFF, 0x0F, 0xF0,
0xE7, 0x9E, 0x7F, 0xE3, 0xFC, 0x0F, 0x00, 0x07, 0x1F, 0x7F, 0xFF, 0xEF,
0x4F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F,
0x1F, 0x07, 0xFC, 0xFF, 0xE6, 0x3E, 0x01, 0xE0, 0x1E, 0x03, 0xE0, 0x3C,
0x07, 0x80, 0xF8, 0x3F, 0x03, 0xE0, 0x7C, 0x0F, 0x80, 0xFF, 0xFF, 0xFF,
0xFF, 0xF0, 0x3E, 0x1F, 0xF3, 0xFF, 0x21, 0xE0, 0x3C, 0x07, 0x87, 0xE0,
0xF8, 0x1F, 0xC0, 0x78, 0x07, 0x80, 0xF0, 0x1E, 0x07, 0xFF, 0xF7, 0xFC,
0x7F, 0x00, 0x03, 0xC0, 0x7C, 0x0F, 0xC0, 0xFC, 0x1F, 0xC3, 0xBC, 0x3B,
0xC7, 0x3C, 0x73, 0xCF, 0x3C, 0xFF, 0xFF, 0xFF, 0xFF, 0xF0, 0x3C, 0x03,
0xC0, 0x3C, 0x03, 0xC0, 0x3F, 0xE3, 0xFE, 0x3F, 0xE3, 0x80, 0x38, 0x07,
0x80, 0x7F, 0x07, 0xFC, 0x7F, 0xE0, 0x3F, 0x00, 0xF0, 0x0F, 0x00, 0xF0,
0x1F, 0xFF, 0xEF, 0xFC, 0x7F, 0x00, 0x01, 0xE0, 0xFE, 0x1F, 0xE3, 0xF0,
0x7C, 0x07, 0x80, 0xFF, 0x8F, 0xFE, 0xFF, 0xEF, 0x1F, 0xF0, 0xFF, 0x0F,
0xF0, 0xF7, 0x9F, 0x7F, 0xE3, 0xFC, 0x1F, 0x80, 0xFF, 0xFF, 0xFF, 0xFF,
0xF0, 0x1E, 0x03, 0xE0, 0x7C, 0x07, 0x80, 0x78, 0x0F, 0x00, 0xF0, 0x1F,
0x01, 0xE0, 0x1E, 0x01, 0xE0, 0x3C, 0x03, 0xC0, 0x3C, 0x00, 0x1F, 0x83,
0xFE, 0x7F, 0xEF, 0x9F, 0xF0, 0xFF, 0x0F, 0xF9, 0xF7, 0xFE, 0x3F, 0xC7,
0xFE, 0xF9, 0xFF, 0x0F, 0xF0, 0xFF, 0x8F, 0xFF, 0xE7, 0xFE, 0x1F, 0x80,
0x1F, 0x83, 0xFC, 0x7F, 0xEF, 0x9E, 0xF0, 0xFF, 0x0F, 0xF0, 0xFF, 0x8F,
0x7F, 0xF7, 0xFF, 0x1F, 0xF0, 0x1E, 0x03, 0xE0, 0x7C, 0x7F, 0x87, 0xF0,
0x78, 0x00, 0x6F, 0xF6, 0x00, 0x00, 0x06, 0xFF, 0x60, 0x33, 0xDE, 0x60,
0x00, 0x00, 0x03, 0xDE, 0xF7, 0xBF, 0xDE, 0x60, 0x00, 0x60, 0x3E, 0x1F,
0xFF, 0xFF, 0xFF, 0x8F, 0x00, 0xFF, 0x8F, 0xFF, 0x1F, 0xF0, 0x3E, 0x00,
0x60, 0xFF, 0xFF, 0xFF, 0xFF, 0x80, 0x00, 0x01, 0xFF, 0xFF, 0xFF, 0xFF,
0x60, 0x07, 0xC0, 0xFF, 0x8F, 0xFF, 0x1F, 0xF0, 0x0F, 0x1F, 0xFF, 0xFF,
0xFF, 0x87, 0xC0, 0x60, 0x00, 0x3E, 0x3F, 0xEF, 0xF9, 0x0F, 0x03, 0xC0,
0xF0, 0x7C, 0x1E, 0x0F, 0x83, 0xC1, 0xE0, 0x78, 0x1C, 0x07, 0x00, 0x00,
0x30, 0x1E, 0x07, 0x80, 0xC0, 0x01, 0xFC, 0x00, 0x3F, 0xFC, 0x03, 0xFF,
0xF0, 0x7E, 0x07, 0xC3, 0xC0, 0x0F, 0x3C, 0x7E, 0x39, 0xC7, 0xF8, 0xFE,
0x7F, 0xC7, 0xE7, 0x8E, 0x3F, 0x38, 0x71, 0xF9, 0xC3, 0x8F, 0xCE, 0x1C,
0x7E, 0x78, 0xE7, 0x71, 0xFF, 0xF9, 0xCF, 0xFF, 0x8F, 0x1F, 0xF8, 0x3C,
0x00, 0x01, 0xF8, 0x00, 0x07, 0xFF, 0x80, 0x0F, 0xFE, 0x00, 0x1F, 0xE0,
0x00, 0x03, 0xE0, 0x01, 0xF0, 0x01, 0xFC, 0x00, 0xFE, 0x00, 0x77, 0x00,
0x7B, 0xC0, 0x3D, 0xE0, 0x3C, 0x78, 0x1E, 0x3C, 0x0F, 0x1E, 0x0F, 0xFF,
0x87, 0xFF, 0xC3, 0xFF, 0xE3, 0xC0, 0x79, 0xE0, 0x3D, 0xF0, 0x1E, 0xF0,
0x07, 0x80, 0xFF, 0x87, 0xFF, 0x3F, 0xF9, 0xE3, 0xEF, 0x0F, 0x78, 0x7B,
0xC7, 0xDF, 0xFC, 0xFF, 0xE7, 0xFF, 0xBC, 0x3F, 0xE0, 0xFF, 0x07, 0xF8,
0x7F, 0xFF, 0xDF, 0xFC, 0xFF, 0xC0, 0x07, 0xF0, 0x7F, 0xF3, 0xFF, 0xDF,
0x82, 0x78, 0x03, 0xE0, 0x0F, 0x00, 0x3C, 0x00, 0xF0, 0x03, 0xC0, 0x0F,
0x00, 0x3E, 0x00, 0x78, 0x01, 0xF0, 0x23, 0xFF, 0xC7, 0xFF, 0x07, 0xF0,
0xFF, 0x81, 0xFF, 0xE3, 0xFF, 0xE7, 0x87, 0xEF, 0x03, 0xDE, 0x07, 0xFC,
0x07, 0xF8, 0x0F, 0xF0, 0x1F, 0xE0, 0x3F, 0xC0, 0x7F, 0x81, 0xFF, 0x03,
0xDE, 0x1F, 0xBF, 0xFE, 0x7F, 0xF0, 0xFF, 0x80, 0xFF, 0xEF, 0xFE, 0xFF,
0xEF, 0x00, 0xF0, 0x0F, 0x00, 0xF0, 0x0F, 0xFE, 0xFF, 0xEF, 0xFE, 0xF0,
0x0F, 0x00, 0xF0, 0x0F, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xF0, 0xFF, 0xFF,
0xFF, 0xFF, 0xF8, 0x0F, 0x01, 0xE0, 0x3C, 0x07, 0xFE, 0xFF, 0xDF, 0xFB,
0xC0, 0x78, 0x0F, 0x01, 0xE0, 0x3C, 0x07, 0x80, 0xF0, 0x00, 0x07, 0xF0,
0x7F, 0xF3, 0xFF, 0xDF, 0x02, 0x78, 0x03, 0xE0, 0x0F, 0x00, 0x3C, 0x00,
0xF0, 0x3F, 0xC0, 0xFF, 0x03, 0xFE, 0x0F, 0x78, 0x3D, 0xF0, 0xF3, 0xFF,
0xC7, 0xFF, 0x07, 0xF8, 0xF0, 0x3F, 0xC0, 0xFF, 0x03, 0xFC, 0x0F, 0xF0,
0x3F, 0xC0, 0xFF, 0x03, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x03, 0xFC,
0x0F, 0xF0, 0x3F, 0xC0, 0xFF, 0x03, 0xFC, 0x0F, 0xF0, 0x3C, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF0, 0x01, 0xE0, 0x3C, 0x07, 0x80,
0xF0, 0x1E, 0x03, 0xC0, 0x78, 0x0F, 0x01, 0xE0, 0x3C, 0x07, 0x80, 0xF0,
0x1E, 0x87, 0xDF, 0xF7, 0xFE, 0x3F, 0x00, 0xF0, 0xFF, 0xC7, 0xEF, 0x1F,
0x3C, 0xF8, 0xF7, 0xC3, 0xFE, 0x0F, 0xF0, 0x3F, 0x80, 0xFF, 0x03, 0xFC,
0x0F, 0x78, 0x3D, 0xF0, 0xF3, 0xE3, 0xC7, 0x8F, 0x1F, 0x3C, 0x3E, 0xF0,
0x7C, 0xF0, 0x0F, 0x00, 0xF0, 0x0F, 0x00, 0xF0, 0x0F, 0x00, 0xF0, 0x0F,
0x00, 0xF0, 0x0F, 0x00, 0xF0, 0x0F, 0x00, 0xF0, 0x0F, 0x00, 0xFF, 0xFF,
0xFF, 0xFF, 0xF0, 0x7C, 0x03, 0xCF, 0x80, 0xF9, 0xF0, 0x1F, 0x3F, 0x07,
0xE7, 0xE0, 0xFC, 0xFE, 0x3F, 0xBD, 0xC7, 0x7F, 0xBD, 0xEF, 0xF3, 0xB9,
0xFE, 0x77, 0x3F, 0xCF, 0xE7, 0xF8, 0xF8, 0xFF, 0x1F, 0x1F, 0xE1, 0xC3,
0xFC, 0x38, 0x7F, 0x80, 0x0F, 0xF0, 0x01, 0xE0, 0xF0, 0x3F, 0xE0, 0xFF,
0x83, 0xFF, 0x0F, 0xFE, 0x3F, 0xF8, 0xFF, 0x73, 0xFD, 0xCF, 0xF3, 0xBF,
0xCF, 0xFF, 0x1F, 0xFC, 0x3F, 0xF0, 0xFF, 0xC1, 0xFF, 0x07, 0xFC, 0x0F,
0xF0, 0x3C, 0x07, 0xF0, 0x0F, 0xFE, 0x0F, 0xFF, 0x8F, 0x83, 0xE7, 0x80,
0xF7, 0xC0, 0x7F, 0xC0, 0x1F, 0xE0, 0x0F, 0xF0, 0x07, 0xF8, 0x03, 0xFC,
0x01, 0xFF, 0x01, 0xF7, 0x80, 0xF3, 0xE0, 0xF8, 0xFF, 0xF8, 0x3F, 0xF8,
0x07, 0xF0, 0x00, 0xFF, 0x0F, 0xFC, 0xFF, 0xEF, 0x1F, 0xF0, 0xFF, 0x0F,
0xF0, 0xFF, 0x1F, 0xFF, 0xEF, 0xFC, 0xFF, 0x8F, 0x00, 0xF0, 0x0F, 0x00,
0xF0, 0x0F, 0x00, 0xF0, 0x00, 0x07, 0xF0, 0x0F, 0xFE, 0x0F, 0xFF, 0x8F,
0x83, 0xE7, 0x80, 0xF7, 0xC0, 0x7F, 0xC0, 0x1F, 0xE0, 0x0F, 0xF0, 0x07,
0xF8, 0x03, 0xFC, 0x01, 0xFF, 0x01, 0xF7, 0x80, 0xF3, 0xE0, 0xF8, 0xFF,
0xF8, 0x3F, 0xF8, 0x07, 0xF0, 0x00, 0xF0, 0x00, 0x3E, 0x00, 0x1F, 0xE0,
0x07, 0xF0, 0x00, 0xF0, 0xFF, 0x83, 0xFF, 0x8F, 0xFF, 0x3C, 0x3E, 0xF0,
0x7B, 0xC1, 0xEF, 0x07, 0xBC, 0x3E, 0xFF, 0xF3, 0xFF, 0x8F, 0xFC, 0x3C,
0xF8, 0xF1, 0xF3, 0xC3, 0xCF, 0x0F, 0xBC, 0x1E, 0xF0, 0x7C, 0x1F, 0x87,
0xFE, 0x7F, 0xCF, 0x04, 0xF0, 0x0F, 0x00, 0xFC, 0x07, 0xF8, 0x3F, 0xC0,
0xFE, 0x01, 0xF0, 0x0F, 0x00, 0xF4, 0x1F, 0xFF, 0xEF, 0xFE, 0x3F, 0x80,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xC1, 0xE0, 0x07, 0x80, 0x1E, 0x00, 0x78,
0x01, 0xE0, 0x07, 0x80, 0x1E, 0x00, 0x78, 0x01, 0xE0, 0x07, 0x80, 0x1E,
0x00, 0x78, 0x01, 0xE0, 0x07, 0x80, 0xF0, 0x7F, 0x83, 0xFC, 0x1F, 0xE0,
0xFF, 0x07, 0xF8, 0x3F, 0xC1, 0xFE, 0x0F, 0xF0, 0x7F, 0x83, 0xFC, 0x1F,
0xE0, 0xFF, 0x07, 0xBC, 0x79, 0xFF, 0xC7, 0xFC, 0x0F, 0x80, 0xF0, 0x07,
0xFC, 0x07, 0x9E, 0x03, 0xCF, 0x01, 0xE3, 0xC1, 0xE1, 0xE0, 0xF0, 0xF0,
0x78, 0x3C, 0x78, 0x1E, 0x3C, 0x0F, 0x1E, 0x03, 0xDE, 0x01, 0xEF, 0x00,
0xF7, 0x80, 0x3F, 0x80, 0x1F, 0xC0, 0x0F, 0xE0, 0x03, 0xE0, 0x00, 0xF0,
0x00, 0x1F, 0xE0, 0x00, 0x3F, 0xE0, 0xE0, 0xFB, 0xC3, 0xE1, 0xE7, 0x87,
0xC3, 0xCF, 0x0F, 0x87, 0x9E, 0x1F, 0x8F, 0x3C, 0x77, 0x3E, 0x7C, 0xEE,
0x7C, 0x7B, 0xDE, 0xF0, 0xF7, 0x1D, 0xE1, 0xEE, 0x3B, 0xC3, 0xFC, 0x7F,
0x83, 0xF0, 0x7E, 0x07, 0xE0, 0xFC, 0x0F, 0xC1, 0xF8, 0x1F, 0x01, 0xF0,
0xF8, 0x1F, 0x7C, 0x3E, 0x3C, 0x3C, 0x3E, 0x7C, 0x1F, 0xF8, 0x0F, 0xF0,
0x0F, 0xF0, 0x07, 0xE0, 0x03, 0xC0, 0x07, 0xE0, 0x0F, 0xF0, 0x0F, 0xF0,
0x1F, 0xF8, 0x3E, 0x7C, 0x3C, 0x3C, 0x7C, 0x3E, 0xF8, 0x1F, 0xF8, 0x1F,
0x78, 0x1E, 0x7C, 0x3E, 0x3C, 0x3C, 0x3E, 0x7C, 0x1F, 0xF8, 0x0F, 0xF0,
0x0F, 0xF0, 0x07, 0xE0, 0x07, 0xE0, 0x03, 0xC0, 0x03, 0xC0, 0x03, 0xC0,
0x03, 0xC0, 0x03, 0xC0, 0x03, 0xC0, 0x03, 0xC0, 0xFF, 0xFF, 0xFF, 0xFF,
0xFE, 0x03, 0xE0, 0x1E, 0x01, 0xF0, 0x1F, 0x00, 0xF0, 0x0F, 0x80, 0xF8,
0x07, 0x80, 0x7C, 0x07, 0xC0, 0x3E, 0x03, 0xFF, 0xFF, 0xFF, 0xFF, 0xF8,
0xFF, 0xFF, 0xFF, 0x8F, 0x1E, 0x3C, 0x78, 0xF1, 0xE3, 0xC7, 0x8F, 0x1E,
0x3C, 0x78, 0xF1, 0xE3, 0xC7, 0x8F, 0xFF, 0xFF, 0x80, 0xF0, 0x07, 0x80,
0x78, 0x07, 0x80, 0x3C, 0x03, 0xC0, 0x3C, 0x01, 0xE0, 0x1E, 0x01, 0xE0,
0x0F, 0x00, 0xF0, 0x0F, 0x00, 0x78, 0x07, 0x80, 0x78, 0x03, 0xC0, 0x3C,
0x03, 0xC0, 0x1E, 0x01, 0xE0, 0x1E, 0x00, 0xF0, 0xFF, 0xFF, 0xF8, 0xF1,
0xE3, 0xC7, 0x8F, 0x1E, 0x3C, 0x78, 0xF1, 0xE3, 0xC7, 0x8F, 0x1E, 0x3C,
0x78, 0xFF, 0xFF, 0xFF, 0x80, 0x07, 0x00, 0x7C, 0x03, 0xE0, 0x3F, 0x83,
0xDE, 0x1E, 0xF1, 0xE3, 0xCE, 0x0E, 0xF0, 0x79, 0x01, 0x00, 0xFF, 0xFF,
0xFF, 0xFF, 0xF0, 0x23, 0xC7, 0x8F, 0x08, 0x7F, 0x1F, 0xE7, 0xFC, 0x0F,
0x03, 0xCF, 0xF7, 0xFF, 0xCF, 0xF3, 0xFC, 0xFF, 0xFD, 0xFF, 0x3F, 0x80,
0xF0, 0x0F, 0x00, 0xF0, 0x0F, 0x00, 0xF0, 0x0F, 0x00, 0xFF, 0x8F, 0xFC,
0xFF, 0xEF, 0x1F, 0xF0, 0xFF, 0x0F, 0xF0, 0xFF, 0x0F, 0xF0, 0xFF, 0x1E,
0xFF, 0xEF, 0xFC, 0x7F, 0x00, 0x0F, 0xCF, 0xF7, 0xFD, 0xE0, 0xF0, 0x3C,
0x0F, 0x03, 0xC0, 0xF0, 0x1E, 0x17, 0xFC, 0xFF, 0x1F, 0x80, 0x00, 0xF0,
0x0F, 0x00, 0xF0, 0x0F, 0x00, 0xF0, 0x0F, 0x1F, 0xF3, 0xFF, 0x7F, 0xFF,
0x8F, 0xF0, 0xFF, 0x0F, 0xF0, 0xFF, 0x0F, 0xF0, 0xF7, 0x8F, 0x7F, 0xF3,
0xFF, 0x0F, 0xE0, 0x0F, 0x83, 0xFC, 0x7F, 0xE7, 0x9E, 0xF0, 0xFF, 0x0F,
0xFF, 0xFF, 0xFF, 0xF0, 0x07, 0x82, 0x7F, 0xE3, 0xFE, 0x0F, 0xC0, 0x1F,
0x3F, 0x9F, 0xDF, 0x0F, 0x07, 0x83, 0xFD, 0xFE, 0xFF, 0x78, 0x3C, 0x1E,
0x0F, 0x07, 0x83, 0xC1, 0xE0, 0xF0, 0x78, 0x3C, 0x00, 0x1F, 0xC7, 0xFD,
0xFF, 0xFC, 0xFF, 0x1F, 0xE3, 0xFC, 0x7F, 0x8F, 0xF9, 0xEF, 0xFD, 0xFF,
0x8F, 0xF0, 0x1E, 0x87, 0xDF, 0xF3, 0xFE, 0x7F, 0x00, 0xF0, 0x1E, 0x03,
0xC0, 0x78, 0x0F, 0x01, 0xE0, 0x3F, 0xE7, 0xFE, 0xFF, 0xDE, 0x7F, 0xC7,
0xF8, 0xFF, 0x1F, 0xE3, 0xFC, 0x7F, 0x8F, 0xF1, 0xFE, 0x3F, 0xC7, 0x80,
0xFF, 0xFF, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF0, 0x1E, 0x3C,
0x78, 0xF0, 0x00, 0x07, 0x8F, 0x1E, 0x3C, 0x78, 0xF1, 0xE3, 0xC7, 0x8F,
0x1E, 0x3C, 0x78, 0xFF, 0xFF, 0xBE, 0x00, 0xF0, 0x0F, 0x00, 0xF0, 0x0F,
0x00, 0xF0, 0x0F, 0x00, 0xF1, 0xFF, 0x3E, 0xF7, 0xCF, 0xF8, 0xFF, 0x0F,
0xE0, 0xFF, 0x0F, 0xF8, 0xF7, 0x8F, 0x7C, 0xF3, 0xEF, 0x1E, 0xF1, 0xF0,
0xF3, 0xCF, 0x3C, 0xF3, 0xCF, 0x3C, 0xF3, 0xCF, 0x3C, 0xF3, 0xCF, 0x3E,
0xFD, 0xF3, 0xC0, 0x7F, 0x3F, 0x3F, 0xFF, 0xEF, 0xFF, 0xFB, 0xCF, 0x9F,
0xF1, 0xE3, 0xFC, 0x78, 0xFF, 0x1E, 0x3F, 0xC7, 0x8F, 0xF1, 0xE3, 0xFC,
0x78, 0xFF, 0x1E, 0x3F, 0xC7, 0x8F, 0xF1, 0xE3, 0xC0, 0x7F, 0x1F, 0xFB,
0xFF, 0x79, 0xFF, 0x1F, 0xE3, 0xFC, 0x7F, 0x8F, 0xF1, 0xFE, 0x3F, 0xC7,
0xF8, 0xFF, 0x1E, 0x0F, 0x81, 0xFF, 0x1F, 0xFC, 0xF1, 0xEF, 0x07, 0xF8,
0x3F, 0xC1, 0xFE, 0x0F, 0xF0, 0x7B, 0xC7, 0x9F, 0xFC, 0x7F, 0xC0, 0xF8,
0x00, 0x7F, 0x0F, 0xFC, 0xFF, 0xEF, 0x1E, 0xF0, 0xFF, 0x0F, 0xF0, 0xFF,
0x0F, 0xF0, 0xFF, 0x1F, 0xFF, 0xEF, 0xFC, 0xFF, 0x8F, 0x00, 0xF0, 0x0F,
0x00, 0xF0, 0x00, 0x0F, 0xF1, 0xFF, 0xDF, 0xFE, 0xF0, 0xFF, 0x87, 0xF8,
0x3F, 0xC1, 0xFE, 0x0F, 0xF0, 0x7F, 0xC3, 0xDF, 0xFE, 0x7F, 0xF1, 0xFF,
0x80, 0x3C, 0x01, 0xE0, 0x0F, 0x00, 0x78, 0x3F, 0xFF, 0xFF, 0xFE, 0x0F,
0x07, 0x83, 0xC1, 0xE0, 0xF0, 0x78, 0x3C, 0x1E, 0x0F, 0x00, 0x1F, 0x9F,
0xEF, 0xFB, 0xC0, 0xF0, 0x3F, 0xE7, 0xFC, 0xFF, 0x03, 0xE0, 0xFF, 0xFF,
0xFE, 0xFE, 0x00, 0xF0, 0x78, 0x3C, 0x1E, 0x0F, 0xF7, 0xFB, 0xFD, 0xE0,
0xF0, 0x78, 0x3C, 0x1E, 0x0F, 0x07, 0xC3, 0xFC, 0xFE, 0x3F, 0x00, 0xF1,
0xFE, 0x3F, 0xC7, 0xF8, 0xFF, 0x1F, 0xE3, 0xFC, 0x7F, 0x8F, 0xF1, 0xFF,
0x3D, 0xFF, 0xBF, 0xF1, 0xFC, 0xF0, 0x7F, 0xC7, 0xDE, 0x3C, 0xF1, 0xE7,
0x8F, 0x1E, 0xF0, 0xF7, 0x87, 0xBC, 0x1D, 0xC0, 0xFE, 0x07, 0xF0, 0x1F,
0x00, 0xF8, 0x00, 0xF0, 0xE1, 0xFE, 0x1C, 0x3D, 0xC7, 0xC7, 0x3C, 0xF9,
0xE7, 0x9F, 0x3C, 0xF7, 0xE7, 0x0E, 0xEE, 0xE1, 0xDD, 0xDC, 0x3F, 0x3F,
0x83, 0xE3, 0xE0, 0x7C, 0x7C, 0x0F, 0x07, 0x80, 0xE0, 0xE0, 0xF8, 0xFB,
0xC7, 0x8F, 0x78, 0x7B, 0xC1, 0xFC, 0x07, 0xC0, 0x1C, 0x01, 0xF0, 0x1F,
0xC1, 0xEF, 0x0F, 0x78, 0xFB, 0xEF, 0x8F, 0x80, 0xF0, 0x7F, 0xC7, 0xDE,
0x3C, 0xF1, 0xE7, 0x8F, 0x1E, 0xF0, 0xF7, 0x87, 0xBC, 0x1D, 0xC0, 0xFE,
0x07, 0xF0, 0x1F, 0x00, 0xF8, 0x0F, 0x83, 0xFC, 0x1F, 0xC0, 0xFC, 0x00,
0xFF, 0xFF, 0xFF, 0xFC, 0x3E, 0x0F, 0x87, 0xC3, 0xE0, 0xF8, 0x7C, 0x1F,
0x0F, 0xFF, 0xFF, 0xFF, 0xC0, 0x0F, 0x1F, 0x3F, 0x3C, 0x3C, 0x3C, 0x3C,
0x3C, 0x3C, 0x7C, 0xF8, 0xF0, 0xF8, 0x7C, 0x3C, 0x3C, 0x3C, 0x3C, 0x3C,
0x3C, 0x3F, 0x1F, 0x0F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xF8, 0xF0, 0xF8, 0xFC, 0x3C, 0x3C, 0x3C, 0x3C, 0x3C, 0x3C, 0x3E, 0x1F,
0x0F, 0x1F, 0x3E, 0x3C, 0x3C, 0x3C, 0x3C, 0x3C, 0x3C, 0xFC, 0xF8, 0xF0,
0x38, 0x27, 0xE7, 0xFF, 0xEE, 0x7E, 0x41, 0xC0, 0xFF, 0xE0, 0x18, 0x06,
0x01, 0x80, 0x60, 0x18, 0x06, 0x01, 0x80, 0x60, 0x18, 0x06, 0x01, 0x80,
0x60, 0x18, 0x06, 0x01, 0x80, 0x7F, 0xF0, 0x00, 0x6F, 0xF6, 0x0F, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xF0, 0x07, 0x00, 0xE0, 0x1C, 0x07, 0xF3, 0xFE,
0xFF, 0x9E, 0x07, 0x80, 0xF0, 0x1E, 0x03, 0xC0, 0x3C, 0x17, 0xFE, 0x7F,
0xC3, 0xF8, 0x38, 0x07, 0x00, 0xE0, 0x07, 0xC3, 0xFC, 0x7F, 0x1F, 0x03,
0xC0, 0x78, 0x0F, 0x07, 0xFC, 0xFF, 0x9F, 0xF0, 0xF0, 0x1E, 0x03, 0xC0,
0x78, 0x0F, 0xFB, 0xFF, 0x7F, 0xE0, 0x60, 0xDF, 0xFF, 0xFF, 0xBF, 0xE7,
0x1C, 0xE3, 0x9C, 0x73, 0xFE, 0xFF, 0xEF, 0xF9, 0x83, 0x00, 0xF8, 0x7D,
0xE1, 0xE7, 0x87, 0x8F, 0x3C, 0x3C, 0xF0, 0x7F, 0x81, 0xFE, 0x03, 0xF0,
0x7F, 0xF9, 0xFF, 0xE0, 0x78, 0x01, 0xE0, 0x7F, 0xF9, 0xFF, 0xE0, 0x78,
0x01, 0xE0, 0x07, 0x80, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x0F, 0xFF,
0xFF, 0xFF, 0xFF, 0xF0, 0x1F, 0x9F, 0xFF, 0xFF, 0xC2, 0xF0, 0x3F, 0x07,
0xF1, 0xFE, 0xF7, 0xFC, 0xFF, 0x3F, 0xCF, 0xFB, 0xDF, 0xE3, 0xF8, 0x3F,
0x03, 0xD0, 0xFF, 0xFF, 0xFE, 0x7F, 0x00, 0x61, 0xBC, 0xFF, 0x3D, 0x86,
0x07, 0xF0, 0x0F, 0xFC, 0x0F, 0x07, 0x8E, 0x00, 0xE6, 0x3E, 0x37, 0x3F,
0x9F, 0x3C, 0x07, 0x9C, 0x03, 0xCE, 0x01, 0xE7, 0x00, 0xF3, 0xC0, 0x7C,
0xFE, 0x76, 0x3E, 0x33, 0x80, 0x38, 0xF0, 0x78, 0x3F, 0xF0, 0x03, 0xF0,
0x00, 0x7E, 0x7F, 0x07, 0x1F, 0x7F, 0xF7, 0xE7, 0xFF, 0x7F, 0x18, 0x61,
0xC7, 0x38, 0xE7, 0x9E, 0x71, 0xCF, 0x3C, 0x71, 0xC7, 0x9E, 0x38, 0xE1,
0xC7, 0x18, 0x60, 0xFF, 0xFF, 0xFF, 0xFF, 0xF0, 0x07, 0x00, 0x70, 0x07,
0x00, 0x70, 0x07, 0x00, 0x70, 0xFF, 0xFF, 0xF8, 0x07, 0xF0, 0x0F, 0xFC,
0x0F, 0x07, 0x8E, 0x00, 0xE6, 0x7E, 0x37, 0x3F, 0xDF, 0x18, 0x67, 0x8C,
0x33, 0xC7, 0xF9, 0xE3, 0xF8, 0xF1, 0x8C, 0x7C, 0xC3, 0x76, 0x61, 0xB3,
0x80, 0x38, 0xF0, 0x78, 0x3F, 0xF0, 0x03, 0xF0, 0x00, 0xFF, 0xFF, 0x38,
0xFB, 0x1E, 0x3C, 0x6F, 0x8E, 0x00, 0x0E, 0x01, 0xC0, 0x38, 0x07, 0x0F,
0xFF, 0xFF, 0xFF, 0xF8, 0x70, 0x0E, 0x01, 0xC0, 0x38, 0x00, 0x0F, 0xFF,
0xFF, 0xFF, 0xF8, 0x3E, 0xFF, 0x47, 0x07, 0x0F, 0x3E, 0x78, 0xF0, 0xFF,
0xFF, 0x7C, 0xFE, 0x0E, 0x0E, 0x3C, 0x3F, 0x07, 0x07, 0xFF, 0xFC, 0x10,
0xF7, 0xBC, 0x40, 0xF1, 0xFE, 0x3F, 0xC7, 0xF8, 0xFF, 0x1F, 0xE3, 0xFC,
0x7F, 0x8F, 0xF1, 0xFF, 0x3F, 0xFF, 0xFF, 0xFF, 0xFD, 0xE0, 0x3C, 0x07,
0x80, 0xF0, 0x00, 0x07, 0xF0, 0xFF, 0xF7, 0xFF, 0xFF, 0xE7, 0xFF, 0x9F,
0xFE, 0x7F, 0xF9, 0xFF, 0xE7, 0x7F, 0x9C, 0xFE, 0x70, 0xF9, 0xC1, 0xE7,
0x07, 0x9C, 0x1E, 0x70, 0x79, 0xC1, 0xE7, 0x07, 0x9C, 0x1E, 0x70, 0x79,
0xC1, 0xE7, 0x07, 0x9C, 0x6F, 0xF6, 0x30, 0xE1, 0xBE, 0xF8, 0x1D, 0xFF,
0xD7, 0x1C, 0x71, 0xC7, 0x1C, 0x70, 0x3E, 0x3F, 0xBD, 0xDC, 0x7E, 0x3F,
0x1F, 0x8F, 0xEF, 0x7F, 0x1F, 0x00, 0x61, 0x8F, 0x3C, 0x71, 0xC7, 0x9E,
0x38, 0xE3, 0xCF, 0x38, 0xE7, 0x9E, 0x71, 0xCF, 0x3C, 0x61, 0x80, 0x1C,
0x0F, 0x07, 0xC0, 0xE0, 0xFC, 0x1E, 0x05, 0xC1, 0xC0, 0x1C, 0x3C, 0x01,
0xC3, 0x80, 0x1C, 0x78, 0x01, 0xC7, 0x1E, 0x1C, 0xF3, 0xE1, 0xCE, 0x3E,
0x01, 0xE7, 0xE0, 0x1C, 0xEE, 0x03, 0xDC, 0xE0, 0x39, 0xFF, 0x07, 0x9F,
0xF0, 0x70, 0x0E, 0x0F, 0x00, 0xE0, 0x1C, 0x1E, 0x0F, 0x83, 0x83, 0xF0,
0xF0, 0x2E, 0x1C, 0x01, 0xC7, 0x80, 0x38, 0xE0, 0x07, 0x3C, 0x00, 0xE7,
0x3E, 0x1D, 0xFF, 0xE3, 0xB9, 0x1C, 0x0F, 0x03, 0x81, 0xC0, 0xF0, 0x78,
0x7C, 0x0E, 0x1E, 0x03, 0xC7, 0x80, 0x70, 0xFF, 0x1E, 0x1F, 0xE0, 0x7C,
0x07, 0x87, 0xF0, 0x38, 0x03, 0x83, 0xC0, 0x1C, 0x1C, 0x03, 0xC1, 0xE0,
0x1F, 0x8E, 0x00, 0x1C, 0xF0, 0x00, 0xE7, 0x1E, 0xFF, 0x79, 0xF7, 0xE3,
0x8F, 0x80, 0x3C, 0xFC, 0x01, 0xCE, 0xE0, 0x1E, 0xE7, 0x00, 0xE7, 0xFC,
0x0F, 0x3F, 0xE0, 0x70, 0x0E, 0x07, 0x80, 0x70, 0x0C, 0x07, 0x81, 0xE0,
0x30, 0x00, 0x03, 0x80, 0xE0, 0x78, 0x3C, 0x1F, 0x07, 0x83, 0xC0, 0xF0,
0x3C, 0x2F, 0xFD, 0xFF, 0x3F, 0x00, 0x01, 0x00, 0x03, 0xC0, 0x00, 0xF0,
0x00, 0x3C, 0x00, 0x04, 0x00, 0x00, 0x00, 0x0F, 0x80, 0x07, 0xC0, 0x07,
0xF0, 0x03, 0xF8, 0x01, 0xDC, 0x01, 0xEF, 0x00, 0xF7, 0x80, 0xF1, 0xE0,
0x78, 0xF0, 0x3C, 0x78, 0x3F, 0xFE, 0x1F, 0xFF, 0x0F, 0xFF, 0x8F, 0x01,
0xE7, 0x80, 0xF7, 0xC0, 0x7B, 0xC0, 0x1E, 0x00, 0x80, 0x00, 0xF0, 0x00,
0xF0, 0x00, 0xF0, 0x00, 0x20, 0x00, 0x00, 0x00, 0x0F, 0x80, 0x07, 0xC0,
0x07, 0xF0, 0x03, 0xF8, 0x01, 0xDC, 0x01, 0xEF, 0x00, 0xF7, 0x80, 0xF1,
0xE0, 0x78, 0xF0, 0x3C, 0x78, 0x3F, 0xFE, 0x1F, 0xFF, 0x0F, 0xFF, 0x8F,
0x01, 0xE7, 0x80, 0xF7, 0xC0, 0x7B, 0xC0, 0x1E, 0x00, 0x80, 0x00, 0xE0,
0x00, 0xF8, 0x00, 0xEE, 0x00, 0x22, 0x00, 0x00, 0x00, 0x0F, 0x80, 0x07,
0xC0, 0x07, 0xF0, 0x03, 0xF8, 0x01, 0xDC, 0x01, 0xEF, 0x00, 0xF7, 0x80,
0xF1, 0xE0, 0x78, 0xF0, 0x3C, 0x78, 0x3F, 0xFE, 0x1F, 0xFF, 0x0F, 0xFF,
0x8F, 0x01, 0xE7, 0x80, 0xF7, 0xC0, 0x7B, 0xC0, 0x1E, 0x03, 0x10, 0x03,
0xFC, 0x03, 0xFC, 0x00, 0x8C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0F, 0x80,
0x07, 0xC0, 0x07, 0xF0, 0x03, 0xF8, 0x01, 0xDC, 0x01, 0xEF, 0x00, 0xF7,
0x80, 0xF1, 0xE0, 0x78, 0xF0, 0x3C, 0x78, 0x3F, 0xFE, 0x1F, 0xFF, 0x0F,
0xFF, 0x8F, 0x01, 0xE7, 0x80, 0xF7, 0xC0, 0x7B, 0xC0, 0x1E, 0x0C, 0x30,
0x0F, 0x3C, 0x07, 0x9E, 0x01, 0x86, 0x00, 0x00, 0x00, 0x1F, 0x00, 0x0F,
0x80, 0x0F, 0xE0, 0x07, 0xF0, 0x03, 0xB8, 0x03, 0xDE, 0x01, 0xEF, 0x01,
0xE3, 0xC0, 0xF1, 0xE0, 0x78, 0xF0, 0x7F, 0xFC, 0x3F, 0xFE, 0x1F, 0xFF,
0x1E, 0x03, 0xCF, 0x01, 0xEF, 0x80, 0xF7, 0x80, 0x3C, 0x03, 0xE0, 0x03,
0xF8, 0x01, 0x8C, 0x00, 0xC6, 0x00, 0x3E, 0x00, 0x1F, 0x00, 0x0F, 0x80,
0x0F, 0xE0, 0x07, 0x70, 0x07, 0xB8, 0x03, 0xDE, 0x01, 0xEF, 0x01, 0xE3,
0xC0, 0xF1, 0xE0, 0x78, 0xF0, 0x7F, 0xFC, 0x3F, 0xFE, 0x3F, 0xFF, 0x1E,
0x03, 0xCF, 0x01, 0xEF, 0x80, 0xF7, 0x80, 0x3C, 0x00, 0x7F, 0xFC, 0x01,
0xFF, 0xF8, 0x03, 0xFF, 0xF0, 0x0F, 0xF8, 0x00, 0x1E, 0xF0, 0x00, 0x7D,
0xE0, 0x00, 0xF3, 0xC0, 0x03, 0xE7, 0xFE, 0x07, 0x8F, 0xFC, 0x1F, 0x1F,
0xF8, 0x7F, 0xFC, 0x00, 0xFF, 0xF8, 0x03, 0xFF, 0xF0, 0x07, 0x81, 0xE0,
0x1F, 0x03, 0xFF, 0xBC, 0x07, 0xFF, 0xF8, 0x0F, 0xFE, 0x07, 0xF0, 0x7F,
0xF3, 0xFF, 0xDF, 0x82, 0x78, 0x03, 0xE0, 0x0F, 0x00, 0x3C, 0x00, 0xF0,
0x03, 0xC0, 0x0F, 0x00, 0x3E, 0x00, 0x78, 0x01, 0xF0, 0x23, 0xFF, 0xC7,
0xFF, 0x03, 0xF8, 0x07, 0x00, 0x1E, 0x00, 0x38, 0x03, 0xE0, 0x0F, 0x00,
0x04, 0x01, 0xE0, 0x0F, 0x00, 0x78, 0x01, 0x00, 0x00, 0xFF, 0xEF, 0xFE,
0xFF, 0xEF, 0x00, 0xF0, 0x0F, 0x00, 0xF0, 0x0F, 0xFE, 0xFF, 0xEF, 0xFE,
0xF0, 0x0F, 0x00, 0xF0, 0x0F, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xF0, 0x02,
0x00, 0x78, 0x0F, 0x01, 0xE0, 0x08, 0x00, 0x00, 0xFF, 0xEF, 0xFE, 0xFF,
0xEF, 0x00, 0xF0, 0x0F, 0x00, 0xF0, 0x0F, 0xFE, 0xFF, 0xEF, 0xFE, 0xF0,
0x0F, 0x00, 0xF0, 0x0F, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xF0, 0x04, 0x00,
0xE0, 0x1F, 0x03, 0xB8, 0x11, 0x00, 0x00, 0xFF, 0xEF, 0xFE, 0xFF, 0xEF,
0x00, 0xF0, 0x0F, 0x00, 0xF0, 0x0F, 0xFE, 0xFF, 0xEF, 0xFE, 0xF0, 0x0F,
0x00, 0xF0, 0x0F, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xF0, 0x30, 0xC7, 0x9E,
0x79, 0xE3, 0x0C, 0x00, 0x0F, 0xFE, 0xFF, 0xEF, 0xFE, 0xF0, 0x0F, 0x00,
0xF0, 0x0F, 0x00, 0xFF, 0xEF, 0xFE, 0xFF, 0xEF, 0x00, 0xF0, 0x0F, 0x00,
0xF0, 0x0F, 0xFF, 0xFF, 0xFF, 0xFF, 0x23, 0xC7, 0x8F, 0x08, 0x07, 0x9E,
0x79, 0xE7, 0x9E, 0x79, 0xE7, 0x9E, 0x79, 0xE7, 0x9E, 0x79, 0xE7, 0x80,
0x10, 0xF7, 0xBC, 0x40, 0x07, 0x9E, 0x79, 0xE7, 0x9E, 0x79, 0xE7, 0x9E,
0x79, 0xE7, 0x9E, 0x79, 0xE7, 0x80, 0x10, 0x71, 0xF7, 0x74, 0x40, 0x1E,
0x3C, 0x78, 0xF1, 0xE3, 0xC7, 0x8F, 0x1E, 0x3C, 0x78, 0xF1, 0xE3, 0xC7,
0x8F, 0x1E, 0x00, 0x61, 0xBC, 0xFF, 0x3D, 0x86, 0x00, 0x07, 0x81, 0xE0,
0x78, 0x1E, 0x07, 0x81, 0xE0, 0x78, 0x1E, 0x07, 0x81, 0xE0, 0x78, 0x1E,
0x07, 0x81, 0xE0, 0x78, 0x1E, 0x07, 0x80, 0x3F, 0xE0, 0x1F, 0xFE, 0x0F,
0xFF, 0x87, 0x87, 0xE3, 0xC0, 0xF1, 0xE0, 0x7C, 0xF0, 0x1F, 0xFF, 0x0F,
0xFF, 0x87, 0xFF, 0xC3, 0xCF, 0x01, 0xE7, 0x81, 0xF3, 0xC0, 0xF1, 0xE1,
0xF8, 0xFF, 0xF8, 0x7F, 0xF0, 0x3F, 0xE0, 0x00, 0x06, 0x20, 0x3F, 0xC1,
0xFE, 0x02, 0x30, 0x00, 0x00, 0x00, 0x0F, 0x03, 0xFE, 0x0F, 0xF8, 0x3F,
0xF0, 0xFF, 0xE3, 0xFF, 0x8F, 0xF7, 0x3F, 0xDC, 0xFF, 0x3B, 0xFC, 0xFF,
0xF1, 0xFF, 0xC3, 0xFF, 0x0F, 0xFC, 0x1F, 0xF0, 0x7F, 0xC0, 0xFF, 0x03,
0xC0, 0x01, 0x00, 0x03, 0xC0, 0x00, 0xF0, 0x00, 0x3C, 0x00, 0x04, 0x00,
0x00, 0x00, 0x1F, 0xC0, 0x3F, 0xF8, 0x3F, 0xFE, 0x3E, 0x0F, 0x9E, 0x03,
0xDF, 0x01, 0xFF, 0x00, 0x7F, 0x80, 0x3F, 0xC0, 0x1F, 0xE0, 0x0F, 0xF0,
0x07, 0xFC, 0x07, 0xDE, 0x03, 0xCF, 0x83, 0xE3, 0xFF, 0xE0, 0xFF, 0xE0,
0x1F, 0xC0, 0x00, 0x40, 0x00, 0x78, 0x00, 0x78, 0x00, 0x78, 0x00, 0x10,
0x00, 0x00, 0x00, 0x1F, 0xC0, 0x3F, 0xF8, 0x3F, 0xFE, 0x3E, 0x0F, 0x9E,
0x03, 0xDF, 0x01, 0xFF, 0x00, 0x7F, 0x80, 0x3F, 0xC0, 0x1F, 0xE0, 0x0F,
0xF0, 0x07, 0xFC, 0x07, 0xDE, 0x03, 0xCF, 0x83, 0xE3, 0xFF, 0xE0, 0xFF,
0xE0, 0x1F, 0xC0, 0x00, 0x80, 0x00, 0xE0, 0x00, 0xF8, 0x00, 0xEE, 0x00,
0x22, 0x00, 0x00, 0x00, 0x1F, 0xC0, 0x3F, 0xF8, 0x3F, 0xFE, 0x3E, 0x0F,
0x9E, 0x03, 0xDF, 0x01, 0xFF, 0x00, 0x7F, 0x80, 0x3F, 0xC0, 0x1F, 0xE0,
0x0F, 0xF0, 0x07, 0xFC, 0x07, 0xDE, 0x03, 0xCF, 0x83, 0xE3, 0xFF, 0xE0,
0xFF, 0xE0, 0x1F, 0xC0, 0x03, 0x10, 0x03, 0xFC, 0x03, 0xFC, 0x00, 0x8C,
0x00, 0x00, 0x00, 0x00, 0x00, 0x1F, 0xC0, 0x3F, 0xF8, 0x3F, 0xFE, 0x3E,
0x0F, 0x9E, 0x03, 0xDF, 0x01, 0xFF, 0x00, 0x7F, 0x80, 0x3F, 0xC0, 0x1F,
0xE0, 0x0F, 0xF0, 0x07, 0xFC, 0x07, 0xDE, 0x03, 0xCF, 0x83, 0xE3, 0xFF,
0xE0, 0xFF, 0xE0, 0x1F, 0xC0, 0x0C, 0x30, 0x0F, 0x3C, 0x07, 0x9E, 0x01,
0x86, 0x00, 0x00, 0x00, 0x3F, 0x80, 0x7F, 0xF0, 0x7F, 0xFC, 0x7C, 0x1F,
0x3C, 0x07, 0xBE, 0x03, 0xFE, 0x00, 0xFF, 0x00, 0x7F, 0x80, 0x3F, 0xC0,
0x1F, 0xE0, 0x0F, 0xF8, 0x0F, 0xBC, 0x07, 0x9F, 0x07, 0xC7, 0xFF, 0xC1,
0xFF, 0xC0, 0x3F, 0x80, 0x20, 0x47, 0x0E, 0xF9, 0xF7, 0xFE, 0x1F, 0x80,
0xF0, 0x1F, 0x83, 0xFC, 0xF9, 0xF7, 0x0E, 0x20, 0x40, 0x00, 0x02, 0x03,
0xFB, 0x87, 0xFF, 0xC7, 0xFF, 0xC7, 0xC3, 0xF3, 0xC1, 0xFB, 0xE1, 0xFF,
0xE1, 0xEF, 0xF0, 0xE7, 0xF8, 0xE3, 0xFC, 0xE1, 0xFE, 0xF0, 0xFF, 0xF0,
0xFB, 0xF0, 0x79, 0xF8, 0x7C, 0x7F, 0xFC, 0x7F, 0xFC, 0x3B, 0xF8, 0x08,
0x00, 0x00, 0x04, 0x00, 0xF0, 0x03, 0xC0, 0x0F, 0x00, 0x10, 0x00, 0x03,
0xC1, 0xFE, 0x0F, 0xF0, 0x7F, 0x83, 0xFC, 0x1F, 0xE0, 0xFF, 0x07, 0xF8,
0x3F, 0xC1, 0xFE, 0x0F, 0xF0, 0x7F, 0x83, 0xFC, 0x1E, 0xF1, 0xE7, 0xFF,
0x1F, 0xF0, 0x3E, 0x00, 0x01, 0x00, 0x1E, 0x01, 0xE0, 0x1E, 0x00, 0x40,
0x00, 0x03, 0xC1, 0xFE, 0x0F, 0xF0, 0x7F, 0x83, 0xFC, 0x1F, 0xE0, 0xFF,
0x07, 0xF8, 0x3F, 0xC1, 0xFE, 0x0F, 0xF0, 0x7F, 0x83, 0xFC, 0x1E, 0xF1,
0xE7, 0xFF, 0x1F, 0xF0, 0x3E, 0x00, 0x02, 0x00, 0x38, 0x03, 0xE0, 0x3B,
0x80, 0x88, 0x00, 0x03, 0xC1, 0xFE, 0x0F, 0xF0, 0x7F, 0x83, 0xFC, 0x1F,
0xE0, 0xFF, 0x07, 0xF8, 0x3F, 0xC1, 0xFE, 0x0F, 0xF0, 0x7F, 0x83, 0xFC,
0x1E, 0xF1, 0xE7, 0xFF, 0x1F, 0xF0, 0x3E, 0x00, 0x30, 0xC3, 0xCF, 0x1E,
0x78, 0x61, 0x80, 0x00, 0x78, 0x3F, 0xC1, 0xFE, 0x0F, 0xF0, 0x7F, 0x83,
0xFC, 0x1F, 0xE0, 0xFF, 0x07, 0xF8, 0x3F, 0xC1, 0xFE, 0x0F, 0xF0, 0x7F,
0x83, 0xDE, 0x3C, 0xFF, 0xE3, 0xFE, 0x07, 0xC0, 0x00, 0x40, 0x00, 0xF0,
0x01, 0xE0, 0x03, 0xC0, 0x01, 0x00, 0x00, 0x00, 0xF8, 0x1F, 0x78, 0x1E,
0x7C, 0x3E, 0x3C, 0x3C, 0x3E, 0x7C, 0x1F, 0xF8, 0x0F, 0xF0, 0x0F, 0xF0,
0x07, 0xE0, 0x07, 0xE0, 0x03, 0xC0, 0x03, 0xC0, 0x03, 0xC0, 0x03, 0xC0,
0x03, 0xC0, 0x03, 0xC0, 0x03, 0xC0, 0xF0, 0x0F, 0x00, 0xFF, 0x8F, 0xFC,
0xFF, 0xEF, 0x1F, 0xF0, 0xFF, 0x0F, 0xF0, 0xFF, 0x0F, 0xF1, 0xFF, 0xFE,
0xFF, 0xCF, 0xF0, 0xF0, 0x0F, 0x00, 0xF0, 0x00, 0x1F, 0x03, 0xFC, 0x1F,
0xF1, 0xE7, 0x8F, 0x3C, 0x79, 0xE3, 0xCF, 0x1E, 0xF0, 0xF7, 0x87, 0xBC,
0x3D, 0xF1, 0xEF, 0xCF, 0x1F, 0x78, 0x7F, 0xC1, 0xFE, 0x8F, 0xF7, 0xFF,
0xBF, 0xBD, 0xF8, 0x08, 0x0F, 0x01, 0xE0, 0x3C, 0x02, 0x00, 0x07, 0xF1,
0xFE, 0x7F, 0xC0, 0xF0, 0x3C, 0xFF, 0x7F, 0xFC, 0xFF, 0x3F, 0xCF, 0xFF,
0xDF, 0xF3, 0xF8, 0x04, 0x03, 0xC1, 0xE0, 0xF0, 0x10, 0x00, 0x07, 0xF1,
0xFE, 0x7F, 0xC0, 0xF0, 0x3C, 0xFF, 0x7F, 0xFC, 0xFF, 0x3F, 0xCF, 0xFF,
0xDF, 0xF3, 0xF8, 0x08, 0x07, 0x03, 0xE1, 0xDC, 0x22, 0x00, 0x07, 0xF1,
0xFE, 0x7F, 0xC0, 0xF0, 0x3C, 0xFF, 0x7F, 0xFC, 0xFF, 0x3F, 0xCF, 0xFF,
0xDF, 0xF3, 0xF8, 0x18, 0x8F, 0xF7, 0xF8, 0x8C, 0x00, 0x00, 0x07, 0xF1,
0xFE, 0x7F, 0xC0, 0xF0, 0x3C, 0xFF, 0x7F, 0xFC, 0xFF, 0x3F, 0xCF, 0xFF,
0xDF, 0xF3, 0xF8, 0x61, 0xBC, 0xFF, 0x3D, 0x86, 0x00, 0x1F, 0xC7, 0xF9,
0xFF, 0x03, 0xC0, 0xF3, 0xFD, 0xFF, 0xF3, 0xFC, 0xFF, 0x3F, 0xFF, 0x7F,
0xCF, 0xE0, 0x1E, 0x0F, 0xC3, 0x30, 0xCC, 0x3F, 0x07, 0x80, 0x01, 0xFC,
0x7F, 0x9F, 0xF0, 0x3C, 0x0F, 0x3F, 0xDF, 0xFF, 0x3F, 0xCF, 0xF3, 0xFF,
0xF7, 0xFC, 0xFE, 0x3F, 0x8F, 0x0F, 0xFF, 0xF0, 0xFF, 0xFF, 0x00, 0xFD,
0xF0, 0x0F, 0x1E, 0x3F, 0xE3, 0xDF, 0xFF, 0xFF, 0xC7, 0xFF, 0xF0, 0xF0,
0x1E, 0x0F, 0x0B, 0xFF, 0xFF, 0x3F, 0xFF, 0xE3, 0xF9, 0xF8, 0x0F, 0x8F,
0xF7, 0xF9, 0xE0, 0xF0, 0x3C, 0x0F, 0x03, 0xC0, 0xF0, 0x1E, 0x17, 0xFC,
0xFF, 0x07, 0xC1, 0x80, 0x70, 0x1C, 0x1F, 0x07, 0x80, 0x04, 0x01, 0xE0,
0x0F, 0x00, 0x78, 0x01, 0x00, 0x00, 0x0F, 0x83, 0xFC, 0x7F, 0xE7, 0x9E,
0xF0, 0xFF, 0x0F, 0xFF, 0xFF, 0xFF, 0xF0, 0x07, 0x82, 0x7F, 0xE3, 0xFE,
0x0F, 0xC0, 0x02, 0x00, 0x78, 0x0F, 0x01, 0xE0, 0x08, 0x00, 0x00, 0x0F,
0x83, 0xFC, 0x7F, 0xE7, 0x9E, 0xF0, 0xFF, 0x0F, 0xFF, 0xFF, 0xFF, 0xF0,
0x07, 0x82, 0x7F, 0xE3, 0xFE, 0x0F, 0xC0, 0x04, 0x00, 0xE0, 0x1F, 0x03,
0xB8, 0x11, 0x00, 0x00, 0x0F, 0x83, 0xFC, 0x7F, 0xE7, 0x9E, 0xF0, 0xFF,
0x0F, 0xFF, 0xFF, 0xFF, 0xF0, 0x07, 0x82, 0x7F, 0xE3, 0xFE, 0x0F, 0xC0,
0x30, 0xC7, 0x9E, 0x79, 0xE3, 0x0C, 0x00, 0x00, 0xF8, 0x3F, 0xC7, 0xFE,
0x79, 0xEF, 0x0F, 0xF0, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x78, 0x27, 0xFE,
0x3F, 0xE0, 0xFC, 0x23, 0xC7, 0x8F, 0x08, 0x07, 0x9E, 0x79, 0xE7, 0x9E,
0x79, 0xE7, 0x9E, 0x79, 0xE7, 0x80, 0x10, 0xF7, 0xBC, 0x40, 0x07, 0x9E,
0x79, 0xE7, 0x9E, 0x79, 0xE7, 0x9E, 0x79, 0xE7, 0x80, 0x10, 0x71, 0xF7,
0x74, 0x40, 0x1E, 0x3C, 0x78, 0xF1, 0xE3, 0xC7, 0x8F, 0x1E, 0x3C, 0x78,
0xF1, 0xE0, 0x61, 0xBC, 0xFF, 0x3D, 0x86, 0x00, 0x07, 0x81, 0xE0, 0x78,
0x1E, 0x07, 0x81, 0xE0, 0x78, 0x1E, 0x07, 0x81, 0xE0, 0x78, 0x1E, 0x07,
0x80, 0x02, 0x00, 0xF1, 0x07, 0xF0, 0x7F, 0x0F, 0xE0, 0x5E, 0x00, 0xE1,
0xFF, 0x3F, 0xF7, 0xFF, 0xF8, 0xFF, 0x0F, 0xF0, 0xFF, 0x0F, 0xF0, 0xFF,
0x9E, 0x7F, 0xE3, 0xFC, 0x1F, 0x00, 0x18, 0x87, 0xF9, 0xFE, 0x11, 0x80,
0x00, 0x00, 0x1F, 0xC7, 0xFE, 0xFF, 0xDE, 0x7F, 0xC7, 0xF8, 0xFF, 0x1F,
0xE3, 0xFC, 0x7F, 0x8F, 0xF1, 0xFE, 0x3F, 0xC7, 0x80, 0x04, 0x00, 0xF0,
0x03, 0xC0, 0x0F, 0x00, 0x10, 0x00, 0x00, 0x3E, 0x07, 0xFC, 0x7F, 0xF3,
0xC7, 0xBC, 0x1F, 0xE0, 0xFF, 0x07, 0xF8, 0x3F, 0xC1, 0xEF, 0x1E, 0x7F,
0xF1, 0xFF, 0x03, 0xE0, 0x01, 0x00, 0x1E, 0x01, 0xE0, 0x1E, 0x00, 0x40,
0x00, 0x00, 0x3E, 0x07, 0xFC, 0x7F, 0xF3, 0xC7, 0xBC, 0x1F, 0xE0, 0xFF,
0x07, 0xF8, 0x3F, 0xC1, 0xEF, 0x1E, 0x7F, 0xF1, 0xFF, 0x03, 0xE0, 0x02,
0x00, 0x38, 0x03, 0xE0, 0x3B, 0x80, 0x88, 0x00, 0x00, 0x3E, 0x07, 0xFC,
0x7F, 0xF3, 0xC7, 0xBC, 0x1F, 0xE0, 0xFF, 0x07, 0xF8, 0x3F, 0xC1, 0xEF,
0x1E, 0x7F, 0xF1, 0xFF, 0x03, 0xE0, 0x0C, 0x40, 0xFF, 0x0F, 0xF0, 0x23,
0x00, 0x00, 0x00, 0x00, 0x3E, 0x07, 0xFC, 0x7F, 0xF3, 0xC7, 0xBC, 0x1F,
0xE0, 0xFF, 0x07, 0xF8, 0x3F, 0xC1, 0xEF, 0x1E, 0x7F, 0xF1, 0xFF, 0x03,
0xE0, 0x30, 0xC3, 0xCF, 0x1E, 0x78, 0x61, 0x80, 0x00, 0x07, 0xC0, 0xFF,
0x8F, 0xFE, 0x78, 0xF7, 0x83, 0xFC, 0x1F, 0xE0, 0xFF, 0x07, 0xF8, 0x3D,
0xE3, 0xCF, 0xFE, 0x3F, 0xE0, 0x7C, 0x00, 0x06, 0x00, 0xF0, 0x0F, 0x00,
0x60, 0x00, 0x0F, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x60, 0x0F, 0x00,
0xF0, 0x06, 0x00, 0x00, 0x00, 0x7D, 0xCF, 0xFC, 0xFF, 0xE7, 0x8F, 0x78,
0xFF, 0xCF, 0xFE, 0xEF, 0xFE, 0x7F, 0xE3, 0xDE, 0x3C, 0xFF, 0xE7, 0xFE,
0x77, 0xC0, 0x00, 0x00, 0x08, 0x07, 0x80, 0x78, 0x07, 0x80, 0x20, 0x00,
0x3C, 0x7F, 0x8F, 0xF1, 0xFE, 0x3F, 0xC7, 0xF8, 0xFF, 0x1F, 0xE3, 0xFC,
0x7F, 0xCF, 0x7F, 0xEF, 0xFC, 0x7F, 0x00, 0x02, 0x00, 0xF0, 0x3C, 0x0F,
0x00, 0x80, 0x00, 0x3C, 0x7F, 0x8F, 0xF1, 0xFE, 0x3F, 0xC7, 0xF8, 0xFF,
0x1F, 0xE3, 0xFC, 0x7F, 0xCF, 0x7F, 0xEF, 0xFC, 0x7F, 0x00, 0x04, 0x01,
0xC0, 0x7C, 0x1D, 0xC1, 0x10, 0x00, 0x3C, 0x7F, 0x8F, 0xF1, 0xFE, 0x3F,
0xC7, 0xF8, 0xFF, 0x1F, 0xE3, 0xFC, 0x7F, 0xCF, 0x7F, 0xEF, 0xFC, 0x7F,
0x00, 0x61, 0x9E, 0x7B, 0xCF, 0x30, 0xC0, 0x01, 0xE3, 0xFC, 0x7F, 0x8F,
0xF1, 0xFE, 0x3F, 0xC7, 0xF8, 0xFF, 0x1F, 0xE3, 0xFE, 0x7B, 0xFF, 0x7F,
0xE3, 0xF8, 0x00, 0x80, 0x0F, 0x00, 0xF0, 0x0F, 0x00, 0x20, 0x00, 0x03,
0xC1, 0xFF, 0x1F, 0x78, 0xF3, 0xC7, 0x9E, 0x3C, 0x7B, 0xC3, 0xDE, 0x1E,
0xF0, 0x77, 0x03, 0xF8, 0x1F, 0xC0, 0x7C, 0x03, 0xE0, 0x3E, 0x0F, 0xF0,
0x7F, 0x03, 0xF0, 0x00, 0xF0, 0x0F, 0x00, 0xF0, 0x0F, 0x00, 0xF0, 0x0F,
0x00, 0xFF, 0x0F, 0xFC, 0xFF, 0xEF, 0x1E, 0xF0, 0xFF, 0x0F, 0xF0, 0xFF,
0x0F, 0xF0, 0xFF, 0x1F, 0xFF, 0xEF, 0xFC, 0xFF, 0x8F, 0x00, 0xF0, 0x0F,
0x00, 0xF0, 0x00, 0x18, 0x61, 0xE7, 0x8F, 0x3C, 0x30, 0xC0, 0x00, 0x78,
0x3F, 0xE3, 0xEF, 0x1E, 0x78, 0xF3, 0xC7, 0x8F, 0x78, 0x7B, 0xC3, 0xDE,
0x0E, 0xE0, 0x7F, 0x03, 0xF8, 0x0F, 0x80, 0x7C, 0x07, 0xC1, 0xFE, 0x0F,
0xE0, 0x7E, 0x00 };
const GFXglyph Ubuntu_Bold12pt8bGlyphs[] PROGMEM = {
{ 0, 1, 1, 6, 0, 0 }, // 0x20 ' ' U+0020
{ 1, 4, 17, 8, 2, -16 }, // 0x21 '!' U+0021
{ 10, 9, 7, 11, 1, -17 }, // 0x22 '"' U+0022
{ 18, 15, 17, 17, 1, -16 }, // 0x23 '#' U+0023
{ 50, 12, 22, 14, 1, -18 }, // 0x24 '$' U+0024
{ 83, 20, 17, 22, 1, -16 }, // 0x25 '%' U+0025
{ 126, 16, 17, 17, 1, -16 }, // 0x26 '&' U+0026
{ 160, 4, 7, 6, 1, -17 }, // 0x27 ''' U+0027
{ 164, 7, 23, 9, 2, -18 }, // 0x28 '(' U+0028
{ 185, 7, 23, 9, 0, -18 }, // 0x29 ')' U+0029
{ 206, 11, 10, 12, 1, -16 }, // 0x2a '*' U+002A
{ 220, 11, 11, 13, 1, -12 }, // 0x2b '+' U+002B
{ 236, 5, 8, 6, 0, -3 }, // 0x2c ',' U+002C
{ 241, 7, 3, 9, 1, -8 }, // 0x2d '-' U+002D
{ 244, 4, 4, 6, 1, -3 }, // 0x2e '.' U+002E
{ 246, 12, 23, 10, -1, -18 }, // 0x2f '/' U+002F
{ 281, 12, 17, 14, 1, -16 }, // 0x30 '0' U+0030
{ 307, 8, 17, 14, 2, -16 }, // 0x31 '1' U+0031
{ 324, 12, 17, 14, 1, -16 }, // 0x32 '2' U+0032
{ 350, 11, 17, 14, 1, -16 }, // 0x33 '3' U+0033
{ 374, 12, 17, 14, 1, -16 }, // 0x34 '4' U+0034
{ 400, 12, 17, 14, 1, -16 }, // 0x35 '5' U+0035
{ 426, 12, 17, 14, 1, -16 }, // 0x36 '6' U+0036
{ 452, 12, 17, 14, 1, -16 }, // 0x37 '7' U+0037
{ 478, 12, 17, 14, 1, -16 }, // 0x38 '8' U+0038
{ 504, 12, 17, 14, 1, -16 }, // 0x39 '9' U+0039
{ 530, 4, 13, 6, 1, -12 }, // 0x3a ':' U+003A
{ 537, 5, 17, 6, 0, -12 }, // 0x3b ';' U+003B
{ 548, 12, 11, 14, 1, -12 }, // 0x3c '<' U+003C
{ 565, 11, 8, 14, 2, -10 }, // 0x3d '=' U+003D
{ 576, 12, 11, 14, 1, -12 }, // 0x3e '>' U+003E
{ 593, 10, 19, 11, 0, -18 }, // 0x3f '?' U+003F
{ 617, 21, 21, 23, 1, -16 }, // 0x40 '@' U+0040
{ 673, 17, 17, 17, 0, -16 }, // 0x41 'A' U+0041
{ 710, 13, 17, 16, 2, -16 }, // 0x42 'B' U+0042
{ 738, 14, 17, 16, 1, -16 }, // 0x43 'C' U+0043
{ 768, 15, 17, 18, 2, -16 }, // 0x44 'D' U+0044
{ 800, 12, 17, 15, 2, -16 }, // 0x45 'E' U+0045
{ 826, 11, 17, 14, 2, -16 }, // 0x46 'F' U+0046
{ 850, 14, 17, 17, 1, -16 }, // 0x47 'G' U+0047
{ 880, 14, 17, 18, 2, -16 }, // 0x48 'H' U+0048
{ 910, 4, 17, 8, 2, -16 }, // 0x49 'I' U+0049
{ 919, 11, 17, 13, 0, -16 }, // 0x4a 'J' U+004A
{ 943, 14, 17, 16, 2, -16 }, // 0x4b 'K' U+004B
{ 973, 12, 17, 14, 2, -16 }, // 0x4c 'L' U+004C
{ 999, 19, 17, 21, 1, -16 }, // 0x4d 'M' U+004D
{ 1040, 14, 17, 18, 2, -16 }, // 0x4e 'N' U+004E
{ 1070, 17, 17, 19, 1, -16 }, // 0x4f 'O' U+004F
{ 1107, 12, 17, 15, 2, -16 }, // 0x50 'P' U+0050
{ 1133, 17, 22, 19, 1, -16 }, // 0x51 'Q' U+0051
{ 1180, 14, 17, 16, 2, -16 }, // 0x52 'R' U+0052
{ 1210, 12, 17, 14, 1, -16 }, // 0x53 'S' U+0053
{ 1236, 14, 17, 14, 0, -16 }, // 0x54 'T' U+0054
{ 1266, 13, 17, 17, 2, -16 }, // 0x55 'U' U+0055
{ 1294, 17, 17, 17, 0, -16 }, // 0x56 'V' U+0056
{ 1331, 23, 17, 23, 0, -16 }, // 0x57 'W' U+0057
{ 1380, 16, 17, 16, 0, -16 }, // 0x58 'X' U+0058
{ 1414, 16, 17, 16, 0, -16 }, // 0x59 'Y' U+0059
{ 1448, 13, 17, 15, 1, -16 }, // 0x5a 'Z' U+005A
{ 1476, 7, 23, 9, 2, -18 }, // 0x5b '[' U+005B
{ 1497, 12, 23, 10, -1, -18 }, // 0x5c '\' U+005C
{ 1532, 7, 23, 9, 0, -18 }, // 0x5d ']' U+005D
{ 1553, 13, 10, 13, 0, -16 }, // 0x5e '^' U+005E
{ 1570, 12, 3, 12, 0, 2 }, // 0x5f '_' U+005F
{ 1575, 6, 5, 7, 1, -18 }, // 0x60 '`' U+0060
{ 1579, 10, 13, 13, 1, -12 }, // 0x61 'a' U+0061
{ 1596, 12, 19, 15, 2, -18 }, // 0x62 'b' U+0062
{ 1625, 10, 13, 12, 1, -12 }, // 0x63 'c' U+0063
{ 1642, 12, 19, 15, 1, -18 }, // 0x64 'd' U+0064
{ 1671, 12, 13, 14, 1, -12 }, // 0x65 'e' U+0065
{ 1691, 9, 19, 10, 2, -18 }, // 0x66 'f' U+0066
{ 1713, 11, 17, 14, 1, -12 }, // 0x67 'g' U+0067
{ 1737, 11, 19, 15, 2, -18 }, // 0x68 'h' U+0068
{ 1764, 4, 19, 8, 2, -18 }, // 0x69 'i' U+0069
{ 1774, 7, 23, 6, -2, -18 }, // 0x6a 'j' U+006A
{ 1795, 12, 19, 14, 2, -18 }, // 0x6b 'k' U+006B
{ 1824, 6, 19, 8, 2, -18 }, // 0x6c 'l' U+006C
{ 1839, 18, 13, 22, 2, -12 }, // 0x6d 'm' U+006D
{ 1869, 11, 13, 15, 2, -12 }, // 0x6e 'n' U+006E
{ 1887, 13, 13, 15, 1, -12 }, // 0x6f 'o' U+006F
{ 1909, 12, 17, 15, 2, -12 }, // 0x70 'p' U+0070
{ 1935, 13, 17, 15, 1, -12 }, // 0x71 'q' U+0071
{ 1963, 9, 13, 11, 2, -12 }, // 0x72 'r' U+0072
{ 1978, 10, 13, 12, 1, -12 }, // 0x73 's' U+0073
{ 1995, 9, 17, 11, 2, -16 }, // 0x74 't' U+0074
{ 2015, 11, 13, 15, 2, -12 }, // 0x75 'u' U+0075
{ 2033, 13, 13, 13, 0, -12 }, // 0x76 'v' U+0076
{ 2055, 19, 13, 19, 0, -12 }, // 0x77 'w' U+0077
{ 2086, 13, 13, 13, 0, -12 }, // 0x78 'x' U+0078
{ 2108, 13, 17, 13, 0, -12 }, // 0x79 'y' U+0079
{ 2136, 10, 13, 12, 1, -12 }, // 0x7a 'z' U+007A
{ 2153, 8, 23, 9, 1, -18 }, // 0x7b '{' U+007B
{ 2176, 3, 23, 7, 2, -18 }, // 0x7c '|' U+007C
{ 2185, 8, 23, 9, 0, -18 }, // 0x7d '}' U+007D
{ 2208, 12, 5, 14, 1, -9 }, // 0x7e '~' U+007E
{ 2216, 10, 18, 12, 1, -17 }, // 0x7f 'REPLACEMENT CHARACTER *' U+2370
{ 2239, 1, 1, 6, 0, 0 }, // 0x80 'NO-BREAK SPACE' U+00A0
{ 2240, 4, 17, 8, 2, -12 }, // 0x81 'INVERTED EXCLAMATION MARK' U+00A1
{ 2249, 11, 18, 14, 1, -15 }, // 0x82 'CENT SIGN' U+00A2
{ 2274, 11, 17, 14, 1, -16 }, // 0x83 'POUND SIGN' U+00A3
{ 2298, 11, 11, 14, 1, -13 }, // 0x84 'CURRENCY SIGN' U+00A4
{ 2314, 14, 17, 14, 0, -16 }, // 0x85 'YEN SIGN' U+00A5
{ 2344, 4, 23, 7, 2, -18 }, // 0x86 'BROKEN BAR' U+00A6
{ 2356, 10, 21, 12, 1, -16 }, // 0x87 'SECTION SIGN' U+00A7
{ 2383, 10, 4, 13, 2, -17 }, // 0x88 'DIAERESIS' U+00A8
{ 2388, 17, 17, 19, 1, -16 }, // 0x89 'COPYRIGHT SIGN' U+00A9
{ 2425, 8, 9, 10, 1, -16 }, // 0x8a 'FEMININE ORDINAL INDICATOR' U+00AA
{ 2434, 12, 11, 14, 1, -11 }, // 0x8b 'LEFT-POINTING DOUBLE ANGLE QUOTATION MARK' U+00AB
{ 2451, 12, 9, 14, 1, -9 }, // 0x8c 'NOT SIGN' U+00AC
{ 2465, 7, 3, 9, 1, -8 }, // 0x8d 'SOFT HYPHEN' U+00AD
{ 2468, 17, 17, 19, 1, -16 }, // 0x8e 'REGISTERED SIGN' U+00AE
{ 2505, 8, 2, 9, 1, -16 }, // 0x8f 'MACRON' U+00AF
{ 2507, 7, 7, 8, 0, -17 }, // 0x90 'DEGREE SIGN' U+00B0
{ 2514, 11, 15, 13, 1, -14 }, // 0x91 'PLUS-MINUS SIGN' U+00B1
{ 2535, 8, 10, 9, 0, -16 }, // 0x92 'SUPERSCRIPT TWO' U+00B2
{ 2545, 8, 10, 9, 0, -16 }, // 0x93 'SUPERSCRIPT THREE' U+00B3
{ 2555, 6, 5, 7, 0, -18 }, // 0x94 'ACUTE ACCENT' U+00B4
{ 2559, 11, 17, 15, 2, -12 }, // 0x95 'MICRO SIGN' U+00B5
{ 2583, 14, 21, 17, 1, -16 }, // 0x96 'PILCROW SIGN' U+00B6
{ 2620, 4, 4, 6, 1, -8 }, // 0x97 'MIDDLE DOT' U+00B7
{ 2622, 6, 5, 8, 1, 1 }, // 0x98 'CEDILLA' U+00B8
{ 2626, 6, 10, 9, 1, -16 }, // 0x99 'SUPERSCRIPT ONE' U+00B9
{ 2634, 9, 10, 11, 1, -16 }, // 0x9a 'MASCULINE ORDINAL INDICATOR' U+00BA
{ 2646, 12, 11, 14, 1, -11 }, // 0x9b 'RIGHT-POINTING DOUBLE ANGLE QUOTATION MARK' U+00BB
{ 2663, 20, 17, 21, 1, -16 }, // 0x9c 'VULGAR FRACTION ONE QUARTER' U+00BC
{ 2706, 19, 17, 21, 1, -16 }, // 0x9d 'VULGAR FRACTION ONE HALF' U+00BD
{ 2747, 21, 17, 21, 0, -16 }, // 0x9e 'VULGAR FRACTION THREE QUARTERS' U+00BE
{ 2792, 10, 17, 11, 1, -12 }, // 0x9f 'INVERTED QUESTION MARK' U+00BF
{ 2814, 17, 23, 17, 0, -22 }, // 0xa0 'LATIN CAPITAL LETTER A WITH GRAVE' U+00C0
{ 2863, 17, 23, 17, 0, -22 }, // 0xa1 'LATIN CAPITAL LETTER A WITH ACUTE' U+00C1
{ 2912, 17, 23, 17, 0, -22 }, // 0xa2 'LATIN CAPITAL LETTER A WITH CIRCUMFLEX' U+00C2
{ 2961, 17, 23, 17, 0, -22 }, // 0xa3 'LATIN CAPITAL LETTER A WITH TILDE' U+00C3
{ 3010, 17, 22, 17, 0, -21 }, // 0xa4 'LATIN CAPITAL LETTER A WITH DIAERESIS' U+00C4
{ 3057, 17, 22, 17, 0, -21 }, // 0xa5 'LATIN CAPITAL LETTER A WITH RING ABOVE' U+00C5
{ 3104, 23, 17, 24, 0, -16 }, // 0xa6 'LATIN CAPITAL LETTER AE' U+00C6
{ 3153, 14, 22, 16, 1, -16 }, // 0xa7 'LATIN CAPITAL LETTER C WITH CEDILLA' U+00C7
{ 3192, 12, 23, 15, 2, -22 }, // 0xa8 'LATIN CAPITAL LETTER E WITH GRAVE' U+00C8
{ 3227, 12, 23, 15, 2, -22 }, // 0xa9 'LATIN CAPITAL LETTER E WITH ACUTE' U+00C9
{ 3262, 12, 23, 15, 2, -22 }, // 0xaa 'LATIN CAPITAL LETTER E WITH CIRCUMFLEX' U+00CA
{ 3297, 12, 22, 15, 2, -21 }, // 0xab 'LATIN CAPITAL LETTER E WITH DIAERESIS' U+00CB
{ 3330, 6, 23, 8, 1, -22 }, // 0xac 'LATIN CAPITAL LETTER I WITH GRAVE' U+00CC
{ 3348, 6, 23, 8, 1, -22 }, // 0xad 'LATIN CAPITAL LETTER I WITH ACUTE' U+00CD
{ 3366, 7, 23, 8, 1, -22 }, // 0xae 'LATIN CAPITAL LETTER I WITH CIRCUMFLEX' U+00CE
{ 3387, 10, 22, 8, -1, -21 }, // 0xaf 'LATIN CAPITAL LETTER I WITH DIAERESIS' U+00CF
{ 3415, 17, 17, 18, 0, -16 }, // 0xb0 'LATIN CAPITAL LETTER ETH' U+00D0
{ 3452, 14, 23, 18, 2, -22 }, // 0xb1 'LATIN CAPITAL LETTER N WITH TILDE' U+00D1
{ 3493, 17, 23, 19, 1, -22 }, // 0xb2 'LATIN CAPITAL LETTER O WITH GRAVE' U+00D2
{ 3542, 17, 23, 19, 1, -22 }, // 0xb3 'LATIN CAPITAL LETTER O WITH ACUTE' U+00D3
{ 3591, 17, 23, 19, 1, -22 }, // 0xb4 'LATIN CAPITAL LETTER O WITH CIRCUMFLEX' U+00D4
{ 3640, 17, 23, 19, 1, -22 }, // 0xb5 'LATIN CAPITAL LETTER O WITH TILDE' U+00D5
{ 3689, 17, 22, 19, 1, -21 }, // 0xb6 'LATIN CAPITAL LETTER O WITH DIAERESIS' U+00D6
{ 3736, 12, 11, 14, 1, -12 }, // 0xb7 'MULTIPLICATION SIGN' U+00D7
{ 3753, 17, 19, 19, 1, -17 }, // 0xb8 'LATIN CAPITAL LETTER O WITH STROKE' U+00D8
{ 3794, 13, 23, 17, 2, -22 }, // 0xb9 'LATIN CAPITAL LETTER U WITH GRAVE' U+00D9
{ 3832, 13, 23, 17, 2, -22 }, // 0xba 'LATIN CAPITAL LETTER U WITH ACUTE' U+00DA
{ 3870, 13, 23, 17, 2, -22 }, // 0xbb 'LATIN CAPITAL LETTER U WITH CIRCUMFLEX' U+00DB
{ 3908, 13, 22, 17, 2, -21 }, // 0xbc 'LATIN CAPITAL LETTER U WITH DIAERESIS' U+00DC
{ 3944, 16, 23, 16, 0, -22 }, // 0xbd 'LATIN CAPITAL LETTER Y WITH ACUTE' U+00DD
{ 3990, 12, 17, 15, 2, -16 }, // 0xbe 'LATIN CAPITAL LETTER THORN' U+00DE
{ 4016, 13, 19, 16, 2, -18 }, // 0xbf 'LATIN SMALL LETTER SHARP S' U+00DF
{ 4047, 10, 19, 13, 1, -18 }, // 0xc0 'LATIN SMALL LETTER A WITH GRAVE' U+00E0
{ 4071, 10, 19, 13, 1, -18 }, // 0xc1 'LATIN SMALL LETTER A WITH ACUTE' U+00E1
{ 4095, 10, 19, 13, 1, -18 }, // 0xc2 'LATIN SMALL LETTER A WITH CIRCUMFLEX' U+00E2
{ 4119, 10, 19, 13, 1, -18 }, // 0xc3 'LATIN SMALL LETTER A WITH TILDE' U+00E3
{ 4143, 10, 18, 13, 1, -17 }, // 0xc4 'LATIN SMALL LETTER A WITH DIAERESIS' U+00E4
{ 4166, 10, 20, 13, 1, -19 }, // 0xc5 'LATIN SMALL LETTER A WITH RING ABOVE' U+00E5
{ 4191, 19, 13, 21, 1, -12 }, // 0xc6 'LATIN SMALL LETTER AE' U+00E6
{ 4222, 10, 18, 12, 1, -12 }, // 0xc7 'LATIN SMALL LETTER C WITH CEDILLA' U+00E7
{ 4245, 12, 19, 14, 1, -18 }, // 0xc8 'LATIN SMALL LETTER E WITH GRAVE' U+00E8
{ 4274, 12, 19, 14, 1, -18 }, // 0xc9 'LATIN SMALL LETTER E WITH ACUTE' U+00E9
{ 4303, 12, 19, 14, 1, -18 }, // 0xca 'LATIN SMALL LETTER E WITH CIRCUMFLEX' U+00EA
{ 4332, 12, 18, 14, 1, -17 }, // 0xcb 'LATIN SMALL LETTER E WITH DIAERESIS' U+00EB
{ 4359, 6, 19, 8, 1, -18 }, // 0xcc 'LATIN SMALL LETTER I WITH GRAVE' U+00EC
{ 4374, 6, 19, 8, 1, -18 }, // 0xcd 'LATIN SMALL LETTER I WITH ACUTE' U+00ED
{ 4389, 7, 19, 8, 1, -18 }, // 0xce 'LATIN SMALL LETTER I WITH CIRCUMFLEX' U+00EE
{ 4406, 10, 18, 8, -1, -17 }, // 0xcf 'LATIN SMALL LETTER I WITH DIAERESIS' U+00EF
{ 4429, 12, 19, 14, 1, -18 }, // 0xd0 'LATIN SMALL LETTER ETH' U+00F0
{ 4458, 11, 19, 15, 2, -18 }, // 0xd1 'LATIN SMALL LETTER N WITH TILDE' U+00F1
{ 4485, 13, 19, 15, 1, -18 }, // 0xd2 'LATIN SMALL LETTER O WITH GRAVE' U+00F2
{ 4516, 13, 19, 15, 1, -18 }, // 0xd3 'LATIN SMALL LETTER O WITH ACUTE' U+00F3
{ 4547, 13, 19, 15, 1, -18 }, // 0xd4 'LATIN SMALL LETTER O WITH CIRCUMFLEX' U+00F4
{ 4578, 13, 19, 15, 1, -18 }, // 0xd5 'LATIN SMALL LETTER O WITH TILDE' U+00F5
{ 4609, 13, 18, 15, 1, -17 }, // 0xd6 'LATIN SMALL LETTER O WITH DIAERESIS' U+00F6
{ 4639, 12, 13, 14, 1, -12 }, // 0xd7 'DIVISION SIGN' U+00F7
{ 4659, 13, 15, 15, 1, -13 }, // 0xd8 'LATIN SMALL LETTER O WITH STROKE' U+00F8
{ 4684, 11, 19, 15, 2, -18 }, // 0xd9 'LATIN SMALL LETTER U WITH GRAVE' U+00F9
{ 4711, 11, 19, 15, 2, -18 }, // 0xda 'LATIN SMALL LETTER U WITH ACUTE' U+00FA
{ 4738, 11, 19, 15, 2, -18 }, // 0xdb 'LATIN SMALL LETTER U WITH CIRCUMFLEX' U+00FB
{ 4765, 11, 18, 15, 2, -17 }, // 0xdc 'LATIN SMALL LETTER U WITH DIAERESIS' U+00FC
{ 4790, 13, 23, 13, 0, -18 }, // 0xdd 'LATIN SMALL LETTER Y WITH ACUTE' U+00FD
{ 4828, 12, 23, 15, 2, -18 }, // 0xde 'LATIN SMALL LETTER THORN' U+00FE
{ 4863, 13, 22, 13, 0, -17 } }; // 0xdf 'LATIN SMALL LETTER Y WITH DIAERESIS' U+000FF
const GFXfont Ubuntu_Bold12pt8b PROGMEM = {
(uint8_t *)Ubuntu_Bold12pt8bBitmaps,
(GFXglyph *)Ubuntu_Bold12pt8bGlyphs,
0x20, 0xDF, 27 };
// Approx. 6250 bytes

View File

@@ -0,0 +1,866 @@
const uint8_t Ubuntu_Bold16pt8bBitmaps[] PROGMEM = {
0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF7, 0x00, 0x0E, 0xFF,
0xFF, 0xF7, 0x00, 0xF9, 0xFF, 0x9F, 0xF9, 0xFF, 0x9F, 0xF9, 0xFF, 0x9F,
0xF9, 0xFF, 0x1E, 0x70, 0xE0, 0x07, 0xCF, 0x81, 0xF3, 0xE0, 0x7D, 0xF8,
0x3E, 0x7C, 0x0F, 0x9F, 0x3F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xC7, 0xCF, 0x81, 0xF3, 0xE0, 0x7C, 0xF8, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x3E, 0x7C, 0x0F, 0x9F, 0x07, 0xEF, 0x81,
0xF3, 0xE0, 0x7C, 0xF8, 0x00, 0x03, 0xC0, 0x03, 0xC0, 0x03, 0xC0, 0x07,
0xF8, 0x1F, 0xFE, 0x3F, 0xFE, 0x3F, 0xFC, 0x7E, 0x0C, 0x7C, 0x00, 0x7C,
0x00, 0x7E, 0x00, 0x7F, 0xE0, 0x3F, 0xF8, 0x1F, 0xFC, 0x0F, 0xFE, 0x01,
0xFF, 0x00, 0x3F, 0x00, 0x1F, 0x00, 0x1F, 0x70, 0x3F, 0x7F, 0xFE, 0xFF,
0xFE, 0xFF, 0xFC, 0x1F, 0xF0, 0x03, 0xC0, 0x03, 0xC0, 0x03, 0xC0, 0x03,
0xC0, 0x1F, 0x80, 0xF8, 0x0F, 0xF0, 0x3C, 0x07, 0xFE, 0x1F, 0x03, 0xE7,
0xC7, 0x80, 0xF0, 0xF3, 0xE0, 0x3C, 0x3C, 0xF0, 0x0F, 0x0F, 0x78, 0x03,
0xC3, 0xFE, 0x00, 0xF9, 0xFF, 0x00, 0x1F, 0xFF, 0xDF, 0x83, 0xFD, 0xEF,
0xF0, 0x7E, 0xFF, 0xFE, 0x00, 0x3F, 0xE7, 0xC0, 0x1F, 0xF0, 0xF0, 0x07,
0xBC, 0x3C, 0x03, 0xCF, 0x0F, 0x01, 0xF3, 0xC3, 0xC0, 0x78, 0xF9, 0xF0,
0x3E, 0x1F, 0xF8, 0x0F, 0x03, 0xFC, 0x07, 0xC0, 0x7E, 0x00, 0x07, 0xF0,
0x00, 0x7F, 0xE0, 0x07, 0xFF, 0x80, 0x7F, 0xFC, 0x03, 0xE3, 0xE0, 0x1F,
0x1F, 0x00, 0xFD, 0xF0, 0x03, 0xFF, 0x80, 0x1F, 0xF8, 0x00, 0xFF, 0x00,
0x0F, 0xFC, 0xF8, 0xFF, 0xF7, 0xC7, 0xDF, 0xBE, 0x7C, 0x7F, 0xE3, 0xE1,
0xFF, 0x1F, 0x07, 0xF0, 0xFC, 0x1F, 0x87, 0xFF, 0xFE, 0x1F, 0xFF, 0xF8,
0x7F, 0xFF, 0xE0, 0xFE, 0x3F, 0x80, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0x70,
0x04, 0x07, 0x87, 0xE3, 0xE3, 0xF1, 0xF1, 0xF8, 0xF8, 0x7C, 0x3E, 0x3E,
0x1F, 0x0F, 0x87, 0xC3, 0xE1, 0xF0, 0xF8, 0x7C, 0x3E, 0x1F, 0x07, 0xC3,
0xE1, 0xF0, 0xFC, 0x3E, 0x1F, 0x87, 0xC3, 0xF0, 0xF0, 0x20, 0x10, 0x3C,
0x3F, 0x0F, 0x87, 0xE1, 0xF0, 0xFC, 0x3E, 0x1F, 0x0F, 0x83, 0xE1, 0xF0,
0xF8, 0x7C, 0x3E, 0x1F, 0x0F, 0x87, 0xC3, 0xE1, 0xF1, 0xF0, 0xF8, 0x7C,
0x7E, 0x3E, 0x3F, 0x1F, 0x1F, 0x87, 0x80, 0x80, 0x0F, 0x80, 0x7C, 0x1B,
0xCC, 0xEE, 0xEF, 0xFF, 0xFF, 0xFC, 0x1C, 0x03, 0xF8, 0x3D, 0xE3, 0xEF,
0x8E, 0x38, 0x11, 0x00, 0x07, 0x80, 0x1E, 0x00, 0x78, 0x01, 0xE0, 0x07,
0x83, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFC, 0x1E, 0x00, 0x78, 0x01,
0xE0, 0x07, 0x80, 0x1E, 0x00, 0x7D, 0xF7, 0xDF, 0x7D, 0xE7, 0xBE, 0xF1,
0xC0, 0xFF, 0xFF, 0xFF, 0xFF, 0xF0, 0x7B, 0xFF, 0xFF, 0xFD, 0xE0, 0x00,
0x3E, 0x00, 0xFC, 0x01, 0xF0, 0x03, 0xE0, 0x0F, 0xC0, 0x1F, 0x00, 0x3E,
0x00, 0xFC, 0x01, 0xF0, 0x03, 0xE0, 0x0F, 0xC0, 0x1F, 0x00, 0x3E, 0x00,
0xFC, 0x01, 0xF0, 0x03, 0xE0, 0x0F, 0xC0, 0x1F, 0x00, 0x3E, 0x00, 0xFC,
0x01, 0xF0, 0x03, 0xE0, 0x0F, 0xC0, 0x1F, 0x00, 0x3E, 0x00, 0xFC, 0x01,
0xF0, 0x03, 0xE0, 0x0F, 0xC0, 0x1F, 0x00, 0x00, 0x07, 0xE0, 0x1F, 0xF8,
0x3F, 0xFC, 0x3F, 0xFC, 0x7E, 0x7E, 0x7C, 0x3E, 0xF8, 0x1F, 0xF8, 0x1F,
0xF8, 0x1F, 0xF8, 0x1F, 0xF8, 0x1F, 0xF8, 0x1F, 0xF8, 0x1F, 0xF8, 0x1F,
0xF8, 0x1F, 0x7C, 0x3E, 0x7E, 0x7E, 0x3F, 0xFC, 0x3F, 0xFC, 0x1F, 0xF8,
0x07, 0xE0, 0x03, 0xC1, 0xF1, 0xFD, 0xFF, 0xFF, 0xFF, 0xF7, 0x7D, 0x1F,
0x07, 0xC1, 0xF0, 0x7C, 0x1F, 0x07, 0xC1, 0xF0, 0x7C, 0x1F, 0x07, 0xC1,
0xF0, 0x7C, 0x1F, 0x07, 0xC0, 0x0F, 0xC0, 0x7F, 0xE3, 0xFF, 0xE7, 0xFF,
0xE7, 0x8F, 0xC4, 0x0F, 0x80, 0x1F, 0x00, 0x3E, 0x00, 0xF8, 0x03, 0xF0,
0x0F, 0xC0, 0x3F, 0x00, 0xFE, 0x01, 0xF8, 0x07, 0xE0, 0x1F, 0x80, 0x3E,
0x00, 0xFF, 0xFD, 0xFF, 0xFB, 0xFF, 0xF7, 0xFF, 0xE0, 0x1F, 0xC0, 0xFF,
0xE3, 0xFF, 0xE3, 0xFF, 0xE6, 0x0F, 0xC0, 0x0F, 0x80, 0x1F, 0x00, 0x7C,
0x1F, 0xF8, 0x3F, 0xC0, 0x7F, 0xE0, 0xFF, 0xE0, 0x0F, 0xE0, 0x07, 0xC0,
0x0F, 0x80, 0x1F, 0x60, 0x7F, 0xFF, 0xFB, 0xFF, 0xF7, 0xFF, 0xC3, 0xFC,
0x00, 0x00, 0x7C, 0x00, 0xFC, 0x01, 0xFC, 0x03, 0xFC, 0x07, 0xFC, 0x0F,
0xFC, 0x0F, 0x7C, 0x1E, 0x7C, 0x3E, 0x7C, 0x3C, 0x7C, 0x78, 0x7C, 0xF8,
0x7C, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x7C, 0x00,
0x7C, 0x00, 0x7C, 0x00, 0x7C, 0x00, 0x7C, 0x3F, 0xFC, 0x7F, 0xF8, 0xFF,
0xF1, 0xFF, 0xE3, 0xC0, 0x07, 0x80, 0x1F, 0x00, 0x3F, 0xC0, 0x7F, 0xF0,
0xFF, 0xF1, 0xFF, 0xF0, 0x0F, 0xF0, 0x07, 0xE0, 0x07, 0xC0, 0x0F, 0x80,
0x1F, 0x40, 0x7F, 0xFF, 0xFB, 0xFF, 0xE7, 0xFF, 0x83, 0xFC, 0x00, 0x00,
0x3E, 0x01, 0xFE, 0x07, 0xFE, 0x0F, 0xFE, 0x1F, 0xE0, 0x3F, 0x00, 0x7E,
0x00, 0x7C, 0x00, 0xFF, 0xF0, 0xFF, 0xFC, 0xFF, 0xFE, 0xFF, 0xFE, 0xF8,
0x3F, 0xF8, 0x1F, 0xF8, 0x1F, 0xF8, 0x1F, 0x7C, 0x3F, 0x7F, 0xFE, 0x3F,
0xFC, 0x1F, 0xF8, 0x07, 0xE0, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0x00, 0x3E, 0x00, 0x7C, 0x00, 0xFC, 0x00, 0xF8, 0x01, 0xF8, 0x01,
0xF0, 0x01, 0xF0, 0x03, 0xE0, 0x03, 0xE0, 0x03, 0xE0, 0x07, 0xC0, 0x07,
0xC0, 0x07, 0xC0, 0x0F, 0x80, 0x0F, 0x80, 0x0F, 0x80, 0x0F, 0x80, 0x0F,
0xE0, 0x3F, 0xF8, 0x7F, 0xFC, 0xFF, 0xFE, 0xFC, 0x7E, 0xF8, 0x3E, 0xF8,
0x3E, 0xFC, 0x7C, 0x7F, 0xFC, 0x3F, 0xF0, 0x3F, 0xFC, 0x7F, 0xFE, 0xFC,
0x3E, 0xF8, 0x1F, 0xF8, 0x1F, 0xF8, 0x1F, 0xFC, 0x3F, 0x7F, 0xFE, 0x7F,
0xFE, 0x3F, 0xFC, 0x07, 0xF0, 0x07, 0xE0, 0x1F, 0xF8, 0x3F, 0xFC, 0x7F,
0xFE, 0xFC, 0x3E, 0xF8, 0x3F, 0xF8, 0x1F, 0xF8, 0x1F, 0xFC, 0x1F, 0x7F,
0xFF, 0x7F, 0xFF, 0x3F, 0xFF, 0x0F, 0xDF, 0x00, 0x3E, 0x00, 0x7E, 0x00,
0xFE, 0x03, 0xFC, 0x3F, 0xF8, 0x3F, 0xF0, 0x3F, 0xC0, 0x3E, 0x00, 0x7B,
0xFF, 0xFF, 0xFD, 0xE0, 0x00, 0x00, 0x00, 0x1E, 0xFF, 0xFF, 0xFF, 0x78,
0x7B, 0xFF, 0xFF, 0xFD, 0xE0, 0x00, 0x00, 0x00, 0x00, 0x7D, 0xF7, 0xDF,
0x7D, 0xE7, 0xBE, 0xF1, 0xC0, 0x00, 0x0C, 0x01, 0xF8, 0x3F, 0xFB, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0x3F, 0xE0, 0x7C, 0x00, 0xFF, 0x81, 0xFF, 0xF3,
0xFF, 0xFB, 0xFF, 0xF0, 0xFF, 0xE0, 0x1F, 0x80, 0x03, 0x00, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7F,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF8, 0x60, 0x01, 0xF0, 0x0F, 0xFC,
0x3F, 0xFE, 0xFF, 0xFC, 0xFF, 0xF0, 0x7F, 0xC0, 0x1F, 0x03, 0xFC, 0xFF,
0xFF, 0xFF, 0xFF, 0xFE, 0xFF, 0xC1, 0xF0, 0x06, 0x00, 0x00, 0x3F, 0x8F,
0xFC, 0xFF, 0xE7, 0xFF, 0x63, 0xF0, 0x1F, 0x01, 0xF0, 0x1F, 0x03, 0xE0,
0x7E, 0x0F, 0xC0, 0xF8, 0x1F, 0x01, 0xE0, 0x1E, 0x01, 0xE0, 0x00, 0x00,
0x00, 0x1E, 0x03, 0xF0, 0x3F, 0x03, 0xF0, 0x3F, 0x01, 0xE0, 0x00, 0x7F,
0x80, 0x00, 0x7F, 0xFC, 0x00, 0x7F, 0xFF, 0xC0, 0x3F, 0x03, 0xF8, 0x1F,
0x00, 0x1F, 0x0F, 0x80, 0x03, 0xC3, 0xC1, 0xF8, 0x79, 0xE1, 0xFF, 0x1E,
0x78, 0xFF, 0xC7, 0xFE, 0x3C, 0xF0, 0xFF, 0x1E, 0x3C, 0x3F, 0xC7, 0x8F,
0x0F, 0xF1, 0xE3, 0xC3, 0xFC, 0x78, 0xF0, 0xFF, 0x1E, 0x3C, 0x3F, 0xC7,
0x8F, 0x0F, 0xF1, 0xE3, 0xC7, 0xBE, 0x3C, 0xF1, 0xE7, 0x8F, 0xFF, 0xF1,
0xE1, 0xFF, 0xF8, 0x3C, 0x3E, 0xF8, 0x0F, 0x80, 0x00, 0x01, 0xF0, 0x00,
0x00, 0x3F, 0x80, 0x40, 0x07, 0xFF, 0xF0, 0x00, 0x7F, 0xFC, 0x00, 0x03,
0xFE, 0x00, 0x00, 0xFC, 0x00, 0x0F, 0xF0, 0x00, 0x7F, 0x80, 0x07, 0xFC,
0x00, 0x3F, 0xF0, 0x03, 0xEF, 0x80, 0x1F, 0x7E, 0x00, 0xFB, 0xF0, 0x0F,
0x8F, 0x80, 0x7C, 0x7E, 0x03, 0xE3, 0xF0, 0x3E, 0x0F, 0x81, 0xFF, 0xFC,
0x1F, 0xFF, 0xF0, 0xFF, 0xFF, 0x87, 0xFF, 0xFC, 0x7E, 0x03, 0xF3, 0xE0,
0x0F, 0x9F, 0x00, 0x7D, 0xF8, 0x03, 0xFF, 0x80, 0x0F, 0x80, 0xFF, 0xE0,
0x7F, 0xFE, 0x3F, 0xFF, 0x9F, 0xFF, 0xEF, 0x83, 0xF7, 0xC0, 0xFB, 0xE0,
0x7D, 0xF0, 0x7E, 0xFF, 0xFE, 0x7F, 0xFE, 0x3F, 0xFF, 0x9F, 0xFF, 0xEF,
0x81, 0xFF, 0xC0, 0x7F, 0xE0, 0x3F, 0xF0, 0x1F, 0xF8, 0x1F, 0xFF, 0xFF,
0xBF, 0xFF, 0xDF, 0xFF, 0x87, 0xFF, 0x00, 0x01, 0xFC, 0x03, 0xFF, 0xC7,
0xFF, 0xE7, 0xFF, 0xE3, 0xF0, 0x33, 0xF0, 0x01, 0xF0, 0x01, 0xF0, 0x00,
0xF8, 0x00, 0x7C, 0x00, 0x3E, 0x00, 0x1F, 0x00, 0x0F, 0x80, 0x07, 0xC0,
0x03, 0xF0, 0x00, 0xFC, 0x00, 0x7F, 0x03, 0x1F, 0xFF, 0x87, 0xFF, 0xE1,
0xFF, 0xF0, 0x3F, 0xC0, 0xFF, 0xE0, 0x1F, 0xFF, 0x83, 0xFF, 0xFC, 0x7F,
0xFF, 0xCF, 0x83, 0xF9, 0xF0, 0x1F, 0xBE, 0x01, 0xF7, 0xC0, 0x1F, 0xF8,
0x03, 0xFF, 0x00, 0x7F, 0xE0, 0x0F, 0xFC, 0x01, 0xFF, 0x80, 0x3F, 0xF0,
0x0F, 0xFE, 0x01, 0xF7, 0xC0, 0x7E, 0xF8, 0x3F, 0x9F, 0xFF, 0xF3, 0xFF,
0xFC, 0x7F, 0xFE, 0x0F, 0xFE, 0x00, 0xFF, 0xFE, 0xFF, 0xFE, 0xFF, 0xFE,
0xFF, 0xFE, 0xF8, 0x00, 0xF8, 0x00, 0xF8, 0x00, 0xF8, 0x00, 0xFF, 0xFC,
0xFF, 0xFC, 0xFF, 0xFC, 0xFF, 0xFC, 0xF8, 0x00, 0xF8, 0x00, 0xF8, 0x00,
0xF8, 0x00, 0xF8, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x80, 0x1F, 0x00, 0x3E,
0x00, 0x7C, 0x00, 0xFF, 0xFD, 0xFF, 0xFB, 0xFF, 0xF7, 0xFF, 0xEF, 0x80,
0x1F, 0x00, 0x3E, 0x00, 0x7C, 0x00, 0xF8, 0x01, 0xF0, 0x03, 0xE0, 0x07,
0xC0, 0x0F, 0x80, 0x00, 0x01, 0xFE, 0x01, 0xFF, 0xF1, 0xFF, 0xFC, 0xFF,
0xFE, 0x3F, 0x01, 0x9F, 0x80, 0x07, 0xC0, 0x03, 0xE0, 0x00, 0xF8, 0x00,
0x3E, 0x00, 0x0F, 0x80, 0x7F, 0xE0, 0x1F, 0xF8, 0x07, 0xFE, 0x01, 0xF7,
0xC0, 0x7D, 0xF8, 0x1F, 0x7F, 0x07, 0xCF, 0xFF, 0xF1, 0xFF, 0xFC, 0x3F,
0xFF, 0x01, 0xFF, 0x00, 0xF8, 0x03, 0xFF, 0x00, 0x7F, 0xE0, 0x0F, 0xFC,
0x01, 0xFF, 0x80, 0x3F, 0xF0, 0x07, 0xFE, 0x00, 0xFF, 0xC0, 0x1F, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x80, 0x3F, 0xF0,
0x07, 0xFE, 0x00, 0xFF, 0xC0, 0x1F, 0xF8, 0x03, 0xFF, 0x00, 0x7F, 0xE0,
0x0F, 0xFC, 0x01, 0xFF, 0x80, 0x3E, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x80, 0x00, 0x7C, 0x01, 0xF0,
0x07, 0xC0, 0x1F, 0x00, 0x7C, 0x01, 0xF0, 0x07, 0xC0, 0x1F, 0x00, 0x7C,
0x01, 0xF0, 0x07, 0xC0, 0x1F, 0x00, 0x7C, 0x01, 0xF0, 0x07, 0xC0, 0x1F,
0x60, 0xFD, 0xFF, 0xEF, 0xFF, 0xBF, 0xFC, 0x3F, 0xC0, 0xF8, 0x1F, 0xDF,
0x07, 0xF3, 0xE1, 0xFC, 0x7C, 0x7F, 0x0F, 0x9F, 0xC1, 0xF7, 0xF0, 0x3F,
0xFC, 0x07, 0xFF, 0x00, 0xFF, 0xC0, 0x1F, 0xF0, 0x03, 0xFF, 0x00, 0x7F,
0xF0, 0x0F, 0xFF, 0x01, 0xF7, 0xE0, 0x3E, 0x7E, 0x07, 0xC7, 0xE0, 0xF8,
0x7E, 0x1F, 0x07, 0xE3, 0xE0, 0x7E, 0x7C, 0x0F, 0xEF, 0x80, 0xFE, 0xF8,
0x03, 0xE0, 0x0F, 0x80, 0x3E, 0x00, 0xF8, 0x03, 0xE0, 0x0F, 0x80, 0x3E,
0x00, 0xF8, 0x03, 0xE0, 0x0F, 0x80, 0x3E, 0x00, 0xF8, 0x03, 0xE0, 0x0F,
0x80, 0x3E, 0x00, 0xF8, 0x03, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFC,
0x3E, 0x00, 0x7C, 0x3E, 0x00, 0x7C, 0x3F, 0x00, 0xFC, 0x7F, 0x00, 0xFE,
0x7F, 0x80, 0xFE, 0x7F, 0x81, 0xFE, 0x7F, 0xC1, 0xFE, 0x7F, 0xC3, 0xFE,
0x7F, 0xE3, 0xFE, 0x7D, 0xE7, 0xBE, 0x79, 0xF7, 0x9E, 0x78, 0xF7, 0x9E,
0x78, 0xFF, 0x1E, 0xF8, 0x7F, 0x1E, 0xF8, 0x7E, 0x1F, 0xF8, 0x3E, 0x1F,
0xF8, 0x3E, 0x1F, 0xF8, 0x3C, 0x1F, 0xF8, 0x00, 0x1F, 0xF8, 0x00, 0x1F,
0xF8, 0x00, 0x1F, 0xF8, 0x03, 0xFF, 0x80, 0x7F, 0xF8, 0x0F, 0xFF, 0x01,
0xFF, 0xF0, 0x3F, 0xFF, 0x07, 0xFF, 0xF0, 0xFF, 0xFF, 0x1F, 0xFB, 0xE3,
0xFF, 0x3E, 0x7F, 0xE3, 0xEF, 0xFC, 0x7F, 0xFF, 0x87, 0xFF, 0xF0, 0x7F,
0xFE, 0x07, 0xFF, 0xC0, 0xFF, 0xF8, 0x0F, 0xFF, 0x00, 0xFF, 0xE0, 0x1F,
0xFC, 0x01, 0xFF, 0x80, 0x3E, 0x01, 0xF8, 0x00, 0xFF, 0xF0, 0x1F, 0xFF,
0x83, 0xFF, 0xFC, 0x3F, 0x0F, 0xC7, 0xE0, 0x7E, 0x7C, 0x03, 0xEF, 0x80,
0x1F, 0xF8, 0x01, 0xFF, 0x80, 0x1F, 0xF8, 0x01, 0xFF, 0x80, 0x1F, 0xF8,
0x01, 0xFF, 0x80, 0x1F, 0x7C, 0x03, 0xE7, 0xE0, 0x7E, 0x7F, 0x0F, 0xE3,
0xFF, 0xFC, 0x1F, 0xFF, 0x80, 0xFF, 0xF0, 0x01, 0xFC, 0x00, 0xFF, 0xE0,
0x7F, 0xFE, 0x3F, 0xFF, 0x9F, 0xFF, 0xEF, 0x83, 0xFF, 0xC0, 0x7F, 0xE0,
0x3F, 0xF0, 0x1F, 0xF8, 0x0F, 0xFC, 0x1F, 0xFF, 0xFF, 0xDF, 0xFF, 0xCF,
0xFF, 0xC7, 0xFF, 0x83, 0xE0, 0x01, 0xF0, 0x00, 0xF8, 0x00, 0x7C, 0x00,
0x3E, 0x00, 0x1F, 0x00, 0x0F, 0x80, 0x00, 0x01, 0xF8, 0x00, 0xFF, 0xF0,
0x1F, 0xFF, 0x83, 0xFF, 0xFC, 0x3F, 0x0F, 0xC7, 0xE0, 0x7E, 0x7C, 0x03,
0xEF, 0x80, 0x1F, 0xF8, 0x01, 0xFF, 0x80, 0x1F, 0xF8, 0x01, 0xFF, 0x80,
0x1F, 0xF8, 0x01, 0xFF, 0x80, 0x1F, 0x7C, 0x03, 0xF7, 0xE0, 0x7E, 0x3F,
0x0F, 0xE3, 0xFF, 0xFC, 0x1F, 0xFF, 0xC0, 0xFF, 0xF0, 0x01, 0xFE, 0x00,
0x07, 0xC0, 0x00, 0x7F, 0x00, 0x03, 0xFE, 0x00, 0x1F, 0xE0, 0x00, 0xFC,
0x00, 0x03, 0xC0, 0xFF, 0xE0, 0x3F, 0xFF, 0x0F, 0xFF, 0xE3, 0xFF, 0xFC,
0xF8, 0x3F, 0xBE, 0x03, 0xEF, 0x80, 0xFB, 0xE0, 0x3E, 0xF8, 0x0F, 0xBE,
0x0F, 0xEF, 0xFF, 0xF3, 0xFF, 0xF8, 0xFF, 0xFC, 0x3F, 0xFE, 0x0F, 0x9F,
0xC3, 0xE3, 0xF0, 0xF8, 0x7E, 0x3E, 0x0F, 0xCF, 0x83, 0xFB, 0xE0, 0x7E,
0xF8, 0x0F, 0xC0, 0x07, 0xF0, 0x3F, 0xFE, 0x7F, 0xFE, 0x7F, 0xFC, 0xFC,
0x0C, 0xF8, 0x00, 0xF8, 0x00, 0xFC, 0x00, 0x7F, 0x80, 0x7F, 0xF0, 0x1F,
0xFC, 0x07, 0xFE, 0x00, 0xFF, 0x00, 0x3F, 0x00, 0x1F, 0x00, 0x1F, 0x70,
0x3F, 0x7F, 0xFF, 0xFF, 0xFE, 0xFF, 0xFC, 0x1F, 0xE0, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF0, 0x3E, 0x00, 0x1F, 0x00, 0x0F, 0x80,
0x07, 0xC0, 0x03, 0xE0, 0x01, 0xF0, 0x00, 0xF8, 0x00, 0x7C, 0x00, 0x3E,
0x00, 0x1F, 0x00, 0x0F, 0x80, 0x07, 0xC0, 0x03, 0xE0, 0x01, 0xF0, 0x00,
0xF8, 0x00, 0x7C, 0x00, 0x3E, 0x00, 0xF8, 0x07, 0xFE, 0x01, 0xFF, 0x80,
0x7F, 0xE0, 0x1F, 0xF8, 0x07, 0xFE, 0x01, 0xFF, 0x80, 0x7F, 0xE0, 0x1F,
0xF8, 0x07, 0xFE, 0x01, 0xFF, 0x80, 0x7F, 0xE0, 0x1F, 0xF8, 0x07, 0xFE,
0x01, 0xFF, 0x80, 0x7F, 0xF0, 0x3F, 0x7E, 0x1F, 0x9F, 0xFF, 0xE3, 0xFF,
0xF0, 0x7F, 0xF8, 0x07, 0xF8, 0x00, 0xF8, 0x00, 0xFF, 0xE0, 0x0F, 0x9F,
0x00, 0x7C, 0xFC, 0x03, 0xE3, 0xE0, 0x3E, 0x1F, 0x01, 0xF0, 0xFC, 0x1F,
0x83, 0xE0, 0xF8, 0x1F, 0x07, 0xC0, 0xFC, 0x7E, 0x07, 0xE3, 0xE0, 0x1F,
0x1F, 0x00, 0xFD, 0xF8, 0x07, 0xEF, 0x80, 0x1F, 0x7C, 0x00, 0xFF, 0xE0,
0x03, 0xFE, 0x00, 0x1F, 0xF0, 0x00, 0xFF, 0x80, 0x03, 0xF8, 0x00, 0x1F,
0xC0, 0x00, 0xF8, 0x00, 0x00, 0xFF, 0xC0, 0x00, 0x0F, 0xDF, 0x03, 0xE0,
0x7C, 0xF8, 0x1F, 0x03, 0xE7, 0xC1, 0xFC, 0x1F, 0x3E, 0x0F, 0xE0, 0xF8,
0xF0, 0x7F, 0x0F, 0x87, 0xC3, 0xF8, 0x7C, 0x3E, 0x3D, 0xE3, 0xE1, 0xF1,
0xEF, 0x1F, 0x07, 0x8F, 0x78, 0xF0, 0x3E, 0xFB, 0xEF, 0x81, 0xF7, 0x8F,
0x7C, 0x0F, 0xBC, 0x7B, 0xE0, 0x3D, 0xE3, 0xFE, 0x01, 0xFF, 0x1F, 0xF0,
0x0F, 0xF0, 0x7F, 0x80, 0x3F, 0x83, 0xF8, 0x01, 0xFC, 0x1F, 0xC0, 0x0F,
0xC0, 0x7E, 0x00, 0x3E, 0x03, 0xE0, 0x00, 0xFC, 0x01, 0xFB, 0xF0, 0x1F,
0x8F, 0xC1, 0xF8, 0x3E, 0x0F, 0x81, 0xF8, 0xFC, 0x07, 0xEF, 0xC0, 0x1F,
0xFC, 0x00, 0xFF, 0xE0, 0x03, 0xFE, 0x00, 0x0F, 0xE0, 0x00, 0x3E, 0x00,
0x03, 0xF8, 0x00, 0x3F, 0xE0, 0x03, 0xFF, 0x80, 0x1F, 0xFC, 0x01, 0xFB,
0xF0, 0x1F, 0x8F, 0xC1, 0xF8, 0x3E, 0x0F, 0xC1, 0xF8, 0xFC, 0x07, 0xEF,
0xC0, 0x1F, 0x80, 0xFC, 0x01, 0xFB, 0xE0, 0x1F, 0x9F, 0x80, 0xFC, 0x7E,
0x0F, 0xC1, 0xF0, 0x7C, 0x0F, 0xC7, 0xE0, 0x3F, 0x7E, 0x01, 0xFB, 0xF0,
0x07, 0xFF, 0x00, 0x1F, 0xF0, 0x00, 0xFF, 0x80, 0x03, 0xF8, 0x00, 0x0F,
0x80, 0x00, 0x7C, 0x00, 0x03, 0xE0, 0x00, 0x1F, 0x00, 0x00, 0xF8, 0x00,
0x07, 0xC0, 0x00, 0x3E, 0x00, 0x01, 0xF0, 0x00, 0x0F, 0x80, 0x00, 0x7F,
0xFF, 0xBF, 0xFF, 0xDF, 0xFF, 0xEF, 0xFF, 0xF0, 0x03, 0xF0, 0x03, 0xF0,
0x03, 0xF0, 0x03, 0xF8, 0x01, 0xF8, 0x01, 0xF8, 0x01, 0xF8, 0x01, 0xFC,
0x00, 0xFC, 0x00, 0xFC, 0x00, 0xFE, 0x00, 0x7E, 0x00, 0x7E, 0x00, 0x7F,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF8, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xF8, 0x3E, 0x0F, 0x83, 0xE0, 0xF8, 0x3E, 0x0F, 0x83, 0xE0, 0xF8,
0x3E, 0x0F, 0x83, 0xE0, 0xF8, 0x3E, 0x0F, 0x83, 0xE0, 0xF8, 0x3E, 0x0F,
0x83, 0xE0, 0xF8, 0x3E, 0x0F, 0xFF, 0xFF, 0xFF, 0xFF, 0xF0, 0xF8, 0x01,
0xF8, 0x01, 0xF0, 0x03, 0xE0, 0x07, 0xE0, 0x07, 0xC0, 0x0F, 0x80, 0x1F,
0x80, 0x1F, 0x00, 0x3E, 0x00, 0x7E, 0x00, 0x7C, 0x00, 0xF8, 0x01, 0xF8,
0x01, 0xF0, 0x03, 0xE0, 0x07, 0xE0, 0x07, 0xC0, 0x0F, 0x80, 0x1F, 0x80,
0x1F, 0x00, 0x3E, 0x00, 0x7E, 0x00, 0x7C, 0x00, 0xF8, 0x01, 0xF8, 0x01,
0xF0, 0x03, 0xE0, 0x07, 0xE0, 0x07, 0xC0, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0x07, 0xC1, 0xF0, 0x7C, 0x1F, 0x07, 0xC1, 0xF0, 0x7C, 0x1F, 0x07, 0xC1,
0xF0, 0x7C, 0x1F, 0x07, 0xC1, 0xF0, 0x7C, 0x1F, 0x07, 0xC1, 0xF0, 0x7C,
0x1F, 0x07, 0xC1, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF0, 0x01, 0xC0, 0x01,
0xF0, 0x01, 0xFC, 0x00, 0xFE, 0x00, 0xFF, 0x80, 0x7B, 0xC0, 0x7D, 0xF0,
0x7C, 0x7C, 0x3C, 0x1E, 0x3E, 0x0F, 0xBE, 0x03, 0xEF, 0x01, 0xE1, 0x00,
0x40, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x20, 0xE3, 0xE3,
0xE3, 0xE3, 0x82, 0x00, 0x1F, 0xE0, 0xFF, 0xC3, 0xFF, 0x8F, 0xFF, 0x00,
0xFC, 0x01, 0xF1, 0xFF, 0xDF, 0xFF, 0x7F, 0xFF, 0xF1, 0xFF, 0x87, 0xFE,
0x1F, 0xFF, 0xFD, 0xFF, 0xF7, 0xFF, 0xC7, 0xFE, 0x38, 0x00, 0xF8, 0x00,
0xF8, 0x00, 0xF8, 0x00, 0xF8, 0x00, 0xF8, 0x00, 0xF8, 0x00, 0xF8, 0x00,
0xFB, 0xE0, 0xFF, 0xF8, 0xFF, 0xFC, 0xFF, 0xFE, 0xF8, 0x3E, 0xF8, 0x3F,
0xF8, 0x1F, 0xF8, 0x1F, 0xF8, 0x1F, 0xF8, 0x1F, 0xF8, 0x3F, 0xF8, 0x7E,
0xFF, 0xFE, 0xFF, 0xFC, 0xFF, 0xF8, 0x3F, 0xE0, 0x03, 0xF8, 0x3F, 0xF3,
0xFF, 0xDF, 0xFE, 0x7E, 0x03, 0xF0, 0x0F, 0x80, 0x3E, 0x00, 0xF8, 0x03,
0xE0, 0x0F, 0xC0, 0x1F, 0x80, 0x7F, 0xFC, 0xFF, 0xF1, 0xFF, 0xC1, 0xFE,
0x00, 0x07, 0x00, 0x1F, 0x00, 0x1F, 0x00, 0x1F, 0x00, 0x1F, 0x00, 0x1F,
0x00, 0x1F, 0x00, 0x1F, 0x07, 0xFF, 0x1F, 0xFF, 0x3F, 0xFF, 0x7F, 0xFF,
0x7C, 0x1F, 0xF8, 0x1F, 0xF8, 0x1F, 0xF8, 0x1F, 0xF8, 0x1F, 0xF8, 0x1F,
0xFC, 0x1F, 0x7E, 0x1F, 0x7F, 0xFF, 0x3F, 0xFF, 0x1F, 0xFF, 0x07, 0xFC,
0x07, 0xE0, 0x1F, 0xF8, 0x3F, 0xFC, 0x7F, 0xFE, 0x7C, 0x3F, 0xF8, 0x1F,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF8, 0x00, 0xFC, 0x00, 0x7E, 0x04,
0x7F, 0xFE, 0x3F, 0xFE, 0x1F, 0xFE, 0x07, 0xF8, 0x0F, 0xE3, 0xFF, 0x7F,
0xE7, 0xFE, 0xFC, 0x0F, 0x80, 0xF8, 0x0F, 0x80, 0xFF, 0xEF, 0xFE, 0xFF,
0xEF, 0xFE, 0xF8, 0x0F, 0x80, 0xF8, 0x0F, 0x80, 0xF8, 0x0F, 0x80, 0xF8,
0x0F, 0x80, 0xF8, 0x0F, 0x80, 0xF8, 0x0F, 0x80, 0x07, 0xF8, 0x3F, 0xFC,
0xFF, 0xFB, 0xFF, 0xF7, 0xC3, 0xFF, 0x87, 0xFE, 0x0F, 0xFC, 0x1F, 0xF8,
0x3F, 0xF0, 0x7F, 0xF0, 0xFB, 0xFF, 0xF7, 0xFF, 0xE7, 0xFF, 0xC3, 0xFF,
0x80, 0x1F, 0x00, 0x3E, 0x40, 0xFD, 0xFF, 0xF3, 0xFF, 0xE7, 0xFF, 0x07,
0xF8, 0x00, 0x38, 0x03, 0xE0, 0x0F, 0x80, 0x3E, 0x00, 0xF8, 0x03, 0xE0,
0x0F, 0x80, 0x3E, 0x00, 0xFF, 0xE3, 0xFF, 0xCF, 0xFF, 0xBF, 0xFE, 0xF8,
0xFF, 0xE1, 0xFF, 0x87, 0xFE, 0x1F, 0xF8, 0x7F, 0xE1, 0xFF, 0x87, 0xFE,
0x1F, 0xF8, 0x7F, 0xE1, 0xFF, 0x87, 0xFE, 0x1F, 0x77, 0xFF, 0xF7, 0x00,
0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x07,
0x07, 0xC3, 0xE1, 0xF0, 0x70, 0x00, 0x00, 0x00, 0x0F, 0x87, 0xC3, 0xE1,
0xF0, 0xF8, 0x7C, 0x3E, 0x1F, 0x0F, 0x87, 0xC3, 0xE1, 0xF0, 0xF8, 0x7C,
0x3E, 0x1F, 0x0F, 0x8F, 0xFF, 0xFF, 0xEF, 0xE7, 0xE0, 0x38, 0x01, 0xF0,
0x03, 0xE0, 0x07, 0xC0, 0x0F, 0x80, 0x1F, 0x00, 0x3E, 0x00, 0x7C, 0x00,
0xF8, 0x7F, 0xF1, 0xFB, 0xE7, 0xE7, 0xDF, 0x8F, 0xFF, 0x1F, 0xFC, 0x3F,
0xF0, 0x7F, 0xC0, 0xFF, 0xC1, 0xFF, 0xC3, 0xFF, 0xC7, 0xDF, 0x8F, 0x9F,
0x9F, 0x1F, 0xBE, 0x3F, 0x7C, 0x3F, 0x38, 0xF8, 0xF8, 0xF8, 0xF8, 0xF8,
0xF8, 0xF8, 0xF8, 0xF8, 0xF8, 0xF8, 0xF8, 0xF8, 0xF8, 0xF8, 0xF8, 0xF8,
0xF8, 0xFC, 0xFF, 0x7F, 0x7F, 0x1E, 0x3F, 0xC3, 0xF1, 0xFF, 0xFF, 0xF3,
0xFF, 0xFF, 0xF7, 0xFF, 0xFF, 0xFF, 0x8F, 0xC7, 0xFF, 0x0F, 0x87, 0xFE,
0x1F, 0x0F, 0xFC, 0x3E, 0x1F, 0xF8, 0x7C, 0x3F, 0xF0, 0xF8, 0x7F, 0xE1,
0xF0, 0xFF, 0xC3, 0xE1, 0xFF, 0x87, 0xC3, 0xFF, 0x0F, 0x87, 0xFE, 0x1F,
0x0F, 0xFC, 0x3E, 0x1F, 0x3F, 0xC3, 0xFF, 0xCF, 0xFF, 0xBF, 0xFE, 0xF8,
0xFF, 0xE1, 0xFF, 0x87, 0xFE, 0x1F, 0xF8, 0x7F, 0xE1, 0xFF, 0x87, 0xFE,
0x1F, 0xF8, 0x7F, 0xE1, 0xFF, 0x87, 0xFE, 0x1F, 0x07, 0xF0, 0x0F, 0xFE,
0x0F, 0xFF, 0x8F, 0xFF, 0xE7, 0xE3, 0xF7, 0xE0, 0xFF, 0xE0, 0x3F, 0xF0,
0x1F, 0xF8, 0x0F, 0xFC, 0x07, 0xFF, 0x07, 0xEF, 0xC7, 0xE7, 0xFF, 0xF1,
0xFF, 0xF0, 0x7F, 0xF0, 0x0F, 0xE0, 0x3F, 0xE0, 0xFF, 0xF8, 0xFF, 0xFC,
0xFF, 0xFE, 0xF8, 0x7E, 0xF8, 0x3F, 0xF8, 0x1F, 0xF8, 0x1F, 0xF8, 0x1F,
0xF8, 0x1F, 0xF8, 0x3F, 0xF8, 0x3E, 0xFF, 0xFE, 0xFF, 0xFC, 0xFF, 0xF8,
0xFB, 0xF0, 0xF8, 0x00, 0xF8, 0x00, 0xF8, 0x00, 0xF8, 0x00, 0xF8, 0x00,
0xF8, 0x00, 0x03, 0xFE, 0x0F, 0xFF, 0xCF, 0xFF, 0xEF, 0xFF, 0xF7, 0xE0,
0xFF, 0xE0, 0x7F, 0xE0, 0x3F, 0xF0, 0x1F, 0xF8, 0x0F, 0xFC, 0x07, 0xFF,
0x03, 0xEF, 0xC1, 0xF7, 0xFF, 0xF9, 0xFF, 0xFC, 0x7F, 0xFE, 0x0F, 0xDF,
0x00, 0x0F, 0x80, 0x07, 0xC0, 0x03, 0xE0, 0x01, 0xF0, 0x00, 0xF8, 0x00,
0x7C, 0x1F, 0xFF, 0xFF, 0xFF, 0xFF, 0xEF, 0x81, 0xF0, 0x3E, 0x07, 0xC0,
0xF8, 0x1F, 0x03, 0xE0, 0x7C, 0x0F, 0x81, 0xF0, 0x3E, 0x07, 0xC0, 0x0F,
0xE1, 0xFF, 0x9F, 0xFD, 0xFF, 0xEF, 0x81, 0x7C, 0x03, 0xFF, 0xDF, 0xFE,
0x7F, 0xF9, 0xFF, 0xC0, 0x3F, 0x81, 0xFF, 0xFF, 0xFF, 0xFB, 0xFF, 0x8F,
0xF0, 0x38, 0x0F, 0x80, 0xF8, 0x0F, 0x80, 0xF8, 0x0F, 0xFE, 0xFF, 0xEF,
0xFE, 0xFF, 0xEF, 0x80, 0xF8, 0x0F, 0x80, 0xF8, 0x0F, 0x80, 0xF8, 0x0F,
0x80, 0xFC, 0x0F, 0xFE, 0x7F, 0xE3, 0xFF, 0x0F, 0xE0, 0xF8, 0x7F, 0xE1,
0xFF, 0x87, 0xFE, 0x1F, 0xF8, 0x7F, 0xE1, 0xFF, 0x87, 0xFE, 0x1F, 0xF8,
0x7F, 0xE1, 0xFF, 0x87, 0xFF, 0x1F, 0x7F, 0xFD, 0xFF, 0xF3, 0xFF, 0xC3,
0xFC, 0xF8, 0x0F, 0xFE, 0x0F, 0xDF, 0x07, 0xCF, 0x83, 0xE7, 0xE3, 0xF1,
0xF1, 0xF0, 0xF8, 0xF8, 0x7E, 0xFC, 0x1F, 0x7C, 0x0F, 0xBE, 0x03, 0xFE,
0x01, 0xFF, 0x00, 0xFF, 0x00, 0x3F, 0x80, 0x1F, 0xC0, 0x07, 0xC0, 0xF8,
0x38, 0x1F, 0xF8, 0x3C, 0x1F, 0x7C, 0x7C, 0x3E, 0x7C, 0x7C, 0x3E, 0x7C,
0x7E, 0x3E, 0x3C, 0x7E, 0x3C, 0x3C, 0xFE, 0x3C, 0x3E, 0xEF, 0x7C, 0x1E,
0xEF, 0x78, 0x1E, 0xEF, 0x78, 0x1F, 0xE7, 0xF8, 0x0F, 0xC7, 0xF0, 0x0F,
0xC3, 0xF0, 0x0F, 0xC3, 0xF0, 0x07, 0x83, 0xE0, 0x07, 0x81, 0xE0, 0xFC,
0x1F, 0xBF, 0x1F, 0x8F, 0xDF, 0x87, 0xEF, 0xC1, 0xFF, 0xC0, 0x7F, 0xC0,
0x3F, 0xE0, 0x0F, 0xE0, 0x07, 0xF0, 0x07, 0xFC, 0x03, 0xFE, 0x03, 0xFF,
0x83, 0xF7, 0xE1, 0xFB, 0xF1, 0xF8, 0xFD, 0xF8, 0x3F, 0xF8, 0x0F, 0xFE,
0x0F, 0xDF, 0x07, 0xCF, 0x83, 0xE7, 0xE3, 0xF1, 0xF1, 0xF0, 0xF8, 0xF8,
0x7C, 0x78, 0x1F, 0x7C, 0x0F, 0xBE, 0x03, 0xDE, 0x01, 0xFF, 0x00, 0x7F,
0x00, 0x3F, 0x80, 0x0F, 0xC0, 0x07, 0xC0, 0x03, 0xE0, 0x03, 0xE0, 0x1F,
0xF0, 0x1F, 0xF0, 0x0F, 0xF0, 0x03, 0xE0, 0x00, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0x03, 0xF8, 0x0F, 0xC0, 0x7E, 0x03, 0xF8, 0x1F, 0xC0,
0xFE, 0x03, 0xF0, 0x1F, 0xC0, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0x03, 0xE1, 0xFC, 0x3F, 0x8F, 0xF1, 0xF8, 0x3E, 0x07, 0xC0, 0xF8, 0x1F,
0x03, 0xE0, 0x7C, 0x0F, 0x83, 0xF1, 0xFC, 0x3F, 0x87, 0xF0, 0xFE, 0x07,
0xE0, 0x7C, 0x0F, 0x81, 0xF0, 0x3E, 0x07, 0xC0, 0xF8, 0x1F, 0x03, 0xF0,
0x7F, 0x87, 0xF0, 0xFE, 0x07, 0xC0, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF8, 0x1F, 0x83,
0xF8, 0x7F, 0x83, 0xF0, 0x3E, 0x07, 0xC0, 0xF8, 0x1F, 0x03, 0xE0, 0x7C,
0x0F, 0x81, 0xF8, 0x1F, 0xC3, 0xF8, 0x7F, 0x0F, 0xE3, 0xF0, 0x7C, 0x0F,
0x81, 0xF0, 0x3E, 0x07, 0xC0, 0xF8, 0x1F, 0x07, 0xE3, 0xFC, 0x7F, 0x0F,
0xE1, 0xF0, 0x00, 0x1E, 0x06, 0x3F, 0x8F, 0x7F, 0xFF, 0xFF, 0xFE, 0xF1,
0xFC, 0x60, 0x78, 0xFF, 0xFF, 0xFF, 0xC0, 0x3C, 0x03, 0xC0, 0x3C, 0x03,
0xC0, 0x3C, 0x03, 0xC0, 0x3C, 0x03, 0xC0, 0x3C, 0x03, 0xC0, 0x3C, 0x03,
0xC0, 0x3C, 0x03, 0xC0, 0x3C, 0x03, 0xC0, 0x3C, 0x03, 0xC0, 0x3F, 0xFF,
0xFF, 0xF0, 0x00, 0x77, 0xFF, 0xFF, 0xB8, 0x0E, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFC, 0x01, 0xE0, 0x07, 0x80, 0x1E, 0x00, 0x78,
0x03, 0xF8, 0x3F, 0xF3, 0xFF, 0xDF, 0xFE, 0x7E, 0x03, 0xF0, 0x0F, 0x80,
0x3E, 0x00, 0xF8, 0x03, 0xF0, 0x07, 0xE0, 0x1F, 0xFF, 0x3F, 0xFC, 0x7F,
0xF0, 0x3F, 0x80, 0x78, 0x01, 0xE0, 0x07, 0x80, 0x1E, 0x00, 0x01, 0xFC,
0x0F, 0xFC, 0x3F, 0xF8, 0x7F, 0xE1, 0xF8, 0x43, 0xE0, 0x07, 0xC0, 0x0F,
0x80, 0x1F, 0x01, 0xFF, 0xE3, 0xFF, 0xC7, 0xFF, 0x8F, 0xFF, 0x03, 0xE0,
0x07, 0xC0, 0x0F, 0x80, 0x1F, 0x00, 0x3F, 0xFC, 0xFF, 0xF9, 0xFF, 0xF3,
0xFF, 0xE0, 0x10, 0x04, 0x3C, 0x03, 0xBE, 0xFB, 0xEF, 0xFF, 0xE3, 0xFF,
0xE1, 0xF1, 0xF0, 0xF0, 0x78, 0x78, 0x3C, 0x3C, 0x1E, 0x1E, 0x0F, 0x0F,
0x8F, 0x87, 0xFF, 0xC7, 0xFF, 0xF7, 0xDF, 0x7D, 0xC0, 0x3C, 0x20, 0x08,
0xFC, 0x07, 0xEF, 0x80, 0xF9, 0xF8, 0x3F, 0x1F, 0x07, 0xC3, 0xF1, 0xF8,
0x3E, 0x7E, 0x07, 0xEF, 0x80, 0x7F, 0xF0, 0x07, 0xFC, 0x00, 0xFF, 0x80,
0xFF, 0xFE, 0x1F, 0xFF, 0xC3, 0xFF, 0xF8, 0x03, 0xE0, 0x00, 0x7C, 0x01,
0xFF, 0xFC, 0x3F, 0xFF, 0x87, 0xFF, 0xF0, 0x07, 0xC0, 0x00, 0xF8, 0x00,
0x1F, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x07, 0xF0, 0x7F, 0xF3, 0xFF, 0xDF, 0xFE,
0x7C, 0x09, 0xF0, 0x07, 0xE0, 0x0F, 0xE0, 0x3F, 0xE0, 0xFF, 0xE7, 0xFF,
0xBE, 0x7F, 0xF8, 0xFF, 0xE1, 0xFF, 0xC7, 0xFF, 0x9E, 0x7F, 0xF0, 0xFF,
0xC1, 0xFF, 0x01, 0xFE, 0x01, 0xF8, 0x03, 0xE6, 0x0F, 0x9F, 0xFE, 0xFF,
0xF3, 0xFF, 0xC3, 0xFC, 0x00, 0x70, 0xEF, 0x9F, 0xF9, 0xFF, 0x9F, 0x70,
0xE0, 0x01, 0xFC, 0x00, 0x3F, 0xF8, 0x03, 0xFF, 0xE0, 0x3E, 0x0F, 0x83,
0xC0, 0x1E, 0x3C, 0x7E, 0x79, 0xC7, 0xF9, 0xDE, 0x7F, 0x8F, 0xE7, 0xC0,
0x3F, 0x3C, 0x01, 0xF9, 0xE0, 0x0F, 0xCF, 0x00, 0x7E, 0x78, 0x03, 0xF3,
0xE0, 0x1F, 0xCF, 0xF1, 0xEE, 0x3F, 0xCE, 0x78, 0xFC, 0xF1, 0xE0, 0x0F,
0x07, 0xC1, 0xF0, 0x1F, 0xFF, 0x00, 0x7F, 0xF0, 0x00, 0xFE, 0x00, 0x7F,
0x1F, 0xE7, 0xFC, 0x0F, 0x01, 0xCF, 0xF7, 0xFF, 0xC7, 0xE1, 0xF8, 0x7F,
0xFD, 0xFF, 0x3F, 0x80, 0x00, 0x00, 0x0E, 0x0E, 0x1F, 0x1F, 0x1E, 0x1E,
0x3E, 0x3E, 0x7C, 0x7C, 0x7C, 0x7C, 0xF8, 0xF8, 0x7C, 0x7C, 0x7C, 0x7C,
0x3E, 0x3E, 0x1E, 0x1E, 0x1F, 0x1F, 0x0E, 0x0E, 0x00, 0x00, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF0, 0x01, 0xE0, 0x03, 0xC0, 0x07, 0x80,
0x0F, 0x00, 0x1E, 0x00, 0x3C, 0x00, 0x78, 0xFF, 0xFF, 0xFF, 0xFF, 0xF0,
0x01, 0xFC, 0x00, 0x3F, 0xF8, 0x03, 0xFF, 0xE0, 0x3E, 0x0F, 0x83, 0xC0,
0x1E, 0x3C, 0x00, 0x79, 0xC7, 0xE1, 0xDE, 0x3F, 0xCF, 0xE1, 0xCE, 0x3F,
0x0E, 0x71, 0xF8, 0x73, 0x8F, 0xC3, 0xF8, 0x7E, 0x1F, 0xC3, 0xF0, 0xEE,
0x1F, 0xC7, 0x39, 0xEE, 0x39, 0xCE, 0x78, 0x00, 0xF1, 0xE0, 0x0F, 0x07,
0xC1, 0xF0, 0x1F, 0xFF, 0x00, 0x7F, 0xF0, 0x00, 0xFE, 0x00, 0xFF, 0xFF,
0xFF, 0xFC, 0x3E, 0x3F, 0xBF, 0xFC, 0x7E, 0x3F, 0x1F, 0xFE, 0xFE, 0x3E,
0x00, 0x07, 0x80, 0x1E, 0x00, 0x78, 0x01, 0xE0, 0x07, 0x83, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFC, 0x1E, 0x00, 0x78, 0x01, 0xE0, 0x07, 0x80,
0x1E, 0x00, 0x00, 0x3F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xC0, 0x3F,
0x3F, 0xEF, 0xFD, 0x87, 0x01, 0xC0, 0xF0, 0x78, 0x38, 0x1C, 0x0E, 0x07,
0xFD, 0xFF, 0x7F, 0xC0, 0x3C, 0x7F, 0x1F, 0xC8, 0xE0, 0x70, 0xF8, 0x7C,
0x3F, 0x03, 0x81, 0xFF, 0xFF, 0xE7, 0xE0, 0x08, 0x38, 0xFB, 0xEF, 0x8E,
0x08, 0x00, 0xF8, 0x7F, 0xE1, 0xFF, 0x87, 0xFE, 0x1F, 0xF8, 0x7F, 0xE1,
0xFF, 0x87, 0xFE, 0x1F, 0xF8, 0x7F, 0xE1, 0xFF, 0x87, 0xFF, 0x1F, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xF8, 0x03, 0xE0, 0x0F, 0x80, 0x3E,
0x00, 0xF8, 0x03, 0xE0, 0x00, 0x00, 0xFE, 0x01, 0xFF, 0xFC, 0xFF, 0xFF,
0xBF, 0xFF, 0xF7, 0xFE, 0x3F, 0xFF, 0xC7, 0xFF, 0xF8, 0xFF, 0xFF, 0x1F,
0xFF, 0xE3, 0xFF, 0xFC, 0x7D, 0xFF, 0x8F, 0xBF, 0xF1, 0xF1, 0xFE, 0x3E,
0x0F, 0xC7, 0xC0, 0x78, 0xF8, 0x0F, 0x1F, 0x01, 0xE3, 0xE0, 0x3C, 0x7C,
0x07, 0x8F, 0x80, 0xF1, 0xF0, 0x1E, 0x3E, 0x03, 0xC7, 0xC0, 0x78, 0xF8,
0x0F, 0x1F, 0x01, 0xE3, 0xE0, 0x3C, 0x7C, 0x07, 0x8F, 0x80, 0xF1, 0xF0,
0x7B, 0xFF, 0xFF, 0xFD, 0xE0, 0x1E, 0x38, 0xF0, 0xF0, 0xEF, 0xDF, 0xBC,
0x0E, 0x3D, 0xFF, 0xF7, 0xE9, 0xC3, 0x87, 0x0E, 0x1C, 0x38, 0x70, 0xE0,
0x1F, 0x83, 0xFC, 0x7F, 0xE7, 0x0E, 0xE0, 0x7E, 0x07, 0xE0, 0x7E, 0x07,
0xE0, 0x77, 0x0E, 0x7F, 0xE3, 0xFC, 0x1F, 0x80, 0x00, 0x00, 0x78, 0x78,
0xF8, 0xF8, 0x7C, 0x7C, 0x7C, 0x7C, 0x3E, 0x3E, 0x3E, 0x3E, 0x1F, 0x1F,
0x3E, 0x3E, 0x3E, 0x3E, 0x7C, 0x7C, 0x7C, 0x7C, 0xF8, 0xF8, 0x78, 0x78,
0x00, 0x00, 0x0E, 0x00, 0x00, 0x07, 0x80, 0x3E, 0x07, 0xE0, 0x0F, 0x03,
0xF8, 0x07, 0xC0, 0x7E, 0x01, 0xE0, 0x13, 0x80, 0xF0, 0x00, 0xE0, 0x3C,
0x00, 0x38, 0x1E, 0x00, 0x0E, 0x07, 0x80, 0x03, 0x83, 0xC1, 0xE0, 0xE0,
0xF0, 0xF8, 0x38, 0x78, 0x3E, 0x0E, 0x1E, 0x1F, 0x80, 0x0F, 0x0E, 0xE0,
0x03, 0xC3, 0x38, 0x01, 0xE1, 0xFF, 0x00, 0x78, 0x7F, 0xC0, 0x3C, 0x1F,
0xF0, 0x0F, 0x00, 0x38, 0x07, 0x80, 0x0E, 0x03, 0xE0, 0x03, 0x80, 0xF0,
0x00, 0x00, 0x0E, 0x00, 0x00, 0x07, 0x80, 0x7C, 0x07, 0xE0, 0x1E, 0x03,
0xF8, 0x0F, 0x80, 0x7E, 0x03, 0xC0, 0x13, 0x81, 0xE0, 0x00, 0xE0, 0x78,
0x00, 0x38, 0x3C, 0x00, 0x0E, 0x0F, 0x00, 0x03, 0x87, 0x8F, 0xC0, 0xE1,
0xEF, 0xF8, 0x38, 0xF3, 0xFF, 0x0E, 0x3C, 0x61, 0xC0, 0x1E, 0x00, 0x70,
0x07, 0x80, 0x3C, 0x03, 0xC0, 0x1E, 0x00, 0xF0, 0x0E, 0x00, 0x78, 0x07,
0x00, 0x1E, 0x03, 0x80, 0x0F, 0x01, 0xFF, 0x07, 0xC0, 0x7F, 0xC1, 0xE0,
0x1F, 0xF0, 0x3C, 0x00, 0x00, 0x7F, 0x00, 0x7C, 0x1F, 0xC0, 0x3C, 0x08,
0xE0, 0x3E, 0x00, 0x70, 0x1E, 0x00, 0xF8, 0x1E, 0x00, 0x7C, 0x0F, 0x00,
0x3F, 0x0F, 0x00, 0x03, 0x87, 0x80, 0x01, 0xC7, 0x87, 0xBF, 0xE3, 0xC7,
0xDF, 0xE3, 0xC3, 0xE7, 0xE1, 0xE3, 0xF0, 0x01, 0xE3, 0xB8, 0x00, 0xF1,
0x9C, 0x00, 0xF1, 0xFF, 0x00, 0x78, 0xFF, 0x80, 0x78, 0x7F, 0xC0, 0x3C,
0x01, 0xC0, 0x3C, 0x00, 0xE0, 0x3E, 0x00, 0x70, 0x1E, 0x00, 0x00, 0x07,
0x80, 0xFC, 0x0F, 0xC0, 0xFC, 0x0F, 0xC0, 0x78, 0x00, 0x00, 0x00, 0x07,
0x80, 0x78, 0x0F, 0x81, 0xF0, 0x3F, 0x07, 0xE0, 0x7C, 0x0F, 0x80, 0xF8,
0x0F, 0x86, 0xFF, 0xE7, 0xFF, 0x7F, 0xF1, 0xFC, 0x00, 0x40, 0x00, 0x07,
0x00, 0x00, 0x7C, 0x00, 0x01, 0xF0, 0x00, 0x07, 0xC0, 0x00, 0x1C, 0x00,
0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0xE0, 0x00, 0x7F,
0x80, 0x03, 0xFC, 0x00, 0x3F, 0xE0, 0x01, 0xFF, 0x80, 0x1F, 0x7C, 0x00,
0xFB, 0xF0, 0x07, 0xDF, 0x80, 0x7C, 0x7C, 0x03, 0xE3, 0xF0, 0x1F, 0x1F,
0x81, 0xF0, 0x7C, 0x0F, 0xFF, 0xE0, 0xFF, 0xFF, 0x87, 0xFF, 0xFC, 0x3F,
0xFF, 0xE3, 0xF0, 0x1F, 0x9F, 0x00, 0x7C, 0xF8, 0x03, 0xEF, 0xC0, 0x1F,
0xFC, 0x00, 0x7C, 0x00, 0x10, 0x00, 0x01, 0xC0, 0x00, 0x1F, 0x00, 0x01,
0xF0, 0x00, 0x1F, 0x00, 0x00, 0x70, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x07, 0xE0, 0x00, 0x7F, 0x80, 0x03, 0xFC, 0x00, 0x3F,
0xE0, 0x01, 0xFF, 0x80, 0x1F, 0x7C, 0x00, 0xFB, 0xF0, 0x07, 0xDF, 0x80,
0x7C, 0x7C, 0x03, 0xE3, 0xF0, 0x1F, 0x1F, 0x81, 0xF0, 0x7C, 0x0F, 0xFF,
0xE0, 0xFF, 0xFF, 0x87, 0xFF, 0xFC, 0x3F, 0xFF, 0xE3, 0xF0, 0x1F, 0x9F,
0x00, 0x7C, 0xF8, 0x03, 0xEF, 0xC0, 0x1F, 0xFC, 0x00, 0x7C, 0x00, 0x20,
0x00, 0x03, 0x80, 0x00, 0x3E, 0x00, 0x03, 0xF8, 0x00, 0x3D, 0xE0, 0x00,
0x82, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFC, 0x00, 0x0F, 0xF0,
0x00, 0x7F, 0x80, 0x07, 0xFC, 0x00, 0x3F, 0xF0, 0x03, 0xEF, 0x80, 0x1F,
0x7E, 0x00, 0xFB, 0xF0, 0x0F, 0x8F, 0x80, 0x7C, 0x7E, 0x03, 0xE3, 0xF0,
0x3E, 0x0F, 0x81, 0xFF, 0xFC, 0x1F, 0xFF, 0xF0, 0xFF, 0xFF, 0x87, 0xFF,
0xFC, 0x7E, 0x03, 0xF3, 0xE0, 0x0F, 0x9F, 0x00, 0x7D, 0xF8, 0x03, 0xFF,
0x80, 0x0F, 0x80, 0x01, 0xC6, 0x00, 0x1F, 0xF8, 0x01, 0xFF, 0x80, 0x06,
0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1F, 0x80,
0x01, 0xFE, 0x00, 0x0F, 0xF0, 0x00, 0xFF, 0x80, 0x07, 0xFE, 0x00, 0x7D,
0xF0, 0x03, 0xEF, 0xC0, 0x1F, 0x7E, 0x01, 0xF1, 0xF0, 0x0F, 0x8F, 0xC0,
0x7C, 0x7E, 0x07, 0xC1, 0xF0, 0x3F, 0xFF, 0x83, 0xFF, 0xFE, 0x1F, 0xFF,
0xF0, 0xFF, 0xFF, 0x8F, 0xC0, 0x7E, 0x7C, 0x01, 0xF3, 0xE0, 0x0F, 0xBF,
0x00, 0x7F, 0xF0, 0x01, 0xF0, 0x07, 0x0E, 0x00, 0x7C, 0xF8, 0x03, 0xE7,
0xC0, 0x1F, 0x3E, 0x00, 0x70, 0xE0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0xFC, 0x00, 0x0F, 0xF0, 0x00, 0x7F, 0x80, 0x07, 0xFC,
0x00, 0x3F, 0xF0, 0x03, 0xEF, 0x80, 0x1F, 0x7E, 0x00, 0xFB, 0xF0, 0x0F,
0x8F, 0x80, 0x7C, 0x7E, 0x03, 0xE3, 0xF0, 0x3E, 0x0F, 0x81, 0xFF, 0xFC,
0x1F, 0xFF, 0xF0, 0xFF, 0xFF, 0x87, 0xFF, 0xFC, 0x7E, 0x03, 0xF3, 0xE0,
0x0F, 0x9F, 0x00, 0x7D, 0xF8, 0x03, 0xFF, 0x80, 0x0F, 0x80, 0x00, 0x70,
0x00, 0x07, 0xC0, 0x00, 0x63, 0x00, 0x03, 0x18, 0x00, 0x18, 0xC0, 0x00,
0xFE, 0x00, 0x07, 0xF0, 0x00, 0x3F, 0xC0, 0x01, 0xFE, 0x00, 0x1F, 0xF8,
0x00, 0xFF, 0xC0, 0x0F, 0xBE, 0x00, 0x7D, 0xF8, 0x03, 0xEF, 0xC0, 0x3E,
0x3E, 0x01, 0xF1, 0xF8, 0x1F, 0x8F, 0xC0, 0xF8, 0x3E, 0x07, 0xFF, 0xF8,
0x7F, 0xFF, 0xC3, 0xFF, 0xFE, 0x1F, 0xFF, 0xF1, 0xF8, 0x0F, 0xCF, 0x80,
0x3E, 0x7C, 0x01, 0xF7, 0xE0, 0x0F, 0xFE, 0x00, 0x3E, 0x00, 0x0F, 0xFF,
0xF8, 0x00, 0x7F, 0xFF, 0xE0, 0x01, 0xFF, 0xFF, 0x80, 0x0F, 0xFF, 0xFE,
0x00, 0x3F, 0xF0, 0x00, 0x01, 0xFF, 0xC0, 0x00, 0x0F, 0xDF, 0x00, 0x00,
0x3F, 0x7C, 0x00, 0x01, 0xF9, 0xFF, 0xF0, 0x07, 0xC7, 0xFF, 0xC0, 0x3F,
0x1F, 0xFF, 0x01, 0xF8, 0x7F, 0xFC, 0x07, 0xFF, 0xF0, 0x00, 0x3F, 0xFF,
0xC0, 0x00, 0xFF, 0xFF, 0x00, 0x07, 0xFF, 0xFC, 0x00, 0x1F, 0x01, 0xF0,
0x00, 0xFC, 0x07, 0xFF, 0xF7, 0xE0, 0x1F, 0xFF, 0xDF, 0x80, 0x7F, 0xFF,
0xFC, 0x01, 0xFF, 0xFC, 0x01, 0xFC, 0x03, 0xFF, 0xC7, 0xFF, 0xE7, 0xFF,
0xE3, 0xF0, 0x33, 0xF0, 0x01, 0xF0, 0x01, 0xF0, 0x00, 0xF8, 0x00, 0x7C,
0x00, 0x3E, 0x00, 0x1F, 0x00, 0x0F, 0x80, 0x07, 0xC0, 0x03, 0xF0, 0x00,
0xFC, 0x00, 0x7F, 0x03, 0x1F, 0xFF, 0x87, 0xFF, 0xE1, 0xFF, 0xF0, 0x1F,
0xC0, 0x0F, 0x00, 0x07, 0x80, 0x00, 0xE0, 0x00, 0x70, 0x01, 0xF8, 0x00,
0xF8, 0x00, 0xF8, 0x00, 0x02, 0x00, 0x07, 0x00, 0x0F, 0x80, 0x07, 0xC0,
0x03, 0xE0, 0x01, 0xC0, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFE,
0xFF, 0xFE, 0xFF, 0xFE, 0xFF, 0xFE, 0xF8, 0x00, 0xF8, 0x00, 0xF8, 0x00,
0xF8, 0x00, 0xFF, 0xFC, 0xFF, 0xFC, 0xFF, 0xFC, 0xFF, 0xFC, 0xF8, 0x00,
0xF8, 0x00, 0xF8, 0x00, 0xF8, 0x00, 0xF8, 0x00, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x80, 0x01, 0xC0, 0x03, 0xE0, 0x07, 0xC0,
0x0F, 0x80, 0x07, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFE,
0xFF, 0xFE, 0xFF, 0xFE, 0xFF, 0xFE, 0xF8, 0x00, 0xF8, 0x00, 0xF8, 0x00,
0xF8, 0x00, 0xFF, 0xFC, 0xFF, 0xFC, 0xFF, 0xFC, 0xFF, 0xFC, 0xF8, 0x00,
0xF8, 0x00, 0xF8, 0x00, 0xF8, 0x00, 0xF8, 0x00, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0x01, 0x00, 0x03, 0x80, 0x07, 0xC0, 0x0F, 0xE0,
0x1E, 0xF0, 0x08, 0x20, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFE, 0xFF, 0xFE,
0xFF, 0xFE, 0xFF, 0xFE, 0xF8, 0x00, 0xF8, 0x00, 0xF8, 0x00, 0xF8, 0x00,
0xFF, 0xFC, 0xFF, 0xFC, 0xFF, 0xFC, 0xFF, 0xFC, 0xF8, 0x00, 0xF8, 0x00,
0xF8, 0x00, 0xF8, 0x00, 0xF8, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0x1C, 0x38, 0x3E, 0x7C, 0x3E, 0x7C, 0x3E, 0x7C, 0x1C, 0x38,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFE, 0xFF, 0xFE, 0xFF, 0xFE,
0xFF, 0xFE, 0xF8, 0x00, 0xF8, 0x00, 0xF8, 0x00, 0xF8, 0x00, 0xFF, 0xFC,
0xFF, 0xFC, 0xFF, 0xFC, 0xFF, 0xFC, 0xF8, 0x00, 0xF8, 0x00, 0xF8, 0x00,
0xF8, 0x00, 0xF8, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0x20, 0xE3, 0xE3, 0xE3, 0xE3, 0x82, 0x00, 0x00, 0xF9, 0xF3, 0xE7, 0xCF,
0x9F, 0x3E, 0x7C, 0xF9, 0xF3, 0xE7, 0xCF, 0x9F, 0x3E, 0x7C, 0xF9, 0xF3,
0xE7, 0xCF, 0x80, 0x08, 0x38, 0xFB, 0xEF, 0x8E, 0x08, 0x00, 0x00, 0xF9,
0xF3, 0xE7, 0xCF, 0x9F, 0x3E, 0x7C, 0xF9, 0xF3, 0xE7, 0xCF, 0x9F, 0x3E,
0x7C, 0xF9, 0xF3, 0xE7, 0xCF, 0x80, 0x08, 0x0E, 0x0F, 0x8F, 0xEF, 0x7A,
0x08, 0x00, 0x00, 0x3E, 0x1F, 0x0F, 0x87, 0xC3, 0xE1, 0xF0, 0xF8, 0x7C,
0x3E, 0x1F, 0x0F, 0x87, 0xC3, 0xE1, 0xF0, 0xF8, 0x7C, 0x3E, 0x1F, 0x0F,
0x87, 0xC3, 0xE0, 0x70, 0xEF, 0x9F, 0xF9, 0xFF, 0x9F, 0x70, 0xE0, 0x00,
0x00, 0x00, 0x00, 0x1F, 0x01, 0xF0, 0x1F, 0x01, 0xF0, 0x1F, 0x01, 0xF0,
0x1F, 0x01, 0xF0, 0x1F, 0x01, 0xF0, 0x1F, 0x01, 0xF0, 0x1F, 0x01, 0xF0,
0x1F, 0x01, 0xF0, 0x1F, 0x01, 0xF0, 0x1F, 0x01, 0xF0, 0x1F, 0x00, 0x1F,
0xFC, 0x00, 0x7F, 0xFE, 0x01, 0xFF, 0xFE, 0x07, 0xFF, 0xFC, 0x1F, 0x07,
0xF0, 0x7C, 0x07, 0xE1, 0xF0, 0x0F, 0x87, 0xC0, 0x1F, 0xFF, 0xF0, 0x7F,
0xFF, 0xC1, 0xFF, 0xFF, 0x07, 0xFF, 0xFC, 0x1F, 0x1F, 0x00, 0x7C, 0x7C,
0x03, 0xF1, 0xF0, 0x0F, 0x87, 0xC0, 0x7E, 0x1F, 0x07, 0xF0, 0x7F, 0xFF,
0xC1, 0xFF, 0xFE, 0x07, 0xFF, 0xE0, 0x1F, 0xFC, 0x00, 0x03, 0x8C, 0x00,
0xFF, 0xC0, 0x3F, 0xF0, 0x03, 0x1C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x07, 0xC0, 0x1F, 0xFC, 0x03, 0xFF, 0xC0, 0x7F, 0xF8, 0x0F, 0xFF,
0x81, 0xFF, 0xF8, 0x3F, 0xFF, 0x87, 0xFF, 0xF8, 0xFF, 0xDF, 0x1F, 0xF9,
0xF3, 0xFF, 0x1F, 0x7F, 0xE3, 0xFF, 0xFC, 0x3F, 0xFF, 0x83, 0xFF, 0xF0,
0x3F, 0xFE, 0x07, 0xFF, 0xC0, 0x7F, 0xF8, 0x07, 0xFF, 0x00, 0xFF, 0xE0,
0x0F, 0xFC, 0x01, 0xF0, 0x00, 0x80, 0x00, 0x1C, 0x00, 0x03, 0xE0, 0x00,
0x1F, 0x00, 0x00, 0xF8, 0x00, 0x07, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x1F, 0x80, 0x0F, 0xFF, 0x01, 0xFF, 0xF8, 0x3F, 0xFF,
0xC3, 0xF0, 0xFC, 0x7E, 0x07, 0xE7, 0xC0, 0x3E, 0xF8, 0x01, 0xFF, 0x80,
0x1F, 0xF8, 0x01, 0xFF, 0x80, 0x1F, 0xF8, 0x01, 0xFF, 0x80, 0x1F, 0xF8,
0x01, 0xF7, 0xC0, 0x3E, 0x7E, 0x07, 0xE7, 0xF0, 0xFE, 0x3F, 0xFF, 0xC1,
0xFF, 0xF8, 0x0F, 0xFF, 0x00, 0x1F, 0xC0, 0x00, 0x10, 0x00, 0x03, 0x80,
0x00, 0x7C, 0x00, 0x0F, 0x80, 0x01, 0xF0, 0x00, 0x0E, 0x00, 0x00, 0x40,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1F, 0x80, 0x0F, 0xFF, 0x01, 0xFF,
0xF8, 0x3F, 0xFF, 0xC3, 0xF0, 0xFC, 0x7E, 0x07, 0xE7, 0xC0, 0x3E, 0xF8,
0x01, 0xFF, 0x80, 0x1F, 0xF8, 0x01, 0xFF, 0x80, 0x1F, 0xF8, 0x01, 0xFF,
0x80, 0x1F, 0xF8, 0x01, 0xF7, 0xC0, 0x3E, 0x7E, 0x07, 0xE7, 0xF0, 0xFE,
0x3F, 0xFF, 0xC1, 0xFF, 0xF8, 0x0F, 0xFF, 0x00, 0x1F, 0xC0, 0x00, 0x40,
0x00, 0x0E, 0x00, 0x01, 0xF0, 0x00, 0x3F, 0x80, 0x07, 0xBC, 0x00, 0x20,
0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xF8, 0x00, 0xFF, 0xF0, 0x1F,
0xFF, 0x83, 0xFF, 0xFC, 0x3F, 0x0F, 0xC7, 0xE0, 0x7E, 0x7C, 0x03, 0xEF,
0x80, 0x1F, 0xF8, 0x01, 0xFF, 0x80, 0x1F, 0xF8, 0x01, 0xFF, 0x80, 0x1F,
0xF8, 0x01, 0xFF, 0x80, 0x1F, 0x7C, 0x03, 0xE7, 0xE0, 0x7E, 0x7F, 0x0F,
0xE3, 0xFF, 0xFC, 0x1F, 0xFF, 0x80, 0xFF, 0xF0, 0x01, 0xFC, 0x00, 0x01,
0xC6, 0x00, 0x3F, 0xF0, 0x07, 0xFE, 0x00, 0x31, 0xC0, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x1F, 0x80, 0x0F, 0xFF, 0x01, 0xFF, 0xF8,
0x3F, 0xFF, 0xC3, 0xF0, 0xFC, 0x7E, 0x07, 0xE7, 0xC0, 0x3E, 0xF8, 0x01,
0xFF, 0x80, 0x1F, 0xF8, 0x01, 0xFF, 0x80, 0x1F, 0xF8, 0x01, 0xFF, 0x80,
0x1F, 0xF8, 0x01, 0xF7, 0xC0, 0x3E, 0x7E, 0x07, 0xE7, 0xF0, 0xFE, 0x3F,
0xFF, 0xC1, 0xFF, 0xF8, 0x0F, 0xFF, 0x00, 0x1F, 0xC0, 0x07, 0x0E, 0x00,
0xF9, 0xF0, 0x0F, 0x9F, 0x00, 0xF9, 0xF0, 0x07, 0x0E, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xF8, 0x00, 0xFF, 0xF0, 0x1F, 0xFF,
0x83, 0xFF, 0xFC, 0x3F, 0x0F, 0xC7, 0xE0, 0x7E, 0x7C, 0x03, 0xEF, 0x80,
0x1F, 0xF8, 0x01, 0xFF, 0x80, 0x1F, 0xF8, 0x01, 0xFF, 0x80, 0x1F, 0xF8,
0x01, 0xFF, 0x80, 0x1F, 0x7C, 0x03, 0xE7, 0xE0, 0x7E, 0x7F, 0x0F, 0xE3,
0xFF, 0xFC, 0x1F, 0xFF, 0x80, 0xFF, 0xF0, 0x01, 0xFC, 0x00, 0x20, 0x11,
0xC0, 0xEF, 0x87, 0xDF, 0x3E, 0x3F, 0xF0, 0x7F, 0x80, 0xFC, 0x03, 0xF0,
0x1F, 0xE0, 0xFF, 0xC7, 0xCF, 0xBE, 0x1F, 0x70, 0x38, 0x80, 0x40, 0x00,
0x00, 0x80, 0x1F, 0x8C, 0x0F, 0xFF, 0xE1, 0xFF, 0xFC, 0x3F, 0xFF, 0xC3,
0xF0, 0xFC, 0x7E, 0x0F, 0xE7, 0xC1, 0xFE, 0xF8, 0x3D, 0xFF, 0x83, 0x9F,
0xF8, 0x79, 0xFF, 0x8F, 0x1F, 0xF9, 0xE1, 0xFF, 0xBC, 0x1F, 0xFB, 0xC1,
0xF7, 0xF8, 0x3E, 0x7F, 0x07, 0xE7, 0xF0, 0xFE, 0x3F, 0xFF, 0xC3, 0xFF,
0xF8, 0x7F, 0xFF, 0x07, 0x1F, 0xC0, 0x10, 0x00, 0x00, 0x01, 0x00, 0x00,
0xE0, 0x00, 0x7C, 0x00, 0x0F, 0x80, 0x01, 0xF0, 0x00, 0x38, 0x00, 0x04,
0x00, 0x00, 0x00, 0x00, 0x00, 0x3E, 0x01, 0xFF, 0x80, 0x7F, 0xE0, 0x1F,
0xF8, 0x07, 0xFE, 0x01, 0xFF, 0x80, 0x7F, 0xE0, 0x1F, 0xF8, 0x07, 0xFE,
0x01, 0xFF, 0x80, 0x7F, 0xE0, 0x1F, 0xF8, 0x07, 0xFE, 0x01, 0xFF, 0x80,
0x7F, 0xE0, 0x1F, 0xFC, 0x0F, 0xDF, 0x87, 0xE7, 0xFF, 0xF8, 0xFF, 0xFC,
0x1F, 0xFE, 0x01, 0xFE, 0x00, 0x00, 0x20, 0x00, 0x1C, 0x00, 0x0F, 0x80,
0x07, 0xC0, 0x03, 0xE0, 0x00, 0x70, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00,
0x00, 0x3E, 0x01, 0xFF, 0x80, 0x7F, 0xE0, 0x1F, 0xF8, 0x07, 0xFE, 0x01,
0xFF, 0x80, 0x7F, 0xE0, 0x1F, 0xF8, 0x07, 0xFE, 0x01, 0xFF, 0x80, 0x7F,
0xE0, 0x1F, 0xF8, 0x07, 0xFE, 0x01, 0xFF, 0x80, 0x7F, 0xE0, 0x1F, 0xFC,
0x0F, 0xDF, 0x87, 0xE7, 0xFF, 0xF8, 0xFF, 0xFC, 0x1F, 0xFE, 0x01, 0xFE,
0x00, 0x00, 0x80, 0x00, 0x70, 0x00, 0x3E, 0x00, 0x1F, 0xC0, 0x0F, 0x78,
0x01, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF8, 0x07, 0xFE, 0x01, 0xFF,
0x80, 0x7F, 0xE0, 0x1F, 0xF8, 0x07, 0xFE, 0x01, 0xFF, 0x80, 0x7F, 0xE0,
0x1F, 0xF8, 0x07, 0xFE, 0x01, 0xFF, 0x80, 0x7F, 0xE0, 0x1F, 0xF8, 0x07,
0xFE, 0x01, 0xFF, 0x80, 0x7F, 0xF0, 0x3F, 0x7E, 0x1F, 0x9F, 0xFF, 0xE3,
0xFF, 0xF0, 0x7F, 0xF8, 0x07, 0xF8, 0x00, 0x0E, 0x1C, 0x07, 0xCF, 0x81,
0xF3, 0xE0, 0x7C, 0xF8, 0x0E, 0x1C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0xF8, 0x07, 0xFE, 0x01, 0xFF, 0x80, 0x7F, 0xE0, 0x1F, 0xF8, 0x07,
0xFE, 0x01, 0xFF, 0x80, 0x7F, 0xE0, 0x1F, 0xF8, 0x07, 0xFE, 0x01, 0xFF,
0x80, 0x7F, 0xE0, 0x1F, 0xF8, 0x07, 0xFE, 0x01, 0xFF, 0x80, 0x7F, 0xF0,
0x3F, 0x7E, 0x1F, 0x9F, 0xFF, 0xE3, 0xFF, 0xF0, 0x7F, 0xF8, 0x07, 0xF8,
0x00, 0x00, 0x08, 0x00, 0x00, 0xE0, 0x00, 0x0F, 0x80, 0x00, 0xF8, 0x00,
0x0F, 0x80, 0x00, 0x38, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00,
0x07, 0xE0, 0x0F, 0xDF, 0x00, 0xFC, 0xFC, 0x07, 0xE3, 0xF0, 0x7E, 0x0F,
0x83, 0xE0, 0x7E, 0x3F, 0x01, 0xFB, 0xF0, 0x0F, 0xDF, 0x80, 0x3F, 0xF8,
0x00, 0xFF, 0x80, 0x07, 0xFC, 0x00, 0x1F, 0xC0, 0x00, 0x7C, 0x00, 0x03,
0xE0, 0x00, 0x1F, 0x00, 0x00, 0xF8, 0x00, 0x07, 0xC0, 0x00, 0x3E, 0x00,
0x01, 0xF0, 0x00, 0x0F, 0x80, 0x00, 0x7C, 0x00, 0xF8, 0x00, 0x7C, 0x00,
0x3E, 0x00, 0x1F, 0xFE, 0x0F, 0xFF, 0xC7, 0xFF, 0xF3, 0xFF, 0xFD, 0xF0,
0x7F, 0xF8, 0x0F, 0xFC, 0x07, 0xFE, 0x03, 0xFF, 0x01, 0xFF, 0x83, 0xFF,
0xFF, 0xFB, 0xFF, 0xF9, 0xFF, 0xF8, 0xFF, 0xF0, 0x7C, 0x00, 0x3E, 0x00,
0x1F, 0x00, 0x0F, 0x80, 0x00, 0x0F, 0xE0, 0x1F, 0xF8, 0x1F, 0xFE, 0x0F,
0xFF, 0x8F, 0xC7, 0xC7, 0xC3, 0xE3, 0xE1, 0xF1, 0xF0, 0xF8, 0xF8, 0xF8,
0x7C, 0x7C, 0x3E, 0x7C, 0x1F, 0x3E, 0x0F, 0x9F, 0x07, 0xCF, 0xE3, 0xE3,
0xF9, 0xF0, 0xFE, 0xF8, 0x1F, 0xFC, 0x07, 0xFE, 0x03, 0xFF, 0x21, 0xFF,
0x9F, 0xFF, 0xCF, 0xFB, 0xE7, 0xFD, 0xF3, 0xF8, 0x04, 0x00, 0x38, 0x01,
0xF0, 0x03, 0xE0, 0x07, 0xC0, 0x0E, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00,
0x7F, 0x83, 0xFF, 0x0F, 0xFE, 0x3F, 0xFC, 0x03, 0xF0, 0x07, 0xC7, 0xFF,
0x7F, 0xFD, 0xFF, 0xFF, 0xC7, 0xFE, 0x1F, 0xF8, 0x7F, 0xFF, 0xF7, 0xFF,
0xDF, 0xFF, 0x1F, 0xF8, 0x00, 0x80, 0x07, 0x00, 0x3E, 0x01, 0xF0, 0x0F,
0x80, 0x1C, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x7F, 0x83, 0xFF, 0x0F,
0xFE, 0x3F, 0xFC, 0x03, 0xF0, 0x07, 0xC7, 0xFF, 0x7F, 0xFD, 0xFF, 0xFF,
0xC7, 0xFE, 0x1F, 0xF8, 0x7F, 0xFF, 0xF7, 0xFF, 0xDF, 0xFF, 0x1F, 0xF8,
0x02, 0x00, 0x1C, 0x00, 0xF8, 0x07, 0xF0, 0x3D, 0xE0, 0x41, 0x00, 0x00,
0x00, 0x00, 0x1F, 0xE0, 0xFF, 0xC3, 0xFF, 0x8F, 0xFF, 0x00, 0xFC, 0x01,
0xF1, 0xFF, 0xDF, 0xFF, 0x7F, 0xFF, 0xF1, 0xFF, 0x87, 0xFE, 0x1F, 0xFF,
0xFD, 0xFF, 0xF7, 0xFF, 0xC7, 0xFE, 0x0E, 0x30, 0x7F, 0xE3, 0xFF, 0x06,
0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0xF8, 0x3F, 0xF0, 0xFF, 0xE3,
0xFF, 0xC0, 0x3F, 0x00, 0x7C, 0x7F, 0xF7, 0xFF, 0xDF, 0xFF, 0xFC, 0x7F,
0xE1, 0xFF, 0x87, 0xFF, 0xFF, 0x7F, 0xFD, 0xFF, 0xF1, 0xFF, 0x80, 0x38,
0x71, 0xF3, 0xE7, 0xCF, 0x9F, 0x3E, 0x38, 0x70, 0x00, 0x00, 0x00, 0x00,
0x00, 0x1F, 0xE0, 0xFF, 0xC3, 0xFF, 0x8F, 0xFF, 0x00, 0xFC, 0x01, 0xF1,
0xFF, 0xDF, 0xFF, 0x7F, 0xFF, 0xF1, 0xFF, 0x87, 0xFE, 0x1F, 0xFF, 0xFD,
0xFF, 0xF7, 0xFF, 0xC7, 0xFE, 0x03, 0x80, 0x1F, 0x00, 0xC6, 0x03, 0x18,
0x0C, 0x60, 0x1F, 0x00, 0x38, 0x00, 0x00, 0x00, 0x00, 0x7F, 0x83, 0xFF,
0x0F, 0xFE, 0x3F, 0xFC, 0x03, 0xF0, 0x07, 0xC7, 0xFF, 0x7F, 0xFD, 0xFF,
0xFF, 0xC7, 0xFE, 0x1F, 0xF8, 0x7F, 0xFF, 0xF7, 0xFF, 0xDF, 0xFF, 0x1F,
0xF8, 0x1F, 0xE1, 0xF8, 0x1F, 0xFB, 0xFF, 0x0F, 0xFF, 0xFF, 0xC7, 0xFF,
0xFF, 0xE2, 0x07, 0xF1, 0xF8, 0x01, 0xF0, 0x7C, 0x3F, 0xFF, 0xFE, 0x7F,
0xFF, 0xFF, 0x7F, 0xFF, 0xFF, 0xFE, 0x1F, 0x00, 0x3E, 0x0F, 0xC0, 0x1F,
0x83, 0xF0, 0x0F, 0xFF, 0xFF, 0xF3, 0xFF, 0xFF, 0xF8, 0xFF, 0xFF, 0xFC,
0x1F, 0xC3, 0xF8, 0x03, 0xF8, 0x7F, 0xF3, 0xFF, 0x9F, 0xFE, 0x7E, 0x0B,
0xF0, 0x0F, 0x80, 0x3E, 0x00, 0xF8, 0x03, 0xE0, 0x0F, 0xC0, 0x1F, 0x80,
0x3F, 0xFC, 0x7F, 0xF0, 0xFF, 0xC0, 0x7E, 0x03, 0xC0, 0x03, 0x80, 0x0E,
0x03, 0xF8, 0x0F, 0xC0, 0x3E, 0x00, 0x02, 0x00, 0x07, 0x00, 0x0F, 0x80,
0x07, 0xC0, 0x03, 0xE0, 0x01, 0xC0, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00,
0x07, 0xE0, 0x1F, 0xF8, 0x3F, 0xFC, 0x7F, 0xFE, 0x7C, 0x3F, 0xF8, 0x1F,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF8, 0x00, 0xFC, 0x00, 0x7E, 0x04,
0x7F, 0xFE, 0x3F, 0xFE, 0x1F, 0xFE, 0x07, 0xF8, 0x00, 0x40, 0x00, 0xE0,
0x01, 0xF0, 0x03, 0xE0, 0x07, 0xC0, 0x03, 0x80, 0x01, 0x00, 0x00, 0x00,
0x00, 0x00, 0x07, 0xE0, 0x1F, 0xF8, 0x3F, 0xFC, 0x7F, 0xFE, 0x7C, 0x3F,
0xF8, 0x1F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF8, 0x00, 0xFC, 0x00,
0x7E, 0x04, 0x7F, 0xFE, 0x3F, 0xFE, 0x1F, 0xFE, 0x07, 0xF8, 0x01, 0x00,
0x03, 0x80, 0x07, 0xC0, 0x0F, 0xE0, 0x1E, 0xF0, 0x08, 0x20, 0x00, 0x00,
0x00, 0x00, 0x07, 0xE0, 0x1F, 0xF8, 0x3F, 0xFC, 0x7F, 0xFE, 0x7C, 0x3F,
0xF8, 0x1F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF8, 0x00, 0xFC, 0x00,
0x7E, 0x04, 0x7F, 0xFE, 0x3F, 0xFE, 0x1F, 0xFE, 0x07, 0xF8, 0x1C, 0x38,
0x3E, 0x7C, 0x3E, 0x7C, 0x3E, 0x7C, 0x1C, 0x38, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x07, 0xE0, 0x1F, 0xF8, 0x3F, 0xFC, 0x7F, 0xFE, 0x7C, 0x3F,
0xF8, 0x1F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF8, 0x00, 0xFC, 0x00,
0x7E, 0x04, 0x7F, 0xFE, 0x3F, 0xFE, 0x1F, 0xFE, 0x07, 0xF8, 0x20, 0xE3,
0xE3, 0xE3, 0xE3, 0x82, 0x00, 0x00, 0xF9, 0xF3, 0xE7, 0xCF, 0x9F, 0x3E,
0x7C, 0xF9, 0xF3, 0xE7, 0xCF, 0x9F, 0x3E, 0x7C, 0x08, 0x38, 0xFB, 0xEF,
0x8E, 0x08, 0x00, 0x00, 0xF9, 0xF3, 0xE7, 0xCF, 0x9F, 0x3E, 0x7C, 0xF9,
0xF3, 0xE7, 0xCF, 0x9F, 0x3E, 0x7C, 0x08, 0x0E, 0x0F, 0x8F, 0xEF, 0x7A,
0x08, 0x00, 0x00, 0x3E, 0x1F, 0x0F, 0x87, 0xC3, 0xE1, 0xF0, 0xF8, 0x7C,
0x3E, 0x1F, 0x0F, 0x87, 0xC3, 0xE1, 0xF0, 0xF8, 0x7C, 0x70, 0xEF, 0x9F,
0xF9, 0xFF, 0x9F, 0x70, 0xE0, 0x00, 0x00, 0x00, 0x00, 0x0F, 0x80, 0xF8,
0x0F, 0x80, 0xF8, 0x0F, 0x80, 0xF8, 0x0F, 0x80, 0xF8, 0x0F, 0x80, 0xF8,
0x0F, 0x80, 0xF8, 0x0F, 0x80, 0xF8, 0x0F, 0x80, 0xF8, 0x00, 0x80, 0x00,
0xF0, 0x00, 0xFC, 0x60, 0x3F, 0xF0, 0x0F, 0xF8, 0x1F, 0xF0, 0x0F, 0xF8,
0x07, 0x3E, 0x00, 0x1F, 0x03, 0xF7, 0x87, 0xFF, 0xE7, 0xFF, 0xF7, 0xFF,
0xFF, 0xE0, 0xFF, 0xE0, 0x3F, 0xF0, 0x1F, 0xF8, 0x0F, 0xFC, 0x07, 0xFE,
0x07, 0xCF, 0x87, 0xE7, 0xFF, 0xE1, 0xFF, 0xF0, 0x7F, 0xF0, 0x0F, 0xE0,
0x0E, 0x30, 0x7F, 0xE3, 0xFF, 0x06, 0x38, 0x00, 0x00, 0x00, 0x00, 0x00,
0x0F, 0xF0, 0xFF, 0xF3, 0xFF, 0xEF, 0xFF, 0xBE, 0x3F, 0xF8, 0x7F, 0xE1,
0xFF, 0x87, 0xFE, 0x1F, 0xF8, 0x7F, 0xE1, 0xFF, 0x87, 0xFE, 0x1F, 0xF8,
0x7F, 0xE1, 0xFF, 0x87, 0xC0, 0x01, 0x00, 0x01, 0xC0, 0x01, 0xF0, 0x00,
0x7C, 0x00, 0x1F, 0x00, 0x07, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
0x03, 0xF8, 0x07, 0xFF, 0x07, 0xFF, 0xC7, 0xFF, 0xF3, 0xF1, 0xFB, 0xF0,
0x7F, 0xF0, 0x1F, 0xF8, 0x0F, 0xFC, 0x07, 0xFE, 0x03, 0xFF, 0x83, 0xF7,
0xE3, 0xF3, 0xFF, 0xF8, 0xFF, 0xF8, 0x3F, 0xF8, 0x07, 0xF0, 0x00, 0x00,
0x40, 0x00, 0x70, 0x00, 0x7C, 0x00, 0x7C, 0x00, 0x7C, 0x00, 0x1C, 0x00,
0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xF8, 0x07, 0xFF, 0x07, 0xFF,
0xC7, 0xFF, 0xF3, 0xF1, 0xFB, 0xF0, 0x7F, 0xF0, 0x1F, 0xF8, 0x0F, 0xFC,
0x07, 0xFE, 0x03, 0xFF, 0x83, 0xF7, 0xE3, 0xF3, 0xFF, 0xF8, 0xFF, 0xF8,
0x3F, 0xF8, 0x07, 0xF0, 0x00, 0x00, 0x80, 0x00, 0xE0, 0x00, 0xF8, 0x00,
0xFE, 0x00, 0xF7, 0x80, 0x20, 0x80, 0x00, 0x00, 0x00, 0x00, 0x07, 0xF0,
0x0F, 0xFE, 0x0F, 0xFF, 0x8F, 0xFF, 0xE7, 0xE3, 0xF7, 0xE0, 0xFF, 0xE0,
0x3F, 0xF0, 0x1F, 0xF8, 0x0F, 0xFC, 0x07, 0xFF, 0x07, 0xEF, 0xC7, 0xE7,
0xFF, 0xF1, 0xFF, 0xF0, 0x7F, 0xF0, 0x0F, 0xE0, 0x07, 0x18, 0x07, 0xFE,
0x07, 0xFE, 0x01, 0x8E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0F,
0xE0, 0x1F, 0xFC, 0x1F, 0xFF, 0x1F, 0xFF, 0xCF, 0xC7, 0xEF, 0xC1, 0xFF,
0xC0, 0x7F, 0xE0, 0x3F, 0xF0, 0x1F, 0xF8, 0x0F, 0xFE, 0x0F, 0xDF, 0x8F,
0xCF, 0xFF, 0xE3, 0xFF, 0xE0, 0xFF, 0xE0, 0x1F, 0xC0, 0x0E, 0x1C, 0x0F,
0x9F, 0x07, 0xCF, 0x83, 0xE7, 0xC0, 0xE1, 0xC0, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x07, 0xF0, 0x0F, 0xFE, 0x0F, 0xFF, 0x8F, 0xFF, 0xE7, 0xE3,
0xF7, 0xE0, 0xFF, 0xE0, 0x3F, 0xF0, 0x1F, 0xF8, 0x0F, 0xFC, 0x07, 0xFF,
0x07, 0xEF, 0xC7, 0xE7, 0xFF, 0xF1, 0xFF, 0xF0, 0x7F, 0xF0, 0x0F, 0xE0,
0x03, 0x80, 0x0F, 0x80, 0x1F, 0x00, 0x3E, 0x00, 0x38, 0x00, 0x00, 0x00,
0x00, 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF8, 0x00, 0x00, 0x00,
0x00, 0x70, 0x01, 0xF0, 0x03, 0xE0, 0x07, 0xC0, 0x07, 0x00, 0x00, 0x02,
0x03, 0xFB, 0x87, 0xFF, 0xC7, 0xFF, 0xC7, 0xFF, 0xF3, 0xF1, 0xFB, 0xF1,
0xFF, 0xF1, 0xFF, 0xF9, 0xEF, 0xFD, 0xE7, 0xFF, 0xE3, 0xFF, 0xE3, 0xF7,
0xE3, 0xF3, 0xFF, 0xF8, 0xFF, 0xF8, 0xFF, 0xF8, 0x77, 0xF0, 0x10, 0x00,
0x00, 0x04, 0x00, 0x38, 0x01, 0xF0, 0x03, 0xE0, 0x07, 0xC0, 0x0E, 0x00,
0x10, 0x00, 0x00, 0x00, 0x03, 0xE1, 0xFF, 0x87, 0xFE, 0x1F, 0xF8, 0x7F,
0xE1, 0xFF, 0x87, 0xFE, 0x1F, 0xF8, 0x7F, 0xE1, 0xFF, 0x87, 0xFE, 0x1F,
0xFC, 0x7D, 0xFF, 0xF7, 0xFF, 0xCF, 0xFF, 0x0F, 0xF0, 0x00, 0x80, 0x07,
0x00, 0x3E, 0x01, 0xF0, 0x0F, 0x80, 0x1C, 0x00, 0x20, 0x00, 0x00, 0x00,
0x03, 0xE1, 0xFF, 0x87, 0xFE, 0x1F, 0xF8, 0x7F, 0xE1, 0xFF, 0x87, 0xFE,
0x1F, 0xF8, 0x7F, 0xE1, 0xFF, 0x87, 0xFE, 0x1F, 0xFC, 0x7D, 0xFF, 0xF7,
0xFF, 0xCF, 0xFF, 0x0F, 0xF0, 0x01, 0x00, 0x0E, 0x00, 0x7C, 0x03, 0xF8,
0x1E, 0xF0, 0x20, 0x80, 0x00, 0x00, 0x00, 0xF8, 0x7F, 0xE1, 0xFF, 0x87,
0xFE, 0x1F, 0xF8, 0x7F, 0xE1, 0xFF, 0x87, 0xFE, 0x1F, 0xF8, 0x7F, 0xE1,
0xFF, 0x87, 0xFF, 0x1F, 0x7F, 0xFD, 0xFF, 0xF3, 0xFF, 0xC3, 0xFC, 0x38,
0x71, 0xF3, 0xE7, 0xCF, 0x9F, 0x3E, 0x38, 0x70, 0x00, 0x00, 0x00, 0x00,
0x00, 0xF8, 0x7F, 0xE1, 0xFF, 0x87, 0xFE, 0x1F, 0xF8, 0x7F, 0xE1, 0xFF,
0x87, 0xFE, 0x1F, 0xF8, 0x7F, 0xE1, 0xFF, 0x87, 0xFF, 0x1F, 0x7F, 0xFD,
0xFF, 0xF3, 0xFF, 0xC3, 0xFC, 0x00, 0x20, 0x00, 0x38, 0x00, 0x3E, 0x00,
0x3E, 0x00, 0x3E, 0x00, 0x0E, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00,
0x7C, 0x07, 0xFF, 0x07, 0xEF, 0x83, 0xE7, 0xC1, 0xF3, 0xF1, 0xF8, 0xF8,
0xF8, 0x7C, 0x7C, 0x3E, 0x3C, 0x0F, 0xBE, 0x07, 0xDF, 0x01, 0xEF, 0x00,
0xFF, 0x80, 0x3F, 0x80, 0x1F, 0xC0, 0x07, 0xE0, 0x03, 0xE0, 0x01, 0xF0,
0x01, 0xF0, 0x0F, 0xF8, 0x0F, 0xF8, 0x07, 0xF8, 0x01, 0xF0, 0x00, 0x38,
0x00, 0xF8, 0x00, 0xF8, 0x00, 0xF8, 0x00, 0xF8, 0x00, 0xF8, 0x00, 0xF8,
0x00, 0xF8, 0x00, 0xFF, 0xE0, 0xFF, 0xF8, 0xFF, 0xFC, 0xFF, 0xFE, 0xF8,
0x7E, 0xF8, 0x3F, 0xF8, 0x1F, 0xF8, 0x1F, 0xF8, 0x1F, 0xF8, 0x1F, 0xF8,
0x3F, 0xF8, 0x3E, 0xFF, 0xFE, 0xFF, 0xFC, 0xFF, 0xF8, 0xFB, 0xF0, 0xF8,
0x00, 0xF8, 0x00, 0xF8, 0x00, 0xF8, 0x00, 0xF8, 0x00, 0xF8, 0x00, 0x0E,
0x1C, 0x0F, 0x9F, 0x07, 0xCF, 0x83, 0xE7, 0xC0, 0xE1, 0xC0, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0xF8, 0x0F, 0xFE, 0x0F, 0xDF, 0x07, 0xCF, 0x83,
0xE7, 0xE3, 0xF1, 0xF1, 0xF0, 0xF8, 0xF8, 0x7C, 0x78, 0x1F, 0x7C, 0x0F,
0xBE, 0x03, 0xDE, 0x01, 0xFF, 0x00, 0x7F, 0x00, 0x3F, 0x80, 0x0F, 0xC0,
0x07, 0xC0, 0x03, 0xE0, 0x03, 0xE0, 0x1F, 0xF0, 0x1F, 0xF0, 0x0F, 0xF0,
0x03, 0xE0, 0x00 };
const GFXglyph Ubuntu_Bold16pt8bGlyphs[] PROGMEM = {
{ 0, 1, 1, 7, 0, 0 }, // 0x20 ' ' U+0020
{ 1, 5, 21, 9, 2, -20 }, // 0x21 '!' U+0021
{ 15, 12, 9, 14, 2, -23 }, // 0x22 '"' U+0022
{ 29, 18, 21, 22, 2, -20 }, // 0x23 '#' U+0023
{ 77, 16, 28, 18, 1, -23 }, // 0x24 '$' U+0024
{ 133, 26, 21, 28, 1, -20 }, // 0x25 '%' U+0025
{ 202, 21, 21, 22, 1, -20 }, // 0x26 '&' U+0026
{ 258, 5, 9, 9, 2, -23 }, // 0x27 ''' U+0027
{ 264, 9, 30, 11, 2, -23 }, // 0x28 '(' U+0028
{ 298, 9, 30, 11, 0, -23 }, // 0x29 ')' U+0029
{ 332, 13, 12, 16, 2, -20 }, // 0x2a '*' U+002A
{ 352, 14, 14, 18, 2, -15 }, // 0x2b '+' U+002B
{ 377, 6, 10, 8, 1, -4 }, // 0x2c ',' U+002C
{ 385, 9, 4, 11, 1, -10 }, // 0x2d '-' U+002D
{ 390, 6, 6, 8, 1, -5 }, // 0x2e '.' U+002E
{ 395, 15, 30, 14, -1, -23 }, // 0x2f '/' U+002F
{ 452, 16, 21, 18, 1, -20 }, // 0x30 '0' U+0030
{ 494, 10, 21, 18, 3, -20 }, // 0x31 '1' U+0031
{ 521, 15, 21, 18, 1, -20 }, // 0x32 '2' U+0032
{ 561, 15, 21, 18, 1, -20 }, // 0x33 '3' U+0033
{ 601, 16, 21, 18, 1, -20 }, // 0x34 '4' U+0034
{ 643, 15, 21, 18, 1, -20 }, // 0x35 '5' U+0035
{ 683, 16, 21, 18, 1, -20 }, // 0x36 '6' U+0036
{ 725, 16, 21, 18, 1, -20 }, // 0x37 '7' U+0037
{ 767, 16, 21, 18, 1, -20 }, // 0x38 '8' U+0038
{ 809, 16, 21, 18, 1, -20 }, // 0x39 '9' U+0039
{ 851, 6, 17, 8, 1, -16 }, // 0x3a ':' U+003A
{ 864, 6, 22, 8, 1, -16 }, // 0x3b ';' U+003B
{ 881, 15, 15, 18, 1, -16 }, // 0x3c '<' U+003C
{ 910, 15, 11, 18, 1, -14 }, // 0x3d '=' U+003D
{ 931, 14, 15, 18, 2, -16 }, // 0x3e '>' U+003E
{ 958, 12, 24, 14, 1, -23 }, // 0x3f '?' U+003F
{ 994, 26, 27, 30, 2, -21 }, // 0x40 '@' U+0040
{ 1082, 21, 21, 21, 0, -20 }, // 0x41 'A' U+0041
{ 1138, 17, 21, 21, 2, -20 }, // 0x42 'B' U+0042
{ 1183, 17, 21, 20, 2, -20 }, // 0x43 'C' U+0043
{ 1228, 19, 21, 23, 2, -20 }, // 0x44 'D' U+0044
{ 1278, 16, 21, 19, 2, -20 }, // 0x45 'E' U+0045
{ 1320, 15, 21, 18, 2, -20 }, // 0x46 'F' U+0046
{ 1360, 18, 21, 22, 2, -20 }, // 0x47 'G' U+0047
{ 1408, 19, 21, 23, 2, -20 }, // 0x48 'H' U+0048
{ 1458, 5, 21, 9, 2, -20 }, // 0x49 'I' U+0049
{ 1472, 14, 21, 16, 0, -20 }, // 0x4a 'J' U+004A
{ 1509, 19, 21, 21, 2, -20 }, // 0x4b 'K' U+004B
{ 1559, 14, 21, 17, 2, -20 }, // 0x4c 'L' U+004C
{ 1596, 24, 21, 28, 2, -20 }, // 0x4d 'M' U+004D
{ 1659, 19, 21, 23, 2, -20 }, // 0x4e 'N' U+004E
{ 1709, 20, 21, 24, 2, -20 }, // 0x4f 'O' U+004F
{ 1762, 17, 21, 20, 2, -20 }, // 0x50 'P' U+0050
{ 1807, 20, 27, 24, 2, -20 }, // 0x51 'Q' U+0051
{ 1875, 18, 21, 21, 2, -20 }, // 0x52 'R' U+0052
{ 1923, 16, 21, 18, 1, -20 }, // 0x53 'S' U+0053
{ 1965, 17, 21, 19, 1, -20 }, // 0x54 'T' U+0054
{ 2010, 18, 21, 22, 2, -20 }, // 0x55 'U' U+0055
{ 2058, 21, 21, 21, 0, -20 }, // 0x56 'V' U+0056
{ 2114, 29, 21, 31, 1, -20 }, // 0x57 'W' U+0057
{ 2191, 21, 21, 21, 0, -20 }, // 0x58 'X' U+0058
{ 2247, 21, 21, 21, 0, -20 }, // 0x59 'Y' U+0059
{ 2303, 17, 21, 19, 1, -20 }, // 0x5a 'Z' U+005A
{ 2348, 10, 30, 12, 2, -23 }, // 0x5b '[' U+005B
{ 2386, 15, 30, 14, -1, -23 }, // 0x5c '\' U+005C
{ 2443, 10, 30, 12, 0, -23 }, // 0x5d ']' U+005D
{ 2481, 17, 13, 19, 1, -21 }, // 0x5e '^' U+005E
{ 2509, 16, 4, 16, 0, 3 }, // 0x5f '_' U+005F
{ 2517, 7, 7, 9, 1, -24 }, // 0x60 '`' U+0060
{ 2524, 14, 16, 17, 1, -15 }, // 0x61 'a' U+0061
{ 2552, 16, 24, 19, 2, -23 }, // 0x62 'b' U+0062
{ 2600, 14, 16, 16, 1, -15 }, // 0x63 'c' U+0063
{ 2628, 16, 24, 19, 1, -23 }, // 0x64 'd' U+0064
{ 2676, 16, 16, 18, 1, -15 }, // 0x65 'e' U+0065
{ 2708, 12, 24, 14, 2, -23 }, // 0x66 'f' U+0066
{ 2744, 15, 22, 18, 1, -15 }, // 0x67 'g' U+0067
{ 2786, 14, 24, 18, 2, -23 }, // 0x68 'h' U+0068
{ 2828, 5, 24, 9, 2, -23 }, // 0x69 'i' U+0069
{ 2843, 9, 30, 9, -2, -23 }, // 0x6a 'j' U+006A
{ 2877, 15, 24, 18, 2, -23 }, // 0x6b 'k' U+006B
{ 2922, 8, 24, 10, 2, -23 }, // 0x6c 'l' U+006C
{ 2946, 23, 16, 27, 2, -15 }, // 0x6d 'm' U+006D
{ 2992, 14, 16, 18, 2, -15 }, // 0x6e 'n' U+006E
{ 3020, 17, 16, 19, 1, -15 }, // 0x6f 'o' U+006F
{ 3054, 16, 22, 19, 2, -15 }, // 0x70 'p' U+0070
{ 3098, 17, 22, 19, 1, -15 }, // 0x71 'q' U+0071
{ 3145, 11, 16, 13, 2, -15 }, // 0x72 'r' U+0072
{ 3167, 13, 16, 15, 1, -15 }, // 0x73 's' U+0073
{ 3193, 12, 21, 15, 2, -20 }, // 0x74 't' U+0074
{ 3225, 14, 16, 18, 2, -15 }, // 0x75 'u' U+0075
{ 3253, 17, 16, 17, 0, -15 }, // 0x76 'v' U+0076
{ 3287, 24, 16, 24, 0, -15 }, // 0x77 'w' U+0077
{ 3335, 17, 16, 17, 0, -15 }, // 0x78 'x' U+0078
{ 3369, 17, 22, 17, 0, -15 }, // 0x79 'y' U+0079
{ 3416, 14, 16, 16, 1, -15 }, // 0x7a 'z' U+007A
{ 3444, 11, 30, 12, 1, -23 }, // 0x7b '{' U+007B
{ 3486, 4, 30, 10, 3, -23 }, // 0x7c '|' U+007C
{ 3501, 11, 30, 12, 0, -23 }, // 0x7d '}' U+007D
{ 3543, 16, 6, 18, 1, -11 }, // 0x7e '~' U+007E
{ 3555, 12, 23, 16, 2, -22 }, // 0x7f 'REPLACEMENT CHARACTER *' U+2370
{ 3590, 1, 1, 7, 0, 0 }, // 0x80 'NO-BREAK SPACE' U+00A0
{ 3591, 5, 22, 9, 2, -15 }, // 0x81 'INVERTED EXCLAMATION MARK' U+00A1
{ 3605, 14, 23, 18, 1, -19 }, // 0x82 'CENT SIGN' U+00A2
{ 3646, 15, 21, 18, 1, -20 }, // 0x83 'POUND SIGN' U+00A3
{ 3686, 17, 16, 18, 0, -18 }, // 0x84 'CURRENCY SIGN' U+00A4
{ 3720, 19, 21, 19, 0, -20 }, // 0x85 'YEN SIGN' U+00A5
{ 3770, 4, 30, 9, 2, -23 }, // 0x86 'BROKEN BAR' U+00A6
{ 3785, 14, 27, 16, 1, -20 }, // 0x87 'SECTION SIGN' U+00A7
{ 3833, 12, 5, 17, 2, -23 }, // 0x88 'DIAERESIS' U+00A8
{ 3841, 21, 22, 25, 2, -21 }, // 0x89 'COPYRIGHT SIGN' U+00A9
{ 3899, 10, 13, 12, 1, -21 }, // 0x8a 'FEMININE ORDINAL INDICATOR' U+00AA
{ 3916, 16, 15, 19, 1, -15 }, // 0x8b 'LEFT-POINTING DOUBLE ANGLE QUOTATION MARK' U+00AB
{ 3946, 15, 11, 18, 2, -12 }, // 0x8c 'NOT SIGN' U+00AC
{ 3967, 9, 4, 11, 1, -10 }, // 0x8d 'SOFT HYPHEN' U+00AD
{ 3972, 21, 22, 25, 2, -21 }, // 0x8e 'REGISTERED SIGN' U+00AE
{ 4030, 10, 3, 12, 1, -21 }, // 0x8f 'MACRON' U+00AF
{ 4034, 9, 9, 11, 1, -23 }, // 0x90 'DEGREE SIGN' U+00B0
{ 4045, 14, 19, 18, 2, -18 }, // 0x91 'PLUS-MINUS SIGN' U+00B1
{ 4079, 10, 13, 11, 0, -21 }, // 0x92 'SUPERSCRIPT TWO' U+00B2
{ 4096, 9, 13, 11, 1, -21 }, // 0x93 'SUPERSCRIPT THREE' U+00B3
{ 4111, 7, 7, 9, 1, -24 }, // 0x94 'ACUTE ACCENT' U+00B4
{ 4118, 14, 22, 18, 2, -15 }, // 0x95 'MICRO SIGN' U+00B5
{ 4157, 19, 28, 22, 1, -21 }, // 0x96 'PILCROW SIGN' U+00B6
{ 4224, 6, 6, 8, 1, -12 }, // 0x97 'MIDDLE DOT' U+00B7
{ 4229, 7, 8, 10, 1, 0 }, // 0x98 'CEDILLA' U+00B8
{ 4236, 7, 13, 11, 1, -21 }, // 0x99 'SUPERSCRIPT ONE' U+00B9
{ 4248, 12, 13, 14, 1, -21 }, // 0x9a 'MASCULINE ORDINAL INDICATOR' U+00BA
{ 4268, 16, 15, 19, 1, -15 }, // 0x9b 'RIGHT-POINTING DOUBLE ANGLE QUOTATION MARK' U+00BB
{ 4298, 26, 22, 27, 0, -21 }, // 0x9c 'VULGAR FRACTION ONE QUARTER' U+00BC
{ 4370, 26, 22, 27, 0, -21 }, // 0x9d 'VULGAR FRACTION ONE HALF' U+00BD
{ 4442, 25, 22, 27, 1, -21 }, // 0x9e 'VULGAR FRACTION THREE QUARTERS' U+00BE
{ 4511, 12, 22, 14, 1, -15 }, // 0x9f 'INVERTED QUESTION MARK' U+00BF
{ 4544, 21, 30, 21, 0, -29 }, // 0xa0 'LATIN CAPITAL LETTER A WITH GRAVE' U+00C0
{ 4623, 21, 30, 21, 0, -29 }, // 0xa1 'LATIN CAPITAL LETTER A WITH ACUTE' U+00C1
{ 4702, 21, 29, 21, 0, -28 }, // 0xa2 'LATIN CAPITAL LETTER A WITH CIRCUMFLEX' U+00C2
{ 4779, 21, 28, 21, 0, -27 }, // 0xa3 'LATIN CAPITAL LETTER A WITH TILDE' U+00C3
{ 4853, 21, 29, 21, 0, -28 }, // 0xa4 'LATIN CAPITAL LETTER A WITH DIAERESIS' U+00C4
{ 4930, 21, 27, 21, 0, -26 }, // 0xa5 'LATIN CAPITAL LETTER A WITH RING ABOVE' U+00C5
{ 5001, 30, 21, 31, 0, -20 }, // 0xa6 'LATIN CAPITAL LETTER AE' U+00C6
{ 5080, 17, 28, 20, 2, -20 }, // 0xa7 'LATIN CAPITAL LETTER C WITH CEDILLA' U+00C7
{ 5140, 16, 30, 19, 2, -29 }, // 0xa8 'LATIN CAPITAL LETTER E WITH GRAVE' U+00C8
{ 5200, 16, 30, 19, 2, -29 }, // 0xa9 'LATIN CAPITAL LETTER E WITH ACUTE' U+00C9
{ 5260, 16, 29, 19, 2, -28 }, // 0xaa 'LATIN CAPITAL LETTER E WITH CIRCUMFLEX' U+00CA
{ 5318, 16, 29, 19, 2, -28 }, // 0xab 'LATIN CAPITAL LETTER E WITH DIAERESIS' U+00CB
{ 5376, 7, 30, 9, 1, -29 }, // 0xac 'LATIN CAPITAL LETTER I WITH GRAVE' U+00CC
{ 5403, 7, 30, 9, 1, -29 }, // 0xad 'LATIN CAPITAL LETTER I WITH ACUTE' U+00CD
{ 5430, 9, 29, 9, 0, -28 }, // 0xae 'LATIN CAPITAL LETTER I WITH CIRCUMFLEX' U+00CE
{ 5463, 12, 29, 9, -1, -28 }, // 0xaf 'LATIN CAPITAL LETTER I WITH DIAERESIS' U+00CF
{ 5507, 22, 21, 23, -1, -20 }, // 0xb0 'LATIN CAPITAL LETTER ETH' U+00D0
{ 5565, 19, 28, 23, 2, -27 }, // 0xb1 'LATIN CAPITAL LETTER N WITH TILDE' U+00D1
{ 5632, 20, 30, 24, 2, -29 }, // 0xb2 'LATIN CAPITAL LETTER O WITH GRAVE' U+00D2
{ 5707, 20, 30, 24, 2, -29 }, // 0xb3 'LATIN CAPITAL LETTER O WITH ACUTE' U+00D3
{ 5782, 20, 29, 24, 2, -28 }, // 0xb4 'LATIN CAPITAL LETTER O WITH CIRCUMFLEX' U+00D4
{ 5855, 20, 28, 24, 2, -27 }, // 0xb5 'LATIN CAPITAL LETTER O WITH TILDE' U+00D5
{ 5925, 20, 29, 24, 2, -28 }, // 0xb6 'LATIN CAPITAL LETTER O WITH DIAERESIS' U+00D6
{ 5998, 14, 14, 18, 2, -15 }, // 0xb7 'MULTIPLICATION SIGN' U+00D7
{ 6023, 20, 23, 24, 2, -21 }, // 0xb8 'LATIN CAPITAL LETTER O WITH STROKE' U+00D8
{ 6081, 18, 30, 22, 2, -29 }, // 0xb9 'LATIN CAPITAL LETTER U WITH GRAVE' U+00D9
{ 6149, 18, 30, 22, 2, -29 }, // 0xba 'LATIN CAPITAL LETTER U WITH ACUTE' U+00DA
{ 6217, 18, 29, 22, 2, -28 }, // 0xbb 'LATIN CAPITAL LETTER U WITH CIRCUMFLEX' U+00DB
{ 6283, 18, 29, 22, 2, -28 }, // 0xbc 'LATIN CAPITAL LETTER U WITH DIAERESIS' U+00DC
{ 6349, 21, 30, 21, 0, -29 }, // 0xbd 'LATIN CAPITAL LETTER Y WITH ACUTE' U+00DD
{ 6428, 17, 21, 20, 2, -20 }, // 0xbe 'LATIN CAPITAL LETTER THORN' U+00DE
{ 6473, 17, 24, 20, 2, -23 }, // 0xbf 'LATIN SMALL LETTER SHARP S' U+00DF
{ 6524, 14, 25, 17, 1, -24 }, // 0xc0 'LATIN SMALL LETTER A WITH GRAVE' U+00E0
{ 6568, 14, 25, 17, 1, -24 }, // 0xc1 'LATIN SMALL LETTER A WITH ACUTE' U+00E1
{ 6612, 14, 24, 17, 1, -23 }, // 0xc2 'LATIN SMALL LETTER A WITH CIRCUMFLEX' U+00E2
{ 6654, 14, 23, 17, 1, -22 }, // 0xc3 'LATIN SMALL LETTER A WITH TILDE' U+00E3
{ 6695, 14, 24, 17, 1, -23 }, // 0xc4 'LATIN SMALL LETTER A WITH DIAERESIS' U+00E4
{ 6737, 14, 25, 17, 1, -24 }, // 0xc5 'LATIN SMALL LETTER A WITH RING ABOVE' U+00E5
{ 6781, 25, 16, 27, 1, -15 }, // 0xc6 'LATIN SMALL LETTER AE' U+00E6
{ 6831, 14, 22, 16, 1, -15 }, // 0xc7 'LATIN SMALL LETTER C WITH CEDILLA' U+00E7
{ 6870, 16, 25, 18, 1, -24 }, // 0xc8 'LATIN SMALL LETTER E WITH GRAVE' U+00E8
{ 6920, 16, 25, 18, 1, -24 }, // 0xc9 'LATIN SMALL LETTER E WITH ACUTE' U+00E9
{ 6970, 16, 24, 18, 1, -23 }, // 0xca 'LATIN SMALL LETTER E WITH CIRCUMFLEX' U+00EA
{ 7018, 16, 24, 18, 1, -23 }, // 0xcb 'LATIN SMALL LETTER E WITH DIAERESIS' U+00EB
{ 7066, 7, 25, 9, 1, -24 }, // 0xcc 'LATIN SMALL LETTER I WITH GRAVE' U+00EC
{ 7088, 7, 25, 9, 1, -24 }, // 0xcd 'LATIN SMALL LETTER I WITH ACUTE' U+00ED
{ 7110, 9, 24, 9, 0, -23 }, // 0xce 'LATIN SMALL LETTER I WITH CIRCUMFLEX' U+00EE
{ 7137, 12, 24, 9, -2, -23 }, // 0xcf 'LATIN SMALL LETTER I WITH DIAERESIS' U+00EF
{ 7173, 17, 24, 19, 1, -23 }, // 0xd0 'LATIN SMALL LETTER ETH' U+00F0
{ 7224, 14, 23, 18, 2, -22 }, // 0xd1 'LATIN SMALL LETTER N WITH TILDE' U+00F1
{ 7265, 17, 25, 19, 1, -24 }, // 0xd2 'LATIN SMALL LETTER O WITH GRAVE' U+00F2
{ 7319, 17, 25, 19, 1, -24 }, // 0xd3 'LATIN SMALL LETTER O WITH ACUTE' U+00F3
{ 7373, 17, 24, 19, 1, -23 }, // 0xd4 'LATIN SMALL LETTER O WITH CIRCUMFLEX' U+00F4
{ 7424, 17, 23, 19, 1, -22 }, // 0xd5 'LATIN SMALL LETTER O WITH TILDE' U+00F5
{ 7473, 17, 24, 19, 1, -23 }, // 0xd6 'LATIN SMALL LETTER O WITH DIAERESIS' U+00F6
{ 7524, 15, 18, 19, 2, -17 }, // 0xd7 'DIVISION SIGN' U+00F7
{ 7558, 17, 18, 19, 1, -16 }, // 0xd8 'LATIN SMALL LETTER O WITH STROKE' U+00F8
{ 7597, 14, 25, 18, 2, -24 }, // 0xd9 'LATIN SMALL LETTER U WITH GRAVE' U+00F9
{ 7641, 14, 25, 18, 2, -24 }, // 0xda 'LATIN SMALL LETTER U WITH ACUTE' U+00FA
{ 7685, 14, 24, 18, 2, -23 }, // 0xdb 'LATIN SMALL LETTER U WITH CIRCUMFLEX' U+00FB
{ 7727, 14, 24, 18, 2, -23 }, // 0xdc 'LATIN SMALL LETTER U WITH DIAERESIS' U+00FC
{ 7769, 17, 31, 17, 0, -24 }, // 0xdd 'LATIN SMALL LETTER Y WITH ACUTE' U+00FD
{ 7835, 16, 30, 19, 2, -23 }, // 0xde 'LATIN SMALL LETTER THORN' U+00FE
{ 7895, 17, 30, 17, 0, -23 } }; // 0xdf 'LATIN SMALL LETTER Y WITH DIAERESIS' U+000FF
const GFXfont Ubuntu_Bold16pt8b PROGMEM = {
(uint8_t *)Ubuntu_Bold16pt8bBitmaps,
(GFXglyph *)Ubuntu_Bold16pt8bGlyphs,
0x20, 0xDF, 36 };
// Approx. 9310 bytes

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,377 @@
const uint8_t Ubuntu_Bold8pt8bBitmaps[] PROGMEM = {
0x00, 0xFF, 0xFC, 0xFC, 0xDE, 0xF7, 0xBD, 0x80, 0x1B, 0x0D, 0x87, 0xDF,
0xFF, 0xF9, 0xB3, 0xFF, 0xFF, 0x6C, 0x36, 0x1B, 0x00, 0x18, 0x30, 0xFF,
0xFC, 0x38, 0x1C, 0x1E, 0x0E, 0x0C, 0x1F, 0xF7, 0xC3, 0x06, 0x00, 0x78,
0x63, 0xF3, 0x0C, 0xCC, 0x33, 0x60, 0xCF, 0xFB, 0xFF, 0xF7, 0xFC, 0xC1,
0xB3, 0x0C, 0xCC, 0x33, 0xF1, 0x87, 0x80, 0x3C, 0x1F, 0x86, 0x61, 0x98,
0x3C, 0x1E, 0x6C, 0xDB, 0x1C, 0xC7, 0x3F, 0xE7, 0xDC, 0xFF, 0xC0, 0x13,
0x66, 0xCC, 0xCC, 0xCC, 0xCC, 0x66, 0x31, 0x8C, 0x66, 0x33, 0x33, 0x33,
0x33, 0x66, 0xC8, 0x39, 0xFF, 0xF8, 0x86, 0xDD, 0xD1, 0x00, 0x18, 0x18,
0x18, 0xFF, 0xFF, 0x18, 0x18, 0x18, 0x6D, 0xEC, 0xFF, 0xC0, 0xFF, 0x80,
0x06, 0x1C, 0x30, 0x60, 0xC3, 0x06, 0x0C, 0x30, 0x60, 0xC3, 0x06, 0x0C,
0x38, 0x60, 0x38, 0xFB, 0xBE, 0x3C, 0x78, 0xF1, 0xE3, 0xEE, 0xF8, 0xE0,
0x1B, 0xFE, 0xB1, 0x8C, 0x63, 0x18, 0xC6, 0x7D, 0xFD, 0x18, 0x30, 0xE3,
0x8E, 0x38, 0xE1, 0xFF, 0xF8, 0x7D, 0xFC, 0x18, 0x33, 0xE7, 0x81, 0x83,
0x87, 0xFF, 0xE0, 0x06, 0x0E, 0x1E, 0x3E, 0x36, 0x66, 0xE6, 0xFF, 0xFF,
0x06, 0x06, 0x7E, 0xFD, 0x83, 0x0F, 0x9F, 0x83, 0x83, 0x07, 0xFB, 0xE0,
0x1C, 0x79, 0xC7, 0x0F, 0xDF, 0xF1, 0xE3, 0xC6, 0xF8, 0xE0, 0xFF, 0xFC,
0x30, 0xE1, 0x87, 0x0C, 0x18, 0x60, 0xC1, 0x80, 0x3C, 0xFF, 0x1E, 0x3E,
0xEF, 0xBF, 0xE3, 0xC7, 0xFD, 0xF0, 0x38, 0xFB, 0x1E, 0x3C, 0x7F, 0xDF,
0x87, 0x1C, 0xF1, 0xC0, 0xFF, 0x80, 0x3F, 0xE0, 0x77, 0x70, 0x00, 0x06,
0x66, 0xCC, 0x06, 0x3E, 0xF8, 0xC0, 0xF8, 0x3F, 0x06, 0xFF, 0xFF, 0x00,
0x00, 0xFF, 0xFF, 0x60, 0x7C, 0x1F, 0x03, 0x1F, 0xFC, 0x60, 0x7D, 0xFC,
0x18, 0x30, 0xC3, 0x0C, 0x18, 0x00, 0x70, 0xE1, 0xC0, 0x0F, 0xC0, 0x7F,
0xC3, 0x83, 0x98, 0x06, 0xE7, 0xCF, 0x3F, 0x3D, 0x8C, 0xF6, 0x33, 0xD8,
0xCF, 0xBF, 0xE6, 0x7F, 0x1E, 0x00, 0x3F, 0xC0, 0x3F, 0x00, 0x0E, 0x01,
0xC0, 0x7C, 0x0D, 0x83, 0xB8, 0x63, 0x0C, 0x63, 0xFE, 0x7F, 0xCC, 0x1B,
0x01, 0x80, 0xFC, 0x7F, 0xB0, 0xD8, 0x6F, 0xE7, 0xFB, 0x07, 0x83, 0xC3,
0xFF, 0xBF, 0x80, 0x1F, 0x1F, 0xD8, 0x18, 0x0C, 0x06, 0x03, 0x01, 0x80,
0x60, 0x3F, 0xC7, 0xC0, 0xFE, 0x3F, 0xCC, 0x3B, 0x07, 0xC0, 0xF0, 0x3C,
0x0F, 0x07, 0xC3, 0xBF, 0xCF, 0xE0, 0xFF, 0xFF, 0xC0, 0xC0, 0xFE, 0xFE,
0xC0, 0xC0, 0xC0, 0xFF, 0xFF, 0xFF, 0xFF, 0xC0, 0xC0, 0xFE, 0xFE, 0xC0,
0xC0, 0xC0, 0xC0, 0xC0, 0x1F, 0x1F, 0xD8, 0x18, 0x0C, 0x06, 0x0F, 0x07,
0x83, 0x71, 0xBF, 0xC7, 0xE0, 0xC1, 0xE0, 0xF0, 0x78, 0x3F, 0xFF, 0xFF,
0x07, 0x83, 0xC1, 0xE0, 0xF0, 0x60, 0xFF, 0xFF, 0xFC, 0x06, 0x0C, 0x18,
0x30, 0x60, 0xC1, 0x83, 0x07, 0xF9, 0xE0, 0xC1, 0xB0, 0xCC, 0x63, 0x30,
0xF8, 0x3C, 0x0D, 0x83, 0x30, 0xC6, 0x30, 0xCC, 0x18, 0xC0, 0xC0, 0xC0,
0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xFF, 0xFF, 0x60, 0x33, 0x83, 0x9C,
0x1C, 0xF1, 0xED, 0x8D, 0xEE, 0xEF, 0x36, 0x79, 0xF3, 0xC7, 0x1E, 0x38,
0xF0, 0x06, 0xC0, 0xF8, 0x3F, 0x0F, 0xE3, 0xDC, 0xF3, 0xBC, 0x7F, 0x0F,
0xC1, 0xF0, 0x7C, 0x0C, 0x1F, 0x07, 0xF1, 0xC7, 0x70, 0x7C, 0x07, 0x80,
0xF0, 0x1F, 0x07, 0x71, 0xCF, 0xF8, 0x7C, 0x00, 0xFC, 0xFE, 0xC3, 0xC3,
0xC3, 0xFE, 0xFC, 0xC0, 0xC0, 0xC0, 0xC0, 0x1F, 0x07, 0xF1, 0xC7, 0x70,
0x7C, 0x07, 0x80, 0xF0, 0x1F, 0x07, 0x71, 0xCF, 0xF0, 0xF8, 0x07, 0x00,
0x7C, 0x07, 0x80, 0xFC, 0x7F, 0x30, 0xD8, 0x6C, 0x37, 0xF3, 0xE1, 0x98,
0xC6, 0x63, 0xB0, 0xE0, 0x3D, 0xFF, 0x06, 0x0F, 0x0F, 0x87, 0x83, 0x07,
0xFD, 0xF0, 0xFF, 0xFF, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
0x18, 0xC1, 0xE0, 0xF0, 0x78, 0x3C, 0x1E, 0x0F, 0x07, 0x83, 0xE3, 0xBF,
0x8F, 0x80, 0xC0, 0x6C, 0x19, 0x83, 0x38, 0xE3, 0x18, 0x63, 0x06, 0xC0,
0xD8, 0x0E, 0x01, 0xC0, 0x38, 0x00, 0xC0, 0x07, 0x87, 0x0D, 0x8E, 0x33,
0x1C, 0x66, 0x6C, 0xC6, 0xDB, 0x0D, 0xB6, 0x1B, 0x6C, 0x1C, 0x70, 0x38,
0xE0, 0x71, 0xC0, 0xE0, 0xEC, 0x18, 0xC6, 0x0D, 0x80, 0xE0, 0x1C, 0x03,
0x80, 0xD8, 0x31, 0x8C, 0x1B, 0x83, 0x80, 0xC0, 0xD8, 0x67, 0x38, 0xCC,
0x1E, 0x07, 0x80, 0xC0, 0x30, 0x0C, 0x03, 0x00, 0xC0, 0xFF, 0xFF, 0x06,
0x0C, 0x1C, 0x18, 0x30, 0x70, 0x60, 0xFF, 0xFF, 0xFF, 0xF1, 0x8C, 0x63,
0x18, 0xC6, 0x31, 0x8C, 0x63, 0xFF, 0xC1, 0xC1, 0x83, 0x06, 0x06, 0x0C,
0x18, 0x18, 0x30, 0x60, 0x60, 0xC1, 0x83, 0x83, 0xFF, 0xC6, 0x31, 0x8C,
0x63, 0x18, 0xC6, 0x31, 0x8F, 0xFF, 0x08, 0x0E, 0x0F, 0x86, 0xC7, 0x77,
0x1D, 0x04, 0xFF, 0xFF, 0x4E, 0x72, 0x7C, 0xFC, 0x1B, 0xFF, 0xF8, 0xFF,
0xBF, 0xC1, 0x83, 0x06, 0x0F, 0x9F, 0xB3, 0xE3, 0xC7, 0x9F, 0xF7, 0xC0,
0x3D, 0xFE, 0x30, 0xC3, 0x87, 0xCF, 0x06, 0x0C, 0x18, 0x33, 0xEF, 0xF9,
0xE3, 0xC7, 0xCD, 0xF9, 0xF0, 0x38, 0xFB, 0x1F, 0xFF, 0xF8, 0x1F, 0x1E,
0x7F, 0xF1, 0x8F, 0xFF, 0x18, 0xC6, 0x31, 0x80, 0x3E, 0xFF, 0x9E, 0x3C,
0x7C, 0xDF, 0x9F, 0x06, 0xFB, 0xE0, 0xC1, 0x83, 0x06, 0x0F, 0xDF, 0xF1,
0xE3, 0xC7, 0x8F, 0x1E, 0x30, 0xFC, 0xFF, 0xFF, 0x33, 0x30, 0x33, 0x33,
0x33, 0x33, 0x3F, 0xE0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC6, 0xCC, 0xD8, 0xF0,
0xD8, 0xCC, 0xC6, 0xC7, 0xDB, 0x6D, 0xB6, 0xDB, 0xB0, 0xFD, 0xEF, 0xFF,
0xC6, 0x3C, 0x63, 0xC6, 0x3C, 0x63, 0xC6, 0x3C, 0x63, 0xFD, 0xFF, 0x1E,
0x3C, 0x78, 0xF1, 0xE3, 0x3C, 0x7E, 0xE7, 0xC3, 0xC3, 0xE7, 0x7E, 0x3C,
0xF9, 0xFB, 0x3E, 0x3C, 0x79, 0xFF, 0x7C, 0xC1, 0x83, 0x00, 0x3E, 0xFF,
0x9E, 0x3C, 0x7C, 0xDF, 0xBF, 0x06, 0x0C, 0x18, 0xFF, 0xF1, 0x8C, 0x63,
0x18, 0x7F, 0xEC, 0x3E, 0x7C, 0x3F, 0xFE, 0xC6, 0x3F, 0xFC, 0x63, 0x18,
0xFB, 0xC0, 0xC7, 0x8F, 0x1E, 0x3C, 0x78, 0xFF, 0xBF, 0xC1, 0xB1, 0x98,
0xCE, 0xE3, 0x61, 0xF0, 0x70, 0x38, 0xC6, 0x79, 0xCD, 0xBB, 0x35, 0x66,
0xAC, 0x77, 0x0E, 0xE1, 0x8C, 0xE3, 0xBB, 0x8D, 0x83, 0x81, 0xC1, 0xB1,
0xDD, 0xC7, 0xE3, 0xB1, 0x98, 0xCE, 0xE3, 0x61, 0xF0, 0x70, 0x38, 0x18,
0x7C, 0x3C, 0x00, 0xFF, 0xF1, 0x8E, 0x71, 0x8F, 0xFF, 0x3B, 0xD8, 0xC6,
0x31, 0x98, 0xC3, 0x18, 0xC6, 0x31, 0xE7, 0xFF, 0xFF, 0xFF, 0xFF, 0xE7,
0x8C, 0x63, 0x18, 0xC3, 0x19, 0x8C, 0x63, 0x1B, 0xDC, 0x73, 0xFF, 0xCE,
0xFE, 0x18, 0x61, 0x86, 0x18, 0x61, 0x86, 0x18, 0x7F, 0x00, 0xFC, 0xFF,
0xFC, 0x18, 0x63, 0xDF, 0xE3, 0x0C, 0x30, 0x7C, 0xF1, 0x86, 0x1F, 0x3F,
0x70, 0x60, 0x60, 0xFE, 0xFE, 0x60, 0x60, 0x7F, 0x7F, 0x42, 0xFF, 0x7E,
0x66, 0x66, 0x7E, 0xFF, 0x42, 0xC3, 0x66, 0x66, 0x3C, 0x3C, 0x7E, 0x7E,
0x18, 0x7E, 0x7E, 0x18, 0xFF, 0xF0, 0x0F, 0xFF, 0x7F, 0xFC, 0x38, 0x7B,
0xFC, 0xF3, 0xFD, 0xE1, 0xC3, 0xFF, 0xE0, 0xDE, 0xC0, 0x1F, 0x07, 0xF1,
0xC7, 0x77, 0x7D, 0xE7, 0xB0, 0xF7, 0x9F, 0x77, 0x71, 0xC7, 0xF0, 0x7C,
0x00, 0x70, 0xDF, 0xFD, 0xBC, 0x11, 0x1D, 0xDD, 0xDD, 0xC7, 0x71, 0xDC,
0x44, 0xFF, 0xFC, 0x18, 0x30, 0x60, 0xC0, 0xFF, 0xC0, 0x1F, 0x07, 0xF1,
0xC7, 0x7F, 0x7D, 0xB7, 0xBE, 0xF6, 0xDF, 0xDF, 0x71, 0xC7, 0xF0, 0x7C,
0x00, 0xFF, 0xC0, 0x7B, 0xFC, 0xF3, 0xFD, 0xE0, 0x18, 0x18, 0x18, 0xFF,
0xFF, 0x18, 0x18, 0x18, 0x00, 0xFF, 0xFF, 0xF0, 0xCE, 0xEE, 0x7C, 0xF8,
0xDC, 0x31, 0x8F, 0xC0, 0x27, 0xE4, 0xC7, 0x8F, 0x1E, 0x3C, 0x78, 0xFF,
0xFF, 0xC1, 0x83, 0x00, 0x7F, 0xFF, 0xFE, 0x7F, 0x3F, 0x9F, 0xCC, 0xE6,
0x33, 0x19, 0x8C, 0xC6, 0x63, 0x31, 0x98, 0xCC, 0xFF, 0x80, 0x47, 0xA0,
0x7F, 0x33, 0x33, 0x7B, 0x3C, 0xF3, 0xCD, 0xE0, 0x44, 0x77, 0x1D, 0xC7,
0x77, 0x77, 0x71, 0x10, 0x70, 0xC7, 0x8C, 0x0C, 0x60, 0x66, 0x03, 0x30,
0x1B, 0x38, 0x19, 0xC1, 0x9E, 0x0D, 0xB0, 0xCF, 0xC6, 0x0C, 0x71, 0x8F,
0x30, 0x33, 0x03, 0x60, 0x36, 0x03, 0xDE, 0x0C, 0x31, 0x87, 0x18, 0xE3,
0x1C, 0x31, 0xF0, 0xF8, 0x60, 0x63, 0x07, 0x0C, 0x06, 0x60, 0x19, 0x80,
0x6C, 0xEF, 0x33, 0x81, 0x9E, 0x06, 0xD8, 0x33, 0xF0, 0xC1, 0x80, 0x38,
0x70, 0xE0, 0x01, 0x87, 0x1C, 0x60, 0xC1, 0xFD, 0xF0, 0x08, 0x03, 0x80,
0x38, 0x02, 0x00, 0x00, 0x1C, 0x03, 0x80, 0xF8, 0x1B, 0x07, 0x70, 0xC6,
0x18, 0xC7, 0xFC, 0xFF, 0x98, 0x36, 0x03, 0x04, 0x01, 0xC0, 0x70, 0x04,
0x00, 0x00, 0x1C, 0x03, 0x80, 0xF8, 0x1B, 0x07, 0x70, 0xC6, 0x18, 0xC7,
0xFC, 0xFF, 0x98, 0x36, 0x03, 0x04, 0x01, 0xC0, 0x6C, 0x00, 0x00, 0xE0,
0x1C, 0x07, 0xC0, 0xD8, 0x3B, 0x86, 0x30, 0xC6, 0x3F, 0xE7, 0xFC, 0xC1,
0xB0, 0x18, 0x0C, 0x83, 0xF0, 0x4C, 0x00, 0x00, 0x00, 0x1C, 0x03, 0x80,
0xF8, 0x1B, 0x07, 0x70, 0xC6, 0x18, 0xC7, 0xFC, 0xFF, 0x98, 0x36, 0x03,
0x1B, 0x03, 0x60, 0x00, 0x00, 0x00, 0xE0, 0x1C, 0x07, 0xC0, 0xD8, 0x3B,
0x86, 0x30, 0xC6, 0x3F, 0xE7, 0xFC, 0xC1, 0xB0, 0x18, 0x0E, 0x03, 0x60,
0x6C, 0x07, 0x00, 0xE0, 0x1C, 0x06, 0xC0, 0xD8, 0x3B, 0x86, 0x30, 0xC6,
0x3F, 0xE7, 0xFC, 0xC1, 0xB0, 0x18, 0x01, 0xFE, 0x07, 0xFC, 0x1F, 0x00,
0x36, 0x00, 0xCF, 0xC1, 0x9F, 0x87, 0xF0, 0x1F, 0xE0, 0x30, 0xC0, 0xC1,
0xFF, 0x83, 0xF8, 0x1F, 0x1F, 0xD8, 0x18, 0x0C, 0x06, 0x03, 0x01, 0x80,
0x60, 0x3F, 0xC7, 0xC1, 0x80, 0x60, 0xE0, 0x10, 0x38, 0x1C, 0x08, 0x00,
0xFF, 0xFF, 0xC0, 0xC0, 0xFE, 0xFE, 0xC0, 0xC0, 0xC0, 0xFF, 0xFF, 0x08,
0x1C, 0x38, 0x10, 0x00, 0xFF, 0xFF, 0xC0, 0xC0, 0xFE, 0xFE, 0xC0, 0xC0,
0xC0, 0xFF, 0xFF, 0x10, 0x38, 0x6C, 0x00, 0xFF, 0xFF, 0xC0, 0xC0, 0xFE,
0xFE, 0xC0, 0xC0, 0xC0, 0xFF, 0xFF, 0x6C, 0x6C, 0x00, 0x00, 0xFF, 0xFF,
0xC0, 0xC0, 0xFE, 0xFE, 0xC0, 0xC0, 0xC0, 0xFF, 0xFF, 0x4E, 0x72, 0x06,
0x66, 0x66, 0x66, 0x66, 0x66, 0x27, 0xE4, 0x06, 0x66, 0x66, 0x66, 0x66,
0x66, 0x23, 0xB6, 0x06, 0x31, 0x8C, 0x63, 0x18, 0xC6, 0x31, 0x80, 0xDE,
0xC0, 0x06, 0x31, 0x8C, 0x63, 0x18, 0xC6, 0x31, 0x80, 0x7F, 0x0F, 0xF1,
0x87, 0x30, 0x7F, 0x87, 0xF0, 0xD8, 0x1B, 0x07, 0x61, 0xCF, 0xF1, 0xFC,
0x00, 0x19, 0x0F, 0xC2, 0x60, 0x00, 0x00, 0x30, 0x3E, 0x0F, 0xC3, 0xF8,
0xF7, 0x3C, 0xEF, 0x1F, 0xC3, 0xF0, 0x7C, 0x1F, 0x03, 0x08, 0x03, 0x80,
0x38, 0x02, 0x00, 0x00, 0x3E, 0x0F, 0xE3, 0x8E, 0xE0, 0xF8, 0x0F, 0x01,
0xE0, 0x3E, 0x0E, 0xE3, 0x9F, 0xF0, 0xF8, 0x02, 0x00, 0xE0, 0x38, 0x02,
0x00, 0x00, 0x3E, 0x0F, 0xE3, 0x8E, 0xE0, 0xF8, 0x0F, 0x01, 0xE0, 0x3E,
0x0E, 0xE3, 0x9F, 0xF0, 0xF8, 0x04, 0x01, 0xC0, 0x6C, 0x00, 0x01, 0xF0,
0x7F, 0x1C, 0x77, 0x07, 0xC0, 0x78, 0x0F, 0x01, 0xF0, 0x77, 0x1C, 0xFF,
0x87, 0xC0, 0x0C, 0x83, 0xF0, 0x4C, 0x00, 0x00, 0x00, 0x3E, 0x0F, 0xE3,
0x8E, 0xE0, 0xF8, 0x0F, 0x01, 0xE0, 0x3E, 0x0E, 0xE3, 0x9F, 0xF0, 0xF8,
0x1B, 0x03, 0x60, 0x00, 0x00, 0x01, 0xF0, 0x7F, 0x1C, 0x77, 0x07, 0xC0,
0x78, 0x0F, 0x01, 0xF0, 0x77, 0x1C, 0xFF, 0x87, 0xC0, 0x45, 0xDD, 0xF1,
0xC7, 0xDD, 0xD1, 0x00, 0x1F, 0xE7, 0xF9, 0x87, 0x61, 0xFC, 0x67, 0x9C,
0xF3, 0x1F, 0xC3, 0x70, 0xCF, 0xFB, 0x7C, 0x00, 0x00, 0x10, 0x1C, 0x07,
0x01, 0x00, 0x06, 0x0F, 0x07, 0x83, 0xC1, 0xE0, 0xF0, 0x78, 0x3C, 0x1F,
0x1D, 0xFC, 0x7C, 0x04, 0x07, 0x07, 0x01, 0x00, 0x06, 0x0F, 0x07, 0x83,
0xC1, 0xE0, 0xF0, 0x78, 0x3C, 0x1F, 0x1D, 0xFC, 0x7C, 0x08, 0x0E, 0x0D,
0x80, 0x0C, 0x1E, 0x0F, 0x07, 0x83, 0xC1, 0xE0, 0xF0, 0x78, 0x3E, 0x3B,
0xF8, 0xF8, 0x36, 0x1B, 0x00, 0x00, 0x0C, 0x1E, 0x0F, 0x07, 0x83, 0xC1,
0xE0, 0xF0, 0x78, 0x3E, 0x3B, 0xF8, 0xF8, 0x04, 0x03, 0x81, 0xC0, 0x20,
0x00, 0x30, 0x36, 0x19, 0xCE, 0x33, 0x07, 0x81, 0xE0, 0x30, 0x0C, 0x03,
0x00, 0xC0, 0x30, 0xC0, 0xC0, 0xFC, 0xFE, 0xC3, 0xC3, 0xC3, 0xFE, 0xFC,
0xC0, 0xC0, 0x3E, 0x3F, 0xB8, 0xD8, 0x6C, 0x66, 0x63, 0x39, 0x8E, 0xC1,
0xE0, 0xF3, 0xFB, 0xE0, 0x20, 0xE0, 0xE0, 0x80, 0x0F, 0x9F, 0x83, 0x7F,
0xFF, 0x1F, 0xF7, 0xE0, 0x08, 0x38, 0xE0, 0x80, 0x0F, 0x9F, 0x83, 0x7F,
0xFF, 0x1F, 0xF7, 0xE0, 0x10, 0x71, 0xB0, 0x07, 0xCF, 0xC1, 0xBF, 0xFF,
0x8F, 0xFB, 0xF0, 0x32, 0xFD, 0x30, 0x00, 0x0F, 0x9F, 0x83, 0x7F, 0xFF,
0x1F, 0xF7, 0xE0, 0x6C, 0xD8, 0x00, 0x07, 0xCF, 0xC1, 0xBF, 0xFF, 0x8F,
0xFB, 0xF0, 0x38, 0xD9, 0xB1, 0xC0, 0x0F, 0x9F, 0x83, 0x7F, 0xFF, 0x1F,
0xF7, 0xE0, 0x7D, 0xC7, 0xFE, 0x06, 0x37, 0xFF, 0xFF, 0xFC, 0x60, 0xFF,
0xE7, 0xDF, 0x3E, 0xFF, 0x86, 0x0C, 0x1C, 0x1F, 0x9F, 0x1C, 0x18, 0xE0,
0x20, 0xE0, 0xE0, 0x80, 0x07, 0x1F, 0x63, 0xFF, 0xFF, 0x03, 0xE3, 0xC0,
0x08, 0x38, 0xE0, 0x80, 0x07, 0x1F, 0x63, 0xFF, 0xFF, 0x03, 0xE3, 0xC0,
0x10, 0x71, 0xB0, 0x03, 0x8F, 0xB1, 0xFF, 0xFF, 0x81, 0xF1, 0xE0, 0x6C,
0xD8, 0x00, 0x03, 0x8F, 0xB1, 0xFF, 0xFF, 0x81, 0xF1, 0xE0, 0x4E, 0x72,
0x06, 0x66, 0x66, 0x66, 0x60, 0x27, 0xE4, 0x06, 0x66, 0x66, 0x66, 0x60,
0x23, 0xB6, 0x06, 0x31, 0x8C, 0x63, 0x18, 0xC0, 0xDE, 0xC0, 0x03, 0x18,
0xC6, 0x31, 0x8C, 0x60, 0x18, 0x1F, 0x1E, 0x06, 0x03, 0x3F, 0x7F, 0xC3,
0xC3, 0xC7, 0x7E, 0x3C, 0x65, 0xFA, 0x60, 0x00, 0x1F, 0xBF, 0xE3, 0xC7,
0x8F, 0x1E, 0x3C, 0x60, 0x10, 0x38, 0x1C, 0x08, 0x00, 0x3C, 0x7E, 0xE7,
0xC3, 0xC3, 0xE7, 0x7E, 0x3C, 0x08, 0x1C, 0x38, 0x10, 0x00, 0x3C, 0x7E,
0xE7, 0xC3, 0xC3, 0xE7, 0x7E, 0x3C, 0x10, 0x38, 0x6C, 0x00, 0x3C, 0x7E,
0xE7, 0xC3, 0xC3, 0xE7, 0x7E, 0x3C, 0x32, 0x7E, 0x4C, 0x00, 0x00, 0x3C,
0x7E, 0xE7, 0xC3, 0xC3, 0xE7, 0x7E, 0x3C, 0x6C, 0x6C, 0x00, 0x00, 0x3C,
0x7E, 0xE7, 0xC3, 0xC3, 0xE7, 0x7E, 0x3C, 0x38, 0x70, 0xE0, 0x0F, 0xFF,
0xC0, 0x1C, 0x38, 0x70, 0x3F, 0x7E, 0xEF, 0xCB, 0xD3, 0xF7, 0x7E, 0xFC,
0x20, 0xE0, 0xE0, 0x80, 0x18, 0xF1, 0xE3, 0xC7, 0x8F, 0x1F, 0xF7, 0xE0,
0x08, 0x38, 0xE0, 0x80, 0x18, 0xF1, 0xE3, 0xC7, 0x8F, 0x1F, 0xF7, 0xE0,
0x10, 0x71, 0xB0, 0x0C, 0x78, 0xF1, 0xE3, 0xC7, 0x8F, 0xFB, 0xF0, 0x6C,
0xD8, 0x00, 0x0C, 0x78, 0xF1, 0xE3, 0xC7, 0x8F, 0xFB, 0xF0, 0x04, 0x07,
0x07, 0x01, 0x00, 0x07, 0x1D, 0x8C, 0xC6, 0x77, 0x1B, 0x0F, 0x83, 0x81,
0xC0, 0xC3, 0xE1, 0xE0, 0xC0, 0xC0, 0xC0, 0xC0, 0xFC, 0xFE, 0xC7, 0xC3,
0xC3, 0xC7, 0xFE, 0xFC, 0xC0, 0xC0, 0xC0, 0x36, 0x1B, 0x00, 0x00, 0x0E,
0x3B, 0x19, 0x8C, 0xEE, 0x36, 0x1F, 0x07, 0x03, 0x81, 0x87, 0xC3, 0xC0 };
const GFXglyph Ubuntu_Bold8pt8bGlyphs[] PROGMEM = {
{ 0, 1, 1, 4, 0, 0 }, // 0x20 ' ' U+0020
{ 1, 2, 11, 4, 1, -10 }, // 0x21 '!' U+0021
{ 4, 5, 5, 7, 1, -11 }, // 0x22 '"' U+0022
{ 8, 9, 11, 11, 1, -10 }, // 0x23 '#' U+0023
{ 21, 7, 15, 9, 1, -12 }, // 0x24 '$' U+0024
{ 35, 14, 11, 16, 1, -10 }, // 0x25 '%' U+0025
{ 55, 10, 11, 11, 1, -10 }, // 0x26 '&' U+0026
{ 69, 2, 5, 4, 1, -11 }, // 0x27 ''' U+0027
{ 71, 4, 16, 6, 1, -12 }, // 0x28 '(' U+0028
{ 79, 4, 16, 6, 1, -12 }, // 0x29 ')' U+0029
{ 87, 7, 7, 8, 1, -10 }, // 0x2a '*' U+002A
{ 94, 8, 8, 10, 1, -8 }, // 0x2b '+' U+002B
{ 102, 3, 5, 4, 0, -1 }, // 0x2c ',' U+002C
{ 104, 5, 2, 7, 1, -5 }, // 0x2d '-' U+002D
{ 106, 3, 3, 5, 1, -2 }, // 0x2e '.' U+002E
{ 108, 7, 16, 7, 0, -12 }, // 0x2f '/' U+002F
{ 122, 7, 11, 9, 1, -10 }, // 0x30 '0' U+0030
{ 132, 5, 11, 9, 1, -10 }, // 0x31 '1' U+0031
{ 139, 7, 11, 9, 1, -10 }, // 0x32 '2' U+0032
{ 149, 7, 11, 9, 1, -10 }, // 0x33 '3' U+0033
{ 159, 8, 11, 9, 1, -10 }, // 0x34 '4' U+0034
{ 170, 7, 11, 9, 1, -10 }, // 0x35 '5' U+0035
{ 180, 7, 11, 9, 1, -10 }, // 0x36 '6' U+0036
{ 190, 7, 11, 9, 1, -10 }, // 0x37 '7' U+0037
{ 200, 7, 11, 9, 1, -10 }, // 0x38 '8' U+0038
{ 210, 7, 11, 9, 1, -10 }, // 0x39 '9' U+0039
{ 220, 3, 9, 5, 1, -8 }, // 0x3a ':' U+003A
{ 224, 4, 12, 5, 0, -8 }, // 0x3b ';' U+003B
{ 230, 8, 7, 9, 1, -7 }, // 0x3c '<' U+003C
{ 237, 8, 6, 10, 1, -7 }, // 0x3d '=' U+003D
{ 243, 8, 7, 9, 0, -7 }, // 0x3e '>' U+003E
{ 250, 7, 12, 7, 0, -11 }, // 0x3f '?' U+003F
{ 261, 14, 14, 16, 1, -10 }, // 0x40 '@' U+0040
{ 286, 11, 11, 11, 0, -10 }, // 0x41 'A' U+0041
{ 302, 9, 11, 11, 1, -10 }, // 0x42 'B' U+0042
{ 315, 9, 11, 11, 1, -10 }, // 0x43 'C' U+0043
{ 328, 10, 11, 12, 1, -10 }, // 0x44 'D' U+0044
{ 342, 8, 11, 10, 1, -10 }, // 0x45 'E' U+0045
{ 353, 8, 11, 9, 1, -10 }, // 0x46 'F' U+0046
{ 364, 9, 11, 11, 1, -10 }, // 0x47 'G' U+0047
{ 377, 9, 11, 11, 1, -10 }, // 0x48 'H' U+0048
{ 390, 2, 11, 4, 1, -10 }, // 0x49 'I' U+0049
{ 393, 7, 11, 8, 0, -10 }, // 0x4a 'J' U+004A
{ 403, 10, 11, 11, 1, -10 }, // 0x4b 'K' U+004B
{ 417, 8, 11, 9, 1, -10 }, // 0x4c 'L' U+004C
{ 428, 13, 11, 15, 1, -10 }, // 0x4d 'M' U+004D
{ 446, 10, 11, 12, 1, -10 }, // 0x4e 'N' U+004E
{ 460, 11, 11, 13, 1, -10 }, // 0x4f 'O' U+004F
{ 476, 8, 11, 10, 1, -10 }, // 0x50 'P' U+0050
{ 487, 11, 14, 13, 1, -10 }, // 0x51 'Q' U+0051
{ 507, 9, 11, 10, 1, -10 }, // 0x52 'R' U+0052
{ 520, 7, 11, 9, 1, -10 }, // 0x53 'S' U+0053
{ 530, 8, 11, 8, 0, -10 }, // 0x54 'T' U+0054
{ 541, 9, 11, 11, 1, -10 }, // 0x55 'U' U+0055
{ 554, 11, 11, 11, 0, -10 }, // 0x56 'V' U+0056
{ 570, 15, 11, 15, 0, -10 }, // 0x57 'W' U+0057
{ 591, 11, 11, 11, 0, -10 }, // 0x58 'X' U+0058
{ 607, 10, 11, 10, 0, -10 }, // 0x59 'Y' U+0059
{ 621, 8, 11, 10, 1, -10 }, // 0x5a 'Z' U+005A
{ 632, 5, 16, 6, 1, -12 }, // 0x5b '[' U+005B
{ 642, 7, 16, 7, 0, -12 }, // 0x5c '\' U+005C
{ 656, 5, 16, 6, 0, -12 }, // 0x5d ']' U+005D
{ 666, 9, 7, 9, 0, -10 }, // 0x5e '^' U+005E
{ 674, 8, 2, 8, 0, 2 }, // 0x5f '_' U+005F
{ 676, 4, 4, 5, 1, -12 }, // 0x60 '`' U+0060
{ 678, 7, 8, 9, 1, -7 }, // 0x61 'a' U+0061
{ 685, 7, 12, 9, 1, -11 }, // 0x62 'b' U+0062
{ 696, 6, 8, 8, 1, -7 }, // 0x63 'c' U+0063
{ 702, 7, 12, 9, 1, -11 }, // 0x64 'd' U+0064
{ 713, 7, 8, 9, 1, -7 }, // 0x65 'e' U+0065
{ 720, 5, 12, 6, 1, -11 }, // 0x66 'f' U+0066
{ 728, 7, 11, 9, 1, -7 }, // 0x67 'g' U+0067
{ 738, 7, 12, 9, 1, -11 }, // 0x68 'h' U+0068
{ 749, 2, 12, 4, 1, -11 }, // 0x69 'i' U+0069
{ 752, 4, 15, 4, -1, -11 }, // 0x6a 'j' U+006A
{ 760, 8, 12, 9, 1, -11 }, // 0x6b 'k' U+006B
{ 772, 3, 12, 4, 1, -11 }, // 0x6c 'l' U+006C
{ 777, 12, 8, 14, 1, -7 }, // 0x6d 'm' U+006D
{ 789, 7, 8, 9, 1, -7 }, // 0x6e 'n' U+006E
{ 796, 8, 8, 10, 1, -7 }, // 0x6f 'o' U+006F
{ 804, 7, 11, 9, 1, -7 }, // 0x70 'p' U+0070
{ 814, 7, 11, 9, 1, -7 }, // 0x71 'q' U+0071
{ 824, 5, 8, 6, 1, -7 }, // 0x72 'r' U+0072
{ 829, 6, 8, 8, 1, -7 }, // 0x73 's' U+0073
{ 835, 5, 10, 7, 1, -9 }, // 0x74 't' U+0074
{ 842, 7, 8, 9, 1, -7 }, // 0x75 'u' U+0075
{ 849, 9, 8, 9, 0, -7 }, // 0x76 'v' U+0076
{ 858, 11, 8, 11, 0, -7 }, // 0x77 'w' U+0077
{ 869, 9, 8, 9, 0, -7 }, // 0x78 'x' U+0078
{ 878, 9, 11, 9, 0, -7 }, // 0x79 'y' U+0079
{ 891, 6, 8, 8, 1, -7 }, // 0x7a 'z' U+007A
{ 897, 5, 16, 5, 0, -12 }, // 0x7b '{' U+007B
{ 907, 2, 16, 4, 1, -12 }, // 0x7c '|' U+007C
{ 911, 5, 16, 5, 0, -12 }, // 0x7d '}' U+007D
{ 921, 8, 3, 9, 1, -5 }, // 0x7e '~' U+007E
{ 924, 6, 12, 8, 1, -11 }, // 0x7f 'REPLACEMENT CHARACTER *' U+2370
{ 933, 1, 1, 4, 0, 0 }, // 0x80 'NO-BREAK SPACE' U+00A0
{ 934, 2, 11, 4, 1, -7 }, // 0x81 'INVERTED EXCLAMATION MARK' U+00A1
{ 937, 6, 12, 9, 1, -9 }, // 0x82 'CENT SIGN' U+00A2
{ 946, 8, 11, 9, 0, -10 }, // 0x83 'POUND SIGN' U+00A3
{ 957, 8, 8, 9, 0, -8 }, // 0x84 'CURRENCY SIGN' U+00A4
{ 965, 8, 11, 8, 0, -10 }, // 0x85 'YEN SIGN' U+00A5
{ 976, 2, 16, 5, 1, -12 }, // 0x86 'BROKEN BAR' U+00A6
{ 980, 6, 14, 8, 1, -10 }, // 0x87 'SECTION SIGN' U+00A7
{ 991, 5, 2, 9, 2, -11 }, // 0x88 'DIAERESIS' U+00A8
{ 993, 11, 11, 13, 1, -10 }, // 0x89 'COPYRIGHT SIGN' U+00A9
{ 1009, 5, 6, 6, 0, -10 }, // 0x8a 'FEMININE ORDINAL INDICATOR' U+00AA
{ 1013, 9, 7, 10, 0, -7 }, // 0x8b 'LEFT-POINTING DOUBLE ANGLE QUOTATION MARK' U+00AB
{ 1021, 7, 6, 9, 1, -6 }, // 0x8c 'NOT SIGN' U+00AC
{ 1027, 5, 2, 7, 1, -5 }, // 0x8d 'SOFT HYPHEN' U+00AD
{ 1029, 11, 11, 13, 1, -10 }, // 0x8e 'REGISTERED SIGN' U+00AE
{ 1045, 5, 2, 6, 0, -11 }, // 0x8f 'MACRON' U+00AF
{ 1047, 6, 6, 6, 0, -12 }, // 0x90 'DEGREE SIGN' U+00B0
{ 1052, 8, 11, 10, 1, -10 }, // 0x91 'PLUS-MINUS SIGN' U+00B1
{ 1063, 5, 6, 6, 0, -10 }, // 0x92 'SUPERSCRIPT TWO' U+00B2
{ 1067, 5, 7, 6, 0, -10 }, // 0x93 'SUPERSCRIPT THREE' U+00B3
{ 1072, 4, 4, 5, 0, -12 }, // 0x94 'ACUTE ACCENT' U+00B4
{ 1074, 7, 11, 9, 1, -7 }, // 0x95 'MICRO SIGN' U+00B5
{ 1084, 9, 14, 11, 1, -10 }, // 0x96 'PILCROW SIGN' U+00B6
{ 1100, 3, 3, 5, 1, -5 }, // 0x97 'MIDDLE DOT' U+00B7
{ 1102, 3, 4, 5, 1, 1 }, // 0x98 'CEDILLA' U+00B8
{ 1104, 4, 6, 6, 1, -10 }, // 0x99 'SUPERSCRIPT ONE' U+00B9
{ 1107, 6, 6, 8, 1, -10 }, // 0x9a 'MASCULINE ORDINAL INDICATOR' U+00BA
{ 1112, 9, 7, 10, 0, -7 }, // 0x9b 'RIGHT-POINTING DOUBLE ANGLE QUOTATION MARK' U+00BB
{ 1120, 13, 11, 14, 1, -10 }, // 0x9c 'VULGAR FRACTION ONE QUARTER' U+00BC
{ 1138, 12, 11, 14, 1, -10 }, // 0x9d 'VULGAR FRACTION ONE HALF' U+00BD
{ 1155, 14, 11, 14, 0, -10 }, // 0x9e 'VULGAR FRACTION THREE QUARTERS' U+00BE
{ 1175, 7, 11, 7, 0, -7 }, // 0x9f 'INVERTED QUESTION MARK' U+00BF
{ 1185, 11, 16, 11, 0, -15 }, // 0xa0 'LATIN CAPITAL LETTER A WITH GRAVE' U+00C0
{ 1207, 11, 16, 11, 0, -15 }, // 0xa1 'LATIN CAPITAL LETTER A WITH ACUTE' U+00C1
{ 1229, 11, 15, 11, 0, -14 }, // 0xa2 'LATIN CAPITAL LETTER A WITH CIRCUMFLEX' U+00C2
{ 1250, 11, 16, 11, 0, -15 }, // 0xa3 'LATIN CAPITAL LETTER A WITH TILDE' U+00C3
{ 1272, 11, 15, 11, 0, -14 }, // 0xa4 'LATIN CAPITAL LETTER A WITH DIAERESIS' U+00C4
{ 1293, 11, 15, 11, 0, -14 }, // 0xa5 'LATIN CAPITAL LETTER A WITH RING ABOVE' U+00C5
{ 1314, 15, 11, 16, 0, -10 }, // 0xa6 'LATIN CAPITAL LETTER AE' U+00C6
{ 1335, 9, 14, 11, 1, -10 }, // 0xa7 'LATIN CAPITAL LETTER C WITH CEDILLA' U+00C7
{ 1351, 8, 16, 10, 1, -15 }, // 0xa8 'LATIN CAPITAL LETTER E WITH GRAVE' U+00C8
{ 1367, 8, 16, 10, 1, -15 }, // 0xa9 'LATIN CAPITAL LETTER E WITH ACUTE' U+00C9
{ 1383, 8, 15, 10, 1, -14 }, // 0xaa 'LATIN CAPITAL LETTER E WITH CIRCUMFLEX' U+00CA
{ 1398, 8, 15, 10, 1, -14 }, // 0xab 'LATIN CAPITAL LETTER E WITH DIAERESIS' U+00CB
{ 1413, 4, 16, 4, 0, -15 }, // 0xac 'LATIN CAPITAL LETTER I WITH GRAVE' U+00CC
{ 1421, 4, 16, 4, 0, -15 }, // 0xad 'LATIN CAPITAL LETTER I WITH ACUTE' U+00CD
{ 1429, 5, 15, 4, 0, -14 }, // 0xae 'LATIN CAPITAL LETTER I WITH CIRCUMFLEX' U+00CE
{ 1439, 5, 15, 4, 0, -14 }, // 0xaf 'LATIN CAPITAL LETTER I WITH DIAERESIS' U+00CF
{ 1449, 11, 11, 12, 0, -10 }, // 0xb0 'LATIN CAPITAL LETTER ETH' U+00D0
{ 1465, 10, 16, 12, 1, -15 }, // 0xb1 'LATIN CAPITAL LETTER N WITH TILDE' U+00D1
{ 1485, 11, 16, 13, 1, -15 }, // 0xb2 'LATIN CAPITAL LETTER O WITH GRAVE' U+00D2
{ 1507, 11, 16, 13, 1, -15 }, // 0xb3 'LATIN CAPITAL LETTER O WITH ACUTE' U+00D3
{ 1529, 11, 15, 13, 1, -14 }, // 0xb4 'LATIN CAPITAL LETTER O WITH CIRCUMFLEX' U+00D4
{ 1550, 11, 16, 13, 1, -15 }, // 0xb5 'LATIN CAPITAL LETTER O WITH TILDE' U+00D5
{ 1572, 11, 15, 13, 1, -14 }, // 0xb6 'LATIN CAPITAL LETTER O WITH DIAERESIS' U+00D6
{ 1593, 7, 7, 9, 1, -7 }, // 0xb7 'MULTIPLICATION SIGN' U+00D7
{ 1600, 11, 12, 13, 1, -10 }, // 0xb8 'LATIN CAPITAL LETTER O WITH STROKE' U+00D8
{ 1617, 9, 16, 11, 1, -15 }, // 0xb9 'LATIN CAPITAL LETTER U WITH GRAVE' U+00D9
{ 1635, 9, 16, 11, 1, -15 }, // 0xba 'LATIN CAPITAL LETTER U WITH ACUTE' U+00DA
{ 1653, 9, 15, 11, 1, -14 }, // 0xbb 'LATIN CAPITAL LETTER U WITH CIRCUMFLEX' U+00DB
{ 1670, 9, 15, 11, 1, -14 }, // 0xbc 'LATIN CAPITAL LETTER U WITH DIAERESIS' U+00DC
{ 1687, 10, 16, 10, 0, -15 }, // 0xbd 'LATIN CAPITAL LETTER Y WITH ACUTE' U+00DD
{ 1707, 8, 11, 10, 1, -10 }, // 0xbe 'LATIN CAPITAL LETTER THORN' U+00DE
{ 1718, 9, 12, 11, 1, -11 }, // 0xbf 'LATIN SMALL LETTER SHARP S' U+00DF
{ 1732, 7, 13, 9, 1, -12 }, // 0xc0 'LATIN SMALL LETTER A WITH GRAVE' U+00E0
{ 1744, 7, 13, 9, 1, -12 }, // 0xc1 'LATIN SMALL LETTER A WITH ACUTE' U+00E1
{ 1756, 7, 12, 9, 1, -11 }, // 0xc2 'LATIN SMALL LETTER A WITH CIRCUMFLEX' U+00E2
{ 1767, 7, 13, 9, 1, -12 }, // 0xc3 'LATIN SMALL LETTER A WITH TILDE' U+00E3
{ 1779, 7, 12, 9, 1, -11 }, // 0xc4 'LATIN SMALL LETTER A WITH DIAERESIS' U+00E4
{ 1790, 7, 13, 9, 1, -12 }, // 0xc5 'LATIN SMALL LETTER A WITH RING ABOVE' U+00E5
{ 1802, 12, 8, 14, 1, -7 }, // 0xc6 'LATIN SMALL LETTER AE' U+00E6
{ 1814, 7, 11, 8, 1, -7 }, // 0xc7 'LATIN SMALL LETTER C WITH CEDILLA' U+00E7
{ 1824, 7, 13, 9, 1, -12 }, // 0xc8 'LATIN SMALL LETTER E WITH GRAVE' U+00E8
{ 1836, 7, 13, 9, 1, -12 }, // 0xc9 'LATIN SMALL LETTER E WITH ACUTE' U+00E9
{ 1848, 7, 12, 9, 1, -11 }, // 0xca 'LATIN SMALL LETTER E WITH CIRCUMFLEX' U+00EA
{ 1859, 7, 12, 9, 1, -11 }, // 0xcb 'LATIN SMALL LETTER E WITH DIAERESIS' U+00EB
{ 1870, 4, 13, 4, 0, -12 }, // 0xcc 'LATIN SMALL LETTER I WITH GRAVE' U+00EC
{ 1877, 4, 13, 4, 0, -12 }, // 0xcd 'LATIN SMALL LETTER I WITH ACUTE' U+00ED
{ 1884, 5, 12, 4, 0, -11 }, // 0xce 'LATIN SMALL LETTER I WITH CIRCUMFLEX' U+00EE
{ 1892, 5, 12, 4, -1, -11 }, // 0xcf 'LATIN SMALL LETTER I WITH DIAERESIS' U+00EF
{ 1900, 8, 12, 10, 1, -11 }, // 0xd0 'LATIN SMALL LETTER ETH' U+00F0
{ 1912, 7, 13, 9, 1, -12 }, // 0xd1 'LATIN SMALL LETTER N WITH TILDE' U+00F1
{ 1924, 8, 13, 10, 1, -12 }, // 0xd2 'LATIN SMALL LETTER O WITH GRAVE' U+00F2
{ 1937, 8, 13, 10, 1, -12 }, // 0xd3 'LATIN SMALL LETTER O WITH ACUTE' U+00F3
{ 1950, 8, 12, 10, 1, -11 }, // 0xd4 'LATIN SMALL LETTER O WITH CIRCUMFLEX' U+00F4
{ 1962, 8, 13, 10, 1, -12 }, // 0xd5 'LATIN SMALL LETTER O WITH TILDE' U+00F5
{ 1975, 8, 12, 10, 1, -11 }, // 0xd6 'LATIN SMALL LETTER O WITH DIAERESIS' U+00F6
{ 1987, 7, 10, 9, 1, -9 }, // 0xd7 'DIVISION SIGN' U+00F7
{ 1996, 8, 8, 10, 1, -7 }, // 0xd8 'LATIN SMALL LETTER O WITH STROKE' U+00F8
{ 2004, 7, 13, 9, 1, -12 }, // 0xd9 'LATIN SMALL LETTER U WITH GRAVE' U+00F9
{ 2016, 7, 13, 9, 1, -12 }, // 0xda 'LATIN SMALL LETTER U WITH ACUTE' U+00FA
{ 2028, 7, 12, 9, 1, -11 }, // 0xdb 'LATIN SMALL LETTER U WITH CIRCUMFLEX' U+00FB
{ 2039, 7, 12, 9, 1, -11 }, // 0xdc 'LATIN SMALL LETTER U WITH DIAERESIS' U+00FC
{ 2050, 9, 16, 9, 0, -12 }, // 0xdd 'LATIN SMALL LETTER Y WITH ACUTE' U+00FD
{ 2068, 8, 15, 10, 1, -11 }, // 0xde 'LATIN SMALL LETTER THORN' U+00FE
{ 2083, 9, 15, 9, 0, -11 } }; // 0xdf 'LATIN SMALL LETTER Y WITH DIAERESIS' U+000FF
const GFXfont Ubuntu_Bold8pt8b PROGMEM = {
(uint8_t *)Ubuntu_Bold8pt8bBitmaps,
(GFXglyph *)Ubuntu_Bold8pt8bGlyphs,
0x20, 0xDF, 18 };
// Approx. 3451 bytes

View File

@@ -1,111 +0,0 @@
#!/bin/perl -w
#A tool to generate the part of config.json that deals with pages and fields.
#List of all pages and the number of parameters they expect.
%NoOfFieldsPerPage=qw(
ApparentWind 0
XTETrack 0
Battery2 0
Battery 0
BME280 0
Clock 0
DST810 0
FourValues2 4
FourValues 4
Generator 0
KeelPosition 0
OneValue 1
RollPitch 0
RudderPosition 0
Solar 0
ThreeValues 3
TwoValues 2
Voltage 0
White 0
WindRose 0
WindRoseFlex 6
);
# No changes needed beyond this point
#max number of pages supported by OBP60
$NoOfPages=10;
#Default selection for each page
@Defaults=qw(Voltage WindRose OneValue TwoValues ThreeValues FourValues FourValues2 Clock RollPitch Battery2);
@Numbers=qw(one two three four five six seven eight nine ten);
@Pages=sort(keys(%NoOfFieldsPerPage));
$MaxNoOfFieldsPerPage=0; # inital value, gets updated with maximum entry from %NoOfFieldsPerPage
#find max. number of fields without additional modules
foreach (values(%NoOfFieldsPerPage)){
if ($_ > $MaxNoOfFieldsPerPage){
$MaxNoOfFieldsPerPage=$_;
}
}
for ($PageNo=1;$PageNo<=$NoOfPages;$PageNo++){
print "{\n";
print "\t","\"name\": \"page", $PageNo,"type\",\n";
print "\t","\"label\": \"Type\",\n";
print "\t",'"type": "list",',"\n";
print "\t",'"default": "';
print "$Defaults[$PageNo-1]";
print'"',"\n";
print "\t",'"description": "Type of page for page ',$PageNo,'",',"\n";
print "\t",'"list": [';
for ($p=0;$p<=$#Pages;$p++) {
print '"', $Pages[$p], '"' ;
if ($p < $#Pages){print ","}
}
print "]\n";
print "\t",'"category": "OBP60 Page ',$PageNo,'",',"\n";
print "\t",'"capabilities": {',"\n";
print "\t\t",'"obp60":"true"',"\n";
print "\t",'}',"\n";
print "\t",'"condition":[';
for ($vp=$PageNo;$vp<=$NoOfPages;$vp++){
print '"{visiblePages":"',$vp,'"},';
}
print "\b",']',"\n";
print '},',"\n";
for ($FieldNo=1; $FieldNo<=$MaxNoOfFieldsPerPage;$FieldNo++){
print "{\n";
print "\t",'"name": "page',$PageNo,'value',$FieldNo,'",',"\n";
print "\t",'"label": "Field ',$FieldNo,'",',"\n";
print "\t",'"type": "boatData",',"\n";
print "\t",'"default": "",',"\n";
print "\t",'"description": "The display for field ',$Numbers[$FieldNo-1],'",',"\n";
print "\t",'"category": "OBP60 Page ',$PageNo,'",',"\n";
print "\t",'"capabilities": {',"\n";
print "\t",' "obp60":"true"',"\n";
print "\t",'}, ',"\n";
print "\t",'"condition":[';
foreach $page (@Pages) {
if($NoOfFieldsPerPage{$page}>=$FieldNo){
print '{"page1type":"',$page,'"},';
}
}
print "\b],\n";
print '},',"\n";
}
print "{\n";
print "\t","\"name\": \"page", $PageNo,"fluid\",\n";
print "\t",'"label": "Fluid type",',"\n";
print "\t",'"type": "list",',"\n";
print "\t",'"default": "0",',"\n";
print "\t",'"list": [',"\n";
print "\t",'{"l":"Fuel (0)","v":"0"},',"\n";
print "\t",'{"l":"Water (1)","v":"1"},',"\n";
print "\t",'{"l":"Gray Water (2)","v":"2"},',"\n";
print "\t",'{"l":"Live Well (3)","v":"3"},',"\n";
print "\t",'{"l":"Oil (4)","v":"4"},',"\n";
print "\t",'{"l":"Black Water (5)","v":"5"},',"\n";
print "\t",'{"l":"Fuel Gasoline (6)","v":"6"}',"\n";
print "\t",'],',"\n";
print "\t",'"description": "Fluid type in tank",',"\n";
print "\t",'"category": "OBP60 Page ',$PageNo,'",',"\n";
print "\t",'"capabilities": {',"\n";
print "\t",'"obp60":"true"',"\n";
print "\t",'},',"\n";
print "\t",'"condition":[{"page',$PageNo,'type":"Fluid"}]',"\n";
print '},',"\n";
}

206
lib/obp60task/gen_set.py Executable file
View File

@@ -0,0 +1,206 @@
#!/usr/bin/env python3
"""
A tool to generate that part of config.json that deals with pages and fields.
Usage example:
1. Delete all lines from config.json from the curly backet before
"name": "page1type" to the end of the file
2. run ./gen_set.py -d obp60 -p 10 >> config.json
TODO Better handling of default pages
"""
import os
import sys
import getopt
import re
import json
__version__ = "0.3"
def detect_pages(filename):
# returns a dictionary with page name and the number of gui fields
pagefiles = []
with open(filename, 'r') as fh:
pattern = r'extern PageDescription\s*register(Page[^;\s]*)'
for line in fh:
if "extern PageDescription" in line:
match = re.search(pattern, line)
if match:
pagefiles.append(match.group(1))
try:
pagefiles.remove('PageSystem')
except ValueError:
pass
pagedata = {}
for pf in pagefiles:
filename = pf + ".cpp"
with open(filename, 'r') as fh:
content = fh.read()
pattern = r'PageDescription\s*?register' + pf + r'\s*\(\s*"([^"]+)".*?\n\s*(\d+)'
match = re.search(pattern, content, re.DOTALL)
if match:
pagedata[match.group(1)] = int(match.group(2))
return pagedata
def get_default_page(pageno):
# Default selection for each page
default_pages = (
"Voltage",
"WindRose",
"OneValue",
"TwoValues",
"ThreeValues",
"FourValues",
"FourValues2",
"Clock",
"RollPitch",
"Battery2"
)
if pageno > len(default_pages):
return "OneValue"
return default_pages[pageno - 1]
def number_to_text(number):
if number < 0 or number > 99:
raise ValueError("Only numbers from 0 to 99 are allowed.")
numbers = ("zero", "one", "two", "three", "four", "five", "six", "seven",
"eight", "nine", "ten", "eleven", "twelve", "thirteen", "fourteen",
"fifteen", "sixteen", "seventeen", "eighteen", "nineteen")
tens = ("", "", "twenty", "thirty", "forty", "fifty", "sixty", "seventy",
"eighty", "ninety")
if number < 20:
return numbers[number]
else:
q, r = divmod(number, 10)
return tens[q] + numbers[r]
def create_json(device, no_of_pages, pagedata):
pages = sorted(pagedata.keys())
max_no_of_fields_per_page = max(pagedata.values())
output = []
for page_no in range(1, no_of_pages + 1):
category = f"{device.upper()} Page {page_no}"
capabilities = {device.lower(): "true"}
visiblepages = [str(vp) for vp in range(page_no, no_of_pages + 1)]
page_data = {
"name": f"page{page_no}type",
"label": "Type",
"type": "list",
"default": get_default_page(page_no),
"description": f"Type of page for page {page_no}",
"list": pages,
"category": category,
"capabilities": {device.lower(): "true"},
"condition": {
"visiblePages": visiblepages
},
#"fields": [],
}
output.append(page_data)
for field_no in range(1, max_no_of_fields_per_page + 1):
field_data = {
"name": f"page{page_no}value{field_no}",
"label": f"Field {field_no}",
"type": "boatData",
"default": "",
"description": "The display for field {}".format(number_to_text(field_no)),
"category": category,
"capabilities": capabilities,
"condition": {
f"page{page_no}type": [ p for p in pages if pagedata[p] >= field_no ]
,"visiblePages": visiblepages
}
}
output.append(field_data)
fluid_data = {
"name": f"page{page_no}fluid",
"label": "Fluid type",
"type": "list",
"default": "0",
"list": [
{"l":"Fuel (0)","v":"0"},
{"l":"Water (1)","v":"1"},
{"l":"Gray Water (2)","v":"2"},
{"l":"Live Well (3)","v":"3"},
{"l":"Oil (4)","v":"4"},
{"l":"Black Water (5)","v":"5"},
{"l":"Fuel Gasoline (6)","v":"6"}
],
"description": "Fluid type in tank",
"category": category,
"capabilities": capabilities,
"condition": {
f"page{page_no}type": "Fluid",
"visiblePages": visiblepages
}
}
output.append(fluid_data)
if device.upper() == 'OBP40':
windsource = {
"name": f"page{page_no}wndsrc",
"label": "Wind source",
"type": "list",
"default": "True wind",
"description": f"Wind source for page {page_no}: [true|apparent]",
"list": [
"True wind",
"Apparant wind"
],
"category": category,
"capabilities": capabilities,
"condition": {
f"page{page_no}type": "WindPlot",
"visiblePages": visiblepages
}
}
output.append(windsource)
return json.dumps(output, indent=4)
def usage():
print("{} v{}".format(os.path.basename(__file__), __version__))
print()
print("Command line options")
print(" -d --device device name to use e.g. obp60")
print(" -p --pages number of pages to create")
print(" -h show this help")
print()
if __name__ == '__main__':
try:
options, remainder = getopt.getopt(sys.argv[1:], 'd:p:', ['device=','--pages='])
except getopt.GetoptError as err:
print(err)
usage()
sys.exit(2)
device = "obp60"
no_of_pages = 10
for opt, arg in options:
if opt in ('-d', '--device'):
device = arg
elif opt in ('-p', '--pages'):
no_of_pages = int(arg)
elif opt == '-h':
usage()
sys.exit(0)
# automatic detect pages and number of fields from sourcecode
pagedata = detect_pages("obp60task.cpp")
json_output = create_json(device, no_of_pages, pagedata)
# print omitting first line containing [ of JSON array
print(json_output[1:])

View File

@@ -1,2 +0,0 @@
cd /workspace/esp32-nmea2000-obp60
bash /workspace/esp32-nmea2000-obp60/lib/obp60task/run

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

Some files were not shown because too many files have changed in this diff Show More