1
0
mirror of https://github.com/thooge/esp32-nmea2000-obp60.git synced 2026-03-29 03:16:35 +02:00

201 Commits

Author SHA1 Message Date
6d052f8827 More work on page anchor 2026-03-08 19:52:51 +01:00
608b782b43 Merge branch 'anchor' of github.com:thooge/esp32-nmea2000-obp60 into anchor 2026-02-15 19:16:19 +01:00
4ab3749028 Merge branch 'master' into anchor 2026-02-15 19:02:58 +01:00
47d9d9f5ca Work on Page Anchor 2026-02-15 18:36:01 +01:00
Norbert Walter
43b0a780d5 Merge pull request #227 from thooge/master
Add feature to optionally apply patches to gateway code
2026-02-15 15:53:26 +01:00
0363ba4379 Add feature to optionally apply patches to gateway code 2026-02-15 13:13:19 +01:00
Norbert Walter
4b6e2abe33 Merge pull request #226 from Scorgan01/master
Small improvements to charts + pages OneValue + TwoValues
2026-02-15 12:21:32 +01:00
Norbert Walter
0401d82b62 Merge pull request #225 from thooge/master
Make code compile for OBP60 v2.0 again
2026-02-15 12:20:41 +01:00
0b02e5b54c More work on anchor page 2026-02-15 11:20:20 +01:00
48b9380724 Added page anchor with background map 2026-02-15 11:20:17 +01:00
Scorgan01
2fecbee492 Merge branch 'norbert-walter:master' into master 2026-02-11 22:19:11 +01:00
Ulrich Meine
fc5daaba37 - change control of key settings on PageOneValue + PageTwoValues
- added taskYIELD() to chart loop to be nice to other tasks
- fix typo in config_obp40.json
- fine tune chart labels
- disable debug messages
2026-02-09 22:31:07 +01:00
Norbert Walter
04dc09e44a Fix directory path for tool installation 2026-02-09 15:25:27 +01:00
1d2ba2f71d Make code compile for OBP60 v2.0 again 2026-02-08 18:02:50 +01:00
norbert-walter
bbecf5e55f Code ceaning 2026-02-07 17:36:14 +01:00
norbert-walter
ded1b2b22e Typo 2026-02-07 17:10:59 +01:00
norbert-walter
a0a88fa2c9 Modify reagatta timer funktion 2026-02-07 17:05:41 +01:00
Norbert Walter
e9bf54e99f Add sync button for Timer 2026-02-07 14:34:54 +00:00
norbert-walter
64950c3974 Merge branch 'newclock' 2026-02-06 23:07:26 +01:00
norbert-walter
fb2fbc85a4 Typo and formats for PageClock 2026-02-06 23:05:26 +01:00
norbert-walter
6b92a5e69c Add more functionality for time and date synchrinisation 2026-02-06 22:41:34 +01:00
norbert-walter
ef4546a2e6 Fix for PageClock 2026-02-06 15:20:43 +01:00
norbert-walter
6870c9b8a4 Modify button labels in PageClock 2026-02-06 12:52:14 +01:00
norbert-walter
a70d976a6e Change design for PageClock 2026-02-06 11:46:35 +01:00
norbert-walter
fbba6ffff2 Fix for PageClock 2026-02-05 23:52:10 +01:00
norbert-walter
d516c82041 New clock page with extended functions 2026-02-05 16:32:15 +01:00
norbert-walter
ccca784ac2 Delete old extra.script.py 2026-02-04 16:56:11 +01:00
norbert-walter
337214d650 Fix config problem for OBP60, missing setup values 2026-02-04 16:48:50 +01:00
Norbert Walter
744cf6469b Merge pull request #224 from thooge/master
Fix build for new gateway extra_script.py
2026-02-04 08:28:52 +01:00
6bc1b60f60 Fix build for new gateway extra_script.py 2026-02-04 08:18:39 +01:00
Norbert Walter
06dcd14bdc Merge branch 'wellenvogel:master' into master 2026-02-02 21:39:00 +01:00
norbert-walter
352009073e Setup to remote project 2026-02-02 21:38:14 +01:00
norbert-walter
aaa4007ae6 Merge remote-tracking branch 'origin/master' 2026-02-02 21:29:23 +01:00
norbert-walter
5b087e61d2 Merge branch 'autopilot' 2026-02-02 21:28:39 +01:00
norbert-walter
753e87068f New function for backlight - 3 brigjhtness steps 2026-02-02 21:26:43 +01:00
Norbert Walter
7445d2db24 Merge pull request #222 from Scorgan01/CalibrationData-v31
Data calibration: Move calibration of boat data to main loop to make calibrated data available everywhere
2026-02-02 18:15:23 +01:00
Norbert Walter
f517abf001 Merge pull request #220 from TobiasE-github/master
use 9.95 as the limit for formatting numbers below and above 10
2026-02-02 18:15:04 +01:00
norbert-walter
6a56a8fb56 Fix I2C adresses for INA219 2026-02-02 17:00:27 +01:00
norbert-walter
576f0a0d4f Fix for LED brightness and add Page Autopilot 2026-02-02 16:29:18 +01:00
norbert-walter
1de936fd47 Typo 2026-02-01 18:09:44 +01:00
TobiasE-github
05dad7a23c Merge branch 'norbert-walter:master' into master 2026-01-31 08:32:06 +01:00
norbert-walter
d19da640ae Add PageDigitalOut 2026-01-30 17:26:10 +01:00
norbert-walter
1da26a90ec Auto stash before merge of "master" and "origin/master" 2026-01-30 10:14:11 +01:00
Ulrich Meine
cb2b85d505 Data Calibration: Extend no. of calibration instances from 3 to 4 2026-01-17 13:45:41 +01:00
Ulrich Meine
cc1d07fac0 True Wind Calculation: change wind angle range to [0..360] temporarily for proper display on pages 2026-01-17 13:07:48 +01:00
Ulrich Meine
b8e64ff64c Update data calibration: calibration in main loop on boat data values:
- modified code to calibrate affected boat data each second in main obp60task loop;
- removed individual calibration call in all pages
- moved code from BoatDataCalibration to OBPDataOperations
- added DBS and HDT to list of supported data calibration types
- changed output format for wind angles from [-180..180] to [0..360], because negative value are not displayed properly on pages
2026-01-17 12:25:57 +01:00
TobiasE-github
e4214beefe general but flexible limits for formatting numbers 2026-01-17 11:02:25 +01:00
TobiasE-github
02c611ead0 use 9.95 as the limit for formatting numbers below and above 10 2026-01-15 17:56:52 +01:00
norbert-walter
0b79b7e2ef Modify PageDigitalOut 2026-01-15 15:08:42 +01:00
Norbert Walter
da975b5175 Merge pull request #217 from Scorgan01/WindPlot-v3
Graphical charts v3 + PageOneValue/PageTwoValues with chart option + updated PageWindPlot
2026-01-11 17:18:24 +01:00
Ulrich Meine
cd3c99d509 PageOneValue: fix unit position 2026-01-11 17:07:27 +01:00
Ulrich Meine
86a078690a PageTwoValues: added chart display option 2026-01-11 15:30:13 +01:00
Ulrich Meine
4747336a69 Code rework for OBPcharts, part 3 2026-01-10 12:31:37 +01:00
Ulrich Meine
84736e6769 OBP60Formatter: add option to switch of creation of simulation data and use pure conversion/formatting function 2026-01-10 01:50:19 +01:00
Scorgan01
1557126823 Merge branch 'norbert-walter:master' into WindPlot-v3 2026-01-07 19:32:58 +01:00
Norbert Walter
9ff5ae36db Merge pull request #216 from TobiasE-github/master
don't skip displayNew at startup (fixes issue 215)
2026-01-07 17:20:51 +01:00
Ulrich Meine
2d4f49659d Code rework for OBPcharts, part 2 2026-01-06 22:57:07 +01:00
Ulrich Meine
559042da78 Code rework for OBPcharts, part 1 2026-01-05 23:19:12 +01:00
TobiasE-github
2b6fc09b7e don't skip displayNew at startup (fixes issue 215) 2026-01-05 13:34:32 +01:00
Ulrich Meine
2e836bc750 added helper method for boat value conversion to OBP60Formatter;
fixed range calculation for temperature and other value formats;
fixed printing for names > len(3);
show "mode" only for supported data types
2026-01-01 22:52:33 +01:00
Norbert Walter
f838194f06 Merge pull request #213 from TobiasE-github/master
force a blank digit in front of two-digit numbers
2025-12-24 11:38:14 +01:00
Ulrich Meine
784cc15b8f adjusted simulation calc in OBPFormatter;
WindPlot + PageOneValue: aligned simulation data handling to standard; added "holdValues";
improved data check for chart buffer data; changed handling of tmpBVal -> always unique_ptr
2025-12-24 01:40:37 +01:00
Ulrich Meine
69754b85fd optimized chart initialization for PageWindPlot; added chart options to PageOneValue;
printing of current boat value on horizontal half charts is selectable;
fixed value axis direction for depth and other boat data; changed time axis labels to full numbers;
changed "INTV" button label to "ZOOM"
2025-12-23 18:16:53 +01:00
TobiasE-github
2deaf07ea4 force a blank digit in front of two-digit numbers 2025-12-23 15:46:48 +01:00
Ulrich Meine
362338a7dd Optimized PageWindPlot code;
added WindUtils AWD/TWD calculation from AWA/TWA if available
2025-12-21 13:10:44 +01:00
Ulrich Meine
41a8e7d078 General history buffers working; fine tuning required 2025-12-20 22:42:42 +01:00
Ulrich Meine
d655674529 Revert wind speed chart for horizontal charts; changed chart format parameters to <char>; adjusted chart size to OBP standard
Revert wind speed chart for horizontal charts; changed chart format parameters to <char>; adjusted chart size to OBP standard - commit from PageWindPlot-v2
2025-12-18 20:24:14 +01:00
norbert-walter
142f6ca774 Add PageDigitalOut 2025-12-14 22:42:30 +01:00
norbert-walter
c6276cdcff Add delay for connection lost warning 2025-12-13 22:11:57 +01:00
norbert-walter
213812ed14 Hold old map by connection lost 2025-12-13 21:28:52 +01:00
norbert-walter
b54acbae42 Backup actual firmware 2025-12-13 21:08:39 +01:00
Ulrich Meine
3ce1e31e64 Initial change to history buffers taking any boat value 2025-12-13 17:59:16 +01:00
norbert-walter
69367b91d7 Add showValues as config parameter 2025-12-12 13:09:06 +01:00
norbert-walter
6edf847958 Fix for HDM 2025-12-06 18:14:55 +01:00
norbert-walter
fe78fb434b Add HDM as fallback for HDT 2025-12-06 17:51:43 +01:00
norbert-walter
fc097b09fe Change SOG to HDT 2025-12-06 17:25:34 +01:00
Norbert Walter
a392d88445 Merge pull request #211 from Scorgan01/PageWindPlot-v2
Page wind plot v2 with separate generic <Chart> object and new OBPcharts library
2025-12-05 18:46:42 +01:00
norbert-walter
f08a119f40 Code cleaning 2025-12-05 18:41:21 +01:00
norbert-walter
ae2b7047f5 Add settings for PageNavigation (multi map) 2025-12-05 18:33:42 +01:00
norbert-walter
eab7d74aef More robust HTTP connection for data reading 2025-12-05 12:27:47 +01:00
Ulrich Meine
0f50b614eb Add lower chart line for horizontal half chart; write current value after chart lines 2025-12-05 00:10:31 +01:00
Ulrich Meine
1b55439135 Few more pixel adjustments for horizontal half screen charts 2025-12-04 23:31:20 +01:00
Ulrich Meine
625f9c087e Fixed OBP60Formatter issue with speeds of 9.9999 knots 2025-11-29 01:21:45 +01:00
Ulrich Meine
3fa7ca5e99 Optimized buffer change for T/A wind;
pixel and font size adjustments;
cleaned #includes
2025-11-28 23:47:39 +01:00
Ulrich Meine
9935cb54a6 Merge branch 'PageWindPlot-v2' of https://github.com/Scorgan01/esp32-nmea2000-obp60 into PageWindPlot-v2 2025-11-28 23:46:06 +01:00
Ulrich Meine
b31addf852 Fixed typo in config.json files 2025-11-28 23:45:44 +01:00
Scorgan01
a9007a6b6f Merge branch 'norbert-walter:master' into PageWindPlot-v2 2025-11-28 23:40:59 +01:00
norbert-walter
0972f12b9e Fix for better GPS accuracy 2025-11-26 19:33:14 +01:00
norbert-walter
f8378c3a2b Next working version 2025-11-26 18:57:58 +01:00
wellenvogel
dd3a4f5093 fix a bug in the actisense reader, version 20251126 2025-11-26 18:02:41 +01:00
norbert-walter
e02ca265ae First working version for PageNavigation, use PSRAM 2025-11-26 14:29:20 +01:00
norbert-walter
16f9f9217d HTTP request for PageNavigation 2025-11-25 22:38:08 +01:00
norbert-walter
f77107616d Add new PageNavigation (not complete) 2025-11-25 18:02:40 +01:00
Ulrich Meine
942ca28ab5 Clean PageWindPlot to adjust to new OBPcharts setup 2025-11-22 19:59:43 +01:00
Norbert Walter
a90689228d Merge pull request #209 from TobiasE-github/master
WindRoseFlex: less clutter, display A or T in the cetner
2025-11-22 19:58:39 +01:00
Ulrich Meine
489ee7ed09 Lots of fixes and enhancements for OBPcharts; ringbuffer now returns <double> values - internally still 2-byte storage; charts operate now with SI values; added flexible multiplier to history buffer; included data calibration for history data 2025-11-22 02:33:58 +01:00
Ulrich Meine
dd5f05922a Added <cvalue> to OBP60Formatter to return numerical converted value 2025-11-22 01:32:50 +01:00
TobiasE-github
469a81f87d WindRoseFlex: less clutter, display A or T in the cetner 2025-11-16 10:44:13 +01:00
5b477331de More work on anchor page 2025-11-05 20:02:36 +01:00
9b9bf76e4d Added page anchor with background map 2025-11-03 20:04:31 +01:00
Ulrich Meine
81825370c0 Merge branch 'PageWindPlot' of https://github.com/Scorgan01/esp32-nmea2000-obp60 into PageWindPlot 2025-10-17 00:42:27 +02:00
Ulrich Meine
bcc24ee99d OBPcharts principle working 2025-10-17 00:42:13 +02:00
wellenvogel
32099487fa prepare relase 20251007 2025-10-07 13:02:19 +02:00
wellenvogel
18b46ae5a0 prepare relase 20251007 2025-10-07 13:00:28 +02:00
wellenvogel
fb62e41bd9 prepare relase 20251007 2025-10-07 12:58:28 +02:00
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
wellenvogel
9211b13dcd #113: add env4 to cibuild 2025-09-30 20:09:13 +02:00
wellenvogel
6da87e4455 add buildname to ci output file names, correctly set initial build name 2025-09-30 20:03:45 +02:00
wellenvogel
5493c9695c add buildname to cibuild, use the firmware name for file names 2025-09-30 15:27:50 +02:00
wellenvogel
034a338a81 set default for serial enable low = 0 2025-09-30 12:16:51 +02:00
wellenvogel
3cd508a239 better description of pins for cibuild 2025-09-30 12:13:06 +02:00
wellenvogel
68239f6199 add fixed baud to cibuild, allow enable pin also for pure rx/tx serial 2025-09-29 19:34:35 +02:00
wellenvogel
b683413129 #117: add handling for an output enable pin for serial channels 2025-09-29 19:13:02 +02:00
wellenvogel
566d84d3e6 correctly handle ifdefs for SHT4X 2025-09-29 18:04:04 +02:00
wellenvogel
432a10bfb1 correctly send 130311 for QMP6988 2025-09-29 17:54:16 +02:00
wellenvogel
32862b9e29 avoid creating unmapped XDR entries for unset N2K values 2025-09-29 17:53:14 +02:00
wellenvogel
c21592599f re-add GwSHTXX* 2025-09-29 17:51:41 +02:00
wellenvogel
fddc3c742b allow to switch PGN 130311 for sensors on/off, simplify config.json for i2csensors 2025-09-29 17:05:48 +02:00
84e99365f7 Config file fixes and generation script update 2025-09-29 14:31:28 +02:00
wellenvogel
9831f8da85 add code for SHT4X, ENV4 2025-09-29 12:43:21 +02:00
wellenvogel
8bf8ada30e #111: allow to add extra scripts with custom_script 2025-09-29 09:35:14 +02:00
wellenvogel
6266f85db6 #116: sda and scl are swapped when building with online service 2025-09-28 19:58:54 +02:00
wellenvogel
70ad5cc903 omit external nmea2ktoais 2025-09-28 19:28:30 +02:00
wellenvogel
df9b377b31 move the nmea2ktoais functions back into our code base. 2025-09-28 18:47:17 +02:00
wellenvogel
d0966159c0 separate building AIS class 24 2025-09-28 18:37:26 +02:00
TobiasE-github
e5950f95fd disabe mode x in PageWind 2025-09-27 20:30:00 +02:00
wellenvogel
3f22164b1d use forked NMEA2000 lib 2025-09-26 20:00:30 +02:00
wellenvogel
60d06cd9ee remove wrong addEmptyField 2025-09-26 19:57:08 +02:00
wellenvogel
9633abc481 correct timestamp for pass format 2025-09-26 19:56:44 +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
wellenvogel
24502e423e set talker/channel when converting AIS from N2K, new lib version n2ktoais 2025-09-25 19:25:54 +02:00
wellenvogel
4a442c6dfb Merge branch 'master' into new-n2ktoais 2025-09-25 17:38:23 +02:00
wellenvogel
448af708d4 fill timestamp for actisense with frame timestamp in sendN2K 2025-09-25 17:37:59 +02:00
wellenvogel
78aafd308a seasmart for sendN2K 2025-09-24 20:30:44 +02:00
wellenvogel
b7cd8c6bdd add pass format to sendN2K 2025-09-24 18:29:22 +02:00
wellenvogel
e5968b8480 some error handling and stats to sendN2K 2025-09-24 18:10:06 +02:00
wellenvogel
e5c4f0b179 add actisense mode to sendN2K 2025-09-23 20:33:34 +02:00
wellenvogel
4b03fa5a23 add filter to sendN2K 2025-09-23 19:07:53 +02:00
wellenvogel
13eac9508d add some helper tools for converting candumps 2025-09-23 18:29:43 +02:00
wellenvogel
ec807c6925 intermediate: adapt handling to new n2ktoais lib 2025-09-23 13:05:28 +02:00
wellenvogel
3df2571ca2 intermediate: prepare AIS class 21 to 129041 2025-09-21 21:02:31 +02:00
wellenvogel
e578b428c9 more parameters for AIS type 21 2025-09-21 19:32:02 +02:00
wellenvogel
6e0d56316b add some testing tools 2025-09-21 16:40:24 +02:00
Tobias E
d94c4bbbdb optimize font size 2025-09-20 11:16:17 +00:00
wellenvogel
7fd1457296 add conversion from NMEA0183 AIS type 21 (aton) to PGN 129041 2025-09-19 20:43:28 +02:00
wellenvogel
e8c5440a79 #114: correctly distinguish between type 1..3 ais messages when converting from N2K 2025-09-19 12:13:41 +02:00
wellenvogel
95df5858ac #112: clearify licenses to be GPL v2 or later 2025-09-17 20:05:45 +02:00
wellenvogel
d5a9568b67 make birectional channels the default, clean up some channel handling 2025-09-17 17:57:03 +02:00
wellenvogel
3d131c7d98 correctly set the mode/type for serial channels 2025-09-17 17:40:28 +02:00
wellenvogel
7ebd582ca0 set serial mode to RX for M5 GPS base 2025-09-17 17:39:57 +02:00
wellenvogel
85f49857da Merge branch 'master' into pr_115 2025-09-17 15:46:33 +02:00
wellenvogel
976e8172e3 Merge branch 'master' of github.com:wellenvogel/esp32-nmea2000 2025-09-16 20:22:50 +02:00
wellenvogel
47fcb26961 add a generic s3 (devkit-m) to cibuild 2025-09-16 20:22:09 +02:00
TobiasE-github
6ef7681a40 use a smaller font on long names in WindRoseFlex 2025-09-14 21:00:02 +02:00
Ulrich Meine
16b8a0dacd Merge branch 'PageWindPlot' of https://github.com/Scorgan01/esp32-nmea2000-obp60 into PageWindPlot 2025-09-12 18:58:53 +02:00
Ulrich Meine
b3e2dea45b Code part for more chart plots 2025-09-12 18:42:49 +02:00
free-x
da6022cb28 #110: add GPS v1.1 unit 2025-09-11 20:23:52 +02:00
free-x
2c97eacd76 minor corrections 2025-09-11 19:36:09 +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
free-x
370fd47deb add GPS V2 Base to webinstall configs 2025-09-09 15:03:48 +02:00
free-x
37d945a0ea intermediate GPS 2.0 base 2025-09-09 13:07:16 +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
111 changed files with 8547 additions and 10133 deletions

1
.gitignore vendored
View File

@@ -6,4 +6,3 @@
generated/* generated/*
lib/generated lib/generated
webinstall/token.php webinstall/token.php
*~

View File

@@ -2,8 +2,6 @@
#define _GWAPI_H #define _GWAPI_H
#include "GwMessage.h" #include "GwMessage.h"
#include "N2kMsg.h" #include "N2kMsg.h"
#include "Nmea2kTwai.h"
#include "N2kDeviceList.h"
#include "NMEA0183Msg.h" #include "NMEA0183Msg.h"
#include "GWConfig.h" #include "GWConfig.h"
#include "GwBoatData.h" #include "GwBoatData.h"
@@ -25,7 +23,6 @@ class GwApi{
bool formatSet=false; bool formatSet=false;
public: public:
double value=0; double value=0;
String svalue="";
bool valid=false; bool valid=false;
int source=-1; int source=-1;
bool changed=false; //will be set by getBoatDataValues bool changed=false; //will be set by getBoatDataValues
@@ -225,8 +222,6 @@ class GwApi{
* accessing boat data must only be executed from within the main thread * accessing boat data must only be executed from within the main thread
* you need to use the request pattern as shown in GwExampleTask.cpp * you need to use the request pattern as shown in GwExampleTask.cpp
*/ */
virtual Nmea2kTwai *getNMEA2000()=0;
virtual tN2kDeviceList *getN2kDeviceList()=0;
virtual GwBoatData *getBoatData()=0; virtual GwBoatData *getBoatData()=0;
virtual ~GwApi(){} virtual ~GwApi(){}
}; };

View File

@@ -2,6 +2,11 @@
#include <GwJsonDocument.h> #include <GwJsonDocument.h>
#include <ArduinoJson/Json/TextFormatter.hpp> #include <ArduinoJson/Json/TextFormatter.hpp>
#include "GWConfig.h" #include "GWConfig.h"
#define GWTYPE_DOUBLE 1
#define GWTYPE_UINT32 2
#define GWTYPE_UINT16 3
#define GWTYPE_INT16 4
#define GWTYPE_USER 100
class GwBoatItemTypes class GwBoatItemTypes
{ {
@@ -10,9 +15,7 @@ public:
static int getType(const uint16_t &x) { return GWTYPE_UINT16; } static int getType(const uint16_t &x) { return GWTYPE_UINT16; }
static int getType(const int16_t &x) { return GWTYPE_INT16; } static int getType(const int16_t &x) { return GWTYPE_INT16; }
static int getType(const double &x) { return GWTYPE_DOUBLE; } static int getType(const double &x) { return GWTYPE_DOUBLE; }
static int getType(const String &x) { return GWTYPE_STRING; }
static int getType(const GwSatInfoList &x) { return GWTYPE_USER + 1; } static int getType(const GwSatInfoList &x) { return GWTYPE_USER + 1; }
static int getType(const GwAisTargetList &x) { return GWTYPE_USER + 1; }
}; };
bool GwBoatItemBase::isValid(unsigned long now) const bool GwBoatItemBase::isValid(unsigned long now) const
@@ -249,10 +252,6 @@ static void writeToString(GwTextWriter *writer, const int16_t &value)
{ {
writer->writeInteger(value); writer->writeInteger(value);
} }
static void writeToString(GwTextWriter *writer, String value)
{
writer->writeString(value.c_str());
}
static void writeToString(GwTextWriter *writer, GwSatInfoList &value) static void writeToString(GwTextWriter *writer, GwSatInfoList &value)
{ {
writer->writeInteger(value.getNumSats()); writer->writeInteger(value.getNumSats());
@@ -289,8 +288,6 @@ template class GwBoatItem<double>;
template class GwBoatItem<uint32_t>; template class GwBoatItem<uint32_t>;
template class GwBoatItem<uint16_t>; template class GwBoatItem<uint16_t>;
template class GwBoatItem<int16_t>; template class GwBoatItem<int16_t>;
template class GwBoatItem<String>;
void GwSatInfoList::houseKeeping(unsigned long ts) void GwSatInfoList::houseKeeping(unsigned long ts)
{ {
if (ts == 0) if (ts == 0)
@@ -304,7 +301,6 @@ void GwSatInfoList::houseKeeping(unsigned long ts)
}), }),
sats.end()); sats.end());
} }
void GwSatInfoList::update(GwSatInfo entry, unsigned long validTill) void GwSatInfoList::update(GwSatInfo entry, unsigned long validTill)
{ {
entry.validTill = validTill; entry.validTill = validTill;
@@ -347,63 +343,6 @@ void GwBoatDataSatList::toJsonDoc(GwJsonDocument *doc, unsigned long minTime)
GwBoatItem<GwSatInfoList>::toJsonDoc(doc, minTime); GwBoatItem<GwSatInfoList>::toJsonDoc(doc, minTime);
} }
void GwAisTargetList::houseKeeping(unsigned long ts)
{
if (ts == 0) {
ts = millis();
}
targets.erase(
std::remove_if(
targets.begin(),
targets.end(),
[ts, this](const GwAisTarget &target) {
return target.validTill < ts;
}
),
targets.end()
);
}
void GwAisTargetList::update(GwAisTarget target, unsigned long validTill)
{
target.validTill = validTill;
for (auto it = targets.begin(); it != targets.end(); it++) {
if (it->mmsi == target.mmsi) {
*it = target;
houseKeeping();
return;
}
}
houseKeeping();
targets.push_back(target);
}
GwBoatDataAisList::GwBoatDataAisList(String name, String formatInfo, GwBoatItemBase::TOType toType, GwBoatItemMap *map) : GwBoatItem<GwAisTargetList>(name, formatInfo, toType, map) {}
bool GwBoatDataAisList::update(GwAisTarget target, int source)
{
unsigned long now = millis();
if (isValid(now))
{
//priority handling
//sources with lower ids will win
//and we will not overwrite their value
if (lastUpdateSource < source)
{
return false;
}
}
lastUpdateSource = source;
uls(now);
data.update(target, now+invalidTime);
return true;
}
void GwBoatDataAisList::toJsonDoc(GwJsonDocument *doc, unsigned long minTime)
{
data.houseKeeping();
GwBoatItem<GwAisTargetList>::toJsonDoc(doc, minTime);
}
GwBoatData::GwBoatData(GwLog *logger, GwConfigHandler *cfg) GwBoatData::GwBoatData(GwLog *logger, GwConfigHandler *cfg)
{ {
this->logger = logger; this->logger = logger;
@@ -573,11 +512,6 @@ bool convertToJson(const GwSatInfoList &si, JsonVariant &variant)
return variant.set(si.getNumSats()); return variant.set(si.getNumSats());
} }
bool convertToJson(const GwAisTargetList &si, JsonVariant &variant)
{
return variant.set(si.getNumTargets());
}
#ifdef _UNDEF #ifdef _UNDEF
#include <ArduinoJson/Json/TextFormatter.hpp> #include <ArduinoJson/Json/TextFormatter.hpp>

View File

@@ -9,13 +9,6 @@
#define GW_BOAT_VALUE_LEN 32 #define GW_BOAT_VALUE_LEN 32
#define GWSC(name) static constexpr const char* name=#name #define GWSC(name) static constexpr const char* name=#name
#define GWTYPE_DOUBLE 1
#define GWTYPE_UINT32 2
#define GWTYPE_UINT16 3
#define GWTYPE_INT16 4
#define GWTYPE_STRING 5
#define GWTYPE_USER 100
//see https://github.com/wellenvogel/esp32-nmea2000/issues/44 //see https://github.com/wellenvogel/esp32-nmea2000/issues/44
//factor to convert from N2k/SI rad/s to current NMEA rad/min //factor to convert from N2k/SI rad/s to current NMEA rad/min
#define ROT_WA_FACTOR 60 #define ROT_WA_FACTOR 60
@@ -65,7 +58,6 @@ class GwBoatItemBase{
GWSC(formatRot); GWSC(formatRot);
GWSC(formatDate); GWSC(formatDate);
GWSC(formatTime); GWSC(formatTime);
GWSC(formatName);
protected: protected:
int type; int type;
unsigned long lastSet=0; unsigned long lastSet=0;
@@ -100,7 +92,6 @@ class GwBoatItemBase{
virtual int getLastSource(){return lastUpdateSource;} virtual int getLastSource(){return lastUpdateSource;}
virtual void refresh(unsigned long ts=0){uls(ts);} virtual void refresh(unsigned long ts=0){uls(ts);}
virtual double getDoubleValue()=0; virtual double getDoubleValue()=0;
virtual String getStringValue()=0;
String getName(){return name;} String getName(){return name;}
const String & getFormat() const{return format;} const String & getFormat() const{return format;}
virtual void setInvalidTime(GwConfigHandler *cfg); virtual void setInvalidTime(GwConfigHandler *cfg);
@@ -129,17 +120,7 @@ template<class T> class GwBoatItem : public GwBoatItemBase{
if (! isValid(millis())) return defaultv; if (! isValid(millis())) return defaultv;
return data; return data;
} }
virtual double getDoubleValue(){ virtual double getDoubleValue(){return (double)data;}
if constexpr (std::is_same<T, String>::value) {
return 0.0; // TODO any better ideas?
} else {
return (double)data;
}
}
virtual String getStringValue(){
return (String)data;
}
virtual void fillString(); virtual void fillString();
virtual void toJsonDoc(GwJsonDocument *doc, unsigned long minTime); virtual void toJsonDoc(GwJsonDocument *doc, unsigned long minTime);
virtual int getLastSource(){return lastUpdateSource;} virtual int getLastSource(){return lastUpdateSource;}
@@ -196,55 +177,6 @@ public:
}; };
class GwAisTarget {
public:
uint32_t mmsi;
char callsign[8];
char name[21];
uint8_t vesseltype;
double lat;
double lon;
float length;
float beam;
float sog;
float cog;
unsigned long validTill;
};
class GwAisTargetList {
public:
static const GwBoatItemBase::TOType toType=GwBoatItemBase::TOType::ais;
std::vector<GwAisTarget> targets;
void houseKeeping(unsigned long ts=0);
void update(GwAisTarget target, unsigned long validTill);
int getNumTargets() const {
return targets.size();
}
GwAisTarget *getAt(int idx){
if (idx >= 0 && idx < targets.size()) return &targets.at(idx);
return NULL;
}
operator double(){ return getNumTargets();}
};
class GwBoatDataAisList : public GwBoatItem<GwAisTargetList> {
public:
GwBoatDataAisList(String name, String formatInfo, GwBoatItemBase::TOType toType, GwBoatItemMap *map = NULL);
bool update(GwAisTarget target, int source);
virtual void toJsonDoc(GwJsonDocument *doc, unsigned long minTime);
GwAisTarget *getAt(int idx) {
if (! isValid()) return NULL;
return data.getAt(idx);
}
int getNumTargets(){
if (! isValid()) return 0;
return data.getNumTargets();
}
virtual double getDoubleValue(){
return (double)(data.getNumTargets());
}
};
class GwBoatItemNameProvider class GwBoatItemNameProvider
{ {
public: public:
@@ -303,9 +235,7 @@ class GwBoatData{
GWBOATDATA(double,XTE,formatXte) // cross track error GWBOATDATA(double,XTE,formatXte) // cross track error
GWBOATDATA(double,WPLat,formatLatitude) // waypoint latitude GWBOATDATA(double,WPLat,formatLatitude) // waypoint latitude
GWBOATDATA(double,WPLon,formatLongitude) // waypoint longitude GWBOATDATA(double,WPLon,formatLongitude) // waypoint longitude
GWBOATDATA(String,WPName,formatName) // waypoint name
GWSPECBOATDATA(GwBoatDataSatList,SatInfo,GwSatInfoList::toType,formatFixed0); GWSPECBOATDATA(GwBoatDataSatList,SatInfo,GwSatInfoList::toType,formatFixed0);
GWSPECBOATDATA(GwBoatDataAisList,AisTarget,GwAisTargetList::toType,formatFixed0);
public: public:
GwBoatData(GwLog *logger, GwConfigHandler *cfg); GwBoatData(GwLog *logger, GwConfigHandler *cfg);
~GwBoatData(); ~GwBoatData();

View File

@@ -79,7 +79,7 @@ GwUpdate::GwUpdate(GwLog *log, GwWebServer *webserver, PasswordChecker ckr)
} }
if (!param->hasError()) if (!param->hasError())
{ {
const AsyncWebParameter *hash=request->getParam("_hash"); AsyncWebParameter *hash=request->getParam("_hash");
if (! hash){ if (! hash){
hash=request->getParam("_hash",true); hash=request->getParam("_hash",true);
} }

View File

@@ -27,7 +27,7 @@ void sendEmbeddedFile(String name,String contentType,AsyncWebServerRequest *requ
std::map<String,EmbeddedFile*>::iterator it=embeddedFiles.find(name); std::map<String,EmbeddedFile*>::iterator it=embeddedFiles.find(name);
if (it != embeddedFiles.end()){ if (it != embeddedFiles.end()){
EmbeddedFile* found=it->second; EmbeddedFile* found=it->second;
AsyncWebServerResponse *response=request->beginResponse(200, contentType, found->start, found->len); AsyncWebServerResponse *response=request->beginResponse_P(200,contentType,found->start,found->len);
response->addHeader(F("Content-Encoding"), F("gzip")); response->addHeader(F("Content-Encoding"), F("gzip"));
request->send(response); request->send(response);
} }

View File

@@ -2,9 +2,6 @@
#define _GWWIFI_H #define _GWWIFI_H
#include <WiFi.h> #include <WiFi.h>
#include <GWConfig.h> #include <GWConfig.h>
#include <freertos/FreeRTOS.h>
#include <freertos/semphr.h>
class GwWifi{ class GwWifi{
private: private:
const GwConfigHandler *config; const GwConfigHandler *config;
@@ -19,19 +16,13 @@ class GwWifi{
bool apActive=false; bool apActive=false;
bool fixedApPass=true; bool fixedApPass=true;
bool clientIsConnected=false; bool clientIsConnected=false;
SemaphoreHandle_t wifiMutex=nullptr;
static const TickType_t WIFI_MUTEX_TIMEOUT=pdMS_TO_TICKS(1000);
bool acquireMutex();
void releaseMutex();
public: public:
const char *AP_password = "esp32nmea2k"; const char *AP_password = "esp32nmea2k";
GwWifi(const GwConfigHandler *config,GwLog *log, bool fixedApPass=true); GwWifi(const GwConfigHandler *config,GwLog *log, bool fixedApPass=true);
~GwWifi();
void setup(); void setup();
void loop(); void loop();
bool clientConnected(); bool clientConnected();
bool connectClient(); // Blocking version bool connectClient();
bool connectClientAsync(); // Non-blocking version for other tasks
String apIP(); String apIP();
bool isApActive(){return apActive;} bool isApActive(){return apActive;}
bool isClientActive(){return wifiClient->asBoolean();} bool isClientActive(){return wifiClient->asBoolean();}

View File

@@ -1,6 +1,7 @@
#include <esp_wifi.h> #include <esp_wifi.h>
#include "GWWifi.h" #include "GWWifi.h"
GwWifi::GwWifi(const GwConfigHandler *config,GwLog *log, bool fixedApPass){ GwWifi::GwWifi(const GwConfigHandler *config,GwLog *log, bool fixedApPass){
this->config=config; this->config=config;
this->logger=log; this->logger=log;
@@ -8,28 +9,6 @@ GwWifi::GwWifi(const GwConfigHandler *config,GwLog *log, bool fixedApPass){
wifiSSID=config->getConfigItem(config->wifiSSID,true); wifiSSID=config->getConfigItem(config->wifiSSID,true);
wifiPass=config->getConfigItem(config->wifiPass,true); wifiPass=config->getConfigItem(config->wifiPass,true);
this->fixedApPass=fixedApPass; this->fixedApPass=fixedApPass;
wifiMutex=xSemaphoreCreateMutex();
if (wifiMutex==nullptr){
LOG_DEBUG(GwLog::ERROR,"GwWifi: unable to create mutex");
}
}
GwWifi::~GwWifi(){
if (wifiMutex!=nullptr){
vSemaphoreDelete(wifiMutex);
wifiMutex=nullptr;
}
}
bool GwWifi::acquireMutex(){
if (wifiMutex==nullptr) return false;
return xSemaphoreTake(wifiMutex,WIFI_MUTEX_TIMEOUT)==pdTRUE;
}
void GwWifi::releaseMutex(){
if (wifiMutex!=nullptr){
xSemaphoreGive(wifiMutex);
}
} }
void GwWifi::setup(){ void GwWifi::setup(){
LOG_DEBUG(GwLog::LOG,"Wifi setup"); LOG_DEBUG(GwLog::LOG,"Wifi setup");
@@ -106,14 +85,8 @@ 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());
// CRITICAL SECTION: WiFi operations has to be serialized
if (!acquireMutex()){
LOG_DEBUG(GwLog::ERROR,"GwWifi: mutex timeout in connectInternal");
return false;
}
WiFi.setAutoReconnect(false); //#102 WiFi.setAutoReconnect(false); //#102
wl_status_t rt=WiFi.begin(wifiSSID->asCString(),wifiPass->asCString()); wl_status_t rt=WiFi.begin(wifiSSID->asCString(),wifiPass->asCString());
releaseMutex();
LOG_DEBUG(GwLog::LOG,"wifiClient connect returns %d",(int)rt); LOG_DEBUG(GwLog::LOG,"wifiClient connect returns %d",(int)rt);
lastConnectStart=millis(); lastConnectStart=millis();
return true; return true;
@@ -131,43 +104,9 @@ void GwWifi::loop(){
if (lastConnectStart > now || (lastConnectStart + RETRY_MILLIS) < now) if (lastConnectStart > now || (lastConnectStart + RETRY_MILLIS) < now)
{ {
LOG_DEBUG(GwLog::LOG,"wifiClient: retry connect to %s", wifiSSID->asCString()); LOG_DEBUG(GwLog::LOG,"wifiClient: retry connect to %s", wifiSSID->asCString());
WiFi.disconnect();
// Keep locked sections short to avoid cross-core stalls/WDT.
if (acquireMutex()){
WiFi.disconnect(true);
releaseMutex();
}
else{
LOG_DEBUG(GwLog::ERROR,"GwWifi: mutex timeout in loop (disconnect)");
}
delay(300);
if (acquireMutex()){
esp_err_t stopErr=esp_wifi_stop();
releaseMutex();
if (stopErr != ESP_OK){
LOG_DEBUG(GwLog::ERROR,"GwWifi: esp_wifi_stop failed: %d",(int)stopErr);
}
}
else{
LOG_DEBUG(GwLog::ERROR,"GwWifi: mutex timeout in loop (stop)");
}
delay(100);
if (acquireMutex()){
esp_err_t startErr=esp_wifi_start();
releaseMutex();
if (startErr != ESP_OK){
LOG_DEBUG(GwLog::ERROR,"GwWifi: esp_wifi_start failed: %d",(int)startErr);
}
connectInternal(); connectInternal();
} }
else{
LOG_DEBUG(GwLog::ERROR,"GwWifi: mutex timeout in loop (start)");
}
}
} }
else{ else{
if (! clientIsConnected){ if (! clientIsConnected){
@@ -187,42 +126,11 @@ void GwWifi::loop(){
} }
} }
} }
bool GwWifi::clientConnected(){ bool GwWifi::clientConnected(){
// CRITICAL SECTION: WiFi.status() has to be protected return WiFi.status() == WL_CONNECTED;
if (!acquireMutex()){
LOG_DEBUG(GwLog::ERROR,"GwWifi: mutex timeout in clientConnected");
return false; // conservative: assume not connected
}
bool result = WiFi.status() == WL_CONNECTED;
releaseMutex();
return result;
}; };
bool GwWifi::connectClient(){ bool GwWifi::connectClient(){
// CRITICAL SECTION: disconnect and connect has to be atomar
if (!acquireMutex()){
LOG_DEBUG(GwLog::ERROR,"GwWifi: mutex timeout in connectClient");
return false;
}
WiFi.disconnect(); WiFi.disconnect();
releaseMutex();
return connectInternal();
}
bool GwWifi::connectClientAsync(){
// Non-blocking version: Try to get Mutex but give up immediately
// Ideal for tasks which should not block
if (wifiMutex==nullptr){
LOG_DEBUG(GwLog::ERROR,"GwWifi: mutex not initialized in connectClientAsync");
return false;
}
if (xSemaphoreTake(wifiMutex, 0)!=pdTRUE){
LOG_DEBUG(GwLog::LOG,"GwWifi: connectClientAsync skipped - WiFi busy");
return false; // WiFiis busy, try again later
}
WiFi.disconnect();
xSemaphoreGive(wifiMutex);
return connectInternal(); return connectInternal();
} }

View File

@@ -355,7 +355,6 @@ private:
AppendN2kRouteWPInfo(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);
} }
boatData->WPName->update(String(rmb.destID), msg.sourceId);
} }
void convertRMC(const SNMEA0183Msg &msg) void convertRMC(const SNMEA0183Msg &msg)
{ {

View File

@@ -9,7 +9,6 @@ static const int TIMEOUT_OFFLINE=256; //# of timeouts to consider offline
Nmea2kTwai::Nmea2kTwai(gpio_num_t _TxPin, gpio_num_t _RxPin, unsigned long recP, unsigned long logP): Nmea2kTwai::Nmea2kTwai(gpio_num_t _TxPin, gpio_num_t _RxPin, unsigned long recP, unsigned long logP):
tNMEA2000(),RxPin(_RxPin),TxPin(_TxPin) tNMEA2000(),RxPin(_RxPin),TxPin(_TxPin)
{ {
pN2kDeviceList = new tN2kDeviceList(this);
if (RxPin < 0 || TxPin < 0){ if (RxPin < 0 || TxPin < 0){
disabled=true; disabled=true;
} }

View File

@@ -1,7 +1,6 @@
#ifndef _NMEA2KTWAI_H #ifndef _NMEA2KTWAI_H
#define _NMEA2KTWAI_H #define _NMEA2KTWAI_H
#include "NMEA2000.h" #include "NMEA2000.h"
#include "N2kDeviceList.h"
#include "GwTimer.h" #include "GwTimer.h"
class Nmea2kTwai : public tNMEA2000{ class Nmea2kTwai : public tNMEA2000{
@@ -27,7 +26,6 @@ class Nmea2kTwai : public tNMEA2000{
STATE state=ST_ERROR; STATE state=ST_ERROR;
} Status; } Status;
Status getStatus(); Status getStatus();
tN2kDeviceList *getDeviceList(){return pN2kDeviceList;}
unsigned long getLastRecoveryStart(){return lastRecoveryStart;} unsigned long getLastRecoveryStart(){return lastRecoveryStart;}
void loop(); void loop();
static const char * stateStr(const STATE &st); static const char * stateStr(const STATE &st);
@@ -60,7 +58,6 @@ class Nmea2kTwai : public tNMEA2000{
GwIntervalRunner timers; GwIntervalRunner timers;
bool disabled=false; bool disabled=false;
unsigned long lastRecoveryStart=0; unsigned long lastRecoveryStart=0;
tN2kDeviceList *pN2kDeviceList;
}; };
#endif #endif

View File

@@ -1,5 +1,16 @@
/* /*
Menu system for online configuration Menu system for online configuration
A menu consists of a list of menuitems.
Graphical representation is stored:
upper left corner: x, y
bounding box:
A menu consists of three columns
- menu text, if selected highlighted
- menu value with optional unit
- menu description or additional data for value
*/ */
#include "ConfigMenu.h" #include "ConfigMenu.h"
@@ -14,12 +25,14 @@ ConfigMenuItem::ConfigMenuItem(String itemtype, String itemlabel, uint16_t itemv
max = std::numeric_limits<uint16_t>::max(); max = std::numeric_limits<uint16_t>::max();
value = itemval; value = itemval;
unit = itemunit; unit = itemunit;
step = 1;
} }
void ConfigMenuItem::setRange(uint16_t valmin, uint16_t valmax, std::vector<uint16_t> valsteps) { void ConfigMenuItem::setRange(uint16_t valmin, uint16_t valmax, std::vector<uint16_t> valsteps) {
min = valmin; min = valmin;
max = valmax; max = valmax;
steps = valsteps; steps = valsteps;
step = steps[0];
}; };
bool ConfigMenuItem::checkRange(uint16_t checkval) { bool ConfigMenuItem::checkRange(uint16_t checkval) {

View File

@@ -10,7 +10,8 @@ private:
String label; String label;
uint16_t value; uint16_t value;
String unit; String unit;
String valtype; // "int" | "bool" String desc; // optional data to display
String valtype; // "int" | "bool" -> TODO "list"
uint16_t min; uint16_t min;
uint16_t max; uint16_t max;
std::vector<uint16_t> steps; std::vector<uint16_t> steps;

View File

@@ -0,0 +1,6 @@
Craete new page for OBP60
1. Create page under /lib/obp60task/PageXXXX.cpp
2. Set page name in PageXXXX.cpp on file name
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.py and run it to auto-generate the relevant section of config.json

View File

@@ -2,22 +2,13 @@
#include <mbedtls/base64.h> #include <mbedtls/base64.h>
// Decoder for Base64 content // Decoder for Base64 content
bool ImageDecoder::decodeBase64(const char* base64, size_t base64Len, uint8_t* outBuffer, size_t outSize, size_t& decodedSize) { bool ImageDecoder::decodeBase64(const String& base64, uint8_t* outBuffer, size_t outSize, size_t& decodedSize) {
if (base64 == nullptr) {
decodedSize = 0;
return false;
}
int ret = mbedtls_base64_decode( int ret = mbedtls_base64_decode(
outBuffer, outBuffer,
outSize, outSize,
&decodedSize, &decodedSize,
(const unsigned char*)base64, (const unsigned char*)base64.c_str(),
base64Len base64.length()
); );
return (ret == 0); return (ret == 0);
} }
// Decoder for Base64 content
bool ImageDecoder::decodeBase64(const String& base64, uint8_t* outBuffer, size_t outSize, size_t& decodedSize) {
return decodeBase64(base64.c_str(), base64.length(), outBuffer, outSize, decodedSize);
}

View File

@@ -5,6 +5,5 @@
class ImageDecoder { class ImageDecoder {
public: public:
bool decodeBase64(const char* base64, size_t base64Len, uint8_t* outBuffer, size_t outSize, size_t& decodedSize);
bool decodeBase64(const String& base64, uint8_t* outBuffer, size_t outSize, size_t& decodedSize); bool decodeBase64(const String& base64, uint8_t* outBuffer, size_t outSize, size_t& decodedSize);
}; };

View File

@@ -1,4 +1,3 @@
// SPDX-License-Identifier: GPL-2.0-or-later
#include <FreeRTOS.h> #include <FreeRTOS.h>
#include "LedSpiTask.h" #include "LedSpiTask.h"
#include "GwHardware.h" #include "GwHardware.h"
@@ -15,30 +14,6 @@ https://controllerstech.com/ws2812-leds-using-spi/
*/ */
String Color::toHex() {
char hexColor[8];
sprintf(hexColor, "#%02X%02X%02X", r, g, b);
return String(hexColor);
}
String Color::toName() {
static std::map<int, String> const names = {
{0xff0000, "Red"},
{0x00ff00, "Green"},
{0x0000ff, "Blue",},
{0xff9900, "Orange"},
{0xffff00, "Yellow"},
{0x3366ff, "Aqua"},
{0xff0066, "Violet"},
{0xffffff, "White"}
};
int color = (r << 16) + (g << 8) + b;
auto it = names.find(color);
if (it == names.end()) {
return toHex();
}
return it->second;
}
static uint8_t mulcolor(uint8_t f1, uint8_t f2){ static uint8_t mulcolor(uint8_t f1, uint8_t f2){
uint16_t rt=f1; uint16_t rt=f1;
@@ -51,7 +26,7 @@ Color setBrightness(const Color &color,uint8_t brightness){
uint16_t br255=brightness*255; uint16_t br255=brightness*255;
br255=br255/100; br255=br255/100;
//very simple for now //Very simple for now
Color rt=color; Color rt=color;
rt.g=mulcolor(rt.g,br255); rt.g=mulcolor(rt.g,br255);
rt.b=mulcolor(rt.b,br255); rt.b=mulcolor(rt.b,br255);
@@ -86,12 +61,12 @@ static size_t ledsToBuffer(int numLeds,const Color *leds,uint8_t *buffer){
bool prepareGpio(GwLog *logger, uint8_t pin){ bool prepareGpio(GwLog *logger, uint8_t pin){
esp_err_t err=gpio_set_direction((gpio_num_t)pin,GPIO_MODE_OUTPUT); esp_err_t err=gpio_set_direction((gpio_num_t)pin,GPIO_MODE_OUTPUT);
if (err != ESP_OK){ if (err != ESP_OK){
logger->logDebug(GwLog::ERROR, "unable to set gpio mode for %d: %d", pin, (int)err); LOG_DEBUG(GwLog::ERROR,"unable to set gpio mode for %d: %d",pin,(int)err);
return false; return false;
} }
err=gpio_set_level((gpio_num_t)pin,0); err=gpio_set_level((gpio_num_t)pin,0);
if (err != ESP_OK){ if (err != ESP_OK){
logger->logDebug(GwLog::ERROR, "unable to set gpio level for %d: %d", pin, (int)err); LOG_DEBUG(GwLog::ERROR,"unable to set gpio level for %d: %d",pin,(int)err);
return false; return false;
} }
return true; return true;
@@ -117,7 +92,7 @@ bool prepareSpi(GwLog *logger,spi_host_device_t bus,spi_device_handle_t *device)
}; };
esp_err_t err=spi_bus_initialize(bus,&buscfg,SPI_DMA_CH_AUTO); esp_err_t err=spi_bus_initialize(bus,&buscfg,SPI_DMA_CH_AUTO);
if (err != ESP_OK){ if (err != ESP_OK){
logger->logDebug(GwLog::ERROR, "unable to initialize SPI bus %d,mosi=%d, error=%d", LOG_DEBUG(GwLog::ERROR,"unable to initialize SPI bus %d,mosi=%d, error=%d",
(int)bus,-1,(int)err); (int)bus,-1,(int)err);
return false; return false;
} }
@@ -136,7 +111,7 @@ bool prepareSpi(GwLog *logger,spi_host_device_t bus,spi_device_handle_t *device)
}; };
err=spi_bus_add_device(bus,&devcfg,device); err=spi_bus_add_device(bus,&devcfg,device);
if (err != ESP_OK){ if (err != ESP_OK){
logger->logDebug(GwLog::ERROR, "unable to add device to SPI bus %d,mosi=%d, error=%d", LOG_DEBUG(GwLog::ERROR,"unable to add device to SPI bus %d,mosi=%d, error=%d",
(int)bus,-1,(int)err); (int)bus,-1,(int)err);
return false; return false;
} }
@@ -144,7 +119,7 @@ bool prepareSpi(GwLog *logger,spi_host_device_t bus,spi_device_handle_t *device)
//as we are the only ones using the bus we can safely acquire it forever //as we are the only ones using the bus we can safely acquire it forever
err=spi_device_acquire_bus(*device,portMAX_DELAY); err=spi_device_acquire_bus(*device,portMAX_DELAY);
if (err != ESP_OK){ if (err != ESP_OK){
logger->logDebug(GwLog::ERROR,"unable to acquire SPI bus %d,mosi=%d, error=%d", LOG_DEBUG(GwLog::ERROR,"unable to acquire SPI bus %d,mosi=%d, error=%d",
(int)bus,-1,(int)err); (int)bus,-1,(int)err);
return false; return false;
} }
@@ -175,7 +150,7 @@ bool sendToLeds(GwLog *logger, uint8_t pin, int numLeds, Color *leds, spi_host_d
buffer = (uint8_t *)heap_caps_malloc(bufferSize, MALLOC_CAP_DMA|MALLOC_CAP_32BIT); buffer = (uint8_t *)heap_caps_malloc(bufferSize, MALLOC_CAP_DMA|MALLOC_CAP_32BIT);
if (!buffer) if (!buffer)
{ {
logger->logDebug(GwLog::ERROR, "unable to allocate %d bytes of DMA buffer", (int)bufferSize); LOG_DEBUG(GwLog::ERROR, "unable to allocate %d bytes of DMA buffer", (int)bufferSize);
return false; return false;
} }
} }
@@ -196,12 +171,12 @@ bool sendToLeds(GwLog *logger, uint8_t pin, int numLeds, Color *leds, spi_host_d
int64_t end = esp_timer_get_time(); int64_t end = esp_timer_get_time();
if (ret != ESP_OK) if (ret != ESP_OK)
{ {
logger->logDebug(GwLog::ERROR, "unable to send led data: %d", (int)ret); LOG_DEBUG(GwLog::ERROR, "unable to send led data: %d", (int)ret);
rv = false; rv = false;
} }
else else
{ {
logger->logDebug(GwLog::DEBUG, "successfully send led data for %d leds, %lld us", numLeds, end - now); LOG_DEBUG(GwLog::DEBUG, "successfully send led data for %d leds, %lld us", numLeds, end - now);
} }
if (ownsBuffer) if (ownsBuffer)
{ {
@@ -214,7 +189,7 @@ bool sendToLeds(GwLog *logger, uint8_t pin, int numLeds, Color *leds, spi_host_d
void handleSpiLeds(void *param){ void handleSpiLeds(void *param){
LedTaskData *taskData=(LedTaskData*)param; LedTaskData *taskData=(LedTaskData*)param;
GwLog *logger=taskData->api->getLogger(); GwLog *logger=taskData->api->getLogger();
logger->logDebug(GwLog::ERROR, "spi led task initialized"); LOG_DEBUG(GwLog::ERROR,"spi led task initialized");
spi_host_device_t bus=SPI3_HOST; spi_host_device_t bus=SPI3_HOST;
bool spiValid=false; bool spiValid=false;
LOG_DEBUG(GwLog::ERROR,"SpiLed task started"); LOG_DEBUG(GwLog::ERROR,"SpiLed task started");
@@ -236,14 +211,14 @@ void handleSpiLeds(void *param){
LedInterface newLeds=taskData->getLedData(); LedInterface newLeds=taskData->getLedData();
if (first || current.backlightChanged(newLeds) || current.flasChanged(newLeds)){ if (first || current.backlightChanged(newLeds) || current.flasChanged(newLeds)){
first=false; first=false;
logger->logDebug(GwLog::ERROR, "handle SPI leds"); LOG_DEBUG(GwLog::ERROR,"handle SPI leds");
if (current.backlightChanged(newLeds) || first){ if (current.backlightChanged(newLeds) || first){
logger->logDebug(GwLog::ERROR, "setting backlight r=%02d,g=%02d,b=%02d", LOG_DEBUG(GwLog::ERROR,"setting backlight r=%02d,g=%02d,b=%02d",
newLeds.backlight[0].r,newLeds.backlight[0].g,newLeds.backlight[0].b); newLeds.backlight[0].r,newLeds.backlight[0].g,newLeds.backlight[0].b);
sendToLeds(logger,OBP_BACKLIGHT_LED,newLeds.backlightLen(),newLeds.backlight,bus,device); sendToLeds(logger,OBP_BACKLIGHT_LED,newLeds.backlightLen(),newLeds.backlight,bus,device);
} }
if (current.flasChanged(newLeds) || first){ if (current.flasChanged(newLeds) || first){
logger->logDebug(GwLog::ERROR, "setting flashr=%02d,g=%02d,b=%02d", LOG_DEBUG(GwLog::ERROR,"setting flashr=%02d,g=%02d,b=%02d",
newLeds.flash[0].r,newLeds.flash[0].g,newLeds.flash[0].b); newLeds.flash[0].r,newLeds.flash[0].g,newLeds.flash[0].b);
sendToLeds(logger,OBP_FLASH_LED,newLeds.flashLen(),newLeds.flash,bus,device); sendToLeds(logger,OBP_FLASH_LED,newLeds.flashLen(),newLeds.flash,bus,device);
} }
@@ -255,10 +230,5 @@ void handleSpiLeds(void *param){
} }
void createSpiLedTask(LedTaskData *param){ void createSpiLedTask(LedTaskData *param){
TaskHandle_t xHandle = NULL; xTaskCreate(handleSpiLeds,"handleLeds",4000,param,3,NULL);
GwLog *logger = param->api->getLogger();
esp_err_t err = xTaskCreate(handleSpiLeds, "handleLeds", configMINIMAL_STACK_SIZE + 2048, param, 3, &xHandle);
if (err != pdPASS) {
logger->logDebug(GwLog::ERROR, "Failed to create spiled task! (err=%d)", err);
};
} }

View File

@@ -22,8 +22,6 @@ class Color{
bool operator != (const Color &other) const{ bool operator != (const Color &other) const{
return ! equal(other); return ! equal(other);
} }
String toHex();
String toName();
}; };
static Color COLOR_GREEN=Color(0,255,0); static Color COLOR_GREEN=Color(0,255,0);

View File

@@ -0,0 +1,939 @@
const unsigned char gImage_Logo_OBP_400x300_sw[15000] = { /* 0X00,0X01,0X90,0X01,0X2C,0X01, */
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X01,
0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XF8,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X40,
0X00,0X03,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,
0XFF,0XF8,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X80,0X00,0X03,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,
0XFF,0XFF,0XFF,0XF8,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X01,0X80,0X00,0X03,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,
0XFF,0XFF,0XFF,0XFF,0XFF,0XF8,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X01,0X80,0X00,0X03,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,
0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XF8,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X03,0X80,0X00,0X03,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,
0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XF8,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X07,0X80,0X00,0X03,0XFF,0XFF,0XFF,0XFF,
0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XF8,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X07,0X80,0X00,0X03,0XFF,0XFF,
0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XF8,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X0F,0X80,0X00,0X03,
0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XF8,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X1F,0X80,
0X00,0X03,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,
0XFF,0XF8,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X3F,0X80,0X00,0X03,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,
0XFF,0XFF,0XFF,0XF8,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X3F,0X80,0X00,0X03,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,
0XFF,0XFF,0XFF,0XFF,0XFF,0XF8,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X7F,0X80,0X00,0X03,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,
0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XF8,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0XFF,0X80,0X00,0X03,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,
0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XF8,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0XFF,0X80,0X00,0X03,0XFF,0XFF,0XFF,0XFF,
0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XF8,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X01,0XFF,0X80,0X00,0X03,0XFF,0XFF,
0XFF,0XFF,0X00,0X03,0XFF,0XFF,0XF8,0X00,0X00,0X0F,0XFF,0XFF,0XFF,0XF8,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X03,0XFF,0X80,0X00,0X03,
0XFF,0XFF,0XFF,0XF8,0X00,0X00,0X7F,0XFF,0XF8,0X00,0X00,0X00,0X3F,0XFF,0XFF,0XF8,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X03,0XFF,0X80,
0X00,0X03,0XFF,0XFF,0XFF,0XE0,0X00,0X00,0X1F,0XFF,0XF8,0X00,0X00,0X00,0X07,0XFF,
0XFF,0XF8,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X07,
0XFF,0X80,0X00,0X03,0XFF,0XFF,0XFF,0X80,0X00,0X00,0X07,0XFF,0XF8,0X00,0X00,0X00,
0X03,0XFF,0XFF,0XF8,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X0F,0XFF,0X80,0X00,0X03,0XFF,0XFF,0XFF,0X00,0X00,0X00,0X03,0XFF,0XF8,0X00,
0X00,0X00,0X01,0XFF,0XFF,0XF8,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X1F,0XFF,0X80,0X00,0X03,0XFF,0XFF,0XFE,0X00,0X00,0X00,0X00,0XFF,
0XF8,0X00,0X00,0X00,0X00,0XFF,0XFF,0XF8,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X1F,0XFF,0X80,0X00,0X03,0XFF,0XFF,0XFC,0X00,0X00,0X00,
0X00,0X7F,0XF8,0X00,0X00,0X00,0X00,0X7F,0XFF,0XF8,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X3F,0XFF,0X80,0X00,0X03,0XFF,0XFF,0XF8,0X00,
0X00,0X00,0X00,0X7F,0XF8,0X00,0X00,0X00,0X00,0X7F,0XFF,0XF8,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X7F,0XFF,0X80,0X00,0X03,0XFF,0XFF,
0XF0,0X00,0X00,0X00,0X00,0X3F,0XF8,0X00,0X00,0X00,0X00,0X3F,0XFF,0XF8,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X7F,0XFF,0X80,0X00,0X03,
0XFF,0XFF,0XE0,0X00,0X00,0X00,0X00,0X1F,0XF8,0X00,0X1E,0X00,0X00,0X3F,0XFF,0XF8,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0XFF,0XFF,0X80,
0X00,0X03,0XFF,0XFF,0XE0,0X00,0X0F,0XC0,0X00,0X1F,0XF8,0X00,0X1F,0XE0,0X00,0X3F,
0XFF,0XF8,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X01,0XFF,
0XFF,0X80,0X00,0X03,0XFF,0XFF,0XC0,0X00,0X3F,0XF0,0X00,0X0F,0XF8,0X00,0X1F,0XF0,
0X00,0X3F,0XFF,0XF8,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X01,0XFF,0XFF,0X80,0X00,0X03,0XFF,0XFF,0XC0,0X00,0X7F,0XF8,0X00,0X0F,0XF8,0X00,
0X1F,0XF8,0X00,0X3F,0XFF,0XF8,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X03,0XFF,0XFF,0X80,0X00,0X03,0XFF,0XFF,0XC0,0X00,0XFF,0XFC,0X00,0X07,
0XF8,0X00,0X1F,0XF8,0X00,0X3F,0XFF,0XF8,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X07,0XFF,0XFF,0X80,0X00,0X03,0XFF,0XFF,0X80,0X01,0XFF,0XFE,
0X00,0X07,0XF8,0X00,0X1F,0XF8,0X00,0X3F,0XFF,0XF8,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X0F,0XFF,0XFF,0X80,0X00,0X03,0XFF,0XFF,0X80,0X01,
0XFF,0XFE,0X00,0X07,0XF8,0X00,0X1F,0XF8,0X00,0X3F,0XFF,0XF8,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X0F,0XFF,0XFF,0X80,0X00,0X03,0XFF,0XFF,
0X80,0X01,0XFF,0XFF,0X00,0X07,0XF8,0X00,0X1F,0XF8,0X00,0X3F,0XFF,0XF8,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X1F,0XFF,0XFF,0X80,0X00,0X03,
0XFF,0XFF,0X80,0X03,0XFF,0XFF,0X00,0X03,0XF8,0X00,0X1F,0XF0,0X00,0X3F,0XFF,0XF8,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X3F,0XFF,0XFF,0X80,
0X00,0X03,0XFF,0XFF,0X80,0X03,0XFF,0XFF,0X00,0X03,0XF8,0X00,0X1F,0XE0,0X00,0X3F,
0XFF,0XF8,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X3F,0XFF,
0XFF,0X80,0X00,0X03,0XFF,0XFF,0X00,0X03,0XFF,0XFF,0X00,0X03,0XF8,0X00,0X00,0X00,
0X00,0X3F,0XFF,0XF8,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X7F,0XFF,0XFF,0X80,0X00,0X03,0XFF,0XFF,0X00,0X03,0XFF,0XFF,0X00,0X03,0XF8,0X00,
0X00,0X00,0X00,0X7F,0XFF,0XF8,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0XFF,0XFF,0XFF,0X80,0X00,0X03,0XFF,0XFF,0X00,0X03,0XFF,0XFF,0X00,0X03,
0XF8,0X00,0X00,0X00,0X00,0X7F,0XFF,0XF8,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0XFF,0XFF,0XFF,0X80,0X00,0X03,0XFF,0XFF,0X00,0X03,0XFF,0XFF,
0X00,0X03,0XF8,0X00,0X00,0X00,0X00,0XFF,0XFF,0XF8,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X01,0XFF,0XFF,0XFF,0X80,0X00,0X03,0XFF,0XFF,0X00,0X03,
0XFF,0XFF,0X00,0X03,0XF8,0X00,0X00,0X00,0X01,0XFF,0XFF,0XF8,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X03,0XFF,0XFF,0XFF,0X80,0X00,0X03,0XFF,0XFF,
0X00,0X03,0XFF,0XFF,0X00,0X03,0XF8,0X00,0X00,0X00,0X03,0XFF,0XFF,0XF8,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X03,0XFF,0XFF,0XFF,0X80,0X00,0X03,
0XFF,0XFF,0X00,0X03,0XFF,0XFF,0X00,0X03,0XF8,0X00,0X00,0X00,0X07,0XFF,0XFF,0XF8,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X07,0XFF,0XFF,0XFF,0X80,
0X00,0X03,0XFF,0XFF,0X00,0X03,0XFF,0XFF,0X00,0X03,0XF8,0X00,0X00,0X00,0X0F,0XFF,
0XFF,0XF8,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X0F,0XFF,0XFF,
0XFF,0X80,0X00,0X03,0XFF,0XFF,0X80,0X03,0XFF,0XFF,0X00,0X03,0XF8,0X00,0X00,0X00,
0X7F,0XFF,0XFF,0XF8,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X1F,
0XFF,0XFF,0XFF,0X80,0X00,0X03,0XFF,0XFF,0X80,0X03,0XFF,0XFF,0X00,0X07,0XF8,0X00,
0X1F,0XFF,0XFF,0XFF,0XFF,0XF8,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X1F,0XFF,0XFF,0XFF,0X80,0X00,0X03,0XFF,0XFF,0X80,0X01,0XFF,0XFE,0X00,0X07,
0XF8,0X00,0X1F,0XFF,0XFF,0XFF,0XFF,0XF8,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X3F,0XFF,0XFF,0XFF,0X80,0X00,0X03,0XFF,0XFF,0X80,0X01,0XFF,0XFE,
0X00,0X07,0XF8,0X00,0X1F,0XFF,0XFF,0XFF,0XFF,0XF8,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X7F,0XFF,0XFF,0XFF,0X80,0X00,0X03,0XFF,0XFF,0X80,0X00,
0XFF,0XFC,0X00,0X07,0XF8,0X00,0X1F,0XFF,0XFF,0XFF,0XFF,0XF8,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X7F,0XFF,0XFF,0XFF,0X80,0X00,0X03,0XFF,0XFF,
0XC0,0X00,0XFF,0XFC,0X00,0X0F,0XF8,0X00,0X1F,0XFF,0XFF,0XFF,0XFF,0XF8,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0XFF,0XFF,0XFF,0XFF,0X80,0X00,0X03,
0XFF,0XFF,0XC0,0X00,0X3F,0XF8,0X00,0X0F,0XF8,0X00,0X1F,0XFF,0XFF,0XFF,0XFF,0XF8,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X01,0XFF,0XFF,0XFF,0XFF,0X80,
0X00,0X03,0XFF,0XFF,0XE0,0X00,0X1F,0XE0,0X00,0X1F,0XF8,0X00,0X1F,0XFF,0XFF,0XFF,
0XFF,0XF8,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X03,0XFF,0XFF,0XFF,
0XFF,0X80,0X00,0X03,0XFF,0XFF,0XE0,0X00,0X00,0X00,0X00,0X1F,0XF8,0X00,0X1F,0XFF,
0XFF,0XFF,0XFF,0XF8,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X03,0XFF,
0XFF,0XFF,0XFF,0X80,0X00,0X03,0XFF,0XFF,0XF0,0X00,0X00,0X00,0X00,0X3F,0XF8,0X00,
0X1F,0XFF,0XFF,0XFF,0XFF,0XF8,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X07,0XFF,0XFF,0XFF,0XFF,0X80,0X00,0X03,0XFF,0XFF,0XF8,0X00,0X00,0X00,0X00,0X3F,
0XF8,0X00,0X1F,0XFF,0XFF,0XFF,0XFF,0XF8,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X0F,0XFF,0XFF,0XFF,0XFF,0X80,0X00,0X03,0XFF,0XFF,0XF8,0X00,0X00,0X00,
0X00,0X7F,0XF8,0X00,0X1F,0XFF,0XFF,0XFF,0XFF,0XF8,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X0F,0XFF,0XFF,0XFF,0XFF,0X80,0X00,0X03,0XFF,0XFF,0XFC,0X00,
0X00,0X00,0X00,0XFF,0XF8,0X00,0X1F,0XFF,0XFF,0XFF,0XFF,0XF8,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X1F,0XFF,0XFF,0XFF,0XFF,0X80,0X00,0X03,0XFF,0XFF,
0XFE,0X00,0X00,0X00,0X01,0XFF,0XF8,0X00,0X1F,0XFF,0XFF,0XFF,0XFF,0XF8,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X3F,0XFF,0XFF,0XFF,0XFF,0X80,0X00,0X03,
0XFF,0XFF,0XFF,0X80,0X00,0X00,0X03,0XFF,0XF8,0X00,0X1F,0XFF,0XFF,0XFF,0XFF,0XF8,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X3F,0XFF,0XFF,0XFF,0XFF,0X80,
0X00,0X03,0XFF,0XFF,0XFF,0XC0,0X00,0X00,0X0F,0XFF,0XF8,0X00,0X1F,0XFF,0XFF,0XFF,
0XFF,0XF8,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X7F,0XFF,0XFF,0XFF,
0XFF,0X80,0X00,0X03,0XFF,0XFF,0XFF,0XF0,0X00,0X00,0X3F,0XFF,0XF8,0X00,0X1F,0XFF,
0XFF,0XFF,0XFF,0XF8,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0XFF,0XFF,
0XFF,0XFF,0XFF,0X80,0X00,0X03,0XFF,0XFF,0XFF,0XFE,0X00,0X01,0XFF,0XFF,0XF8,0X00,
0X3F,0XFF,0XFF,0XFF,0XFF,0XF8,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X01,
0XFF,0XFF,0XFF,0XFF,0XFF,0X80,0X00,0X03,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,
0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XF8,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X01,0XFF,0XFF,0XFF,0XFF,0XFF,0X80,0X00,0X03,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,
0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XF8,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X03,0XFF,0XFF,0XFF,0XFF,0XFF,0X80,0X00,0X03,0XFF,0XFF,0XFF,0XFF,
0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XF8,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X07,0XFF,0XFF,0XFF,0XFF,0XFF,0X80,0X00,0X03,0XFF,0XFF,
0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XF8,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X07,0XFF,0XFF,0XFF,0XFF,0XFF,0X80,0X00,0X03,
0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XF8,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X0F,0XFF,0XFF,0XFF,0XFF,0XFF,0X80,
0X00,0X03,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,
0XFF,0XF8,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X1F,0XFF,0XFF,0XFF,0XFF,
0XFF,0X80,0X00,0X03,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,
0XFF,0XFF,0XFF,0XF8,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X1F,0XFF,0XFF,
0XFF,0XFF,0XFF,0X80,0X00,0X03,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,
0XFF,0XFF,0XFF,0XFF,0XFF,0XF8,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X3F,
0XFF,0XFF,0XFF,0XFF,0XFF,0X80,0X00,0X03,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,
0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XF8,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X7F,0XFF,0XFF,0XFF,0XFF,0XFF,0X80,0X00,0X03,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,
0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XF8,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0X80,0X00,0X03,0XFF,0XFF,0XFF,0XFF,
0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XF8,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0X80,0X00,0X03,0XFF,0XFF,
0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XF8,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X01,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0X80,0X00,0X03,
0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XF8,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X03,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0X80,
0X00,0X03,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,
0XFF,0XF8,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X03,0XFF,0XFF,0XFF,0XFF,0XFF,
0XFF,0X80,0X00,0X03,0XFF,0XFF,0XC0,0X00,0X00,0X00,0X03,0XFE,0X00,0X1F,0XFF,0XF8,
0X00,0X7F,0XFF,0XF8,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X07,0XFF,0XFF,0XFF,
0XFF,0XFF,0XFF,0X80,0X00,0X03,0XFF,0XFF,0XC0,0X00,0X00,0X00,0X03,0XFE,0X00,0X0F,
0XFF,0XF0,0X00,0X7F,0XFF,0XF8,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X0F,0XFF,
0XFF,0XFF,0XFF,0XFF,0XFF,0X80,0X00,0X03,0XFF,0XFF,0XC0,0X00,0X00,0X00,0X03,0XFE,
0X00,0X07,0XFF,0XF0,0X00,0X7F,0XFF,0XF8,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X1F,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0X80,0X00,0X03,0XFF,0XFF,0XC0,0X00,0X00,0X00,
0X03,0XFE,0X00,0X03,0XFF,0XF0,0X00,0X7F,0XFF,0XF8,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X1F,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0X80,0X00,0X03,0XFF,0XFF,0XC0,0X00,
0X00,0X00,0X03,0XFE,0X00,0X03,0XFF,0XF0,0X00,0X7F,0XFF,0XF8,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X3F,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0X80,0X00,0X03,0XFF,0XFF,
0XC0,0X00,0X00,0X00,0X03,0XFE,0X00,0X01,0XFF,0XF0,0X00,0X7F,0XFF,0XF8,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X7F,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0X80,0X00,0X03,
0XFF,0XFF,0XC0,0X00,0X00,0X00,0X03,0XFE,0X00,0X00,0XFF,0XF0,0X00,0X7F,0XFF,0XF8,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X7F,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0X80,
0X00,0X03,0XFF,0XFF,0XC0,0X00,0X00,0X00,0X03,0XFE,0X00,0X00,0XFF,0XF0,0X00,0X7F,
0XFF,0XF8,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,
0XFF,0X80,0X00,0X03,0XFF,0XFF,0XC0,0X00,0X00,0X00,0X03,0XFE,0X00,0X00,0X7F,0XF0,
0X00,0X7F,0XFF,0XF8,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X01,0XFF,0XFF,0XFF,0XFF,
0XFF,0XFF,0XFF,0X80,0X00,0X03,0XFF,0XFF,0XC0,0X00,0X00,0X00,0X03,0XFE,0X00,0X00,
0X3F,0XF0,0X00,0X7F,0XFF,0XF8,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X01,0XFF,0XFF,
0XFF,0XFF,0XFF,0XFF,0XFF,0X80,0X00,0X03,0XFF,0XFF,0XC0,0X01,0XFF,0XFF,0XFF,0XFE,
0X00,0X00,0X3F,0XF0,0X00,0X7F,0XFF,0XF8,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X03,
0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0X80,0X00,0X03,0XFF,0XFF,0XC0,0X01,0XFF,0XFF,
0XFF,0XFE,0X00,0X00,0X1F,0XF0,0X00,0X7F,0XFF,0XF8,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X07,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0X80,0X00,0X03,0XFF,0XFF,0XC0,0X01,
0XFF,0XFF,0XFF,0XFE,0X00,0X00,0X0F,0XF0,0X00,0X7F,0XFF,0XF8,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X07,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0X80,0X00,0X03,0XFF,0XFF,
0XC0,0X01,0XFF,0XFF,0XFF,0XFE,0X00,0X00,0X0F,0XF0,0X00,0X7F,0XFF,0XF8,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X0F,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0X80,0X00,0X03,
0XFF,0XFF,0XC0,0X01,0XFF,0XFF,0XFF,0XFE,0X00,0X00,0X07,0XF0,0X00,0X7F,0XFF,0XF8,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X1F,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0X80,
0X00,0X03,0XFF,0XFF,0XC0,0X01,0XFF,0XFF,0XFF,0XFE,0X00,0X00,0X03,0XF0,0X00,0X7F,
0XFF,0XF8,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X3F,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,
0XFF,0X80,0X00,0X03,0XFF,0XFF,0XC0,0X01,0XFF,0XFF,0XFF,0XFE,0X00,0X00,0X03,0XF0,
0X00,0X7F,0XFF,0XF8,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X3F,0XFF,0XFF,0XFF,0XFF,
0XFF,0XFF,0XFF,0X80,0X00,0X03,0XFF,0XFF,0XC0,0X00,0X00,0X00,0X07,0XFE,0X00,0X00,
0X01,0XF0,0X00,0X7F,0XFF,0XF8,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X7F,0XFF,0XFF,
0XFF,0XFF,0XFF,0XFF,0XFF,0X80,0X00,0X03,0XFF,0XFF,0XC0,0X00,0X00,0X00,0X07,0XFE,
0X00,0X00,0X00,0XF0,0X00,0X7F,0XFF,0XF8,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0XFF,
0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0X80,0X00,0X03,0XFF,0XFF,0XC0,0X00,0X00,0X00,
0X07,0XFE,0X00,0X00,0X00,0XF0,0X00,0X7F,0XFF,0XF8,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0X80,0X00,0X03,0XFF,0XFF,0XC0,0X00,
0X00,0X00,0X07,0XFE,0X00,0X00,0X00,0X70,0X00,0X7F,0XFF,0XF8,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X01,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0X80,0X00,0X03,0XFF,0XFF,
0XC0,0X00,0X00,0X00,0X07,0XFE,0X00,0X00,0X00,0X30,0X00,0X7F,0XFF,0XF8,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X03,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0X80,0X00,0X03,
0XFF,0XFF,0XC0,0X00,0X00,0X00,0X07,0XFE,0X00,0X08,0X00,0X30,0X00,0X7F,0XFF,0XF8,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X03,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0X80,
0X00,0X03,0XFF,0XFF,0XC0,0X00,0X00,0X00,0X07,0XFE,0X00,0X0C,0X00,0X10,0X00,0X7F,
0XFF,0XF8,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X07,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,
0XFF,0X80,0X00,0X03,0XFF,0XFF,0XC0,0X00,0X00,0X00,0X07,0XFE,0X00,0X0C,0X00,0X00,
0X00,0X7F,0XFF,0XF8,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X0F,0XFF,0XFF,0XFF,0XFF,0XFF,
0XFF,0XFF,0XFF,0X80,0X00,0X03,0XFF,0XFF,0XC0,0X00,0X00,0X00,0X07,0XFE,0X00,0X0E,
0X00,0X00,0X00,0X7F,0XFF,0XF8,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X1F,0XFF,0XFF,0XFF,
0XFF,0XFF,0XFF,0XFF,0XFF,0X80,0X00,0X03,0XFF,0XFF,0XC0,0X01,0XFF,0XFF,0XFF,0XFE,
0X00,0X0F,0X00,0X00,0X00,0X7F,0XFF,0XF8,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X1F,0XFF,
0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0X80,0X00,0X03,0XFF,0XFF,0XC0,0X01,0XFF,0XFF,
0XFF,0XFE,0X00,0X0F,0X00,0X00,0X00,0X7F,0XFF,0XF8,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X3F,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0X80,0X00,0X03,0XFF,0XFF,0XC0,0X01,
0XFF,0XFF,0XFF,0XFE,0X00,0X0F,0X80,0X00,0X00,0X7F,0XFF,0XF8,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X7F,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0X80,0X00,0X03,0XFF,0XFF,
0XC0,0X01,0XFF,0XFF,0XFF,0XFE,0X00,0X0F,0XC0,0X00,0X00,0X7F,0XFF,0XF8,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X7F,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0X80,0X00,0X03,
0XFF,0XFF,0XC0,0X01,0XFF,0XFF,0XFF,0XFE,0X00,0X0F,0XC0,0X00,0X00,0X7F,0XFF,0XF8,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0X80,
0X00,0X03,0XFF,0XFF,0XC0,0X01,0XFF,0XFF,0XFF,0XFE,0X00,0X0F,0XE0,0X00,0X00,0X7F,
0XFF,0XF8,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X01,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,
0XFF,0X80,0X00,0X03,0XFF,0XFF,0XC0,0X01,0XFF,0XFF,0XFF,0XFE,0X00,0X0F,0XF0,0X00,
0X00,0X7F,0XFF,0XF8,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X01,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,
0XFF,0XFF,0XFF,0X80,0X00,0X03,0XFF,0XFF,0XC0,0X01,0XFF,0XFF,0XFF,0XFE,0X00,0X0F,
0XF8,0X00,0X00,0X7F,0XFF,0XF8,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X03,0XFF,0XFF,0XFF,0XFF,
0XFF,0XFF,0XFF,0XFF,0XFF,0X80,0X00,0X03,0XFF,0XFF,0XC0,0X00,0XFF,0XFF,0XFF,0XFE,
0X00,0X0F,0XF8,0X00,0X00,0X7F,0XFF,0XF8,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X07,0XFF,0XFF,
0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0X80,0X00,0X03,0XFF,0XFF,0XC0,0X00,0X00,0X00,
0X01,0XFE,0X00,0X0F,0XFC,0X00,0X00,0X7F,0XFF,0XF8,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X0F,
0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0X80,0X00,0X03,0XFF,0XFF,0XC0,0X00,
0X00,0X00,0X01,0XFE,0X00,0X0F,0XFE,0X00,0X00,0X7F,0XFF,0XF8,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X0F,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0X80,0X00,0X03,0XFF,0XFF,
0XC0,0X00,0X00,0X00,0X01,0XFE,0X00,0X0F,0XFE,0X00,0X00,0X7F,0XFF,0XF8,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X1F,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0X80,0X00,0X03,
0XFF,0XFF,0XC0,0X00,0X00,0X00,0X01,0XFE,0X00,0X0F,0XFF,0X00,0X00,0X7F,0XFF,0XF8,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X3F,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0X80,
0X00,0X03,0XFF,0XFF,0XC0,0X00,0X00,0X00,0X01,0XFE,0X00,0X0F,0XFF,0X80,0X00,0X7F,
0XFF,0XF8,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X3F,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,
0XFF,0X80,0X00,0X03,0XFF,0XFF,0XC0,0X00,0X00,0X00,0X01,0XFE,0X00,0X0F,0XFF,0X80,
0X00,0X7F,0XFF,0XF8,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X7F,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,
0XFF,0XFF,0XFF,0X80,0X00,0X03,0XFF,0XFF,0XC0,0X00,0X00,0X00,0X01,0XFE,0X00,0X0F,
0XFF,0XC0,0X00,0X7F,0XFF,0XF8,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0XFF,0XFF,0XFF,0XFF,0XFF,
0XFF,0XFF,0XFF,0XFF,0XFF,0X80,0X00,0X03,0XFF,0XFF,0XC0,0X00,0X00,0X00,0X01,0XFE,
0X00,0X0F,0XFF,0XE0,0X00,0X7F,0XFF,0XF8,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0XFF,0XFF,0XFF,
0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0X80,0X00,0X03,0XFF,0XFF,0XC0,0X00,0X00,0X00,
0X01,0XFE,0X00,0X0F,0XFF,0XE0,0X00,0X7F,0XFF,0XF8,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X01,0XFF,
0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0X80,0X00,0X03,0XFF,0XFF,0XC0,0X00,
0X00,0X00,0X01,0XFE,0X00,0X1F,0XFF,0XF0,0X00,0X7F,0XFF,0XF8,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X03,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0X80,0X00,0X03,0XFF,0XFF,
0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XF8,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X07,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0X80,0X00,0X03,
0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XF8,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X07,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0X80,
0X00,0X03,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,
0XFF,0XF8,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X0F,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,
0XFF,0X80,0X00,0X03,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,
0XFF,0XFF,0XFF,0XF8,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X1F,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,
0XFF,0XFF,0XFF,0X80,0X00,0X03,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,
0XFF,0XFF,0XFF,0XFF,0XFF,0XF8,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X1F,0XFF,0XFF,0XFF,0XFF,0XFF,
0XFF,0XFF,0XFF,0XFF,0XFF,0X80,0X00,0X03,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,
0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XF8,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X3F,0XFF,0XFF,0XFF,
0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0X80,0X00,0X03,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,
0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XF8,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X7F,0XFF,
0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0X80,0X00,0X03,0XFF,0XFF,0XFF,0XFF,
0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XF8,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X7F,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0X80,0X00,0X03,0XFF,0XFF,
0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XF8,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0X80,0X00,0X03,
0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XF8,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X01,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0X80,
0X00,0X03,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,
0XFF,0XF8,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X03,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,
0XFF,0X80,0X00,0X01,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,
0XFF,0XFF,0XFF,0XF8,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X03,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,
0XFF,0XFF,0XFF,0XC0,0X00,0X03,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,
0XFF,0XFF,0XFF,0XFF,0XFF,0XF8,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X03,
0XFF,0XFE,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X40,0X00,0X0F,0XFF,0XE0,0X00,0X00,
0X00,0X00,0X00,0X7E,0X00,0X00,0X00,0X00,0X00,0X00,0X02,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X03,0XFF,0XFF,0X00,0X00,0X00,0X00,0X00,0X00,0X01,0XC0,0X00,0X0F,0XFF,0XF8,
0X00,0X00,0X00,0X00,0X00,0X7E,0X00,0X00,0X00,0X00,0X00,0X00,0X0E,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X03,0XFF,0XFF,0XC0,0X00,0X00,0X00,0X00,0X00,0X07,0XC0,0X00,0X0F,
0XFF,0XFC,0X00,0X00,0X00,0X00,0X00,0X7E,0X00,0X00,0X00,0X00,0X00,0X00,0X3E,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X03,0XFF,0XFF,0XC0,0X00,0X00,0X00,0X00,0X00,0X0F,0XC0,
0X00,0X0F,0XFF,0XFE,0X00,0X00,0X00,0X00,0X00,0X7E,0X00,0X00,0X00,0X00,0X00,0X00,
0X7E,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X03,0XFF,0XFF,0XE0,0X00,0X00,0X00,0X00,0X00,
0X0F,0XC0,0X00,0X0F,0XFF,0XFE,0X00,0X00,0X00,0X00,0X00,0X7E,0X00,0X00,0X00,0X00,
0X00,0X00,0XFE,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X03,0XF8,0X1F,0XE0,0X00,0X00,0X00,
0X00,0X00,0X0F,0XC0,0X00,0X0F,0XE0,0XFF,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0XFE,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X03,0XF8,0X0F,0XE0,0X03,
0XE0,0X00,0X3F,0XE0,0X1F,0XC0,0X00,0X0F,0XE0,0X7F,0X00,0X0E,0X00,0X7E,0X00,0X00,
0X00,0X3F,0X00,0X01,0XFC,0X00,0XFE,0X00,0X3F,0XC0,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X03,0XF8,0X0F,
0XC0,0X1F,0XFC,0X00,0XFF,0XF8,0X7F,0XFC,0X00,0X0F,0XE0,0X7F,0X1F,0X9F,0X83,0XFF,
0X80,0X7E,0X01,0XFF,0XE0,0X07,0XFF,0X03,0XFF,0XE0,0XFF,0XF0,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X03,
0XF8,0X1F,0XC0,0X3F,0XFE,0X03,0XFF,0XFC,0X7F,0XFC,0X00,0X0F,0XE0,0X7F,0X1F,0XFF,
0X07,0XFF,0XE0,0X7E,0X03,0XFF,0XF0,0X1F,0XFF,0XC3,0XFF,0XE1,0XFF,0XF8,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X03,0XFF,0XFF,0X80,0X7F,0XFF,0X83,0XFF,0XFE,0X7F,0XFC,0X00,0X0F,0XE0,0XFF,
0X1F,0XFF,0X0F,0XFF,0XF0,0X7E,0X07,0XFF,0XF8,0X3F,0XFF,0XE3,0XFF,0XE3,0XFF,0XFC,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X03,0XFF,0XFF,0X00,0XFF,0XFF,0X87,0XF8,0XFE,0X7F,0XFC,0X00,0X0F,
0XFF,0XFE,0X1F,0XFE,0X1F,0XFF,0XF0,0X7E,0X0F,0XE3,0XF8,0X3F,0XFF,0XE3,0XFF,0XE3,
0XE1,0XFC,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X03,0XFF,0XFF,0X01,0XFE,0X3F,0XC7,0XF0,0X7E,0X1F,0XC0,
0X00,0X0F,0XFF,0XFE,0X1F,0XF2,0X1F,0XC3,0XF8,0X7E,0X0F,0XC1,0XFC,0X7F,0X87,0XF0,
0XFE,0X07,0XE0,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X03,0XFF,0XFF,0XC1,0XFC,0X1F,0XC0,0X00,0XFE,
0X0F,0XC0,0X00,0X0F,0XFF,0XFC,0X1F,0XE0,0X3F,0X83,0XF8,0X7E,0X1F,0XC0,0XFC,0X7F,
0X03,0X80,0XFE,0X03,0XFC,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X03,0XFF,0XFF,0XE1,0XFC,0X0F,0XC0,
0X07,0XFE,0X0F,0XC0,0X00,0X0F,0XFF,0XF8,0X1F,0XC0,0X3F,0X81,0XF8,0X7E,0X1F,0XC0,
0XFC,0X7E,0X00,0X00,0XFE,0X03,0XFF,0XE0,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X03,0XF8,0X0F,0XE1,0XF8,
0X0F,0XC0,0XFF,0XFE,0X0F,0XC0,0X00,0X0F,0XFF,0XE0,0X1F,0XC0,0X3F,0X81,0XF8,0X7E,
0X1F,0XFF,0XFE,0X7E,0X00,0X00,0XFE,0X01,0XFF,0XF8,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X03,0XF8,0X0F,
0XF1,0XF8,0X0F,0XC3,0XFF,0XFE,0X0F,0XC0,0X00,0X0F,0XE0,0X00,0X1F,0XC0,0X3F,0X01,
0XFC,0X7E,0X1F,0XFF,0XFE,0X7E,0X00,0X00,0XFE,0X00,0XFF,0XFC,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X03,
0XF8,0X0F,0XF1,0XF8,0X0F,0XC7,0XFC,0X7E,0X0F,0XC0,0X00,0X0F,0XE0,0X00,0X1F,0XC0,
0X3F,0X81,0XF8,0X7E,0X1F,0XFF,0XFE,0X7E,0X00,0X00,0XFE,0X00,0X3F,0XFC,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X03,0XF8,0X0F,0XF1,0XFC,0X1F,0XC7,0XF0,0X7E,0X0F,0XC0,0X00,0X0F,0XE0,0X00,
0X1F,0XC0,0X3F,0X81,0XF8,0X7E,0X1F,0XC0,0X00,0X7F,0X03,0XC0,0XFE,0X00,0X01,0XFE,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X03,0XFC,0X3F,0XF1,0XFC,0X1F,0XC7,0XE0,0X7E,0X0F,0XC0,0X00,0X0F,
0XE0,0X00,0X1F,0XC0,0X1F,0X83,0XF8,0X7E,0X1F,0XC0,0X00,0X7F,0X07,0XF0,0XFE,0X00,
0X60,0X7E,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X03,0XFF,0XFF,0XE0,0XFF,0X3F,0X87,0XE0,0XFE,0X0F,0XFC,
0X00,0X0F,0XE0,0X00,0X1F,0XC0,0X1F,0XE7,0XF0,0X7E,0X0F,0XE1,0XFC,0X7F,0XCF,0XF0,
0X7F,0XC7,0XF0,0X7E,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X03,0XFF,0XFF,0XE0,0XFF,0XFF,0X87,0XFF,0XFE,
0X0F,0XFC,0X00,0X0F,0XE0,0X00,0X1F,0XC0,0X0F,0XFF,0XF0,0X7E,0X0F,0XFF,0XF8,0X3F,
0XFF,0XE0,0X7F,0XC7,0XFF,0XFC,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X03,0XFF,0XFF,0XC0,0X7F,0XFF,0X07,
0XFF,0XFE,0X0F,0XFC,0X00,0X0F,0XE0,0X00,0X1F,0XC0,0X07,0XFF,0XE0,0X7E,0X07,0XFF,
0XF0,0X1F,0XFF,0XC0,0X7F,0XE3,0XFF,0XF8,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X03,0XFF,0XFF,0X80,0X1F,
0XFE,0X03,0XFF,0X3F,0X07,0XFC,0X00,0X0F,0XE0,0X00,0X1F,0XC0,0X03,0XFF,0XC0,0X7E,
0X03,0XFF,0XE0,0X0F,0XFF,0X80,0X3F,0XE1,0XFF,0XF0,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X03,0XFF,0XF8,
0X00,0X07,0XF8,0X00,0XFC,0X3F,0X03,0XFC,0X00,0X0F,0XE0,0X00,0X1F,0XC0,0X00,0XFF,
0X00,0X7E,0X00,0XFF,0X80,0X03,0XFE,0X00,0X1F,0XE0,0X7F,0XC0,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0XFE,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X03,0XFE,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X03,0XFE,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X07,0XFE,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X07,0XFC,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X07,0XFC,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X01,0XE0,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,};

View File

@@ -0,0 +1,939 @@
const unsigned char gImage_MFD_OBP60_400x300_sw[15000] = { /* 0X00,0X01,0X90,0X01,0X2C,0X01, */
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X1F,0XE0,0X3F,
0XC0,0X00,0X00,0X7E,0X00,0X40,0XFC,0X07,0XF0,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X08,0X1F,0X80,0X00,0X00,0X00,0X00,0X00,0X07,0XFF,0XE0,0X0F,0XC0,0X00,0X00,0X00,
0X00,0X1F,0X80,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X1F,
0XF0,0X3F,0XC0,0X00,0X00,0X7E,0X01,0XC0,0XFC,0X1F,0XF0,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X38,0X1F,0X80,0X00,0X00,0X00,0X00,0X00,0X07,0XFF,0XF8,0X0F,0XC0,0X00,
0X00,0X00,0X00,0X1F,0X80,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X1F,0XF0,0X7F,0XC0,0X00,0X00,0X7E,0X07,0XC0,0XFC,0X1F,0XF0,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0XF8,0X1F,0X80,0X00,0X00,0X00,0X00,0X00,0X07,0XFF,0XFC,0X0F,
0XC0,0X00,0X00,0X00,0X00,0X1F,0X80,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X1F,0XF0,0X7F,0XC0,0X00,0X00,0X7E,0X0F,0XC0,0XFC,0X3F,0XE0,0X00,
0X00,0X00,0X00,0X00,0X00,0X01,0XF8,0X1F,0X80,0X00,0X00,0X00,0X00,0X00,0X07,0XFF,
0XFE,0X0F,0XC0,0X00,0X00,0X00,0X00,0X1F,0X80,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X1F,0XF0,0X7F,0XC0,0X00,0X00,0X7E,0X0F,0XC0,0X00,0X3F,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X01,0XF8,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X07,0XFF,0XFE,0X00,0X00,0X00,0X00,0X00,0X00,0X1F,0X80,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X1F,0XF8,0X7F,0XC0,0X00,0X00,0X7E,0X0F,0XC0,
0X00,0X3F,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X01,0XF8,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X07,0XF0,0XFF,0X00,0X00,0X00,0X00,0X00,0X00,0X1F,0X80,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X1F,0XF8,0XFF,0XC3,0XE0,0XF8,0X7E,
0X3F,0XF8,0XFC,0XFF,0XE7,0XC1,0XF0,0XF8,0XF8,0X01,0XFC,0X07,0XFF,0X1F,0X80,0X7F,
0X00,0XF8,0XF8,0X00,0X07,0XF0,0X7F,0X0F,0XC0,0X7F,0X80,0XF9,0XF0,0X1F,0X80,0XFF,
0X83,0XF0,0X3F,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X1F,0X78,0XF7,0XC3,0XE0,
0XF8,0X7E,0X3F,0XF8,0XFC,0XFF,0XE7,0XC1,0XF0,0XFB,0XFC,0X07,0XFF,0X07,0XFF,0X1F,
0X81,0XFF,0XC0,0XFB,0XFC,0X00,0X07,0XF0,0X3F,0X0F,0XC1,0XFF,0XE0,0XFB,0XFC,0X1F,
0X83,0XFF,0XE1,0XF8,0X3E,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X1F,0X78,0XF7,
0XC3,0XE0,0XF8,0X7E,0X3F,0XF8,0XFC,0XFF,0XE7,0XC1,0XF0,0XFF,0XFE,0X0F,0XFF,0XC7,
0XFF,0X1F,0X83,0XFF,0XE0,0XFF,0XFE,0X00,0X07,0XF0,0X3F,0X0F,0XC3,0XFF,0XF0,0XFF,
0XFE,0X1F,0X87,0XFF,0XF1,0XF8,0X3E,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X1F,
0X7C,0XF7,0XC3,0XE0,0XF8,0X7E,0X3F,0XF8,0XFC,0XFF,0XE7,0XC1,0XF0,0XFF,0XFE,0X1F,
0XFF,0XC7,0XFF,0X1F,0X87,0XFF,0XF0,0XFF,0XFE,0X00,0X07,0XF0,0X3F,0X8F,0XC3,0XE3,
0XF0,0XFF,0XFE,0X1F,0X87,0XFF,0XF0,0XF8,0X7E,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X1F,0X3D,0XE7,0XC3,0XE0,0XF8,0X7E,0X0F,0XC0,0XFC,0X3F,0X07,0XC1,0XF0,0XFF,
0XFE,0X1F,0X87,0XE1,0XF8,0X1F,0X87,0XE3,0XF0,0XFF,0XFE,0X00,0X07,0XF0,0X3F,0X8F,
0XC3,0XE1,0X80,0XFC,0X7F,0X1F,0X87,0XC1,0XF0,0XFC,0X7C,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X1F,0X3D,0XE7,0XC3,0XE0,0XF8,0X7E,0X0F,0XC0,0XFC,0X3F,0X07,0XC1,
0XF0,0XFC,0X7E,0X3F,0X07,0X01,0XF8,0X1F,0X8F,0XC1,0XF8,0XFC,0X7E,0X00,0X07,0XF0,
0X3F,0X8F,0XC3,0XFC,0X00,0XFC,0X3F,0X1F,0X80,0X07,0XF0,0X7C,0X7C,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X1F,0X3F,0XE7,0XC3,0XE0,0XF8,0X7E,0X0F,0XC0,0XFC,0X3F,
0X07,0XC1,0XF0,0XFC,0X3E,0X3F,0X00,0X01,0XF8,0X1F,0X8F,0XC1,0XF8,0XFC,0X3E,0X00,
0X07,0XF0,0X3F,0X0F,0XC3,0XFF,0XE0,0XF8,0X3F,0X1F,0X80,0X7F,0XF0,0X7C,0XF8,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X1F,0X3F,0XE7,0XC3,0XE0,0XF8,0X7E,0X0F,0XC0,
0XFC,0X3F,0X07,0XC1,0XF0,0XF8,0X3E,0X3F,0X00,0X01,0XF8,0X1F,0X8F,0XC1,0XF8,0XF8,
0X3E,0X00,0X07,0XF0,0X3F,0X0F,0XC1,0XFF,0XF0,0XF8,0X3F,0X1F,0X83,0XFF,0XF0,0X7C,
0XF8,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X1F,0X1F,0XC7,0XC3,0XE1,0XF8,0X7E,
0X0F,0XC0,0XFC,0X3F,0X07,0XC3,0XF0,0XF8,0X3E,0X3F,0X00,0X01,0XF8,0X1F,0X8F,0XC1,
0XF8,0XF8,0X3E,0X00,0X07,0XF0,0X3F,0X0F,0XC0,0X7F,0XF8,0XF8,0X3F,0X1F,0X87,0XF1,
0XF0,0X3E,0XF8,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X1F,0X1F,0XC7,0XC3,0XF1,
0XF8,0X7E,0X0F,0XC0,0XFC,0X3F,0X07,0XE3,0XF0,0XF8,0X3E,0X3F,0X03,0X01,0XF8,0X1F,
0X8F,0XC1,0XF8,0XF8,0X3E,0X00,0X07,0XF0,0X7F,0X0F,0XC0,0X03,0XF8,0XFC,0X3F,0X1F,
0X8F,0XC1,0XF0,0X3F,0XF0,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X1F,0X1F,0XC7,
0XC3,0XFF,0XF8,0X7E,0X0F,0XC0,0XFC,0X3F,0X07,0XFF,0XF0,0XF8,0X3E,0X1F,0X87,0XE1,
0XF8,0X1F,0X87,0XE3,0XF0,0XF8,0X3E,0X00,0X07,0XFF,0XFE,0X0F,0XC0,0XE0,0XF8,0XFC,
0X7F,0X1F,0X8F,0XC3,0XF0,0X1F,0XF0,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X1F,
0X1F,0XC7,0XC3,0XFF,0XF8,0X7E,0X0F,0XF8,0XFC,0X3F,0X07,0XFF,0XF0,0XF8,0X3E,0X1F,
0XFF,0XC1,0XFF,0X1F,0X87,0XFF,0XF0,0XF8,0X3E,0X00,0X07,0XFF,0XFE,0X0F,0XC7,0XE1,
0XF8,0XFF,0XFE,0X1F,0X8F,0XFF,0XF0,0X1F,0XF0,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X1F,0X0F,0X87,0XC3,0XFF,0XF8,0X7E,0X0F,0XF8,0XFC,0X3F,0X07,0XFF,0XF0,0XF8,
0X3E,0X0F,0XFF,0XC1,0XFF,0X1F,0X83,0XFF,0XE0,0XF8,0X3E,0X00,0X07,0XFF,0XFC,0X0F,
0XC3,0XFF,0XF0,0XFF,0XFE,0X1F,0X8F,0XFF,0XF0,0X1F,0XE0,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X1F,0X0F,0X87,0XC1,0XFE,0XF8,0X7E,0X07,0XF8,0XFC,0X3F,0X03,0XFD,
0XF0,0XF8,0X3E,0X07,0XFF,0X80,0XFF,0X1F,0X81,0XFF,0XC0,0XF8,0X3E,0X00,0X07,0XFF,
0XF8,0X0F,0XC3,0XFF,0XE0,0XFF,0XFC,0X1F,0X87,0XFD,0XF0,0X0F,0XE0,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X1F,0X0F,0X87,0XC0,0XF8,0XF8,0X7E,0X03,0XF8,0XFC,0X3F,
0X01,0XF1,0XF0,0XF8,0X3E,0X01,0XFE,0X00,0X7F,0X1F,0X80,0X7F,0X00,0XF8,0X3E,0X00,
0X07,0XFF,0XC0,0X0F,0XC0,0X7F,0X80,0XF9,0XF0,0X1F,0X81,0XF0,0XF8,0X0F,0XC0,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0XF8,0X00,0X00,0X00,0X00,0X00,0X0F,
0XC0,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0XF8,0X00,0X00,0X00,0X00,
0X00,0X0F,0XC0,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0XF8,0X00,0X00,
0X00,0X00,0X01,0XFF,0X80,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0XF8,
0X00,0X00,0X00,0X00,0X01,0XFF,0X80,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0XF8,0X00,0X00,0X00,0X00,0X01,0XFF,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0XF8,0X00,0X00,0X00,0X00,0X00,0XFC,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X07,0XF8,0X01,0XFF,0XFC,0X03,0XFF,0XF0,0X00,0XFC,
0X00,0X3F,0X80,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X1F,0XFE,0X01,0XFF,0XFF,0X03,0XFF,0XFC,
0X03,0XFF,0X00,0XFF,0XE0,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X7F,0XFF,0X81,0XFF,0XFF,0X83,
0XFF,0XFE,0X07,0XFF,0X81,0XFF,0XF0,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X7F,0XFF,0XC1,0XFF,
0XFF,0X83,0XFF,0XFE,0X0F,0XFF,0XC3,0XFF,0XF0,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0XFF,0XFF,
0XC1,0XFF,0XFF,0XC3,0XFF,0XFE,0X1F,0X8F,0XC3,0XF1,0XF8,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X01,
0XFE,0X1F,0XE1,0XFC,0X1F,0XC3,0XF0,0X7F,0X1F,0X87,0X83,0XE0,0XF8,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X01,0XFC,0X0F,0XE1,0XFC,0X1F,0X83,0XF0,0X3F,0X1F,0X00,0X07,0XE0,0XF8,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X01,0XF8,0X07,0XE1,0XFC,0X1F,0X83,0XF0,0X3F,0X3F,0X3E,0X07,0XE0,
0XFC,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X03,0XF8,0X07,0XF1,0XFF,0XFF,0X03,0XF0,0X7F,0X3F,0X7F,
0X87,0XE0,0XFC,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X03,0XF8,0X07,0XF1,0XFF,0XFE,0X03,0XFF,0XFE,
0X3F,0XFF,0XC7,0XE0,0XFC,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X03,0XF8,0X07,0XF1,0XFF,0XFF,0X03,
0XFF,0XFE,0X3F,0XFF,0XC7,0XE0,0XFC,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X03,0XF8,0X07,0XF1,0XFF,
0XFF,0X83,0XFF,0XFC,0X3F,0X87,0XE7,0XE0,0XFC,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X03,0XF8,0X07,
0XF1,0XFF,0XFF,0XC3,0XFF,0XF8,0X3F,0X07,0XE7,0XE0,0XFC,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X01,
0XF8,0X07,0XE1,0XFC,0X0F,0XC3,0XFF,0XE0,0X3F,0X03,0XE7,0XE0,0XFC,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X01,0XFC,0X0F,0XE1,0XFC,0X0F,0XE3,0XF0,0X00,0X1F,0X03,0XE3,0XE0,0XFC,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X01,0XFE,0X1F,0XE1,0XFC,0X0F,0XE3,0XF0,0X00,0X1F,0X07,0XE3,0XE0,
0XF8,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0XFF,0XFF,0XC1,0XFF,0XFF,0XC3,0XF0,0X00,0X1F,0X87,
0XE3,0XF1,0XF8,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X7F,0XFF,0XC1,0XFF,0XFF,0XC3,0XF0,0X00,
0X0F,0XFF,0XC1,0XFF,0XF8,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X7F,0XFF,0X81,0XFF,0XFF,0X83,
0XF0,0X00,0X07,0XFF,0X81,0XFF,0XF0,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X1F,0XFE,0X01,0XFF,
0XFF,0X83,0XF0,0X00,0X03,0XFF,0X00,0XFF,0XE0,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X07,0XF8,
0X01,0XFF,0XFC,0X03,0XF0,0X00,0X00,0XFC,0X00,0X3F,0X80,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X07,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XF0,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X07,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XF0,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X40,0X07,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XF0,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0XC0,0X07,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XF0,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X01,0XC0,0X07,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,
0XFF,0XF0,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X01,0XC0,0X07,0XFF,0XFF,0XFF,0XFF,
0XFF,0XFF,0XFF,0XF0,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X03,0XC0,0X07,0XFF,0XFF,
0XFF,0XFF,0XFF,0XFF,0XFF,0XF0,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X07,0XC0,0X07,
0XFF,0XFC,0X07,0XFF,0X00,0X07,0XFF,0XF0,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X07,
0XC0,0X07,0XFF,0XE0,0X00,0XFF,0X00,0X00,0XFF,0XF0,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X0F,0XC0,0X07,0XFF,0XC0,0X00,0X7F,0X00,0X00,0X7F,0XF0,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X1F,0XC0,0X07,0XFF,0X80,0X00,0X3F,0X00,0X00,0X3F,0XF0,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X1F,0XC0,0X07,0XFF,0X00,0X00,0X1F,0X00,0X00,0X1F,0XF0,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X3F,0XC0,0X07,0XFE,0X00,0XC0,0X0F,0X01,0XE0,
0X1F,0XF0,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X7F,0XC0,0X07,0XFE,0X03,0XF0,0X0F,
0X01,0XF0,0X1F,0XF0,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0XFF,0XC0,0X07,0XFC,0X03,
0XF8,0X0F,0X01,0XF0,0X1F,0XF0,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0XFF,0XC0,0X07,
0XFC,0X07,0XF8,0X07,0X01,0XF0,0X1F,0XF0,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X01,0XFF,
0XC0,0X07,0XFC,0X07,0XFC,0X07,0X01,0XE0,0X1F,0XF0,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X03,0XFF,0XC0,0X07,0XFC,0X07,0XFC,0X07,0X00,0X00,0X3F,0XF0,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X03,0XFF,0XC0,0X07,0XFC,0X07,0XFC,0X07,0X00,0X00,0X3F,0XF0,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X07,0XFF,0XC0,0X07,0XFC,0X07,0XFC,0X07,0X00,0X00,0X7F,0XF0,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X0F,0XFF,0XC0,0X07,0XFC,0X07,0XFC,0X07,0X00,0X00,
0XFF,0XF0,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X0F,0XFF,0XC0,0X07,0XFC,0X07,0XF8,0X07,
0X01,0XDF,0XFF,0XF0,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X1F,0XFF,0XC0,0X07,0XFC,0X07,
0XF8,0X0F,0X01,0XFF,0XFF,0XF0,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X3F,0XFF,0XC0,0X07,
0XFE,0X03,0XF8,0X0F,0X01,0XFF,0XFF,0XF0,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X7F,0XFF,
0XC0,0X07,0XFE,0X00,0XE0,0X0F,0X01,0XFF,0XFF,0XF0,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X7F,0XFF,0XC0,0X07,0XFF,0X00,0X00,0X1F,0X01,0XFF,0XFF,0XF0,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0XFF,0XFF,0XC0,0X07,0XFF,0X00,0X00,0X3F,0X01,0XFF,0XFF,0XF0,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X01,0XFF,0XFF,0XC0,0X07,0XFF,0X80,0X00,0X7F,0X01,0XFF,0XFF,0XF0,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X01,0XFF,0XFF,0XC0,0X07,0XFF,0XE0,0X00,0XFF,0X01,0XFF,
0XFF,0XF0,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X03,0XFF,0XFF,0XC0,0X07,0XFF,0XF8,0X03,0XFF,
0X01,0XFF,0XFF,0XF0,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X07,0XFF,0XFF,0XC0,0X07,0XFF,0XFF,
0XFF,0XFF,0XFF,0XFF,0XFF,0XF0,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X07,0XFF,0XFF,0XC0,0X07,
0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XF0,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X0F,0XFF,0XFF,
0XC0,0X07,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XF0,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X1F,
0XFF,0XFF,0XC0,0X07,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XF0,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X3F,0XFF,0XFF,0XC0,0X07,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XF0,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X3F,0XFF,0XFF,0XC0,0X07,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XF0,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X7F,0XFF,0XFF,0XC0,0X07,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,
0XFF,0XF0,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0XFF,0XFF,0XFF,0XC0,0X07,0XFE,0X00,0X00,0X78,
0X0F,0XF0,0X3F,0XF0,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0XFF,0XFF,0XFF,0XC0,0X07,0XFE,0X00,
0X00,0X78,0X07,0XF0,0X3F,0XF0,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X01,0XFF,0XFF,0XFF,0XC0,0X07,
0XFE,0X00,0X00,0X78,0X07,0XF0,0X3F,0XF0,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X03,0XFF,0XFF,0XFF,
0XC0,0X07,0XFE,0X00,0X00,0X78,0X03,0XF0,0X3F,0XF0,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X03,0XFF,
0XFF,0XFF,0XC0,0X07,0XFE,0X00,0X00,0X78,0X01,0XF0,0X3F,0XF0,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X07,0XFF,0XFF,0XFF,0XC0,0X07,0XFE,0X03,0XFF,0XF8,0X01,0XF0,0X3F,0XF0,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X0F,0XFF,0XFF,0XFF,0XC0,0X07,0XFE,0X03,0XFF,0XF8,0X00,0XF0,0X3F,0XF0,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X1F,0XFF,0XFF,0XFF,0XC0,0X07,0XFE,0X03,0XFF,0XF8,0X00,0X70,
0X3F,0XF0,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X1F,0XFF,0XFF,0XFF,0XC0,0X07,0XFE,0X00,0X00,0XF8,
0X00,0X70,0X3F,0XF0,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X3F,0XFF,0XFF,0XFF,0XC0,0X07,0XFE,0X00,
0X00,0XF8,0X00,0X30,0X3F,0XF0,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X7F,0XFF,0XFF,0XFF,0XC0,0X07,
0XFE,0X00,0X00,0XF8,0X00,0X10,0X3F,0XF0,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X7F,0XFF,0XFF,0XFF,
0XC0,0X07,0XFE,0X00,0X00,0XF8,0X00,0X10,0X3F,0XF0,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0XFF,0XFF,
0XFF,0XFF,0XC0,0X07,0XFE,0X00,0X00,0XF8,0X08,0X00,0X3F,0XF0,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X01,
0XFF,0XFF,0XFF,0XFF,0XC0,0X07,0XFE,0X03,0XFF,0XF8,0X0C,0X00,0X3F,0XF0,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X01,0XFF,0XFF,0XFF,0XFF,0XC0,0X07,0XFE,0X03,0XFF,0XF8,0X0E,0X00,0X3F,0XF0,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X03,0XFF,0XFF,0XFF,0XFF,0XC0,0X07,0XFE,0X03,0XFF,0XF8,0X0E,0X00,
0X3F,0XF0,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X07,0XFF,0XFF,0XFF,0XFF,0XC0,0X07,0XFE,0X03,0XFF,0XF8,
0X0F,0X00,0X3F,0XF0,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X0F,0XFF,0XFF,0XFF,0XFF,0XC0,0X07,0XFE,0X00,
0X00,0X78,0X0F,0X80,0X3F,0XF0,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X0F,0XFF,0XFF,0XFF,0XFF,0XC0,0X07,
0XFE,0X00,0X00,0X78,0X0F,0X80,0X3F,0XF0,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X1F,0XFF,0XFF,0XFF,0XFF,
0XC0,0X07,0XFE,0X00,0X00,0X78,0X0F,0XC0,0X3F,0XF0,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X3F,0XFF,0XFF,
0XFF,0XFF,0XC0,0X07,0XFE,0X00,0X00,0X78,0X0F,0XE0,0X3F,0XF0,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X3F,
0XFF,0XFF,0XFF,0XFF,0XC0,0X07,0XFE,0X00,0X00,0X78,0X0F,0XE0,0X3F,0XF0,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X7F,0XFF,0XFF,0XFF,0XFF,0XC0,0X07,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XF0,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0XFF,0XFF,0XFF,0XFF,0XFF,0XC0,0X07,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,
0XFF,0XF0,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0XFF,0XFF,0XFF,0XFF,0XFF,0XC0,0X07,0XFF,0XFF,0XFF,0XFF,
0XFF,0XFF,0XFF,0XF0,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X01,0XFF,0XFF,0XFF,0XFF,0XFF,0XC0,0X07,0XFF,0XFF,
0XFF,0XFF,0XFF,0XFF,0XFF,0XF0,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X03,0XFF,0XFF,0XFF,0XFF,0XFF,0XC0,0X07,
0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XF0,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X07,0XFF,0XFF,0XFF,0XFF,0XFF,
0XC0,0X07,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XF0,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X07,0XFF,0XFF,0XFF,
0XFF,0XFF,0XC0,0X07,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XF0,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X07,0XF8,0X00,0X00,0X00,0X20,0X0F,0XE0,
0X00,0X00,0X38,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X07,0XFC,0X00,0X00,0X00,0X60,
0X0F,0XF8,0X00,0X00,0X38,0X00,0X00,0X01,0X80,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X07,0XFE,0X00,0X00,
0X00,0XE0,0X0F,0XF8,0X00,0X00,0X00,0X00,0X00,0X03,0X80,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X07,0X0E,
0X06,0X00,0XC0,0XE0,0X0E,0X3C,0X08,0X38,0X00,0X18,0X03,0X03,0X81,0XC0,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X07,0X1E,0X1F,0X87,0XFB,0XF8,0X0E,0X3C,0XDC,0XFE,0X38,0X7F,0X0F,0XE7,0XE7,0XF0,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X07,0XFC,0X3F,0XCF,0X3B,0XF8,0X0F,0XF8,0XF9,0XFF,0X38,0XE7,0X1F,0XE7,
0XE6,0X38,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X07,0XFE,0X78,0XE0,0X38,0XE0,0X0F,0XF8,0XE1,0XC7,0X39,0XE3,
0XBC,0X03,0X87,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X07,0X0E,0X70,0XE3,0XF8,0XE0,0X0F,0XE0,0XE1,0XC7,
0X39,0XFF,0XB8,0X03,0X87,0XF0,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X07,0X0F,0X70,0XEF,0X38,0XE0,0X0E,0X00,
0XE1,0XC7,0X39,0XFF,0XB8,0X03,0X81,0XF8,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X07,0X1E,0X39,0XEE,0X38,0XE0,
0X0E,0X00,0XE1,0XC7,0X38,0XE0,0X3C,0XF3,0X80,0X38,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X07,0XFE,0X3F,0XCF,
0XF8,0XF0,0X0E,0X00,0XE0,0XFF,0X38,0XFF,0X1F,0XE3,0XEF,0X78,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X07,0XFC,
0X1F,0X87,0XD8,0XF8,0X0E,0X00,0XE0,0X7C,0X38,0X7E,0X0F,0XC1,0XE7,0XF0,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X38,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X78,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0XF8,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X20,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,};

View File

@@ -1,140 +1,16 @@
#include "NetworkClient.h" #include "NetworkClient.h"
#include "GWWifi.h" // WiFi management (thread-safe)
extern GwWifi gwWifi; // Extern declaration of global WiFi instance
extern "C" { extern "C" {
#include "puff.h" #include "puff.h"
} }
static uint32_t crc32_update(uint32_t crc, const uint8_t* data, size_t len) {
crc = ~crc;
for (size_t i = 0; i < len; ++i) {
crc ^= data[i];
for (int bit = 0; bit < 8; ++bit) {
uint32_t mask = -(int32_t)(crc & 1U);
crc = (crc >> 1) ^ (0xEDB88320U & mask);
}
}
return ~crc;
}
// Constructor // Constructor
NetworkClient::NetworkClient(size_t reserveSize) NetworkClient::NetworkClient(size_t reserveSize)
: _doc(reserveSize), : _doc(reserveSize),
_valid(false), _valid(false)
_jsonRaw(nullptr),
_jsonRawLen(0),
_imageWidth(0),
_imageHeight(0),
_numberPixels(0),
_pictureBase64(nullptr),
_pictureBase64Len(0)
{ {
} }
NetworkClient::~NetworkClient() {
if (_jsonRaw != nullptr) {
free(_jsonRaw);
_jsonRaw = nullptr;
_jsonRawLen = 0;
}
}
bool NetworkClient::findJsonIntField(const char* json, size_t len, const char* key, int& outValue) {
if (json == nullptr || key == nullptr || len == 0) {
return false;
}
char pattern[64];
int plen = snprintf(pattern, sizeof(pattern), "\"%s\"", key);
if (plen <= 0 || (size_t)plen >= sizeof(pattern)) {
return false;
}
const char* keyPos = strstr(json, pattern);
if (keyPos == nullptr) {
return false;
}
const char* end = json + len;
const char* colon = strchr(keyPos + plen, ':');
if (colon == nullptr || colon >= end) {
return false;
}
const char* p = colon + 1;
while (p < end && (*p == ' ' || *p == '\t' || *p == '\r' || *p == '\n')) {
++p;
}
if (p >= end) {
return false;
}
char* parseEnd = nullptr;
long value = strtol(p, &parseEnd, 10);
if (parseEnd == p) {
return false;
}
outValue = (int)value;
return true;
}
bool NetworkClient::extractJsonStringInPlace(char* json, size_t len, const char* key, char*& outValue, size_t& outLen) {
outValue = nullptr;
outLen = 0;
if (json == nullptr || key == nullptr || len == 0) {
return false;
}
char pattern[64];
int plen = snprintf(pattern, sizeof(pattern), "\"%s\"", key);
if (plen <= 0 || (size_t)plen >= sizeof(pattern)) {
return false;
}
char* keyPos = strstr(json, pattern);
if (keyPos == nullptr) {
return false;
}
char* end = json + len;
char* colon = strchr(keyPos + plen, ':');
if (colon == nullptr || colon >= end) {
return false;
}
char* p = colon + 1;
while (p < end && (*p == ' ' || *p == '\t' || *p == '\r' || *p == '\n')) {
++p;
}
if (p >= end || *p != '"') {
return false;
}
char* valueStart = p + 1;
char* cur = valueStart;
while (cur < end) {
if (*cur == '\\') {
++cur;
if (cur < end) {
++cur;
}
continue;
}
if (*cur == '"') {
*cur = '\0';
outValue = valueStart;
outLen = (size_t)(cur - valueStart);
return true;
}
++cur;
}
return false;
}
// Skip GZIP Header an goto DEFLATE content // Skip GZIP Header an goto DEFLATE content
int NetworkClient::skipGzipHeader(const uint8_t* data, size_t len) { int NetworkClient::skipGzipHeader(const uint8_t* data, size_t len) {
if (len < 10) return -1; if (len < 10) return -1;
@@ -175,15 +51,8 @@ bool NetworkClient::httpGetGzip(const String& url, uint8_t*& outData, size_t& ou
const size_t capacity = READLIMIT; // Read limit for data (can be adjusted in NetworkClient.h) const size_t capacity = READLIMIT; // Read limit for data (can be adjusted in NetworkClient.h)
uint8_t* buffer = (uint8_t*)malloc(capacity); uint8_t* buffer = (uint8_t*)malloc(capacity);
// If not with WiFi connectetd then return without any activities
if (!gwWifi.clientConnected()) {
if (DEBUGING) {Serial.println("No WiFi connection");}
return false;
}
// If frame buffer not correct allocated then return without any activities
if (!buffer) { if (!buffer) {
if (DEBUGING) {Serial.println("Malloc failed buffer");} if (DEBUG) {Serial.println("Malloc failed (buffer");}
return false; return false;
} }
@@ -194,50 +63,20 @@ bool NetworkClient::httpGetGzip(const String& url, uint8_t*& outData, size_t& ou
http.setTimeout(TCPREADTIMEOUT); // Read timeout in ms (can be adjusted in NetworkClient.h) http.setTimeout(TCPREADTIMEOUT); // Read timeout in ms (can be adjusted in NetworkClient.h)
http.begin(url); http.begin(url);
// NEW: force server to close the connection after the response (prevents "stuck" keep-alive reads)
http.addHeader("Connection", "close");
// NEW: request gzip, but we will only decompress if the server actually answers with gzip
http.addHeader("Accept-Encoding", "gzip"); http.addHeader("Accept-Encoding", "gzip");
// NEW: register headers BEFORE GET() (more reliable with Arduino HTTPClient)
if (DEBUGING) {
// We need follow key words
const char* keys[] = {
"Content-Encoding",
"Transfer-Encoding",
"Content-Length"
};
// Read header
http.collectHeaders(keys, 3);
}
int code = http.GET(); int code = http.GET();
if (code != HTTP_CODE_OK) { if (code != HTTP_CODE_OK) {
Serial.printf("HTTP Client ERROR: %d (%s)\n", code, http.errorToString(code).c_str()); Serial.printf("HTTP ERROR: %d\n", code);
// Hard reset HTTP + socket // Hard reset HTTP + socket
WiFiClient* tmp = http.getStreamPtr(); WiFiClient* tmp = http.getStreamPtr();
if (tmp) tmp->stop(); // Force close TCP socket if (tmp) tmp->stop(); // Force close TCP socket
http.end(); http.end();
free(buffer); free(buffer);
return false; return false;
} }
else{
if (DEBUGING) {
String ce = http.header("Content-Encoding");
String te = http.header("Transfer-Encoding");
String cl = http.header("Content-Length");
// Print header informations
Serial.printf("Content-Encoding=%s Transfer-Encoding=%s Content-Length=%s\n",
ce.c_str(),
te.c_str(),
cl.c_str());
}
}
WiFiClient* stream = http.getStreamPtr(); WiFiClient* stream = http.getStreamPtr();
@@ -246,251 +85,55 @@ bool NetworkClient::httpGetGzip(const String& url, uint8_t*& outData, size_t& ou
const uint32_t READ_TIMEOUT = READDATATIMEOUT; // Timeout for reading data (can be adjusted in NetworkClient.h) const uint32_t READ_TIMEOUT = READDATATIMEOUT; // Timeout for reading data (can be adjusted in NetworkClient.h)
bool complete = false; bool complete = false;
bool aborting = false; // NEW: remember if we must force-close socket
// NEW: detect if server really sent gzip while (http.connected() && !complete) {
String ce = http.header("Content-Encoding");
bool isGzip = ce.equalsIgnoreCase("gzip");
// NEW: read expected body size if provided by server (prevents waiting forever for missing bytes) size_t avail = stream->available();
int total = http.getSize(); // returns Content-Length, or -1 if unknown/chunked
// NEW: fail fast if server claims something larger than our buffer
if (total > 0 && (size_t)total > capacity) {
Serial.println("Response exceeds READLIMIT.");
aborting = true;
}
// NEW: if not gzip, we will not try to decompress (prevents false "Decompress OK" / random success)
// You can either handle plain JSON here or just fail-fast.
if (!isGzip && !aborting) {
if (DEBUGING) {
Serial.println("Server response is NOT gzip (Content-Encoding != gzip).");
Serial.println("Either disable Accept-Encoding: gzip or add plain-body handling here.");
}
// --- Plain-body handling (recommended): read full body into outData as-is ---
// NEW: try to read Content-Length bytes if available (more robust)
if (total > 0 && (size_t)total > capacity) {
Serial.println("Plain response exceeds READLIMIT.");
aborting = true;
} else {
// Read until we have all bytes (Content-Length) or until connection closes + buffer drains
while ((http.connected() || (stream && stream->available())) && !aborting) {
size_t avail = stream ? stream->available() : 0;
if (avail == 0) {
if (millis() - lastData > READ_TIMEOUT) {
Serial.println("TIMEOUT waiting for data (plain)!");
aborting = true;
break;
}
delay(1);
continue;
}
if (len >= capacity) {
Serial.println("READLIMIT reached, aborting (plain).");
aborting = true;
break;
}
if (len + avail > capacity)
avail = capacity - len;
int read = stream->readBytes(buffer + len, avail);
if (read > 0) {
len += (size_t)read;
lastData = millis();
}
// NEW: stop reading as soon as we have the full response
if (total > 0 && (int)len >= total) {
break; // we got full body
}
}
}
if (aborting) {
// --- Added: Force-close connection only if aborted to avoid TCP RST storms ---
if (stream) stream->stop(); // Force close TCP socket
http.end();
free(buffer);
return false;
}
if (total > 0 && (int)len != total) {
Serial.printf("Plain response incomplete: got=%d expected=%d\n", (int)len, total);
if (stream) stream->stop();
http.end();
free(buffer);
return false;
}
// Return plain body to caller
outData = (uint8_t*)malloc(len + 1);
if (!outData) {
Serial.println("Malloc failed outData (plain).");
// --- Added: Force-close connection only if aborted to avoid TCP RST storms ---
if (stream) stream->stop(); // Force close TCP socket
http.end();
free(buffer);
return false;
}
memcpy(outData, buffer, len);
outData[len] = 0;
outLen = len;
http.end();
free(buffer);
return true;
}
// --- GZIP path (only if Content-Encoding is gzip) ---
if (!aborting) {
// NEW: read exactly Content-Length bytes when available (prevents partial-body timeout loops)
while ((http.connected() || (stream && stream->available())) && !complete && !aborting) {
size_t avail = stream ? stream->available() : 0;
if (avail == 0) { if (avail == 0) {
// NEW: if Content-Length is known and we already read it all, stop immediately
if (total > 0 && (int)len >= total) {
break;
}
if (millis() - lastData > READ_TIMEOUT) { if (millis() - lastData > READ_TIMEOUT) {
Serial.println("TIMEOUT waiting for data!"); Serial.println("TIMEOUT waiting for data!");
aborting = true; // NEW: mark abnormal exit
break; break;
} }
delay(1); delay(1);
continue; continue;
} }
// NEW: safety check if buffer limit is reached
if (len >= capacity) {
Serial.println("READLIMIT reached, aborting.");
aborting = true;
break;
}
// NEW: if Content-Length is known, do not read beyond it
if (total > 0) {
size_t remaining = (size_t)total - len;
if (avail > remaining) avail = remaining;
}
if (len + avail > capacity) if (len + avail > capacity)
avail = capacity - len; avail = capacity - len;
int read = stream->readBytes(buffer + len, avail); int read = stream->readBytes(buffer + len, avail);
if (read <= 0) { len += read;
// NEW: avoid tight loop if read returns zero
delay(1);
continue;
}
len += (size_t)read;
lastData = millis(); lastData = millis();
if (DEBUGING) {Serial.printf("Read chunk: %d (total: %d)\n", read, (int)len);} if (DEBUG) {Serial.printf("Read chunk: %d (total: %d)\n", read, (int)len);}
// NEW: if Content-Length is known and fully received, we can stop reading if (len < 20) continue; // Not enough data for header
if (total > 0 && (int)len >= total) {
int headerOffset = skipGzipHeader(buffer, len);
if (headerOffset < 0) continue;
unsigned long testLen = len * 8; // Dynamic expansion
uint8_t* test = (uint8_t*)malloc(testLen);
if (!test) continue;
unsigned long srcLen = len - headerOffset;
int res = puff(test, &testLen, buffer + headerOffset, &srcLen);
if (res == 0) {
if (DEBUG) {Serial.printf("Decompress OK! Size: %lu bytes\n", testLen);}
outData = test;
outLen = testLen;
complete = true;
break; break;
} }
}
// NEW: only attempt gzip parse/decompress after we have a complete body (when Content-Length is known)
// This avoids wasting heap with repeated malloc/free and reduces fragmentation over long runtimes.
if (!aborting) {
if (total > 0 && (int)len != total) {
Serial.printf("GZIP response incomplete: got=%d expected=%d\n", (int)len, total);
aborting = true;
}
}
if (!aborting) {
if (len < 20) {
aborting = true;
} else {
int headerOffset = skipGzipHeader(buffer, len);
if (headerOffset < 0) {
aborting = true;
} else {
size_t deflateLen = len - (size_t)headerOffset;
// GZIP trailer (CRC32 + ISIZE) is 8 bytes and not part of deflate stream.
if (deflateLen >= 8) {
deflateLen -= 8;
}
unsigned long srcLenForSize = (unsigned long)deflateLen;
unsigned long outNeeded = 0;
int sizeRes = puff(NIL, &outNeeded, buffer + headerOffset, &srcLenForSize);
if (sizeRes != 0) {
if (DEBUGING) {
Serial.printf("Decompress size probe failed: res=%d src=%lu\n", sizeRes, srcLenForSize);
}
aborting = true;
} else {
uint8_t* test = (uint8_t*)malloc((size_t)outNeeded + 1);
if (!test) {
Serial.println("Malloc failed test buffer, aborting.");
aborting = true;
} else {
unsigned long srcLen = (unsigned long)deflateLen;
unsigned long testLen = outNeeded;
int res = puff(test, &testLen, buffer + headerOffset, &srcLen);
if (res == 0) {
uint32_t trailerCrc =
(uint32_t)buffer[len - 8] |
((uint32_t)buffer[len - 7] << 8) |
((uint32_t)buffer[len - 6] << 16) |
((uint32_t)buffer[len - 5] << 24);
uint32_t trailerIsize =
(uint32_t)buffer[len - 4] |
((uint32_t)buffer[len - 3] << 8) |
((uint32_t)buffer[len - 2] << 16) |
((uint32_t)buffer[len - 1] << 24);
uint32_t calcCrc = crc32_update(0, test, (size_t)testLen);
uint32_t calcIsize = (uint32_t)testLen;
if (calcCrc != trailerCrc || calcIsize != trailerIsize) {
Serial.printf(
"GZIP CRC/ISIZE mismatch crc=%08lx/%08lx isize=%lu/%lu\n",
(unsigned long)calcCrc,
(unsigned long)trailerCrc,
(unsigned long)calcIsize,
(unsigned long)trailerIsize
);
free(test); free(test);
aborting = true;
} else {
test[testLen] = 0;
if (DEBUGING) {Serial.printf("Decompress OK! Size: %lu bytes\n", testLen);}
outData = test;
outLen = (size_t)testLen;
complete = true;
}
} else {
if (DEBUGING) {
Serial.printf("Decompress failed: res=%d out=%lu src=%lu\n", res, testLen, srcLen);
}
free(test);
aborting = true;
}
}
}
}
}
}
} }
// --- Added: Force-close connection only if aborted to avoid TCP RST storms --- // --- Added: Force-close connection in all cases to avoid stuck TCP sockets ---
if (aborting && stream) stream->stop(); // NEW: stop() only on abnormal termination if (stream) stream->stop();
http.end(); http.end();
free(buffer); free(buffer);
@@ -507,18 +150,6 @@ bool NetworkClient::httpGetGzip(const String& url, uint8_t*& outData, size_t& ou
bool NetworkClient::fetchAndDecompressJson(const String& url) { bool NetworkClient::fetchAndDecompressJson(const String& url) {
_valid = false; _valid = false;
_doc.clear();
_imageWidth = 0;
_imageHeight = 0;
_numberPixels = 0;
_pictureBase64 = nullptr;
_pictureBase64Len = 0;
if (_jsonRaw != nullptr) {
free(_jsonRaw);
_jsonRaw = nullptr;
_jsonRawLen = 0;
}
uint8_t* raw = nullptr; uint8_t* raw = nullptr;
size_t rawLen = 0; size_t rawLen = 0;
@@ -528,38 +159,15 @@ bool NetworkClient::fetchAndDecompressJson(const String& url) {
return false; return false;
} }
char* json = reinterpret_cast<char*>(raw); DeserializationError err = deserializeJson(_doc, raw, rawLen);
bool ok = true;
ok = findJsonIntField(json, rawLen, "number_pixels", _numberPixels) && ok;
ok = findJsonIntField(json, rawLen, "width", _imageWidth) && ok;
ok = findJsonIntField(json, rawLen, "height", _imageHeight) && ok;
ok = extractJsonStringInPlace(json, rawLen, "picture_base64", _pictureBase64, _pictureBase64Len) && ok;
if (!ok) {
Serial.println("JSON field extraction failed.");
free(raw); free(raw);
if (err) {
Serial.printf("JSON ERROR: %s\n", err.c_str());
return false; return false;
} }
if (_imageWidth <= 0 || _imageHeight <= 0 || _pictureBase64Len == 0) { if (DEBUG) {Serial.println("JSON OK!");}
Serial.printf("JSON invalid geometry/data w=%d h=%d b64=%u\n",
_imageWidth,
_imageHeight,
(unsigned int)_pictureBase64Len);
free(raw);
return false;
}
_jsonRaw = raw;
_jsonRawLen = rawLen;
if (DEBUGING) {
Serial.printf("JSON fields OK: num=%d w=%d h=%d b64=%u\n",
_numberPixels,
_imageWidth,
_imageHeight,
(unsigned int)_pictureBase64Len);
}
_valid = true; _valid = true;
return true; return true;
} }
@@ -568,26 +176,6 @@ JsonDocument& NetworkClient::json() {
return _doc; return _doc;
} }
int NetworkClient::imageWidth() const {
return _imageWidth;
}
int NetworkClient::imageHeight() const {
return _imageHeight;
}
int NetworkClient::numberPixels() const {
return _numberPixels;
}
const char* NetworkClient::pictureBase64() const {
return _pictureBase64;
}
size_t NetworkClient::pictureBase64Len() const {
return _pictureBase64Len;
}
bool NetworkClient::isValid() const { bool NetworkClient::isValid() const {
return _valid; return _valid;
} }

View File

@@ -3,7 +3,7 @@
#include <WiFi.h> #include <WiFi.h>
#include <HTTPClient.h> #include <HTTPClient.h>
#define DEBUGING true // Debug flag for NetworkClient for more live information #define DEBUG false // Debug flag for NetworkClient for more live information
#define READLIMIT 200000 // HTTP read limit in byte for gzip content (can be adjusted) #define READLIMIT 200000 // HTTP read limit in byte for gzip content (can be adjusted)
#define CONNECTIONTIMEOUT 3000 // Timeout in ms for HTTP connection #define CONNECTIONTIMEOUT 3000 // Timeout in ms for HTTP connection
#define TCPREADTIMEOUT 2000 // Timeout in ms for read HTTP client stack #define TCPREADTIMEOUT 2000 // Timeout in ms for read HTTP client stack
@@ -12,31 +12,16 @@
class NetworkClient { class NetworkClient {
public: public:
NetworkClient(size_t reserveSize = 0); NetworkClient(size_t reserveSize = 0);
~NetworkClient();
bool fetchAndDecompressJson(const String& url); bool fetchAndDecompressJson(const String& url);
JsonDocument& json(); JsonDocument& json();
int imageWidth() const;
int imageHeight() const;
int numberPixels() const;
const char* pictureBase64() const;
size_t pictureBase64Len() const;
bool isValid() const; bool isValid() const;
private: private:
DynamicJsonDocument _doc; DynamicJsonDocument _doc;
bool _valid; bool _valid;
uint8_t* _jsonRaw;
size_t _jsonRawLen;
int _imageWidth;
int _imageHeight;
int _numberPixels;
char* _pictureBase64;
size_t _pictureBase64Len;
int skipGzipHeader(const uint8_t* data, size_t len); int skipGzipHeader(const uint8_t* data, size_t len);
bool httpGetGzip(const String& url, uint8_t*& outData, size_t& outLen); bool httpGetGzip(const String& url, uint8_t*& outData, size_t& outLen);
static bool findJsonIntField(const char* json, size_t len, const char* key, int& outValue);
static bool extractJsonStringInPlace(char* json, size_t len, const char* key, char*& outValue, size_t& outLen);
}; };

View File

@@ -1,4 +1,3 @@
// SPDX-License-Identifier: GPL-2.0-or-later
#if defined BOARD_OBP60S3 || defined BOARD_OBP40S3 #if defined BOARD_OBP60S3 || defined BOARD_OBP40S3
#include <Arduino.h> #include <Arduino.h>
@@ -25,30 +24,37 @@
#include "fonts/Ubuntu_Bold20pt8b.h" #include "fonts/Ubuntu_Bold20pt8b.h"
#include "fonts/Ubuntu_Bold32pt8b.h" #include "fonts/Ubuntu_Bold32pt8b.h"
#include "fonts/Atari16px8b.h" // Key label font #include "fonts/Atari16px8b.h" // Key label font
#include "fonts/Atari6px8b.h" // Very small (6x6) font
#include "fonts/IBM8x8px.h" #include "fonts/IBM8x8px.h"
// E-Ink Display // E-Ink Display
#define GxEPD_WIDTH 400 // Display width // Definition for e-paper width an height refer OBP60Hardware.h
#define GxEPD_HEIGHT 300 // Display height
#ifdef DISPLAY_GDEW042T2 #ifdef DISPLAY_GDEW042T2
// Set display type and SPI pins for display // Set display type and SPI pins for display
GxEPD2_BW<GxEPD2_420, GxEPD2_420::HEIGHT> display(GxEPD2_420(OBP_SPI_CS, OBP_SPI_DC, OBP_SPI_RST, OBP_SPI_BUSY)); // GDEW042T2 400x300, UC8176 (IL0398) GxEPD2_BW<GxEPD2_420, GxEPD2_420::HEIGHT> display(GxEPD2_420(OBP_SPI_CS, OBP_SPI_DC, OBP_SPI_RST, OBP_SPI_BUSY)); // GDEW042T2 400x300, UC8176 (IL0398)
// Export display in new funktion
GxEPD2_BW<GxEPD2_420, GxEPD2_420::HEIGHT> & getdisplay(){return display;}
#endif #endif
#ifdef DISPLAY_GDEY042T81 #ifdef DISPLAY_GDEY042T81
// Set display type and SPI pins for display // Set display type and SPI pins for display
GxEPD2_BW<GxEPD2_420_GDEY042T81, GxEPD2_420_GDEY042T81::HEIGHT> display(GxEPD2_420_GDEY042T81(OBP_SPI_CS, OBP_SPI_DC, OBP_SPI_RST, OBP_SPI_BUSY)); // GDEW042T2 400x300, UC8176 (IL0398) GxEPD2_BW<GxEPD2_420_GDEY042T81, GxEPD2_420_GDEY042T81::HEIGHT> display(GxEPD2_420_GDEY042T81(OBP_SPI_CS, OBP_SPI_DC, OBP_SPI_RST, OBP_SPI_BUSY)); // GDEW042T2 400x300, UC8176 (IL0398)
// Export display in new funktion
GxEPD2_BW<GxEPD2_420_GDEY042T81, GxEPD2_420_GDEY042T81::HEIGHT> & getdisplay(){return display;}
#endif #endif
#ifdef DISPLAY_GYE042A87 #ifdef DISPLAY_GYE042A87
// Set display type and SPI pins for display // Set display type and SPI pins for display
GxEPD2_BW<GxEPD2_420_GYE042A87, GxEPD2_420_GYE042A87::HEIGHT> display(GxEPD2_420_GYE042A87(OBP_SPI_CS, OBP_SPI_DC, OBP_SPI_RST, OBP_SPI_BUSY)); // GDEW042T2 400x300, UC8176 (IL0398) GxEPD2_BW<GxEPD2_420_GYE042A87, GxEPD2_420_GYE042A87::HEIGHT> display(GxEPD2_420_GYE042A87(OBP_SPI_CS, OBP_SPI_DC, OBP_SPI_RST, OBP_SPI_BUSY)); // GDEW042T2 400x300, UC8176 (IL0398)
// Export display in new funktion
GxEPD2_BW<GxEPD2_420_GYE042A87, GxEPD2_420_GYE042A87::HEIGHT> & getdisplay(){return display;}
#endif #endif
#ifdef DISPLAY_SE0420NQ04 #ifdef DISPLAY_SE0420NQ04
// Set display type and SPI pins for display // Set display type and SPI pins for display
GxEPD2_BW<GxEPD2_420_SE0420NQ04, GxEPD2_420_SE0420NQ04::HEIGHT> display(GxEPD2_420_SE0420NQ04(OBP_SPI_CS, OBP_SPI_DC, OBP_SPI_RST, OBP_SPI_BUSY)); // GDEW042T2 400x300, UC8176 (IL0398) GxEPD2_BW<GxEPD2_420_SE0420NQ04, GxEPD2_420_SE0420NQ04::HEIGHT> display(GxEPD2_420_SE0420NQ04(OBP_SPI_CS, OBP_SPI_DC, OBP_SPI_RST, OBP_SPI_BUSY)); // GDEW042T2 400x300, UC8176 (IL0398)
// Export display in new funktion
GxEPD2_BW<GxEPD2_420_SE0420NQ04, GxEPD2_420_SE0420NQ04::HEIGHT> & getdisplay(){return display;}
#endif #endif
gxepd2display *epd = &display;
// Horter I2C moduls // Horter I2C moduls
PCF8574 pcf8574_Modul1(PCF8574_I2C_ADDR1); // First digital IO modul PCF8574 from Horter PCF8574 pcf8574_Modul1(PCF8574_I2C_ADDR1); // First digital IO modul PCF8574 from Horter
@@ -64,7 +70,6 @@ sdmmc_card_t *sdcard;
bool hasSDCard = false; bool hasSDCard = false;
// Global vars // Global vars
bool heartbeat = false; // Heartbeat indicator with two different states
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
bool statusBacklightLED = false;// Actual status of flash LED on/off bool statusBacklightLED = false;// Actual status of flash LED on/off
@@ -83,11 +88,11 @@ void hardwareInit(GwApi *api)
Wire.begin(); Wire.begin();
// Init PCF8574 digital outputs // Init PCF8574 digital outputs
Wire.setClock(I2C_SPEED_LOW); // Set I2C clock to low for external devices Wire.setClock(I2C_SPEED_LOW); // Set I2C clock on 10 kHz
if(pcf8574_Modul1.begin()){ // Initialize PCF8574 if(pcf8574_Modul1.begin()){ // Initialize PCF8574
pcf8574_Modul1.write8(255); // Clear all outputs (low activ) pcf8574_Modul1.write8(255); // Clear all outputs (low activ)
} }
Wire.setClock(I2C_SPEED); // Set I2C clock to normal Wire.setClock(I2C_SPEED); // Set I2C clock on 100 kHz
fram = Adafruit_FRAM_I2C(); fram = Adafruit_FRAM_I2C();
if (esp_reset_reason() == ESP_RST_POWERON) { if (esp_reset_reason() == ESP_RST_POWERON) {
// help initialize FRAM // help initialize FRAM
@@ -237,17 +242,17 @@ void deepSleep(CommonData &common){
setFlashLED(false); // Flash LED Off setFlashLED(false); // Flash LED Off
buzzer(TONE4, 20); // Buzzer tone 4kHz 20ms buzzer(TONE4, 20); // Buzzer tone 4kHz 20ms
// Shutdown EInk display // Shutdown EInk display
epd->setFullWindow(); // Set full Refresh getdisplay().setFullWindow(); // Set full Refresh
epd->fillScreen(common.bgcolor); // Clear screen getdisplay().fillScreen(common.bgcolor); // Clear screen
epd->setTextColor(common.fgcolor); getdisplay().setTextColor(common.fgcolor);
epd->setFont(&Ubuntu_Bold20pt8b); getdisplay().setFont(&Ubuntu_Bold20pt8b);
epd->setCursor(85, 150); getdisplay().setCursor(85, 150);
epd->print("Sleep Mode"); getdisplay().print("Sleep Mode");
epd->setFont(&Ubuntu_Bold8pt8b); getdisplay().setFont(&Ubuntu_Bold8pt8b);
epd->setCursor(65, 175); getdisplay().setCursor(65, 175);
epd->print("To wake up press key and wait 5s"); getdisplay().print("To wake up press key and wait 5s");
epd->nextPage(); // Update display contents getdisplay().nextPage(); // Update display contents
epd->powerOff(); // Display power off getdisplay().powerOff(); // Display power off
setPortPin(OBP_POWER_50, false); // Power off ePaper display setPortPin(OBP_POWER_50, false); // Power off ePaper display
// Stop system // Stop system
esp_deep_sleep_start(); // Deep Sleep with weakup via touch pin esp_deep_sleep_start(); // Deep Sleep with weakup via touch pin
@@ -261,18 +266,18 @@ void deepSleep(CommonData &common){
setPortPin(OBP_BACKLIGHT_LED, false); // Backlight Off setPortPin(OBP_BACKLIGHT_LED, false); // Backlight Off
setFlashLED(false); // Flash LED Off setFlashLED(false); // Flash LED Off
// Shutdown EInk display // Shutdown EInk display
epd->setFullWindow(); // Set full Refresh getdisplay().setFullWindow(); // Set full Refresh
//epd->setPartialWindow(0, 0, epd->width(), epd->height()); // Set partial update //getdisplay().setPartialWindow(0, 0, getdisplay().width(), getdisplay().height()); // Set partial update
epd->fillScreen(common.bgcolor); // Clear screen getdisplay().fillScreen(common.bgcolor); // Clear screen
epd->setTextColor(common.fgcolor); getdisplay().setTextColor(common.fgcolor);
epd->setFont(&Ubuntu_Bold20pt8b); getdisplay().setFont(&Ubuntu_Bold20pt8b);
epd->setCursor(85, 150); getdisplay().setCursor(85, 150);
epd->print("Sleep Mode"); getdisplay().print("Sleep Mode");
epd->setFont(&Ubuntu_Bold8pt8b); getdisplay().setFont(&Ubuntu_Bold8pt8b);
epd->setCursor(65, 175); getdisplay().setCursor(65, 175);
epd->print("To wake up press wheel and wait 5s"); getdisplay().print("To wake up press wheel and wait 5s");
epd->nextPage(); // Partial update getdisplay().nextPage(); // Partial update
epd->powerOff(); // Display power off getdisplay().powerOff(); // Display power off
setPortPin(OBP_POWER_EPD, false); // Power off ePaper display setPortPin(OBP_POWER_EPD, false); // Power off ePaper display
setPortPin(OBP_POWER_SD, false); // Power off SD card setPortPin(OBP_POWER_SD, false); // Power off SD card
// Stop system // Stop system
@@ -331,18 +336,18 @@ void stepsBacklightLED(uint brightness, const Color &color) {
uint actBrightness = 0; uint actBrightness = 0;
// Different brightness steps // Different brightness steps
if(step == 0){ if(step == 0){
actBrightness = brightness; // 100% from brightness actBrightness = brightness; // 100% from brightess
statusBacklightLED = true; statusBacklightLED = true;
} }
else if (step == 1) { if(step == 1){
actBrightness = brightness * 0.5; // 50% from brighntess actBrightness = brightness * 0.5; // 50% from brightess
statusBacklightLED = true; statusBacklightLED = true;
} }
else if (step == 2) { if(step == 2){
actBrightness = brightness * 0.2; // 20% from brightness actBrightness = brightness * 0.2; // 20% from brightess
statusBacklightLED = true; statusBacklightLED = true;
} }
else if (step == 3) { if(step == 3){
actBrightness = 0; // 0% actBrightness = 0; // 0%
statusBacklightLED = false; statusBacklightLED = false;
} }
@@ -353,9 +358,7 @@ void stepsBacklightLED(uint brightness, const Color &color) {
if(step == 4){ // Reset counter if(step == 4){ // Reset counter
step = 0; step = 0;
} }
if (ledTaskData == nullptr) { if (ledTaskData == nullptr) return;
return;
}
Color nv=setBrightness(statusBacklightLED?color:COLOR_BLACK,actBrightness); Color nv=setBrightness(statusBacklightLED?color:COLOR_BLACK,actBrightness);
LedInterface current=ledTaskData->getLedData(); LedInterface current=ledTaskData->getLedData();
current.setBacklight(nv); current.setBacklight(nv);
@@ -363,9 +366,7 @@ void stepsBacklightLED(uint brightness, const Color &color) {
} }
void setFlashLED(bool status){ void setFlashLED(bool status){
if (ledTaskData == nullptr) { if (ledTaskData == nullptr) return;
return;
}
Color c=status?COLOR_RED:COLOR_BLACK; Color c=status?COLOR_RED:COLOR_BLACK;
LedInterface current=ledTaskData->getLedData(); LedInterface current=ledTaskData->getLedData();
current.setFlash(c); current.setFlash(c);
@@ -409,29 +410,26 @@ void setBuzzerPower(uint power){
buzzerpower = power; buzzerpower = power;
} }
// Delete xdr prefix from string and optional limit length // Delete xdr prefix from string
String xdrDelete(String input, uint8_t maxlen) { String xdrDelete(String input){
if(input.substring(0,3) == "xdr"){ if(input.substring(0,3) == "xdr"){
input = input.substring(3, input.length()); input = input.substring(3, input.length());
} }
if (maxlen > 0) {
return input.substring(0, maxlen);
}
return input; return input;
} }
void fillPoly4(const std::vector<Point>& p4, uint16_t color) { void fillPoly4(const std::vector<Point>& p4, uint16_t color) {
epd->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[1].x, p4[1].y, p4[2].x, p4[2].y, color);
epd->fillTriangle(p4[0].x, p4[0].y, p4[2].x, p4[2].y, p4[3].x, p4[3].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) { void drawPoly(const std::vector<Point>& points, uint16_t color) {
size_t polysize = points.size(); size_t polysize = points.size();
for (size_t i = 0; i < polysize - 1; i++) { for (size_t i = 0; i < polysize - 1; i++) {
epd->drawLine(points[i].x, points[i].y, points[i+1].x, points[i+1].y, color); getdisplay().drawLine(points[i].x, points[i].y, points[i+1].x, points[i+1].y, color);
} }
// close path // close path
epd->drawLine(points[polysize-1].x, points[polysize-1].y, points[0].x, points[0].y, color); getdisplay().drawLine(points[polysize-1].x, points[polysize-1].y, points[0].x, points[0].y, color);
} }
// Split string into words, whitespace separated // Split string into words, whitespace separated
@@ -477,33 +475,57 @@ std::vector<String> wordwrap(String &line, uint16_t maxwidth) {
return lines; return lines;
} }
// Helper function to just get the exact width of a string
uint16_t getStringPixelWidth(const char* str, const GFXfont* font) {
int16_t minx = INT16_MAX;
int16_t maxx = INT16_MIN;
int16_t cursor_x = 0;
while (*str) {
char c = *str++;
if (c < font->first || c > font->last) {
continue;
}
GFXglyph* glyph = &font->glyph[c - font->first];
if (glyph->width > 0) {
int16_t glyphStart = cursor_x + glyph->xOffset;
int16_t glyphEnd = glyphStart + glyph->width;
if (glyphStart < minx) minx = glyphStart;
if (glyphEnd > maxx) maxx = glyphEnd;
}
cursor_x += glyph->xAdvance;
}
if (minx > maxx)
return 0;
return maxx - minx;
}
// Draw centered text // Draw centered text
void drawTextCenter(int16_t cx, int16_t cy, String text) { void drawTextCenter(int16_t cx, int16_t cy, String text) {
int16_t x1, y1; int16_t x1, y1;
uint16_t w, h; uint16_t w, h;
epd->getTextBounds(text, 0, 150, &x1, &y1, &w, &h); getdisplay().getTextBounds(text, 0, 150, &x1, &y1, &w, &h);
epd->setCursor(cx - w / 2, cy + h / 2); getdisplay().setCursor(cx - w / 2, cy + h / 2);
epd->print(text); getdisplay().print(text);
} }
// Draw centered botton with centered text // Draw centered botton with centered text
void drawButtonCenter(int16_t cx, int16_t cy, int8_t sx, int8_t sy, String text, uint16_t fg, uint16_t bg, bool inverted) { void drawButtonCenter(int16_t cx, int16_t cy, int8_t sx, int8_t sy, String text, uint16_t fg, uint16_t bg, bool inverted) {
int16_t x1, y1; int16_t x1, y1;
uint16_t w, h; uint16_t w, h;
epd->getTextBounds(text, 0, 150, &x1, &y1, &w, &h); uint16_t color;
int16_t cursorX = cx - (x1 + static_cast<int16_t>(w / 2));
int16_t cursorY = cy - (y1 + static_cast<int16_t>(h / 2)); getdisplay().getTextBounds(text, cx, cy, &x1, &y1, &w, &h); // Find text center
//epd->drawPixel(cx, cy, fg); // Debug pixel for center position getdisplay().setCursor(cx - w/2, cy + h/2); // Set cursor to center
//getdisplay().drawPixel(cx, cy, fg); // Debug pixel for center position
if (inverted) { if (inverted) {
epd->fillRoundRect(cx - sx / 2, cy - sy / 2, sx, sy, 5, fg); getdisplay().fillRoundRect(cx - sx / 2, cy - sy / 2, sx, sy, 5, fg); // Draw button
epd->setTextColor(bg); getdisplay().setTextColor(bg);
epd->setCursor(cursorX, cursorY); getdisplay().print(text); // Draw text
epd->print(text); }
} else { else{
epd->drawRoundRect(cx - sx / 2, cy - sy / 2, sx, sy, 5, fg); // Draw button getdisplay().drawRoundRect(cx - sx / 2, cy - sy / 2, sx, sy, 5, fg); // Draw button
epd->setTextColor(fg); getdisplay().setTextColor(fg);
epd->setCursor(cursorX, cursorY); getdisplay().print(text); // Draw text
epd->print(text);
} }
} }
@@ -511,42 +533,43 @@ void drawButtonCenter(int16_t cx, int16_t cy, int8_t sx, int8_t sy, String text,
void drawTextRalign(int16_t x, int16_t y, String text) { void drawTextRalign(int16_t x, int16_t y, String text) {
int16_t x1, y1; int16_t x1, y1;
uint16_t w, h; uint16_t w, h;
epd->getTextBounds(text, 0, 150, &x1, &y1, &w, &h); getdisplay().getTextBounds(text, 0, 150, &x1, &y1, &w, &h);
epd->setCursor(x - w, y); getdisplay().setCursor(x - w - 1, y); // '-1' required since some strings wrap around w/o it
epd->print(text); getdisplay().print(text);
} }
// Draw text inside box, normal or inverted // Draw text inside box, normal or inverted
void drawTextBoxed(Rect box, String text, uint16_t fg, uint16_t bg, bool inverted, bool border) { void drawTextBoxed(Rect box, String text, uint16_t fg, uint16_t bg, bool inverted, bool border) {
if (inverted) { if (inverted) {
epd->fillRect(box.x, box.y, box.w, box.h, fg); getdisplay().fillRect(box.x, box.y, box.w, box.h, fg);
epd->setTextColor(bg); getdisplay().setTextColor(bg);
} else { } else {
if (border) { if (border) {
epd->fillRect(box.x + 1, box.y + 1, box.w - 2, box.h - 2, bg); getdisplay().fillRect(box.x + 1, box.y + 1, box.w - 2, box.h - 2, bg);
epd->drawRect(box.x, box.y, box.w, box.h, fg); getdisplay().drawRect(box.x, box.y, box.w, box.h, fg);
} }
epd->setTextColor(fg); getdisplay().setTextColor(fg);
} }
uint16_t border_offset = box.h / 4; // 25% of box height uint16_t border_offset = box.h / 4; // 25% of box height
epd->setCursor(box.x + border_offset, box.y + box.h - border_offset); getdisplay().setCursor(box.x + border_offset, box.y + box.h - border_offset);
epd->print(text); getdisplay().print(text);
epd->setTextColor(fg); 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){
epd->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);
} }
// Show a triangle for trend direction low (x, y is the left edge) // Show a triangle for trend direction low (x, y is the left edge)
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){
epd->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);
} }
// Show header informations // Show header informations
void displayHeader(CommonData &commonData, bool symbolmode, GwApi::BoatValue *date, GwApi::BoatValue *time, GwApi::BoatValue *hdop){ void displayHeader(CommonData &commonData, GwApi::BoatValue *date, GwApi::BoatValue *time, GwApi::BoatValue *hdop){
static bool heartbeat = false;
static unsigned long usbRxOld = 0; static unsigned long usbRxOld = 0;
static unsigned long usbTxOld = 0; static unsigned long usbTxOld = 0;
static unsigned long serRxOld = 0; static unsigned long serRxOld = 0;
@@ -558,65 +581,31 @@ void displayHeader(CommonData &commonData, bool symbolmode, GwApi::BoatValue *da
static unsigned long n2kRxOld = 0; static unsigned long n2kRxOld = 0;
static unsigned long n2kTxOld = 0; static unsigned long n2kTxOld = 0;
uint16_t symbol_x = 2;
static const uint16_t symbol_offset = 20;
// TODO invert and get rid of the if
if(commonData.config->getBool(commonData.config->statusLine) == true){ if(commonData.config->getBool(commonData.config->statusLine) == true){
// Show status info // Show status info
epd->setTextColor(commonData.fgcolor); getdisplay().setTextColor(commonData.fgcolor);
epd->setFont(&Ubuntu_Bold8pt8b); getdisplay().setFont(&Ubuntu_Bold8pt8b);
epd->setCursor(0, 15); getdisplay().setCursor(0, 15);
if(commonData.status.wifiApOn){ if(commonData.status.wifiApOn){
if (symbolmode) { getdisplay().print(" AP ");
epd->drawXBitmap(symbol_x, 1, iconmap["AP"], icon_width, icon_height, commonData.fgcolor);
symbol_x += symbol_offset;
} else {
epd->print(" AP ");
}
} }
// If receive new telegram data then display bus name // If receive new telegram data then display bus name
if(commonData.status.tcpClRx != tcpClRxOld || commonData.status.tcpClTx != tcpClTxOld || commonData.status.tcpSerRx != tcpSerRxOld || commonData.status.tcpSerTx != tcpSerTxOld){ if(commonData.status.tcpClRx != tcpClRxOld || commonData.status.tcpClTx != tcpClTxOld || commonData.status.tcpSerRx != tcpSerRxOld || commonData.status.tcpSerTx != tcpSerTxOld){
if (symbolmode) { getdisplay().print("TCP ");
epd->drawXBitmap(symbol_x, 1, iconmap["TCP"], icon_width, icon_height, commonData.fgcolor);
symbol_x += symbol_offset;
} else {
epd->print("TCP ");
}
} }
if(commonData.status.n2kRx != n2kRxOld || commonData.status.n2kTx != n2kTxOld){ if(commonData.status.n2kRx != n2kRxOld || commonData.status.n2kTx != n2kTxOld){
if (symbolmode) { getdisplay().print("N2K ");
epd->drawXBitmap(symbol_x, 1, iconmap["N2K"], icon_width, icon_height, commonData.fgcolor);
symbol_x += symbol_offset;
} else {
epd->print("N2K ");
}
} }
if(commonData.status.serRx != serRxOld || commonData.status.serTx != serTxOld){ if(commonData.status.serRx != serRxOld || commonData.status.serTx != serTxOld){
if (symbolmode) { getdisplay().print("183 ");
epd->drawXBitmap(symbol_x, 1, iconmap["0183"], icon_width, icon_height, commonData.fgcolor);
symbol_x += symbol_offset;
} else {
epd->print("183 ");
}
} }
if(commonData.status.usbRx != usbRxOld || commonData.status.usbTx != usbTxOld){ if(commonData.status.usbRx != usbRxOld || commonData.status.usbTx != usbTxOld){
if (symbolmode) { getdisplay().print("USB ");
epd->drawXBitmap(symbol_x, 1, iconmap["USB"], icon_width, icon_height, commonData.fgcolor);
symbol_x += symbol_offset;
} else {
epd->print("USB ");
} }
} double gpshdop = formatValue(hdop, commonData).value;
double gpshdop = commonData.fmt->formatValue(hdop, commonData).value;
if(commonData.config->getString(commonData.config->useGPS) != "off" && gpshdop <= commonData.config->getInt(commonData.config->hdopAccuracy) && hdop->valid == true){ if(commonData.config->getString(commonData.config->useGPS) != "off" && gpshdop <= commonData.config->getInt(commonData.config->hdopAccuracy) && hdop->valid == true){
if (symbolmode) { getdisplay().print("GPS");
epd->drawXBitmap(symbol_x, 1, iconmap["GPS"], icon_width, icon_height, commonData.fgcolor);
symbol_x += symbol_offset;
} else {
epd->print("GPS");
}
} }
// Save old telegram counter // Save old telegram counter
tcpClRxOld = commonData.status.tcpClRx; tcpClRxOld = commonData.status.tcpClRx;
@@ -633,26 +622,26 @@ void displayHeader(CommonData &commonData, bool symbolmode, GwApi::BoatValue *da
#ifdef HARDWARE_V21 #ifdef HARDWARE_V21
// Display key lock status // Display key lock status
if (commonData.keylock) { if (commonData.keylock) {
epd->drawXBitmap(170, 1, lock_bits, icon_width, icon_height, commonData.fgcolor); getdisplay().drawXBitmap(170, 1, lock_bits, icon_width, icon_height, commonData.fgcolor);
} else { } else {
epd->drawXBitmap(166, 1, swipe_bits, swipe_width, swipe_height, commonData.fgcolor); getdisplay().drawXBitmap(166, 1, swipe_bits, swipe_width, swipe_height, commonData.fgcolor);
} }
#endif #endif
#ifdef LIPO_ACCU_1200 #ifdef LIPO_ACCU_1200
if (commonData.data.BatteryChargeStatus == 1) { if (commonData.data.BatteryChargeStatus == 1) {
epd->drawXBitmap(170, 1, battery_loading_bits, battery_width, battery_height, commonData.fgcolor); getdisplay().drawXBitmap(170, 1, battery_loading_bits, battery_width, battery_height, commonData.fgcolor);
} else { } else {
#ifdef VOLTAGE_SENSOR #ifdef VOLTAGE_SENSOR
if (commonData.data.batteryLevelLiPo < 10) { if (commonData.data.batteryLevelLiPo < 10) {
epd->drawXBitmap(170, 1, battery_0_bits, battery_width, battery_height, commonData.fgcolor); getdisplay().drawXBitmap(170, 1, battery_0_bits, battery_width, battery_height, commonData.fgcolor);
} else if (commonData.data.batteryLevelLiPo < 25) { } else if (commonData.data.batteryLevelLiPo < 25) {
epd->drawXBitmap(170, 1, battery_25_bits, battery_width, battery_height, commonData.fgcolor); getdisplay().drawXBitmap(170, 1, battery_25_bits, battery_width, battery_height, commonData.fgcolor);
} else if (commonData.data.batteryLevelLiPo < 50) { } else if (commonData.data.batteryLevelLiPo < 50) {
epd->drawXBitmap(170, 1, battery_50_bits, battery_width, battery_height, commonData.fgcolor); getdisplay().drawXBitmap(170, 1, battery_50_bits, battery_width, battery_height, commonData.fgcolor);
} else if (commonData.data.batteryLevelLiPo < 75) { } else if (commonData.data.batteryLevelLiPo < 75) {
epd->drawXBitmap(170, 1, battery_75_bits, battery_width, battery_height, commonData.fgcolor); getdisplay().drawXBitmap(170, 1, battery_75_bits, battery_width, battery_height, commonData.fgcolor);
} else { } else {
epd->drawXBitmap(170, 1, battery_100_bits, battery_width, battery_height, commonData.fgcolor); getdisplay().drawXBitmap(170, 1, battery_100_bits, battery_width, battery_height, commonData.fgcolor);
} }
#endif // VOLTAGE_SENSOR #endif // VOLTAGE_SENSOR
} }
@@ -660,33 +649,32 @@ void displayHeader(CommonData &commonData, bool symbolmode, GwApi::BoatValue *da
// Heartbeat as page number // Heartbeat as page number
if (heartbeat) { if (heartbeat) {
epd->setTextColor(commonData.bgcolor); getdisplay().setTextColor(commonData.bgcolor);
epd->fillRect(201, 0, 23, 19, commonData.fgcolor); getdisplay().fillRect(201, 0, 23, 19, commonData.fgcolor);
} else { } else {
epd->setTextColor(commonData.fgcolor); getdisplay().setTextColor(commonData.fgcolor);
epd->drawRect(201, 0, 23, 19, commonData.fgcolor); getdisplay().drawRect(201, 0, 23, 19, commonData.fgcolor);
} }
epd->setFont(&Ubuntu_Bold8pt8b); getdisplay().setFont(&Ubuntu_Bold8pt8b);
drawTextCenter(211, 9, String(commonData.data.actpage)); drawTextCenter(211, 9, String(commonData.data.actpage));
heartbeat = !heartbeat; heartbeat = !heartbeat;
// Date and time // Date and time
fmtDate fmttype = commonData.fmt->getDateFormat(commonData.config->getString(commonData.config->dateFormat)); String fmttype = commonData.config->getString(commonData.config->dateFormat);
String timesource = commonData.config->getString(commonData.config->timeSource); String timesource = commonData.config->getString(commonData.config->timeSource);
double tz = commonData.config->getString(commonData.config->timeZone).toDouble(); getdisplay().setTextColor(commonData.fgcolor);
epd->setTextColor(commonData.fgcolor); getdisplay().setFont(&Ubuntu_Bold8pt8b);
epd->setFont(&Ubuntu_Bold8pt8b); getdisplay().setCursor(230, 15);
epd->setCursor(230, 15);
if (timesource == "RTC" or timesource == "iRTC") { if (timesource == "RTC" or timesource == "iRTC") {
// TODO take DST into account // TODO take DST into account
if (commonData.data.rtcValid) { if (commonData.data.rtcValid) {
time_t tv = mktime(&commonData.data.rtcTime) + (int)(tz * 3600); time_t tv = mktime(&commonData.data.rtcTime) + (int)(commonData.tz * 3600);
struct tm *local_tm = localtime(&tv); struct tm *local_tm = localtime(&tv);
epd->print(formatTime(fmtTime::MMHH, local_tm->tm_hour, local_tm->tm_min, 0)); getdisplay().print(formatTime('m', local_tm->tm_hour, local_tm->tm_min, 0));
epd->print(" "); getdisplay().print(" ");
epd->print(formatDate(fmttype, local_tm->tm_year + 1900, local_tm->tm_mon + 1, local_tm->tm_mday)); getdisplay().print(formatDate(fmttype, local_tm->tm_year + 1900, local_tm->tm_mon + 1, local_tm->tm_mday));
epd->print(" "); getdisplay().print(" ");
epd->print(tz == 0 ? "UTC" : "LOT"); getdisplay().print(commonData.tz == 0 ? "UTC" : "LOT");
} else { } else {
drawTextRalign(396, 15, "RTC invalid"); drawTextRalign(396, 15, "RTC invalid");
} }
@@ -694,18 +682,18 @@ void displayHeader(CommonData &commonData, bool symbolmode, GwApi::BoatValue *da
else if (timesource == "GPS") { else if (timesource == "GPS") {
// Show date and time if date present // Show date and time if date present
if(date->valid == true){ if(date->valid == true){
String acttime = commonData.fmt->formatValue(time, commonData).svalue; String acttime = formatValue(time, commonData).svalue;
acttime = acttime.substring(0, 5); acttime = acttime.substring(0, 5);
String actdate = commonData.fmt->formatValue(date, commonData).svalue; String actdate = formatValue(date, commonData).svalue;
epd->print(acttime); getdisplay().print(acttime);
epd->print(" "); getdisplay().print(" ");
epd->print(actdate); getdisplay().print(actdate);
epd->print(" "); getdisplay().print(" ");
epd->print(tz == 0 ? "UTC" : "LOT"); getdisplay().print(commonData.tz == 0 ? "UTC" : "LOT");
} }
else{ else{
if(commonData.config->getBool(commonData.config->useSimuData) == true){ if(commonData.config->getBool(commonData.config->useSimuData) == true){
epd->print("12:00 01.01.2024 LOT"); getdisplay().print("12:00 01.01.2024 LOT");
} }
else{ else{
drawTextRalign(396, 15, "No GPS data"); drawTextRalign(396, 15, "No GPS data");
@@ -713,15 +701,15 @@ void displayHeader(CommonData &commonData, bool symbolmode, GwApi::BoatValue *da
} }
} // timesource == "GPS" } // timesource == "GPS"
else { else {
epd->print("No time source"); getdisplay().print("No time source");
} }
} }
} }
void displayFooter(CommonData &commonData) { void displayFooter(CommonData &commonData) {
epd->setFont(&Atari16px); getdisplay().setFont(&Atari16px);
epd->setTextColor(commonData.fgcolor); getdisplay().setTextColor(commonData.fgcolor);
#ifdef HARDWARE_V21 #ifdef HARDWARE_V21
// Frame around key icon area // Frame around key icon area
@@ -730,14 +718,14 @@ void displayFooter(CommonData &commonData) {
const uint16_t top = 280; const uint16_t top = 280;
const uint16_t bottom = 299; const uint16_t bottom = 299;
// horizontal stub lines // horizontal stub lines
epd->drawLine(commonData.keydata[0].x, top, commonData.keydata[0].x+10, top, commonData.fgcolor); getdisplay().drawLine(commonData.keydata[0].x, top, commonData.keydata[0].x+10, top, commonData.fgcolor);
for (int i = 1; i <= 5; i++) { for (int i = 1; i <= 5; i++) {
epd->drawLine(commonData.keydata[i].x-10, top, commonData.keydata[i].x+10, top, commonData.fgcolor); getdisplay().drawLine(commonData.keydata[i].x-10, top, commonData.keydata[i].x+10, top, commonData.fgcolor);
} }
epd->drawLine(commonData.keydata[5].x + commonData.keydata[5].w - 10, top, commonData.keydata[5].x + commonData.keydata[5].w + 1, 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 // vertical key separators
for (int i = 0; i <= 4; i++) { for (int i = 0; i <= 4; i++) {
epd->drawLine(commonData.keydata[i].x + commonData.keydata[i].w, top, commonData.keydata[i].x + commonData.keydata[i].w, bottom, commonData.fgcolor); 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++) { for (int i = 0; i < 6; i++) {
uint16_t x, y; uint16_t x, y;
@@ -748,7 +736,7 @@ void displayFooter(CommonData &commonData) {
if (iconmap.find(icon_name) != iconmap.end()) { if (iconmap.find(icon_name) != iconmap.end()) {
x = commonData.keydata[i].x + (commonData.keydata[i].w - icon_width) / 2; x = commonData.keydata[i].x + (commonData.keydata[i].w - icon_width) / 2;
y = commonData.keydata[i].y + (commonData.keydata[i].h - icon_height) / 2; y = commonData.keydata[i].y + (commonData.keydata[i].h - icon_height) / 2;
epd->drawXBitmap(x, y, iconmap[icon_name], icon_width, icon_height, commonData.fgcolor); getdisplay().drawXBitmap(x, y, iconmap[icon_name], icon_width, icon_height, commonData.fgcolor);
} else { } else {
// icon is missing, use name instead // icon is missing, use name instead
x = commonData.keydata[i].x + commonData.keydata[i].w / 2; x = commonData.keydata[i].x + commonData.keydata[i].w / 2;
@@ -763,8 +751,8 @@ void displayFooter(CommonData &commonData) {
} }
} }
} else { } else {
epd->setCursor(65, 295); getdisplay().setCursor(65, 295);
epd->print("Press 1 and 6 fast to unlock keys"); getdisplay().print("Press 1 and 6 fast to unlock keys");
} }
#endif #endif
#ifdef BOARD_OBP40S3 #ifdef BOARD_OBP40S3
@@ -775,21 +763,21 @@ void displayFooter(CommonData &commonData) {
uint16_t x0 = (GxEPD_WIDTH - w) / 2 + r * 2; uint16_t x0 = (GxEPD_WIDTH - w) / 2 + r * 2;
for (int i = 0; i < commonData.data.maxpage; i++) { for (int i = 0; i < commonData.data.maxpage; i++) {
if (i == (commonData.data.actpage - 1)) { if (i == (commonData.data.actpage - 1)) {
epd->fillCircle(x0 + i * (r * 2 + space), 290, r, commonData.fgcolor); getdisplay().fillCircle(x0 + i * (r * 2 + space), 290, r, commonData.fgcolor);
} else { } else {
epd->drawCircle(x0 + i * (r * 2 + space), 290, r, commonData.fgcolor); getdisplay().drawCircle(x0 + i * (r * 2 + space), 290, r, commonData.fgcolor);
} }
} }
// key indicators // key indicators
// left side = top key "menu" // left side = top key "menu"
epd->drawLine(0, 280, 10, 280, commonData.fgcolor); getdisplay().drawLine(0, 280, 10, 280, commonData.fgcolor);
epd->drawLine(55, 280, 65, 280, commonData.fgcolor); getdisplay().drawLine(55, 280, 65, 280, commonData.fgcolor);
epd->drawLine(65, 280, 65, 299, commonData.fgcolor); getdisplay().drawLine(65, 280, 65, 299, commonData.fgcolor);
drawTextCenter(33, 291, commonData.keydata[0].label); drawTextCenter(33, 291, commonData.keydata[0].label);
// right side = bottom key "exit" // right side = bottom key "exit"
epd->drawLine(390, 280, 399, 280, commonData.fgcolor); getdisplay().drawLine(390, 280, 399, 280, commonData.fgcolor);
epd->drawLine(335, 280, 345, 280, commonData.fgcolor); getdisplay().drawLine(335, 280, 345, 280, commonData.fgcolor);
epd->drawLine(335, 280, 335, 399, commonData.fgcolor); getdisplay().drawLine(335, 280, 335, 399, commonData.fgcolor);
drawTextCenter(366, 291, commonData.keydata[1].label); drawTextCenter(366, 291, commonData.keydata[1].label);
#endif #endif
} }
@@ -802,31 +790,31 @@ void displayAlarm(CommonData &commonData) {
const uint16_t w = 300; const uint16_t w = 300;
const uint16_t h = 150; const uint16_t h = 150;
epd->setFont(&Atari16px); getdisplay().setFont(&Atari16px);
epd->setTextColor(commonData.fgcolor); getdisplay().setTextColor(commonData.fgcolor);
// overlay // overlay
epd->drawRect(x, y, w, h, commonData.fgcolor); getdisplay().drawRect(x, y, w, h, commonData.fgcolor);
epd->fillRect(x + 1, y + 1, w - 1, h - 1, commonData.bgcolor); getdisplay().fillRect(x + 1, y + 1, w - 1, h - 1, commonData.bgcolor);
epd->drawRect(x + 3, y + 3, w - 5, h - 5, commonData.fgcolor); getdisplay().drawRect(x + 3, y + 3, w - 5, h - 5, commonData.fgcolor);
// exclamation icon in left top corner // exclamation icon in left top corner
epd->drawXBitmap(x + 16, y + 16, exclamation_bits, exclamation_width, exclamation_height, commonData.fgcolor); getdisplay().drawXBitmap(x + 16, y + 16, exclamation_bits, exclamation_width, exclamation_height, commonData.fgcolor);
// title // title
epd->setCursor(x + 64, y + 30); getdisplay().setCursor(x + 64, y + 30);
epd->print("A L A R M"); getdisplay().print("A L A R M");
epd->setCursor(x + 64, y + 48); getdisplay().setCursor(x + 64, y + 48);
epd->print("#" + commonData.alarm.id); getdisplay().print("#" + commonData.alarm.id);
epd->print(" from "); getdisplay().print(" from ");
epd->print(commonData.alarm.source); getdisplay().print(commonData.alarm.source);
// message, but maximum 4 lines // message, but maximum 4 lines
std::vector<String> lines = wordwrap (commonData.alarm.message, w - 16 - 8 / 8); std::vector<String> lines = wordwrap (commonData.alarm.message, w - 16 - 8 / 8);
int n = 0; int n = 0;
for (const auto& l : lines) { for (const auto& l : lines) {
epd->setCursor(x + 16, y + 80 + n); getdisplay().setCursor(x + 16, y + 80 + n);
epd->print(l); getdisplay().print(l);
n += 16; n += 16;
if (n > 64) { if (n > 64) {
break; break;
@@ -919,20 +907,20 @@ void batteryGraphic(uint x, uint y, float percent, int pcolor, int bcolor){
} }
// Battery corpus 100x80 with fill level // Battery corpus 100x80 with fill level
int level = int((100.0 - percent) * (80-(2*t)) / 100.0); int level = int((100.0 - percent) * (80-(2*t)) / 100.0);
epd->fillRect(xb, yb, 100, 80, pcolor); getdisplay().fillRect(xb, yb, 100, 80, pcolor);
if(percent < 99){ if(percent < 99){
epd->fillRect(xb+t, yb+t, 100-(2*t), level, bcolor); getdisplay().fillRect(xb+t, yb+t, 100-(2*t), level, bcolor);
} }
// Plus pol 20x15 // Plus pol 20x15
int xp = xb + 20; int xp = xb + 20;
int yp = yb - 15 + t; int yp = yb - 15 + t;
epd->fillRect(xp, yp, 20, 15, pcolor); getdisplay().fillRect(xp, yp, 20, 15, pcolor);
epd->fillRect(xp+t, yp+t, 20-(2*t), 15-(2*t), bcolor); getdisplay().fillRect(xp+t, yp+t, 20-(2*t), 15-(2*t), bcolor);
// Minus pol 20x15 // Minus pol 20x15
int xm = xb + 60; int xm = xb + 60;
int ym = yb -15 + t; int ym = yb -15 + t;
epd->fillRect(xm, ym, 20, 15, pcolor); getdisplay().fillRect(xm, ym, 20, 15, pcolor);
epd->fillRect(xm+t, ym+t, 20-(2*t), 15-(2*t), bcolor); getdisplay().fillRect(xm+t, ym+t, 20-(2*t), 15-(2*t), bcolor);
} }
// Solar graphic with fill level // Solar graphic with fill level
@@ -944,17 +932,17 @@ void solarGraphic(uint x, uint y, int pcolor, int bcolor){
int percent = 0; int percent = 0;
// Solar corpus 100x80 // Solar corpus 100x80
int level = int((100.0 - percent) * (80-(2*t)) / 100.0); int level = int((100.0 - percent) * (80-(2*t)) / 100.0);
epd->fillRect(xb, yb, 100, 80, pcolor); getdisplay().fillRect(xb, yb, 100, 80, pcolor);
if(percent < 99){ if(percent < 99){
epd->fillRect(xb+t, yb+t, 100-(2*t), level, bcolor); getdisplay().fillRect(xb+t, yb+t, 100-(2*t), level, bcolor);
} }
// Draw horizontel lines // Draw horizontel lines
epd->fillRect(xb, yb+28-t, 100, t, pcolor); getdisplay().fillRect(xb, yb+28-t, 100, t, pcolor);
epd->fillRect(xb, yb+54-t, 100, t, pcolor); getdisplay().fillRect(xb, yb+54-t, 100, t, pcolor);
// Draw vertical lines // Draw vertical lines
epd->fillRect(xb+19+t, yb, t, 80, pcolor); getdisplay().fillRect(xb+19+t, yb, t, 80, pcolor);
epd->fillRect(xb+39+2*t, yb, t, 80, pcolor); getdisplay().fillRect(xb+39+2*t, yb, t, 80, pcolor);
epd->fillRect(xb+59+3*t, yb, t, 80, pcolor); getdisplay().fillRect(xb+59+3*t, yb, t, 80, pcolor);
} }
@@ -966,86 +954,13 @@ void generatorGraphic(uint x, uint y, int pcolor, int bcolor){
int t = 4; // Line thickness int t = 4; // Line thickness
// Generator corpus with radius 45 // Generator corpus with radius 45
epd->fillCircle(xb, yb, 45, pcolor); getdisplay().fillCircle(xb, yb, 45, pcolor);
epd->fillCircle(xb, yb, 41, bcolor); getdisplay().fillCircle(xb, yb, 41, bcolor);
// Insert G // Insert G
epd->setTextColor(pcolor); getdisplay().setTextColor(pcolor);
epd->setFont(&Ubuntu_Bold32pt8b); getdisplay().setFont(&Ubuntu_Bold32pt8b);
epd->setCursor(xb-22, yb+20); getdisplay().setCursor(xb-22, yb+20);
epd->print("G"); getdisplay().print("G");
}
// Display rudder position as horizontal bargraph with configurable +/- range (degrees)
void displayRudderPosition(int rudderPosition, uint8_t rangeDeg, uint16_t cx, uint16_t cy, uint16_t fg, uint16_t bg) {
const int w = 360;
const int h = 20;
const int t = 3; // Line thickness
const int halfw = w/2;
const int halfh = h/2;
// Calculate top-left of bar (cx,cy are center of 0°)
int left = int(cx) - halfw;
int top = int(cy) - halfh;
// clamp provided range to allowed bounds [10,45]
if (rangeDeg < 10) {
rangeDeg = 10;
} else if (rangeDeg > 45) {
rangeDeg = 45;
}
// Pixels per degree for +/-rangeDeg -> total span = 2*rangeDeg
const float pxPerDeg = float(w) / (2.0f * float(rangeDeg));
// Draw outer border (thickness t)
for (int i = 0; i < t; i++) {
epd->drawRect(left + i, top + i, w - 2 * i, h - 2 * i, fg);
}
// Fill inner area with background
epd->fillRect(left + t, top + t, w - 2 * t, h - 2 * t, bg);
// Draw center line
epd->drawRect(cx - 1, top + 1, 3 , h - 2, fg);
// Clamp rudder position to -rangeDeg..rangeDeg
if (rudderPosition > (int)rangeDeg) {
rudderPosition = (int)rangeDeg;
} else if (rudderPosition < -((int)rangeDeg)) {
rudderPosition = -((int)rangeDeg);
}
// Compute fill width in pixels
int fillPx = int(round(rudderPosition * pxPerDeg)); // positive -> right
// Fill area from center to position (if non-zero)
int centerx = cx;
int innerTop = top + t;
int innerH = h - 2 * t;
if (fillPx > 0) {
// Right side
epd->fillRect(centerx, innerTop, fillPx, innerH, fg);
} else if (fillPx < 0) {
// Left side
epd->fillRect(centerx + fillPx, innerTop, -fillPx, innerH, fg);
}
// Draw tick marks every 5° and labels outside the bar
epd->setTextColor(fg);
epd->setFont(&Ubuntu_Bold8pt8b);
for (int angle = -((int)rangeDeg); angle <= (int)rangeDeg; angle += 5) {
int xpos = int(round(centerx + angle * pxPerDeg));
// Vertical tick inside bar
epd->drawLine(xpos, top, xpos, top + h + 2, fg);
// Label outside: below the bar
String lbl = String(angle);
int16_t bx, by;
uint16_t bw, bh;
epd->getTextBounds(lbl, 0, 0, &bx, &by, &bw, &bh);
int16_t tx = xpos - bw/2;
int16_t ty = top + h + bh + 5; // A little spacing
epd->setCursor(tx, ty);
epd->print(lbl);
}
} }
// Function to handle HTTP image request // Function to handle HTTP image request
@@ -1059,7 +974,7 @@ void doImageRequest(GwApi *api, int *pageno, const PageStruct pages[MAX_PAGE_NUM
logger->logDebug(GwLog::LOG,"handle image request [%s]: %s", imgformat, filename); logger->logDebug(GwLog::LOG,"handle image request [%s]: %s", imgformat, filename);
uint8_t *fb = epd->getBuffer(); // EPD framebuffer uint8_t *fb = getdisplay().getBuffer(); // EPD framebuffer
std::vector<uint8_t> imageBuffer; // image in webserver transferbuffer std::vector<uint8_t> imageBuffer; // image in webserver transferbuffer
String mimetype; String mimetype;
@@ -1082,11 +997,37 @@ void doImageRequest(GwApi *api, int *pageno, const PageStruct pages[MAX_PAGE_NUM
createPBM(fb, &imageBuffer, GxEPD_WIDTH, GxEPD_HEIGHT); createPBM(fb, &imageBuffer, GxEPD_WIDTH, GxEPD_HEIGHT);
} }
AsyncWebServerResponse *response = request->beginResponse(200, mimetype, (const uint8_t*)imageBuffer.data(), imageBuffer.size()); AsyncWebServerResponse *response = request->beginResponse_P(200, mimetype, (const uint8_t*)imageBuffer.data(), imageBuffer.size());
response->addHeader("Content-Disposition", "inline; filename=" + filename); response->addHeader("Content-Disposition", "inline; filename=" + filename);
request->send(response); request->send(response);
imageBuffer.clear(); imageBuffer.clear();
} }
// Calculate the distance between two Geo coordinates
double distanceBetweenCoordinates(double lat1, double lon1, double lat2, double lon2) {
// Grad → Radiant
double lat1Rad = lat1 * DEG_TO_RAD;
double lon1Rad = lon1 * DEG_TO_RAD;
double lat2Rad = lat2 * DEG_TO_RAD;
double lon2Rad = lon2 * DEG_TO_RAD;
// Differenzen
double dLat = lat2Rad - lat1Rad;
double dLon = lon2Rad - lon1Rad;
// Haversine-Formel
double a = sin(dLat / 2.0) * sin(dLat / 2.0) +
cos(lat1Rad) * cos(lat2Rad) *
sin(dLon / 2.0) * sin(dLon / 2.0);
double c = 2.0 * atan2(sqrt(a), sqrt(1.0 - a));
// Abstand in Metern
return double(EARTH_RADIUS) * c;
}
#endif #endif

View File

@@ -1,14 +1,13 @@
// SPDX-License-Identifier: GPL-2.0-or-later
#ifndef _OBP60EXTENSIONPORT_H #ifndef _OBP60EXTENSIONPORT_H
#define _OBP60EXTENSIONPORT_H #define _OBP60EXTENSIONPORT_H
#include <Arduino.h> #include <Arduino.h>
#include "OBP60Hardware.h" #include "OBP60Hardware.h"
#include "OBP60Formatter.h"
#include "LedSpiTask.h" #include "LedSpiTask.h"
#include "Graphics.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 #include <Adafruit_FRAM_I2C.h> // I2C FRAM
#include <math.h>
#ifdef BOARD_OBP40S3 #ifdef BOARD_OBP40S3
#include "esp_vfs_fat.h" #include "esp_vfs_fat.h"
@@ -32,6 +31,9 @@
#define FRAM_BAROGRAPH_START 0x0400 #define FRAM_BAROGRAPH_START 0x0400
#define FRAM_BAROGRAPH_END 0x13FF #define FRAM_BAROGRAPH_END 0x13FF
#define PI 3.1415926535897932384626433832795
#define EARTH_RADIUS 6371000.0
extern Adafruit_FRAM_I2C fram; extern Adafruit_FRAM_I2C fram;
extern bool hasFRAM; extern bool hasFRAM;
extern bool hasSDCard; extern bool hasSDCard;
@@ -39,8 +41,6 @@ extern bool hasSDCard;
extern sdmmc_card_t *sdcard; extern sdmmc_card_t *sdcard;
#endif #endif
extern bool heartbeat;
// Fonts declarations for display (#includes see OBP60Extensions.cpp) // 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;
@@ -55,22 +55,24 @@ extern const GFXfont Ubuntu_Bold16pt8b;
extern const GFXfont Ubuntu_Bold20pt8b; extern const GFXfont Ubuntu_Bold20pt8b;
extern const GFXfont Ubuntu_Bold32pt8b; extern const GFXfont Ubuntu_Bold32pt8b;
extern const GFXfont Atari16px; extern const GFXfont Atari16px;
extern const GFXfont Atari6px; extern const GFXfont IBM8x8px;
// Global functions // Global functions
#ifdef DISPLAY_GDEW042T2 #ifdef DISPLAY_GDEW042T2
typedef GxEPD2_BW<GxEPD2_420, GxEPD2_420::HEIGHT> gxepd2display; GxEPD2_BW<GxEPD2_420, GxEPD2_420::HEIGHT> & getdisplay();
#endif #endif
#ifdef DISPLAY_GDEY042T81 #ifdef DISPLAY_GDEY042T81
typedef GxEPD2_BW<GxEPD2_420_GDEY042T81, GxEPD2_420_GDEY042T81::HEIGHT> gxepd2display; GxEPD2_BW<GxEPD2_420_GDEY042T81, GxEPD2_420_GDEY042T81::HEIGHT> & getdisplay();
#endif #endif
#ifdef DISPLAY_GYE042A87 #ifdef DISPLAY_GYE042A87
typedef GxEPD2_BW<GxEPD2_420_GYE042A87, GxEPD2_420_GYE042A87::HEIGHT> gxepd2display; GxEPD2_BW<GxEPD2_420_GYE042A87, GxEPD2_420_GYE042A87::HEIGHT> & getdisplay();
#endif #endif
#ifdef DISPLAY_SE0420NQ04 #ifdef DISPLAY_SE0420NQ04
typedef GxEPD2_BW<GxEPD2_420_SE0420NQ04, GxEPD2_420_SE0420NQ04::HEIGHT> gxepd2display; GxEPD2_BW<GxEPD2_420_SE0420NQ04, GxEPD2_420_SE0420NQ04::HEIGHT> & getdisplay();
#endif #endif
extern gxepd2display *epd;
// Page display return values // Page display return values
#define PAGE_OK 0 // all ok, do nothing #define PAGE_OK 0 // all ok, do nothing
@@ -104,7 +106,7 @@ void setBlinkingLED(bool on); // Set blinking flash LED active
void buzzer(uint frequency, uint duration); // Buzzer function void buzzer(uint frequency, uint duration); // Buzzer function
void setBuzzerPower(uint power); // Set buzzer power void setBuzzerPower(uint power); // Set buzzer power
String xdrDelete(String input, uint8_t maxlen = 0); // Delete xdr prefix from string and optional limit length String xdrDelete(String input); // Delete xdr prefix from string
void drawTextCenter(int16_t cx, int16_t cy, String text); void drawTextCenter(int16_t cx, int16_t cy, String text);
void drawButtonCenter(int16_t cx, int16_t cy, int8_t sx, int8_t sy, String text, uint16_t fg, uint16_t bg, bool inverted); void drawButtonCenter(int16_t cx, int16_t cy, int8_t sx, int8_t sy, String text, uint16_t fg, uint16_t bg, bool inverted);
@@ -114,7 +116,7 @@ void drawTextBoxed(Rect box, String text, uint16_t fg, uint16_t bg, bool inverte
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, bool symbolmode, 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 displayFooter(CommonData &commonData);
void displayAlarm(CommonData &commonData); void displayAlarm(CommonData &commonData);
@@ -126,10 +128,6 @@ void solarGraphic(uint x, uint y, int pcolor, int bcolor); // S
void generatorGraphic(uint x, uint y, int pcolor, int bcolor); // Generator graphic void generatorGraphic(uint x, uint y, int pcolor, int bcolor); // Generator graphic
void startLedTask(GwApi *api); void startLedTask(GwApi *api);
// Display rudder position as horizontal bargraph with configurable +/- range (degrees)
// 'rangeDeg' is unsigned and will be clamped to [10,45]
void displayRudderPosition(int rudderPosition, uint8_t rangeDeg, uint16_t cx, uint16_t cy, uint16_t fg, uint16_t bg);
void doImageRequest(GwApi *api, int *pageno, const PageStruct pages[MAX_PAGE_NUMBER], AsyncWebServerRequest *request); void doImageRequest(GwApi *api, int *pageno, const PageStruct pages[MAX_PAGE_NUMBER], AsyncWebServerRequest *request);
// Icons // Icons
@@ -166,62 +164,24 @@ static unsigned char fram_bits[] PROGMEM = {
0xff, 0xff, 0xf8, 0x1f, 0xf8, 0x1f, 0xff, 0xff, 0xff, 0xff, 0xf8, 0x1f, 0xff, 0xff, 0xf8, 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 };
// Header symbols
static unsigned char ap_bits[] PROGMEM = { static unsigned char ap_bits[] PROGMEM = {
0xe0, 0x03, 0x18, 0x0c, 0x04, 0x10, 0xc2, 0x21, 0x30, 0x06, 0x08, 0x08, 0xe0, 0x03, 0x18, 0x0c, 0x04, 0x10, 0xc2, 0x21, 0x30, 0x06, 0x08, 0x08,
0xc0, 0x01, 0x20, 0x02, 0x00, 0x00, 0x80, 0x00, 0xc0, 0x01, 0xc0, 0x01, 0xc0, 0x01, 0x20, 0x02, 0x00, 0x00, 0x80, 0x00, 0xc0, 0x01, 0xc0, 0x01,
0x80, 0x00, 0x80, 0x00, 0x80, 0x00, 0x80, 0x00 }; 0x80, 0x00, 0x80, 0x00, 0x80, 0x00, 0x80, 0x00 };
static unsigned char gps_bits[] PROGMEM = { static unsigned char dish_bits[] PROGMEM = {
0x3c, 0x00, 0x42, 0x18, 0xfa, 0x1b, 0x02, 0x04, 0x02, 0x0a, 0x02, 0x09, 0x3c, 0x00, 0x42, 0x18, 0xfa, 0x1b, 0x02, 0x04, 0x02, 0x0a, 0x02, 0x09,
0x82, 0x08, 0x06, 0x0a, 0x0e, 0x1b, 0x9c, 0x2b, 0x38, 0x2b, 0x74, 0x20, 0x82, 0x08, 0x06, 0x0a, 0x0e, 0x1b, 0x9c, 0x2b, 0x38, 0x2b, 0x74, 0x20,
0xec, 0x1f, 0x1c, 0x00, 0xf4, 0x00, 0xfe, 0x03 }; 0xec, 0x1f, 0x1c, 0x00, 0xf4, 0x00, 0xfe, 0x03 };
static unsigned char nmea_bits[] PROGMEM = {
0x00, 0x00, 0x22, 0x21, 0x26, 0x33, 0x26, 0x33, 0x2a, 0x2d, 0x32, 0x2d,
0x32, 0x21, 0x22, 0x21, 0x00, 0x00, 0x3c, 0x0c, 0x04, 0x0c, 0x04, 0x12,
0x3c, 0x12, 0x04, 0x1e, 0x04, 0x21, 0x3c, 0x21 };
static unsigned char n2k_bits[] PROGMEM = {
0xe0, 0x07, 0x18, 0x18, 0x04, 0x20, 0x02, 0x40, 0x32, 0x4c, 0x31, 0x8c,
0x01, 0x80, 0x81, 0x81, 0x81, 0x81, 0x01, 0x80, 0x31, 0x8c, 0x32, 0x4c,
0x02, 0x40, 0x04, 0x20, 0x98, 0x19, 0xe0, 0x07 };
static unsigned char tcp_bits[] PROGMEM = {
0x00, 0x00, 0xe0, 0x03, 0x20, 0x02, 0x20, 0x02, 0x20, 0x02, 0xe0, 0x03,
0x80, 0x00, 0x80, 0x00, 0xff, 0xff, 0x08, 0x10, 0x08, 0x10, 0x3e, 0x7c,
0x22, 0x44, 0x22, 0x44, 0x22, 0x44, 0x3e, 0x7c };
static unsigned char usb_bits[] PROGMEM = {
0x00, 0x00, 0x92, 0x39, 0x52, 0x4a, 0x52, 0x48, 0x92, 0x39, 0x12, 0x4a,
0x52, 0x4a, 0x8c, 0x39, 0x00, 0x00, 0x00, 0x00, 0xf8, 0x1f, 0x04, 0x20,
0xf4, 0x2f, 0x04, 0x20, 0xf8, 0x1f, 0x00, 0x00 };
static unsigned char sdcard_bits[] PROGMEM = {
0xf8, 0x07, 0x0c, 0x08, 0x04, 0x08, 0xc4, 0x09, 0x24, 0x1a, 0xe4, 0x13,
0x04, 0x20, 0x24, 0x21, 0xa4, 0x12, 0x44, 0x12, 0x04, 0x20, 0x04, 0x20,
0xc4, 0x23, 0x34, 0x2c, 0xd8, 0x1b, 0x00, 0x00 };
static unsigned char bluetooth_bits[] PROGMEM = {
0x00, 0x00, 0x22, 0x21, 0x26, 0x33, 0x26, 0x33, 0x2a, 0x2d, 0x32, 0x2d,
0x32, 0x21, 0x22, 0x21, 0x00, 0x00, 0x3c, 0x0c, 0x04, 0x0c, 0x04, 0x12,
0x3c, 0x12, 0x04, 0x1e, 0x04, 0x21, 0x3c, 0x21 };
static std::map<String, unsigned char *> iconmap = { static std::map<String, unsigned char *> iconmap = {
{"LEFT", left_bits}, {"LEFT", left_bits},
{"RIGHT", right_bits}, {"RIGHT", right_bits},
{"LOCK", lock_bits}, {"LOCK", lock_bits},
{"PLUS", plus_bits}, {"PLUS", plus_bits},
{"MINUS", minus_bits}, {"MINUS", minus_bits},
{"GPS", gps_bits}, {"DISH", dish_bits},
{"AP", ap_bits}, {"AP", ap_bits}
{"0183", nmea_bits},
{"N2K", n2k_bits},
{"TCP", tcp_bits},
{"USB", usb_bits},
{"SDCARD", sdcard_bits},
{"BLUE", bluetooth_bits}
}; };
// Battery // Battery

View File

@@ -3,91 +3,115 @@
#include <Arduino.h> #include <Arduino.h>
#include "GwApi.h" #include "GwApi.h"
#include "Pagedata.h" #include "Pagedata.h"
#include "OBP60Formatter.h"
// ToDo // ToDo
// simulation data // simulation data
// hold values by missing data // hold values by missing data
Formatter::Formatter(GwConfigHandler *config) { String formatDate(String fmttype, uint16_t year, uint8_t month, uint8_t day) {
// Load configuration values char buffer[12];
// TODO do not use strings but enums, see header file if (fmttype == "GB") {
stimeZone = config->getString(config->timeZone); snprintf(buffer, 12, "%02d/%02d/%04d", day , month, year);
timeZone = stimeZone.toDouble(); }
lengthFormat = config->getString(config->lengthFormat); else if (fmttype == "US") {
distanceFormat = config->getString(config->distanceFormat); snprintf(buffer, 12, "%02d/%02d/%04d", month, day, year);
speedFormat = config->getString(config->speedFormat); }
windspeedFormat = config->getString(config->windspeedFormat); else if (fmttype == "ISO") {
tempFormat = config->getString(config->tempFormat); snprintf(buffer, 12, "%04d-%02d-%02d", year, month, day);
dateFormat = config->getString(config->dateFormat); }
dateFmt = getDateFormat(dateFormat); else {
usesimudata = config->getBool(config->useSimuData); snprintf(buffer, 12, "%02d.%02d.%04d", day, month, year);
precision = config->getString(config->valueprecision); }
return String(buffer);
if (precision == "1") {
fmt_dec_1 = "%3.1f";
fmt_dec_10 = "%3.0f";
fmt_dec_100 = "%3.0f";
} else {
fmt_dec_1 = "%3.2f";
fmt_dec_10 = "%3.1f";
fmt_dec_100 = "%3.0f";
} }
String formatTime(char fmttype, uint8_t hour, uint8_t minute, uint8_t second) {
// fmttype: s: with seconds, m: only minutes
char buffer[10];
if (fmttype == 'm') {
snprintf(buffer, 10, "%02d:%02d", hour , minute);
}
else {
snprintf(buffer, 10, "%02d:%02d:%02d", hour, minute, second);
}
return String(buffer);
} }
fmtType Formatter::stringToFormat(const char* formatStr) { String formatLatitude(double lat) {
auto it = formatMap.find(formatStr); float degree = abs(int(lat));
if (it != formatMap.end()) { float minute = abs((lat - int(lat)) * 60);
return it->second; return String(degree, 0) + "\x90 " + String(minute, 4) + "' " + ((lat > 0) ? "N" : "S");
}
return fmtType::XDR_G; // generic as default
} }
fmtDate Formatter::getDateFormat(String sformat) { String formatLongitude(double lon) {
if (sformat == "DE") { float degree = abs(int(lon));
return fmtDate::DE; float minute = abs((lon - int(lon)) * 60);
} return String(degree, 0) + "\x90 " + String(minute, 4) + "' " + ((lon > 0) ? "E" : "W");
if (sformat == "GB") {
return fmtDate::GB;
}
if (sformat == "US") {
return fmtDate::US;
}
return fmtDate::ISO; // default
} }
fmtTime Formatter::getTimeFormat(String sformat) { // Convert and format boat value from SI to user defined format (definition for compatibility purposes)
if (sformat == "MMHH") { FormattedData formatValue(GwApi::BoatValue *value, CommonData &commondata) {
return fmtTime::MMHH;
} return formatValue(value, commondata, false); // call <formatValue> with standard handling of user setting for simulation data
if (sformat == "MMHHSS") {
return fmtTime::MMHHSS;
}
return fmtTime::MMHH; // default
} }
FormattedData Formatter::formatValue(GwApi::BoatValue *value, CommonData &commondata, bool ignoreSimuDataSetting) { // Convert and format boat value from SI to user defined format
// generate random simulation data; can be deselected to use conversion+formatting function even in simulation mode
FormattedData formatValue(GwApi::BoatValue *value, CommonData &commondata, bool ignoreSimuDataSetting){
GwLog *logger = commondata.logger; GwLog *logger = commondata.logger;
FormattedData result; FormattedData result;
static int dayoffset = 0; static int dayoffset = 0;
double rawvalue = 0; double rawvalue = 0;
bool simulation;
if (ignoreSimuDataSetting) {
simulation = false; // ignore user setting for simulation data; we want to format the boat value passed to this function
} else {
simulation = usesimudata; // use setting from configuration
}
result.cvalue = value->value; result.cvalue = value->value;
// Load configuration values
String stimeZone = commondata.config->getString(commondata.config->timeZone); // [UTC -14.00...+12.00]
double timeZone = stimeZone.toDouble();
String lengthFormat = commondata.config->getString(commondata.config->lengthFormat); // [m|ft]
String distanceFormat = commondata.config->getString(commondata.config->distanceFormat); // [m|km|nm]
String speedFormat = commondata.config->getString(commondata.config->speedFormat); // [m/s|km/h|kn]
String windspeedFormat = commondata.config->getString(commondata.config->windspeedFormat); // [m/s|km/h|kn|bft]
String tempFormat = commondata.config->getString(commondata.config->tempFormat); // [K|°C|°F]
String dateFormat = commondata.config->getString(commondata.config->dateFormat); // [DE|GB|US]
String precision = commondata.config->getString(commondata.config->valueprecision); // [1|2]
bool usesimudata;
if (ignoreSimuDataSetting){
usesimudata = false; // ignore user setting for simulation data; we want to format the boat value passed to this function
} else {
usesimudata = commondata.config->getBool(commondata.config->useSimuData); // [on|off]
}
// If boat value not valid // If boat value not valid
if (! value->valid && !simulation){ if (! value->valid && !usesimudata){
result.svalue = placeholder; result.svalue = "---";
return result; return result;
} }
const char* fmt_dec_1;
const char* fmt_dec_10;
const char* fmt_dec_100;
double limit_dec_10;
double limit_dec_100;
if (precision == "1") {
//
//All values are displayed using a DSEG7* font. In this font, ' ' is a very short space, and '.' takes up no space at all.
//For a space that is as long as a number, '!' is used. For details see https://www.keshikan.net/fonts-e.html
//
fmt_dec_1 = "!%1.1f"; //insert a blank digit and then display a two-digit number
fmt_dec_10 = "!%2.0f"; //insert a blank digit and then display a two-digit number
fmt_dec_100 = "%3.0f"; //dispay a three digit number
limit_dec_10=9.95; // use fmt_dec_1 below this number to avoid formatting 9.96 as 10.0 instead of 10
limit_dec_100=99.5;
} else {
fmt_dec_1 = "%3.2f";
fmt_dec_10 = "%3.1f";
fmt_dec_100 = "%3.0f";
limit_dec_10=9.995;
limit_dec_100=99.95;
}
// LOG_DEBUG(GwLog::DEBUG,"formatValue init: getFormat: %s date->value: %f time->value: %f", value->getFormat(), commondata.date->value, commondata.time->value); // LOG_DEBUG(GwLog::DEBUG,"formatValue init: getFormat: %s date->value: %f time->value: %f", value->getFormat(), commondata.date->value, commondata.time->value);
static const int bsize = 30; static const int bsize = 30;
char buffer[bsize+1]; char buffer[bsize+1];
@@ -107,7 +131,7 @@ FormattedData Formatter::formatValue(GwApi::BoatValue *value, CommonData &common
tmElements_t parts; tmElements_t parts;
time_t tv=tNMEA0183Msg::daysToTime_t(value->value + dayoffset); time_t tv=tNMEA0183Msg::daysToTime_t(value->value + dayoffset);
tNMEA0183Msg::breakTime(tv,parts); tNMEA0183Msg::breakTime(tv,parts);
if (simulation == false) { if (usesimudata == false) {
if (String(dateFormat) == "DE") { if (String(dateFormat) == "DE") {
snprintf(buffer,bsize, "%02d.%02d.%04d", parts.tm_mday, parts.tm_mon+1, parts.tm_year+1900); snprintf(buffer,bsize, "%02d.%02d.%04d", parts.tm_mday, parts.tm_mon+1, parts.tm_year+1900);
} }
@@ -127,7 +151,12 @@ FormattedData Formatter::formatValue(GwApi::BoatValue *value, CommonData &common
else{ else{
snprintf(buffer, bsize, "01.01.2022"); snprintf(buffer, bsize, "01.01.2022");
} }
result.unit = ((timeZone == 0) ? "UTC" : "LOT"); if(timeZone == 0){
result.unit = "UTC";
}
else{
result.unit = "LOT";
}
} }
//######################################################## //########################################################
else if(value->getFormat() == "formatTime"){ else if(value->getFormat() == "formatTime"){
@@ -141,7 +170,7 @@ FormattedData Formatter::formatValue(GwApi::BoatValue *value, CommonData &common
if (timeInSeconds > 86400) {timeInSeconds = timeInSeconds - 86400;} if (timeInSeconds > 86400) {timeInSeconds = timeInSeconds - 86400;}
if (timeInSeconds < 0) {timeInSeconds = timeInSeconds + 86400;} if (timeInSeconds < 0) {timeInSeconds = timeInSeconds + 86400;}
// LOG_DEBUG(GwLog::DEBUG,"... formatTime value: %f tz: %f corrected timeInSeconds: %f ", value->value, timeZone, timeInSeconds); // LOG_DEBUG(GwLog::DEBUG,"... formatTime value: %f tz: %f corrected timeInSeconds: %f ", value->value, timeZone, timeInSeconds);
if (simulation == false) { if (usesimudata == false) {
val = modf(timeInSeconds/3600.0, &inthr); val = modf(timeInSeconds/3600.0, &inthr);
val = modf(val*3600.0/60.0, &intmin); val = modf(val*3600.0/60.0, &intmin);
modf(val*60.0,&intsec); modf(val*60.0,&intsec);
@@ -159,11 +188,16 @@ FormattedData Formatter::formatValue(GwApi::BoatValue *value, CommonData &common
result.cvalue = sec; result.cvalue = sec;
lasttime = millis(); lasttime = millis();
} }
result.unit = ((timeZone == 0) ? "UTC" : "LOT"); if(timeZone == 0){
result.unit = "UTC";
}
else{
result.unit = "LOT";
}
} }
//######################################################## //########################################################
else if (value->getFormat() == "formatFixed0"){ else if (value->getFormat() == "formatFixed0"){
if(simulation == false) { if(usesimudata == false) {
snprintf(buffer, bsize, "%3.0f", value->value); snprintf(buffer, bsize, "%3.0f", value->value);
rawvalue = value->value; rawvalue = value->value;
} }
@@ -177,7 +211,7 @@ FormattedData Formatter::formatValue(GwApi::BoatValue *value, CommonData &common
//######################################################## //########################################################
else if (value->getFormat() == "formatCourse" || value->getFormat() == "formatWind"){ else if (value->getFormat() == "formatCourse" || value->getFormat() == "formatWind"){
double course = 0; double course = 0;
if (simulation == false) { if (usesimudata == false) {
course = value->value; course = value->value;
rawvalue = value->value; rawvalue = value->value;
} }
@@ -185,7 +219,7 @@ FormattedData Formatter::formatValue(GwApi::BoatValue *value, CommonData &common
course = M_PI_2 + float(random(-17, 17) / 100.0); // create random course/wind values with 90° +/- 10° course = M_PI_2 + float(random(-17, 17) / 100.0); // create random course/wind values with 90° +/- 10°
rawvalue = course; rawvalue = course;
} }
course = course * 57.2958; // Unit conversion form rad to deg course = course * RAD_TO_DEG; // Unit conversion form rad to deg
// Format 3 numbers with prefix zero // Format 3 numbers with prefix zero
snprintf(buffer,bsize,"%03.0f",course); snprintf(buffer,bsize,"%03.0f",course);
@@ -195,7 +229,7 @@ FormattedData Formatter::formatValue(GwApi::BoatValue *value, CommonData &common
//######################################################## //########################################################
else if (value->getFormat() == "formatKnots" && (value->getName() == "SOG" || value->getName() == "STW")){ else if (value->getFormat() == "formatKnots" && (value->getName() == "SOG" || value->getName() == "STW")){
double speed = 0; double speed = 0;
if (simulation == false) { if (usesimudata == false) {
speed = value->value; speed = value->value;
rawvalue = value->value; rawvalue = value->value;
} }
@@ -215,10 +249,10 @@ FormattedData Formatter::formatValue(GwApi::BoatValue *value, CommonData &common
speed = speed; // Unit conversion form m/s to m/s speed = speed; // Unit conversion form m/s to m/s
result.unit = "m/s"; result.unit = "m/s";
} }
if(speed < 10) { if(speed < limit_dec_10) {
snprintf(buffer, bsize, fmt_dec_1, speed); snprintf(buffer, bsize, fmt_dec_1, speed);
} }
else if (speed < 100) { else if (speed < limit_dec_100) {
snprintf(buffer, bsize, fmt_dec_10, speed); snprintf(buffer, bsize, fmt_dec_10, speed);
} }
else { else {
@@ -229,7 +263,7 @@ FormattedData Formatter::formatValue(GwApi::BoatValue *value, CommonData &common
//######################################################## //########################################################
else if (value->getFormat() == "formatKnots" && (value->getName() == "AWS" || value->getName() == "TWS" || value->getName() == "MaxAws" || value->getName() == "MaxTws")){ else if (value->getFormat() == "formatKnots" && (value->getName() == "AWS" || value->getName() == "TWS" || value->getName() == "MaxAws" || value->getName() == "MaxTws")){
double speed = 0; double speed = 0;
if (simulation == false) { if (usesimudata == false) {
speed = value->value; speed = value->value;
rawvalue = value->value; rawvalue = value->value;
} }
@@ -295,10 +329,10 @@ FormattedData Formatter::formatValue(GwApi::BoatValue *value, CommonData &common
snprintf(buffer, bsize, "%2.0f", speed); snprintf(buffer, bsize, "%2.0f", speed);
} }
else{ else{
if (speed < 10){ if (speed < limit_dec_10){
snprintf(buffer, bsize, fmt_dec_1, speed); snprintf(buffer, bsize, fmt_dec_1, speed);
} }
else if (speed < 100){ else if (speed < limit_dec_100){
snprintf(buffer, bsize, fmt_dec_10, speed); snprintf(buffer, bsize, fmt_dec_10, speed);
} }
else { else {
@@ -310,7 +344,7 @@ FormattedData Formatter::formatValue(GwApi::BoatValue *value, CommonData &common
//######################################################## //########################################################
else if (value->getFormat() == "formatRot"){ else if (value->getFormat() == "formatRot"){
double rotation = 0; double rotation = 0;
if (simulation == false) { if (usesimudata == false) {
rotation = value->value; rotation = value->value;
rawvalue = value->value; rawvalue = value->value;
} }
@@ -323,13 +357,13 @@ FormattedData Formatter::formatValue(GwApi::BoatValue *value, CommonData &common
if (rotation < -100){ if (rotation < -100){
rotation = -99; rotation = -99;
} }
else if (rotation > 100){ if (rotation > 100){
rotation = 99; rotation = 99;
} }
if (rotation > -10 && rotation < 10){ if (rotation > -10 && rotation < 10){
snprintf(buffer, bsize, "%3.2f", rotation); snprintf(buffer, bsize, "%3.2f", rotation);
} }
else { if (rotation <= -10 || rotation >= 10){
snprintf(buffer, bsize, "%3.0f", rotation); snprintf(buffer, bsize, "%3.0f", rotation);
} }
result.cvalue = rotation; result.cvalue = rotation;
@@ -337,7 +371,7 @@ FormattedData Formatter::formatValue(GwApi::BoatValue *value, CommonData &common
//######################################################## //########################################################
else if (value->getFormat() == "formatDop"){ else if (value->getFormat() == "formatDop"){
double dop = 0; double dop = 0;
if (simulation == false) { if (usesimudata == false) {
dop = value->value; dop = value->value;
rawvalue = value->value; rawvalue = value->value;
} }
@@ -349,10 +383,10 @@ FormattedData Formatter::formatValue(GwApi::BoatValue *value, CommonData &common
if (dop > 99.9){ if (dop > 99.9){
dop = 99.9; dop = 99.9;
} }
if (dop < 10){ if (dop < limit_dec_10){
snprintf(buffer, bsize, fmt_dec_1, dop); snprintf(buffer, bsize, fmt_dec_1, dop);
} }
else if(dop < 100){ else if(dop < limit_dec_100){
snprintf(buffer, bsize, fmt_dec_10, dop); snprintf(buffer, bsize, fmt_dec_10, dop);
} }
else { else {
@@ -362,14 +396,19 @@ FormattedData Formatter::formatValue(GwApi::BoatValue *value, CommonData &common
} }
//######################################################## //########################################################
else if (value->getFormat() == "formatLatitude"){ else if (value->getFormat() == "formatLatitude"){
if (simulation == false) { if (usesimudata == false) {
double lat = value->value; double lat = value->value;
rawvalue = value->value; rawvalue = value->value;
String latitude = ""; String latitude = "";
String latdir = ""; String latdir = "";
float degree = abs(int(lat)); float degree = abs(int(lat));
float minute = abs((lat - int(lat)) * 60); float minute = abs((lat - int(lat)) * 60);
latdir = (lat > 0) ? "N" : "S"; if (lat > 0){
latdir = "N";
}
else {
latdir = "S";
}
latitude = String(degree,0) + "\x90 " + String(minute,4) + "' " + latdir; latitude = String(degree,0) + "\x90 " + String(minute,4) + "' " + latdir;
result.unit = ""; result.unit = "";
strcpy(buffer, latitude.c_str()); strcpy(buffer, latitude.c_str());
@@ -382,14 +421,19 @@ FormattedData Formatter::formatValue(GwApi::BoatValue *value, CommonData &common
} }
//######################################################## //########################################################
else if (value->getFormat() == "formatLongitude"){ else if (value->getFormat() == "formatLongitude"){
if (simulation == false) { if (usesimudata == false) {
double lon = value->value; double lon = value->value;
rawvalue = value->value; rawvalue = value->value;
String longitude = ""; String longitude = "";
String londir = ""; String londir = "";
float degree = abs(int(lon)); float degree = abs(int(lon));
float minute = abs((lon - int(lon)) * 60); float minute = abs((lon - int(lon)) * 60);
londir = (lon > 0) ? "E" : "W"; if (lon > 0){
londir = "E";
}
else {
londir = "W";
}
longitude = String(degree,0) + "\x90 " + String(minute,4) + "' " + londir; longitude = String(degree,0) + "\x90 " + String(minute,4) + "' " + londir;
result.unit = ""; result.unit = "";
strcpy(buffer, longitude.c_str()); strcpy(buffer, longitude.c_str());
@@ -403,7 +447,7 @@ FormattedData Formatter::formatValue(GwApi::BoatValue *value, CommonData &common
//######################################################## //########################################################
else if (value->getFormat() == "formatDepth"){ else if (value->getFormat() == "formatDepth"){
double depth = 0; double depth = 0;
if (simulation == false) { if (usesimudata == false) {
depth = value->value; depth = value->value;
rawvalue = value->value; rawvalue = value->value;
} }
@@ -412,16 +456,16 @@ FormattedData Formatter::formatValue(GwApi::BoatValue *value, CommonData &common
depth = rawvalue; depth = rawvalue;
} }
if(String(lengthFormat) == "ft"){ if(String(lengthFormat) == "ft"){
depth = depth * 3.28084; // TODO use global defined factor depth = depth * 3.28084;
result.unit = "ft"; result.unit = "ft";
} }
else{ else{
result.unit = "m"; result.unit = "m";
} }
if (depth < 10) { if (depth < limit_dec_10) {
snprintf(buffer, bsize, fmt_dec_1, depth); snprintf(buffer, bsize, fmt_dec_1, depth);
} }
else if (depth < 100){ else if (depth < limit_dec_100){
snprintf(buffer, bsize, fmt_dec_10, depth); snprintf(buffer, bsize, fmt_dec_10, depth);
} }
else { else {
@@ -432,28 +476,32 @@ FormattedData Formatter::formatValue(GwApi::BoatValue *value, CommonData &common
//######################################################## //########################################################
else if (value->getFormat() == "formatXte"){ else if (value->getFormat() == "formatXte"){
double xte = 0; double xte = 0;
if (simulation == false) { if(usesimudata == false) {
xte = value->value; xte = value->value;
rawvalue = value->value; rawvalue = value->value;
} else { }
else{
rawvalue = 6.0 + float(random(0, 4)); rawvalue = 6.0 + float(random(0, 4));
xte = rawvalue; xte = rawvalue;
} }
if (distanceFormat == "km") { if(String(distanceFormat) == "km"){
xte = xte * 0.001; xte = xte * 0.001;
result.unit = "km"; result.unit = "km";
} else if (distanceFormat == "nm") { }
xte = xte * 0.000539957; // TODO use global defined factor else if(String(distanceFormat) == "nm"){
xte = xte * 0.000539957;
result.unit = "nm"; result.unit = "nm";
} else { }
else{;
result.unit = "m"; result.unit = "m";
} }
if(xte < 10){ if(xte < 10){
snprintf(buffer,bsize,"%3.2f",xte); snprintf(buffer,bsize,"%3.2f",xte);
} else if (xte < 100) { }
if(xte >= 10 && xte < 100){
snprintf(buffer,bsize,"%3.1f",xte); snprintf(buffer,bsize,"%3.1f",xte);
} }
else { if(xte >= 100){
snprintf(buffer,bsize,"%3.0f",xte); snprintf(buffer,bsize,"%3.0f",xte);
} }
result.cvalue = xte; result.cvalue = xte;
@@ -461,7 +509,7 @@ FormattedData Formatter::formatValue(GwApi::BoatValue *value, CommonData &common
//######################################################## //########################################################
else if (value->getFormat() == "kelvinToC"){ else if (value->getFormat() == "kelvinToC"){
double temp = 0; double temp = 0;
if (simulation == false) { if (usesimudata == false) {
temp = value->value; temp = value->value;
rawvalue = value->value; rawvalue = value->value;
} }
@@ -480,10 +528,10 @@ FormattedData Formatter::formatValue(GwApi::BoatValue *value, CommonData &common
else{ else{
result.unit = "K"; result.unit = "K";
} }
if (temp < 10) { if(temp < limit_dec_10) {
snprintf(buffer, bsize, fmt_dec_1, temp); snprintf(buffer, bsize, fmt_dec_1, temp);
} }
else if (temp < 100) { else if (temp < limit_dec_100) {
snprintf(buffer, bsize, fmt_dec_10, temp); snprintf(buffer, bsize, fmt_dec_10, temp);
} }
else { else {
@@ -494,7 +542,7 @@ FormattedData Formatter::formatValue(GwApi::BoatValue *value, CommonData &common
//######################################################## //########################################################
else if (value->getFormat() == "mtr2nm"){ else if (value->getFormat() == "mtr2nm"){
double distance = 0; double distance = 0;
if (simulation == false) { if (usesimudata == false) {
distance = value->value; distance = value->value;
rawvalue = value->value; rawvalue = value->value;
} }
@@ -507,16 +555,16 @@ FormattedData Formatter::formatValue(GwApi::BoatValue *value, CommonData &common
result.unit = "km"; result.unit = "km";
} }
else if (String(distanceFormat) == "nm") { else if (String(distanceFormat) == "nm") {
distance = distance * 0.000539957; // TODO use global defined factor distance = distance * 0.000539957;
result.unit = "nm"; result.unit = "nm";
} }
else { else {
result.unit = "m"; result.unit = "m";
} }
if (distance < 10){ if (distance < limit_dec_10){
snprintf(buffer, bsize, fmt_dec_1, distance); snprintf(buffer, bsize, fmt_dec_1, distance);
} }
else if (distance < 100){ else if (distance < limit_dec_100){
snprintf(buffer, bsize, fmt_dec_10, distance); snprintf(buffer, bsize, fmt_dec_10, distance);
} }
else { else {
@@ -530,7 +578,7 @@ FormattedData Formatter::formatValue(GwApi::BoatValue *value, CommonData &common
//######################################################## //########################################################
else if (value->getFormat() == "formatXdr:P:P"){ else if (value->getFormat() == "formatXdr:P:P"){
double pressure = 0; double pressure = 0;
if (simulation == false) { if (usesimudata == false) {
pressure = value->value; pressure = value->value;
rawvalue = value->value; rawvalue = value->value;
pressure = pressure / 100.0; // Unit conversion form Pa to hPa pressure = pressure / 100.0; // Unit conversion form Pa to hPa
@@ -546,7 +594,7 @@ FormattedData Formatter::formatValue(GwApi::BoatValue *value, CommonData &common
//######################################################## //########################################################
else if (value->getFormat() == "formatXdr:P:B"){ else if (value->getFormat() == "formatXdr:P:B"){
double pressure = 0; double pressure = 0;
if (simulation == false) { if (usesimudata == false) {
pressure = value->value; pressure = value->value;
rawvalue = value->value; rawvalue = value->value;
pressure = pressure / 100.0; // Unit conversion form Pa to mBar pressure = pressure / 100.0; // Unit conversion form Pa to mBar
@@ -562,7 +610,7 @@ FormattedData Formatter::formatValue(GwApi::BoatValue *value, CommonData &common
//######################################################## //########################################################
else if (value->getFormat() == "formatXdr:U:V"){ else if (value->getFormat() == "formatXdr:U:V"){
double voltage = 0; double voltage = 0;
if (simulation == false) { if (usesimudata == false) {
voltage = value->value; voltage = value->value;
rawvalue = value->value; rawvalue = value->value;
} }
@@ -570,7 +618,7 @@ FormattedData Formatter::formatValue(GwApi::BoatValue *value, CommonData &common
rawvalue = 12 + float(random(0, 30)) / 10.0; rawvalue = 12 + float(random(0, 30)) / 10.0;
voltage = rawvalue; voltage = rawvalue;
} }
if (voltage < 10) { if (voltage < limit_dec_10) {
snprintf(buffer, bsize, fmt_dec_1, voltage); snprintf(buffer, bsize, fmt_dec_1, voltage);
} }
else { else {
@@ -582,7 +630,7 @@ FormattedData Formatter::formatValue(GwApi::BoatValue *value, CommonData &common
//######################################################## //########################################################
else if (value->getFormat() == "formatXdr:I:A"){ else if (value->getFormat() == "formatXdr:I:A"){
double current = 0; double current = 0;
if (simulation == false) { if (usesimudata == false) {
current = value->value; current = value->value;
rawvalue = value->value; rawvalue = value->value;
} }
@@ -590,10 +638,10 @@ FormattedData Formatter::formatValue(GwApi::BoatValue *value, CommonData &common
rawvalue = 8.2 + float(random(0, 50)) / 10.0; rawvalue = 8.2 + float(random(0, 50)) / 10.0;
current = rawvalue; current = rawvalue;
} }
if (current < 10) { if (current < limit_dec_10) {
snprintf(buffer, bsize, fmt_dec_1, current); snprintf(buffer, bsize, fmt_dec_1, current);
} }
else if(current < 100) { else if(current < limit_dec_100) {
snprintf(buffer, bsize, fmt_dec_10, current); snprintf(buffer, bsize, fmt_dec_10, current);
} }
else { else {
@@ -605,7 +653,7 @@ FormattedData Formatter::formatValue(GwApi::BoatValue *value, CommonData &common
//######################################################## //########################################################
else if (value->getFormat() == "formatXdr:C:K"){ else if (value->getFormat() == "formatXdr:C:K"){
double temperature = 0; double temperature = 0;
if (simulation == false) { if (usesimudata == false) {
temperature = value->value - 273.15; // Convert K to C temperature = value->value - 273.15; // Convert K to C
rawvalue = value->value - 273.15; rawvalue = value->value - 273.15;
} }
@@ -613,10 +661,10 @@ FormattedData Formatter::formatValue(GwApi::BoatValue *value, CommonData &common
rawvalue = 21.8 + float(random(0, 50)) / 10.0; rawvalue = 21.8 + float(random(0, 50)) / 10.0;
temperature = rawvalue; temperature = rawvalue;
} }
if (temperature < 10) { if (temperature < limit_dec_10) {
snprintf(buffer, bsize, fmt_dec_1, temperature); snprintf(buffer, bsize, fmt_dec_1, temperature);
} }
else if (temperature < 100) { else if (temperature < limit_dec_100) {
snprintf(buffer, bsize, fmt_dec_10, temperature); snprintf(buffer, bsize, fmt_dec_10, temperature);
} }
else { else {
@@ -628,7 +676,7 @@ FormattedData Formatter::formatValue(GwApi::BoatValue *value, CommonData &common
//######################################################## //########################################################
else if (value->getFormat() == "formatXdr:C:C"){ else if (value->getFormat() == "formatXdr:C:C"){
double temperature = 0; double temperature = 0;
if (simulation == false) { if (usesimudata == false) {
temperature = value->value; // Value in C temperature = value->value; // Value in C
rawvalue = value->value; rawvalue = value->value;
} }
@@ -636,10 +684,10 @@ FormattedData Formatter::formatValue(GwApi::BoatValue *value, CommonData &common
rawvalue = 21.8 + float(random(0, 50)) / 10.0; rawvalue = 21.8 + float(random(0, 50)) / 10.0;
temperature = rawvalue; temperature = rawvalue;
} }
if (temperature < 10) { if (temperature < limit_dec_10) {
snprintf(buffer, bsize, fmt_dec_1, temperature); snprintf(buffer, bsize, fmt_dec_1, temperature);
} }
else if(temperature < 100) { else if(temperature < limit_dec_100) {
snprintf(buffer, bsize, fmt_dec_10, temperature); snprintf(buffer, bsize, fmt_dec_10, temperature);
} }
else { else {
@@ -651,7 +699,7 @@ FormattedData Formatter::formatValue(GwApi::BoatValue *value, CommonData &common
//######################################################## //########################################################
else if (value->getFormat() == "formatXdr:H:P"){ else if (value->getFormat() == "formatXdr:H:P"){
double humidity = 0; double humidity = 0;
if (simulation == false) { if (usesimudata == false) {
humidity = value->value; // Value in % humidity = value->value; // Value in %
rawvalue = value->value; rawvalue = value->value;
} }
@@ -659,10 +707,10 @@ FormattedData Formatter::formatValue(GwApi::BoatValue *value, CommonData &common
rawvalue = 41.3 + float(random(0, 50)) / 10.0; rawvalue = 41.3 + float(random(0, 50)) / 10.0;
humidity = rawvalue; humidity = rawvalue;
} }
if (humidity < 10) { if (humidity < limit_dec_10) {
snprintf(buffer, bsize, fmt_dec_1, humidity); snprintf(buffer, bsize, fmt_dec_1, humidity);
} }
else if(humidity < 100) { else if(humidity < limit_dec_100) {
snprintf(buffer, bsize, fmt_dec_10, humidity); snprintf(buffer, bsize, fmt_dec_10, humidity);
} }
else { else {
@@ -674,7 +722,7 @@ FormattedData Formatter::formatValue(GwApi::BoatValue *value, CommonData &common
//######################################################## //########################################################
else if (value->getFormat() == "formatXdr:V:P"){ else if (value->getFormat() == "formatXdr:V:P"){
double volume = 0; double volume = 0;
if (simulation == false) { if (usesimudata == false) {
volume = value->value; // Value in % volume = value->value; // Value in %
rawvalue = value->value; rawvalue = value->value;
} }
@@ -682,13 +730,13 @@ FormattedData Formatter::formatValue(GwApi::BoatValue *value, CommonData &common
rawvalue = 85.8 + float(random(0, 50)) / 10.0; rawvalue = 85.8 + float(random(0, 50)) / 10.0;
volume = rawvalue; volume = rawvalue;
} }
if (volume < 10) { if (volume < limit_dec_10) {
snprintf(buffer, bsize, fmt_dec_1, volume); snprintf(buffer, bsize, fmt_dec_1, volume);
} }
else if (volume < 100) { else if (volume < limit_dec_100) {
snprintf(buffer, bsize, fmt_dec_10, volume); snprintf(buffer, bsize, fmt_dec_10, volume);
} }
else if (volume >= 100) { else if (volume >= limit_dec_100) {
snprintf(buffer, bsize, fmt_dec_100, volume); snprintf(buffer, bsize, fmt_dec_100, volume);
} }
result.unit = "%"; result.unit = "%";
@@ -697,7 +745,7 @@ FormattedData Formatter::formatValue(GwApi::BoatValue *value, CommonData &common
//######################################################## //########################################################
else if (value->getFormat() == "formatXdr:V:M"){ else if (value->getFormat() == "formatXdr:V:M"){
double volume = 0; double volume = 0;
if (simulation == false) { if (usesimudata == false) {
volume = value->value; // Value in l volume = value->value; // Value in l
rawvalue = value->value; rawvalue = value->value;
} }
@@ -705,10 +753,10 @@ FormattedData Formatter::formatValue(GwApi::BoatValue *value, CommonData &common
rawvalue = 75.2 + float(random(0, 50)) / 10.0; rawvalue = 75.2 + float(random(0, 50)) / 10.0;
volume = rawvalue; volume = rawvalue;
} }
if (volume < 10) { if (volume < limit_dec_10) {
snprintf(buffer, bsize, fmt_dec_1, volume); snprintf(buffer, bsize, fmt_dec_1, volume);
} }
else if (volume < 100) { else if (volume < limit_dec_100) {
snprintf(buffer, bsize, fmt_dec_10, volume); snprintf(buffer, bsize, fmt_dec_10, volume);
} }
else { else {
@@ -720,7 +768,7 @@ FormattedData Formatter::formatValue(GwApi::BoatValue *value, CommonData &common
//######################################################## //########################################################
else if (value->getFormat() == "formatXdr:R:I"){ else if (value->getFormat() == "formatXdr:R:I"){
double flow = 0; double flow = 0;
if (simulation == false) { if (usesimudata == false) {
flow = value->value; // Value in l/min flow = value->value; // Value in l/min
rawvalue = value->value; rawvalue = value->value;
} }
@@ -728,10 +776,10 @@ FormattedData Formatter::formatValue(GwApi::BoatValue *value, CommonData &common
rawvalue = 7.5 + float(random(0, 20)) / 10.0; rawvalue = 7.5 + float(random(0, 20)) / 10.0;
flow = rawvalue; flow = rawvalue;
} }
if (flow < 10) { if (flow < limit_dec_10) {
snprintf(buffer, bsize, fmt_dec_1, flow); snprintf(buffer, bsize, fmt_dec_1, flow);
} }
else if (flow < 100) { else if (flow < limit_dec_100) {
snprintf(buffer, bsize, fmt_dec_10, flow); snprintf(buffer, bsize, fmt_dec_10, flow);
} }
else { else {
@@ -743,7 +791,7 @@ FormattedData Formatter::formatValue(GwApi::BoatValue *value, CommonData &common
//######################################################## //########################################################
else if (value->getFormat() == "formatXdr:G:"){ else if (value->getFormat() == "formatXdr:G:"){
double generic = 0; double generic = 0;
if (simulation == false) { if (usesimudata == false) {
generic = value->value; generic = value->value;
rawvalue = value->value; rawvalue = value->value;
} }
@@ -751,10 +799,10 @@ FormattedData Formatter::formatValue(GwApi::BoatValue *value, CommonData &common
rawvalue = 18.5 + float(random(0, 20)) / 10.0; rawvalue = 18.5 + float(random(0, 20)) / 10.0;
generic = rawvalue; generic = rawvalue;
} }
if (generic < 10) { if (generic < limit_dec_10) {
snprintf(buffer, bsize, fmt_dec_1, generic); snprintf(buffer, bsize, fmt_dec_1, generic);
} }
else if (generic < 100) { else if (generic < limit_dec_100) {
snprintf(buffer, bsize, fmt_dec_10, generic); snprintf(buffer, bsize, fmt_dec_10, generic);
} }
else { else {
@@ -766,7 +814,7 @@ FormattedData Formatter::formatValue(GwApi::BoatValue *value, CommonData &common
//######################################################## //########################################################
else if (value->getFormat() == "formatXdr:A:P"){ else if (value->getFormat() == "formatXdr:A:P"){
double dplace = 0; double dplace = 0;
if (simulation == false) { if (usesimudata == false) {
dplace = value->value; // Value in % dplace = value->value; // Value in %
rawvalue = value->value; rawvalue = value->value;
} }
@@ -774,10 +822,10 @@ FormattedData Formatter::formatValue(GwApi::BoatValue *value, CommonData &common
rawvalue = 55.3 + float(random(0, 20)) / 10.0; rawvalue = 55.3 + float(random(0, 20)) / 10.0;
dplace = rawvalue; dplace = rawvalue;
} }
if (dplace < 10) { if (dplace < limit_dec_10) {
snprintf(buffer, bsize, fmt_dec_1, dplace); snprintf(buffer, bsize, fmt_dec_1, dplace);
} }
else if (dplace < 100) { else if (dplace < limit_dec_100) {
snprintf(buffer, bsize, fmt_dec_10, dplace); snprintf(buffer, bsize, fmt_dec_10, dplace);
} }
else { else {
@@ -787,9 +835,9 @@ FormattedData Formatter::formatValue(GwApi::BoatValue *value, CommonData &common
result.cvalue = dplace; result.cvalue = dplace;
} }
//######################################################## //########################################################
else if ((value->getFormat() == "formatXdr:A:D") || ((value->getFormat() == "formatXdr:A:rd"))){ else if (value->getFormat() == "formatXdr:A:D"){
double angle = 0; double angle = 0;
if (simulation == false) { if (usesimudata == false) {
angle = value->value; angle = value->value;
angle = angle * 57.2958; // Unit conversion form rad to deg angle = angle * 57.2958; // Unit conversion form rad to deg
rawvalue = value->value; rawvalue = value->value;
@@ -810,7 +858,7 @@ FormattedData Formatter::formatValue(GwApi::BoatValue *value, CommonData &common
//######################################################## //########################################################
else if (value->getFormat() == "formatXdr:T:R"){ else if (value->getFormat() == "formatXdr:T:R"){
double rpm = 0; double rpm = 0;
if (simulation == false) { if (usesimudata == false) {
rpm = value->value; // Value in rpm rpm = value->value; // Value in rpm
rawvalue = value->value; rawvalue = value->value;
} }
@@ -818,10 +866,10 @@ FormattedData Formatter::formatValue(GwApi::BoatValue *value, CommonData &common
rawvalue = 2505 + random(0, 20); rawvalue = 2505 + random(0, 20);
rpm = rawvalue; rpm = rawvalue;
} }
if (rpm < 10) { if (rpm < limit_dec_10) {
snprintf(buffer, bsize, fmt_dec_1, rpm); snprintf(buffer, bsize, fmt_dec_1, rpm);
} }
else if (rpm < 100) { else if (rpm < limit_dec_100) {
snprintf(buffer, bsize, fmt_dec_10, rpm); snprintf(buffer, bsize, fmt_dec_10, rpm);
} }
else { else {
@@ -834,10 +882,10 @@ FormattedData Formatter::formatValue(GwApi::BoatValue *value, CommonData &common
// Default format // Default format
//######################################################## //########################################################
else { else {
if (value->value < 10) { if (value->value < limit_dec_10) {
snprintf(buffer, bsize, fmt_dec_1, value->value); snprintf(buffer, bsize, fmt_dec_1, value->value);
} }
else if (value->value < 100) { else if (value->value < limit_dec_100) {
snprintf(buffer, bsize, fmt_dec_10, value->value); snprintf(buffer, bsize, fmt_dec_10, value->value);
} }
else { else {
@@ -852,13 +900,8 @@ FormattedData Formatter::formatValue(GwApi::BoatValue *value, CommonData &common
return result; return result;
} }
// Convert and format boat value from SI to user defined format (definition for compatibility purposes)
FormattedData Formatter::formatValue(GwApi::BoatValue *value, CommonData &commondata) {
return formatValue(value, commondata, false); // call <formatValue> with standard handling of user setting for simulation data
}
// Helper method for conversion of any data value from SI to user defined format // Helper method for conversion of any data value from SI to user defined format
double Formatter::convertValue(const double &value, const String &name, const String &format, CommonData &commondata) double convertValue(const double &value, const String &name, const String &format, CommonData &commondata)
{ {
std::unique_ptr<GwApi::BoatValue> tmpBValue; // Temp variable to get converted data value from <OBP60Formatter::formatValue> std::unique_ptr<GwApi::BoatValue> tmpBValue; // Temp variable to get converted data value from <OBP60Formatter::formatValue>
double result; // data value converted to user defined target data format double result; // data value converted to user defined target data format
@@ -875,7 +918,7 @@ double Formatter::convertValue(const double &value, const String &name, const St
} }
// Helper method for conversion of any data value from SI to user defined format // Helper method for conversion of any data value from SI to user defined format
double Formatter::convertValue(const double &value, const String &format, CommonData &commondata) double convertValue(const double &value, const String &format, CommonData &commondata)
{ {
double result; // data value converted to user defined target data format double result; // data value converted to user defined target data format
@@ -883,49 +926,4 @@ double Formatter::convertValue(const double &value, const String &format, Common
return result; return result;
} }
String formatDate(fmtDate fmttype, uint16_t year, uint8_t month, uint8_t day) {
char buffer[12];
if (fmttype == fmtDate::GB) {
snprintf(buffer, 12, "%02d/%02d/%04d", day , month, year);
}
else if (fmttype == fmtDate::US) {
snprintf(buffer, 12, "%02d/%02d/%04d", month, day, year);
}
else if (fmttype == fmtDate::ISO) {
snprintf(buffer, 12, "%04d-%02d-%02d", year, month, day);
}
else if (fmttype == fmtDate::DE) {
snprintf(buffer, 12, "%02d.%02d.%04d", day, month, year);
} else {
snprintf(buffer, 12, "%04d-%02d-%02d", year, month, day);
}
return String(buffer);
}
String formatTime(fmtTime fmttype, uint8_t hour, uint8_t minute, uint8_t second) {
char buffer[10];
if (fmttype == fmtTime::MMHH) {
snprintf(buffer, 10, "%02d:%02d", hour , minute);
}
else if (fmttype == fmtTime::MMHHSS) {
snprintf(buffer, 10, "%02d:%02d:%02d", hour, minute, second);
}
else {
snprintf(buffer, 10, "%02d%02d%02d", hour, minute, second);
}
return String(buffer);
}
String formatLatitude(double lat) {
float degree = abs(int(lat));
float minute = abs((lat - int(lat)) * 60);
return String(degree, 0) + "\x90 " + String(minute, 4) + "' " + ((lat > 0) ? "N" : "S");
}
String formatLongitude(double lon) {
float degree = abs(int(lon));
float minute = abs((lon - int(lon)) * 60);
return String(degree, 0) + "\x90 " + String(minute, 4) + "' " + ((lon > 0) ? "E" : "W");
}
#endif #endif

View File

@@ -1,176 +0,0 @@
// SPDX-License-Identifier: GPL-2.0-or-later
#ifndef _OBP60FORMATTER_H
#define _OBP60FORMATTER_H
#include "GwApi.h"
#include "Pagedata.h"
#include <unordered_map>
/*
XDR types
A Angular displacement
C Temperature
D Linear displacement
F Frequency
G Generic
H Humidity
I Current
L Salinity
N Force
P Pressure
R Flow
S Switch or valve
T Tachometer
U Voltage
V Volume
XDR units
A Ampere
B Bar
C Celsius
D Degrees
H Hertz
I Liter per second?
M Meter / Cubic meter
N Newton
P Percent
R RPM
V Volt
*/
enum class fmtType {
// Formatter names as defined in BoatItemBase
COURSE,
KNOTS,
WIND,
LATITUDE,
LONGITUDE,
XTE,
FIXED0,
DEPTH,
DOP, // dilution of precision
ROT,
DATE,
TIME,
NAME,
kelvinToC, // TODO not a format but conversion
mtr2nm, // TODO not a format but conversion
// XDR Formatter names
XDR_PP, // pressure percent
XDR_PB, // pressure bar
XDR_UV, // voltage volt
XDR_IA, // current ampere
XDR_CK, // temperature kelvin
XDR_CC, // temperature celsius
XDR_HP, // humidity percent
XDR_VP, // volume percent
XDR_VM, // volume cubic meters
XDR_RI, // flow liter per second?
XDR_G, // generic
XDR_AP, // angle percent
XDR_AD, // angle degrees
XDR_TR // tachometer rpm
};
// Hint: String is not supported
static std::unordered_map<const char*, fmtType> formatMap PROGMEM = {
{"formatCourse", fmtType::COURSE},
{"formatKnots", fmtType::KNOTS},
{"formatWind", fmtType::WIND},
{"formatLatitude", fmtType::LATITUDE},
{"formatLongitude", fmtType::LONGITUDE},
{"formatXte", fmtType::XTE},
{"formatFixed0", fmtType::FIXED0},
{"formatDepth", fmtType::DEPTH},
{"formatDop", fmtType::DOP},
{"formatRot", fmtType::ROT},
{"formatDate", fmtType::DATE},
{"formatTime", fmtType::TIME},
{"formatName", fmtType::NAME},
{"kelvinToC", fmtType::kelvinToC},
{"mtr2nm", fmtType::mtr2nm},
{"formatXdr:P:P", fmtType::XDR_PP},
{"formatXdr:P:B", fmtType::XDR_PB},
{"formatXdr:U:V", fmtType::XDR_UV},
{"formatXdr:I:A", fmtType::XDR_IA},
{"formatXdr:C:K", fmtType::XDR_CK},
{"formatXdr:C:C", fmtType::XDR_CC},
{"formatXdr:H:P", fmtType::XDR_HP},
{"formatXdr:V:P", fmtType::XDR_VP},
{"formatXdr:V:M", fmtType::XDR_VM},
{"formatXdr:R:I", fmtType::XDR_RI},
{"formatXdr:G:", fmtType::XDR_G},
{"formatXdr:A:P", fmtType::XDR_AP},
{"formatXdr:A:D", fmtType::XDR_AD},
{"formatXdr:T:R", fmtType::XDR_TR}
};
// Possible formats as scoped enums
enum class fmtDate {DE, GB, US, ISO};
enum class fmtTime {MMHH, MMHHSS};
enum class fmtLength {METER, FEET, FATHOM, CABLE};
enum class fmtDepth {METER, FEET, FATHOM};
enum class fmtWind {KMH, MS, KN, BFT};
enum class fmtCourse {DEG, RAD};
enum class fmtRot {DEGS, RADS};
enum class fmtXte {M, KM, NM, CABLE};
enum class fmtPress {PA, BAR};
enum class fmtTemp {KELVIN, CELSUIS, FAHRENHEIT};
// Conversion factors
#define CONV_M_FT 3.2808399 // meter too feet
#define CONV_M_FM 0.5468 // meter to fathom
#define CONV_M_CBL 0.0053961182483768 // meter to cable
#define CONV_CBL_FT 608 // cable to feet
#define CONV_FM_FT 6 // fathom to feet
// Structure for formatted boat values
typedef struct {
double value; // SI value of boat data value
double cvalue; // value converted to target unit
String svalue; // value converted to target unit and formatted
String unit; // target value unit
} FormattedData;
// Formatter for boat values
class Formatter {
private:
String stimeZone = "0";
double timeZone = 0.0; // [UTC -14.00...+12.00]
String lengthFormat = "m"; // [m|ft]
String distanceFormat = "nm"; // [m|km|nm]
String speedFormat = "kn"; // [m/s|km/h|kn]
String windspeedFormat = "kn"; // [m/s|km/h|kn|bft]
String tempFormat = "C"; // [K|°C|°F]
String dateFormat = "ISO"; // [DE|GB|US|ISO]
fmtDate dateFmt;
bool usesimudata = false; // [on|off]
String precision = "2"; // [1|2]
const char* fmt_dec_1;
const char* fmt_dec_10;
const char* fmt_dec_100;
public:
Formatter(GwConfigHandler *config);
fmtType stringToFormat(const char* formatStr);
fmtDate getDateFormat(String sformat);
fmtTime getTimeFormat(String sformat);
FormattedData formatValue(GwApi::BoatValue *value, CommonData &commondata, bool ignoreSimuDataSetting);
FormattedData formatValue(GwApi::BoatValue *value, CommonData &commondata);
double convertValue(const double &value, const String &name, const String &format, CommonData &commondata);
double convertValue(const double &value, const String &format, CommonData &commondata);
String placeholder = "---";
};
// Standard format functions without class and overhead
String formatDate(fmtDate fmttype, uint16_t year, uint8_t month, uint8_t day);
String formatTime(fmtTime fmttype, uint8_t hour, uint8_t minute, uint8_t second);
String formatLatitude(double lat);
String formatLongitude(double lon);
#endif

View File

@@ -5,7 +5,7 @@
// Direction pin for RS485 NMEA0183 // Direction pin for RS485 NMEA0183
#define OBP_DIRECTION_PIN 18 #define OBP_DIRECTION_PIN 18
// I2C // I2C
#define I2C_SPEED 10000UL // 10kHz clock speed on I2C bus #define I2C_SPEED 10000UL // 100kHz clock speed on I2C bus
#define I2C_SPEED_LOW 1000UL // 10kHz clock speed on I2C bus for external bus #define I2C_SPEED_LOW 1000UL // 10kHz clock speed on I2C bus for external bus
#define OBP_I2C_SDA 47 #define OBP_I2C_SDA 47
#define OBP_I2C_SCL 21 #define OBP_I2C_SCL 21
@@ -43,6 +43,8 @@
#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 GxEPD_WIDTH 400 // Display width
#define GxEPD_HEIGHT 300 // Display height
// GPS (NEO-6M, NEO-M8N, ATGM336H) // GPS (NEO-6M, NEO-M8N, ATGM336H)
#define OBP_GPS_RX 2 #define OBP_GPS_RX 2
@@ -121,6 +123,8 @@
#define OBP_SPI_DIN 11 #define OBP_SPI_DIN 11
#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 GxEPD_WIDTH 400 // Display width
#define GxEPD_HEIGHT 300 // Display height
// SPI SD-Card // SPI SD-Card
#define SD_SPI_CS GPIO_NUM_10 #define SD_SPI_CS GPIO_NUM_10
#define SD_SPI_MOSI GPIO_NUM_40 #define SD_SPI_MOSI GPIO_NUM_40

View File

@@ -1,9 +1,8 @@
// SPDX-License-Identifier: GPL-2.0-or-later #ifndef _OBP60FUNCTIONS_H
#if defined BOARD_OBP60S3 || defined BOARD_OBP40S3 #define _OBP60FUNCTIONS_H
#include <Arduino.h> #include <Arduino.h>
#include "OBP60Hardware.h" #include "OBP60Hardware.h"
#include "OBP60Extensions.h" // for buzzer
#include "OBPKeyboardTask.h"
// Global vars // Global vars
@@ -59,9 +58,9 @@ void initKeys(CommonData &commonData) {
commonData.keydata[5].h = height; commonData.keydata[5].h = height;
} }
#ifdef HARDWARE_V21 #if defined HARDWARE_V20 || HARDWARE_V21
// Keypad functions for original OBP60 hardware // Keypad functions for original OBP60 hardware
int readKeypad(GwLog* logger, uint thSensitivity) { int readKeypad(GwLog* logger, uint thSensitivity, bool use_syspage) {
// Touch sensor values // Touch sensor values
// 35000 - Not touched // 35000 - Not touched
@@ -287,7 +286,6 @@ int readKeypad(GwLog* logger, uint thSensitivity, bool use_syspage) {
} }
// Copy keycode // Copy keycode
keycodeold = keycode; keycodeold = keycode;
// 100% Task-CPU RLY?
while(readSensorpads() > 0){} // Wait for pad release while(readSensorpads() > 0){} // Wait for pad release
delay(keydelay); delay(keydelay);
} }
@@ -302,40 +300,4 @@ int readKeypad(GwLog* logger, uint thSensitivity, bool use_syspage) {
} }
#endif #endif
void keyboardTask(void *param) {
// params needed:
// queue
// logger
// sensitivity
// use_syspage for deep sleep activation
KbTaskData *data = (KbTaskData *)param;
int keycode = 0;
data->logger->logDebug(GwLog::LOG, "Start keyboard task");
while (true) {
#ifdef BOARD_OBP40S3
keycode = readKeypad(data->logger, data->sensitivity, data->use_syspage);
#else
keycode = readKeypad(data->logger, data->sensitivity);
#endif
//send a key event
if (keycode != 0) {
xQueueSend(data->queue, &keycode, 0);
data->logger->logDebug(GwLog::LOG,"kbtask: send keycode: %d", keycode);
}
delay(20); // 50Hz update rate (20ms)
}
vTaskDelete(NULL);
}
void createKeyboardTask(KbTaskData *param) {
TaskHandle_t xHandle = NULL;
if (xTaskCreate(keyboardTask, "keyboard", configMINIMAL_STACK_SIZE + 1024, param, configMAX_PRIORITIES-1, &xHandle) != pdPASS) {
param->logger->logDebug(GwLog::ERROR, "Failed to create keyboard task!");
};
}
#endif #endif

View File

@@ -26,20 +26,20 @@ void qrWiFi(String ssid, String passwd, uint16_t fgcolor, uint16_t bgcolor){
// Each horizontal module // Each horizontal module
for (uint8_t x = 0; x < qrcode.size; x++) { for (uint8_t x = 0; x < qrcode.size; x++) {
if(qrcode_getModule(&qrcode, x, y)){ if(qrcode_getModule(&qrcode, x, y)){
epd->fillRect(box_x, box_y, box_s, box_s, fgcolor); getdisplay().fillRect(box_x, box_y, box_s, box_s, fgcolor);
} else { } else {
epd->fillRect(box_x, box_y, box_s, box_s, bgcolor); getdisplay().fillRect(box_x, box_y, box_s, box_s, bgcolor);
} }
box_x = box_x + box_s; box_x = box_x + box_s;
} }
box_y = box_y + box_s; box_y = box_y + box_s;
box_x = init_x; box_x = init_x;
} }
epd->setFont(&Ubuntu_Bold32pt8b); getdisplay().setFont(&Ubuntu_Bold32pt8b);
epd->setTextColor(fgcolor); getdisplay().setTextColor(fgcolor);
epd->setCursor(140, 285); getdisplay().setCursor(140, 285);
epd->print("WiFi"); getdisplay().print("WiFi");
epd->nextPage(); // Full Refresh getdisplay().nextPage(); // Full Refresh
} }
#endif #endif

View File

@@ -1,4 +1,3 @@
#include "OBP60Formatter.h"
#include "OBPDataOperations.h" #include "OBPDataOperations.h"
//#include "BoatDataCalibration.h" // Functions lib for data instance calibration //#include "BoatDataCalibration.h" // Functions lib for data instance calibration
@@ -265,7 +264,7 @@ void HstryBuf::handle(bool useSimuData, CommonData& common)
add(boatValue->value); add(boatValue->value);
} else if (useSimuData) { // add simulated value to history buffer } else if (useSimuData) { // add simulated value to history buffer
double simSIValue = common.fmt->formatValue(tmpBVal.get(), common).value; // simulated value is generated at <formatValue>; here: retreive SI value double simSIValue = formatValue(tmpBVal.get(), common).value; // simulated value is generated at <formatValue>; here: retreive SI value
add(simSIValue); add(simSIValue);
} else { } else {
// here we will add invalid (DBL_MAX) value; this will mark periods of missing data in buffer together with a timestamp // here we will add invalid (DBL_MAX) value; this will mark periods of missing data in buffer together with a timestamp

View File

@@ -1,16 +0,0 @@
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include "GwLog.h"
#include "Pagedata.h"
typedef struct {
QueueHandle_t queue;
GwLog* logger = nullptr;
uint sensitivity = 100;
#ifdef BOARD_OBP40S3
bool use_syspage = true;
#endif
} KbTaskData;
void initKeys(CommonData &commonData);
void createKeyboardTask(KbTaskData *param);

View File

@@ -1,4 +1,3 @@
// SPDX-License-Identifier: GPL-2.0-or-later
#if defined BOARD_OBP60S3 || defined BOARD_OBP40S3 #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
@@ -50,8 +49,10 @@ void sensorTask(void *param){
// Init sensor stuff // Init sensor stuff
bool oneWire_ready = false; // 1Wire initialized and ready to use bool oneWire_ready = false; // 1Wire initialized and ready to use
bool iRTC_ready = false; // Software RTC initialized and ready to use
bool RTC_ready = false; // DS1388 initialized and ready to use bool RTC_ready = false; // DS1388 initialized and ready to use
bool GPS_ready = false; // GPS initialized and ready to use bool GPS_ready = false; // GPS initialized and ready to use
bool N2K_GPS_ready = false; // GPS time on N2K bus
bool BME280_ready = false; // BME280 initialized and ready to use bool BME280_ready = false; // BME280 initialized and ready to use
bool BMP280_ready = false; // BMP280 initialized and ready to use bool BMP280_ready = false; // BMP280 initialized and ready to use
bool BMP180_ready = false; // BMP180 initialized and ready to use bool BMP180_ready = false; // BMP180 initialized and ready to use
@@ -383,6 +384,7 @@ void sensorTask(void *param){
if (getLocalTime(&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); 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); rtc.setTimeStruct(timeinfo);
iRTC_ready = true;
sensors.rtcValid = true; sensors.rtcValid = true;
} else { } else {
api->getLogger()->logDebug(GwLog::LOG,"NTP time fetch failed!"); api->getLogger()->logDebug(GwLog::LOG,"NTP time fetch failed!");
@@ -401,7 +403,7 @@ void sensorTask(void *param){
if (millis() > starttime0 + 100) if (millis() > starttime0 + 100)
{ {
starttime0 = millis(); starttime0 = millis();
// Send NMEA0183 GPS data on several bus systems all 100ms // Send NMEA0183 GPS data on several bus systems (N2K an 0183) all 100ms
if (GPS_ready == true && hdop->value <= hdopAccuracy) if (GPS_ready == true && hdop->value <= hdopAccuracy)
{ {
SNMEA0183Msg NMEA0183Msg; SNMEA0183Msg NMEA0183Msg;
@@ -413,9 +415,55 @@ void sensorTask(void *param){
} }
// If RTC DS1388 ready, then copy GPS data to RTC all 5min /*
if(millis() > starttime11 + 5*60*1000){ Time set logic for RTC and N2K
###############################
iRTC = Software RTC updatetd with NTP via internet
RTC = RTC chip on PCB
GPS = GPS Receiver on PCB
N2K = GPS time on N2K od 183 bus
0 = device not ready
1 = device ready
X = independend
() = source for set time N2K
-> = set RTC via iRTC
<- = set RTC via GPS
iRTC RTC GPS N2K
0 0 0 (1)
0 0 (1) (X)
0 (1) 0 (X)
0 1 <-(1) (X)
(1) 0 0 (X)
1 0 (1) (X)
1 ->(1) 0 (X)
1 1 <-(1) (X)
*/
// If RTC DS1388 ready, then copy iRTC and GPS data to RTC all 1min
if(millis() > starttime11 + 1*60*1000){
starttime11 = millis(); starttime11 = millis();
// Set RTC chip via iRTC (NTP)
if(iRTC_ready == true && RTC_ready == true && GPS_ready == false){
GwApi::Status status;
api->getStatus(status);
// Check WiFi connection
if (status.wifiClientConnected) {
sensors.rtcTime = rtc.getTimeStruct(); // Get time from software RTC (iRTC)
DateTime now = DateTime(
sensors.rtcTime.tm_year + 1900,
sensors.rtcTime.tm_mon + 1,
sensors.rtcTime.tm_mday,
sensors.rtcTime.tm_hour,
sensors.rtcTime.tm_min,
sensors.rtcTime.tm_sec
);
ds1388.adjust(now);
}
}
// Set RTC chip via internal GPS
if(rtcOn == "DS1388" && RTC_ready == true && GPS_ready == true){ if(rtcOn == "DS1388" && RTC_ready == true && GPS_ready == true){
api->getBoatDataValues(3,valueList); api->getBoatDataValues(3,valueList);
if(gpsdays->valid && gpsseconds->valid && hdop->valid){ if(gpsdays->valid && gpsseconds->valid && hdop->valid){
@@ -423,38 +471,30 @@ void sensorTask(void *param){
// sample input: date = "Dec 26 2009", time = "12:34:56" // sample input: date = "Dec 26 2009", time = "12:34:56"
// ds1388.adjust(DateTime("Dec 26 2009", "12:34:56")); // ds1388.adjust(DateTime("Dec 26 2009", "12:34:56"));
DateTime adjusttime(ts); DateTime adjusttime(ts);
api->getLogger()->logDebug(GwLog::LOG,"Adjust RTC time: %04d/%02d/%02d %02d:%02d:%02d",adjusttime.year(), adjusttime.month(), adjusttime.day(), adjusttime.hour(), adjusttime.minute(), adjusttime.second()); api->getLogger()->logDebug(GwLog::LOG,"Adjust RTC time via internal GPS: %04d/%02d/%02d %02d:%02d:%02d",adjusttime.year(), adjusttime.month(), adjusttime.day(), adjusttime.hour(), adjusttime.minute(), adjusttime.second());
// Adjust RTC time as unix time value // Adjust RTC time as unix time value
ds1388.adjust(adjusttime); ds1388.adjust(adjusttime);
} }
} }
} }
// Send 1Wire data for all temperature sensors all 2s // Set RTC chip via N2K or 183 in case the internal GPS is off (only one time)
if(millis() > starttime13 + 2000 && String(oneWireOn) == "DS18B20" && oneWire_ready == true){ if(N2K_GPS_ready == false && RTC_ready == true && GPS_ready == false){
starttime13 = millis(); api->getBoatDataValues(3,valueList);
float tempC; if(gpsdays->valid && gpsseconds->valid && hdop->valid){
ds18b20.requestTemperatures(); // Collect all temperature values (max.8) long ts = tNMEA0183Msg::daysToTime_t(gpsdays->value - (30*365+7))+floor(gpsseconds->value); // Adjusted to reference year 2000 (-30 years and 7 days for switch years)
for(int i=0;i<numberOfDevices; i++){ // sample input: date = "Dec 26 2009", time = "12:34:56"
// Send only one 1Wire data per loop step (time reduction) // ds1388.adjust(DateTime("Dec 26 2009", "12:34:56"));
if(i == loopCounter % numberOfDevices){ DateTime adjusttime(ts);
if(ds18b20.getAddress(tempDeviceAddress, i)){ api->getLogger()->logDebug(GwLog::LOG,"Adjust RTC time via N2K/183: %04d/%02d/%02d %02d:%02d:%02d",adjusttime.year(), adjusttime.month(), adjusttime.day(), adjusttime.hour(), adjusttime.minute(), adjusttime.second());
// Read temperature value in Celsius // Adjust RTC time as unix time value
tempC = ds18b20.getTempC(tempDeviceAddress); ds1388.adjust(adjusttime);
// N2K GPS time ready
N2K_GPS_ready = true;
} }
// Send to NMEA200 bus for each sensor with instance number
if(!isnan(tempC)){
sensors.onewireTemp[i] = tempC; // Save values in SensorData
api->getLogger()->logDebug(GwLog::DEBUG,"DS18B20-%1d Temp: %.1f",i,tempC);
SetN2kPGN130316(N2kMsg, 0, i, N2kts_OutsideTemperature, CToKelvin(tempC), N2kDoubleNA);
api->sendN2kMessage(N2kMsg);
}
}
}
loopCounter++;
} }
// Get current RTC date and time all 500ms // Send RTC date and time to N2K all 500ms
if (millis() > starttime12 + 500) { if (millis() > starttime12 + 500) {
starttime12 = millis(); starttime12 = millis();
// Send date and time from RTC chip if GPS not ready // Send date and time from RTC chip if GPS not ready
@@ -483,17 +523,34 @@ void sensorTask(void *param){
} }
// N2K sysTime is double in n2klib // N2K sysTime is double in n2klib
double sysTime = (dt.hour() * 3600) + (dt.minute() * 60) + dt.second(); double sysTime = (dt.hour() * 3600) + (dt.minute() * 60) + dt.second();
// WHY? isnan should always fail here if(!isnan(daysAt1970) && !isnan(sysTime)){
//if(!isnan(daysAt1970) && !isnan(sysTime)){ //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->getLogger()->logDebug(GwLog::LOG,"Send PGN126992: %10d %10d",daysAt1970, (uint16_t)sysTime);
SetN2kPGN126992(N2kMsg,0,daysAt1970,sysTime,N2ktimes_LocalCrystalClock);
api->sendN2kMessage(N2kMsg);
}
}
}
// Send date and time from software RTC (iRTC)
if (iRTC_ready == true && RTC_ready == false && GPS_ready == false) {
// Use internal RTC feature
sensors.rtcTime = rtc.getTimeStruct(); // Save software RTC values in SensorData
// 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};
uint16_t switchYear = ((sensors.rtcTime.tm_year-1)-1968)/4 - ((sensors.rtcTime.tm_year-1)-1900)/100 + ((sensors.rtcTime.tm_year-1)-1600)/400;
long daysAt1970 = (sensors.rtcTime.tm_year-1970)*365 + switchYear + daysOfYear[sensors.rtcTime.tm_mon-1] + sensors.rtcTime.tm_mday-1;
// If switch year then add one day
if ((sensors.rtcTime.tm_mon > 2) && (sensors.rtcTime.tm_year % 4 == 0 && (sensors.rtcTime.tm_year % 100 != 0 || sensors.rtcTime.tm_year % 400 == 0))) {
daysAt1970 += 1;
}
// N2K sysTime is double in n2klib
double sysTime = (sensors.rtcTime.tm_hour * 3600) + (sensors.rtcTime.tm_min * 60) + sensors.rtcTime.tm_sec;
if(!isnan(daysAt1970) && !isnan(sysTime)){
//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->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->getLogger()->logDebug(GwLog::LOG,"Send PGN126992: %10d %10d",daysAt1970, (uint16_t)sysTime); //api->getLogger()->logDebug(GwLog::LOG,"Send PGN126992: %10d %10d",daysAt1970, (uint16_t)sysTime);
SetN2kPGN126992(N2kMsg,0,daysAt1970,sysTime,N2ktimes_LocalCrystalClock); SetN2kPGN126992(N2kMsg,0,daysAt1970,sysTime,N2ktimes_LocalCrystalClock);
api->sendN2kMessage(N2kMsg); api->sendN2kMessage(N2kMsg);
// }
} }
} else if (sensors.rtcValid) {
// use internal rtc feature
sensors.rtcTime = rtc.getTimeStruct();
} }
} }
@@ -594,11 +651,6 @@ void sensorTask(void *param){
// Send data from environment sensor to N2K all 2s // Send data from environment sensor to N2K all 2s
if(millis() > starttime6 + 2000){ if(millis() > starttime6 + 2000){
starttime6 = millis(); starttime6 = millis();
// DEBUG
UBaseType_t stackfree = uxTaskGetStackHighWaterMark(NULL);
api->getLogger()->logDebug(GwLog::LOG, "obpSensortask Stack=%d", stackfree);
unsigned char TempSource = 2; // Inside temperature unsigned char TempSource = 2; // Inside temperature
unsigned char PressureSource = 0; // Atmospheric pressure unsigned char PressureSource = 0; // Atmospheric pressure
unsigned char HumiditySource = 0; // Inside humidity unsigned char HumiditySource = 0; // Inside humidity
@@ -816,12 +868,8 @@ void sensorTask(void *param){
vTaskDelete(NULL); vTaskDelete(NULL);
} }
void createSensorTask(SharedData *shared){ void createSensorTask(SharedData *shared){
TaskHandle_t xHandle = NULL; xTaskCreate(sensorTask,"readSensors",10000,shared,3,NULL);
GwLog *logger = shared->api->getLogger();
esp_err_t err = xTaskCreate(sensorTask, "readSensors", configMINIMAL_STACK_SIZE + 8192, shared, 3, &xHandle);
if ( err != pdPASS) {
logger->logDebug(GwLog::ERROR, "Failed to create sensor task! (err=%d)", err);
};
} }
#endif #endif

View File

@@ -1,4 +1,3 @@
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once #pragma once
#include "GwSynchronized.h" #include "GwSynchronized.h"
#include "GwApi.h" #include "GwApi.h"

View File

@@ -28,9 +28,8 @@ Chart::Chart(RingBuffer<uint16_t>& dataBuf, double dfltRng, CommonData& common,
fgColor = commonData->fgcolor; fgColor = commonData->fgcolor;
bgColor = commonData->bgcolor; bgColor = commonData->bgcolor;
// display dimensions (avoid calling width()/height() on incomplete LGFX type) dWidth = getdisplay().width();
dWidth = epd->width(); dHeight = getdisplay().height();
dHeight = epd->height();
dataBuf.getMetaData(dbName, dbFormat); dataBuf.getMetaData(dbName, dbFormat);
dbMIN_VAL = dataBuf.getMinVal(); dbMIN_VAL = dataBuf.getMinVal();
@@ -439,15 +438,15 @@ void Chart::drawChrtTimeAxis(const char chrtDir, const int8_t chrtSz, const int8
char sTime[6]; char sTime[6];
int timeRng = chrtIntv * 4; // chart time interval: [1] 4 min., [2] 8 min., [3] 12 min., [4] 16 min., [8] 32 min. int timeRng = chrtIntv * 4; // chart time interval: [1] 4 min., [2] 8 min., [3] 12 min., [4] 16 min., [8] 32 min.
epd->setFont(&Ubuntu_Bold8pt8b); getdisplay().setFont(&Ubuntu_Bold8pt8b);
epd->setTextColor(fgColor); getdisplay().setTextColor(fgColor);
axSlots = 5; // number of axis labels axSlots = 5; // number of axis labels
intv = timAxis / (axSlots - 1); // minutes per chart axis interval (interval is 1 less than axSlots) intv = timAxis / (axSlots - 1); // minutes per chart axis interval (interval is 1 less than axSlots)
i = timeRng; // Chart axis label start at -32, -16, -12, ... minutes i = timeRng; // Chart axis label start at -32, -16, -12, ... minutes
if (chrtDir == HORIZONTAL) { if (chrtDir == HORIZONTAL) {
epd->fillRect(0, cRoot.y, dWidth, 2, fgColor); getdisplay().fillRect(0, cRoot.y, dWidth, 2, fgColor);
for (float j = 0; j < timAxis - 1; j += intv) { // fill time axis with values but keep area free on right hand side for value label for (float j = 0; j < timAxis - 1; j += intv) { // fill time axis with values but keep area free on right hand side for value label
@@ -455,7 +454,7 @@ void Chart::drawChrtTimeAxis(const char chrtDir, const int8_t chrtSz, const int8
int tOffset = j == 0 ? 13 : -4; int tOffset = j == 0 ? 13 : -4;
snprintf(sTime, sizeof(sTime), "-%.0f", i); snprintf(sTime, sizeof(sTime), "-%.0f", i);
drawTextCenter(cRoot.x + j + tOffset, cRoot.y - 8, sTime); drawTextCenter(cRoot.x + j + tOffset, cRoot.y - 8, sTime);
epd->drawLine(cRoot.x + j, cRoot.y, cRoot.x + j, cRoot.y + 5, fgColor); // draw short vertical time mark getdisplay().drawLine(cRoot.x + j, cRoot.y, cRoot.x + j, cRoot.y + 5, fgColor); // draw short vertical time mark
i -= chrtIntv; i -= chrtIntv;
} }
@@ -466,12 +465,12 @@ void Chart::drawChrtTimeAxis(const char chrtDir, const int8_t chrtSz, const int8
i -= chrtIntv; // we start not at top chart position i -= chrtIntv; // we start not at top chart position
snprintf(sTime, sizeof(sTime), "-%.0f", i); snprintf(sTime, sizeof(sTime), "-%.0f", i);
epd->drawLine(cRoot.x, cRoot.y + j, cRoot.x + valAxis, cRoot.y + j, fgColor); // Grid line getdisplay().drawLine(cRoot.x, cRoot.y + j, cRoot.x + valAxis, cRoot.y + j, fgColor); // Grid line
if (chrtSz == FULL_SIZE) { // full size chart if (chrtSz == FULL_SIZE) { // full size chart
epd->fillRect(0, cRoot.y + j - 9, 32, 15, bgColor); // clear small area to remove potential chart lines getdisplay().fillRect(0, cRoot.y + j - 9, 32, 15, bgColor); // clear small area to remove potential chart lines
epd->setCursor((4 - strlen(sTime)) * 7, cRoot.y + j + 3); // time value; print left screen; value right-formated getdisplay().setCursor((4 - strlen(sTime)) * 7, cRoot.y + j + 3); // time value; print left screen; value right-formated
epd->printf("%s", sTime); // Range value getdisplay().printf("%s", sTime); // Range value
} else if (chrtSz == HALF_SIZE_RIGHT) { // half size chart; right side } else if (chrtSz == HALF_SIZE_RIGHT) { // half size chart; right side
drawTextCenter(dWidth / 2, cRoot.y + j, sTime); // time value; print mid screen drawTextCenter(dWidth / 2, cRoot.y + j, sTime); // time value; print mid screen
} }
@@ -486,7 +485,7 @@ void Chart::drawChrtValAxis(const char chrtDir, const int8_t chrtSz, bool prntNa
constexpr bool NO_LABEL = false; constexpr bool NO_LABEL = false;
constexpr bool LABEL = true; constexpr bool LABEL = true;
epd->setTextColor(fgColor); getdisplay().setTextColor(fgColor);
if (chrtDir == HORIZONTAL) { if (chrtDir == HORIZONTAL) {
@@ -495,7 +494,7 @@ void Chart::drawChrtValAxis(const char chrtDir, const int8_t chrtSz, bool prntNa
font = &Ubuntu_Bold12pt8b; font = &Ubuntu_Bold12pt8b;
// print buffer data name on right hand side of time axis (max. size 5 characters) // print buffer data name on right hand side of time axis (max. size 5 characters)
epd->setFont(font); getdisplay().setFont(font);
drawTextRalign(cRoot.x + timAxis, cRoot.y - 3, dbName.substring(0, 5)); drawTextRalign(cRoot.x + timAxis, cRoot.y - 3, dbName.substring(0, 5));
if (chrtDataFmt == WIND) { if (chrtDataFmt == WIND) {
@@ -513,7 +512,7 @@ void Chart::drawChrtValAxis(const char chrtDir, const int8_t chrtSz, bool prntNa
if (prntName) { if (prntName) {
// print buffer data name on right hand side of time axis (max. size 5 characters) // print buffer data name on right hand side of time axis (max. size 5 characters)
epd->setFont(font); getdisplay().setFont(font);
drawTextRalign(cRoot.x + timAxis, cRoot.y - 3, dbName.substring(0, 5)); drawTextRalign(cRoot.x + timAxis, cRoot.y - 3, dbName.substring(0, 5));
} }
@@ -525,7 +524,7 @@ void Chart::drawChrtValAxis(const char chrtDir, const int8_t chrtSz, bool prntNa
if (chrtSz == FULL_SIZE) { if (chrtSz == FULL_SIZE) {
font = &Ubuntu_Bold12pt8b; font = &Ubuntu_Bold12pt8b;
epd->setFont(font); // use larger font getdisplay().setFont(font); // use larger font
drawTextRalign(cRoot.x + (valAxis * 0.42), cRoot.y - 2, dbName.substring(0, 6)); // print buffer data name (max. size 5 characters) drawTextRalign(cRoot.x + (valAxis * 0.42), cRoot.y - 2, dbName.substring(0, 6)); // print buffer data name (max. size 5 characters)
} else { } else {
@@ -543,23 +542,23 @@ void Chart::prntCurrValue(const char direction, GwApi::BoatValue& currValue)
const int xPosVal = (direction == HORIZONTAL) ? cRoot.x + (timAxis / 2) - 56 : cRoot.x + 32; const int xPosVal = (direction == HORIZONTAL) ? cRoot.x + (timAxis / 2) - 56 : cRoot.x + 32;
const int yPosVal = (direction == HORIZONTAL) ? cRoot.y + valAxis - 7 : cRoot.y + timAxis - 7; const int yPosVal = (direction == HORIZONTAL) ? cRoot.y + valAxis - 7 : cRoot.y + timAxis - 7;
FormattedData frmtDbData = commonData->fmt->formatValue(&currValue, *commonData, NO_SIMUDATA); FormattedData frmtDbData = formatValue(&currValue, *commonData, NO_SIMUDATA);
String sdbValue = frmtDbData.svalue; // value as formatted string String sdbValue = frmtDbData.svalue; // value as formatted string
String dbUnit = frmtDbData.unit; // Unit of value; limit length to 3 characters String dbUnit = frmtDbData.unit; // Unit of value; limit length to 3 characters
epd->fillRect(xPosVal - 1, yPosVal - 35, 128, 41, bgColor); // Clear area for TWS value getdisplay().fillRect(xPosVal - 1, yPosVal - 35, 128, 41, bgColor); // Clear area for TWS value
epd->drawRect(xPosVal, yPosVal - 34, 126, 40, fgColor); // Draw box for TWS value getdisplay().drawRect(xPosVal, yPosVal - 34, 126, 40, fgColor); // Draw box for TWS value
epd->setFont(&DSEG7Classic_BoldItalic16pt7b); getdisplay().setFont(&DSEG7Classic_BoldItalic16pt7b);
epd->setCursor(xPosVal + 1, yPosVal); getdisplay().setCursor(xPosVal + 1, yPosVal);
epd->print(sdbValue); // value getdisplay().print(sdbValue); // value
epd->setFont(&Ubuntu_Bold10pt8b); getdisplay().setFont(&Ubuntu_Bold10pt8b);
epd->setCursor(xPosVal + 76, yPosVal - 17); getdisplay().setCursor(xPosVal + 76, yPosVal - 17);
epd->print(dbName.substring(0, 3)); // Name, limited to 3 characters getdisplay().print(dbName.substring(0, 3)); // Name, limited to 3 characters
epd->setFont(&Ubuntu_Bold8pt8b); getdisplay().setFont(&Ubuntu_Bold8pt8b);
epd->setCursor(xPosVal + 76, yPosVal + 0); getdisplay().setCursor(xPosVal + 76, yPosVal + 0);
epd->print(dbUnit); // Unit getdisplay().print(dbUnit); // Unit
} }
// print message for no valid data availabletemplate <typename T> // print message for no valid data availabletemplate <typename T>
@@ -567,7 +566,7 @@ void Chart::prntNoValidData(const char direction)
{ {
Pos p; Pos p;
epd->setFont(&Ubuntu_Bold10pt8b); getdisplay().setFont(&Ubuntu_Bold10pt8b);
if (direction == HORIZONTAL) { if (direction == HORIZONTAL) {
p.x = cRoot.x + (timAxis / 2); p.x = cRoot.x + (timAxis / 2);
@@ -577,7 +576,7 @@ void Chart::prntNoValidData(const char direction)
p.y = cRoot.y + (timAxis / 2) - 10; p.y = cRoot.y + (timAxis / 2) - 10;
} }
epd->fillRect(p.x - 37, p.y - 10, 78, 24, bgColor); // Clear area for message getdisplay().fillRect(p.x - 37, p.y - 10, 78, 24, bgColor); // Clear area for message
drawTextCenter(p.x, p.y, "No data"); drawTextCenter(p.x, p.y, "No data");
LOG_DEBUG(GwLog::LOG, "Page chart <%s>: No valid data available", dbName); LOG_DEBUG(GwLog::LOG, "Page chart <%s>: No valid data available", dbName);
@@ -624,28 +623,28 @@ double Chart::getAngleRng(const double center, size_t amount)
double cVal; double cVal;
char sVal[7]; char sVal[7];
epd->fillRect(cRoot.x, cRoot.y, valAxis, 2, fgColor); // top chart line getdisplay().fillRect(cRoot.x, cRoot.y, valAxis, 2, fgColor); // top chart line
epd->setFont(font); getdisplay().setFont(font);
cVal = chrtMin; cVal = chrtMin;
cVal = commonData->fmt->convertValue(cVal, dbName, dbFormat, *commonData); // value (converted) cVal = convertValue(cVal, dbName, dbFormat, *commonData); // value (converted)
snprintf(sVal, sizeof(sVal), "%.0f", round(cVal)); snprintf(sVal, sizeof(sVal), "%.0f", round(cVal));
epd->setCursor(cRoot.x, cRoot.y - 2); getdisplay().setCursor(cRoot.x, cRoot.y - 2);
epd->printf("%s", sVal); // Range low end getdisplay().printf("%s", sVal); // Range low end
cVal = chrtMid; cVal = chrtMid;
cVal = commonData->fmt->convertValue(cVal, dbName, dbFormat, *commonData); // value (converted) cVal = convertValue(cVal, dbName, dbFormat, *commonData); // value (converted)
snprintf(sVal, sizeof(sVal), "%.0f", round(cVal)); snprintf(sVal, sizeof(sVal), "%.0f", round(cVal));
drawTextCenter(cRoot.x + (valAxis / 2), cRoot.y - 9, sVal); // Range mid end drawTextCenter(cRoot.x + (valAxis / 2), cRoot.y - 9, sVal); // Range mid end
cVal = chrtMax; cVal = chrtMax;
cVal = commonData->fmt->convertValue(cVal, dbName, dbFormat, *commonData); // value (converted) cVal = convertValue(cVal, dbName, dbFormat, *commonData); // value (converted)
snprintf(sVal, sizeof(sVal), "%.0f", round(cVal)); snprintf(sVal, sizeof(sVal), "%.0f", round(cVal));
drawTextRalign(cRoot.x + valAxis - 2, cRoot.y - 2, sVal); // Range high end drawTextRalign(cRoot.x + valAxis - 2, cRoot.y - 2, sVal); // Range high end
// draw vertical grid lines for each axis label // draw vertical grid lines for each axis label
for (int j = 0; j <= valAxis; j += (valAxis / 2)) { for (int j = 0; j <= valAxis; j += (valAxis / 2)) {
epd->drawLine(cRoot.x + j, cRoot.y, cRoot.x + j, cRoot.y + timAxis, fgColor); getdisplay().drawLine(cRoot.x + j, cRoot.y, cRoot.x + j, cRoot.y + timAxis, fgColor);
} }
} }
@@ -664,12 +663,12 @@ void Chart::prntHorizChartThreeValueAxisLabel(const GFXfont* font)
xOffset = 51; xOffset = 51;
yOffset = 18; yOffset = 18;
} }
epd->setFont(font); getdisplay().setFont(font);
// convert & round chart bottom+top label to next range step // convert & round chart bottom+top label to next range step
chrtMin = commonData->fmt->convertValue(this->chrtMin, dbName, dbFormat, *commonData); chrtMin = convertValue(this->chrtMin, dbName, dbFormat, *commonData);
chrtMid = commonData->fmt->convertValue(this->chrtMid, dbName, dbFormat, *commonData); chrtMid = convertValue(this->chrtMid, dbName, dbFormat, *commonData);
chrtMax = commonData->fmt->convertValue(this->chrtMax, dbName, dbFormat, *commonData); chrtMax = convertValue(this->chrtMax, dbName, dbFormat, *commonData);
chrtMin = std::round(chrtMin * 100.0) / 100.0; chrtMin = std::round(chrtMin * 100.0) / 100.0;
chrtMid = std::round(chrtMid * 100.0) / 100.0; chrtMid = std::round(chrtMid * 100.0) / 100.0;
chrtMax = std::round(chrtMax * 100.0) / 100.0; chrtMax = std::round(chrtMax * 100.0) / 100.0;
@@ -677,22 +676,22 @@ void Chart::prntHorizChartThreeValueAxisLabel(const GFXfont* font)
// print top axis label // print top axis label
axLabel = (chrtDataFmt == SPEED || chrtDataFmt == TEMPERATURE) ? chrtMax : chrtMin; axLabel = (chrtDataFmt == SPEED || chrtDataFmt == TEMPERATURE) ? chrtMax : chrtMin;
sVal = formatLabel(axLabel); sVal = formatLabel(axLabel);
epd->fillRect(cRoot.x, cRoot.y + 2, xOffset + 3, yOffset, bgColor); // Clear small area to remove potential chart lines getdisplay().fillRect(cRoot.x, cRoot.y + 2, xOffset + 3, yOffset, bgColor); // Clear small area to remove potential chart lines
drawTextRalign(cRoot.x + xOffset, cRoot.y + yOffset, sVal); // range value drawTextRalign(cRoot.x + xOffset, cRoot.y + yOffset, sVal); // range value
// print mid axis label // print mid axis label
axLabel = chrtMid; axLabel = chrtMid;
sVal = formatLabel(axLabel); sVal = formatLabel(axLabel);
epd->fillRect(cRoot.x, cRoot.y + (valAxis / 2) - 8, xOffset + 3, 16, bgColor); // Clear small area to remove potential chart lines getdisplay().fillRect(cRoot.x, cRoot.y + (valAxis / 2) - 8, xOffset + 3, 16, bgColor); // Clear small area to remove potential chart lines
drawTextRalign(cRoot.x + xOffset, cRoot.y + (valAxis / 2) + 6, sVal); // range value drawTextRalign(cRoot.x + xOffset, cRoot.y + (valAxis / 2) + 6, sVal); // range value
epd->drawLine(cRoot.x + xOffset + 3, cRoot.y + (valAxis / 2), cRoot.x + timAxis, cRoot.y + (valAxis / 2), fgColor); getdisplay().drawLine(cRoot.x + xOffset + 3, cRoot.y + (valAxis / 2), cRoot.x + timAxis, cRoot.y + (valAxis / 2), fgColor);
// print bottom axis label // print bottom axis label
axLabel = (chrtDataFmt == SPEED || chrtDataFmt == TEMPERATURE) ? chrtMin : chrtMax; axLabel = (chrtDataFmt == SPEED || chrtDataFmt == TEMPERATURE) ? chrtMin : chrtMax;
sVal = formatLabel(axLabel); sVal = formatLabel(axLabel);
epd->fillRect(cRoot.x, cRoot.y + valAxis - 14, xOffset + 3, 15, bgColor); // Clear small area to remove potential chart lines getdisplay().fillRect(cRoot.x, cRoot.y + valAxis - 14, xOffset + 3, 15, bgColor); // Clear small area to remove potential chart lines
drawTextRalign(cRoot.x + xOffset, cRoot.y + valAxis, sVal); // range value drawTextRalign(cRoot.x + xOffset, cRoot.y + valAxis, sVal); // range value
epd->drawLine(cRoot.x + xOffset + 3, cRoot.y + valAxis, cRoot.x + timAxis, cRoot.y + valAxis, fgColor); getdisplay().drawLine(cRoot.x + xOffset + 3, cRoot.y + valAxis, cRoot.x + timAxis, cRoot.y + valAxis, fgColor);
} }
// print value axis label with multiple axis lines for horizontal chart // print value axis label with multiple axis lines for horizontal chart
@@ -708,12 +707,12 @@ void Chart::prntHorizChartMultiValueAxisLabel(const GFXfont* font)
} else if (font == &Ubuntu_Bold12pt8b) { } else if (font == &Ubuntu_Bold12pt8b) {
xOffset = 50; xOffset = 50;
} }
epd->setFont(font); getdisplay().setFont(font);
chrtMin = commonData->fmt->convertValue(this->chrtMin, dbName, dbFormat, *commonData); chrtMin = convertValue(this->chrtMin, dbName, dbFormat, *commonData);
// chrtMin = std::floor(chrtMin / rngStep) * rngStep; // chrtMin = std::floor(chrtMin / rngStep) * rngStep;
chrtMin = std::round(chrtMin * 100.0) / 100.0; chrtMin = std::round(chrtMin * 100.0) / 100.0;
chrtMax = commonData->fmt->convertValue(this->chrtMax, dbName, dbFormat, *commonData); chrtMax = convertValue(this->chrtMax, dbName, dbFormat, *commonData);
// chrtMax = std::ceil(chrtMax / rngStep) * rngStep; // chrtMax = std::ceil(chrtMax / rngStep) * rngStep;
chrtMax = std::round(chrtMax * 100.0) / 100.0; chrtMax = std::round(chrtMax * 100.0) / 100.0;
chrtRng = std::round((chrtMax - chrtMin) * 100) / 100; chrtRng = std::round((chrtMax - chrtMin) * 100) / 100;
@@ -738,9 +737,9 @@ void Chart::prntHorizChartMultiValueAxisLabel(const GFXfont* font)
for (int j = loopStrt; (loopStp > 0) ? (j < loopEnd) : (j > loopEnd); j += loopStp) { for (int j = loopStrt; (loopStp > 0) ? (j < loopEnd) : (j > loopEnd); j += loopStp) {
sVal = formatLabel(axLabel); sVal = formatLabel(axLabel);
epd->fillRect(cRoot.x, cRoot.y + j - 11, xOffset + 3, 21, bgColor); // Clear small area to remove potential chart lines getdisplay().fillRect(cRoot.x, cRoot.y + j - 11, xOffset + 3, 21, bgColor); // Clear small area to remove potential chart lines
drawTextRalign(cRoot.x + xOffset, cRoot.y + j + 7, sVal); // range value drawTextRalign(cRoot.x + xOffset, cRoot.y + j + 7, sVal); // range value
epd->drawLine(cRoot.x + xOffset + 3, cRoot.y + j, cRoot.x + timAxis, cRoot.y + j, fgColor); getdisplay().drawLine(cRoot.x + xOffset + 3, cRoot.y + j, cRoot.x + timAxis, cRoot.y + j, fgColor);
axLabel += axIntv; axLabel += axIntv;
} }
@@ -752,12 +751,12 @@ void Chart::drawBoldLine(const int16_t x1, const int16_t y1, const int16_t x2, c
int16_t dx = std::abs(x2 - x1); int16_t dx = std::abs(x2 - x1);
int16_t dy = std::abs(y2 - y1); int16_t dy = std::abs(y2 - y1);
epd->drawLine(x1, y1, x2, y2, fgColor); getdisplay().drawLine(x1, y1, x2, y2, fgColor);
if (dx >= dy) { // line has horizontal tendency if (dx >= dy) { // line has horizontal tendency
epd->drawLine(x1, y1 - 1, x2, y2 - 1, fgColor); getdisplay().drawLine(x1, y1 - 1, x2, y2 - 1, fgColor);
} else { // line has vertical tendency } else { // line has vertical tendency
epd->drawLine(x1 - 1, y1, x2 - 1, y2, fgColor); getdisplay().drawLine(x1 - 1, y1, x2 - 1, y2, fgColor);
} }
} }
@@ -770,7 +769,7 @@ String Chart::convNformatLabel(const double& label)
tmpBVal.setFormat(dbFormat); tmpBVal.setFormat(dbFormat);
tmpBVal.valid = true; tmpBVal.valid = true;
tmpBVal.value = label; tmpBVal.value = label;
sVal = commonData->fmt->formatValue(&tmpBVal, *commonData, NO_SIMUDATA).svalue; // Formatted value as string including unit conversion and switching decimal places sVal = formatValue(&tmpBVal, *commonData, NO_SIMUDATA).svalue; // Formatted value as string including unit conversion and switching decimal places
if (sVal.length() > 0 && sVal[0] == '!') { if (sVal.length() > 0 && sVal[0] == '!') {
sVal = sVal.substring(1); // cut leading "!" created at OBPFormatter; doesn't work for other fonts than 7SEG sVal = sVal.substring(1); // cut leading "!" created at OBPFormatter; doesn't work for other fonts than 7SEG
} }

View File

@@ -1,178 +0,0 @@
// SPDX-License-Identifier: GPL-2.0-or-later
#if defined BOARD_OBP60S3 || defined BOARD_OBP40S3
#include "Pagedata.h"
#include "OBP60Extensions.h"
/*
AIS Overview
- circle with certain range, e.g. 5nm
- AIS-Targets in range with speed and heading
- perhaps collision alarm
Data: LAT LON SOG HDT
Feature possibilities
- switch between North up / Heading up
- filter
- zoom
- special vessel symbols
*/
class PageAIS : public Page
{
private:
int scale = 5; // Radius of display circle in nautical miles
bool alarm = false;
bool alarm_enabled = false;
int alarm_range = 3;
char mode = 'N'; // (N)ormal, (C)onfig
void displayModeNormal(PageData &pageData) {
// TBD Boatvalues: ...
logger->logDebug(GwLog::DEBUG,"Drawing at PageAIS");
Point c = {200, 150}; // center = current boat position
uint16_t r = 125;
const std::vector<Point> pts_boat = { // polygon lines
{c.x - 5, c.y},
{c.x - 5, c.y - 10},
{c.x, c.y - 16},
{c.x + 5, c.y - 10},
{c.x + 5, c.y}
};
drawPoly(pts_boat, commonData->fgcolor);
// Title and corner value headings
epd->setTextColor(commonData->fgcolor);
epd->setFont(&Ubuntu_Bold12pt8b);
epd->setCursor(8, 48);
epd->print("AIS");
// zoom scale
epd->drawLine(c.x + 10, c.y, c.x + r - 4, c.y, commonData->fgcolor);
// arrow left
epd->drawLine(c.x + 10, c.y, c.x + 16, c.y - 4, commonData->fgcolor);
epd->drawLine(c.x + 10, c.y, c.x + 16, c.y + 4, commonData->fgcolor);
// arrow right
epd->drawLine(c.x + r - 4, c.y, c.x + r - 10, c.y - 4, commonData->fgcolor);
epd->drawLine(c.x + r - 4, c.y, c.x + r - 10, c.y + 4, commonData->fgcolor);
epd->setFont(&Ubuntu_Bold8pt8b);
drawTextCenter(c.x + r / 2, c.y + 8, String(scale) + "nm");
}
void displayModeConfig() {
epd->setTextColor(commonData->fgcolor);
epd->setFont(&Ubuntu_Bold12pt8b);
epd->setCursor(8, 48);
epd->print("AIS configuration");
epd->setFont(&Ubuntu_Bold8pt8b);
// TODO menu
}
public:
PageAIS(CommonData &common) : Page(common)
{
logger->logDebug(GwLog::LOG, "Instantiate PageAIS");
alarm_range = 3;
}
void setupKeys(){
Page::setupKeys();
commonData->keydata[0].label = "MODE";
commonData->keydata[1].label = "ALARM";
}
#ifdef BOARD_OBP60S3
int handleKey(int key){
if (key == 1) { // Switch between normal and config mode
if (mode == 'N') {
mode = 'C';
} else {
mode = 'N';
}
return 0;
}
if (key == 11) { // Code for keylock
commonData->keylock = !commonData->keylock;
return 0;
}
return key;
}
#endif
#ifdef BOARD_OBP40S3
int handleKey(int key) {
if (key == 1) { // Switch between normal and config mode
if (mode == 'N') {
mode = 'C';
} else {
mode = 'N';
}
return 0;
}
if (key == 11) { // Code for keylock
commonData->keylock = !commonData->keylock;
return 0;
}
return key;
}
#endif
void displayNew(PageData &pageData) {
#ifdef BOARD_OBP60S3
// Clear optical warning
if (flashLED == "Limit Violation") {
setBlinkingLED(false);
setFlashLED(false);
}
#endif
};
int displayPage(PageData &pageData){
// Logging boat values
logger->logDebug(GwLog::LOG, "Drawing at PageAIS; Mode=%c", mode);
// Set display in partial refresh mode
epd->setPartialWindow(0, 0, epd->width(), epd->height());
if (mode == 'N') {
displayModeNormal(pageData);
} else if (mode == 'C') {
displayModeConfig();
}
return PAGE_UPDATE;
};
};
static Page *createPage(CommonData &common){
return new PageAIS(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 registerPageAIS(
"AIS", // Page name
createPage, // Action
0, // Number of bus values depends on selection in Web configuration
{"LAT", "LON", "SOG", "HDT"}, // Names of bus values undepends on selection in Web configuration (refer GwBoatData.h)
true // Show display header on/off
);
#endif

View File

@@ -1,53 +1,70 @@
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
#if defined BOARD_OBP60S3 || defined BOARD_OBP40S3 #if defined BOARD_OBP60S3 || defined BOARD_OBP40S3
#include "Pagedata.h"
#include "OBP60Extensions.h"
#include "ConfigMenu.h"
/* /*
Anchor overview with additional associated data
This page is in experimental stage so be warned! This page is in experimental stage so be warned!
North is up. North is up.
Anchor page with background map from mapservice
Boatdata used Boatdata used
DBS - Water depth DBS - Water depth
HDT - Boat heading HDT - Boat heading, true
AWS - Wind strength; Boat not moving so we assume AWS=TWS and AWD=TWD AWS - Wind strength; Boat not moving so we assume AWS=TWS and AWD=TWD
AWD - Wind direction AWD - Wind direction
LAT/LON - Boat position, current LAT/LON - Boat position, current
HDOP - Position error HDOP - Position error, horizontal
This is the fist page to contain a configuration page with Raise function in device OBP40 has to be done inside config mode
data entry option. because of limited number of buttons.
Also it will make use of the new alarm function.
Data
Anchor position lat/lon
Depth at anchor position
Chain length used
Boat position current
Depth at boat position
Boat heading
Wind direction
Wind strength
Alarm j/n
Alarm radius
GPS position error
Timestamp while dropping anchor
Drop / raise function in device OBP40 has to be done inside
config mode because of limited number of buttons.
TODO
gzip for data transfer,
miniz.c from ROM?
manually inflating with tinflate from ROM
Save position in FRAM Save position in FRAM
Alarm: gps fix lost Alarm: gps fix lost
switch unit feet/meter switch unit feet/meter
force map update if new position is different from old position by
a certain level (e.g. 10m)
windlass integration
chain counter
Map service options / URL parameters
- mandatory
lat: latitude
lon: longitude
width: image width in px (400)
height: image height in px (300)
- optional
zoom: zoom level, default 15
mrot: map rotation angle in degrees
mtype: map type, default="Open Street Map"
dtype: dithering type, default="Atkinson"
cutout: image cutout type 0=none
alpha: alpha blending for cutout
tab: tab size, 0=none
border: border line zize in px, default 2
symbol: synmol number, default=2 triangle
srot: symbol rotation in degrees
ssize: symbol size in px, default=15
grid: show map grid
*/ */
#include <WiFi.h>
#include <HTTPClient.h>
#include "Pagedata.h"
#include "OBP60Extensions.h"
#include "ConfigMenu.h"
// #include "miniz.h" // devices without PSRAM use <rom/miniz.h>
// extern "C" {
#include "rom/miniz.h"
// }
#define anchor_width 16 #define anchor_width 16
#define anchor_height 16 #define anchor_height 16
static unsigned char anchor_bits[] = { static unsigned char anchor_bits[] PROGMEM = {
0x80, 0x01, 0x40, 0x02, 0x40, 0x02, 0x80, 0x01, 0xf0, 0x0f, 0x80, 0x01, 0x80, 0x01, 0x40, 0x02, 0x40, 0x02, 0x80, 0x01, 0xf0, 0x0f, 0x80, 0x01,
0x80, 0x01, 0x88, 0x11, 0x8c, 0x31, 0x8e, 0x71, 0x84, 0x21, 0x86, 0x61, 0x80, 0x01, 0x88, 0x11, 0x8c, 0x31, 0x8e, 0x71, 0x84, 0x21, 0x86, 0x61,
0x86, 0x61, 0xfc, 0x3f, 0xf8, 0x1f, 0x80, 0x01 }; 0x86, 0x61, 0xfc, 0x3f, 0xf8, 0x1f, 0x80, 0x01 };
@@ -55,16 +72,35 @@ static unsigned char anchor_bits[] = {
class PageAnchor : public Page class PageAnchor : public Page
{ {
private: private:
char mode = 'N'; // (N)ormal, (C)onfig
int8_t editmode = -1; // marker for menu/edit/set function
ConfigMenu *menu;
//uint8_t *mapbuf = new uint8_t[10000]; // 8450 Byte without header
//int mapbuf_size = 10000;
//uint8_t *mapbuf = (uint8_t*) heap_caps_malloc(mapbuf_size, MALLOC_CAP_SPIRAM);
GFXcanvas1 *canvas;
const uint16_t map_width = 264;
const uint16_t map_height = 260;
bool map_valid = false;
char map_service = 'R'; // (O)BP Service, (L)ocal Service, (R)emote Service
double map_lat = 0; // current center of valid map
double map_lon = 0;
String server_name; // server with map service
uint16_t server_port = 80;
String tile_path;
String lengthformat; String lengthformat;
int scale = 50; // Radius of display circle in meter double scale = 50; // Radius of display circle in meter, depends on lat
uint8_t zoom = 15; // map zoom level
bool alarm = false; bool alarm = false;
bool alarm_enabled = false; bool alarm_enabled = false;
uint8_t alarm_range; uint8_t alarm_range;
uint8_t chain_length; uint8_t chain_length;
uint8_t chain; uint8_t chain = 0;
bool anchor_set = false; bool anchor_set = false;
double anchor_lat; double anchor_lat;
@@ -72,37 +108,70 @@ private:
double anchor_depth; double anchor_depth;
int anchor_ts; // time stamp anchor dropped int anchor_ts; // time stamp anchor dropped
char mode = 'N'; // (N)ormal, (C)onfig GwApi::BoatValue *bv_dbs; // depth below surface
int8_t editmode = -1; // marker for menu/edit/set function GwApi::BoatValue *bv_hdt; // true heading
GwApi::BoatValue *bv_aws; // apparent wind speed
GwApi::BoatValue *bv_awd; // apparent wind direction
GwApi::BoatValue *bv_lat; // latitude, current
GwApi::BoatValue *bv_lon; // longitude, current
GwApi::BoatValue *bv_hdop; // horizontal position error
ConfigMenu *menu; bool simulation = false;
int last_mapsize = 0;
String errmsg = "";
int loops;
int readbytes = 0;
void displayModeNormal(PageData &pageData) { void displayModeNormal(PageData &pageData) {
// Boatvalues: DBS, HDT, AWS, AWD, LAT, LON, HDOP // get currrent boatvalues
GwApi::BoatValue *bv_dbs = pageData.values[0]; // DBS bv_dbs = pageData.values[0]; // DBS
String sval_dbs = commonData->fmt->formatValue(bv_dbs, *commonData).svalue; String sval_dbs = formatValue(bv_dbs, *commonData).svalue;
String sunit_dbs = commonData->fmt->formatValue(bv_dbs, *commonData).unit; String sunit_dbs = formatValue(bv_dbs, *commonData).unit;
GwApi::BoatValue *bv_hdt = pageData.values[1]; // HDT bv_hdt = pageData.values[1]; // HDT
String sval_hdt = commonData->fmt->formatValue(bv_hdt, *commonData).svalue; String sval_hdt = formatValue(bv_hdt, *commonData).svalue;
GwApi::BoatValue *bv_aws = pageData.values[2]; // AWS bv_aws = pageData.values[2]; // AWS
String sval_aws = commonData->fmt->formatValue(bv_aws, *commonData).svalue; String sval_aws = formatValue(bv_aws, *commonData).svalue;
String sunit_aws = commonData->fmt->formatValue(bv_aws, *commonData).unit; String sunit_aws = formatValue(bv_aws, *commonData).unit;
GwApi::BoatValue *bv_awd = pageData.values[3]; // AWD bv_awd = pageData.values[3]; // AWD
String sval_awd = commonData->fmt->formatValue(bv_awd, *commonData).svalue; String sval_awd = formatValue(bv_awd, *commonData).svalue;
GwApi::BoatValue *bv_lat = pageData.values[4]; // LAT bv_lat = pageData.values[4]; // LAT
String sval_lat = commonData->fmt->formatValue(bv_lat, *commonData).svalue; String sval_lat = formatValue(bv_lat, *commonData).svalue;
GwApi::BoatValue *bv_lon = pageData.values[5]; // LON bv_lon = pageData.values[5]; // LON
String sval_lon = commonData->fmt->formatValue(bv_lon, *commonData).svalue; String sval_lon = formatValue(bv_lon, *commonData).svalue;
GwApi::BoatValue *bv_hdop = pageData.values[6]; // HDOP bv_hdop = pageData.values[6]; // HDOP
String sval_hdop = commonData->fmt->formatValue(bv_hdop, *commonData).svalue; String sval_hdop = formatValue(bv_hdop, *commonData).svalue;
String sunit_hdop = commonData->fmt->formatValue(bv_hdop, *commonData).unit; String sunit_hdop = formatValue(bv_hdop, *commonData).unit;
logger->logDebug(GwLog::DEBUG, "Drawing at PageAnchor; DBS=%f, HDT=%f, AWS=%f", bv_dbs->value, bv_hdt->value, bv_aws->value); commonData->logger->logDebug(GwLog::DEBUG, "Drawing at PageAnchor; DBS=%f, HDT=%f, AWS=%f", bv_dbs->value, bv_hdt->value, bv_aws->value);
// Draw canvas with background map
// rhumb(map_lat, map_lon, bv_lat->value, bv_lon->value)
int posdiff = 0;
if (map_valid) {
if (bv_lat->valid and bv_lon->valid) {
// calculate movement since last map refresh
posdiff = rhumb(map_lat, map_lon, bv_lat->value, bv_lon->value);
if (posdiff > 25) {
map_lat = bv_lat->value;
map_lon = bv_lon->value;
map_valid = getBackgroundMap(map_lat, map_lon, zoom);
if (map_valid) {
// prepare visible space for anchor-symbol or boat
canvas->fillCircle(132, 130, 12, commonData->fgcolor);
}
}
}
getdisplay().drawBitmap(68, 20, canvas->getBuffer(), map_width, map_height, commonData->fgcolor);
}
Point c = {200, 150}; // center = anchor position Point c = {200, 150}; // center = anchor position
uint16_t r = 125; uint16_t r = 125;
// Circle as map border
getdisplay().drawCircle(c.x, c.y, r, commonData->fgcolor);
getdisplay().drawCircle(c.x, c.y, r + 1, commonData->fgcolor);
Point b = {200, 180}; // boat position while dropping anchor Point b = {200, 180}; // boat position while dropping anchor
const std::vector<Point> pts_boat = { // polygon lines const std::vector<Point> pts_boat = { // polygon lines
@@ -112,10 +181,21 @@ private:
{b.x + 5, b.y - 10}, {b.x + 5, b.y - 10},
{b.x + 5, b.y} {b.x + 5, b.y}
}; };
//rotatePoints und dann Linien zeichnen //rotatePoints then draw lines
// TODO rotate boat according to current heading // TODO rotate boat according to current heading
//drawPoly(rotatePoints(c, pts, RadToDeg(value2)), commonData->fgcolor); if (bv_hdt->valid) {
if (map_valid) {
Point b1 = rotatePoint(c, {b.x, b.y - 8}, bv_hdt->value * RAD_TO_DEG);
getdisplay().fillCircle(b1.x, b1.y, 10, commonData->bgcolor);
}
drawPoly(rotatePoints(c, pts_boat, bv_hdt->value * RAD_TO_DEG), commonData->fgcolor);
} else {
// no heading available: draw north oriented
if (map_valid) {
getdisplay().fillCircle(b.x, b.y - 8, 10, commonData->bgcolor);
}
drawPoly(pts_boat, commonData->fgcolor); drawPoly(pts_boat, commonData->fgcolor);
}
// Draw wind arrow // Draw wind arrow
const std::vector<Point> pts_wind = { const std::vector<Point> pts_wind = {
@@ -129,112 +209,142 @@ private:
} }
// Title and corner value headings // Title and corner value headings
epd->setTextColor(commonData->fgcolor); getdisplay().setTextColor(commonData->fgcolor);
epd->setFont(&Ubuntu_Bold12pt8b); getdisplay().setFont(&Ubuntu_Bold10pt8b);
epd->setCursor(8, 48); // Left
epd->print("Anchor"); getdisplay().setCursor(8, 36);
getdisplay().print("Anchor");
epd->setFont(&Ubuntu_Bold10pt8b); getdisplay().setCursor(8, 210);
epd->setCursor(8, 200); getdisplay().print("Depth");
epd->print("Depth"); // Right
drawTextRalign(392, 38, "Chain"); drawTextRalign(392, 80, "Chain");
drawTextRalign(392, 200, "Wind"); drawTextRalign(392, 210, "Wind");
// Units // Units
epd->setCursor(8, 272); getdisplay().setCursor(8, 272);
epd->print(sunit_dbs); getdisplay().print(sunit_dbs);
drawTextRalign(392, 272, sunit_aws); drawTextRalign(392, 272, sunit_aws);
drawTextRalign(392, 100, lengthformat); // chain unit not implemented // drawTextRalign(392, 100, lengthformat); // chain unit not implemented
// Corner values // Corner values
epd->setFont(&Ubuntu_Bold8pt8b); getdisplay().setFont(&Ubuntu_Bold8pt8b);
epd->setCursor(8, 70); getdisplay().setCursor(8, 54);
epd->print("Alarm: "); getdisplay().print(anchor_set ? "Dropped" : "Ready"); // Anchor state
epd->print(alarm_enabled ? "On" : "Off"); getdisplay().setCursor(8, 72);
getdisplay().print("Alarm: "); // Alarm state
getdisplay().print(alarm_enabled ? "on" : "off");
epd->setCursor(8, 90); getdisplay().setCursor(8, 120);
epd->print("HDOP"); getdisplay().print("Zoom");
epd->setCursor(8, 106); getdisplay().setCursor(8, 136);
if (bv_hdop->valid) { getdisplay().print(zoom);
epd->print(round(bv_hdop->value), 0);
epd->print(sunit_hdop); getdisplay().setCursor(8, 160);
getdisplay().print("diff");
getdisplay().setCursor(8, 176);
if (map_valid and bv_lat->valid and bv_lon->valid) {
getdisplay().print(String(posdiff));
} else { } else {
epd->print("n/a"); getdisplay().print("n/a");
} }
// Values // Chain out TODO lengthformat ft/m
epd->setFont(&DSEG7Classic_BoldItalic20pt7b); drawTextRalign(392, 96, String(chain) + " m");
// Current chain used drawTextRalign(392, 96+16, "of " + String(chain_length) + " m");
epd->setCursor(328, 85);
epd->print("27"); getdisplay().setFont(&DSEG7Classic_BoldItalic16pt7b);
// Depth // Depth
epd->setCursor(8, 250); getdisplay().setCursor(8, 250);
epd->print(sval_dbs); getdisplay().print(sval_dbs);
// Wind
epd->setCursor(328, 250);
epd->print(sval_aws);
epd->drawCircle(c.x, c.y, r, commonData->fgcolor); // Wind
epd->drawCircle(c.x, c.y, r + 1, commonData->fgcolor); getdisplay().setCursor(320, 250);
getdisplay().print(sval_aws);
// Position of boat in center of map
getdisplay().setFont(&IBM8x8px);
drawTextRalign(392, 34, sval_lat);
drawTextRalign(392, 44, sval_lon);
// quality
String hdop = "HDOP: ";
if (bv_hdop->valid) {
hdop += String(round(bv_hdop->value));
} else {
hdop += " n/a";
}
drawTextRalign(392, 54, hdop);
// zoom scale // zoom scale
epd->drawLine(c.x + 10, c.y, c.x + r - 4, c.y, commonData->fgcolor); getdisplay().drawLine(c.x + 10, c.y, c.x + r - 4, c.y, commonData->fgcolor);
// arrow left // arrow left
epd->drawLine(c.x + 10, c.y, c.x + 16, c.y - 4, commonData->fgcolor); getdisplay().drawLine(c.x + 10, c.y, c.x + 16, c.y - 4, commonData->fgcolor);
epd->drawLine(c.x + 10, c.y, c.x + 16, c.y + 4, commonData->fgcolor); getdisplay().drawLine(c.x + 10, c.y, c.x + 16, c.y + 4, commonData->fgcolor);
// arrow right // arrow right
epd->drawLine(c.x + r - 4, c.y, c.x + r - 10, c.y - 4, commonData->fgcolor); getdisplay().drawLine(c.x + r - 4, c.y, c.x + r - 10, c.y - 4, commonData->fgcolor);
epd->drawLine(c.x + r - 4, c.y, c.x + r - 10, c.y + 4, commonData->fgcolor); getdisplay().drawLine(c.x + r - 4, c.y, c.x + r - 10, c.y + 4, commonData->fgcolor);
epd->setFont(&Ubuntu_Bold8pt8b); getdisplay().setFont(&Ubuntu_Bold8pt8b);
drawTextCenter(c.x + r / 2, c.y + 8, String(scale) + "m"); drawTextCenter(c.x + r / 2, c.y + 8, String(scale, 0) + "m");
// alarm range circle
if (alarm_enabled) {
// alarm range in meter has to be smaller than the scale in meter
// r and r_range are pixel values
uint16_t r_range = int(alarm_range * r / scale);
logger->logDebug(GwLog::LOG, "Drawing at PageAnchor; Alarm range = %d", r_range);
epd->drawCircle(c.x, c.y, r_range, commonData->fgcolor);
}
// draw anchor symbol (as bitmap) // draw anchor symbol (as bitmap)
epd->drawXBitmap(c.x - anchor_width / 2, c.y - anchor_height / 2, getdisplay().drawXBitmap(c.x - anchor_width / 2, c.y - anchor_height / 2,
anchor_bits, anchor_width, anchor_height, commonData->fgcolor); anchor_bits, anchor_width, anchor_height, commonData->fgcolor);
} }
void displayModeConfig() { void displayModeConfig(PageData &pageData) {
epd->setTextColor(commonData->fgcolor); getdisplay().setTextColor(commonData->fgcolor);
epd->setFont(&Ubuntu_Bold12pt8b); getdisplay().setFont(&Ubuntu_Bold12pt8b);
epd->setCursor(8, 48); getdisplay().setCursor(8, 48);
epd->print("Anchor configuration"); getdisplay().print("Anchor configuration");
// TODO getdisplay().setFont(&Ubuntu_Bold8pt8b);
// show lat/lon for anchor pos
// show lat/lon for boat pos
// show distance anchor <-> boat
epd->setFont(&Ubuntu_Bold8pt8b); getdisplay().setCursor(8, 260);
getdisplay().print("Press BACK to leave config");
/* getdisplay().setCursor(8, 68);
getdisplay().printf("Server: %s", server_name.c_str());
getdisplay().setCursor(8, 88);
getdisplay().printf("Port: %d", server_port);
getdisplay().setCursor(8, 108);
getdisplay().printf("Tilepath: %s", tile_path.c_str());
getdisplay().setCursor(8, 128);
getdisplay().printf("Last mapsize: %d", last_mapsize);
getdisplay().setCursor(8, 148);
getdisplay().printf("Last error: %s", errmsg);
getdisplay().setCursor(8, 168);
getdisplay().printf("Loops: %d, Readbytes: %d", loops, readbytes);
*/
GwApi::BoatValue *bv_lat = pageData.values[4]; // LAT
GwApi::BoatValue *bv_lon = pageData.values[5]; // LON
if (!bv_lat->valid or !bv_lon->valid) {
getdisplay().setCursor(8, 228);
getdisplay().printf("No valid position: background map disabled");
}
// Display menu
getdisplay().setFont(&Ubuntu_Bold8pt8b);
for (int i = 0 ; i < menu->getItemCount(); i++) { for (int i = 0 ; i < menu->getItemCount(); i++) {
ConfigMenuItem *itm = menu->getItemByIndex(i); ConfigMenuItem *itm = menu->getItemByIndex(i);
if (!itm) { if (!itm) {
logger->logDebug(GwLog::ERROR, "Menu item not found: %d", i); commonData->logger->logDebug(GwLog::ERROR, "Menu item not found: %d", i);
} else { } else {
Rect r = menu->getItemRect(i); Rect r = menu->getItemRect(i);
bool inverted = (i == menu->getActiveIndex()); bool inverted = (i == menu->getActiveIndex());
drawTextBoxed(r, itm->getLabel(), commonData->fgcolor, commonData->bgcolor, inverted, false); drawTextBoxed(r, itm->getLabel(), commonData->fgcolor, commonData->bgcolor, inverted, false);
if (inverted and editmode > 0) { if (inverted and editmode > 0) {
// triangle as edit marker // triangle as edit marker
epd->fillTriangle(r.x + r.w + 20, r.y, r.x + r.w + 30, r.y + r.h / 2, r.x + r.w + 20, r.y + r.h, commonData->fgcolor); getdisplay().fillTriangle(r.x + r.w + 20, r.y, r.x + r.w + 30, r.y + r.h / 2, r.x + r.w + 20, r.y + r.h, commonData->fgcolor);
} }
epd->setCursor(r.x + r.w + 40, r.y + r.h - 4); getdisplay().setCursor(r.x + r.w + 40, r.y + r.h - 4);
if (itm->getType() == "int") { if (itm->getType() == "int") {
epd->print(itm->getValue()); getdisplay().print(itm->getValue());
epd->print(itm->getUnit()); getdisplay().print(itm->getUnit());
} else { } else {
epd->print(itm->getValue() == 0 ? "No" : "Yes"); getdisplay().print(itm->getValue() == 0 ? "No" : "Yes");
} }
} }
} }
@@ -242,168 +352,397 @@ private:
} }
public: public:
PageAnchor(CommonData &common) : Page(common) PageAnchor(CommonData &common)
{ {
logger->logDebug(GwLog::LOG, "Instantiate PageAnchor"); commonData = &common;
common.logger->logDebug(GwLog::LOG,"Instantiate PageAnchor");
// preload configuration data String mapsource = common.config->getString(common.config->mapsource);
lengthformat = config->getString(config->lengthFormat); if (mapsource == "Local Service") {
chain_length = config->getInt(config->chainLength); map_service = 'L';
server_name = common.config->getString(common.config->ipAddress);
server_port = common.config->getInt(common.config->localPort);
tile_path = "";
} else if (mapsource == "Remote Service") {
map_service = 'R';
server_name = common.config->getString(common.config->mapServer);
tile_path = common.config->getString(common.config->mapTilePath);
} else { // OBP Service or undefined
map_service = 'O';
server_name = "norbert-walter.dnshome.de";
tile_path = "";
}
zoom = common.config->getInt(common.config->zoomlevel);
chain = 0; lengthformat = common.config->getString(common.config->lengthFormat);
anchor_set = false; chain_length = common.config->getInt(common.config->chainLength);
alarm_range = 30;
if (simulation) {
map_lat = 53.56938345759218;
map_lon = 9.679658234303275;
}
canvas = new GFXcanvas1(264, 260); // Byte aligned, no padding!
// Initialize config menu // Initialize config menu
menu = new ConfigMenu("Options", 40, 80); menu = new ConfigMenu("Options", 40, 80);
menu->setItemDimension(150, 20); menu->setItemDimension(150, 20);
ConfigMenuItem *newitem; ConfigMenuItem *newitem;
newitem = menu->addItem("chain", "Chain out", "int", 0, "m"); newitem = menu->addItem("chain", "Chain out", "int", 0, "m");
if (! newitem) { newitem->setRange(0, 200, {1, 2, 5, 10});
// Demo: in case of failure exit here, should never be happen newitem = menu->addItem("alarm", "Alarm", "bool", 0, "");
logger->logDebug(GwLog::ERROR, "Menu item creation failed"); newitem = menu->addItem("alarm", "Alarm range", "int", 50, "m");
return; newitem->setRange(0, 200, {1, 2, 5, 10});
} newitem = menu->addItem("raise", "Raise Anchor", "bool", 0, "");
newitem->setRange(0, 200, {1, 5, 10}); newitem = menu->addItem("zoom", "Zoom", "int", 15, "");
newitem = menu->addItem("chainmax", "Chain max", "int", chain_length, "m"); newitem->setRange(14, 17, {1});
newitem->setRange(0, 200, {1, 5, 10}); menu->setItemActive("chain");
newitem = menu->addItem("zoom", "Zoom", "int", 50, "m");
newitem->setRange(0, 200, {1, });
newitem = menu->addItem("range", "Alarm range", "int", 40, "m");
newitem->setRange(0, 200, {1, 5, 10});
newitem = menu->addItem("alat", "Adjust anchor lat.", "int", 0, "m");
newitem->setRange(0, 200, {1, 5, 10});
newitem = menu->addItem("alon", "Adjust anchor lon.", "int", 0, "m");
newitem->setRange(0, 200, {1, 5, 10});
#ifdef BOARD_OBP40S3
// Intodruced here because of missing keys for OBP40
newitem = menu->addItem("anchor", "Anchor down", "bool", 0, "");
#endif
menu->setItemActive("zoom");
} }
void setupKeys(){ void setupKeys(){
Page::setupKeys(); Page::setupKeys();
commonData->keydata[0].label = "MODE"; commonData->keydata[0].label = "CFG";
commonData->keydata[1].label = "ALARM"; #ifdef BOARD_OBP40S3
commonData->keydata[1].label = "DROP";
#endif
#ifdef BOARD_OBP60S3
commonData->keydata[4].label = "DROP";
#endif
} }
#ifdef BOARD_OBP60S3 // TODO OBP40 / OBP60 different handling
int handleKey(int key) { int handleKey(int key) {
commonData->logger->logDebug(GwLog::LOG, "Page Anchor handle key %d", key);
if (key == 1) { // Switch between normal and config mode if (key == 1) { // Switch between normal and config mode
if (mode == 'N') { if (mode == 'N') {
mode = 'C'; mode = 'C';
} else {
mode = 'N';
}
return 0;
}
if (mode == 'N') {
if (key == 2) { // Toggle alarm
alarm_enabled = !alarm_enabled;
return 0;
}
} else { // Config mode
if (key == 3) {
// menu down
menu->goNext();
return 0;
}
if (key == 4) {
// menu up
menu->goPrev();
return 0;
}
}
if (key == 11) { // Code for keylock
commonData->keylock = !commonData->keylock;
return 0;
}
return key;
}
#endif
#ifdef BOARD_OBP40S3 #ifdef BOARD_OBP40S3
int handleKey(int key){ commonData->keydata[0].label = "BACK";
if (key == 1) { // Switch between normal and config mode
if (mode == 'N') {
mode = 'C';
commonData->keydata[1].label = "EDIT"; commonData->keydata[1].label = "EDIT";
#endif
} else { } else {
mode = 'N'; mode = 'N';
commonData->keydata[1].label = "ALARM"; #ifdef BOARD_OBP40S3
} commonData->keydata[0].label = "CFG";
return 0; commonData->keydata[1].label = anchor_set ? "RAISE": "DROP";
} #endif
if (mode == 'N') { #ifdef BOARD_OBP60S3
if (key == 2) { // Toggle alarm commonData->keydata[4].label = anchor_set ? "RAISE": "DROP";
alarm_enabled = !alarm_enabled; #endif
return 0;
}
} else { // Config mode
// TODO different code for OBP40 / OBP60
if (key == 9) {
// menu down
if (editmode > 0) {
// decrease item value
menu->getActiveItem()->decValue();
} else {
menu->goNext();
}
return 0;
}
if (key == 10) {
// menu up or value up
if (editmode > 0) {
// increase item value
menu->getActiveItem()->incValue();
} else {
menu->goPrev();
} }
return 0; return 0;
} }
if (key == 2) { if (key == 2) {
// enter / leave edit mode for current menu item if (mode == 'N') {
anchor_set = !anchor_set;
commonData->keydata[1].label = anchor_set ? "ALARM": "DROP";
if (anchor_set) {
anchor_lat = bv_lat->value;
anchor_lon = bv_lon->value;
anchor_depth = bv_dbs->value;
// TODO set timestamp
// anchor_ts =
}
return 0;
} else if (mode == 'C') {
// Change edit mode
if (editmode > 0) { if (editmode > 0) {
commonData->keydata[1].label = "EDIT";
editmode = 0; editmode = 0;
commonData->keydata[1].label = "EDIT";
} else { } else {
commonData->keydata[1].label = "SET";
editmode = 1; editmode = 1;
commonData->keydata[1].label = "OK";
}
}
}
if (key == 9) {
// OBP40 Down
if (mode == 'C') {
if (editmode > 0) {
// decrease current menu item
menu->getActiveItem()->decValue();
} else {
// move to next menu item
menu->goNext();
} }
return 0; return 0;
} }
} }
if (key == 11) { // Code for keylock if (key == 10) {
// OBP40 Up
if (mode == 'C') {
if (editmode > 0) {
// increase current menu item
ConfigMenuItem *itm = menu->getActiveItem();
commonData->logger->logDebug(GwLog::LOG, "step = %d", itm->getStep());
itm->incValue();
} else {
// move to previous menu item
menu->goPrev();
}
return 0;
}
}
// Code for keylock
if (key == 11) {
commonData->keylock = !commonData->keylock; commonData->keylock = !commonData->keylock;
return 0; return 0;
} }
return key; return key;
} }
#endif
int rhumb(double lat1, double lon1, double lat2, double lon2) {
// calc distance in m between two geo points
static const double degToRad = M_PI / 180.0;
lat1 = degToRad * lat1;
lon1 = degToRad * lon1;
lat2 = degToRad * lat2;
lon2 = degToRad * lon2;
double dlon = lon2 - lon1;
double dlat = lat2 - lat1;
double mlat = (lat1 + lat2) / 2;
return (int) (6371000 * sqrt(pow(dlat, 2) + pow(cos(mlat) * dlon, 2)));
}
bool getBackgroundMap(double lat, double lon, uint8_t zoom) {
// HTTP-Request for map
// TODO über pagedata -> status abfragen?
if (WiFi.status() != WL_CONNECTED) {
return false;
}
bool valid = false;
HTTPClient http;
const char* headerKeys[] = { "Content-Encoding", "Content-Length" };
http.collectHeaders(headerKeys, 2);
String url = "http://" + server_name + "/" + tile_path;
String parameter = "?lat=" + String(lat, 6) + "&lon=" + String(lon, 6)+ "&zoom=" + String(zoom)
+ "&width=" + String(map_width) + "&height=" + String(map_height);
commonData->logger->logDebug(GwLog::LOG, "HTTP query: %s", String(url + parameter).c_str());
http.begin(url + parameter);
http.addHeader("Accept-Encoding", "deflate");
int httpCode = http.GET();
if (httpCode > 0) {
commonData->logger->logDebug(GwLog::LOG, "HTTP GET result code: %d", httpCode);
if (httpCode == HTTP_CODE_OK) {
WiFiClient* stream = http.getStreamPtr();
int size = http.getSize();
String encoding = http.header("Content-Encoding");
commonData->logger->logDebug(GwLog::LOG, "HTTP size: %d, encoding: '%s'", size, encoding);
bool is_gzip = encoding.equalsIgnoreCase("deflate");
uint8_t header[14]; // max: P4<LF>wwww wwww<LF>
int header_size = 0;
bool header_read = false;
int n = 0;
int ix = 0;
uint8_t* buf = canvas->getBuffer();
if (is_gzip) {
/* gzip compressed data
* has to be decompressed into a buffer big enough
* to hold the whole data.
* so the PBM header is included
* search a method to use that as canvas without
* additional copy
*/
commonData->logger->logDebug(GwLog::LOG, "Map received in gzip encoding");
#define HEADER_MAX 24
#define HTTP_CHUNK 512
uint8_t in_buf[HTTP_CHUNK];
uint8_t header_buf[HEADER_MAX];
tinfl_decompressor decomp;
tinfl_init(&decomp);
size_t bitmap_written = 0;
size_t header_written = 0;
bool header_done = false;
int row_bytes = 0;
size_t expected_bitmap = 0;
while (stream->connected() || stream->available()) {
int bytes_read = stream->read(in_buf, HTTP_CHUNK);
if (bytes_read <= 0) break;
commonData->logger->logDebug(GwLog::LOG, "stream: bytes_read=%d", bytes_read);
size_t in_ofs = 0; // offset
while (in_ofs < (size_t)bytes_read) {
size_t in_size = bytes_read - in_ofs;
size_t out_size;
uint8_t *out_ptr;
uint8_t *out_ptr_next;
if (!header_done) {
if (header_written >= HEADER_MAX) {
commonData->logger->logDebug(GwLog::LOG, "PBM header too large");
return false;
}
out_ptr = header_buf + header_written;
out_size = HEADER_MAX - header_written;
} else {
out_ptr = buf + bitmap_written;
out_size = expected_bitmap - bitmap_written;
}
commonData->logger->logDebug(GwLog::LOG, "in_size=%d, out_size=%d", in_size, out_size);
// TODO correct loop !!!
// tinfl_status tinfl_decompress(
// tinfl_decompressor *r,
// const mz_uint8 *pIn_buf_next,
// size_t *pIn_buf_size,
// mz_uint8 *pOut_buf_start
// mz_uint8 *pOut_buf_next,
// size_t *pOut_buf_size,
// const mz_uint32 decomp_flags)
tinfl_status status = tinfl_decompress(
&decomp,
in_buf + in_ofs, // start address in input buffer
&in_size, // number of bytes to process
out_ptr, // start of output buffer
out_ptr, // next write position in output buffer
&out_size, // free size in output buffer
// TINFL_FLAG_PARSE_ZLIB_HEADER |
TINFL_FLAG_HAS_MORE_INPUT |
TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF
);
if (status < 0) {
commonData->logger->logDebug(GwLog::LOG, "Decompression error (%d)", status);
return false;
}
in_ofs += in_size;
commonData->logger->logDebug(GwLog::LOG, "in_size=%d, in_ofs=%d", in_size, in_ofs);
if (!header_done) {
commonData->logger->logDebug(GwLog::LOG, "Decoding header");
header_written += out_size;
// Detect header end: two '\n'
char *first_nl = strchr((char*)header_buf, '\n');
if (!first_nl) continue;
char *second_nl = strchr(first_nl + 1, '\n');
if (!second_nl) continue;
// Null-terminate header for sscanf
header_buf[header_written < HEADER_MAX ? header_written : HEADER_MAX - 1] = 0;
// Check magic
if (strncmp((char*)header_buf, "P4", 2) != 0) {
commonData->logger->logDebug(GwLog::LOG, "Invalid PBM magic");
return false;
}
// Parse width and height strictly
int header_width = 0, header_height = 0;
if (sscanf((char*)header_buf, "P4\n%d %d", &header_width, &header_height) != 2) {
commonData->logger->logDebug(GwLog::LOG, "Failed to parse PBM dimensions");
return false;
}
if (header_width != map_width || header_height != map_height) {
commonData->logger->logDebug(GwLog::LOG, "PBM size mismatch: header %dx%d, requested %dx%d\n",
header_width, header_height, map_width, map_height);
return false;
}
commonData->logger->logDebug(GwLog::LOG, "Header: %dx%d", header_width, header_height);
// Compute row bytes and expected bitmap size
row_bytes = (header_width + 7) / 8;
commonData->logger->logDebug(GwLog::LOG, "row_bytes=%d", row_bytes);
expected_bitmap = (size_t)row_bytes * header_height;
commonData->logger->logDebug(GwLog::LOG, "expected_bitmap=%d", expected_bitmap);
// Copy any extra decompressed bitmap after header
size_t header_size = (second_nl + 1) - (char*)header_buf;
commonData->logger->logDebug(GwLog::LOG, "header_size=%d", header_size);
size_t extra_bitmap = header_written - header_size;
commonData->logger->logDebug(GwLog::LOG, "extra bitmap=%d", extra_bitmap);
header_done = true;
if (extra_bitmap > 0) {
memcpy(buf, header_buf + header_size, extra_bitmap);
bitmap_written = extra_bitmap;
}
} else {
bitmap_written += out_size;
if (bitmap_written >= expected_bitmap) {
commonData->logger->logDebug(GwLog::LOG, "Image fully received");
}
}
commonData->logger->logDebug(GwLog::LOG, "bitmap_written=%d", bitmap_written);
}
}
} else {
// uncompressed data
commonData->logger->logDebug(GwLog::LOG, "Map received uncompressed");
while (stream->available()) {
uint8_t b = stream->read();
n += 1;
if ((! header_read) and (n < 13) ) {
header[n-1] = b;
if ((n > 3) and (b == 0x0a)) {
header_read = true;
header_size = n;
header[n] = 0;
}
} else {
// write image data to canvas buffer
buf[ix++] = b;
}
}
if (n == size) {
valid = true;
}
}
commonData->logger->logDebug(GwLog::LOG, "HTTP: final bytesRead=%d, header-size=%d", n, header_size);
} else {
commonData->logger->logDebug(GwLog::LOG, "HTTP result #%d", httpCode);
}
} else {
commonData->logger->logDebug(GwLog::ERROR, "HTTP error #%d", httpCode);
}
http.end();
return valid;
}
void displayNew(PageData &pageData){ void displayNew(PageData &pageData){
#ifdef BOARD_OBP60S3
// Clear optical warning GwApi::BoatValue *bv_lat = pageData.values[4]; // LAT
if (flashLED == "Limit Violation") { GwApi::BoatValue *bv_lon = pageData.values[5]; // LON
setBlinkingLED(false);
setFlashLED(false); // check if valid data available
if (!bv_lat->valid or !bv_lon->valid) {
map_valid = false;
return;
}
errmsg = "";
map_lat = bv_lat->value; // save for later comparison
map_lon = bv_lon->value;
map_valid = getBackgroundMap(map_lat, map_lon, zoom);
if (map_valid) {
// prepare visible space for anchor-symbol or boat
canvas->fillCircle(132, 130, 10, commonData->fgcolor);
} }
#endif
}; };
void display_side_keys() {
// An rechter Seite neben dem Rad inc, dec, set etc ?
}
int displayPage(PageData &pageData) { int displayPage(PageData &pageData) {
// Logging boat values // Logging boat values
logger->logDebug(GwLog::LOG, "Drawing at PageAnchor; Mode=%c", mode); commonData->logger->logDebug(GwLog::LOG, "Drawing at PageAnchor; Mode=%c", mode);
// Set display in partial refresh mode // Set display in partial refresh mode
epd->setPartialWindow(0, 0, epd->width(), epd->height()); // Set partial update getdisplay().setPartialWindow(0, 0, getdisplay().width(), getdisplay().height()); // Set partial update
if (mode == 'N') { if (mode == 'N') {
displayModeNormal(pageData); displayModeNormal(pageData);
} else if (mode == 'C') { } else if (mode == 'C') {
displayModeConfig(); displayModeConfig(pageData);
} }
return PAGE_UPDATE; return PAGE_UPDATE;

View File

@@ -1,126 +1,251 @@
// SPDX-License-Identifier: GPL-2.0-or-later
#if defined BOARD_OBP60S3 || defined BOARD_OBP40S3 #if defined BOARD_OBP60S3 || defined BOARD_OBP40S3
#include "Pagedata.h" #include "Pagedata.h"
#include "OBP60Extensions.h" #include "OBP60Extensions.h"
/* // These constants have to match the declaration below in :
Autopilot // PageDescription registerPageAutopilot(
// {"HDM","HDT", "COG", "STW", "SOG", "DBT","XTE", "DTW", "BTW"}, // Bus values we need in the page
const int HowManyValues = 9;
*/ 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 ShowDBT = 5;
const int ShowXTE = 6;
const int ShowDTW = 7;
const int ShowBTW = 8;
const int Compass_X0 = 200; // X center point of compass band
const int Compass_Y0 = 220; // Y 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 PageAutopilot : public Page class PageAutopilot : public Page
{ {
private: int WhichDataCompass = ShowHDM; // Start value
char mode = 'N'; // (N)ormal, (C)onfig int WhichDataDisplay = ShowHDM; // Start value
void displayModeNormal(PageData &pageData) {
// TBD Boatvalues: ...
logger->logDebug(GwLog::DEBUG, "Drawing at PageAutopilot");
// Title and corner value headings
epd->setTextColor(commonData->fgcolor);
epd->setFont(&Ubuntu_Bold12pt8b);
epd->setCursor(8, 48);
epd->print("Autopilot");
}
void displayModeConfig() {
epd->setTextColor(commonData->fgcolor);
epd->setFont(&Ubuntu_Bold12pt8b);
epd->setCursor(8, 48);
epd->print("Autopilot configuration");
epd->setFont(&Ubuntu_Bold8pt8b);
// TODO menu
}
public: public:
PageAutopilot(CommonData &common) : Page(common) PageAutopilot(CommonData &common){
{ commonData = &common;
logger->logDebug(GwLog::LOG, "Instantiate PageAutopilot"); common.logger->logDebug(GwLog::LOG,"Instantiate PageAutopilot");
} }
void setupKeys() { virtual void setupKeys(){
Page::setupKeys(); Page::setupKeys();
commonData->keydata[0].label = "MODE"; commonData->keydata[0].label = "CMP";
commonData->keydata[1].label = "SRC";
} }
#ifdef BOARD_OBP60S3 virtual int handleKey(int key){
int handleKey(int key) { // Code for keylock
if (key == 1) { // Switch between normal and config mode
if (mode == 'N') { if ( key == 1 ) {
mode = 'C'; WhichDataCompass += 1;
} else { if ( WhichDataCompass > ShowCOG)
mode = 'N'; WhichDataCompass = ShowHDM;
}
return 0; return 0;
} }
if (key == 11) { // Code for keylock if ( key == 2 ) {
WhichDataDisplay += 1;
if ( WhichDataDisplay > ShowDBT)
WhichDataDisplay = ShowHDM;
}
if(key == 11){
commonData->keylock = !commonData->keylock; commonData->keylock = !commonData->keylock;
return 0; return 0; // Commit the key
} }
return key; return key;
} }
#endif
#ifdef BOARD_OBP40S3
int handleKey(int key){
if (key == 1) { // Switch between normal and config mode
if (mode == 'N') {
mode = 'C';
commonData->keydata[1].label = "EDIT";
} else {
mode = 'N';
commonData->keydata[1].label = "ALARM";
}
return 0;
}
if (key == 11) { // Code for keylock
commonData->keylock = !commonData->keylock;
return 0;
}
return key;
}
#endif
void displayNew(PageData &pageData){ int displayPage(PageData &pageData){
#ifdef BOARD_OBP60S3 GwConfigHandler *config = commonData->config;
// Clear optical warning GwLog *logger = commonData->logger;
if (flashLED == "Limit Violation") {
// 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 PageAutopilot: %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); setBlinkingLED(false);
setFlashLED(false); setFlashLED(false);
} }
#endif
};
int displayPage(PageData &pageData){ if (bvalue == NULL) return PAGE_OK; // WTF why this statement?
// Logging boat values //***********************************************************
logger->logDebug(GwLog::LOG, "Drawing at PageAutopilot; Mode=%c", mode);
// Set display in partial refresh mode // Set display in partial refresh mode
epd->setPartialWindow(0, 0, epd->width(), epd->height()); getdisplay().setPartialWindow(0, 0, getdisplay().width(), getdisplay().height()); // Set partial update
getdisplay().setTextColor(commonData->fgcolor);
if (mode == 'N') { // Horizontal line 2 pix top & bottom
displayModeNormal(pageData); // Print data on top half
} else if (mode == 'C') { getdisplay().fillRect(0, 130, 400, 2, commonData->fgcolor);
displayModeConfig(); 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; return PAGE_UPDATE;
}; };
}; };
static Page *createPage(CommonData &common){ static Page *createPage(CommonData &common){
return new PageAutopilot(common); return new PageAutopilot(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
@@ -131,8 +256,8 @@ PageDescription registerPageAutopilot(
"Autopilot", // Page name "Autopilot", // 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
{}, // Names of bus values undepends on selection in Web configuration (refer GwBoatData.h) {"HDM","HDT", "COG", "STW", "SOG", "DBT","XTE", "DTW", "BTW"}, // Bus values we need in the page
false // Show display header on/off true // Show display header on/off
); );
#endif #endif

View File

@@ -1,4 +1,3 @@
// SPDX-License-Identifier: GPL-2.0-or-later
#if defined BOARD_OBP60S3 || defined BOARD_OBP40S3 #if defined BOARD_OBP60S3 || defined BOARD_OBP40S3
#include "Pagedata.h" #include "Pagedata.h"
@@ -6,21 +5,13 @@
class PageBME280 : public Page class PageBME280 : public Page
{ {
private:
String tempformat;
String useenvsensor;
public: public:
PageBME280(CommonData &common) : Page(common) PageBME280(CommonData &common){
{ commonData = &common;
logger->logDebug(GwLog::LOG, "Instantiate PageBME280"); common.logger->logDebug(GwLog::LOG,"Instantiate PageBME280");
// Get config data
tempformat = config->getString(config->tempFormat);
useenvsensor = config->getString(config->useEnvSensor);
} }
int handleKey(int key){ virtual int handleKey(int key){
// Code for keylock // Code for keylock
if(key == 11){ if(key == 11){
commonData->keylock = !commonData->keylock; commonData->keylock = !commonData->keylock;
@@ -30,6 +21,8 @@ public:
} }
int displayPage(PageData &pageData){ int displayPage(PageData &pageData){
GwConfigHandler *config = commonData->config;
GwLog *logger = commonData->logger;
double value1 = 0; double value1 = 0;
double value2 = 0; double value2 = 0;
@@ -38,6 +31,13 @@ public:
String svalue2 = ""; String svalue2 = "";
String svalue3 = ""; String svalue3 = "";
// Get config data
String tempformat = config->getString(config->tempFormat);
bool simulation = config->getBool(config->useSimuData);
String flashLED = config->getString(config->flashLED);
String backlightMode = config->getString(config->backlight);
String useenvsensor = config->getString(config->useEnvSensor);
// Get sensor values #1 // Get sensor values #1
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
@@ -99,82 +99,82 @@ public:
} }
// Logging boat values // Logging boat values
logger->logDebug(GwLog::LOG, "Drawing at PageBME280, %s: %f, %s: %f, %s: %f", name1.c_str(), value1, name2.c_str(), value2, name3.c_str(), value3); LOG_DEBUG(GwLog::LOG,"Drawing at PageBME280, %s: %f, %s: %f, %s: %f", name1.c_str(), value1, name2.c_str(), value2, name3.c_str(), value3);
// Draw page // Draw page
//*********************************************************** //***********************************************************
// Set display in partial refresh mode // Set display in partial refresh mode
epd->setPartialWindow(0, 0, epd->width(), epd->height()); // Set partial update getdisplay().setPartialWindow(0, 0, getdisplay().width(), getdisplay().height()); // Set partial update
epd->setTextColor(commonData->fgcolor); getdisplay().setTextColor(commonData->fgcolor);
// ############### Value 1 ################ // ############### Value 1 ################
// Show name // Show name
epd->setFont(&Ubuntu_Bold20pt8b); getdisplay().setFont(&Ubuntu_Bold20pt8b);
epd->setCursor(20, 55); getdisplay().setCursor(20, 55);
epd->print(name1); // Page name getdisplay().print(name1); // Page name
// Show unit // Show unit
epd->setFont(&Ubuntu_Bold12pt8b); getdisplay().setFont(&Ubuntu_Bold12pt8b);
epd->setCursor(20, 90); getdisplay().setCursor(20, 90);
epd->print(unit1); // Unit getdisplay().print(unit1); // Unit
// Switch font if format for any values // Switch font if format for any values
epd->setFont(&DSEG7Classic_BoldItalic30pt7b); getdisplay().setFont(&DSEG7Classic_BoldItalic30pt7b);
epd->setCursor(180, 90); getdisplay().setCursor(180, 90);
// Show bus data // Show bus data
epd->print(svalue1); // Real value as formated string getdisplay().print(svalue1); // Real value as formated string
// ############### Horizontal Line ################ // ############### Horizontal Line ################
// Horizontal line 3 pix // Horizontal line 3 pix
epd->fillRect(0, 105, 400, 3, commonData->fgcolor); getdisplay().fillRect(0, 105, 400, 3, commonData->fgcolor);
// ############### Value 2 ################ // ############### Value 2 ################
// Show name // Show name
epd->setFont(&Ubuntu_Bold20pt8b); getdisplay().setFont(&Ubuntu_Bold20pt8b);
epd->setCursor(20, 145); getdisplay().setCursor(20, 145);
epd->print(name2); // Page name getdisplay().print(name2); // Page name
// Show unit // Show unit
epd->setFont(&Ubuntu_Bold12pt8b); getdisplay().setFont(&Ubuntu_Bold12pt8b);
epd->setCursor(20, 180); getdisplay().setCursor(20, 180);
epd->print(unit2); // Unit getdisplay().print(unit2); // Unit
// Switch font if format for any values // Switch font if format for any values
epd->setFont(&DSEG7Classic_BoldItalic30pt7b); getdisplay().setFont(&DSEG7Classic_BoldItalic30pt7b);
epd->setCursor(180, 180); getdisplay().setCursor(180, 180);
// Show bus data // Show bus data
epd->print(svalue2); // Real value as formated string getdisplay().print(svalue2); // Real value as formated string
// ############### Horizontal Line ################ // ############### Horizontal Line ################
// Horizontal line 3 pix // Horizontal line 3 pix
epd->fillRect(0, 195, 400, 3, commonData->fgcolor); getdisplay().fillRect(0, 195, 400, 3, commonData->fgcolor);
// ############### Value 3 ################ // ############### Value 3 ################
// Show name // Show name
epd->setFont(&Ubuntu_Bold20pt8b); getdisplay().setFont(&Ubuntu_Bold20pt8b);
epd->setCursor(20, 235); getdisplay().setCursor(20, 235);
epd->print(name3); // Page name getdisplay().print(name3); // Page name
// Show unit // Show unit
epd->setFont(&Ubuntu_Bold12pt8b); getdisplay().setFont(&Ubuntu_Bold12pt8b);
epd->setCursor(20, 270); getdisplay().setCursor(20, 270);
epd->print(unit3); // Unit getdisplay().print(unit3); // Unit
// Switch font if format for any values // Switch font if format for any values
epd->setFont(&DSEG7Classic_BoldItalic30pt7b); getdisplay().setFont(&DSEG7Classic_BoldItalic30pt7b);
epd->setCursor(140, 270); getdisplay().setCursor(140, 270);
// Show bus data // Show bus data
epd->print(svalue3); // Real value as formated string getdisplay().print(svalue3); // Real value as formated string
return PAGE_UPDATE; return PAGE_UPDATE;
}; };

View File

@@ -1,238 +0,0 @@
// SPDX-License-Identifier: GPL-2.0-or-later
#if defined BOARD_OBP60S3 || defined BOARD_OBP40S3
/*
* Barograph WIP
* - Zoom feature
* - Saves data in FRAM if available
*/
#include "Pagedata.h"
#include "OBP60Extensions.h"
class PageBarograph : public Page
{
private:
bool keylock = false;
bool has_fram = false;
String flashLED;
String useenvsensor;
char source = 'I'; // (I)nternal, e(X)ternal
const int series[5] = {75, 150, 300, 600, 900};
const int zoom[5] = {1, 2, 3, 6, 12};
int zoomindex = 4;
uint16_t data[336] = {0}; // current data to display
// y-axis
uint16_t vmin;
uint16_t vmax;
uint16_t scalemin = 1000;
uint16_t scalemax = 1020;
uint16_t scalestep = 5;
int hist1 = 0; // one hour trend
int hist3 = 0; // three hours trend
long refresh = 0; // millis
void loadData() {
// Transfer data from history to page buffer,
// set y-axis according to data
int i = zoom[zoomindex];
// get min and max values of measured data
vmin = data[0];
vmax = data[0];
for (int x = 0; x < 336; x++) {
if (data[x] != 0) {
if (data[x] < vmin) {
vmin = data[x];
} else if (data[x] > vmax) {
vmax = data[x];
}
}
}
// calculate y-axis scale
uint16_t diff = vmax - vmin;
if (diff < 20) {
scalestep = 5;
} else if (diff < 40) {
scalestep = 10;
} else {
scalestep = 15;
}
scalemin = vmin - (vmin % scalestep);
scalemax = vmax + scalestep - (vmax % scalestep);
// TODO implement history buffer
};
public:
PageBarograph(CommonData &common): Page(common)
{
logger->logDebug(GwLog::LOG, "Instantiate PageBarograph");
// Get config data
useenvsensor = config->getString(common.config->useEnvSensor);
// possible values for internal sensor
static std::vector<String> sensorList = {
"BME280", "BMP280", "BMP180", "BMP085", "HTU21", "SHT21"
};
if (std::find(sensorList.begin(), sensorList.end(), useenvsensor) != sensorList.end()) {
source = 'I';
} else {
// "off" means user external data if available
source = 'X';
}
//common.logger->logDebug(GwLog::LOG,"Source=%s (%s)", source, useenvsensor);
loadData(); // initial load
}
int handleKey(int key) {
if (key == 1) {
// zoom in
if (zoomindex > 0) {
zoomindex -= 1;
}
return 0;
}
if (key == 2) {
// zoom out
if (zoomindex < sizeof(zoom)) {
zoomindex += 1;
}
return 0;
}
if (key == 11) {
keylock = !keylock;
return 0;
}
return key;
}
void displayNew(PageData &pageData) {
#ifdef BOARD_OBP60S3
// Clear optical warning
if (flashLED == "Limit Violation") {
setBlinkingLED(false);
setFlashLED(false);
}
#endif
};
int displayPage(PageData &pageData) {
// Logging boat values
logger->logDebug(GwLog::LOG, "Drawing at PageBarograph");
// Draw page
//***********************************************************
// Set display in partial refresh mode
epd->setPartialWindow(0, 0, epd->width(), epd->height()); // Set partial update
// Frames
epd->fillRect(0, 75, 400, 2, commonData->fgcolor); // fillRect: x, y, w, h
epd->fillRect(130, 20, 2, 55, commonData->fgcolor);
epd->fillRect(270, 20, 2, 55, commonData->fgcolor);
epd->fillRect(325, 20, 2, 55, commonData->fgcolor);
epd->setTextColor(commonData->fgcolor);
epd->setFont(&Ubuntu_Bold8pt8b);
if (source == 'I') {
drawTextCenter(360, 40, useenvsensor);
} else {
drawTextCenter(360, 40, "ext.");
}
// Trend
drawTextCenter(295, 62, "0.0");
// Alarm placeholder
drawTextCenter(70, 62, "Alarm Off");
// Zoom
int datastep = series[zoomindex];
String fmt;
if (datastep > 120) {
if (datastep % 60 == 0) {
fmt = String(datastep / 60.0, 0) + " min";
} else {
fmt = String(datastep / 60.0, 1) + " min";
}
} else {
fmt = String(datastep) + " s";
}
drawTextCenter(360, 62, fmt);
// Current measurement
epd->setFont(&Ubuntu_Bold16pt8b);
drawTextCenter(200, 40, String(commonData->data.airPressure / 100, 1));
epd->setFont(&Ubuntu_Bold8pt8b);
drawTextCenter(200, 62, "hPa"); // Unit
// Diagram
const int xstep = 48; // x-axis-grid
const int x0 = 350; // origin
const int y0 = 270;
const int w = 7 * 48;
const int h = 180;
// epd->drawRect(x0 - w, y0 - h, w, h, commonData->fgcolor);
// x-axis are hours
for (int i = 1; i <= 6; i++) {
String label = String(-1 * zoom[zoomindex] * i);
epd->drawLine(x0 - i * xstep, y0, x0 - i * xstep, y0 - h, commonData->fgcolor);
drawTextCenter(x0 - i * xstep, y0 - 10, label);
}
// y-axis
epd->drawLine(x0 + 5, y0, x0 + 5, y0 - h, commonData->fgcolor); // drawLine: x1, y1, x2, y2
epd->drawLine(x0 - w, y0, x0 - w, y0 - h, commonData->fgcolor);
epd->drawLine(x0 - w - 5, y0, x0 - w - 5, y0 - h, commonData->fgcolor);
epd->drawLine(x0, y0, x0, y0 - h, commonData->fgcolor);
int16_t dy = 9; // px for one hPa
int16_t y = y0;
int16_t ys = scalemin;
while (y >= y0 - h) {
if (y % scalestep == 0) {
// big step, show label and long line
epd->setCursor(x0 + 10, y + 5);
epd->print(String(ys));
epd->drawLine(x0 + 5, y, x0 - w - 5, y, commonData->fgcolor);
} else {
// small step, only short lines left and right
epd->drawLine(x0 + 5, y, x0, y, commonData->fgcolor);
epd->drawLine(x0 - w - 5, y, x0 - w, y, commonData->fgcolor);
}
y -= dy;
ys += 1;
}
return PAGE_UPDATE;
};
};
static Page* createPage(CommonData &common){
return new PageBarograph(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 registerPageBarograph(
"Barograph", // Page name
createPage, // Action
0, // No bus values needed
true // Show display header on/off
);
#endif

View File

@@ -1,4 +1,3 @@
// SPDX-License-Identifier: GPL-2.0-or-later
#if defined BOARD_OBP60S3 || defined BOARD_OBP40S3 #if defined BOARD_OBP60S3 || defined BOARD_OBP40S3
#include "Pagedata.h" #include "Pagedata.h"
@@ -6,25 +5,20 @@
class PageBattery : public Page class PageBattery : public Page
{ {
private:
String powsensor1;
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) : Page(common) PageBattery(CommonData &common){
{ commonData = &common;
logger->logDebug(GwLog::LOG, "Instantiate PageBattery"); common.logger->logDebug(GwLog::LOG,"Instantiate PageBattery");
// Get config data
String powsensor1 = config->getString(config->usePowSensor1);
} }
void setupKeys(){ virtual void setupKeys(){
Page::setupKeys(); Page::setupKeys();
commonData->keydata[0].label = "AVG"; commonData->keydata[0].label = "AVG";
} }
int handleKey(int key){ virtual int handleKey(int key){
// Change average // Change average
if(key == 1){ if(key == 1){
average ++; average ++;
@@ -40,17 +34,9 @@ public:
return key; return key;
} }
void displayNew(PageData &pageData) {
#ifdef BOARD_OBP60S3
// Clear optical warning
if (flashLED == "Limit Violation") {
setBlinkingLED(false);
setFlashLED(false);
}
#endif
};
int displayPage(PageData &pageData){ int displayPage(PageData &pageData){
GwConfigHandler *config = commonData->config;
GwLog *logger = commonData->logger;
// Old values for hold function // Old values for hold function
double value1 = 0; double value1 = 0;
@@ -63,6 +49,14 @@ public:
static String svalue3old = ""; static String svalue3old = "";
static String unit3old = ""; static String unit3old = "";
// Get config data
String lengthformat = config->getString(config->lengthFormat);
// bool simulation = config->getBool(config->useSimuData);
String flashLED = config->getString(config->flashLED);
String backlightMode = config->getString(config->backlight);
String powsensor1 = config->getString(config->usePowSensor1);
bool simulation = config->getBool(config->useSimuData);
// Get voltage value // Get voltage value
String name1 = "VBat"; // Value name String name1 = "VBat"; // Value name
if(String(powsensor1) == "INA219" || String(powsensor1) == "INA226"){ if(String(powsensor1) == "INA219" || String(powsensor1) == "INA226"){
@@ -151,141 +145,147 @@ public:
String svalue3 = String(value3); // Formatted value as string including unit conversion and switching decimal places String svalue3 = String(value3); // Formatted value as string including unit conversion and switching decimal places
String unit3 = "W"; // Unit of value String unit3 = "W"; // Unit of value
// Optical warning by limit violation (unused)
if(String(flashLED) == "Limit Violation"){
setBlinkingLED(false);
setFlashLED(false);
}
// Logging boat values // Logging boat values
logger->logDebug(GwLog::LOG, "Drawing at PageBattery, %s: %f, %s: %f, %s: %f, Avg: %d", name1.c_str(), value1, name2.c_str(), value2, name3.c_str(), value3, average); LOG_DEBUG(GwLog::LOG,"Drawing at PageBattery, %s: %f, %s: %f, %s: %f, Avg: %d", name1.c_str(), value1, name2.c_str(), value2, name3.c_str(), value3, average);
// Draw page // Draw page
//*********************************************************** //***********************************************************
// Set display in partial refresh mode // Set display in partial refresh mode
epd->setPartialWindow(0, 0, epd->width(), epd->height()); // Set partial update getdisplay().setPartialWindow(0, 0, getdisplay().width(), getdisplay().height()); // Set partial update
// Show average settings // Show average settings
epd->setTextColor(commonData->fgcolor); getdisplay().setTextColor(commonData->fgcolor);
epd->setFont(&Ubuntu_Bold8pt8b); getdisplay().setFont(&Ubuntu_Bold8pt8b);
switch (average) { switch (average) {
case 0: case 0:
epd->setCursor(60, 90); getdisplay().setCursor(60, 90);
epd->print("Avg: 1s"); getdisplay().print("Avg: 1s");
epd->setCursor(60, 180); getdisplay().setCursor(60, 180);
epd->print("Avg: 1s"); getdisplay().print("Avg: 1s");
epd->setCursor(60, 270); getdisplay().setCursor(60, 270);
epd->print("Avg: 1s"); getdisplay().print("Avg: 1s");
break; break;
case 1: case 1:
epd->setCursor(60, 90); getdisplay().setCursor(60, 90);
epd->print("Avg: 10s"); getdisplay().print("Avg: 10s");
epd->setCursor(60, 180); getdisplay().setCursor(60, 180);
epd->print("Avg: 10s"); getdisplay().print("Avg: 10s");
epd->setCursor(60, 270); getdisplay().setCursor(60, 270);
epd->print("Avg: 10s"); getdisplay().print("Avg: 10s");
break; break;
case 2: case 2:
epd->setCursor(60, 90); getdisplay().setCursor(60, 90);
epd->print("Avg: 60s"); getdisplay().print("Avg: 60s");
epd->setCursor(60, 180); getdisplay().setCursor(60, 180);
epd->print("Avg: 60s"); getdisplay().print("Avg: 60s");
epd->setCursor(60, 270); getdisplay().setCursor(60, 270);
epd->print("Avg: 60s"); getdisplay().print("Avg: 60s");
break; break;
case 3: case 3:
epd->setCursor(60, 90); getdisplay().setCursor(60, 90);
epd->print("Avg: 300s"); getdisplay().print("Avg: 300s");
epd->setCursor(60, 180); getdisplay().setCursor(60, 180);
epd->print("Avg: 300s"); getdisplay().print("Avg: 300s");
epd->setCursor(60, 270); getdisplay().setCursor(60, 270);
epd->print("Avg: 300s"); getdisplay().print("Avg: 300s");
break; break;
default: default:
epd->setCursor(60, 90); getdisplay().setCursor(60, 90);
epd->print("Avg: 1s"); getdisplay().print("Avg: 1s");
epd->setCursor(60, 180); getdisplay().setCursor(60, 180);
epd->print("Avg: 1s"); getdisplay().print("Avg: 1s");
epd->setCursor(60, 270); getdisplay().setCursor(60, 270);
epd->print("Avg: 1s"); getdisplay().print("Avg: 1s");
break; break;
} }
// ############### Value 1 ################ // ############### Value 1 ################
// Show name // Show name
epd->setFont(&Ubuntu_Bold20pt8b); getdisplay().setFont(&Ubuntu_Bold20pt8b);
epd->setCursor(20, 55); getdisplay().setCursor(20, 55);
epd->print(name1); // Value name getdisplay().print(name1); // Value name
// Show unit // Show unit
epd->setFont(&Ubuntu_Bold12pt8b); getdisplay().setFont(&Ubuntu_Bold12pt8b);
epd->setCursor(20, 90); getdisplay().setCursor(20, 90);
epd->print(unit1); // Unit getdisplay().print(unit1); // Unit
// Show value // Show value
epd->setFont(&DSEG7Classic_BoldItalic30pt7b); getdisplay().setFont(&DSEG7Classic_BoldItalic30pt7b);
epd->setCursor(180, 90); getdisplay().setCursor(180, 90);
// Show bus data // Show bus data
if(String(powsensor1) != "off"){ if(String(powsensor1) != "off"){
epd->print(value1,2); // Real value as formated string getdisplay().print(value1,2); // Real value as formated string
} }
else{ else{
epd->print(commonData->fmt->placeholder); // No sensor data (sensor is off) getdisplay().print("---"); // No sensor data (sensor is off)
} }
// ############### Horizontal Line ################ // ############### Horizontal Line ################
// Horizontal line 3 pix // Horizontal line 3 pix
epd->fillRect(0, 105, 400, 3, commonData->fgcolor); getdisplay().fillRect(0, 105, 400, 3, commonData->fgcolor);
// ############### Value 2 ################ // ############### Value 2 ################
// Show name // Show name
epd->setFont(&Ubuntu_Bold20pt8b); getdisplay().setFont(&Ubuntu_Bold20pt8b);
epd->setCursor(20, 145); getdisplay().setCursor(20, 145);
epd->print(name2); // Value name getdisplay().print(name2); // Value name
// Show unit // Show unit
epd->setFont(&Ubuntu_Bold12pt8b); getdisplay().setFont(&Ubuntu_Bold12pt8b);
epd->setCursor(20, 180); getdisplay().setCursor(20, 180);
epd->print(unit2); // Unit getdisplay().print(unit2); // Unit
// Show value // Show value
epd->setFont(&DSEG7Classic_BoldItalic30pt7b); getdisplay().setFont(&DSEG7Classic_BoldItalic30pt7b);
epd->setCursor(180, 180); getdisplay().setCursor(180, 180);
// Show bus data // Show bus data
if(String(powsensor1) != "off"){ if(String(powsensor1) != "off"){
epd->print(value2,1); // Real value as formated string getdisplay().print(value2,1); // Real value as formated string
} }
else{ else{
epd->print(commonData->fmt->placeholder); // No sensor data (sensor is off) getdisplay().print("---"); // No sensor data (sensor is off)
} }
// ############### Horizontal Line ################ // ############### Horizontal Line ################
// Horizontal line 3 pix // Horizontal line 3 pix
epd->fillRect(0, 195, 400, 3, commonData->fgcolor); getdisplay().fillRect(0, 195, 400, 3, commonData->fgcolor);
// ############### Value 3 ################ // ############### Value 3 ################
// Show name // Show name
epd->setFont(&Ubuntu_Bold20pt8b); getdisplay().setFont(&Ubuntu_Bold20pt8b);
epd->setCursor(20, 235); getdisplay().setCursor(20, 235);
epd->print(name3); // Value name getdisplay().print(name3); // Value name
// Show unit // Show unit
epd->setFont(&Ubuntu_Bold12pt8b); getdisplay().setFont(&Ubuntu_Bold12pt8b);
epd->setCursor(20, 270); getdisplay().setCursor(20, 270);
epd->print(unit3); // Unit getdisplay().print(unit3); // Unit
// Show value // Show value
epd->setFont(&DSEG7Classic_BoldItalic30pt7b); getdisplay().setFont(&DSEG7Classic_BoldItalic30pt7b);
epd->setCursor(180, 270); getdisplay().setCursor(180, 270);
// Show bus data // Show bus data
if(String(powsensor1) != "off"){ if(String(powsensor1) != "off"){
epd->print(value3,1); // Real value as formated string getdisplay().print(value3,1); // Real value as formated string
} }
else{ else{
epd->print(commonData->fmt->placeholder); // No sensor data (sensor is off) getdisplay().print("---"); // No sensor data (sensor is off)
} }
return PAGE_UPDATE; return PAGE_UPDATE;

View File

@@ -1,4 +1,3 @@
// SPDX-License-Identifier: GPL-2.0-or-later
#if defined BOARD_OBP60S3 || defined BOARD_OBP40S3 #if defined BOARD_OBP60S3 || defined BOARD_OBP40S3
#include "Pagedata.h" #include "Pagedata.h"
@@ -7,34 +6,23 @@
class PageBattery2 : public Page class PageBattery2 : public Page
{ {
private:
String batVoltage;
int batCapacity;
String batType;
String powerSensor;
bool init = false; // Marker for init done bool init = false; // Marker for init done
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) : Page(common) PageBattery2(CommonData &common){
{ commonData = &common;
logger->logDebug(GwLog::LOG, "Instantiate PageBattery2"); common.logger->logDebug(GwLog::LOG,"Instantiate PageBattery2");
// Get config data
batVoltage = config->getString(config->batteryVoltage);
batCapacity = config->getInt(config->batteryCapacity);
batType = config->getString(config->batteryType);
powerSensor = config->getString(config->usePowSensor1);
} }
void setupKeys(){ virtual void setupKeys(){
Page::setupKeys(); Page::setupKeys();
commonData->keydata[0].label = "AVG"; commonData->keydata[0].label = "AVG";
} }
int handleKey(int key) { virtual int handleKey(int key){
// Change average // Change average
if(key == 1){ if(key == 1){
average ++; average ++;
@@ -56,7 +44,11 @@ public:
return key; return key;
} }
int displayPage(PageData &pageData) { int displayPage(PageData &pageData)
{
GwConfigHandler *config = commonData->config;
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
float x0[4] = {+3082.5178, +1656.1571, +1316.8766, +14986.9336}; // Offset float x0[4] = {+3082.5178, +1656.1571, +1316.8766, +14986.9336}; // Offset
@@ -65,6 +57,16 @@ public:
int batPercentage = 0; // Battery level int batPercentage = 0; // Battery level
float batRange = 0; // Range in hours float batRange = 0; // Range in hours
// Get config data
bool simulation = config->getBool(config->useSimuData);
bool holdvalues = config->getBool(config->holdvalues);
String flashLED = config->getString(config->flashLED);
String batVoltage = config->getString(config->batteryVoltage);
int batCapacity = config->getInt(config->batteryCapacity);
String batType = config->getString(config->batteryType);
String backlightMode = config->getString(config->backlight);
String powerSensor = config->getString(config->usePowSensor1);
double value1 = 0; // Battery voltage double value1 = 0; // Battery voltage
double value2 = 0; // Battery current double value2 = 0; // Battery current
double value3 = 0; // Battery power consumption double value3 = 0; // Battery power consumption
@@ -140,133 +142,149 @@ public:
if(batRange > 99) batRange = 99; if(batRange > 99) batRange = 99;
// Optical warning by limit violation // Optical warning by limit violation
if (flashLED == "Limit Violation") { if(String(flashLED) == "Limit Violation"){
bool violation = false; // Limits for Pb battery
if (batType == "Pb") { if(String(batType) == "Pb" && (raw < 11.8 || raw > 14.8)){
violation = (raw < 11.8 || raw > 14.8);
} else if (batType == "Gel") {
violation = (raw < 11.8 || raw > 14.4);
} else if (batType == "AGM") {
violation = (raw < 11.8 || raw > 14.7);
} else if (batType == "LiFePo4") {
violation = (raw < 12.0 || raw > 14.6);
}
if (violation) {
setBlinkingLED(true); setBlinkingLED(true);
} else { }
if(String(batType) == "Pb" && (raw >= 11.8 && raw <= 14.8)){
setBlinkingLED(false);
setFlashLED(false);
}
// Limits for Gel battery
if(String(batType) == "Gel" && (raw < 11.8 || raw > 14.4)){
setBlinkingLED(true);
}
if(String(batType) == "Gel" && (raw >= 11.8 && raw <= 14.4)){
setBlinkingLED(false);
setFlashLED(false);
}
// Limits for AGM battery
if(String(batType) == "AGM" && (raw < 11.8 || raw > 14.7)){
setBlinkingLED(true);
}
if(String(batType) == "AGM" && (raw >= 11.8 && raw <= 14.7)){
setBlinkingLED(false);
setFlashLED(false);
}
// Limits for LiFePo4 battery
if(String(batType) == "LiFePo4" && (raw < 12.0 || raw > 14.6)){
setBlinkingLED(true);
}
if(String(batType) == "LiFePo4" && (raw >= 12.0 && raw <= 14.6)){
setBlinkingLED(false); setBlinkingLED(false);
setFlashLED(false); setFlashLED(false);
} }
} }
// Logging voltage value // Logging voltage value
logger->logDebug(GwLog::LOG, "Drawing at PageBattery2, Type:%s %s:=%f", batType.c_str(), name1.c_str(), raw); LOG_DEBUG(GwLog::LOG,"Drawing at PageBattery2, Type:%s %s:=%f", batType.c_str(), name1.c_str(), raw);
// Draw page // Draw page
//*********************************************************** //***********************************************************
// Set display in partial refresh mode // Set display in partial refresh mode
epd->setPartialWindow(0, 0, epd->width(), epd->height()); // Set partial update getdisplay().setPartialWindow(0, 0, getdisplay().width(), getdisplay().height()); // Set partial update
epd->setTextColor(commonData->fgcolor); getdisplay().setTextColor(commonData->fgcolor);
// Show name // Show name
epd->setFont(&Ubuntu_Bold20pt8b); getdisplay().setFont(&Ubuntu_Bold20pt8b);
epd->setCursor(10, 65); getdisplay().setCursor(10, 65);
epd->print("Bat."); getdisplay().print("Bat.");
// Show battery type // Show battery type
epd->setFont(&Ubuntu_Bold8pt8b); getdisplay().setFont(&Ubuntu_Bold8pt8b);
epd->setCursor(90, 65); getdisplay().setCursor(90, 65);
epd->print(batType); getdisplay().print(batType);
// Show voltage type // Show voltage type
epd->setFont(&DSEG7Classic_BoldItalic20pt7b); getdisplay().setFont(&DSEG7Classic_BoldItalic20pt7b);
epd->setCursor(10, 140); getdisplay().setCursor(10, 140);
int bvoltage = 0; int bvoltage = 0;
if(String(batVoltage) == "12V") bvoltage = 12; if(String(batVoltage) == "12V") bvoltage = 12;
else bvoltage = 24; else bvoltage = 24;
epd->print(bvoltage); getdisplay().print(bvoltage);
epd->setFont(&Ubuntu_Bold16pt8b); getdisplay().setFont(&Ubuntu_Bold16pt8b);
epd->print("V"); getdisplay().print("V");
// Show battery capacity // Show battery capacity
epd->setFont(&DSEG7Classic_BoldItalic20pt7b); getdisplay().setFont(&DSEG7Classic_BoldItalic20pt7b);
epd->setCursor(10, 200); getdisplay().setCursor(10, 200);
if(batCapacity <= 999) epd->print(batCapacity, 0); if(batCapacity <= 999) getdisplay().print(batCapacity, 0);
if(batCapacity > 999) epd->print(float(batCapacity/1000.0), 1); if(batCapacity > 999) getdisplay().print(float(batCapacity/1000.0), 1);
epd->setFont(&Ubuntu_Bold16pt8b); getdisplay().setFont(&Ubuntu_Bold16pt8b);
if(batCapacity <= 999) epd->print("Ah"); if(batCapacity <= 999) getdisplay().print("Ah");
if(batCapacity > 999) epd->print("kAh"); if(batCapacity > 999) getdisplay().print("kAh");
// Show info // Show info
epd->setFont(&Ubuntu_Bold8pt8b); getdisplay().setFont(&Ubuntu_Bold8pt8b);
epd->setCursor(10, 235); getdisplay().setCursor(10, 235);
epd->print("Installed"); getdisplay().print("Installed");
epd->setCursor(10, 255); getdisplay().setCursor(10, 255);
epd->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
epd->setFont(&Ubuntu_Bold8pt8b); getdisplay().setFont(&Ubuntu_Bold8pt8b);
epd->setCursor(150, 145); getdisplay().setCursor(150, 145);
switch (average) { switch (average) {
case 0: case 0:
epd->print("Avg: 1s"); getdisplay().print("Avg: 1s");
break; break;
case 1: case 1:
epd->print("Avg: 10s"); getdisplay().print("Avg: 10s");
break; break;
case 2: case 2:
epd->print("Avg: 60s"); getdisplay().print("Avg: 60s");
break; break;
case 3: case 3:
epd->print("Avg: 300s"); getdisplay().print("Avg: 300s");
break; break;
default: default:
epd->print("Avg: 1s"); getdisplay().print("Avg: 1s");
break; break;
} }
// Show fill level in percent // Show fill level in percent
epd->setFont(&DSEG7Classic_BoldItalic20pt7b); getdisplay().setFont(&DSEG7Classic_BoldItalic20pt7b);
epd->setCursor(150, 200); getdisplay().setCursor(150, 200);
epd->print(batPercentage); getdisplay().print(batPercentage);
epd->setFont(&Ubuntu_Bold16pt8b); getdisplay().setFont(&Ubuntu_Bold16pt8b);
epd->print("%"); getdisplay().print("%");
// Show time to full discharge // Show time to full discharge
epd->setFont(&DSEG7Classic_BoldItalic20pt7b); getdisplay().setFont(&DSEG7Classic_BoldItalic20pt7b);
epd->setCursor(150, 260); getdisplay().setCursor(150, 260);
if((powerSensor == "INA219" || powerSensor == "INA226") && simulation == false){ if((powerSensor == "INA219" || powerSensor == "INA226") && simulation == false){
if(batRange < 9.9) epd->print(batRange, 1); if(batRange < 9.9) getdisplay().print(batRange, 1);
else epd->print(batRange, 0); else getdisplay().print(batRange, 0);
} }
else epd->print("--"); else getdisplay().print("--");
epd->setFont(&Ubuntu_Bold16pt8b); getdisplay().setFont(&Ubuntu_Bold16pt8b);
epd->print("h"); getdisplay().print("h");
// Show sensor type info // Show sensor type info
String i2cAddr = ""; String i2cAddr = "";
epd->setFont(&Ubuntu_Bold8pt8b); getdisplay().setFont(&Ubuntu_Bold8pt8b);
epd->setCursor(270, 60); getdisplay().setCursor(270, 60);
if(powerSensor == "off") epd->print("Internal"); if(powerSensor == "off") getdisplay().print("Internal");
if(powerSensor == "INA219"){ if(powerSensor == "INA219"){
epd->print("INA219"); getdisplay().print("INA219");
} }
if(powerSensor == "INA226"){ if(powerSensor == "INA226"){
epd->print("INA226"); getdisplay().print("INA226");
i2cAddr = " (0x" + String(INA226_I2C_ADDR1, HEX) + ")"; i2cAddr = " (0x" + String(INA226_I2C_ADDR1, HEX) + ")";
} }
epd->print(i2cAddr); getdisplay().print(i2cAddr);
epd->setCursor(270, 80); getdisplay().setCursor(270, 80);
epd->print("Sensor Modul"); getdisplay().print("Sensor Modul");
// Reading bus data or using simulation data // Reading bus data or using simulation data
epd->setFont(&DSEG7Classic_BoldItalic20pt7b); getdisplay().setFont(&DSEG7Classic_BoldItalic20pt7b);
epd->setCursor(260, 140); getdisplay().setCursor(260, 140);
if(simulation == true){ if(simulation == true){
if(batVoltage == "12V"){ if(batVoltage == "12V"){
value1 = 12.0; value1 = 12.0;
@@ -275,50 +293,46 @@ public:
value1 = 24.0; value1 = 24.0;
} }
value1 += float(random(0, 5)) / 10; // Simulation data value1 += float(random(0, 5)) / 10; // Simulation data
epd->print(value1,1); getdisplay().print(value1,1);
} }
else{ else{
// Check for valid real data, display also if hold values activated // Check for valid real data, display also if hold values activated
if(valid1 == true || holdvalues == true){ if(valid1 == true || holdvalues == true){
// Resolution switching // Resolution switching
if(value1 <= 9.9) epd->print(value1, 2); if(value1 <= 9.9) getdisplay().print(value1, 2);
if(value1 > 9.9 && value1 <= 99.9)epd->print(value1, 1); if(value1 > 9.9 && value1 <= 99.9)getdisplay().print(value1, 1);
if(value1 > 99.9) epd->print(value1, 0); if(value1 > 99.9) getdisplay().print(value1, 0);
} }
else{ else{
epd->print(commonData->fmt->placeholder); // Missing bus data getdisplay().print("---"); // Missing bus data
} }
} }
epd->setFont(&Ubuntu_Bold16pt8b); getdisplay().setFont(&Ubuntu_Bold16pt8b);
epd->print("V"); getdisplay().print("V");
// Show actual current in A // Show actual current in A
epd->setFont(&DSEG7Classic_BoldItalic20pt7b); getdisplay().setFont(&DSEG7Classic_BoldItalic20pt7b);
epd->setCursor(260, 200); getdisplay().setCursor(260, 200);
if((powerSensor == "INA219" || powerSensor == "INA226") && simulation == false){ if((powerSensor == "INA219" || powerSensor == "INA226") && simulation == false){
if(value2 <= 9.9) epd->print(value2, 2); if(value2 <= 9.9) getdisplay().print(value2, 2);
if(value2 > 9.9 && value2 <= 99.9)epd->print(value2, 1); if(value2 > 9.9 && value2 <= 99.9)getdisplay().print(value2, 1);
if(value2 > 99.9) epd->print(value2, 0); if(value2 > 99.9) getdisplay().print(value2, 0);
} }
else { else getdisplay().print("---");
epd->print(commonData->fmt->placeholder); getdisplay().setFont(&Ubuntu_Bold16pt8b);
} getdisplay().print("A");
epd->setFont(&Ubuntu_Bold16pt8b);
epd->print("A");
// Show actual consumption in W // Show actual consumption in W
epd->setFont(&DSEG7Classic_BoldItalic20pt7b); getdisplay().setFont(&DSEG7Classic_BoldItalic20pt7b);
epd->setCursor(260, 260); getdisplay().setCursor(260, 260);
if((powerSensor == "INA219" || powerSensor == "INA226") && simulation == false){ if((powerSensor == "INA219" || powerSensor == "INA226") && simulation == false){
if(value3 <= 9.9) epd->print(value3, 2); if(value3 <= 9.9) getdisplay().print(value3, 2);
if(value3 > 9.9 && value3 <= 99.9)epd->print(value3, 1); if(value3 > 9.9 && value3 <= 99.9)getdisplay().print(value3, 1);
if(value3 > 99.9) epd->print(value3, 0); if(value3 > 99.9) getdisplay().print(value3, 0);
} }
else { else getdisplay().print("---");
epd->print(commonData->fmt->placeholder); getdisplay().setFont(&Ubuntu_Bold16pt8b);
} getdisplay().print("W");
epd->setFont(&Ubuntu_Bold16pt8b);
epd->print("W");
return PAGE_UPDATE; return PAGE_UPDATE;
}; };

View File

@@ -1,113 +1,301 @@
// SPDX-License-Identifier: GPL-2.0-or-later
#if defined BOARD_OBP60S3 || defined BOARD_OBP40S3 #if defined BOARD_OBP60S3 || defined BOARD_OBP40S3
#include "Pagedata.h" #include "Pagedata.h"
#include "OBP60Extensions.h" #include "OBP60Extensions.h"
/* /*
* TODO mode: race timer: keys * PageClock: Clock page with
* - prepare: set countdown to 5min * - Analog mode (mode == 'A')
* reset: abort current countdown and start over with 5min preparation * - Digital mode (mode == 'D')
* - 5min: key press * - Countdown timer mode (mode == 'T')
* - 4min: key press to sync * - Keys in mode analog and digital clock:
* - 1min: buzzer signal * K1: MODE (A/D/T)
* - start: buzzer signal for start * K2: POS (select field: HH / MM / SS)
* K3:
* K4:
* K5: TZ (Local/UTC)
* *
* Regatta timer mode:
* - Format HH:MM:SS (24h, leading zeros)
* - Keys in timer mode:
* K1: MODE (A/D/T)
* K2: POS (select field: HH / MM / SS)
* K3: + (increment selected field)
* K4: - (decrement selected field)
* K5: RUN (start/stop countdown)
* - Selection marker: line under active field (width 2px, not wider than digits)
* - Editing only possible when timer is not running
* - When page is left, running timer continues in background using RTC time
* (on re-entry, remaining time is recalculated from RTC)
*/ */
class PageClock : public Page class PageClock : public Page
{ {
private: bool simulation = false;
fmtDate dateformat;
int simtime; int simtime;
bool keylock = false; bool keylock = false;
#ifdef BOARD_OBP60S3
char source = 'G'; // Time source (R)TC | (G)PS | (N)TP
#endif
#ifdef BOARD_OBP40S3
char source = 'R'; // time source (R)TC | (G)PS | (N)TP char source = 'R'; // time source (R)TC | (G)PS | (N)TP
char mode = 'A'; // display mode (A)nalog | (D)igital | race (T)imer #endif
char tz = 'L'; // time zone (L)ocal | (U)TC char mode = 'A'; // Display mode (A)nalog | (D)igital | race (T)imer
double timezone = 0; // there are timezones with non int offsets, e.g. 5.5 or 5.75 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 homelat;
double homelon; double homelon;
bool homevalid = false; // homelat and homelon are valid bool homevalid = false; // Homelat and homelon are valid
// Timer state (static so it survives page switches)
static bool timerInitialized;
static bool timerRunning;
static int timerHours;
static int timerMinutes;
static int timerSeconds;
// Preset seconds for sync button (default 4 minutes)
static const int timerPresetSeconds = 4 * 60;
// Initial timer setting at start (so we can restore it)
static int timerStartHours;
static int timerStartMinutes;
static int timerStartSeconds;
static int selectedField; // 0 = hours, 1 = minutes, 2 = seconds
static bool showSelectionMarker;
static time_t timerEndEpoch; // Absolute end time based on RTC
void setupTimerDefaults()
{
if (!timerInitialized) {
timerInitialized = true;
timerRunning = false;
timerHours = 0;
timerMinutes = 0;
timerSeconds = 0;
timerStartHours = 0;
timerStartMinutes = 0;
timerStartSeconds = 0;
selectedField = 0;
showSelectionMarker = true;
timerEndEpoch = 0;
}
}
// Limiter for overrun settings values
static int clamp(int value, int minVal, int maxVal)
{
if (value < minVal) return maxVal;
if (value > maxVal) return minVal;
return value;
}
void incrementSelected()
{
if (selectedField == 0) {
timerHours = clamp(timerHours + 1, 0, 23);
} else if (selectedField == 1) {
timerMinutes = clamp(timerMinutes + 1, 0, 59);
} else {
timerSeconds = clamp(timerSeconds + 1, 0, 59);
}
}
void decrementSelected()
{
if (selectedField == 0) {
timerHours = clamp(timerHours - 1, 0, 23);
} else if (selectedField == 1) {
timerMinutes = clamp(timerMinutes - 1, 0, 59);
} else {
timerSeconds = clamp(timerSeconds - 1, 0, 59);
}
}
int totalTimerSeconds() const
{
return timerHours * 3600 + timerMinutes * 60 + timerSeconds;
}
public: public:
PageClock(CommonData &common) : Page(common) PageClock(CommonData& common)
{ {
logger->logDebug(GwLog::LOG, "Instantiate PageClock"); commonData = &common;
common.logger->logDebug(GwLog::LOG, "Instantiate PageClock");
// Get config data simulation = common.config->getBool(common.config->useSimuData);
dateformat = common.fmt->getDateFormat(config->getString(config->dateFormat)); timezone = common.config->getString(common.config->timeZone).toDouble();
timezone = config->getString(config->timeZone).toDouble(); homelat = common.config->getString(common.config->homeLAT).toDouble();
homelat = config->getString(config->homeLAT).toDouble(); homelon = common.config->getString(common.config->homeLON).toDouble();
homelon = config->getString(config->homeLON).toDouble();
homevalid = homelat >= -180.0 and homelat <= 180 and homelon >= -90.0 and homelon <= 90.0; homevalid = homelat >= -180.0 and homelat <= 180 and homelon >= -90.0 and homelon <= 90.0;
simtime = 38160; // time value 11:36 simtime = 38160; // time value 11:36
setupTimerDefaults();
#ifdef BOARD_OBP60S3
// WIP time source
String use_rtc = config->getString(config->useRTC);
if (use_rtc == "off") {
source = 'G';
}
#endif
} }
void setupKeys(){ virtual void setupKeys()
{
Page::setupKeys(); Page::setupKeys();
commonData->keydata[0].label = "SRC";
commonData->keydata[1].label = "MODE"; if (mode == 'T') {
// Timer mode: MODE, POS, +, -, RUN
commonData->keydata[0].label = "MODE";
commonData->keydata[1].label = "POS";
// K3: '+' while editing, 'SYNC' while running to set a preset countdown
commonData->keydata[2].label = timerRunning ? "SYNC" : "+";
commonData->keydata[3].label = "-";
commonData->keydata[4].label = timerRunning ? "RESET" : "START";
} else {
// Clock modes: like original
commonData->keydata[0].label = "MODE";
commonData->keydata[1].label = "SRC";
commonData->keydata[4].label = "TZ"; commonData->keydata[4].label = "TZ";
} }
}
// Key functions // Key functions
int handleKey(int key){ virtual int handleKey(int key)
// Time source {
if (key == 1) { setupTimerDefaults();
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
return 0; // Commit the key return 0; // Commit the key
} }
if (mode == 'T') {
// Timer mode key handling
// MODE (K1): cycle display mode A/D/T
if (key == 1) {
switch (mode) {
case 'A': mode = 'D'; break;
case 'D': mode = 'T'; break;
case 'T': mode = 'A'; break;
default: mode = 'A'; break;
}
setupKeys();
return 0;
}
// POS (K2): select field HH / MM / SS (only if timer not running)
if (key == 2 && !timerRunning) {
selectedField = (selectedField + 1) % 3;
showSelectionMarker = true;
return 0;
}
// + (K3): increment selected field (only if timer not running)
if (key == 3 && !timerRunning) {
incrementSelected();
return 0;
}
if (key == 3 && timerRunning) {
// When timer is running, K3 acts as a synchronization button:
// set remaining countdown to the preset value (e.g. 4 minutes).
if (commonData->data.rtcValid) {
int preset = timerPresetSeconds;
// update start-setting so STOP will restore this preset
timerStartHours = preset / 3600;
timerStartMinutes = (preset % 3600) / 60;
timerStartSeconds = preset % 60;
struct tm rtcCopy = commonData->data.rtcTime;
time_t nowEpoch = mktime(&rtcCopy);
timerEndEpoch = nowEpoch + preset;
// Update visible timer fields immediately
timerHours = timerStartHours;
timerMinutes = timerStartMinutes;
timerSeconds = timerStartSeconds;
// commonData->keydata[4].label = "RESET";
}
return 0;
}
// - (K4): decrement selected field (only if timer not running)
if (key == 4 && !timerRunning) {
decrementSelected();
return 0;
}
if (key == 4 && timerRunning) { // No action if timer running
return 0;
}
// RUN (K5): start/stop timer
if (key == 5) {
if (!timerRunning) {
// Start timer if a non-zero duration is set
int total = totalTimerSeconds();
if (total > 0 && commonData->data.rtcValid) {
// Remember initial timer setting at start
timerStartHours = timerHours;
timerStartMinutes = timerMinutes;
timerStartSeconds = timerSeconds;
struct tm rtcCopy = commonData->data.rtcTime;
time_t nowEpoch = mktime(&rtcCopy);
timerEndEpoch = nowEpoch + total;
timerRunning = true;
showSelectionMarker = false;
}
} else {
// Stop timer: restore initial start setting
timerHours = timerStartHours;
timerMinutes = timerStartMinutes;
timerSeconds = timerStartSeconds;
timerRunning = false;
showSelectionMarker = true;
// marker will become visible again only after POS press
}
return 0;
}
// In timer mode, other keys are passed through
return key; return key;
} }
void displayNew(PageData &pageData) { // Clock (A/D) modes key handling like original PageClock
#ifdef BOARD_OBP60S3
// Clear optical warning
if (flashLED == "Limit Violation") {
setBlinkingLED(false);
setFlashLED(false);
}
#endif
};
int displayPage(PageData &pageData) { // MODE (K1)
if (key == 1) {
switch (mode) {
case 'A': mode = 'D'; break;
case 'D': mode = 'T'; break;
case 'T': mode = 'A'; break;
default: mode = 'A'; break;
}
setupKeys();
return 0;
}
// Time source (K2)
if (key == 2) {
switch (source) {
case 'G': source = 'R'; break;
case 'R': source = 'G'; break;
default: source = 'G'; break;
}
return 0;
}
// Time zone: Local / UTC (K5)
if (key == 5) {
switch (tz) {
case 'L': tz = 'U'; break;
case 'U': tz = 'L'; break;
default: tz = 'L'; break;
}
return 0;
}
return key;
}
int displayPage(PageData& pageData)
{
GwConfigHandler* config = commonData->config;
GwLog* logger = commonData->logger;
setupTimerDefaults();
setupKeys(); // Ensure correct key labels for current mode
static String svalue1old = ""; static String svalue1old = "";
static String unit1old = ""; static String unit1old = "";
@@ -119,58 +307,60 @@ public:
static String svalue5old = ""; static String svalue5old = "";
static String svalue6old = ""; static String svalue6old = "";
double value1 = 0; // GPS time double value1 = 0;
double value2 = 0; // GPS date FIXME date defined as uint32_t! double value2 = 0;
double value3 = 0; // HDOP double value3 = 0;
bool gpsvalid = false;
// Get config data
String lengthformat = config->getString(config->lengthFormat);
String dateformat = config->getString(config->dateFormat);
bool holdvalues = config->getBool(config->holdvalues);
String flashLED = config->getString(config->flashLED);
String backlightMode = config->getString(config->backlight);
// 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
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
if (simulation == false) { if (simulation == false) {
value1 = bvalue1->value; // Value as double in SI unit value1 = bvalue1->value; // Value as double in SI unit
} } else {
else{
value1 = simtime++; // Simulation data for time value 11:36 in seconds value1 = simtime++; // Simulation data for time value 11:36 in seconds
} // Other simulation data see OBP60Formatter.cpp } // Other simulation data see OBP60Formatter.cpp
bool valid1 = bvalue1->valid; // Valid information bool valid1 = bvalue1->valid; // Valid information
String svalue1 = commonData->fmt->formatValue(bvalue1, *commonData).svalue; // Formatted value as string including unit conversion and switching decimal places String svalue1 = formatValue(bvalue1, *commonData).svalue; // Formatted value
String unit1 = commonData->fmt->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 GPS date // Get boat values for GPS date
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 = bvalue2->getName().c_str(); // Value name 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
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 informationgetdisplay().print("RTC");
String svalue2 = commonData->fmt->formatValue(bvalue2, *commonData).svalue; // Formatted value as string including unit conversion and switching decimal places String svalue2 = formatValue(bvalue2, *commonData).svalue; // Formatted value
String unit2 = commonData->fmt->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 for HDOP date // Get boat values for HDOP
GwApi::BoatValue *bvalue3 = pageData.values[2]; // Third element in list (only one value by PageOneValue) GwApi::BoatValue* bvalue3 = pageData.values[2]; // Third element in list
String name3 = bvalue3->getName().c_str(); // Value name String name3 = bvalue3->getName().c_str(); // Value name
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 = commonData->fmt->formatValue(bvalue3, *commonData).svalue; // Formatted value as string including unit conversion and switching decimal places String svalue3 = formatValue(bvalue3, *commonData).svalue; // Formatted value
String unit3 = commonData->fmt->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
} }
// GPS date and time are valid and can be used
gpsvalid = (valid1 && valid2 && valid3);
// 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);
@@ -178,159 +368,317 @@ public:
} }
// Logging boat values // Logging boat values
if (bvalue1 == NULL) return PAGE_OK; // WTF why this statement? if (bvalue1 == NULL) return PAGE_OK;
logger->logDebug(GwLog::LOG, "Drawing at PageClock, %s:%f, %s:%f, %s:%f", name1.c_str(), value1, name2.c_str(), value2, name3.c_str(), value3); LOG_DEBUG(GwLog::LOG, "Drawing at PageClock, %s:%f, %s:%f", name1.c_str(), value1, name2.c_str(), value2);
// Draw page // Draw page
//*********************************************************** //***********************************************************
// Set display in partial refresh mode // Set display in partial refresh mode
epd->setPartialWindow(0, 0, epd->width(), epd->height()); // Set partial update getdisplay().setPartialWindow(0, 0, getdisplay().width(), getdisplay().height()); // Set partial update
epd->setTextColor(commonData->fgcolor); getdisplay().setTextColor(commonData->fgcolor);
time_t tv = mktime(&commonData->data.rtcTime) + timezone * 3600; time_t tv = mktime(&commonData->data.rtcTime) + timezone * 3600;
struct tm* local_tm = localtime(&tv); struct tm* local_tm = localtime(&tv);
if (mode == 'T') {
// REGATTA TIMER MODE: countdown timer HH:MM:SS in the center with 7-segment font
//*******************************************************************************
int dispH = timerHours;
int dispM = timerMinutes;
int dispS = timerSeconds;
// Update remaining time if timer is running (based on RTC)
if (timerRunning && commonData->data.rtcValid) {
struct tm rtcCopy = commonData->data.rtcTime;
time_t nowEpoch = mktime(&rtcCopy);
time_t remaining = timerEndEpoch - nowEpoch;
if(remaining <= 5 && remaining != 0){
// Short pre buzzer alarm (100% power)
setBuzzerPower(100);
buzzer(TONE2, 75);
setBuzzerPower(config->getInt(config->buzzerPower));
}
if (remaining <= 0) {
remaining = 0;
timerRunning = false;
commonData->keydata[3].label = "-";
commonData->keydata[4].label = "START";
showSelectionMarker = true;
// Buzzer alarm (100% power)
setBuzzerPower(100);
buzzer(TONE2, 800);
setBuzzerPower(config->getInt(config->buzzerPower));
// When countdown is finished, restore the initial start time
timerHours = timerStartHours;
timerMinutes = timerStartMinutes;
timerSeconds = timerStartSeconds;
}
else{
commonData->keydata[3].label = "";
commonData->keydata[4].label = "RESET";
}
int rem = static_cast<int>(remaining);
dispH = rem / 3600;
rem -= dispH * 3600;
dispM = rem / 60;
dispS = rem % 60;
}
char buf[9]; // "HH:MM:SS"
snprintf(buf, sizeof(buf), "%02d:%02d:%02d", dispH, dispM, dispS);
String timeStr = String(buf);
// Clear central area and draw large digital time
getdisplay().fillRect(0, 110, getdisplay().width(), 80, commonData->bgcolor);
getdisplay().setFont(&DSEG7Classic_BoldItalic30pt7b);
// Determine widths for digits and colon to position selection underline exactly
int16_t x0, y0;
uint16_t wDigit, hDigit;
uint16_t wColon, hColon;
getdisplay().getTextBounds("00", 0, 0, &x0, &y0, &wDigit, &hDigit);
getdisplay().getTextBounds(":", 0, 0, &x0, &y0, &wColon, &hColon);
uint16_t totalWidth = 3 * wDigit + 2 * wColon;
int16_t baseX = (static_cast<int16_t>(getdisplay().width()) - static_cast<int16_t>(totalWidth)) / 2;
int16_t centerY = 150;
// Draw time string centered
int16_t x1b, y1b;
uint16_t wb, hb;
getdisplay().getTextBounds(timeStr, 0, 0, &x1b, &y1b, &wb, &hb);
int16_t textX = (static_cast<int16_t>(getdisplay().width()) - static_cast<int16_t>(wb)) / 2;
int16_t textY = centerY + hb / 2;
//getdisplay().setCursor(textX, textY); // horzontal jitter
getdisplay().setCursor(47, textY); // static X position
getdisplay().print(timeStr);
// Selection marker (only visible when not running and POS pressed)
if (!timerRunning && showSelectionMarker) {
int16_t selX = baseX - 8; // Hours start
if (selectedField == 1) {
selX = baseX + wDigit + wColon; // Minutes start
} else if (selectedField == 2) {
selX = baseX + 2 * wDigit + 2 * wColon + 12; // Seconds start
}
int16_t underlineY = centerY + hb / 2 + 5;
//getdisplay().fillRect(selX, underlineY, wDigit, 6, commonData->fgcolor);
getdisplay().fillRoundRect(selX, underlineY, wDigit, 6, 2, commonData->fgcolor);
}
// Page label
getdisplay().setFont(&Ubuntu_Bold16pt8b);
getdisplay().setCursor(100, 70);
getdisplay().print("Regatta Timer");
} else if (mode == 'D') {
// DIGITAL CLOCK MODE: large 7-segment time based on GPS/RTC
//**********************************************************
int hour24 = 0;
int minute24 = 0;
int second24 = 0;
if (source == 'R' && commonData->data.rtcValid) {
time_t tv2 = mktime(&commonData->data.rtcTime);
if (tz == 'L') {
tv2 += static_cast<time_t>(timezone * 3600);
}
struct tm* tm2 = localtime(&tv2);
hour24 = tm2->tm_hour;
minute24 = tm2->tm_min;
second24 = tm2->tm_sec;
} else {
double t = value1;
if (tz == 'L') {
t += timezone * 3600;
}
if (t >= 86400) t -= 86400;
if (t < 0) t += 86400;
hour24 = static_cast<int>(t / 3600.0);
int rest = static_cast<int>(t) - hour24 * 3600;
minute24 = rest / 60;
second24 = rest % 60;
}
char buf[9]; // "HH:MM:SS"
snprintf(buf, sizeof(buf), "%02d:%02d:%02d", hour24, minute24, second24);
String timeStr = String(buf);
getdisplay().fillRect(0, 110, getdisplay().width(), 80, commonData->bgcolor);
getdisplay().setFont(&DSEG7Classic_BoldItalic30pt7b);
int16_t x1b, y1b;
uint16_t wb, hb;
getdisplay().getTextBounds(timeStr, 0, 0, &x1b, &y1b, &wb, &hb);
int16_t x = (static_cast<int16_t>(getdisplay().width()) - static_cast<int16_t>(wb)) / 2;
int16_t y = 150 + hb / 2;
//getdisplay().setCursor(x, y); // horizontal jitter
getdisplay().setCursor(47, y); // static X position
getdisplay().print(timeStr); // Display actual time
// Small indicators: timezone and source
getdisplay().setFont(&Ubuntu_Bold8pt8b);
getdisplay().setCursor(47, 110);
if (source == 'G') {
getdisplay().print("GPS");
} else {
getdisplay().print("RTC");
}
getdisplay().setCursor(47 + 40, 110);
if (holdvalues == false) {
getdisplay().print(tz == 'L' ? "LOT" : "UTC");
} else {
getdisplay().print(unit2old); // date unit
}
// Page label
getdisplay().setFont(&Ubuntu_Bold16pt8b);
getdisplay().setCursor(100, 70);
getdisplay().print("Digital Clock");
} else {
// ANALOG CLOCK MODE (mode == 'A')
//********************************
// Show values GPS date // Show values GPS date
epd->setFont(&Ubuntu_Bold8pt8b); getdisplay().setFont(&Ubuntu_Bold8pt8b);
epd->setCursor(10, 65); getdisplay().setCursor(10, 65);
if (holdvalues == false) { if (holdvalues == false) {
if (source == 'G') { if (source == 'G') {
// GPS value // GPS value
epd->print(svalue2); getdisplay().print(svalue2);
} else if (commonData->data.rtcValid) { } else if (commonData->data.rtcValid) {
// RTC value // RTC value
if (tz == 'L') { if (tz == 'L') {
epd->print(formatDate(dateformat, local_tm->tm_year + 1900, local_tm->tm_mon + 1, local_tm->tm_mday)); getdisplay().print(formatDate(dateformat, local_tm->tm_year + 1900, local_tm->tm_mon + 1, local_tm->tm_mday));
} } else {
else { getdisplay().print(formatDate(dateformat, commonData->data.rtcTime.tm_year + 1900, commonData->data.rtcTime.tm_mon + 1, commonData->data.rtcTime.tm_mday));
epd->print(formatDate(dateformat, commonData->data.rtcTime.tm_year + 1900, commonData->data.rtcTime.tm_mon + 1, commonData->data.rtcTime.tm_mday));
} }
} else { } else {
epd->print(commonData->fmt->placeholder); getdisplay().print("---");
} }
} else { } else {
epd->print(svalue2old); getdisplay().print(svalue2old);
} }
epd->setFont(&Ubuntu_Bold12pt8b); getdisplay().setFont(&Ubuntu_Bold12pt8b);
epd->setCursor(10, 95); getdisplay().setCursor(10, 95);
epd->print("Date"); // Name getdisplay().print("Date"); // Name
// Horizintal separator left // Horizontal separator left
epd->fillRect(0, 149, 60, 3, commonData->fgcolor); getdisplay().fillRect(0, 149, 60, 3, commonData->fgcolor);
// Show values GPS time // Show values GPS time (small text bottom left)
epd->setFont(&Ubuntu_Bold8pt8b); getdisplay().setFont(&Ubuntu_Bold8pt8b);
epd->setCursor(10, 250); getdisplay().setCursor(10, 250);
if (holdvalues == false) { if (holdvalues == false) {
if (source == 'G') { if (source == 'G') {
epd->print(svalue1); // Value getdisplay().print(svalue1); // Value
} } else if (commonData->data.rtcValid) {
else if (commonData->data.rtcValid) {
if (tz == 'L') { if (tz == 'L') {
epd->print(formatTime(fmtTime::MMHHSS, local_tm->tm_hour, local_tm->tm_min, local_tm->tm_sec)); getdisplay().print(formatTime('s', local_tm->tm_hour, local_tm->tm_min, local_tm->tm_sec));
} } else {
else { getdisplay().print(formatTime('s', commonData->data.rtcTime.tm_hour, commonData->data.rtcTime.tm_min, commonData->data.rtcTime.tm_sec));
epd->print(formatTime(fmtTime::MMHHSS, commonData->data.rtcTime.tm_hour, commonData->data.rtcTime.tm_min, commonData->data.rtcTime.tm_sec));
} }
} else { } else {
epd->print(commonData->fmt->placeholder); getdisplay().print("---");
} }
} else {
getdisplay().print(svalue1old);
} }
else { getdisplay().setFont(&Ubuntu_Bold12pt8b);
epd->print(svalue1old); getdisplay().setCursor(10, 220);
} getdisplay().print("Time"); // Name
epd->setFont(&Ubuntu_Bold12pt8b);
epd->setCursor(10, 220);
epd->print("Time"); // Name
// Show values sunrise // Show values sunrise
String sunrise = commonData->fmt->placeholder; String sunrise = "---";
if (((source == 'G') and gpsvalid) or (homevalid and commonData->data.rtcValid)) { 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) { } else if (simulation) {
sunrise = String("06:42"); sunrise = String("06:42");
} }
epd->setFont(&Ubuntu_Bold8pt8b); getdisplay().setFont(&Ubuntu_Bold8pt8b);
epd->setCursor(335, 65); getdisplay().setCursor(335, 65);
if(holdvalues == false) epd->print(sunrise); // Value if (holdvalues == false) getdisplay().print(sunrise); // Value
else epd->print(svalue5old); else getdisplay().print(svalue5old);
epd->setFont(&Ubuntu_Bold12pt8b); getdisplay().setFont(&Ubuntu_Bold12pt8b);
epd->setCursor(335, 95); getdisplay().setCursor(335, 95);
epd->print("SunR"); // Name getdisplay().print("SunR"); // Name
// Horizintal separator right // Horizontal separator right
epd->fillRect(340, 149, 80, 3, commonData->fgcolor); getdisplay().fillRect(340, 149, 80, 3, commonData->fgcolor);
// Show values sunset // Show values sunset
String sunset = commonData->fmt->placeholder; String sunset = "---";
if (((source == 'G') and gpsvalid) or (homevalid and commonData->data.rtcValid)) { 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) { } else if (simulation) {
sunset = String("21:03"); sunset = String("21:03");
} }
epd->setFont(&Ubuntu_Bold8pt8b); getdisplay().setFont(&Ubuntu_Bold8pt8b);
epd->setCursor(335, 250); getdisplay().setCursor(335, 250);
if(holdvalues == false) epd->print(sunset); // Value if (holdvalues == false) getdisplay().print(sunset); // Value
else epd->print(svalue6old); else getdisplay().print(svalue6old);
epd->setFont(&Ubuntu_Bold12pt8b); getdisplay().setFont(&Ubuntu_Bold12pt8b);
epd->setCursor(335, 220); getdisplay().setCursor(335, 220);
epd->print("SunS"); // Name getdisplay().print("SunS"); // Name
//*******************************************************************************************
// Draw clock
int rInstrument = 110; // Radius of clock int rInstrument = 110; // Radius of clock
float pi = 3.141592; float pi = 3.141592;
epd->fillCircle(200, 150, rInstrument + 10, commonData->fgcolor); // Outer circle getdisplay().fillCircle(200, 150, rInstrument + 10, commonData->fgcolor); // Outer circle
epd->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)
{ {
// 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 * 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 dots
const char* ii = ""; const char* ii = "";
switch (i) switch (i)
{ {
case 0: ii = "12"; break; case 0: ii = "12"; break;
case 30 : ii=""; break;
case 60 : ii=""; break;
case 90: ii = "3"; break; case 90: ii = "3"; break;
case 120 : ii=""; break;
case 150 : ii=""; break;
case 180: ii = "6"; break; case 180: ii = "6"; break;
case 210 : ii=""; break;
case 240 : ii=""; break;
case 270: ii = "9"; break; case 270: ii = "9"; break;
case 300 : ii=""; break;
case 330 : ii=""; break;
default: break; default: break;
} }
// Print text centered on position x, y // Print text centered on position x, y
int16_t x1, y1; // Return values of getTextBounds int16_t x1c, y1c; // Return values of getTextBounds
uint16_t w, h; // Return values of getTextBounds uint16_t wc, hc; // Return values of getTextBounds
epd->getTextBounds(ii, int(x), int(y), &x1, &y1, &w, &h); // Calc width of new string getdisplay().getTextBounds(ii, int(x), int(y), &x1c, &y1c, &wc, &hc); // Calc width of new string
epd->setCursor(x-w/2, y+h/2); getdisplay().setCursor(x - wc / 2, y + hc / 2);
if(i % 30 == 0){ if (i % 90 == 0) {
epd->setFont(&Ubuntu_Bold12pt8b); getdisplay().setFont(&Ubuntu_Bold12pt8b);
epd->print(ii); getdisplay().print(ii);
} }
// Draw sub scale with dots // Draw sub scale with dots
float sinx = 0; float sinx = 0;
float cosx = 0; float cosx = 0;
if (i % 6 == 0) { if (i % 6 == 0) {
float x1c = 200 + rInstrument*sin(i/180.0*pi); float x1d = 200 + rInstrument * sin(i / 180.0 * pi);
float y1c = 150 - rInstrument*cos(i/180.0*pi); float y1d = 150 - rInstrument * cos(i / 180.0 * pi);
epd->fillCircle((int)x1c, (int)y1c, 2, commonData->fgcolor); getdisplay().fillCircle((int)x1d, (int)y1d, 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);
} }
@@ -342,31 +690,30 @@ public:
float xx2 = +dx; float xx2 = +dx;
float yy1 = -(rInstrument - 10); float yy1 = -(rInstrument - 10);
float yy2 = -(rInstrument + 10); float yy2 = -(rInstrument + 10);
epd->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);
epd->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
epd->setFont(&Ubuntu_Bold12pt8b); getdisplay().setFont(&Ubuntu_Bold12pt8b);
epd->setCursor(175, 110); getdisplay().setCursor(175, 110);
if (holdvalues == false) { if (holdvalues == false) {
epd->print(tz == 'L' ? "LOT" : "UTC"); getdisplay().print(tz == 'L' ? "LOT" : "UTC");
} } else {
else{ getdisplay().print(unit2old); // date unit
epd->print(unit2old); // date unit
} }
epd->setFont(&Ubuntu_Bold8pt8b); getdisplay().setFont(&Ubuntu_Bold8pt8b);
epd->setCursor(185, 190); getdisplay().setCursor(185, 190);
if (source == 'G') { if (source == 'G') {
epd->print("GPS"); getdisplay().print("GPS");
} else { } else {
epd->print("RTC"); getdisplay().print("RTC");
} }
// Clock values // Clock values
@@ -374,10 +721,10 @@ public:
double minute = 0; double minute = 0;
if (source == 'R') { if (source == 'R') {
if (tz == 'L') { if (tz == 'L') {
time_t tv = mktime(&commonData->data.rtcTime) + timezone * 3600; time_t tv2 = mktime(&commonData->data.rtcTime) + timezone * 3600;
struct tm *local_tm = localtime(&tv); struct tm* local_tm2 = localtime(&tv2);
minute = local_tm->tm_min; minute = local_tm2->tm_min;
hour = local_tm->tm_hour; hour = local_tm2->tm_hour;
} else { } else {
minute = commonData->data.rtcTime.tm_min; minute = commonData->data.rtcTime.tm_min;
hour = commonData->data.rtcTime.tm_hour; hour = commonData->data.rtcTime.tm_hour;
@@ -396,7 +743,7 @@ public:
if (hour > 12) { if (hour > 12) {
hour -= 12.0; hour -= 12.0;
} }
logger->logDebug(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
@@ -409,7 +756,7 @@ public:
float xx2 = startwidth; float xx2 = startwidth;
float yy1 = -startwidth; float yy1 = -startwidth;
float yy2 = -(rInstrument * 0.5); float yy2 = -(rInstrument * 0.5);
epd->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
@@ -419,7 +766,7 @@ public:
float ix2 = -endwidth; float ix2 = -endwidth;
float iy1 = -(rInstrument * 0.5); float iy1 = -(rInstrument * 0.5);
float iy2 = -endwidth; float iy2 = -endwidth;
epd->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);
} }
@@ -435,7 +782,7 @@ public:
float xx2 = startwidth; float xx2 = startwidth;
float yy1 = -startwidth; float yy1 = -startwidth;
float yy2 = -(rInstrument - 15); float yy2 = -(rInstrument - 15);
epd->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
@@ -445,28 +792,43 @@ public:
float ix2 = -endwidth; float ix2 = -endwidth;
float iy1 = -(rInstrument - 15); float iy1 = -(rInstrument - 15);
float iy2 = -endwidth; float iy2 = -endwidth;
epd->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
epd->fillCircle(200, 150, startwidth + 6, commonData->bgcolor); getdisplay().fillCircle(200, 150, startwidth + 6, commonData->bgcolor);
epd->fillCircle(200, 150, startwidth + 4, commonData->fgcolor); getdisplay().fillCircle(200, 150, startwidth + 4, commonData->fgcolor);
}
return PAGE_UPDATE; return PAGE_UPDATE;
}; };
}; };
static Page *createPage(CommonData &common){ // Static member definitions
bool PageClock::timerInitialized = false;
bool PageClock::timerRunning = false;
int PageClock::timerHours = 0;
int PageClock::timerMinutes = 0;
int PageClock::timerSeconds = 0;
int PageClock::timerStartHours = 0;
int PageClock::timerStartMinutes = 0;
int PageClock::timerStartSeconds = 0;
int PageClock::selectedField = 0;
bool PageClock::showSelectionMarker = true;
time_t PageClock::timerEndEpoch = 0;
static Page* createPage(CommonData& common)
{
return new PageClock(common); return new PageClock(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) * we provide the number of user parameters we expect (0 here)
* and will will provide the names of the fixed values we need * and we provide the names of the fixed values we need
*/ */
PageDescription registerPageClock( PageDescription registerPageClock(
"Clock", // Page name "Clock", // Page name
@@ -477,3 +839,4 @@ PageDescription registerPageClock(
); );
#endif #endif

View File

@@ -1,4 +1,3 @@
// SPDX-License-Identifier: GPL-2.0-or-later
#if defined BOARD_OBP60S3 || defined BOARD_OBP40S3 #if defined BOARD_OBP60S3 || defined BOARD_OBP40S3
#include "Pagedata.h" #include "Pagedata.h"
@@ -18,31 +17,31 @@ const int ShowSTW = 3;
const int ShowSOG = 4; const int ShowSOG = 4;
const int ShowDBS = 5; const int ShowDBS = 5;
const int Compass_X0 = 200; // center point of compass band const int Compass_X0 = 200; // X center point of compass band
const int Compass_Y0 = 220; // position of compass lines const int Compass_Y0 = 220; // Y position of compass lines
const int Compass_LineLength = 22; // length 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 const float Compass_LineDelta = 8.0;// Compass band: 1deg = 5 Pixels, 10deg = 50 Pixels
class PageCompass : public Page class PageCompass : public Page
{ {
private:
int WhichDataCompass = ShowHDM; int WhichDataCompass = ShowHDM;
int WhichDataDisplay = ShowHDM; int WhichDataDisplay = ShowHDM;
public: public:
PageCompass(CommonData &common) : Page(common) PageCompass(CommonData &common){
{ commonData = &common;
logger->logDebug(GwLog::LOG, "Instantiate PageCompass"); common.logger->logDebug(GwLog::LOG,"Instantiate PageCompass");
} }
void setupKeys(){ virtual void setupKeys(){
Page::setupKeys(); Page::setupKeys();
commonData->keydata[0].label = "CMP"; commonData->keydata[0].label = "CMP";
commonData->keydata[1].label = "SRC"; commonData->keydata[1].label = "SRC";
} }
int handleKey(int key){ virtual int handleKey(int key){
// Code for keylock // Code for keylock
if ( key == 1 ) { if ( key == 1 ) {
WhichDataCompass += 1; WhichDataCompass += 1;
if ( WhichDataCompass > ShowCOG) if ( WhichDataCompass > ShowCOG)
@@ -54,6 +53,7 @@ public:
if ( WhichDataDisplay > ShowDBS) if ( WhichDataDisplay > ShowDBS)
WhichDataDisplay = ShowHDM; WhichDataDisplay = ShowHDM;
} }
if(key == 11){ if(key == 11){
commonData->keylock = !commonData->keylock; commonData->keylock = !commonData->keylock;
return 0; // Commit the key return 0; // Commit the key
@@ -61,22 +61,21 @@ public:
return key; return key;
} }
void displayNew(PageData &pageData) {
#ifdef BOARD_OBP60S3
// Clear optical warning
if (flashLED == "Limit Violation") {
setBlinkingLED(false);
setFlashLED(false);
}
#endif
};
int displayPage(PageData &pageData){ int displayPage(PageData &pageData){
GwConfigHandler *config = commonData->config;
GwLog *logger = commonData->logger;
// Old values for hold function // Old values for hold function
static String OldDataText[HowManyValues] = {"", "", "","", "", ""}; static String OldDataText[HowManyValues] = {"", "", "","", "", ""};
static String OldDataUnits[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; GwApi::BoatValue *bvalue;
String DataName[HowManyValues]; String DataName[HowManyValues];
double DataValue[HowManyValues]; double DataValue[HowManyValues];
@@ -88,15 +87,21 @@ public:
for (int i = 0; i < HowManyValues; i++){ for (int i = 0; i < HowManyValues; i++){
bvalue = pageData.values[i]; bvalue = pageData.values[i];
TheFormattedData = commonData->fmt->formatValue(bvalue, *commonData); TheFormattedData = formatValue(bvalue, *commonData);
DataName[i] = xdrDelete(bvalue->getName()); DataName[i] = xdrDelete(bvalue->getName());
DataName[i] = DataName[i].substring(0, 6); // String length limit for value name DataName[i] = DataName[i].substring(0, 6); // String length limit for value name
DataUnits[i] = commonData->fmt->formatValue(bvalue, *commonData).unit; DataUnits[i] = formatValue(bvalue, *commonData).unit;
DataText[i] = TheFormattedData.svalue; // Formatted value as string including unit conversion and switching decimal places 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 DataValue[i] = TheFormattedData.value; // Value as double in SI unit
DataValid[i] = bvalue->valid; DataValid[i] = bvalue->valid;
DataFormat[i] = bvalue->getFormat(); // Unit of value DataFormat[i] = bvalue->getFormat(); // Unit of value
logger->logDebug(GwLog::LOG, "Drawing at PageCompass: %d %s %f %s %s", i, DataName[i], DataValue[i], DataFormat[i], DataText[i] ); 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? if (bvalue == NULL) return PAGE_OK; // WTF why this statement?
@@ -104,27 +109,27 @@ public:
//*********************************************************** //***********************************************************
// Set display in partial refresh mode // Set display in partial refresh mode
epd->setPartialWindow(0, 0, epd->width(), epd->height()); // Set partial update getdisplay().setPartialWindow(0, 0, getdisplay().width(), getdisplay().height()); // Set partial update
epd->setTextColor(commonData->fgcolor); getdisplay().setTextColor(commonData->fgcolor);
// Horizontal line 2 pix top & bottom // Horizontal line 2 pix top & bottom
// Print data on top half // Print data on top half
epd->fillRect(0, 130, 400, 2, commonData->fgcolor); getdisplay().fillRect(0, 130, 400, 2, commonData->fgcolor);
epd->setFont(&Ubuntu_Bold20pt8b); getdisplay().setFont(&Ubuntu_Bold20pt8b);
epd->setCursor(10, 70); getdisplay().setCursor(10, 70);
epd->print(DataName[WhichDataDisplay]); // Page name getdisplay().print(DataName[WhichDataDisplay]); // Page name
// Show unit // Show unit
epd->setFont(&Ubuntu_Bold12pt8b); getdisplay().setFont(&Ubuntu_Bold12pt8b);
epd->setCursor(10, 120); getdisplay().setCursor(10, 120);
epd->print(DataUnits[WhichDataDisplay]); getdisplay().print(DataUnits[WhichDataDisplay]);
epd->setCursor(190, 120); getdisplay().setCursor(190, 120);
epd->setFont(&DSEG7Classic_BoldItalic42pt7b); getdisplay().setFont(&DSEG7Classic_BoldItalic42pt7b);
if(holdvalues == false){ if(holdvalues == false){
epd->print(DataText[WhichDataDisplay]); // Real value as formated string getdisplay().print(DataText[WhichDataDisplay]); // Real value as formated string
} }
else{ else{
epd->print(OldDataText[WhichDataDisplay]); // Old value as formated string getdisplay().print(OldDataText[WhichDataDisplay]); // Old value as formated string
} }
if(DataValid[WhichDataDisplay] == true){ if(DataValid[WhichDataDisplay] == true){
OldDataText[WhichDataDisplay] = DataText[WhichDataDisplay]; // Save the old value OldDataText[WhichDataDisplay] = DataText[WhichDataDisplay]; // Save the old value
@@ -143,14 +148,14 @@ public:
char buffer[bsize+1]; char buffer[bsize+1];
buffer[0]=0; buffer[0]=0;
epd->setFont(&Ubuntu_Bold16pt8b); getdisplay().setFont(&Ubuntu_Bold16pt8b);
epd->setCursor(10, Compass_Y0-60); getdisplay().setCursor(10, Compass_Y0-60);
epd->print(DataName[WhichDataCompass]); // Page name getdisplay().print(DataName[WhichDataCompass]); // Page name
// Draw compass base line and pointer // Draw compass base line and pointer
epd->fillRect(0, Compass_Y0, 400, 3, commonData->fgcolor); getdisplay().fillRect(0, Compass_Y0, 400, 3, commonData->fgcolor);
epd->fillTriangle(Compass_X0,Compass_Y0-40,Compass_X0-10,Compass_Y0-80,Compass_X0+10,Compass_Y0-80,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 // Draw trendlines
for ( int i = 1; i < abs(TheTrend) / 2; i++){ for ( int i = 1; i < abs(TheTrend) / 2; i++){
int x1; int x1;
@@ -159,7 +164,7 @@ public:
else else
x1 = Compass_X0 - 20 * ( i + 1 ); x1 = Compass_X0 - 20 * ( i + 1 );
epd->fillRect(x1, Compass_Y0 -55, 10, 6, commonData->fgcolor); getdisplay().fillRect(x1, Compass_Y0 -55, 10, 6, commonData->fgcolor);
} }
// Central line + satellite lines // Central line + satellite lines
double NextSector = round(TheAngle / ( M_PI / 9 )) * ( M_PI / 9 ); // Get the next 20degree value double NextSector = round(TheAngle / ( M_PI / 9 )) * ( M_PI / 9 ); // Get the next 20degree value
@@ -169,28 +174,28 @@ public:
for ( int i = 0; i <=4; i++ ){ for ( int i = 0; i <=4; i++ ){
int x0; int x0;
x0 = Compass_X0 + Delta_X + 2 * i * 5 * Compass_LineDelta; x0 = Compass_X0 + Delta_X + 2 * i * 5 * Compass_LineDelta;
epd->fillRect(x0-2, Compass_Y0 - 2 * Compass_LineLength, 5, 2 * Compass_LineLength, commonData->fgcolor); 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; x0 = Compass_X0 + Delta_X + ( 2 * i + 1 ) * 5 * Compass_LineDelta;
epd->fillRect(x0-1, Compass_Y0 - Compass_LineLength, 3, Compass_LineLength, commonData->fgcolor); getdisplay().fillRect(x0-1, Compass_Y0 - Compass_LineLength, 3, Compass_LineLength, commonData->fgcolor);
x0 = Compass_X0 + Delta_X - 2 * i * 5 * Compass_LineDelta; x0 = Compass_X0 + Delta_X - 2 * i * 5 * Compass_LineDelta;
epd->fillRect(x0-2, Compass_Y0 - 2 * Compass_LineLength, 5, 2 * Compass_LineLength, commonData->fgcolor); 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; x0 = Compass_X0 + Delta_X - ( 2 * i + 1 ) * 5 * Compass_LineDelta;
epd->fillRect(x0-1, Compass_Y0 - Compass_LineLength, 3, Compass_LineLength, commonData->fgcolor); getdisplay().fillRect(x0-1, Compass_Y0 - Compass_LineLength, 3, Compass_LineLength, commonData->fgcolor);
} }
epd->fillRect(0, Compass_Y0, 400, 3, commonData->fgcolor); getdisplay().fillRect(0, Compass_Y0, 400, 3, commonData->fgcolor);
// Add the numbers to the compass band // Add the numbers to the compass band
int x0; int x0;
float AngleToDisplay = NextSector * 180.0 / M_PI; float AngleToDisplay = NextSector * 180.0 / M_PI;
x0 = Compass_X0 + Delta_X; x0 = Compass_X0 + Delta_X;
epd->setFont(&DSEG7Classic_BoldItalic16pt7b); getdisplay().setFont(&DSEG7Classic_BoldItalic16pt7b);
do { do {
epd->setCursor(x0 - 40, Compass_Y0 + 40); getdisplay().setCursor(x0 - 40, Compass_Y0 + 40);
snprintf(buffer,bsize,"%03.0f", AngleToDisplay); snprintf(buffer,bsize,"%03.0f", AngleToDisplay);
epd->print(buffer); getdisplay().print(buffer);
AngleToDisplay += 20; AngleToDisplay += 20;
if ( AngleToDisplay >= 360.0 ) if ( AngleToDisplay >= 360.0 )
AngleToDisplay -= 360.0; AngleToDisplay -= 360.0;
@@ -203,7 +208,7 @@ public:
x0 = Compass_X0 + Delta_X + 4 * 5 * Compass_LineDelta; x0 = Compass_X0 + Delta_X + 4 * 5 * Compass_LineDelta;
do { do {
epd->setCursor(x0 - 40, Compass_Y0 + 40); getdisplay().setCursor(x0 - 40, Compass_Y0 + 40);
snprintf(buffer,bsize,"%03.0f", AngleToDisplay); snprintf(buffer,bsize,"%03.0f", AngleToDisplay);
// Quick and dirty way to prevent wrapping text in next line // Quick and dirty way to prevent wrapping text in next line
if ( ( x0 - 40 ) > 380 ) if ( ( x0 - 40 ) > 380 )
@@ -213,7 +218,7 @@ public:
else if ( ( x0 - 40 ) > 325 ) else if ( ( x0 - 40 ) > 325 )
buffer[2] = 0; buffer[2] = 0;
epd->print(buffer); getdisplay().print(buffer);
AngleToDisplay -= 20; AngleToDisplay -= 20;
if ( AngleToDisplay < 0 ) if ( AngleToDisplay < 0 )
@@ -225,8 +230,8 @@ public:
// x_test += 2; // x_test += 2;
// snprintf(buffer,bsize,"%03d", x_test); // snprintf(buffer,bsize,"%03d", x_test);
// epd->setCursor(x_test, Compass_Y0 - 60); // getdisplay().setCursor(x_test, Compass_Y0 - 60);
// epd->print(buffer); // getdisplay().print(buffer);
// if ( x_test > 390) // if ( x_test > 390)
// x_test = 320; // x_test = 320;

View File

@@ -1,4 +1,3 @@
// SPDX-License-Identifier: GPL-2.0-or-later
#if defined BOARD_OBP60S3 || defined BOARD_OBP40S3 #if defined BOARD_OBP60S3 || defined BOARD_OBP40S3
#include "Pagedata.h" #include "Pagedata.h"
@@ -6,19 +5,13 @@
class PageDST810 : public Page class PageDST810 : public Page
{ {
private:
String lengthformat;
public: public:
PageDST810(CommonData &common) : Page(common) PageDST810(CommonData &common){
{ commonData = &common;
logger->logDebug(GwLog::LOG, "Instantiate PageDST810"); common.logger->logDebug(GwLog::LOG,"Instantiate PageDST810");
// Get config data
lengthformat = config->getString(config->lengthFormat);
} }
int handleKey(int key) { virtual int handleKey(int key){
// Code for keylock // Code for keylock
if(key == 11){ if(key == 11){
commonData->keylock = !commonData->keylock; commonData->keylock = !commonData->keylock;
@@ -28,6 +21,8 @@ public:
} }
int displayPage(PageData &pageData){ int displayPage(PageData &pageData){
GwConfigHandler *config = commonData->config;
GwLog *logger = commonData->logger;
// Old values for hold function // Old values for hold function
static String svalue1old = ""; static String svalue1old = "";
@@ -39,14 +34,21 @@ public:
static String svalue4old = ""; static String svalue4old = "";
static String unit4old = ""; static String unit4old = "";
// 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 #1 // Get boat values #1
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
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 = commonData->fmt->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 = commonData->fmt->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)
@@ -54,8 +56,8 @@ public:
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 = commonData->fmt->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 = commonData->fmt->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)
@@ -63,8 +65,8 @@ public:
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 = commonData->fmt->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 = commonData->fmt->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)
@@ -72,8 +74,8 @@ public:
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 = commonData->fmt->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 = commonData->fmt->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"){
@@ -83,43 +85,43 @@ public:
// Logging boat values // Logging boat values
if (bvalue1 == NULL) return PAGE_OK; // WTF why this statement? if (bvalue1 == NULL) return PAGE_OK; // WTF why this statement?
logger->logDebug(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
//*********************************************************** //***********************************************************
// Set display in partial refresh mode // Set display in partial refresh mode
epd->setPartialWindow(0, 0, epd->width(), epd->height()); // Set partial update getdisplay().setPartialWindow(0, 0, getdisplay().width(), getdisplay().height()); // Set partial update
epd->setTextColor(commonData->fgcolor); getdisplay().setTextColor(commonData->fgcolor);
// ############### Value 1 ################ // ############### Value 1 ################
// Show name // Show name
epd->setFont(&Ubuntu_Bold20pt8b); getdisplay().setFont(&Ubuntu_Bold20pt8b);
epd->setCursor(20, 55); getdisplay().setCursor(20, 55);
epd->print("Depth"); // Page name getdisplay().print("Depth"); // Page name
// Show unit // Show unit
epd->setFont(&Ubuntu_Bold12pt8b); getdisplay().setFont(&Ubuntu_Bold12pt8b);
epd->setCursor(20, 90); getdisplay().setCursor(20, 90);
if(holdvalues == false){ if(holdvalues == false){
epd->print(unit1); // Unit getdisplay().print(unit1); // Unit
} }
else{ else{
epd->print(unit1old); getdisplay().print(unit1old);
} }
// Set font // Set font
epd->setFont(&DSEG7Classic_BoldItalic30pt7b); getdisplay().setFont(&DSEG7Classic_BoldItalic30pt7b);
epd->setCursor(180, 90); getdisplay().setCursor(180, 90);
// Show bus data // Show bus data
if(holdvalues == false){ if(holdvalues == false){
epd->print(svalue1); // Real value as formated string getdisplay().print(svalue1); // Real value as formated string
} }
else{ else{
epd->print(svalue1old); // Old value as formated string getdisplay().print(svalue1old); // Old value as formated string
} }
if(valid1 == true){ if(valid1 == true){
svalue1old = svalue1; // Save the old value svalue1old = svalue1; // Save the old value
@@ -129,35 +131,35 @@ public:
// ############### Horizontal Line ################ // ############### Horizontal Line ################
// Horizontal line 3 pix // Horizontal line 3 pix
epd->fillRect(0, 105, 400, 3, commonData->fgcolor); getdisplay().fillRect(0, 105, 400, 3, commonData->fgcolor);
// ############### Value 2 ################ // ############### Value 2 ################
// Show name // Show name
epd->setFont(&Ubuntu_Bold20pt8b); getdisplay().setFont(&Ubuntu_Bold20pt8b);
epd->setCursor(20, 145); getdisplay().setCursor(20, 145);
epd->print("Speed"); // Page name getdisplay().print("Speed"); // Page name
// Show unit // Show unit
epd->setFont(&Ubuntu_Bold12pt8b); getdisplay().setFont(&Ubuntu_Bold12pt8b);
epd->setCursor(20, 180); getdisplay().setCursor(20, 180);
if(holdvalues == false){ if(holdvalues == false){
epd->print(unit2); // Unit getdisplay().print(unit2); // Unit
} }
else{ else{
epd->print(unit2old); getdisplay().print(unit2old);
} }
// Setfont // Setfont
epd->setFont(&DSEG7Classic_BoldItalic30pt7b); getdisplay().setFont(&DSEG7Classic_BoldItalic30pt7b);
epd->setCursor(180, 180); getdisplay().setCursor(180, 180);
// Show bus data // Show bus data
if(holdvalues == false){ if(holdvalues == false){
epd->print(svalue2); // Real value as formated string getdisplay().print(svalue2); // Real value as formated string
} }
else{ else{
epd->print(svalue2old); // Old value as formated string getdisplay().print(svalue2old); // Old value as formated string
} }
if(valid2 == true){ if(valid2 == true){
svalue2old = svalue2; // Save the old value svalue2old = svalue2; // Save the old value
@@ -167,35 +169,35 @@ public:
// ############### Horizontal Line ################ // ############### Horizontal Line ################
// Horizontal line 3 pix // Horizontal line 3 pix
epd->fillRect(0, 195, 400, 3, commonData->fgcolor); getdisplay().fillRect(0, 195, 400, 3, commonData->fgcolor);
// ############### Value 3 ################ // ############### Value 3 ################
// Show name // Show name
epd->setFont(&Ubuntu_Bold12pt8b); getdisplay().setFont(&Ubuntu_Bold12pt8b);
epd->setCursor(20, 220); getdisplay().setCursor(20, 220);
epd->print("Log"); // Page name getdisplay().print("Log"); // Page name
// Show unit // Show unit
epd->setFont(&Ubuntu_Bold8pt8b); getdisplay().setFont(&Ubuntu_Bold8pt8b);
epd->setCursor(20, 240); getdisplay().setCursor(20, 240);
if(holdvalues == false){ if(holdvalues == false){
epd->print(unit3); // Unit getdisplay().print(unit3); // Unit
} }
else{ else{
epd->print(unit3old); getdisplay().print(unit3old);
} }
// Set font // Set font
epd->setFont(&DSEG7Classic_BoldItalic20pt7b); getdisplay().setFont(&DSEG7Classic_BoldItalic20pt7b);
epd->setCursor(80, 270); getdisplay().setCursor(80, 270);
// Show bus data // Show bus data
if(holdvalues == false){ if(holdvalues == false){
epd->print(svalue3); // Real value as formated string getdisplay().print(svalue3); // Real value as formated string
} }
else{ else{
epd->print(svalue3old); // Old value as formated string getdisplay().print(svalue3old); // Old value as formated string
} }
if(valid3 == true){ if(valid3 == true){
svalue3old = svalue3; // Save the old value svalue3old = svalue3; // Save the old value
@@ -205,35 +207,35 @@ public:
// ############### Vertical Line ################ // ############### Vertical Line ################
// Vertical line 3 pix // Vertical line 3 pix
epd->fillRect(200, 195, 3, 75, commonData->fgcolor); getdisplay().fillRect(200, 195, 3, 75, commonData->fgcolor);
// ############### Value 4 ################ // ############### Value 4 ################
// Show name // Show name
epd->setFont(&Ubuntu_Bold12pt8b); getdisplay().setFont(&Ubuntu_Bold12pt8b);
epd->setCursor(220, 220); getdisplay().setCursor(220, 220);
epd->print("Temp"); // Page name getdisplay().print("Temp"); // Page name
// Show unit // Show unit
epd->setFont(&Ubuntu_Bold8pt8b); getdisplay().setFont(&Ubuntu_Bold8pt8b);
epd->setCursor(220, 240); getdisplay().setCursor(220, 240);
if(holdvalues == false){ if(holdvalues == false){
epd->print(unit4); // Unit getdisplay().print(unit4); // Unit
} }
else{ else{
epd->print(unit4old); getdisplay().print(unit4old);
} }
// Set font // Set font
epd->setFont(&DSEG7Classic_BoldItalic20pt7b); getdisplay().setFont(&DSEG7Classic_BoldItalic20pt7b);
epd->setCursor(280, 270); getdisplay().setCursor(280, 270);
// Show bus data // Show bus data
if(holdvalues == false){ if(holdvalues == false){
epd->print(svalue4); // Real value as formated string getdisplay().print(svalue4); // Real value as formated string
} }
else{ else{
epd->print(svalue4old); // Old value as formated string getdisplay().print(svalue4old); // Old value as formated string
} }
if(valid4 == true){ if(valid4 == true){
svalue4old = svalue4; // Save the old value svalue4old = svalue4; // Save the old value

View File

@@ -1,13 +1,20 @@
// SPDX-License-Identifier: GPL-2.0-or-later
#if defined BOARD_OBP60S3 || defined BOARD_OBP40S3 #if defined BOARD_OBP60S3 || defined BOARD_OBP40S3
#include <PCF8574.h> // PCF8574 modules from Horter #include <PCF8574.h> // PCF8574 modules from Horter
#include "Pagedata.h" #include "Pagedata.h"
#include "OBP60Extensions.h" #include "OBP60Extensions.h"
#include "images/OBP_400x300.xbm" // OBP Logo
#ifdef BOARD_OBP60S3
#include "images/OBP60_400x300.xbm" // MFD with logo
#endif
#ifdef BOARD_OBP40S3
#include "images/OBP40_400x300.xbm" // MFD with logo
#endif
class PageDigitalOut : public Page class PageDigitalOut : public Page
{ {
private:
// Status values // Status values
bool button1 = false; bool button1 = false;
bool button2 = false; bool button2 = false;
@@ -15,25 +22,10 @@ private:
bool button4 = false; bool button4 = false;
bool button5 = false; bool button5 = false;
// Button labels
String name1;
String name2;
String name3;
String name4;
String name5;
public: public:
PageDigitalOut(CommonData &common) : Page(common) PageDigitalOut(CommonData &common){
{ commonData = &common;
logger->logDebug(GwLog::LOG, "Instantiate PageDigitalOut"); common.logger->logDebug(GwLog::LOG,"Instantiate PageDigitalOut");
// Get config data
String lengthformat = config->getString(config->lengthFormat);
name1 = config->getString(config->mod1Out1);
name2 = config->getString(config->mod1Out2);
name3 = config->getString(config->mod1Out3);
name4 = config->getString(config->mod1Out4);
name5 = config->getString(config->mod1Out5);
} }
// Set botton labels // Set botton labels
@@ -50,52 +42,62 @@ public:
// Code for keylock // Code for keylock
if(key == 11){ if(key == 11){
commonData->keylock = !commonData->keylock; commonData->keylock = !commonData->keylock;
return 0; return 0; // Commit the key
} }
// Code for button 1 // Code for button 1
if(key == 1){ if(key == 1){
button1 = !button1; button1 = !button1;
setPCF8574PortPinModul1(0, button1 ? 0 : 1); // Attention! Inverse logic for PCF8574 setPCF8574PortPinModul1(0, button1 ? 0 : 1); // Attention! Inverse logic for PCF8574
return 0; return 0; // Commit the key
} }
// Code for button 2 // Code for button 2
if(key == 2){ if(key == 2){
button2 = !button2; button2 = !button2;
setPCF8574PortPinModul1(1, button2 ? 0 : 1); // Attention! Inverse logic for PCF8574 setPCF8574PortPinModul1(1, button2 ? 0 : 1); // Attention! Inverse logic for PCF8574
return 0; return 0; // Commit the key
} }
// Code for button 3 // Code for button 3
if(key == 3){ if(key == 3){
button3 = !button3; button3 = !button3;
setPCF8574PortPinModul1(2, button3 ? 0 : 1); // Attention! Inverse logic for PCF8574 setPCF8574PortPinModul1(2, button3 ? 0 : 1); // Attention! Inverse logic for PCF8574
return 0; return 0; // Commit the key
} }
// Code for button 4 // Code for button 4
if(key == 4){ if(key == 4){
button4 = !button4; button4 = !button4;
setPCF8574PortPinModul1(3, button4 ? 0 : 1); // Attention! Inverse logic for PCF8574 setPCF8574PortPinModul1(3, button4 ? 0 : 1); // Attention! Inverse logic for PCF8574
return 0; return 0; // Commit the key
} }
// Code for button 5 // Code for button 5
if(key == 5){ if(key == 5){
button5 = !button5; button5 = !button5;
setPCF8574PortPinModul1(4, button5 ? 0 : 1); // Attention! Inverse logic for PCF8574 setPCF8574PortPinModul1(4, button5 ? 0 : 1); // Attention! Inverse logic for PCF8574
return 0; return 0; // Commit the key
} }
return key; return key;
} }
void displayNew(PageData &pageData) { int displayPage(PageData &pageData){
#ifdef BOARD_OBP60S3 GwConfigHandler *config = commonData->config;
// Clear optical warning GwLog *logger = commonData->logger;
if (flashLED == "Limit Violation") {
// 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);
String name1 = config->getString(config->mod1Out1);
String name2 = config->getString(config->mod1Out2);
String name3 = config->getString(config->mod1Out3);
String name4 = config->getString(config->mod1Out4);
String name5 = config->getString(config->mod1Out5);
// Optical warning by limit violation (unused)
if(String(flashLED) == "Limit Violation"){
setBlinkingLED(false); setBlinkingLED(false);
setFlashLED(false); setFlashLED(false);
} }
#endif
};
int displayPage(PageData &pageData){
// Logging boat values // Logging boat values
LOG_DEBUG(GwLog::LOG,"Drawing at PageDigitalOut"); LOG_DEBUG(GwLog::LOG,"Drawing at PageDigitalOut");
@@ -104,23 +106,20 @@ public:
//*********************************************************** //***********************************************************
// Set display in partial refresh mode // Set display in partial refresh mode
epd->setPartialWindow(0, 0, epd->width(), epd->height()); // Set partial update getdisplay().setPartialWindow(0, 0, getdisplay().width(), getdisplay().height()); // Set partial update
getdisplay().setTextColor(commonData->fgcolor);
epd->setTextColor(commonData->fgcolor); getdisplay().setFont(&Ubuntu_Bold12pt8b);
epd->setFont(&Ubuntu_Bold12pt8b); // Write text
getdisplay().setCursor(100, 50 + 8);
// Draw labels getdisplay().print(name1);
epd->setCursor(100, 50 + 8); getdisplay().setCursor(100, 100 + 8);
epd->print(name1); getdisplay().print(name2);
epd->setCursor(100, 100 + 8); getdisplay().setCursor(100, 150 + 8);
epd->print(name2); getdisplay().print(name3);
epd->setCursor(100, 150 + 8); getdisplay().setCursor(100,200 + 8);
epd->print(name3); getdisplay().print(name4);
epd->setCursor(100,200 + 8); getdisplay().setCursor(100, 250 + 8);
epd->print(name4); getdisplay().print(name5);
epd->setCursor(100, 250 + 8);
epd->print(name5);
// Draw bottons // Draw bottons
drawButtonCenter(50, 50, 40, 27, "1", commonData->fgcolor, commonData->bgcolor, button1); drawButtonCenter(50, 50, 40, 27, "1", commonData->fgcolor, commonData->bgcolor, button1);
drawButtonCenter(50, 100, 40, 27, "2", commonData->fgcolor, commonData->bgcolor, button2); drawButtonCenter(50, 100, 40, 27, "2", commonData->fgcolor, commonData->bgcolor, button2);

View File

@@ -1,145 +0,0 @@
// SPDX-License-Identifier: GPL-2.0-or-later
#if defined BOARD_OBP60S3 || defined BOARD_OBP40S3
#include "Pagedata.h"
#include "OBP60Extensions.h"
/*
Electric propulsion
- Current, voltage, power
- 12, 24, 48 etc. Voltage
- rpm
- throttle position
- controller state
- error codes
- temperature engine, controller, batteries
*/
class PageEPropulsion : public Page
{
private:
char mode = 'N'; // (N)ormal, (C)onfig
void displayModeNormal(PageData &pageData) {
// TBD Boatvalues: ...
logger->logDebug(GwLog::DEBUG, "Drawing at PageEPropulsion");
// Title and corner value headings
epd->setTextColor(commonData->fgcolor);
epd->setFont(&Ubuntu_Bold12pt8b);
epd->setCursor(8, 48);
epd->print("Electric propulsion");
}
void displayModeConfig() {
epd->setTextColor(commonData->fgcolor);
epd->setFont(&Ubuntu_Bold12pt8b);
epd->setCursor(8, 48);
epd->print("EPropulsion configuration");
epd->setFont(&Ubuntu_Bold8pt8b);
// TODO menu
}
public:
PageEPropulsion(CommonData &common) : Page(common)
{
logger->logDebug(GwLog::LOG,"Instantiate PageEPropulsion");
}
void setupKeys(){
Page::setupKeys();
}
#ifdef BOARD_OBP60S3
int handleKey(int key){
if (key == 1) { // Switch between normal and config mode
if (mode == 'N') {
mode = 'C';
} else {
mode = 'N';
}
return 0;
}
if (key == 11) { // Code for keylock
commonData->keylock = !commonData->keylock;
return 0;
}
return key;
}
#endif
#ifdef BOARD_OBP40S3
int handleKey(int key){
if (key == 1) { // Switch between normal and config mode
if (mode == 'N') {
mode = 'C';
commonData->keydata[1].label = "EDIT";
} else {
mode = 'N';
commonData->keydata[1].label = "ALARM";
}
return 0;
}
if (key == 11) { // Code for keylock
commonData->keylock = !commonData->keylock;
return 0;
}
return key;
}
#endif
void displayNew(PageData &pageData){
#ifdef BOARD_OBP60S3
// Clear optical warning
if (flashLED == "Limit Violation") {
setBlinkingLED(false);
setFlashLED(false);
}
#endif
};
int displayPage(PageData &pageData){
// Logging boat values
logger->logDebug(GwLog::LOG,"Drawing at PageEPropulsion; Mode=%c", mode);
// Set display in partial refresh mode
epd->setPartialWindow(0, 0, epd->width(), epd->height());
if (mode == 'N') {
displayModeNormal(pageData);
} else if (mode == 'C') {
displayModeConfig();
}
return PAGE_UPDATE;
};
};
static Page *createPage(CommonData &common){
return new PageEPropulsion(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 registerPageEPropulsion(
"EPropulsion", // Page name
createPage, // Action
0, // Number of bus values depends on selection in Web configuration
{}, // Names of bus values undepends on selection in Web configuration (refer GwBoatData.h)
true // Show display header on/off
);
#endif

View File

@@ -1,4 +1,3 @@
// SPDX-License-Identifier: GPL-2.0-or-later
#if defined BOARD_OBP60S3 || defined BOARD_OBP40S3 #if defined BOARD_OBP60S3 || defined BOARD_OBP40S3
#include "Pagedata.h" #include "Pagedata.h"
@@ -67,22 +66,25 @@ static unsigned char fish_bits[] = {
class PageFluid : public Page class PageFluid : public Page
{ {
private: bool simulation = false;
double simgoto; double simgoto;
double simval; double simval;
double simstep; double simstep;
bool holdvalues = false;
int fluidtype; int fluidtype;
public: public:
PageFluid(CommonData &common) : Page(common) PageFluid(CommonData &common){
{ commonData = &common;
logger->logDebug(GwLog::LOG, "Instantiate PageFluid"); 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)); simval = double(random(0, 100));
simgoto = double(random(0, 100)); simgoto = double(random(0, 100));
simstep = (simgoto - simval) / 20.0; simstep = (simgoto - simval) / 20.0;
} }
int handleKey(int key) { virtual int handleKey(int key){
// Code for keylock // Code for keylock
if(key == 11){ if(key == 11){
commonData->keylock = !commonData->keylock; commonData->keylock = !commonData->keylock;
@@ -91,23 +93,28 @@ public:
return key; return key;
} }
void displayNew(PageData &pageData) { virtual void displayNew(PageData &pageData){
fluidtype = config->getInt("page" + String(pageData.pageNumber) + "fluid", 0); fluidtype = commonData->config->getInt("page" + String(pageData.pageNumber) + "fluid", 0);
logger->logDebug(GwLog::LOG, "New PageFluid: fluidtype=%d", fluidtype); commonData->logger->logDebug(GwLog::LOG,"New PageFluid: fluidtype=%d", fluidtype);
#ifdef BOARD_OBP60S3
// Clear optical warning
if (flashLED == "Limit Violation") {
setBlinkingLED(false);
setFlashLED(false);
}
#endif
} }
int displayPage(PageData &pageData){ int displayPage(PageData &pageData){
GwConfigHandler *config = commonData->config;
GwLog *logger = commonData->logger;
// Old values for hold function // Old values for hold function
static double value1old; static double value1old;
// Get config data
String flashLED = config->getString(config->flashLED);
String backlightMode = config->getString(config->backlight);
// Optical warning by limit violation (unused)
if(String(flashLED) == "Limit Violation"){
setBlinkingLED(false);
setFlashLED(false);
}
GwApi::BoatValue *bvalue1 = pageData.values[0]; GwApi::BoatValue *bvalue1 = pageData.values[0];
String name1 = bvalue1->getName(); String name1 = bvalue1->getName();
double fluidlevel = bvalue1->value; double fluidlevel = bvalue1->value;
@@ -125,23 +132,23 @@ public:
} }
// Logging boat values // Logging boat values
logger->logDebug(GwLog::LOG, "Drawing at PageFluid: value=%f", bvalue1->value); LOG_DEBUG(GwLog::LOG,"Drawing at PageFluid: value=%f", bvalue1->value);
// Draw page // Draw page
//*********************************************************** //***********************************************************
// Set display in partial refresh mode // Set display in partial refresh mode
epd->setPartialWindow(0, 0, epd->width(), epd->height()); getdisplay().setPartialWindow(0, 0, getdisplay().width(), getdisplay().height());
epd->setTextColor(commonData->fgcolor); getdisplay().setTextColor(commonData->fgcolor);
// descriptions // descriptions
epd->setFont(&Ubuntu_Bold12pt8b); getdisplay().setFont(&Ubuntu_Bold12pt8b);
epd->setCursor(20, 60); getdisplay().setCursor(20, 60);
epd->print("Fluid"); getdisplay().print("Fluid");
epd->setCursor(300, 60); getdisplay().setCursor(300, 60);
epd->print(xdrDelete(name1).substring(0, 6)); getdisplay().print(xdrDelete(name1).substring(0, 6));
// analog instrument // analog instrument
// scale from -120 to 120 // scale from -120 to 120
@@ -151,11 +158,11 @@ public:
uint8_t r = 110; uint8_t r = 110;
// circular frame // circular frame
epd->drawCircle(c.x, c.y, r+5, commonData->fgcolor); getdisplay().drawCircle(c.x, c.y, r+5, commonData->fgcolor);
epd->fillCircle(c.x, c.y, r+2, commonData->fgcolor); getdisplay().fillCircle(c.x, c.y, r+2, commonData->fgcolor);
epd->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
epd->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];
@@ -169,32 +176,32 @@ public:
// draw symbol (as bitmap) // draw symbol (as bitmap)
switch (fluidtype) { switch (fluidtype) {
case 0: case 0:
epd->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:
epd->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; break;
case 2: // gray water no symbol yet case 2: // gray water no symbol yet
// epd->drawXBitmap(c.x-8, c.y-50, gray_bits, gray_width, gray_height, commonData->fgcolor); // getdisplay().drawXBitmap(c.x-8, c.y-50, gray_bits, gray_width, gray_height, commonData->fgcolor);
break; break;
case 3: case 3:
epd->drawXBitmap(c.x-8, c.y-50, fish_bits, fish_width, fish_height, commonData->fgcolor); getdisplay().drawXBitmap(c.x-8, c.y-50, fish_bits, fish_width, fish_height, commonData->fgcolor);
break; break;
case 4: case 4:
epd->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:
epd->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:
epd->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
epd->setFont(&Ubuntu_Bold8pt8b); getdisplay().setFont(&Ubuntu_Bold8pt8b);
p = {c.x, c.y - r + 30}; p = {c.x, c.y - r + 30};
drawTextCenter(p.x, p.y, "1/2"); drawTextCenter(p.x, p.y, "1/2");
pr = rotatePoint(c, p, -60); pr = rotatePoint(c, p, -60);
@@ -203,7 +210,7 @@ public:
drawTextCenter(pr.x, pr.y, "3/4"); drawTextCenter(pr.x, pr.y, "3/4");
// empty and full // empty and full
epd->setFont(&Ubuntu_Bold12pt8b); getdisplay().setFont(&Ubuntu_Bold12pt8b);
p = rotatePoint(c, {c.x, c.y - r + 30}, -130); p = rotatePoint(c, {c.x, c.y - r + 30}, -130);
drawTextCenter(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);
@@ -229,7 +236,7 @@ public:
continue; continue;
} }
p = rotatePoint(c, {c.x, c.y - r + 10}, angle); p = rotatePoint(c, {c.x, c.y - r + 10}, angle);
epd->fillCircle(p.x, p.y, 3, commonData->fgcolor); getdisplay().fillCircle(p.x, p.y, 3, commonData->fgcolor);
} }
// pointer // pointer
@@ -242,7 +249,7 @@ public:
}; };
fillPoly4(rotatePoints(c, pts, -120 + fluidlevel * 2.4), commonData->fgcolor); fillPoly4(rotatePoints(c, pts, -120 + fluidlevel * 2.4), commonData->fgcolor);
// Pointer axis is white // Pointer axis is white
epd->fillCircle(c.x, c.y, 6, commonData->bgcolor); getdisplay().fillCircle(c.x, c.y, 6, commonData->bgcolor);
} }
return PAGE_UPDATE; return PAGE_UPDATE;

View File

@@ -1,28 +1,17 @@
// SPDX-License-Identifier: GPL-2.0-or-later
#if defined BOARD_OBP60S3 || defined BOARD_OBP40S3 #if defined BOARD_OBP60S3 || defined BOARD_OBP40S3
#include "Pagedata.h" #include "Pagedata.h"
#include "OBP60Extensions.h" #include "OBP60Extensions.h"
#ifdef ENABLE_CALIBRATION
#include "BoatDataCalibration.h"
#endif
class PageFourValues : public Page class PageFourValues : public Page
{ {
private:
String lengthformat;
public: public:
PageFourValues(CommonData &common) : Page(common) PageFourValues(CommonData &common){
{ commonData = &common;
logger->logDebug(GwLog::LOG, "Instantiate PageFourValues"); common.logger->logDebug(GwLog::LOG,"Instantiate PageFourValues");
// Get config data
lengthformat = config->getString(config->lengthFormat);
} }
int handleKey(int key){ virtual int handleKey(int key){
// Code for keylock // Code for keylock
if(key == 11){ if(key == 11){
commonData->keylock = !commonData->keylock; commonData->keylock = !commonData->keylock;
@@ -31,17 +20,9 @@ public:
return key; return key;
} }
void displayNew(PageData &pageData) {
#ifdef BOARD_OBP60S3
// Clear optical warning
if (flashLED == "Limit Violation") {
setBlinkingLED(false);
setFlashLED(false);
}
#endif
};
int displayPage(PageData &pageData){ int displayPage(PageData &pageData){
GwConfigHandler *config = commonData->config;
GwLog *logger = commonData->logger;
// Old values for hold function // Old values for hold function
static String svalue1old = ""; static String svalue1old = "";
@@ -53,103 +34,104 @@ public:
static String svalue4old = ""; static String svalue4old = "";
static String unit4old = ""; static String unit4old = "";
// 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 #1 // Get boat values #1
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
#ifdef ENABLE_CALIBRATION
calibrationData.calibrateInstance(bvalue1, logger); // Check if boat data value is to be calibrated
#endif
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 = commonData->fmt->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 = commonData->fmt->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 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
#ifdef ENABLE_CALIBRATION
calibrationData.calibrateInstance(bvalue2, logger); // Check if boat data value is to be calibrated
#endif
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 = commonData->fmt->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 = commonData->fmt->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]; // Third element in list 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
#ifdef ENABLE_CALIBRATION
calibrationData.calibrateInstance(bvalue3, logger); // Check if boat data value is to be calibrated
#endif
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 = commonData->fmt->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 = commonData->fmt->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]; // Fourth element in list 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
#ifdef ENABLE_CALIBRATION
calibrationData.calibrateInstance(bvalue4, logger); // Check if boat data value is to be calibrated
#endif
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 = commonData->fmt->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 = commonData->fmt->formatValue(bvalue4, *commonData).unit; // Unit of value String unit4 = formatValue(bvalue4, *commonData).unit; // Unit of value
// Optical warning by limit violation (unused)
if(String(flashLED) == "Limit Violation"){
setBlinkingLED(false);
setFlashLED(false);
}
// Logging boat values // Logging boat values
if (bvalue1 == NULL) return PAGE_OK; // WTF why this statement? if (bvalue1 == NULL) return PAGE_OK; // WTF why this statement?
logger->logDebug(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
//*********************************************************** //***********************************************************
// Set display in partial refresh mode // Set display in partial refresh mode
epd->setPartialWindow(0, 0, epd->width(), epd->height()); // Set partial update getdisplay().setPartialWindow(0, 0, getdisplay().width(), getdisplay().height()); // Set partial update
epd->setTextColor(commonData->fgcolor); getdisplay().setTextColor(commonData->fgcolor);
// ############### Value 1 ################ // ############### Value 1 ################
// Show name // Show name
epd->setFont(&Ubuntu_Bold16pt8b); getdisplay().setFont(&Ubuntu_Bold16pt8b);
epd->setCursor(20, 45); getdisplay().setCursor(20, 45);
epd->print(name1); // Page name getdisplay().print(name1); // Page name
// Show unit // Show unit
epd->setFont(&Ubuntu_Bold8pt8b); getdisplay().setFont(&Ubuntu_Bold8pt8b);
epd->setCursor(20, 65); getdisplay().setCursor(20, 65);
if(holdvalues == false){ if(holdvalues == false){
epd->print(unit1); // Unit getdisplay().print(unit1); // Unit
} }
else{ else{
epd->print(unit1old); getdisplay().print(unit1old);
} }
// 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"){
epd->setFont(&Ubuntu_Bold12pt8b); getdisplay().setFont(&Ubuntu_Bold12pt8b);
epd->setCursor(120, 55); getdisplay().setCursor(120, 55);
} }
else if(bvalue1->getFormat() == "formatTime" || bvalue1->getFormat() == "formatDate"){ else if(bvalue1->getFormat() == "formatTime" || bvalue1->getFormat() == "formatDate"){
epd->setFont(&Ubuntu_Bold12pt8b); getdisplay().setFont(&Ubuntu_Bold12pt8b);
epd->setCursor(150, 58); getdisplay().setCursor(150, 58);
} }
else{ else{
epd->setFont(&DSEG7Classic_BoldItalic20pt7b); getdisplay().setFont(&DSEG7Classic_BoldItalic20pt7b);
epd->setCursor(180, 65); getdisplay().setCursor(180, 65);
} }
// Show bus data // Show bus data
if(holdvalues == false){ if(holdvalues == false){
epd->print(svalue1); // Real value as formated string getdisplay().print(svalue1); // Real value as formated string
} }
else{ else{
epd->print(svalue1old); // Old value as formated string getdisplay().print(svalue1old); // Old value as formated string
} }
if(valid1 == true){ if(valid1 == true){
svalue1old = svalue1; // Save the old value svalue1old = svalue1; // Save the old value
@@ -159,45 +141,45 @@ public:
// ############### Horizontal Line ################ // ############### Horizontal Line ################
// Horizontal line 3 pix // Horizontal line 3 pix
epd->fillRect(0, 80, 400, 3, commonData->fgcolor); getdisplay().fillRect(0, 80, 400, 3, commonData->fgcolor);
// ############### Value 2 ################ // ############### Value 2 ################
// Show name // Show name
epd->setFont(&Ubuntu_Bold16pt8b); getdisplay().setFont(&Ubuntu_Bold16pt8b);
epd->setCursor(20, 113); getdisplay().setCursor(20, 113);
epd->print(name2); // Page name getdisplay().print(name2); // Page name
// Show unit // Show unit
epd->setFont(&Ubuntu_Bold8pt8b); getdisplay().setFont(&Ubuntu_Bold8pt8b);
epd->setCursor(20, 133); getdisplay().setCursor(20, 133);
if(holdvalues == false){ if(holdvalues == false){
epd->print(unit2); // Unit getdisplay().print(unit2); // Unit
} }
else{ else{
epd->print(unit2old); getdisplay().print(unit2old);
} }
// 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"){
epd->setFont(&Ubuntu_Bold12pt8b); getdisplay().setFont(&Ubuntu_Bold12pt8b);
epd->setCursor(120, 123); getdisplay().setCursor(120, 123);
} }
else if(bvalue2->getFormat() == "formatTime" || bvalue2->getFormat() == "formatDate"){ else if(bvalue2->getFormat() == "formatTime" || bvalue2->getFormat() == "formatDate"){
epd->setFont(&Ubuntu_Bold12pt8b); getdisplay().setFont(&Ubuntu_Bold12pt8b);
epd->setCursor(150, 123); getdisplay().setCursor(150, 123);
} }
else{ else{
epd->setFont(&DSEG7Classic_BoldItalic20pt7b); getdisplay().setFont(&DSEG7Classic_BoldItalic20pt7b);
epd->setCursor(180, 133); getdisplay().setCursor(180, 133);
} }
// Show bus data // Show bus data
if(holdvalues == false){ if(holdvalues == false){
epd->print(svalue2); // Real value as formated string getdisplay().print(svalue2); // Real value as formated string
} }
else{ else{
epd->print(svalue2old); // Old value as formated string getdisplay().print(svalue2old); // Old value as formated string
} }
if(valid2 == true){ if(valid2 == true){
svalue2old = svalue2; // Save the old value svalue2old = svalue2; // Save the old value
@@ -207,45 +189,45 @@ public:
// ############### Horizontal Line ################ // ############### Horizontal Line ################
// Horizontal line 3 pix // Horizontal line 3 pix
epd->fillRect(0, 146, 400, 3, commonData->fgcolor); getdisplay().fillRect(0, 146, 400, 3, commonData->fgcolor);
// ############### Value 3 ################ // ############### Value 3 ################
// Show name // Show name
epd->setFont(&Ubuntu_Bold16pt8b); getdisplay().setFont(&Ubuntu_Bold16pt8b);
epd->setCursor(20, 181); getdisplay().setCursor(20, 181);
epd->print(name3); // Page name getdisplay().print(name3); // Page name
// Show unit // Show unit
epd->setFont(&Ubuntu_Bold8pt8b); getdisplay().setFont(&Ubuntu_Bold8pt8b);
epd->setCursor(20, 201); getdisplay().setCursor(20, 201);
if(holdvalues == false){ if(holdvalues == false){
epd->print(unit3); // Unit getdisplay().print(unit3); // Unit
} }
else{ else{
epd->print(unit3old); getdisplay().print(unit3old);
} }
// 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"){
epd->setFont(&Ubuntu_Bold12pt8b); getdisplay().setFont(&Ubuntu_Bold12pt8b);
epd->setCursor(120, 191); getdisplay().setCursor(120, 191);
} }
else if(bvalue3->getFormat() == "formatTime" || bvalue3->getFormat() == "formatDate"){ else if(bvalue3->getFormat() == "formatTime" || bvalue3->getFormat() == "formatDate"){
epd->setFont(&Ubuntu_Bold12pt8b); getdisplay().setFont(&Ubuntu_Bold12pt8b);
epd->setCursor(150, 191); getdisplay().setCursor(150, 191);
} }
else{ else{
epd->setFont(&DSEG7Classic_BoldItalic20pt7b); getdisplay().setFont(&DSEG7Classic_BoldItalic20pt7b);
epd->setCursor(180, 201); getdisplay().setCursor(180, 201);
} }
// Show bus data // Show bus data
if(holdvalues == false){ if(holdvalues == false){
epd->print(svalue3); // Real value as formated string getdisplay().print(svalue3); // Real value as formated string
} }
else{ else{
epd->print(svalue3old); // Old value as formated string getdisplay().print(svalue3old); // Old value as formated string
} }
if(valid3 == true){ if(valid3 == true){
svalue3old = svalue3; // Save the old value svalue3old = svalue3; // Save the old value
@@ -255,45 +237,45 @@ public:
// ############### Horizontal Line ################ // ############### Horizontal Line ################
// Horizontal line 3 pix // Horizontal line 3 pix
epd->fillRect(0, 214, 400, 3, commonData->fgcolor); getdisplay().fillRect(0, 214, 400, 3, commonData->fgcolor);
// ############### Value 4 ################ // ############### Value 4 ################
// Show name // Show name
epd->setFont(&Ubuntu_Bold16pt8b); getdisplay().setFont(&Ubuntu_Bold16pt8b);
epd->setCursor(20, 249); getdisplay().setCursor(20, 249);
epd->print(name4); // Page name getdisplay().print(name4); // Page name
// Show unit // Show unit
epd->setFont(&Ubuntu_Bold8pt8b); getdisplay().setFont(&Ubuntu_Bold8pt8b);
epd->setCursor(20, 269); getdisplay().setCursor(20, 269);
if(holdvalues == false){ if(holdvalues == false){
epd->print(unit4); // Unit getdisplay().print(unit4); // Unit
} }
else{ else{
epd->print(unit4old); getdisplay().print(unit4old);
} }
// 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"){
epd->setFont(&Ubuntu_Bold12pt8b); getdisplay().setFont(&Ubuntu_Bold12pt8b);
epd->setCursor(120, 259); getdisplay().setCursor(120, 259);
} }
else if(bvalue4->getFormat() == "formatTime" || bvalue4->getFormat() == "formatDate"){ else if(bvalue4->getFormat() == "formatTime" || bvalue4->getFormat() == "formatDate"){
epd->setFont(&Ubuntu_Bold12pt8b); getdisplay().setFont(&Ubuntu_Bold12pt8b);
epd->setCursor(150, 259); getdisplay().setCursor(150, 259);
} }
else{ else{
epd->setFont(&DSEG7Classic_BoldItalic20pt7b); getdisplay().setFont(&DSEG7Classic_BoldItalic20pt7b);
epd->setCursor(180, 269); getdisplay().setCursor(180, 269);
} }
// Show bus data // Show bus data
if(holdvalues == false){ if(holdvalues == false){
epd->print(svalue4); // Real value as formated string getdisplay().print(svalue4); // Real value as formated string
} }
else{ else{
epd->print(svalue4old); // Old value as formated string getdisplay().print(svalue4old); // Old value as formated string
} }
if(valid4 == true){ if(valid4 == true){
svalue4old = svalue4; // Save the old value svalue4old = svalue4; // Save the old value
@@ -302,11 +284,6 @@ public:
return PAGE_UPDATE; return PAGE_UPDATE;
}; };
void leavePage(PageData &pageData) {
logger->logDebug(GwLog::LOG, "Leaving PageFourvalues");
}
}; };
static Page *createPage(CommonData &common){ static Page *createPage(CommonData &common){

View File

@@ -1,4 +1,3 @@
// SPDX-License-Identifier: GPL-2.0-or-later
#if defined BOARD_OBP60S3 || defined BOARD_OBP40S3 #if defined BOARD_OBP60S3 || defined BOARD_OBP40S3
#include "Pagedata.h" #include "Pagedata.h"
@@ -6,19 +5,13 @@
class PageFourValues2 : public Page class PageFourValues2 : public Page
{ {
private:
String lengthformat;
public: public:
PageFourValues2(CommonData &common) : Page(common) PageFourValues2(CommonData &common){
{ commonData = &common;
logger->logDebug(GwLog::LOG, "Instantiate PageFourValues2"); common.logger->logDebug(GwLog::LOG,"Instantiate PageFourValues2");
// Get config data
lengthformat = config->getString(config->lengthFormat);
} }
int handleKey(int key) { virtual int handleKey(int key){
// Code for keylock // Code for keylock
if(key == 11){ if(key == 11){
commonData->keylock = !commonData->keylock; // Toggle keylock commonData->keylock = !commonData->keylock; // Toggle keylock
@@ -27,17 +20,9 @@ public:
return key; return key;
} }
void displayNew(PageData &pageData) {
#ifdef BOARD_OBP60S3
// Clear optical warning
if (flashLED == "Limit Violation") {
setBlinkingLED(false);
setFlashLED(false);
}
#endif
};
int displayPage(PageData &pageData){ int displayPage(PageData &pageData){
GwConfigHandler *config = commonData->config;
GwLog *logger = commonData->logger;
// Old values for hold function // Old values for hold function
static String svalue1old = ""; static String svalue1old = "";
@@ -49,14 +34,21 @@ public:
static String svalue4old = ""; static String svalue4old = "";
static String unit4old = ""; static String unit4old = "";
// 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 #1 // Get boat values #1
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
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 = commonData->fmt->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 = commonData->fmt->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)
@@ -64,8 +56,8 @@ public:
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 = commonData->fmt->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 = commonData->fmt->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)
@@ -73,8 +65,8 @@ public:
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 = commonData->fmt->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 = commonData->fmt->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)
@@ -82,58 +74,64 @@ public:
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 = commonData->fmt->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 = commonData->fmt->formatValue(bvalue4, *commonData).unit; // Unit of value String unit4 = formatValue(bvalue4, *commonData).unit; // Unit of value
// Optical warning by limit violation (unused)
if(String(flashLED) == "Limit Violation"){
setBlinkingLED(false);
setFlashLED(false);
}
// Logging boat values // Logging boat values
if (bvalue1 == NULL) return PAGE_OK; // WTF why this statement? if (bvalue1 == NULL) return PAGE_OK; // WTF why this statement?
logger->logDebug(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
//*********************************************************** //***********************************************************
// Set display in partial refresh mode // Set display in partial refresh mode
epd->setPartialWindow(0, 0, epd->width(), epd->height()); // Set partial update getdisplay().setPartialWindow(0, 0, getdisplay().width(), getdisplay().height()); // Set partial update
epd->setTextColor(commonData->fgcolor); getdisplay().setTextColor(commonData->fgcolor);
// ############### Value 1 ################ // ############### Value 1 ################
// Show name // Show name
epd->setFont(&Ubuntu_Bold20pt8b); getdisplay().setFont(&Ubuntu_Bold20pt8b);
epd->setCursor(20, 55); getdisplay().setCursor(20, 55);
epd->print(name1); // Page name getdisplay().print(name1); // Page name
// Show unit // Show unit
epd->setFont(&Ubuntu_Bold12pt8b); getdisplay().setFont(&Ubuntu_Bold12pt8b);
epd->setCursor(20, 90); getdisplay().setCursor(20, 90);
if(holdvalues == false){ if(holdvalues == false){
epd->print(unit1); // Unit getdisplay().print(unit1); // Unit
} }
else{ else{
epd->print(unit1old); getdisplay().print(unit1old);
} }
// 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"){
epd->setFont(&Ubuntu_Bold12pt8b); getdisplay().setFont(&Ubuntu_Bold12pt8b);
epd->setCursor(100, 90); getdisplay().setCursor(100, 90);
} }
else if(bvalue1->getFormat() == "formatTime" || bvalue1->getFormat() == "formatDate"){ else if(bvalue1->getFormat() == "formatTime" || bvalue1->getFormat() == "formatDate"){
epd->setFont(&Ubuntu_Bold12pt8b); getdisplay().setFont(&Ubuntu_Bold12pt8b);
epd->setCursor(180, 77); getdisplay().setCursor(180, 77);
} }
else{ else{
epd->setFont(&DSEG7Classic_BoldItalic30pt7b); getdisplay().setFont(&DSEG7Classic_BoldItalic30pt7b);
epd->setCursor(180, 90); getdisplay().setCursor(180, 90);
} }
// Show bus data // Show bus data
if(holdvalues == false){ if(holdvalues == false){
epd->print(svalue1); // Real value as formated string getdisplay().print(svalue1); // Real value as formated string
} }
else{ else{
epd->print(svalue1old); // Old value as formated string getdisplay().print(svalue1old); // Old value as formated string
} }
if(valid1 == true){ if(valid1 == true){
svalue1old = svalue1; // Save the old value svalue1old = svalue1; // Save the old value
@@ -143,45 +141,45 @@ public:
// ############### Horizontal Line ################ // ############### Horizontal Line ################
// Horizontal line 3 pix // Horizontal line 3 pix
epd->fillRect(0, 105, 400, 3, commonData->fgcolor); getdisplay().fillRect(0, 105, 400, 3, commonData->fgcolor);
// ############### Value 2 ################ // ############### Value 2 ################
// Show name // Show name
epd->setFont(&Ubuntu_Bold20pt8b); getdisplay().setFont(&Ubuntu_Bold20pt8b);
epd->setCursor(20, 145); getdisplay().setCursor(20, 145);
epd->print(name2); // Page name getdisplay().print(name2); // Page name
// Show unit // Show unit
epd->setFont(&Ubuntu_Bold12pt8b); getdisplay().setFont(&Ubuntu_Bold12pt8b);
epd->setCursor(20, 180); getdisplay().setCursor(20, 180);
if(holdvalues == false){ if(holdvalues == false){
epd->print(unit2); // Unit getdisplay().print(unit2); // Unit
} }
else{ else{
epd->print(unit2old); getdisplay().print(unit2old);
} }
// 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"){
epd->setFont(&Ubuntu_Bold12pt8b); getdisplay().setFont(&Ubuntu_Bold12pt8b);
epd->setCursor(100, 180); getdisplay().setCursor(100, 180);
} }
else if(bvalue2->getFormat() == "formatTime" || bvalue2->getFormat() == "formatDate"){ else if(bvalue2->getFormat() == "formatTime" || bvalue2->getFormat() == "formatDate"){
epd->setFont(&Ubuntu_Bold12pt8b); getdisplay().setFont(&Ubuntu_Bold12pt8b);
epd->setCursor(180, 158); getdisplay().setCursor(180, 158);
} }
else{ else{
epd->setFont(&DSEG7Classic_BoldItalic30pt7b); getdisplay().setFont(&DSEG7Classic_BoldItalic30pt7b);
epd->setCursor(180, 180); getdisplay().setCursor(180, 180);
} }
// Show bus data // Show bus data
if(holdvalues == false){ if(holdvalues == false){
epd->print(svalue2); // Real value as formated string getdisplay().print(svalue2); // Real value as formated string
} }
else{ else{
epd->print(svalue2old); // Old value as formated string getdisplay().print(svalue2old); // Old value as formated string
} }
if(valid2 == true){ if(valid2 == true){
svalue2old = svalue2; // Save the old value svalue2old = svalue2; // Save the old value
@@ -191,45 +189,45 @@ public:
// ############### Horizontal Line ################ // ############### Horizontal Line ################
// Horizontal line 3 pix // Horizontal line 3 pix
epd->fillRect(0, 195, 400, 3, commonData->fgcolor); getdisplay().fillRect(0, 195, 400, 3, commonData->fgcolor);
// ############### Value 3 ################ // ############### Value 3 ################
// Show name // Show name
epd->setFont(&Ubuntu_Bold12pt8b); getdisplay().setFont(&Ubuntu_Bold12pt8b);
epd->setCursor(20, 220); getdisplay().setCursor(20, 220);
epd->print(name3); // Page name getdisplay().print(name3); // Page name
// Show unit // Show unit
epd->setFont(&Ubuntu_Bold8pt8b); getdisplay().setFont(&Ubuntu_Bold8pt8b);
epd->setCursor(20, 240); getdisplay().setCursor(20, 240);
if(holdvalues == false){ if(holdvalues == false){
epd->print(unit3); // Unit getdisplay().print(unit3); // Unit
} }
else{ else{
epd->print(unit3old); getdisplay().print(unit3old);
} }
// 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"){
epd->setFont(&Ubuntu_Bold8pt8b); getdisplay().setFont(&Ubuntu_Bold8pt8b);
epd->setCursor(50, 240); getdisplay().setCursor(50, 240);
} }
else if(bvalue3->getFormat() == "formatTime" || bvalue3->getFormat() == "formatDate"){ else if(bvalue3->getFormat() == "formatTime" || bvalue3->getFormat() == "formatDate"){
epd->setFont(&Ubuntu_Bold8pt8b); getdisplay().setFont(&Ubuntu_Bold8pt8b);
epd->setCursor(100, 240); getdisplay().setCursor(100, 240);
} }
else{ else{
epd->setFont(&DSEG7Classic_BoldItalic20pt7b); getdisplay().setFont(&DSEG7Classic_BoldItalic20pt7b);
epd->setCursor(80, 270); getdisplay().setCursor(80, 270);
} }
// Show bus data // Show bus data
if(holdvalues == false){ if(holdvalues == false){
epd->print(svalue3); // Real value as formated string getdisplay().print(svalue3); // Real value as formated string
} }
else{ else{
epd->print(svalue3old); // Old value as formated string getdisplay().print(svalue3old); // Old value as formated string
} }
if(valid3 == true){ if(valid3 == true){
svalue3old = svalue3; // Save the old value svalue3old = svalue3; // Save the old value
@@ -239,45 +237,45 @@ public:
// ############### Vertical Line ################ // ############### Vertical Line ################
// Vertical line 3 pix // Vertical line 3 pix
epd->fillRect(200, 195, 3, 75, commonData->fgcolor); getdisplay().fillRect(200, 195, 3, 75, commonData->fgcolor);
// ############### Value 4 ################ // ############### Value 4 ################
// Show name // Show name
epd->setFont(&Ubuntu_Bold12pt8b); getdisplay().setFont(&Ubuntu_Bold12pt8b);
epd->setCursor(220, 220); getdisplay().setCursor(220, 220);
epd->print(name4); // Page name getdisplay().print(name4); // Page name
// Show unit // Show unit
epd->setFont(&Ubuntu_Bold8pt8b); getdisplay().setFont(&Ubuntu_Bold8pt8b);
epd->setCursor(220, 240); getdisplay().setCursor(220, 240);
if(holdvalues == false){ if(holdvalues == false){
epd->print(unit4); // Unit getdisplay().print(unit4); // Unit
} }
else{ else{
epd->print(unit4old); getdisplay().print(unit4old);
} }
// 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"){
epd->setFont(&Ubuntu_Bold8pt8b); getdisplay().setFont(&Ubuntu_Bold8pt8b);
epd->setCursor(250, 240); getdisplay().setCursor(250, 240);
} }
else if(bvalue4->getFormat() == "formatTime" || bvalue4->getFormat() == "formatDate"){ else if(bvalue4->getFormat() == "formatTime" || bvalue4->getFormat() == "formatDate"){
epd->setFont(&Ubuntu_Bold8pt8b); getdisplay().setFont(&Ubuntu_Bold8pt8b);
epd->setCursor(300, 240); getdisplay().setCursor(300, 240);
} }
else{ else{
epd->setFont(&DSEG7Classic_BoldItalic20pt7b); getdisplay().setFont(&DSEG7Classic_BoldItalic20pt7b);
epd->setCursor(280, 270); getdisplay().setCursor(280, 270);
} }
// Show bus data // Show bus data
if(holdvalues == false){ if(holdvalues == false){
epd->print(svalue4); // Real value as formated string getdisplay().print(svalue4); // Real value as formated string
} }
else{ else{
epd->print(svalue4old); // Old value as formated string getdisplay().print(svalue4old); // Old value as formated string
} }
if(valid4 == true){ if(valid4 == true){
svalue4old = svalue4; // Save the old value svalue4old = svalue4; // Save the old value

View File

@@ -1,4 +1,3 @@
// SPDX-License-Identifier: GPL-2.0-or-later
#if defined BOARD_OBP60S3 || defined BOARD_OBP40S3 #if defined BOARD_OBP60S3 || defined BOARD_OBP40S3
#include "Pagedata.h" #include "Pagedata.h"
@@ -7,23 +6,12 @@
class PageGenerator : public Page class PageGenerator : public Page
{ {
private:
String batVoltage;
int genPower;
String powerSensor;
public: public:
PageGenerator(CommonData &common) : Page(common) PageGenerator(CommonData &common){
{ commonData = &common;
logger->logDebug(GwLog::LOG, "Instantiate PageGenerator"); common.logger->logDebug(GwLog::LOG,"Instantiate PageGenerator");
// Get config data
batVoltage = config->getString(config->batteryVoltage);
genPower = config->getInt(config->genPower);
powerSensor = config->getString(config->usePowSensor3);
} }
virtual int handleKey(int key){
int handleKey(int key){
// Code for keylock // Code for keylock
if(key == 11){ if(key == 11){
commonData->keylock = !commonData->keylock; commonData->keylock = !commonData->keylock;
@@ -32,7 +20,19 @@ public:
return key; return key;
} }
int displayPage(PageData &pageData) { int displayPage(PageData &pageData)
{
GwConfigHandler *config = commonData->config;
GwLog *logger = commonData->logger;
// Get config data
bool simulation = config->getBool(config->useSimuData);
bool holdvalues = config->getBool(config->holdvalues);
String flashLED = config->getString(config->flashLED);
String batVoltage = config->getString(config->batteryVoltage);
int genPower = config->getInt(config->genPower);
String backlightMode = config->getString(config->backlight);
String powerSensor = config->getString(config->usePowSensor3);
double value1 = 0; // Solar voltage double value1 = 0; // Solar voltage
double value2 = 0; // Solar current double value2 = 0; // Solar current
@@ -60,95 +60,100 @@ public:
bool valid1 = true; bool valid1 = true;
// Optical warning by limit violation // Optical warning by limit violation
if (flashLED == "Limit Violation") { if(String(flashLED) == "Limit Violation"){
// Over voltage? // Over voltage
if (batVoltage == "12V") { if(value1 > 14.8 && batVoltage == "12V"){
setBlinkingLED(value1 > 14.8); setBlinkingLED(true);
} else if (batVoltage == "24V") { }
setBlinkingLED(value1 > 29.6); if(value1 <= 14.8 && batVoltage == "12V"){
} else { setBlinkingLED(false);
}
if(value1 > 29.6 && batVoltage == "24V"){
setBlinkingLED(true);
}
if(value1 <= 29.6 && batVoltage == "24V"){
setBlinkingLED(false); setBlinkingLED(false);
} }
} }
// Logging voltage value // Logging voltage value
logger->logDebug(GwLog::LOG, "Drawing at PageGenerator, Type:%iW %s:=%f", genPower, name1.c_str(), value1); LOG_DEBUG(GwLog::LOG,"Drawing at PageGenerator, Type:%iW %s:=%f", genPower, name1.c_str(), value1);
// Draw page // Draw page
//*********************************************************** //***********************************************************
// Set display in partial refresh mode // Set display in partial refresh mode
epd->setPartialWindow(0, 0, epd->width(), epd->height()); // Set partial update getdisplay().setPartialWindow(0, 0, getdisplay().width(), getdisplay().height()); // Set partial update
epd->setTextColor(commonData->fgcolor); getdisplay().setTextColor(commonData->fgcolor);
// Show name // Show name
epd->setFont(&Ubuntu_Bold20pt8b); getdisplay().setFont(&Ubuntu_Bold20pt8b);
epd->setCursor(10, 65); getdisplay().setCursor(10, 65);
epd->print("Power"); getdisplay().print("Power");
epd->setFont(&Ubuntu_Bold8pt8b); getdisplay().setFont(&Ubuntu_Bold8pt8b);
epd->setCursor(12, 82); getdisplay().setCursor(12, 82);
epd->print("Generator"); getdisplay().print("Generator");
// Show voltage type // Show voltage type
epd->setFont(&DSEG7Classic_BoldItalic20pt7b); getdisplay().setFont(&DSEG7Classic_BoldItalic20pt7b);
epd->setCursor(10, 140); getdisplay().setCursor(10, 140);
int bvoltage = 0; int bvoltage = 0;
if(String(batVoltage) == "12V") bvoltage = 12; if(String(batVoltage) == "12V") bvoltage = 12;
else bvoltage = 24; else bvoltage = 24;
epd->print(bvoltage); getdisplay().print(bvoltage);
epd->setFont(&Ubuntu_Bold16pt8b); getdisplay().setFont(&Ubuntu_Bold16pt8b);
epd->print("V"); getdisplay().print("V");
// Show solar power // Show solar power
epd->setFont(&DSEG7Classic_BoldItalic20pt7b); getdisplay().setFont(&DSEG7Classic_BoldItalic20pt7b);
epd->setCursor(10, 200); getdisplay().setCursor(10, 200);
if(genPower <= 999) epd->print(genPower, 0); if(genPower <= 999) getdisplay().print(genPower, 0);
if(genPower > 999) epd->print(float(genPower/1000.0), 1); if(genPower > 999) getdisplay().print(float(genPower/1000.0), 1);
epd->setFont(&Ubuntu_Bold16pt8b); getdisplay().setFont(&Ubuntu_Bold16pt8b);
if(genPower <= 999) epd->print("W"); if(genPower <= 999) getdisplay().print("W");
if(genPower > 999) epd->print("kW"); if(genPower > 999) getdisplay().print("kW");
// Show info // Show info
epd->setFont(&Ubuntu_Bold8pt8b); getdisplay().setFont(&Ubuntu_Bold8pt8b);
epd->setCursor(10, 235); getdisplay().setCursor(10, 235);
epd->print("Installed"); getdisplay().print("Installed");
epd->setCursor(10, 255); getdisplay().setCursor(10, 255);
epd->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
epd->setFont(&DSEG7Classic_BoldItalic20pt7b); getdisplay().setFont(&DSEG7Classic_BoldItalic20pt7b);
epd->setCursor(150, 200); getdisplay().setCursor(150, 200);
epd->print(genPercentage); getdisplay().print(genPercentage);
epd->setFont(&Ubuntu_Bold16pt8b); getdisplay().setFont(&Ubuntu_Bold16pt8b);
epd->print("%"); getdisplay().print("%");
epd->setFont(&Ubuntu_Bold8pt8b); getdisplay().setFont(&Ubuntu_Bold8pt8b);
epd->setCursor(150, 235); getdisplay().setCursor(150, 235);
epd->print("Load"); getdisplay().print("Load");
// Show sensor type info // Show sensor type info
String i2cAddr = ""; String i2cAddr = "";
epd->setFont(&Ubuntu_Bold8pt8b); getdisplay().setFont(&Ubuntu_Bold8pt8b);
epd->setCursor(270, 60); getdisplay().setCursor(270, 60);
if(powerSensor == "off") epd->print("Internal"); if(powerSensor == "off") getdisplay().print("Internal");
if(powerSensor == "INA219"){ if(powerSensor == "INA219"){
epd->print("INA219"); getdisplay().print("INA219");
i2cAddr = " (0x" + String(INA219_I2C_ADDR3, HEX) + ")"; i2cAddr = " (0x" + String(INA219_I2C_ADDR3, HEX) + ")";
} }
if(powerSensor == "INA226"){ if(powerSensor == "INA226"){
epd->print("INA226"); getdisplay().print("INA226");
i2cAddr = " (0x" + String(INA226_I2C_ADDR3, HEX) + ")"; i2cAddr = " (0x" + String(INA226_I2C_ADDR3, HEX) + ")";
} }
epd->print(i2cAddr); getdisplay().print(i2cAddr);
epd->setCursor(270, 80); getdisplay().setCursor(270, 80);
epd->print("Sensor Modul"); getdisplay().print("Sensor Modul");
// Reading bus data or using simulation data // Reading bus data or using simulation data
epd->setFont(&DSEG7Classic_BoldItalic20pt7b); getdisplay().setFont(&DSEG7Classic_BoldItalic20pt7b);
epd->setCursor(260, 140); getdisplay().setCursor(260, 140);
if(simulation == true){ if(simulation == true){
if(batVoltage == "12V"){ if(batVoltage == "12V"){
value1 = 12.0; value1 = 12.0;
@@ -157,59 +162,46 @@ public:
value1 = 24.0; value1 = 24.0;
} }
value1 += float(random(0, 5)) / 10; // Simulation data value1 += float(random(0, 5)) / 10; // Simulation data
epd->print(value1,1); getdisplay().print(value1,1);
} }
else{ else{
// Check for valid real data, display also if hold values activated // Check for valid real data, display also if hold values activated
if(valid1 == true || holdvalues == true){ if(valid1 == true || holdvalues == true){
// Resolution switching // Resolution switching
if(value1 <= 9.9) epd->print(value1, 2); if(value1 <= 9.9) getdisplay().print(value1, 2);
if(value1 > 9.9 && value1 <= 99.9)epd->print(value1, 1); if(value1 > 9.9 && value1 <= 99.9)getdisplay().print(value1, 1);
if(value1 > 99.9) epd->print(value1, 0); if(value1 > 99.9) getdisplay().print(value1, 0);
} }
else{ else{
epd->print(commonData->fmt->placeholder); // Missing bus data getdisplay().print("---"); // Missing bus data
} }
} }
epd->setFont(&Ubuntu_Bold16pt8b); getdisplay().setFont(&Ubuntu_Bold16pt8b);
epd->print("V"); getdisplay().print("V");
// Show actual current in A // Show actual current in A
epd->setFont(&DSEG7Classic_BoldItalic20pt7b); getdisplay().setFont(&DSEG7Classic_BoldItalic20pt7b);
epd->setCursor(260, 200); getdisplay().setCursor(260, 200);
if ((powerSensor == "INA219" || powerSensor == "INA226") && (simulation == false)) { if((powerSensor == "INA219" || powerSensor == "INA226") && simulation == false){
// TODO use formatter for this? if(value2 <= 9.9) getdisplay().print(value2, 2);
if (value2 <= 9.9) { if(value2 > 9.9 && value2 <= 99.9)getdisplay().print(value2, 1);
epd->print(value2, 2); if(value2 > 99.9) getdisplay().print(value2, 0);
} else if (value2 <= 99.9) {
epd->print(value2, 1);
} else {
epd->print(value2, 0);
} }
} else getdisplay().print("---");
else { getdisplay().setFont(&Ubuntu_Bold16pt8b);
epd->print(commonData->fmt->placeholder); getdisplay().print("A");
}
epd->setFont(&Ubuntu_Bold16pt8b);
epd->print("A");
// Show actual consumption in W // Show actual consumption in W
epd->setFont(&DSEG7Classic_BoldItalic20pt7b); getdisplay().setFont(&DSEG7Classic_BoldItalic20pt7b);
epd->setCursor(260, 260); getdisplay().setCursor(260, 260);
if ((powerSensor == "INA219" || powerSensor == "INA226") && (simulation == false)) { if((powerSensor == "INA219" || powerSensor == "INA226") && simulation == false){
if(value3 <= 9.9) { if(value3 <= 9.9) getdisplay().print(value3, 2);
epd->print(value3, 2); if(value3 > 9.9 && value3 <= 99.9)getdisplay().print(value3, 1);
} else if (value3 <= 99.9) { if(value3 > 99.9) getdisplay().print(value3, 0);
epd->print(value3, 1);
} else {
epd->print(value3, 0);
} }
} else getdisplay().print("---");
else { getdisplay().setFont(&Ubuntu_Bold16pt8b);
epd->print(commonData->fmt->placeholder); getdisplay().print("W");
}
epd->setFont(&Ubuntu_Bold16pt8b);
epd->print("W");
return PAGE_UPDATE; return PAGE_UPDATE;
}; };

View File

@@ -1,4 +1,3 @@
// SPDX-License-Identifier: GPL-2.0-or-later
#if defined BOARD_OBP60S3 || defined BOARD_OBP40S3 #if defined BOARD_OBP60S3 || defined BOARD_OBP40S3
#include "Pagedata.h" #include "Pagedata.h"
@@ -6,24 +5,14 @@
class PageKeelPosition : public Page class PageKeelPosition : public Page
{ {
private:
String lengthformat;
String rotsensor;
String rotfunction;
public: public:
PageKeelPosition(CommonData &common) : Page(common) PageKeelPosition(CommonData &common){
{ commonData = &common;
logger->logDebug(GwLog::LOG, "Instantiate PageKeelPosition"); common.logger->logDebug(GwLog::LOG,"Instantiate PageKeelPosition");
// Get config data
lengthformat = config->getString(config->lengthFormat);
rotsensor = config->getString(config->useRotSensor);
rotfunction = config->getString(config->rotFunction);
} }
// Key functions // Key functions
int handleKey(int key){ virtual int handleKey(int key){
// Code for keylock // Code for keylock
if(key == 11){ if(key == 11){
commonData->keylock = !commonData->keylock; commonData->keylock = !commonData->keylock;
@@ -32,21 +21,23 @@ public:
return key; return key;
} }
void displayNew(PageData &pageData) { int displayPage(PageData &pageData)
#ifdef BOARD_OBP60S3 {
// Clear optical warning GwConfigHandler *config = commonData->config;
if (flashLED == "Limit Violation") { GwLog *logger = commonData->logger;
setBlinkingLED(false);
setFlashLED(false);
}
#endif
};
int displayPage(PageData &pageData) {
double value1 = 0; double value1 = 0;
double value1old = 0; double value1old = 0;
// 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);
String rotsensor = config->getString(config->useRotSensor);
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"){
@@ -64,14 +55,20 @@ public:
value1old = value1; // Save old value value1old = value1; // Save old value
} }
// Optical warning by limit violation (unused)
if(String(flashLED) == "Limit Violation"){
setBlinkingLED(false);
setFlashLED(false);
}
// Logging boat values // Logging boat values
logger->logDebug(GwLog::LOG, "Drawing at PageKeelPosition, Keel:%f", value1); LOG_DEBUG(GwLog::LOG,"Drawing at PageKeelPosition, Keel:%f", value1);
// Draw page // Draw page
//*********************************************************** //***********************************************************
// Set display in partial refresh mode // Set display in partial refresh mode
epd->setPartialWindow(0, 0, epd->width(), epd->height()); // Set partial update getdisplay().setPartialWindow(0, 0, getdisplay().width(), getdisplay().height()); // Set partial update
//******************************************************************************************* //*******************************************************************************************
@@ -79,9 +76,9 @@ public:
int rInstrument = 110; // Radius of KeelPosition int rInstrument = 110; // Radius of KeelPosition
float pi = 3.141592; float pi = 3.141592;
epd->fillCircle(200, 150, rInstrument + 10, commonData->fgcolor); // Outer circle getdisplay().fillCircle(200, 150, rInstrument + 10, commonData->fgcolor); // Outer circle
epd->fillCircle(200, 150, rInstrument + 7, commonData->bgcolor); // Outer circle getdisplay().fillCircle(200, 150, rInstrument + 7, commonData->bgcolor); // Outer circle
epd->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)
{ {
@@ -108,17 +105,17 @@ public:
// Print text centered on position x, y // Print text centered on position x, y
int16_t x1, y1; // Return values of getTextBounds int16_t x1, y1; // Return values of getTextBounds
uint16_t w, h; // Return values of getTextBounds uint16_t w, h; // Return values of getTextBounds
epd->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
epd->setCursor(x-w/2, y+h/2); getdisplay().setCursor(x-w/2, y+h/2);
if(i % 30 == 0){ if(i % 30 == 0){
epd->setFont(&Ubuntu_Bold8pt8b); getdisplay().setFont(&Ubuntu_Bold8pt8b);
epd->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);
epd->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);
@@ -129,10 +126,10 @@ public:
float xx2 = +dx; float xx2 = +dx;
float yy1 = -(rInstrument-10); float yy1 = -(rInstrument-10);
float yy2 = -(rInstrument+10); float yy2 = -(rInstrument+10);
epd->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);
epd->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);
} }
@@ -166,7 +163,7 @@ public:
float xx2 = startwidth; float xx2 = startwidth;
float yy1 = -startwidth; float yy1 = -startwidth;
float yy2 = -(rInstrument * 0.6); float yy2 = -(rInstrument * 0.6);
epd->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
@@ -176,36 +173,36 @@ public:
float ix2 = -endwidth; float ix2 = -endwidth;
float iy1 = -(rInstrument * 0.6); float iy1 = -(rInstrument * 0.6);
float iy2 = -endwidth; float iy2 = -endwidth;
epd->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
epd->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
epd->fillCircle(200, 140, startwidth + 22, commonData->bgcolor); getdisplay().fillCircle(200, 140, startwidth + 22, commonData->bgcolor);
epd->fillCircle(200, 140, startwidth + 20, commonData->fgcolor); // Boat circle getdisplay().fillCircle(200, 140, startwidth + 20, commonData->fgcolor); // Boat circle
epd->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
epd->fillRect(150, 150, 100, 4, commonData->fgcolor); // Water line getdisplay().fillRect(150, 150, 100, 4, commonData->fgcolor); // Water line
// Print label // Print label
epd->setFont(&Ubuntu_Bold16pt8b); getdisplay().setFont(&Ubuntu_Bold16pt8b);
epd->setCursor(100, 70); getdisplay().setCursor(100, 70);
epd->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
epd->setFont(&Ubuntu_Bold12pt8b); getdisplay().setFont(&Ubuntu_Bold12pt8b);
epd->setCursor(175, 110); getdisplay().setCursor(175, 110);
epd->print(unit1); // Unit getdisplay().print(unit1); // Unit
} }
else{ else{
// Print Unit of keel position // Print Unit of keel position
epd->setFont(&Ubuntu_Bold8pt8b); getdisplay().setFont(&Ubuntu_Bold8pt8b);
epd->setCursor(145, 110); getdisplay().setCursor(145, 110);
epd->print("No sensor data"); // Info missing sensor getdisplay().print("No sensor data"); // Info missing sensor
} }
return PAGE_UPDATE; return PAGE_UPDATE;

View File

@@ -4,7 +4,8 @@
#include "OBP60Extensions.h" #include "OBP60Extensions.h"
#include "NetworkClient.h" // Network connection #include "NetworkClient.h" // Network connection
#include "ImageDecoder.h" // Image decoder for navigation map #include "ImageDecoder.h" // Image decoder for navigation map
#include <mbedtls/base64.h>
#include "Logo_OBP_400x300_sw.h"
// Defines for reading of navigation map // Defines for reading of navigation map
#define JSON_BUFFER 30000 // Max buffer size for JSON content (30 kB picture + values) #define JSON_BUFFER 30000 // Max buffer size for JSON content (30 kB picture + values)
@@ -13,65 +14,34 @@ ImageDecoder decoder; // Define image decoder
class PageNavigation : public Page class PageNavigation : public Page
{ {
private:
// Values for buttons // Values for buttons
bool firstRun = true; // Detect the first page run bool firstRun = true; // Detect the first page run
int zoom = 15; // Default zoom level int zoom = 15; // Default zoom level
bool showValues = false; // Show values HDT, SOG, DBT in navigation map bool showValues = false; // Show values HDT, SOG, DBT in navigation map
private:
uint8_t* imageBackupData = nullptr; uint8_t* imageBackupData = nullptr;
int imageBackupWidth = 0; int imageBackupWidth = 0;
int imageBackupHeight = 0; int imageBackupHeight = 0;
size_t imageBackupSize = 0; size_t imageBackupSize = 0;
size_t imageBackupCapacity = 0;
bool hasImageBackup = false; bool hasImageBackup = false;
bool imageBackupIsRgb565 = false;
String lengthformat;
String mapsource;
String ipAddress;
int localPort;
String mapType;
int zoomLevel;
bool grid;
String orientation;
int refreshDistance;
bool showValuesMap;
bool ownHeading;
public: public:
PageNavigation(CommonData &common) : Page(common) PageNavigation(CommonData &common){
{ commonData = &common;
logger->logDebug(GwLog::LOG,"Instantiate PageNavigation"); common.logger->logDebug(GwLog::LOG,"Instantiate PageNavigation");
imageBackupData = (uint8_t*)heap_caps_malloc((GxEPD_WIDTH * GxEPD_HEIGHT), MALLOC_CAP_SPIRAM);
imageBackupCapacity = (size_t) epd->width() * (size_t) epd->height();
imageBackupData = (uint8_t*)heap_caps_malloc(imageBackupCapacity, MALLOC_CAP_SPIRAM);
// Get config data
lengthformat = config->getString(config->lengthFormat);
mapsource = config->getString(config->mapsource);
ipAddress = config->getString(config->ipAddress);
localPort = config->getInt(config->localPort);
mapType = config->getString(config->maptype);
zoomLevel = config->getInt(config->zoomlevel);
grid = config->getBool(config->grid);
orientation = config->getString(config->orientation);
refreshDistance = config->getInt(config->refreshDistance);
showValuesMap = config->getBool(config->showvalues);
ownHeading = config->getBool(config->ownheading);
} }
// Set botton labels // Set botton labels
void setupKeys(){ virtual void setupKeys(){
Page::setupKeys(); Page::setupKeys();
commonData->keydata[0].label = "ZOOM -"; commonData->keydata[0].label = "ZOOM -";
commonData->keydata[1].label = "ZOOM +"; commonData->keydata[1].label = "ZOOM +";
commonData->keydata[4].label = "VALUES"; commonData->keydata[4].label = "VALUES";
} }
int handleKey(int key){ virtual int handleKey(int key){
// Code for keylock // Code for keylock
if(key == 11){ if(key == 11){
commonData->keylock = !commonData->keylock; commonData->keylock = !commonData->keylock;
@@ -100,22 +70,31 @@ public:
return key; return key;
} }
void displayNew(PageData &pageData) {
#ifdef BOARD_OBP60S3
// Clear optical warning
if (flashLED == "Limit Violation") {
setBlinkingLED(false);
setFlashLED(false);
}
#endif
};
int displayPage(PageData &pageData){ int displayPage(PageData &pageData){
GwConfigHandler *config = commonData->config;
GwLog *logger = commonData->logger;
// 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);
String mapsource = config->getString(config->mapsource);
String ipAddress = config->getString(config->ipAddress);
int localPort = config->getInt(config->localPort);
String mapType = config->getString(config->maptype);
int zoomLevel = config->getInt(config->zoomlevel);
bool grid = config->getBool(config->grid);
String orientation = config->getString(config->orientation);
int refreshDistance = config->getInt(config->refreshDistance);
bool showValuesMap = config->getBool(config->showvalues);
bool ownHeading = config->getBool(config->ownheading);
if(firstRun == true){ if(firstRun == true){
zoom = zoomLevel; // Over write zoom level with setup value zoom = zoomLevel; // Over write zoom level with setup value
showValues = showValuesMap; // Over write showValues with setup value showValues = showValuesMap; // Over write showValues with setup value
firstRun = false; firstRun = false; // Restet variable
} }
// Local variables // Local variables
@@ -127,6 +106,7 @@ public:
int symbolRot = 0; int symbolRot = 0;
int mapGrid = 0; int mapGrid = 0;
// Old values for hold function // Old values for hold function
static double value1old = 0; static double value1old = 0;
static String svalue1old = ""; static String svalue1old = "";
@@ -165,8 +145,8 @@ public:
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 = commonData->fmt->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 = commonData->fmt->formatValue(bvalue1, *commonData).unit; // Unit of value String unit1 = formatValue(bvalue1, *commonData).unit; // Unit of value
// Get boat values #2 Longitude // Get boat values #2 Longitude
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)
@@ -174,8 +154,8 @@ public:
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 = commonData->fmt->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 = commonData->fmt->formatValue(bvalue2, *commonData).unit; // Unit of value String unit2 = formatValue(bvalue2, *commonData).unit; // Unit of value
// Get boat values #3 HDT // Get boat values #3 HDT
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)
@@ -183,8 +163,8 @@ public:
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 = commonData->fmt->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 = commonData->fmt->formatValue(bvalue3, *commonData).unit; // Unit of value String unit3 = formatValue(bvalue3, *commonData).unit; // Unit of value
// Get boat values #4 HDM // Get boat values #4 HDM
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)
@@ -192,8 +172,8 @@ public:
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 = commonData->fmt->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 = commonData->fmt->formatValue(bvalue4, *commonData).unit; // Unit of value String unit4 = formatValue(bvalue4, *commonData).unit; // Unit of value
// Get boat values #5 SOG // Get boat values #5 SOG
GwApi::BoatValue *bvalue5 = pageData.values[4]; // Second element in list (only one value by PageOneValue) GwApi::BoatValue *bvalue5 = pageData.values[4]; // Second element in list (only one value by PageOneValue)
@@ -201,8 +181,8 @@ public:
name5 = name5.substring(0, 6); // String length limit for value name name5 = name5.substring(0, 6); // String length limit for value name
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 = commonData->fmt->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 = commonData->fmt->formatValue(bvalue5, *commonData).unit; // Unit of value String unit5 = formatValue(bvalue5, *commonData).unit; // Unit of value
// Get boat values #6 DBT // Get boat values #6 DBT
GwApi::BoatValue *bvalue6 = pageData.values[5]; // Second element in list (only one value by PageOneValue) GwApi::BoatValue *bvalue6 = pageData.values[5]; // Second element in list (only one value by PageOneValue)
@@ -210,14 +190,18 @@ public:
name6 = name6.substring(0, 6); // String length limit for value name name6 = name6.substring(0, 6); // String length limit for value name
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 = commonData->fmt->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 = commonData->fmt->formatValue(bvalue6, *commonData).unit; // Unit of value String unit6 = formatValue(bvalue6, *commonData).unit; // Unit of value
// Optical warning by limit violation (unused)
if(String(flashLED) == "Limit Violation"){
setBlinkingLED(false);
setFlashLED(false);
}
// Logging boat values // Logging boat values
if (bvalue1 == NULL) return PAGE_OK; // WTF why this statement? if (bvalue1 == NULL) return PAGE_OK; // WTF why this statement?
logger->logDebug(GwLog::LOG, "Drawing at PageNavigation, %s: %f, %s: %f, %s: %f, %s: %f, %s: %f, %s: %f", LOG_DEBUG(GwLog::LOG,"Drawing at PageNavigation, %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);
name1.c_str(), value1, name2.c_str(), value2, name3.c_str(), value3,
name4.c_str(), value4, name5.c_str(), value5, name6.c_str(), value6);
// Set variables // Set variables
//*********************************************************** //***********************************************************
@@ -311,18 +295,6 @@ public:
mType = 9; mType = 9;
dType = 1; dType = 1;
} }
else if(mapType == "C-Map"){
mType = 103486987;
dType = 1;
}
else if(mapType == "Garmin Fish"){
mType = 113486987;
dType = 1;
}
else if(mapType == "Garmin Nav"){
mType = 123486987;
dType = 1;
}
else{ else{
mType = 1; mType = 1;
dType = 1; dType = 1;
@@ -376,31 +348,20 @@ public:
// For more details see: https://github.com/norbert-walter/maps-converter // For more details see: https://github.com/norbert-walter/maps-converter
String url = String("http://") + server + ":" + port + // OBP Server String url = String("http://") + server + ":" + port + // OBP Server
String("/get_image_json?") + // Service: Output B&W picture as JSON (Base64 + gzip) String("/get_image_json?") + // Service: Output B&W picture as JSON (Base64 + gzip)
#ifdef DISPLAY_ST7796 "zoom=" + zoom + // Default zoom level: 15
"oformat=3" + // Image output format in JSON: 3=RGB565 format
#else
"oformat=4" + // Image output format in JSON: 4=b/w 1-Bit format
#endif
"&zoom=" + zoom + // Default zoom level: 15
"&lat=" + String(latitude, 6) + // Latitude "&lat=" + String(latitude, 6) + // Latitude
"&lon=" + String(longitude, 6) + // Longitude "&lon=" + String(longitude, 6) + // Longitude
"&mrot=" + mapRot + // Rotation angle navigation map in degree "&mrot=" + mapRot + // Rotation angle navigation map in degree
"&mtype=" + mType + // Default Map: Open Street Map "&mtype=" + mType + // Default Map: Open Street Map
#ifdef DISPLAY_ST7796 "&dtype=" + dType + // Dithering type: Atkinson dithering
"&itype=1" + // Image type: 1=Color
#else
"&itype=4" + // Image type: 4=b/w with dithering
#endif
"&dtype=" + dType + // Dithering type: Atkinson dithering (only activ when itype=4 otherwise inactive)
"&width=400" + // With navigation map "&width=400" + // With navigation map
"&height=250" + // Height navigation map "&height=250" + // Height navigation map
"&cutout=0" + // No picture cutouts (tab, border and alpha are unused when cutout=0) "&cutout=0" + // No picture cutouts
"&tab=0" + // No tab size (only available when sqare cutouts selected coutout=3...7) "&tab=0" + // No tab size
"&border=2" + // Border line size: 2 pixel (only available when sqare cutouts selected) "&border=2" + // Border line size: 2 pixel
"&alpha=80" + // Alpha for tabs: 80% visible (only available when sqare cutouts selected)
"&symbol=2" + // Symbol: Triangle "&symbol=2" + // Symbol: Triangle
"&srot=" + symbolRot + // Symbol rotation angle "&srot=" + symbolRot + // Symbol rotation angle
"&ssize=15" + // Symbole size: 15 pixel (center pointer) "&ssize=15" + // Symbole size: 15 pixel
"&grid=" + mapGrid // Show grid: On "&grid=" + mapGrid // Show grid: On
; ;
@@ -410,102 +371,54 @@ public:
// ############### Draw Navigation Map ################ // ############### Draw Navigation Map ################
// Set display in partial refresh mode // Set display in partial refresh mode
epd->setPartialWindow(0, 0, epd->width(), epd->height()); // Set partial update getdisplay().setPartialWindow(0, 0, getdisplay().width(), getdisplay().height()); // Set partial update
epd->setTextColor(commonData->fgcolor); getdisplay().setTextColor(commonData->fgcolor);
// NEW: simple exponential backoff for 1 Hz polling (prevents connection-refused storms)
static uint32_t nextAllowedMs = 0;
static uint8_t failCount = 0;
uint32_t now = millis();
// NEW: if we are in backoff window, skip network call and use backup immediately
bool allowFetch = ((int32_t)(now - nextAllowedMs) >= 0);
// If a network connection to URL then load the navigation map // If a network connection to URL then load the navigation map
if (allowFetch && net.fetchAndDecompressJson(url)) { if (net.fetchAndDecompressJson(url)) {
// NEW: reset backoff on success auto& json = net.json(); // Extract JSON content
failCount = 0; int numPix = json["number_pixels"] | 0; // Read number of pixels
nextAllowedMs = now + 1000; // keep 1 Hz on success imgWidth = json["width"] | 0; // Read width of image
imgHeight = json["height"] | 0; // Read height og image
int numPix = net.numberPixels(); // Read number of pixels const char* b64src = json["picture_base64"].as<const char*>(); // Read picture as Base64 content
imgWidth = net.imageWidth(); // Read width of image size_t b64len = strlen(b64src); // Calculate length of Base64 content
imgHeight = net.imageHeight(); // Read height of image
size_t requiredBytesMono = 0;
size_t requiredBytesRgb565 = 0;
if (imgWidth > 0 && imgHeight > 0){
requiredBytesMono = (size_t)((imgWidth + 7) / 8) * (size_t)imgHeight;
requiredBytesRgb565 = (size_t)imgWidth * (size_t)imgHeight * 2U;
}
if (requiredBytesMono == 0){
logger->logDebug(GwLog::ERROR,"Error PageNavigation: invalid image geometry w=%d h=%d",imgWidth,imgHeight);
return PAGE_UPDATE;
}
const char* b64src = net.pictureBase64(); // Read picture as Base64 content
if (b64src == nullptr){
logger->logDebug(GwLog::ERROR,"Error PageNavigation: picture_base64 missing");
return PAGE_UPDATE;
}
size_t b64len = net.pictureBase64Len(); // Calculate length of Base64 content
// Copy Base64 content in PSRAM // Copy Base64 content in PSRAM
char* b64 = (char*) heap_caps_malloc(b64len + 1, MALLOC_CAP_SPIRAM); // Allcate PSRAM for Base64 content char* b64 = (char*) heap_caps_malloc(b64len + 1, MALLOC_CAP_SPIRAM); // Allcate PSRAM for Base64 content
if (!b64) { if (!b64) {
logger->logDebug(GwLog::ERROR,"Error PageNavigation: PSRAM alloc base64 failed"); LOG_DEBUG(GwLog::ERROR,"Error PageNavigation: PSRAM alloc base64 failed");
return PAGE_UPDATE; return PAGE_UPDATE;
} }
memcpy(b64, b64src, b64len + 1); // Copy Base64 content in PSRAM memcpy(b64, b64src, b64len + 1); // Copy Base64 content in PSRAM
// Set image buffer in PSRAM // Set image buffer in PSRAM
size_t imgSize = (numPix > 0) ? (size_t)numPix : requiredBytesMono; // Calculate image size //size_t imgSize = getdisplay().width() * getdisplay().height();
if (imgSize < requiredBytesMono){ size_t imgSize = numPix; // Calculate image size
imgSize = requiredBytesMono;
}
uint8_t* imageData = (uint8_t*) heap_caps_malloc(imgSize, MALLOC_CAP_SPIRAM); // Allocate PSRAM for image uint8_t* imageData = (uint8_t*) heap_caps_malloc(imgSize, MALLOC_CAP_SPIRAM); // Allocate PSRAM for image
if (!imageData) { if (!imageData) {
logger->logDebug(GwLog::ERROR,"Error PageNavigation: PSRAM alloc image buffer failed"); LOG_DEBUG(GwLog::ERROR,"Error PageNavigation: PPSRAM alloc image buffer failed");
free(b64); free(b64);
return PAGE_UPDATE; return PAGE_UPDATE;
} }
// Decode Base64 content to image // Decode Base64 content to image
size_t decodedSize = 0; size_t decodedSize = 0;
bool decodeOk = decoder.decodeBase64(b64, b64len, imageData, imgSize, decodedSize); decoder.decodeBase64(b64, imageData, imgSize, decodedSize);
if (!decodeOk || decodedSize < requiredBytesMono){
int base64Ret = mbedtls_base64_decode(
nullptr,
0,
&decodedSize,
(const unsigned char*)b64,
b64len
);
logger->logDebug(GwLog::ERROR,
"Error PageNavigation: decode failed (ok=%d, decoded=%u, required=%u, b64ret=%d)",
decodeOk ? 1 : 0,
(unsigned int)decodedSize,
(unsigned int)requiredBytesMono,
base64Ret
);
free(b64);
free(imageData);
return PAGE_UPDATE;
}
// Copy actual navigation map to backup map // Copy actual navigation man to ackup map
imageBackupWidth = imgWidth; imageBackupWidth = imgWidth;
imageBackupHeight = imgHeight; imageBackupHeight = imgHeight;
imageBackupSize = imgSize; imageBackupSize = imgSize;
if (decodedSize > 0 && imageBackupData != nullptr) { if (decodedSize > 0) {
size_t copySize = (decodedSize > imageBackupCapacity) ? imageBackupCapacity : decodedSize; memcpy(imageBackupData, imageData, decodedSize);
memcpy(imageBackupData, imageData, copySize); imageBackupSize = decodedSize;
imageBackupSize = copySize;
} }
hasImageBackup = (imageBackupData != nullptr); hasImageBackup = true;
lostCounter = 0; lostCounter = 0;
// Show image (navigation map) // Show image (navigation map)
epd->drawBitmap(0, 25, imageData, imgWidth, imgHeight, commonData->fgcolor); getdisplay().drawBitmap(0, 25, imageData, imgWidth, imgHeight, commonData->fgcolor);
// Clean PSRAM // Clean PSRAM
free(b64); free(b64);
@@ -513,74 +426,62 @@ public:
} }
// If no network connection then use backup navigation map // If no network connection then use backup navigation map
else{ else{
// NEW: update backoff only if we actually attempted a fetch (not when skipping due to backoff)
if (allowFetch) {
// NEW: exponential backoff: 1s,2s,4s,8s,16s,30s (capped)
if (failCount < 6) failCount++;
uint32_t backoffMs = 1000u << failCount;
if (backoffMs > 30000u) backoffMs = 30000u;
nextAllowedMs = now + backoffMs;
} else {
// NEW: we are currently backing off; do not increase failCount further
// nextAllowedMs stays unchanged
}
// Show backup image (backup navigation map) // Show backup image (backup navigation map)
if (hasImageBackup) { if (hasImageBackup) {
epd->drawBitmap(0, 25, imageBackupData, imageBackupWidth, imageBackupHeight, commonData->fgcolor); getdisplay().drawBitmap(0, 25, imageBackupData, imageBackupWidth, imageBackupHeight, commonData->fgcolor);
} }
// Show connection lost info when 5 page refreshes has a connection lost to the map server // Show info: Connection lost when 5 page refreshes has a connection lost to the map server
// Short connection losts are uncritical // Short connection losts are uncritical
if(lostCounter >= 5){ if(lostCounter >= 5){
epd->setFont(&Ubuntu_Bold12pt8b); getdisplay().setFont(&Ubuntu_Bold12pt8b);
epd->fillRect(200, 250 , 200, 25, commonData->fgcolor); getdisplay().fillRect(200, 250 , 200, 25, commonData->fgcolor); // Black rect
epd->fillRect(202, 252 , 196, 21, commonData->bgcolor); getdisplay().fillRect(202, 252 , 196, 21, commonData->bgcolor); // White rect
epd->setCursor(210, 270); getdisplay().setCursor(210, 270);
epd->print("Map server lost"); getdisplay().print("Map server lost");
} }
lostCounter++; lostCounter++; // Increment lost counter
} }
// ############### Draw Values ################ // ############### Draw Values ################
epd->setFont(&Ubuntu_Bold12pt8b); getdisplay().setFont(&Ubuntu_Bold12pt8b);
// Show zoom level // Show zoom level
epd->fillRect(355, 25 , 45, 25, commonData->fgcolor); getdisplay().fillRect(355, 25 , 45, 25, commonData->fgcolor); // Black rect
epd->fillRect(357, 27 , 41, 21, commonData->bgcolor); getdisplay().fillRect(357, 27 , 41, 21, commonData->bgcolor); // White rect
epd->setCursor(364, 45); getdisplay().setCursor(364, 45);
epd->print(zoom); getdisplay().print(zoom);
// If true heading available then use HDT oterwise HDM // If true heading available then use HDT oterwise HDM
if(showValues == true){ if(showValues == true){
// Frame // Frame
epd->fillRect(0, 25 , 130, 65, commonData->fgcolor); getdisplay().fillRect(0, 25 , 130, 65, commonData->fgcolor); // Black rect
epd->fillRect(2, 27 , 126, 61, commonData->bgcolor); getdisplay().fillRect(2, 27 , 126, 61, commonData->bgcolor); // White rect
if(valid3 == true){ if(valid3 == true){
// HDT // HDT
epd->setCursor(10, 45); getdisplay().setCursor(10, 45);
epd->print(name3); getdisplay().print(name3);
epd->setCursor(70, 45); getdisplay().setCursor(70, 45);
epd->print(svalue3); getdisplay().print(svalue3);
} }
else{ else{
// HDM // HDM
epd->setCursor(10, 45); getdisplay().setCursor(10, 45);
epd->print(name4); getdisplay().print(name4);
epd->setCursor(70, 45); getdisplay().setCursor(70, 45);
epd->print(svalue4); getdisplay().print(svalue4);
} }
// SOG // SOG
epd->setCursor(10, 65); getdisplay().setCursor(10, 65);
epd->print(name5); getdisplay().print(name5);
epd->setCursor(70, 65); getdisplay().setCursor(70, 65);
epd->print(svalue5); getdisplay().print(svalue5);
// DBT // DBT
epd->setCursor(10, 85); getdisplay().setCursor(10, 85);
epd->print(name6); getdisplay().print(name6);
epd->setCursor(70, 85); getdisplay().setCursor(70, 85);
epd->print(svalue6); getdisplay().print(svalue6);
} }
return PAGE_UPDATE; return PAGE_UPDATE;

View File

@@ -1,37 +1,233 @@
// SPDX-License-Identifier: GPL-2.0-or-later
#if defined BOARD_OBP60S3 || defined BOARD_OBP40S3 #if defined BOARD_OBP60S3 || defined BOARD_OBP40S3
#include "Pagedata.h" #include "Pagedata.h"
#include "OBP60Extensions.h" #include "OBP60Extensions.h"
#include "OBPDataOperations.h"
#include "OBPcharts.h"
#ifdef ENABLE_CALIBRATION class PageOneValue : public Page {
#include "BoatDataCalibration.h"
#endif
class PageOneValue : public Page
{
private: private:
String lengthformat; GwLog* logger;
public: enum PageMode {
PageOneValue(CommonData &common) : Page(common) VALUE,
BOTH,
CHART
};
enum DisplayMode {
FULL,
HALF
};
static constexpr char HORIZONTAL = 'H';
static constexpr char VERTICAL = 'V';
static constexpr int8_t FULL_SIZE = 0;
static constexpr int8_t HALF_SIZE_TOP = 1;
static constexpr int8_t HALF_SIZE_BOTTOM = 2;
static constexpr bool PRNT_NAME = true;
static constexpr bool NO_PRNT_NAME = false;
static constexpr bool PRNT_VALUE = true;
static constexpr bool NO_PRNT_VALUE = false;
int width; // Screen width
int height; // Screen height
bool keylock = false; // Keylock
PageMode pageMode = VALUE; // Page display mode
int8_t 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
// String lengthformat;
bool useSimuData;
bool holdValues;
String flashLED;
String backlightMode;
String tempFormat;
// Old values for hold function
String sValue1Old = "";
String unit1Old = "";
// Data buffer pointer (owned by HstryBuffers)
RingBuffer<uint16_t>* dataHstryBuf = nullptr;
std::unique_ptr<Chart> dataChart; // Chart object
// display data value in display <mode> [FULL|HALF]
void showData(GwApi::BoatValue* bValue1, DisplayMode mode)
{ {
logger->logDebug(GwLog::LOG, "Instantiate PageOneValue"); int nameXoff, nameYoff, unitXoff, unitYoff, value1Xoff, value1Yoff;
const GFXfont *nameFnt, *unitFnt, *valueFnt1, *valueFnt2, *valueFnt3;
// Get config data if (mode == FULL) { // full size data display
lengthformat = config->getString(config->lengthFormat); nameXoff = 0;
nameYoff = 0;
nameFnt = &Ubuntu_Bold32pt8b;
unitXoff = 0;
unitYoff = 0;
unitFnt = &Ubuntu_Bold20pt8b;
value1Xoff = 0;
value1Yoff = 0;
valueFnt1 = &Ubuntu_Bold20pt8b;
valueFnt2 = &Ubuntu_Bold32pt8b;
valueFnt3 = &DSEG7Classic_BoldItalic60pt7b;
} else { // half size data and chart display
nameXoff = -10;
nameYoff = -34;
nameFnt = &Ubuntu_Bold20pt8b;
unitXoff = -295;
unitYoff = -119;
unitFnt = &Ubuntu_Bold12pt8b;
valueFnt1 = &Ubuntu_Bold12pt8b;
value1Xoff = 153;
value1Yoff = -119;
valueFnt2 = &Ubuntu_Bold20pt8b;
valueFnt3 = &DSEG7Classic_BoldItalic42pt7b;
} }
int handleKey(int key) { String name1 = xdrDelete(bValue1->getName()); // Value name
// Code for keylock name1 = name1.substring(0, 6); // String length limit for value name
if(key == 11){ 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
// Show name
getdisplay().setTextColor(commonData->fgcolor);
getdisplay().setFont(nameFnt);
getdisplay().setCursor(20 + nameXoff, 100 + nameYoff);
getdisplay().print(name1); // name
// Show unit
getdisplay().setFont(unitFnt);
getdisplay().setCursor(305 + unitXoff, 240 + unitYoff);
if (holdValues) {
getdisplay().print(unit1Old); // name
} else {
getdisplay().print(unit1); // name
}
// Switch font depending on value format and adjust position
if (bValue1->getFormat() == "formatLatitude" || bValue1->getFormat() == "formatLongitude") {
getdisplay().setFont(valueFnt1);
getdisplay().setCursor(20 + value1Xoff, 180 + value1Yoff);
} else if (bValue1->getFormat() == "formatTime" || bValue1->getFormat() == "formatDate") {
getdisplay().setFont(valueFnt2);
getdisplay().setCursor(20 + value1Xoff, 200 + value1Yoff);
} else {
getdisplay().setFont(valueFnt3);
getdisplay().setCursor(20 + value1Xoff, 240 + value1Yoff);
}
// Show bus data
if (!holdValues || useSimuData) {
getdisplay().print(sValue1); // Real value as formated string
} else {
getdisplay().print(sValue1Old); // Old value as formated string
}
if (valid1 == true) {
sValue1Old = sValue1; // Save the old value
unit1Old = unit1; // Save the old unit
}
}
public:
PageOneValue(CommonData& common)
{
commonData = &common;
logger = commonData->logger;
LOG_DEBUG(GwLog::LOG, "Instantiate PageOneValue");
width = getdisplay().width(); // Screen width
height = getdisplay().height(); // Screen height
// Get config data
// lengthformat = commonData->config->getString(commonData->config->lengthFormat);
useSimuData = commonData->config->getBool(commonData->config->useSimuData);
holdValues = commonData->config->getBool(commonData->config->holdvalues);
flashLED = commonData->config->getString(commonData->config->flashLED);
backlightMode = commonData->config->getString(commonData->config->backlight);
tempFormat = commonData->config->getString(commonData->config->tempFormat); // [K|°C|°F]
}
virtual void setupKeys()
{
Page::setupKeys();
#if defined BOARD_OBP60S3
constexpr int ZOOM_KEY = 4;
#elif defined BOARD_OBP40S3
constexpr int ZOOM_KEY = 1;
#endif
if (dataHstryBuf) { // show "Mode" key only if chart-supported boat data type is available
commonData->keydata[0].label = "MODE";
if (pageMode != VALUE) { // show "ZOOM" key only if chart is visible
commonData->keydata[ZOOM_KEY].label = "ZOOM";
} else {
commonData->keydata[ZOOM_KEY].label = "";
}
} else {
commonData->keydata[0].label = "";
commonData->keydata[ZOOM_KEY].label = "";
}
}
// Key functions
virtual int handleKey(int key)
{
if (dataHstryBuf) { // if boat data type supports charts
// Set page mode: value | value/half chart | full chart
if (key == 1) {
switch (pageMode) {
case VALUE:
pageMode = BOTH;
break;
case BOTH:
pageMode = CHART;
break;
case CHART:
pageMode = VALUE;
break;
}
setupKeys(); // Adjust key definition depending on <pageMode> and chart-supported boat data type
return 0; // Commit the key
}
// Set time frame to show for chart
#if defined BOARD_OBP60S3
if (key == 5 && pageMode != VALUE) {
#elif defined BOARD_OBP40S3
if (key == 2 && pageMode != VALUE) {
#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; commonData->keylock = !commonData->keylock;
return 0; // Commit the key return 0; // Commit the key
} }
return key; return key;
} }
void displayNew(PageData &pageData) { virtual void displayNew(PageData& pageData)
{
#ifdef BOARD_OBP60S3 #ifdef BOARD_OBP60S3
// Clear optical warning // Clear optical warning
if (flashLED == "Limit Violation") { if (flashLED == "Limit Violation") {
@@ -39,84 +235,71 @@ public:
setFlashLED(false); setFlashLED(false);
} }
#endif #endif
}; // buffer initialization will fail, if page is default page, because <displayNew> is not executed at system start for default page
if (!dataChart) { // Create chart objects if they don't exist
int displayPage(PageData &pageData) { GwApi::BoatValue* bValue1 = pageData.values[0]; // Page boat data element
String bValName1 = bValue1->getName(); // Value name
String bValFormat = bValue1->getFormat(); // Value format
// Old values for hold function dataHstryBuf = pageData.hstryBuffers->getBuffer(bValName1);
static String svalue1old = "";
static String unit1old = "";
if (dataHstryBuf) {
dataChart.reset(new Chart(*dataHstryBuf, Chart::dfltChrtDta[bValFormat].range, *commonData, useSimuData));
LOG_DEBUG(GwLog::DEBUG, "PageOneValue: Created chart objects for %s", bValName1);
} else {
LOG_DEBUG(GwLog::DEBUG, "PageOneValue: No chart objects available for %s", bValName1);
}
}
// Get boat values setupKeys(); // Adjust key definition depending on <pageMode> and chart-supported boat data type
GwApi::BoatValue *bvalue1 = pageData.values[0]; // First element in list (only one value by PageOneValue) }
String name1 = xdrDelete(bvalue1->getName()); // Value name
name1 = name1.substring(0, 6); // String length limit for value name
#ifdef ENABLE_CALIBRATION
calibrationData.calibrateInstance(bvalue1, logger); // Check if boat data value is to be calibrated
#endif
double value1 = bvalue1->value; // Value as double in SI unit
bool valid1 = bvalue1->valid; // Valid information
String svalue1 = commonData->fmt->formatValue(bvalue1, *commonData).svalue; // Formatted value as string including unit conversion and switching decimal places
String unit1 = commonData->fmt->formatValue(bvalue1, *commonData).unit; // Unit of value
// Logging boat values int displayPage(PageData& pageData)
if (bvalue1 == NULL) return PAGE_OK; // WTF why this statement? {
logger->logDebug(GwLog::LOG, "Drawing at PageOneValue, %s: %f", name1.c_str(), value1); LOG_DEBUG(GwLog::LOG, "Display PageOneValue");
// Get boat value for page
GwApi::BoatValue* bValue1 = pageData.values[0]; // Page boat data element
// Optical warning by limit violation (unused)
if (String(flashLED) == "Limit Violation") {
setBlinkingLED(false);
setFlashLED(false);
}
if (bValue1 == NULL)
return PAGE_OK; // no data, no page to display
LOG_DEBUG(GwLog::DEBUG, "PageOneValue: printing %s, %.3f", bValue1->getName().c_str(), bValue1->value);
// Draw page // Draw page
//*********************************************************** //***********************************************************
/// Set display in partial refresh mode getdisplay().setPartialWindow(0, 0, width, height); // Set partial update
epd->setPartialWindow(0, 0, epd->width(), epd->height()); // Set partial update
// Show name if (pageMode == VALUE || dataHstryBuf == nullptr) {
epd->setTextColor(commonData->fgcolor); // show only data value; ignore other pageMode options if no chart supported boat data history buffer is available
epd->setFont(&Ubuntu_Bold32pt8b); showData(bValue1, FULL);
epd->setCursor(20, 100);
epd->print(name1); // Page name
// Show unit } else if (pageMode == CHART) { // show only data chart
epd->setFont(&Ubuntu_Bold20pt8b); if (dataChart) {
epd->setCursor(270, 100); dataChart->showChrt(HORIZONTAL, FULL_SIZE, dataIntv, PRNT_NAME, PRNT_VALUE, *bValue1);
if(holdvalues == false){
epd->print(unit1); // Unit
}
else{
epd->print(unit1old);
} }
// Switch font if format for any values } else if (pageMode == BOTH) { // show data value and chart
if(bvalue1->getFormat() == "formatLatitude" || bvalue1->getFormat() == "formatLongitude"){ showData(bValue1, HALF);
epd->setFont(&Ubuntu_Bold20pt8b); if (dataChart) {
epd->setCursor(20, 180); dataChart->showChrt(HORIZONTAL, HALF_SIZE_BOTTOM, dataIntv, NO_PRNT_NAME, NO_PRNT_VALUE, *bValue1);
} }
else if(bvalue1->getFormat() == "formatTime" || bvalue1->getFormat() == "formatDate"){
epd->setFont(&Ubuntu_Bold32pt8b);
epd->setCursor(20, 200);
}
else{
epd->setFont(&DSEG7Classic_BoldItalic60pt7b);
epd->setCursor(20, 240);
}
// Show bus data
if(holdvalues == false){
epd->print(svalue1); // Real value as formated string
}
else{
epd->print(svalue1old); // Old value as formated string
}
if(valid1 == true){
svalue1old = svalue1; // Save the old value
unit1old = unit1; // Save the old unit
} }
return PAGE_UPDATE; return PAGE_UPDATE;
}; };
}; };
static Page* createPage(CommonData &common){ static Page* createPage(CommonData& common)
{
return new PageOneValue(common); return new PageOneValue(common);
} }

View File

@@ -1,4 +1,3 @@
// SPDX-License-Identifier: GPL-2.0-or-later
#if defined BOARD_OBP60S3 || defined BOARD_OBP40S3 #if defined BOARD_OBP60S3 || defined BOARD_OBP40S3
#include "Pagedata.h" #include "Pagedata.h"
@@ -6,30 +5,14 @@
class PageRollPitch : public Page class PageRollPitch : public Page
{ {
private:
String lengthformat;
int rolllimit;
String roffset;
double rolloffset;
String poffset;
double pitchoffset;
public: public:
PageRollPitch(CommonData &common) : Page(common) PageRollPitch(CommonData &common){
{ commonData = &common;
logger->logDebug(GwLog::LOG, "Instantiate PageRollPitch"); common.logger->logDebug(GwLog::LOG,"Instantiate PageRollPitch");
// Get config data
String lengthformat = config->getString(config->lengthFormat);
rolllimit = config->getInt(config->rollLimit);
roffset = config->getString(config->rollOffset);
rolloffset = roffset.toFloat() / 360 * (2 * M_PI);
poffset = config->getString(config->pitchOffset);
pitchoffset = poffset.toFloat() / 360 * (2 * M_PI);
} }
// Key functions // Key functions
int handleKey(int key){ virtual int handleKey(int key){
// Code for keylock // Code for keylock
if(key == 11){ if(key == 11){
commonData->keylock = !commonData->keylock; commonData->keylock = !commonData->keylock;
@@ -39,6 +22,8 @@ public:
} }
int displayPage(PageData &pageData){ int displayPage(PageData &pageData){
GwConfigHandler *config = commonData->config;
GwLog *logger = commonData->logger;
double value1 = 0; double value1 = 0;
double value2 = 0; double value2 = 0;
@@ -47,6 +32,19 @@ public:
String svalue2 = ""; String svalue2 = "";
String svalue2old = ""; String svalue2old = "";
// 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);
int rolllimit = config->getInt(config->rollLimit);
String roffset = config->getString(config->rollOffset);
double rolloffset = roffset.toFloat()/360*(2*M_PI);
String poffset = config->getString(config->pitchOffset);
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)
String name1 = xdrDelete(bvalue1->getName()); // Value name String name1 = xdrDelete(bvalue1->getName()); // Value name
@@ -99,70 +97,71 @@ public:
} }
// Optical warning by limit violation // Optical warning by limit violation
if (flashLED == "Limit Violation") { if(String(flashLED) == "Limit Violation"){
// Limits for roll // Limits for roll
if(value1*360/(2*M_PI) >= -1*rolllimit && value1*360/(2*M_PI) <= rolllimit){ if(value1*360/(2*M_PI) >= -1*rolllimit && value1*360/(2*M_PI) <= rolllimit){
setBlinkingLED(false); setBlinkingLED(false);
setFlashLED(false); setFlashLED(false);
} else { }
else{
setBlinkingLED(true); setBlinkingLED(true);
} }
} }
// Logging boat values // Logging boat values
if (bvalue1 == NULL) return PAGE_OK; // WTF why this statement? if (bvalue1 == NULL) return PAGE_OK; // WTF why this statement?
logger->logDebug(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
//*********************************************************** //***********************************************************
// Set display in partial refresh mode // Set display in partial refresh mode
epd->setPartialWindow(0, 0, epd->width(), epd->height()); // Set partial update getdisplay().setPartialWindow(0, 0, getdisplay().width(), getdisplay().height()); // Set partial update
epd->setTextColor(commonData->fgcolor); getdisplay().setTextColor(commonData->fgcolor);
// Show roll limit // Show roll limit
epd->setFont(&DSEG7Classic_BoldItalic20pt7b); getdisplay().setFont(&DSEG7Classic_BoldItalic20pt7b);
epd->setCursor(10, 65); getdisplay().setCursor(10, 65);
epd->print(rolllimit); // Value getdisplay().print(rolllimit); // Value
//epd->print(svalue1); // Value //getdisplay().print(svalue1); // Value
epd->setFont(&Ubuntu_Bold12pt8b); getdisplay().setFont(&Ubuntu_Bold12pt8b);
epd->setCursor(10, 95); getdisplay().setCursor(10, 95);
epd->print("Limit"); // Name getdisplay().print("Limit"); // Name
epd->setFont(&Ubuntu_Bold8pt8b); getdisplay().setFont(&Ubuntu_Bold8pt8b);
epd->setCursor(10, 115); getdisplay().setCursor(10, 115);
epd->print("DEG"); getdisplay().print("DEG");
// Horizintal separator left // Horizintal separator left
epd->fillRect(0, 149, 60, 3, commonData->fgcolor); getdisplay().fillRect(0, 149, 60, 3, commonData->fgcolor);
// Show roll value // Show roll value
epd->setFont(&DSEG7Classic_BoldItalic20pt7b); getdisplay().setFont(&DSEG7Classic_BoldItalic20pt7b);
epd->setCursor(10, 270); getdisplay().setCursor(10, 270);
if(holdvalues == false) epd->print(svalue1); // Value if(holdvalues == false) getdisplay().print(svalue1); // Value
else epd->print(svalue1old); else getdisplay().print(svalue1old);
epd->setFont(&Ubuntu_Bold12pt8b); getdisplay().setFont(&Ubuntu_Bold12pt8b);
epd->setCursor(10, 220); getdisplay().setCursor(10, 220);
epd->print(name1); // Name getdisplay().print(name1); // Name
epd->setFont(&Ubuntu_Bold8pt8b); getdisplay().setFont(&Ubuntu_Bold8pt8b);
epd->setCursor(10, 190); getdisplay().setCursor(10, 190);
epd->print("Deg"); getdisplay().print("Deg");
// Horizintal separator right // Horizintal separator right
epd->fillRect(340, 149, 80, 3, commonData->fgcolor); getdisplay().fillRect(340, 149, 80, 3, commonData->fgcolor);
// Show pitch value // Show pitch value
epd->setFont(&DSEG7Classic_BoldItalic20pt7b); getdisplay().setFont(&DSEG7Classic_BoldItalic20pt7b);
epd->setCursor(295, 270); getdisplay().setCursor(295, 270);
if(holdvalues == false) epd->print(svalue2); // Value if(holdvalues == false) getdisplay().print(svalue2); // Value
else epd->print(svalue2old); else getdisplay().print(svalue2old);
epd->setFont(&Ubuntu_Bold12pt8b); getdisplay().setFont(&Ubuntu_Bold12pt8b);
epd->setCursor(335, 220); getdisplay().setCursor(335, 220);
epd->print(name2); // Name getdisplay().print(name2); // Name
epd->setFont(&Ubuntu_Bold8pt8b); getdisplay().setFont(&Ubuntu_Bold8pt8b);
epd->setCursor(335, 190); getdisplay().setCursor(335, 190);
epd->print("Deg"); getdisplay().print("Deg");
//******************************************************************************************* //*******************************************************************************************
@@ -170,8 +169,8 @@ public:
int rInstrument = 100; // Radius of instrument int rInstrument = 100; // Radius of instrument
float pi = 3.141592; float pi = 3.141592;
epd->fillCircle(200, 150, rInstrument + 10, commonData->fgcolor); // Outer circle getdisplay().fillCircle(200, 150, rInstrument + 10, commonData->fgcolor); // Outer circle
epd->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)
{ {
@@ -195,17 +194,17 @@ public:
// Print text centered on position x, y // Print text centered on position x, y
int16_t x1, y1; // Return values of getTextBounds int16_t x1, y1; // Return values of getTextBounds
uint16_t w, h; // Return values of getTextBounds uint16_t w, h; // Return values of getTextBounds
epd->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
epd->setCursor(x-w/2, y+h/2); getdisplay().setCursor(x-w/2, y+h/2);
if(i % 20 == 0){ if(i % 20 == 0){
epd->setFont(&Ubuntu_Bold8pt8b); getdisplay().setFont(&Ubuntu_Bold8pt8b);
epd->print(ii); getdisplay().print(ii);
} }
// Draw sub scale with dots // Draw sub scale with dots
float x1c = 200 + rInstrument*sin(i/180.0*M_PI); float x1c = 200 + rInstrument*sin(i/180.0*M_PI);
float y1c = 150 - rInstrument*cos(i/180.0*M_PI); float y1c = 150 - rInstrument*cos(i/180.0*M_PI);
epd->fillCircle((int)x1c, (int)y1c, 2, commonData->fgcolor); getdisplay().fillCircle((int)x1c, (int)y1c, 2, commonData->fgcolor);
float sinx=sin(i/180.0*M_PI); float sinx=sin(i/180.0*M_PI);
float cosx=cos(i/180.0*M_PI); float cosx=cos(i/180.0*M_PI);
@@ -216,10 +215,10 @@ public:
float xx2 = +dx; float xx2 = +dx;
float yy1 = -(rInstrument-10); float yy1 = -(rInstrument-10);
float yy2 = -(rInstrument+10); float yy2 = -(rInstrument+10);
epd->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);
epd->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);
} }
@@ -240,7 +239,7 @@ public:
float xx2 = startwidth; float xx2 = startwidth;
float yy1 = -startwidth; float yy1 = -startwidth;
float yy2 = -(rInstrument * 0.7); float yy2 = -(rInstrument * 0.7);
epd->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
@@ -250,28 +249,28 @@ public:
float ix2 = -endwidth; float ix2 = -endwidth;
float iy1 = -(rInstrument * 0.7); float iy1 = -(rInstrument * 0.7);
float iy2 = -endwidth; float iy2 = -endwidth;
epd->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
epd->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
epd->fillCircle(200, 150, startwidth + 22, commonData->bgcolor); getdisplay().fillCircle(200, 150, startwidth + 22, commonData->bgcolor);
epd->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);
epd->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);
epd->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)
epd->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
@@ -284,7 +283,7 @@ public:
float xx2 = startwidth; float xx2 = startwidth;
float yy1 = -startwidth; float yy1 = -startwidth;
float yy2 = -(rInstrument - 15); float yy2 = -(rInstrument - 15);
epd->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
@@ -294,15 +293,15 @@ public:
float ix2 = -endwidth; float ix2 = -endwidth;
float iy1 = -(rInstrument - 15); float iy1 = -(rInstrument - 15);
float iy2 = -endwidth; float iy2 = -endwidth;
epd->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
epd->setFont(&Ubuntu_Bold8pt8b); getdisplay().setFont(&Ubuntu_Bold8pt8b);
epd->setCursor(145, 200); getdisplay().setCursor(145, 200);
epd->print("No sensor data"); // Info missing sensor getdisplay().print("No sensor data"); // Info missing sensor
} }
return PAGE_UPDATE; return PAGE_UPDATE;

View File

@@ -1,29 +1,18 @@
// SPDX-License-Identifier: GPL-2.0-or-later
#if defined BOARD_OBP60S3 || defined BOARD_OBP40S3 #if defined BOARD_OBP60S3 || defined BOARD_OBP40S3
#include "Pagedata.h" #include "Pagedata.h"
#include "OBP60Extensions.h" #include "OBP60Extensions.h"
#ifdef ENABLE_CALIBRATION
#include "BoatDataCalibration.h"
#endif
class PageRudderPosition : public Page class PageRudderPosition : public Page
{ {
private:
String lengthformat;
public: public:
PageRudderPosition(CommonData &common) : Page(common) PageRudderPosition(CommonData &common){
{ commonData = &common;
logger->logDebug(GwLog::LOG, "Instantiate PageRudderPosition"); common.logger->logDebug(GwLog::LOG,"Show PageRudderPosition");
// Get config data
String lengthformat = config->getString(config->lengthFormat);
} }
// Key functions // Key functions
int handleKey(int key) { virtual int handleKey(int key){
// Code for keylock // Code for keylock
if(key == 11){ if(key == 11){
commonData->keylock = !commonData->keylock; commonData->keylock = !commonData->keylock;
@@ -32,64 +21,68 @@ public:
return key; return key;
} }
void displayNew(PageData &pageData) {
#ifdef BOARD_OBP60S3
// Clear optical warning
if (flashLED == "Limit Violation") {
setBlinkingLED(false);
setFlashLED(false);
}
#endif
};
int displayPage(PageData &pageData){ int displayPage(PageData &pageData){
GwConfigHandler *config = commonData->config;
GwLog *logger = commonData->logger;
static String unit1old = ""; static String unit1old = "";
double value1 = 0.1; double value1 = 0.1;
double value1old = 0.1; double value1old = 0.1;
// 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 rudder position // Get boat values for rudder position
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
#ifdef ENABLE_CALIBRATION
calibrationData.calibrateInstance(bvalue1, logger); // Check if boat data value is to be calibrated
#endif
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 = commonData->fmt->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 = commonData->fmt->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 { } else {
if(simulation == true){ if(simulation == true){
value1 = (3 + float(random(0, 50)) / 10.0) / 360 * 2 * M_PI; value1 = (3 + float(random(0, 50)) / 10.0)/360*2*PI;
unit1 = "Deg"; unit1 = "Deg";
} else { }
else{
value1 = 0; value1 = 0;
} }
} }
// Log boat values // Optical warning by limit violation (unused)
logger->logDebug(GwLog::LOG, "Drawing at PageRudderPosition, %s:%f", name1.c_str(), value1); 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 PageRudderPosition, %s:%f", name1.c_str(), value1);
// Draw page // Draw page
//*********************************************************** //***********************************************************
// Set display in partial refresh mode // Set display in partial refresh mode
epd->setPartialWindow(0, 0, epd->width(), epd->height()); // Set partial update getdisplay().setPartialWindow(0, 0, getdisplay().width(), getdisplay().height()); // Set partial update
//******************************************************************************************* //*******************************************************************************************
// Draw RudderPosition // Draw RudderPosition
int rInstrument = 110; // Radius of RudderPosition int rInstrument = 110; // Radius of RudderPosition
const float pi = 3.141592; float pi = 3.141592;
epd->fillCircle(200, 150, rInstrument + 10, commonData->fgcolor); // Outer circle getdisplay().fillCircle(200, 150, rInstrument + 10, commonData->fgcolor); // Outer circle
epd->fillCircle(200, 150, rInstrument + 7, commonData->bgcolor); // Outer circle getdisplay().fillCircle(200, 150, rInstrument + 7, commonData->bgcolor); // Outer circle
epd->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)
{ {
@@ -97,7 +90,8 @@ 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;
@@ -116,17 +110,17 @@ public:
// Print text centered on position x, y // Print text centered on position x, y
int16_t x1, y1; // Return values of getTextBounds int16_t x1, y1; // Return values of getTextBounds
uint16_t w, h; // Return values of getTextBounds uint16_t w, h; // Return values of getTextBounds
epd->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
epd->setCursor(x-w/2, y+h/2); getdisplay().setCursor(x-w/2, y+h/2);
if(i % 30 == 0){ if(i % 30 == 0){
epd->setFont(&Ubuntu_Bold8pt8b); getdisplay().setFont(&Ubuntu_Bold8pt8b);
epd->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);
epd->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);
@@ -137,10 +131,10 @@ public:
float xx2 = +dx; float xx2 = +dx;
float yy1 = -(rInstrument-10); float yy1 = -(rInstrument-10);
float yy2 = -(rInstrument+10); float yy2 = -(rInstrument+10);
epd->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);
epd->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);
} }
@@ -148,28 +142,28 @@ public:
} }
// Print label // Print label
epd->setFont(&Ubuntu_Bold16pt8b); getdisplay().setFont(&Ubuntu_Bold16pt8b);
epd->setCursor(80, 70); getdisplay().setCursor(80, 70);
epd->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){
epd->setFont(&Ubuntu_Bold12pt8b); getdisplay().setFont(&Ubuntu_Bold12pt8b);
epd->setCursor(175, 110); getdisplay().setCursor(175, 110);
epd->print(unit1); // Unit getdisplay().print(unit1); // Unit
} }
else{ else{
epd->setFont(&Ubuntu_Bold12pt8b); getdisplay().setFont(&Ubuntu_Bold12pt8b);
epd->setCursor(175, 110); getdisplay().setCursor(175, 110);
epd->print(unit1old); // Unit getdisplay().print(unit1old); // Unit
} }
} }
else{ else{
// Print Unit of keel position // Print Unit of keel position
epd->setFont(&Ubuntu_Bold8pt8b); getdisplay().setFont(&Ubuntu_Bold8pt8b);
epd->setCursor(145, 110); getdisplay().setCursor(145, 110);
epd->print("No sensor data"); // Info missing sensor getdisplay().print("No sensor data"); // Info missing sensor
} }
// Calculate rudder position // Calculate rudder position
@@ -192,7 +186,7 @@ public:
float xx2 = startwidth; float xx2 = startwidth;
float yy1 = -startwidth; float yy1 = -startwidth;
float yy2 = -(rInstrument * 0.5); float yy2 = -(rInstrument * 0.5);
epd->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
@@ -202,14 +196,14 @@ public:
float ix2 = -endwidth; float ix2 = -endwidth;
float iy1 = -(rInstrument * 0.5); float iy1 = -(rInstrument * 0.5);
float iy2 = -endwidth; float iy2 = -endwidth;
epd->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
epd->fillCircle(200, 150, startwidth + 6, commonData->bgcolor); getdisplay().fillCircle(200, 150, startwidth + 6, commonData->bgcolor);
epd->fillCircle(200, 150, startwidth + 4, commonData->fgcolor); getdisplay().fillCircle(200, 150, startwidth + 4, commonData->fgcolor);
return PAGE_UPDATE; return PAGE_UPDATE;
}; };

View File

@@ -1,13 +1,8 @@
// SPDX-License-Identifier: GPL-2.0-or-later
#if defined BOARD_OBP60S3 || defined BOARD_OBP40S3 #if defined BOARD_OBP60S3 || defined BOARD_OBP40S3
#include "Pagedata.h" #include "Pagedata.h"
#include "OBP60Extensions.h" #include "OBP60Extensions.h"
#ifdef ENABLE_CALIBRATION
#include "BoatDataCalibration.h"
#endif
const int SixValues_x1 = 5; const int SixValues_x1 = 5;
const int SixValues_DeltaX = 200; const int SixValues_DeltaX = 200;
@@ -19,12 +14,12 @@ const int HowManyValues = 6;
class PageSixValues : public Page class PageSixValues : public Page
{ {
public: public:
PageSixValues(CommonData &common) : Page(common) PageSixValues(CommonData &common){
{ commonData = &common;
logger->logDebug(GwLog::LOG, "Instantiate PageSixValues"); common.logger->logDebug(GwLog::LOG,"Instantiate PageSixValues");
} }
int handleKey(int key) { virtual int handleKey(int key){
// Code for keylock // Code for keylock
if(key == 11){ if(key == 11){
commonData->keylock = !commonData->keylock; commonData->keylock = !commonData->keylock;
@@ -33,17 +28,10 @@ public:
return key; return key;
} }
void displayNew(PageData &pageData) {
#ifdef BOARD_OBP60S3
// Clear optical warning
if (flashLED == "Limit Violation") {
setBlinkingLED(false);
setFlashLED(false);
}
#endif
};
int displayPage(PageData &pageData){ int displayPage(PageData &pageData){
GwConfigHandler *config = commonData->config;
GwLog *logger = commonData->logger;
// Old values for hold function // Old values for hold function
static String OldDataText[HowManyValues] = {"", "", "", "", "", ""}; static String OldDataText[HowManyValues] = {"", "", "", "", "", ""};
@@ -68,29 +56,32 @@ public:
bvalue = pageData.values[i]; bvalue = pageData.values[i];
DataName[i] = xdrDelete(bvalue->getName()); DataName[i] = xdrDelete(bvalue->getName());
DataName[i] = DataName[i].substring(0, 6); // String length limit for value name DataName[i] = DataName[i].substring(0, 6); // String length limit for value name
#ifdef ENABLE_CALIBRATION
calibrationData.calibrateInstance(bvalue, logger); // Check if boat data value is to be calibrated
#endif
DataValue[i] = bvalue->value; // Value as double in SI unit DataValue[i] = bvalue->value; // Value as double in SI unit
DataValid[i] = bvalue->valid; DataValid[i] = bvalue->valid;
DataText[i] = commonData->fmt->formatValue(bvalue, *commonData).svalue; // Formatted value as string including unit conversion and switching decimal places DataText[i] = formatValue(bvalue, *commonData).svalue; // Formatted value as string including unit conversion and switching decimal places
DataUnits[i] = commonData->fmt->formatValue(bvalue, *commonData).unit; DataUnits[i] = formatValue(bvalue, *commonData).unit;
DataFormat[i] = bvalue->getFormat(); // Unit of value 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? if (bvalue == NULL) return PAGE_OK; // WTF why this statement?
// Draw page // Draw page
//*********************************************************** //***********************************************************
// Set display in partial refresh mode // Set display in partial refresh mode
epd->setPartialWindow(0, 0, epd->width(), epd->height()); // Set partial update getdisplay().setPartialWindow(0, 0, getdisplay().width(), getdisplay().height()); // Set partial update
epd->setTextColor(commonData->fgcolor); getdisplay().setTextColor(commonData->fgcolor);
for (int i = 0; i < ( HowManyValues / 2 ); i++){ 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 if (i < (HowManyValues / 2) - 1) { // Don't draw horizontal line after last line of values -> standard design
// Horizontal line 3 pix // Horizontal line 3 pix
epd->fillRect(0, SixValues_y1+(i+1)*SixValues_DeltaY, 400, 3, commonData->fgcolor); getdisplay().fillRect(0, SixValues_y1+(i+1)*SixValues_DeltaY, 400, 3, commonData->fgcolor);
} }
for (int j = 0; j < 2; j++){ for (int j = 0; j < 2; j++){
int ValueIndex = i * 2 + j; int ValueIndex = i * 2 + j;
@@ -99,67 +90,67 @@ public:
LOG_DEBUG(GwLog::LOG,"Drawing at PageSixValue: %d %s %f %s", ValueIndex, DataName[ValueIndex], DataValue[ValueIndex], DataFormat[ValueIndex] ); LOG_DEBUG(GwLog::LOG,"Drawing at PageSixValue: %d %s %f %s", ValueIndex, DataName[ValueIndex], DataValue[ValueIndex], DataFormat[ValueIndex] );
// Show name // Show name
epd->setFont(&Ubuntu_Bold12pt8b); getdisplay().setFont(&Ubuntu_Bold12pt8b);
epd->setCursor(x0, y0+25); getdisplay().setCursor(x0, y0+25);
epd->print(DataName[ValueIndex]); // Page name getdisplay().print(DataName[ValueIndex]); // Page name
// Show unit // Show unit
epd->setFont(&Ubuntu_Bold8pt8b); getdisplay().setFont(&Ubuntu_Bold8pt8b);
epd->setCursor(x0, y0+72); getdisplay().setCursor(x0, y0+72);
if(holdvalues == false){ if(holdvalues == false){
epd->print(DataUnits[ValueIndex]); // Unit getdisplay().print(DataUnits[ValueIndex]); // Unit
} else { }
epd->print(OldDataUnits[ValueIndex]); else{
getdisplay().print(OldDataUnits[ValueIndex]);
} }
// Switch font if format for any values // Switch font if format for any values
if(DataFormat[ValueIndex] == "formatLatitude" || DataFormat[ValueIndex] == "formatLongitude"){ if(DataFormat[ValueIndex] == "formatLatitude" || DataFormat[ValueIndex] == "formatLongitude"){
epd->setFont(&Ubuntu_Bold12pt8b); getdisplay().setFont(&Ubuntu_Bold12pt8b);
epd->setCursor(x0+10, y0+60); getdisplay().setCursor(x0+10, y0+60);
} }
else if(DataFormat[ValueIndex] == "formatTime" || DataFormat[ValueIndex] == "formatDate"){ else if(DataFormat[ValueIndex] == "formatTime" || DataFormat[ValueIndex] == "formatDate"){
epd->setFont(&Ubuntu_Bold16pt8b); getdisplay().setFont(&Ubuntu_Bold16pt8b);
epd->setCursor(x0+20,y0+55); getdisplay().setCursor(x0+20,y0+55);
} }
// pressure in hPa // pressure in hPa
else if(DataFormat[ValueIndex] == "formatXdr:P:P"){ else if(DataFormat[ValueIndex] == "formatXdr:P:P"){
epd->setFont(&DSEG7Classic_BoldItalic26pt7b); getdisplay().setFont(&DSEG7Classic_BoldItalic26pt7b);
epd->setCursor(x0+5, y0+70); getdisplay().setCursor(x0+5, y0+70);
} }
// RPM // RPM
else if(DataFormat[ValueIndex] == "formatXdr:T:R"){ else if(DataFormat[ValueIndex] == "formatXdr:T:R"){
epd->setFont(&DSEG7Classic_BoldItalic16pt7b); getdisplay().setFont(&DSEG7Classic_BoldItalic16pt7b);
epd->setCursor(x0+25, y0+70); getdisplay().setCursor(x0+25, y0+70);
} }
else{ else{
epd->setFont(&DSEG7Classic_BoldItalic26pt7b); getdisplay().setFont(&DSEG7Classic_BoldItalic26pt7b);
if (DataText[ValueIndex][0] == '-' ) { if ( DataText[ValueIndex][0] == '-' )
epd->setCursor(x0+25, y0+70); getdisplay().setCursor(x0+25, y0+70);
} else { else
epd->setCursor(x0+65, y0+70); getdisplay().setCursor(x0+65, y0+70);
}
} }
// Show bus data // Show bus data
if(holdvalues == false){ if(holdvalues == false){
epd->print(DataText[ValueIndex]); // Real value as formated string getdisplay().print(DataText[ValueIndex]); // Real value as formated string
} else{ }
epd->print(OldDataText[ValueIndex]); // Old value as formated string else{
getdisplay().print(OldDataText[ValueIndex]); // Old value as formated string
} }
if(DataValid[ValueIndex] == true){ if(DataValid[ValueIndex] == true){
OldDataText[ValueIndex] = DataText[ValueIndex]; // Save the old value OldDataText[ValueIndex] = DataText[ValueIndex]; // Save the old value
OldDataUnits[ValueIndex] = DataUnits[ValueIndex]; // Save the old unit OldDataUnits[ValueIndex] = DataUnits[ValueIndex]; // Save the old unit
} }
} // for j }
// Vertical line 3 pix // Vertical line 3 pix
epd->fillRect(SixValues_x1+SixValues_DeltaX-8, SixValues_y1+i*SixValues_DeltaY, 3, SixValues_DeltaY, commonData->fgcolor); getdisplay().fillRect(SixValues_x1+SixValues_DeltaX-8, SixValues_y1+i*SixValues_DeltaY, 3, SixValues_DeltaY, commonData->fgcolor);
} // for i }
return PAGE_UPDATE; return PAGE_UPDATE;
}; };
}; };
static Page *createPage(CommonData &common){ static Page *createPage(CommonData &common){
return new PageSixValues(common); return new PageSixValues(common);
}/** }/**

View File

@@ -14,15 +14,20 @@
class PageSkyView : public Page class PageSkyView : public Page
{ {
private: private:
String flashLED;
GwBoatData *bd; GwBoatData *bd;
public: public:
PageSkyView(CommonData &common) : Page(common) PageSkyView(CommonData &common)
{ {
commonData = &common;
// task name access is for example purpose only // task name access is for example purpose only
TaskHandle_t currentTaskHandle = xTaskGetCurrentTaskHandle(); TaskHandle_t currentTaskHandle = xTaskGetCurrentTaskHandle();
const char* taskName = pcTaskGetName(currentTaskHandle); const char* taskName = pcTaskGetName(currentTaskHandle);
logger->logDebug(GwLog::LOG, "Instantiate PageSkyView in task '%s'", taskName); common.logger->logDebug(GwLog::LOG, "Instantiate PageSkyView in task '%s'", taskName);
flashLED = common.config->getString(common.config->flashLED);
} }
int handleKey(int key) { int handleKey(int key) {
@@ -52,6 +57,7 @@ public:
} }
int displayPage(PageData &pageData) { int displayPage(PageData &pageData) {
GwLog *logger = commonData->logger;
std::vector<GwSatInfo> sats; std::vector<GwSatInfo> sats;
int nSat = bd->SatInfo->getNumSats(); int nSat = bd->SatInfo->getNumSats();
@@ -67,109 +73,111 @@ public:
//*********************************************************** //***********************************************************
// Set display in partial refresh mode // Set display in partial refresh mode
epd->setPartialWindow(0, 0, epd->width(), epd->height()); // Set partial update getdisplay().setPartialWindow(0, 0, getdisplay().width(), getdisplay().height()); // Set partial update
// current position // current position
epd->setFont(&Ubuntu_Bold8pt8b); getdisplay().setFont(&Ubuntu_Bold8pt8b);
GwApi::BoatValue *bv_lat = pageData.values[0];
String sv_lat = commonData->fmt->formatValue(bv_lat, *commonData).svalue;
//epd->setCursor(300, 40);
//epd->print(sv_lat);
GwApi::BoatValue *bv_lon = pageData.values[1];
String sv_lon = commonData->fmt->formatValue(bv_lon, *commonData).svalue;
//epd->setCursor(300, 60);
//epd->print(sv_lon);
GwApi::BoatValue *bv_hdop = pageData.values[2];
String sv_hdop = commonData->fmt->formatValue(bv_hdop, *commonData).svalue;
//epd->setCursor(300, 80);
//epd->print(sv_hdop);
// sky view // sky view
Point c = {130, 148}; Point c = {130, 148};
uint16_t r = 125; uint16_t r = 120;
uint16_t r1 = r / 2; uint16_t r1 = r / 2;
epd->fillCircle(c.x, c.y, r, commonData->bgcolor); getdisplay().fillCircle(c.x, c.y, r + 2, commonData->fgcolor);
epd->drawCircle(c.x, c.y, r + 1, commonData->fgcolor); getdisplay().fillCircle(c.x, c.y, r - 1, commonData->bgcolor);
epd->drawCircle(c.x, c.y, r + 2, commonData->fgcolor); getdisplay().drawCircle(c.x, c.y, r1, commonData->fgcolor);
epd->drawCircle(c.x, c.y, r1, commonData->fgcolor);
// separation lines // separation lines
epd->drawLine(c.x - r, c.y, c.x + r, c.y, commonData->fgcolor); getdisplay().drawLine(c.x - r, c.y, c.x + r, c.y, commonData->fgcolor);
epd->drawLine(c.x, c.y - r, c.x, c.y + r, commonData->fgcolor); getdisplay().drawLine(c.x, c.y - r, c.x, c.y + r, commonData->fgcolor);
Point p = {c.x, c.y - r}; Point p = {c.x, c.y - r};
Point p1, p2; Point p1, p2;
p1 = rotatePoint(c, p, 45); p1 = rotatePoint(c, p, 45);
p2 = rotatePoint(c, p, 45 + 180); p2 = rotatePoint(c, p, 45 + 180);
epd->drawLine(p1.x, p1.y, p2.x, p2.y, commonData->fgcolor); getdisplay().drawLine(p1.x, p1.y, p2.x, p2.y, commonData->fgcolor);
p1 = rotatePoint(c, p, -45); p1 = rotatePoint(c, p, -45);
p2 = rotatePoint(c, p, -45 + 180); p2 = rotatePoint(c, p, -45 + 180);
epd->drawLine(p1.x, p1.y, p2.x, p2.y, commonData->fgcolor); getdisplay().drawLine(p1.x, p1.y, p2.x, p2.y, commonData->fgcolor);
// directions // directions
int16_t x1, y1; int16_t x1, y1;
uint16_t w, h; uint16_t w, h;
epd->setFont(&Ubuntu_Bold12pt8b); getdisplay().setFont(&Ubuntu_Bold12pt8b);
epd->getTextBounds("N", 0, 150, &x1, &y1, &w, &h); getdisplay().getTextBounds("N", 0, 150, &x1, &y1, &w, &h);
epd->setCursor(c.x - w / 2, c.y - r + h + 2); getdisplay().setCursor(c.x - w / 2, c.y - r + h + 3);
epd->print("N"); getdisplay().print("N");
epd->getTextBounds("S", 0, 150, &x1, &y1, &w, &h); getdisplay().getTextBounds("S", 0, 150, &x1, &y1, &w, &h);
epd->setCursor(c.x - w / 2, c.y + r - 2); getdisplay().setCursor(c.x - w / 2, c.y + r - 3);
epd->print("S"); getdisplay().print("S");
epd->getTextBounds("E", 0, 150, &x1, &y1, &w, &h); getdisplay().getTextBounds("E", 0, 150, &x1, &y1, &w, &h);
epd->setCursor(c.x + r - w - 2, c.y + h / 2); getdisplay().setCursor(c.x + r - w - 3, c.y + h / 2);
epd->print("E"); getdisplay().print("E");
epd->getTextBounds("W", 0, 150, &x1, &y1, &w, &h); getdisplay().getTextBounds("W", 0, 150, &x1, &y1, &w, &h);
epd->setCursor(c.x - r + 2 , c.y + h / 2); getdisplay().setCursor(c.x - r + 3 , c.y + h / 2);
epd->print("W"); getdisplay().print("W");
// show satellites in "map" // show satellites in "map"
epd->setFont(&Atari6px); getdisplay().setFont(&IBM8x8px);
for (int i = 0; i < nSat; i++) { for (int i = 0; i < nSat; i++) {
float arad = sats[i].Azimut * M_PI / 180.0; float arad = (sats[i].Azimut * M_PI / 180.0) + M_PI;
float erad = sats[i].Elevation * M_PI / 180.0; float erad = sats[i].Elevation * M_PI / 180.0;
uint16_t x = c.x + sin(arad) * erad * r; uint16_t x = c.x + sin(arad) * erad * r1;
uint16_t y = c.y + cos(arad) * erad * r; uint16_t y = c.y + cos(arad) * erad * r1;
epd->drawRect(x-4, y-4, 8, 8, commonData->fgcolor); getdisplay().fillRect(x-4, y-4, 8, 8, commonData->fgcolor);
// Add Sat number getdisplay().setCursor(x-7, y+12);
epd->setCursor(x+5, y); getdisplay().printf("%02d", static_cast<int>(sats[i].PRN));
char buffer[3];
snprintf(buffer, 3, "%02d", static_cast<int>(sats[i].PRN));
epd->print(String(buffer));
} }
// Signal / Noise bars // Signal / Noise bars
epd->setFont(&Ubuntu_Bold8pt8b); getdisplay().setFont(&Ubuntu_Bold8pt8b);
epd->setCursor(325, 34); getdisplay().setCursor(325, 34);
epd->print("SNR"); getdisplay().print("SNR");
epd->drawRect(270, 20, 125, 257, commonData->fgcolor); // getdisplay().drawRect(270, 20, 125, 257, commonData->fgcolor);
int maxsat = std::min(nSat, 12); int maxsat = std::min(nSat, 12);
for (int i = 0; i < maxsat; i++) { for (int i = 0; i < maxsat; i++) {
uint16_t y = 29 + (i + 1) * 20; uint16_t y = 29 + (i + 1) * 20;
epd->setCursor(276, y); getdisplay().setCursor(276, y);
char buffer[3]; char buffer[3];
snprintf(buffer, 3, "%02d", static_cast<int>(sats[i].PRN)); snprintf(buffer, 3, "%02d", static_cast<int>(sats[i].PRN));
epd->print(String(buffer)); getdisplay().print(String(buffer));
epd->drawRect(305, y-12, 85, 14, commonData->fgcolor); getdisplay().drawRect(305, y-12, 85, 14, commonData->fgcolor);
epd->setCursor(315, y); getdisplay().setCursor(315, y);
// TODO SNR as number or as bar via mode key? // TODO SNR as number or as bar via mode key?
if (sats[i].SNR <= 100) { if (sats[i].SNR <= 100) {
// epd->print(sats[i].SNR); // getdisplay().print(sats[i].SNR);
epd->fillRect(307, y-10, int(81 * sats[i].SNR / 100.0), 10, commonData->fgcolor); getdisplay().fillRect(307, y-10, int(81 * sats[i].SNR / 100.0), 10, commonData->fgcolor);
} else { } else {
epd->print("n/a"); 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; return PAGE_UPDATE;
}; };
}; };
@@ -189,7 +197,7 @@ PageDescription registerPageSkyView(
"SkyView", // Page name "SkyView", // 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
{"LAT", "LON", "HDOP"}, // Bus values we need in the page {"SatInfo", "HDOP"}, // Bus values we need in the page
true // Show display header on/off true // Show display header on/off
); );

View File

@@ -1,4 +1,3 @@
// SPDX-License-Identifier: GPL-2.0-or-later
#if defined BOARD_OBP60S3 || defined BOARD_OBP40S3 #if defined BOARD_OBP60S3 || defined BOARD_OBP40S3
#include "Pagedata.h" #include "Pagedata.h"
@@ -7,23 +6,12 @@
class PageSolar : public Page class PageSolar : public Page
{ {
private:
String batVoltage;
int solPower;
String powerSensor;
public: public:
PageSolar(CommonData &common) : Page(common) PageSolar(CommonData &common){
{ commonData = &common;
logger->logDebug(GwLog::LOG, "Instantiate PageSolar"); common.logger->logDebug(GwLog::LOG,"Instantiate PageSolar");
// Get config data
String batVoltage = config->getString(config->batteryVoltage);
int solPower = config->getInt(config->solarPower);
String powerSensor = config->getString(config->usePowSensor2);
} }
virtual int handleKey(int key){
int handleKey(int key){
// Code for keylock // Code for keylock
if(key == 11){ if(key == 11){
commonData->keylock = !commonData->keylock; commonData->keylock = !commonData->keylock;
@@ -33,6 +21,17 @@ public:
} }
int displayPage(PageData &pageData){ int displayPage(PageData &pageData){
GwConfigHandler *config = commonData->config;
GwLog *logger = commonData->logger;
// Get config data
bool simulation = config->getBool(config->useSimuData);
bool holdvalues = config->getBool(config->holdvalues);
String flashLED = config->getString(config->flashLED);
String batVoltage = config->getString(config->batteryVoltage);
int solPower = config->getInt(config->solarPower);
String backlightMode = config->getString(config->backlight);
String powerSensor = config->getString(config->usePowSensor2);
double value1 = 0; // Solar voltage double value1 = 0; // Solar voltage
double value2 = 0; // Solar current double value2 = 0; // Solar current
@@ -60,92 +59,97 @@ public:
bool valid1 = true; bool valid1 = true;
// Optical warning by limit violation // Optical warning by limit violation
if (flashLED == "Limit Violation") { if(String(flashLED) == "Limit Violation"){
// Over voltage? // Over voltage
if (batVoltage == "12V") { if(value1 > 14.8 && batVoltage == "12V"){
setBlinkingLED(value1 > 14.8); setBlinkingLED(true);
} else if (batVoltage == "24V") { }
setBlinkingLED(value1 > 29.6); if(value1 <= 14.8 && batVoltage == "12V"){
} else { setBlinkingLED(false);
}
if(value1 > 29.6 && batVoltage == "24V"){
setBlinkingLED(true);
}
if(value1 <= 29.6 && batVoltage == "24V"){
setBlinkingLED(false); setBlinkingLED(false);
} }
} }
// Logging voltage value // Logging voltage value
logger->logDebug(GwLog::LOG, "Drawing at PageSolar, Type:%iW %s:=%f", solPower, name1.c_str(), value1); LOG_DEBUG(GwLog::LOG,"Drawing at PageSolar, Type:%iW %s:=%f", solPower, name1.c_str(), value1);
// Draw page // Draw page
//*********************************************************** //***********************************************************
// Set display in partial refresh mode // Set display in partial refresh mode
epd->setPartialWindow(0, 0, epd->width(), epd->height()); // Set partial update getdisplay().setPartialWindow(0, 0, getdisplay().width(), getdisplay().height()); // Set partial update
epd->setTextColor(commonData->fgcolor); getdisplay().setTextColor(commonData->fgcolor);
// Show name // Show name
epd->setFont(&Ubuntu_Bold20pt8b); getdisplay().setFont(&Ubuntu_Bold20pt8b);
epd->setCursor(10, 65); getdisplay().setCursor(10, 65);
epd->print("Solar"); getdisplay().print("Solar");
// Show voltage type // Show voltage type
epd->setFont(&DSEG7Classic_BoldItalic20pt7b); getdisplay().setFont(&DSEG7Classic_BoldItalic20pt7b);
epd->setCursor(10, 140); getdisplay().setCursor(10, 140);
int bvoltage = 0; int bvoltage = 0;
if(String(batVoltage) == "12V") bvoltage = 12; if(String(batVoltage) == "12V") bvoltage = 12;
else bvoltage = 24; else bvoltage = 24;
epd->print(bvoltage); getdisplay().print(bvoltage);
epd->setFont(&Ubuntu_Bold16pt8b); getdisplay().setFont(&Ubuntu_Bold16pt8b);
epd->print("V"); getdisplay().print("V");
// Show solar power // Show solar power
epd->setFont(&DSEG7Classic_BoldItalic20pt7b); getdisplay().setFont(&DSEG7Classic_BoldItalic20pt7b);
epd->setCursor(10, 200); getdisplay().setCursor(10, 200);
if(solPower <= 999) epd->print(solPower, 0); if(solPower <= 999) getdisplay().print(solPower, 0);
if(solPower > 999) epd->print(float(solPower/1000.0), 1); if(solPower > 999) getdisplay().print(float(solPower/1000.0), 1);
epd->setFont(&Ubuntu_Bold16pt8b); getdisplay().setFont(&Ubuntu_Bold16pt8b);
if(solPower <= 999) epd->print("W"); if(solPower <= 999) getdisplay().print("W");
if(solPower > 999) epd->print("kW"); if(solPower > 999) getdisplay().print("kW");
// Show info // Show info
epd->setFont(&Ubuntu_Bold8pt8b); getdisplay().setFont(&Ubuntu_Bold8pt8b);
epd->setCursor(10, 235); getdisplay().setCursor(10, 235);
epd->print("Installed"); getdisplay().print("Installed");
epd->setCursor(10, 255); getdisplay().setCursor(10, 255);
epd->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
epd->setFont(&DSEG7Classic_BoldItalic20pt7b); getdisplay().setFont(&DSEG7Classic_BoldItalic20pt7b);
epd->setCursor(150, 200); getdisplay().setCursor(150, 200);
epd->print(solPercentage); getdisplay().print(solPercentage);
epd->setFont(&Ubuntu_Bold16pt8b); getdisplay().setFont(&Ubuntu_Bold16pt8b);
epd->print("%"); getdisplay().print("%");
epd->setFont(&Ubuntu_Bold8pt8b); getdisplay().setFont(&Ubuntu_Bold8pt8b);
epd->setCursor(150, 235); getdisplay().setCursor(150, 235);
epd->print("Load"); getdisplay().print("Load");
// Show sensor type info // Show sensor type info
String i2cAddr = ""; String i2cAddr = "";
epd->setFont(&Ubuntu_Bold8pt8b); getdisplay().setFont(&Ubuntu_Bold8pt8b);
epd->setCursor(270, 60); getdisplay().setCursor(270, 60);
if(powerSensor == "off") epd->print("Internal"); if(powerSensor == "off") getdisplay().print("Internal");
if(powerSensor == "INA219"){ if(powerSensor == "INA219"){
epd->print("INA219"); getdisplay().print("INA219");
i2cAddr = " (0x" + String(INA219_I2C_ADDR2, HEX) + ")"; i2cAddr = " (0x" + String(INA219_I2C_ADDR2, HEX) + ")";
} }
if(powerSensor == "INA226"){ if(powerSensor == "INA226"){
epd->print("INA226"); getdisplay().print("INA226");
i2cAddr = " (0x" + String(INA226_I2C_ADDR2, HEX) + ")"; i2cAddr = " (0x" + String(INA226_I2C_ADDR2, HEX) + ")";
} }
epd->print(i2cAddr); getdisplay().print(i2cAddr);
epd->setCursor(270, 80); getdisplay().setCursor(270, 80);
epd->print("Sensor Modul"); getdisplay().print("Sensor Modul");
// Reading bus data or using simulation data // Reading bus data or using simulation data
epd->setFont(&DSEG7Classic_BoldItalic20pt7b); getdisplay().setFont(&DSEG7Classic_BoldItalic20pt7b);
epd->setCursor(260, 140); getdisplay().setCursor(260, 140);
if(simulation == true){ if(simulation == true){
if(batVoltage == "12V"){ if(batVoltage == "12V"){
value1 = 12.0; value1 = 12.0;
@@ -154,62 +158,46 @@ public:
value1 = 24.0; value1 = 24.0;
} }
value1 += float(random(0, 5)) / 10; // Simulation data value1 += float(random(0, 5)) / 10; // Simulation data
epd->print(value1,1); getdisplay().print(value1,1);
} }
else{ else{
// Check for valid real data, display also if hold values activated // Check for valid real data, display also if hold values activated
if(valid1 == true || holdvalues == true){ if(valid1 == true || holdvalues == true){
// Resolution switching // Resolution switching
if (value1 <= 9.9) { if(value1 <= 9.9) getdisplay().print(value1, 2);
epd->print(value1, 2); if(value1 > 9.9 && value1 <= 99.9)getdisplay().print(value1, 1);
} else if (value1 <= 99.9) { if(value1 > 99.9) getdisplay().print(value1, 0);
epd->print(value1, 1);
} else {
epd->print(value1, 0);
}
} }
else{ else{
epd->print(commonData->fmt->placeholder); // Missing bus data getdisplay().print("---"); // Missing bus data
} }
} }
epd->setFont(&Ubuntu_Bold16pt8b); getdisplay().setFont(&Ubuntu_Bold16pt8b);
epd->print("V"); getdisplay().print("V");
// Show actual current in A // Show actual current in A
epd->setFont(&DSEG7Classic_BoldItalic20pt7b); getdisplay().setFont(&DSEG7Classic_BoldItalic20pt7b);
epd->setCursor(260, 200); getdisplay().setCursor(260, 200);
if ((powerSensor == "INA219" || powerSensor == "INA226") && (simulation == false)) { if((powerSensor == "INA219" || powerSensor == "INA226") && simulation == false){
if (value2 <= 9.9) { if(value2 <= 9.9) getdisplay().print(value2, 2);
epd->print(value2, 2); if(value2 > 9.9 && value2 <= 99.9)getdisplay().print(value2, 1);
} else if (value2 <= 99.9) { if(value2 > 99.9) getdisplay().print(value2, 0);
epd->print(value2, 1);
} else {
epd->print(value2, 0);
} }
} else getdisplay().print("---");
else { getdisplay().setFont(&Ubuntu_Bold16pt8b);
epd->print(commonData->fmt->placeholder); getdisplay().print("A");
}
epd->setFont(&Ubuntu_Bold16pt8b);
epd->print("A");
// Show actual consumption in W // Show actual consumption in W
epd->setFont(&DSEG7Classic_BoldItalic20pt7b); getdisplay().setFont(&DSEG7Classic_BoldItalic20pt7b);
epd->setCursor(260, 260); getdisplay().setCursor(260, 260);
if ((powerSensor == "INA219" || powerSensor == "INA226") && (simulation == false)) { if((powerSensor == "INA219" || powerSensor == "INA226") && simulation == false){
if (value3 <= 9.9) { if(value3 <= 9.9) getdisplay().print(value3, 2);
epd->print(value3, 2); if(value3 > 9.9 && value3 <= 99.9)getdisplay().print(value3, 1);
} else if (value3 <= 99.9) { if(value3 > 99.9) getdisplay().print(value3, 0);
epd->print(value3, 1);
} else {
epd->print(value3, 0);
} }
} else getdisplay().print("---");
else { getdisplay().setFont(&Ubuntu_Bold16pt8b);
epd->print(commonData->fmt->placeholder); getdisplay().print("W");
}
epd->setFont(&Ubuntu_Bold16pt8b);
epd->print("W");
return PAGE_UPDATE; return PAGE_UPDATE;
}; };

File diff suppressed because it is too large Load Diff

View File

@@ -1,28 +1,17 @@
// SPDX-License-Identifier: GPL-2.0-or-later
#if defined BOARD_OBP60S3 || defined BOARD_OBP40S3 #if defined BOARD_OBP60S3 || defined BOARD_OBP40S3
#include "Pagedata.h" #include "Pagedata.h"
#include "OBP60Extensions.h" #include "OBP60Extensions.h"
#ifdef ENABLE_CALIBRATION
#include "BoatDataCalibration.h"
#endif
class PageThreeValues : public Page class PageThreeValues : public Page
{ {
private:
String lengthformat;
public: public:
PageThreeValues(CommonData &common) : Page(common) PageThreeValues(CommonData &common){
{ commonData = &common;
logger->logDebug(GwLog::LOG, "Instantiate PageThreeValue"); common.logger->logDebug(GwLog::LOG,"Instantiate PageThreeValue");
// Get config data
lengthformat = config->getString(config->lengthFormat);
} }
int handleKey(int key){ virtual int handleKey(int key){
// Code for keylock // Code for keylock
if(key == 11){ if(key == 11){
commonData->keylock = !commonData->keylock; commonData->keylock = !commonData->keylock;
@@ -31,17 +20,9 @@ public:
return key; return key;
} }
void displayNew(PageData &pageData) {
#ifdef BOARD_OBP60S3
// Clear optical warning
if (flashLED == "Limit Violation") {
setBlinkingLED(false);
setFlashLED(false);
}
#endif
};
int displayPage(PageData &pageData){ int displayPage(PageData &pageData){
GwConfigHandler *config = commonData->config;
GwLog *logger = commonData->logger;
// Old values for hold function // Old values for hold function
static String svalue1old = ""; static String svalue1old = "";
@@ -51,92 +32,94 @@ public:
static String svalue3old = ""; static String svalue3old = "";
static String unit3old = ""; static String unit3old = "";
// 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 #1 // Get boat values #1
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
#ifdef ENABLE_CALIBRATION
calibrationData.calibrateInstance(bvalue1, logger); // Check if boat data value is to be calibrated
#endif
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 = commonData->fmt->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 = commonData->fmt->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 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
#ifdef ENABLE_CALIBRATION
calibrationData.calibrateInstance(bvalue2, logger); // Check if boat data value is to be calibrated
#endif
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 = commonData->fmt->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 = commonData->fmt->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]; // Third element in list 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
#ifdef ENABLE_CALIBRATION
calibrationData.calibrateInstance(bvalue3, logger); // Check if boat data value is to be calibrated
#endif
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 = commonData->fmt->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 = commonData->fmt->formatValue(bvalue3, *commonData).unit; // Unit of value String unit3 = formatValue(bvalue3, *commonData).unit; // Unit of value
// Log boat values // Optical warning by limit violation (unused)
logger->logDebug(GwLog::LOG, "Drawing at PageThreeValues, %s: %f, %s: %f, %s: %f", if(String(flashLED) == "Limit Violation"){
name1.c_str(), value1, setBlinkingLED(false);
name2.c_str(), value2, setFlashLED(false);
name3.c_str(), value3); }
// Logging boat values
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);
// Draw page // Draw page
//*********************************************************** //***********************************************************
/// Set display in partial refresh mode /// Set display in partial refresh mode
epd->setPartialWindow(0, 0, epd->width(), epd->height()); // Set partial update getdisplay().setPartialWindow(0, 0, getdisplay().width(), getdisplay().height()); // Set partial update
// ############### Value 1 ################ // ############### Value 1 ################
// Show name // Show name
epd->setTextColor(commonData->fgcolor); getdisplay().setTextColor(commonData->fgcolor);
epd->setFont(&Ubuntu_Bold20pt8b); getdisplay().setFont(&Ubuntu_Bold20pt8b);
epd->setCursor(20, 55); getdisplay().setCursor(20, 55);
epd->print(name1); // Page name getdisplay().print(name1); // Page name
// Show unit // Show unit
epd->setFont(&Ubuntu_Bold12pt8b); getdisplay().setFont(&Ubuntu_Bold12pt8b);
epd->setCursor(20, 90); getdisplay().setCursor(20, 90);
if(holdvalues == false){ if(holdvalues == false){
epd->print(unit1); // Unit getdisplay().print(unit1); // Unit
} }
else{ else{
epd->print(unit1old); getdisplay().print(unit1old);
} }
// 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"){
epd->setFont(&Ubuntu_Bold20pt8b); getdisplay().setFont(&Ubuntu_Bold20pt8b);
epd->setCursor(50, 90); getdisplay().setCursor(50, 90);
} }
else if(bvalue1->getFormat() == "formatTime" || bvalue1->getFormat() == "formatDate"){ else if(bvalue1->getFormat() == "formatTime" || bvalue1->getFormat() == "formatDate"){
epd->setFont(&Ubuntu_Bold20pt8b); getdisplay().setFont(&Ubuntu_Bold20pt8b);
epd->setCursor(170, 68); getdisplay().setCursor(170, 68);
} }
else{ else{
epd->setFont(&DSEG7Classic_BoldItalic30pt7b); getdisplay().setFont(&DSEG7Classic_BoldItalic30pt7b);
epd->setCursor(180, 90); getdisplay().setCursor(180, 90);
} }
// Show bus data // Show bus data
if(holdvalues == false){ if(holdvalues == false){
epd->print(svalue1); // Real value as formated string getdisplay().print(svalue1); // Real value as formated string
} }
else{ else{
epd->print(svalue1old); // Old value as formated string getdisplay().print(svalue1old); // Old value as formated string
} }
if(valid1 == true){ if(valid1 == true){
svalue1old = svalue1; // Save the old value svalue1old = svalue1; // Save the old value
@@ -146,45 +129,45 @@ public:
// ############### Horizontal Line ################ // ############### Horizontal Line ################
// Horizontal line 3 pix // Horizontal line 3 pix
epd->fillRect(0, 105, 400, 3, commonData->fgcolor); getdisplay().fillRect(0, 105, 400, 3, commonData->fgcolor);
// ############### Value 2 ################ // ############### Value 2 ################
// Show name // Show name
epd->setFont(&Ubuntu_Bold20pt8b); getdisplay().setFont(&Ubuntu_Bold20pt8b);
epd->setCursor(20, 145); getdisplay().setCursor(20, 145);
epd->print(name2); // Page name getdisplay().print(name2); // Page name
// Show unit // Show unit
epd->setFont(&Ubuntu_Bold12pt8b); getdisplay().setFont(&Ubuntu_Bold12pt8b);
epd->setCursor(20, 180); getdisplay().setCursor(20, 180);
if(holdvalues == false){ if(holdvalues == false){
epd->print(unit2); // Unit getdisplay().print(unit2); // Unit
} }
else{ else{
epd->print(unit2old); getdisplay().print(unit2old);
} }
// 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"){
epd->setFont(&Ubuntu_Bold20pt8b); getdisplay().setFont(&Ubuntu_Bold20pt8b);
epd->setCursor(50, 180); getdisplay().setCursor(50, 180);
} }
else if(bvalue2->getFormat() == "formatTime" || bvalue2->getFormat() == "formatDate"){ else if(bvalue2->getFormat() == "formatTime" || bvalue2->getFormat() == "formatDate"){
epd->setFont(&Ubuntu_Bold20pt8b); getdisplay().setFont(&Ubuntu_Bold20pt8b);
epd->setCursor(170, 158); getdisplay().setCursor(170, 158);
} }
else{ else{
epd->setFont(&DSEG7Classic_BoldItalic30pt7b); getdisplay().setFont(&DSEG7Classic_BoldItalic30pt7b);
epd->setCursor(180, 180); getdisplay().setCursor(180, 180);
} }
// Show bus data // Show bus data
if(holdvalues == false){ if(holdvalues == false){
epd->print(svalue2); // Real value as formated string getdisplay().print(svalue2); // Real value as formated string
} }
else{ else{
epd->print(svalue2old); // Old value as formated string getdisplay().print(svalue2old); // Old value as formated string
} }
if(valid2 == true){ if(valid2 == true){
svalue2old = svalue2; // Save the old value svalue2old = svalue2; // Save the old value
@@ -194,45 +177,45 @@ public:
// ############### Horizontal Line ################ // ############### Horizontal Line ################
// Horizontal line 3 pix // Horizontal line 3 pix
epd->fillRect(0, 195, 400, 3, commonData->fgcolor); getdisplay().fillRect(0, 195, 400, 3, commonData->fgcolor);
// ############### Value 3 ################ // ############### Value 3 ################
// Show name // Show name
epd->setFont(&Ubuntu_Bold20pt8b); getdisplay().setFont(&Ubuntu_Bold20pt8b);
epd->setCursor(20, 235); getdisplay().setCursor(20, 235);
epd->print(name3); // Page name getdisplay().print(name3); // Page name
// Show unit // Show unit
epd->setFont(&Ubuntu_Bold12pt8b); getdisplay().setFont(&Ubuntu_Bold12pt8b);
epd->setCursor(20, 270); getdisplay().setCursor(20, 270);
if(holdvalues == false){ if(holdvalues == false){
epd->print(unit3); // Unit getdisplay().print(unit3); // Unit
} }
else{ else{
epd->print(unit3old); getdisplay().print(unit3old);
} }
// 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"){
epd->setFont(&Ubuntu_Bold20pt8b); getdisplay().setFont(&Ubuntu_Bold20pt8b);
epd->setCursor(50, 270); getdisplay().setCursor(50, 270);
} }
else if(bvalue3->getFormat() == "formatTime" || bvalue3->getFormat() == "formatDate"){ else if(bvalue3->getFormat() == "formatTime" || bvalue3->getFormat() == "formatDate"){
epd->setFont(&Ubuntu_Bold20pt8b); getdisplay().setFont(&Ubuntu_Bold20pt8b);
epd->setCursor(170, 248); getdisplay().setCursor(170, 248);
} }
else{ else{
epd->setFont(&DSEG7Classic_BoldItalic30pt7b); getdisplay().setFont(&DSEG7Classic_BoldItalic30pt7b);
epd->setCursor(180, 270); getdisplay().setCursor(180, 270);
} }
// Show bus data // Show bus data
if(holdvalues == false){ if(holdvalues == false){
epd->print(svalue3); // Real value as formated string getdisplay().print(svalue3); // Real value as formated string
} }
else{ else{
epd->print(svalue3old); // Old value as formated string getdisplay().print(svalue3old); // Old value as formated string
} }
if(valid3 == true){ if(valid3 == true){
svalue3old = svalue3; // Save the old value svalue3old = svalue3; // Save the old value

View File

@@ -1,121 +0,0 @@
// SPDX-License-Identifier: GPL-2.0-or-later
#if defined BOARD_OBP60S3 || defined BOARD_OBP40S3
#include "Pagedata.h"
#include "OBP60Extensions.h"
/*
Tracker
- standalone with SD card backend
- standalone with server backend
- Regatta Hero integration
*/
class PageTracker : public Page
{
private:
char mode = 'N'; // (N)ormal, (C)onfig
void displayModeNormal(PageData &pageData) {
// TBD Boatvalues: ...
logger->logDebug(GwLog::DEBUG,"Drawing at PageTracker");
// Title
epd->setTextColor(commonData->fgcolor);
epd->setFont(&Ubuntu_Bold12pt8b);
epd->setCursor(8, 48);
epd->print("Tracker");
}
void displayModeConfig() {
epd->setTextColor(commonData->fgcolor);
epd->setFont(&Ubuntu_Bold12pt8b);
epd->setCursor(8, 48);
epd->print("Tracker configuration");
epd->setFont(&Ubuntu_Bold8pt8b);
// TODO
}
public:
PageTracker(CommonData &common) : Page(common)
{
logger->logDebug(GwLog::LOG, "Instantiate PageTracker");
}
void setupKeys(){
Page::setupKeys();
commonData->keydata[0].label = "START";
commonData->keydata[1].label = "STOP";
}
int handleKey(int key){
if (key == 1) { // Switch between normal and config mode
if (mode == 'N') {
mode = 'C';
} else {
mode = 'N';
}
return 0;
}
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
};
int displayPage(PageData &pageData){
// Logging boat values
logger->logDebug(GwLog::LOG, "Drawing at PageTracker; Mode=%c", mode);
// Set display in partial refresh mode
epd->setPartialWindow(0, 0, epd->width(), epd->height());
if (mode == 'N') {
displayModeNormal(pageData);
} else if (mode == 'C') {
displayModeConfig();
}
return PAGE_UPDATE;
};
};
static Page *createPage(CommonData &common){
return new PageTracker(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 registerPageTracker(
"Tracker", // Page name
createPage, // Action
0, // Number of bus values depends on selection in Web configuration
{"LAT", "LON"}, // Names of bus values undepends on selection in Web configuration (refer GwBoatData.h)
true // Show display header on/off
);
#endif

View File

@@ -1,37 +1,235 @@
// SPDX-License-Identifier: GPL-2.0-or-later
#if defined BOARD_OBP60S3 || defined BOARD_OBP40S3 #if defined BOARD_OBP60S3 || defined BOARD_OBP40S3
#include "Pagedata.h" #include "Pagedata.h"
#include "OBP60Extensions.h" #include "OBP60Extensions.h"
#include "OBPDataOperations.h"
#include "OBPcharts.h"
#ifdef ENABLE_CALIBRATION class PageTwoValues : public Page {
#include "BoatDataCalibration.h"
#endif
class PageTwoValues : public Page
{
private: private:
String lengthformat; GwLog* logger;
public: enum PageMode {
PageTwoValues(CommonData &common) : Page(common) VALUES,
VAL1_CHART,
VAL2_CHART,
CHARTS
};
enum DisplayMode {
FULL,
HALF
};
static constexpr char HORIZONTAL = 'H';
static constexpr char VERTICAL = 'V';
static constexpr int8_t FULL_SIZE = 0;
static constexpr int8_t HALF_SIZE_TOP = 1;
static constexpr int8_t HALF_SIZE_BOTTOM = 2;
static constexpr bool PRNT_NAME = true;
static constexpr bool NO_PRNT_NAME = false;
static constexpr bool PRNT_VALUE = true;
static constexpr bool NO_PRNT_VALUE = false;
static constexpr int YOFFSET = 130; // y offset for display of 2nd boat value
int width; // Screen width
int height; // Screen height
bool keylock = false; // Keylock
PageMode pageMode = VALUES; // Page display mode
int8_t 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
// String lengthformat;
bool useSimuData;
bool holdValues;
String flashLED;
String backlightMode;
String tempFormat;
// Data buffer pointer (owned by HstryBuffers)
static constexpr int NUMVALUES = 2; // two data values in this page
RingBuffer<uint16_t>* dataHstryBuf[NUMVALUES] = { nullptr };
std::unique_ptr<Chart> dataChart[NUMVALUES]; // Chart object
// Old values for hold function
String sValueOld[NUMVALUES] = { "", "" };
String unitOld[NUMVALUES] = { "", "" };
// display data values in display <mode> [FULL|HALF]
void showData(const std::vector<GwApi::BoatValue*>& bValue, DisplayMode mode)
{ {
logger->logDebug(GwLog::LOG, "Instantiate PageTwoValue"); getdisplay().setTextColor(commonData->fgcolor);
// Get config data int numValues = bValue.size(); // do we have to handle 1 or 2 values?
lengthformat = config->getString(config->lengthFormat);
for (int i = 0; i < numValues; i++) {
int yOffset = YOFFSET * i;
String name = xdrDelete(bValue[i]->getName()); // Value name
name = name.substring(0, 6); // String length limit for value name
double value = bValue[i]->value; // Value as double in SI unit
bool valid = bValue[i]->valid; // Valid information
String sValue = formatValue(bValue[i], *commonData).svalue; // Formatted value as string including unit conversion and switching decimal places
String unit = formatValue(bValue[i], *commonData).unit; // Unit of value
// Show name
getdisplay().setFont(&Ubuntu_Bold20pt8b);
getdisplay().setCursor(20, 75 + yOffset);
getdisplay().print(name); // name
// Show unit
getdisplay().setFont(&Ubuntu_Bold12pt8b);
getdisplay().setCursor(20, 125 + yOffset);
if (holdValues) {
getdisplay().print(unitOld[i]); // name
} else {
getdisplay().print(unit); // name
} }
int handleKey(int key){ // Switch font depending on value format and adjust position
// Code for keylock if (bValue[i]->getFormat() == "formatLatitude" || bValue[i]->getFormat() == "formatLongitude") {
if(key == 11){ getdisplay().setFont(&Ubuntu_Bold20pt8b);
getdisplay().setCursor(50, 125 + yOffset);
} else if (bValue[i]->getFormat() == "formatTime" || bValue[i]->getFormat() == "formatDate") {
getdisplay().setFont(&Ubuntu_Bold20pt8b);
getdisplay().setCursor(170, 105 + yOffset);
} else { // Default font for other formats
getdisplay().setFont(&DSEG7Classic_BoldItalic42pt7b);
getdisplay().setCursor(180, 125 + yOffset);
}
// Show bus data
if (!holdValues || useSimuData) {
getdisplay().print(sValue); // Real value as formated string
} else {
getdisplay().print(sValueOld[i]); // Old value as formated string
}
if (valid == true) {
sValueOld[i] = sValue; // Save the old value
unitOld[i] = unit; // Save the old unit
}
}
if (numValues == 2 && mode == FULL) { // print line only, if we want to show 2 data values
getdisplay().fillRect(0, 145, width, 3, commonData->fgcolor); // Horizontal line 3 pix
}
}
public:
PageTwoValues(CommonData& common)
{
commonData = &common;
logger = commonData->logger;
LOG_DEBUG(GwLog::LOG, "Instantiate PageTwoValues");
width = getdisplay().width(); // Screen width
height = getdisplay().height(); // Screen height
// Get config data
// lengthformat = commonData->config->getString(commonData->config->lengthFormat);
useSimuData = commonData->config->getBool(commonData->config->useSimuData);
holdValues = commonData->config->getBool(commonData->config->holdvalues);
flashLED = commonData->config->getString(commonData->config->flashLED);
backlightMode = commonData->config->getString(commonData->config->backlight);
tempFormat = commonData->config->getString(commonData->config->tempFormat); // [K|°C|°F]
}
virtual void setupKeys()
{
Page::setupKeys();
#if defined BOARD_OBP60S3
constexpr int ZOOM_KEY = 4;
#elif defined BOARD_OBP40S3
constexpr int ZOOM_KEY = 1;
#endif
if (dataHstryBuf[0] || dataHstryBuf[1]) { // show "Mode" key only if at least 1 chart supported boat data type is available
commonData->keydata[0].label = "MODE";
if (pageMode != VALUES) { // show "ZOOM" key only if chart is visible
commonData->keydata[ZOOM_KEY].label = "ZOOM";
} else {
commonData->keydata[ZOOM_KEY].label = "";
}
} else {
commonData->keydata[0].label = "";
commonData->keydata[ZOOM_KEY].label = "";
}
}
// Key functions
virtual int handleKey(int key)
{
if (dataHstryBuf[0] || dataHstryBuf[1]) { // if at least 1 boat data type supports charts
// Set page mode: value | value/half chart | full charts
if (key == 1) {
switch (pageMode) {
case VALUES:
if (dataHstryBuf[0]) {
pageMode = VAL1_CHART;
} else if (dataHstryBuf[1]) {
pageMode = VAL2_CHART;
}
break;
case VAL1_CHART:
if (dataHstryBuf[1]) {
pageMode = VAL2_CHART;
} else {
pageMode = CHARTS;
}
break;
case VAL2_CHART:
pageMode = CHARTS;
break;
case CHARTS:
pageMode = VALUES;
break;
}
setupKeys(); // Adjust key definition depending on <pageMode> and chart-supported boat data type
return 0; // Commit the key
}
// Set time frame to show for chart
#if defined BOARD_OBP60S3
if (key == 5 && pageMode != VALUES) {
#elif defined BOARD_OBP40S3
if (key == 2 && pageMode != VALUES) {
#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; commonData->keylock = !commonData->keylock;
return 0; // Commit the key return 0; // Commit the key
} }
return key; return key;
} }
void displayNew(PageData &pageData) { virtual void displayNew(PageData& pageData)
{
#ifdef BOARD_OBP60S3 #ifdef BOARD_OBP60S3
// Clear optical warning // Clear optical warning
if (flashLED == "Limit Violation") { if (flashLED == "Limit Violation") {
@@ -39,150 +237,96 @@ public:
setFlashLED(false); setFlashLED(false);
} }
#endif #endif
}; // buffer initialization will fail, if page is default page, because <displayNew> is not executed at system start for default page
for (int i = 0; i < NUMVALUES; i++) {
if (!dataChart[i]) { // Create chart objects if they don't exist
int displayPage(PageData &pageData) { GwApi::BoatValue* bValue = pageData.values[i]; // Page boat data element
String bValName = bValue->getName(); // Value name
String bValFormat = bValue->getFormat(); // Value format
// Old values for hold function dataHstryBuf[i] = pageData.hstryBuffers->getBuffer(bValName);
static String svalue1old = "";
static String unit1old = "";
static String svalue2old = "";
static String unit2old = "";
if (dataHstryBuf[i]) {
dataChart[i].reset(new Chart(*dataHstryBuf[i], Chart::dfltChrtDta[bValFormat].range, *commonData, useSimuData));
LOG_DEBUG(GwLog::DEBUG, "PageTwoValues: Created chart object%d for %s", i, bValName.c_str());
} else {
LOG_DEBUG(GwLog::DEBUG, "PageTwoValues: No chart object available for %s", bValName.c_str());
}
}
}
// Get boat values #1 setupKeys(); // Adjust key definition depending on <pageMode> and chart-supported boat data type
GwApi::BoatValue *bvalue1 = pageData.values[0]; // First element in list (only one value by PageOneValue) }
String name1 = xdrDelete(bvalue1->getName()); // Value name
name1 = name1.substring(0, 6); // String length limit for value name
#ifdef ENABLE_CALIBRATION
calibrationData.calibrateInstance(bvalue1, logger); // Check if boat data value is to be calibrated
#endif
double value1 = bvalue1->value; // Value as double in SI unit
bool valid1 = bvalue1->valid; // Valid information
String svalue1 = commonData->fmt->formatValue(bvalue1, *commonData).svalue; // Formatted value as string including unit conversion and switching decimal places
String unit1 = commonData->fmt->formatValue(bvalue1, *commonData).unit; // Unit of value
// Get boat values #2 int displayPage(PageData& pageData)
GwApi::BoatValue *bvalue2 = pageData.values[1]; // Second element in list {
String name2 = xdrDelete(bvalue2->getName()); // Value name LOG_DEBUG(GwLog::LOG, "Display PageTwoValues");
name2 = name2.substring(0, 6); // String length limit for value name
#ifdef ENABLE_CALIBRATION
calibrationData.calibrateInstance(bvalue2, logger); // Check if boat data value is to be calibrated
#endif
double value2 = bvalue2->value; // Value as double in SI unit
bool valid2 = bvalue2->valid; // Valid information
String svalue2 = commonData->fmt->formatValue(bvalue2, *commonData).svalue; // Formatted value as string including unit conversion and switching decimal places
String unit2 = commonData->fmt->formatValue(bvalue2, *commonData).unit; // Unit of value
// Logging boat values // Get boat values for page
if (bvalue1 == NULL) return PAGE_OK; // WTF why this statement? std::vector<GwApi::BoatValue*> bValue;
logger->logDebug(GwLog::LOG, "Drawing at PageTwoValues, %s: %f, %s: %f", name1.c_str(), value1, name2.c_str(), value2); bValue.push_back(pageData.values[0]); // Page boat data element 1
bValue.push_back(pageData.values[1]); // Page boat data element 2
// Optical warning by limit violation (unused)
if (String(flashLED) == "Limit Violation") {
setBlinkingLED(false);
setFlashLED(false);
}
if (bValue[0] == NULL && bValue[1] == NULL)
return PAGE_OK; // no data, no page to display
LOG_DEBUG(GwLog::DEBUG, "PageTwoValues: printing #1: %s, %.3f, #2: %s, %.3f",
bValue[0]->getName().c_str(), bValue[0]->value, bValue[1]->getName().c_str(), bValue[1]->value);
// Draw page // Draw page
//*********************************************************** //***********************************************************
// Set display in partial refresh mode getdisplay().setPartialWindow(0, 0, width, height); // Set partial update
epd->setPartialWindow(0, 0, epd->width(), epd->height()); // Set partial update
// ############### Value 1 ################ if (pageMode == VALUES || (dataHstryBuf[0] == nullptr && dataHstryBuf[1] == nullptr)) {
// show only data value; ignore other pageMode options if no chart supported boat data history buffer is available
showData(bValue, FULL);
// Show name } else if (pageMode == VAL1_CHART) { // show data value 1 and chart
epd->setTextColor(commonData->fgcolor); showData({ bValue[0] }, HALF);
epd->setFont(&Ubuntu_Bold20pt8b); if (dataChart[0]) {
epd->setCursor(20, 80); dataChart[0]->showChrt(HORIZONTAL, HALF_SIZE_BOTTOM, dataIntv, NO_PRNT_NAME, NO_PRNT_VALUE, *bValue[0]);
epd->print(name1); // Page name
// Show unit
epd->setFont(&Ubuntu_Bold12pt8b);
epd->setCursor(20, 130);
if(holdvalues == false){
epd->print(unit1); // Unit
}
else{
epd->print(unit1old);
} }
// Switch font if format for any values } else if (pageMode == VAL2_CHART) { // show data value 2 and chart
if(bvalue1->getFormat() == "formatLatitude" || bvalue1->getFormat() == "formatLongitude"){ showData({ bValue[1] }, HALF);
epd->setFont(&Ubuntu_Bold20pt8b); if (dataChart[1]) {
epd->setCursor(50, 130); dataChart[1]->showChrt(HORIZONTAL, HALF_SIZE_BOTTOM, dataIntv, NO_PRNT_NAME, NO_PRNT_VALUE, *bValue[1]);
}
else if(bvalue1->getFormat() == "formatTime" || bvalue1->getFormat() == "formatDate"){
epd->setFont(&Ubuntu_Bold20pt8b);
epd->setCursor(170, 105);
}
else{
epd->setFont(&DSEG7Classic_BoldItalic42pt7b);
epd->setCursor(180, 130);
} }
// Show bus data } else if (pageMode == CHARTS) { // show both data charts
if(holdvalues == false){ if (dataChart[0]) {
epd->print(svalue1); // Real value as formated string if (dataChart[1]) {
dataChart[0]->showChrt(HORIZONTAL, HALF_SIZE_TOP, dataIntv, PRNT_NAME, PRNT_VALUE, *bValue[0]);
} else {
dataChart[0]->showChrt(HORIZONTAL, FULL_SIZE, dataIntv, PRNT_NAME, PRNT_VALUE, *bValue[0]);
} }
else{
epd->print(svalue1old); // Old value as formated string
} }
if(valid1 == true){ if (dataChart[1]) {
svalue1old = svalue1; // Save the old value if (dataChart[0]) {
unit1old = unit1; // Save the old unit dataChart[1]->showChrt(HORIZONTAL, HALF_SIZE_BOTTOM, dataIntv, PRNT_NAME, PRNT_VALUE, *bValue[1]);
} else {
dataChart[1]->showChrt(HORIZONTAL, FULL_SIZE, dataIntv, PRNT_NAME, PRNT_VALUE, *bValue[1]);
} }
// ############### Horizontal Line ################
// Horizontal line 3 pix
epd->fillRect(0, 145, 400, 3, commonData->fgcolor);
// ############### Value 2 ################
// Show name
epd->setFont(&Ubuntu_Bold20pt8b);
epd->setCursor(20, 190);
epd->print(name2); // Page name
// Show unit
epd->setFont(&Ubuntu_Bold12pt8b);
epd->setCursor(20, 240);
if(holdvalues == false){
epd->print(unit2); // Unit
} }
else{
epd->print(unit2old);
}
// Switch font if format for any values
if(bvalue2->getFormat() == "formatLatitude" || bvalue2->getFormat() == "formatLongitude"){
epd->setFont(&Ubuntu_Bold20pt8b);
epd->setCursor(50, 240);
}
else if(bvalue2->getFormat() == "formatTime" || bvalue2->getFormat() == "formatDate"){
epd->setFont(&Ubuntu_Bold20pt8b);
epd->setCursor(170, 215);
}
else{
epd->setFont(&DSEG7Classic_BoldItalic42pt7b);
epd->setCursor(180, 240);
}
// Show bus data
if(holdvalues == false){
epd->print(svalue2); // Real value as formated string
}
else{
epd->print(svalue2old); // Old value as formated string
}
if(valid2 == true){
svalue2old = svalue2; // Save the old value
unit2old = unit2; // Save the old unit
} }
return PAGE_UPDATE; return PAGE_UPDATE;
}; };
}; };
static Page *createPage(CommonData &common){ static Page* createPage(CommonData& common)
{
return new PageTwoValues(common); return new PageTwoValues(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

View File

@@ -1,4 +1,3 @@
// SPDX-License-Identifier: GPL-2.0-or-later
#if defined BOARD_OBP60S3 || defined BOARD_OBP40S3 #if defined BOARD_OBP60S3 || defined BOARD_OBP40S3
#include "Pagedata.h" #include "Pagedata.h"
@@ -7,9 +6,6 @@
class PageVoltage : public Page class PageVoltage : public Page
{ {
private:
String batVoltage;
String batType;
bool init = false; // Marker for init done bool init = false; // Marker for init done
uint8_t average = 0; // Average type [0...3], 0=off, 1=10s, 2=60s, 3=300s uint8_t 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
@@ -17,14 +13,9 @@ private:
char mode = 'D'; // display mode (A)nalog | (D)igital char mode = 'D'; // display mode (A)nalog | (D)igital
public: public:
PageVoltage(CommonData &common) : Page(common) PageVoltage(CommonData &common){
{ commonData = &common;
logger->logDebug(GwLog::LOG, "Instantiate PageVoltage"); common.logger->logDebug(GwLog::LOG,"Instantiate PageVoltage");
// Get config data
batVoltage = config->getString(config->batteryVoltage);
batType = config->getString(config->batteryType);
if (hasFRAM) { if (hasFRAM) {
average = fram.read(FRAM_VOLTAGE_AVG); average = fram.read(FRAM_VOLTAGE_AVG);
trend = fram.read(FRAM_VOLTAGE_TREND); trend = fram.read(FRAM_VOLTAGE_TREND);
@@ -32,18 +23,14 @@ public:
} }
} }
~PageVoltage(){ virtual void setupKeys(){
logger->logDebug(GwLog::LOG, "Destroy PageVoltage");
}
void setupKeys() {
Page::setupKeys(); Page::setupKeys();
commonData->keydata[0].label = "AVG"; commonData->keydata[0].label = "AVG";
commonData->keydata[1].label = "MODE"; commonData->keydata[1].label = "MODE";
commonData->keydata[4].label = "TRD"; commonData->keydata[4].label = "TRD";
} }
int handleKey(int key) { virtual int handleKey(int key){
// Change average // Change average
if(key == 1){ if(key == 1){
average ++; average ++;
@@ -79,41 +66,51 @@ public:
} }
void printAvg(int avg, uint16_t x, uint16_t y, bool prefix) { void printAvg(int avg, uint16_t x, uint16_t y, bool prefix) {
epd->setFont(&Ubuntu_Bold8pt8b); getdisplay().setFont(&Ubuntu_Bold8pt8b);
epd->setCursor(x, y); getdisplay().setCursor(x, y);
if (prefix) { if (prefix) {
epd->print("Avg: "); getdisplay().print("Avg: ");
} }
switch (average) { switch (average) {
case 0: case 0:
epd->print("1s"); getdisplay().print("1s");
break; break;
case 1: case 1:
epd->print("10s"); getdisplay().print("10s");
break; break;
case 2: case 2:
epd->print("60s"); getdisplay().print("60s");
break; break;
case 3: case 3:
epd->print("300s"); getdisplay().print("300s");
break; break;
default: default:
epd->print("1s"); getdisplay().print("1s");
break; break;
} }
} }
void printVoltageSymbol(uint16_t x, uint16_t y, uint16_t color) { void printVoltageSymbol(uint16_t x, uint16_t y, uint16_t color) {
epd->setFont(&Ubuntu_Bold16pt8b); getdisplay().setFont(&Ubuntu_Bold16pt8b);
epd->setCursor(x, y); getdisplay().setCursor(x, y);
epd->print("V"); getdisplay().print("V");
epd->fillRect(x, y + 6, 22, 3, color); getdisplay().fillRect(x, y + 6, 22, 3, color);
epd->fillRect(x, y + 11, 6, 3, color); getdisplay().fillRect(x, y + 11, 6, 3, color);
epd->fillRect(x + 8, y + 11, 6, 3, color); getdisplay().fillRect(x + 8, y + 11, 6, 3, color);
epd->fillRect(x + 16, y + 11, 6, 3, color); getdisplay().fillRect(x + 16, y + 11, 6, 3, color);
} }
int displayPage(PageData &pageData){ int displayPage(PageData &pageData){
GwConfigHandler *config = commonData->config;
GwLog *logger = commonData->logger;
// Get config data
bool simulation = config->getBool(config->useSimuData);
bool holdvalues = config->getBool(config->holdvalues);
String flashLED = config->getString(config->flashLED);
String batVoltage = config->getString(config->batteryVoltage);
String batType = config->getString(config->batteryType);
String backlightMode = config->getString(config->backlight);
double value1 = 0; double value1 = 0;
double valueTrend = 0; // Average over 10 values double valueTrend = 0; // Average over 10 values
@@ -154,76 +151,92 @@ public:
bool valid1 = true; bool valid1 = true;
// Optical warning by limit violation // Optical warning by limit violation
if (flashLED == "Limit Violation") { if(String(flashLED) == "Limit Violation"){
bool violation = false; // Limits for Pb battery
if (batType == "Pb") { if(String(batType) == "Pb" && (raw < 11.8 || raw > 14.8)){
violation = (raw < 11.8 || raw > 14.8);
} else if (batType == "Gel") {
violation = (raw < 11.8 || raw > 14.4);
} else if (batType == "AGM") {
violation = (raw < 11.8 || raw > 14.7);
} else if (batType == "LiFePo4") {
violation = (raw < 12.0 || raw > 14.6);
}
if (violation) {
setBlinkingLED(true); setBlinkingLED(true);
} else { }
if(String(batType) == "Pb" && (raw >= 11.8 && raw <= 14.8)){
setBlinkingLED(false);
setFlashLED(false);
}
// Limits for Gel battery
if(String(batType) == "Gel" && (raw < 11.8 || raw > 14.4)){
setBlinkingLED(true);
}
if(String(batType) == "Gel" && (raw >= 11.8 && raw <= 14.4)){
setBlinkingLED(false);
setFlashLED(false);
}
// Limits for AGM battery
if(String(batType) == "AGM" && (raw < 11.8 || raw > 14.7)){
setBlinkingLED(true);
}
if(String(batType) == "AGM" && (raw >= 11.8 && raw <= 14.7)){
setBlinkingLED(false);
setFlashLED(false);
}
// Limits for LiFePo4 battery
if(String(batType) == "LiFePo4" && (raw < 12.0 || raw > 14.6)){
setBlinkingLED(true);
}
if(String(batType) == "LiFePo4" && (raw >= 12.0 && raw <= 14.6)){
setBlinkingLED(false); setBlinkingLED(false);
setFlashLED(false); setFlashLED(false);
} }
} }
// Logging voltage value // Logging voltage value
logger->logDebug(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
//*********************************************************** //***********************************************************
// Set display in partial refresh mode // Set display in partial refresh mode
epd->setPartialWindow(0, 0, epd->width(), epd->height()); // Set partial update getdisplay().setPartialWindow(0, 0, getdisplay().width(), getdisplay().height()); // Set partial update
if (mode == 'D') { if (mode == 'D') {
// Display mode digital // Display mode digital
// Show name // Show name
epd->setTextColor(commonData->fgcolor); getdisplay().setTextColor(commonData->fgcolor);
epd->setFont(&Ubuntu_Bold32pt8b); getdisplay().setFont(&Ubuntu_Bold32pt8b);
epd->setCursor(20, 100); getdisplay().setCursor(20, 100);
epd->print(name1); // Value name getdisplay().print(name1); // Value name
#if defined BOARD_OBP40S3 && defined LIPO_ACCU_1200 && defined VOLTAGE_SENSOR #if defined BOARD_OBP40S3 && defined LIPO_ACCU_1200 && defined VOLTAGE_SENSOR
// Show charge status // Show charge status
epd->setFont(&Ubuntu_Bold8pt8b); getdisplay().setFont(&Ubuntu_Bold8pt8b);
epd->setCursor(185, 100); getdisplay().setCursor(185, 100);
if(commonData->data.BatteryChargeStatus == true){ if(commonData->data.BatteryChargeStatus == true){
epd->print("Charge"); getdisplay().print("Charge");
} }
else{ else{
epd->print("Discharge"); getdisplay().print("Discharge");
} }
#endif #endif
// Show unit // Show unit
epd->setFont(&Ubuntu_Bold20pt8b); getdisplay().setFont(&Ubuntu_Bold20pt8b);
epd->setCursor(270, 100); getdisplay().setCursor(270, 100);
epd->print("V"); getdisplay().print("V");
// Show battery type // Show battery type
epd->setFont(&Ubuntu_Bold8pt8b); getdisplay().setFont(&Ubuntu_Bold8pt8b);
epd->setCursor(295, 100); getdisplay().setCursor(295, 100);
#ifdef BOARD_OBP60S3 #ifdef BOARD_OBP60S3
epd->print(batType); getdisplay().print(batType);
#endif #endif
#if defined BOARD_OBP40S3 && defined LIPO_ACCU_1200 && defined VOLTAGE_SENSOR #if defined BOARD_OBP40S3 && defined LIPO_ACCU_1200 && defined VOLTAGE_SENSOR
epd->print("LiPo"); getdisplay().print("LiPo");
#endif #endif
// Show average settings // Show average settings
printAvg(average, 320, 84, true); printAvg(average, 320, 84, true);
// Reading bus data or using simulation data // Reading bus data or using simulation data
epd->setFont(&DSEG7Classic_BoldItalic60pt7b); getdisplay().setFont(&DSEG7Classic_BoldItalic60pt7b);
epd->setCursor(20, 240); getdisplay().setCursor(20, 240);
if(simulation == true){ if(simulation == true){
if(batVoltage == "12V"){ if(batVoltage == "12V"){
value1 = 12.0; value1 = 12.0;
@@ -232,30 +245,30 @@ public:
value1 = 24.0; value1 = 24.0;
} }
value1 += float(random(0, 5)) / 10; // Simulation data value1 += float(random(0, 5)) / 10; // Simulation data
epd->print(value1,1); getdisplay().print(value1,1);
} }
else{ else{
// Check for valid real data, display also if hold values activated // Check for valid real data, display also if hold values activated
if(valid1 == true || holdvalues == true){ if(valid1 == true || holdvalues == true){
// Resolution switching // Resolution switching
if(value1 < 10){ if(value1 < 10){
epd->print(value1,2); getdisplay().print(value1,2);
} }
if(value1 >= 10 && value1 < 100){ if(value1 >= 10 && value1 < 100){
epd->print(value1,1); getdisplay().print(value1,1);
} }
if(value1 >= 100){ if(value1 >= 100){
epd->print(value1,0); getdisplay().print(value1,0);
} }
} }
else{ else{
epd->print(commonData->fmt->placeholder); // Missing bus data getdisplay().print("---"); // Missing bus data
} }
} }
// Show trend indicator // Show trend indicator
if(trend == true){ if(trend == true){
epd->fillRect(315, 183, 35, 4, commonData->fgcolor); // Draw separator getdisplay().fillRect(315, 183, 35, 4, commonData->fgcolor); // Draw separator
if(int(raw * 10) > int(valueTrend * 10)){ if(int(raw * 10) > int(valueTrend * 10)){
displayTrendHigh(320, 174, 11, commonData->fgcolor); // Show high indicator displayTrendHigh(320, 174, 11, commonData->fgcolor); // Show high indicator
} }
@@ -276,9 +289,9 @@ public:
std::vector<Point> pts; std::vector<Point> pts;
// Instrument // Instrument
epd->drawCircleHelper(c.x, c.y, r + 2, 0x01, commonData->fgcolor); getdisplay().drawCircleHelper(c.x, c.y, r + 2, 0x01, commonData->fgcolor);
epd->drawCircleHelper(c.x, c.y, r + 1, 0x01, commonData->fgcolor); getdisplay().drawCircleHelper(c.x, c.y, r + 1, 0x01, commonData->fgcolor);
epd->drawCircleHelper(c.x, c.y, r , 0x01, commonData->fgcolor); getdisplay().drawCircleHelper(c.x, c.y, r , 0x01, commonData->fgcolor);
// Scale // Scale
// angle to voltage scale mapping // angle to voltage scale mapping
@@ -291,7 +304,7 @@ public:
{c.x - r + 12, c.y + 1}, {c.x - r + 12, c.y + 1},
{c.x - r, c.y + 1} {c.x - r, c.y + 1}
}; };
epd->setFont(&Ubuntu_Bold10pt8b); getdisplay().setFont(&Ubuntu_Bold10pt8b);
for (int angle = 3; angle < 90; angle += 3) { for (int angle = 3; angle < 90; angle += 3) {
if (angle % 15 == 0) { if (angle % 15 == 0) {
fillPoly4(rotatePoints(c, pts, angle), commonData->fgcolor); fillPoly4(rotatePoints(c, pts, angle), commonData->fgcolor);
@@ -301,7 +314,7 @@ public:
else { else {
p1 = rotatePoint(c, {c.x - r, c.y}, angle); p1 = rotatePoint(c, {c.x - r, c.y}, angle);
p2 = rotatePoint(c, {c.x - r + 6, c.y}, angle); p2 = rotatePoint(c, {c.x - r + 6, c.y}, angle);
epd->drawLine(p1.x, p1.y, p2.x, p2.y, commonData->fgcolor); getdisplay().drawLine(p1.x, p1.y, p2.x, p2.y, commonData->fgcolor);
} }
} }
@@ -341,31 +354,31 @@ public:
fillPoly4(rotatePoints(c, pts, angle), commonData->fgcolor); fillPoly4(rotatePoints(c, pts, angle), commonData->fgcolor);
// base // base
epd->fillCircle(c.x, c.y, 7, commonData->fgcolor); getdisplay().fillCircle(c.x, c.y, 7, commonData->fgcolor);
epd->fillCircle(c.x, c.y, 4, commonData->bgcolor); getdisplay().fillCircle(c.x, c.y, 4, commonData->bgcolor);
// Symbol // Symbol
printVoltageSymbol(40, 60, commonData->fgcolor); printVoltageSymbol(40, 60, commonData->fgcolor);
// Additional information at right side // Additional information at right side
epd->setFont(&Ubuntu_Bold8pt8b); getdisplay().setFont(&Ubuntu_Bold8pt8b);
epd->setCursor(300, 60); getdisplay().setCursor(300, 60);
epd->print("Source:"); getdisplay().print("Source:");
epd->setCursor(300, 80); getdisplay().setCursor(300, 80);
epd->print(name1); getdisplay().print(name1);
epd->setCursor(300, 110); getdisplay().setCursor(300, 110);
epd->print("Type:"); getdisplay().print("Type:");
epd->setCursor(300, 130); getdisplay().setCursor(300, 130);
epd->print(batType); getdisplay().print(batType);
epd->setCursor(300, 160); getdisplay().setCursor(300, 160);
epd->print("Avg:"); getdisplay().print("Avg:");
printAvg(average, 300, 180, false); printAvg(average, 300, 180, false);
// FRAM indicator // FRAM indicator
if (hasFRAM) { if (hasFRAM) {
epd->drawXBitmap(300, 240, fram_bits, icon_width, icon_height, commonData->fgcolor); getdisplay().drawXBitmap(300, 240, fram_bits, icon_width, icon_height, commonData->fgcolor);
} }
} }

View File

@@ -1,4 +1,3 @@
// SPDX-License-Identifier: GPL-2.0-or-later
#if defined BOARD_OBP60S3 || defined BOARD_OBP40S3 #if defined BOARD_OBP60S3 || defined BOARD_OBP40S3
#include "Pagedata.h" #include "Pagedata.h"
@@ -14,17 +13,16 @@
class PageWhite : public Page class PageWhite : public Page
{ {
private:
char mode = 'W'; // display mode (W)hite | (L)ogo | (M)FD logo char mode = 'W'; // display mode (W)hite | (L)ogo | (M)FD logo
public: public:
PageWhite(CommonData &common) : Page(common) PageWhite(CommonData &common){
{ commonData = &common;
logger->logDebug(GwLog::LOG, "Instantiate PageWhite"); common.logger->logDebug(GwLog::LOG,"Instantiate PageWhite");
refreshtime = 15000; refreshtime = 15000;
} }
int handleKey(int key) { virtual int handleKey(int key) {
// Change display mode // Change display mode
if (key == 1) { if (key == 1) {
if (mode == 'W') { if (mode == 'W') {
@@ -39,20 +37,21 @@ public:
return key; return key;
} }
void displayNew(PageData &pageData) { int displayPage(PageData &pageData){
#ifdef BOARD_OBP60S3 GwConfigHandler *config = commonData->config;
// Clear optical warning GwLog *logger = commonData->logger;
if (flashLED == "Limit Violation") {
// Get config data
String flashLED = config->getString(config->flashLED);
// Optical warning by limit violation (unused)
if(String(flashLED) == "Limit Violation"){
setBlinkingLED(false); setBlinkingLED(false);
setFlashLED(false); setFlashLED(false);
} }
#endif
};
int displayPage(PageData &pageData) {
// Logging boat values // Logging boat values
logger->logDebug(GwLog::LOG, "Drawing at PageWhite"); LOG_DEBUG(GwLog::LOG,"Drawing at PageWhite");
// Draw page // Draw page
//*********************************************************** //***********************************************************
@@ -62,19 +61,19 @@ public:
// Set display in partial refresh mode // Set display in partial refresh mode
if (mode == 'W') { if (mode == 'W') {
epd->setFullWindow(); getdisplay().setFullWindow();
} else { } else {
epd->setPartialWindow(0, 0, epd->width(), epd->height()); // Set partial update getdisplay().setPartialWindow(0, 0, getdisplay().width(), getdisplay().height()); // Set partial update
} }
if (mode == 'L') { if (mode == 'L') {
epd->drawXBitmap(0, 0, OBP_400x300_bits, OBP_400x300_width, OBP_400x300_height, commonData->fgcolor); getdisplay().drawXBitmap(0, 0, OBP_400x300_bits, OBP_400x300_width, OBP_400x300_height, commonData->fgcolor);
} else if (mode == 'M') { } else if (mode == 'M') {
#ifdef BOARD_OBP60S3 #ifdef BOARD_OBP60S3
epd->drawXBitmap(0, 0, OBP60_400x300_bits, OBP60_400x300_width, OBP60_400x300_height, commonData->fgcolor); getdisplay().drawXBitmap(0, 0, OBP60_400x300_bits, OBP60_400x300_width, OBP60_400x300_height, commonData->fgcolor);
#endif #endif
#ifdef BOARD_OBP40S3 #ifdef BOARD_OBP40S3
epd->drawXBitmap(0, 0, OBP40_400x300_bits, OBP40_400x300_width, OBP40_400x300_height, commonData->fgcolor); getdisplay().drawXBitmap(0, 0, OBP40_400x300_bits, OBP40_400x300_width, OBP40_400x300_height, commonData->fgcolor);
#endif #endif
} }

View File

@@ -1,14 +1,9 @@
// SPDX-License-Identifier: GPL-2.0-or-later
#if defined BOARD_OBP60S3 || defined BOARD_OBP40S3 #if defined BOARD_OBP60S3 || defined BOARD_OBP40S3
#include "Pagedata.h" #include "Pagedata.h"
#include "OBP60Extensions.h" #include "OBP60Extensions.h"
#include "N2kMessages.h" #include "N2kMessages.h"
#ifdef ENABLE_CALIBRATION
#include "BoatDataCalibration.h"
#endif
#define front_width 120 #define front_width 120
#define front_height 162 #define front_height 162
static unsigned char front_bits[] PROGMEM = { static unsigned char front_bits[] PROGMEM = {
@@ -218,21 +213,15 @@ static unsigned char front_bits[] PROGMEM = {
class PageWind : public Page class PageWind : public Page
{ {
private:
String lengthformat;
bool keylock = false; // Keylock bool keylock = false; // Keylock
int8_t lp = 80; // Pointer length int8_t lp = 80; // Pointer length
char mode = 'N'; // page mode (N)ormal | (L)ens | e(X)ample char mode = 'N'; // page mode (N)ormal | (L)ens | e(X)ample
char source = 'A'; // data source (A)pparent | (T)rue char source = 'A'; // data source (A)pparent | (T)rue
public: public:
PageWind(CommonData &common) : Page(common) PageWind(CommonData &common){
{ commonData = &common;
logger->logDebug(GwLog::LOG, "Instantiate PageWind"); common.logger->logDebug(GwLog::LOG,"Instantiate PageWind");
// Get config data
lengthformat = config->getString(config->lengthFormat);
if (hasFRAM) { if (hasFRAM) {
lp = fram.read(FRAM_WIND_SIZE); lp = fram.read(FRAM_WIND_SIZE);
source = fram.read(FRAM_WIND_SRC); source = fram.read(FRAM_WIND_SRC);
@@ -240,7 +229,7 @@ public:
} }
} }
void setupKeys() { virtual void setupKeys(){
Page::setupKeys(); Page::setupKeys();
commonData->keydata[0].label = "MODE"; commonData->keydata[0].label = "MODE";
if (mode == 'X') { if (mode == 'X') {
@@ -252,13 +241,13 @@ public:
} }
// Key functions // Key functions
int handleKey(int key) { virtual int handleKey(int key){
if(key == 1){ // Mode switch if(key == 1){ // Mode switch
if(mode == 'N'){ if(mode == 'N'){
mode = 'L'; mode = 'L';
} else if (mode == 'L') { // } else if (mode == 'L') {
mode = 'X'; // mode = 'X';
} else { } else {
mode = 'N'; mode = 'N';
} }
@@ -306,23 +295,23 @@ public:
return key; return key;
} }
void displayNew(PageData &pageData) { int displayPage(PageData &pageData)
#ifdef BOARD_OBP60S3 {
// Clear optical warning GwConfigHandler *config = commonData->config;
if (flashLED == "Limit Violation") { GwLog *logger = commonData->logger;
setBlinkingLED(false);
setFlashLED(false);
}
#endif
};
int displayPage(PageData &pageData) {
static String svalue1old = ""; static String svalue1old = "";
static String unit1old = ""; static String unit1old = "";
static String svalue2old = ""; static String svalue2old = "";
static String unit2old = ""; 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 *bvalue1; // Value 1 for speed on top
GwApi::BoatValue *bvalue2; // Value 2 for angle on bottom GwApi::BoatValue *bvalue2; // Value 2 for angle on bottom
@@ -334,13 +323,10 @@ public:
} }
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
#ifdef ENABLE_CALIBRATION
calibrationData.calibrateInstance(bvalue1, logger); // Check if boat data value is to be calibrated
#endif
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 = commonData->fmt->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 = commonData->fmt->formatValue(bvalue1, *commonData).unit; // Unit of value String unit1 = formatValue(bvalue1, *commonData).unit; // Unit of value
// Get boat values for angle (AWD/TWD) // Get boat values for angle (AWD/TWD)
if (source == 'A') { if (source == 'A') {
@@ -350,70 +336,73 @@ public:
} }
String name2 = bvalue2->getName().c_str(); // Value name 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
#ifdef ENABLE_CALIBRATION
calibrationData.calibrateInstance(bvalue2, logger); // Check if boat data value is to be calibrated
#endif
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
if (simulation) { if (simulation) {
value2 = 0.62731; // some random value value2 = 0.62731; // some random value
} }
String svalue2 = commonData->fmt->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 = commonData->fmt->formatValue(bvalue2, *commonData).unit; // Unit of value 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 // Logging boat values
if (bvalue1 == NULL) return PAGE_OK; // WTF why this statement? if (bvalue1 == NULL) return PAGE_OK; // WTF why this statement?
logger->logDebug(GwLog::LOG, "Drawing at PageWind, %s:%f, %s:%f", name1.c_str(), value1, name2.c_str(), value2); LOG_DEBUG(GwLog::LOG,"Drawing at PageWind, %s:%f, %s:%f", name1.c_str(), value1, name2.c_str(), value2);
// Draw page // Draw page
//*********************************************************** //***********************************************************
// Set display in partial refresh mode // Set display in partial refresh mode
epd->setPartialWindow(0, 0, epd->width(), epd->height()); // Set partial update getdisplay().setPartialWindow(0, 0, getdisplay().width(), getdisplay().height()); // Set partial update
epd->setTextColor(commonData->fgcolor); getdisplay().setTextColor(commonData->fgcolor);
if (mode == 'X') { if (mode == 'X') {
// Original example code with scaling circle // Original example code with scaling circle
// Show values AWS/TWS // Show values AWS/TWS
epd->setFont(&Ubuntu_Bold20pt8b); getdisplay().setFont(&Ubuntu_Bold20pt8b);
epd->setCursor(20, 50); getdisplay().setCursor(20, 50);
epd->print(name1); // Value name getdisplay().print(name1); // Value name
epd->print(": "); getdisplay().print(": ");
if(holdvalues == false){ if(holdvalues == false){
epd->print(svalue1); // Value getdisplay().print(svalue1); // Value
epd->print(" "); getdisplay().print(" ");
epd->print(unit1); // Unit getdisplay().print(unit1); // Unit
} }
else{ else{
epd->print(svalue1old); // Value old getdisplay().print(svalue1old); // Value old
epd->print(" "); getdisplay().print(" ");
epd->print(unit1old); // Unit old getdisplay().print(unit1old); // Unit old
} }
// Show values AWD/TWD // Show values AWD/TWD
epd->setFont(&Ubuntu_Bold20pt8b); getdisplay().setFont(&Ubuntu_Bold20pt8b);
epd->setCursor(20, 260); getdisplay().setCursor(20, 260);
epd->print(name2); // Value name getdisplay().print(name2); // Value name
epd->print(": "); getdisplay().print(": ");
if(holdvalues == false){ if(holdvalues == false){
epd->print(svalue2); // Value getdisplay().print(svalue2); // Value
epd->print(" "); getdisplay().print(" ");
epd->print(unit2); // Unit getdisplay().print(unit2); // Unit
} }
else{ else{
epd->print(svalue2old); // Value old getdisplay().print(svalue2old); // Value old
epd->print(" "); getdisplay().print(" ");
epd->print(unit2old); // Unit old getdisplay().print(unit2old); // Unit old
} }
Point c = {200, 145}; Point c = {200, 145};
// Draw instrument // Draw instrument
epd->fillCircle(c.x, c.y, lp + 5, commonData->fgcolor); getdisplay().fillCircle(c.x, c.y, lp + 5, commonData->fgcolor);
epd->fillCircle(c.x, c.y, lp + 1, commonData->bgcolor); getdisplay().fillCircle(c.x, c.y, lp + 1, commonData->bgcolor);
// Wind pointer // Wind pointer
if (bvalue2->valid or simulation) { if (bvalue2->valid or simulation) {
@@ -428,7 +417,7 @@ public:
}; };
fillPoly4(rotatePoints(c, pts, RadToDeg(value2)), commonData->fgcolor); fillPoly4(rotatePoints(c, pts, RadToDeg(value2)), commonData->fgcolor);
} else { } else {
epd->setFont(&Ubuntu_Bold12pt8b); getdisplay().setFont(&Ubuntu_Bold12pt8b);
drawTextCenter(c.x, c.y, "no data"); drawTextCenter(c.x, c.y, "no data");
} }
@@ -446,7 +435,7 @@ public:
}; };
int angle; int angle;
epd->setFont(&Ubuntu_Bold12pt8b); getdisplay().setFont(&Ubuntu_Bold12pt8b);
// starbord // starbord
// text with line // text with line
@@ -461,7 +450,7 @@ public:
for (int i = 30; i < 138; i += 6) { for (int i = 30; i < 138; i += 6) {
if (i % 15 != 0) { if (i % 15 != 0) {
p = rotatePoint(c, {c.x, c.y - r + 5}, i); p = rotatePoint(c, {c.x, c.y - r + 5}, i);
epd->fillCircle(p.x, p.y, 2, commonData->fgcolor); getdisplay().fillCircle(p.x, p.y, 2, commonData->fgcolor);
} }
} }
@@ -478,17 +467,17 @@ public:
for (int i = 228; i < 330; i += 6) { for (int i = 228; i < 330; i += 6) {
if (i % 15 != 0) { if (i % 15 != 0) {
p = rotatePoint(c, {c.x, c.y - r + 5}, i); p = rotatePoint(c, {c.x, c.y - r + 5}, i);
epd->fillCircle(p.x, p.y, 2, commonData->fgcolor); getdisplay().fillCircle(p.x, p.y, 2, commonData->fgcolor);
} }
} }
// data source // data source
epd->setFont(&Ubuntu_Bold12pt8b); getdisplay().setFont(&Ubuntu_Bold12pt8b);
epd->setCursor(8, 50); getdisplay().setCursor(8, 50);
if (source == 'A') { if (source == 'A') {
epd->print("APP"); getdisplay().print("APP");
} else { } else {
epd->print("TRUE"); getdisplay().print("TRUE");
} }
// Wind pointer (angle) // Wind pointer (angle)
@@ -508,7 +497,7 @@ public:
alpha *= -1; alpha *= -1;
} }
epd->fillCircle(c.x, c.y, 8, commonData->fgcolor); getdisplay().fillCircle(c.x, c.y, 8, commonData->fgcolor);
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)},
@@ -516,39 +505,39 @@ public:
{c.x - 6, c.y + 15} {c.x - 6, c.y + 15}
}; };
fillPoly4(rotatePoints(c, pts, alpha), commonData->fgcolor); fillPoly4(rotatePoints(c, pts, alpha), commonData->fgcolor);
epd->fillCircle(c.x, c.y, 6, commonData->bgcolor); getdisplay().fillCircle(c.x, c.y, 6, commonData->bgcolor);
} else { } else {
epd->setFont(&Ubuntu_Bold12pt8b); getdisplay().setFont(&Ubuntu_Bold12pt8b);
drawTextCenter(c.x, c.y, "no data"); drawTextCenter(c.x, c.y, "no data");
} }
// Wind speed as decimal number // Wind speed as decimal number
epd->setFont(&DSEG7Classic_BoldItalic20pt7b); getdisplay().setFont(&DSEG7Classic_BoldItalic20pt7b);
epd->setCursor(150, 250); getdisplay().setCursor(150, 250);
if (holdvalues == false) { if (holdvalues == false) {
epd->print(svalue1); getdisplay().print(svalue1);
} else { } else {
epd->print(svalue1old); getdisplay().print(svalue1old);
} }
// unit // unit
epd->setFont(&Ubuntu_Bold8pt8b); getdisplay().setFont(&Ubuntu_Bold8pt8b);
epd->setCursor(220, 265); getdisplay().setCursor(220, 265);
epd->print("kts"); getdisplay().print("kts");
} }
else { else {
// Normal mode // Normal mode
// data source // data source
epd->setFont(&Ubuntu_Bold12pt8b); getdisplay().setFont(&Ubuntu_Bold12pt8b);
epd->setCursor(8, 50); getdisplay().setCursor(8, 50);
if (source == 'A') { if (source == 'A') {
epd->print("APP"); getdisplay().print("APP");
} else { } else {
epd->print("TRUE"); getdisplay().print("TRUE");
} }
// draw ship front symbol (as bitmap) // draw ship front symbol (as bitmap)
epd->drawXBitmap(140, 30, front_bits, front_width, front_height, commonData->fgcolor); getdisplay().drawXBitmap(140, 30, front_bits, front_width, front_height, commonData->fgcolor);
Point c = {200, 155}; Point c = {200, 155};
uint16_t r = 150; uint16_t r = 150;
@@ -573,7 +562,7 @@ public:
for (int i = 30; i < 150; i += 10) { for (int i = 30; i < 150; i += 10) {
if (i % 30 != 0) { if (i % 30 != 0) {
p = rotatePoint(c, {c.x, c.y - r + 5}, i); p = rotatePoint(c, {c.x, c.y - r + 5}, i);
epd->fillCircle(p.x, p.y, 3, commonData->fgcolor); getdisplay().fillCircle(p.x, p.y, 3, commonData->fgcolor);
} }
} }
@@ -590,27 +579,27 @@ public:
for (int i = 210; i < 340; i += 10) { for (int i = 210; i < 340; i += 10) {
if (i % 30 != 0) { if (i % 30 != 0) {
p = rotatePoint(c, {c.x, c.y - r + 5}, i); p = rotatePoint(c, {c.x, c.y - r + 5}, i);
epd->fillCircle(p.x, p.y, 3, commonData->fgcolor); getdisplay().fillCircle(p.x, p.y, 3, commonData->fgcolor);
} }
} }
// Wind speed as decimal number // Wind speed as decimal number
epd->setFont(&DSEG7Classic_BoldItalic20pt7b); getdisplay().setFont(&DSEG7Classic_BoldItalic20pt7b);
epd->setCursor(150, 250); getdisplay().setCursor(150, 250);
if (holdvalues == false) { if (holdvalues == false) {
epd->print(svalue1); getdisplay().print(svalue1);
} else { } else {
epd->print(svalue1old); getdisplay().print(svalue1old);
} }
// unit // unit
epd->setFont(&Ubuntu_Bold8pt8b); getdisplay().setFont(&Ubuntu_Bold8pt8b);
epd->setCursor(220, 265); getdisplay().setCursor(220, 265);
epd->print("kts"); getdisplay().print("kts");
// Wind pointer (angle) // Wind pointer (angle)
if (bvalue2->valid or simulation) { if (bvalue2->valid or simulation) {
float alpha = RadToDeg(value2); float alpha = RadToDeg(value2);
epd->fillCircle(c.x, c.y, 8, commonData->fgcolor); getdisplay().fillCircle(c.x, c.y, 8, commonData->fgcolor);
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)},
@@ -618,9 +607,9 @@ public:
{c.x - 6, c.y + 15} {c.x - 6, c.y + 15}
}; };
fillPoly4(rotatePoints(c, pts, alpha), commonData->fgcolor); fillPoly4(rotatePoints(c, pts, alpha), commonData->fgcolor);
epd->fillCircle(c.x, c.y, 6, commonData->bgcolor); getdisplay().fillCircle(c.x, c.y, 6, commonData->bgcolor);
} else { } else {
epd->setFont(&Ubuntu_Bold12pt8b); getdisplay().setFont(&Ubuntu_Bold12pt8b);
drawTextCenter(c.x, c.y, "no data"); drawTextCenter(c.x, c.y, "no data");
} }

View File

@@ -6,8 +6,7 @@
#include "OBPcharts.h" #include "OBPcharts.h"
// **************************************************************** // ****************************************************************
class PageWindPlot : public Page class PageWindPlot : public Page {
{
private: private:
GwLog* logger; GwLog* logger;
@@ -29,8 +28,8 @@ private:
static constexpr bool PRNT_VALUE = true; static constexpr bool PRNT_VALUE = true;
static constexpr bool NO_PRNT_VALUE = false; static constexpr bool NO_PRNT_VALUE = false;
// int width; // Screen width int width; // Screen width
// int height; // Screen height int height; // Screen height
bool keylock = false; // Keylock bool keylock = false; // Keylock
ChartMode chrtMode = DIRECTION; ChartMode chrtMode = DIRECTION;
@@ -65,18 +64,25 @@ private:
GwApi::BoatValue* wsBVal = nullptr; GwApi::BoatValue* wsBVal = nullptr;
public: public:
PageWindPlot(CommonData& common) : Page(common) PageWindPlot(CommonData& common)
{ {
logger->logDebug(GwLog::LOG, "Instantiate PageWindPlot"); commonData = &common;
logger = commonData->logger;
LOG_DEBUG(GwLog::LOG, "Instantiate PageWindPlot");
//width = epd->width(); // Screen width width = getdisplay().width(); // Screen width
//height = epd->height(); // Screen height height = getdisplay().height(); // Screen height
// Get config data // 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);
oldShowTruW = !showTruW; // makes wind source being initialized at initial page call oldShowTruW = !showTruW; // makes wind source being initialized at initial page call
} }
void setupKeys() virtual void setupKeys()
{ {
Page::setupKeys(); Page::setupKeys();
commonData->keydata[0].label = "MODE"; commonData->keydata[0].label = "MODE";
@@ -89,7 +95,7 @@ public:
} }
// Key functions // Key functions
int handleKey(int key) virtual int handleKey(int key)
{ {
// Set chart mode // Set chart mode
if (key == 1) { if (key == 1) {
@@ -100,14 +106,14 @@ public:
} else { } else {
chrtMode = DIRECTION; chrtMode = DIRECTION;
} }
return 0; return 0; // Commit the key
} }
#if defined BOARD_OBP60S3 #if defined BOARD_OBP60S3
// Set data source TRUE | APP // Set data source TRUE | APP
if (key == 2) { if (key == 2) {
showTruW = !showTruW; showTruW = !showTruW;
return 0; return 0; // Commit the key
} }
// Set interval for wind history chart update time (interval) // Set interval for wind history chart update time (interval)
@@ -126,7 +132,7 @@ public:
} else { } else {
dataIntv = 1; dataIntv = 1;
} }
return 0; return 0; // Commit the key
} }
// Keylock function // Keylock function
@@ -189,7 +195,7 @@ public:
int displayPage(PageData& pageData) int displayPage(PageData& pageData)
{ {
logger->logDebug(GwLog::LOG, "Display PageWindPlot"); LOG_DEBUG(GwLog::LOG, "Display PageWindPlot");
// ulong pageTime = millis(); // ulong pageTime = millis();
if (showTruW != oldShowTruW) { if (showTruW != oldShowTruW) {
@@ -209,14 +215,14 @@ public:
oldShowTruW = showTruW; oldShowTruW = showTruW;
} }
logger->logDebug(GwLog::DEBUG, "PageWindPlot: draw with data %s: %.2f, %s: %.2f", wdBVal->getName().c_str(), wdBVal->value, wsBVal->getName().c_str(), wsBVal->value); LOG_DEBUG(GwLog::DEBUG, "PageWindPlot: draw with data %s: %.2f, %s: %.2f", wdBVal->getName().c_str(), wdBVal->value, wsBVal->getName().c_str(), wsBVal->value);
// Draw page // Draw page
//*********************************************************** //***********************************************************
// Set display in partial refresh mode // Set display in partial refresh mode
epd->setPartialWindow(0, 0, epd->width(), epd->height()); getdisplay().setPartialWindow(0, 0, width, height); // Set partial update
epd->setTextColor(commonData->fgcolor); getdisplay().setTextColor(commonData->fgcolor);
if (chrtMode == DIRECTION) { if (chrtMode == DIRECTION) {
if (wdChart) { if (wdChart) {
@@ -237,7 +243,7 @@ public:
} }
} }
// logger->logDebug(GwLog::DEBUG, "PageWindPlot: page time %ldms", millis() - pageTime); // LOG_DEBUG(GwLog::DEBUG, "PageWindPlot: page time %ldms", millis() - pageTime);
return PAGE_UPDATE; return PAGE_UPDATE;
} }
}; };

View File

@@ -1,4 +1,3 @@
// SPDX-License-Identifier: GPL-2.0-or-later
#if defined BOARD_OBP60S3 || defined BOARD_OBP40S3 #if defined BOARD_OBP60S3 || defined BOARD_OBP40S3
#include "Pagedata.h" #include "Pagedata.h"
@@ -6,21 +5,16 @@
class PageWindRose : public Page class PageWindRose : public Page
{ {
private:
String lengthformat;
int16_t lp = 80; // Pointer length int16_t lp = 80; // Pointer length
public: public:
PageWindRose(CommonData &common) : Page(common) PageWindRose(CommonData &common){
{ commonData = &common;
logger->logDebug(GwLog::LOG, "Instantiate PageWindRose"); common.logger->logDebug(GwLog::LOG,"Instantiate PageWindRose");
// Get config data
String lengthformat = config->getString(config->lengthFormat);
} }
// Key functions // Key functions
int handleKey(int key) { virtual int handleKey(int key){
// Code for keylock // Code for keylock
if(key == 11){ if(key == 11){
commonData->keylock = !commonData->keylock; commonData->keylock = !commonData->keylock;
@@ -29,174 +23,216 @@ public:
return key; return key;
} }
void displayNew(PageData &pageData) {
#ifdef BOARD_OBP60S3
// Clear optical warning
if (flashLED == "Limit Violation") {
setBlinkingLED(false);
setFlashLED(false);
}
#endif
};
int displayPage(PageData &pageData){ int displayPage(PageData &pageData){
GwConfigHandler *config = commonData->config;
GwLog *logger = commonData->logger;
// storage for hold values static String svalue1old = "";
static FormattedData bvf_awa_old; static String unit1old = "";
static FormattedData bvf_aws_old; static String svalue2old = "";
static FormattedData bvf_twd_old; static String unit2old = "";
static FormattedData bvf_tws_old; static String svalue3old = "";
static FormattedData bvf_dbt_old; static String unit3old = "";
static FormattedData bvf_stw_old; static String svalue4old = "";
// units? why? static String unit4old = "";
static String svalue5old = "";
static String unit5old = "";
static String svalue6old = "";
static String unit6old = "";
// 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 value for AWA // Get boat value for AWA
GwApi::BoatValue *bv_awa = pageData.values[0]; // First element in list GwApi::BoatValue *bvalue1 = pageData.values[0]; // First element in list (only one value by PageOneValue)
String name_awa = xdrDelete(bv_awa->getName(), 6); // get name without prefix and limit length String name1 = xdrDelete(bvalue1->getName()); // Value name
FormattedData bvf_awa = commonData->fmt->formatValue(bv_awa, *commonData); name1 = name1.substring(0, 6); // String length limit for value name
if (bv_awa->valid) { // Save formatted data for hold feature double value1 = bvalue1->value; // Value as double in SI unit
bvf_awa_old = bvf_awa; 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 unit1 = formatValue(bvalue1, *commonData).unit; // Unit of value
if(valid1 == true){
svalue1old = svalue1; // Save old value
unit1old = unit1; // Save old unit
} }
// Get boat value for AWS // Get boat value for AWS
GwApi::BoatValue *bv_aws = pageData.values[1]; // Second element in list GwApi::BoatValue *bvalue2 = pageData.values[1]; // Second element in list
String name_aws = xdrDelete(bv_aws->getName(), 6); // get name without prefix and limit length String name2 = xdrDelete(bvalue2->getName()); // Value name
FormattedData bvf_aws = commonData->fmt->formatValue(bv_aws, *commonData); name2 = name2.substring(0, 6); // String length limit for value name
if (bv_aws->valid) { // Save formatted data for hold feature double value2 = bvalue2->value; // Value as double in SI unit
bvf_aws_old = bvf_aws; 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
if(valid2 == true){
svalue2old = svalue2; // Save old value
unit2old = unit2; // Save old unit
} }
// Get boat value for TWD // Get boat value for TWD
GwApi::BoatValue *bv_twd = pageData.values[2]; // Third element in list GwApi::BoatValue *bvalue3 = pageData.values[2]; // Third element in list
String name_twd = xdrDelete(bv_twd->getName(), 6); // get name without prefix and limit length String name3 = xdrDelete(bvalue3->getName()); // Value name
#ifdef ENABLE_CALIBRATION name3 = name3.substring(0, 6); // String length limit for value name
calibrationData.calibrateInstance(bv_twd, logger); // Check if boat data value is to be calibrated double value3 = bvalue3->value; // Value as double in SI unit
#endif bool valid3 = bvalue3->valid; // Valid information
FormattedData bvf_twd = commonData->fmt->formatValue(bv_twd, *commonData); String svalue3 = formatValue(bvalue3, *commonData).svalue; // Formatted value as string including unit conversion and switching decimal places
if (bv_twd->valid) { // Save formatted data for hold feature String unit3 = formatValue(bvalue3, *commonData).unit; // Unit of value
bvf_twd_old = bvf_twd; if(valid3 == true){
svalue3old = svalue3; // Save old value
unit3old = unit3; // Save old unit
} }
// Get boat value for TWS // Get boat value for TWS
GwApi::BoatValue *bv_tws = pageData.values[3]; // Fourth element in list GwApi::BoatValue *bvalue4 = pageData.values[3]; // Fourth element in list
String name_tws = xdrDelete(bv_tws->getName(), 6); // get name without prefix and limit length String name4 = xdrDelete(bvalue4->getName()); // Value name
#ifdef ENABLE_CALIBRATION name4 = name4.substring(0, 6); // String length limit for value name
calibrationData.calibrateInstance(bv_tws, logger); // Check if boat data value is to be calibrated double value4 = bvalue4->value; // Value as double in SI unit
#endif bool valid4 = bvalue4->valid; // Valid information
FormattedData bvf_tws = commonData->fmt->formatValue(bv_tws, *commonData); String svalue4 = formatValue(bvalue4, *commonData).svalue; // Formatted value as string including unit conversion and switching decimal places
if (bv_tws->valid) { // Save formatted data for hold feature String unit4 = formatValue(bvalue4, *commonData).unit; // Unit of value
bvf_tws_old = bvf_tws; if(valid4 == true){
svalue4old = svalue4; // Save old value
unit4old = unit4; // Save old unit
} }
// Get boat value for DBT // Get boat value for DBT
GwApi::BoatValue *bv_dbt = pageData.values[4]; // Fifth element in list GwApi::BoatValue *bvalue5 = pageData.values[4]; // Fifth element in list
String name_dbt = xdrDelete(bv_dbt->getName(), 6); // get name without prefix and limit length String name5 = xdrDelete(bvalue5->getName()); // Value name
#ifdef ENABLE_CALIBRATION name5 = name5.substring(0, 6); // String length limit for value name
calibrationData.calibrateInstance(bv_dbt, logger); // Check if boat data value is to be calibrated double value5 = bvalue5->value; // Value as double in SI unit
#endif bool valid5 = bvalue5->valid; // Valid information
FormattedData bvf_dbt = commonData->fmt->formatValue(bv_dbt, *commonData); String svalue5 = formatValue(bvalue5, *commonData).svalue; // Formatted value as string including unit conversion and switching decimal places
if (bv_dbt->valid) { // Save formatted data for hold feature String unit5 = formatValue(bvalue5, *commonData).unit; // Unit of value
bvf_dbt_old = bvf_dbt; if(valid5 == true){
svalue5old = svalue5; // Save old value
unit5old = unit5; // Save old unit
} }
// Get boat value for STW // Get boat value for STW
GwApi::BoatValue *bv_stw = pageData.values[5]; // Sixth element in list GwApi::BoatValue *bvalue6 = pageData.values[5]; // Sixth element in list
String name_stw = xdrDelete(bv_stw->getName(), 6); // get name without prefix and limit length String name6 = xdrDelete(bvalue6->getName()); // Value name
#ifdef ENABLE_CALIBRATION name6 = name6.substring(0, 6); // String length limit for value name
calibrationData.calibrateInstance(bv_stw, logger); // Check if boat data value is to be calibrated double value6 = bvalue6->value; // Value as double in SI unit
#endif bool valid6 = bvalue6->valid; // Valid information
FormattedData bvf_stw = commonData->fmt->formatValue(bv_stw, *commonData); String svalue6 = formatValue(bvalue6, *commonData).svalue; // Formatted value as string including unit conversion and switching decimal places
if (bv_stw->valid) { // Save formatted data for hold feature String unit6 = formatValue(bvalue6, *commonData).unit; // Unit of value
bvf_stw_old = bvf_stw; if(valid6 == true){
svalue6old = svalue6; // Save old value
unit6old = unit6; // Save old unit
} }
// Log boat values // Optical warning by limit violation (unused)
logger->logDebug(GwLog::LOG, "Drawing at PageWindRose, %s:%f, %s:%f, %s:%f, %s:%f, %s:%f, %s:%f", if(String(flashLED) == "Limit Violation"){
name_awa.c_str(), bv_awa->value, setBlinkingLED(false);
name_aws.c_str(), bv_aws->value, setFlashLED(false);
name_twd.c_str(), bv_twd->value, }
name_tws.c_str(), bv_tws->value,
name_dbt.c_str(), bv_dbt->value, // Logging boat values
name_stw.c_str(), bv_stw->value); 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);
// Draw page // Draw page
// ********************************************************************* //***********************************************************
// Set display in partial refresh mode // Set display in partial refresh mode
epd->setPartialWindow(0, 0, epd->width(), epd->height()); getdisplay().setPartialWindow(0, 0, getdisplay().width(), getdisplay().height()); // Set partial update
epd->setTextColor(commonData->fgcolor); getdisplay().setTextColor(commonData->fgcolor);
// Show values AWA // Show values AWA
epd->setFont(&DSEG7Classic_BoldItalic20pt7b); getdisplay().setFont(&DSEG7Classic_BoldItalic20pt7b);
epd->setCursor(10, 65); getdisplay().setCursor(10, 65);
epd->print(holdvalues ? bvf_awa_old.value : bvf_awa.value); getdisplay().print(svalue1); // Value
epd->setFont(&Ubuntu_Bold12pt8b); getdisplay().setFont(&Ubuntu_Bold12pt8b);
epd->setCursor(10, 95); getdisplay().setCursor(10, 95);
epd->print(name_awa); getdisplay().print(name1); // Name
epd->setFont(&Ubuntu_Bold8pt8b); getdisplay().setFont(&Ubuntu_Bold8pt8b);
epd->setCursor(10, 115); getdisplay().setCursor(10, 115);
epd->print(" "); getdisplay().print(" ");
epd->print(holdvalues ? bvf_awa_old.unit : bvf_awa.unit); if(holdvalues == false){
getdisplay().print(unit1); // Unit
// Horizintal separator left
epd->fillRect(0, 149, 60, 3, commonData->fgcolor);
// Show values AWS
epd->setFont(&DSEG7Classic_BoldItalic20pt7b);
epd->setCursor(10, 270);
epd->print(holdvalues ? bvf_aws_old.value : bvf_aws.value);
epd->setFont(&Ubuntu_Bold12pt8b);
epd->setCursor(10, 220);
epd->print(name_aws);
epd->setFont(&Ubuntu_Bold8pt8b);
epd->setCursor(10, 190);
epd->print(" ");
epd->print(holdvalues ? bvf_aws_old.unit : bvf_aws.unit);
// Show value TWD
epd->setFont(&DSEG7Classic_BoldItalic20pt7b);
epd->setCursor(295, 65);
// TODO WTF? Der Formatter sollte das korrekt machen
if (bv_twd->valid) {
epd->print(abs(bv_twd->value * 180 / PI), 0); // Value
} }
else{ else{
epd->print(commonData->fmt->placeholder); 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().setFont(&Ubuntu_Bold12pt8b);
getdisplay().setCursor(10, 220);
getdisplay().print(name2); // Name
getdisplay().setFont(&Ubuntu_Bold8pt8b);
getdisplay().setCursor(10, 190);
getdisplay().print(" ");
if(holdvalues == false){
getdisplay().print(unit2); // Unit
}
else{
getdisplay().print(unit2old); // Unit
}
// Show values TWD
getdisplay().setFont(&DSEG7Classic_BoldItalic20pt7b);
getdisplay().setCursor(295, 65);
if(valid3 == true){
getdisplay().print(abs(value3 * 180 / PI), 0); // Value
}
else{
getdisplay().print("---"); // Value
}
getdisplay().setFont(&Ubuntu_Bold12pt8b);
getdisplay().setCursor(335, 95);
getdisplay().print(name3); // Name
getdisplay().setFont(&Ubuntu_Bold8pt8b);
getdisplay().setCursor(335, 115);
getdisplay().print(" ");
if(holdvalues == false){
getdisplay().print(unit3); // Unit
}
else{
getdisplay().print(unit3old); // Unit
} }
epd->setFont(&Ubuntu_Bold12pt8b);
epd->setCursor(335, 95);
epd->print(name_twd); // Name
epd->setFont(&Ubuntu_Bold8pt8b);
epd->setCursor(335, 115);
epd->print(" ");
epd->print(holdvalues ? bvf_twd_old.unit : bvf_twd.unit);
// Horizintal separator right // Horizintal separator right
epd->fillRect(340, 149, 80, 3, commonData->fgcolor); getdisplay().fillRect(340, 149, 80, 3, commonData->fgcolor);
// Show values TWS // Show values TWS
epd->setFont(&DSEG7Classic_BoldItalic20pt7b); getdisplay().setFont(&DSEG7Classic_BoldItalic20pt7b);
epd->setCursor(295, 270); getdisplay().setCursor(295, 270);
epd->print(name_tws); getdisplay().print(svalue4); // Value
epd->setFont(&Ubuntu_Bold12pt8b); getdisplay().setFont(&Ubuntu_Bold12pt8b);
epd->setCursor(335, 220); getdisplay().setCursor(335, 220);
epd->print(holdvalues ? bvf_tws_old.value : bvf_tws.value); getdisplay().print(name4); // Name
epd->setFont(&Ubuntu_Bold8pt8b); getdisplay().setFont(&Ubuntu_Bold8pt8b);
epd->setCursor(335, 190); getdisplay().setCursor(335, 190);
epd->print(" "); getdisplay().print(" ");
epd->print(holdvalues ? bvf_tws_old.unit : bvf_tws.unit); if(holdvalues == false){
getdisplay().print(unit4); // Unit
}
else{
getdisplay().print(unit4old); // 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; float pi = 3.141592;
epd->fillCircle(200, 150, rInstrument + 10, commonData->fgcolor); // Outer circle getdisplay().fillCircle(200, 150, rInstrument + 10, commonData->fgcolor); // Outer circle
epd->fillCircle(200, 150, rInstrument + 7, commonData->bgcolor); // Outer circle getdisplay().fillCircle(200, 150, rInstrument + 7, commonData->bgcolor); // Outer circle
epd->fillCircle(200, 150, rInstrument - 10, commonData->fgcolor); // Inner circle getdisplay().fillCircle(200, 150, rInstrument - 10, commonData->fgcolor); // Inner circle
epd->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)
{ {
@@ -204,7 +240,8 @@ 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="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;
@@ -223,17 +260,17 @@ public:
// Print text centered on position x, y // Print text centered on position x, y
int16_t x1, y1; // Return values of getTextBounds int16_t x1, y1; // Return values of getTextBounds
uint16_t w, h; // Return values of getTextBounds uint16_t w, h; // Return values of getTextBounds
epd->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
epd->setCursor(x-w/2, y+h/2); getdisplay().setCursor(x-w/2, y+h/2);
if(i % 30 == 0){ if(i % 30 == 0){
epd->setFont(&Ubuntu_Bold8pt8b); // TODO move out of loop getdisplay().setFont(&Ubuntu_Bold8pt8b);
epd->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);
epd->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);
@@ -244,10 +281,10 @@ public:
float xx2 = +dx; float xx2 = +dx;
float yy1 = -(rInstrument-10); float yy1 = -(rInstrument-10);
float yy2 = -(rInstrument+10); float yy2 = -(rInstrument+10);
epd->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);
epd->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);
} }
@@ -255,16 +292,16 @@ public:
// Draw wind pointer // Draw wind pointer
float startwidth = 8; // Start width of pointer float startwidth = 8; // Start width of pointer
if (bv_aws->valid || holdvalues || simulation) { if(valid2 == true || holdvalues == true || simulation == true){
float sinx = sin(bv_awa->value); // Wind direction float sinx=sin(value1); // Wind direction
float cosx = cos(bv_awa->value); float cosx=cos(value1);
// 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-15); float yy2 = -(rInstrument-15);
epd->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
@@ -274,42 +311,47 @@ public:
float ix2 = -endwidth; float ix2 = -endwidth;
float iy1 = -(rInstrument-15); float iy1 = -(rInstrument-15);
float iy2 = -endwidth; float iy2 = -endwidth;
epd->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
epd->fillCircle(200, 150, startwidth + 6, commonData->bgcolor); getdisplay().fillCircle(200, 150, startwidth + 6, commonData->bgcolor);
epd->fillCircle(200, 150, startwidth + 4, commonData->fgcolor); getdisplay().fillCircle(200, 150, startwidth + 4, commonData->fgcolor);
// ********************************************************************* //*******************************************************************************************
// Show value DBT // Show values DBT
epd->setFont(&DSEG7Classic_BoldItalic16pt7b); getdisplay().setFont(&DSEG7Classic_BoldItalic16pt7b);
epd->setCursor(160, 200); getdisplay().setCursor(160, 200);
epd->print(holdvalues ? bvf_dbt_old.value : bvf_dbt.value); getdisplay().print(svalue5); // Value
epd->setFont(&Ubuntu_Bold8pt8b); getdisplay().setFont(&Ubuntu_Bold8pt8b);
epd->setCursor(190, 215); getdisplay().setCursor(190, 215);
epd->print(" "); getdisplay().print(" ");
epd->print(holdvalues ? bvf_dbt_old.unit : bvf_dbt.unit); if(holdvalues == false){
getdisplay().print(unit5); // Unit
}
else{
getdisplay().print(unit5old); // Unit
}
// Show value STW // Show values STW
epd->setFont(&DSEG7Classic_BoldItalic16pt7b); getdisplay().setFont(&DSEG7Classic_BoldItalic16pt7b);
epd->setCursor(160, 130); getdisplay().setCursor(160, 130);
epd->print(holdvalues ? bvf_stw_old.value : bvf_stw.value); getdisplay().print(svalue6); // Value
epd->setFont(&Ubuntu_Bold8pt8b); getdisplay().setFont(&Ubuntu_Bold8pt8b);
epd->setCursor(190, 90); getdisplay().setCursor(190, 90);
epd->print(" "); getdisplay().print(" ");
epd->print(holdvalues ? bvf_stw_old.unit : bvf_stw.unit); if(holdvalues == false){
getdisplay().print(unit6); // Unit
}
else{
getdisplay().print(unit6old); // Unit
}
return PAGE_UPDATE; return PAGE_UPDATE;
}; };
void leavePage(PageData &pageData) {
logger->logDebug(GwLog::LOG, "Leaving PageWindRose");
}
}; };
static Page *createPage(CommonData &common){ static Page *createPage(CommonData &common){

View File

@@ -1,37 +1,35 @@
// SPDX-License-Identifier: GPL-2.0-or-later
#if defined BOARD_OBP60S3 || defined BOARD_OBP40S3 #if defined BOARD_OBP60S3 || defined BOARD_OBP40S3
#include "Pagedata.h" #include "Pagedata.h"
#include "OBP60Extensions.h" #include "OBP60Extensions.h"
#ifdef ENABLE_CALIBRATION
#include "BoatDataCalibration.h"
#endif
class PageWindRoseFlex : public Page class PageWindRoseFlex : public Page
{ {
private:
String lengthformat;
int16_t lp = 80; // Pointer length int16_t lp = 80; // Pointer length
char source = 'A'; // data source (A)pparent | (T)rue char source = 'A'; // data source (A)pparent | (T)rue
String ssource="App."; // String for Data Source
public: public:
PageWindRoseFlex(CommonData &common) : Page(common) PageWindRoseFlex(CommonData &common){
{ commonData = &common;
logger->logDebug(GwLog::LOG, "Instantiate PageWindRoseFlex"); common.logger->logDebug(GwLog::LOG,"Instantiate PageWindRoseFlex");
// Get config data
lengthformat = config->getString(config->lengthFormat);
} }
virtual void setupKeys(){
void setupKeys() {
Page::setupKeys(); Page::setupKeys();
commonData->keydata[1].label = "SRC"; commonData->keydata[1].label = "SRC";
} }
// Key functions // Key functions
int handleKey(int key) { virtual int handleKey(int key){
if(key == 2){
// Code for set source
if(source == 'A'){
source = 'T';
} else {
source = 'A';
}
}
return key; // Commit the key
// Code for keylock // Code for keylock
if(key == 11){ if(key == 11){
commonData->keylock = !commonData->keylock; commonData->keylock = !commonData->keylock;
@@ -40,17 +38,9 @@ public:
return key; return key;
} }
void displayNew(PageData &pageData) {
#ifdef BOARD_OBP60S3
// Clear optical warning
if (flashLED == "Limit Violation") {
setBlinkingLED(false);
setFlashLED(false);
}
#endif
};
int displayPage(PageData &pageData){ int displayPage(PageData &pageData){
GwConfigHandler *config = commonData->config;
GwLog *logger = commonData->logger;
static String svalue1old = ""; static String svalue1old = "";
static String unit1old = ""; static String unit1old = "";
@@ -64,6 +54,18 @@ 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
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 angle GwApi::BoatValue *bvalue1; // Value 1 for angle
GwApi::BoatValue *bvalue2; // Value 2 for speed GwApi::BoatValue *bvalue2; // Value 2 for speed
@@ -76,13 +78,10 @@ public:
} }
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
#ifdef ENABLE_CALIBRATION
calibrationData.calibrateInstance(bvalue1, logger); // Check if boat data value is to be calibrated
#endif
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 = commonData->fmt->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 = commonData->fmt->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
@@ -96,32 +95,34 @@ public:
} }
String name2 = bvalue2->getName().c_str(); // Value name 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
#ifdef ENABLE_CALIBRATION
calibrationData.calibrateInstance(bvalue2, logger); // Check if boat data value is to be calibrated
#endif
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
if (simulation) { if (simulation) {
value2 = 0.62731; // some random value value2 = 0.62731; // some random value
} }
String svalue2 = commonData->fmt->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 = commonData->fmt->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 value for bottom left corner // Get boat value for bottom left corner
GwApi::BoatValue *bvalue3 = pageData.values[0]; 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
#ifdef ENABLE_CALIBRATION if (name3.length()>3){
calibrationData.calibrateInstance(bvalue3, logger); // Check if boat data value is to be calibrated name3font=Ubuntu_Bold8pt8b;
#endif }
else{
name3font=Ubuntu_Bold12pt8b;
}
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 = commonData->fmt->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 = commonData->fmt->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
@@ -131,132 +132,162 @@ public:
GwApi::BoatValue *bvalue4 = pageData.values[1]; 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
#ifdef ENABLE_CALIBRATION if (name4.length()>3){
calibrationData.calibrateInstance(bvalue4, logger); // Check if boat data value is to be calibrated name4font=Ubuntu_Bold8pt8b;
#endif }
else{
name4font=Ubuntu_Bold12pt8b;
}
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 = commonData->fmt->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 = commonData->fmt->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 value for bottom right corner // Get boat value bottom right corner
GwApi::BoatValue *bvalue5 = pageData.values[2]; 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
#ifdef ENABLE_CALIBRATION if (name5.length()>3){
calibrationData.calibrateInstance(bvalue5, logger); // Check if boat data value is to be calibrated name5font=Ubuntu_Bold8pt8b;
#endif }
else{
name5font=Ubuntu_Bold12pt8b;
}
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 = commonData->fmt->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 = commonData->fmt->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 value for center // Get boat value for center (name is not displayed)
GwApi::BoatValue *bvalue6 = pageData.values[3]; 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
#ifdef ENABLE_CALIBRATION if (name6.length()>3){
calibrationData.calibrateInstance(bvalue6, logger); // Check if boat data value is to be calibrated name6font=Ubuntu_Bold8pt8b;
#endif }
else{
name6font=Ubuntu_Bold8pt8b;
}
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 = commonData->fmt->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 = commonData->fmt->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)
if(String(flashLED) == "Limit Violation"){
setBlinkingLED(false);
setFlashLED(false);
}
// Logging boat values // Logging boat values
if (bvalue1 == NULL) return PAGE_OK; // WTF why this statement? if (bvalue1 == NULL) return PAGE_OK; // WTF why this statement?
logger->logDebug(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
//*********************************************************** //***********************************************************
// Set display in partial refresh mode // Set display in partial refresh mode
epd->setPartialWindow(0, 0, epd->width(), epd->height()); // Set partial update getdisplay().setPartialWindow(0, 0, getdisplay().width(), getdisplay().height()); // Set partial update
epd->setTextColor(commonData->fgcolor); getdisplay().setTextColor(commonData->fgcolor);
// Show AWS or TWS top left // Show AWS or TWS top left
epd->setFont(&DSEG7Classic_BoldItalic20pt7b); getdisplay().setFont(&DSEG7Classic_BoldItalic20pt7b);
epd->setCursor(10, 65); getdisplay().setCursor(10, 65);
epd->print(svalue2); // Value getdisplay().print(svalue2); // Value
epd->setFont(&Ubuntu_Bold12pt8b); getdisplay().setFont(&Ubuntu_Bold12pt8b);
epd->setCursor(10, 95); getdisplay().setCursor(10, 95);
epd->print(name2); // Name getdisplay().print(name2); // Name
epd->setFont(&Ubuntu_Bold8pt8b); getdisplay().setFont(&Ubuntu_Bold8pt8b);
epd->setCursor(10, 115); getdisplay().setCursor(10, 115);
epd->print(" "); getdisplay().print(" ");
epd->print(holdvalues ? unit2old : unit2); if(holdvalues == false){
getdisplay().print(unit2); // Unit
// Horizintal separator left
epd->fillRect(0, 149, 60, 3, commonData->fgcolor);
// Show value 3 (=first user-configured parameter) at bottom left
epd->setFont(&DSEG7Classic_BoldItalic20pt7b);
epd->setCursor(10, 270);
epd->print(svalue3); // Value
epd->setFont(&Ubuntu_Bold12pt8b);
epd->setCursor(10, 220);
epd->print(name3); // Name
epd->setFont(&Ubuntu_Bold8pt8b);
epd->setCursor(10, 190);
epd->print(" ");
epd->print(holdvalues ? unit3old : unit3);
// Show value 4 (=second user-configured parameter) at top right
epd->setFont(&DSEG7Classic_BoldItalic20pt7b);
epd->setCursor(295, 65);
if(valid3 == true){
epd->print(svalue4); // Value
} }
else{ else{
epd->print(commonData->fmt->placeholder); getdisplay().print(unit2old); // Unit
} }
epd->setFont(&Ubuntu_Bold12pt8b);
epd->setCursor(335, 95);
epd->print(name4); // Name
epd->setFont(&Ubuntu_Bold8pt8b);
epd->setCursor(335, 115);
epd->print(" ");
epd->print(holdvalues ? unit4old : unit4);
// 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().setCursor(10, 270);
getdisplay().print(svalue3); // Value
getdisplay().setFont(&name3font);
getdisplay().setCursor(10, 220);
getdisplay().print(name3); // Name
getdisplay().setFont(&Ubuntu_Bold8pt8b);
getdisplay().setCursor(10, 190);
getdisplay().print(" ");
if(holdvalues == false){
getdisplay().print(unit3); // Unit
}
else{
getdisplay().print(unit3old); // Unit
}
// Show value 4 (=second user-configured parameter) at top right
getdisplay().setFont(&DSEG7Classic_BoldItalic20pt7b);
getdisplay().setCursor(295, 65);
getdisplay().print(svalue4); // Value
getdisplay().setFont(&name4font);
getdisplay().setCursor(325, 95);
getdisplay().print(name4); // Name
getdisplay().setFont(&Ubuntu_Bold8pt8b);
getdisplay().setCursor(325, 115);
getdisplay().print(" ");
if(holdvalues == false){
getdisplay().print(unit4); // Unit
}
else{
getdisplay().print(unit4old); // Unit
}
// Horizintal separator right // Horizintal separator right
epd->fillRect(340, 149, 80, 3, commonData->fgcolor); getdisplay().fillRect(340, 149, 80, 3, commonData->fgcolor);
// Show value 5 (=third user-configured parameter) at bottom right // Show value 5 (=third user-configured parameter) at bottom right
epd->setFont(&DSEG7Classic_BoldItalic20pt7b); getdisplay().setFont(&DSEG7Classic_BoldItalic20pt7b);
epd->setCursor(295, 270); getdisplay().setCursor(295, 270);
epd->print(svalue5); // Value getdisplay().print(svalue5); // Value
epd->setFont(&Ubuntu_Bold12pt8b); getdisplay().setFont(&name5font);
epd->setCursor(335, 220); getdisplay().setCursor(325, 220);
epd->print(name5); // Name getdisplay().print(name5); // Name
epd->setFont(&Ubuntu_Bold8pt8b); getdisplay().setFont(&Ubuntu_Bold8pt8b);
epd->setCursor(335, 190); getdisplay().setCursor(325, 190);
epd->print(" "); getdisplay().print(" ");
epd->print(holdvalues ? unit5old : unit5); 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
epd->fillCircle(200, 150, rInstrument + 10, commonData->fgcolor); // Outer circle getdisplay().fillCircle(200, 150, rInstrument + 10, commonData->fgcolor); // Outer circle
epd->fillCircle(200, 150, rInstrument + 7, commonData->bgcolor); // Outer circle getdisplay().fillCircle(200, 150, rInstrument + 7, commonData->bgcolor); // Outer circle
epd->fillCircle(200, 150, rInstrument - 10, commonData->fgcolor); // Inner circle getdisplay().fillCircle(200, 150, rInstrument - 10, commonData->fgcolor); // Inner circle
epd->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)
{ {
@@ -283,17 +314,17 @@ public:
// Print text centered on position x, y // Print text centered on position x, y
int16_t x1, y1; // Return values of getTextBounds int16_t x1, y1; // Return values of getTextBounds
uint16_t w, h; // Return values of getTextBounds uint16_t w, h; // Return values of getTextBounds
epd->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
epd->setCursor(x-w/2, y+h/2); getdisplay().setCursor(x-w/2, y+h/2);
if(i % 30 == 0){ if(i % 30 == 0){
epd->setFont(&Ubuntu_Bold8pt8b); getdisplay().setFont(&Ubuntu_Bold8pt8b);
epd->print(ii); getdisplay().print(ii);
} }
// Draw sub scale with dots // Draw sub scale with dots
float x1c = 200 + rInstrument*sin(i/180.0*M_PI); float x1c = 200 + rInstrument*sin(i/180.0*M_PI);
float y1c = 150 - rInstrument*cos(i/180.0*M_PI); float y1c = 150 - rInstrument*cos(i/180.0*M_PI);
epd->fillCircle((int)x1c, (int)y1c, 2, commonData->fgcolor); getdisplay().fillCircle((int)x1c, (int)y1c, 2, commonData->fgcolor);
float sinx=sin(i/180.0*M_PI); float sinx=sin(i/180.0*M_PI);
float cosx=cos(i/180.0*M_PI); float cosx=cos(i/180.0*M_PI);
@@ -304,10 +335,10 @@ public:
float xx2 = +dx; float xx2 = +dx;
float yy1 = -(rInstrument-10); float yy1 = -(rInstrument-10);
float yy2 = -(rInstrument+10); float yy2 = -(rInstrument+10);
epd->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);
epd->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);
} }
@@ -324,7 +355,7 @@ public:
float xx2 = startwidth; float xx2 = startwidth;
float yy1 = -startwidth; float yy1 = -startwidth;
float yy2 = -(rInstrument-15); float yy2 = -(rInstrument-15);
epd->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
@@ -334,49 +365,57 @@ public:
float ix2 = -endwidth; float ix2 = -endwidth;
float iy1 = -(rInstrument-15); float iy1 = -(rInstrument-15);
float iy2 = -endwidth; float iy2 = -endwidth;
epd->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
epd->fillCircle(200, 150, startwidth + 6, commonData->bgcolor); getdisplay().fillCircle(200, 150, startwidth + 8, commonData->bgcolor);
epd->fillCircle(200, 150, startwidth + 4, commonData->fgcolor); getdisplay().fillCircle(200, 150, startwidth + 6, commonData->fgcolor);
getdisplay().fillCircle(200, 150, startwidth + 4, commonData->bgcolor);
getdisplay().setFont(&Ubuntu_Bold10pt8b);
if (source=='A'){
getdisplay().setCursor(193, 155);
}
else {
getdisplay().setCursor(195, 156);
}
getdisplay().print({source});
//******************************************************************************************* //*******************************************************************************************
// Show value6 (=fourth user-configured parameter) and ssource, so that they do not collide with the wind pointer // Show value6 (=fourth user-configured parameter)
if ( cos(value1) > 0){ if ( cos(value1) > 0){
//pointer points upwards //pointer points upwards
epd->setFont(&DSEG7Classic_BoldItalic16pt7b); getdisplay().setFont(&DSEG7Classic_BoldItalic16pt7b);
epd->setCursor(160, 200); getdisplay().setCursor(160, 200);
epd->print(svalue6); // Value getdisplay().print(svalue6); // Value
epd->setFont(&Ubuntu_Bold8pt8b); getdisplay().setFont(&Ubuntu_Bold8pt8b);
epd->setCursor(190, 215); getdisplay().setCursor(190, 215);
epd->print(" "); getdisplay().print(" ");
epd->print(holdvalues ? unit6old : unit6); if(holdvalues == false){
if (sin(value1) > 0) { getdisplay().print(unit6); // Unit
epd->setCursor(160, 130); }
} else { else{
epd->setCursor(220, 130); getdisplay().print(unit6old); // Unit
} }
epd->print(ssource); // true or app.
} }
else{ else{
// pointer points downwards // pointer points downwards
epd->setFont(&DSEG7Classic_BoldItalic16pt7b); getdisplay().setFont(&DSEG7Classic_BoldItalic16pt7b);
epd->setCursor(160, 130); getdisplay().setCursor(160, 130);
epd->print(svalue6); getdisplay().print(svalue6); // Value
epd->setFont(&Ubuntu_Bold8pt8b); getdisplay().setFont(&Ubuntu_Bold8pt8b);
epd->setCursor(190, 90); getdisplay().setCursor(190, 90);
epd->print(" "); getdisplay().print(" ");
epd->print(holdvalues ? unit6old : unit6); if(holdvalues == false){
if (sin(value1) > 0) { getdisplay().print(unit6); // Unit
epd->setCursor(160, 200); }
} else { else{
epd->setCursor(220, 200); getdisplay().print(unit6old); // Unit
} }
epd->print(ssource); //true or app.
} }
return PAGE_UPDATE; return PAGE_UPDATE;

View File

@@ -1,4 +1,3 @@
// SPDX-License-Identifier: GPL-2.0-or-later
#if defined BOARD_OBP60S3 || defined BOARD_OBP40S3 #if defined BOARD_OBP60S3 || defined BOARD_OBP40S3
#include "Pagedata.h" #include "Pagedata.h"
@@ -29,18 +28,15 @@ static unsigned char ship_bits[] PROGMEM = {
class PageXTETrack : public Page class PageXTETrack : public Page
{ {
private: bool simulation = false;
String trackStep; bool holdvalues = false;
double seg_step;
public: public:
PageXTETrack(CommonData &common) : Page(common) PageXTETrack(CommonData &common){
{ commonData = &common;
logger->logDebug(GwLog::LOG, "Instantiate PageXTETrack"); common.logger->logDebug(GwLog::LOG,"Instantiate PageXTETrack");
simulation = common.config->getBool(common.config->useSimuData);
// Get config data holdvalues = common.config->getBool(common.config->holdvalues);
String trackStep = config->getString(config->trackStep);
seg_step = trackStep.toDouble() * M_PI / 180;
} }
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,
@@ -49,18 +45,18 @@ public:
if (fill == true) { if (fill == true) {
// no primitive for quadrangular object // no primitive for quadrangular object
// we create it from 2 triangles // we create it from 2 triangles
epd->fillTriangle(x0, y0, x1, y1, x3, y3, color); getdisplay().fillTriangle(x0, y0, x1, y1, x3, y3, color);
epd->fillTriangle(x1, y1, x2, y2, x3, y3, color); getdisplay().fillTriangle(x1, y1, x2, y2, x3, y3, color);
} else { } else {
// draw outline // draw outline
epd->drawLine(x0, y0, x1, y1, color); getdisplay().drawLine(x0, y0, x1, y1, color);
epd->drawLine(x1, y1, x2, y2, color); getdisplay().drawLine(x1, y1, x2, y2, color);
epd->drawLine(x2, y2, x3, y3, color); getdisplay().drawLine(x2, y2, x3, y3, color);
epd->drawLine(x3, y3, x0, y0, color); getdisplay().drawLine(x3, y3, x0, y0, color);
} }
} }
int handleKey(int key) { virtual int handleKey(int key){
// Code for keylock // Code for keylock
if(key == 11){ if(key == 11){
commonData->keylock = !commonData->keylock; commonData->keylock = !commonData->keylock;
@@ -69,93 +65,96 @@ public:
return key; return key;
} }
void displayNew(PageData &pageData) { int displayPage(PageData &pageData){
#ifdef BOARD_OBP60S3 GwConfigHandler *config = commonData->config;
// Clear optical warning GwLog *logger = commonData->logger;
if (flashLED == "Limit Violation") {
// Get config data
String flashLED = config->getString(config->flashLED);
String backlightMode = config->getString(config->backlight);
String trackStep = config->getString(config->trackStep);
double seg_step = trackStep.toFloat() * PI / 180;
// Optical warning by limit violation (unused)
if(String(flashLED) == "Limit Violation"){
setBlinkingLED(false); setBlinkingLED(false);
setFlashLED(false); setFlashLED(false);
} }
#endif
};
int displayPage(PageData &pageData) {
// Logging boat values // Logging boat values
logger->logDebug(GwLog::LOG, "Drawing at PageXTETrack"); LOG_DEBUG(GwLog::LOG,"Drawing at PageXTETrack");
// Draw page // Draw page
//*********************************************************** //***********************************************************
// Set display in partial refresh mode // Set display in partial refresh mode
epd->setPartialWindow(0, 0, epd->width(), epd->height()); // Set partial update getdisplay().setPartialWindow(0, 0, getdisplay().width(), getdisplay().height()); // Set partial update
epd->setTextColor(commonData->fgcolor); getdisplay().setTextColor(commonData->fgcolor);
// descriptions // descriptions
epd->setFont(&Ubuntu_Bold8pt8b); getdisplay().setFont(&Ubuntu_Bold8pt8b);
epd->setCursor(50, 188); getdisplay().setCursor(50, 188);
epd->print("Cross-track error"); getdisplay().print("Cross-track error");
epd->setCursor(270, 188); getdisplay().setCursor(270, 188);
epd->print("Track"); getdisplay().print("Track");
epd->setCursor(45, 275); getdisplay().setCursor(45, 275);
epd->print("Distance to waypoint"); getdisplay().print("Distance to waypoint");
epd->setCursor(260, 275); getdisplay().setCursor(260, 275);
epd->print("Bearing"); getdisplay().print("Bearing");
// values // values
epd->setFont(&DSEG7Classic_BoldItalic30pt7b); getdisplay().setFont(&DSEG7Classic_BoldItalic30pt7b);
int16_t x, y; int16_t x, y;
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 = commonData->fmt->formatValue(bv_xte, *commonData).svalue; String sval_xte = formatValue(bv_xte, *commonData).svalue;
epd->getTextBounds(sval_xte, 0, 0, &x, &y, &w, &h); getdisplay().getTextBounds(sval_xte, 0, 0, &x, &y, &w, &h);
epd->setCursor(160-w, 170); getdisplay().setCursor(160-w, 170);
epd->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 = commonData->fmt->formatValue(bv_cog, *commonData).svalue; String sval_cog = formatValue(bv_cog, *commonData).svalue;
epd->getTextBounds(sval_cog, 0, 0, &x, &y, &w, &h); getdisplay().getTextBounds(sval_cog, 0, 0, &x, &y, &w, &h);
epd->setCursor(360-w, 170); getdisplay().setCursor(360-w, 170);
epd->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 = commonData->fmt->formatValue(bv_dtw, *commonData).svalue; String sval_dtw = formatValue(bv_dtw, *commonData).svalue;
epd->getTextBounds(sval_dtw, 0, 0, &x, &y, &w, &h); getdisplay().getTextBounds(sval_dtw, 0, 0, &x, &y, &w, &h);
epd->setCursor(160-w, 257); getdisplay().setCursor(160-w, 257);
epd->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 = commonData->fmt->formatValue(bv_btw, *commonData).svalue; String sval_btw = formatValue(bv_btw, *commonData).svalue;
epd->getTextBounds(sval_btw, 0, 0, &x, &y, &w, &h); getdisplay().getTextBounds(sval_btw, 0, 0, &x, &y, &w, &h);
epd->setCursor(360-w, 257); getdisplay().setCursor(360-w, 257);
epd->print(sval_btw); getdisplay().print(sval_btw);
GwApi::BoatValue *bv_wpname = pageData.values[4]; // WPName
bool valid = bv_cog->valid && bv_btw->valid; bool valid = bv_cog->valid && bv_btw->valid;
// XTETrack view // XTETrack view
// draw ship symbol (as bitmap) // draw ship symbol (as bitmap)
epd->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 = bv_wpname->svalue; sval_wpname = "Tonne 122";
} }
epd->setFont(&Ubuntu_Bold10pt8b); getdisplay().setFont(&Ubuntu_Bold10pt8b);
epd->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
// last resort: clip with ellipsis // last resort: clip with ellipsis
epd->setCursor(200 - w / 2, 60); getdisplay().setCursor(200 - w / 2, 60);
epd->print(sval_wpname); getdisplay().print(sval_wpname);
// draw course segments // draw course segments
@@ -227,7 +226,7 @@ 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", "WPName"}, // 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
); );

View File

@@ -1,12 +1,9 @@
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once #pragma once
#include <Arduino.h> #include <Arduino.h>
#include "GwApi.h" #include "GwApi.h"
#include <functional> #include <functional>
#include <vector> #include <vector>
#include "LedSpiTask.h" #include "LedSpiTask.h"
// #include "OBPDataOperations.h"
#define MAX_PAGE_NUMBER 10 // Max number of pages for show data #define MAX_PAGE_NUMBER 10 // Max number of pages for show data
@@ -54,7 +51,7 @@ typedef struct{
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
struct tm rtcTime; // UTC time from internal RTC struct tm rtcTime; // UTC time from internal RTC
bool rtcValid = false; bool rtcValid = false; // Internal RTC chip
int sunsetHour = 0; int sunsetHour = 0;
int sunsetMinute = 0; int sunsetMinute = 0;
int sunriseHour = 0; int sunriseHour = 0;
@@ -102,24 +99,18 @@ typedef struct{
uint8_t length_sec; // seconds until alarm disappeares without user interaction uint8_t length_sec; // seconds until alarm disappeares without user interaction
} AlarmData; } AlarmData;
typedef struct{
int voltage = 0;
} AvgData;
class Formatter; // forward declaration
typedef struct{ typedef struct{
GwApi::Status status; GwApi::Status status;
GwLog *logger = nullptr; GwLog *logger = nullptr;
GwConfigHandler *config = nullptr; GwConfigHandler *config = nullptr;
Formatter *fmt = nullptr;
SensorData data; SensorData data;
SunData sundata; SunData sundata;
TouchKeyData keydata[6]; TouchKeyData keydata[6];
BacklightData backlight; BacklightData backlight;
AlarmData alarm; AlarmData alarm;
AvgData avgdata;
GwApi::BoatValue *time = nullptr; GwApi::BoatValue *time = nullptr;
GwApi::BoatValue *date = nullptr; GwApi::BoatValue *date = nullptr;
float tz = 0.0; // timezone from config
uint16_t fgcolor; uint16_t fgcolor;
uint16_t bgcolor; uint16_t bgcolor;
bool keylock = false; bool keylock = false;
@@ -129,25 +120,8 @@ typedef struct{
//a base class that all pages must inherit from //a base class that all pages must inherit from
class Page{ class Page{
protected: protected:
// TODO Future: GwApi *api;
CommonData *commonData; CommonData *commonData;
GwConfigHandler *config;
GwLog *logger;
bool simulation = false;
bool holdvalues = false;
String flashLED;
String backlightMode;
public: public:
Page(CommonData &common) {
commonData = &common;
config = commonData->config;
logger = commonData->logger;
// preload generic configuration data
simulation = config->getBool(config->useSimuData);
holdvalues = config->getBool(config->holdvalues);
flashLED = config->getString(config->flashLED);
backlightMode = config->getString(config->backlight);
}
int refreshtime = 1000; int refreshtime = 1000;
virtual int displayPage(PageData &pageData)=0; virtual int displayPage(PageData &pageData)=0;
virtual void displayNew(PageData &pageData){} virtual void displayNew(PageData &pageData){}
@@ -214,3 +188,25 @@ class PageStruct{
PageData parameters; PageData parameters;
PageDescription *description = nullptr; 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{
double value; // SI value of boat data value
double cvalue; // value converted to target unit
String svalue; // value converted to target unit and formatted
String unit; // target value unit
} FormattedData;
// Formatter for boat values
FormattedData formatValue(GwApi::BoatValue *value, CommonData &commondata);
FormattedData formatValue(GwApi::BoatValue *value, CommonData &commondata, bool ignoreSimuDataSetting);
// Helper method for conversion of any data value from SI to user defined format (defined in OBP60Formatter)
double convertValue(const double &value, const String &format, CommonData &commondata);
double convertValue(const double &value, const String &name, const String &format, CommonData &commondata);

View File

@@ -1,97 +0,0 @@
Development information
=======================
This file contains some hints concerning building the firmware as well as
developing and debugging it.
Coding style
------------
WIP
Please format your new code the same as already existing code.
Some rules:
- Preprocessor directives go to column zero
- Identation is 4 spaces
Git commands
------------
Some useful commands are
git status
git fetch upstream
git diff --name-status upstream/master
git checkout upstream/master platformio.ini
# how to reset my Repo to match norbert's status
git remote add upstream https://github.com/norbert-walter/esp32-nmea2000-obp60
git fetch upstream
git checkout master
git reset --hard upstream/master
git push origin master --force
New pages
---------
To create a new page for OBP60 the following steps are necessary:
1. Create a page under /lib/obp60task/PageXXXX.cpp. You can use a simple
page e.g. PageOneValue.cpp as template
2. Set page name in PageXXXX.cpp on file name
3. Register new page in /lib/obp60task/obp60task.cpp in function
'registerAllPages'
4. Add new page in /lib/obp60task/config.json for each page type
or use gen_set.py to auto-generate the relevant section of
config.json. For further information on that read the comments
in gen_set.py.
5. Copy the changes in config.json to config_obp40.json and rename
strings accordingly. E.g. obp60 to obp40.
Using Gitpod
------------
Warning: You have to register with gitpod!
Open web page:
https://gitpod.io/#https://github.com/norbert-walter/esp32-nmea2000-obp60/tree/master/lib/obp60task
Input in terminal:
cd /workspace/esp32-nmea2000-obp60
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 for OBP60:
/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/partitions.bin
/workspace/esp32-nmea2000-obp60/.pio/build/obp60_s3/obp60_s3-all.bin
/workspace/esp32-nmea2000-obp60/.pio/build/obp60_s3/obp60_s3-dev<yyyymmdd>-all.bin
/workspace/esp32-nmea2000-obp60/.pio/build/obp60_s3/obp60_s3-dev<yyyymmdd>-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-dev<yyyymmdd>-all.bin
/workspace/esp32-nmea2000-obp60/.pio/build/obp40_s3/obp40_s3-dev<yyyymmdd>-update.bin
/workspace/esp32-nmea2000-obp60/.pio/build/obp40_s3/obp40_s3-all.bin, ready to flash to offset 0x0000
Compilation issues
------------------
? Error while linking: "undefined reference to `registerPageXXX'"
: Check if the required page is enabled for current board/environment: #if defined ...
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

@@ -1,19 +0,0 @@
- fix unstable accesspoint availability
- page refresh after page change and not connected to key codes
- config: getFloat, getDouble
- dseg7 font to new version
- new pages: ais, autopilot, epropulsion
- automate config.json generation with extra_task.py
- extend boatdata: ais, waypoints, alarms
- page clock: sunrise / sunset in local time or UTC
- implement alerts
- implement formatter as class

View File

@@ -0,0 +1,38 @@
Using Gitpod
############
Open web page:
https://gitpod.io/#https://github.com/norbert-walter/esp32-nmea2000-obp60/tree/master/lib/obp60task
Input in terminal:
cd /workspace/esp32-nmea2000-obp60
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 for OBP60
########################
/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/partitions.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-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

@@ -2,14 +2,6 @@
import subprocess import subprocess
def cleanup_patches(source, target, env):
for p in patchfiles:
patch = os.path.join(patchdir, p)
print(f"removing {patch}")
res = subprocess.run(["git", "apply", "-R", patch], capture_output=True, text=True)
if res.returncode != 0:
print(res.stderr)
patching = False patching = False
epdtype = "unknown" epdtype = "unknown"
@@ -45,7 +37,6 @@ except:
env["CPPDEFINES"].extend([("BOARD", env["BOARD"]), ("EPDTYPE", epdtype), ("PCBVERS", pcbvers), ("GXEPD2VERS", gxepd2vers)]) env["CPPDEFINES"].extend([("BOARD", env["BOARD"]), ("EPDTYPE", epdtype), ("PCBVERS", pcbvers), ("GXEPD2VERS", gxepd2vers)])
print("added hardware info to CPPDEFINES") print("added hardware info to CPPDEFINES")
print("friendly board name is '{}'".format(env.GetProjectOption("board_name")))
if patching: if patching:
# apply patches to gateway code # apply patches to gateway code
@@ -55,13 +46,11 @@ if patching:
print("patchdir not found, no patches applied") print("patchdir not found, no patches applied")
else: else:
patchfiles = [f for f in os.listdir(patchdir)] patchfiles = [f for f in os.listdir(patchdir)]
if len(patchfiles) > 0:
for p in patchfiles: for p in patchfiles:
patch = os.path.join(patchdir, p) patch = os.path.join(patchdir, p)
print(f"applying {patch}") print(f"applying {patch}")
res = subprocess.run(["git", "apply", patch], capture_output=True, text=True) res = subprocess.run(["git", "apply", patch], capture_output=True, text=True)
if res.returncode != 0: if res.returncode != 0:
print(res.stderr) print(res.stderr)
env.AddPostAction("$PROGPATH", cleanup_patches)
else: else:
print("no patches found") print("no patches found")

View File

@@ -1,273 +0,0 @@
const uint8_t Atari6pxBitmaps[] PROGMEM = {
0x00, 0xF0, 0x30, 0xCF, 0x38, 0x80, 0x53, 0xF5, 0x14, 0xFD, 0x40, 0x7E,
0x47, 0x85, 0x13, 0xE1, 0x00, 0xC7, 0x21, 0x00, 0x4E, 0x30, 0x63, 0x26,
0x39, 0xC3, 0x26, 0x40, 0x6F, 0x00, 0x7B, 0x24, 0xC0, 0xCD, 0xA5, 0x80,
0x8B, 0x3E, 0x00, 0x44, 0x10, 0x4F, 0xC4, 0x10, 0x40, 0x6F, 0x00, 0xF8,
0xF0, 0x0C, 0x66, 0x18, 0x82, 0x00, 0x7A, 0x39, 0x58, 0x61, 0xE0, 0x75,
0x50, 0xF8, 0x17, 0xA0, 0x83, 0xF0, 0xF8, 0x17, 0x80, 0x07, 0xE0, 0x39,
0x28, 0xA2, 0xFC, 0x20, 0xFE, 0x0F, 0x80, 0x07, 0xE0, 0x7A, 0x0F, 0xC0,
0x01, 0xE0, 0xF8, 0x44, 0x02, 0x10, 0x7A, 0x17, 0x80, 0x85, 0xE0, 0x7A,
0x17, 0xC0, 0x01, 0xE0, 0xF0, 0xF0, 0xF3, 0x58, 0x1B, 0x30, 0x42, 0x0C,
0xF8, 0x3E, 0xC3, 0x06, 0xC4, 0x60, 0x7A, 0x31, 0x86, 0x00, 0x01, 0x80,
0x7A, 0x19, 0xE6, 0x82, 0x07, 0xC0, 0x7A, 0x1F, 0xE1, 0x86, 0x10, 0xFA,
0x1F, 0xA0, 0x87, 0xE0, 0x7E, 0x08, 0x00, 0x01, 0xF0, 0xFA, 0x18, 0x60,
0x83, 0xE0, 0xFE, 0x0F, 0xA0, 0x83, 0xF0, 0xFE, 0x0F, 0xA0, 0x82, 0x00,
0x7E, 0x08, 0xC1, 0x05, 0xF0, 0x86, 0x1F, 0xE1, 0x86, 0x10, 0xF4, 0x44,
0x4F, 0x04, 0x10, 0x41, 0x85, 0xE0, 0x8C, 0xB9, 0x09, 0x44, 0x84, 0x21,
0x08, 0x7C, 0x83, 0xDE, 0x4C, 0x18, 0x30, 0x40, 0x83, 0xC6, 0x4C, 0x18,
0xF0, 0x40, 0x7A, 0x18, 0x40, 0x01, 0xE0, 0xFA, 0x1F, 0xA0, 0x82, 0x00,
0x7A, 0x18, 0x40, 0x01, 0xE0, 0xC0, 0xFA, 0x1F, 0xA4, 0x92, 0x30, 0x7E,
0x07, 0x80, 0x07, 0xE0, 0xFC, 0x41, 0x04, 0x10, 0x40, 0x86, 0x18, 0x61,
0x85, 0xE0, 0x86, 0x10, 0x00, 0x48, 0x40, 0x83, 0x06, 0x4C, 0x1E, 0xF0,
0x40, 0x85, 0x21, 0x00, 0x4A, 0x10, 0x86, 0x14, 0x80, 0x10, 0x40, 0xFC,
0x21, 0x10, 0x43, 0xF0, 0xFC, 0xCC, 0xCF, 0xC1, 0x81, 0x86, 0x04, 0x10,
0xF3, 0x33, 0x3F, 0x11, 0xEC, 0xC0, 0xFC, 0xD9, 0x80, 0x78, 0x10, 0x7F,
0x7C, 0x83, 0xE8, 0x61, 0x83, 0xE0, 0x7E, 0x08, 0x00, 0x7C, 0x05, 0xF8,
0x61, 0x05, 0xF0, 0x7B, 0xF8, 0x00, 0x78, 0x1A, 0x3E, 0x84, 0x20, 0x7E,
0x17, 0xC1, 0x07, 0xE0, 0x81, 0x33, 0x8C, 0x18, 0x30, 0x40, 0xC4, 0x44,
0xF0, 0x08, 0x42, 0x10, 0x87, 0xC0, 0x82, 0x2F, 0x20, 0x8A, 0x10, 0xC4,
0x44, 0x4F, 0x4B, 0xF9, 0x61, 0x84, 0xFA, 0x18, 0x61, 0x84, 0x7A, 0x18,
0x40, 0x78, 0xFA, 0x18, 0x60, 0xFA, 0x00, 0x7E, 0x18, 0x41, 0x7C, 0x10,
0xF4, 0x61, 0x08, 0x00, 0x7E, 0x00, 0x3F, 0x00, 0x4F, 0x44, 0x41, 0x86,
0x10, 0x41, 0x7C, 0x86, 0x10, 0x12, 0x10, 0x83, 0x26, 0x4C, 0x1E, 0xE0,
0x8B, 0x18, 0x08, 0x80, 0x86, 0x17, 0xC1, 0x07, 0xE0, 0xF8, 0x94, 0x8F,
0x80, 0x76, 0xC6, 0x67, 0xFF, 0xFC, 0xE6, 0x36, 0x6E, 0x41, 0x0B, 0x6F,
0x08, 0xFC, 0x7F, 0xDD, 0xFC, 0xFC, 0x7F, 0xDD, 0xFC, 0xFC, 0x7F, 0xDD,
0xFC, 0xFC, 0x7F, 0xDD, 0xFC, 0xFC, 0x7F, 0xDD, 0xFC, 0xFC, 0x7F, 0xDD,
0xFC, 0xFC, 0x7F, 0xDD, 0xFC, 0xFC, 0x7F, 0xDD, 0xFC, 0xFC, 0x7F, 0xDD,
0xFC, 0xFC, 0x7F, 0xDD, 0xFC, 0xFC, 0x7F, 0xDD, 0xFC, 0xFC, 0x7F, 0xDD,
0xFC, 0xFC, 0x7F, 0xDD, 0xFC, 0xFC, 0x7F, 0xDD, 0xFC, 0xFC, 0x7F, 0xDD,
0xFC, 0xFC, 0x7F, 0xDD, 0xFC, 0xFC, 0x7F, 0xDD, 0xFC, 0xFC, 0x7F, 0xDD,
0xFC, 0xFC, 0x7F, 0xDD, 0xFC, 0xFC, 0x7F, 0xDD, 0xFC, 0xFC, 0x7F, 0xDD,
0xFC, 0xFC, 0x7F, 0xDD, 0xFC, 0xFC, 0x7F, 0xDD, 0xFC, 0xFC, 0x7F, 0xDD,
0xFC, 0xFC, 0x7F, 0xDD, 0xFC, 0xFC, 0x7F, 0xDD, 0xFC, 0xFC, 0x7F, 0xDD,
0xFC, 0xFC, 0x7F, 0xDD, 0xFC, 0xFC, 0x7F, 0xDD, 0xFC, 0xFC, 0x7F, 0xDD,
0xFC, 0xFC, 0x7F, 0xDD, 0xFC, 0xFC, 0x7F, 0xDD, 0xFC, 0xFC, 0x7F, 0xDD,
0xFC, 0x11, 0xEC, 0x71, 0xFC, 0x11, 0xEC, 0xC4, 0x10, 0x41, 0x00, 0x10,
0x40, 0x33, 0x49, 0xE1, 0x00, 0x08, 0x1B, 0xD8, 0x40, 0x81, 0x00, 0x10,
0xE3, 0x39, 0x01, 0x02, 0x00, 0x7A, 0x5E, 0xC4, 0x11, 0xE0, 0xFF, 0xEF,
0x3C, 0xC6, 0x30, 0xEE, 0x57, 0xA1, 0x87, 0xB0, 0x06, 0x1A, 0x64, 0x86,
0x08, 0x00, 0x7D, 0x06, 0x4C, 0xD8, 0x30, 0x5F, 0x00, 0x18, 0xF9, 0xF7,
0xF0, 0x00, 0x06, 0x00, 0x08, 0x30, 0x9E, 0x7B, 0xE6, 0x00, 0xF1, 0x03,
0xBC, 0x48, 0x81, 0x82, 0x00, 0xF1, 0x02, 0x77, 0xA1, 0x43, 0x85, 0x80,
0x12, 0x24, 0x48, 0x97, 0x3E, 0x70, 0x80, 0x92, 0x4D, 0xD8, 0xFF, 0x1C,
0x73, 0xCF, 0x3F, 0xC0, 0xC4, 0x4F, 0xFF, 0xF0, 0x7C, 0x1F, 0xF0, 0xC3,
0x0F, 0xC0, 0xF8, 0x2F, 0xC3, 0x0C, 0x3F, 0xC0, 0xC3, 0x0C, 0xFF, 0x0C,
0x30, 0xC0, 0xFB, 0x0F, 0xC3, 0x0C, 0x3F, 0xC0, 0xC3, 0x0F, 0xE3, 0xCF,
0x3F, 0xC0, 0xFC, 0x13, 0xCC, 0x30, 0xC3, 0x00, 0x71, 0x47, 0x37, 0xDF,
0x7F, 0xC0, 0xFF, 0x3C, 0x7F, 0x0C, 0x30, 0xC0, 0x78, 0x00, 0x7F, 0x78,
0xF0, 0x80, 0xEF, 0x88, 0x88, 0xF8, 0x0F, 0x1E, 0xFF, 0x84, 0x08, 0x1E,
0x26, 0x00, 0xF3, 0x20, 0xC1, 0x05, 0xF1, 0x40, 0x42, 0x2C, 0x50, 0x40,
0x1E, 0xF0, 0x00, 0x07, 0x24, 0x80, 0x93, 0x80, 0xFC, 0x7F, 0xDD, 0xFC,
0xFC, 0x7F, 0xDD, 0xFC, 0xFC, 0x7F, 0xDD, 0xFC, 0xFC, 0x7F, 0xDD, 0xFC,
0xFC, 0x7F, 0xDD, 0xFC, 0xFC, 0x7F, 0xDD, 0xFC, 0xFC, 0x7F, 0xDD, 0xFC,
0xFC, 0x7F, 0xDD, 0xFC, 0xFC, 0x7F, 0xDD, 0xFC, 0xFC, 0x7F, 0xDD, 0xFC,
0xFC, 0x7F, 0xDD, 0xFC, 0xFC, 0x7F, 0xDD, 0xFC, 0xFC, 0x7F, 0xDD, 0xFC,
0xFC, 0x7F, 0xDD, 0xFC, 0xFC, 0x7F, 0xDD, 0xFC, 0xFC, 0x7F, 0xDD, 0xFC,
0xFC, 0x7F, 0xDD, 0xFC, 0xFC, 0x7F, 0xDD, 0xFC, 0xFC, 0x7F, 0xDD, 0xFC,
0xFC, 0x7F, 0xDD, 0xFC, 0xFC, 0x7F, 0xDD, 0xFC, 0xFC, 0x7F, 0xDD, 0xFC,
0xFC, 0x7F, 0xDD, 0xFC, 0xFC, 0x7F, 0xDD, 0xFC, 0xFC, 0x7F, 0xDD, 0xFC,
0xFC, 0x7F, 0xDD, 0xFC, 0xFC, 0x7F, 0xDD, 0xFC, 0xFC, 0x7F, 0xDD, 0xFC,
0xFC, 0x7F, 0xDD, 0xFC, 0xFC, 0x7F, 0xDD, 0xFC, 0xFC, 0x7F, 0xDD, 0xFC,
0xFC, 0x7F, 0xDD, 0xFC };
const GFXglyph Atari6pxGlyphs[] PROGMEM = {
{ 0, 1, 1, 7, 0, 0 }, // 0x20 ' ' U+0020
{ 1, 2, 6, 6, 2, -5 }, // 0x21 '!' U+0021
{ 3, 6, 3, 7, 0, -5 }, // 0x22 '"' U+0022
{ 6, 6, 6, 7, 0, -5 }, // 0x23 '#' U+0023
{ 11, 6, 7, 7, 0, -5 }, // 0x24 '$' U+0024
{ 17, 6, 6, 7, 0, -5 }, // 0x25 '%' U+0025
{ 22, 6, 7, 7, 0, -6 }, // 0x26 '&' U+0026
{ 28, 3, 3, 6, 1, -5 }, // 0x27 ''' U+0027
{ 30, 3, 6, 6, 1, -5 }, // 0x28 '(' U+0028
{ 33, 3, 6, 6, 1, -5 }, // 0x29 ')' U+0029
{ 36, 5, 6, 7, 1, -5 }, // 0x2a '*' U+002A
{ 40, 6, 6, 7, 0, -5 }, // 0x2b '+' U+002B
{ 45, 3, 3, 6, 1, -1 }, // 0x2c ',' U+002C
{ 47, 5, 1, 7, 1, -3 }, // 0x2d '-' U+002D
{ 48, 2, 2, 6, 2, -1 }, // 0x2e '.' U+002E
{ 49, 6, 6, 7, 0, -5 }, // 0x2f '/' U+002F
{ 54, 6, 6, 7, 0, -5 }, // 0x30 '0' U+0030
{ 59, 2, 6, 7, 3, -5 }, // 0x31 '1' U+0031
{ 61, 6, 6, 7, 0, -5 }, // 0x32 '2' U+0032
{ 66, 6, 6, 7, 0, -5 }, // 0x33 '3' U+0033
{ 71, 6, 6, 7, 0, -5 }, // 0x34 '4' U+0034
{ 76, 6, 6, 7, 0, -5 }, // 0x35 '5' U+0035
{ 81, 6, 6, 7, 0, -5 }, // 0x36 '6' U+0036
{ 86, 5, 6, 7, 0, -5 }, // 0x37 '7' U+0037
{ 90, 6, 6, 7, 0, -5 }, // 0x38 '8' U+0038
{ 95, 6, 6, 7, 0, -5 }, // 0x39 '9' U+0039
{ 100, 2, 6, 6, 2, -5 }, // 0x3a ':' U+003A
{ 102, 2, 7, 6, 2, -5 }, // 0x3b ';' U+003B
{ 104, 5, 6, 7, 1, -5 }, // 0x3c '<' U+003C
{ 108, 5, 3, 7, 1, -4 }, // 0x3d '=' U+003D
{ 110, 5, 6, 7, 1, -5 }, // 0x3e '>' U+003E
{ 114, 6, 7, 7, 0, -5 }, // 0x3f '?' U+003F
{ 120, 6, 7, 7, 0, -5 }, // 0x40 '@' U+0040
{ 126, 6, 6, 7, 0, -5 }, // 0x41 'A' U+0041
{ 131, 6, 6, 7, 0, -5 }, // 0x42 'B' U+0042
{ 136, 6, 6, 7, 0, -5 }, // 0x43 'C' U+0043
{ 141, 6, 6, 7, 0, -5 }, // 0x44 'D' U+0044
{ 146, 6, 6, 7, 0, -5 }, // 0x45 'E' U+0045
{ 151, 6, 6, 7, 0, -5 }, // 0x46 'F' U+0046
{ 156, 6, 6, 7, 0, -5 }, // 0x47 'G' U+0047
{ 161, 6, 6, 7, 0, -5 }, // 0x48 'H' U+0048
{ 166, 4, 6, 7, 1, -5 }, // 0x49 'I' U+0049
{ 169, 6, 6, 7, 0, -5 }, // 0x4a 'J' U+004A
{ 174, 5, 6, 7, 1, -5 }, // 0x4b 'K' U+004B
{ 178, 5, 6, 7, 1, -5 }, // 0x4c 'L' U+004C
{ 182, 7, 6, 8, 0, -5 }, // 0x4d 'M' U+004D
{ 188, 7, 6, 8, 0, -5 }, // 0x4e 'N' U+004E
{ 194, 6, 6, 7, 0, -5 }, // 0x4f 'O' U+004F
{ 199, 6, 6, 7, 0, -5 }, // 0x50 'P' U+0050
{ 204, 6, 7, 7, 0, -5 }, // 0x51 'Q' U+0051
{ 210, 6, 6, 7, 0, -5 }, // 0x52 'R' U+0052
{ 215, 6, 6, 7, 0, -5 }, // 0x53 'S' U+0053
{ 220, 6, 6, 7, 0, -5 }, // 0x54 'T' U+0054
{ 225, 6, 6, 7, 0, -5 }, // 0x55 'U' U+0055
{ 230, 6, 6, 7, 0, -5 }, // 0x56 'V' U+0056
{ 235, 7, 6, 8, 0, -5 }, // 0x57 'W' U+0057
{ 241, 6, 6, 7, 0, -5 }, // 0x58 'X' U+0058
{ 246, 6, 6, 7, 0, -5 }, // 0x59 'Y' U+0059
{ 251, 6, 6, 7, 0, -5 }, // 0x5a 'Z' U+005A
{ 256, 4, 6, 7, 1, -5 }, // 0x5b '[' U+005B
{ 259, 6, 6, 7, 0, -5 }, // 0x5c '\' U+005C
{ 264, 4, 6, 7, 2, -5 }, // 0x5d ']' U+005D
{ 267, 6, 3, 7, 0, -5 }, // 0x5e '^' U+005E
{ 270, 6, 1, 7, 0, 0 }, // 0x5f '_' U+005F
{ 271, 3, 3, 6, 1, -5 }, // 0x60 '`' U+0060
{ 273, 6, 5, 7, 0, -4 }, // 0x61 'a' U+0061
{ 277, 6, 6, 7, 0, -5 }, // 0x62 'b' U+0062
{ 282, 6, 5, 7, 0, -4 }, // 0x63 'c' U+0063
{ 286, 6, 6, 7, 0, -5 }, // 0x64 'd' U+0064
{ 291, 6, 5, 7, 0, -4 }, // 0x65 'e' U+0065
{ 295, 5, 6, 7, 1, -5 }, // 0x66 'f' U+0066
{ 299, 6, 6, 7, 0, -4 }, // 0x67 'g' U+0067
{ 304, 7, 6, 8, 0, -5 }, // 0x68 'h' U+0068
{ 310, 4, 5, 7, 1, -4 }, // 0x69 'i' U+0069
{ 313, 5, 7, 7, 0, -5 }, // 0x6a 'j' U+006A
{ 318, 6, 6, 7, 0, -5 }, // 0x6b 'k' U+006B
{ 323, 4, 6, 7, 1, -5 }, // 0x6c 'l' U+006C
{ 326, 6, 5, 7, 0, -4 }, // 0x6d 'm' U+006D
{ 330, 6, 5, 7, 0, -4 }, // 0x6e 'n' U+006E
{ 334, 6, 5, 7, 0, -4 }, // 0x6f 'o' U+006F
{ 338, 6, 6, 7, 0, -4 }, // 0x70 'p' U+0070
{ 343, 6, 6, 7, 0, -4 }, // 0x71 'q' U+0071
{ 348, 5, 5, 7, 1, -4 }, // 0x72 'r' U+0072
{ 352, 5, 5, 7, 1, -4 }, // 0x73 's' U+0073
{ 356, 4, 6, 7, 1, -5 }, // 0x74 't' U+0074
{ 359, 6, 5, 7, 0, -4 }, // 0x75 'u' U+0075
{ 363, 6, 5, 7, 0, -4 }, // 0x76 'v' U+0076
{ 367, 7, 5, 8, 0, -4 }, // 0x77 'w' U+0077
{ 372, 5, 5, 7, 0, -4 }, // 0x78 'x' U+0078
{ 376, 6, 6, 7, 0, -4 }, // 0x79 'y' U+0079
{ 381, 5, 5, 7, 1, -4 }, // 0x7a 'z' U+007A
{ 385, 4, 6, 6, 1, -5 }, // 0x7b '{' U+007B
{ 388, 2, 7, 7, 4, -5 }, // 0x7c '|' U+007C
{ 390, 4, 6, 6, 1, -5 }, // 0x7d '}' U+007D
{ 393, 6, 5, 7, 0, -5 }, // 0x7e '~' U+007E
{ 397, 5, 6, 6, 0, -5 }, // 0x7f 'REPLACEMENT CHARACTER *' U+2370
{ 401, 5, 6, 6, 0, -5 }, // 0x80 'NO-BREAK SPACE' U+00A0
{ 405, 5, 6, 6, 0, -5 }, // 0x81 'INVERTED EXCLAMATION MARK' U+00A1
{ 409, 5, 6, 6, 0, -5 }, // 0x82 'CENT SIGN' U+00A2
{ 413, 5, 6, 6, 0, -5 }, // 0x83 'POUND SIGN' U+00A3
{ 417, 5, 6, 6, 0, -5 }, // 0x84 'CURRENCY SIGN' U+00A4
{ 421, 5, 6, 6, 0, -5 }, // 0x85 'YEN SIGN' U+00A5
{ 425, 5, 6, 6, 0, -5 }, // 0x86 'BROKEN BAR' U+00A6
{ 429, 5, 6, 6, 0, -5 }, // 0x87 'SECTION SIGN' U+00A7
{ 433, 5, 6, 6, 0, -5 }, // 0x88 'DIAERESIS' U+00A8
{ 437, 5, 6, 6, 0, -5 }, // 0x89 'COPYRIGHT SIGN' U+00A9
{ 441, 5, 6, 6, 0, -5 }, // 0x8a 'FEMININE ORDINAL INDICATOR' U+00AA
{ 445, 5, 6, 6, 0, -5 }, // 0x8b 'LEFT-POINTING DOUBLE ANGLE QUOTATION MARK' U+00AB
{ 449, 5, 6, 6, 0, -5 }, // 0x8c 'NOT SIGN' U+00AC
{ 453, 5, 6, 6, 0, -5 }, // 0x8d 'SOFT HYPHEN' U+00AD
{ 457, 5, 6, 6, 0, -5 }, // 0x8e 'REGISTERED SIGN' U+00AE
{ 461, 5, 6, 6, 0, -5 }, // 0x8f 'MACRON' U+00AF
{ 465, 5, 6, 6, 0, -5 }, // 0x90 'DEGREE SIGN' U+00B0
{ 469, 5, 6, 6, 0, -5 }, // 0x91 'PLUS-MINUS SIGN' U+00B1
{ 473, 5, 6, 6, 0, -5 }, // 0x92 'SUPERSCRIPT TWO' U+00B2
{ 477, 5, 6, 6, 0, -5 }, // 0x93 'SUPERSCRIPT THREE' U+00B3
{ 481, 5, 6, 6, 0, -5 }, // 0x94 'ACUTE ACCENT' U+00B4
{ 485, 5, 6, 6, 0, -5 }, // 0x95 'MICRO SIGN' U+00B5
{ 489, 5, 6, 6, 0, -5 }, // 0x96 'PILCROW SIGN' U+00B6
{ 493, 5, 6, 6, 0, -5 }, // 0x97 'MIDDLE DOT' U+00B7
{ 497, 5, 6, 6, 0, -5 }, // 0x98 'CEDILLA' U+00B8
{ 501, 5, 6, 6, 0, -5 }, // 0x99 'SUPERSCRIPT ONE' U+00B9
{ 505, 5, 6, 6, 0, -5 }, // 0x9a 'MASCULINE ORDINAL INDICATOR' U+00BA
{ 509, 5, 6, 6, 0, -5 }, // 0x9b 'RIGHT-POINTING DOUBLE ANGLE QUOTATION MARK' U+00BB
{ 513, 5, 6, 6, 0, -5 }, // 0x9c 'VULGAR FRACTION ONE QUARTER' U+00BC
{ 517, 5, 6, 6, 0, -5 }, // 0x9d 'VULGAR FRACTION ONE HALF' U+00BD
{ 521, 5, 6, 6, 0, -5 }, // 0x9e 'VULGAR FRACTION THREE QUARTERS' U+00BE
{ 525, 5, 6, 6, 0, -5 }, // 0x9f 'INVERTED QUESTION MARK' U+00BF
{ 529, 6, 5, 7, 0, -4 }, // 0xa0 'LATIN CAPITAL LETTER A WITH GRAVE' U+00C0
{ 533, 6, 7, 7, 0, -5 }, // 0xa1 'LATIN CAPITAL LETTER A WITH ACUTE' U+00C1
{ 539, 6, 7, 7, 0, -5 }, // 0xa2 'LATIN CAPITAL LETTER A WITH CIRCUMFLEX' U+00C2
{ 545, 7, 6, 7, 0, -5 }, // 0xa3 'LATIN CAPITAL LETTER A WITH TILDE' U+00C3
{ 551, 7, 6, 7, 0, -5 }, // 0xa4 'LATIN CAPITAL LETTER A WITH DIAERESIS' U+00C4
{ 557, 6, 6, 7, 0, -5 }, // 0xa5 'LATIN CAPITAL LETTER A WITH RING ABOVE' U+00C5
{ 562, 6, 6, 7, 0, -5 }, // 0xa6 'LATIN CAPITAL LETTER AE' U+00C6
{ 567, 6, 6, 7, 0, -5 }, // 0xa7 'LATIN CAPITAL LETTER C WITH CEDILLA' U+00C7
{ 572, 7, 6, 7, 0, -5 }, // 0xa8 'LATIN CAPITAL LETTER E WITH GRAVE' U+00C8
{ 578, 7, 7, 7, 0, -5 }, // 0xa9 'LATIN CAPITAL LETTER E WITH ACUTE' U+00C9
{ 585, 7, 7, 7, 0, -5 }, // 0xaa 'LATIN CAPITAL LETTER E WITH CIRCUMFLEX' U+00CA
{ 592, 6, 7, 7, 0, -5 }, // 0xab 'LATIN CAPITAL LETTER E WITH DIAERESIS' U+00CB
{ 598, 7, 7, 7, 0, -5 }, // 0xac 'LATIN CAPITAL LETTER I WITH GRAVE' U+00CC
{ 605, 7, 7, 7, 0, -5 }, // 0xad 'LATIN CAPITAL LETTER I WITH ACUTE' U+00CD
{ 612, 7, 7, 8, 1, -5 }, // 0xae 'LATIN CAPITAL LETTER I WITH CIRCUMFLEX' U+00CE
{ 619, 3, 7, 6, 1, -5 }, // 0xaf 'LATIN CAPITAL LETTER I WITH DIAERESIS' U+00CF
{ 622, 6, 7, 7, 0, -5 }, // 0xb0 'LATIN CAPITAL LETTER ETH' U+00D0
{ 628, 4, 7, 7, 1, -5 }, // 0xb1 'LATIN CAPITAL LETTER N WITH TILDE' U+00D1
{ 632, 6, 7, 7, 0, -5 }, // 0xb2 'LATIN CAPITAL LETTER O WITH GRAVE' U+00D2
{ 638, 6, 7, 7, 0, -5 }, // 0xb3 'LATIN CAPITAL LETTER O WITH ACUTE' U+00D3
{ 644, 6, 7, 7, 0, -5 }, // 0xb4 'LATIN CAPITAL LETTER O WITH CIRCUMFLEX' U+00D4
{ 650, 6, 7, 7, 0, -5 }, // 0xb5 'LATIN CAPITAL LETTER O WITH TILDE' U+00D5
{ 656, 6, 7, 7, 0, -5 }, // 0xb6 'LATIN CAPITAL LETTER O WITH DIAERESIS' U+00D6
{ 662, 6, 7, 7, 0, -5 }, // 0xb7 'MULTIPLICATION SIGN' U+00D7
{ 668, 6, 7, 7, 0, -5 }, // 0xb8 'LATIN CAPITAL LETTER O WITH STROKE' U+00D8
{ 674, 6, 7, 7, 0, -5 }, // 0xb9 'LATIN CAPITAL LETTER U WITH GRAVE' U+00D9
{ 680, 6, 5, 7, 0, -4 }, // 0xba 'LATIN CAPITAL LETTER U WITH ACUTE' U+00DA
{ 684, 8, 7, 8, 0, -5 }, // 0xbb 'LATIN CAPITAL LETTER U WITH CIRCUMFLEX' U+00DB
{ 691, 7, 7, 8, 1, -5 }, // 0xbc 'LATIN CAPITAL LETTER U WITH DIAERESIS' U+00DC
{ 698, 6, 7, 7, 0, -5 }, // 0xbd 'LATIN CAPITAL LETTER Y WITH ACUTE' U+00DD
{ 704, 7, 7, 7, 0, -5 }, // 0xbe 'LATIN CAPITAL LETTER THORN' U+00DE
{ 711, 6, 6, 7, 0, -5 }, // 0xbf 'LATIN SMALL LETTER SHARP S' U+00DF
{ 716, 5, 6, 6, 0, -5 }, // 0xc0 'LATIN SMALL LETTER A WITH GRAVE' U+00E0
{ 720, 5, 6, 6, 0, -5 }, // 0xc1 'LATIN SMALL LETTER A WITH ACUTE' U+00E1
{ 724, 5, 6, 6, 0, -5 }, // 0xc2 'LATIN SMALL LETTER A WITH CIRCUMFLEX' U+00E2
{ 728, 5, 6, 6, 0, -5 }, // 0xc3 'LATIN SMALL LETTER A WITH TILDE' U+00E3
{ 732, 5, 6, 6, 0, -5 }, // 0xc4 'LATIN SMALL LETTER A WITH DIAERESIS' U+00E4
{ 736, 5, 6, 6, 0, -5 }, // 0xc5 'LATIN SMALL LETTER A WITH RING ABOVE' U+00E5
{ 740, 5, 6, 6, 0, -5 }, // 0xc6 'LATIN SMALL LETTER AE' U+00E6
{ 744, 5, 6, 6, 0, -5 }, // 0xc7 'LATIN SMALL LETTER C WITH CEDILLA' U+00E7
{ 748, 5, 6, 6, 0, -5 }, // 0xc8 'LATIN SMALL LETTER E WITH GRAVE' U+00E8
{ 752, 5, 6, 6, 0, -5 }, // 0xc9 'LATIN SMALL LETTER E WITH ACUTE' U+00E9
{ 756, 5, 6, 6, 0, -5 }, // 0xca 'LATIN SMALL LETTER E WITH CIRCUMFLEX' U+00EA
{ 760, 5, 6, 6, 0, -5 }, // 0xcb 'LATIN SMALL LETTER E WITH DIAERESIS' U+00EB
{ 764, 5, 6, 6, 0, -5 }, // 0xcc 'LATIN SMALL LETTER I WITH GRAVE' U+00EC
{ 768, 5, 6, 6, 0, -5 }, // 0xcd 'LATIN SMALL LETTER I WITH ACUTE' U+00ED
{ 772, 5, 6, 6, 0, -5 }, // 0xce 'LATIN SMALL LETTER I WITH CIRCUMFLEX' U+00EE
{ 776, 5, 6, 6, 0, -5 }, // 0xcf 'LATIN SMALL LETTER I WITH DIAERESIS' U+00EF
{ 780, 5, 6, 6, 0, -5 }, // 0xd0 'LATIN SMALL LETTER ETH' U+00F0
{ 784, 5, 6, 6, 0, -5 }, // 0xd1 'LATIN SMALL LETTER N WITH TILDE' U+00F1
{ 788, 5, 6, 6, 0, -5 }, // 0xd2 'LATIN SMALL LETTER O WITH GRAVE' U+00F2
{ 792, 5, 6, 6, 0, -5 }, // 0xd3 'LATIN SMALL LETTER O WITH ACUTE' U+00F3
{ 796, 5, 6, 6, 0, -5 }, // 0xd4 'LATIN SMALL LETTER O WITH CIRCUMFLEX' U+00F4
{ 800, 5, 6, 6, 0, -5 }, // 0xd5 'LATIN SMALL LETTER O WITH TILDE' U+00F5
{ 804, 5, 6, 6, 0, -5 }, // 0xd6 'LATIN SMALL LETTER O WITH DIAERESIS' U+00F6
{ 808, 5, 6, 6, 0, -5 }, // 0xd7 'DIVISION SIGN' U+00F7
{ 812, 5, 6, 6, 0, -5 }, // 0xd8 'LATIN SMALL LETTER O WITH STROKE' U+00F8
{ 816, 5, 6, 6, 0, -5 }, // 0xd9 'LATIN SMALL LETTER U WITH GRAVE' U+00F9
{ 820, 5, 6, 6, 0, -5 }, // 0xda 'LATIN SMALL LETTER U WITH ACUTE' U+00FA
{ 824, 5, 6, 6, 0, -5 }, // 0xdb 'LATIN SMALL LETTER U WITH CIRCUMFLEX' U+00FB
{ 828, 5, 6, 6, 0, -5 }, // 0xdc 'LATIN SMALL LETTER U WITH DIAERESIS' U+00FC
{ 832, 5, 6, 6, 0, -5 }, // 0xdd 'LATIN SMALL LETTER Y WITH ACUTE' U+00FD
{ 836, 5, 6, 6, 0, -5 }, // 0xde 'LATIN SMALL LETTER THORN' U+00FE
{ 840, 5, 6, 6, 0, -5 } }; // 0xdf 'LATIN SMALL LETTER Y WITH DIAERESIS' U+000FF
const GFXfont Atari6px PROGMEM = {
(uint8_t *)Atari6pxBitmaps,
(GFXglyph *)Atari6pxGlyphs,
0x20, 0xDF, 9 };
// Approx. 2195 bytes

View File

@@ -20,7 +20,7 @@ import getopt
import re import re
import json import json
__version__ = "1.3" __version__ = "0.3"
def detect_pages(filename): def detect_pages(filename):
# returns a dictionary with page name and the number of gui fields # returns a dictionary with page name and the number of gui fields
@@ -90,7 +90,7 @@ def create_json(device, no_of_pages, pagedata):
category = f"{device.upper()} Page {page_no}" category = f"{device.upper()} Page {page_no}"
capabilities = {device.lower(): "true"} capabilities = {device.lower(): "true"}
visiblepages = [vp for vp in range(page_no, no_of_pages + 1)] visiblepages = [str(vp) for vp in range(page_no, no_of_pages + 1)]
page_data = { page_data = {
"name": f"page{page_no}type", "name": f"page{page_no}type",
@@ -118,8 +118,8 @@ def create_json(device, no_of_pages, pagedata):
"category": category, "category": category,
"capabilities": capabilities, "capabilities": capabilities,
"condition": { "condition": {
f"page{page_no}type": [page for page in pages if pagedata[page] >= field_no], f"page{page_no}type": [ p for p in pages if pagedata[p] >= field_no ]
"visiblePages": visiblepages ,"visiblePages": visiblepages
} }
} }
output.append(field_data) output.append(field_data)
@@ -141,7 +141,10 @@ def create_json(device, no_of_pages, pagedata):
"description": "Fluid type in tank", "description": "Fluid type in tank",
"category": category, "category": category,
"capabilities": capabilities, "capabilities": capabilities,
"condition":[{f"page{page_no}type":"Fluid"}] "condition": {
f"page{page_no}type": "Fluid",
"visiblePages": visiblepages
}
} }
output.append(fluid_data) output.append(fluid_data)
@@ -173,13 +176,12 @@ def usage():
print("Command line options") print("Command line options")
print(" -d --device device name to use e.g. obp60") print(" -d --device device name to use e.g. obp60")
print(" -p --pages number of pages to create") print(" -p --pages number of pages to create")
print(" -m --merge json with device config to merge to")
print(" -h show this help") print(" -h show this help")
print() print()
if __name__ == '__main__': if __name__ == '__main__':
try: try:
options, remainder = getopt.getopt(sys.argv[1:], 'd:p:m:', ['device=','--pages=','--merge']) options, remainder = getopt.getopt(sys.argv[1:], 'd:p:', ['device=','--pages='])
except getopt.GetoptError as err: except getopt.GetoptError as err:
print(err) print(err)
usage() usage()
@@ -187,14 +189,11 @@ if __name__ == '__main__':
device = "obp60" device = "obp60"
no_of_pages = 10 no_of_pages = 10
merge_json = None
for opt, arg in options: for opt, arg in options:
if opt in ('-d', '--device'): if opt in ('-d', '--device'):
device = arg device = arg
elif opt in ('-p', '--pages'): elif opt in ('-p', '--pages'):
no_of_pages = int(arg) no_of_pages = int(arg)
elif opt in ('-m', '--merge'):
merge_json = arg
elif opt == '-h': elif opt == '-h':
usage() usage()
sys.exit(0) sys.exit(0)
@@ -203,12 +202,5 @@ if __name__ == '__main__':
pagedata = detect_pages("obp60task.cpp") pagedata = detect_pages("obp60task.cpp")
json_output = create_json(device, no_of_pages, pagedata) json_output = create_json(device, no_of_pages, pagedata)
if merge_json and os.path.isfile(merge_json):
with open(merge_json, 'r') as fh:
device_json = json.load(fh)
page_json = json.loads(json_output)
device_json.extend(page_json)
print(json.dumps(device_json, indent=4))
else:
# print omitting first line containing [ of JSON array # print omitting first line containing [ of JSON array
print(json_output[1:]) print(json_output[1:])

View File

@@ -0,0 +1,12 @@
git status
git fetch upstream
git diff --name-status upstream/master
git checkout upstream/master platformio.ini
# how to reset my Repo to match norbert'status
git remote add upstream https://github.com/norbert-walter/esp32-nmea2000-obp60
git fetch upstream
git checkout master
git reset --hard upstream/master
git push origin master --force

View File

@@ -1,88 +0,0 @@
/* History Buffer
*
* Storage backed buffer for sensordata
* Permanent storage only supported type: FRAM on I2C-Bus
*
* Values can be 1 to 4 bytes in length
*
* Header: 32 bytes of size
* 0 0x00 HB00 4 magic number
* 4 0x04 xxxxxxxxxxxxxxxx 16 name, space padded
* 20 0x14 n 1 byte size of values in buffer
* 21 0x15 mm 2 buffer size in count of values
* 23 0x17 dd 2 time step in seconds between values
* 25 0x19 tttt 4 unix timestamp of head
* 29 0x1d hh 2 head pointer
* 31 0x1f 0xff 1 header end sign
*
* 32 0x20 ... start of buffer data
*
* Usage example: 7 hours of data collected every 75 seconds
* TODO
*
*/
#include <stdint.h>
#include <time.h>
class HistoryBuffer {
private:
// Header prototype for permanent storage
uint8_t header[32] = {
0x41, 0x48, 0x30, 0x30, // magic: HB00
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // empty name
0x01, // byte size
0x50, 0x01, // value count
0x4b, 0x00, // time step
0x00, 0x00, 0x00, 0x00, // unix time stamp
0x00, 0x00, // head pointer
0xff // end sign
};
uint16_t head = 0; // head pointer to next new value position
time_t timestamp; // last modification time of head
uint16_t delta_t; // time step in seconds
public:
HistoryBuffer(uint16_t size) {
}
~HistoryBuffer() {
// free memory
}
void begin() {
//
}
void finish() {
}
uint16_t add() {
// returns new head value pointer
return 0;
}
uint8_t* get() {
// returns complete buffer in order new to old
return 0;
}
uint8_t getvalue(uint16_t dt) {
// Return a single value delta seconds ago
uint16_t index = head - abs(dt) / delta_t;
return 0;
}
uint8_t getvalue3() {
return 0;
}
bool clear() {
// clears buffer and permanent storage
return true;
}
};
class History {
public:
History() {
}
~History() {
}
void *addSeries() {
return nullptr;
}
};

View File

@@ -1,21 +0,0 @@
#ifndef __HBUFFER_H__
#define __HBUFFER_H__
class HistoryBuffer {
public:
HistoryBuffer(uint16_t size);
void begin();
void finish();
uint16_t add();
uint8_t* get() ;
uint8_t getvalue(uint16_t dt);
uint8_t getvalue3();
void clear();
};
class History {
public:
History();
void *addSeries();
};
#endif

View File

@@ -1,67 +0,0 @@
#define foxtrot_width 96
#define foxtrot_height 64
static unsigned char foxtrot_bits[] = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0xa8, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x54, 0x15, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0xaa, 0xaa, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x55, 0x55, 0x01, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0xa0, 0xaa, 0xaa, 0x02, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x50, 0x55, 0x55, 0x05, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0xa8, 0xaa, 0xaa, 0x2a, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x54, 0x55, 0x55, 0x55, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x80, 0xaa, 0xaa, 0xaa, 0xaa, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x40, 0x55, 0x55, 0x55, 0x55, 0x01, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0xa0, 0xaa, 0xaa, 0xaa, 0xaa, 0x0a, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x50, 0x55, 0x55, 0x55, 0x55, 0x15, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0x2a, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x00, 0x00, 0x00,
0x00, 0x00, 0x80, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0x02, 0x00, 0x00,
0x00, 0x00, 0x40, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x05, 0x00, 0x00,
0x00, 0x00, 0xa8, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0x0a, 0x00, 0x00,
0x00, 0x00, 0x54, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x15, 0x00, 0x00,
0x00, 0x00, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0x00, 0x00,
0x00, 0x00, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x01, 0x00,
0x00, 0xa0, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0x02, 0x00,
0x00, 0x50, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x05, 0x00,
0x00, 0xa8, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0x2a, 0x00,
0x00, 0x54, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x00,
0x80, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0x00,
0x40, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x01,
0xa0, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0x0a,
0x50, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x15,
0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0x2a,
0x54, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55,
0xa8, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0x0a,
0x50, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x05,
0x80, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0x02,
0x00, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x01,
0x00, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0x2a, 0x00,
0x00, 0x54, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x15, 0x00,
0x00, 0xa0, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0x0a, 0x00,
0x00, 0x40, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x05, 0x00,
0x00, 0x80, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0x00, 0x00,
0x00, 0x00, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x00, 0x00,
0x00, 0x00, 0xa8, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0x2a, 0x00, 0x00,
0x00, 0x00, 0x50, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x15, 0x00, 0x00,
0x00, 0x00, 0xa0, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0x02, 0x00, 0x00,
0x00, 0x00, 0x40, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x01, 0x00, 0x00,
0x00, 0x00, 0x00, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x54, 0x55, 0x55, 0x55, 0x55, 0x55, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0xa8, 0xaa, 0xaa, 0xaa, 0xaa, 0x0a, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x50, 0x55, 0x55, 0x55, 0x55, 0x05, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x80, 0xaa, 0xaa, 0xaa, 0xaa, 0x02, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x55, 0x55, 0x55, 0x55, 0x01, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0xaa, 0xaa, 0xaa, 0x2a, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x54, 0x55, 0x55, 0x15, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0xa0, 0xaa, 0xaa, 0x0a, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x40, 0x55, 0x55, 0x05, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x80, 0xaa, 0xaa, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x55, 0x55, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0xa8, 0x2a, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x50, 0x15, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0xa0, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };

View File

@@ -1,807 +0,0 @@
[DEFAULT]
calibration_fields = AWA, AWS, COG, DBT, HDM, PRPOS, RPOS, SOG, STW, TWA, TWS, TWD, WTemp
[deviceName]
label = system name
type = string
default = OBP40
description = system name, used for the access point and for services
check = checkSystemName
category = system
[timeServer]
label = time server
type = string
default = pool.ntp.org
description = NTP time server. Use only one hostname or IP address
category = wifi client
capabilities = obp40:true
[timeZone]
label = Time Zone
type = number
default = 0.00
description = Time zone [UTC -12...+14]
check = checkMinMax
min = -12.0
max = 14.0
category = OBP40 Settings
capabilities = obp40:true
[homeLAT]
label = Home latitude
type = number
default = 0.00000
description = Latitude of boat home location [-90.0...+90.0]
check = checkMinMax
min = -90.0
max = 90.0
category = OBP40 Settings
capabilities = obp40:true
[homeLON]
label = Home longitude
type = number
default = 0.00000
description = Longitude of boat home location [-180.0...+180.0]
check = checkMinMax
min = -180.0
max = 180.0
category = OBP40 Settings
capabilities = obp40:true
[draft]
label = Boat Draft [m]
type = number
default = 0.00
description = The draft of the boat [0...50m]
check = checkMinMax
min = 0.0
max = 50.0
category = OBP40 Settings
capabilities = obp40:true
[chainLength]
label = Anchor Chain Length [m]
type = number
default = 0
description = The length of the anchor chain [0...255m]
check = checkMinMax
min = 0
max = 255
category = OBP40 Settings
capabilities = obp40:true
[fuelTank]
label = Fuel Tank [l]
type = number
default = 0
description = Fuel tank capacity [0...5000l]
check = checkMinMax
min = 0
max = 5000
category = OBP40 Settings
capabilities = obp40:true
[fuelConsumption]
label = Fuel Consuption [l/h]
type = number
default = 0.00
description = Medium fuel consumption [0...1000l/h]
check = checkMinMax
min = 0.0
max = 1000.0
category = OBP40 Settings
capabilities = obp40:true
[waterTank]
label = Water Tank [l]
type = number
default = 0
description = Water tank capacity [0...5000l]
check = checkMinMax
min = 0
max = 5000
category = OBP40 Settings
capabilities = obp40:true
[wasteTank]
label = Waste Tank [l]
type = number
default = 0
description = Waste tank capacity [0...5000l]
check = checkMinMax
min = 0
max = 5000
category = OBP40 Settings
capabilities = obp40:true
[batteryVoltage]
label = Battery Voltage [V]
type = list
default = 12V
description = Battery Voltage [12V|24V]
list = 12V, 24V
category = OBP40 Settings
capabilities = obp40:true
[batteryType]
label = Battery Type
type = list
default = Pb
description = Type of battery [Pb|Gel|AGM|LiFePo4]
list = Pb, Gel, AGM, LiFePo4
category = OBP40 Settings
capabilities = obp40:true
[batteryCapacity]
label = Battery Capacity [Ah]
type = number
default = 0.0
description = Battery capacity [0...10000Ah]
check = checkMinMax
min = 0.0
max = 10000.0
category = OBP40 Settings
capabilities = obp40:true
[solarPower]
label = Solar Power [W]
type = number
default = 0.0
description = Solar power [0...10000W]
check = checkMinMax
min = 0.0
max = 10000.0
category = OBP40 Settings
capabilities = obp40:true
[genPower]
label = Genarator Power [W]
type = number
default = 0.0
description = Generator power [0...10000W]
check = checkMinMax
min = 0.0
max = 10000.0
category = OBP40 Settings
capabilities = obp40:true
[trackStep]
label = angle [deg]
type = number
default = 3.0
description = track step offset [1...12deg]
check = checkMinMax
min = 1.0
max = 12.0
category = OBP40 Settings
capabilities = obp40:true
[calcTrueWnds]
label = Calculate True Wind
type = boolean
default = false
description = If not available, calculate true wind data from appearant wind and other boat data
category = OBP40 Settings
capabilities = obp40:true
[lengthFormat]
label = Length Format
type = list
default = m
description = Length format [m|ft]
list = m, ft
category = OBP40 Units
capabilities = obp40:true
[distanceFormat]
label = Distance Format
type = list
default = nm
description = Distance format [m|km|nm]
list = m, km, nm
category = OBP40 Units
capabilities = obp40:true
[speedFormat]
label = Speed Format
type = list
default = kn
description = Distance format [m/s|km/h|kn]
list = m/s, km/h, kn
category = OBP40 Units
capabilities = obp40:true
[windspeedFormat]
label = Wind Speed Format
type = list
default = kn
description = Wind speed format [m/s|km/h|kn|bft]
list = m/s, km/h, kn, bft
category = OBP40 Units
capabilities = obp40:true
[tempFormat]
label = Temperature Format
type = list
default = C
description = Temperature format [K|C|F]
list = K, C, F
category = OBP40 Units
capabilities = obp40:true
[dateFormat]
label = Date Format
type = list
default = DE
description = Date format [DE|GB|US|ISO] DE: 31.12.2022, GB: 31/12/2022, US: 12/31/2022, ISO: 2022-12-31
list = DE, GB, US, ISO
category = OBP40 Units
capabilities = obp40:true
[cpuSpeed]
label = CPU Speed [MHz]
type = list
default = 160
description = CPU speed in MHz [80|160|240]
list = 80, 160, 240
category = OBP40 Hardware
capabilities = obp40:true
[useRTC]
label = RTC Modul
type = list
default = off
description = Use RTC module type [off|DS1388]
list = off, DS1388
category = OBP40 Hardware
capabilities = obp40:true
[useGPS]
label = GPS Sensor
type = list
default = off
description = Use internal GPS module type [off|NEO-6M|NEO-M8N|ATGM336H]
list = off, NEO-6M, NEO-M8N, ATGM336H
category = OBP40 Hardware
capabilities = obp40:true
[hdopAccuracy]
label = HDOP Accuracy [m]
type = number
default = 20
description = HDOP ccuracy in m for a valid GPS signal [1...50]
check = checkMinMax
min = 1
max = 50
category = OBP40 Hardware
capabilities = obp40:true
[useEnvSensor]
label = Env. Sensor
type = list
default = off
description = Use internal or external environment sensor via I2C bus [off|BME280|BMP280|BMP180|BMP085|HTU21|SHT21]
list = off, BME280, BMP280, BMP180, BMP085, HTU21, SHT21
category = OBP40 Hardware
capabilities = obp40:true
[usePowSensor1]
label = Battery Sensor
type = list
default = off
description = Use external power management sensor via I2C bus for battery [off|INA219|INA226|]
list = off, INA219, INA226
category = OBP40 Hardware
capabilities = obp40:true
[shunt1]
label = Battery Shunt
type = list
default = 10
description = Shunt current value [10A|50A|100A|200A|300A|400A|500A]
list = 10, 50, 100, 200, 300, 400, 500
category = OBP40 Hardware
capabilities = obp40:true
[usePowSensor2]
label = Solar Sensor
type = list
default = off
description = Use external power management sensor via I2C bus for solar panels [off|INA219|INA226|]
list = off, INA219, INA226
category = OBP40 Hardware
capabilities = obp40:true
[shunt2]
label = Solar Shunt
type = list
default = 10
description = Shunt current value [10A|50A|100A|200A|300A|400A|500A]
list = 10, 50, 100, 200, 300, 400, 500
category = OBP40 Hardware
capabilities = obp40:true
[usePowSensor3]
label = Gen. Sensor
type = list
default = off
description = Use external power management sensor via I2C bus for generator [off|INA219|INA226|]
list = off, INA219, INA226
category = OBP40 Hardware
capabilities = obp40:true
[shunt3]
label = Gen. Shunt
type = list
default = 10
description = Shunt current value [10A|50A|100A|200A|300A|400A|500A] @ 75mV
list = 10, 50, 100, 200, 300, 400, 500
category = OBP40 Hardware
capabilities = obp40:true
[useRotSensor]
label = Rot. Sensor
type = list
default = off
description = Use external rotation sensor via I2C bus [off|AS5600]
list = off, AS5600
category = OBP40 Hardware
capabilities = obp40:true
[rotFunction]
label = Rot. Function
type = list
default = off
description = Function for rotation sensor [off|Rudder|Wind|Mast|Keel|Trim|Boom]
list = off, Rudder, Wind, Mast, Keel, Trim, Boom
category = OBP40 Hardware
capabilities = obp40:true
[rotOffset]
label = Rot. Offset
type = number
default = 0
description = Offset for rotation sensor [-180°...+180°]
check = checkMinMax
min = -180
max = 180
category = OBP40 Hardware
capabilities = obp40:true
[rollLimit]
label = Roll Limit
type = number
default = 25
description = Limit violation for roll angle [-90°...+90°]
check = checkMinMax
min = -90
max = 90
category = OBP40 Hardware
capabilities = obp40:true
[rollOffset]
label = Roll Offset
type = number
default = 0
description = Roll offset angle [-45°...+45°]
check = checkMinMax
min = -45
max = 45
category = OBP40 Hardware
capabilities = obp40:true
[pitchOffset]
label = Pitch Offset
type = number
default = 0
description = Pitch offset angle [-45°...+45°]
check = checkMinMax
min = -45
max = 45
category = OBP40 Hardware
capabilities = obp40:true
[useTempSensor]
label = Temp. Sensor
type = boolean
default = off
description = Use max. 8 external 1Wire devices [off|DS18B20]
list = off, DS18B20
category = OBP40 Hardware
capabilities = obp40:true
[useSDCard]
label = SD Card
type = boolean
default = false
description = Use internal SD card interface [off|on]
category = OBP40 Hardware
capabilities = obp40:true
[powerMode]
label = Power Mode
type = list
default = Max Power
description = Settings for power mode
list = Max Power, Only 5.0V, Min Power
category = OBP40 Hardware
capabilities = obp40:true
[underVoltage]
label = Undervoltage
type = boolean
default = false
description = Switch off device if LiPo voltage drops below 3.65V [on|off]
category = OBP40 Hardware
capabilities = obp40:true
[useSimuData]
label = Simulation Data
type = boolean
default = false
description = Use simulation data when bus data are missing [on|off]
category = OBP40 Hardware
capabilities = obp40:true
[tSensitivity]
label = Touch Sensitivity [%%]
type = number
default = 100
description = Touch sensitivity [0...100%%] for sensor buttons
check = checkMinMax
min = 0
max = 100
category = OBP40 Calibrations
capabilities = obp40:false
[vOffset]
label = VSensor Offset
type = number
default = -1.00
description = Offset for internal voltage sensor (ESP32)
category = OBP40 Calibrations
capabilities = obp40:true
[vSlope]
label = VSensor Slope
type = number
default = 1.00
description = Slope for internal voltage sensor (ESP32)
category = OBP40 Calibrations
capabilities = obp40:true
[calInstance1]
label = Calibration Data Instance 1
type = list
default = ---
description = Data instance for calibration
list = ---, %(calibration_fields)s
category = OBP40 Calibrations
capabilities = obp40:true
[calOffset1]
label = Data Instance 1 Calibration Offset
type = number
default = 0.00
description = Offset for data instance 1
category = OBP40 Calibrations
capabilities = obp40:true
condition = calInstance1 IN %(calibration_fields)s
[calSlope1]
label = Data Instance 1 Calibration Slope
type = number
default = 1.00
description = Slope for data instance 1
category = OBP40 Calibrations
capabilities = obp40:true
condition = calInstance1 IN %(calibration_fields)s
[calSmooth1]
label = Data Instance 1 Smoothing
type = number
default = 0
description = Smoothing factor [0..10]; 0 = no smoothing
check = checkMinMax
min = 0
max = 10
category = OBP40 Calibrations
capabilities = obp40:true
condition = calInstance1 IN %(calibration_fields)s
[calInstance2]
label = Calibration Data Instance 2
type = list
default = ---
description = Data instance for calibration
list = ---, %(calibration_fields)s
category = OBP40 Calibrations
capabilities = obp40:true
[calOffset2]
label = Data Instance 2 Calibration Offset
type = number
default = 0.00
description = Offset for data instance 2
category = OBP40 Calibrations
capabilities = obp40:true
condition = calInstance2 IN %(calibration_fields)s
[calSlope2]
label = Data Instance 2 Calibration Slope
type = number
default = 1.00
description = Slope for data instance 2
category = OBP40 Calibrations
capabilities = obp40:true
condition = calInstance2 IN %(calibration_fields)s
[calSmooth2]
label = Data Instance 2 Smoothing
type = number
default = 0
description = Smoothing factor [0..10]; 0 = no smoothing
check = checkMinMax
min = 0
max = 10
category = OBP40 Calibrations
capabilities = obp40:true
condition = calInstance2 IN %(calibration_fields)s
[calInstance3]
label = Calibration Data Instance 3
type = list
default = ---
description = Data instance for calibration
list = ---, %(calibration_fields)s
category = OBP40 Calibrations
capabilities = obp40:true
[calOffset3]
label = Data Instance 3 Calibration Offset
type = number
default = 0.00
description = Offset for data instance 3
category = OBP40 Calibrations
capabilities = obp40:true
condition = calinstance3 IN %(calibration_fields)s
[calSlope3]
label = Data Instance 3 Calibration Slope
type = number
default = 1.00
description = Slope for data instance 3
category = OBP40 Calibrations
capabilities = obp40:true
condition = calinstance3 IN %(calibration_fields)s
[calSmooth3]
label = Data Instance 3 Smoothing
type = number
default = 0
description = Smoothing factor [0..10]; 0 = no smoothing
check = checkMinMax
min = 0
max = 10
category = OBP40 Calibrations
capabilities = obp40:true
condition = calinstance3 IN %(calibration_fields)s
[display]
label = Display Mode
type = list
default = Logo + QR Code
description = Settings for startup display
list = White Screen, Logo, Logo + QR Code, Off
category = OBP40 Display
capabilities = obp40:true
[displaycolor]
label = Inverted Display Mode
type = list
default = Normal
description = Invert display to white letters on black background [Normal|Inverse]
list = Normal, Inverse
category = OBP40 Display
capabilities = obp40:true
[statusLine]
label = Status Line
type = boolean
default = true
description = Show status line [on|off]
category = OBP40 Display
capabilities = obp40:true
[timeSource]
label = Status Time Source
type = list
default = GPS
description = Data source for date and time display in status line [RTC|iRTC|GPS]
dict =
iRTC:Internal real time clock (iRTC)
RTC:External real time clock (RTC)
GPS:External time via bus (GPS)
category = OBP40 Display
capabilities = obp40:true
[refresh]
label = Refresh
type = boolean
default = true
description = Refresh e-paper display after each new page request to reduce ghost effects [on|off]
category = OBP40 Display
capabilities = obp40:true
[fastRefresh]
label = Fast Refresh
type = boolean
default = false
description = Fast refresh for e-paper display [on|off]
category = OBP40 Display
capabilities = obp40:true
[fullRefreshTime]
label = Full Refresh Time [min]
type = number
default = 1
description = E-Paper full refresh time all [1...10 min]
check = checkMinMax
min = 1
max = 10
category = OBP40 Display
capabilities = obp40:true
[holdvalues]
label = Hold Values
type = boolean
default = false
description = Retain old values when data stream stops [on|off]
category = OBP40 Display
capabilities = obp40:true
[valueprecision]
label = Display value precision
type = list
default = 2
description = Maximum number of decimal places to display [1|2]
list = 1, 2
category = OBP40 Display
capabilities = obp40:true
[headerFormat]
label = Header Format
type = list
default = TEXT
description = Header format: Text or Symbols
dict =
TEXT:Text
ICON:Symbols
category = OBP40 Display
capabilities = obp40:true
[backlight]
label = Backlight Mode
type = list
default = off
description = Settings for automatic backlight mode
list = Off, Control by Sun, Control by Bus, Control by Time, Control by Key, On
category = OBP40 Display
capabilities = obp40:false
[blColor]
label = Backlight Color
type = list
default = Red
description = Backlight color
list = Red, Orange, Yellow, Green, Blue, Aqua, Violet, White
category = OBP40 Display
capabilities = obp40:false
[blBrightness]
label = Brightness [%%]
type = number
default = 50
description = Backlight brightness [20...100%%]
check = checkMinMax
min = 20
max = 100
category = OBP40 Display
capabilities = obp40:false
[flashLED]
label = Flash LED Mode
type = list
default = Off
description = Settings for flash LED
list = Off, Bus Data, GPS Fix Lost, Limit Violation
category = OBP40 Display
capabilities = obp40:false
[buzzerError]
label = Buzzer Error
type = boolean
default = false
description = Sound on error [on|off]
category = OBP40 Buzzer
capabilities = obp40:false
[buzzerGps]
label = Buzzer GPS Fix
type = boolean
default = false
description = Sound on missing or lost GPS fix
category = OBP40 Buzzer
capabilities = obp40:false
[buzzerLim]
label = Buzzer by Limits
type = boolean
default = false
description = Sound on limit violation
category = OBP40 Buzzer
capabilities = obp40:false
[buzzerMode]
label = Buzzer Mode
type = list
default = Off
description = Settings for buzzer behaviour
list = Off, Short Single Beep, Longer Single Beep, Beep until Confirmation
category = OBP40 Buzzer
capabilities = obp40:false
[buzzerPower]
label = Buzzer Power [%%]
type = number
default = 50
description = Buzzer loudness [0...100%%]
check = checkMinMax
min = 0
max = 100
category = OBP40 Buzzer
capabilities = obp40:false
[visiblePages]
label = Number of Pages
type = number
default = 10
description = Number of visible data pages [1...10]
check = checkMinMax
min = 1
max = 10
category = OBP40 Pages
capabilities = obp40:true
[startPage]
label = Start Page
type = number
default = 1
description = First page number to display after device startup
check = checkMinMax
min = 1
max = 10
category = OBP40 Pages
capabilities = obp40:true
[systemPage]
label = System Page
type = boolean
default = false
description = Use wheel button for system page or direct deep sleep mode
category = OBP40 Pages
capabilities = obp40:true
[imageFormat]
label = Screenshot Format
type = list
default = PBM
description = Graphics file format for screenshots [GIF|PBM|BMP]
dict =
GIF:Compressed image (GIF)
PBM:Portable bitmap (PBM)
BMP:Windows bitmap (BMP)
category = OBP40 Pages
capabilities = obp40:true

View File

@@ -1,857 +0,0 @@
[DEFAULT]
calibration_fields = AWA, AWS, COG, DBT, HDM, PRPOS, RPOS, SOG, STW, TWA, TWS, TWD, WTemp
[deviceName]
label = system name
type = string
default = OBP60V2
description = system name, used for the access point and for services
check = checkSystemName
category = system
[timeServer]
label = time server
type = string
default = pool.ntp.org
description = NTP time server. Use only one hostname or IP address
category = wifi client
capabilities = obp60:true
[timeZone]
label = Time Zone
type = number
default = 0.00
description = Time zone [UTC -12...+14]
check = checkMinMax
min = -12.0
max = 14.0
category = OBP60 Settings
capabilities = obp60:true
[homeLAT]
label = Home latitude
type = number
default =
description = Latitude of boat home location [-90.0...+90.0]
check = checkMinMax
min = -90.0
max = 90.0
category = OBP60 Settings
capabilities = obp60:true
[homeLON]
label = Home longitude
type = number
default =
description = Longitude of boat home location [-180.0...+180.0]
check = checkMinMax
min = -180.0
max = 180.0
category = OBP60 Settings
capabilities = obp60:true
[draft]
label = Boat Draft [m]
type = number
default = 0.00
description = The draft of the boat [0...50m]
check = checkMinMax
min = 0.0
max = 50.0
category = OBP60 Settings
capabilities = obp60:true
[chainLength]
label = Anchor Chain Length [m]
type = number
default = 0
description = The length of the anchor chain [0...255m]
check = checkMinMax
min = 0
max = 255
category = OBP60 Settings
capabilities = obp60:true
[fuelTank]
label = Fuel Tank [l]
type = number
default = 0
description = Fuel tank capacity [0...5000l]
check = checkMinMax
min = 0
max = 5000
category = OBP60 Settings
capabilities = obp60:true
[fuelConsumption]
label = Fuel Consuption [l/h]
type = number
default = 0.00
description = Medium fuel consumption [0...1000l/h]
check = checkMinMax
min = 0.0
max = 1000.0
category = OBP60 Settings
capabilities = obp60:true
[waterTank]
label = Water Tank [l]
type = number
default = 0
description = Water tank capacity [0...5000l]
check = checkMinMax
min = 0
max = 5000
category = OBP60 Settings
capabilities = obp60:true
[wasteTank]
label = Waste Tank [l]
type = number
default = 0
description = Waste tank capacity [0...5000l]
check = checkMinMax
min = 0
max = 5000
category = OBP60 Settings
capabilities = obp60:true
[batteryVoltage]
label = Battery Voltage [V]
type = list
default = 12V
description = Battery Voltage [12V|24V]
list = 12V, 24V
category = OBP60 Settings
capabilities = obp60:true
[batteryType]
label = Battery Type
type = list
default = Pb
description = Type of battery [Pb|Gel|AGM|LiFePo4]
list = Pb, Gel, AGM, LiFePo4
category = OBP60 Settings
capabilities = obp60:true
[batteryCapacity]
label = Battery Capacity [Ah]
type = number
default = 0.0
description = Battery capacity [0...10000Ah]
check = checkMinMax
min = 0.0
max = 10000.0
category = OBP60 Settings
capabilities = obp60:true
[solarPower]
label = Solar Power [W]
type = number
default = 0.0
description = Solar power [0...10000W]
check = checkMinMax
min = 0.0
max = 10000.0
category = OBP60 Settings
capabilities = obp60:true
[genPower]
label = Genarator Power [W]
type = number
default = 0.0
description = Generator power [0...10000W]
check = checkMinMax
min = 0.0
max = 10000.0
category = OBP60 Settings
capabilities = obp60:true
[trackStep]
label = angle [deg]
type = number
default = 3.0
description = track step offset [1...12deg]
check = checkMinMax
min = 1.0
max = 12.0
category = OBP60 Settings
capabilities = obp60:true
[calcTrueWnds]
label = Calculate True Wind
type = boolean
default = false
description = If not available, calculate true wind data from appearant wind and other boat data
category = OBP60 Settings
capabilities = obp60:true
[lengthFormat]
label = Length Format
type = list
default = m
description = Length format [m|ft]
list = m, ft
category = OBP60 Units
capabilities = obp60:true
[distanceFormat]
label = Distance Format
type = list
default = nm
description = Distance format [m|km|nm]
list = m, km, nm
category = OBP60 Units
capabilities = obp60:true
[speedFormat]
label = Speed Format
type = list
default = kn
description = Distance format [m/s|km/h|kn]
list = m/s, km/h, kn
category = OBP60 Units
capabilities = obp60:true
[windspeedFormat]
label = Wind Speed Format
type = list
default = kn
description = Wind speed format [m/s|km/h|kn|bft]
list = m/s, km/h, kn, bft
category = OBP60 Units
capabilities = obp60:true
[tempFormat]
label = Temperature Format
type = list
default = C
description = Temperature format [K|C|F]
list = K, C, F
category = OBP60 Units
capabilities = obp60:true
[dateFormat]
label = Date Format
type = list
default = DE
description = Date format [DE|GB|US|ISO] DE: 31.12.2022, GB: 31/12/2022, US: 12/31/2022, ISO: 2022-12-31
list = DE, GB, US, ISO
category = OBP60 Units
capabilities = obp60:true
[cpuSpeed]
label = CPU Speed [MHz]
type = list
default = 160
description = CPU speed in MHz [80|160|240]
list = 80, 160, 240
category = OBP60 Hardware
capabilities = obp60:true
[useRTC]
label = RTC Modul
type = list
default = DS1388
description = Use internal RTC module type [off|DS1388]
list = off, DS1388
category = OBP60 Hardware
capabilities = obp60:true
[useGPS]
label = GPS Sensor
type = list
default = ATGM336H
description = Use internal GPS module type [off|NEO-6M|NEO-M8N|ATGM336H]
list = off, NEO-6M, NEO-M8N, ATGM336H
category = OBP60 Hardware
capabilities = obp60:true
[hdopAccuracy]
label = HDOP Accuracy [m]
type = number
default = 20
description = HDOP ccuracy in m for a valid GPS signal [1...50]
check = checkMinMax
min = 1
max = 50
category = OBP60 Hardware
capabilities = obp60:true
[useEnvSensor]
label = Env. Sensor
type = list
default = BMP280
description = Use internal or external environment sensor via I2C bus [off|BME280|BMP280|BMP180|BMP085|HTU21|SHT21]
list = off, BME280, BMP280, BMP180, BMP085, HTU21, SHT21
category = OBP60 Hardware
capabilities = obp60:true
[usePowSensor1]
label = Battery Sensor
type = list
default = off
description = Use external power management sensor via I2C bus for battery [off|INA219|INA226|]
list = off, INA219, INA226
category = OBP60 Hardware
capabilities = obp60:true
[shunt1]
label = Battery Shunt
type = list
default = 10
description = Shunt current value [10A|50A|100A|200A|300A|400A|500A]
list = 10, 50, 100, 200, 300, 400, 500
category = OBP60 Hardware
capabilities = obp60:true
[usePowSensor2]
label = Solar Sensor
type = list
default = off
description = Use external power management sensor via I2C bus for solar panels [off|INA219|INA226|]
list = off, INA219, INA226
category = OBP60 Hardware
capabilities = obp60:true
[shunt2]
label = Solar Shunt
type = list
default = 10
description = Shunt current value [10A|50A|100A|200A|300A|400A|500A]
list = 10, 50, 100, 200, 300, 400, 500
category = OBP60 Hardware
capabilities = obp60:true
[usePowSensor3]
label = Gen. Sensor
type = list
default = off
description = Use external power management sensor via I2C bus for generator [off|INA219|INA226|]
list = off, INA219, INA226
category = OBP60 Hardware
capabilities = obp60:true
[shunt3]
label = Gen. Shunt
type = list
default = 10
description = Shunt current value [10A|50A|100A|200A|300A|400A|500A] @ 75mV
list = 10, 50, 100, 200, 300, 400, 500
category = OBP60 Hardware
capabilities = obp60:true
[useRotSensor]
label = Rot. Sensor
type = list
default = off
description = Use external rotation sensor via I2C bus [off|AS5600]
list = off, AS5600
category = OBP60 Hardware
capabilities = obp60:true
[rotFunction]
label = Rot. Function
type = list
default = off
description = Function for rotation sensor [off|Rudder|Wind|Mast|Keel|Trim|Boom]
list = off, Rudder, Wind, Mast, Keel, Trim, Boom
category = OBP60 Hardware
capabilities = obp60:true
[rotOffset]
label = Rot. Offset
type = number
default = 0
description = Offset for rotation sensor [-180°...+180°]
check = checkMinMax
min = -180
max = 180
category = OBP60 Hardware
capabilities = obp60:true
[rollLimit]
label = Roll Limit
type = number
default = 25
description = Limit violation for roll angle [-90°...+90°]
check = checkMinMax
min = -90
max = 90
category = OBP60 Hardware
capabilities = obp60:true
[rollOffset]
label = Roll Offset
type = number
default = 0
description = Roll offset angle [-45°...+45°]
check = checkMinMax
min = -45
max = 45
category = OBP60 Hardware
capabilities = obp60:true
[pitchOffset]
label = Pitch Offset
type = number
default = 0
description = Pitch offset angle [-45°...+45°]
check = checkMinMax
min = -45
max = 45
category = OBP60 Hardware
capabilities = obp60:true
[useTempSensor]
label = Temp. Sensor
type = boolean
default = off
description = Use max. 8 external 1Wire devices [off|DS18B20]
list = off, DS18B20
category = OBP60 Hardware
capabilities = obp60:true
[powerMode]
label = Power Mode
type = list
default = Max Power
description = Settings for power mode
list = Max Power, Only 5.0V, Min Power
category = OBP60 Hardware
capabilities = obp60:true
[underVoltage]
label = Undervoltage
type = boolean
default = false
description = Switch off device if voltage drops below 9V [on|off]
category = OBP60 Hardware
capabilities = obp60:true
[useSimuData]
label = Simulation Data
type = boolean
default = false
description = Use simulation data when bus data are missing [on|off]
category = OBP60 Hardware
capabilities = obp60:true
[tSensitivity]
label = Touch Sensitivity [%%]
type = number
default = 100
description = Touch sensitivity [0...100%%] for sensor buttons
check = checkMinMax
min = 0
max = 100
category = OBP60 Calibrations
capabilities = obp60:true
[vOffset]
label = VSensor Offset
type = number
default = -1.00
description = Offset for internal voltage sensor (ESP32)
category = OBP60 Calibrations
capabilities = obp60:true
[vSlope]
label = VSensor Slope
type = number
default = 1.00
description = Slope for internal voltage sensor (ESP32)
category = OBP60 Calibrations
capabilities = obp60:true
[calInstance1]
label = Calibration Data Instance 1
type = list
default = ---
description = Data instance for calibration
list = ---, %(calibration_fields)s
category = OBP60 Calibrations
capabilities = obp60:true
[calOffset1]
label = Data Instance 1 Calibration Offset
type = number
default = 0.00
description = Offset for data instance 1
category = OBP60 Calibrations
capabilities = obp60:true
condition = calInstance1 IN %(calibration_fields)s
[calSlope1]
label = Data Instance 1 Calibration Slope
type = number
default = 1.00
description = Slope for data instance 1
category = OBP60 Calibrations
capabilities = obp60:true
condition = calInstance1 IN %(calibration_fields)s
[calSmooth1]
label = Data Instance 1 Smoothing
type = number
default = 0
description = Smoothing factor [0..10]; 0 = no smoothing
check = checkMinMax
min = 0
max = 10
category = OBP60 Calibrations
capabilities = obp60:true
condition = calInstance1 IN %(calibration_fields)s
[calInstance2]
label = Calibration Data Instance 2
type = list
default = ---
description = Data instance for calibration
list = ---, %(calibration_fields)s
category = OBP60 Calibrations
capabilities = obp60:true
[calOffset2]
label = Data Instance 2 Calibration Offset
type = number
default = 0.00
description = Offset for data instance 2
category = OBP60 Calibrations
capabilities = obp60:true
condition = calInstance2 IN %(calibration_fields)s
[calSlope2]
label = Data Instance 2 Calibration Slope
type = number
default = 1.00
description = Slope for data instance 2
category = OBP60 Calibrations
capabilities = obp60:true
condition = calInstance2 IN %(calibration_fields)s
[calSmooth2]
label = Data Instance 2 Smoothing
type = number
default = 0
description = Smoothing factor [0..10]; 0 = no smoothing
check = checkMinMax
min = 0
max = 10
category = OBP60 Calibrations
capabilities = obp60:true
condition = calInstance2 IN %(calibration_fields)s
[calInstance3]
label = Calibration Data Instance 3
type = list
default = ---
description = Data instance for calibration
list = ---, %(calibration_fields)s
category = OBP60 Calibrations
capabilities = obp60:true
[calOffset3]
label = Data Instance 3 Calibration Offset
type = number
default = 0.00
description = Offset for data instance 3
category = OBP60 Calibrations
capabilities = obp60:true
condition = calinstance3 IN %(calibration_fields)s
[calSlope3]
label = Data Instance 3 Calibration Slope
type = number
default = 1.00
description = Slope for data instance 3
category = OBP60 Calibrations
capabilities = obp60:true
condition = calinstance3 IN %(calibration_fields)s
[calSmooth3]
label = Data Instance 3 Smoothing
type = number
default = 0
description = Smoothing factor [0..10]; 0 = no smoothing
check = checkMinMax
min = 0
max = 10
category = OBP60 Calibrations
capabilities = obp60:true
condition = calinstance3 IN %(calibration_fields)s
[display]
label = Display Mode
type = list
default = Logo + QR Code
description = Settings for startup display
list = White Screen, Logo, Logo + QR Code, Off
category = OBP60 Display
capabilities = obp60:true
[displaycolor]
label = Inverted Display Mode
type = list
default = Normal
description = Invert display to white letters on black background [Normal|Inverse]
list = Normal, Inverse
category = OBP60 Display
capabilities = obp60:true
[statusLine]
label = Status Line
type = boolean
default = true
description = Show status line [on|off]
category = OBP60 Display
capabilities = obp60:true
[timeSource]
label = Status Time Source
type = list
default = GPS
description = Data source for date and time display in status line [RTC|GPS]
dict =
RTC:Real time clock (RTC)
GPS:Time via bus (GPS)
category = OBP60 Display
capabilities = obp60:true
[refresh]
label = Refresh
type = boolean
default = true
description = Refresh e-paper display after each new page request to reduce ghost effects [on|off]
category = OBP60 Display
capabilities = obp60:true
[fastRefresh]
label = Fast Refresh
type = boolean
default = false
description = Fast refresh for e-paper display [on|off]
category = OBP60 Display
capabilities = obp60:true
[fullRefreshTime]
label = Full Refresh Time [min]
type = number
default = 1
description = E-Paper full refresh time all [1...10 min]
check = checkMinMax
min = 1
max = 10
category = OBP60 Display
capabilities = obp60:true
[holdvalues]
label = Hold Values
type = boolean
default = false
description = Retain old values when data stream stops [on|off]
category = OBP60 Display
capabilities = obp60:true
[valueprecision]
label = Display value precision
type = list
default = 2
description = Maximum number of decimal places to display [1|2]
list = 1, 2
category = OBP60 Display
capabilities = obp60:true
[headerFormat]
label = Header Format
type = list
default = TEXT
description = Header format: Text or Symbols
dict =
TEXT:Text
ICON:Symbols
category = OBP60 Pages
capabilities = obp60:true
[backlight]
label = Backlight Mode
type = list
default = Control by Key
description = Settings for automatic backlight mode
list = Off, Control by Sun, Control by Bus, Control by Time, Control by Key, On
category = OBP60 Display
capabilities = obp60:true
[blColor]
label = Backlight Color
type = list
default = Red
description = Backlight color
list = Red, Orange, Yellow, Green, Blue, Aqua, Violet, White
category = OBP60 Display
capabilities = obp60:true
[blBrightness]
label = Brightness [%%]
type = number
default = 50
description = Backlight brightness [20...100%%]
check = checkMinMax
min = 20
max = 100
category = OBP60 Display
capabilities = obp60:true
[flashLED]
label = Flash LED Mode
type = list
default = Limit Violation
description = Settings for flash LED
list = Off, Bus Data, GPS Fix Lost, Limit Violation
category = OBP60 Display
capabilities = obp60:true
[buzzerError]
label = Buzzer Error
type = boolean
default = false
description = Sound on error [on|off]
category = OBP60 Buzzer
capabilities = obp60:true
[buzzerGps]
label = Buzzer GPS Fix
type = boolean
default = false
description = Sound on missing or lost GPS fix
category = OBP60 Buzzer
capabilities = obp60:true
[buzzerLim]
label = Buzzer by Limits
type = boolean
default = false
description = Sound on limit violation
category = OBP60 Buzzer
capabilities = obp60:true
[buzzerMode]
label = Buzzer Mode
type = list
default = Off
description = Settings for buzzer behaviour
list = Off, Short Single Beep, Longer Single Beep, Beep until Confirmation
category = OBP60 Buzzer
capabilities = obp60:true
[buzzerPower]
label = Buzzer Power [%%]
type = number
default = 50
description = Buzzer loudness [0...100%%]
check = checkMinMax
min = 0
max = 100
category = OBP60 Buzzer
capabilities = obp60:true
[visiblePages]
label = Number of Pages
type = number
default = 10
description = Number of visible data pages [1...10]
check = checkMinMax
min = 1
max = 10
category = OBP60 Pages
capabilities = obp60:true
[startPage]
label = Start Page
type = number
default = 1
description = First page number to display after device startup
check = checkMinMax
min = 1
max = 10
category = OBP60 Pages
capabilities = obp60:true
[imageFormat]
label = Screenshot Format
type = list
default = PBM
description = Graphics file format for screenshots [GIF|PBM|BMP]
dict =
GIF:Compressed image (GIF)
PBM:Portable bitmap (PBM)
BMP:Windows bitmap (BMP)
category = OBP60 Pages
capabilities = obp60:true
# WIP
[trackerType]
label = Tracker Type
type = list
default = off
description = Type of tracker to use [OFF|SDCARD|SERVER|HERO]
dict = OFF:No tracker
SDCARD:Log to SD-Card
SERVER:Log to Server
HERO:Connect with Regatta Hero
category = OBP60 Pages
capabilities = obp60:true
[trackerOrganization]
label = Tracker team
type = string
default = demo
description = Tracker organization for login
category = OBP60 Pages
[trackerPasscode]
label = Tracker team
type = password
default = 291758
description = Your tracker team you belong to. E.g. short name of association
category = OBP60 Pages
[trackerTeam]
label = Tracker team
type = string
default = none
description = Your tracker team you belong to. E.g. short name of association
category = OBP60 Pages
[trackerHandicap]
label = Boat handicap
type = number
default = 100
check = checkMinMax
min = 50
max = 1000
description = The handicap value of your boat. E.g. yardstick value
category = OBP60 Pages
[boatName]
label = Boat Name
type = string
default = Unsinkbar II
description = name of your boat
category = OBP60 Pages
[boatClass]
label = Boat Class
type = string
default = One off
description = Class name of your boat if available or "One off"
category = OBP60 Pages
[sailNumber]
label = Sail number
type = string
default = GER 11
description = Identification number on sail
category = OBP60 Pages

View File

@@ -1,8 +1,6 @@
// SPDX-License-Identifier: GPL-2.0-or-later
#if defined BOARD_OBP60S3 || defined BOARD_OBP40S3 #if defined BOARD_OBP60S3 || defined BOARD_OBP40S3
#include "obp60task.h" #include "obp60task.h"
#include "Pagedata.h" // Data exchange for pages #include "Pagedata.h" // Data exchange for pages
#include "OBP60Formatter.h" // Data formatting for boat values
#include "OBP60Hardware.h" // PIN definitions #include "OBP60Hardware.h" // PIN definitions
#include <Wire.h> // I2C connections #include <Wire.h> // I2C connections
#include <MCP23017.h> // MCP23017 extension Port #include <MCP23017.h> // MCP23017 extension Port
@@ -13,12 +11,8 @@
#include <NMEA0183Messages.h> #include <NMEA0183Messages.h>
#include <GxEPD2_BW.h> // GxEPD2 lib for b/w E-Ink displays #include <GxEPD2_BW.h> // GxEPD2 lib for b/w E-Ink displays
#include "OBP60Extensions.h" // Functions lib for extension board #include "OBP60Extensions.h" // Functions lib for extension board
#include "OBPKeyboardTask.h" // Functions lib for keyboard handling #include "OBP60Keypad.h" // Functions for keypad
#include "OBPDataOperations.h" // Functions lib for data operations such as true wind calculation #include "OBPDataOperations.h" // Functions lib for data operations such as true wind calculation
#include "OBP60QRWiFi.h" // Functions lib for WiFi QR code
#include "OBPSensorTask.h" // Functions lib for sensor data
#include "freertos/task.h" // WIP possible unused
#ifdef BOARD_OBP40S3 #ifdef BOARD_OBP40S3
#include "driver/rtc_io.h" // Needs for weakup from deep sleep #include "driver/rtc_io.h" // Needs for weakup from deep sleep
@@ -26,8 +20,12 @@
#endif #endif
// Pictures // Pictures
#include "images/OBP_400x300.xbm" // OBP Logo //#include GxEPD_BitmapExamples // Example picture
#include "MFD_OBP60_400x300_sw.h" // MFD with logo
#include "Logo_OBP_400x300_sw.h" // OBP Logo
#include "images/unknown.xbm" // unknown page indicator #include "images/unknown.xbm" // unknown page indicator
#include "OBP60QRWiFi.h" // Functions lib for WiFi QR code
#include "OBPSensorTask.h" // Functions lib for sensor data
// Global vars // Global vars
bool initComplete = false; // Initialization complete bool initComplete = false; // Initialization complete
@@ -65,7 +63,7 @@ void OBP60Init(GwApi *api){
#endif #endif
// Settings for e-paper display // Settings for e-paper display
String fastrefresh = config->getConfigItem(config->fastRefresh,true)->asString(); String fastrefresh = api->getConfig()->getConfigItem(api->getConfig()->fastRefresh,true)->asString();
logger->logDebug(GwLog::DEBUG, "Fast Refresh Mode is: %s", fastrefresh.c_str()); logger->logDebug(GwLog::DEBUG, "Fast Refresh Mode is: %s", fastrefresh.c_str());
#ifdef DISPLAY_GDEY042T81 #ifdef DISPLAY_GDEY042T81
if(fastrefresh == "true"){ if(fastrefresh == "true"){
@@ -88,24 +86,24 @@ void OBP60Init(GwApi *api){
logger->logDebug(GwLog::LOG,"CPU speed at boot: %i MHz", freq); logger->logDebug(GwLog::LOG,"CPU speed at boot: %i MHz", freq);
// Settings for backlight // Settings for backlight
String backlightMode = config->getConfigItem(config->backlight,true)->asString(); String backlightMode = api->getConfig()->getConfigItem(api->getConfig()->backlight,true)->asString();
logger->logDebug(GwLog::DEBUG,"Backlight Mode is: %s", backlightMode.c_str()); logger->logDebug(GwLog::DEBUG,"Backlight Mode is: %s", backlightMode.c_str());
uint brightness = uint(config->getConfigItem(config->blBrightness,true)->asInt()); uint brightness = uint(api->getConfig()->getConfigItem(api->getConfig()->blBrightness,true)->asInt());
String backlightColor = config->getConfigItem(config->blColor,true)->asString(); String backlightColor = api->getConfig()->getConfigItem(api->getConfig()->blColor,true)->asString();
if (backlightMode == "On") { if(String(backlightMode) == "On"){
setBacklightLED(brightness, colorMapping(backlightColor)); setBacklightLED(brightness, colorMapping(backlightColor));
} }
else if (backlightMode == "Off") { else if(String(backlightMode) == "Off"){
setBacklightLED(0, COLOR_BLACK); // Backlight LEDs off (blue without britghness) setBacklightLED(0, COLOR_BLACK); // Backlight LEDs off (blue without britghness)
} }
else if (backlightMode == "Control by Key") { else if(String(backlightMode) == "Control by Key"){
setBacklightLED(0, COLOR_BLUE); // Backlight LEDs off (blue without britghness) setBacklightLED(0, COLOR_BLUE); // Backlight LEDs off (blue without britghness)
} }
// Settings flash LED mode // Settings flash LED mode
String ledMode = config->getConfigItem(config->flashLED,true)->asString(); String ledMode = api->getConfig()->getConfigItem(api->getConfig()->flashLED,true)->asString();
logger->logDebug(GwLog::DEBUG,"LED Mode is: %s", ledMode.c_str()); logger->logDebug(GwLog::DEBUG,"LED Mode is: %s", ledMode.c_str());
if (ledMode == "Off") { if(String(ledMode) == "Off"){
setBlinkingLED(false); setBlinkingLED(false);
} }
@@ -114,32 +112,41 @@ void OBP60Init(GwApi *api){
initComplete = true; initComplete = true;
// Buzzer tone for initialization finish // Buzzer tone for initialization finish
setBuzzerPower(uint(config->getConfigItem(config->buzzerPower,true)->asInt())); setBuzzerPower(uint(api->getConfig()->getConfigItem(api->getConfig()->buzzerPower,true)->asInt()));
buzzer(TONE4, 500); buzzer(TONE4, 500);
} }
/* ux-functions not working WTF? typedef struct {
bool listTasks(GwLog *logger) { int page0=0;
UBaseType_t taskCount = uxTaskGetNumberOfTasks(); QueueHandle_t queue;
TaskStatus_t *taskStatusArray; GwLog* logger = nullptr;
// GwApi* api = nullptr;
uint sensitivity = 100;
bool use_syspage = true;
} MyData;
taskStatusArray = (TaskStatus_t *)pvPortMalloc(taskCount * sizeof(TaskStatus_t)); // Keyboard Task
if (taskStatusArray != NULL) { void keyboardTask(void *param){
taskCount = uxTaskGetSystemState(taskStatusArray, taskCount, NULL); MyData *data=(MyData *)param;
for (UBaseType_t i = 0; i < taskCount; i++) {
logger->logDebug(GwLog::LOG, "Task Name: %s (Stack=%d)", taskStatusArray[i].pcTaskName,
taskStatusArray[i].usStackHighWaterMark);
// more fields in task status
// xHandle, uxCurrentPriority, uxBasePriority, usStackHighWaterMark
}
vPortFree(taskStatusArray);
return true;
}
logger->logDebug(GwLog::ERROR, "Failed to allocate memory for task list");
return false;
} */
int keycode = 0;
data->logger->logDebug(GwLog::LOG,"Start keyboard task");
// Loop for keyboard task
while (true){
keycode = readKeypad(data->logger, data->sensitivity, data->use_syspage);
//send a key event
if(keycode != 0){
xQueueSend(data->queue, &keycode, 0);
data->logger->logDebug(GwLog::LOG,"Send keycode: %d", keycode);
}
delay(20); // 50Hz update rate (20ms)
}
vTaskDelete(NULL);
}
// --- Class BoatValueList --------------
bool BoatValueList::addValueToList(GwApi::BoatValue *v){ bool BoatValueList::addValueToList(GwApi::BoatValue *v){
for (int i=0;i<numValues;i++){ for (int i=0;i<numValues;i++){
if (allBoatValues[i] == v){ if (allBoatValues[i] == v){
@@ -152,7 +159,6 @@ bool BoatValueList::addValueToList(GwApi::BoatValue *v){
numValues++; numValues++;
return true; return true;
} }
//helper to ensure that each BoatValue is only queried once //helper to ensure that each BoatValue is only queried once
GwApi::BoatValue *BoatValueList::findValueOrCreate(String name){ GwApi::BoatValue *BoatValueList::findValueOrCreate(String name){
for (int i=0;i<numValues;i++){ for (int i=0;i<numValues;i++){
@@ -164,6 +170,7 @@ GwApi::BoatValue *BoatValueList::findValueOrCreate(String name){
addValueToList(rt); addValueToList(rt);
return rt; return rt;
} }
// --- End Class BoatValueList --------------
//we want to have a list that has all our page definitions //we want to have a list that has all our page definitions
//this way each page can easily be added here //this way each page can easily be added here
@@ -190,13 +197,12 @@ class PageList{
* each page should have defined a registerXXXPage variable of type * each page should have defined a registerXXXPage variable of type
* PageData that describes what it needs * PageData that describes what it needs
*/ */
void registerAllPages(GwLog *logger, PageList &list){ void registerAllPages(PageList &list){
//the next line says that this variable is defined somewhere else //the next line says that this variable is defined somewhere else
//in our case in a separate C++ source file //in our case in a separate C++ source file
//this way this separate source file can be compiled by it's own //this way this separate source file can be compiled by it's own
//and has no access to any of our data except the one that we //and has no access to any of our data except the one that we
//give as a parameter to the page function //give as a parameter to the page function
logger->logDebug(GwLog::LOG, "Memory before registering pages: stack=%d, heap=%d", uxTaskGetStackHighWaterMark(NULL), ESP.getFreeHeap());
extern PageDescription registerPageSystem; extern PageDescription registerPageSystem;
//we add the variable to our list //we add the variable to our list
list.add(&registerPageSystem); list.add(&registerPageSystem);
@@ -256,15 +262,10 @@ void registerAllPages(GwLog *logger, PageList &list){
list.add(&registerPageNavigation); list.add(&registerPageNavigation);
extern PageDescription registerPageDigitalOut; extern PageDescription registerPageDigitalOut;
list.add(&registerPageDigitalOut); list.add(&registerPageDigitalOut);
extern PageDescription registerPageAutopilot;
list.add(&registerPageAutopilot);
extern PageDescription registerPageAnchor; extern PageDescription registerPageAnchor;
list.add(&registerPageAnchor); list.add(&registerPageAnchor);
extern PageDescription registerPageAIS;
list.add(&registerPageAIS);
extern PageDescription registerPageBarograph;
list.add(&registerPageBarograph);
extern PageDescription registerPageTracker;
list.add(&registerPageTracker);
logger->logDebug(GwLog::LOG,"Memory after registering pages: stack=%d, heap=%d", uxTaskGetStackHighWaterMark(NULL), ESP.getFreeHeap());
} }
// Undervoltage detection for shutdown display // Undervoltage detection for shutdown display
@@ -275,18 +276,18 @@ void underVoltageError(CommonData &common) {
setFlashLED(false); // Flash LED Off setFlashLED(false); // Flash LED Off
buzzer(TONE4, 20); // Buzzer tone 4kHz 20ms buzzer(TONE4, 20); // Buzzer tone 4kHz 20ms
// Shutdown EInk display // Shutdown EInk display
epd->setFullWindow(); // Set full Refresh getdisplay().setFullWindow(); // Set full Refresh
//epd->setPartialWindow(0, 0, epd->width(), epd->height()); // Set partial update //getdisplay().setPartialWindow(0, 0, getdisplay().width(), getdisplay().height()); // Set partial update
epd->fillScreen(common.bgcolor);// Clear screen getdisplay().fillScreen(common.bgcolor);// Clear screen
epd->setTextColor(common.fgcolor); getdisplay().setTextColor(common.fgcolor);
epd->setFont(&Ubuntu_Bold20pt8b); getdisplay().setFont(&Ubuntu_Bold20pt8b);
epd->setCursor(65, 150); getdisplay().setCursor(65, 150);
epd->print("Undervoltage"); getdisplay().print("Undervoltage");
epd->setFont(&Ubuntu_Bold8pt8b); getdisplay().setFont(&Ubuntu_Bold8pt8b);
epd->setCursor(65, 175); getdisplay().setCursor(65, 175);
epd->print("Charge battery and restart system"); getdisplay().print("Charge battery and restart system");
epd->nextPage(); // Partial update getdisplay().nextPage(); // Partial update
epd->powerOff(); // Display power off getdisplay().powerOff(); // Display power off
setPortPin(OBP_POWER_EPD, false); // Power off ePaper display setPortPin(OBP_POWER_EPD, false); // Power off ePaper display
setPortPin(OBP_POWER_SD, false); // Power off SD card setPortPin(OBP_POWER_SD, false); // Power off SD card
#else #else
@@ -296,17 +297,17 @@ void underVoltageError(CommonData &common) {
buzzer(TONE4, 20); // Buzzer tone 4kHz 20ms buzzer(TONE4, 20); // Buzzer tone 4kHz 20ms
setPortPin(OBP_POWER_50, false); // Power rail 5.0V Off setPortPin(OBP_POWER_50, false); // Power rail 5.0V Off
// Shutdown EInk display // Shutdown EInk display
epd->setPartialWindow(0, 0, epd->width(), epd->height()); // Set partial update getdisplay().setPartialWindow(0, 0, getdisplay().width(), getdisplay().height()); // Set partial update
epd->fillScreen(common.bgcolor);// Clear screen getdisplay().fillScreen(common.bgcolor);// Clear screen
epd->setTextColor(common.fgcolor); getdisplay().setTextColor(common.fgcolor);
epd->setFont(&Ubuntu_Bold20pt8b); getdisplay().setFont(&Ubuntu_Bold20pt8b);
epd->setCursor(65, 150); getdisplay().setCursor(65, 150);
epd->print("Undervoltage"); getdisplay().print("Undervoltage");
epd->setFont(&Ubuntu_Bold8pt8b); getdisplay().setFont(&Ubuntu_Bold8pt8b);
epd->setCursor(65, 175); getdisplay().setCursor(65, 175);
epd->print("To wake up repower system"); getdisplay().print("To wake up repower system");
epd->nextPage(); // Partial update getdisplay().nextPage(); // Partial update
epd->powerOff(); // Display power off getdisplay().powerOff(); // Display power off
#endif #endif
while (true) { while (true) {
esp_deep_sleep_start(); // Deep Sleep without wakeup. Wakeup only after power cycle (restart). esp_deep_sleep_start(); // Deep Sleep without wakeup. Wakeup only after power cycle (restart).
@@ -333,17 +334,16 @@ void OBP60Task(GwApi *api){
// return; // return;
GwLog *logger=api->getLogger(); GwLog *logger=api->getLogger();
GwConfigHandler *config=api->getConfig(); GwConfigHandler *config=api->getConfig();
#ifdef HARDWARE_V21 #if defined HARDWARE_V20 || HARDWARE_V21
startLedTask(api); startLedTask(api);
#endif #endif
PageList allPages; PageList allPages;
registerAllPages(logger, allPages); registerAllPages(allPages);
CommonData commonData; CommonData commonData;
commonData.logger=logger; commonData.logger=logger;
commonData.config=config; commonData.config=config;
commonData.fmt = new Formatter(config);
#ifdef HARDWARE_V21 #if defined HARDWARE_V20 || HARDWARE_V21
// Keyboard coordinates for page footer // Keyboard coordinates for page footer
initKeys(commonData); initKeys(commonData);
#endif #endif
@@ -356,8 +356,8 @@ void OBP60Task(GwApi *api){
} }
// Init E-Ink display // Init E-Ink display
String displaymode = config->getConfigItem(config->display,true)->asString(); String displaymode = api->getConfig()->getConfigItem(api->getConfig()->display,true)->asString();
String displaycolor = config->getConfigItem(config->displaycolor,true)->asString(); String displaycolor = api->getConfig()->getConfigItem(api->getConfig()->displaycolor,true)->asString();
if (displaycolor == "Normal") { if (displaycolor == "Normal") {
commonData.fgcolor = GxEPD_BLACK; commonData.fgcolor = GxEPD_BLACK;
commonData.bgcolor = GxEPD_WHITE; commonData.bgcolor = GxEPD_WHITE;
@@ -366,48 +366,47 @@ void OBP60Task(GwApi *api){
commonData.fgcolor = GxEPD_WHITE; commonData.fgcolor = GxEPD_WHITE;
commonData.bgcolor = GxEPD_BLACK; commonData.bgcolor = GxEPD_BLACK;
} }
String systemname = config->getConfigItem(config->systemName, true)->asString(); String systemname = api->getConfig()->getConfigItem(api->getConfig()->systemName,true)->asString();
String wifipass = config->getConfigItem(config->apPassword, true)->asString(); String wifipass = api->getConfig()->getConfigItem(api->getConfig()->apPassword,true)->asString();
bool refreshmode = config->getConfigItem(config->refresh, true)->asBoolean(); bool refreshmode = api->getConfig()->getConfigItem(api->getConfig()->refresh,true)->asBoolean();
bool symbolmode = (config->getString(config->headerFormat) == "ICON"); String fastrefresh = api->getConfig()->getConfigItem(api->getConfig()->fastRefresh,true)->asString();
String fastrefresh = config->getConfigItem(config->fastRefresh, true)->asString(); uint fullrefreshtime = uint(api->getConfig()->getConfigItem(api->getConfig()->fullRefreshTime,true)->asInt());
uint fullrefreshtime = uint(config->getConfigItem(config->fullRefreshTime, true)->asInt());
#ifdef BOARD_OBP40S3 #ifdef BOARD_OBP40S3
bool syspage_enabled = config->getBool(config->systemPage); bool syspage_enabled = config->getBool(config->systemPage);
#endif #endif
#ifdef DISPLAY_GDEY042T81 #ifdef DISPLAY_GDEY042T81
epd->init(115200, true, 2, false); // Init for Waveshare boards with "clever" reset circuit, 2ms reset pulse getdisplay().init(115200, true, 2, false); // Init for Waveshare boards with "clever" reset circuit, 2ms reset pulse
#else #else
epd->init(115200); // Init for normal displays getdisplay().init(115200); // Init for normal displays
#endif #endif
epd->setRotation(0); // Set display orientation (horizontal) getdisplay().setRotation(0); // Set display orientation (horizontal)
epd->setFullWindow(); // Set full Refresh getdisplay().setFullWindow(); // Set full Refresh
epd->firstPage(); // set first page getdisplay().firstPage(); // set first page
epd->fillScreen(commonData.bgcolor); getdisplay().fillScreen(commonData.bgcolor);
epd->setTextColor(commonData.fgcolor); getdisplay().setTextColor(commonData.fgcolor);
epd->nextPage(); // Full Refresh getdisplay().nextPage(); // Full Refresh
epd->setPartialWindow(0, 0, epd->width(), epd->height()); // Set partial update getdisplay().setPartialWindow(0, 0, getdisplay().width(), getdisplay().height()); // Set partial update
epd->fillScreen(commonData.bgcolor); getdisplay().fillScreen(commonData.bgcolor);
epd->nextPage(); // Fast Refresh getdisplay().nextPage(); // Fast Refresh
epd->nextPage(); // Fast Refresh getdisplay().nextPage(); // Fast Refresh
if(String(displaymode) == "Logo + QR Code" || String(displaymode) == "Logo"){ if(String(displaymode) == "Logo + QR Code" || String(displaymode) == "Logo"){
epd->fillScreen(commonData.bgcolor); getdisplay().fillScreen(commonData.bgcolor);
epd->drawXBitmap(0, 0, OBP_400x300_bits, OBP_400x300_width, OBP_400x300_height, commonData.fgcolor); getdisplay().drawBitmap(0, 0, gImage_Logo_OBP_400x300_sw, getdisplay().width(), getdisplay().height(), commonData.fgcolor); // Draw start logo
epd->nextPage(); // Fast Refresh getdisplay().nextPage(); // Fast Refresh
epd->nextPage(); // Fast Refresh getdisplay().nextPage(); // Fast Refresh
delay(SHOW_TIME); // Logo show time delay(SHOW_TIME); // Logo show time
if(String(displaymode) == "Logo + QR Code"){ if(String(displaymode) == "Logo + QR Code"){
epd->fillScreen(commonData.bgcolor); getdisplay().fillScreen(commonData.bgcolor);
qrWiFi(systemname, wifipass, commonData.fgcolor, commonData.bgcolor); // Show QR code for WiFi connection qrWiFi(systemname, wifipass, commonData.fgcolor, commonData.bgcolor); // Show QR code for WiFi connection
epd->nextPage(); // Fast Refresh getdisplay().nextPage(); // Fast Refresh
epd->nextPage(); // Fast Refresh getdisplay().nextPage(); // Fast Refresh
delay(SHOW_TIME); // QR code show time delay(SHOW_TIME); // QR code show time
} }
epd->fillScreen(commonData.bgcolor); getdisplay().fillScreen(commonData.bgcolor);
epd->nextPage(); // Fast Refresh getdisplay().nextPage(); // Fast Refresh
epd->nextPage(); // Fast Refresh getdisplay().nextPage(); // Fast Refresh
} }
// Init pages // Init pages
@@ -435,7 +434,7 @@ void OBP60Task(GwApi *api){
#endif #endif
LOG_DEBUG(GwLog::LOG,"...done"); LOG_DEBUG(GwLog::LOG,"...done");
int lastPage=-1; // initialize with an impossible value, so we can detect wether we are during startup and no page has been displayed yet int lastPage=-1; // initialize with an impiossible value, so we can detect wether we are during startup and no page has been displayed yet
BoatValueList boatValues; //all the boat values for the api query BoatValueList boatValues; //all the boat values for the api query
HstryBuffers hstryBufferList(1920, &boatValues, logger); // Create empty list of boat data history buffers (1.920 values = seconds = 32 min.) HstryBuffers hstryBufferList(1920, &boatValues, logger); // Create empty list of boat data history buffers (1.920 values = seconds = 32 min.)
@@ -510,15 +509,15 @@ void OBP60Task(GwApi *api){
//now we have prepared the page data //now we have prepared the page data
//we start a separate task that will fetch our keys... //we start a separate task that will fetch our keys...
KbTaskData kbparams; MyData allParameters;
kbparams.logger = api->getLogger(); allParameters.logger=api->getLogger();
kbparams.queue = xQueueCreate(10, sizeof(int)); allParameters.page0=3;
kbparams.sensitivity = api->getConfig()->getInt(GwConfigDefinitions::tSensitivity); allParameters.queue=xQueueCreate(10,sizeof(int));
allParameters.sensitivity= api->getConfig()->getInt(GwConfigDefinitions::tSensitivity);
#ifdef BOARD_OBP40S3 #ifdef BOARD_OBP40S3
kbparams.use_syspage = syspage_enabled; allParameters.use_syspage = syspage_enabled;
#endif #endif
createKeyboardTask(&kbparams); xTaskCreate(keyboardTask,"keyboard",2000,&allParameters,configMAX_PRIORITIES-1,NULL);
// we start a separate task to collect sensor data
SharedData *shared=new SharedData(api); SharedData *shared=new SharedData(api);
createSensorTask(shared); createSensorTask(shared);
@@ -526,28 +525,28 @@ void OBP60Task(GwApi *api){
//#################################################################################### //####################################################################################
// Configuration values for main loop // Configuration values for main loop
String gpsFix = config->getConfigItem(config->flashLED,true)->asString(); String gpsFix = api->getConfig()->getConfigItem(api->getConfig()->flashLED,true)->asString();
String gpsOn = config->getConfigItem(config->useGPS,true)->asString(); String gpsOn=api->getConfig()->getConfigItem(api->getConfig()->useGPS,true)->asString();
float tz = config->getConfigItem(config->timeZone,true)->asFloat();
commonData.tz = api->getConfig()->getConfigItem(api->getConfig()->timeZone,true)->asFloat();
commonData.backlight.mode = backlightMapping(config->getConfigItem(config->backlight,true)->asString()); commonData.backlight.mode = backlightMapping(config->getConfigItem(config->backlight,true)->asString());
commonData.backlight.color = colorMapping(config->getConfigItem(config->blColor,true)->asString()); commonData.backlight.color = colorMapping(config->getConfigItem(config->blColor,true)->asString());
commonData.backlight.brightness = 2.55 * uint(config->getConfigItem(config->blBrightness, true)->asInt()); commonData.backlight.brightness = uint(config->getConfigItem(config->blBrightness,true)->asInt());
commonData.powermode = api->getConfig()->getConfigItem(api->getConfig()->powerMode,true)->asString(); commonData.powermode = api->getConfig()->getConfigItem(api->getConfig()->powerMode,true)->asString();
bool uvoltage = config->getConfigItem(config->underVoltage, true)->asBoolean(); bool uvoltage = config->getConfigItem(config->underVoltage, true)->asBoolean();
float voffset = (config->getConfigItem(config->vOffset,true)->asString()).toFloat(); float voffset = (config->getConfigItem(config->vOffset,true)->asString()).toFloat();
float vslope = (config->getConfigItem(config->vSlope,true)->asString()).toFloat(); float vslope = (config->getConfigItem(config->vSlope,true)->asString()).toFloat();
String cpuspeed = config->getConfigItem(config->cpuSpeed, true)->asString(); String cpuspeed = api->getConfig()->getConfigItem(api->getConfig()->cpuSpeed,true)->asString();
uint hdopAccuracy = uint(config->getConfigItem(config->hdopAccuracy, true)->asInt()); uint hdopAccuracy = uint(api->getConfig()->getConfigItem(api->getConfig()->hdopAccuracy,true)->asInt());
double homelat = config->getString(config->homeLAT).toDouble(); double homelat = commonData.config->getString(commonData.config->homeLAT).toDouble();
double homelon = config->getString(config->homeLON).toDouble(); double homelon = commonData.config->getString(commonData.config->homeLON).toDouble();
bool homevalid = homelat >= -180.0 and homelat <= 180 and homelon >= -90.0 and homelon <= 90.0; bool homevalid = homelat >= -180.0 and homelat <= 180 and homelon >= -90.0 and homelon <= 90.0;
if (homevalid) { if (homevalid) {
logger->logDebug(GwLog::LOG, "Home location set to lat=%f, lon=%f", homelat, homelon); LOG_DEBUG(GwLog::LOG, "Home location set to lat=%f, lon=%f", homelat, homelon);
} else { } else {
logger->logDebug(GwLog::LOG, "No valid home location found"); LOG_DEBUG(GwLog::LOG, "No valid home location found");
} }
// refreshmode defined in init section // refreshmode defined in init section
@@ -575,8 +574,6 @@ void OBP60Task(GwApi *api){
pages[pageNumber].page->setupKeys(); // Initialize keys for first page pages[pageNumber].page->setupKeys(); // Initialize keys for first page
// listTasks(logger);
// Main loop runs with 100ms // Main loop runs with 100ms
//#################################################################################### //####################################################################################
@@ -628,7 +625,7 @@ void OBP60Task(GwApi *api){
// Check the keyboard message // Check the keyboard message
int keyboardMessage=0; int keyboardMessage=0;
while (xQueueReceive(kbparams.queue,&keyboardMessage, 0)) { while (xQueueReceive(allParameters.queue,&keyboardMessage,0)){
LOG_DEBUG(GwLog::LOG,"new key from keyboard %d",keyboardMessage); LOG_DEBUG(GwLog::LOG,"new key from keyboard %d",keyboardMessage);
keypressed = true; keypressed = true;
@@ -661,8 +658,6 @@ void OBP60Task(GwApi *api){
// if(String(backlight) == "Control by Key"){ // if(String(backlight) == "Control by Key"){
if(keyboardMessage == 6){ if(keyboardMessage == 6){
LOG_DEBUG(GwLog::LOG,"Toggle Backlight LED"); LOG_DEBUG(GwLog::LOG,"Toggle Backlight LED");
// TODO config: toogle vs steps
// toggleBacklightLED(commonData.backlight.brightness, commonData.backlight.color);
stepsBacklightLED(commonData.backlight.brightness, commonData.backlight.color); stepsBacklightLED(commonData.backlight.brightness, commonData.backlight.color);
} }
} }
@@ -708,7 +703,7 @@ void OBP60Task(GwApi *api){
starttime5 = millis(); starttime5 = millis();
if(time->valid == true && date->valid == true && lat->valid == true && lon->valid == true){ if(time->valid == true && date->valid == true && lat->valid == true && lon->valid == true){
// Provide sundata to all pages // Provide sundata to all pages
commonData.sundata = calcSunsetSunrise(time->value , date->value, lat->value, lon->value, tz); commonData.sundata = calcSunsetSunrise(time->value , date->value, lat->value, lon->value, commonData.tz);
// Backlight with sun control // Backlight with sun control
if (commonData.backlight.mode == BacklightMode::SUN) { if (commonData.backlight.mode == BacklightMode::SUN) {
// if(String(backlight) == "Control by Sun"){ // if(String(backlight) == "Control by Sun"){
@@ -721,7 +716,7 @@ void OBP60Task(GwApi *api){
} }
} else if (homevalid and commonData.data.rtcValid) { } else if (homevalid and commonData.data.rtcValid) {
// No gps fix but valid home location and time configured // No gps fix but valid home location and time configured
commonData.sundata = calcSunsetSunriseRTC(&commonData.data.rtcTime, homelat, homelon, tz); commonData.sundata = calcSunsetSunriseRTC(&commonData.data.rtcTime, homelat, homelon, commonData.tz);
} }
} }
@@ -729,23 +724,24 @@ void OBP60Task(GwApi *api){
if(millis() > starttime4 + 8000 && delayedDisplayUpdate == true){ if(millis() > starttime4 + 8000 && delayedDisplayUpdate == true){
starttime1 = millis(); starttime1 = millis();
starttime2 = millis(); starttime2 = millis();
epd->setFullWindow(); // Set full update getdisplay().setFullWindow(); // Set full update
if(fastrefresh == "true"){ if(fastrefresh == "true"){
epd->nextPage(); // Full update getdisplay().nextPage(); // Full update
} }
else{ else{
epd->fillScreen(commonData.fgcolor); // Clear display getdisplay().fillScreen(commonData.fgcolor); // Clear display
#ifdef DISPLAY_GDEY042T81 #ifdef DISPLAY_GDEY042T81
epd->init(115200, true, 2, false); // Init for Waveshare boards with "clever" reset circuit, 2ms reset pulse getdisplay().hibernate(); // Set display in hybenate mode
getdisplay().init(115200, true, 2, false); // Init for Waveshare boards with "clever" reset circuit, 2ms reset pulse
#else #else
epd->init(115200); // Init for normal displays getdisplay().init(115200); // Init for normal displays
#endif #endif
epd->firstPage(); // Full update getdisplay().firstPage(); // Full update
epd->nextPage(); // Full update getdisplay().nextPage(); // Full update
// epd->setPartialWindow(0, 0, epd->width(), epd->height()); // Set partial update // getdisplay().setPartialWindow(0, 0, getdisplay().width(), getdisplay().height()); // Set partial update
// epd->fillScreen(commonData.bgcolor); // Clear display // getdisplay().fillScreen(commonData.bgcolor); // Clear display
// epd->nextPage(); // Partial update // getdisplay().nextPage(); // Partial update
// epd->nextPage(); // Partial update // getdisplay().nextPage(); // Partial update
} }
delayedDisplayUpdate = false; delayedDisplayUpdate = false;
} }
@@ -756,23 +752,24 @@ void OBP60Task(GwApi *api){
starttime1 = millis(); starttime1 = millis();
starttime2 = millis(); starttime2 = millis();
LOG_DEBUG(GwLog::DEBUG,"E-Ink full refresh first 5 min"); LOG_DEBUG(GwLog::DEBUG,"E-Ink full refresh first 5 min");
epd->setFullWindow(); // Set full update getdisplay().setFullWindow(); // Set full update
if(fastrefresh == "true"){ if(fastrefresh == "true"){
epd->nextPage(); // Full update getdisplay().nextPage(); // Full update
} }
else{ else{
epd->fillScreen(commonData.fgcolor); // Clear display getdisplay().fillScreen(commonData.fgcolor); // Clear display
#ifdef DISPLAY_GDEY042T81 #ifdef DISPLAY_GDEY042T81
epd->init(115200, true, 2, false); // Init for Waveshare boards with "clever" reset circuit, 2ms reset pulse getdisplay().hibernate(); // Set display in hybenate mode
getdisplay().init(115200, true, 2, false); // Init for Waveshare boards with "clever" reset circuit, 2ms reset pulse
#else #else
epd->init(115200); // Init for normal displays getdisplay().init(115200); // Init for normal displays
#endif #endif
epd->firstPage(); // Full update getdisplay().firstPage(); // Full update
epd->nextPage(); // Full update getdisplay().nextPage(); // Full update
// epd->setPartialWindow(0, 0, epd->width(), epd->height()); // Set partial update // getdisplay().setPartialWindow(0, 0, getdisplay().width(), getdisplay().height()); // Set partial update
// epd->fillScreen(commonData.bgcolor); // Clear display // getdisplay().fillScreen(commonData.bgcolor); // Clear display
// epd->nextPage(); // Partial update // getdisplay().nextPage(); // Partial update
// epd->nextPage(); // Partial update // getdisplay().nextPage(); // Partial update
} }
} }
@@ -780,23 +777,24 @@ void OBP60Task(GwApi *api){
if(millis() > starttime2 + fullrefreshtime * 60 * 1000){ if(millis() > starttime2 + fullrefreshtime * 60 * 1000){
starttime2 = millis(); starttime2 = millis();
LOG_DEBUG(GwLog::DEBUG,"E-Ink full refresh"); LOG_DEBUG(GwLog::DEBUG,"E-Ink full refresh");
epd->setFullWindow(); // Set full update getdisplay().setFullWindow(); // Set full update
if(fastrefresh == "true"){ if(fastrefresh == "true"){
epd->nextPage(); // Full update getdisplay().nextPage(); // Full update
} }
else{ else{
epd->fillScreen(commonData.fgcolor); // Clear display getdisplay().fillScreen(commonData.fgcolor); // Clear display
#ifdef DISPLAY_GDEY042T81 #ifdef DISPLAY_GDEY042T81
epd->init(115200, true, 2, false); // Init for Waveshare boards with "clever" reset circuit, 2ms reset pulse getdisplay().hibernate(); // Set display in hybenate mode
getdisplay().init(115200, true, 2, false); // Init for Waveshare boards with "clever" reset circuit, 2ms reset pulse
#else #else
epd->init(115200); // Init for normal displays getdisplay().init(115200); // Init for normal displays
#endif #endif
epd->firstPage(); // Full update getdisplay().firstPage(); // Full update
epd->nextPage(); // Full update getdisplay().nextPage(); // Full update
// epd->setPartialWindow(0, 0, epd->width(), epd->height()); // Set partial update // getdisplay().setPartialWindow(0, 0, getdisplay().width(), getdisplay().height()); // Set partial update
// epd->fillScreen(commonData.bgcolor); // Clear display // getdisplay().fillScreen(commonData.bgcolor); // Clear display
// epd->nextPage(); // Partial update // getdisplay().nextPage(); // Partial update
// epd->nextPage(); // Partial update // getdisplay().nextPage(); // Partial update
} }
} }
@@ -822,13 +820,13 @@ void OBP60Task(GwApi *api){
hstryBufferList.handleHstryBufs(useSimuData, commonData); // Handle history buffers for certain boat data for windplot page and other usage hstryBufferList.handleHstryBufs(useSimuData, commonData); // Handle history buffers for certain boat data for windplot page and other usage
// Clear display // Clear display
// epd->fillRect(0, 0, epd->width(), epd->height(), commonData.bgcolor); // getdisplay().fillRect(0, 0, getdisplay().width(), getdisplay().height(), commonData.bgcolor);
epd->fillScreen(commonData.bgcolor); // Clear display getdisplay().fillScreen(commonData.bgcolor); // Clear display
// Show header if enabled // Show header if enabled
if (pages[pageNumber].description && pages[pageNumber].description->header or systemPage){ if (pages[pageNumber].description && pages[pageNumber].description->header or systemPage){
// build header using commonData // build header using commonData
displayHeader(commonData, symbolmode, date, time, hdop); // Show page header displayHeader(commonData, date, time, hdop); // Show page header
} }
// Call the particular page // Call the particular page
@@ -846,17 +844,17 @@ void OBP60Task(GwApi *api){
if (currentPage == NULL){ if (currentPage == NULL){
LOG_DEBUG(GwLog::ERROR,"page number %d not found", pageNumber); LOG_DEBUG(GwLog::ERROR,"page number %d not found", pageNumber);
// Error handling for missing page // Error handling for missing page
epd->setPartialWindow(0, 0, epd->width(), epd->height()); // Set partial update getdisplay().setPartialWindow(0, 0, getdisplay().width(), getdisplay().height()); // Set partial update
epd->fillScreen(commonData.bgcolor); // Clear display getdisplay().fillScreen(commonData.bgcolor); // Clear display
epd->drawXBitmap(200 - unknown_width / 2, 150 - unknown_height / 2, unknown_bits, unknown_width, unknown_height, commonData.fgcolor); getdisplay().drawXBitmap(200 - unknown_width / 2, 150 - unknown_height / 2, unknown_bits, unknown_width, unknown_height, commonData.fgcolor);
epd->setCursor(140, 250); getdisplay().setCursor(140, 250);
epd->setFont(&Atari16px); getdisplay().setFont(&Atari16px);
epd->print("Here be dragons!"); getdisplay().print("Here be dragons!");
epd->nextPage(); // Partial update (fast) getdisplay().nextPage(); // Partial update (fast)
} }
else{ else{
if (lastPage != pageNumber){ if (lastPage != pageNumber){
if (lastPage != -1) { // skip cleanup if we are during startup, and no page has been displayed yet if (lastPage != -1){ // skip cleanup if we are during startup, and no page has been displayed yet.
pages[lastPage].page->leavePage(pages[lastPage].parameters); // call page cleanup code pages[lastPage].page->leavePage(pages[lastPage].parameters); // call page cleanup code
if (hasFRAM) fram.write(FRAM_PAGE_NO, pageNumber); // remember new page for device restart if (hasFRAM) fram.write(FRAM_PAGE_NO, pageNumber); // remember new page for device restart
} }
@@ -875,10 +873,10 @@ void OBP60Task(GwApi *api){
displayAlarm(commonData); displayAlarm(commonData);
} }
if (ret & PAGE_UPDATE) { if (ret & PAGE_UPDATE) {
epd->nextPage(); // Partial update (fast) getdisplay().nextPage(); // Partial update (fast)
} }
if (ret & PAGE_HIBERNATE) { if (ret & PAGE_HIBERNATE) {
epd->hibernate(); getdisplay().hibernate();
} }
} }

View File

@@ -1,4 +1,3 @@
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once #pragma once
#include "GwApi.h" #include "GwApi.h"
//we only compile for some boards //we only compile for some boards

View File

@@ -8,6 +8,7 @@ default_envs =
obp40_s3 obp40_s3
[env:obp60_s3] [env:obp60_s3]
platform = espressif32@6.8.1
board_build.variants_dir = variants board_build.variants_dir = variants
#board = obp60_s3_n8 #ESP32-S3 N8, 8MB flash, no PSRAM #board = obp60_s3_n8 #ESP32-S3 N8, 8MB flash, no PSRAM
#board = obp60_s3_n16 #ESP32-S3 N16,16MB flash, no PSRAM, zero series #board = obp60_s3_n16 #ESP32-S3 N16,16MB flash, no PSRAM, zero series
@@ -17,7 +18,6 @@ board = obp60_s3_n16r8 #ESP32-S3 N16R8, 16MB flash, 8MB PSRAM, production serie
board_build.partitions = default_16MB.csv #ESP32-S3 N16, 16MB flash board_build.partitions = default_16MB.csv #ESP32-S3 N16, 16MB flash
custom_config = lib/obp60task/config_obp60.json custom_config = lib/obp60task/config_obp60.json
custom_script = lib/obp60task/extra_task.py custom_script = lib/obp60task/extra_task.py
board_name = OBP60
framework = arduino framework = arduino
lib_deps = lib_deps =
${basedeps.lib_deps} ${basedeps.lib_deps}
@@ -26,11 +26,14 @@ lib_deps =
ESP32time ESP32time
HTTPClient HTTPClient
WiFiClientSecure WiFiClientSecure
esphome/AsyncTCP-esphome@2.0.1
robtillaart/PCF8574@0.3.9 robtillaart/PCF8574@0.3.9
adafruit/Adafruit Unified Sensor @ 1.1.13 adafruit/Adafruit Unified Sensor @ 1.1.13
blemasle/MCP23017@2.0.0 blemasle/MCP23017@2.0.0
adafruit/Adafruit BusIO@1.5.0 adafruit/Adafruit BusIO@1.5.0
adafruit/Adafruit GFX Library@1.11.9 adafruit/Adafruit GFX Library@1.11.9
#zinggjm/GxEPD2@1.5.8
#https://github.com/ZinggJM/GxEPD2
https://github.com/thooge/GxEPD2 https://github.com/thooge/GxEPD2
sstaub/Ticker@4.4.0 sstaub/Ticker@4.4.0
adafruit/Adafruit BMP280 Library@2.6.2 adafruit/Adafruit BMP280 Library@2.6.2
@@ -55,6 +58,7 @@ build_flags=
# -D DISPLAY_GYE042A87 #alternativ E-Ink display from Genyo Optical, R10 2.2 ohm - medium # -D DISPLAY_GYE042A87 #alternativ E-Ink display from Genyo Optical, R10 2.2 ohm - medium
# -D DISPLAY_SE0420NQ04 #alternativ E-Ink display from SID Technology, R10 2.2 ohm - bad (burn in effects) # -D DISPLAY_SE0420NQ04 #alternativ E-Ink display from SID Technology, R10 2.2 ohm - bad (burn in effects)
# -D DISPLAY_ZJY400300-042CAAMFGN #alternativ E-Ink display from ZZE Technology, R10 2.2 ohm - very good # -D DISPLAY_ZJY400300-042CAAMFGN #alternativ E-Ink display from ZZE Technology, R10 2.2 ohm - very good
# -D ENABLE_PATCHES #enable patching of gateway code
${env.build_flags} ${env.build_flags}
#CONFIG_ESP_TASK_WDT_TIMEOUT_S = 10 #Task Watchdog timeout period (seconds) [1...60] 5 default #CONFIG_ESP_TASK_WDT_TIMEOUT_S = 10 #Task Watchdog timeout period (seconds) [1...60] 5 default
upload_port = /dev/ttyACM0 #OBP60 download via USB-C direct upload_port = /dev/ttyACM0 #OBP60 download via USB-C direct
@@ -63,25 +67,29 @@ upload_speed = 230400
monitor_speed = 115200 monitor_speed = 115200
[env:obp40_s3] [env:obp40_s3]
platform = espressif32@6.8.1
board_build.variants_dir = variants board_build.variants_dir = variants
board = obp40_s3_n8r8 #ESP32-S3 N8R8, 8MB flash, 8MB PSRAM, OBP60 clone (CrowPanel 4.2) board = obp40_s3_n8r8 #ESP32-S3 N8R8, 8MB flash, 8MB PSRAM, OBP60 clone (CrowPanel 4.2)
board_build.partitions = default_8MB.csv #ESP32-S3 N8, 8MB flash board_build.partitions = default_8MB.csv #ESP32-S3 N8, 8MB flash
custom_config = lib/obp60task/config_obp40.json custom_config = lib/obp60task/config_obp40.json
custom_script = lib/obp60task/extra_task.py custom_script = lib/obp60task/extra_task.py
board_name = OBP40
framework = arduino framework = arduino
lib_deps = lib_deps =
${basedeps.lib_deps} ${basedeps.lib_deps}
Wire Wire
SPI SPI
SD
ESP32time ESP32time
HTTPClient HTTPClient
WiFiClientSecure WiFiClientSecure
esphome/AsyncTCP-esphome@2.0.1
robtillaart/PCF8574@0.3.9 robtillaart/PCF8574@0.3.9
adafruit/Adafruit Unified Sensor @ 1.1.13 adafruit/Adafruit Unified Sensor @ 1.1.13
blemasle/MCP23017@2.0.0 blemasle/MCP23017@2.0.0
adafruit/Adafruit BusIO@1.5.0 adafruit/Adafruit BusIO@1.5.0
adafruit/Adafruit GFX Library@1.11.9 adafruit/Adafruit GFX Library@1.11.9
#zinggjm/GxEPD2@1.5.8
#https://github.com/ZinggJM/GxEPD2
https://github.com/thooge/GxEPD2 https://github.com/thooge/GxEPD2
sstaub/Ticker@4.4.0 sstaub/Ticker@4.4.0
adafruit/Adafruit BMP280 Library@2.6.2 adafruit/Adafruit BMP280 Library@2.6.2
@@ -101,6 +109,7 @@ build_flags=
#-D DISPLAY_ZJY400300-042CAAMFGN #alternativ E-Ink display from ZZE Technology, R10 2.2 ohm - very good #-D DISPLAY_ZJY400300-042CAAMFGN #alternativ E-Ink display from ZZE Technology, R10 2.2 ohm - very good
#-D LIPO_ACCU_1200 #Hardware extension, LiPo accu 3,7V 1200mAh #-D LIPO_ACCU_1200 #Hardware extension, LiPo accu 3,7V 1200mAh
#-D VOLTAGE_SENSOR #Hardware extension, LiPo voltage sensor with two resistors #-D VOLTAGE_SENSOR #Hardware extension, LiPo voltage sensor with two resistors
#-D ENABLE_PATCHES #enable patching of gateway code
${env.build_flags} ${env.build_flags}
upload_port = /dev/ttyUSB0 #OBP40 download via external USB/Serail converter upload_port = /dev/ttyUSB0 #OBP40 download via external USB/Serail converter
upload_protocol = esptool #firmware upload via USB OTG seriell, by first upload need to set the ESP32-S3 in the upload mode with shortcut GND to Pin27 upload_protocol = esptool #firmware upload via USB OTG seriell, by first upload need to set the ESP32-S3 in the upload mode with shortcut GND to Pin27

View File

@@ -1,17 +0,0 @@
import subprocess
# Import("env")
def get_firmware_specifier_build_flag():
#ret = subprocess.run(["git", "describe"], stdout=subprocess.PIPE, text=True) #Uses only annotated tags
ret = subprocess.run(["git", "describe", "--tags"], stdout=subprocess.PIPE, text=True) #Uses any tags
build_version = ret.stdout.strip()
build_flag = "-D AUTO_VERSION=\\\"" + build_version + "\\\""
print ("Firmware Revision: " + build_version)
return (build_flag)
#env.Append(
# BUILD_FLAGS=[get_firmware_specifier_build_flag()]
#)
get_firmware_specifier_build_flag()

View File

@@ -1,172 +0,0 @@
#!/usr/bin/python
"""
Convert config file to JSON
The JSON contains array of single fields. There is no hierarchy.
Fields are being grouped in GUI with "category".
A group is shown if a minimum of one field is visible.
Hints
capabilities is a dictionary
field:[true | false]
optional comma separated multiple values
"""
import os
import sys
import getopt
import configparser
from io import StringIO
from pyparsing import alphas, alphanums, Word, Literal, Combine, Group, Forward, ZeroOrMore, delimitedList
__author__ = "Thomas Hooge"
__copyright__ = "Copyleft 2025, all rights reversed"
__version__ = "0.1"
__email__ = "thomas@hoogi.de"
__status__ = "Development"
infile = None
outfile = ""
force = False # overwrite outfile
# Variables for condition parsing
fieldname = Combine(Word(alphas, exact=1) + Word(alphanums, max=15))
fieldvalue = Word(alphanums, max=16)
equals = Literal("=")
in_op = Literal("IN")
and_op = Literal("AND")
or_op = Literal("OR")
comparison = Group(fieldname + equals + fieldvalue) \
| Group(fieldname + in_op + delimitedList(fieldvalue))
expr = Forward()
expr <<= comparison + ZeroOrMore((and_op | or_op) + comparison)
def parse_condition(condition):
try:
result = expr.parseString(condition, parseAll=True)
except Exception as e:
return ""
out = StringIO()
andlist = []
for token in result:
# list: field = value or field IN value [, value ...]
# str: AND, OR
# combine ANDs and output reaching OR
if type(token) == str:
if token == "OR":
andstr = ",\n".join(andlist)
out.write(f'\t\t{{ {andstr} }},\n')
andlist = []
else:
if token[1] == '=':
andlist.append(f'"{token[0]}": "{token[2]}"')
elif token[1] == 'IN':
n = len(token) - 2
if n == 1:
# no list, write single value
andlist.append(f'"{token[0]}": "{token[2]}"')
else:
# write list
inlist = '", "'.join(token[2:])
andlist.append(f'"{token[0]}": [ "{inlist}" ]\n')
if len(andlist) > 0:
out.write("\t\t{{ {} }}".format(", ".join(andlist)))
return out.getvalue()
def create_flist():
flist = []
for field in config.sections():
properties = [f'\t"name": "{field}"']
for prop, val in config.items(field):
if prop in ["label", "type", "default", "description", "category", "check"]:
properties.append(f'\t"{prop}": "{val}"')
elif prop == "capabilities":
# multiple values possible
capas = []
for capa in val.split(','):
k, v = capa.split(':')
capas.append(f'"{k.strip()}":"{v.strip()}"')
capalist = ','.join(capas)
properties.append(f'\t"{prop}": {{{capalist}}}')
elif prop in ("min", "max"):
properties.append(f'\t"{prop}": {val}')
elif prop == "list":
entries = '", "'.join([x.strip() for x in val.split(',')])
properties.append(f'\t"list": ["{entries}"]')
elif prop == "dict":
d = {}
for l in val.splitlines():
if len(l) < 3:
continue
k, v = l.split(':')
d[k.strip()] = v.strip()
lines = []
for k,v in d.items():
lines.append(f'\t\t{{"l":"{v}","v":"{k}"}}')
entries = ",\n".join(lines)
properties.append(f'\t"list": [\n{entries}\n\t]')
elif prop == "condition":
jsoncond = parse_condition(val)
properties.append(f'\t"{prop}": [\n{jsoncond}\n\t]\n')
else:
pass # ignore unknown stuff
fieldprops = ",\n".join(properties)
flist.append(f'{{\n{fieldprops}\n}}')
return flist
def usage():
print("{} v{}".format(os.path.basename(__file__), __version__))
print(__copyright__)
print()
print("Command line options")
print(" -c --config config file name to use")
print(" -j --json json file name to generate")
print(" -f force overwrite of existing json file")
print(" -h show this help")
print()
if __name__ == '__main__':
try:
options, remainder = getopt.getopt(sys.argv[1:], 'c:j:fh', ['config=', 'json='])
except getopt.GetoptError as e:
print(e)
sys.exit(1)
filename = None
for opt, arg in options:
if opt in ('-c', '--config'):
infile = arg
elif opt in ('-j', '--json'):
outfile = arg
elif opt == '-h':
usage()
sys.exit(0)
elif opt == '-f':
force = True
if not infile:
print("Error: config filename missing")
sys.exit(1)
if not os.path.isfile(infile):
print(f"Error: configuration file '{filename} not found'")
sys.exit(1)
if os.path.isfile(outfile) and not force:
print(f"Error: json file '{outfile}' already exists")
sys.exit(1)
config = configparser.ConfigParser()
ret = config.read(infile)
if len(ret) == 0:
print(f"ERROR: Config file '{infile}' not found")
sys.exit(1)
flist = create_flist()
out = "[\n{}\n]\n".format(",\n".join(flist))
if not outfile:
# print to console
print(out)
else:
# write to file
with open(outfile, "w") as fh:
fh.write(out)

View File

@@ -1,61 +0,0 @@
#!/usr/bin/python
#
# Convert a Gimp-created XBM file to bitmap useable by drawBitmap()
#
import os
import sys
import re
from PIL import Image
if len(sys.argv) < 2:
print("Usage: xbmconvert.py <filename>")
sys.exit(1)
xbmfilename = sys.argv[1]
if not os.path.isfile(xbmfilename):
print(f"The file '{xbmfilename}' does not exists.")
sys.exit(1)
im = Image.open(xbmfilename)
imname = "image"
with open(xbmfilename, 'r') as fh:
pattern = r'static\s+unsigned\s+char\s+(\w+)_bits$$$$'
for line in fh:
match = re.search(pattern, line)
if match:
imname = match.group(1)
break
bytecount = int(im.width * im.height / 8)
print(f"#ifndef _{imname.upper()}_H_")
print(f"#define _{imname.upper()}_H_ 1\n")
print(f"#define {imname}_width {im.width}")
print(f"#define {imname}_height {im.height}")
print(f"const unsigned char {imname}_bits[{bytecount}] PROGMEM = {{")
n = 0
print(" ", end='')
f = im.tobytes()
switched_bytes = bytearray()
for i in range(0, len(f), 2):
# Switch LSB and MSB
switched_bytes.append(f[i + 1]) # Append MSB
switched_bytes.append(f[i]) # Append LSB
#for b in im.tobytes():
for b in switched_bytes:
#b2 = 0
#for i in range(8):
# # b2 |= ((b >> i) & 1) << (7 - i)
# b2 <<= 1
# b2 |= b & 1
# b >>= 1
n += 1
print(f"0x{b:02x}", end='')
if n < bytecount:
print(', ', end='')
if n % 12 == 0:
print("\n ", end='')
print("};\n\n#endif")

View File

@@ -1,13 +1,5 @@
#pragma once #pragma once
#include <Arduino.h> #include <Arduino.h>
#include <freertos/FreeRTOS.h>
#include <freertos/task.h>
static inline int64_t gwMonotonicUs(){
TickType_t ticks=xTaskGetTickCount();
return ((int64_t)ticks) * ((int64_t)portTICK_PERIOD_MS) * 1000;
}
class TimeAverage{ class TimeAverage{
double factor=0.3; double factor=0.3;
double current=0; double current=0;
@@ -78,7 +70,7 @@ class TimeMonitor{
} }
void reset(){ void reset(){
if (last != 0 && start != 0) loop->add(last-start); if (last != 0 && start != 0) loop->add(last-start);
start=gwMonotonicUs(); start=esp_timer_get_time();
for (size_t i=0;i<len;i++) current[i]=0; for (size_t i=0;i<len;i++) current[i]=0;
count++; count++;
@@ -122,7 +114,7 @@ class TimeMonitor{
for (size_t i=0;i<index;i++){ for (size_t i=0;i<index;i++){
if (current[i] != 0) sv=current[i]; if (current[i] != 0) sv=current[i];
} }
int64_t now=gwMonotonicUs(); int64_t now=esp_timer_get_time();
last=now; last=now;
current[index]=now; current[index]=now;
int64_t currentv=now-sv; int64_t currentv=now-sv;

View File

@@ -216,14 +216,6 @@ public:
{ {
return api->getLogger(); return api->getLogger();
} }
virtual Nmea2kTwai *getNMEA2000()
{
return api->getNMEA2000();
}
virtual tN2kDeviceList *getN2kDeviceList()
{
return api->getN2kDeviceList();
}
virtual GwBoatData *getBoatData() virtual GwBoatData *getBoatData()
{ {
return api->getBoatData(); return api->getBoatData();

View File

@@ -18,19 +18,31 @@ extra_configs=
[basedeps] [basedeps]
lib_deps = lib_deps =
ttlappalainen/NMEA2000-library @ 4.22.0 ttlappalainen_NMEA2000=https://github.com/wellenvogel/NMEA2000.git#20251126
ttlappalainen/NMEA0183 @ 1.10.1 ttlappalainen/NMEA0183 @ 1.10.1
ArduinoJson @ 6.18.5 ArduinoJson @ 6.18.5
ESP32Async/AsyncTCP @ 3.4.7 AsyncTCP-esphome @ 2.0.1
ESP32Async/ESPAsyncWebServer @ 3.8.0 ottowinter/ESPAsyncWebServer-esphome@2.0.1
FS FS
Preferences Preferences
ESPmDNS ESPmDNS
WiFi WiFi
Update Update
[devdeps]
lib_deps=
ttlappalainen_NMEA2000=symlink://../NMEA2000
ttlappalainen/NMEA0183 @ 1.10.1
ArduinoJson @ 6.18.5
AsyncTCP-esphome @ 2.0.1
ottowinter/ESPAsyncWebServer-esphome@2.0.1
FS
Preferences
ESPmDNS
WiFi
Update
[env] [env]
platform = espressif32 @ 6.11.0 platform = espressif32 @ 6.8.1
framework = arduino framework = arduino
;platform_packages= ;platform_packages=
; framework-arduinoespressif32 @ 3.20017.0 ; framework-arduinoespressif32 @ 3.20017.0
@@ -56,11 +68,6 @@ lib_ldf_mode = off
monitor_speed = 115200 monitor_speed = 115200
build_flags = build_flags =
-D PIO_ENV_BUILD=$PIOENV -D PIO_ENV_BUILD=$PIOENV
# -D CONFIG_ASYNC_TCP_RUNNING_CORE=1 # async_tcp task core assignment (default is any core)
-D CONFIG_ASYNC_TCP_STACK_SIZE=4096 # reduce the stack size (default is 16K)
-std=gnu++17
build_unflags =
-std=gnu++11
[sensors] [sensors]
; collect the libraries for sensors here ; collect the libraries for sensors here
@@ -72,6 +79,17 @@ lib_deps =
adafruit/Adafruit BusIO @ 1.14.5 adafruit/Adafruit BusIO @ 1.14.5
adafruit/Adafruit Unified Sensor @ 1.1.13 adafruit/Adafruit Unified Sensor @ 1.1.13
[env:m5stack-atom-dev]
board = m5stack-atom
lib_deps =
${devdeps.lib_deps}
fastled/FastLED @ 3.6.0
build_flags =
-D BOARD_M5ATOM
${env.build_flags}
upload_port = /dev/esp32
upload_protocol = esptool
[env:m5stack-atom] [env:m5stack-atom]
board = m5stack-atom board = m5stack-atom
lib_deps = ${env.lib_deps} lib_deps = ${env.lib_deps}

View File

@@ -100,7 +100,6 @@ GwLog logger(LOGLEVEL,NULL);
GwConfigHandler config(&logger); GwConfigHandler config(&logger);
#include "Nmea2kTwai.h" #include "Nmea2kTwai.h"
#include <N2kDeviceList.h>
static const unsigned long CAN_RECOVERY_PERIOD=3000; //ms static const unsigned long CAN_RECOVERY_PERIOD=3000; //ms
static const unsigned long NMEA2000_HEARTBEAT_INTERVAL=5000; static const unsigned long NMEA2000_HEARTBEAT_INTERVAL=5000;
class Nmea2kTwaiLog : public Nmea2kTwai{ class Nmea2kTwaiLog : public Nmea2kTwai{
@@ -127,7 +126,6 @@ class Nmea2kTwaiLog : public Nmea2kTwai{
#endif #endif
Nmea2kTwai &NMEA2000=*(new Nmea2kTwaiLog((gpio_num_t)ESP32_CAN_TX_PIN,(gpio_num_t)ESP32_CAN_RX_PIN,CAN_RECOVERY_PERIOD,&logger)); Nmea2kTwai &NMEA2000=*(new Nmea2kTwaiLog((gpio_num_t)ESP32_CAN_TX_PIN,(gpio_num_t)ESP32_CAN_RX_PIN,CAN_RECOVERY_PERIOD,&logger));
tN2kDeviceList *pN2kDeviceList;
#ifdef GWBUTTON_PIN #ifdef GWBUTTON_PIN
bool fixedApPass=false; bool fixedApPass=false;
@@ -304,15 +302,9 @@ public:
if (newValid != list[i]->valid) list[i]->changed=true; if (newValid != list[i]->valid) list[i]->changed=true;
list[i]->valid=newValid; list[i]->valid=newValid;
if (newValid){ if (newValid){
if (item->getCurrentType() == GWTYPE_STRING) {
String newValue=item->getStringValue();
if (newValue != list[i]->svalue) list[i]->changed=true;
list[i]->svalue=newValue;
} else {
double newValue=item->getDoubleValue(); double newValue=item->getDoubleValue();
if (newValue != list[i]->value) list[i]->changed=true; if (newValue != list[i]->value) list[i]->changed=true;
list[i]->value=newValue; list[i]->value=newValue;
}
int newSource=item->getLastSource(); int newSource=item->getLastSource();
if (newSource != list[i]->source){ if (newSource != list[i]->source){
list[i]->source=newSource; list[i]->source=newSource;
@@ -341,12 +333,6 @@ public:
status.n2kTx=countNMEA2KOut.getGlobal(); status.n2kTx=countNMEA2KOut.getGlobal();
channels.fillStatus(status); channels.fillStatus(status);
} }
virtual Nmea2kTwai *getNMEA2000(){
return &NMEA2000;
}
virtual tN2kDeviceList *getN2kDeviceList(){
return pN2kDeviceList;
}
virtual GwBoatData *getBoatData(){ virtual GwBoatData *getBoatData(){
return &boatData; return &boatData;
} }
@@ -781,7 +767,7 @@ void loopFunction(void *){
//if(Serial1.available()) {} //if(Serial1.available()) {}
//if(Serial.available()) {} //if(Serial.available()) {}
//if(Serial2.available()) {} //if(Serial2.available()) {}
vTaskDelay(1); //delay(1);
} }
} }
const String USERPREFIX="/api/user/"; const String USERPREFIX="/api/user/";
@@ -949,7 +935,6 @@ void setup() {
NMEA2000.SetMsgHandler([](const tN2kMsg &n2kMsg){ NMEA2000.SetMsgHandler([](const tN2kMsg &n2kMsg){
handleN2kMessage(n2kMsg,N2K_CHANNEL_ID); handleN2kMessage(n2kMsg,N2K_CHANNEL_ID);
}); });
pN2kDeviceList = new tN2kDeviceList(&NMEA2000);
NMEA2000.Open(); NMEA2000.Open();
logger.logDebug(GwLog::LOG,"starting addon tasks"); logger.logDebug(GwLog::LOG,"starting addon tasks");
logger.flush(); logger.flush();