Compare commits

...

243 Commits

Author SHA1 Message Date
Michael Clemens
6dceffd993 reactivated 80m, changed to color scheme to green on black 2021-05-17 17:29:57 +02:00
Michael Clemens
36cd505e8e
changed band limits, enabled 12m, disabled 80m 2021-05-13 15:20:01 +02:00
Michael Clemens
6d4fc996d9
Update callsign.cpp 2021-05-13 15:15:19 +02:00
reedbn
6c9e759901
Add user manual link to readme 2021-04-11 18:19:42 -07:00
reedbn
4918670eaa Save 12 bytes by adjusting math, inspried by Jack Purdum 2020-12-13 21:51:46 -08:00
reedbn
4eecda170d Add size to comment 2020-12-13 21:34:55 -08:00
reedbn
50381da6de Adjust keyer behavior to always run PTT button as if in KEYER_STRAIGHT mode 2020-12-13 18:19:13 -08:00
reedbn
b136f5e43c Add parentheses to force correct addition operation. Thanks to Barry Halterman for catching this. 2020-12-13 11:44:40 -08:00
Reed Nightingale
a4f5f4b3cb Update displayed version 2020-07-18 20:43:36 -07:00
reedbn
ab678f3e8b
Merge pull request #13 from reedbn/ide_1_8_13-size-fixes
Arduino IDE 1.8.13 size fixes
2020-07-18 20:30:40 -07:00
Reed Nightingale
c62eaaba2e Update readme with compiler info 2020-07-18 20:28:43 -07:00
Reed Nightingale
c11c8bfbe3 Remove bands that aren't on dial from band lookup struct 2020-07-18 18:58:43 -07:00
Reed Nightingale
84f47b0a23 Bump version 2020-05-05 21:41:45 -07:00
Reed Nightingale
740e881d28 Update readme due to new feature 2020-05-05 21:41:45 -07:00
Reed Nightingale
71e5f877da Add touch cancellation option 2020-05-05 21:32:30 -07:00
Reed Nightingale
e6fa6f935b Fix zraw assignment issue that was preventing touch press-and-hold from working 2020-05-05 21:25:52 -07:00
Reed Nightingale
b3a7e34d1b Replace 16-bit read style with slower, but apparently more reliable 8-bit read style 2020-05-05 20:36:03 -07:00
Reed Nightingale
d282062eef Force redraw after setupTouch is called so that menu is on screen 2020-05-05 20:35:17 -07:00
Reed Nightingale
f76b50fcc3 Update commented out debug print, and remove black screen draw at the end 2020-05-05 20:34:28 -07:00
Reed Nightingale
488e462c6c Debugging checkpoint 2020-05-05 19:54:07 -07:00
Reed Nightingale
c323cec0cb Update readme with lots of information for other prospective developers 2020-05-04 23:17:05 -07:00
Reed Nightingale
f541ff8928 Get rid of do-nothing functions 2020-05-04 23:00:31 -07:00
Reed Nightingale
e7748d2878 Switch colors from defines to consts 2020-05-04 22:53:41 -07:00
Reed Nightingale
e168a4d5b0 Bump version 2020-05-04 22:28:43 -07:00
Reed Nightingale
e3f23ae755 Slight tweak to how frequencies are changed so that quicklist changes will update the actual oscillators 2020-05-04 22:27:37 -07:00
Reed Nightingale
0b65559e97 Revert the previous commit 2020-05-04 22:26:09 -07:00
Reed Nightingale
2a412fd34a Force frequency change after recall. Unfortunately, this puts us just 4 bytes over the limit 2020-05-04 22:06:47 -07:00
Reed Nightingale
a138490dcc Change decision making in main menu loop to ensure display consistency when CAT changes 2020-05-04 21:57:24 -07:00
Reed Nightingale
4cf8f7a840 Use ACK/RACK, and set default response length to 1 so that it usually doesn't have to be re-set. 2020-05-04 01:01:05 -07:00
Reed Nightingale
6a6dc2a29a Remove debug drawing 2020-05-04 00:56:25 -07:00
Reed Nightingale
a7cbf50384 Fix comment based on reviewing hamlib code 2020-05-04 00:43:38 -07:00
Reed Nightingale
e809b37e29 Invert split value from what Yaesu's documentation says, because hamlib reads it inverted 2020-05-04 00:34:15 -07:00
Reed Nightingale
e770230d14 CAT debug stuff added 2020-05-04 00:03:43 -07:00
Reed Nightingale
7a32c8e205 Bump version number 2020-05-03 23:58:58 -07:00
Reed Nightingale
97d825351c Re-enable numpad, now that we have some more space 2020-05-03 23:56:08 -07:00
Reed Nightingale
2e74193d93 Give 0xBB a name 2020-05-03 23:54:38 -07:00
Reed Nightingale
ce0fac95fe Remove unused and fixed-output cases 2020-05-03 23:02:10 -07:00
Reed Nightingale
2505e54235 Remove unused cases 2020-05-03 22:37:38 -07:00
Reed Nightingale
8045b9b02b Remove explicit 0 sets 2020-05-03 22:35:35 -07:00
Reed Nightingale
eba70c64ef Swap EEPROM virtualization hardcoding for multi-byte reading using enums 2020-05-03 21:35:44 -07:00
Reed Nightingale
53a39b0699 Revise switch to use a single Serial.write, based on switched changes 2020-05-03 17:55:23 -07:00
Reed Nightingale
c6d83beff0 Use enums instead of hardcoded numbers 2020-05-03 17:45:43 -07:00
Reed Nightingale
8e4adbff72 Add enums for a bunch of commands 2020-05-03 16:36:27 -07:00
Reed Nightingale
a3b9f703ba Replace more indicies with enums 2020-05-03 15:36:37 -07:00
Reed Nightingale
8aa80bdbc7 Rename processing function, remove trailing spaces 2020-05-03 15:34:13 -07:00
Reed Nightingale
18a05460aa Simplify timeout logic, saving nearly 100 bytes 2020-05-03 15:32:06 -07:00
Reed Nightingale
e8c80a8304 Replace 'byte' with 'uint8_t' 2020-05-03 14:48:11 -07:00
Reed Nightingale
4e961bdea7 Give indicies names, so that it's easier to tell what's being extracted/set 2020-05-03 14:46:42 -07:00
Reed Nightingale
501205ebd5 Save ~100 bytes by rolling up the frequency parser 2020-04-27 00:17:41 -07:00
Reed Nightingale
a9b9b62dd1 Use driver-specified min/max frequencies 2020-04-26 23:49:26 -07:00
Reed Nightingale
55822dadf7 Give min/max frequency variable names 2020-04-26 23:46:32 -07:00
Reed Nightingale
ab8ba66067 Reuse menu running code between quicklist and numpad 2020-04-26 23:36:16 -07:00
Reed Nightingale
61c22474e9 Slim down some of the setup text 2020-04-26 23:36:16 -07:00
Reed Nightingale
77b1bd291e Save ~100 bytes by combining these two functions 2020-04-26 23:36:16 -07:00
Reed Nightingale
aa7e6825b4 Add quicklist to main menu, but disable numpad temporarily for size reasons 2020-04-26 23:36:16 -07:00
Reed Nightingale
fafc9acd0e Create quicklist menu 2020-04-26 23:36:16 -07:00
Reed Nightingale
b49b61615b Add quick list to EEPROM settings 2020-04-26 23:36:16 -07:00
Reed Nightingale
cd3364c835 Update version 2020-04-26 23:36:06 -07:00
Reed Nightingale
e980cff9ff Remove brackets from callsign 2020-04-26 23:35:54 -07:00
Reed Nightingale
0467223fe6 If RIT is on, turn it off before switching VFO 2020-04-26 15:17:18 -07:00
Reed Nightingale
94be10fd87 Justify RIT text left so that it doesn't interfere with the TX text 2020-04-26 15:11:44 -07:00
Reed Nightingale
8cc272b065 Use numpad icon 2020-04-26 15:03:32 -07:00
Reed Nightingale
f9907cebde Add icons for numpad and star 2020-04-26 15:03:18 -07:00
Reed Nightingale
b1e4466b0e Remove ornamental borders on setup page 2020-04-26 14:31:09 -07:00
Reed Nightingale
6cd72a5b8a Update sideband selection graphic on band switch 2020-04-25 23:06:16 -07:00
Reed Nightingale
e85db6781a Use same color for version and callsign for better callsign visibility 2020-04-25 22:56:35 -07:00
Reed Nightingale
bd1ff23d8f Add comment about toneAC2 library license 2020-04-25 22:55:10 -07:00
Reed Nightingale
c45a824276 Switch back to nicer font 2020-04-25 22:52:39 -07:00
Reed Nightingale
2c4448f6a0 Replace calls to the default Arduino Tone library to toneAC2, saving over 1024 bytes 2020-04-25 22:44:26 -07:00
Reed Nightingale
7a788b6297 Proxy the tone library so that it builds 2020-04-25 22:36:40 -07:00
Reed Nightingale
da45643035 Remove secondary pin from library 2020-04-25 22:36:17 -07:00
Reed Nightingale
0fe54d054f Add toneAC2 library 2020-04-25 22:32:17 -07:00
Reed Nightingale
2a8ff5e758 Replace fastest line routines with fillRect to save ~300 bytes 2020-04-25 21:09:54 -07:00
Reed Nightingale
06fa0e9ad7 Update README 2020-04-25 21:08:43 -07:00
Reed Nightingale
1f5f3a1f9f Use safer copy 2020-04-25 20:34:47 -07:00
Reed Nightingale
85ea714955 Show current setting on setting screen 2020-04-25 20:31:35 -07:00
Reed Nightingale
4e7d1717e0 Re-add version to main display, add callsign display, and re-enable TX indicator 2020-04-25 20:31:10 -07:00
Reed Nightingale
9b9a1610c2 Revert "Rough pass at button grids"
This reverts commit 1f97be3263.
2020-04-25 19:47:02 -07:00
Reed Nightingale
1f97be3263 Rough pass at button grids 2020-04-25 19:46:36 -07:00
Reed Nightingale
2db9899d4d Move SI5351 driver stuff into it's own header 2020-04-25 14:31:59 -07:00
Reed Nightingale
fdd2afce98 Fix auto-selection of sideband mode when tuning 2020-04-25 12:35:59 -07:00
Reed Nightingale
0f45a2fc43 Fix out-of-band midpoint selection 2020-04-25 12:17:15 -07:00
Reed Nightingale
f66a173dbe Get LSB and USB buttons to update properly 2020-04-25 12:10:25 -07:00
Reed Nightingale
8af59b0a85 Move button pressing logic to it's own file 2020-04-25 12:00:19 -07:00
Reed Nightingale
5596c0c8ab Remove function declarations from pin definition file 2020-04-25 11:47:11 -07:00
Reed Nightingale
91776fdcc7 Clean up pin descriptions a bit 2020-04-25 11:33:17 -07:00
Reed Nightingale
80494b62b9 Try a couple other fonts. None are great. 2020-04-21 21:56:51 -07:00
Reed Nightingale
98e62a6dca Switch to using a tiny but terrible looking font for size reasons 2020-04-21 21:40:02 -07:00
Reed Nightingale
40a13f1b5f Re-enable frequency entering 2020-04-21 21:38:34 -07:00
Reed Nightingale
1ee39ec535 Update includes, and disable numpad for space reasons for now 2020-04-21 21:19:47 -07:00
Reed Nightingale
cc013e5be4 stub out some functions that still need to be removed or refactored 2020-04-21 21:19:17 -07:00
Reed Nightingale
1766386411 Update pin names 2020-04-21 21:16:30 -07:00
Reed Nightingale
a2eae89733 Remove code that was moved to tuner, update includes and pin names 2020-04-21 21:16:10 -07:00
Reed Nightingale
1622d0ebe7 Update includes 2020-04-21 21:15:14 -07:00
Reed Nightingale
4a96027a07 Update includes 2020-04-21 21:14:42 -07:00
Reed Nightingale
67d34a3461 Update includes and pin names 2020-04-21 21:14:24 -07:00
Reed Nightingale
6face82c36 Update includes and pin names 2020-04-21 21:13:55 -07:00
Reed Nightingale
e593e0a060 Update includes 2020-04-21 21:13:33 -07:00
Reed Nightingale
5217858e23 Update includes and pin names 2020-04-21 21:13:14 -07:00
Reed Nightingale
363ea027f9 Update includes and pin names 2020-04-21 21:12:51 -07:00
Reed Nightingale
f5f914a2a9 Update includes 2020-04-21 21:12:02 -07:00
Reed Nightingale
ff4cc62e70 Update includes and pin names 2020-04-21 21:10:48 -07:00
Reed Nightingale
0091a1e17a Update includes, and make VFO switch explicit 2020-04-21 21:10:24 -07:00
Reed Nightingale
7d84ab744b Remove include 2020-04-21 21:09:55 -07:00
Reed Nightingale
b87ef7575a Move tuning functions to a single file 2020-04-21 21:09:21 -07:00
Reed Nightingale
f090a75221 Add dedicated scratch-space header so that we can track where these are still used 2020-04-21 21:08:51 -07:00
Reed Nightingale
f113551aa6 Use beginTransaction for spi, since it's surprisingly less additional memory than the setClockDivider call. Also, add SPI cleanup to early return case 2020-04-21 21:06:19 -07:00
Reed Nightingale
1e66731649 Remove unnecessary file 2020-04-12 17:45:06 -07:00
Reed Nightingale
b2e17548c2 Remove more redundant checks 2020-02-15 18:44:29 -08:00
Reed Nightingale
b116be8644 Remove redundant check 2020-02-15 18:43:10 -08:00
Reed Nightingale
3438bef4fb Clean up types a bit 2020-02-15 18:42:06 -08:00
Reed Nightingale
b909f4d652 Remove active delay 2020-02-15 17:10:27 -08:00
Reed Nightingale
f7229ebaf2 Save 406 bytes by refactoring the touch calibration routine 2020-02-15 17:07:18 -08:00
Reed Nightingale
6481812b4f Link in the setup menu. Exceeds available space :( 2020-02-15 15:31:14 -08:00
Reed Nightingale
cad3c3c57e Add an enterSubmenu routine to centralize that behavior 2020-02-15 15:27:48 -08:00
Reed Nightingale
98adba28ba Have a high level function in charge of finding the active submenu 2020-02-15 14:43:05 -08:00
Reed Nightingale
2bfa97687d Fix compiler errors 2020-02-15 14:29:23 -08:00
Reed Nightingale
0b96cfb1f7 Simplification of setting extraction, and setting menu running 2020-02-15 14:17:55 -08:00
Reed Nightingale
3dcbb8a0fe Small whitespace change 2020-02-15 14:14:44 -08:00
Reed Nightingale
e9e134079a Create a root menu for setup 2020-02-15 13:49:30 -08:00
Reed Nightingale
c8a90efee8 First pass at making menu selector run like a Menu_t 2020-02-15 13:49:09 -08:00
Reed Nightingale
72e4a71a4d First pass at splitting runSetting into Menu_t-compatible functions 2020-02-15 12:36:01 -08:00
Reed Nightingale
4582521f1b Play current frequency on entering 2020-02-14 19:28:58 -08:00
Reed Nightingale
bba125590b Change rendering of the input frequency 2020-02-14 19:16:11 -08:00
Reed Nightingale
f47c45194e Add actual menu running functions 2020-02-14 19:05:12 -08:00
Reed Nightingale
2f964b3ffb Remove unused function 2020-02-14 18:53:04 -08:00
Reed Nightingale
04560ef8b6 Add numpad functionality to main menu 2020-02-14 18:51:28 -08:00
Reed Nightingale
d42571f7a5 Start building out numpad menu 2020-02-14 18:50:57 -08:00
Reed Nightingale
4a0b0ec653 First pass at making numpad buttons 2020-02-14 18:50:26 -08:00
Reed Nightingale
2d3cc48b19 Play current frequency when VFO is selected 2020-02-12 22:18:02 -08:00
Reed Nightingale
423246f2b8 Add audio feedback for when entering/exiting bands 2020-02-11 23:36:43 -08:00
Reed Nightingale
5721e06a51 Instantly update tuning when changing modes 2020-02-11 23:31:14 -08:00
Reed Nightingale
23ecb4e4b2 Upcast to uint64 to avoid overflow 2020-02-11 23:24:37 -08:00
Reed Nightingale
77fffccb46 update band buttons when tuning 2020-02-11 23:10:06 -08:00
Reed Nightingale
405df5a757 Add method to update band buttons 2020-02-11 23:09:18 -08:00
Reed Nightingale
70a1025603 Fix progmem extraction 2020-02-11 22:59:44 -08:00
Reed Nightingale
4a27962a5b Save settings on VFO change or sideband change 2020-02-11 22:18:52 -08:00
Reed Nightingale
3b932d54b4 Actually activate buttons! 2020-02-11 22:17:30 -08:00
Reed Nightingale
c176604320 Use pointer passed in for work space 2020-02-11 22:15:46 -08:00
Reed Nightingale
2c20483931 Fix extraction in menu_utils 2020-02-11 22:01:07 -08:00
Reed Nightingale
6686f199b0 Unpack progmem fully 2020-02-11 21:45:26 -08:00
Reed Nightingale
2ff1d0d7e8 Fix types and expose some buttons 2020-02-11 21:44:40 -08:00
Reed Nightingale
c727d876eb Fix button drawing routine 2020-02-11 21:42:50 -08:00
Reed Nightingale
efd3bfc5ad Add color distinction to button drawing 2020-02-10 01:34:02 -08:00
Reed Nightingale
2722b4367a Update include annotation 2020-02-10 01:30:21 -08:00
Reed Nightingale
2826dfde56 First pass at button drawing implementation 2020-02-10 01:30:01 -08:00
Reed Nightingale
8239761a26 Move button declarations together, declaring rather than defining required functions 2020-02-10 01:17:47 -08:00
Reed Nightingale
25d5f3aaa9 Move most of the button stuff to a separate file 2020-02-10 01:02:03 -08:00
Reed Nightingale
4583288bfa Update functions to use new button array 2020-02-10 00:53:53 -08:00
Reed Nightingale
55096c9284 Fix most compiler errors 2020-02-10 00:53:17 -08:00
Reed Nightingale
8bd9d59e5a Displaying text should accept const char* 2020-02-10 00:51:58 -08:00
Reed Nightingale
2f9a0a3e04 Fix compile errors 2020-02-10 00:51:23 -08:00
Reed Nightingale
51288f1db3 Define types for formatFreq 2020-02-10 00:35:32 -08:00
Reed Nightingale
90d550a03a Prevent multi-include problems 2020-02-10 00:29:46 -08:00
Reed Nightingale
61b79ba06a Move formatFreq to utils 2020-02-10 00:16:15 -08:00
Reed Nightingale
5c0ecb087b Comment out submenus for now 2020-02-10 00:06:28 -08:00
Reed Nightingale
fa62b1136f Update morse playback to use new version of button 2020-02-10 00:06:11 -08:00
Reed Nightingale
1077b0bf84 First pass on main menu button conversion complete. Not expected to compile 2020-02-10 00:01:49 -08:00
Reed Nightingale
254b60d88f More button conversion 2020-02-09 23:52:02 -08:00
Reed Nightingale
1869e20cf0 Add some band utility functions for later use 2020-02-09 23:35:33 -08:00
Reed Nightingale
846c59e5cc Update more buttons 2020-02-09 23:32:22 -08:00
Reed Nightingale
dc5104324b Start converting buttons. Not expected to compile 2020-02-09 23:07:12 -08:00
Reed Nightingale
c4e9d3c156 Add function stub for drawing buttons 2020-02-09 23:06:44 -08:00
Reed Nightingale
6dacc470a2 Copy size of the actual object 2020-02-09 23:05:41 -08:00
Reed Nightingale
bc1e546ece const the size 2020-02-09 22:27:49 -08:00
Reed Nightingale
7a149a979d Add some comments to button and move char to the end 2020-02-09 22:19:54 -08:00
Reed Nightingale
bafe61d10a Update button struct to provide more functionality 2020-02-09 22:17:50 -08:00
Reed Nightingale
ecc3b9c1fb Add an init routine to the menu struct 2020-02-09 22:01:19 -08:00
Reed Nightingale
cc763a0dce Add endSelector util 2020-02-09 21:59:15 -08:00
Reed Nightingale
5c04e23504 Show puck on first selection 2020-02-09 21:45:51 -08:00
Reed Nightingale
9541c009be Extract progmem variables, and fix LIMITing on menu selection 2020-02-09 21:34:30 -08:00
Reed Nightingale
d4867ac35b Partial move of main menu buttons 2020-02-09 21:14:43 -08:00
Reed Nightingale
4483852742 Fix includes 2020-02-09 18:44:17 -08:00
Reed Nightingale
a92fdc7793 Update to use new touch functions 2020-02-09 18:41:47 -08:00
Reed Nightingale
26c670ce14 Include new point location 2020-02-09 18:41:26 -08:00
Reed Nightingale
947624518d Move touch functions to their own files 2020-02-09 18:41:13 -08:00
Reed Nightingale
72fc92b584 Add classes for button timing variables and point 2020-02-09 18:40:42 -08:00
Reed Nightingale
b29bd955ca Swap loop logic for new menu logic 2020-02-09 17:59:58 -08:00
Reed Nightingale
e50857f181 Have checkTouch support ButtonPress_e 2020-02-09 17:59:29 -08:00
Reed Nightingale
bd616caaba Have touch functions read to passed pointer 2020-02-09 17:58:45 -08:00
Reed Nightingale
d9667d6756 Add some debugging lines for now 2020-02-09 17:09:46 -08:00
Reed Nightingale
5e30551bf2 Move selector logic to menu_utils 2020-02-09 17:04:47 -08:00
Reed Nightingale
9e21faa6f1 morseText should take a const char* 2020-02-09 17:04:00 -08:00
Reed Nightingale
5a12a636be Make button aware of it's types 2020-02-09 17:03:37 -08:00
Reed Nightingale
79a37620c2 Play morse when selecting 2020-02-09 16:31:24 -08:00
Reed Nightingale
66a9d82308 Set size variable for main menu button array 2020-02-09 16:17:18 -08:00
Reed Nightingale
e42b21fdf3 Add touch button finding util 2020-02-09 16:16:57 -08:00
Reed Nightingale
49c5607dd3 Move menu utils to their own file 2020-02-09 16:01:52 -08:00
Reed Nightingale
e2249af826 Move color definitions to their own file 2020-02-09 15:56:43 -08:00
Reed Nightingale
5fb47187a6 Add definition for movePuck 2020-02-09 15:42:37 -08:00
Reed Nightingale
8faa9c58c7 Make function inputs const, and fix some compiler errors 2020-02-09 15:30:37 -08:00
Reed Nightingale
a7a2655f02 Give readTouch a standard type 2020-02-09 15:29:48 -08:00
Reed Nightingale
f42ad2bcd2 Move button definition 2020-02-09 15:28:46 -08:00
Reed Nightingale
21eb8978d5 Add selection 2020-02-09 14:52:21 -08:00
Reed Nightingale
801b1b08c1 Rough out the main menu 2020-02-09 14:10:32 -08:00
Reed Nightingale
b805761415 Start building a menu 'class' 2020-02-09 14:10:08 -08:00
Reed Nightingale
e35a9eecec Move LIMIT macro to utils 2020-02-08 14:59:06 -08:00
reedbn
1c60b8b63f
Merge pull request #11 from reedbn/cw-transmit-at-displayed-freq
Display TX (not RX) frequency in CW mode
2020-02-04 21:51:03 -08:00
reedbn
94aae2f289
Merge pull request #12 from reedbn/morse-menu
More work is probably needed here, but this is working better than nothing.
2020-02-04 21:50:16 -08:00
Reed Nightingale
225464c51a Remove TX offset in CW mode, and refactor frequency setting code so that it's (almost) all in one place. W0EB pointed out this issue, and it appears that it was discussed but never implemented in https://groups.io/g/BITX20/topic/20916360 2020-02-02 15:19:44 -08:00
Reed Nightingale
03406f62cc Merge branch 'master' into morse-menu 2020-01-30 20:25:30 -08:00
Reed Nightingale
bc5c39357f Fix inter-element timing 2020-01-30 20:23:17 -08:00
Reed Nightingale
d440be6c66 Rename INO file, so that it matches the repo name, and thus doesn't cause issues when checked out initially with default folder name 2020-01-29 21:44:46 -08:00
Reed Nightingale
13fa83d440 Update reported version 2020-01-28 20:54:02 -08:00
Reed Nightingale
0a880fe8a6 Merge master into morse-menu 2020-01-28 20:45:33 -08:00
reedbn
80454612c0
Merge pull request #10 from reedbn/text-justification
Text justification
2020-01-28 20:40:29 -08:00
Reed Nightingale
0ad20caa4b Merge master into text-justification 2020-01-28 20:34:23 -08:00
reedbn
87cf184fe6
Merge pull request #9 from reedbn/fixed-width-freqs
Fixed width freqs and replace MNU with gear symbol
2020-01-28 20:29:53 -08:00
Reed Nightingale
40c23919ed Add space between colon and frequency output 2020-01-28 20:26:34 -08:00
reedbn
fbc718b2ee
Merge pull request #8 from reedbn/cw-tx-issues
CW RX->TX responsiveness issues
2020-01-28 20:17:18 -08:00
Reed Nightingale
2d48b58461 Force a fix frequency width when possible 2020-01-28 02:31:14 -08:00
Reed Nightingale
6ddd46a853 Add spacers to keep frequency display a fixed width 2020-01-28 02:30:34 -08:00
Reed Nightingale
6313aa5adc Add two spacer characters 2020-01-28 02:29:37 -08:00
Reed Nightingale
1115441a57 Use gear icon for menu button 2020-01-28 01:52:31 -08:00
Reed Nightingale
04d836ab73 Removed unused function 2020-01-28 01:52:11 -08:00
Reed Nightingale
14d19ebbd8 Bigger settings icon 2020-01-28 01:42:59 -08:00
Reed Nightingale
015afb5cde First pass at menu icon 2020-01-28 01:25:19 -08:00
Reed Nightingale
21736fcc1d Forgot to add version files to the project. Silly me. 2020-01-27 23:11:32 -08:00
Reed Nightingale
03c159ba51 Render menus to the left, rather than center 2020-01-27 23:09:34 -08:00
Reed Nightingale
ceec8e2eb2 Push CW settings text to left, version to right 2020-01-27 23:09:05 -08:00
Reed Nightingale
0417907d03 Add justification option for text rendering 2020-01-27 23:08:32 -08:00
Reed Nightingale
8db6401bec Remove delay entirely. active_delay delays 10ms even when asked for 0 2020-01-27 21:51:34 -08:00
Reed Nightingale
e67885d8a0 disable delay before starting CW, allow straight key values through the paddle resistors, set correct timeout 2020-01-26 13:03:50 -08:00
Reed Nightingale
953a59f309 Don't play bools if morseMenu is off 2020-01-26 11:15:11 -08:00
Reed Nightingale
b0b11a93f0 Merge warning fixes, and fix 'new' warnings 2020-01-26 11:12:07 -08:00
Reed Nightingale
1175d1f7eb Add morseBool to main screen buttons 2020-01-25 16:32:04 -08:00
Reed Nightingale
9635d404b0 Add morseBool function 2020-01-25 16:31:32 -08:00
Reed Nightingale
c6b21bfa6d Instead of fast tuning, play back the current frequency upon selection 2020-01-25 15:54:47 -08:00
Reed Nightingale
68b85e6fa0 Add a brief pause before continuing to play the actual characters 2020-01-25 15:54:06 -08:00
Reed Nightingale
0c575d222f Add a way to terminate playback 2020-01-25 11:27:58 -08:00
Reed Nightingale
6dae08e222 Merge branch 'master' into morse-menu 2020-01-25 11:13:12 -08:00
Reed Nightingale
1b8966103d Send audio for 'Exit' menu option correctly 2020-01-22 00:49:59 -08:00
Reed Nightingale
814b646ab0 Shuffle CW menu a bit, and reduce lots of menu name string lengths so that they play faster when in CW mode 2020-01-22 00:11:52 -08:00
Reed Nightingale
70153f77d2 Add morse menu playback to setup 2020-01-22 00:04:12 -08:00
Reed Nightingale
13df195efc Replace morse table with direct binary values so that there can't be mis-translations of the hex 2020-01-21 23:54:42 -08:00
Reed Nightingale
418acf644b Fix morse encoding for 'y' 2020-01-21 23:53:09 -08:00
Reed Nightingale
a9e73bb102 Instead of having long press of tuner enter menu, have it turn on morse menu readout mode instead 2020-01-21 23:19:00 -08:00
Reed Nightingale
d854a51184 Add start and end of transmission to morse list 2020-01-21 23:17:47 -08:00
Reed Nightingale
edbb17e4c4 Fix keypad frequency entry code 2020-01-21 22:59:48 -08:00
Reed Nightingale
1c099663fc Have morseLetter respect global playback setting 2020-01-21 22:07:11 -08:00
Reed Nightingale
b0193ca762 Add option to turn on and off morse menu playback 2020-01-21 22:06:37 -08:00
Reed Nightingale
88d16bb073 Add morse playback of main menu 2020-01-21 21:30:18 -08:00
Reed Nightingale
3087830bfb Expose single letter morse to other code areas 2020-01-21 21:21:50 -08:00
72 changed files with 4514 additions and 2492 deletions

View File

@ -101,7 +101,17 @@ const uint8_t FreeSansBold9pt7bBitmaps[] PROGMEM = {
0x78, 0x1E, 0x03, 0x00, 0xC0, 0x70, 0x38, 0x0E, 0x00, 0xFE, 0xFE, 0x0E,
0x1C, 0x38, 0x38, 0x70, 0xE0, 0xFF, 0xFF, 0x37, 0x66, 0x66, 0x6E, 0xE6,
0x66, 0x66, 0x67, 0x30, 0xFF, 0xFF, 0x80, 0xCE, 0x66, 0x66, 0x67, 0x76,
0x66, 0x66, 0x6E, 0xC0, 0x71, 0x8E };
0x66, 0x66, 0x6E, 0xC0, 0x71, 0x8E, 0x0C, 0x00, 0xF0, 0x00, 0x8F, 0x10,
0x1C, 0xF3, 0x83, 0xFF, 0xFC, 0x7F, 0xFF, 0xE3, 0xF0, 0xFE, 0x1E, 0x07,
0xC1, 0xC0, 0x38, 0xF8, 0x01, 0xFF, 0x80, 0x1F, 0xF8, 0x01, 0xFF, 0x80,
0x1F, 0x1C, 0x03, 0x81, 0xE0, 0x78, 0x3F, 0x0F, 0xC7, 0xFF, 0xFE, 0x3F,
0xFF, 0xC3, 0xCF, 0x38, 0x08, 0xF1, 0x00, 0x0F, 0x00, 0x00, 0xEE, 0xFD,
0xDF, 0xBB, 0x80, 0x0E, 0xEF, 0xDD, 0xFB, 0xB8, 0x00, 0xEE, 0xFD, 0xDF,
0xBB, 0x80, 0x00, 0xE0, 0x1C, 0x03, 0x80, 0x00, 0x01, 0x00, 0x02, 0x00,
0x0E, 0x00, 0x1C, 0x00, 0x38, 0x1F, 0xFF, 0xDF, 0xFF, 0x0F, 0xF8, 0x0F,
0xE0, 0x1F, 0xC0, 0x3F, 0x80, 0x7F, 0x01, 0xEF, 0x03, 0x06, 0x04, 0x04,
0x00
};
const GFXglyph FreeSansBold9pt7bGlyphs[] PROGMEM = {
{ 0, 0, 0, 5, 0, 1 }, // 0x20 ' '
@ -198,11 +208,18 @@ const GFXglyph FreeSansBold9pt7bGlyphs[] PROGMEM = {
{ 1207, 4, 17, 7, 1, -12 }, // 0x7B '{'
{ 1216, 1, 17, 5, 2, -12 }, // 0x7C '|'
{ 1219, 4, 17, 7, 2, -12 }, // 0x7D '}'
{ 1228, 8, 2, 9, 0, -4 } }; // 0x7E '~'
{ 1228, 8, 2, 9, 0, -4 }, // 0x7E '~'
{ 1231, 20, 20, 22, 1, -12 }, // 0x7F gear icon
{ 1231, 0, 0, 10, 0, 1 }, // 0x80 10px space to match numbers
{ 1231, 0, 0, 4, 0, 1 }, // 0x81 4px space to match period
{ 1282, 11, 15, 13, 1, -10 }, // 0x82 numpad icon
{ 1304, 15, 15, 17, 1, -10 }, // 0x83 star icon
};
const GFXfont FreeSansBold9pt7b PROGMEM = {
(uint8_t *)FreeSansBold9pt7bBitmaps,
(GFXglyph *)FreeSansBold9pt7bGlyphs,
0x20, 0x7E, 22 };
// Approx. 1902 bytes
0x20, //first character
0x83, //last character
22 //yAdvance (newline)
};

View File

@ -158,7 +158,7 @@ public:
static inline uint8_t getRotation() __attribute__ ((always_inline)) { return rotation; }
static inline coord_t getCursorX() __attribute__ ((always_inline)) { return cursor_x; }
static inline coord_t getCursorY() __attribute__ ((always_inline)) { return cursor_y; }
static inline void getTextBounds(char *string, coord_t x, coord_t y, int16_t *x1, int16_t *y1, uint16_t *w, uint16_t *h, coord_t wi = _width);
static inline void getTextBounds(const char *string, coord_t x, coord_t y, int16_t *x1, int16_t *y1, uint16_t *w, uint16_t *h, coord_t wi = _width);
static inline void getTextBounds(const __FlashStringHelper *s, coord_t x, coord_t y, int16_t *x1, int16_t *y1, uint16_t *w, uint16_t *h, coord_t wi = _width);
virtual size_t write(uint8_t); // used by Arduino "Print.h" (and the one required virtual function)
@ -1061,7 +1061,7 @@ void PDQ_GFX<HW>::setFont(const GFXfont *f)
// Pass string and a cursor position, returns UL corner and W,H.
template<class HW>
void PDQ_GFX<HW>::getTextBounds(char *str, coord_t x, coord_t y, int16_t *x1, int16_t *y1, uint16_t *w, uint16_t *h, coord_t wi)
void PDQ_GFX<HW>::getTextBounds(const char *str, coord_t x, coord_t y, int16_t *x1, int16_t *y1, uint16_t *w, uint16_t *h, coord_t wi)
{
uint8_t c; // Current character

120
PDQ_MinLib/Picopixel.h Normal file
View File

@ -0,0 +1,120 @@
// Picopixel by Sebastian Weber. A tiny font
// with all characters within a 6 pixel height.
const uint8_t PicopixelBitmaps[] PROGMEM = {
0xE8, 0xB4, 0x57, 0xD5, 0xF5, 0x00, 0x4E, 0x3E, 0x80, 0xA5, 0x4A, 0x4A,
0x5A, 0x50, 0xC0, 0x6A, 0x40, 0x95, 0x80, 0xAA, 0x80, 0x5D, 0x00, 0x60,
0xE0, 0x80, 0x25, 0x48, 0x56, 0xD4, 0x75, 0x40, 0xC5, 0x4E, 0xC5, 0x1C,
0x97, 0x92, 0xF3, 0x1C, 0x53, 0x54, 0xE5, 0x48, 0x55, 0x54, 0x55, 0x94,
0xA0, 0x46, 0x64, 0xE3, 0x80, 0x98, 0xC5, 0x04, 0x56, 0xC6, 0x57, 0xDA,
0xD7, 0x5C, 0x72, 0x46, 0xD6, 0xDC, 0xF3, 0xCE, 0xF3, 0x48, 0x72, 0xD4,
0xB7, 0xDA, 0xF8, 0x24, 0xD4, 0xBB, 0x5A, 0x92, 0x4E, 0x8E, 0xEB, 0x58,
0x80, 0x9D, 0xB9, 0x90, 0x56, 0xD4, 0xD7, 0x48, 0x56, 0xD4, 0x40, 0xD7,
0x5A, 0x71, 0x1C, 0xE9, 0x24, 0xB6, 0xD4, 0xB6, 0xA4, 0x8C, 0x6B, 0x55,
0x00, 0xB5, 0x5A, 0xB5, 0x24, 0xE5, 0x4E, 0xEA, 0xC0, 0x91, 0x12, 0xD5,
0xC0, 0x54, 0xF0, 0x90, 0xC7, 0xF0, 0x93, 0x5E, 0x71, 0x80, 0x25, 0xDE,
0x5E, 0x30, 0x6E, 0x80, 0x77, 0x9C, 0x93, 0x5A, 0xB8, 0x45, 0x60, 0x92,
0xEA, 0xAA, 0x40, 0xD5, 0x6A, 0xD6, 0x80, 0x55, 0x00, 0xD7, 0x40, 0x75,
0x90, 0xE8, 0x71, 0xE0, 0xBA, 0x40, 0xB5, 0x80, 0xB5, 0x00, 0x8D, 0x54,
0xAA, 0x80, 0xAC, 0xE0, 0xE5, 0x70, 0x6A, 0x26, 0xFC, 0xC8, 0xAC, 0x5A};
const GFXglyph PicopixelGlyphs[] PROGMEM = {{0, 0, 0, 2, 0, 1}, // 0x20 ' '
{0, 1, 5, 2, 0, -4}, // 0x21 '!'
{1, 3, 2, 4, 0, -4}, // 0x22 '"'
{2, 5, 5, 6, 0, -4}, // 0x23 '#'
{6, 3, 6, 4, 0, -4}, // 0x24 '$'
{9, 3, 5, 4, 0, -4}, // 0x25 '%'
{11, 4, 5, 5, 0, -4}, // 0x26 '&'
{14, 1, 2, 2, 0, -4}, // 0x27 '''
{15, 2, 5, 3, 0, -4}, // 0x28 '('
{17, 2, 5, 3, 0, -4}, // 0x29 ')'
{19, 3, 3, 4, 0, -3}, // 0x2A '*'
{21, 3, 3, 4, 0, -3}, // 0x2B '+'
{23, 2, 2, 3, 0, 0}, // 0x2C ','
{24, 3, 1, 4, 0, -2}, // 0x2D '-'
{25, 1, 1, 2, 0, 0}, // 0x2E '.'
{26, 3, 5, 4, 0, -4}, // 0x2F '/'
{28, 3, 5, 4, 0, -4}, // 0x30 '0'
{30, 2, 5, 3, 0, -4}, // 0x31 '1'
{32, 3, 5, 4, 0, -4}, // 0x32 '2'
{34, 3, 5, 4, 0, -4}, // 0x33 '3'
{36, 3, 5, 4, 0, -4}, // 0x34 '4'
{38, 3, 5, 4, 0, -4}, // 0x35 '5'
{40, 3, 5, 4, 0, -4}, // 0x36 '6'
{42, 3, 5, 4, 0, -4}, // 0x37 '7'
{44, 3, 5, 4, 0, -4}, // 0x38 '8'
{46, 3, 5, 4, 0, -4}, // 0x39 '9'
{48, 1, 3, 2, 0, -3}, // 0x3A ':'
{49, 2, 4, 3, 0, -3}, // 0x3B ';'
{50, 2, 3, 3, 0, -3}, // 0x3C '<'
{51, 3, 3, 4, 0, -3}, // 0x3D '='
{53, 2, 3, 3, 0, -3}, // 0x3E '>'
{54, 3, 5, 4, 0, -4}, // 0x3F '?'
{56, 3, 5, 4, 0, -4}, // 0x40 '@'
{58, 3, 5, 4, 0, -4}, // 0x41 'A'
{60, 3, 5, 4, 0, -4}, // 0x42 'B'
{62, 3, 5, 4, 0, -4}, // 0x43 'C'
{64, 3, 5, 4, 0, -4}, // 0x44 'D'
{66, 3, 5, 4, 0, -4}, // 0x45 'E'
{68, 3, 5, 4, 0, -4}, // 0x46 'F'
{70, 3, 5, 4, 0, -4}, // 0x47 'G'
{72, 3, 5, 4, 0, -4}, // 0x48 'H'
{74, 1, 5, 2, 0, -4}, // 0x49 'I'
{75, 3, 5, 4, 0, -4}, // 0x4A 'J'
{77, 3, 5, 4, 0, -4}, // 0x4B 'K'
{79, 3, 5, 4, 0, -4}, // 0x4C 'L'
{81, 5, 5, 6, 0, -4}, // 0x4D 'M'
{85, 4, 5, 5, 0, -4}, // 0x4E 'N'
{88, 3, 5, 4, 0, -4}, // 0x4F 'O'
{90, 3, 5, 4, 0, -4}, // 0x50 'P'
{92, 3, 6, 4, 0, -4}, // 0x51 'Q'
{95, 3, 5, 4, 0, -4}, // 0x52 'R'
{97, 3, 5, 4, 0, -4}, // 0x53 'S'
{99, 3, 5, 4, 0, -4}, // 0x54 'T'
{101, 3, 5, 4, 0, -4}, // 0x55 'U'
{103, 3, 5, 4, 0, -4}, // 0x56 'V'
{105, 5, 5, 6, 0, -4}, // 0x57 'W'
{109, 3, 5, 4, 0, -4}, // 0x58 'X'
{111, 3, 5, 4, 0, -4}, // 0x59 'Y'
{113, 3, 5, 4, 0, -4}, // 0x5A 'Z'
{115, 2, 5, 3, 0, -4}, // 0x5B '['
{117, 3, 5, 4, 0, -4}, // 0x5C '\'
{119, 2, 5, 3, 0, -4}, // 0x5D ']'
{121, 3, 2, 4, 0, -4}, // 0x5E '^'
{122, 4, 1, 4, 0, 1}, // 0x5F '_'
{123, 2, 2, 3, 0, -4}, // 0x60 '`'
{124, 3, 4, 4, 0, -3}, // 0x61 'a'
{126, 3, 5, 4, 0, -4}, // 0x62 'b'
{128, 3, 3, 4, 0, -2}, // 0x63 'c'
{130, 3, 5, 4, 0, -4}, // 0x64 'd'
{132, 3, 4, 4, 0, -3}, // 0x65 'e'
{134, 2, 5, 3, 0, -4}, // 0x66 'f'
{136, 3, 5, 4, 0, -3}, // 0x67 'g'
{138, 3, 5, 4, 0, -4}, // 0x68 'h'
{140, 1, 5, 2, 0, -4}, // 0x69 'i'
{141, 2, 6, 3, 0, -4}, // 0x6A 'j'
{143, 3, 5, 4, 0, -4}, // 0x6B 'k'
{145, 2, 5, 3, 0, -4}, // 0x6C 'l'
{147, 5, 3, 6, 0, -2}, // 0x6D 'm'
{149, 3, 3, 4, 0, -2}, // 0x6E 'n'
{151, 3, 3, 4, 0, -2}, // 0x6F 'o'
{153, 3, 4, 4, 0, -2}, // 0x70 'p'
{155, 3, 4, 4, 0, -2}, // 0x71 'q'
{157, 2, 3, 3, 0, -2}, // 0x72 'r'
{158, 3, 4, 4, 0, -3}, // 0x73 's'
{160, 2, 5, 3, 0, -4}, // 0x74 't'
{162, 3, 3, 4, 0, -2}, // 0x75 'u'
{164, 3, 3, 4, 0, -2}, // 0x76 'v'
{166, 5, 3, 6, 0, -2}, // 0x77 'w'
{168, 3, 3, 4, 0, -2}, // 0x78 'x'
{170, 3, 4, 4, 0, -2}, // 0x79 'y'
{172, 3, 4, 4, 0, -3}, // 0x7A 'z'
{174, 3, 5, 4, 0, -4}, // 0x7B '{'
{176, 1, 6, 2, 0, -4}, // 0x7C '|'
{177, 3, 5, 4, 0, -4}, // 0x7D '}'
{179, 4, 2, 5, 0, -3}}; // 0x7E '~'
const GFXfont Picopixel PROGMEM = {(uint8_t *)PicopixelBitmaps,
(GFXglyph *)PicopixelGlyphs, 0x20, 0x7E, 7};
// Approx. 852 bytes

471
PDQ_MinLib/TomThumb.h Normal file
View File

@ -0,0 +1,471 @@
/**
** The original 3x5 font is licensed under the 3-clause BSD license:
**
** Copyright 1999 Brian J. Swetland
** Copyright 1999 Vassilii Khachaturov
** Portions (of vt100.c/vt100.h) copyright Dan Marks
**
** All rights reserved.
**
** Redistribution and use in source and binary forms, with or without
** modification, are permitted provided that the following conditions
** are met:
** 1. Redistributions of source code must retain the above copyright
** notice, this list of conditions, and the following disclaimer.
** 2. Redistributions in binary form must reproduce the above copyright
** notice, this list of conditions, and the following disclaimer in the
** documentation and/or other materials provided with the distribution.
** 3. The name of the authors may not be used to endorse or promote products
** derived from this software without specific prior written permission.
**
** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
**
** Modifications to Tom Thumb for improved readability are from Robey Pointer,
** see:
** http://robey.lag.net/2010/01/23/tiny-monospace-font.html
**
** The original author does not have any objection to relicensing of Robey
** Pointer's modifications (in this file) in a more permissive license. See
** the discussion at the above blog, and also here:
** http://opengameart.org/forumtopic/how-to-submit-art-using-the-3-clause-bsd-license
**
** Feb 21, 2016: Conversion from Linux BDF --> Adafruit GFX font,
** with the help of this Python script:
** https://gist.github.com/skelliam/322d421f028545f16f6d
** William Skellenger (williamj@skellenger.net)
** Twitter: @skelliam
**
*/
#define TOMTHUMB_USE_EXTENDED 0
const uint8_t TomThumbBitmaps[] PROGMEM = {
0x00, /* 0x20 space */
0x80, 0x80, 0x80, 0x00, 0x80, /* 0x21 exclam */
0xA0, 0xA0, /* 0x22 quotedbl */
0xA0, 0xE0, 0xA0, 0xE0, 0xA0, /* 0x23 numbersign */
0x60, 0xC0, 0x60, 0xC0, 0x40, /* 0x24 dollar */
0x80, 0x20, 0x40, 0x80, 0x20, /* 0x25 percent */
0xC0, 0xC0, 0xE0, 0xA0, 0x60, /* 0x26 ampersand */
0x80, 0x80, /* 0x27 quotesingle */
0x40, 0x80, 0x80, 0x80, 0x40, /* 0x28 parenleft */
0x80, 0x40, 0x40, 0x40, 0x80, /* 0x29 parenright */
0xA0, 0x40, 0xA0, /* 0x2A asterisk */
0x40, 0xE0, 0x40, /* 0x2B plus */
0x40, 0x80, /* 0x2C comma */
0xE0, /* 0x2D hyphen */
0x80, /* 0x2E period */
0x20, 0x20, 0x40, 0x80, 0x80, /* 0x2F slash */
0x60, 0xA0, 0xA0, 0xA0, 0xC0, /* 0x30 zero */
0x40, 0xC0, 0x40, 0x40, 0x40, /* 0x31 one */
0xC0, 0x20, 0x40, 0x80, 0xE0, /* 0x32 two */
0xC0, 0x20, 0x40, 0x20, 0xC0, /* 0x33 three */
0xA0, 0xA0, 0xE0, 0x20, 0x20, /* 0x34 four */
0xE0, 0x80, 0xC0, 0x20, 0xC0, /* 0x35 five */
0x60, 0x80, 0xE0, 0xA0, 0xE0, /* 0x36 six */
0xE0, 0x20, 0x40, 0x80, 0x80, /* 0x37 seven */
0xE0, 0xA0, 0xE0, 0xA0, 0xE0, /* 0x38 eight */
0xE0, 0xA0, 0xE0, 0x20, 0xC0, /* 0x39 nine */
0x80, 0x00, 0x80, /* 0x3A colon */
0x40, 0x00, 0x40, 0x80, /* 0x3B semicolon */
0x20, 0x40, 0x80, 0x40, 0x20, /* 0x3C less */
0xE0, 0x00, 0xE0, /* 0x3D equal */
0x80, 0x40, 0x20, 0x40, 0x80, /* 0x3E greater */
0xE0, 0x20, 0x40, 0x00, 0x40, /* 0x3F question */
0x40, 0xA0, 0xE0, 0x80, 0x60, /* 0x40 at */
0x40, 0xA0, 0xE0, 0xA0, 0xA0, /* 0x41 A */
0xC0, 0xA0, 0xC0, 0xA0, 0xC0, /* 0x42 B */
0x60, 0x80, 0x80, 0x80, 0x60, /* 0x43 C */
0xC0, 0xA0, 0xA0, 0xA0, 0xC0, /* 0x44 D */
0xE0, 0x80, 0xE0, 0x80, 0xE0, /* 0x45 E */
0xE0, 0x80, 0xE0, 0x80, 0x80, /* 0x46 F */
0x60, 0x80, 0xE0, 0xA0, 0x60, /* 0x47 G */
0xA0, 0xA0, 0xE0, 0xA0, 0xA0, /* 0x48 H */
0xE0, 0x40, 0x40, 0x40, 0xE0, /* 0x49 I */
0x20, 0x20, 0x20, 0xA0, 0x40, /* 0x4A J */
0xA0, 0xA0, 0xC0, 0xA0, 0xA0, /* 0x4B K */
0x80, 0x80, 0x80, 0x80, 0xE0, /* 0x4C L */
0xA0, 0xE0, 0xE0, 0xA0, 0xA0, /* 0x4D M */
0xA0, 0xE0, 0xE0, 0xE0, 0xA0, /* 0x4E N */
0x40, 0xA0, 0xA0, 0xA0, 0x40, /* 0x4F O */
0xC0, 0xA0, 0xC0, 0x80, 0x80, /* 0x50 P */
0x40, 0xA0, 0xA0, 0xE0, 0x60, /* 0x51 Q */
0xC0, 0xA0, 0xE0, 0xC0, 0xA0, /* 0x52 R */
0x60, 0x80, 0x40, 0x20, 0xC0, /* 0x53 S */
0xE0, 0x40, 0x40, 0x40, 0x40, /* 0x54 T */
0xA0, 0xA0, 0xA0, 0xA0, 0x60, /* 0x55 U */
0xA0, 0xA0, 0xA0, 0x40, 0x40, /* 0x56 V */
0xA0, 0xA0, 0xE0, 0xE0, 0xA0, /* 0x57 W */
0xA0, 0xA0, 0x40, 0xA0, 0xA0, /* 0x58 X */
0xA0, 0xA0, 0x40, 0x40, 0x40, /* 0x59 Y */
0xE0, 0x20, 0x40, 0x80, 0xE0, /* 0x5A Z */
0xE0, 0x80, 0x80, 0x80, 0xE0, /* 0x5B bracketleft */
0x80, 0x40, 0x20, /* 0x5C backslash */
0xE0, 0x20, 0x20, 0x20, 0xE0, /* 0x5D bracketright */
0x40, 0xA0, /* 0x5E asciicircum */
0xE0, /* 0x5F underscore */
0x80, 0x40, /* 0x60 grave */
0xC0, 0x60, 0xA0, 0xE0, /* 0x61 a */
0x80, 0xC0, 0xA0, 0xA0, 0xC0, /* 0x62 b */
0x60, 0x80, 0x80, 0x60, /* 0x63 c */
0x20, 0x60, 0xA0, 0xA0, 0x60, /* 0x64 d */
0x60, 0xA0, 0xC0, 0x60, /* 0x65 e */
0x20, 0x40, 0xE0, 0x40, 0x40, /* 0x66 f */
0x60, 0xA0, 0xE0, 0x20, 0x40, /* 0x67 g */
0x80, 0xC0, 0xA0, 0xA0, 0xA0, /* 0x68 h */
0x80, 0x00, 0x80, 0x80, 0x80, /* 0x69 i */
0x20, 0x00, 0x20, 0x20, 0xA0, 0x40, /* 0x6A j */
0x80, 0xA0, 0xC0, 0xC0, 0xA0, /* 0x6B k */
0xC0, 0x40, 0x40, 0x40, 0xE0, /* 0x6C l */
0xE0, 0xE0, 0xE0, 0xA0, /* 0x6D m */
0xC0, 0xA0, 0xA0, 0xA0, /* 0x6E n */
0x40, 0xA0, 0xA0, 0x40, /* 0x6F o */
0xC0, 0xA0, 0xA0, 0xC0, 0x80, /* 0x70 p */
0x60, 0xA0, 0xA0, 0x60, 0x20, /* 0x71 q */
0x60, 0x80, 0x80, 0x80, /* 0x72 r */
0x60, 0xC0, 0x60, 0xC0, /* 0x73 s */
0x40, 0xE0, 0x40, 0x40, 0x60, /* 0x74 t */
0xA0, 0xA0, 0xA0, 0x60, /* 0x75 u */
0xA0, 0xA0, 0xE0, 0x40, /* 0x76 v */
0xA0, 0xE0, 0xE0, 0xE0, /* 0x77 w */
0xA0, 0x40, 0x40, 0xA0, /* 0x78 x */
0xA0, 0xA0, 0x60, 0x20, 0x40, /* 0x79 y */
0xE0, 0x60, 0xC0, 0xE0, /* 0x7A z */
0x60, 0x40, 0x80, 0x40, 0x60, /* 0x7B braceleft */
0x80, 0x80, 0x00, 0x80, 0x80, /* 0x7C bar */
0xC0, 0x40, 0x20, 0x40, 0xC0, /* 0x7D braceright */
0x60, 0xC0, /* 0x7E asciitilde */
#if (TOMTHUMB_USE_EXTENDED)
0x80, 0x00, 0x80, 0x80, 0x80, /* 0xA1 exclamdown */
0x40, 0xE0, 0x80, 0xE0, 0x40, /* 0xA2 cent */
0x60, 0x40, 0xE0, 0x40, 0xE0, /* 0xA3 sterling */
0xA0, 0x40, 0xE0, 0x40, 0xA0, /* 0xA4 currency */
0xA0, 0xA0, 0x40, 0xE0, 0x40, /* 0xA5 yen */
0x80, 0x80, 0x00, 0x80, 0x80, /* 0xA6 brokenbar */
0x60, 0x40, 0xA0, 0x40, 0xC0, /* 0xA7 section */
0xA0, /* 0xA8 dieresis */
0x60, 0x80, 0x60, /* 0xA9 copyright */
0x60, 0xA0, 0xE0, 0x00, 0xE0, /* 0xAA ordfeminine */
0x40, 0x80, 0x40, /* 0xAB guillemotleft */
0xE0, 0x20, /* 0xAC logicalnot */
0xC0, /* 0xAD softhyphen */
0xC0, 0xC0, 0xA0, /* 0xAE registered */
0xE0, /* 0xAF macron */
0x40, 0xA0, 0x40, /* 0xB0 degree */
0x40, 0xE0, 0x40, 0x00, 0xE0, /* 0xB1 plusminus */
0xC0, 0x40, 0x60, /* 0xB2 twosuperior */
0xE0, 0x60, 0xE0, /* 0xB3 threesuperior */
0x40, 0x80, /* 0xB4 acute */
0xA0, 0xA0, 0xA0, 0xC0, 0x80, /* 0xB5 mu */
0x60, 0xA0, 0x60, 0x60, 0x60, /* 0xB6 paragraph */
0xE0, 0xE0, 0xE0, /* 0xB7 periodcentered */
0x40, 0x20, 0xC0, /* 0xB8 cedilla */
0x80, 0x80, 0x80, /* 0xB9 onesuperior */
0x40, 0xA0, 0x40, 0x00, 0xE0, /* 0xBA ordmasculine */
0x80, 0x40, 0x80, /* 0xBB guillemotright */
0x80, 0x80, 0x00, 0x60, 0x20, /* 0xBC onequarter */
0x80, 0x80, 0x00, 0xC0, 0x60, /* 0xBD onehalf */
0xC0, 0xC0, 0x00, 0x60, 0x20, /* 0xBE threequarters */
0x40, 0x00, 0x40, 0x80, 0xE0, /* 0xBF questiondown */
0x40, 0x20, 0x40, 0xE0, 0xA0, /* 0xC0 Agrave */
0x40, 0x80, 0x40, 0xE0, 0xA0, /* 0xC1 Aacute */
0xE0, 0x00, 0x40, 0xE0, 0xA0, /* 0xC2 Acircumflex */
0x60, 0xC0, 0x40, 0xE0, 0xA0, /* 0xC3 Atilde */
0xA0, 0x40, 0xA0, 0xE0, 0xA0, /* 0xC4 Adieresis */
0xC0, 0xC0, 0xA0, 0xE0, 0xA0, /* 0xC5 Aring */
0x60, 0xC0, 0xE0, 0xC0, 0xE0, /* 0xC6 AE */
0x60, 0x80, 0x80, 0x60, 0x20, 0x40, /* 0xC7 Ccedilla */
0x40, 0x20, 0xE0, 0xC0, 0xE0, /* 0xC8 Egrave */
0x40, 0x80, 0xE0, 0xC0, 0xE0, /* 0xC9 Eacute */
0xE0, 0x00, 0xE0, 0xC0, 0xE0, /* 0xCA Ecircumflex */
0xA0, 0x00, 0xE0, 0xC0, 0xE0, /* 0xCB Edieresis */
0x40, 0x20, 0xE0, 0x40, 0xE0, /* 0xCC Igrave */
0x40, 0x80, 0xE0, 0x40, 0xE0, /* 0xCD Iacute */
0xE0, 0x00, 0xE0, 0x40, 0xE0, /* 0xCE Icircumflex */
0xA0, 0x00, 0xE0, 0x40, 0xE0, /* 0xCF Idieresis */
0xC0, 0xA0, 0xE0, 0xA0, 0xC0, /* 0xD0 Eth */
0xC0, 0x60, 0xA0, 0xE0, 0xA0, /* 0xD1 Ntilde */
0x40, 0x20, 0xE0, 0xA0, 0xE0, /* 0xD2 Ograve */
0x40, 0x80, 0xE0, 0xA0, 0xE0, /* 0xD3 Oacute */
0xE0, 0x00, 0xE0, 0xA0, 0xE0, /* 0xD4 Ocircumflex */
0xC0, 0x60, 0xE0, 0xA0, 0xE0, /* 0xD5 Otilde */
0xA0, 0x00, 0xE0, 0xA0, 0xE0, /* 0xD6 Odieresis */
0xA0, 0x40, 0xA0, /* 0xD7 multiply */
0x60, 0xA0, 0xE0, 0xA0, 0xC0, /* 0xD8 Oslash */
0x80, 0x40, 0xA0, 0xA0, 0xE0, /* 0xD9 Ugrave */
0x20, 0x40, 0xA0, 0xA0, 0xE0, /* 0xDA Uacute */
0xE0, 0x00, 0xA0, 0xA0, 0xE0, /* 0xDB Ucircumflex */
0xA0, 0x00, 0xA0, 0xA0, 0xE0, /* 0xDC Udieresis */
0x20, 0x40, 0xA0, 0xE0, 0x40, /* 0xDD Yacute */
0x80, 0xE0, 0xA0, 0xE0, 0x80, /* 0xDE Thorn */
0x60, 0xA0, 0xC0, 0xA0, 0xC0, 0x80, /* 0xDF germandbls */
0x40, 0x20, 0x60, 0xA0, 0xE0, /* 0xE0 agrave */
0x40, 0x80, 0x60, 0xA0, 0xE0, /* 0xE1 aacute */
0xE0, 0x00, 0x60, 0xA0, 0xE0, /* 0xE2 acircumflex */
0x60, 0xC0, 0x60, 0xA0, 0xE0, /* 0xE3 atilde */
0xA0, 0x00, 0x60, 0xA0, 0xE0, /* 0xE4 adieresis */
0x60, 0x60, 0x60, 0xA0, 0xE0, /* 0xE5 aring */
0x60, 0xE0, 0xE0, 0xC0, /* 0xE6 ae */
0x60, 0x80, 0x60, 0x20, 0x40, /* 0xE7 ccedilla */
0x40, 0x20, 0x60, 0xE0, 0x60, /* 0xE8 egrave */
0x40, 0x80, 0x60, 0xE0, 0x60, /* 0xE9 eacute */
0xE0, 0x00, 0x60, 0xE0, 0x60, /* 0xEA ecircumflex */
0xA0, 0x00, 0x60, 0xE0, 0x60, /* 0xEB edieresis */
0x80, 0x40, 0x80, 0x80, 0x80, /* 0xEC igrave */
0x40, 0x80, 0x40, 0x40, 0x40, /* 0xED iacute */
0xE0, 0x00, 0x40, 0x40, 0x40, /* 0xEE icircumflex */
0xA0, 0x00, 0x40, 0x40, 0x40, /* 0xEF idieresis */
0x60, 0xC0, 0x60, 0xA0, 0x60, /* 0xF0 eth */
0xC0, 0x60, 0xC0, 0xA0, 0xA0, /* 0xF1 ntilde */
0x40, 0x20, 0x40, 0xA0, 0x40, /* 0xF2 ograve */
0x40, 0x80, 0x40, 0xA0, 0x40, /* 0xF3 oacute */
0xE0, 0x00, 0x40, 0xA0, 0x40, /* 0xF4 ocircumflex */
0xC0, 0x60, 0x40, 0xA0, 0x40, /* 0xF5 otilde */
0xA0, 0x00, 0x40, 0xA0, 0x40, /* 0xF6 odieresis */
0x40, 0x00, 0xE0, 0x00, 0x40, /* 0xF7 divide */
0x60, 0xE0, 0xA0, 0xC0, /* 0xF8 oslash */
0x80, 0x40, 0xA0, 0xA0, 0x60, /* 0xF9 ugrave */
0x20, 0x40, 0xA0, 0xA0, 0x60, /* 0xFA uacute */
0xE0, 0x00, 0xA0, 0xA0, 0x60, /* 0xFB ucircumflex */
0xA0, 0x00, 0xA0, 0xA0, 0x60, /* 0xFC udieresis */
0x20, 0x40, 0xA0, 0x60, 0x20, 0x40, /* 0xFD yacute */
0x80, 0xC0, 0xA0, 0xC0, 0x80, /* 0xFE thorn */
0xA0, 0x00, 0xA0, 0x60, 0x20, 0x40, /* 0xFF ydieresis */
0x00, /* 0x11D gcircumflex */
0x60, 0xC0, 0xE0, 0xC0, 0x60, /* 0x152 OE */
0x60, 0xE0, 0xC0, 0xE0, /* 0x153 oe */
0xA0, 0x60, 0xC0, 0x60, 0xC0, /* 0x160 Scaron */
0xA0, 0x60, 0xC0, 0x60, 0xC0, /* 0x161 scaron */
0xA0, 0x00, 0xA0, 0x40, 0x40, /* 0x178 Ydieresis */
0xA0, 0xE0, 0x60, 0xC0, 0xE0, /* 0x17D Zcaron */
0xA0, 0xE0, 0x60, 0xC0, 0xE0, /* 0x17E zcaron */
0x00, /* 0xEA4 uni0EA4 */
0x00, /* 0x13A0 uni13A0 */
0x80, /* 0x2022 bullet */
0xA0, /* 0x2026 ellipsis */
0x60, 0xE0, 0xE0, 0xC0, 0x60, /* 0x20AC Euro */
0xE0, 0xA0, 0xA0, 0xA0, 0xE0, /* 0xFFFD uniFFFD */
#endif /* (TOMTHUMB_USE_EXTENDED) */
};
/* {offset, width, height, advance cursor, x offset, y offset} */
const GFXglyph TomThumbGlyphs[] PROGMEM = {
{0, 8, 1, 2, 0, -5}, /* 0x20 space */
{1, 8, 5, 2, 0, -5}, /* 0x21 exclam */
{6, 8, 2, 4, 0, -5}, /* 0x22 quotedbl */
{8, 8, 5, 4, 0, -5}, /* 0x23 numbersign */
{13, 8, 5, 4, 0, -5}, /* 0x24 dollar */
{18, 8, 5, 4, 0, -5}, /* 0x25 percent */
{23, 8, 5, 4, 0, -5}, /* 0x26 ampersand */
{28, 8, 2, 2, 0, -5}, /* 0x27 quotesingle */
{30, 8, 5, 3, 0, -5}, /* 0x28 parenleft */
{35, 8, 5, 3, 0, -5}, /* 0x29 parenright */
{40, 8, 3, 4, 0, -5}, /* 0x2A asterisk */
{43, 8, 3, 4, 0, -4}, /* 0x2B plus */
{46, 8, 2, 3, 0, -2}, /* 0x2C comma */
{48, 8, 1, 4, 0, -3}, /* 0x2D hyphen */
{49, 8, 1, 2, 0, -1}, /* 0x2E period */
{50, 8, 5, 4, 0, -5}, /* 0x2F slash */
{55, 8, 5, 4, 0, -5}, /* 0x30 zero */
{60, 8, 5, 3, 0, -5}, /* 0x31 one */
{65, 8, 5, 4, 0, -5}, /* 0x32 two */
{70, 8, 5, 4, 0, -5}, /* 0x33 three */
{75, 8, 5, 4, 0, -5}, /* 0x34 four */
{80, 8, 5, 4, 0, -5}, /* 0x35 five */
{85, 8, 5, 4, 0, -5}, /* 0x36 six */
{90, 8, 5, 4, 0, -5}, /* 0x37 seven */
{95, 8, 5, 4, 0, -5}, /* 0x38 eight */
{100, 8, 5, 4, 0, -5}, /* 0x39 nine */
{105, 8, 3, 2, 0, -4}, /* 0x3A colon */
{108, 8, 4, 3, 0, -4}, /* 0x3B semicolon */
{112, 8, 5, 4, 0, -5}, /* 0x3C less */
{117, 8, 3, 4, 0, -4}, /* 0x3D equal */
{120, 8, 5, 4, 0, -5}, /* 0x3E greater */
{125, 8, 5, 4, 0, -5}, /* 0x3F question */
{130, 8, 5, 4, 0, -5}, /* 0x40 at */
{135, 8, 5, 4, 0, -5}, /* 0x41 A */
{140, 8, 5, 4, 0, -5}, /* 0x42 B */
{145, 8, 5, 4, 0, -5}, /* 0x43 C */
{150, 8, 5, 4, 0, -5}, /* 0x44 D */
{155, 8, 5, 4, 0, -5}, /* 0x45 E */
{160, 8, 5, 4, 0, -5}, /* 0x46 F */
{165, 8, 5, 4, 0, -5}, /* 0x47 G */
{170, 8, 5, 4, 0, -5}, /* 0x48 H */
{175, 8, 5, 4, 0, -5}, /* 0x49 I */
{180, 8, 5, 4, 0, -5}, /* 0x4A J */
{185, 8, 5, 4, 0, -5}, /* 0x4B K */
{190, 8, 5, 4, 0, -5}, /* 0x4C L */
{195, 8, 5, 4, 0, -5}, /* 0x4D M */
{200, 8, 5, 4, 0, -5}, /* 0x4E N */
{205, 8, 5, 4, 0, -5}, /* 0x4F O */
{210, 8, 5, 4, 0, -5}, /* 0x50 P */
{215, 8, 5, 4, 0, -5}, /* 0x51 Q */
{220, 8, 5, 4, 0, -5}, /* 0x52 R */
{225, 8, 5, 4, 0, -5}, /* 0x53 S */
{230, 8, 5, 4, 0, -5}, /* 0x54 T */
{235, 8, 5, 4, 0, -5}, /* 0x55 U */
{240, 8, 5, 4, 0, -5}, /* 0x56 V */
{245, 8, 5, 4, 0, -5}, /* 0x57 W */
{250, 8, 5, 4, 0, -5}, /* 0x58 X */
{255, 8, 5, 4, 0, -5}, /* 0x59 Y */
{260, 8, 5, 4, 0, -5}, /* 0x5A Z */
{265, 8, 5, 4, 0, -5}, /* 0x5B bracketleft */
{270, 8, 3, 4, 0, -4}, /* 0x5C backslash */
{273, 8, 5, 4, 0, -5}, /* 0x5D bracketright */
{278, 8, 2, 4, 0, -5}, /* 0x5E asciicircum */
{280, 8, 1, 4, 0, -1}, /* 0x5F underscore */
{281, 8, 2, 3, 0, -5}, /* 0x60 grave */
{283, 8, 4, 4, 0, -4}, /* 0x61 a */
{287, 8, 5, 4, 0, -5}, /* 0x62 b */
{292, 8, 4, 4, 0, -4}, /* 0x63 c */
{296, 8, 5, 4, 0, -5}, /* 0x64 d */
{301, 8, 4, 4, 0, -4}, /* 0x65 e */
{305, 8, 5, 4, 0, -5}, /* 0x66 f */
{310, 8, 5, 4, 0, -4}, /* 0x67 g */
{315, 8, 5, 4, 0, -5}, /* 0x68 h */
{320, 8, 5, 2, 0, -5}, /* 0x69 i */
{325, 8, 6, 4, 0, -5}, /* 0x6A j */
{331, 8, 5, 4, 0, -5}, /* 0x6B k */
{336, 8, 5, 4, 0, -5}, /* 0x6C l */
{341, 8, 4, 4, 0, -4}, /* 0x6D m */
{345, 8, 4, 4, 0, -4}, /* 0x6E n */
{349, 8, 4, 4, 0, -4}, /* 0x6F o */
{353, 8, 5, 4, 0, -4}, /* 0x70 p */
{358, 8, 5, 4, 0, -4}, /* 0x71 q */
{363, 8, 4, 4, 0, -4}, /* 0x72 r */
{367, 8, 4, 4, 0, -4}, /* 0x73 s */
{371, 8, 5, 4, 0, -5}, /* 0x74 t */
{376, 8, 4, 4, 0, -4}, /* 0x75 u */
{380, 8, 4, 4, 0, -4}, /* 0x76 v */
{384, 8, 4, 4, 0, -4}, /* 0x77 w */
{388, 8, 4, 4, 0, -4}, /* 0x78 x */
{392, 8, 5, 4, 0, -4}, /* 0x79 y */
{397, 8, 4, 4, 0, -4}, /* 0x7A z */
{401, 8, 5, 4, 0, -5}, /* 0x7B braceleft */
{406, 8, 5, 2, 0, -5}, /* 0x7C bar */
{411, 8, 5, 4, 0, -5}, /* 0x7D braceright */
{416, 8, 2, 4, 0, -5}, /* 0x7E asciitilde */
#if (TOMTHUMB_USE_EXTENDED)
{418, 8, 5, 2, 0, -5}, /* 0xA1 exclamdown */
{423, 8, 5, 4, 0, -5}, /* 0xA2 cent */
{428, 8, 5, 4, 0, -5}, /* 0xA3 sterling */
{433, 8, 5, 4, 0, -5}, /* 0xA4 currency */
{438, 8, 5, 4, 0, -5}, /* 0xA5 yen */
{443, 8, 5, 2, 0, -5}, /* 0xA6 brokenbar */
{448, 8, 5, 4, 0, -5}, /* 0xA7 section */
{453, 8, 1, 4, 0, -5}, /* 0xA8 dieresis */
{454, 8, 3, 4, 0, -5}, /* 0xA9 copyright */
{457, 8, 5, 4, 0, -5}, /* 0xAA ordfeminine */
{462, 8, 3, 3, 0, -5}, /* 0xAB guillemotleft */
{465, 8, 2, 4, 0, -4}, /* 0xAC logicalnot */
{467, 8, 1, 3, 0, -3}, /* 0xAD softhyphen */
{468, 8, 3, 4, 0, -5}, /* 0xAE registered */
{471, 8, 1, 4, 0, -5}, /* 0xAF macron */
{472, 8, 3, 4, 0, -5}, /* 0xB0 degree */
{475, 8, 5, 4, 0, -5}, /* 0xB1 plusminus */
{480, 8, 3, 4, 0, -5}, /* 0xB2 twosuperior */
{483, 8, 3, 4, 0, -5}, /* 0xB3 threesuperior */
{486, 8, 2, 3, 0, -5}, /* 0xB4 acute */
{488, 8, 5, 4, 0, -5}, /* 0xB5 mu */
{493, 8, 5, 4, 0, -5}, /* 0xB6 paragraph */
{498, 8, 3, 4, 0, -4}, /* 0xB7 periodcentered */
{501, 8, 3, 4, 0, -3}, /* 0xB8 cedilla */
{504, 8, 3, 2, 0, -5}, /* 0xB9 onesuperior */
{507, 8, 5, 4, 0, -5}, /* 0xBA ordmasculine */
{512, 8, 3, 3, 0, -5}, /* 0xBB guillemotright */
{515, 8, 5, 4, 0, -5}, /* 0xBC onequarter */
{520, 8, 5, 4, 0, -5}, /* 0xBD onehalf */
{525, 8, 5, 4, 0, -5}, /* 0xBE threequarters */
{530, 8, 5, 4, 0, -5}, /* 0xBF questiondown */
{535, 8, 5, 4, 0, -5}, /* 0xC0 Agrave */
{540, 8, 5, 4, 0, -5}, /* 0xC1 Aacute */
{545, 8, 5, 4, 0, -5}, /* 0xC2 Acircumflex */
{550, 8, 5, 4, 0, -5}, /* 0xC3 Atilde */
{555, 8, 5, 4, 0, -5}, /* 0xC4 Adieresis */
{560, 8, 5, 4, 0, -5}, /* 0xC5 Aring */
{565, 8, 5, 4, 0, -5}, /* 0xC6 AE */
{570, 8, 6, 4, 0, -5}, /* 0xC7 Ccedilla */
{576, 8, 5, 4, 0, -5}, /* 0xC8 Egrave */
{581, 8, 5, 4, 0, -5}, /* 0xC9 Eacute */
{586, 8, 5, 4, 0, -5}, /* 0xCA Ecircumflex */
{591, 8, 5, 4, 0, -5}, /* 0xCB Edieresis */
{596, 8, 5, 4, 0, -5}, /* 0xCC Igrave */
{601, 8, 5, 4, 0, -5}, /* 0xCD Iacute */
{606, 8, 5, 4, 0, -5}, /* 0xCE Icircumflex */
{611, 8, 5, 4, 0, -5}, /* 0xCF Idieresis */
{616, 8, 5, 4, 0, -5}, /* 0xD0 Eth */
{621, 8, 5, 4, 0, -5}, /* 0xD1 Ntilde */
{626, 8, 5, 4, 0, -5}, /* 0xD2 Ograve */
{631, 8, 5, 4, 0, -5}, /* 0xD3 Oacute */
{636, 8, 5, 4, 0, -5}, /* 0xD4 Ocircumflex */
{641, 8, 5, 4, 0, -5}, /* 0xD5 Otilde */
{646, 8, 5, 4, 0, -5}, /* 0xD6 Odieresis */
{651, 8, 3, 4, 0, -4}, /* 0xD7 multiply */
{654, 8, 5, 4, 0, -5}, /* 0xD8 Oslash */
{659, 8, 5, 4, 0, -5}, /* 0xD9 Ugrave */
{664, 8, 5, 4, 0, -5}, /* 0xDA Uacute */
{669, 8, 5, 4, 0, -5}, /* 0xDB Ucircumflex */
{674, 8, 5, 4, 0, -5}, /* 0xDC Udieresis */
{679, 8, 5, 4, 0, -5}, /* 0xDD Yacute */
{684, 8, 5, 4, 0, -5}, /* 0xDE Thorn */
{689, 8, 6, 4, 0, -5}, /* 0xDF germandbls */
{695, 8, 5, 4, 0, -5}, /* 0xE0 agrave */
{700, 8, 5, 4, 0, -5}, /* 0xE1 aacute */
{705, 8, 5, 4, 0, -5}, /* 0xE2 acircumflex */
{710, 8, 5, 4, 0, -5}, /* 0xE3 atilde */
{715, 8, 5, 4, 0, -5}, /* 0xE4 adieresis */
{720, 8, 5, 4, 0, -5}, /* 0xE5 aring */
{725, 8, 4, 4, 0, -4}, /* 0xE6 ae */
{729, 8, 5, 4, 0, -4}, /* 0xE7 ccedilla */
{734, 8, 5, 4, 0, -5}, /* 0xE8 egrave */
{739, 8, 5, 4, 0, -5}, /* 0xE9 eacute */
{744, 8, 5, 4, 0, -5}, /* 0xEA ecircumflex */
{749, 8, 5, 4, 0, -5}, /* 0xEB edieresis */
{754, 8, 5, 3, 0, -5}, /* 0xEC igrave */
{759, 8, 5, 3, 0, -5}, /* 0xED iacute */
{764, 8, 5, 4, 0, -5}, /* 0xEE icircumflex */
{769, 8, 5, 4, 0, -5}, /* 0xEF idieresis */
{774, 8, 5, 4, 0, -5}, /* 0xF0 eth */
{779, 8, 5, 4, 0, -5}, /* 0xF1 ntilde */
{784, 8, 5, 4, 0, -5}, /* 0xF2 ograve */
{789, 8, 5, 4, 0, -5}, /* 0xF3 oacute */
{794, 8, 5, 4, 0, -5}, /* 0xF4 ocircumflex */
{799, 8, 5, 4, 0, -5}, /* 0xF5 otilde */
{804, 8, 5, 4, 0, -5}, /* 0xF6 odieresis */
{809, 8, 5, 4, 0, -5}, /* 0xF7 divide */
{814, 8, 4, 4, 0, -4}, /* 0xF8 oslash */
{818, 8, 5, 4, 0, -5}, /* 0xF9 ugrave */
{823, 8, 5, 4, 0, -5}, /* 0xFA uacute */
{828, 8, 5, 4, 0, -5}, /* 0xFB ucircumflex */
{833, 8, 5, 4, 0, -5}, /* 0xFC udieresis */
{838, 8, 6, 4, 0, -5}, /* 0xFD yacute */
{844, 8, 5, 4, 0, -4}, /* 0xFE thorn */
{849, 8, 6, 4, 0, -5}, /* 0xFF ydieresis */
{855, 8, 1, 2, 0, -1}, /* 0x11D gcircumflex */
{856, 8, 5, 4, 0, -5}, /* 0x152 OE */
{861, 8, 4, 4, 0, -4}, /* 0x153 oe */
{865, 8, 5, 4, 0, -5}, /* 0x160 Scaron */
{870, 8, 5, 4, 0, -5}, /* 0x161 scaron */
{875, 8, 5, 4, 0, -5}, /* 0x178 Ydieresis */
{880, 8, 5, 4, 0, -5}, /* 0x17D Zcaron */
{885, 8, 5, 4, 0, -5}, /* 0x17E zcaron */
{890, 8, 1, 2, 0, -1}, /* 0xEA4 uni0EA4 */
{891, 8, 1, 2, 0, -1}, /* 0x13A0 uni13A0 */
{892, 8, 1, 2, 0, -3}, /* 0x2022 bullet */
{893, 8, 1, 4, 0, -1}, /* 0x2026 ellipsis */
{894, 8, 5, 4, 0, -5}, /* 0x20AC Euro */
{899, 8, 5, 4, 0, -5}, /* 0xFFFD uniFFFD */
#endif /* (TOMTHUMB_USE_EXTENDED) */
};
const GFXfont TomThumb PROGMEM = {(uint8_t *)TomThumbBitmaps,
(GFXglyph *)TomThumbGlyphs, 0x20, 0x7E, 6};

128
PDQ_MinLib/org_01.h Normal file
View File

@ -0,0 +1,128 @@
// Org_v01 by Orgdot (www.orgdot.com/aliasfonts). A tiny,
// stylized font with all characters within a 6 pixel height.
const uint8_t Org_01Bitmaps[] PROGMEM = {
0xE8, 0xA0, 0x57, 0xD5, 0xF5, 0x00, 0xFD, 0x3E, 0x5F, 0x80, 0x88, 0x88,
0x88, 0x80, 0xF4, 0xBF, 0x2E, 0x80, 0x80, 0x6A, 0x40, 0x95, 0x80, 0xAA,
0x80, 0x5D, 0x00, 0xC0, 0xF0, 0x80, 0x08, 0x88, 0x88, 0x00, 0xFC, 0x63,
0x1F, 0x80, 0xF8, 0xF8, 0x7F, 0x0F, 0x80, 0xF8, 0x7E, 0x1F, 0x80, 0x8C,
0x7E, 0x10, 0x80, 0xFC, 0x3E, 0x1F, 0x80, 0xFC, 0x3F, 0x1F, 0x80, 0xF8,
0x42, 0x10, 0x80, 0xFC, 0x7F, 0x1F, 0x80, 0xFC, 0x7E, 0x1F, 0x80, 0x90,
0xB0, 0x2A, 0x22, 0xF0, 0xF0, 0x88, 0xA8, 0xF8, 0x4E, 0x02, 0x00, 0xFD,
0x6F, 0x0F, 0x80, 0xFC, 0x7F, 0x18, 0x80, 0xF4, 0x7D, 0x1F, 0x00, 0xFC,
0x21, 0x0F, 0x80, 0xF4, 0x63, 0x1F, 0x00, 0xFC, 0x3F, 0x0F, 0x80, 0xFC,
0x3F, 0x08, 0x00, 0xFC, 0x2F, 0x1F, 0x80, 0x8C, 0x7F, 0x18, 0x80, 0xF9,
0x08, 0x4F, 0x80, 0x78, 0x85, 0x2F, 0x80, 0x8D, 0xB1, 0x68, 0x80, 0x84,
0x21, 0x0F, 0x80, 0xFD, 0x6B, 0x5A, 0x80, 0xFC, 0x63, 0x18, 0x80, 0xFC,
0x63, 0x1F, 0x80, 0xFC, 0x7F, 0x08, 0x00, 0xFC, 0x63, 0x3F, 0x80, 0xFC,
0x7F, 0x29, 0x00, 0xFC, 0x3E, 0x1F, 0x80, 0xF9, 0x08, 0x42, 0x00, 0x8C,
0x63, 0x1F, 0x80, 0x8C, 0x62, 0xA2, 0x00, 0xAD, 0x6B, 0x5F, 0x80, 0x8A,
0x88, 0xA8, 0x80, 0x8C, 0x54, 0x42, 0x00, 0xF8, 0x7F, 0x0F, 0x80, 0xEA,
0xC0, 0x82, 0x08, 0x20, 0x80, 0xD5, 0xC0, 0x54, 0xF8, 0x80, 0xF1, 0xFF,
0x8F, 0x99, 0xF0, 0xF8, 0x8F, 0x1F, 0x99, 0xF0, 0xFF, 0x8F, 0x6B, 0xA4,
0xF9, 0x9F, 0x10, 0x8F, 0x99, 0x90, 0xF0, 0x55, 0xC0, 0x8A, 0xF9, 0x90,
0xF8, 0xFD, 0x63, 0x10, 0xF9, 0x99, 0xF9, 0x9F, 0xF9, 0x9F, 0x80, 0xF9,
0x9F, 0x20, 0xF8, 0x88, 0x47, 0x1F, 0x27, 0xC8, 0x42, 0x00, 0x99, 0x9F,
0x99, 0x97, 0x8C, 0x6B, 0xF0, 0x96, 0x69, 0x99, 0x9F, 0x10, 0x2E, 0x8F,
0x2B, 0x22, 0xF8, 0x89, 0xA8, 0x0F, 0xE0};
const GFXglyph Org_01Glyphs[] PROGMEM = {{0, 0, 0, 6, 0, 1}, // 0x20 ' '
{0, 1, 5, 2, 0, -4}, // 0x21 '!'
{1, 3, 1, 4, 0, -4}, // 0x22 '"'
{2, 5, 5, 6, 0, -4}, // 0x23 '#'
{6, 5, 5, 6, 0, -4}, // 0x24 '$'
{10, 5, 5, 6, 0, -4}, // 0x25 '%'
{14, 5, 5, 6, 0, -4}, // 0x26 '&'
{18, 1, 1, 2, 0, -4}, // 0x27 '''
{19, 2, 5, 3, 0, -4}, // 0x28 '('
{21, 2, 5, 3, 0, -4}, // 0x29 ')'
{23, 3, 3, 4, 0, -3}, // 0x2A '*'
{25, 3, 3, 4, 0, -3}, // 0x2B '+'
{27, 1, 2, 2, 0, 0}, // 0x2C ','
{28, 4, 1, 5, 0, -2}, // 0x2D '-'
{29, 1, 1, 2, 0, 0}, // 0x2E '.'
{30, 5, 5, 6, 0, -4}, // 0x2F '/'
{34, 5, 5, 6, 0, -4}, // 0x30 '0'
{38, 1, 5, 2, 0, -4}, // 0x31 '1'
{39, 5, 5, 6, 0, -4}, // 0x32 '2'
{43, 5, 5, 6, 0, -4}, // 0x33 '3'
{47, 5, 5, 6, 0, -4}, // 0x34 '4'
{51, 5, 5, 6, 0, -4}, // 0x35 '5'
{55, 5, 5, 6, 0, -4}, // 0x36 '6'
{59, 5, 5, 6, 0, -4}, // 0x37 '7'
{63, 5, 5, 6, 0, -4}, // 0x38 '8'
{67, 5, 5, 6, 0, -4}, // 0x39 '9'
{71, 1, 4, 2, 0, -3}, // 0x3A ':'
{72, 1, 4, 2, 0, -3}, // 0x3B ';'
{73, 3, 5, 4, 0, -4}, // 0x3C '<'
{75, 4, 3, 5, 0, -3}, // 0x3D '='
{77, 3, 5, 4, 0, -4}, // 0x3E '>'
{79, 5, 5, 6, 0, -4}, // 0x3F '?'
{83, 5, 5, 6, 0, -4}, // 0x40 '@'
{87, 5, 5, 6, 0, -4}, // 0x41 'A'
{91, 5, 5, 6, 0, -4}, // 0x42 'B'
{95, 5, 5, 6, 0, -4}, // 0x43 'C'
{99, 5, 5, 6, 0, -4}, // 0x44 'D'
{103, 5, 5, 6, 0, -4}, // 0x45 'E'
{107, 5, 5, 6, 0, -4}, // 0x46 'F'
{111, 5, 5, 6, 0, -4}, // 0x47 'G'
{115, 5, 5, 6, 0, -4}, // 0x48 'H'
{119, 5, 5, 6, 0, -4}, // 0x49 'I'
{123, 5, 5, 6, 0, -4}, // 0x4A 'J'
{127, 5, 5, 6, 0, -4}, // 0x4B 'K'
{131, 5, 5, 6, 0, -4}, // 0x4C 'L'
{135, 5, 5, 6, 0, -4}, // 0x4D 'M'
{139, 5, 5, 6, 0, -4}, // 0x4E 'N'
{143, 5, 5, 6, 0, -4}, // 0x4F 'O'
{147, 5, 5, 6, 0, -4}, // 0x50 'P'
{151, 5, 5, 6, 0, -4}, // 0x51 'Q'
{155, 5, 5, 6, 0, -4}, // 0x52 'R'
{159, 5, 5, 6, 0, -4}, // 0x53 'S'
{163, 5, 5, 6, 0, -4}, // 0x54 'T'
{167, 5, 5, 6, 0, -4}, // 0x55 'U'
{171, 5, 5, 6, 0, -4}, // 0x56 'V'
{175, 5, 5, 6, 0, -4}, // 0x57 'W'
{179, 5, 5, 6, 0, -4}, // 0x58 'X'
{183, 5, 5, 6, 0, -4}, // 0x59 'Y'
{187, 5, 5, 6, 0, -4}, // 0x5A 'Z'
{191, 2, 5, 3, 0, -4}, // 0x5B '['
{193, 5, 5, 6, 0, -4}, // 0x5C '\'
{197, 2, 5, 3, 0, -4}, // 0x5D ']'
{199, 3, 2, 4, 0, -4}, // 0x5E '^'
{200, 5, 1, 6, 0, 1}, // 0x5F '_'
{201, 1, 1, 2, 0, -4}, // 0x60 '`'
{202, 4, 4, 5, 0, -3}, // 0x61 'a'
{204, 4, 5, 5, 0, -4}, // 0x62 'b'
{207, 4, 4, 5, 0, -3}, // 0x63 'c'
{209, 4, 5, 5, 0, -4}, // 0x64 'd'
{212, 4, 4, 5, 0, -3}, // 0x65 'e'
{214, 3, 5, 4, 0, -4}, // 0x66 'f'
{216, 4, 5, 5, 0, -3}, // 0x67 'g'
{219, 4, 5, 5, 0, -4}, // 0x68 'h'
{222, 1, 4, 2, 0, -3}, // 0x69 'i'
{223, 2, 5, 3, 0, -3}, // 0x6A 'j'
{225, 4, 5, 5, 0, -4}, // 0x6B 'k'
{228, 1, 5, 2, 0, -4}, // 0x6C 'l'
{229, 5, 4, 6, 0, -3}, // 0x6D 'm'
{232, 4, 4, 5, 0, -3}, // 0x6E 'n'
{234, 4, 4, 5, 0, -3}, // 0x6F 'o'
{236, 4, 5, 5, 0, -3}, // 0x70 'p'
{239, 4, 5, 5, 0, -3}, // 0x71 'q'
{242, 4, 4, 5, 0, -3}, // 0x72 'r'
{244, 4, 4, 5, 0, -3}, // 0x73 's'
{246, 5, 5, 6, 0, -4}, // 0x74 't'
{250, 4, 4, 5, 0, -3}, // 0x75 'u'
{252, 4, 4, 5, 0, -3}, // 0x76 'v'
{254, 5, 4, 6, 0, -3}, // 0x77 'w'
{257, 4, 4, 5, 0, -3}, // 0x78 'x'
{259, 4, 5, 5, 0, -3}, // 0x79 'y'
{262, 4, 4, 5, 0, -3}, // 0x7A 'z'
{264, 3, 5, 4, 0, -4}, // 0x7B '{'
{266, 1, 5, 2, 0, -4}, // 0x7C '|'
{267, 3, 5, 4, 0, -4}, // 0x7D '}'
{269, 5, 3, 6, 0, -3}}; // 0x7E '~'
const GFXfont Org_01 PROGMEM = {(uint8_t *)Org_01Bitmaps,
(GFXglyph *)Org_01Glyphs, 0x20, 0x7E, 7};
// Approx. 943 bytes

View File

@ -1,9 +1,72 @@
# ubitxv6
uBitx v6.3.1 Arduino sketch
IMPORTANT: It will compile only if you place this in the Arduino's own sketch directory! This is because of the restricted places that the Arduino searches for it's include files (the headers).
# uBiTXv6
- This is refactored to remove dependencies on any library except the standard Arduino libraries of SPI, I2C, EEPROM, etc.
- This works with ILI9341 display controller. The pins used by the TFT display are the same as that of the 16x2 LCD display of the previous versions.
- As the files are now split into .cpp files, the nano gui, morse reader, etc. can be reused in other projects as well
This project is found at https://github.com/reedbn/ubitxv6/
This is released under GPL v3 license.
It was forked from https://github.com/afarhan/ubitxv6/
The purpose of this project is to clean up (modularize) the source code, and add features that were not present
in Ashhar's original version of the project, without requiring any hardware modifications to a stock uBiTXv6.
New features include:
* Much faster screen refresh (vs Ashhar's 6.3.1 aka 6.0 release)
* Morse code readback for sightless operation
* Save/recall your favorite frequencies
* When adjusting settings, the existing/current setting is shown as reference
* Cancel touch recalibration
User Manual: https://docs.google.com/document/d/1jlllZbvFMCzO1MJLzlJDGb10HXSehlFNMDPsxGJZtvY/edit?usp=drivesdk
# Installing on Your Radio
There are plenty of tutorials on how to upload sketches to Arduino Nanos. Just search for them. Addtionally,
Ashhar created a video explaining the process specifically for uBiTX v6: https://www.youtube.com/watch?v=3n_V3prSJ_E
I developed this code using the Arduino IDE 1.8.9 toolchain, with -Wall and -Wextra compiler options turned on.
Arduino IDE 1.8.13 was reported to compile too big (see https://groups.io/g/BITX20/topic/75008576), but this
should be resolved in this project's tag R1.5.1.
# Personalized Callsign
To edit the callsign displayed, open the file `callsign.cpp` and change the string. Then re-compile and upload.
# Future Features/Modifications
There are some features that would be good to add, but I just didn't get around to.
* Setting to choose the tuning step size
* Setting to choose whether or not the knob tuning should accelerate (current behavior) or have a fixed interval
* Provide an option in each menu screen to load the default option for each setting
While the current code (as of 2020-05-05) is ~100 bytes shy of the full 30720 available on the nano, there's still
opportunity to add new features by "creating" room. Below is a list of places you might create room:
I added lots of bounds checking, especially on string writes, that, if removed, could free a good number of bytes.
While keeping them is best practice, for a non-IoT, non-critical piece of hardware, it shouldn't be a huge issue.
I added the RACK to the CAT to better emulate the FT-817 (I hope, at least!). Removing the RACK's and just leaving
the default ACK's will also free up bytes.
I added a bunch of strings to the menuing with the intention of helping people understand their functions, but
technically they're not necessary, and could all be removed.
I switched to a smaller footprint font than Ashhar's original code, but there are MUCH smaller fonts out there.
Changing to a lower resolution, scaled up font can save hundreds or thousands of bytes, but won't look as pretty.
Also, the star, gear, and numpad icons will need to be either added to the new font, or replaced with characters.
The first change I made to this fork was to replace Ashhar's original (incredibly slow) screen drawing routines
with PDQ. Since that change, Ashhar has updated his drawing routine to be MUCH faster than his original, but
still slightly slower than PDQ. It may be that Ashhar's new routines are smaller that PDQ, but I don't actually
know that for certain.
There are a good number of instances of back-to-back calls of strncpy_P and displayText. Creating a single
function that performs these operations together, and then calling that new function instead of the
back-to-back calls everywhere may save space.
# License
The majority of this code is released under GPL v3 license, per Ashhar's original code.
The files in the PDQ_MinLib subdirectory were copied from https://github.com/XarkLabs/PDQ_GFX_Libs, and have Adafruit's BSD License.
The files in the toneAC2 were copied from https://bitbucket.org/teckel12/arduino-toneac2/wiki/Home, and are GPL v3 licensed.

129
bands.cpp Normal file
View File

@ -0,0 +1,129 @@
#include <avr/pgmspace.h>
#include "bands.h"
#include "utils.h"
/*
* These are the bands for USA. Your bands may vary
*/
struct Band_t {
uint32_t min;
uint32_t max;
uint8_t band_meters;
char name[3];//Two characters + null terminator. Fixed width so we don't need to build separate pointers
};
const char UNKNOWN_BAND_NAME [] PROGMEM = "??";
constexpr Band_t bands [] PROGMEM {
// { 0UL, 255UL, 255, "U8"},//Utility conversion option
// { 0UL, 65535UL, 254, "UF"},//Utility conversion option
// { 530000UL, 1700000UL, 253, "AM"},//Broadcast AM, actually centers at 268, but uint8 can't do that
// { 1800000UL, 2000000UL, 160, "A0"},//0xA0 is 160
{ 3500000UL, 3800000UL, 80, "80"},
// { 5330500UL, 5403500UL, 60, "60"},
{ 7000000UL, 7200000UL, 40, "40"},
{10100000UL, 10150000UL, 30, "30"},
{14000000UL, 14350000UL, 20, "20"},
{18068000UL, 18168000UL, 17, "17"},
{21000000UL, 21450000UL, 15, "15"},
{24890000UL, 24990000UL, 12, "12"},
// {26965000UL, 27405000UL, 11, "CB"},//Citizen's Band
{28000000UL, 29700000UL, 10, "10"},
};
constexpr uint8_t NUM_BANDS = sizeof(bands)/sizeof(bands[0]);
int8_t findBandIndexFromBand(const uint8_t target_band)
{
Band_t band;
for(uint8_t i = 0; i < NUM_BANDS; ++i){
memcpy_P(&band,&bands[i],sizeof(band));
if(target_band == band.band_meters){
return i;
}
}
return -1;
}
int8_t findBandIndexFromFreq(uint32_t frequency)
{
Band_t band;
for(uint8_t i = 0; i < NUM_BANDS; ++i){
memcpy_P(&band,&bands[i],sizeof(band));
if(frequency <= band.max){
if(band.min <= frequency){
return i;
}
//No bands overlap, and they are ordered in strictly increasing frequency, so we need search no further
return -1;
}
}
return -1;
}
void getBandString(const unsigned long frequency,
char* band_string_out,
uint16_t max_string_length)
{
int8_t band_index = findBandIndexFromFreq(frequency);
if(-1 == band_index){
strncpy_P(band_string_out,UNKNOWN_BAND_NAME,max_string_length);
}
else{
Band_t band;
memcpy_P(&band,&bands[band_index],sizeof(band));
strncpy_P(band_string_out,band.name,max_string_length);
}
}
uint32_t getFreqInBand(const uint32_t frequency,
const uint8_t target_band)
{
int8_t target_band_index = findBandIndexFromBand(target_band);
if(-1 == target_band_index){
//Hard to target a band we don't know about...
return frequency;
}
//See if we're currrently in a valid band
int8_t current_band_index = findBandIndexFromFreq(frequency);
if(-1 == current_band_index){
//We're not in a known band - just go to the center of the target band
Band_t band;
memcpy_P(&band,&bands[target_band_index],sizeof(band));
return band.min + ((band.max - band.min)/2/100)*100;//midpoint truncated to 100Hz resolution
}
else{
//We're in a known band. Match the relative position in the target band.
Band_t current_band;
memcpy_P(&current_band,&bands[current_band_index],sizeof(current_band));
Band_t target_band;
memcpy_P(&target_band,&bands[target_band_index],sizeof(target_band));
const uint32_t range_current = current_band.max - current_band.min;
const uint32_t range_target = target_band.max - target_band.min;
return (((frequency - current_band.min) * (uint64_t)range_target / range_current + target_band.min)/100)*100;//truncated 100Hz
}
}
bool isFreqInBand(const uint32_t frequency,
const uint8_t check_band)
{
int8_t band_index = findBandIndexFromBand(check_band);
if(-1 == band_index){
//Unknown band - can't be in it
return false;
}
Band_t band;
memcpy_P(&band,&bands[band_index],sizeof(band));
if( (frequency <= band.max)
&& (band.min <= frequency)){
return true;
}
return false;
}

11
bands.h Normal file
View File

@ -0,0 +1,11 @@
#include <stdint.h>
void getBandString(const uint32_t frequency,
char* band_string_out,
const uint16_t max_string_length);
uint32_t getFreqInBand(const uint32_t frequency,
const uint8_t target_band);
bool isFreqInBand(const uint32_t frequency,
const uint8_t band);

50
button.cpp Normal file
View File

@ -0,0 +1,50 @@
#include "button.h"
#include <avr/pgmspace.h>
#include "color_theme.h"
#include "nano_gui.h"
#include "scratch_space.h"
void drawButton(Button* button)
{
uint16_t tc = COLOR_INACTIVE_TEXT;
uint16_t bgc = COLOR_INACTIVE_BACKGROUND;
const uint16_t bdc = COLOR_INACTIVE_BORDER;
switch(button->status())
{
case ButtonStatus_e::Stateless://Fallthrough intended
case ButtonStatus_e::Inactive://Fallthrough intended
default:
{
//Colors are initialized for this, so we're done
break;
}
case ButtonStatus_e::Active:
{
tc = COLOR_ACTIVE_TEXT;
bgc = COLOR_ACTIVE_BACKGROUND;
break;
}
}
if(nullptr != button->text){
strncpy_P(b,button->text,sizeof(b));
}
else if(nullptr != button->text_override){
button->text_override(b,sizeof(b));
}
else{
//Something's messed up
//Serial.println(F("No text for button!"));
return;
}
displayText(b, button->x, button->y, button->w, button->h, tc, bgc, bdc);
}
void extractAndDrawButton(Button* button_out, const Button* button_P)
{
memcpy_P(button_out,button_P,sizeof(*button_out));
drawButton(button_out);
}

20
button.h Normal file
View File

@ -0,0 +1,20 @@
#pragma once
#include <stdint.h>
enum ButtonStatus_e : uint8_t {
Stateless,
Inactive,
Active
};
struct Button {
int16_t x, y, w, h;
const char* text;//nullptr if text_override should be used
void (*text_override)(char* text_out, const uint16_t max_text_size);//nullptr if text should be used
ButtonStatus_e (*status)();//Used for coloring and morse menu
void (*on_select)();//Action to take when selected
char morse;
};
void extractAndDrawButton(Button* button_out, const Button* button_P);

7
button_press_e.h Normal file
View File

@ -0,0 +1,7 @@
#pragma once
enum ButtonPress_e : uint8_t {
NotPressed,
ShortPress,
LongPress
};

5
button_timing.h Normal file
View File

@ -0,0 +1,5 @@
#pragma once
static const uint8_t DEBOUNCE_DELAY_MS = 50;
static const uint16_t LONG_PRESS_TIME_MS = 3000;
static const uint8_t LONG_PRESS_POLL_TIME_MS = 10;

4
callsign.cpp Normal file
View File

@ -0,0 +1,4 @@
#include "callsign.h"
const char CALLSIGN_STRING_PRIVATE [] PROGMEM = "DL6MHC";
const char* const CALLSIGN_STRING = CALLSIGN_STRING_PRIVATE;

9
callsign.h Normal file
View File

@ -0,0 +1,9 @@
#pragma once
#include <avr/pgmspace.h>
/*
* VERSION_STRING is a PROGMEM string, so extract it before use, e.g.
* strncpy_P(char_buffer_out,VERSION_STRING,size_of_char_buffer_out);
*/
extern const char* const CALLSIGN_STRING;

22
color_theme.h Normal file
View File

@ -0,0 +1,22 @@
#pragma once
#include "colors.h"
static const unsigned int COLOR_TEXT = DISPLAY_WHITE;
static const unsigned int COLOR_BACKGROUND = DISPLAY_BLACK;
static const unsigned int COLOR_ACTIVE_VFO_TEXT = DISPLAY_WHITE;
static const unsigned int COLOR_ACTIVE_VFO_BACKGROUND = DISPLAY_BLACK;
static const unsigned int COLOR_INACTIVE_VFO_TEXT = DISPLAY_GREEN;
static const unsigned int COLOR_INACTIVE_VFO_BACKGROUND = DISPLAY_BLACK;
static const unsigned int COLOR_INACTIVE_TEXT = DISPLAY_GREEN;
static const unsigned int COLOR_INACTIVE_BACKGROUND = DISPLAY_BLACK;
static const unsigned int COLOR_INACTIVE_BORDER = DISPLAY_DARKGREY;
static const unsigned int COLOR_ACTIVE_TEXT = DISPLAY_BLACK;
static const unsigned int COLOR_ACTIVE_BACKGROUND = DISPLAY_GREEN;
static const unsigned int COLOR_ACTIVE_BORDER = DISPLAY_WHITE;
static const unsigned int COLOR_VERSION_TEXT = DISPLAY_GREEN;

20
colors.h Normal file
View File

@ -0,0 +1,20 @@
// Color definitions
static const uint16_t DISPLAY_BLACK = 0x0000; ///< 0, 0, 0
static const uint16_t DISPLAY_NAVY = 0x000F; ///< 0, 0, 123
static const uint16_t DISPLAY_DARKGREEN = 0x03E0; ///< 0, 125, 0
static const uint16_t DISPLAY_DARKCYAN = 0x03EF; ///< 0, 125, 123
static const uint16_t DISPLAY_MAROON = 0x7800; ///< 123, 0, 0
static const uint16_t DISPLAY_PURPLE = 0x780F; ///< 123, 0, 123
static const uint16_t DISPLAY_OLIVE = 0x7BE0; ///< 123, 125, 0
static const uint16_t DISPLAY_LIGHTGREY = 0xC618; ///< 198, 195, 198
static const uint16_t DISPLAY_DARKGREY = 0x7BEF; ///< 123, 125, 123
static const uint16_t DISPLAY_BLUE = 0x001F; ///< 0, 0, 255
static const uint16_t DISPLAY_GREEN = 0x07E0; ///< 0, 255, 0
static const uint16_t DISPLAY_CYAN = 0x07FF; ///< 0, 255, 255
static const uint16_t DISPLAY_RED = 0xF800; ///< 255, 0, 0
static const uint16_t DISPLAY_MAGENTA = 0xF81F; ///< 255, 0, 255
static const uint16_t DISPLAY_YELLOW = 0xFFE0; ///< 255, 255, 0
static const uint16_t DISPLAY_WHITE = 0xFFFF; ///< 255, 255, 255
static const uint16_t DISPLAY_ORANGE = 0xFD20; ///< 255, 165, 0
static const uint16_t DISPLAY_GREENYELLOW = 0xAFE5; ///< 173, 255, 41
static const uint16_t DISPLAY_PINK = 0xFC18; ///< 255, 130, 198

View File

@ -2,7 +2,7 @@
#include <stdint.h>
#include "encoder.h"
#include "ubitx.h"//Pin definitions
#include "pin_definitions.h"
//Normal encoder state
uint8_t prev_enc = 0;
@ -16,7 +16,7 @@ static const uint8_t MOMENTUM_MULTIPLIER = 1;
uint8_t enc_state (void)
{
return (digitalRead(ENC_A)?1:0 + digitalRead(ENC_B)?2:0);
return (digitalRead(PIN_ENC_B) << 1) + (digitalRead(PIN_ENC_A) << 0);
}
/*
@ -71,13 +71,13 @@ void enc_setup(void)
{
enc_count = 0;
// This is already done in setup() ?
//pinMode(ENC_A, INPUT);
//pinMode(ENC_B, INPUT);
//pinMode(PIN_ENC_A, INPUT);
//pinMode(PIN_ENC_B, INPUT);
prev_enc = enc_state();
// Setup Pin Change Interrupts for the encoder inputs
pci_setup(ENC_A);
pci_setup(ENC_B);
pci_setup(PIN_ENC_A);
pci_setup(PIN_ENC_B);
//Set up timer interrupt for momentum
TCCR1A = 0;//"normal" mode

100
keyer.cpp
View File

@ -1,6 +1,7 @@
#include <Arduino.h>
#include "toneAC2/toneAC2.h"
#include "pin_definitions.h"
#include "settings.h"
#include "ubitx.h"
#include "tuner.h"
/**
CW Keyer
@ -38,16 +39,14 @@ static const unsigned int cwAdcDotTo = 600;
static const unsigned int cwAdcDashFrom = cwAdcDotTo + 1;
static const unsigned int cwAdcDashTo = 800;
static const unsigned int delayBeforeCWStartTime = 50;
/**
* Starts transmitting the carrier with the sidetone
* It assumes that we have called cwTxStart and not called cwTxStop
* each time it is called, the cwTimeOut is pushed further into the future
*/
void cwKeydown(){
tone(CW_TONE, globalSettings.cwSideToneFreq);
digitalWrite(CW_KEY, 1);
toneAC2(PIN_CW_TONE, globalSettings.cwSideToneFreq);
digitalWrite(PIN_CW_KEY, 1);
globalSettings.cwExpirationTimeMs = millis() + globalSettings.cwActiveTimeoutMs;
}
@ -57,8 +56,8 @@ void cwKeydown(){
* Pushes the cwTimeout further into the future
*/
void cwKeyUp(){
noTone(CW_TONE);
digitalWrite(CW_KEY, 0);
noToneAC2();
digitalWrite(PIN_CW_KEY, 0);
globalSettings.cwExpirationTimeMs = millis() + globalSettings.cwActiveTimeoutMs;
}
@ -78,13 +77,8 @@ uint8_t keyerControl = 0;
//create by KD8CEC for compatible with new CW Logic
char update_PaddleLatch(bool isUpdateKeyState) {
unsigned char tmpKeyerControl = 0;
unsigned int paddle = analogRead(ANALOG_KEYER);
//use the PTT as the key for tune up, quick QSOs
if (digitalRead(PTT) == 0)
tmpKeyerControl |= DIT_L;
else if (paddle >= cwAdcDashFrom && paddle <= cwAdcDashTo)
unsigned int paddle = analogRead(PIN_ANALOG_KEYER);
if (paddle >= cwAdcDashFrom && paddle <= cwAdcDashTo)
tmpKeyerControl |= DAH_L;
else if (paddle >= cwAdcDotFrom && paddle <= cwAdcDotTo)
tmpKeyerControl |= DIT_L;
@ -93,7 +87,7 @@ char update_PaddleLatch(bool isUpdateKeyState) {
else{
if (KeyerMode_e::KEYER_STRAIGHT != globalSettings.keyerMode)
tmpKeyerControl = 0 ;
else if (paddle <= cwAdcSTTo)
else if (paddle <= cwAdcDashTo)
tmpKeyerControl = DIT_L ;
else
tmpKeyerControl = 0 ;
@ -111,9 +105,41 @@ char update_PaddleLatch(bool isUpdateKeyState) {
******************************************************************************/
void cwKeyer(void){
bool continue_loop = true;
unsigned tmpKeyControl = 0;
char tmpKeyControl = 0;
if(KeyerMode_e::KEYER_STRAIGHT != globalSettings.keyerMode){
if((KeyerMode_e::KEYER_STRAIGHT == globalSettings.keyerMode)
|| (digitalRead(PIN_PTT) == 0)){//use the PTT as the key for tune up, quick QSOs
while(1){
tmpKeyControl = update_PaddleLatch(0) | (digitalRead(PIN_PTT)?0:DIT_L);
//Serial.println((int)tmpKeyControl);
if ((tmpKeyControl & DIT_L) == DIT_L) {
// if we are here, it is only because the key is pressed
if (!globalSettings.txActive){
startTx(TuningMode_e::TUNE_CW);
globalSettings.cwExpirationTimeMs = millis() + globalSettings.cwActiveTimeoutMs;
}
cwKeydown();
while ( tmpKeyControl & DIT_L == DIT_L){
tmpKeyControl = update_PaddleLatch(0) | (digitalRead(PIN_PTT)?0:DIT_L);
//Serial.println((int)tmpKeyControl);
}
cwKeyUp();
}
else{
if (0 < globalSettings.cwExpirationTimeMs && globalSettings.cwExpirationTimeMs < millis()){
globalSettings.cwExpirationTimeMs = 0;
stopTx();
}
return;//Tx stop control by Main Loop
}
checkCAT();
} //end of while
}
else{//KEYER_IAMBIC_*
while(continue_loop){
switch(keyerState){
case IDLE:
@ -156,8 +182,6 @@ void cwKeyer(void){
case KEYED_PREP:
//modified KD8CEC
if (!globalSettings.txActive){
//DelayTime Option
active_delay(delayBeforeCWStartTime * 2);
globalSettings.cwExpirationTimeMs = millis() + globalSettings.cwActiveTimeoutMs;
startTx(TuningMode_e::TUNE_CW);
}
@ -171,7 +195,7 @@ void cwKeyer(void){
case KEYED:
if (millis() > ktimer) { // are we at end of key down ?
cwKeyUp();
ktimer = millis() + (globalSettings.cwDitDurationMs / 10); // inter-element time
ktimer = millis() + globalSettings.cwDitDurationMs; // inter-element time
keyerState = INTER_ELEMENT; // next state
}
else if(KeyerMode_e::KEYER_IAMBIC_B == globalSettings.keyerMode){
@ -196,39 +220,7 @@ void cwKeyer(void){
checkCAT();
} //end of while
}
else{//KEYER_STRAIGHT
while(1){
char state = update_PaddleLatch(0);
// Serial.println((int)state);
if (state == DIT_L) {
// if we are here, it is only because the key is pressed
if (!globalSettings.txActive){
startTx(TuningMode_e::TUNE_CW);
//DelayTime Option
active_delay(delayBeforeCWStartTime * 2);
globalSettings.cwExpirationTimeMs = millis() + globalSettings.cwDitDurationMs;
}
cwKeydown();
while ( update_PaddleLatch(0) == DIT_L )
active_delay(1);
cwKeyUp();
}
else{
if (0 < globalSettings.cwExpirationTimeMs && globalSettings.cwExpirationTimeMs < millis()){
globalSettings.cwExpirationTimeMs = 0;
stopTx();
}
return;//Tx stop control by Main Loop
}
checkCAT();
} //end of while
}//end of else KEYER_STRAIGHT
}//end of KEYER_IAMBIC_*
}

43
menu.cpp Normal file
View File

@ -0,0 +1,43 @@
#include "menu.h"
#include "menu_main.h"
void runActiveMenu(const ButtonPress_e tuner_button,
const ButtonPress_e touch_button,
const Point touch_point,
const int16_t knob)
{
Menu_t* parent_menu = rootMenu;//rootMenu is it's own parent
Menu_t* active_menu = rootMenu;
while(nullptr != active_menu->active_submenu){
parent_menu = active_menu;
active_menu = parent_menu->active_submenu;
}
MenuReturn_e mr = active_menu->runMenu(tuner_button,touch_button,touch_point,knob);
switch(mr){
case MenuReturn_e::StillActive://Fallthrough intended
case MenuReturn_e::ExitedNoRedraw:
{
//Nothing to do here - just return
break;
}
default://Fallthrough intended. Default to this menu being active
case MenuReturn_e::ExitedRedraw:
{
//Turn off submenu, redraw, then return
parent_menu->active_submenu = nullptr;
parent_menu->initMenu();
break;
}
}//end switch
}
void enterSubmenu(Menu_t *const submenu)
{
Menu_t* current_menu = rootMenu;
while(nullptr != current_menu->active_submenu){
current_menu = current_menu->active_submenu;
}
current_menu->active_submenu = submenu;
submenu->initMenu();
}

29
menu.h Normal file
View File

@ -0,0 +1,29 @@
#pragma once
#include <stdint.h>
#include "button_press_e.h"
#include "point.h"
enum MenuReturn_e : uint8_t {
StillActive,
ExitedRedraw,
ExitedNoRedraw
};
struct Menu_t {
void (*const initMenu)();//Any initial draw routines or state initialization
MenuReturn_e (*const runMenu)(const ButtonPress_e tuner_button,
const ButtonPress_e touch_button,
const Point touch_point,
const int16_t knob);
Menu_t* active_submenu;
};
static const uint8_t MENU_KNOB_COUNTS_PER_ITEM = 10;
void runActiveMenu(const ButtonPress_e tuner_button,
const ButtonPress_e touch_button,
const Point touch_point,
const int16_t knob);
void enterSubmenu(Menu_t *const submenu);

192
menu_main.cpp Normal file
View File

@ -0,0 +1,192 @@
#include "menu_main.h"
#include "menu_main_buttons.h"
#include <avr/pgmspace.h>
#include <Arduino.h>
#include "button.h"
#include "color_theme.h"
#include "menu_utils.h"
#include "morse.h"
#include "nano_gui.h"
#include "scratch_space.h"
#include "settings.h"
#include "tuner.h"//THRESHOLD_USB_LSB
#include "utils.h"
void drawMainMenu(void);
MenuReturn_e runMainMenu(const ButtonPress_e tuner_button,
const ButtonPress_e touch_button,
const Point touch_point,
const int16_t knob);
Menu_t mainMenu = {
drawMainMenu,
runMainMenu,
nullptr
};
Menu_t* const rootMenu = &mainMenu;
bool mainMenuSelecting = false;//Tracks if we're selecting buttons with knob, or adjusting frequency
int16_t mainMenuSelectedItemRaw = 0;//Allow negative only for easier checks on wrap around
void drawMainMenu(void)
{
displayClear(COLOR_BACKGROUND);
Button button;
Button* bp;
for(uint8_t i = 0; i < MAIN_MENU_NUM_BUTTONS; ++i){
memcpy_P(&bp, &(mainMenuButtons[i]), sizeof(bp));
extractAndDrawButton(&button,bp);
}
drawVersion();
drawCallsign();
ltoa(GetActiveVfoFreq(),b,10);
morseText(b);
}
void drawMainMenuIncrement()
{
//State variables
static uint32_t last_freq = 0;
static Vfo_e last_vfo = Vfo_e::VFO_A;
static VfoMode_e last_mode = VfoMode_e::VFO_MODE_LSB;
static bool last_split = false;
static bool last_rit = false;
static TuningMode_e last_tuning = TuningMode_e::TUNE_SSB;
Button button;
if((last_freq != GetActiveVfoFreq())
||(last_vfo != globalSettings.activeVfo)){
extractAndDrawButton(&button,&bVfoA);
extractAndDrawButton(&button,&bVfoB);
updateBandButtons(last_freq);
last_freq = GetActiveVfoFreq();
last_vfo = globalSettings.activeVfo;
//We set this here so that we're always hearing what's displayed
setFrequency(last_freq);
}
if(last_mode != GetActiveVfoMode()){
updateSidebandButtons();
last_mode = GetActiveVfoMode();
}
if(last_split != globalSettings.splitOn){
extractAndDrawButton(&button,&bVfoA);
extractAndDrawButton(&button,&bVfoB);
extractAndDrawButton(&button,&bSpl);
last_split = globalSettings.splitOn;
}
if(last_rit != globalSettings.ritOn){
extractAndDrawButton(&button,&bRit);
last_rit = globalSettings.ritOn;
}
if(last_tuning != globalSettings.tuningMode){
extractAndDrawButton(&button,&bCw);
last_tuning = globalSettings.tuningMode;
}
}
void mainMenuTune(int16_t knob)
{
if(0 == knob){
//Nothing to do - we're already set!
return;
}
const uint32_t current_freq = GetActiveVfoFreq();
const uint32_t new_freq = current_freq + (50 * knob);
SetActiveVfoFreq(new_freq);
autoSelectSidebandChanged(current_freq);
}
MenuReturn_e runMainMenu(const ButtonPress_e tuner_button,
const ButtonPress_e touch_button,
const Point touch_point,
const int16_t knob)
{
//Check tuner_button
if(ButtonPress_e::NotPressed != tuner_button){
switch(tuner_button){
default://Fallthrough intended
case ButtonPress_e::NotPressed:
{
//Nothing to do
break;
}
case ButtonPress_e::ShortPress:
{
if(mainMenuSelecting){
uint8_t menu_index = mainMenuSelectedItemRaw/MENU_KNOB_COUNTS_PER_ITEM;
Button button;
Button* bp;
memcpy_P(&bp,&(mainMenuButtons[menu_index]),sizeof(bp));
memcpy_P(&button,bp,sizeof(button));
endSelector(&button);
button.on_select();
}
else{
initSelector(&mainMenuSelectedItemRaw,
mainMenuButtons,
MAIN_MENU_NUM_BUTTONS,
MorsePlaybackType_e::PlayChar);
}
mainMenuSelecting = !mainMenuSelecting;
//Don't handle touch or knob on this run
return MenuReturn_e::StillActive;//main menu always returns StillActive
break;
}
case ButtonPress_e::LongPress:
{
if(!globalSettings.morseMenuOn){
globalSettings.morseMenuOn = true;//set before playing
morseLetter(2);
}
else{
morseLetter(4);
globalSettings.morseMenuOn = false;//unset after playing
}
SaveSettingsToEeprom();
//Don't handle touch or knob on this run
return MenuReturn_e::StillActive;//main menu always returns StillActive
break;
}
}//switch
}//tuner_button
else if(ButtonPress_e::NotPressed != touch_button){
//We treat long and short presses the same, so no need to have a switch
Button button;
if(findPressedButton(mainMenuButtons,MAIN_MENU_NUM_BUTTONS,&button,touch_point)){
button.on_select();
}
else{
//Touch detected, but not on our buttons, so ignore
}
}//touch_button
else{//Neither button input type found, so handle the knob
if(mainMenuSelecting){
adjustSelector(&mainMenuSelectedItemRaw,
knob,
mainMenuButtons,
MAIN_MENU_NUM_BUTTONS,
MorsePlaybackType_e::PlayChar);
}
else{
mainMenuTune(knob);
}
}
drawMainMenuIncrement();
return MenuReturn_e::StillActive;//main menu always returns StillActive
}

2
menu_main.h Normal file
View File

@ -0,0 +1,2 @@
#include "menu.h"
extern Menu_t* const rootMenu;

637
menu_main_buttons.cpp Normal file
View File

@ -0,0 +1,637 @@
#include "menu_main_buttons.h"
#include <avr/pgmspace.h>
#include <string.h>
#include <WString.h>//F()
#include "bands.h"
#include "button.h"
#include "callsign.h"
#include "color_theme.h"
#include "menu_main.h"
#include "menu_numpad.h"
#include "menu_quicklist.h"
#include "morse.h"
#include "nano_gui.h"
#include "scratch_space.h"
#include "settings.h"
#include "setup.h"
#include "tuner.h"
#include "utils.h"
#include "version.h"
static const unsigned int LAYOUT_VFO_LABEL_X = 0;
static const unsigned int LAYOUT_VFO_LABEL_Y = 10;
static const unsigned int LAYOUT_VFO_LABEL_WIDTH = 159;
static const unsigned int LAYOUT_VFO_LABEL_HEIGHT = 36;
static const unsigned int LAYOUT_VFO_LABEL_PITCH_X = 160;
static const unsigned int LAYOUT_MODE_TEXT_X = 0;
static const unsigned int LAYOUT_MODE_TEXT_Y = LAYOUT_VFO_LABEL_Y + LAYOUT_VFO_LABEL_HEIGHT + 1;
static const unsigned int LAYOUT_MODE_TEXT_WIDTH = 320;
static const unsigned int LAYOUT_MODE_TEXT_HEIGHT = 36;
static const unsigned int LAYOUT_BUTTON_X = 2;
static const unsigned int LAYOUT_BUTTON_Y = LAYOUT_MODE_TEXT_Y + LAYOUT_MODE_TEXT_HEIGHT + 1;
static const unsigned int LAYOUT_BUTTON_WIDTH = 60;
static const unsigned int LAYOUT_BUTTON_HEIGHT = 36;
static const unsigned int LAYOUT_BUTTON_PITCH_X = 64;
static const unsigned int LAYOUT_BUTTON_PITCH_Y = 40;
static const unsigned int LAYOUT_CW_TEXT_X = 0;
static const unsigned int LAYOUT_CW_TEXT_Y = LAYOUT_BUTTON_Y + 3*LAYOUT_BUTTON_PITCH_Y + 1;
static const unsigned int LAYOUT_CW_TEXT_WIDTH = 220;
static const unsigned int LAYOUT_CW_TEXT_HEIGHT = 36;
static const unsigned int LAYOUT_VERSION_TEXT_X = LAYOUT_CW_TEXT_X + LAYOUT_CW_TEXT_WIDTH + 1;
static const unsigned int LAYOUT_VERSION_TEXT_Y = LAYOUT_CW_TEXT_Y;
static const unsigned int LAYOUT_VERSION_TEXT_WIDTH = 320 - LAYOUT_CW_TEXT_WIDTH - 1;
static const unsigned int LAYOUT_VERSION_TEXT_HEIGHT = LAYOUT_CW_TEXT_HEIGHT;
static const unsigned int LAYOUT_TX_X = 280;
static const unsigned int LAYOUT_TX_Y = LAYOUT_MODE_TEXT_Y;
static const unsigned int LAYOUT_TX_WIDTH = 40;
static const unsigned int LAYOUT_TX_HEIGHT = 36;
void drawTx()
{
if(globalSettings.txActive){
strncpy_P(b,(const char*)F("TX"),sizeof(b));
displayText(b,LAYOUT_TX_X,LAYOUT_TX_Y,LAYOUT_TX_WIDTH,LAYOUT_TX_HEIGHT,COLOR_ACTIVE_TEXT,COLOR_ACTIVE_BACKGROUND,COLOR_BACKGROUND);
}
else{
displayFillrect(LAYOUT_TX_X,LAYOUT_TX_Y,LAYOUT_TX_WIDTH,LAYOUT_TX_HEIGHT,COLOR_BACKGROUND);
}
}
void drawVersion()
{
strncpy_P(b,VERSION_STRING,sizeof(b));
displayText(b,LAYOUT_VERSION_TEXT_X,LAYOUT_VERSION_TEXT_Y,LAYOUT_VERSION_TEXT_WIDTH,LAYOUT_VERSION_TEXT_HEIGHT,COLOR_VERSION_TEXT,COLOR_BACKGROUND,COLOR_BACKGROUND);
}
void drawCallsign()
{
strncpy_P(b,CALLSIGN_STRING,sizeof(b));
displayText(b,LAYOUT_CW_TEXT_X,LAYOUT_CW_TEXT_Y,LAYOUT_CW_TEXT_WIDTH,LAYOUT_CW_TEXT_HEIGHT,COLOR_VERSION_TEXT,COLOR_BACKGROUND,COLOR_BACKGROUND);
}
void toVfoA(char* text_out, const uint16_t max_text_size);
ButtonStatus_e bsVfoA();
void osVfoA();
constexpr Button bVfoA PROGMEM = {
LAYOUT_VFO_LABEL_X + 0*LAYOUT_VFO_LABEL_PITCH_X,
LAYOUT_VFO_LABEL_Y,
LAYOUT_VFO_LABEL_WIDTH,
LAYOUT_VFO_LABEL_HEIGHT,
nullptr,
toVfoA,
bsVfoA,
osVfoA,
'A'
};
void toVfoB(char* text_out, const uint16_t max_text_size);
ButtonStatus_e bsVfoB();
void osVfoB();
constexpr Button bVfoB PROGMEM = {
LAYOUT_VFO_LABEL_X + 1*LAYOUT_VFO_LABEL_PITCH_X,
LAYOUT_VFO_LABEL_Y,
LAYOUT_VFO_LABEL_WIDTH,
LAYOUT_VFO_LABEL_HEIGHT,
nullptr,
toVfoB,
bsVfoB,
osVfoB,
'B'
};
constexpr char txtRit [] PROGMEM = "RIT";
ButtonStatus_e bsRit();
void osRit();
constexpr Button bRit PROGMEM = {
LAYOUT_BUTTON_X + 0*LAYOUT_BUTTON_PITCH_X,
LAYOUT_BUTTON_Y + 0*LAYOUT_BUTTON_PITCH_Y,
LAYOUT_BUTTON_WIDTH,
LAYOUT_BUTTON_HEIGHT,
txtRit,
nullptr,
bsRit,
osRit,
'R'
};
constexpr char txtUsb [] PROGMEM = "USB";
ButtonStatus_e bsUsb();
void osUsb();
constexpr Button bUsb PROGMEM = {
LAYOUT_BUTTON_X + 1*LAYOUT_BUTTON_PITCH_X,
LAYOUT_BUTTON_Y + 0*LAYOUT_BUTTON_PITCH_Y,
LAYOUT_BUTTON_WIDTH,
LAYOUT_BUTTON_HEIGHT,
txtUsb,
nullptr,
bsUsb,
osUsb,
'U'
};
constexpr char txtLsb [] PROGMEM = "LSB";
ButtonStatus_e bsLsb();
void osLsb();
constexpr Button bLsb PROGMEM = {
LAYOUT_BUTTON_X + 2*LAYOUT_BUTTON_PITCH_X,
LAYOUT_BUTTON_Y + 0*LAYOUT_BUTTON_PITCH_Y,
LAYOUT_BUTTON_WIDTH,
LAYOUT_BUTTON_HEIGHT,
txtLsb,
nullptr,
bsLsb,
osLsb,
'L'
};
constexpr char txtCw [] PROGMEM = "CW";
ButtonStatus_e bsCw();
void osCw();
constexpr Button bCw PROGMEM = {
LAYOUT_BUTTON_X + 3*LAYOUT_BUTTON_PITCH_X,
LAYOUT_BUTTON_Y + 0*LAYOUT_BUTTON_PITCH_Y,
LAYOUT_BUTTON_WIDTH,
LAYOUT_BUTTON_HEIGHT,
txtCw,
nullptr,
bsCw,
osCw,
'C'
};
constexpr char txtSpl [] PROGMEM = "SPL";
ButtonStatus_e bsSpl();
void osSpl();
constexpr Button bSpl PROGMEM = {
LAYOUT_BUTTON_X + 4*LAYOUT_BUTTON_PITCH_X,
LAYOUT_BUTTON_Y + 0*LAYOUT_BUTTON_PITCH_Y,
LAYOUT_BUTTON_WIDTH,
LAYOUT_BUTTON_HEIGHT,
txtSpl,
nullptr,
bsSpl,
osSpl,
'S'
};
constexpr char txt80 [] PROGMEM = "80";
ButtonStatus_e bs80();
void os80();
constexpr Button b80 PROGMEM = {
LAYOUT_BUTTON_X + 0*LAYOUT_BUTTON_PITCH_X,
LAYOUT_BUTTON_Y + 1*LAYOUT_BUTTON_PITCH_Y,
LAYOUT_BUTTON_WIDTH,
LAYOUT_BUTTON_HEIGHT,
txt80,
nullptr,
bs80,
os80,
'8'
};
constexpr char txt40 [] PROGMEM = "40";
ButtonStatus_e bs40();
void os40();
constexpr Button b40 PROGMEM = {
LAYOUT_BUTTON_X + 1*LAYOUT_BUTTON_PITCH_X,
LAYOUT_BUTTON_Y + 1*LAYOUT_BUTTON_PITCH_Y,
LAYOUT_BUTTON_WIDTH,
LAYOUT_BUTTON_HEIGHT,
txt40,
nullptr,
bs40,
os40,
'4'
};
constexpr char txt30 [] PROGMEM = "30";
ButtonStatus_e bs30();
void os30();
constexpr Button b30 PROGMEM = {
LAYOUT_BUTTON_X + 2*LAYOUT_BUTTON_PITCH_X,
LAYOUT_BUTTON_Y + 1*LAYOUT_BUTTON_PITCH_Y,
LAYOUT_BUTTON_WIDTH,
LAYOUT_BUTTON_HEIGHT,
txt30,
nullptr,
bs30,
os30,
'3'
};
constexpr char txt20 [] PROGMEM = "20";
ButtonStatus_e bs20();
void os20();
constexpr Button b20 PROGMEM = {
LAYOUT_BUTTON_X + 3*LAYOUT_BUTTON_PITCH_X,
LAYOUT_BUTTON_Y + 1*LAYOUT_BUTTON_PITCH_Y,
LAYOUT_BUTTON_WIDTH,
LAYOUT_BUTTON_HEIGHT,
txt20,
nullptr,
bs20,
os20,
'2'
};
constexpr char txt17 [] PROGMEM = "17";
ButtonStatus_e bs17();
void os17();
constexpr Button b17 PROGMEM = {
LAYOUT_BUTTON_X + 4*LAYOUT_BUTTON_PITCH_X,
LAYOUT_BUTTON_Y + 1*LAYOUT_BUTTON_PITCH_Y,
LAYOUT_BUTTON_WIDTH,
LAYOUT_BUTTON_HEIGHT,
txt17,
nullptr,
bs17,
os17,
'7'
};
constexpr char txt15 [] PROGMEM = "15";
ButtonStatus_e bs15();
void os15();
constexpr Button b15 PROGMEM = {
LAYOUT_BUTTON_X + 0*LAYOUT_BUTTON_PITCH_X,
LAYOUT_BUTTON_Y + 2*LAYOUT_BUTTON_PITCH_Y,
LAYOUT_BUTTON_WIDTH,
LAYOUT_BUTTON_HEIGHT,
txt15,
nullptr,
bs15,
os15,
'5'
};
constexpr char txt10 [] PROGMEM = "10";
ButtonStatus_e bs10();
void os10();
constexpr Button b10 PROGMEM = {
LAYOUT_BUTTON_X + 1*LAYOUT_BUTTON_PITCH_X,
LAYOUT_BUTTON_Y + 2*LAYOUT_BUTTON_PITCH_Y,
LAYOUT_BUTTON_WIDTH,
LAYOUT_BUTTON_HEIGHT,
txt10,
nullptr,
bs10,
os10,
'1'
};
constexpr char txtQuickList [] PROGMEM = "\x83";//star icon
ButtonStatus_e bsIgnore();
void osQuickList();
constexpr Button bQuickList PROGMEM = {
LAYOUT_BUTTON_X + 2*LAYOUT_BUTTON_PITCH_X,
LAYOUT_BUTTON_Y + 2*LAYOUT_BUTTON_PITCH_Y,
LAYOUT_BUTTON_WIDTH,
LAYOUT_BUTTON_HEIGHT,
txtQuickList,
nullptr,
bsIgnore,
osQuickList,
'Q'
};
constexpr char txtMenu [] PROGMEM = "\x7F";//gear icon
ButtonStatus_e bsIgnore();
void osMenu();
constexpr Button bMenu PROGMEM = {
LAYOUT_BUTTON_X + 3*LAYOUT_BUTTON_PITCH_X,
LAYOUT_BUTTON_Y + 2*LAYOUT_BUTTON_PITCH_Y,
LAYOUT_BUTTON_WIDTH,
LAYOUT_BUTTON_HEIGHT,
txtMenu,
nullptr,
bsIgnore,
osMenu,
'M'
};
constexpr char txtNumpad [] PROGMEM = "\x82";//numpad icon
ButtonStatus_e bsIgnore();
void osNumpad();
constexpr Button bNumpad PROGMEM = {
LAYOUT_BUTTON_X + 4*LAYOUT_BUTTON_PITCH_X,
LAYOUT_BUTTON_Y + 2*LAYOUT_BUTTON_PITCH_Y,
LAYOUT_BUTTON_WIDTH,
LAYOUT_BUTTON_HEIGHT,
txtNumpad,
nullptr,
bsIgnore,
osNumpad,
'F'
};
const Button* const mainMenuButtons [] PROGMEM = {
&bVfoA, &bVfoB,
&bRit, &bUsb, &bLsb, &bCw, &bSpl,
&b80, &b40, &b30, &b20, &b17,
&b15, &b10, &bQuickList, &bMenu, &bNumpad
};
const uint8_t MAIN_MENU_NUM_BUTTONS = sizeof(mainMenuButtons) / sizeof(mainMenuButtons[0]);
void updateBandButtons(const uint32_t old_freq)
{
const Button* band_buttons[] = {&b80,&b40,&b30,&b20,&b17,&b15,&b10};
const uint8_t bands [] = { 80, 40, 30, 20, 17, 15, 10};
const uint32_t curr_freq = GetActiveVfoFreq();
Button button;
for(uint8_t i = 0; i < sizeof(bands)/sizeof(bands[0]); ++i){
if(isFreqInBand(old_freq,bands[i]) != isFreqInBand(curr_freq,bands[i])){
extractAndDrawButton(&button,band_buttons[i]);
morseBool(ButtonStatus_e::Active == button.status());
}
}
}
void toVfo(char* text_out, const uint16_t max_text_size, const Vfo_e vfo)
{
if(max_text_size < 2){
return;//Can't do much with that space
}
if(max_text_size < (3+10+1)){
//Give an indicator that's debuggable
text_out[0] = 'X';
text_out[1] = '\0';
return;
}
//Normal operation
if (globalSettings.splitOn){
if(vfo == globalSettings.activeVfo){
text_out[0] = 'R';
}
else{
text_out[0] = 'T';
}
}
else{
if(VFO_A == vfo){
text_out[0] = 'A';
}
else if(VFO_B == vfo){
text_out[0] = 'B';
}
else{
text_out[0] = '?';
}
}
text_out[1] = ':';
text_out[2] = ' ';
if(VFO_A == vfo){
formatFreq(globalSettings.vfoA.frequency, text_out+3, max_text_size-3, 10);
}
else if(VFO_B == vfo){
formatFreq(globalSettings.vfoB.frequency, text_out+3, max_text_size-3, 10);
}
else{
text_out[3] = '?';
text_out[4] = '\0';
}
}
ButtonStatus_e bsVfo(const Vfo_e vfo){
return (vfo == globalSettings.activeVfo) ? ButtonStatus_e::Active : ButtonStatus_e::Inactive;
}
void osVfo(const Vfo_e vfo){
const uint32_t old_freq = GetActiveVfoFreq();
if(globalSettings.ritOn){
osRit();//Turn off RIT
}
globalSettings.activeVfo = vfo;
SaveSettingsToEeprom();
ltoa(GetActiveVfoFreq(),b,10);
morseText(b);
Button button;
extractAndDrawButton(&button,&bVfoA);
extractAndDrawButton(&button,&bVfoB);
updateBandButtons(old_freq);
updateSidebandButtons();
}
void toVfoA(char* text_out, const uint16_t max_text_size){
toVfo(text_out,max_text_size,Vfo_e::VFO_A);
}
ButtonStatus_e bsVfoA(){
return bsVfo(Vfo_e::VFO_A);
}
void osVfoA(){
osVfo(Vfo_e::VFO_A);
}
void toVfoB(char* text_out, const uint16_t max_text_size){
toVfo(text_out,max_text_size,Vfo_e::VFO_B);
}
ButtonStatus_e bsVfoB(){
return bsVfo(Vfo_e::VFO_B);
}
void osVfoB(){
osVfo(Vfo_e::VFO_B);
}
ButtonStatus_e bsRit(){
return globalSettings.ritOn ? ButtonStatus_e::Active : ButtonStatus_e::Inactive;
}
void osRit(){
Button button;
if(!globalSettings.ritOn){
globalSettings.ritOn = true;
globalSettings.ritFrequency = GetActiveVfoFreq();
strncpy_P(b,(const char*)F("TX: "),sizeof(b));
formatFreq(globalSettings.ritFrequency, b + strlen(b), sizeof(b)-strlen(b));
if (VFO_A == globalSettings.activeVfo){
displayText(b, LAYOUT_VFO_LABEL_X + 0*LAYOUT_VFO_LABEL_PITCH_X, LAYOUT_MODE_TEXT_Y, LAYOUT_VFO_LABEL_WIDTH, LAYOUT_MODE_TEXT_HEIGHT, COLOR_TEXT, COLOR_BACKGROUND, COLOR_BACKGROUND, TextJustification_e::Left);
}
else{
displayText(b, LAYOUT_VFO_LABEL_X + 1*LAYOUT_VFO_LABEL_PITCH_X, LAYOUT_MODE_TEXT_Y, LAYOUT_VFO_LABEL_WIDTH, LAYOUT_MODE_TEXT_HEIGHT, COLOR_TEXT, COLOR_BACKGROUND, COLOR_BACKGROUND, TextJustification_e::Left);
}
}
else{
globalSettings.ritOn = false;
setFrequency(globalSettings.ritFrequency);
displayFillrect(LAYOUT_MODE_TEXT_X,LAYOUT_MODE_TEXT_Y,LAYOUT_MODE_TEXT_WIDTH,LAYOUT_MODE_TEXT_HEIGHT, COLOR_BACKGROUND);
if(Vfo_e::VFO_A == globalSettings.activeVfo){
extractAndDrawButton(&button,&bVfoA);
}
else{
extractAndDrawButton(&button,&bVfoB);
}
}
extractAndDrawButton(&button,&bRit);
}
void osSidebandMode(VfoMode_e mode){
SetActiveVfoMode(mode);
setFrequency(GetActiveVfoFreq());
SaveSettingsToEeprom();
Button button;
extractAndDrawButton(&button,&bUsb);
extractAndDrawButton(&button,&bLsb);
}
void updateSidebandButtons()
{
osSidebandMode(GetActiveVfoMode());
}
ButtonStatus_e bsUsb(){
return (VfoMode_e::VFO_MODE_USB == GetActiveVfoMode()) ? ButtonStatus_e::Active : ButtonStatus_e::Inactive;
}
void osUsb(){
osSidebandMode(VfoMode_e::VFO_MODE_USB);
}
ButtonStatus_e bsLsb(){
return (VfoMode_e::VFO_MODE_LSB == GetActiveVfoMode()) ? ButtonStatus_e::Active : ButtonStatus_e::Inactive;
}
void osLsb(){
osSidebandMode(VfoMode_e::VFO_MODE_LSB);
}
ButtonStatus_e bsCw(){
return (TuningMode_e::TUNE_CW == globalSettings.tuningMode) ? ButtonStatus_e::Active : ButtonStatus_e::Inactive;
}
void osCw(){
if(TuningMode_e::TUNE_CW != globalSettings.tuningMode){
globalSettings.tuningMode = TuningMode_e::TUNE_CW;
}
else{
globalSettings.tuningMode = TuningMode_e::TUNE_SSB;
}
setFrequency(GetActiveVfoFreq());
Button button;
extractAndDrawButton(&button,&bCw);
}
ButtonStatus_e bsSpl(){
return globalSettings.splitOn ? ButtonStatus_e::Active : ButtonStatus_e::Inactive;
}
void osSpl(){
globalSettings.splitOn = !globalSettings.splitOn;
Button button;
extractAndDrawButton(&button,&bSpl);
extractAndDrawButton(&button,&bVfoA);
extractAndDrawButton(&button,&bVfoB);
}
ButtonStatus_e bsBand(const uint8_t band){
return isFreqInBand(GetActiveVfoFreq(),band) ? ButtonStatus_e::Active : ButtonStatus_e::Inactive;
}
void osBand(const uint8_t band){
const uint32_t old_freq = GetActiveVfoFreq();
SetActiveVfoFreq(getFreqInBand(old_freq,band));
if(autoSelectSidebandChanged(old_freq)){
updateSidebandButtons();
}
Button button;
if(Vfo_e::VFO_A == globalSettings.activeVfo){
extractAndDrawButton(&button,&bVfoA);
}
else if(Vfo_e::VFO_B == globalSettings.activeVfo){
extractAndDrawButton(&button,&bVfoB);
}
updateBandButtons(old_freq);
}
ButtonStatus_e bs80(){
return bsBand(80);
}
void os80(){
osBand(80);
}
ButtonStatus_e bs40(){
return bsBand(40);
}
void os40(){
osBand(40);
}
ButtonStatus_e bs30(){
return bsBand(30);
}
void os30(){
osBand(30);
}
ButtonStatus_e bs20(){
return bsBand(20);
}
void os20(){
osBand(20);
}
ButtonStatus_e bs17(){
return bsBand(17);
}
void os17(){
osBand(17);
}
ButtonStatus_e bs15(){
return bsBand(15);
}
void os15(){
osBand(15);
}
ButtonStatus_e bs10(){
return bsBand(10);
}
void os10(){
osBand(10);
}
ButtonStatus_e bsIgnore(){
return ButtonStatus_e::Stateless;
}
void osQuickList(){
enterSubmenu(quickListMenu);
}
void osMenu(){
enterSubmenu(setupMenu);
}
void osNumpad(){
enterSubmenu(numpadMenu);
}

19
menu_main_buttons.h Normal file
View File

@ -0,0 +1,19 @@
#pragma once
#include <stdint.h>
#include "button.h"
extern const Button* const mainMenuButtons[];
extern const uint8_t MAIN_MENU_NUM_BUTTONS;
extern const Button bVfoA;
extern const Button bVfoB;
extern const Button bRit;
extern const Button bCw;
extern const Button bSpl;
void updateBandButtons(const uint32_t old_freq);
void updateSidebandButtons();
void drawTx();
void drawVersion();
void drawCallsign();

52
menu_np_ql_shared.cpp Normal file
View File

@ -0,0 +1,52 @@
#include "menu_np_ql_shared.h"
#include <avr/pgmspace.h>
#include "button.h"
#include "menu_utils.h"
MenuReturn_e runNpQlShared(const ButtonPress_e tuner_button,
const ButtonPress_e touch_button,
const Point touch_point,
const int16_t knob,
int16_t *const menuSelectedItemRaw,
const Button *const *const menu_buttons,
const uint8_t menu_num_buttons,
ButtonPress_e *const selection_mode)
{
if(ButtonPress_e::NotPressed != tuner_button){
uint8_t menu_index = *menuSelectedItemRaw/MENU_KNOB_COUNTS_PER_ITEM;
Button button;
Button* bp;
memcpy_P(&bp,&(menu_buttons[menu_index]),sizeof(bp));
memcpy_P(&button,bp,sizeof(button));
*selection_mode = tuner_button;
button.on_select();
}//tuner_button
else if(ButtonPress_e::NotPressed != touch_button){
Button button;
if(findPressedButton(menu_buttons,menu_num_buttons,&button,touch_point)){
*selection_mode = touch_button;
button.on_select();
}
else{
//Touch detected, but not on our buttons, so ignore
}
}//touch_button
else{//Neither button input type found, so handle the knob
adjustSelector(menuSelectedItemRaw,
knob,
menu_buttons,
menu_num_buttons,
MorsePlaybackType_e::PlayChar);
}
if(ButtonPress_e::NotPressed == *selection_mode){
return MenuReturn_e::ExitedRedraw;
}
else{
return MenuReturn_e::StillActive;
}
}

14
menu_np_ql_shared.h Normal file
View File

@ -0,0 +1,14 @@
#pragma once
#include "button.h"
#include "menu.h"
MenuReturn_e runNpQlShared(const ButtonPress_e tuner_button,
const ButtonPress_e touch_button,
const Point touch_point,
const int16_t knob,
int16_t *const menuSelectedItemRaw,
const Button *const *const menu_buttons,
const uint8_t menu_num_buttons,
ButtonPress_e *const selection_mode);

61
menu_numpad.cpp Normal file
View File

@ -0,0 +1,61 @@
#include "menu_numpad.h"
#include "menu_numpad_buttons.h"
#include <avr/pgmspace.h>
#include "color_theme.h"
#include "menu_np_ql_shared.h"
#include "menu_utils.h"
#include "nano_gui.h"
void initNumpad(void);
MenuReturn_e runNumpad(const ButtonPress_e tuner_button,
const ButtonPress_e touch_button,
const Point touch_point,
const int16_t knob);
Menu_t numpad_menu = {
initNumpad,
runNumpad,
nullptr
};
Menu_t *const numpadMenu = &numpad_menu;
int16_t numpadMenuSelectedItemRaw = 0;//Allow negative only for easier checks on wrap around
void drawNumpad(void)
{
displayFillrect(0,47,320,200,COLOR_BACKGROUND);
Button button;
Button* bp;
for(uint8_t i = 0; i < NUMPAD_MENU_NUM_BUTTONS; ++i){
memcpy_P(&bp, &(numpadMenuButtons[i]), sizeof(bp));
extractAndDrawButton(&button,bp);
}
}
void initNumpad(void)
{
numpadMenuFrequency = 0;
numpadSelectionMode = ButtonPress_e::LongPress;//Anything except NotPressed
drawNumpad();
initSelector(&numpadMenuSelectedItemRaw,
numpadMenuButtons,
NUMPAD_MENU_NUM_BUTTONS,
MorsePlaybackType_e::PlayChar);
}
MenuReturn_e runNumpad(const ButtonPress_e tuner_button,
const ButtonPress_e touch_button,
const Point touch_point,
const int16_t knob)
{
return runNpQlShared(tuner_button,
touch_button,
touch_point,
knob,
&numpadMenuSelectedItemRaw,
numpadMenuButtons,
NUMPAD_MENU_NUM_BUTTONS,
&numpadSelectionMode);
}

2
menu_numpad.h Normal file
View File

@ -0,0 +1,2 @@
#include "menu.h"
extern Menu_t* const numpadMenu;

161
menu_numpad_buttons.cpp Normal file
View File

@ -0,0 +1,161 @@
#include "menu_numpad_buttons.h"
#include <avr/pgmspace.h>
#include <WString.h>//F()
#include "color_theme.h"
#include "nano_gui.h"
#include "scratch_space.h"
#include "settings.h"
#include "tuner.h"//setFrequency
#include "utils.h"
static const unsigned int LAYOUT_MODE_TEXT_X = 60;
static const unsigned int LAYOUT_MODE_TEXT_Y = 47;
static const unsigned int LAYOUT_MODE_TEXT_WIDTH = 140;
static const unsigned int LAYOUT_MODE_TEXT_HEIGHT = 36;
static const unsigned int LAYOUT_BUTTON_X = 2;
static const unsigned int LAYOUT_BUTTON_Y = LAYOUT_MODE_TEXT_Y + LAYOUT_MODE_TEXT_HEIGHT + 1;
static const unsigned int LAYOUT_BUTTON_WIDTH = 60;
static const unsigned int LAYOUT_BUTTON_HEIGHT = 36;
static const unsigned int LAYOUT_BUTTON_PITCH_X = 64;
static const unsigned int LAYOUT_BUTTON_PITCH_Y = 40;
uint32_t numpadMenuFrequency;
ButtonPress_e numpadSelectionMode;
#define D_STRINGIFY(x) #x
#define D_STRING(x) D_STRINGIFY(x)
#define NUMBER_BUTTON_GENERATE(number,x,y) \
constexpr char txt##number [] PROGMEM = "" D_STRING(number);\
ButtonStatus_e bs##number();\
void os##number();\
constexpr Button b##number PROGMEM = {\
LAYOUT_BUTTON_X + x*LAYOUT_BUTTON_PITCH_X,\
LAYOUT_BUTTON_Y + y*LAYOUT_BUTTON_PITCH_Y,\
LAYOUT_BUTTON_WIDTH,\
LAYOUT_BUTTON_HEIGHT,\
txt##number,\
nullptr,\
bsNumpad,\
os##number,\
'0'+number\
}
ButtonStatus_e bsNumpad(void){
return ButtonStatus_e::Stateless;
}
// 1 2 3 Ok
// 4 5 6 0 <-
// 7 8 9 Can
NUMBER_BUTTON_GENERATE(1,0,0);
NUMBER_BUTTON_GENERATE(2,1,0);
NUMBER_BUTTON_GENERATE(3,2,0);
NUMBER_BUTTON_GENERATE(4,0,1);
NUMBER_BUTTON_GENERATE(5,1,1);
NUMBER_BUTTON_GENERATE(6,2,1);
NUMBER_BUTTON_GENERATE(7,0,2);
NUMBER_BUTTON_GENERATE(8,1,2);
NUMBER_BUTTON_GENERATE(9,2,2);
NUMBER_BUTTON_GENERATE(0,3,1);
constexpr char txtOk [] PROGMEM = "OK";
void osOk();
constexpr Button bOk PROGMEM = {
LAYOUT_BUTTON_X + 4*LAYOUT_BUTTON_PITCH_X,
LAYOUT_BUTTON_Y + 0*LAYOUT_BUTTON_PITCH_Y,
LAYOUT_BUTTON_WIDTH,
LAYOUT_BUTTON_HEIGHT,
txtOk,
nullptr,
bsNumpad,
osOk,
'K'
};
constexpr char txtBackspace [] PROGMEM = "<-";
void osBackspace();
constexpr Button bBackspace PROGMEM = {
LAYOUT_BUTTON_X + 4*LAYOUT_BUTTON_PITCH_X,
LAYOUT_BUTTON_Y + 1*LAYOUT_BUTTON_PITCH_Y,
LAYOUT_BUTTON_WIDTH,
LAYOUT_BUTTON_HEIGHT,
txtBackspace,
nullptr,
bsNumpad,
osBackspace,
'B'
};
constexpr char txtCancel [] PROGMEM = "Can";
void osCancel();
constexpr Button bCancel PROGMEM = {
LAYOUT_BUTTON_X + 4*LAYOUT_BUTTON_PITCH_X,
LAYOUT_BUTTON_Y + 2*LAYOUT_BUTTON_PITCH_Y,
LAYOUT_BUTTON_WIDTH,
LAYOUT_BUTTON_HEIGHT,
txtCancel,
nullptr,
bsNumpad,
osCancel,
'C'
};
//Declare in menu select order, not graphical order
const Button* const numpadMenuButtons [] PROGMEM = {
&b1, &b2, &b3, &b4, &b5, &b6, &b7, &b8, &b9, &b0,
&bOk, &bBackspace, &bCancel
};
const uint8_t NUMPAD_MENU_NUM_BUTTONS = sizeof(numpadMenuButtons)/sizeof(numpadMenuButtons[0]);
void updateCurrentEnteredFrequency(void)
{
formatFreq(numpadMenuFrequency,b,sizeof(b),0);
strncat_P(b,(const char*)F(" Hz"),sizeof(b)-strlen(b));
displayText(b,LAYOUT_MODE_TEXT_X,LAYOUT_MODE_TEXT_Y,LAYOUT_MODE_TEXT_WIDTH,LAYOUT_MODE_TEXT_HEIGHT,COLOR_TEXT,COLOR_BACKGROUND,COLOR_BACKGROUND,TextJustification_e::Right);
}
void osNumpad(uint8_t new_digit)
{
numpadMenuFrequency *= 10;
numpadMenuFrequency += new_digit;
updateCurrentEnteredFrequency();
}
void osBackspace(void)
{
numpadMenuFrequency /= 10;
updateCurrentEnteredFrequency();
}
#define NUMBER_ONSELECT_GENERATE(number) \
void os##number(void){\
osNumpad(number);\
}
NUMBER_ONSELECT_GENERATE(0);
NUMBER_ONSELECT_GENERATE(1);
NUMBER_ONSELECT_GENERATE(2);
NUMBER_ONSELECT_GENERATE(3);
NUMBER_ONSELECT_GENERATE(4);
NUMBER_ONSELECT_GENERATE(5);
NUMBER_ONSELECT_GENERATE(6);
NUMBER_ONSELECT_GENERATE(7);
NUMBER_ONSELECT_GENERATE(8);
NUMBER_ONSELECT_GENERATE(9);
void osOk(void)
{
SetActiveVfoFreq(numpadMenuFrequency);
SaveSettingsToEeprom();
setFrequency(numpadMenuFrequency);
numpadSelectionMode = ButtonPress_e::NotPressed;
}
void osCancel(void)
{
numpadSelectionMode = ButtonPress_e::NotPressed;
}

13
menu_numpad_buttons.h Normal file
View File

@ -0,0 +1,13 @@
#pragma once
#include <stdint.h>
#include "button.h"
#include "button_press_e.h"
extern const Button* const numpadMenuButtons[];
extern const uint8_t NUMPAD_MENU_NUM_BUTTONS;
extern const uint32_t NUMPAD_MENU_EXIT_FREQ;
extern uint32_t numpadMenuFrequency;
extern ButtonPress_e numpadSelectionMode;//NotPressed means exit menu. Other press types are consumed by selectors

64
menu_quicklist.cpp Normal file
View File

@ -0,0 +1,64 @@
#include "menu_quicklist.h"
#include "menu_quicklist_buttons.h"
#include <avr/pgmspace.h>
#include <WString.h>//F()
#include "color_theme.h"
#include "menu_np_ql_shared.h"
#include "menu_utils.h"
#include "nano_gui.h"
#include "scratch_space.h"
void initQuickList(void);
MenuReturn_e runQuickList(const ButtonPress_e tuner_button,
const ButtonPress_e touch_button,
const Point touch_point,
const int16_t knob);
Menu_t quickList_menu = {
initQuickList,
runQuickList,
nullptr
};
Menu_t *const quickListMenu = &quickList_menu;
int16_t quickListMenuSelectedItemRaw = 0;//Allow negative only for easier checks on wrap around
void drawQuickList(void)
{
displayFillrect(0,47,320,200,COLOR_BACKGROUND);
Button button;
Button* bp;
for(uint8_t i = 0; i < QUICKLIST_MENU_NUM_BUTTONS; ++i){
memcpy_P(&bp, &(quickListMenuButtons[i]), sizeof(bp));
extractAndDrawButton(&button,bp);
}
strncpy_P(b,(const char*)F("Short press = load\nLong press = save"),sizeof(b));
displayText(b,10,47,170,200,COLOR_TEXT,COLOR_BACKGROUND,COLOR_BACKGROUND);
}
void initQuickList(void)
{
quickListSelectionMode = ButtonPress_e::ShortPress;//Anything except NotPressed
drawQuickList();
initSelector(&quickListMenuSelectedItemRaw,
quickListMenuButtons,
QUICKLIST_MENU_NUM_BUTTONS,
MorsePlaybackType_e::PlayChar);
}
MenuReturn_e runQuickList(const ButtonPress_e tuner_button,
const ButtonPress_e touch_button,
const Point touch_point,
const int16_t knob)
{
return runNpQlShared(tuner_button,
touch_button,
touch_point,
knob,
&quickListMenuSelectedItemRaw,
quickListMenuButtons,
QUICKLIST_MENU_NUM_BUTTONS,
&quickListSelectionMode);
}

2
menu_quicklist.h Normal file
View File

@ -0,0 +1,2 @@
#include "menu.h"
extern Menu_t* const quickListMenu;

137
menu_quicklist_buttons.cpp Normal file
View File

@ -0,0 +1,137 @@
#include "menu_quicklist_buttons.h"
#include <avr/pgmspace.h>
#include <WString.h>//F()
#include "color_theme.h"
#include "nano_gui.h"
#include "scratch_space.h"
#include "settings.h"
#include "utils.h"
static const unsigned int LAYOUT_BUTTON_X = 180;
static const unsigned int LAYOUT_BUTTON_Y = 47;
static const unsigned int LAYOUT_BUTTON_WIDTH = 140;
static const unsigned int LAYOUT_BUTTON_HEIGHT = 36;
static const unsigned int LAYOUT_BUTTON_PITCH_X = LAYOUT_BUTTON_WIDTH + 1;
static const unsigned int LAYOUT_BUTTON_PITCH_Y = 40;
ButtonPress_e quickListSelectionMode;
#define D_STRINGIFY(x) #x
#define D_STRING(x) D_STRINGIFY(x)
#define QUICKLIST_BUTTON_GENERATE(number) \
void toQL##number(char* text_out, const uint16_t max_text_size);\
void osQL##number();\
constexpr Button bQL##number PROGMEM = {\
LAYOUT_BUTTON_X,\
LAYOUT_BUTTON_Y + number*LAYOUT_BUTTON_PITCH_Y,\
LAYOUT_BUTTON_WIDTH,\
LAYOUT_BUTTON_HEIGHT,\
nullptr,\
toQL##number,\
bsQuickList,\
osQL##number,\
'0'+number\
}
ButtonStatus_e bsQuickList(void){
return ButtonStatus_e::Stateless;
}
//Unfortunately there's no easy way to auto-generate NUM_QUICKLIST_SETTINGS copies of this
QUICKLIST_BUTTON_GENERATE(0);
QUICKLIST_BUTTON_GENERATE(1);
QUICKLIST_BUTTON_GENERATE(2);
QUICKLIST_BUTTON_GENERATE(3);
constexpr char txtQLCancel [] PROGMEM = "Can";
void osQLCancel();
constexpr Button bQLCancel PROGMEM = {
LAYOUT_BUTTON_X,
LAYOUT_BUTTON_Y + 4*LAYOUT_BUTTON_PITCH_Y,
LAYOUT_BUTTON_WIDTH,
LAYOUT_BUTTON_HEIGHT,
txtQLCancel,
nullptr,
bsQuickList,
osQLCancel,
'C'
};
//Declare in menu select order, not graphical order
const Button* const quickListMenuButtons [] PROGMEM = {
&bQL0, &bQL1, &bQL2, &bQL3, &bQLCancel
};
const uint8_t QUICKLIST_MENU_NUM_BUTTONS = sizeof(quickListMenuButtons)/sizeof(quickListMenuButtons[0]);
void osQuickListRecall(uint8_t index)
{
SetActiveVfoFreq(globalSettings.quickList[index].frequency);
SetActiveVfoMode(globalSettings.quickList[index].mode);
}
void osQuickListSave(uint8_t index)
{
globalSettings.quickList[index].frequency = GetActiveVfoFreq();
globalSettings.quickList[index].mode = GetActiveVfoMode();
SaveSettingsToEeprom();
}
void osQuickList(uint8_t index)
{
if(ButtonPress_e::ShortPress == quickListSelectionMode){
osQuickListRecall(index);
}
else if(ButtonPress_e::LongPress == quickListSelectionMode){
osQuickListSave(index);
}
quickListSelectionMode = ButtonPress_e::NotPressed;//Selection was made. Exit.
}
#define QUICKLIST_ONSELECT_GENERATE(number) \
void osQL##number(void){\
osQuickList(number);\
}
//Unfortunately there's no easy way to auto-generate NUM_QUICKLIST_SETTINGS copies of this
QUICKLIST_ONSELECT_GENERATE(0);
QUICKLIST_ONSELECT_GENERATE(1);
QUICKLIST_ONSELECT_GENERATE(2);
QUICKLIST_ONSELECT_GENERATE(3);
#define QUICKLIST_TEXTOVERRIDE_GENERATE(number) \
void toQL##number(char* text_out, const uint16_t max_text_size){\
toQuickList(number, text_out, max_text_size);\
}
void toQuickList(uint8_t index, char* text_out, const uint16_t max_text_size)
{
if(max_text_size < 2){
return;//Can't do much with that space
}
if(max_text_size < (3+10+1)){
//Give an indicator that's debuggable
text_out[0] = 'X';
text_out[1] = '\0';
return;
}
//Normal operation
text_out[0] = '0'+index;//Assume a 1-digit number for now
text_out[1] = ':';
text_out[2] = ' ';
formatFreq(globalSettings.quickList[index].frequency, text_out+3, max_text_size-3, 10);
}
//Unfortunately there's no easy way to auto-generate NUM_QUICKLIST_SETTINGS copies of this
QUICKLIST_TEXTOVERRIDE_GENERATE(0);
QUICKLIST_TEXTOVERRIDE_GENERATE(1);
QUICKLIST_TEXTOVERRIDE_GENERATE(2);
QUICKLIST_TEXTOVERRIDE_GENERATE(3);
void osQLCancel()
{
quickListSelectionMode = ButtonPress_e::NotPressed;
}

11
menu_quicklist_buttons.h Normal file
View File

@ -0,0 +1,11 @@
#pragma once
#include <stdint.h>
#include "button.h"
#include "button_press_e.h"
extern const Button* const quickListMenuButtons[];
extern const uint8_t QUICKLIST_MENU_NUM_BUTTONS;
extern ButtonPress_e quickListSelectionMode;//NotPressed means exit menu. Other press types are consumed by selectors

103
menu_utils.cpp Normal file
View File

@ -0,0 +1,103 @@
#include "menu_utils.h"
#include <avr/pgmspace.h>
#include "button.h"
#include "color_theme.h"
#include "morse.h"
#include "nano_gui.h"
#include "utils.h"
bool findPressedButton(const Button* const* buttons,
const uint8_t num_buttons,
Button *const button_out,
const Point touch_point)
{
Button* bp;
for(uint16_t i = 0; i < num_buttons; ++i){
memcpy_P(&bp,&(buttons[i]),sizeof(bp));
memcpy_P(button_out,bp,sizeof(*button_out));
if((button_out->x <= touch_point.x)
&&(touch_point.x <= button_out->x + button_out->w)
&&(button_out->y <= touch_point.y)
&&(touch_point.y <= button_out->y + button_out->h)){
return true;
}
}
return false;
}
void movePuck(const Button *const b_old,
const Button *const b_new)
{
if(nullptr != b_old){
displayRect(b_old->x,b_old->y,b_old->w,b_old->h,COLOR_INACTIVE_BORDER);
}
if(nullptr != b_new){
displayRect(b_new->x,b_new->y,b_new->w,b_new->h,COLOR_ACTIVE_BORDER);
}
}
void playButtonMorse(const Button *const button,
const MorsePlaybackType_e play_type)
{
if(MorsePlaybackType_e::PlayText == play_type){
morseText(button->text);
}
else{
morseLetter(button->morse);
}
const ButtonStatus_e bs = button->status();
if(ButtonStatus_e::Inactive == bs){
morseBool(false);
}
else if(ButtonStatus_e::Active == bs){
morseBool(true);
}
}
void initSelector(int16_t *const raw_select_val_in_out,
const Button* const* buttons,
const uint8_t num_buttons,
const MorsePlaybackType_e play_type)
{
*raw_select_val_in_out = 0;
if(0 < num_buttons){
Button button;
Button* bp;
memcpy_P(&bp,&(buttons[0]),sizeof(bp));
memcpy_P(&button,bp,sizeof(button));
movePuck(nullptr,&button);
playButtonMorse(&button,play_type);
}
}
void adjustSelector(int16_t *const raw_select_val_in_out,
const int16_t knob,
const Button* const* buttons,
const uint8_t num_buttons,
const MorsePlaybackType_e play_type)
{
const uint8_t prev_select = (*raw_select_val_in_out)/MENU_KNOB_COUNTS_PER_ITEM;
*raw_select_val_in_out = LIMIT((*raw_select_val_in_out)+knob,0,num_buttons*MENU_KNOB_COUNTS_PER_ITEM - 1);
const uint8_t new_select = (*raw_select_val_in_out)/MENU_KNOB_COUNTS_PER_ITEM;
if(prev_select != new_select){
Button prev_button;
Button* bp;
memcpy_P(&bp,&(buttons[prev_select]),sizeof(bp));
memcpy_P(&prev_button,bp,sizeof(prev_button));
Button new_button;
memcpy_P(&bp,&(buttons[new_select]),sizeof(bp));
memcpy_P(&new_button,bp,sizeof(new_button));
movePuck(&prev_button,&new_button);
playButtonMorse(&new_button,play_type);
}
}
void endSelector(const Button *const button)
{
movePuck(button,nullptr);
}

27
menu_utils.h Normal file
View File

@ -0,0 +1,27 @@
#pragma once
#include "button.h"
#include "menu.h"
//Returns true if button was found, false otherwise
bool findPressedButton(const Button* const* buttons,
const uint8_t num_buttons,
Button *const button_out,
const Point touch_point);
enum MorsePlaybackType_e : uint8_t {
PlayChar,
PlayText
};
void initSelector(int16_t *const raw_select_val_in_out,
const Button* const* buttons,
const uint8_t num_buttons,
const MorsePlaybackType_e);
void adjustSelector(int16_t *const raw_select_val_in_out,
int16_t knob,
const Button* const* buttons,
const uint8_t num_buttons,
const MorsePlaybackType_e);
void endSelector(const Button *const button);

117
morse.cpp
View File

@ -1,7 +1,9 @@
#include <Arduino.h>
#include "ubitx.h"
#include "settings.h"
#include "toneAC2/toneAC2.h"
#include "encoder.h"
#include "morse.h"
#include "pin_definitions.h"
#include "settings.h"
struct Morse {
char letter;
@ -15,53 +17,58 @@ struct Morse {
* The first zero after the 1s indicates the start of the letter, it MUST be discarded
*/
static const PROGMEM struct Morse morse_table[] = {
{'a', 0xf9}, // 11111001
{'b', 0xe8}, // 11101000
{'c', 0xea}, // 11101010
{'d', 0xf4}, // 11110100
{'e', 0xfc}, // 11111100
{'f', 0xe2}, // 11100010
{'g', 0xf6}, // 11110110
{'h', 0xe0}, // 11100000
{'i', 0xf8}, // 11111000
{'j', 0xe7}, // 11100111
{'k', 0xf6}, // 11110101
{'l', 0xe4}, // 11100100
{'m', 0xfb}, // 11111011
{'n', 0xfa}, // 11111010
{'o', 0xf7}, // 11110111
{'p', 0xe6}, // 11100110
{'q', 0xed}, // 11101101
{'r', 0xf2}, // 11110010
{'s', 0xf0}, // 11110000
{'t', 0xfd}, // 11111101
{'u', 0xf1}, // 11110001
{'v', 0xe1}, // 11100001
{'w', 0xf3}, // 11110011
{'x', 0xe9}, // 11101001
{'y', 0xe3}, // 11101011
{'z', 0xec}, // 11101100
{'1', 0xcf}, // 11001111
{'2', 0xc7}, // 11000111
{'3', 0xc3}, // 11000011
{'4', 0xc1}, // 11000001
{'5', 0xc0}, // 11000000
{'6', 0xd0}, // 11010000
{'7', 0xd8}, // 11011000
{'8', 0xdc}, // 11011100
{'9', 0xde}, // 11011110
{'0', 0xdf}, // 11011111
{'.', 0x95}, // 10010101
{',', 0xb3}, // 10110011
{'?', 0x8c}, // 10001100
{'a', 0b11111001},
{'b', 0b11101000},
{'c', 0b11101010},
{'d', 0b11110100},
{'e', 0b11111100},
{'f', 0b11100010},
{'g', 0b11110110},
{'h', 0b11100000},
{'i', 0b11111000},
{'j', 0b11100111},
{'k', 0b11110101},
{'l', 0b11100100},
{'m', 0b11111011},
{'n', 0b11111010},
{'o', 0b11110111},
{'p', 0b11100110},
{'q', 0b11101101},
{'r', 0b11110010},
{'s', 0b11110000},
{'t', 0b11111101},
{'u', 0b11110001},
{'v', 0b11100001},
{'w', 0b11110011},
{'x', 0b11101001},
{'y', 0b11101011},
{'z', 0b11101100},
{'1', 0b11001111},
{'2', 0b11000111},
{'3', 0b11000011},
{'4', 0b11000001},
{'5', 0b11000000},
{'6', 0b11010000},
{'7', 0b11011000},
{'8', 0b11011100},
{'9', 0b11011110},
{'0', 0b11011111},
{'.', 0b10010101},
{',', 0b10110011},
{'?', 0b10001100},
{ 2 , 0b11010101}, // ASCII 0x02 is Start of Text - <CT>
{ 4 , 0b10000101}, // ASCII 0x04 is End of Transmission - <CL> is too long for our encoding scheme in 8 bits, but <SK> fits
};
static void morseLetter(char c, uint16_t dit_duration_ms){
void morseLetter(char c, uint16_t dit_duration_ms){
if(!globalSettings.morseMenuOn){
return;
}
unsigned char mask = 0x80;
//handle space character as three dashes
if (c == ' '){
active_delay(7 * dit_duration_ms);
delay(7 * dit_duration_ms);
//Serial.print(' ');
return;
}
@ -79,7 +86,7 @@ static void morseLetter(char c, uint16_t dit_duration_ms){
//now we are at the first zero, skip and carry on
mask = mask >> 1;
while(mask){
tone(CW_TONE, globalSettings.cwSideToneFreq,10000);
toneAC2(PIN_CW_TONE, globalSettings.cwSideToneFreq);
if (mask & code){
delay(3 * dit_duration_ms);
//Serial.print('-');
@ -89,7 +96,7 @@ static void morseLetter(char c, uint16_t dit_duration_ms){
//Serial.print('.');
}
//Serial.print('#');
noTone(CW_TONE);
noToneAC2();
delay(dit_duration_ms); // space between dots and dashes
mask = mask >> 1;
}
@ -100,9 +107,23 @@ static void morseLetter(char c, uint16_t dit_duration_ms){
}
}
void morseText(char *text, uint16_t dit_duration_ms){
while(*text){
static const uint8_t RELATIVE_OFFSET_HZ = 100;
void morseText(const char *text, uint16_t dit_duration_ms){
int16_t total_counts = 0;
morseBool(false);
enc_read();//Don't count initial tone against total_counts
while(*text && (abs(total_counts) < 10)){
morseLetter(*text++, dit_duration_ms);
total_counts += enc_read();
}
}
void morseBool(bool val){
if(!globalSettings.morseMenuOn){
return;
}
toneAC2(PIN_CW_TONE, globalSettings.cwSideToneFreq + (val ? RELATIVE_OFFSET_HZ : -RELATIVE_OFFSET_HZ));
delay(3*globalSettings.cwDitDurationMs);
noToneAC2();
delay(3*globalSettings.cwDitDurationMs);
}

View File

@ -1,3 +1,7 @@
#include "settings.h"
//sends out morse code at the speed set by cwSpeed
void morseText(char *text, uint16_t dit_duration_ms = globalSettings.cwDitDurationMs);
void morseLetter(char c, uint16_t dit_duration_ms = globalSettings.cwDitDurationMs);
void morseText(const char *text, uint16_t dit_duration_ms = globalSettings.cwDitDurationMs);
//Plays either a higher or lower tone to indicate a boolean value
void morseBool(bool val);

View File

@ -2,5 +2,11 @@
#include <avr/pgmspace.h>
#include "PDQ_MinLib/FreeSansBold9pt7b.h"
//#include "PDQ_MinLib/Picopixel.h"
//#include "PDQ_MinLib/org_01.h"
//#include "PDQ_MinLib/TomThumb.h"
const GFXfont* ubitx_font = &FreeSansBold9pt7b;
const GFXfont* ubitx_font = &FreeSansBold9pt7b;
//const GFXfont* ubitx_font = &Picopixel;
//const GFXfont* ubitx_font = &Org_01;
//const GFXfont* ubitx_font = &TomThumb;

View File

@ -1,180 +1,35 @@
#include <Arduino.h>
#include "settings.h"
#include "ubitx.h"
#include "nano_gui.h"
#include "colors.h"
#include "pin_definitions.h"
#include "push_button.h"
#include "scratch_space.h"
#include "settings.h"
#include "touch.h"
#include <SPI.h>
#include <avr/pgmspace.h>
struct Point ts_point;
/*
* This formats the frequency given in f
*/
void formatFreq(uint32_t freq, char* buff, uint16_t buff_size) {
memset(buff, 0, buff_size);
ultoa(freq, buff, DEC);
uint8_t num_digits = strlen(buff);
const uint8_t num_spacers = (num_digits-1) / 3;
const uint8_t num_leading_digits_raw = num_digits % 3;
const uint8_t num_leading_digits = (0 == num_leading_digits_raw) ? 3 : num_leading_digits_raw;
if(0 == num_spacers){
return;
}
buff += num_leading_digits;
num_digits -= num_leading_digits;
for(int i = num_digits-1; i >= 0; --i){
buff[i + (i/3 + 1)] = buff[i];
}
for(unsigned int i = 0; i < num_spacers; ++i){
memcpy_P(buff,F("."),1);
buff += 4;
}
}
void readTouchCalibration(){
LoadSettingsFromEeprom();
/* for debugging
Serial.print(globalSettings.touchSlopeX); Serial.print(' ');
Serial.print(globalSettings.touchSlopeY); Serial.print(' ');
Serial.print(globalSettings.touchOffsetX); Serial.print(' ');
Serial.println(globalSettings.touchOffsetY); Serial.println(' ');
//*/
}
void writeTouchCalibration(){
SaveSettingsToEeprom();
}
#define Z_THRESHOLD 400
#define Z_THRESHOLD_INT 75
#define MSEC_THRESHOLD 3
static uint32_t msraw=0x80000000;
static int16_t xraw=0, yraw=0, zraw=0;
static uint8_t rotation = 1;
static int16_t touch_besttwoavg( int16_t x , int16_t y , int16_t z ) {
int16_t da, db, dc;
int16_t reta = 0;
if ( x > y ) da = x - y; else da = y - x;
if ( x > z ) db = x - z; else db = z - x;
if ( z > y ) dc = z - y; else dc = y - z;
if ( da <= db && da <= dc ) reta = (x + y) >> 1;
else if ( db <= da && db <= dc ) reta = (x + z) >> 1;
else reta = (y + z) >> 1; // else if ( dc <= da && dc <= db ) reta = (x + y) >> 1;
return (reta);
}
static void touch_update(){
int16_t data[6];
uint32_t now = millis();
if (now - msraw < MSEC_THRESHOLD) return;
SPI.setClockDivider(SPI_CLOCK_DIV8);//2MHz
digitalWrite(CS_PIN, LOW);
SPI.transfer(0xB1 /* Z1 */);
int16_t z1 = SPI.transfer16(0xC1 /* Z2 */) >> 3;
int z = z1 + 4095;
int16_t z2 = SPI.transfer16(0x91 /* X */) >> 3;
z -= z2;
if (z >= Z_THRESHOLD) {
SPI.transfer16(0x91 /* X */); // dummy X measure, 1st is always noisy
data[0] = SPI.transfer16(0xD1 /* Y */) >> 3;
data[1] = SPI.transfer16(0x91 /* X */) >> 3; // make 3 x-y measurements
data[2] = SPI.transfer16(0xD1 /* Y */) >> 3;
data[3] = SPI.transfer16(0x91 /* X */) >> 3;
}
else data[0] = data[1] = data[2] = data[3] = 0; // Compiler warns these values may be used unset on early exit.
data[4] = SPI.transfer16(0xD0 /* Y */) >> 3; // Last Y touch power down
data[5] = SPI.transfer16(0) >> 3;
digitalWrite(CS_PIN, HIGH);
SPI.setClockDivider(SPI_CLOCK_DIV2);//Return to full speed for TFT
if (z < 0) z = 0;
if (z < Z_THRESHOLD) { // if ( !touched ) {
// Serial.println();
zraw = 0;
return;
}
zraw = z;
int16_t x = touch_besttwoavg( data[0], data[2], data[4] );
int16_t y = touch_besttwoavg( data[1], data[3], data[5] );
//Serial.printf(" %d,%d", x, y);
//Serial.println();
if (z >= Z_THRESHOLD) {
msraw = now; // good read completed, set wait
switch (rotation) {
case 0:
xraw = 4095 - y;
yraw = x;
break;
case 1:
xraw = x;
yraw = y;
break;
case 2:
xraw = y;
yraw = 4095 - x;
break;
default: // 3
xraw = 4095 - x;
yraw = 4095 - y;
}
}
}
bool readTouch(){
touch_update();
if (zraw >= Z_THRESHOLD) {
ts_point.x = xraw;
ts_point.y = yraw;
//Serial.print(ts_point.x); Serial.print(",");Serial.println(ts_point.y);
return true;
}
return false;
}
void scaleTouch(struct Point *p){
p->x = ((long)(p->x - globalSettings.touchOffsetX) * 10L)/ (long)globalSettings.touchSlopeX;
p->y = ((long)(p->y - globalSettings.touchOffsetY) * 10L)/ (long)globalSettings.touchSlopeY;
//Serial.print(p->x); Serial.print(",");Serial.println(p->y);
}
/*****************
* Begin TFT functions
*****************/
#define ILI9341_CS_PIN TFT_CS
#define ILI9341_DC_PIN TFT_DC
#define ILI9341_CS_PIN PIN_TFT_CS
#define ILI9341_DC_PIN PIN_TFT_DC
#define ILI9341_SAVE_SPCR (1) //Save state before/after to play nice with the touch screen
#include "PDQ_MinLib/PDQ_ILI9341.h"
PDQ_ILI9341 tft;
#include "nano_font.h"
void xpt2046_Init(){
pinMode(CS_PIN, OUTPUT);
digitalWrite(CS_PIN, HIGH);
}
void displayInit(void){
//Pulling this low 6 times should exit deep sleep mode
pinMode(TFT_CS,OUTPUT);
pinMode(PIN_TFT_CS,OUTPUT);
for(uint8_t i = 0; i < 6; ++i){
digitalWrite(TFT_CS,HIGH);
digitalWrite(TFT_CS,LOW);
digitalWrite(PIN_TFT_CS,HIGH);
digitalWrite(PIN_TFT_CS,LOW);
}
digitalWrite(TFT_CS,HIGH);//Disable writing for now
digitalWrite(PIN_TFT_CS,HIGH);//Disable writing for now
tft.begin();
tft.setFont(ubitx_font);
@ -182,20 +37,18 @@ void displayInit(void){
tft.setTextColor(DISPLAY_GREEN,DISPLAY_BLACK);
tft.setTextSize(1);
tft.setRotation(1);
xpt2046_Init();
}
void displayPixel(unsigned int x, unsigned int y, unsigned int c){
tft.drawPixel(x,y,c);
tft.fillRect(x,y,1,1,c);
}
void displayHline(unsigned int x, unsigned int y, unsigned int w, unsigned int c){
tft.drawFastHLine(x,y,w,c);
tft.fillRect(x,y,w,1,c);
}
void displayVline(unsigned int x, unsigned int y, unsigned int l, unsigned int c){
tft.drawFastVLine(x,y,l,c);
tft.fillRect(x,y,1,l,c);
}
void displayClear(unsigned int color){
@ -203,7 +56,10 @@ void displayClear(unsigned int color){
}
void displayRect(unsigned int x,unsigned int y,unsigned int w,unsigned int h,unsigned int c){
tft.drawRect(x,y,w,h,c);
tft.fillRect(x,y,w,1,c);
tft.fillRect(x,y,1,h,c);
tft.fillRect(x,y+h-1,w,1,c);
tft.fillRect(x+w-1,y,1,h,c);
}
void displayFillrect(unsigned int x,unsigned int y,unsigned int w,unsigned int h,unsigned int c){
@ -214,21 +70,15 @@ void displayChar(int16_t x, int16_t y, unsigned char c, uint16_t color, uint16_t
tft.drawCharGFX(x,y,c,color,bg,1);
}
void displayRawText(char *text, int x1, int y1, int color, int background){
tft.setTextColor(color,background);
tft.setCursor(x1,y1);
tft.setBound(0,320);
tft.print(text);
}
void displayRawText(char *text, int x1, int y1, int w, int color, int background){
void displayRawText(const char *text, int x1, int y1, int w, int color, int background){
tft.setTextColor(color,background);
tft.setCursor(x1,y1);
tft.setBound(x1,x1+w);
tft.print(text);
}
void displayText(char *text, int x1, int y1, int w, int h, int color, int background, int border) {
void displayText(const char *const text, int x1, int y1, int w, int h, int color, int background, int border, TextJustification_e justification)
{
displayFillrect(x1, y1, w ,h, background);
displayRect(x1, y1, w ,h, border);
@ -237,106 +87,90 @@ void displayText(char *text, int x1, int y1, int w, int h, int color, int backgr
uint16_t width_out;
uint16_t height_out;
tft.getTextBounds(text,x1,y1,&x1_out,&y1_out,&width_out,&height_out,w);
x1 += (w - ( (int32_t)width_out + (x1_out-x1)))/2;
if(TextJustification_e::Center == justification){
x1 += (w - ( (int32_t)width_out + (x1_out-x1)))/2;
}
else if(TextJustification_e::Right == justification){
x1 += w - ((int32_t)width_out + (x1_out-x1));
}
else{
x1 += 2;//Give a little bit of padding from the border
}
y1 += (ubitx_font->yAdvance + h - ( (int32_t)height_out))/2;
displayRawText(text,x1,y1,w,color,background);
}
void drawCross(int16_t x_center,int16_t y_center,uint16_t color)
{
constexpr uint8_t HALF_SIZE = 10;
displayHline(x_center-HALF_SIZE,y_center,2*HALF_SIZE,color);
displayVline(x_center,y_center-HALF_SIZE,2*HALF_SIZE,color);
}
void setupTouch(){
int x1, y1, x2, y2, x3, y3, x4, y4;
constexpr int16_t CROSS_CORNER_OFFSET = 20;
constexpr Point CROSS_CORNER_POINTS [] = {
{CROSS_CORNER_OFFSET,CROSS_CORNER_OFFSET},//Top left
{PDQ_ILI9341::ILI9341_TFTHEIGHT-CROSS_CORNER_OFFSET,CROSS_CORNER_OFFSET},//Top right
{CROSS_CORNER_OFFSET, PDQ_ILI9341::ILI9341_TFTWIDTH-CROSS_CORNER_OFFSET},//Bottom left
{PDQ_ILI9341::ILI9341_TFTHEIGHT-CROSS_CORNER_OFFSET,PDQ_ILI9341::ILI9341_TFTWIDTH-CROSS_CORNER_OFFSET}//Bottom right
};
displayClear(DISPLAY_BLACK);
strncpy_P(b,(const char*)F("Click on the cross"),sizeof(b));
strncpy_P(b,(const char*)F("Click on the cross\nPush tune to cancel"),sizeof(b));
displayText(b, 20,100, 200, 50, DISPLAY_WHITE, DISPLAY_BLACK, DISPLAY_BLACK);
// TOP-LEFT
displayHline(10,20,20,DISPLAY_WHITE);
displayVline(20,10,20, DISPLAY_WHITE);
Point cal_points[sizeof(CROSS_CORNER_POINTS)/sizeof(CROSS_CORNER_POINTS[0])];
while(!readTouch())
delay(100);
while(readTouch())
delay(100);
x1 = ts_point.x;
y1 = ts_point.y;
for(uint8_t i = 0; i < sizeof(CROSS_CORNER_POINTS)/sizeof(CROSS_CORNER_POINTS[0]); ++i){
drawCross(CROSS_CORNER_POINTS[i].x,CROSS_CORNER_POINTS[i].y,DISPLAY_WHITE);
while(!readTouch(&cal_points[i])){
if(ButtonPress_e::NotPressed != CheckTunerButton()){
return;
}
delay(100);
}
while(readTouch(&cal_points[i])){
delay(100);
}
drawCross(CROSS_CORNER_POINTS[i].x,CROSS_CORNER_POINTS[i].y,DISPLAY_BLACK);
delay(1000);//Ensure that nobody is pressing the screen before we do the next point
}
//rubout the previous one
displayHline(10,20,20,DISPLAY_BLACK);
displayVline(20,10,20, DISPLAY_BLACK);
//We can get nicer scaling if we allow more resolution on the divisor
constexpr int32_t SCALE_SENSITIVITY_MULTIPLIER = 10;
delay(1000);
//TOP RIGHT
displayHline(290,20,20,DISPLAY_WHITE);
displayVline(300,10,20, DISPLAY_WHITE);
const int16_t diff_x_top = cal_points[1].x - cal_points[0].x;
const int16_t diff_x_bottom = cal_points[3].x - cal_points[2].x;
constexpr int32_t diff_x_target = CROSS_CORNER_POINTS[1].x - CROSS_CORNER_POINTS[0].x;
while(!readTouch())
delay(100);
while(readTouch())
delay(100);
x2 = ts_point.x;
y2 = ts_point.y;
//Average the measured differences
globalSettings.touchSlopeX = SCALE_SENSITIVITY_MULTIPLIER*(diff_x_top + diff_x_bottom) / (2*diff_x_target);
displayHline(290,20,20,DISPLAY_BLACK);
displayVline(300,10,20, DISPLAY_BLACK);
const int16_t diff_y_left = cal_points[2].y - cal_points[0].y;
const int16_t diff_y_right = cal_points[3].y - cal_points[1].y;
constexpr int32_t diff_y_target = CROSS_CORNER_POINTS[2].y - CROSS_CORNER_POINTS[0].y;
delay(1000);
//Average the measured differences
globalSettings.touchSlopeY = SCALE_SENSITIVITY_MULTIPLIER*(diff_y_left + diff_y_right) / (2*diff_y_target);
//BOTTOM LEFT
displayHline(10,220,20,DISPLAY_WHITE);
displayVline(20,210,20, DISPLAY_WHITE);
while(!readTouch())
delay(100);
x3 = ts_point.x;
y3 = ts_point.y;
while(readTouch())
delay(100);
displayHline(10,220,20,DISPLAY_BLACK);
displayVline(20,210,20, DISPLAY_BLACK);
globalSettings.touchOffsetX = cal_points[0].x - ((CROSS_CORNER_OFFSET * globalSettings.touchSlopeX)/SCALE_SENSITIVITY_MULTIPLIER);
globalSettings.touchOffsetY = cal_points[0].y - ((CROSS_CORNER_OFFSET * globalSettings.touchSlopeY)/SCALE_SENSITIVITY_MULTIPLIER);
delay(1000);
//BOTTOM RIGHT
displayHline(290,220,20,DISPLAY_WHITE);
displayVline(300,210,20, DISPLAY_WHITE);
while(!readTouch())
delay(100);
x4 = ts_point.x;
y4 = ts_point.y;
displayHline(290,220,20,DISPLAY_BLACK);
displayVline(300,210,20, DISPLAY_BLACK);
// we average two readings and divide them by half and store them as scaled integers 10 times their actual, fractional value
//the x points are located at 20 and 300 on x axis, hence, the delta x is 280, we take 28 instead, to preserve fractional value,
//there are two readings (x1,x2) and (x3, x4). Hence, we have to divide by 28 * 2 = 56
globalSettings.touchSlopeX = ((x4 - x3) + (x2 - x1))/56;
//the y points are located at 20 and 220 on the y axis, hence, the delta is 200. we take it as 20 instead, to preserve the fraction value
//there are two readings (y1, y2) and (y3, y4). Hence we have to divide by 20 * 2 = 40
globalSettings.touchSlopeY = ((y3 - y1) + (y4 - y2))/40;
//x1, y1 is at 20 pixels
globalSettings.touchOffsetX = x1 + -((20 * globalSettings.touchSlopeX)/10);
globalSettings.touchOffsetY = y1 + -((20 * globalSettings.touchSlopeY)/10);
/*
Serial.print(x1);Serial.print(':');Serial.println(y1);
Serial.print(x2);Serial.print(':');Serial.println(y2);
Serial.print(x3);Serial.print(':');Serial.println(y3);
Serial.print(x4);Serial.print(':');Serial.println(y4);
for(uint8_t i = 0; i < sizeof(cal_points)/sizeof(cal_points[0]); ++i){
Serial.print(cal_points[i].x);Serial.print(':');Serial.println(cal_points[i].y);
}
//for debugging
Serial.print(globalSettings.touchSlopeX); Serial.print(' ');
Serial.print(globalSettings.touchSlopeY); Serial.print(' ');
Serial.print(globalSettings.touchOffsetX); Serial.print(' ');
Serial.println(globalSettings.touchOffsetY); Serial.println(' ');
*/
writeTouchCalibration();
displayClear(DISPLAY_BLACK);
*/
SaveSettingsToEeprom();
}

View File

@ -1,11 +1,12 @@
#ifndef _NANO_GUI_H_
#define _NANO_GUI_H_
/* UI functions */
struct Point {
int x, y;
enum TextJustification_e : uint8_t
{
Left,
Right,
Center
};
extern struct Point ts_point;
void displayInit();
void displayClear(unsigned int color);
@ -15,36 +16,10 @@ void displayVline(unsigned int x, unsigned int y, unsigned int l, unsigned int c
void displayRect(unsigned int x,unsigned int y,unsigned int w,unsigned int h,unsigned int c);
void displayFillrect(unsigned int x,unsigned int y,unsigned int w,unsigned int h,unsigned int c);
void displayChar(int16_t x, int16_t y, unsigned char c, uint16_t color, uint16_t bg);
void displayRawText(char *text, int x1, int y1, int color, int background);
void displayText(char *text, int x1, int y1, int w, int h, int color, int background, int border);
void displayText(const char *const text, int x1, int y1, int w, int h, int color, int background, int border, TextJustification_e justification = TextJustification_e::Center);
void formatFreq(uint32_t freq, char* buff, uint16_t buff_size);
/* touch functions */
boolean readTouch();
void scaleTouch(struct Point *p);
// Color definitions
#define DISPLAY_BLACK 0x0000 ///< 0, 0, 0
#define DISPLAY_NAVY 0x000F ///< 0, 0, 123
#define DISPLAY_DARKGREEN 0x03E0 ///< 0, 125, 0
#define DISPLAY_DARKCYAN 0x03EF ///< 0, 125, 123
#define DISPLAY_MAROON 0x7800 ///< 123, 0, 0
#define DISPLAY_PURPLE 0x780F ///< 123, 0, 123
#define DISPLAY_OLIVE 0x7BE0 ///< 123, 125, 0
#define DISPLAY_LIGHTGREY 0xC618 ///< 198, 195, 198
#define DISPLAY_DARKGREY 0x7BEF ///< 123, 125, 123
#define DISPLAY_BLUE 0x001F ///< 0, 0, 255
#define DISPLAY_GREEN 0x07E0 ///< 0, 255, 0
#define DISPLAY_CYAN 0x07FF ///< 0, 255, 255
#define DISPLAY_RED 0xF800 ///< 255, 0, 0
#define DISPLAY_MAGENTA 0xF81F ///< 255, 0, 255
#define DISPLAY_YELLOW 0xFFE0 ///< 255, 255, 0
#define DISPLAY_WHITE 0xFFFF ///< 255, 255, 255
#define DISPLAY_ORANGE 0xFD20 ///< 255, 165, 0
#define DISPLAY_GREENYELLOW 0xAFE5 ///< 173, 255, 41
#define DISPLAY_PINK 0xFC18 ///< 255, 130, 198
/* these functions are called universally to update the display */
void drawTx();
#define TEXT_LINE_HEIGHT 18
#define TEXT_LINE_INDENT 5

45
pin_definitions.h Normal file
View File

@ -0,0 +1,45 @@
#pragma once
/*
* Pin configurations for uBiTXv6
*
* Attached devices are:
* * A rotory encoder for tuning, which has a built-in push button for selection
* * A Push-to-talk button input
* * A morse keyer input, using analog voltage divider for paddle support
* * A SI5351A 3-output frequency generator
* * A model tjctm24028-spi touch screen LCD, which has:
* * An ILI9341 display controller
* * A XPT2046 touch controller
* * A TX/RX output control pin
* * A tone output pin to provide audio feedback to the operator when sending CW
* * Three filter selection output control pins
* * A CW keydown output
* * Optionally, a serial CAT interface
*/
static const uint8_t PIN_ENC_A = A0; // Tuning encoder interface
static const uint8_t PIN_ENC_B = A1; // Tuning encoder interface
static const uint8_t PIN_ENC_PUSH_BUTTON = A2; // Tuning encoder interface
static const uint8_t PIN_PTT = A3; // Sense it for ssb and as a straight key for cw operation
//A4 is I2C SDA
//A5 is I2C SCK
static const uint8_t PIN_ANALOG_KEYER = A6; // This is used as keyer. The analog port has 4.7K pull up resistor. Details are in the circuit description on www.hfsignals.com
static const uint8_t PIN_ANALOG_SPARE = A7; // Not used yet
//13 is SPI CLK
//12 is SPI MISO
//11 is SPI MOSI
static const uint8_t PIN_TFT_CS = 10; // Selects the LCD controller on SPI interface (active low)
static const uint8_t PIN_TFT_DC = 9; // Tells the LCD controller if it's getting data (D, high) or commands (C, low)
static const uint8_t PIN_TOUCH_CS = 8; // Selects the touch controller on SPI interface (active low)
static const uint8_t PIN_TX_RXn = 7; // Pin from the Nano to the radio to switch to TX (HIGH) and RX(LOW)
static const uint8_t PIN_CW_TONE = 6; // Generates a square wave sidetone while sending the CW
static const uint8_t PIN_TX_LPF_A = 5; // The 30 MHz LPF is permanently connected in the output of the PA...
static const uint8_t PIN_TX_LPF_B = 4; // ...Alternatively, either 3.5 MHz, 7 MHz or 14 Mhz LPFs are...
static const uint8_t PIN_TX_LPF_C = 3; // ...switched inline depending upon the TX frequency
static const uint8_t PIN_CW_KEY = 2; // Pin goes high during CW keydown to transmit the carrier.
// ... The PIN_CW_KEY is needed in addition to the TX/RX key as the...
// ...key can be up within a tx period
//1 is UART RX
//0 is UART TX

8
point.h Normal file
View File

@ -0,0 +1,8 @@
#pragma once
#include <stdint.h>
struct Point {
int16_t x;
int16_t y;
};

34
push_button.cpp Normal file
View File

@ -0,0 +1,34 @@
#include <Arduino.h>
#include "button_timing.h"
#include "pin_definitions.h"
#include "push_button.h"
bool IsButtonPressed()
{
//Button has a pullup, so it reads high normally,
//and reads low when pressed down
return !digitalRead(PIN_ENC_PUSH_BUTTON);
}
ButtonPress_e CheckTunerButton(){
if (!IsButtonPressed()){
return ButtonPress_e::NotPressed;
}
delay(DEBOUNCE_DELAY_MS);
if (!IsButtonPressed()){//debounce
return ButtonPress_e::NotPressed;
}
uint16_t down_time = 0;
while(IsButtonPressed() && (down_time < LONG_PRESS_TIME_MS)){
delay(LONG_PRESS_POLL_TIME_MS);
down_time += LONG_PRESS_POLL_TIME_MS;
}
if(down_time < LONG_PRESS_TIME_MS){
return ButtonPress_e::ShortPress;
}
else{
return ButtonPress_e::LongPress;
}
}

5
push_button.h Normal file
View File

@ -0,0 +1,5 @@
#pragma once
#include "button_press_e.h"
ButtonPress_e CheckTunerButton();

13
scratch_space.h Normal file
View File

@ -0,0 +1,13 @@
/**
* The Arduino, unlike C/C++ on a regular computer with gigabytes of RAM, has very little memory.
* We have to be very careful with variables that are declared inside the functions as they are
* created in a memory region called the stack. The stack has just a few bytes of space on the Arduino
* if you declare large strings inside functions, they can easily exceed the capacity of the stack
* and mess up your programs.
* We circumvent this by declaring a few global buffers as kitchen counters where we can
* slice and dice our strings. These strings are mostly used to control the display or handle
* the input and output from the USB port. We must keep a count of the bytes used while reading
* the serial port as we can easily run out of buffer space. This is done in the serial_in_count variable.
*/
extern char c[30], b[128];

View File

@ -2,8 +2,9 @@
#include <stdint.h>
#include <EEPROM.h>
#include <Arduino.h>//only needed for debugging's Serial.print stuff
#include "nano_gui.h"//redrawVFOs() function
#include "settings.h"
#include "ubitx.h"//redrawVFOs() function
#include "si5351.h"
/**
* These are the "magic" indices where these user changable settinngs are stored in the EEPROM
@ -20,10 +21,14 @@ static const uint16_t EEPROM_ADDR_TOUCH_SLOPE_X = 32;//int16_t
static const uint16_t EEPROM_ADDR_TOUCH_SLOPE_Y = 36;//int16_t
static const uint16_t EEPROM_ADDR_TOUCH_OFFSET_X = 40;//int16_t
static const uint16_t EEPROM_ADDR_TOUCH_OFFSET_Y = 44;//int16_t
static const uint16_t EEPROM_ADDR_CW_DELAYTIME = 48;
static const uint16_t EEPROM_ADDR_VFO_A_MODE = 256;
static const uint16_t EEPROM_ADDR_VFO_B_MODE = 257;
static const uint16_t EEPROM_ADDR_CW_KEY_TYPE = 358;
static const uint16_t EEPROM_ADDR_MORSE_MENU = 46;//uint8_t
static const uint16_t EEPROM_ADDR_CW_DELAYTIME = 48;//uint16_t
static const uint16_t EEPROM_ADDR_VFO_A_MODE = 256;//uint8_t
static const uint16_t EEPROM_ADDR_VFO_B_MODE = 257;//uint8_t
static const uint16_t EEPROM_ADDR_CW_KEY_TYPE = 358;//uint8_t
static const uint16_t EEPROM_ADDR_QUICKLIST_FREQ = 630;//uint32_t array of size NUM_QUICKLIST_SETTINGS
static const uint16_t EEPROM_ADDR_QUICKLIST_MODE = 710;//uint8_t array of size NUM_QUICKLIST_SETTINGS
template<typename T>
bool LoadSane(T& dest,uint16_t addr, T min, T max)
@ -61,6 +66,11 @@ void LoadDefaultSettings()
globalSettings.vfoB.frequency = 14150000UL;
globalSettings.vfoB.mode = VFO_MODE_USB;
for(uint8_t i = 0; i < NUM_QUICKLIST_SETTINGS; ++i){
globalSettings.quickList[i].frequency = i*1000000;
globalSettings.quickList[i].mode = VfoMode_e::VFO_MODE_LSB;
}
globalSettings.keyerMode = KEYER_STRAIGHT;
globalSettings.cwSideToneFreq = 800;
globalSettings.cwDitDurationMs = 100;
@ -81,13 +91,14 @@ void LoadDefaultSettings()
globalSettings.txActive = false;
globalSettings.txCatActive = false;
globalSettings.cwExpirationTimeMs = 0;
globalSettings.morseMenuOn = false;
}
void LoadSettingsFromEeprom()
{
LoadSane(globalSettings.usbCarrierFreq,EEPROM_ADDR_USB_CAL,11048000UL,11060000UL);
LoadSane(globalSettings.vfoA.frequency,EEPROM_ADDR_VFO_A_FREQ,500000UL+1,109000000UL-1);//Allow all freq supported by si5351 driver
LoadSane(globalSettings.vfoB.frequency,EEPROM_ADDR_VFO_B_FREQ,500000UL+1,109000000UL-1);//Allow all freq supported by si5351 driver
LoadSane(globalSettings.vfoA.frequency,EEPROM_ADDR_VFO_A_FREQ,SI5351_MIN_FREQUENCY_HZ,SI5351_MAX_FREQUENCY_HZ);
LoadSane(globalSettings.vfoB.frequency,EEPROM_ADDR_VFO_B_FREQ,SI5351_MIN_FREQUENCY_HZ,SI5351_MAX_FREQUENCY_HZ);
LoadSane(globalSettings.cwSideToneFreq,EEPROM_ADDR_CW_SIDETONE,100UL,2000UL);
LoadSane(globalSettings.cwDitDurationMs,EEPROM_ADDR_CW_DIT_TIME,10U,1000U);
if(LoadSane(globalSettings.cwActiveTimeoutMs,EEPROM_ADDR_CW_DELAYTIME,10U,100U)){
@ -97,6 +108,15 @@ void LoadSettingsFromEeprom()
LoadSane(globalSettings.vfoB.mode,EEPROM_ADDR_VFO_B_MODE,VFO_MODE_LSB,VFO_MODE_USB);
LoadSane(globalSettings.keyerMode,EEPROM_ADDR_CW_KEY_TYPE,KEYER_STRAIGHT,KEYER_IAMBIC_B);
uint8_t morse_on = 0;
LoadSane(morse_on,EEPROM_ADDR_MORSE_MENU,(uint8_t)0,(uint8_t)1);
globalSettings.morseMenuOn = morse_on;
for(uint8_t i = 0; i < NUM_QUICKLIST_SETTINGS; ++i){
LoadSane(globalSettings.quickList[i].frequency,EEPROM_ADDR_QUICKLIST_FREQ+(sizeof(uint32_t)*i),SI5351_MIN_FREQUENCY_HZ,SI5351_MAX_FREQUENCY_HZ);
LoadSane(globalSettings.quickList[i].mode,EEPROM_ADDR_QUICKLIST_MODE+(sizeof(uint8_t)*i),VFO_MODE_LSB,VFO_MODE_USB);
}
//No sanity check on these - cal your heart out
EEPROM.get(EEPROM_ADDR_MASTER_CAL,globalSettings.oscillatorCal);
EEPROM.get(EEPROM_ADDR_TOUCH_SLOPE_X,globalSettings.touchSlopeX);
@ -122,6 +142,12 @@ void SaveSettingsToEeprom()
EEPROM.put(EEPROM_ADDR_VFO_A_MODE,globalSettings.vfoA.mode);
EEPROM.put(EEPROM_ADDR_VFO_B_MODE,globalSettings.vfoB.mode);
EEPROM.put(EEPROM_ADDR_CW_KEY_TYPE,globalSettings.keyerMode);
EEPROM.put(EEPROM_ADDR_MORSE_MENU,(uint8_t)globalSettings.morseMenuOn);
for(uint8_t i = 0; i < NUM_QUICKLIST_SETTINGS; ++i){
EEPROM.put(EEPROM_ADDR_QUICKLIST_FREQ+(sizeof(uint32_t)*i),globalSettings.quickList[i].frequency);
EEPROM.put(EEPROM_ADDR_QUICKLIST_MODE+(sizeof(uint8_t)*i),globalSettings.quickList[i].mode);
}
}
uint32_t GetActiveVfoFreq()
@ -164,5 +190,4 @@ void SetActiveVfoMode(VfoMode_e mode)
else{
globalSettings.vfoB.mode = mode;
}
redrawVFOs();
}

View File

@ -12,6 +12,10 @@
#pragma once
#include <stdint.h>//uint8_t etc.
static const uint8_t NUM_QUICKLIST_SETTINGS = 4;
/*
* Loads default values for all settings
*/
@ -77,6 +81,8 @@ struct SettingsRam
VfoSettings_t vfoA;
VfoSettings_t vfoB;
VfoSettings_t quickList[4];
KeyerMode_e keyerMode;
uint32_t cwSideToneFreq;
uint16_t cwDitDurationMs;
@ -97,6 +103,7 @@ struct SettingsRam
bool txActive;
bool txCatActive;
uint32_t cwExpirationTimeMs;
bool morseMenuOn;
};
//This is the shared declaration

496
setup.cpp
View File

@ -1,9 +1,16 @@
#include <Arduino.h>
#include "toneAC2/toneAC2.h"
#include "colors.h"
#include "encoder.h"
#include "menu.h"
#include "morse.h"
#include "nano_gui.h"
#include "pin_definitions.h"
#include "scratch_space.h"
#include "setup.h"
#include "settings.h"
#include "ubitx.h"
#include "si5351.h"
#include "tuner.h"
#include "utils.h"
/** Menus
* The Radio menus are accessed by tapping on the function button.
@ -25,19 +32,9 @@ static const unsigned int COLOR_SETTING_BACKGROUND = DISPLAY_NAVY;
static const unsigned int COLOR_ACTIVE_BORDER = DISPLAY_WHITE;
static const unsigned int COLOR_INACTIVE_BORDER = COLOR_BACKGROUND;
static const unsigned int LAYOUT_OUTER_BORDER_X = 10;
static const unsigned int LAYOUT_OUTER_BORDER_Y = 10;
static const unsigned int LAYOUT_OUTER_BORDER_WIDTH = 300;
static const unsigned int LAYOUT_OUTER_BORDER_HEIGHT = 220;
static const unsigned int LAYOUT_INNER_BORDER_X = 12;
static const unsigned int LAYOUT_INNER_BORDER_Y = 12;
static const unsigned int LAYOUT_INNER_BORDER_WIDTH = 296;
static const unsigned int LAYOUT_INNER_BORDER_HEIGHT = 216;
static const unsigned int LAYOUT_TITLE_X = LAYOUT_INNER_BORDER_X;
static const unsigned int LAYOUT_TITLE_Y = LAYOUT_INNER_BORDER_Y;
static const unsigned int LAYOUT_TITLE_WIDTH = LAYOUT_INNER_BORDER_WIDTH;
static const unsigned int LAYOUT_TITLE_X = 12;
static const unsigned int LAYOUT_TITLE_Y = 12;
static const unsigned int LAYOUT_TITLE_WIDTH = 296;
static const unsigned int LAYOUT_TITLE_HEIGHT = 35;
static const unsigned int LAYOUT_ITEM_X = 30;
@ -46,30 +43,37 @@ static const unsigned int LAYOUT_ITEM_WIDTH = 260;
static const unsigned int LAYOUT_ITEM_HEIGHT = 30;
static const unsigned int LAYOUT_ITEM_PITCH_Y = LAYOUT_ITEM_HEIGHT + 1;
static const unsigned int LAYOUT_SETTING_REF_VALUE_X = LAYOUT_ITEM_X;
static const unsigned int LAYOUT_SETTING_REF_VALUE_Y = LAYOUT_ITEM_Y + 3*LAYOUT_ITEM_PITCH_Y;
static const unsigned int LAYOUT_SETTING_REF_VALUE_WIDTH = LAYOUT_ITEM_WIDTH;
static const unsigned int LAYOUT_SETTING_REF_VALUE_HEIGHT = LAYOUT_ITEM_HEIGHT;
static const unsigned int LAYOUT_SETTING_VALUE_X = LAYOUT_ITEM_X;
static const unsigned int LAYOUT_SETTING_VALUE_Y = LAYOUT_ITEM_Y + 3*LAYOUT_ITEM_PITCH_Y;
static const unsigned int LAYOUT_SETTING_VALUE_Y = LAYOUT_ITEM_Y + 4*LAYOUT_ITEM_PITCH_Y;
static const unsigned int LAYOUT_SETTING_VALUE_WIDTH = LAYOUT_ITEM_WIDTH;
static const unsigned int LAYOUT_SETTING_VALUE_HEIGHT = LAYOUT_ITEM_HEIGHT;
static const unsigned int LAYOUT_INSTRUCTIONS_TEXT_X = 20;
static const unsigned int LAYOUT_INSTRUCTIONS_TEXT_Y = LAYOUT_ITEM_Y;
static const unsigned int LAYOUT_INSTRUCTIONS_TEXT_WIDTH = LAYOUT_ITEM_WIDTH;
static const unsigned int LAYOUT_INSTRUCTIONS_TEXT_HEIGHT = LAYOUT_SETTING_VALUE_Y - LAYOUT_ITEM_Y - 1;
static const unsigned int LAYOUT_INSTRUCTIONS_TEXT_HEIGHT = LAYOUT_SETTING_REF_VALUE_Y - LAYOUT_ITEM_Y - 1;
static const unsigned int LAYOUT_CONFIRM_TEXT_X = 20;
static const unsigned int LAYOUT_CONFIRM_TEXT_Y = LAYOUT_ITEM_Y + 5*LAYOUT_ITEM_PITCH_Y;
static const unsigned int LAYOUT_CONFIRM_TEXT_WIDTH = LAYOUT_ITEM_WIDTH;
static const unsigned int LAYOUT_CONFIRM_TEXT_HEIGHT = LAYOUT_ITEM_HEIGHT;
constexpr char strYes [] PROGMEM = "Yes";
constexpr char strNo [] PROGMEM = "No";
constexpr char strHz [] PROGMEM = "Hz";
void displayDialog(const char* title,
const char* instructions){
displayClear(COLOR_BACKGROUND);
displayRect(LAYOUT_OUTER_BORDER_X,LAYOUT_OUTER_BORDER_Y,LAYOUT_OUTER_BORDER_WIDTH,LAYOUT_OUTER_BORDER_HEIGHT, COLOR_ACTIVE_BORDER);
displayRect(LAYOUT_INNER_BORDER_X,LAYOUT_INNER_BORDER_Y,LAYOUT_INNER_BORDER_WIDTH,LAYOUT_INNER_BORDER_HEIGHT, COLOR_ACTIVE_BORDER);
strncpy_P(b,title,sizeof(b));
displayText(b, LAYOUT_TITLE_X, LAYOUT_TITLE_Y, LAYOUT_TITLE_WIDTH, LAYOUT_TITLE_HEIGHT, COLOR_TEXT, COLOR_TITLE_BACKGROUND, COLOR_ACTIVE_BORDER);
strncpy_P(b,instructions,sizeof(b));
displayText(b, LAYOUT_INSTRUCTIONS_TEXT_X, LAYOUT_INSTRUCTIONS_TEXT_Y, LAYOUT_INSTRUCTIONS_TEXT_WIDTH, LAYOUT_INSTRUCTIONS_TEXT_HEIGHT, COLOR_TEXT, COLOR_BACKGROUND, COLOR_BACKGROUND);
displayText(b, LAYOUT_INSTRUCTIONS_TEXT_X, LAYOUT_INSTRUCTIONS_TEXT_Y, LAYOUT_INSTRUCTIONS_TEXT_WIDTH, LAYOUT_INSTRUCTIONS_TEXT_HEIGHT, COLOR_TEXT, COLOR_BACKGROUND, COLOR_BACKGROUND, TextJustification_e::Left);
strncpy_P(b,(const char*)F("Push Tune to Save"),sizeof(b));
displayText(b, LAYOUT_CONFIRM_TEXT_X, LAYOUT_CONFIRM_TEXT_Y, LAYOUT_CONFIRM_TEXT_WIDTH, LAYOUT_CONFIRM_TEXT_HEIGHT, COLOR_TEXT, COLOR_BACKGROUND, COLOR_BACKGROUND);
}
@ -85,61 +89,91 @@ struct SettingScreen_t {
void (*Finalize)(const long int final_value);
};
void runSetting(const SettingScreen_t* const p_screen)
void drawSetting(const SettingScreen_t* const screen)
{
SettingScreen_t screen = {nullptr,nullptr,0,0,nullptr,nullptr,nullptr,nullptr};
memcpy_P(&screen,p_screen,sizeof(screen));
displayDialog(screen.Title,
screen.AdditionalText);
displayDialog(screen->Title,
screen->AdditionalText);
}
//Wait for button to stop being pressed
while(btnDown()){
active_delay(10);
//State variables for settings
int32_t setupMenuRawValue = 0;
int32_t setupMenuLastValue = 0;
const SettingScreen_t* activeSettingP;
void activateSetting(SettingScreen_t* new_setting_P);
void initSetting();
MenuReturn_e runSetting(const ButtonPress_e tuner_button,
const ButtonPress_e touch_button,
const Point touch_point,
const int16_t knob);
Menu_t setupMenuActiveSettingMenu = {
initSetting,
runSetting,
nullptr
};
void initSetting()
{
if(nullptr == activeSettingP){
return;
}
active_delay(10);
long int raw_value = 0;
long int last_value = 0;
screen.Initialize(&last_value);
screen.OnValueChange(last_value,b,sizeof(b));
SettingScreen_t screen = {nullptr,nullptr,0,0,nullptr,nullptr,nullptr,nullptr};
memcpy_P(&screen,activeSettingP,sizeof(screen));
drawSetting(&screen);
screen.Initialize(&setupMenuLastValue);
screen.OnValueChange(setupMenuLastValue,b,sizeof(b));
displayText(b, LAYOUT_SETTING_VALUE_X, LAYOUT_SETTING_VALUE_Y, LAYOUT_SETTING_VALUE_WIDTH, LAYOUT_SETTING_VALUE_HEIGHT, COLOR_TEXT, COLOR_TITLE_BACKGROUND, COLOR_BACKGROUND);
displayText(b, LAYOUT_SETTING_REF_VALUE_X, LAYOUT_SETTING_REF_VALUE_Y, LAYOUT_SETTING_REF_VALUE_WIDTH, LAYOUT_SETTING_REF_VALUE_HEIGHT, COLOR_SETTING_BACKGROUND, COLOR_BACKGROUND, COLOR_BACKGROUND);
setupMenuRawValue = setupMenuLastValue * (int32_t)screen.KnobDivider;
}
raw_value = last_value * (int32_t)screen.KnobDivider;
MenuReturn_e runSetting(const ButtonPress_e tuner_button,
const ButtonPress_e touch_button,
const Point touch_point,
const int16_t knob)
{
if(nullptr == activeSettingP){
return MenuReturn_e::ExitedRedraw;
}
SettingScreen_t screen = {nullptr,nullptr,0,0,nullptr,nullptr,nullptr,nullptr};
memcpy_P(&screen,activeSettingP,sizeof(screen));
while (!btnDown())
{
int knob = enc_read();
if(knob != 0){
raw_value += knob * screen.StepSize;
}
else{
continue;
}
if(ButtonPress_e::NotPressed != tuner_button){
//Long or short press, we do the same thing
screen.Finalize(setupMenuLastValue);
return MenuReturn_e::ExitedRedraw;
}
const long int candidate_value = raw_value / (int32_t)screen.KnobDivider;
long int value = 0;
(void)touch_button;(void)touch_point;//TODO: handle touch input?
if(0 != knob){
setupMenuRawValue += knob * screen.StepSize;
const int32_t candidate_value = setupMenuRawValue / (int32_t)screen.KnobDivider;
int32_t value = 0;
screen.Validate(candidate_value,&value);
//If we're going out of bounds, prevent the raw value from going too far out
if(candidate_value != value){
raw_value = value * (int32_t)screen.KnobDivider;
setupMenuRawValue = value * (int32_t)screen.KnobDivider;
}
if(value == last_value){
continue;
}
else{
if(value != setupMenuLastValue){
screen.OnValueChange(value,b,sizeof(b));
displayText(b, LAYOUT_SETTING_VALUE_X, LAYOUT_SETTING_VALUE_Y, LAYOUT_SETTING_VALUE_WIDTH, LAYOUT_SETTING_VALUE_HEIGHT, COLOR_TEXT, COLOR_TITLE_BACKGROUND, COLOR_BACKGROUND);
last_value = value;
setupMenuLastValue = value;
}
}
screen.Finalize(last_value);
return MenuReturn_e::StillActive;
}
#define LIMIT(val,min,max) ((val) < (min)) ? (min) : (((max) < (val)) ? (max) : (val))
void activateSetting(const SettingScreen_t *const new_setting_P)
{
activeSettingP = new_setting_P;
enterSubmenu(&setupMenuActiveSettingMenu);
}
//Local Oscillator
void ssLocalOscInitialize(long int* start_value_out){
@ -147,7 +181,6 @@ void ssLocalOscInitialize(long int* start_value_out){
uint32_t freq = GetActiveVfoFreq();
freq = (freq/1000L) * 1000L;//round off the current frequency the nearest kHz
setFrequency(freq);
si5351bx_setfreq(0, globalSettings.usbCarrierFreq); //set back the carrier oscillator, cw tx switches it off
}
*start_value_out = globalSettings.oscillatorCal;
}
@ -165,7 +198,7 @@ void ssLocalOscChange(const long int new_value, char* buff_out, const size_t buf
++buff_out;
}
formatFreq(u,buff_out,buff_out_size - strlen(buff_out));
strncat_P(buff_out,(const char*)F("Hz"),buff_out_size - strlen(buff_out));
strncat_P(buff_out,strHz,buff_out_size - strlen(buff_out));
}
void ssLocalOscFinalize(const long int final_value)
{
@ -175,7 +208,7 @@ void ssLocalOscFinalize(const long int final_value)
setFrequency(GetActiveVfoFreq());
}
const char SS_LOCAL_OSC_T [] PROGMEM = "Local Oscillator";
const char SS_LOCAL_OSC_A [] PROGMEM = "Exit menu, tune so that the\ndial displays the desired freq,\nthen tune here until the\nsignal is zerobeat";
const char SS_LOCAL_OSC_A [] PROGMEM = "Tune so that the dial displays\na known freq, then tune here\nuntil the signal is zerobeat";
const SettingScreen_t ssLocalOsc PROGMEM = {
SS_LOCAL_OSC_T,
SS_LOCAL_OSC_A,
@ -186,7 +219,7 @@ const SettingScreen_t ssLocalOsc PROGMEM = {
ssLocalOscChange,
ssLocalOscFinalize
};
void runLocalOscSetting(){runSetting(&ssLocalOsc);}
void runLocalOscSetting(){activateSetting(&ssLocalOsc);}
//BFO
void ssBfoInitialize(long int* start_value_out){
@ -203,7 +236,7 @@ void ssBfoChange(const long int new_value, char* buff_out, const size_t buff_out
setFrequency(GetActiveVfoFreq());
si5351bx_setfreq(0, new_value);
formatFreq(new_value,buff_out,buff_out_size);
strncat_P(buff_out,(const char*)F("Hz"),buff_out_size - strlen(buff_out));
strncat_P(buff_out,strHz,buff_out_size - strlen(buff_out));
}
void ssBfoFinalize(const long int final_value)
{
@ -213,7 +246,7 @@ void ssBfoFinalize(const long int final_value)
setFrequency(GetActiveVfoFreq());
}
const char SS_BFO_T [] PROGMEM = "Beat Frequency Osc (BFO)";
const char SS_BFO_A [] PROGMEM = "Exit menu, tune to an unused\nfrequency, then tune here\nuntil the audio is between\n300-3000Hz";
const char SS_BFO_A [] PROGMEM = "Tune until the audio is\nbetween 300-3000Hz";
const SettingScreen_t ssBfo PROGMEM = {
SS_BFO_T,
SS_BFO_A,
@ -224,39 +257,7 @@ const SettingScreen_t ssBfo PROGMEM = {
ssBfoChange,
ssBfoFinalize
};
void runBfoSetting(){runSetting(&ssBfo);}
//CW Speed
void ssCwSpeedInitialize(long int* start_value_out)
{
*start_value_out = 1200L/globalSettings.cwDitDurationMs;
}
void ssCwSpeedValidate(const long int candidate_value_in, long int* validated_value_out)
{
*validated_value_out = LIMIT(candidate_value_in,1,100);
}
void ssCwSpeedChange(const long int new_value, char* buff_out, const size_t /*buff_out_size*/)
{
ltoa(new_value, buff_out, 10);
}
void ssCwSpeedFinalize(const long int final_value)
{
globalSettings.cwDitDurationMs = 1200L/final_value;
SaveSettingsToEeprom();
}
const char SS_CW_SPEED_T [] PROGMEM = "CW Play Speed";
const char SS_CW_SPEED_A [] PROGMEM = "Select speed to play CW\ncharacters";
const SettingScreen_t ssCwSpeed PROGMEM = {
SS_CW_SPEED_T,
SS_CW_SPEED_A,
5,
1,
ssCwSpeedInitialize,
ssCwSpeedValidate,
ssCwSpeedChange,
ssCwSpeedFinalize
};
void runCwSpeedSetting(){runSetting(&ssCwSpeed);}
void runBfoSetting(){activateSetting(&ssBfo);}
//CW Tone
void ssCwToneInitialize(long int* start_value_out)
@ -270,17 +271,17 @@ void ssCwToneValidate(const long int candidate_value_in, long int* validated_val
void ssCwToneChange(const long int new_value, char* buff_out, const size_t buff_out_size)
{
globalSettings.cwSideToneFreq = new_value;
tone(CW_TONE, globalSettings.cwSideToneFreq);
toneAC2(PIN_CW_TONE, globalSettings.cwSideToneFreq);
ltoa(globalSettings.cwSideToneFreq,buff_out,10);
strncat_P(buff_out,(const char*)F("Hz"),buff_out_size - strlen(buff_out));
strncat_P(buff_out,strHz,buff_out_size - strlen(buff_out));
}
void ssCwToneFinalize(const long int final_value)
{
noTone(CW_TONE);
noToneAC2();
globalSettings.cwSideToneFreq = final_value;
SaveSettingsToEeprom();
}
const char SS_CW_TONE_T [] PROGMEM = "CW Tone Frequency";
const char SS_CW_TONE_T [] PROGMEM = "Tone";
const char SS_CW_TONE_A [] PROGMEM = "Select a frequency that\nCW mode to tune for";
const SettingScreen_t ssTone PROGMEM = {
SS_CW_TONE_T,
@ -292,7 +293,7 @@ const SettingScreen_t ssTone PROGMEM = {
ssCwToneChange,
ssCwToneFinalize
};
void runToneSetting(){runSetting(&ssTone);}
void runToneSetting(){activateSetting(&ssTone);}
//CW Switch Delay
void ssCwSwitchDelayInitialize(long int* start_value_out)
@ -307,14 +308,16 @@ void ssCwSwitchDelayChange(const long int new_value, char* buff_out, const size_
{
ltoa(new_value,buff_out,10);
strncat_P(buff_out,(const char*)F("ms"),buff_out_size - strlen(buff_out));
morseText(buff_out);
enc_read();//Consume any rotations during morse playback
}
void ssCwSwitchDelayFinalize(const long int final_value)
{
globalSettings.cwActiveTimeoutMs = final_value;
SaveSettingsToEeprom();
}
const char SS_CW_SWITCH_T [] PROGMEM = "CW Tx -> Rx Switch Delay";
const char SS_CW_SWITCH_A [] PROGMEM = "Select how long the radio\nshould wait before switching\nbetween TX and RX when in\nCW mode";
const char SS_CW_SWITCH_T [] PROGMEM = "Tx to Rx Delay";
const char SS_CW_SWITCH_A [] PROGMEM = "How long to wait before\nswitching from TX to RX when\nin CW mode";
const SettingScreen_t ssCwSwitchDelay PROGMEM = {
SS_CW_SWITCH_T,
SS_CW_SWITCH_A,
@ -325,7 +328,7 @@ const SettingScreen_t ssCwSwitchDelay PROGMEM = {
ssCwSwitchDelayChange,
ssCwSwitchDelayFinalize
};
void runCwSwitchDelaySetting(){runSetting(&ssCwSwitchDelay);}
void runCwSwitchDelaySetting(){activateSetting(&ssCwSwitchDelay);}
//CW Keyer
void ssKeyerInitialize(long int* start_value_out)
@ -338,23 +341,29 @@ void ssKeyerValidate(const long int candidate_value_in, long int* validated_valu
}
void ssKeyerChange(const long int new_value, char* buff_out, const size_t buff_out_size)
{
char m;
if(KeyerMode_e::KEYER_STRAIGHT == new_value){
strncpy_P(buff_out,(const char*)F("< Hand Key >"),buff_out_size);
strncpy_P(buff_out,(const char*)F("Hand Key"),buff_out_size);
m = 'S';
}
else if(KeyerMode_e::KEYER_IAMBIC_A == new_value){
strncpy_P(buff_out,(const char*)F("< Iambic A >"),buff_out_size);
strncpy_P(buff_out,(const char*)F("Iambic A"),buff_out_size);
m = 'A';
}
else{
strncpy_P(buff_out,(const char*)F("< Iambic B >"),buff_out_size);
strncpy_P(buff_out,(const char*)F("Iambic B"),buff_out_size);
m = 'B';
}
morseLetter(m);
enc_read();//Consume any rotations during morse playback
}
void ssKeyerFinalize(const long int final_value)
{
globalSettings.keyerMode = (KeyerMode_e)final_value;
SaveSettingsToEeprom();
}
const char SS_KEYER_T [] PROGMEM = "CW Keyer/Paddle Type";
const char SS_KEYER_A [] PROGMEM = "Select which type of\nkeyer/paddle is being used";
const char SS_KEYER_T [] PROGMEM = "Keyer Type";
const char SS_KEYER_A [] PROGMEM = "Select which type of keyer\nor paddle is being used";
const SettingScreen_t ssKeyer PROGMEM = {
SS_KEYER_T,
SS_KEYER_A,
@ -365,7 +374,83 @@ const SettingScreen_t ssKeyer PROGMEM = {
ssKeyerChange,
ssKeyerFinalize
};
void runKeyerSetting(){runSetting(&ssKeyer);}
void runKeyerSetting(){activateSetting(&ssKeyer);}
//Morse menu playback
void ssMorseMenuInitialize(long int* start_value_out)
{
*start_value_out = globalSettings.morseMenuOn;
}
void ssMorseMenuValidate(const long int candidate_value_in, long int* validated_value_out)
{
*validated_value_out = LIMIT(candidate_value_in,0,1);
}
void ssMorseMenuChange(const long int new_value, char* buff_out, const size_t buff_out_size)
{
char m;
if(new_value){
strncpy_P(buff_out,strYes,buff_out_size);
m = 'Y';
}
else{
strncpy_P(buff_out,strNo,buff_out_size);
m = 'N';
}
morseLetter(m);
enc_read();//Consume any rotations during morse playback
}
void ssMorseMenuFinalize(const long int final_value)
{
globalSettings.morseMenuOn = final_value;
SaveSettingsToEeprom();
}
const char SS_MORSE_MENU_T [] PROGMEM = "Menu Audio";
const char SS_MORSE_MENU_A [] PROGMEM = "Menu selections will play\nmorse code";
const SettingScreen_t ssMorseMenu PROGMEM = {
SS_MORSE_MENU_T,
SS_MORSE_MENU_A,
10,
1,
ssMorseMenuInitialize,
ssMorseMenuValidate,
ssMorseMenuChange,
ssMorseMenuFinalize
};
void runMorseMenuSetting(){activateSetting(&ssMorseMenu);}
//CW Speed
void ssCwSpeedInitialize(long int* start_value_out)
{
*start_value_out = 1200L/globalSettings.cwDitDurationMs;
}
void ssCwSpeedValidate(const long int candidate_value_in, long int* validated_value_out)
{
*validated_value_out = LIMIT(candidate_value_in,1,100);
}
void ssCwSpeedChange(const long int new_value, char* buff_out, const size_t /*buff_out_size*/)
{
ltoa(new_value, buff_out, 10);
morseText(buff_out,1200L/new_value);
enc_read();//Consume any rotations during morse playback
}
void ssCwSpeedFinalize(const long int final_value)
{
globalSettings.cwDitDurationMs = 1200L/final_value;
SaveSettingsToEeprom();
}
const char SS_CW_SPEED_T [] PROGMEM = "Play Speed";
const char SS_CW_SPEED_A [] PROGMEM = "Speed to play CW characters";
const SettingScreen_t ssCwSpeed PROGMEM = {
SS_CW_SPEED_T,
SS_CW_SPEED_A,
5,
1,
ssCwSpeedInitialize,
ssCwSpeedValidate,
ssCwSpeedChange,
ssCwSpeedFinalize
};
void runCwSpeedSetting(){activateSetting(&ssCwSpeed);}
//Reset all settings
void ssResetAllInitialize(long int* start_value_out)
@ -378,12 +463,17 @@ void ssResetAllValidate(const long int candidate_value_in, long int* validated_v
}
void ssResetAllChange(const long int new_value, char* buff_out, const size_t buff_out_size)
{
char m;
if(new_value){
strncpy_P(buff_out,(const char*)F("Yes"),buff_out_size);
strncpy_P(buff_out,strYes,buff_out_size);
m = 'Y';
}
else{
strncpy_P(buff_out,(const char*)F("No"),buff_out_size);
strncpy_P(buff_out,strNo,buff_out_size);
m = 'N';
}
morseLetter(m);
enc_read();//Consume any rotations during morse playback
}
void ssResetAllFinalize(const long int final_value)
{
@ -393,8 +483,8 @@ void ssResetAllFinalize(const long int final_value)
setup();
}
}
const char SS_RESET_ALL_T [] PROGMEM = "Reset All Cals/Settings";
const char SS_RESET_ALL_A [] PROGMEM = "WARNING: Selecting \"Yes\"\nwill reset all calibrations and\nsettings to their default\nvalues";
const char SS_RESET_ALL_T [] PROGMEM = "Reset All";
const char SS_RESET_ALL_A [] PROGMEM = "Resets all calibrations and\nsettings to their default\nvalues";
const SettingScreen_t ssResetAll PROGMEM = {
SS_RESET_ALL_T,
SS_RESET_ALL_A,
@ -405,43 +495,87 @@ const SettingScreen_t ssResetAll PROGMEM = {
ssResetAllChange,
ssResetAllFinalize
};
void runResetAllSetting(){runSetting(&ssResetAll);}
void runResetAllSetting(){activateSetting(&ssResetAll);}
struct MenuItem_t {
const char* const ItemName;
void (*OnSelect)();
};
void runMenu(const MenuItem_t* const menu_items, const uint16_t num_items);
#define RUN_MENU(menu) runMenu(menu,sizeof(menu)/sizeof(menu[0]))
void initSetupMenu(const MenuItem_t* const menu_items,
const uint16_t num_items);
MenuReturn_e runSetupMenu(const MenuItem_t* const menu_items,
const uint16_t num_items,
const ButtonPress_e tuner_button,
const ButtonPress_e touch_button,
const Point touch_point,
const int16_t knob);
#define GENERATE_MENU_T(menu_name) \
void initSetupMenu##menu_name(void)\
{\
initSetupMenu(menuItems##menu_name,sizeof(menuItems##menu_name)/sizeof(menuItems##menu_name[0]));\
}\
MenuReturn_e runSetupMenu##menu_name(const ButtonPress_e tuner_button,\
const ButtonPress_e touch_button,\
const Point touch_point,\
const int16_t knob)\
{\
return runSetupMenu(menuItems##menu_name,\
sizeof(menuItems##menu_name)/sizeof(menuItems##menu_name[0]),\
tuner_button,\
touch_button,\
touch_point,\
knob\
);\
}\
Menu_t setupMenu##menu_name = {\
initSetupMenu##menu_name,\
runSetupMenu##menu_name,\
nullptr\
};\
void run##menu_name##Menu(void)\
{\
enterSubmenu(&setupMenu##menu_name);\
}\
const char MI_TOUCH [] PROGMEM = "Touch Screen";
void setupTouchSetting();
const char MT_CAL [] PROGMEM = "Calibrations";
const char MI_TOUCH [] PROGMEM = "Touch Screen";
const MenuItem_t calibrationMenu [] PROGMEM {
const MenuItem_t menuItemsCalibration [] PROGMEM {
{MT_CAL,nullptr},//Title
{SS_LOCAL_OSC_T,runLocalOscSetting},
{SS_BFO_T,runBfoSetting},
{MI_TOUCH,setupTouch},
{MI_TOUCH,setupTouchSetting},
};
void runCalibrationMenu(){RUN_MENU(calibrationMenu);}
GENERATE_MENU_T(Calibration);
const char MT_CW [] PROGMEM = "CW/Morse Setup";
const MenuItem_t cwMenu [] PROGMEM {
void setupTouchSetting(){
setupTouch();
initSetupMenuCalibration();
}
const char MT_CW [] PROGMEM = "CW Setup";
const MenuItem_t menuItemsCw [] PROGMEM {
{MT_CW,nullptr},//Title
{SS_CW_SPEED_T,runCwSpeedSetting},
{SS_CW_TONE_T,runToneSetting},
{SS_CW_SWITCH_T,runCwSwitchDelaySetting},
{SS_KEYER_T,runKeyerSetting},
{SS_MORSE_MENU_T,runMorseMenuSetting},
{SS_CW_SPEED_T,runCwSpeedSetting},
};
void runCwMenu(){RUN_MENU(cwMenu);}
GENERATE_MENU_T(Cw);
const char MT_SETTINGS [] PROGMEM = "Settings";
const MenuItem_t mainMenu [] PROGMEM {
const MenuItem_t menuItemsSetupRoot [] PROGMEM {
{MT_SETTINGS,nullptr},//Title
{MT_CAL,runCalibrationMenu},
{MT_CW,runCwMenu},
{SS_RESET_ALL_T,runResetAllSetting},
};
GENERATE_MENU_T(SetupRoot);
Menu_t *const setupMenu = &setupMenuSetupRoot;
const char MI_EXIT [] PROGMEM = "Exit";
const MenuItem_t exitMenu PROGMEM = {MI_EXIT,nullptr};
@ -456,14 +590,15 @@ void drawMenu(const MenuItem_t* const items, const uint16_t num_items)
for(unsigned int i = 1; i < num_items; ++i){
memcpy_P(&mi,&items[i],sizeof(mi));
strncpy_P(b,mi.ItemName,sizeof(b));
displayText(b, LAYOUT_ITEM_X, LAYOUT_ITEM_Y + (i-1)*LAYOUT_ITEM_PITCH_Y, LAYOUT_ITEM_WIDTH, LAYOUT_ITEM_HEIGHT, COLOR_TEXT, COLOR_BACKGROUND, COLOR_INACTIVE_BORDER);
displayText(b, LAYOUT_ITEM_X, LAYOUT_ITEM_Y + (i-1)*LAYOUT_ITEM_PITCH_Y, LAYOUT_ITEM_WIDTH, LAYOUT_ITEM_HEIGHT, COLOR_TEXT, COLOR_BACKGROUND, COLOR_INACTIVE_BORDER, TextJustification_e::Left);
}
memcpy_P(&mi,&exitMenu,sizeof(mi));
strncpy_P(b,mi.ItemName,sizeof(b));
displayText(b, LAYOUT_ITEM_X, LAYOUT_ITEM_Y + (num_items-1)*LAYOUT_ITEM_PITCH_Y, LAYOUT_ITEM_WIDTH, LAYOUT_ITEM_HEIGHT, COLOR_TEXT, COLOR_BACKGROUND, COLOR_INACTIVE_BORDER);
displayText(b, LAYOUT_ITEM_X, LAYOUT_ITEM_Y + (num_items-1)*LAYOUT_ITEM_PITCH_Y, LAYOUT_ITEM_WIDTH, LAYOUT_ITEM_HEIGHT, COLOR_TEXT, COLOR_BACKGROUND, COLOR_INACTIVE_BORDER, TextJustification_e::Left);
}
void movePuck(unsigned int old_index, unsigned int new_index)
void movePuck(unsigned int old_index,
unsigned int new_index)
{
//Don't update if we're already on the right selection
if(old_index == new_index){
@ -477,66 +612,57 @@ void movePuck(unsigned int old_index, unsigned int new_index)
displayRect(LAYOUT_ITEM_X, LAYOUT_ITEM_Y + (new_index*LAYOUT_ITEM_PITCH_Y), LAYOUT_ITEM_WIDTH, LAYOUT_ITEM_HEIGHT, COLOR_ACTIVE_BORDER);
}
void runMenu(const MenuItem_t* const menu_items, const uint16_t num_items)
int16_t setupMenuSelector = 0;
void initSetupMenu(const MenuItem_t* const menu_items,
const uint16_t num_items)
{
static const unsigned int COUNTS_PER_ITEM = 10;
const int MAX_KNOB_VALUE = num_items*COUNTS_PER_ITEM - 1;
int knob_sum = 0;
unsigned int old_index = 0;
drawMenu(menu_items,num_items);
movePuck(1,0);//Force draw of puck
//wait for the button to be raised up
while(btnDown()){
active_delay(50);
}
active_delay(50); //debounce
while (true){
knob_sum += enc_read();
if(knob_sum < 0){
knob_sum = 0;
}
else if(MAX_KNOB_VALUE < knob_sum){
knob_sum = MAX_KNOB_VALUE;
}
uint16_t index = knob_sum/COUNTS_PER_ITEM;
movePuck(old_index,index);
old_index = index;
if (!btnDown()){
active_delay(50);
continue;
}
//wait for the touch to lift off and debounce
while(btnDown()){
active_delay(50);
}
active_delay(50);//debounce
if(num_items-1 > index){
MenuItem_t mi = {"",nullptr};
memcpy_P(&mi,&menu_items[index+1],sizeof(mi));//The 0th element in the array is the title, so offset by 1
mi.OnSelect();
drawMenu(menu_items,num_items);//Need to re-render, since whatever ran just now is assumed to have drawn something
old_index = -1;//Force redraw
}
else{
break;
}
}
//debounce the button
while(btnDown()){
active_delay(50);
}
active_delay(50);//debounce
setupMenuSelector = 0;
movePuck(-1,0);//Force draw of puck
}
void doSetup2(){
RUN_MENU(mainMenu);
guiUpdate();
MenuReturn_e runSetupMenu(const MenuItem_t* const menu_items,
const uint16_t num_items,
const ButtonPress_e tuner_button,
const ButtonPress_e touch_button,
const Point touch_point,
const int16_t knob)
{
const int16_t cur_index = setupMenuSelector/MENU_KNOB_COUNTS_PER_ITEM;
const int16_t exit_index = num_items - 1;
if(ButtonPress_e::NotPressed != tuner_button){
//Don't care what kind of press
if(exit_index <= cur_index){
return MenuReturn_e::ExitedRedraw;
}
MenuItem_t mi = {"",nullptr};
memcpy_P(&mi,&menu_items[cur_index+1],sizeof(mi));//The 0th element in the array is the title, so offset by 1
mi.OnSelect();
}
(void)touch_button;(void)touch_point;//TODO: handle touch input?
if(0 != knob){
setupMenuSelector = LIMIT(setupMenuSelector + knob,0,(int16_t)(num_items*MENU_KNOB_COUNTS_PER_ITEM - 1));
const int16_t new_index = setupMenuSelector/MENU_KNOB_COUNTS_PER_ITEM;
if(cur_index != new_index){
movePuck(cur_index,new_index);
if(globalSettings.morseMenuOn){//Only spend cycles copying menu item into RAM if we actually need to
if(exit_index <= cur_index){
strncpy_P(b,MI_EXIT,sizeof(b));
}
else{
MenuItem_t mi = {"",nullptr};
memcpy_P(&mi,&menu_items[cur_index+1],sizeof(mi));//The 0th element in the array is the title, so offset by 1
strncpy_P(b,mi.ItemName,sizeof(b));
}
morseText(b);
}
}
}
return MenuReturn_e::StillActive;
}

View File

@ -1,6 +1,9 @@
#pragma once
void doSetup2(); //main setup function, displays the setup menu, calls various dialog boxes
#include "menu.h"
extern Menu_t* const setupMenu;
void setupTouch();
void runLocalOscSetting();
void runBfoSetting();

View File

@ -1,7 +1,7 @@
#include <Arduino.h>
#include <Wire.h>
#include "settings.h"
#include "ubitx.h"
#include "si5351.h"
// ************* SI5315 routines - tks Jerry Gaffke, KE7ER ***********************
@ -85,7 +85,7 @@ void si5351bx_init() { // Call once at power-up, start PLLA
void si5351bx_setfreq(uint8_t clknum, uint32_t fout) { // Set a CLK to fout Hz
uint32_t msa, msb, msc, msxp1, msxp2, msxp3p2top;
if ((fout < 500000) || (fout > 109000000)) // If clock freq out of range
if ((fout < SI5351_MIN_FREQUENCY_HZ) || (fout > SI5351_MAX_FREQUENCY_HZ)) // If clock freq out of range
si5351bx_clken |= 1 << clknum; // shut down the clock
else {
msa = si5351bx_vcoa / fout; // Integer part of vco/fout

10
si5351.h Normal file
View File

@ -0,0 +1,10 @@
#pragma once
#include <stdint.h>
static const uint32_t SI5351_MIN_FREQUENCY_HZ = 500000;
static const uint32_t SI5351_MAX_FREQUENCY_HZ = 109000000;
void initOscillators();
void si5351bx_setfreq(uint8_t clknum, uint32_t fout);
void si5351_set_calibration(int32_t cal); //calibration is a small value that is nudged to make up for the inaccuracies of the reference 25 MHz crystal frequency

View File

@ -0,0 +1,27 @@
// ---------------------------------------------------------------------------
// Be sure to include an in-line 100 ohm resistor on one pin as you normally do when connecting a piezo or speaker.
// ---------------------------------------------------------------------------
#include <toneAC2.h>
// Melody liberated from the toneMelody Arduino example sketch by Tom Igoe.
int melody[] = { 262, 196, 196, 220, 196, 0, 247, 262 };
int noteDurations[] = { 4, 8, 8, 4, 4, 4, 4, 4 };
void setup() {} // Nothing to setup, just start playing!
void loop() {
for (unsigned long freq = 125; freq <= 15000; freq += 10) {
toneAC2(2, 3, freq, 1); // Play the frequency (125 Hz to 15 kHz sweep in 10 Hz steps) for 1ms.
}
delay(1000); // Wait a second.
for (int thisNote = 0; thisNote < 8; thisNote++) {
int noteDuration = 1000/noteDurations[thisNote];
toneAC2(2, 3, melody[thisNote], noteDuration, true); // Play thisNote at full volume for noteDuration in the background.
delay(noteDuration * 4 / 3); // Wait while the tone plays in the background, plus another 33% delay between notes.
}
while(1); // Stop (so it doesn't repeat forever driving you crazy--you're welcome).
}

18
toneAC2/keywords.txt Normal file
View File

@ -0,0 +1,18 @@
###################################
# Syntax Coloring Map For toneAC2
###################################
###################################
# Datatypes (KEYWORD1)
###################################
###################################
# Methods and Functions (KEYWORD2)
###################################
toneAC2 KEYWORD2
noToneAC2 KEYWORD2
###################################
# Constants (LITERAL1)
###################################

56
toneAC2/toneAC2.cpp Normal file
View File

@ -0,0 +1,56 @@
// ---------------------------------------------------------------------------
// Created by Tim Eckel - teckel@leethost.com
// Copyright 2015 License: GNU GPL v3 http://www.gnu.org/licenses/gpl-3.0.html
//
// See "toneAC2.h" for purpose, syntax, version history, links, and more.
// ---------------------------------------------------------------------------
#include "toneAC2.h"
unsigned long _tAC2_time; // Used to track end note with timer when playing note in the background.
volatile uint8_t *_pinMode1; // Pin modes.
uint8_t _pinMask1 = 0; // Bitmask for pins.
volatile uint8_t *_pinOutput1; // Output port registers for each pin.
int _tAC2_prescale[] = { 2, 16, 64, 128, 256, 512, 2048 }; // Prescaler.
void toneAC2(uint8_t pin1, unsigned int frequency, unsigned long length, uint8_t background) {
long top;
uint8_t prescaler;
for (prescaler = 1; prescaler < 8; prescaler++) { // Find the appropriate prescaler
top = F_CPU / (long) frequency / (long) _tAC2_prescale[prescaler - 1] - 1; // Calculate the top.
if (top < 256) break; // Fits, break out of for loop.
}
if (top > 255) { noToneAC2(); return; } // Frequency is out of range, turn off sound and return.
if (length > 0) _tAC2_time = millis() + length - 1; else _tAC2_time = 0xFFFFFFFF; // Set when the note should end, or play "forever".
if (_pinMask1 == 0) { // This gets the port registers and bitmaps for the two pins and sets the pins to output mode.
_pinMask1 = digitalPinToBitMask(pin1); // Get the port register bitmask for pin 1.
_pinOutput1 = portOutputRegister(digitalPinToPort(pin1)); // Get the output port register for pin 1.
_pinMode1 = (uint8_t *) portModeRegister(digitalPinToPort(pin1)); // Get the port mode register for pin 1.
*_pinMode1 |= _pinMask1; // Set pin 1 to Output mode.
}
OCR2A = top; // Set the top.
if (TCNT2 > top) TCNT2 = top; // Counter over the top, put within range.
TCCR2B = _BV(WGM22) | prescaler; // Set Fast PWM and prescaler.
TCCR2A = _BV(WGM20) | _BV(WGM21); // Fast PWM and normal port operation, OC2A/OC2B disconnected.
TIMSK2 &= ~_BV(OCIE2A); // Stop timer 2 interrupt while we set the pin states.
TIMSK2 |= _BV(OCIE2A); // Activate the timer interrupt.
if (length > 0 && !background) { delay(length); noToneAC2(); } // Just a simple delay, doesn't return control till finished.
}
void noToneAC2() {
TIMSK2 &= ~_BV(OCIE2A); // Remove the timer interrupt.
TCCR2B = _BV(CS22); // Default clock prescaler of 64.
TCCR2A = _BV(WGM20); // Set to defaults so PWM can work like normal (PWM, phase corrected, 8bit).
*_pinMode1 &= ~_pinMask1; // Set pin 1 to INPUT.
_pinMask1 = 0; // Flag so we know note is no longer playing.
}
ISR(TIMER2_COMPA_vect) { // Timer interrupt vector.
if (millis() > _tAC2_time) noToneAC2(); // Check to see if it's time for the note to end.
*_pinOutput1 ^= _pinMask1; // Toggle the pin 1 state.
}

73
toneAC2/toneAC2.h Normal file
View File

@ -0,0 +1,73 @@
// ---------------------------------------------------------------------------
// toneAC2 Library - v1.1 - 09/15/2015
//
// AUTHOR/LICENSE:
// Created by Tim Eckel - teckel@leethost.com
// Copyright 2015 License: GNU GPL v3 http://www.gnu.org/licenses/gpl-3.0.html
//
// LINKS:
// Project home: https://bitbucket.org/teckel12/arduino-toneac/wiki/Home
// Blog: http://forum.arduino.cc/index.php?topic=142097.0
//
// DISCLAIMER:
// This software is furnished "as is", without technical support, and with no
// warranty, express or implied, as to its usefulness for any purpose.
//
// PURPOSE:
// Replacement to the standard tone library with the advantage of nearly twice
// the volume, 800 bytes smaller compiled code size, and less stress on the
// speaker. This alternate version uses timer 2 and allows for flexible pin
// assignment. The primary version (toneAC) allows for higher frequencies,
// higher quality, and even smaller code size. However, toneAC is fixed to
// using the PWM timer 1 pins unlike toneAC2 which can use any two pins. Both
// exclusively use port registers for the fast and smallest code possible.
//
// USAGE:
// Connection is very similar to a piezo or standard speaker. Except, instead
// of connecting one speaker wire to ground you connect both speaker wires to
// Arduino pins. Unlike toneAC, with toneAC2 you can connect to any two pins.
// Just as usual when connecting a speaker, make sure you add an in-line 100
// ohm resistor between one of the pins and the speaker wire.
//
// SYNTAX:
// toneAC2( pin1, pin2, frequency [, length [, background ]] ) - Play a note.
// Parameters:
// * pin1 - Pin to attach one of the speaker wires.
// * pin2 - Pin to attach the other speaker wire.
// * frequency - Play the specified frequency indefinitely, turn off with noToneAC2().
// * length - [optional] Set the length to play in milliseconds. (default: 0 [forever], range: 0 to 2^32-1)
// * background - [optional] Play note in background or pause till finished? (default: false, values: true/false)
// noToneAC2() - Stop playing.
//
// HISTORY:
// 09/15/2015 v1.1 - Fix a potential race condition with _tAC2_time. Moved
// development to Bitbucket.
//
// 01/27/2013 v1.0 - Initial release.
//
// ---------------------------------------------------------------------------
#ifndef toneAC2_h
#define toneAC2_h
#if defined(ARDUINO) && ARDUINO >= 100
#include <Arduino.h>
#else
#include <WProgram.h>
#endif
// This doesn't currently work. Would require more work than simply doing this.
#if defined(__AVR_ATmega8__) || defined(__AVR_ATmega128__)
#define TCCR2A TCCR2
#define TCCR2B TCCR2
#define TIMSK2 TIMSK
#define COM2A1 COM21
#define COM2A0 COM20
#define OCIE2A OCIE2
#define OCR2A OCR2
#define TIMER2_COMPA_vect TIMER2_COMP_vect
#endif
void toneAC2(uint8_t pin1, unsigned int frequency = 0, unsigned long length = 0, uint8_t background = false);
void noToneAC2();
#endif

1
toneAC2Proxy.cpp Normal file
View File

@ -0,0 +1 @@
#include "toneAC2/toneAC2.cpp"

139
touch.cpp Normal file
View File

@ -0,0 +1,139 @@
#include "touch.h"
#include <SPI.h>
#include "pin_definitions.h"
#include "settings.h"
constexpr int16_t Z_THRESHOLD = 400;
constexpr uint8_t MSEC_THRESHOLD = 3;//Max sample rate is 125kHz, but we'll limit ourselves conservatively
constexpr uint8_t START_COMMAND = 1 << 7;
constexpr uint8_t CHANNEL_Y = 1 << 4;
constexpr uint8_t CHANNEL_Z1 = 3 << 4;
constexpr uint8_t CHANNEL_Z2 = 4 << 4;
constexpr uint8_t CHANNEL_X = 5 << 4;
constexpr uint8_t CHANNEL_TEMPERATURE = 7 << 4;
constexpr uint8_t USE_8_INSTEAD_OF_12_BIT = 1 << 3;
constexpr uint8_t USE_SINGLE_ENDED_MEASUREMENT = 1 << 2;
constexpr uint8_t POWER_OFF = 0 << 0;
constexpr uint8_t POWER_ADC = 1 << 0;
constexpr uint8_t POWER_REF = 2 << 0;
constexpr uint8_t POWER_ADC_REF = 3 << 0;
constexpr uint16_t MEASURE_X = START_COMMAND | POWER_ADC | CHANNEL_Y;//X and Y channel labelling flip due to screen orientation
constexpr uint16_t MEASURE_Y = START_COMMAND | POWER_ADC | CHANNEL_X;//X and Y channel labelling flip due to screen orientation
constexpr uint16_t MEASURE_Z1 = START_COMMAND | POWER_ADC | CHANNEL_Z1;
constexpr uint16_t MEASURE_Z2 = START_COMMAND | POWER_ADC | CHANNEL_Z2;
constexpr uint8_t RAW_READ_TO_12BIT_VALUE_SHIFT = 3;//16 bits read, zero-padded, but the MSB of the 16 is where the "BUSY" signal is asserted, so only need to shift by 3 instead of 4
uint32_t msraw=0x80000000;
int16_t xraw=0, yraw=0, zraw=0;
constexpr uint8_t rotation = 1;
SPISettings spiSettingsTouch(2000000,MSBFIRST,SPI_MODE0);
int16_t touch_besttwoavg( int16_t x , int16_t y , int16_t z ) {
int16_t da, db, dc;
int16_t reta = 0;
if ( x > y ) da = x - y; else da = y - x;
if ( x > z ) db = x - z; else db = z - x;
if ( z > y ) dc = z - y; else dc = y - z;
if ( da <= db && da <= dc ) reta = (x + y) >> 1;
else if ( db <= da && db <= dc ) reta = (x + z) >> 1;
else reta = (y + z) >> 1; // else if ( dc <= da && dc <= db ) reta = (x + y) >> 1;
return (reta);
}
uint16_t touchReadChannel(uint8_t channel_command){
//We assume that SPI.beginTransaction has already been called, and CS is LOW
SPI.transfer(channel_command);//Throw away any bytes here
const uint16_t tmpH = SPI.transfer(0) & 0x7F;//Leading 0 (during "busy" signal), followed by bits 11-5
const uint16_t tmpL = SPI.transfer(0);//Bits 4-0, followed by 0s
return tmpH << 5 | tmpL >> 3;
}
void touch_update(){
uint32_t now = millis();
if (now - msraw < MSEC_THRESHOLD){
return;
}
SPI.beginTransaction(spiSettingsTouch);
digitalWrite(PIN_TOUCH_CS, LOW);
int16_t z1 = touchReadChannel(MEASURE_Z1);//~0 when not pressed, increases with pressure
int32_t z = z1;
int16_t z2 = touchReadChannel(MEASURE_Z2);//~4095 when not pressed, decreases with pressure
z += (4095 - z2);
//Serial.print(F("z1:"));Serial.print(z1);Serial.print(F(" z2:"));Serial.print(z2);Serial.print(F(" z:"));Serial.println(z);
zraw = z;
if (zraw < Z_THRESHOLD) {//Don't bother reading x/y if we're not being touched
digitalWrite(PIN_TOUCH_CS, HIGH);
SPI.endTransaction();
return;
}
// make 3 x-y measurements
int16_t data[6];
data[0] = touchReadChannel(MEASURE_X);
data[1] = touchReadChannel(MEASURE_Y);
data[2] = touchReadChannel(MEASURE_X);
data[3] = touchReadChannel(MEASURE_Y);
data[4] = touchReadChannel(MEASURE_X);
data[5] = touchReadChannel(MEASURE_Y & ~POWER_ADC_REF);//Turn off sensor
digitalWrite(PIN_TOUCH_CS, HIGH);
SPI.endTransaction();
int16_t x = touch_besttwoavg( data[0], data[2], data[4] );
int16_t y = touch_besttwoavg( data[1], data[3], data[5] );
msraw = now; // good read completed, set wait
switch (rotation) {
case 0:
xraw = 4095 - y;
yraw = x;
break;
case 1:
xraw = x;
yraw = y;
break;
case 2:
xraw = y;
yraw = 4095 - x;
break;
default: // 3
xraw = 4095 - x;
yraw = 4095 - y;
}
}
void initTouch(){
pinMode(PIN_TOUCH_CS, OUTPUT);
digitalWrite(PIN_TOUCH_CS, HIGH);
}
bool readTouch(Point *const touch_point_out){
touch_update();
//Serial.print(F("readTouch found zraw of "));Serial.println(zraw);
if (zraw >= Z_THRESHOLD) {
touch_point_out->x = xraw;
touch_point_out->y = yraw;
//Serial.print(ts_point.x); Serial.print(",");Serial.println(ts_point.y);
return true;
}
return false;
}
void scaleTouch(Point *const touch_point_in_out){
touch_point_in_out->x = ((long)(touch_point_in_out->x - globalSettings.touchOffsetX) * 10L)/ (long)globalSettings.touchSlopeX;
touch_point_in_out->y = ((long)(touch_point_in_out->y - globalSettings.touchOffsetY) * 10L)/ (long)globalSettings.touchSlopeY;
//Serial.print(p->x); Serial.print(",");Serial.println(p->y);
}

11
touch.h Normal file
View File

@ -0,0 +1,11 @@
#pragma once
#include "point.h"
void initTouch();
//Returns true if touched, false otherwise
bool readTouch(Point *const touch_point_out);
//Applies the touch calibration the point passed in
void scaleTouch(Point *const touch_point_in_out);

217
tuner.cpp Normal file
View File

@ -0,0 +1,217 @@
#include "tuner.h"
#include <Arduino.h>
#include "nano_gui.h"
#include "pin_definitions.h"
#include "si5351.h"
static const uint32_t THRESHOLD_USB_LSB = 10000000L;
void saveVFOs()
{
SaveSettingsToEeprom();
}
void switchVFO(Vfo_e new_vfo){
ritDisable();//If we are in RIT mode, we need to disable it before setting the active VFO so that the correct VFO gets it's frequency restored
globalSettings.activeVfo = new_vfo;
setFrequency(GetActiveVfoFreq());
saveVFOs();
}
/**
* Select the properly tx harmonic filters
* The four harmonic filters use only three relays
* the four LPFs cover 30-21 Mhz, 18 - 14 Mhz, 7-10 MHz and 3.5 to 5 Mhz
* Briefly, it works like this,
* - When KT1 is OFF, the 'off' position routes the PA output through the 30 MHz LPF
* - When KT1 is ON, it routes the PA output to KT2. Which is why you will see that
* the KT1 is on for the three other cases.
* - When the KT1 is ON and KT2 is off, the off position of KT2 routes the PA output
* to 18 MHz LPF (That also works for 14 Mhz)
* - When KT1 is On, KT2 is On, it routes the PA output to KT3
* - KT3, when switched on selects the 7-10 Mhz filter
* - KT3 when switched off selects the 3.5-5 Mhz filter
* See the circuit to understand this
*/
void setTXFilters(unsigned long freq){
if (freq > 21000000L){ // the default filter is with 35 MHz cut-off
digitalWrite(PIN_TX_LPF_A, 0);
digitalWrite(PIN_TX_LPF_B, 0);
digitalWrite(PIN_TX_LPF_C, 0);
}
else if (freq >= 14000000L){ //thrown the KT1 relay on, the 30 MHz LPF is bypassed and the 14-18 MHz LPF is allowd to go through
digitalWrite(PIN_TX_LPF_A, 1);
digitalWrite(PIN_TX_LPF_B, 0);
digitalWrite(PIN_TX_LPF_C, 0);
}
else if (freq > 7000000L){
digitalWrite(PIN_TX_LPF_A, 0);
digitalWrite(PIN_TX_LPF_B, 1);
digitalWrite(PIN_TX_LPF_C, 0);
}
else {
digitalWrite(PIN_TX_LPF_A, 0);
digitalWrite(PIN_TX_LPF_B, 0);
digitalWrite(PIN_TX_LPF_C, 1);
}
}
/**
* This is the most frequently called function that configures the
* radio to a particular frequeny, sideband and sets up the transmit filters
*
* The transmit filter relays are powered up only during the tx so they dont
* draw any current during rx.
*
* The carrier oscillator of the detector/modulator is permanently fixed at
* uppper sideband. The sideband selection is done by placing the second oscillator
* either 12 Mhz below or above the 45 Mhz signal thereby inverting the sidebands
* through mixing of the second local oscillator.
*/
void setFrequency(const unsigned long freq,
const bool transmit){
static const unsigned long FIRST_IF = 45005000UL;
setTXFilters(freq);
//Nominal values for the oscillators
uint32_t local_osc_freq = FIRST_IF + freq;
uint32_t ssb_osc_freq = FIRST_IF;//will be changed depending on sideband
uint32_t bfo_osc_freq = globalSettings.usbCarrierFreq;
if(TuningMode_e::TUNE_CW == globalSettings.tuningMode){
if(transmit){
//We don't do any mixing or converting when transmitting
local_osc_freq = freq;
ssb_osc_freq = 0;
bfo_osc_freq = 0;
}
else{
//We offset when receiving CW so that it's audible
if(VfoMode_e::VFO_MODE_USB == GetActiveVfoMode()){
local_osc_freq -= globalSettings.cwSideToneFreq;
ssb_osc_freq += globalSettings.usbCarrierFreq;
}
else{
local_osc_freq += globalSettings.cwSideToneFreq;
ssb_osc_freq -= globalSettings.usbCarrierFreq;
}
}
}
else{//SSB mode
if(VfoMode_e::VFO_MODE_USB == GetActiveVfoMode()){
ssb_osc_freq += globalSettings.usbCarrierFreq;
}
else{
ssb_osc_freq -= globalSettings.usbCarrierFreq;
}
}
si5351bx_setfreq(2, local_osc_freq);
si5351bx_setfreq(1, ssb_osc_freq);
si5351bx_setfreq(0, bfo_osc_freq);
SetActiveVfoFreq(freq);
}
/**
* startTx is called by the PTT, cw keyer and CAT protocol to
* put the uBitx in tx mode. It takes care of rit settings, sideband settings
* Note: In cw mode, doesnt key the radio, only puts it in tx mode
* CW offest is calculated as lower than the operating frequency when in LSB mode, and vice versa in USB mode
*/
void startTx(TuningMode_e tx_mode){
globalSettings.tuningMode = tx_mode;
if (globalSettings.ritOn){
//save the current as the rx frequency
uint32_t rit_tx_freq = globalSettings.ritFrequency;
globalSettings.ritFrequency = GetActiveVfoFreq();
setFrequency(rit_tx_freq,true);
}
else{
if(globalSettings.splitOn){
if(Vfo_e::VFO_B == globalSettings.activeVfo){
globalSettings.activeVfo = Vfo_e::VFO_A;
}
else{
globalSettings.activeVfo = Vfo_e::VFO_B;
}
}
setFrequency(GetActiveVfoFreq(),true);
}
digitalWrite(PIN_TX_RXn, 1);//turn on the tx
globalSettings.txActive = true;
drawTx();
}
void stopTx(){
digitalWrite(PIN_TX_RXn, 0);//turn off the tx
globalSettings.txActive = false;
if(globalSettings.ritOn){
uint32_t rit_rx_freq = globalSettings.ritFrequency;
globalSettings.ritFrequency = GetActiveVfoFreq();
setFrequency(rit_rx_freq);
}
else{
if(globalSettings.splitOn){
if(Vfo_e::VFO_B == globalSettings.activeVfo){
globalSettings.activeVfo = Vfo_e::VFO_A;
}
else{
globalSettings.activeVfo = Vfo_e::VFO_B;
}
}
setFrequency(GetActiveVfoFreq());
}
drawTx();
}
/**
* ritEnable is called with a frequency parameter that determines
* what the tx frequency will be
*/
void ritEnable(unsigned long freq){
globalSettings.ritOn = true;
//save the non-rit frequency back into the VFO memory
//as RIT is a temporary shift, this is not saved to EEPROM
globalSettings.ritFrequency = freq;
}
// this is called by the RIT menu routine
void ritDisable(){
if(globalSettings.ritOn){
globalSettings.ritOn = false;
setFrequency(globalSettings.ritFrequency);
}
}
bool autoSelectSidebandChanged(const uint32_t old_frequency)
{
const uint32_t new_freq = GetActiveVfoFreq();
//Transition from below to above the traditional threshold for USB
if(old_frequency < THRESHOLD_USB_LSB && new_freq >= THRESHOLD_USB_LSB){
SetActiveVfoMode(VfoMode_e::VFO_MODE_USB);
setFrequency(new_freq);//Refresh tuning to activate the new sideband mode
return true;
}
//Transition from above to below the traditional threshold for USB
if(old_frequency >= THRESHOLD_USB_LSB && new_freq < THRESHOLD_USB_LSB){
SetActiveVfoMode(VfoMode_e::VFO_MODE_LSB);
setFrequency(new_freq);//Refresh tuning to activate the new sideband mode
return true;
}
return false;
}

15
tuner.h Normal file
View File

@ -0,0 +1,15 @@
#pragma once
#include "settings.h"
void saveVFOs();
void setFrequency(const unsigned long freq, const bool transmit = false);
void startTx(TuningMode_e tx_mode);
void stopTx();
void ritEnable(unsigned long f);
void ritDisable();
void checkCAT();
void cwKeyer(void);
void switchVFO(Vfo_e vfoSelect);
bool autoSelectSidebandChanged(const uint32_t old_frequency); //if the current frequency defaults to a different sideband mode, updates to that sideband mode and returns true. Else, returns false

130
ubitx.h
View File

@ -1,130 +0,0 @@
#include "settings.h"
/* The ubitx is powered by an arduino nano. The pin assignment is as folows
*
*/
#define ENC_A (A0) // Tuning encoder interface
#define ENC_B (A1) // Tuning encoder interface
#define FBUTTON (A2) // Tuning encoder interface
#define PTT (A3) // Sense it for ssb and as a straight key for cw operation
#define ANALOG_KEYER (A6) // This is used as keyer. The analog port has 4.7K pull up resistor. Details are in the circuit description on www.hfsignals.com
#define ANALOG_SPARE (A7) // Not used yet
#define TX_RX (7) // Pin from the Nano to the radio to switch to TX (HIGH) and RX(LOW)
#define CW_TONE (6) // Generates a square wave sidetone while sending the CW.
#define TX_LPF_A (5) // The 30 MHz LPF is permanently connected in the output of the PA...
#define TX_LPF_B (4) // ...Alternatively, either 3.5 MHz, 7 MHz or 14 Mhz LPFs are...
#define TX_LPF_C (3) // ...switched inline depending upon the TX frequency
#define CW_KEY (2) // Pin goes high during CW keydown to transmit the carrier.
// ... The CW_KEY is needed in addition to the TX/RX key as the...
// ...key can be up within a tx period
/** pin assignments
14 T_IRQ 2 std changed
13 T_DOUT (parallel to SOD/MOSI, pin 9 of display)
12 T_DIN (parallel to SDI/MISO, pin 6 of display)
11 T_CS 9 (we need to specify this)
10 T_CLK (parallel to SCK, pin 7 of display)
9 SDO(MSIO) 12 12 (spi)
8 LED A0 8 (not needed, permanently on +3.3v) (resistor from 5v,
7 SCK 13 13 (spi)
6 SDI 11 11 (spi)
5 D/C A3 7 (changable)
4 RESET A4 9 (not needed, permanently +5v)
3 CS A5 10 (changable)
2 GND GND
1 VCC VCC
The model is called tjctm24028-spi
it uses an ILI9341 display controller and an XPT2046 touch controller.
*/
#define TFT_DC 9
#define TFT_CS 10
#define CS_PIN 8 //this is the pin to select the touch controller on spi interface
/**
* The Arduino, unlike C/C++ on a regular computer with gigabytes of RAM, has very little memory.
* We have to be very careful with variables that are declared inside the functions as they are
* created in a memory region called the stack. The stack has just a few bytes of space on the Arduino
* if you declare large strings inside functions, they can easily exceed the capacity of the stack
* and mess up your programs.
* We circumvent this by declaring a few global buffers as kitchen counters where we can
* slice and dice our strings. These strings are mostly used to control the display or handle
* the input and output from the USB port. We must keep a count of the bytes used while reading
* the serial port as we can easily run out of buffer space. This is done in the serial_in_count variable.
*/
extern char c[30], b[128];
/**
* The second set of 16 pins on the Raduino's bottom connector are have the three clock outputs and the digital lines to control the rig.
* This assignment is as follows :
* Pin 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
* GND +5V CLK0 GND GND CLK1 GND GND CLK2 GND D2 D3 D4 D5 D6 D7
* These too are flexible with what you may do with them, for the Raduino, we use them to :
* - TX_RX line : Switches between Transmit and Receive after sensing the PTT or the morse keyer
* - CW_KEY line : turns on the carrier for CW
*/
/**
* The uBITX is an upconnversion transceiver. The first IF is at 45 MHz.
* The first IF frequency is not exactly at 45 Mhz but about 5 khz lower,
* this shift is due to the loading on the 45 Mhz crystal filter by the matching
* L-network used on it's either sides.
* The first oscillator works between 48 Mhz and 75 MHz. The signal is subtracted
* from the first oscillator to arriive at 45 Mhz IF. Thus, it is inverted : LSB becomes USB
* and USB becomes LSB.
* The second IF of 11.059 Mhz has a ladder crystal filter. If a second oscillator is used at
* 56 Mhz (appox), the signal is subtracted FROM the oscillator, inverting a second time, and arrives
* at the 11.059 Mhz ladder filter thus doouble inversion, keeps the sidebands as they originally were.
* If the second oscillator is at 33 Mhz, the oscilaltor is subtracated from the signal,
* thus keeping the signal's sidebands inverted. The USB will become LSB.
* We use this technique to switch sidebands. This is to avoid placing the lsbCarrier close to
* 11 MHz where its fifth harmonic beats with the arduino's 16 Mhz oscillator's fourth harmonic
*/
#define INIT_USB_FREQ (11059200l)
// limits the tuning and working range of the ubitx between 3 MHz and 30 MHz
#define LOWEST_FREQ (100000l)
#define HIGHEST_FREQ (30000000l)
static const uint32_t THRESHOLD_USB_LSB = 10000000L;
/* these are functions implemented in the main file named as ubitx_xxx.ino */
void active_delay(int delay_by);
void saveVFOs();
void setFrequency(unsigned long f);
void startTx(TuningMode_e tx_mode);
void stopTx();
void ritEnable(unsigned long f);
void ritDisable();
void checkCAT();
void cwKeyer(void);
void switchVFO(Vfo_e vfoSelect);
int enc_read(void); // returns the number of ticks in a short interval, +ve in clockwise, -ve in anti-clockwise
void enc_setup(void); // Setups up initial values and interrupts.
int btnDown(); //returns true if the encoder button is pressed
/* these functions are called universally to update the display */
void updateDisplay(); //updates just the VFO frequency to show what is in 'frequency' variable
void redrawVFOs(); //redraws only the changed digits of the vfo
void guiUpdate(); //repaints the entire screen. Slow!!
void drawCommandbar(char *text);
void drawTx();
//getValueByKnob() provides a reusable dialog box to get a value from the encoder, the prefix and postfix
//are useful to concatanate the values with text like "Set Freq to " x " KHz"
int getValueByKnob(int minimum, int maximum, int step_size, int initial, char* prefix, char *postfix);
//main functions to check if any button is pressed and other user interface events
void doCommands(); //does the commands with encoder to jump from button to button
void checkTouch(); //does the commands with a touch on the buttons
/* these are functiosn implemented in ubitx_si5351.cpp */
void si5351bx_setfreq(uint8_t clknum, uint32_t fout);
void initOscillators();
void si5351_set_calibration(int32_t cal); //calibration is a small value that is nudged to make up for the inaccuracies of the reference 25 MHz crystal frequency

View File

@ -1,68 +1,157 @@
#include <Arduino.h>
#include "settings.h"
#include "ubitx.h"
#include "nano_gui.h"
#include "scratch_space.h"
#include "settings.h"
#include "tuner.h"
/**
* The CAT protocol is used by many radios to provide remote control to comptuers through
* the serial port.
*
*
* This is very much a work in progress. Parts of this code have been liberally
* borrowed from other GPLicensed works like hamlib.
*
* WARNING : This is an unstable version and it has worked with fldigi,
* it gives time out error with WSJTX 1.8.0
*
* WARNING : This is an unstable version and it has worked with fldigi,
* it gives time out error with WSJTX 1.8.0
*/
static unsigned long rxBufferArriveTime = 0;
static byte rxBufferCheckCount = 0;
#define CAT_RECEIVE_TIMEOUT 500
static byte cat[5];
static byte insideCat = 0;
static const uint8_t FT817_MESSAGE_SIZE = 5;
static const uint8_t ACK = 0x00;
static const uint8_t RACK = 0xF0;//Re-Acknowledge sent when the state requests is already active
//Data is ordered parameters 1-4, then command code last
enum CatDataIndex_e : uint8_t {
P1 = 0,
P2 = 1,
P3 = 2,
P4 = 3,
CMD = 4
};
enum Ft817Command_e : uint8_t {
//Listed in the order presented by FT-817ND_OM_ENG_E13771011.pdf
OffBit = 0x80,
LockOn = 0x00,
LockOff = LockOn | OffBit,
PttOn = 0x08,
PttOff = PttOn | OffBit,
SetFrequency = 0x01,//P1-P4 are BCD, 0x01 0x42 0x34 0x56 = 14.23456MHz
OperatingMode = 0x07,//See OperatingMode_e for P1 decode
ClarOn = 0x05,
ClarOff = ClarOn | OffBit,
ClarFrequency = 0xF5,//P1 is sign/direction (0x00 = +, - otherwise), P3-P4 are BCD, 0x12 0x34 = 12.34kHz
VfoToggle = 0x81,
SplitOn = 0x02,
SplitOff = SplitOn | OffBit,
RepeaterMode = 0x09,//See RepeaterMode_e for P1 decode
RepeaterOffset = 0xF9,//P1-P4 are BCD
CtcssDcsMode = 0x0A,//See CtcssDcsMode_e for P1 decode
CtcssTone = 0x0B,//P1-P2 are BCD, 0x08 0x85 = 88.5MHz
DcsTone = 0x0C,//P1-P2 are BCD, 0x00 0x23 = code 023
ReadRxStatus = 0xE7,//Returns ReadRxStatus_t
ReadTxStatus = 0xF7,//Returns ReadTxStatus_t
ReadFreqAndMode = 0x03,//Returns current frequency (BCD, 4 bytes), then mode (OperatingMode_e)
PowerOn = 0x0F,
PowerOff = PowerOn | OffBit,
//Unofficial commands
ReadEeprom = 0xBB,
};
enum OperatingMode_e : uint8_t {
LSB = 0x00,
USB = 0x01,
CW = 0x02,
CWR = 0x03,//CW-reverse aka LSB CW
AM = 0x04,
FM = 0x08,
DIG = 0x0A,
PKT = 0x0C,
};
enum RepeaterMode_e : uint8_t {
ShiftMinus = 0x09,
ShiftPlus = 0x49,
Simplex = 0x89,
};
enum CtcssDcsMode_e : uint8_t {
DcsOn = 0x0A,
CtcssOn = 0x2A,
EncoderOn = 0x4A,
Off = 0x8A,
};
struct ReadRxStatus_t {
//Bitfields are not defined by the standard to be portable, which is unfortunate
uint8_t Smeter : 4;//0x00 = S0, 0x09 = S9, etc.
uint8_t Dummy : 1;
uint8_t DiscriminatorCenteringOff : 1;
uint8_t CodeUnmatched : 1;
uint8_t SquelchSuppressionActive : 1;
};
struct ReadTxStatus_t {
//Bitfields are not defined by the standard to be portable, which is unfortunate
uint8_t PowerOutputMeter : 4;
uint8_t Dummy : 1;
uint8_t SplitOff : 1;
uint8_t HighSwrDetected : 1;
uint8_t PttOff : 1;
};
//Values based on http://www.ka7oei.com/ft817_memmap.html
//hamlib likes to read addresses 0x0065 (read as 0x0064) and 0x007A, but including support for some others
enum Ft817Eeprom_e : uint16_t {
VfoAndBankSelect = 0x0055,
TuningModes = 0x0057,
KeyerStatus = 0x0058,
BandSelect = 0x0059,
BeepVolume = 0x005C,
CwPitch = 0x005E,
CwWeight = 0x005F,
CwDelay = 0x0060,
SidetoneVolume = 0x0061,
CwSpeed = 0x0062,
VoxGain = 0x0063,
CatBaudRate = 0x0064,
SsbMicVolume = 0x0067,
AmMicVolume = 0x0068,
FmMicVolume = 0x0069,
TxPower = 0x0079,
AntennaSelectAndSplit = 0x007A,
VfoAPhantomMode = 0x01E9,
};
//for broken protocol
#define CAT_RECEIVE_TIMEOUT 500
static const uint16_t CAT_RECEIVE_TIMEOUT_MS = 500;
#define CAT_MODE_LSB 0x00
#define CAT_MODE_USB 0x01
#define CAT_MODE_CW 0x02
#define CAT_MODE_CWR 0x03
#define CAT_MODE_AM 0x04
#define CAT_MODE_FM 0x08
#define CAT_MODE_DIG 0x0A
#define CAT_MODE_PKT 0x0C
#define CAT_MODE_FMN 0x88
#define ACK 0
unsigned int skipTimeCount = 0;
byte setHighNibble(byte b,byte v) {
uint8_t setHighNibble(uint8_t b, uint8_t v) {
// Clear the high nibble
b &= 0x0f;
// Set the high nibble
return b | ((v & 0x0f) << 4);
}
byte setLowNibble(byte b,byte v) {
uint8_t setLowNibble(uint8_t b, uint8_t v) {
// Clear the low nibble
b &= 0xf0;
// Set the low nibble
return b | (v & 0x0f);
}
byte getHighNibble(byte b) {
uint8_t getHighNibble(uint8_t b) {
return (b >> 4) & 0x0f;
}
byte getLowNibble(byte b) {
uint8_t getLowNibble(uint8_t b) {
return b & 0x0f;
}
// Takes a number and produces the requested number of decimal digits, staring
// from the least significant digit.
// from the least significant digit.
//
void getDecimalDigits(unsigned long number,byte* result,int digits) {
void getDecimalDigits(unsigned long number, uint8_t* result,int digits) {
for (int i = 0; i < digits; i++) {
// "Mask off" (in a decimal sense) the LSD and return it
result[i] = number % 10;
@ -73,77 +162,46 @@ void getDecimalDigits(unsigned long number,byte* result,int digits) {
// Takes a frequency and writes it into the CAT command buffer in BCD form.
//
void writeFreq(unsigned long freq,byte* cmd) {
void writeFreq(unsigned long freq, uint8_t* cmd) {
// Convert the frequency to a set of decimal digits. We are taking 9 digits
// so that we can get up to 999 MHz. But the protocol doesn't care about the
// LSD (1's place), so we ignore that digit.
byte digits[9];
uint8_t digits[9];
getDecimalDigits(freq,digits,9);
// Start from the LSB and get each nibble
cmd[3] = setLowNibble(cmd[3],digits[1]);
cmd[3] = setHighNibble(cmd[3],digits[2]);
cmd[2] = setLowNibble(cmd[2],digits[3]);
cmd[2] = setHighNibble(cmd[2],digits[4]);
cmd[1] = setLowNibble(cmd[1],digits[5]);
cmd[1] = setHighNibble(cmd[1],digits[6]);
cmd[0] = setLowNibble(cmd[0],digits[7]);
cmd[0] = setHighNibble(cmd[0],digits[8]);
// Start from the LSB and get each nibble
cmd[P4] = setLowNibble(cmd[P4],digits[1]);
cmd[P4] = setHighNibble(cmd[P4],digits[2]);
cmd[P3] = setLowNibble(cmd[P3],digits[3]);
cmd[P3] = setHighNibble(cmd[P3],digits[4]);
cmd[P2] = setLowNibble(cmd[P2],digits[5]);
cmd[P2] = setHighNibble(cmd[P2],digits[6]);
cmd[P1] = setLowNibble(cmd[P1],digits[7]);
cmd[P1] = setHighNibble(cmd[P1],digits[8]);
}
// This function takes a frquency that is encoded using 4 bytes of BCD
// This function takes a frquency that is encoded using 4 uint8_ts of BCD
// representation and turns it into an long measured in Hz.
//
// [12][34][56][78] = 123.45678? Mhz
//
unsigned long readFreq(byte* cmd) {
uint32_t readFreq(uint8_t* cmd) {
// Pull off each of the digits
byte d7 = getHighNibble(cmd[0]);
byte d6 = getLowNibble(cmd[0]);
byte d5 = getHighNibble(cmd[1]);
byte d4 = getLowNibble(cmd[1]);
byte d3 = getHighNibble(cmd[2]);
byte d2 = getLowNibble(cmd[2]);
byte d1 = getHighNibble(cmd[3]);
byte d0 = getLowNibble(cmd[3]);
return
(unsigned long)d7 * 100000000L +
(unsigned long)d6 * 10000000L +
(unsigned long)d5 * 1000000L +
(unsigned long)d4 * 100000L +
(unsigned long)d3 * 10000L +
(unsigned long)d2 * 1000L +
(unsigned long)d1 * 100L +
(unsigned long)d0 * 10L;
unsigned long ret = 0;
for(uint8_t i = 0; i < 4; ++i){
const uint8_t d1 = getHighNibble(cmd[i]);
const uint8_t d0 = getLowNibble(cmd[i]);
ret *= 100;
ret += 10*d1 + d0;
}
return ret*10;
}
//void ReadEEPRom_FT817(byte fromType)
void catReadEEPRom(void)
void catGetEeprom(const uint16_t read_address, uint8_t* response)
{
//for remove warnings
byte temp0 = cat[0];
byte temp1 = cat[1];
cat[0] = 0;
cat[1] = 0;
//for remove warnings[1] = 0;
switch (temp1)
switch (read_address)
{
case 0x45 : //
if (temp0 == 0x03)
{
cat[0] = 0x00;
cat[1] = 0xD0;
}
break;
case 0x47 : //
if (temp0 == 0x03)
{
cat[0] = 0xDC;
cat[1] = 0xE0;
}
break;
case 0x55 :
case Ft817Eeprom_e::VfoAndBankSelect:
//0 : VFO A/B 0 = VFO-A, 1 = VFO-B
//1 : MTQMB Select 0 = (Not MTQMB), 1 = MTQMB ("Memory Tune Quick Memory Bank")
//2 : QMB Select 0 = (Not QMB), 1 = QMB ("Quick Memory Bank")
@ -152,295 +210,211 @@ void catReadEEPRom(void)
//5 : Memory/MTUNE select 0 = Memory, 1 = MTUNE
//6 :
//7 : MEM/VFO Select 0 = Memory, 1 = VFO (A or B - see bit 0)
cat[0] = 0x80 + ((VFO_B == globalSettings.activeVfo) ? 1 : 0);
cat[1] = 0x00;
*response = 0x80 //always report VFO mode
| ((VFO_B == globalSettings.activeVfo) ? 0x01 : 0x00);
break;
case 0x57 : //
//0 : 1-0 AGC Mode 00 = Auto, 01 = Fast, 10 = Slow, 11 = Off
//2 DSP On/Off 0 = Off, 1 = On (Display format)
//4 PBT On/Off 0 = Off, 1 = On (Passband Tuning)
//5 NB On/Off 0 = Off, 1 = On (Noise Blanker)
//6 Lock On/Off 0 = Off, 1 = On (Dial Lock)
//7 FST (Fast Tuning) On/Off 0 = Off, 1 = On (Fast tuning)
cat[0] = 0xC0;
cat[1] = 0x40;
break;
case 0x59 : // band select VFO A Band Select 0000 = 160 M, 0001 = 75 M, 0010 = 40 M, 0011 = 30 M, 0100 = 20 M, 0101 = 17 M, 0110 = 15 M, 0111 = 12 M, 1000 = 10 M, 1001 = 6 M, 1010 = FM BCB, 1011 = Air, 1100 = 2 M, 1101 = UHF, 1110 = (Phantom)
//http://www.ka7oei.com/ft817_memmap.html
//CAT_BUFF[0] = 0xC2;
//CAT_BUFF[1] = 0x82;
break;
case 0x5C : //Beep Volume (0-100) (#13)
cat[0] = 0xB2;
cat[1] = 0x42;
break;
case 0x5E :
case Ft817Eeprom_e::CwPitch:
//3-0 : CW Pitch (300-1000 Hz) (#20) From 0 to E (HEX) with 0 = 300 Hz and each step representing 50 Hz
//5-4 : Lock Mode (#32) 00 = Dial, 01 = Freq, 10 = Panel
//7-6 : Op Filter (#38) 00 = Off, 01 = SSB, 10 = CW
//CAT_BUFF[0] = 0x08;
cat[0] = (globalSettings.cwSideToneFreq - 300)/50;
cat[1] = 0x25;
*response = (globalSettings.cwSideToneFreq - 300)/50;
break;
case 0x61 : //globalSettings.cwSideToneFreq (Volume) (#44)
cat[0] = globalSettings.cwSideToneFreq % 50;
cat[1] = 0x08;
case Ft817Eeprom_e::SidetoneVolume:
//Sidetone (Volume) (#44) 0-100
*response = globalSettings.cwSideToneFreq / 100;
break;
case 0x5F : //
//4-0 CW Weight (1.:2.5-1:4.5) (#22) From 0 to 14 (HEX) with 0 = 1:2.5, incrementing in 0.1 weight steps
//5 420 ARS (#2) 0 = Off, 1 = On
//6 144 ARS (#1) 0 = Off, 1 = On
//7 Sql/RF-G (#45) 0 = Off, 1 = On
cat[0] = 0x32;
cat[1] = 0x08;
case Ft817Eeprom_e::CwDelay:
//CW Delay (10-2500 ms) (#17) From 1 to 250 (decimal) with each step representing 10 ms
*response = globalSettings.cwActiveTimeoutMs / 10;
break;
case 0x60 : //CW Delay (10-2500 ms) (#17) From 1 to 250 (decimal) with each step representing 10 ms
cat[0] = globalSettings.cwActiveTimeoutMs / 10;
cat[1] = 0x32;
break;
case 0x62 : //
case Ft817Eeprom_e::CwSpeed:
//5-0 CW Speed (4-60 WPM) (#21) From 0 to 38 (HEX) with 0 = 4 WPM and 38 = 60 WPM (1 WPM steps)
//7-6 Batt-Chg (6/8/10 Hours (#11) 00 = 6 Hours, 01 = 8 Hours, 10 = 10 Hours
//CAT_BUFF[0] = 0x08;
cat[0] = 1200 / globalSettings.cwDitDurationMs - 4;
cat[1] = 0xB2;
*response = (1200 / globalSettings.cwDitDurationMs) - 4;
break;
case 0x63 : //
//6-0 VOX Gain (#51) Contains 1-100 (decimal) as displayed
//7 Disable AM/FM Dial (#4) 0 = Enable, 1 = Disable
cat[0] = 0xB2;
cat[1] = 0xA5;
case Ft817Eeprom_e::CatBaudRate:
//4-0 : VOX Delay (#50) 0 = 100 Ms with each step representing 100 Ms. 24 = 2500 Ms
//5 : Emergency (#28) 0 = Off, 1 = On
//7-6 : CAT Rate (4800, 9600, 38400) (#14) 00 = 4800, 01 = 9600, 10 = 38400 Baud
*response = 0xA5;
break;
case 0x64 : //
case Ft817Eeprom_e::VfoAPhantomMode:
//2-0 : 000 = LSB, 001 = USB, 010 = CW, 011 = CWR, 100 = AM, 101 = FM, 110 = DIG, 111 = PKT
//7-3 : ?
if (VfoMode_e::VFO_MODE_USB == GetActiveVfoMode()){
*response = OperatingMode_e::USB;
}
else{
*response = OperatingMode_e::LSB;
}
break;
case 0x67 : //6-0 SSB Mic (#46) Contains 0-100 (decimal) as displayed
cat[0] = 0xB2;
cat[1] = 0xB2;
break; case 0x69 : //FM Mic (#29) Contains 0-100 (decimal) as displayed
case 0x78 :
if (VfoMode_e::VFO_MODE_USB == GetActiveVfoMode())
cat[0] = CAT_MODE_USB;
else
cat[0] = CAT_MODE_LSB;
if (cat[0] != 0) cat[0] = 1 << 5;
case Ft817Eeprom_e::AntennaSelectAndSplit:
//0 : HF Antenna Select 0 = Front, 1 = Rear
//1 : 6 M Antenna Select 0 = Front, 1 = Rear
//2 : FM BCB Antenna Select 0 = Front, 1 = Rear
//3 : Air Antenna Select 0 = Front, 1 = Rear
//4 : 2 M Antenna Select 0 = Front, 1 = Rear
//5 : UHF Antenna Select 0 = Front, 1 = Rear
//6 : ? ?
//7 : SPL On/Off 0 = Off, 1 = On
*response = (globalSettings.splitOn ? 0xFF : 0x7F);
break;
case 0x79 : //
//1-0 TX Power (All bands) 00 = High, 01 = L3, 10 = L2, 11 = L1
//3 PRI On/Off 0 = Off, 1 = On
//DW On/Off 0 = Off, 1 = On
//SCN (Scan) Mode 00 = No scan, 10 = Scan up, 11 = Scan down
//ART On/Off 0 = Off, 1 = On
cat[0] = 0x00;
cat[1] = 0x00;
break;
case 0x7A : //SPLIT
//7A 0 HF Antenna Select 0 = Front, 1 = Rear
//7A 1 6 M Antenna Select 0 = Front, 1 = Rear
//7A 2 FM BCB Antenna Select 0 = Front, 1 = Rear
//7A 3 Air Antenna Select 0 = Front, 1 = Rear
//7A 4 2 M Antenna Select 0 = Front, 1 = Rear
//7A 5 UHF Antenna Select 0 = Front, 1 = Rear
//7A 6 ? ?
//7A 7 SPL On/Off 0 = Off, 1 = On
cat[0] = (globalSettings.splitOn ? 0xFF : 0x7F);
break;
case 0xB3 : //
cat[0] = 0x00;
cat[1] = 0x4D;
break;
}
// sent the data
Serial.write(cat, 2);
}
void processCATCommand2(byte* cmd) {
byte response[5];
unsigned long f;
switch(cmd[4]){
/* case 0x00:
response[0]=0;
Serial.write(response, 1);
break;
*/
case 0x01:
//set frequency
f = readFreq(cmd);
setFrequency(f);
updateDisplay();
response[0]=0;
Serial.write(response, 1);
//sprintf(b, "set:%ld", f);
//printLine2(b);
break;
//Maps some of the fixed memory layout of the FT817's EEPROM
void catReadEEPRom(uint8_t* cmd, uint8_t* response)
{
const uint16_t read_address = cmd[P1] << 8 | cmd[P2];
case 0x02:
//split on
globalSettings.splitOn = 1;
break;
case 0x82:
//split off
globalSettings.splitOn = 0;
break;
case 0x03:
writeFreq(GetActiveVfoFreq(),response); // Put the frequency into the buffer
if (VfoMode_e::VFO_MODE_USB == GetActiveVfoMode())
response[4] = 0x01; //USB
else
response[4] = 0x00; //LSB
Serial.write(response,5);
//printLine2("cat:getfreq");
break;
case 0x07: // set mode
if (cmd[0] == 0x00 || cmd[0] == 0x03)
SetActiveVfoMode(VfoMode_e::VFO_MODE_LSB);
else
SetActiveVfoMode(VfoMode_e::VFO_MODE_USB);
response[0] = 0x00;
Serial.write(response, 1);
setFrequency(GetActiveVfoFreq());
//printLine2("cat: mode changed");
//updateDisplay();
break;
case 0x08: // PTT On
if (!globalSettings.txActive) {
response[0] = 0;
globalSettings.txCatActive = true;
startTx(TuningMode_e::TUNE_SSB);
updateDisplay();
} else {
response[0] = 0xf0;
}
Serial.write(response,1);
updateDisplay();
break;
catGetEeprom(read_address,response);
catGetEeprom(read_address+1,response+1);
}
case 0x88 : //PTT OFF
if (globalSettings.txActive) {
stopTx();
globalSettings.txCatActive = false;
}
response[0] = 0;
Serial.write(response,1);
updateDisplay();
break;
void processCatCommand(uint8_t* cmd) {
//A response of a single byte, 0x00, is an ACK, so default to that
uint8_t response[FT817_MESSAGE_SIZE] = {ACK};
uint8_t response_length = 1;
case 0x81:
//toggle the VFOs
response[0] = 0;
if (VFO_A == globalSettings.activeVfo)
switchVFO(VFO_B);
else
switchVFO(VFO_A);
//menuVfoToggle(1); // '1' forces it to change the VFO
Serial.write(response,1);
updateDisplay();
break;
case 0xBB: //Read FT-817 EEPROM Data (for comfirtable)
catReadEEPRom();
break;
case 0xe7 :
// get receiver status, we have hardcoded this as
//as we dont' support ctcss, etc.
response[0] = 0x09;
Serial.write(response,1);
break;
case 0xf7:
switch(cmd[CMD]){
case Ft817Command_e::SetFrequency:
{
boolean isHighSWR = false;
boolean issplitOn = false;
/*
Inverted -> *ptt = ((p->tx_status & 0x80) == 0); <-- souce code in ft817.c (hamlib)
*/
response[0] = ((globalSettings.txActive ? 0 : 1) << 7) +
((isHighSWR ? 1 : 0) << 6) + //hi swr off / on
((issplitOn ? 1 : 0) << 5) + //Split on / off
(0 << 4) + //dummy data
0x08; //P0 meter data
Serial.write(response, 1);
uint32_t f = readFreq(cmd);
setFrequency(f);
break;
}
break;
default:
//somehow, get this to print the four bytes
ultoa(*((unsigned long *)cmd), c, 16);
response[0] = 0x00;
Serial.write(response[0]);
case Ft817Command_e::SplitOn:
if(globalSettings.splitOn){
response[0] = RACK;
}
globalSettings.splitOn = true;
break;
case Ft817Command_e::SplitOff:
if(!globalSettings.splitOn){
response[0] = RACK;
}
globalSettings.splitOn = false;
break;
case Ft817Command_e::ReadFreqAndMode:
//First 4 bytes are the frequency
writeFreq(GetActiveVfoFreq(),response);//bytes 0-3
//Last byte is the mode
if (VfoMode_e::VFO_MODE_USB == GetActiveVfoMode()){
response[4] = OperatingMode_e::USB;
}
else{
response[4] = OperatingMode_e::LSB;
}
response_length = 5;
break;
case Ft817Command_e::OperatingMode:
if(OperatingMode_e::LSB == cmd[P1] || OperatingMode_e::CWR == cmd[P1]){
SetActiveVfoMode(VfoMode_e::VFO_MODE_LSB);
}
else{
SetActiveVfoMode(VfoMode_e::VFO_MODE_USB);
}
setFrequency(GetActiveVfoFreq());//Refresh frequency to get new mode to take effect
break;
case Ft817Command_e::PttOn:
if (!globalSettings.txActive) {
globalSettings.txCatActive = true;
startTx(globalSettings.tuningMode);
}
else {
response[0] = RACK;
}
break;
case Ft817Command_e::PttOff:
if (globalSettings.txActive) {
stopTx();
}
else{
response[0] = RACK;
}
globalSettings.txCatActive = false;
break;
case Ft817Command_e::VfoToggle:
if (Vfo_e::VFO_A == globalSettings.activeVfo){
globalSettings.activeVfo = Vfo_e::VFO_B;
}
else{
globalSettings.activeVfo = Vfo_e::VFO_A;
}
break;
case Ft817Command_e::ReadEeprom:
catReadEEPRom(cmd,response);
response_length = 2;
break;
case Ft817Command_e::ReadRxStatus:
//We don't have visibility into these values, so just hard code stuff
ReadRxStatus_t reply_status;
reply_status.Dummy = 0;
reply_status.Smeter = 9;//S9
reply_status.SquelchSuppressionActive = 0;
reply_status.DiscriminatorCenteringOff = 1;
reply_status.CodeUnmatched = 0;
response[0] = *(uint8_t*)&reply_status;
break;
case Ft817Command_e::ReadTxStatus:
{
//We don't have visibility into some of these values, so just hard code stuff
ReadTxStatus_t reply_status;
reply_status.Dummy = 0;
reply_status.HighSwrDetected = 0;
reply_status.PowerOutputMeter = 0xF;
reply_status.PttOff = !globalSettings.txActive;
reply_status.SplitOff = globalSettings.splitOn;//Yaesu's documentation says that 1 = split off, but as of 2020-05-04 hamlib reads (*split = (p->tx_status & 0x20) ? RIG_SPLIT_ON : RIG_SPLIT_OFF), so do what hamlib wants
response[0] = *(uint8_t*)&reply_status;
break;
}
default:
//Do something?
break;
}
insideCat = false;
Serial.write(response, response_length);
}
int catCount = 0;
void checkCAT(){
byte i;
static uint8_t rx_buffer[FT817_MESSAGE_SIZE];
static uint8_t current_index = 0;
static uint32_t timeout = 0;
//Check Serial Port Buffer
if (Serial.available() == 0) { //Set Buffer Clear status
rxBufferCheckCount = 0;
return;
}
else if (Serial.available() < 5) { //First Arrived
if (rxBufferCheckCount == 0){
rxBufferCheckCount = Serial.available();
rxBufferArriveTime = millis() + CAT_RECEIVE_TIMEOUT; //Set time for timeout
}
else if (rxBufferArriveTime < millis()){ //Clear Buffer
for (i = 0; i < Serial.available(); i++)
rxBufferCheckCount = Serial.read();
rxBufferCheckCount = 0;
}
else if (rxBufferCheckCount < Serial.available()){ // Increase buffer count, slow arrive
rxBufferCheckCount = Serial.available();
rxBufferArriveTime = millis() + CAT_RECEIVE_TIMEOUT; //Set time for timeout
if(timeout < millis()){
current_index = 0;
timeout = 0;
}
return;
}
//Arived CAT DATA
for (i = 0; i < 5; i++)
cat[i] = Serial.read();
//this code is not re-entrant.
if (insideCat == 1)
return;
insideCat = 1;
/**
* This routine is enabled to debug the cat protocol
**/
catCount++;
/*
if (cat[4] != 0xf7 && cat[4] != 0xbb && cat[4] != 0x03){
sprintf(b, "%d %02x %02x%02x%02x%02x", catCount, cat[4],cat[0], cat[1], cat[2], cat[3]);
printLine2(b);
else{
if(0 == current_index){
timeout = millis() + CAT_RECEIVE_TIMEOUT_MS;
}
rx_buffer[current_index] = Serial.read();
++current_index;
if(current_index < FT817_MESSAGE_SIZE){
return;
}
}
*/
/*
if (!doingCAT){
doingCAT = 1;
displayText("CAT on", 100,120,100,40, ILI9341_ORANGE, ILI9341_BLACK, ILI9341_WHITE);
}
*/
processCATCommand2(cat);
insideCat = 0;
processCatCommand(rx_buffer);
current_index = 0;
timeout = 0;
}

View File

@ -1,905 +0,0 @@
#include <Arduino.h>
#include "morse.h"
#include "nano_gui.h"
#include "settings.h"
#include "setup.h"
#include "ubitx.h"
#include "version.h"
static const unsigned int COLOR_TEXT = DISPLAY_WHITE;
static const unsigned int COLOR_BACKGROUND = DISPLAY_NAVY;
static const unsigned int COLOR_ACTIVE_VFO_TEXT = DISPLAY_WHITE;
static const unsigned int COLOR_ACTIVE_VFO_BACKGROUND = DISPLAY_BLACK;
static const unsigned int COLOR_INACTIVE_VFO_TEXT = DISPLAY_GREEN;
static const unsigned int COLOR_INACTIVE_VFO_BACKGROUND = DISPLAY_BLACK;
static const unsigned int COLOR_INACTIVE_TEXT = DISPLAY_GREEN;
static const unsigned int COLOR_INACTIVE_BACKGROUND = DISPLAY_BLACK;
static const unsigned int COLOR_INACTIVE_BORDER = DISPLAY_DARKGREY;
static const unsigned int COLOR_ACTIVE_TEXT = DISPLAY_BLACK;
static const unsigned int COLOR_ACTIVE_BACKGROUND = DISPLAY_ORANGE;
static const unsigned int COLOR_ACTIVE_BORDER = DISPLAY_WHITE;
static const unsigned int COLOR_VERSION_TEXT = DISPLAY_LIGHTGREY;
static const unsigned int LAYOUT_VFO_LABEL_X = 0;
static const unsigned int LAYOUT_VFO_LABEL_Y = 10;
static const unsigned int LAYOUT_VFO_LABEL_WIDTH = 159;
static const unsigned int LAYOUT_VFO_LABEL_HEIGHT = 36;
static const unsigned int LAYOUT_VFO_LABEL_PITCH_X = 160;
static const unsigned int LAYOUT_MODE_TEXT_X = 0;
static const unsigned int LAYOUT_MODE_TEXT_Y = LAYOUT_VFO_LABEL_Y + LAYOUT_VFO_LABEL_HEIGHT + 1;
static const unsigned int LAYOUT_MODE_TEXT_WIDTH = 320;
static const unsigned int LAYOUT_MODE_TEXT_HEIGHT = 36;
static const unsigned int LAYOUT_BUTTON_X = 2;
static const unsigned int LAYOUT_BUTTON_Y = LAYOUT_MODE_TEXT_Y + LAYOUT_MODE_TEXT_HEIGHT + 1;
static const unsigned int LAYOUT_BUTTON_WIDTH = 60;
static const unsigned int LAYOUT_BUTTON_HEIGHT = 36;
static const unsigned int LAYOUT_BUTTON_PITCH_X = 64;
static const unsigned int LAYOUT_BUTTON_PITCH_Y = 40;
static const unsigned int LAYOUT_CW_TEXT_X = 0;
static const unsigned int LAYOUT_CW_TEXT_Y = LAYOUT_BUTTON_Y + 3*LAYOUT_BUTTON_PITCH_Y + 1;
static const unsigned int LAYOUT_CW_TEXT_WIDTH = 220;
static const unsigned int LAYOUT_CW_TEXT_HEIGHT = 36;
static const unsigned int LAYOUT_VERSION_TEXT_X = LAYOUT_CW_TEXT_X + LAYOUT_CW_TEXT_WIDTH + 1;
static const unsigned int LAYOUT_VERSION_TEXT_Y = LAYOUT_CW_TEXT_Y;
static const unsigned int LAYOUT_VERSION_TEXT_WIDTH = 320 - LAYOUT_CW_TEXT_WIDTH - 1;
static const unsigned int LAYOUT_VERSION_TEXT_HEIGHT = LAYOUT_CW_TEXT_HEIGHT;
static const unsigned int LAYOUT_TX_X = 280;
static const unsigned int LAYOUT_TX_Y = LAYOUT_MODE_TEXT_Y;
static const unsigned int LAYOUT_TX_WIDTH = 40;
static const unsigned int LAYOUT_TX_HEIGHT = 36;
enum btn_set_e {
BUTTON_VFOA,
BUTTON_VFOB,
BUTTON_RIT,
BUTTON_USB,
BUTTON_LSB,
BUTTON_CW,
BUTTON_SPL,
BUTTON_80,
BUTTON_40,
BUTTON_30,
BUTTON_20,
BUTTON_17,
BUTTON_15,
BUTTON_10,
BUTTON_BLANK_1,
BUTTON_MNU,
BUTTON_FRQ,
BUTTON_TOTAL
};
struct Button {
int x, y, w, h;
unsigned int id;
char text[5];
char morse;
};
constexpr Button btn_set[BUTTON_TOTAL] PROGMEM = {
{LAYOUT_VFO_LABEL_X + 0*LAYOUT_VFO_LABEL_PITCH_X, LAYOUT_VFO_LABEL_Y, LAYOUT_VFO_LABEL_WIDTH, LAYOUT_VFO_LABEL_HEIGHT, BUTTON_VFOA, "VFOA", 'A'},
{LAYOUT_VFO_LABEL_X + 1*LAYOUT_VFO_LABEL_PITCH_X, LAYOUT_VFO_LABEL_Y, LAYOUT_VFO_LABEL_WIDTH, LAYOUT_VFO_LABEL_HEIGHT, BUTTON_VFOB, "VFOB", 'B'},
{LAYOUT_BUTTON_X + 0*LAYOUT_BUTTON_PITCH_X, LAYOUT_BUTTON_Y + 0*LAYOUT_BUTTON_PITCH_Y, LAYOUT_BUTTON_WIDTH, LAYOUT_BUTTON_HEIGHT, BUTTON_RIT, "RIT", 'R'},
{LAYOUT_BUTTON_X + 1*LAYOUT_BUTTON_PITCH_X, LAYOUT_BUTTON_Y + 0*LAYOUT_BUTTON_PITCH_Y, LAYOUT_BUTTON_WIDTH, LAYOUT_BUTTON_HEIGHT, BUTTON_USB, "USB", 'U'},
{LAYOUT_BUTTON_X + 2*LAYOUT_BUTTON_PITCH_X, LAYOUT_BUTTON_Y + 0*LAYOUT_BUTTON_PITCH_Y, LAYOUT_BUTTON_WIDTH, LAYOUT_BUTTON_HEIGHT, BUTTON_LSB, "LSB", 'L'},
{LAYOUT_BUTTON_X + 3*LAYOUT_BUTTON_PITCH_X, LAYOUT_BUTTON_Y + 0*LAYOUT_BUTTON_PITCH_Y, LAYOUT_BUTTON_WIDTH, LAYOUT_BUTTON_HEIGHT, BUTTON_CW , "CW", 'C'},
{LAYOUT_BUTTON_X + 4*LAYOUT_BUTTON_PITCH_X, LAYOUT_BUTTON_Y + 0*LAYOUT_BUTTON_PITCH_Y, LAYOUT_BUTTON_WIDTH, LAYOUT_BUTTON_HEIGHT, BUTTON_SPL, "SPL", 'S'},
{LAYOUT_BUTTON_X + 0*LAYOUT_BUTTON_PITCH_X, LAYOUT_BUTTON_Y + 1*LAYOUT_BUTTON_PITCH_Y, LAYOUT_BUTTON_WIDTH, LAYOUT_BUTTON_HEIGHT, BUTTON_80, "80", '8'},
{LAYOUT_BUTTON_X + 1*LAYOUT_BUTTON_PITCH_X, LAYOUT_BUTTON_Y + 1*LAYOUT_BUTTON_PITCH_Y, LAYOUT_BUTTON_WIDTH, LAYOUT_BUTTON_HEIGHT, BUTTON_40, "40", '4'},
{LAYOUT_BUTTON_X + 2*LAYOUT_BUTTON_PITCH_X, LAYOUT_BUTTON_Y + 1*LAYOUT_BUTTON_PITCH_Y, LAYOUT_BUTTON_WIDTH, LAYOUT_BUTTON_HEIGHT, BUTTON_30, "30", '3'},
{LAYOUT_BUTTON_X + 3*LAYOUT_BUTTON_PITCH_X, LAYOUT_BUTTON_Y + 1*LAYOUT_BUTTON_PITCH_Y, LAYOUT_BUTTON_WIDTH, LAYOUT_BUTTON_HEIGHT, BUTTON_20, "20", '2'},
{LAYOUT_BUTTON_X + 4*LAYOUT_BUTTON_PITCH_X, LAYOUT_BUTTON_Y + 1*LAYOUT_BUTTON_PITCH_Y, LAYOUT_BUTTON_WIDTH, LAYOUT_BUTTON_HEIGHT, BUTTON_17, "17", '7'},
{LAYOUT_BUTTON_X + 0*LAYOUT_BUTTON_PITCH_X, LAYOUT_BUTTON_Y + 2*LAYOUT_BUTTON_PITCH_Y, LAYOUT_BUTTON_WIDTH, LAYOUT_BUTTON_HEIGHT, BUTTON_15 , "15", '5'},
{LAYOUT_BUTTON_X + 1*LAYOUT_BUTTON_PITCH_X, LAYOUT_BUTTON_Y + 2*LAYOUT_BUTTON_PITCH_Y, LAYOUT_BUTTON_WIDTH, LAYOUT_BUTTON_HEIGHT, BUTTON_10 , "10", '1'},
{LAYOUT_BUTTON_X + 2*LAYOUT_BUTTON_PITCH_X, LAYOUT_BUTTON_Y + 2*LAYOUT_BUTTON_PITCH_Y, LAYOUT_BUTTON_WIDTH, LAYOUT_BUTTON_HEIGHT, BUTTON_BLANK_1, "", '\0'},
{LAYOUT_BUTTON_X + 3*LAYOUT_BUTTON_PITCH_X, LAYOUT_BUTTON_Y + 2*LAYOUT_BUTTON_PITCH_Y, LAYOUT_BUTTON_WIDTH, LAYOUT_BUTTON_HEIGHT, BUTTON_MNU, "MNU", 'M'},
{LAYOUT_BUTTON_X + 4*LAYOUT_BUTTON_PITCH_X, LAYOUT_BUTTON_Y + 2*LAYOUT_BUTTON_PITCH_Y, LAYOUT_BUTTON_WIDTH, LAYOUT_BUTTON_HEIGHT, BUTTON_FRQ, "FRQ", 'F'},
};
static const unsigned int KEYS_OFFSET = 256;//Unique from buttons
enum keypad_e {
KEYS_1 = KEYS_OFFSET,
KEYS_2,
KEYS_3,
KEYS_BLANK_1,
KEYS_OK,
KEYS_4,
KEYS_5,
KEYS_6,
KEYS_0,
KEYS_BACKSPACE,
KEYS_7,
KEYS_8,
KEYS_9,
KEYS_BLANK_2,
KEYS_CANCEL,
KEYS_TOTAL = KEYS_CANCEL - KEYS_OFFSET + 1
};
constexpr Button keypad[KEYS_TOTAL] PROGMEM = {
{LAYOUT_BUTTON_X + 0*LAYOUT_BUTTON_PITCH_X, LAYOUT_BUTTON_Y + 0*LAYOUT_BUTTON_PITCH_Y, LAYOUT_BUTTON_WIDTH, LAYOUT_BUTTON_HEIGHT, KEYS_1, "1", '1'},
{LAYOUT_BUTTON_X + 1*LAYOUT_BUTTON_PITCH_X, LAYOUT_BUTTON_Y + 0*LAYOUT_BUTTON_PITCH_Y, LAYOUT_BUTTON_WIDTH, LAYOUT_BUTTON_HEIGHT, KEYS_2, "2", '2'},
{LAYOUT_BUTTON_X + 2*LAYOUT_BUTTON_PITCH_X, LAYOUT_BUTTON_Y + 0*LAYOUT_BUTTON_PITCH_Y, LAYOUT_BUTTON_WIDTH, LAYOUT_BUTTON_HEIGHT, KEYS_3, "3", '3'},
{LAYOUT_BUTTON_X + 3*LAYOUT_BUTTON_PITCH_X, LAYOUT_BUTTON_Y + 0*LAYOUT_BUTTON_PITCH_Y, LAYOUT_BUTTON_WIDTH, LAYOUT_BUTTON_HEIGHT, KEYS_BLANK_1, "", '\0'},
{LAYOUT_BUTTON_X + 4*LAYOUT_BUTTON_PITCH_X, LAYOUT_BUTTON_Y + 0*LAYOUT_BUTTON_PITCH_Y, LAYOUT_BUTTON_WIDTH, LAYOUT_BUTTON_HEIGHT, KEYS_OK, "OK", 'K'},
{LAYOUT_BUTTON_X + 0*LAYOUT_BUTTON_PITCH_X, LAYOUT_BUTTON_Y + 1*LAYOUT_BUTTON_PITCH_Y, LAYOUT_BUTTON_WIDTH, LAYOUT_BUTTON_HEIGHT, KEYS_4, "4", '4'},
{LAYOUT_BUTTON_X + 1*LAYOUT_BUTTON_PITCH_X, LAYOUT_BUTTON_Y + 1*LAYOUT_BUTTON_PITCH_Y, LAYOUT_BUTTON_WIDTH, LAYOUT_BUTTON_HEIGHT, KEYS_5, "5", '5'},
{LAYOUT_BUTTON_X + 2*LAYOUT_BUTTON_PITCH_X, LAYOUT_BUTTON_Y + 1*LAYOUT_BUTTON_PITCH_Y, LAYOUT_BUTTON_WIDTH, LAYOUT_BUTTON_HEIGHT, KEYS_6, "6", '6'},
{LAYOUT_BUTTON_X + 3*LAYOUT_BUTTON_PITCH_X, LAYOUT_BUTTON_Y + 1*LAYOUT_BUTTON_PITCH_Y, LAYOUT_BUTTON_WIDTH, LAYOUT_BUTTON_HEIGHT, KEYS_0, "0", '0'},
{LAYOUT_BUTTON_X + 4*LAYOUT_BUTTON_PITCH_X, LAYOUT_BUTTON_Y + 1*LAYOUT_BUTTON_PITCH_Y, LAYOUT_BUTTON_WIDTH, LAYOUT_BUTTON_HEIGHT, KEYS_BACKSPACE, "<-", 'B'},
{LAYOUT_BUTTON_X + 0*LAYOUT_BUTTON_PITCH_X, LAYOUT_BUTTON_Y + 2*LAYOUT_BUTTON_PITCH_Y, LAYOUT_BUTTON_WIDTH, LAYOUT_BUTTON_HEIGHT, KEYS_7, "7", '7'},
{LAYOUT_BUTTON_X + 1*LAYOUT_BUTTON_PITCH_X, LAYOUT_BUTTON_Y + 2*LAYOUT_BUTTON_PITCH_Y, LAYOUT_BUTTON_WIDTH, LAYOUT_BUTTON_HEIGHT, KEYS_8, "8", '8'},
{LAYOUT_BUTTON_X + 2*LAYOUT_BUTTON_PITCH_X, LAYOUT_BUTTON_Y + 2*LAYOUT_BUTTON_PITCH_Y, LAYOUT_BUTTON_WIDTH, LAYOUT_BUTTON_HEIGHT, KEYS_9, "9", '9'},
{LAYOUT_BUTTON_X + 3*LAYOUT_BUTTON_PITCH_X, LAYOUT_BUTTON_Y + 2*LAYOUT_BUTTON_PITCH_Y, LAYOUT_BUTTON_WIDTH, LAYOUT_BUTTON_HEIGHT, KEYS_BLANK_2, "", '\0'},
{LAYOUT_BUTTON_X + 4*LAYOUT_BUTTON_PITCH_X, LAYOUT_BUTTON_Y + 2*LAYOUT_BUTTON_PITCH_Y, LAYOUT_BUTTON_WIDTH, LAYOUT_BUTTON_HEIGHT, KEYS_CANCEL, "Can", 'C'},
};
boolean getButton(btn_set_e index, Button* button){
if(BUTTON_TOTAL == index){
return false;
}
memcpy_P(button, &(btn_set[index]), sizeof(Button));
return true;
}
inline void drawCommandbar(char* text){
displayText(text, LAYOUT_MODE_TEXT_X, LAYOUT_MODE_TEXT_Y, LAYOUT_MODE_TEXT_WIDTH, LAYOUT_MODE_TEXT_HEIGHT, COLOR_TEXT, COLOR_BACKGROUND, COLOR_BACKGROUND);
}
/** A generic control to read variable values
*/
int getValueByKnob(int minimum, int maximum, int step_size, int initial, const __FlashStringHelper* prefix, const __FlashStringHelper* postfix)
{
int knob = 0;
int knob_value;
while (btnDown())
active_delay(100);
active_delay(200);
knob_value = initial;
strncpy_P(b,(const char*)prefix,sizeof(b));
itoa(knob_value, c, 10);
strncat(b, c, sizeof(b) - strlen(b));
strncat_P(b, (const char*)postfix, sizeof(b) - strlen(b));
drawCommandbar(b);
while(!btnDown() && digitalRead(PTT) == HIGH){
knob = enc_read();
if (knob != 0){
if (knob_value > minimum && knob < 0)
knob_value -= step_size;
if (knob_value < maximum && knob > 0)
knob_value += step_size;
strncpy_P(b,(const char*)prefix,sizeof(b));
itoa(knob_value, c, 10);
strncat(b, c, sizeof(b) - strlen(b));
strncat_P(b,(const char*)postfix, sizeof(b) - strlen(b));
drawCommandbar(b);
}
checkCAT();
}
b[0] = 0;
drawCommandbar(b);
return knob_value;
}
void displayVFO(Vfo_e vfo){
uint16_t displayColor = COLOR_INACTIVE_TEXT;
uint16_t displayBackground = COLOR_INACTIVE_BACKGROUND;
uint16_t displayBorder = COLOR_INACTIVE_BORDER;
Button button;
if (globalSettings.splitOn){
if (vfo == globalSettings.activeVfo){
c[0] = 'R';
}
else{
c[0] = 'T';
}
}
else{
if(VFO_A == vfo){
c[0] = 'A';
}
else if(VFO_B == vfo){
c[0] = 'B';
}
else{
c[0] = '?';
}
}
c[1] = ':';
if (VFO_A == vfo){
getButton(BUTTON_VFOA, &button);
formatFreq(globalSettings.vfoA.frequency, c+2, sizeof(c)-2);
if (VFO_A == globalSettings.activeVfo){
displayColor = COLOR_ACTIVE_VFO_TEXT;
displayBackground = COLOR_ACTIVE_VFO_BACKGROUND;
displayBorder = COLOR_ACTIVE_BORDER;
}else{
displayColor = COLOR_INACTIVE_VFO_TEXT;
displayBackground = COLOR_INACTIVE_VFO_BACKGROUND;
displayBorder = COLOR_INACTIVE_BORDER;
}
}
if (VFO_B == vfo){
getButton(BUTTON_VFOB, &button);
formatFreq(globalSettings.vfoB.frequency, c+2, sizeof(c)-2);
if (VFO_B == globalSettings.activeVfo){
displayColor = COLOR_ACTIVE_VFO_TEXT;
displayBackground = COLOR_ACTIVE_VFO_BACKGROUND;
displayBorder = COLOR_ACTIVE_BORDER;
} else {
displayColor = COLOR_INACTIVE_VFO_TEXT;
displayBackground = COLOR_INACTIVE_VFO_BACKGROUND;
displayBorder = COLOR_INACTIVE_BORDER;
}
}
displayText(c, button.x, button.y, button.w, button.h, displayColor, displayBackground, displayBorder);
}
void btnDrawActive(Button* button){
displayText(button->text, button->x, button->y, button->w, button->h, COLOR_ACTIVE_TEXT, COLOR_ACTIVE_BACKGROUND, COLOR_INACTIVE_BORDER);
}
void btnDrawInactive(Button* button){
displayText(button->text, button->x, button->y, button->w, button->h, COLOR_INACTIVE_TEXT, COLOR_INACTIVE_BACKGROUND, COLOR_INACTIVE_BORDER);
}
void btnDraw(struct Button *button){
switch(button->id){
case BUTTON_VFOA:
{
displayVFO(VFO_A);
break;
}
case BUTTON_VFOB:
{
displayVFO(VFO_B);
break;
}
case BUTTON_RIT:
{
if(globalSettings.ritOn){
btnDrawActive(button);
}
else{
btnDrawInactive(button);
}
break;
}
case BUTTON_USB:
{
if(VFO_MODE_USB == GetActiveVfoMode()){
btnDrawActive(button);
}
else{
btnDrawInactive(button);
}
break;
}
case BUTTON_LSB:
{
if(VFO_MODE_LSB == GetActiveVfoMode()){
btnDrawActive(button);
}
else{
btnDrawInactive(button);
}
break;
}
case BUTTON_SPL:
{
if(globalSettings.splitOn){
btnDrawActive(button);
}
else{
btnDrawInactive(button);
}
break;
}
case BUTTON_CW:
{
if(TuningMode_e::TUNE_CW == globalSettings.tuningMode){
btnDrawActive(button);
}
else{
btnDrawInactive(button);
}
break;
}
default:
{
btnDrawInactive(button);
break;
}
}//End switch
}
void displayRIT(){
c[0] = 0;
displayFillrect(LAYOUT_MODE_TEXT_X,LAYOUT_MODE_TEXT_Y,LAYOUT_MODE_TEXT_WIDTH,LAYOUT_MODE_TEXT_HEIGHT, COLOR_BACKGROUND);
if(globalSettings.ritOn){
strncpy_P(c,(const char*)F("TX:"),sizeof(c));
formatFreq(globalSettings.ritFrequency, c+3, sizeof(c)-strlen(c));
if (VFO_A == globalSettings.activeVfo)
displayText(c, LAYOUT_VFO_LABEL_X + 0*LAYOUT_VFO_LABEL_PITCH_X, LAYOUT_MODE_TEXT_Y, LAYOUT_VFO_LABEL_WIDTH, LAYOUT_MODE_TEXT_HEIGHT, COLOR_TEXT, COLOR_BACKGROUND, COLOR_BACKGROUND);
else
displayText(c, LAYOUT_VFO_LABEL_X + 1*LAYOUT_VFO_LABEL_PITCH_X, LAYOUT_MODE_TEXT_Y, LAYOUT_VFO_LABEL_WIDTH, LAYOUT_MODE_TEXT_HEIGHT, COLOR_TEXT, COLOR_BACKGROUND, COLOR_BACKGROUND);
}
}
void fastTune(){
//if the btn is down, wait until it is up
while(btnDown())
active_delay(50);
active_delay(300);
strncpy_P(c,(const char*)F("Fast tune"),sizeof(c));
displayText(c, LAYOUT_MODE_TEXT_X, LAYOUT_MODE_TEXT_Y, LAYOUT_MODE_TEXT_WIDTH, LAYOUT_MODE_TEXT_HEIGHT, COLOR_TEXT, COLOR_BACKGROUND, COLOR_BACKGROUND);
while(true){
checkCAT();
//exit after debouncing the btnDown
if (btnDown()){
displayFillrect(LAYOUT_MODE_TEXT_X, LAYOUT_MODE_TEXT_Y, LAYOUT_MODE_TEXT_WIDTH, LAYOUT_MODE_TEXT_HEIGHT, COLOR_BACKGROUND);
//wait until the button is realsed and then return
while(btnDown())
active_delay(50);
active_delay(300);
return;
}
int encoder = enc_read();
if (encoder != 0){
uint32_t freq = GetActiveVfoFreq();
if (encoder > 0 && freq < 30000000l){
freq += 50000l;
}
else if (encoder < 0 && freq > 600000l){
freq -= 50000l;
}
setFrequency(freq);
displayVFO(globalSettings.activeVfo);
}
}// end of the event loop
}
void enterFreq(){
//force the display to refresh everything
//display all the buttons
for (int i = 0; i < KEYS_TOTAL; i++){
Button button;
memcpy_P(&button, &(keypad[i]), sizeof(Button));
btnDraw(&button);
}
int cursor_pos = 0;
memset(c, 0, sizeof(c));
while(1){
checkCAT();
if(!readTouch())
continue;
scaleTouch(&ts_point);
for (int i = 0; i < KEYS_TOTAL; i++){
Button button;
memcpy_P(&button, &(keypad[i]), sizeof(Button));
int x2 = button.x + button.w;
int y2 = button.y + button.h;
if(button.x < ts_point.x && ts_point.x < x2 &&
button.y < ts_point.y && ts_point.y < y2){
//Serial.print(F("Entered key: "));
//Serial.println(button.text);
switch(button.id){
case KEYS_OK:
{
uint32_t new_freq = atol(c);
if((LOWEST_FREQ/1000 <= new_freq) && (new_freq <= HIGHEST_FREQ/1000)){
new_freq *= 1000L;
uint32_t prev_freq = GetActiveVfoFreq();
//Transition from below to above the traditional threshold for USB
if(prev_freq < THRESHOLD_USB_LSB && new_freq >= THRESHOLD_USB_LSB){
SetActiveVfoMode(VfoMode_e::VFO_MODE_USB);
}
//Transition from aboveo to below the traditional threshold for USB
if(prev_freq >= THRESHOLD_USB_LSB && new_freq < THRESHOLD_USB_LSB){
SetActiveVfoMode(VfoMode_e::VFO_MODE_LSB);
}
if (VFO_A == globalSettings.activeVfo){
globalSettings.vfoA.frequency = new_freq;
}
else{
globalSettings.vfoB.frequency = new_freq;
}
setFrequency(new_freq);
saveVFOs();
}
guiUpdate();
return;
break;
}
case KEYS_BACKSPACE:
{
c[cursor_pos] = 0;
if (cursor_pos > 0){
cursor_pos--;
}
c[cursor_pos] = 0;
break;
}
case KEYS_CANCEL:
{
guiUpdate();
return;
break;
}
case KEYS_0:
case KEYS_1:
case KEYS_2:
case KEYS_3:
case KEYS_4:
case KEYS_5:
case KEYS_6:
case KEYS_7:
case KEYS_8:
case KEYS_9:
{
c[cursor_pos++] = button.text[0];
c[cursor_pos] = 0;
break;
}
default:
{
//Serial.print(F("Unknown key id: "));
//Serial.println(button.id);
break;
}
}//switch
}//if button hit test
}// end of the button scanning loop
strncpy(b, c, sizeof(b));
strncat_P(b,(const char*)F(" KHz"),sizeof(b) - strlen(b));
displayText(b, LAYOUT_MODE_TEXT_X, LAYOUT_MODE_TEXT_Y, LAYOUT_MODE_TEXT_WIDTH, LAYOUT_MODE_TEXT_HEIGHT, COLOR_TEXT, COLOR_BACKGROUND, COLOR_BACKGROUND);
active_delay(300);
while(readTouch())
checkCAT();
} // end of event loop : while(1)
}
void drawCWStatus(){
strncpy_P(b,(const char*)F(" cw: "),sizeof(b));
int wpm = 1200/globalSettings.cwDitDurationMs;
itoa(wpm,c, 10);
strncat(b, c, sizeof(b) - strlen(b));
strncat_P(b,(const char*)F("wpm, "), sizeof(b) - strlen(b));
itoa(globalSettings.cwSideToneFreq, c, 10);
strncat(b, c, sizeof(b) - strlen(b));
strncat_P(b,(const char*)F("hz"), sizeof(b) - strlen(b));
displayText(b, LAYOUT_CW_TEXT_X, LAYOUT_CW_TEXT_Y, LAYOUT_CW_TEXT_WIDTH, LAYOUT_CW_TEXT_HEIGHT, COLOR_TEXT, COLOR_BACKGROUND, COLOR_BACKGROUND);
}
void drawVersion()
{
strncpy_P(b,VERSION_STRING,sizeof(b));
displayText(b, LAYOUT_VERSION_TEXT_X, LAYOUT_VERSION_TEXT_Y, LAYOUT_VERSION_TEXT_WIDTH, LAYOUT_VERSION_TEXT_HEIGHT, COLOR_VERSION_TEXT, COLOR_BACKGROUND, COLOR_BACKGROUND);
}
void drawTx(){
if (globalSettings.txActive){
strncpy_P(b,(const char*)F("TX"), sizeof(b));
displayText(b, LAYOUT_TX_X, LAYOUT_TX_Y, LAYOUT_TX_WIDTH, LAYOUT_TX_HEIGHT, COLOR_ACTIVE_TEXT, COLOR_ACTIVE_BACKGROUND, COLOR_BACKGROUND);
}
else{
displayFillrect(LAYOUT_TX_X, LAYOUT_TX_Y, LAYOUT_TX_WIDTH, LAYOUT_TX_HEIGHT, COLOR_BACKGROUND);
}
}
void drawStatusbar(){
drawCWStatus();
drawVersion();
}
void guiUpdate(){
/*
if (doingCAT)
return;
*/
// use the current frequency as the VFO frequency for the active VFO
displayClear(COLOR_BACKGROUND);
displayVFO(VFO_A);
displayVFO(VFO_B);
checkCAT();
displayRIT();
checkCAT();
//force the display to refresh everything
//display all the buttons
for (int i = 0; i < BUTTON_TOTAL; i++){
Button button;
memcpy_P(&button, &(btn_set[i]), sizeof(Button));
btnDraw(&button);
checkCAT();
}
drawStatusbar();
checkCAT();
}
// this builds up the top line of the display with frequency and mode
void updateDisplay() {
displayVFO(globalSettings.activeVfo);
}
void ritToggle(struct Button *button){
if(!globalSettings.ritOn){
ritEnable(GetActiveVfoFreq());
}
else{
ritDisable();
}
btnDraw(button);
displayRIT();
}
void splitToggle(Button *button){
globalSettings.splitOn = !globalSettings.splitOn;
btnDraw(button);
//disable rit as well
ritDisable();
Button button2;
getButton(BUTTON_RIT, &button2);
btnDraw(&button2);
displayRIT();
displayVFO(VFO_A);
displayVFO(VFO_B);
}
void vfoReset(){
Button button;
if (VFO_A == globalSettings.activeVfo)
globalSettings.vfoB.frequency = globalSettings.vfoA.frequency;
else
globalSettings.vfoA.frequency = globalSettings.vfoB.frequency;
if(globalSettings.splitOn){
getButton(BUTTON_SPL, &button);
splitToggle(&button);
}
if(globalSettings.ritOn){
getButton(BUTTON_RIT, &button);
ritToggle(&button);
}
displayVFO(VFO_A);
displayVFO(VFO_B);
saveVFOs();
}
void cwToggle(struct Button *b){
if (TuningMode_e::TUNE_SSB == globalSettings.tuningMode){
globalSettings.tuningMode = TuningMode_e::TUNE_CW;
}
else{
globalSettings.tuningMode = TuningMode_e::TUNE_SSB;
}
setFrequency(GetActiveVfoFreq());
btnDraw(b);
}
void sidebandToggle(Button* button){
if(BUTTON_LSB == button->id){
SetActiveVfoMode(VfoMode_e::VFO_MODE_LSB);
}
else{
SetActiveVfoMode(VfoMode_e::VFO_MODE_USB);
}
setFrequency(GetActiveVfoFreq());
struct Button button2;
getButton(BUTTON_USB, &button2);
btnDraw(&button2);
getButton(BUTTON_LSB, &button2);
btnDraw(&button2);
saveVFOs();
}
void redrawVFOs(){
struct Button button;
ritDisable();
getButton(BUTTON_RIT, &button);
btnDraw(&button);
displayRIT();
displayVFO(VFO_A);
displayVFO(VFO_B);
//draw the lsb/usb buttons, the sidebands might have changed
getButton(BUTTON_LSB, &button);
btnDraw(&button);
getButton(BUTTON_USB, &button);
btnDraw(&button);
}
void switchBand(uint32_t bandfreq){
//Serial.println(frequency);
//Serial.println(bandfreq);
uint32_t offset;
uint32_t freq = GetActiveVfoFreq();
if (3500000L <= freq && freq <= 4000000L)
offset = freq - 3500000l;
else if (24800000L <= freq && freq <= 25000000L)
offset = freq - 24800000L;
else
offset = freq % 1000000L;
//Serial.println(offset);
setFrequency(bandfreq + offset);
if(bandfreq >= THRESHOLD_USB_LSB){
SetActiveVfoMode(VfoMode_e::VFO_MODE_USB);
}
else{
SetActiveVfoMode(VfoMode_e::VFO_MODE_LSB);
}
updateDisplay();
saveVFOs();
}
void doCommand(Button* button){
//Serial.print(F("Doing command: "));
//Serial.print(button->text);
//Serial.print(F(", Hitbox: "));
//Serial.print(button->x);Serial.print(F(","));
//Serial.print(button->y);Serial.print(F(";"));
//Serial.print(button->x + button->w);Serial.print(F(","));
//Serial.println(button->y + button->h);
switch(button->id){
case BUTTON_RIT:
{
ritToggle(button);
break;
}
case BUTTON_LSB:
case BUTTON_USB:
{
sidebandToggle(button);
break;
}
case BUTTON_CW:
{
cwToggle(button);
break;
}
case BUTTON_SPL:
{
splitToggle(button);
break;
}
case BUTTON_VFOA:
{
if(VFO_A == globalSettings.activeVfo){
fastTune();
}
else{
switchVFO(VFO_A);
}
break;
}
case BUTTON_VFOB:
{
if(VFO_B == globalSettings.activeVfo){
fastTune();
}
else{
switchVFO(VFO_B);
}
break;
}
case BUTTON_80:
{
switchBand(3500000L);
break;
}
case BUTTON_40:
{
switchBand(7000000L);
break;
}
case BUTTON_30:
{
switchBand(10000000L);
break;
}
case BUTTON_20:
{
switchBand(14000000L);
break;
}
case BUTTON_17:
{
switchBand(18000000L);
break;
}
case BUTTON_15:
{
switchBand(21000000L);
break;
}
case BUTTON_10:
{
switchBand(28000000L);
break;
}
case BUTTON_FRQ:
{
enterFreq();
break;
}
case BUTTON_MNU:
{
doSetup2();
break;
}
default:
{
//Serial.print(F("Unknown command: "));
//Serial.println(button.id);
break;
}
}
}
void checkTouch(){
if (!readTouch())
return;
while(readTouch())
checkCAT();
scaleTouch(&ts_point);
/* //debug code
Serial.print(ts_point.x); Serial.print(' ');Serial.println(ts_point.y);
*/
for (int i = 0; i < BUTTON_TOTAL; i++){
Button button;
memcpy_P(&button, &(btn_set[i]), sizeof(Button));
int x2 = button.x + button.w;
int y2 = button.y + button.h;
if(button.x < ts_point.x && ts_point.x < x2 &&
button.y < ts_point.y && ts_point.y < y2)
doCommand(&button);
}
}
//returns true if the button is pressed
int btnDown(){
if (digitalRead(FBUTTON) == HIGH)
return 0;
else
return 1;
}
void drawFocus(int ibtn, int color){
Button button;
memcpy_P(&button, &(btn_set[ibtn]), sizeof(Button));
displayRect(button.x, button.y, button.w, button.h, color);
}
void doCommands(){
int select = 0;
int prev_button = 0;
//wait for the button to be raised up
while(btnDown())
active_delay(50);
active_delay(50); //debounce
while (true){
//check if the knob's button was pressed
if (btnDown()){
Button button;
memcpy_P(&button, &(btn_set[select/10]), sizeof(Button));
doCommand(&button);
//unfocus the buttons
drawFocus(select, COLOR_INACTIVE_BORDER);
if (VFO_A == globalSettings.activeVfo)
drawFocus(BUTTON_VFOA, COLOR_ACTIVE_BORDER);
else
drawFocus(BUTTON_VFOB, COLOR_ACTIVE_BORDER);
//wait for the button to be up and debounce
while(btnDown())
active_delay(100);
active_delay(500);
return;
}
int knob = enc_read();
if (knob == 0){
active_delay(50);
continue;
}
if (knob > 0){
if (select + knob < BUTTON_TOTAL * 10)
select += knob;
}
if (knob < 0 && select + knob >= 0)
select += knob; //caught ya, i is already -ve here, so you add it
if (prev_button == select / 10)
continue;
//we are on a new button
drawFocus(prev_button, COLOR_INACTIVE_BORDER);
drawFocus(select/10, COLOR_ACTIVE_BORDER);
prev_button = select/10;
}
// guiUpdate();
//debounce the button
while(btnDown())
active_delay(50);
active_delay(50);
checkCAT();
}

View File

@ -1,513 +0,0 @@
/**
* This source file is under General Public License version 3.
*
* This verision uses a built-in Si5351 library
* Most source code are meant to be understood by the compilers and the computers.
* Code that has to be hackable needs to be well understood and properly documented.
* Donald Knuth coined the term Literate Programming to indicate code that is written be
* easily read and understood.
*
* The Raduino is a small board that includes the Arduin Nano, a TFT display and
* an Si5351a frequency synthesizer. This board is manufactured by HF Signals Electronics Pvt Ltd
*
* To learn more about Arduino you may visit www.arduino.cc.
*
* The Arduino works by starts executing the code in a function called setup() and then it
* repeatedly keeps calling loop() forever. All the initialization code is kept in setup()
* and code to continuously sense the tuning knob, the function button, transmit/receive,
* etc is all in the loop() function. If you wish to study the code top down, then scroll
* to the bottom of this file and read your way up.
*
* Below are the libraries to be included for building the Raduino
* The EEPROM library is used to store settings like the frequency memory, caliberation data, etc.
*
* The main chip which generates upto three oscillators of various frequencies in the
* Raduino is the Si5351a. To learn more about Si5351a you can download the datasheet
* from www.silabs.com although, strictly speaking it is not a requirment to understand this code.
* Instead, you can look up the Si5351 library written by xxx, yyy. You can download and
* install it from www.url.com to complile this file.
* The Wire.h library is used to talk to the Si5351 and we also declare an instance of
* Si5351 object to control the clocks.
*/
#include <Wire.h>
#include "settings.h"
#include "setup.h"
#include "ubitx.h"
#include "nano_gui.h"
/**
* The Arduino, unlike C/C++ on a regular computer with gigabytes of RAM, has very little memory.
* We have to be very careful with variables that are declared inside the functions as they are
* created in a memory region called the stack. The stack has just a few bytes of space on the Arduino
* if you declare large strings inside functions, they can easily exceed the capacity of the stack
* and mess up your programs.
* We circumvent this by declaring a few global buffers as kitchen counters where we can
* slice and dice our strings. These strings are mostly used to control the display or handle
* the input and output from the USB port. We must keep a count of the bytes used while reading
* the serial port as we can easily run out of buffer space. This is done in the serial_in_count variable.
*/
char b[128];
char c[30];
//during CAT commands, we will freeeze the display until CAT is disengaged
unsigned char doingCAT = 0;
/**
* Below are the basic functions that control the uBitx. Understanding the functions before
* you start hacking around
*/
/**
* Our own delay. During any delay, the raduino should still be processing a few times.
*/
void active_delay(int delay_by){
unsigned long timeStart = millis();
while (millis() - timeStart <= (unsigned long)delay_by) {
delay(10);
//Background Work
checkCAT();
}
}
void saveVFOs()
{
SaveSettingsToEeprom();
}
/**
* Select the properly tx harmonic filters
* The four harmonic filters use only three relays
* the four LPFs cover 30-21 Mhz, 18 - 14 Mhz, 7-10 MHz and 3.5 to 5 Mhz
* Briefly, it works like this,
* - When KT1 is OFF, the 'off' position routes the PA output through the 30 MHz LPF
* - When KT1 is ON, it routes the PA output to KT2. Which is why you will see that
* the KT1 is on for the three other cases.
* - When the KT1 is ON and KT2 is off, the off position of KT2 routes the PA output
* to 18 MHz LPF (That also works for 14 Mhz)
* - When KT1 is On, KT2 is On, it routes the PA output to KT3
* - KT3, when switched on selects the 7-10 Mhz filter
* - KT3 when switched off selects the 3.5-5 Mhz filter
* See the circuit to understand this
*/
void setTXFilters(unsigned long freq){
if (freq > 21000000L){ // the default filter is with 35 MHz cut-off
digitalWrite(TX_LPF_A, 0);
digitalWrite(TX_LPF_B, 0);
digitalWrite(TX_LPF_C, 0);
}
else if (freq >= 14000000L){ //thrown the KT1 relay on, the 30 MHz LPF is bypassed and the 14-18 MHz LPF is allowd to go through
digitalWrite(TX_LPF_A, 1);
digitalWrite(TX_LPF_B, 0);
digitalWrite(TX_LPF_C, 0);
}
else if (freq > 7000000L){
digitalWrite(TX_LPF_A, 0);
digitalWrite(TX_LPF_B, 1);
digitalWrite(TX_LPF_C, 0);
}
else {
digitalWrite(TX_LPF_A, 0);
digitalWrite(TX_LPF_B, 0);
digitalWrite(TX_LPF_C, 1);
}
}
void setTXFilters_v5(unsigned long freq){
if (freq > 21000000L){ // the default filter is with 35 MHz cut-off
digitalWrite(TX_LPF_A, 0);
digitalWrite(TX_LPF_B, 0);
digitalWrite(TX_LPF_C, 0);
}
else if (freq >= 14000000L){ //thrown the KT1 relay on, the 30 MHz LPF is bypassed and the 14-18 MHz LPF is allowd to go through
digitalWrite(TX_LPF_A, 1);
digitalWrite(TX_LPF_B, 0);
digitalWrite(TX_LPF_C, 0);
}
else if (freq > 7000000L){
digitalWrite(TX_LPF_A, 0);
digitalWrite(TX_LPF_B, 1);
digitalWrite(TX_LPF_C, 0);
}
else {
digitalWrite(TX_LPF_A, 0);
digitalWrite(TX_LPF_B, 0);
digitalWrite(TX_LPF_C, 1);
}
}
/**
* This is the most frequently called function that configures the
* radio to a particular frequeny, sideband and sets up the transmit filters
*
* The transmit filter relays are powered up only during the tx so they dont
* draw any current during rx.
*
* The carrier oscillator of the detector/modulator is permanently fixed at
* uppper sideband. The sideband selection is done by placing the second oscillator
* either 12 Mhz below or above the 45 Mhz signal thereby inverting the sidebands
* through mixing of the second local oscillator.
*/
void setFrequency(unsigned long freq){
static const unsigned long firstIF = 45005000L;
setTXFilters(freq);
uint32_t local_osc_freq;
if(TuningMode_e::TUNE_CW == globalSettings.tuningMode){
local_osc_freq = firstIF + freq + globalSettings.cwSideToneFreq;
}
else{
local_osc_freq = firstIF + freq;
}
uint32_t ssb_osc_freq;
if(VfoMode_e::VFO_MODE_USB == GetActiveVfoMode()){
ssb_osc_freq = firstIF + globalSettings.usbCarrierFreq;
}
else{
ssb_osc_freq = firstIF - globalSettings.usbCarrierFreq;
}
si5351bx_setfreq(2, local_osc_freq);
si5351bx_setfreq(1, ssb_osc_freq);
SetActiveVfoFreq(freq);
}
/**
* startTx is called by the PTT, cw keyer and CAT protocol to
* put the uBitx in tx mode. It takes care of rit settings, sideband settings
* Note: In cw mode, doesnt key the radio, only puts it in tx mode
* CW offest is calculated as lower than the operating frequency when in LSB mode, and vice versa in USB mode
*/
void startTx(TuningMode_e tx_mode){
globalSettings.tuningMode = tx_mode;
if (globalSettings.ritOn){
//save the current as the rx frequency
uint32_t rit_tx_freq = globalSettings.ritFrequency;
globalSettings.ritFrequency = GetActiveVfoFreq();
setFrequency(rit_tx_freq);
}
else{
if(globalSettings.splitOn){
if(Vfo_e::VFO_B == globalSettings.activeVfo){
globalSettings.activeVfo = Vfo_e::VFO_A;
}
else{
globalSettings.activeVfo = Vfo_e::VFO_B;
}
}
setFrequency(GetActiveVfoFreq());
}
if(TuningMode_e::TUNE_CW == globalSettings.tuningMode){
//turn off the second local oscillator and the bfo
si5351bx_setfreq(0, 0);
si5351bx_setfreq(1, 0);
//shift the first oscillator to the tx frequency directly
//the key up and key down will toggle the carrier unbalancing
//the exact cw frequency is the tuned frequency + sidetone
if(VfoMode_e::VFO_MODE_USB == GetActiveVfoMode()){
si5351bx_setfreq(2, GetActiveVfoFreq() + globalSettings.cwSideToneFreq);
}
else{
si5351bx_setfreq(2, GetActiveVfoFreq() - globalSettings.cwSideToneFreq);
}
delay(20);
}
digitalWrite(TX_RX, 1);//turn on the tx
globalSettings.txActive = true;
drawTx();
}
void stopTx(){
digitalWrite(TX_RX, 0);//turn off the tx
globalSettings.txActive = false;
//set back the carrier oscillator - cw tx switches it off
si5351bx_setfreq(0, globalSettings.usbCarrierFreq);
if(globalSettings.ritOn){
uint32_t rit_rx_freq = globalSettings.ritFrequency;
globalSettings.ritFrequency = GetActiveVfoFreq();
setFrequency(rit_rx_freq);
}
else{
if(globalSettings.splitOn){
if(Vfo_e::VFO_B == globalSettings.activeVfo){
globalSettings.activeVfo = Vfo_e::VFO_A;
}
else{
globalSettings.activeVfo = Vfo_e::VFO_B;
}
}
setFrequency(GetActiveVfoFreq());
}
drawTx();
}
/**
* ritEnable is called with a frequency parameter that determines
* what the tx frequency will be
*/
void ritEnable(unsigned long freq){
globalSettings.ritOn = true;
//save the non-rit frequency back into the VFO memory
//as RIT is a temporary shift, this is not saved to EEPROM
globalSettings.ritFrequency = freq;
}
// this is called by the RIT menu routine
void ritDisable(){
if(globalSettings.ritOn){
globalSettings.ritOn = false;
setFrequency(globalSettings.ritFrequency);
updateDisplay();
}
}
/**
* Basic User Interface Routines. These check the front panel for any activity
*/
/**
* The PTT is checked only if we are not already in a cw transmit session
* If the PTT is pressed, we shift to the ritbase if the rit was on
* flip the T/R line to T and update the display to denote transmission
*/
void checkPTT(){
//we don't check for ptt when transmitting cw
if (globalSettings.cwExpirationTimeMs > 0){
return;
}
if(digitalRead(PTT) == 0 && !globalSettings.txActive){
startTx(TuningMode_e::TUNE_SSB);
active_delay(50); //debounce the PTT
}
if (digitalRead(PTT) == 1 && globalSettings.txActive)
stopTx();
}
//check if the encoder button was pressed
void checkButton(){
//only if the button is pressed
if (!btnDown())
return;
active_delay(50);
if (!btnDown()) //debounce
return;
//disengage any CAT work
doingCAT = 0;
int downTime = 0;
while(btnDown()){
active_delay(10);
downTime++;
if (downTime > 300){
doSetup2();
return;
}
}
active_delay(100);
doCommands();
//wait for the button to go up again
while(btnDown())
active_delay(10);
active_delay(50);//debounce
}
void switchVFO(Vfo_e new_vfo){
ritDisable();//If we are in RIT mode, we need to disable it before setting the active VFO so that the correct VFO gets it's frequency restored
globalSettings.activeVfo = new_vfo;
setFrequency(GetActiveVfoFreq());
redrawVFOs();
saveVFOs();
}
/**
* The tuning jumps by 50 Hz on each step when you tune slowly
* As you spin the encoder faster, the jump size also increases
* This way, you can quickly move to another band by just spinning the
* tuning knob
*/
void doTuning(){
static unsigned long prev_freq;
static unsigned long nextFrequencyUpdate = 0;
unsigned long now = millis();
if (now >= nextFrequencyUpdate && prev_freq != GetActiveVfoFreq()){
updateDisplay();
nextFrequencyUpdate = now + 100;
prev_freq = GetActiveVfoFreq();
}
int s = enc_read();
if (!s)
return;
//Serial.println(s);
doingCAT = 0; // go back to manual mode if you were doing CAT
prev_freq = GetActiveVfoFreq();
uint32_t new_freq = prev_freq;
if (s > 10 || s < -10){
new_freq += 200L * s;
}
else if (s > 5 || s < -5){
new_freq += 100L * s;
}
else{
new_freq += 50L * s;
}
//Transition from below to above the traditional threshold for USB
if(prev_freq < THRESHOLD_USB_LSB && new_freq >= THRESHOLD_USB_LSB){
SetActiveVfoMode(VfoMode_e::VFO_MODE_USB);
}
//Transition from aboveo to below the traditional threshold for USB
if(prev_freq >= THRESHOLD_USB_LSB && new_freq < THRESHOLD_USB_LSB){
SetActiveVfoMode(VfoMode_e::VFO_MODE_LSB);
}
setFrequency(new_freq);
}
/**
* RIT only steps back and forth by 100 hz at a time
*/
void doRIT(){
int knob = enc_read();
uint32_t old_freq = GetActiveVfoFreq();
uint32_t new_freq = old_freq;
if (knob < 0)
new_freq -= 100l;
else if (knob > 0)
new_freq += 100;
if (old_freq != new_freq){
setFrequency(new_freq);
updateDisplay();
}
}
/**
* The settings are read from EEPROM. The first time around, the values may not be
* present or out of range, in this case, some intelligent defaults are copied into the
* variables.
*/
void initSettings(){
LoadDefaultSettings();
LoadSettingsFromEeprom();
}
void initPorts(){
analogReference(DEFAULT);
//??
pinMode(ENC_A, INPUT_PULLUP);
pinMode(ENC_B, INPUT_PULLUP);
pinMode(FBUTTON, INPUT_PULLUP);
enc_setup();
//configure the function button to use the external pull-up
// pinMode(FBUTTON, INPUT);
// digitalWrite(FBUTTON, HIGH);
pinMode(PTT, INPUT_PULLUP);
// pinMode(ANALOG_KEYER, INPUT_PULLUP);
pinMode(CW_TONE, OUTPUT);
digitalWrite(CW_TONE, 0);
pinMode(TX_RX,OUTPUT);
digitalWrite(TX_RX, 0);
pinMode(TX_LPF_A, OUTPUT);
pinMode(TX_LPF_B, OUTPUT);
pinMode(TX_LPF_C, OUTPUT);
digitalWrite(TX_LPF_A, 0);
digitalWrite(TX_LPF_B, 0);
digitalWrite(TX_LPF_C, 0);
pinMode(CW_KEY, OUTPUT);
digitalWrite(CW_KEY, 0);
}
void setup()
{
Serial.begin(38400);
Serial.flush();
initSettings();
displayInit();
initPorts();
initOscillators();
setFrequency(globalSettings.vfoA.frequency);
//Run initial calibration routine if button is pressed during power up
if(btnDown()){
LoadDefaultSettings();
setupTouch();
SetActiveVfoMode(VfoMode_e::VFO_MODE_USB);
setFrequency(10000000L);
runLocalOscSetting();
SetActiveVfoMode(VfoMode_e::VFO_MODE_LSB);
setFrequency(7100000L);
runBfoSetting();
}
guiUpdate();
}
/**
* The loop checks for keydown, ptt, function button and tuning.
*/
void loop(){
if(TuningMode_e::TUNE_CW == globalSettings.tuningMode){
cwKeyer();
}
else if(!globalSettings.txCatActive){
checkPTT();
}
checkButton();
//tune only when not tranmsitting
if(!globalSettings.txActive){
if(globalSettings.ritOn){
doRIT();
}
else{
doTuning();
}
checkTouch();
}
checkCAT();
}

185
ubitxv6.ino Normal file
View File

@ -0,0 +1,185 @@
/**
* This source file is under General Public License version 3.
*
* This verision uses a built-in Si5351 library
* Most source code are meant to be understood by the compilers and the computers.
* Code that has to be hackable needs to be well understood and properly documented.
* Donald Knuth coined the term Literate Programming to indicate code that is written be
* easily read and understood.
*
* The Raduino is a small board that includes the Arduin Nano, a TFT display and
* an Si5351a frequency synthesizer. This board is manufactured by HF Signals Electronics Pvt Ltd
*
* To learn more about Arduino you may visit www.arduino.cc.
*
* The Arduino works by starts executing the code in a function called setup() and then it
* repeatedly keeps calling loop() forever. All the initialization code is kept in setup()
* and code to continuously sense the tuning knob, the function button, transmit/receive,
* etc is all in the loop() function. If you wish to study the code top down, then scroll
* to the bottom of this file and read your way up.
*
* Below are the libraries to be included for building the Raduino
* The EEPROM library is used to store settings like the frequency memory, caliberation data, etc.
*
* The main chip which generates upto three oscillators of various frequencies in the
* Raduino is the Si5351a. To learn more about Si5351a you can download the datasheet
* from www.silabs.com although, strictly speaking it is not a requirment to understand this code.
* Instead, you can look up the Si5351 library written by xxx, yyy. You can download and
* install it from www.url.com to complile this file.
* The Wire.h library is used to talk to the Si5351 and we also declare an instance of
* Si5351 object to control the clocks.
*/
#include <Wire.h>
#include "encoder.h"
#include "menu.h"
#include "menu_main.h"
#include "morse.h"
#include "pin_definitions.h"
#include "push_button.h"
#include "nano_gui.h"
#include "settings.h"
#include "setup.h"
#include "si5351.h"
#include "touch.h"
#include "tuner.h"
#include "ui_touch.h"
/**
* The Arduino, unlike C/C++ on a regular computer with gigabytes of RAM, has very little memory.
* We have to be very careful with variables that are declared inside the functions as they are
* created in a memory region called the stack. The stack has just a few bytes of space on the Arduino
* if you declare large strings inside functions, they can easily exceed the capacity of the stack
* and mess up your programs.
* We circumvent this by declaring a few global buffers as kitchen counters where we can
* slice and dice our strings. These strings are mostly used to control the display or handle
* the input and output from the USB port. We must keep a count of the bytes used while reading
* the serial port as we can easily run out of buffer space. This is done in the serial_in_count variable.
*/
char b[128];
char c[30];
//during CAT commands, we will freeeze the display until CAT is disengaged
unsigned char doingCAT = 0;
/**
* Basic User Interface Routines. These check the front panel for any activity
*/
/**
* The PTT is checked only if we are not already in a cw transmit session
* If the PTT is pressed, we shift to the ritbase if the rit was on
* flip the T/R line to T and update the display to denote transmission
*/
void checkPTT(){
//we don't check for ptt when transmitting cw
if (globalSettings.cwExpirationTimeMs > 0){
return;
}
if(digitalRead(PIN_PTT) == 0 && !globalSettings.txActive){
startTx(TuningMode_e::TUNE_SSB);
delay(50); //debounce the PTT
}
if (digitalRead(PIN_PTT) == 1 && globalSettings.txActive)
stopTx();
}
/**
* The settings are read from EEPROM. The first time around, the values may not be
* present or out of range, in this case, some intelligent defaults are copied into the
* variables.
*/
void initSettings(){
LoadDefaultSettings();
LoadSettingsFromEeprom();
}
void initPorts(){
analogReference(DEFAULT);
//??
pinMode(PIN_ENC_A, INPUT_PULLUP);
pinMode(PIN_ENC_B, INPUT_PULLUP);
pinMode(PIN_ENC_PUSH_BUTTON, INPUT_PULLUP);
enc_setup();
//configure the function button to use the external pull-up
// pinMode(PIN_ENC_PUSH_BUTTON, INPUT);
// digitalWrite(PIN_ENC_PUSH_BUTTON, HIGH);
pinMode(PIN_PTT, INPUT_PULLUP);
// pinMode(PIN_ANALOG_KEYER, INPUT_PULLUP);
pinMode(PIN_CW_TONE, OUTPUT);
digitalWrite(PIN_CW_TONE, 0);
pinMode(PIN_TX_RXn,OUTPUT);
digitalWrite(PIN_TX_RXn, 0);
pinMode(PIN_TX_LPF_A, OUTPUT);
pinMode(PIN_TX_LPF_B, OUTPUT);
pinMode(PIN_TX_LPF_C, OUTPUT);
digitalWrite(PIN_TX_LPF_A, 0);
digitalWrite(PIN_TX_LPF_B, 0);
digitalWrite(PIN_TX_LPF_C, 0);
pinMode(PIN_CW_KEY, OUTPUT);
digitalWrite(PIN_CW_KEY, 0);
}
void setup()
{
Serial.begin(38400);
Serial.flush();
initSettings();
displayInit();
initTouch();
initPorts();
initOscillators();
setFrequency(globalSettings.vfoA.frequency);
//Run initial calibration routine if button is pressed during power up
if(ButtonPress_e::NotPressed != CheckTunerButton()){
LoadDefaultSettings();
setupTouch();
SetActiveVfoMode(VfoMode_e::VFO_MODE_USB);
setFrequency(10000000L);
runLocalOscSetting();
SetActiveVfoMode(VfoMode_e::VFO_MODE_LSB);
setFrequency(7100000L);
runBfoSetting();
}
rootMenu->initMenu();
}
/**
* The loop checks for keydown, ptt, function button and tuning.
*/
void loop(){
if(TuningMode_e::TUNE_CW == globalSettings.tuningMode){
cwKeyer();
}
else if(!globalSettings.txCatActive){
checkPTT();
}
checkCAT();
if(globalSettings.txActive){
//Don't run menus when transmitting
return;
}
ButtonPress_e tuner_button = CheckTunerButton();
Point touch_point;
ButtonPress_e touch_button = checkTouch(&touch_point);
int16_t knob = enc_read();
runActiveMenu(tuner_button,touch_button,touch_point,knob);
}

31
ui_touch.cpp Normal file
View File

@ -0,0 +1,31 @@
#include "ui_touch.h"
#include <Arduino.h>//delay
#include "button_timing.h"
#include "touch.h"
ButtonPress_e checkTouch(Point *const touch_point_out){
if (!readTouch(touch_point_out)){
return ButtonPress_e::NotPressed;
}
delay(DEBOUNCE_DELAY_MS);
if (!readTouch(touch_point_out)){//debounce
return ButtonPress_e::NotPressed;
}
uint16_t down_time = 0;
while(readTouch(touch_point_out) && (down_time < LONG_PRESS_TIME_MS)){
delay(LONG_PRESS_POLL_TIME_MS);
down_time += LONG_PRESS_POLL_TIME_MS;
}
scaleTouch(touch_point_out);
if(down_time < LONG_PRESS_TIME_MS){
return ButtonPress_e::ShortPress;
}
else{
return ButtonPress_e::LongPress;
}
}

6
ui_touch.h Normal file
View File

@ -0,0 +1,6 @@
#pragma once
#include "menu.h"
#include "point.h"
ButtonPress_e checkTouch(Point *const touch_point_out);

44
utils.cpp Normal file
View File

@ -0,0 +1,44 @@
#include "utils.h"
#include <avr/pgmspace.h>
#include <stdlib.h>//ultoa
#include <string.h>//memset, strlen
#include <WString.h>//F()
/*
* Formats the frequency given
*/
void formatFreq(uint32_t freq, char* buff, uint16_t buff_size, uint8_t fixed_width)
{
memset(buff, 0, buff_size);
ultoa(freq, buff, 10);
uint8_t num_digits = strlen(buff);
const uint8_t num_spacers = (num_digits-1) / 3;
const uint8_t num_leading_digits_raw = num_digits % 3;
const uint8_t num_leading_digits = (0 == num_leading_digits_raw) ? 3 : num_leading_digits_raw;
if(0 < fixed_width){
while(0 < fixed_width - num_digits - num_spacers){
if(0 == fixed_width % 4){
buff[0] = '\x81';//separator size
}
else{
buff[0] = '\x80';//digit size
}
--fixed_width;
++buff;
}
}
ultoa(freq, buff, 10);
buff += num_leading_digits;
num_digits -= num_leading_digits;
for(int i = num_digits-1; i >= 0; --i){
buff[i + (i/3 + 1)] = buff[i];
}
for(unsigned int i = 0; i < num_spacers; ++i){
memcpy_P(buff,F("."),1);
buff += 4;
}
}

7
utils.h Normal file
View File

@ -0,0 +1,7 @@
#pragma once
#include <stdint.h>
#define LIMIT(val,min,max) ((val) < (min)) ? (min) : (((max) < (val)) ? (max) : (val))
void formatFreq(uint32_t freq, char* buff_out, uint16_t buff_size, uint8_t fixed_width = 0);

4
version.cpp Normal file
View File

@ -0,0 +1,4 @@
#include "version.h"
const char VERSION_STRING_PRIVATE [] PROGMEM = "R1.5.1";
const char* const VERSION_STRING = VERSION_STRING_PRIVATE;

9
version.h Normal file
View File

@ -0,0 +1,9 @@
#pragma once
#include <avr/pgmspace.h>
/*
* VERSION_STRING is a PROGMEM string, so extract it before use, e.g.
* strncpy_P(char_buffer_out,VERSION_STRING,size_of_char_buffer_out);
*/
extern const char* const VERSION_STRING;