diff --git a/LICENSE.md b/LICENSE.md
new file mode 100644
index 0000000..99c85d0
--- /dev/null
+++ b/LICENSE.md
@@ -0,0 +1,674 @@
+ GNU GENERAL PUBLIC LICENSE
+ Version 3, 29 June 2007
+
+ Copyright (C) 2007 Free Software Foundation, Inc.
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The GNU General Public License is a free, copyleft license for
+software and other kinds of works.
+
+ The licenses for most software and other practical works are designed
+to take away your freedom to share and change the works. By contrast,
+the GNU General Public License is intended to guarantee your freedom to
+share and change all versions of a program--to make sure it remains free
+software for all its users. We, the Free Software Foundation, use the
+GNU General Public License for most of our software; it applies also to
+any other work released this way by its authors. You can apply it to
+your programs, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+them if you wish), that you receive source code or can get it if you
+want it, that you can change the software or use pieces of it in new
+free programs, and that you know you can do these things.
+
+ To protect your rights, we need to prevent others from denying you
+these rights or asking you to surrender the rights. Therefore, you have
+certain responsibilities if you distribute copies of the software, or if
+you modify it: responsibilities to respect the freedom of others.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must pass on to the recipients the same
+freedoms that you received. You must make sure that they, too, receive
+or can get the source code. And you must show them these terms so they
+know their rights.
+
+ Developers that use the GNU GPL protect your rights with two steps:
+(1) assert copyright on the software, and (2) offer you this License
+giving you legal permission to copy, distribute and/or modify it.
+
+ For the developers' and authors' protection, the GPL clearly explains
+that there is no warranty for this free software. For both users' and
+authors' sake, the GPL requires that modified versions be marked as
+changed, so that their problems will not be attributed erroneously to
+authors of previous versions.
+
+ Some devices are designed to deny users access to install or run
+modified versions of the software inside them, although the manufacturer
+can do so. This is fundamentally incompatible with the aim of
+protecting users' freedom to change the software. The systematic
+pattern of such abuse occurs in the area of products for individuals to
+use, which is precisely where it is most unacceptable. Therefore, we
+have designed this version of the GPL to prohibit the practice for those
+products. If such problems arise substantially in other domains, we
+stand ready to extend this provision to those domains in future versions
+of the GPL, as needed to protect the freedom of users.
+
+ Finally, every program is threatened constantly by software patents.
+States should not allow patents to restrict development and use of
+software on general-purpose computers, but in those that do, we wish to
+avoid the special danger that patents applied to a free program could
+make it effectively proprietary. To prevent this, the GPL assures that
+patents cannot be used to render the program non-free.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ TERMS AND CONDITIONS
+
+ 0. Definitions.
+
+ "This License" refers to version 3 of the GNU General Public License.
+
+ "Copyright" also means copyright-like laws that apply to other kinds of
+works, such as semiconductor masks.
+
+ "The Program" refers to any copyrightable work licensed under this
+License. Each licensee is addressed as "you". "Licensees" and
+"recipients" may be individuals or organizations.
+
+ To "modify" a work means to copy from or adapt all or part of the work
+in a fashion requiring copyright permission, other than the making of an
+exact copy. The resulting work is called a "modified version" of the
+earlier work or a work "based on" the earlier work.
+
+ A "covered work" means either the unmodified Program or a work based
+on the Program.
+
+ To "propagate" a work means to do anything with it that, without
+permission, would make you directly or secondarily liable for
+infringement under applicable copyright law, except executing it on a
+computer or modifying a private copy. Propagation includes copying,
+distribution (with or without modification), making available to the
+public, and in some countries other activities as well.
+
+ To "convey" a work means any kind of propagation that enables other
+parties to make or receive copies. Mere interaction with a user through
+a computer network, with no transfer of a copy, is not conveying.
+
+ An interactive user interface displays "Appropriate Legal Notices"
+to the extent that it includes a convenient and prominently visible
+feature that (1) displays an appropriate copyright notice, and (2)
+tells the user that there is no warranty for the work (except to the
+extent that warranties are provided), that licensees may convey the
+work under this License, and how to view a copy of this License. If
+the interface presents a list of user commands or options, such as a
+menu, a prominent item in the list meets this criterion.
+
+ 1. Source Code.
+
+ The "source code" for a work means the preferred form of the work
+for making modifications to it. "Object code" means any non-source
+form of a work.
+
+ A "Standard Interface" means an interface that either is an official
+standard defined by a recognized standards body, or, in the case of
+interfaces specified for a particular programming language, one that
+is widely used among developers working in that language.
+
+ The "System Libraries" of an executable work include anything, other
+than the work as a whole, that (a) is included in the normal form of
+packaging a Major Component, but which is not part of that Major
+Component, and (b) serves only to enable use of the work with that
+Major Component, or to implement a Standard Interface for which an
+implementation is available to the public in source code form. A
+"Major Component", in this context, means a major essential component
+(kernel, window system, and so on) of the specific operating system
+(if any) on which the executable work runs, or a compiler used to
+produce the work, or an object code interpreter used to run it.
+
+ The "Corresponding Source" for a work in object code form means all
+the source code needed to generate, install, and (for an executable
+work) run the object code and to modify the work, including scripts to
+control those activities. However, it does not include the work's
+System Libraries, or general-purpose tools or generally available free
+programs which are used unmodified in performing those activities but
+which are not part of the work. For example, Corresponding Source
+includes interface definition files associated with source files for
+the work, and the source code for shared libraries and dynamically
+linked subprograms that the work is specifically designed to require,
+such as by intimate data communication or control flow between those
+subprograms and other parts of the work.
+
+ The Corresponding Source need not include anything that users
+can regenerate automatically from other parts of the Corresponding
+Source.
+
+ The Corresponding Source for a work in source code form is that
+same work.
+
+ 2. Basic Permissions.
+
+ All rights granted under this License are granted for the term of
+copyright on the Program, and are irrevocable provided the stated
+conditions are met. This License explicitly affirms your unlimited
+permission to run the unmodified Program. The output from running a
+covered work is covered by this License only if the output, given its
+content, constitutes a covered work. This License acknowledges your
+rights of fair use or other equivalent, as provided by copyright law.
+
+ You may make, run and propagate covered works that you do not
+convey, without conditions so long as your license otherwise remains
+in force. You may convey covered works to others for the sole purpose
+of having them make modifications exclusively for you, or provide you
+with facilities for running those works, provided that you comply with
+the terms of this License in conveying all material for which you do
+not control copyright. Those thus making or running the covered works
+for you must do so exclusively on your behalf, under your direction
+and control, on terms that prohibit them from making any copies of
+your copyrighted material outside their relationship with you.
+
+ Conveying under any other circumstances is permitted solely under
+the conditions stated below. Sublicensing is not allowed; section 10
+makes it unnecessary.
+
+ 3. Protecting Users' Legal Rights From Anti-Circumvention Law.
+
+ No covered work shall be deemed part of an effective technological
+measure under any applicable law fulfilling obligations under article
+11 of the WIPO copyright treaty adopted on 20 December 1996, or
+similar laws prohibiting or restricting circumvention of such
+measures.
+
+ When you convey a covered work, you waive any legal power to forbid
+circumvention of technological measures to the extent such circumvention
+is effected by exercising rights under this License with respect to
+the covered work, and you disclaim any intention to limit operation or
+modification of the work as a means of enforcing, against the work's
+users, your or third parties' legal rights to forbid circumvention of
+technological measures.
+
+ 4. Conveying Verbatim Copies.
+
+ You may convey verbatim copies of the Program's source code as you
+receive it, in any medium, provided that you conspicuously and
+appropriately publish on each copy an appropriate copyright notice;
+keep intact all notices stating that this License and any
+non-permissive terms added in accord with section 7 apply to the code;
+keep intact all notices of the absence of any warranty; and give all
+recipients a copy of this License along with the Program.
+
+ You may charge any price or no price for each copy that you convey,
+and you may offer support or warranty protection for a fee.
+
+ 5. Conveying Modified Source Versions.
+
+ You may convey a work based on the Program, or the modifications to
+produce it from the Program, in the form of source code under the
+terms of section 4, provided that you also meet all of these conditions:
+
+ a) The work must carry prominent notices stating that you modified
+ it, and giving a relevant date.
+
+ b) The work must carry prominent notices stating that it is
+ released under this License and any conditions added under section
+ 7. This requirement modifies the requirement in section 4 to
+ "keep intact all notices".
+
+ c) You must license the entire work, as a whole, under this
+ License to anyone who comes into possession of a copy. This
+ License will therefore apply, along with any applicable section 7
+ additional terms, to the whole of the work, and all its parts,
+ regardless of how they are packaged. This License gives no
+ permission to license the work in any other way, but it does not
+ invalidate such permission if you have separately received it.
+
+ d) If the work has interactive user interfaces, each must display
+ Appropriate Legal Notices; however, if the Program has interactive
+ interfaces that do not display Appropriate Legal Notices, your
+ work need not make them do so.
+
+ A compilation of a covered work with other separate and independent
+works, which are not by their nature extensions of the covered work,
+and which are not combined with it such as to form a larger program,
+in or on a volume of a storage or distribution medium, is called an
+"aggregate" if the compilation and its resulting copyright are not
+used to limit the access or legal rights of the compilation's users
+beyond what the individual works permit. Inclusion of a covered work
+in an aggregate does not cause this License to apply to the other
+parts of the aggregate.
+
+ 6. Conveying Non-Source Forms.
+
+ You may convey a covered work in object code form under the terms
+of sections 4 and 5, provided that you also convey the
+machine-readable Corresponding Source under the terms of this License,
+in one of these ways:
+
+ a) Convey the object code in, or embodied in, a physical product
+ (including a physical distribution medium), accompanied by the
+ Corresponding Source fixed on a durable physical medium
+ customarily used for software interchange.
+
+ b) Convey the object code in, or embodied in, a physical product
+ (including a physical distribution medium), accompanied by a
+ written offer, valid for at least three years and valid for as
+ long as you offer spare parts or customer support for that product
+ model, to give anyone who possesses the object code either (1) a
+ copy of the Corresponding Source for all the software in the
+ product that is covered by this License, on a durable physical
+ medium customarily used for software interchange, for a price no
+ more than your reasonable cost of physically performing this
+ conveying of source, or (2) access to copy the
+ Corresponding Source from a network server at no charge.
+
+ c) Convey individual copies of the object code with a copy of the
+ written offer to provide the Corresponding Source. This
+ alternative is allowed only occasionally and noncommercially, and
+ only if you received the object code with such an offer, in accord
+ with subsection 6b.
+
+ d) Convey the object code by offering access from a designated
+ place (gratis or for a charge), and offer equivalent access to the
+ Corresponding Source in the same way through the same place at no
+ further charge. You need not require recipients to copy the
+ Corresponding Source along with the object code. If the place to
+ copy the object code is a network server, the Corresponding Source
+ may be on a different server (operated by you or a third party)
+ that supports equivalent copying facilities, provided you maintain
+ clear directions next to the object code saying where to find the
+ Corresponding Source. Regardless of what server hosts the
+ Corresponding Source, you remain obligated to ensure that it is
+ available for as long as needed to satisfy these requirements.
+
+ e) Convey the object code using peer-to-peer transmission, provided
+ you inform other peers where the object code and Corresponding
+ Source of the work are being offered to the general public at no
+ charge under subsection 6d.
+
+ A separable portion of the object code, whose source code is excluded
+from the Corresponding Source as a System Library, need not be
+included in conveying the object code work.
+
+ A "User Product" is either (1) a "consumer product", which means any
+tangible personal property which is normally used for personal, family,
+or household purposes, or (2) anything designed or sold for incorporation
+into a dwelling. In determining whether a product is a consumer product,
+doubtful cases shall be resolved in favor of coverage. For a particular
+product received by a particular user, "normally used" refers to a
+typical or common use of that class of product, regardless of the status
+of the particular user or of the way in which the particular user
+actually uses, or expects or is expected to use, the product. A product
+is a consumer product regardless of whether the product has substantial
+commercial, industrial or non-consumer uses, unless such uses represent
+the only significant mode of use of the product.
+
+ "Installation Information" for a User Product means any methods,
+procedures, authorization keys, or other information required to install
+and execute modified versions of a covered work in that User Product from
+a modified version of its Corresponding Source. The information must
+suffice to ensure that the continued functioning of the modified object
+code is in no case prevented or interfered with solely because
+modification has been made.
+
+ If you convey an object code work under this section in, or with, or
+specifically for use in, a User Product, and the conveying occurs as
+part of a transaction in which the right of possession and use of the
+User Product is transferred to the recipient in perpetuity or for a
+fixed term (regardless of how the transaction is characterized), the
+Corresponding Source conveyed under this section must be accompanied
+by the Installation Information. But this requirement does not apply
+if neither you nor any third party retains the ability to install
+modified object code on the User Product (for example, the work has
+been installed in ROM).
+
+ The requirement to provide Installation Information does not include a
+requirement to continue to provide support service, warranty, or updates
+for a work that has been modified or installed by the recipient, or for
+the User Product in which it has been modified or installed. Access to a
+network may be denied when the modification itself materially and
+adversely affects the operation of the network or violates the rules and
+protocols for communication across the network.
+
+ Corresponding Source conveyed, and Installation Information provided,
+in accord with this section must be in a format that is publicly
+documented (and with an implementation available to the public in
+source code form), and must require no special password or key for
+unpacking, reading or copying.
+
+ 7. Additional Terms.
+
+ "Additional permissions" are terms that supplement the terms of this
+License by making exceptions from one or more of its conditions.
+Additional permissions that are applicable to the entire Program shall
+be treated as though they were included in this License, to the extent
+that they are valid under applicable law. If additional permissions
+apply only to part of the Program, that part may be used separately
+under those permissions, but the entire Program remains governed by
+this License without regard to the additional permissions.
+
+ When you convey a copy of a covered work, you may at your option
+remove any additional permissions from that copy, or from any part of
+it. (Additional permissions may be written to require their own
+removal in certain cases when you modify the work.) You may place
+additional permissions on material, added by you to a covered work,
+for which you have or can give appropriate copyright permission.
+
+ Notwithstanding any other provision of this License, for material you
+add to a covered work, you may (if authorized by the copyright holders of
+that material) supplement the terms of this License with terms:
+
+ a) Disclaiming warranty or limiting liability differently from the
+ terms of sections 15 and 16 of this License; or
+
+ b) Requiring preservation of specified reasonable legal notices or
+ author attributions in that material or in the Appropriate Legal
+ Notices displayed by works containing it; or
+
+ c) Prohibiting misrepresentation of the origin of that material, or
+ requiring that modified versions of such material be marked in
+ reasonable ways as different from the original version; or
+
+ d) Limiting the use for publicity purposes of names of licensors or
+ authors of the material; or
+
+ e) Declining to grant rights under trademark law for use of some
+ trade names, trademarks, or service marks; or
+
+ f) Requiring indemnification of licensors and authors of that
+ material by anyone who conveys the material (or modified versions of
+ it) with contractual assumptions of liability to the recipient, for
+ any liability that these contractual assumptions directly impose on
+ those licensors and authors.
+
+ All other non-permissive additional terms are considered "further
+restrictions" within the meaning of section 10. If the Program as you
+received it, or any part of it, contains a notice stating that it is
+governed by this License along with a term that is a further
+restriction, you may remove that term. If a license document contains
+a further restriction but permits relicensing or conveying under this
+License, you may add to a covered work material governed by the terms
+of that license document, provided that the further restriction does
+not survive such relicensing or conveying.
+
+ If you add terms to a covered work in accord with this section, you
+must place, in the relevant source files, a statement of the
+additional terms that apply to those files, or a notice indicating
+where to find the applicable terms.
+
+ Additional terms, permissive or non-permissive, may be stated in the
+form of a separately written license, or stated as exceptions;
+the above requirements apply either way.
+
+ 8. Termination.
+
+ You may not propagate or modify a covered work except as expressly
+provided under this License. Any attempt otherwise to propagate or
+modify it is void, and will automatically terminate your rights under
+this License (including any patent licenses granted under the third
+paragraph of section 11).
+
+ However, if you cease all violation of this License, then your
+license from a particular copyright holder is reinstated (a)
+provisionally, unless and until the copyright holder explicitly and
+finally terminates your license, and (b) permanently, if the copyright
+holder fails to notify you of the violation by some reasonable means
+prior to 60 days after the cessation.
+
+ Moreover, your license from a particular copyright holder is
+reinstated permanently if the copyright holder notifies you of the
+violation by some reasonable means, this is the first time you have
+received notice of violation of this License (for any work) from that
+copyright holder, and you cure the violation prior to 30 days after
+your receipt of the notice.
+
+ Termination of your rights under this section does not terminate the
+licenses of parties who have received copies or rights from you under
+this License. If your rights have been terminated and not permanently
+reinstated, you do not qualify to receive new licenses for the same
+material under section 10.
+
+ 9. Acceptance Not Required for Having Copies.
+
+ You are not required to accept this License in order to receive or
+run a copy of the Program. Ancillary propagation of a covered work
+occurring solely as a consequence of using peer-to-peer transmission
+to receive a copy likewise does not require acceptance. However,
+nothing other than this License grants you permission to propagate or
+modify any covered work. These actions infringe copyright if you do
+not accept this License. Therefore, by modifying or propagating a
+covered work, you indicate your acceptance of this License to do so.
+
+ 10. Automatic Licensing of Downstream Recipients.
+
+ Each time you convey a covered work, the recipient automatically
+receives a license from the original licensors, to run, modify and
+propagate that work, subject to this License. You are not responsible
+for enforcing compliance by third parties with this License.
+
+ An "entity transaction" is a transaction transferring control of an
+organization, or substantially all assets of one, or subdividing an
+organization, or merging organizations. If propagation of a covered
+work results from an entity transaction, each party to that
+transaction who receives a copy of the work also receives whatever
+licenses to the work the party's predecessor in interest had or could
+give under the previous paragraph, plus a right to possession of the
+Corresponding Source of the work from the predecessor in interest, if
+the predecessor has it or can get it with reasonable efforts.
+
+ You may not impose any further restrictions on the exercise of the
+rights granted or affirmed under this License. For example, you may
+not impose a license fee, royalty, or other charge for exercise of
+rights granted under this License, and you may not initiate litigation
+(including a cross-claim or counterclaim in a lawsuit) alleging that
+any patent claim is infringed by making, using, selling, offering for
+sale, or importing the Program or any portion of it.
+
+ 11. Patents.
+
+ A "contributor" is a copyright holder who authorizes use under this
+License of the Program or a work on which the Program is based. The
+work thus licensed is called the contributor's "contributor version".
+
+ A contributor's "essential patent claims" are all patent claims
+owned or controlled by the contributor, whether already acquired or
+hereafter acquired, that would be infringed by some manner, permitted
+by this License, of making, using, or selling its contributor version,
+but do not include claims that would be infringed only as a
+consequence of further modification of the contributor version. For
+purposes of this definition, "control" includes the right to grant
+patent sublicenses in a manner consistent with the requirements of
+this License.
+
+ Each contributor grants you a non-exclusive, worldwide, royalty-free
+patent license under the contributor's essential patent claims, to
+make, use, sell, offer for sale, import and otherwise run, modify and
+propagate the contents of its contributor version.
+
+ In the following three paragraphs, a "patent license" is any express
+agreement or commitment, however denominated, not to enforce a patent
+(such as an express permission to practice a patent or covenant not to
+sue for patent infringement). To "grant" such a patent license to a
+party means to make such an agreement or commitment not to enforce a
+patent against the party.
+
+ If you convey a covered work, knowingly relying on a patent license,
+and the Corresponding Source of the work is not available for anyone
+to copy, free of charge and under the terms of this License, through a
+publicly available network server or other readily accessible means,
+then you must either (1) cause the Corresponding Source to be so
+available, or (2) arrange to deprive yourself of the benefit of the
+patent license for this particular work, or (3) arrange, in a manner
+consistent with the requirements of this License, to extend the patent
+license to downstream recipients. "Knowingly relying" means you have
+actual knowledge that, but for the patent license, your conveying the
+covered work in a country, or your recipient's use of the covered work
+in a country, would infringe one or more identifiable patents in that
+country that you have reason to believe are valid.
+
+ If, pursuant to or in connection with a single transaction or
+arrangement, you convey, or propagate by procuring conveyance of, a
+covered work, and grant a patent license to some of the parties
+receiving the covered work authorizing them to use, propagate, modify
+or convey a specific copy of the covered work, then the patent license
+you grant is automatically extended to all recipients of the covered
+work and works based on it.
+
+ A patent license is "discriminatory" if it does not include within
+the scope of its coverage, prohibits the exercise of, or is
+conditioned on the non-exercise of one or more of the rights that are
+specifically granted under this License. You may not convey a covered
+work if you are a party to an arrangement with a third party that is
+in the business of distributing software, under which you make payment
+to the third party based on the extent of your activity of conveying
+the work, and under which the third party grants, to any of the
+parties who would receive the covered work from you, a discriminatory
+patent license (a) in connection with copies of the covered work
+conveyed by you (or copies made from those copies), or (b) primarily
+for and in connection with specific products or compilations that
+contain the covered work, unless you entered into that arrangement,
+or that patent license was granted, prior to 28 March 2007.
+
+ Nothing in this License shall be construed as excluding or limiting
+any implied license or other defenses to infringement that may
+otherwise be available to you under applicable patent law.
+
+ 12. No Surrender of Others' Freedom.
+
+ If conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot convey a
+covered work so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you may
+not convey it at all. For example, if you agree to terms that obligate you
+to collect a royalty for further conveying from those to whom you convey
+the Program, the only way you could satisfy both those terms and this
+License would be to refrain entirely from conveying the Program.
+
+ 13. Use with the GNU Affero General Public License.
+
+ Notwithstanding any other provision of this License, you have
+permission to link or combine any covered work with a work licensed
+under version 3 of the GNU Affero General Public License into a single
+combined work, and to convey the resulting work. The terms of this
+License will continue to apply to the part which is the covered work,
+but the special requirements of the GNU Affero General Public License,
+section 13, concerning interaction through a network will apply to the
+combination as such.
+
+ 14. Revised Versions of this License.
+
+ The Free Software Foundation may publish revised and/or new versions of
+the GNU General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+ Each version is given a distinguishing version number. If the
+Program specifies that a certain numbered version of the GNU General
+Public License "or any later version" applies to it, you have the
+option of following the terms and conditions either of that numbered
+version or of any later version published by the Free Software
+Foundation. If the Program does not specify a version number of the
+GNU General Public License, you may choose any version ever published
+by the Free Software Foundation.
+
+ If the Program specifies that a proxy can decide which future
+versions of the GNU General Public License can be used, that proxy's
+public statement of acceptance of a version permanently authorizes you
+to choose that version for the Program.
+
+ Later license versions may give you additional or different
+permissions. However, no additional obligations are imposed on any
+author or copyright holder as a result of your choosing to follow a
+later version.
+
+ 15. Disclaimer of Warranty.
+
+ THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
+APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
+HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
+OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
+THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
+IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
+ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+ 16. Limitation of Liability.
+
+ IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
+THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
+GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
+USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
+DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
+PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
+EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGES.
+
+ 17. Interpretation of Sections 15 and 16.
+
+ If the disclaimer of warranty and limitation of liability provided
+above cannot be given local legal effect according to their terms,
+reviewing courts shall apply local law that most closely approximates
+an absolute waiver of all civil liability in connection with the
+Program, unless a warranty or assumption of liability accompanies a
+copy of the Program in return for a fee.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+state the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+ uBITX - An Arduino sketch to control the uBITX transceiver
+ Copyright (C) 2017, Ashhar Farhan
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see .
+
+Also add information on how to contact you by electronic and paper mail.
+
+ If the program does terminal interaction, make it output a short
+notice like this when it starts in an interactive mode:
+
+ {project} Copyright (C) {year} {fullname}
+ This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+ This is free software, and you are welcome to redistribute it
+ under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License. Of course, your program's commands
+might be different; for a GUI interface, you would use an "about box".
+
+ You should also get your employer (if you work as a programmer) or school,
+if any, to sign a "copyright disclaimer" for the program, if necessary.
+For more information on this, and how to apply and follow the GNU GPL, see
+.
+
+ The GNU General Public License does not permit incorporating your program
+into proprietary programs. If your program is a subroutine library, you
+may consider it more useful to permit linking proprietary applications with
+the library. If this is what you want to do, use the GNU Lesser General
+Public License instead of this License. But first, please read
+.
diff --git a/README.md b/README.md
index e69de29..a4bdd90 100644
--- a/README.md
+++ b/README.md
@@ -0,0 +1,236 @@
+#NOTICE
+----------------------------------------------------------------------------
+- Now Release Version 1.20 on my blog (http://www.hamskey.com)
+- You can download and compiled hex file and uBITX Manager application on release section (https://github.com/phdlee/ubitx/releases)
+- For more information, see my blog (http://www.hamskey.com)
+
+http://www.hamskey.com
+
+Ian KD8CEC
+kd8cec@gmail.com
+
+#uBITX
+uBITX firmware, written for the Raduino/Arduino control of uBITX transceivers
+This project is based on https://github.com/afarhan/ubitx and all copyright is inherited.
+The copyright information of the original is below.
+
+KD8CEC
+----------------------------------------------------------------------------
+Prepared or finished tasks for the next version
+ - Add TTS module
+ - Direct control for Student
+
+----------------------------------------------------------------------------
+## REVISION RECORD
+1.20
+ - Support uBITX V5
+ - Change to SDR Frequency (Remove just RTL-SDR's error Frequency (2390Hz))
+1.12
+ - Support Custom LPF Control
+ - Other Minor Bugs
+1.1
+ - Support Nextion LCD, TJC LCD
+ - Read & Backup uBITX, ADC Monitoring, ATT, IF-Shift and more on Nextion LCD (TJC LCD)
+ - Factory Reset (Both Character LCD and Nextion LCD are applicable)
+ - Support Signal Meter using ADC (A7 Port)
+ - Supoort I2C Signal Meter
+ - Spectrum
+ - Band Scan
+ - Memory Control on Nextion LCD (TJC LCD)
+ - Speed Change CW-Option on Nextion LCD
+ - Fixed Band Change Bug (Both Character LCD and Nextion LCD are applicable)
+ - uBITX Manager removed the Encode and Decode buttons. The procedure has become a bit easier.
+ - I2C Device Scan on uBITX Manager ( Both Character LCD and Nextion LCD are applicable)
+ - Si5351 I2C Address can be changed
+ - Recovery using QR-Code Data from Server
+ - Nextion LCD and TJC LCD can display Spectrum and CW Decode (using Stand alone S-Meter)
+ - Other Minor Bugs
+
+1.09 (Beta)
+- include 1.094 beta, 1.095 beta, 1.097 beta
+
+1.08
+ - Receive performance is improved compared to the original firmware or version 1.061
+ - ATT function has been added to reduce RF gain (Shift 45Mhz IF)
+ - Added the ability to connect SDR. (Low cost RTL-SDR available)
+ - Added a protocol to ADC Monitoring in CAT communications
+ - Various LCD support, 16x02 Parallel LCD - It is the LCD equipped with uBITX, 16x02 I2C LCD, 20x04 Parallel LCD, 20x04 I2C LCD, 16x02 I2C Dual LCD
+ - Added Extended Switch Support
+ - Support S Meter
+ - Added S-Meter setting assistant to uBITX Manager
+ - Add recovery mode (such as Factory Reset)
+ - There have been many other improvements and fixes. More information is available on the blog. (http://www.hamskey.com)
+
+1.07 (Beta)
+ - include 1.071 beta, 1.073 beta, 1.075 beta
+ - Features implemented in the beta version have been applied to Version 1.08 above.
+
+1.061
+ - Added WSPR
+ You only need uBITX to use WSPR. No external devices are required.
+ Added Si5351 module for WSPR
+ - Update uBITX Manager to Version 1.0
+ - Reduce program size
+ for WSPR
+ for other Module
+ - Fixed IF Shift Bug
+ Disable IF Shift on TX
+ IF shift available in USB mode
+ Fixed cat routine in IF Shift setup
+- Bugs fixed
+ cw start delay option
+ Auto key Bug
+ (found bug : LZ1LDO)
+ Message selection when Auto Key is used in RIT mode
+ (found bug : gerald)
+- Improve CW Keying (start TX)
+
+1.05
+ - include 1.05W, 1.051, 1.051W
+ - for WSPR Beta Test Version
+
+1.04
+ - Optimized from Version1.03
+ - Reduce program size (97% -> 95%)
+
+1.03
+ - Change eBFO Calibration Step (50 to 5)
+ - Change CW Frequency Display type
+
+1.02
+ - Applied CW Start Delay to New CW Key logic (This is my mistake when applying the new CW Key Logic.Since uBITX operations are not significantly affected, this does not create a separate Release, It will be reflected in the next release.) - complete
+ - Modified CW Key Logic for Auto Key, (available AutoKey function by any cw keytype) - complete
+ - reduce cpu use usage (working)
+ - reduce (working)
+
+1.01
+ - Fixed Cat problem with (IAMBIC A or B Selected)
+1.0
+ - rename 0.30 to 1.0
+
+0.35
+ - vfo to channel bug fixed (not saved mode -> fixed, channel has frequency and mode)
+ - add Channel tag (ch.1 ~ 10) by uBITX Manager
+ - add VFO to Channel, Channel To VFO
+
+0.34
+ - TX Status check in auto Keysend logic
+ - optimize codes
+ - change default tune step size, and fixed bug
+ - change IF shift step (1Hz -> 50Hz)
+
+0.33
+ - Added CWL, CWU Mode, (dont complete test yet)
+ - fixed VFO changed bug.
+ - Added Additional BFO for CWL, CWL
+ - Added IF Shift
+ - Change confirmation key PTT -> function key (not critical menus)
+ - Change CW Key Select type, (toggle -> select by dial)
+
+0.32
+ - Added function Scroll Frequencty on upper line
+ - Added Example code for Draw meter and remarked (you can see and use this code in source codes)
+ - Added Split function, just toggle VFOs when TX/RX
+
+0.31
+ - Fixed CW ADC Range error
+ - Display Message on Upper Line (anothor VFO Frequency, Tune Step, Selected Key Type)
+
+0.30
+ - implemented the function to monitor the value of all analog inputs. This allows you to monitor the status of the CW keys connected to your uBITX.
+ - possible to set the ADC range for CW Keying. If no setting is made, it will have the same range as the original code. If you set the CW Keying ADC Values using uBITX Manager 0.3, you can reduce the key error.
+ - Added the function to select Straight Key, IAMBICA, IAMBICB key from the menu.
+ - default Band select is Ham Band mode, if you want common type, long press function key at band select menu, uBITX Manager can be used to modify frequencies to suit your country.
+
+0.29
+ - Remove the use of initialization values in BFO settings - using crruent value, if factory reset
+ - Select Tune Step, default 0, 20, 50, 100, 200, Use the uBITX Manager to set the steps value you want. You can select Step by pressing and holding the Function Key (1sec ~ 2sec).
+ - Modify Dial Lock Function, Press the Function key for more than 3 seconds to toggle dial lock.
+ - created a new frequency tune method. remove original source codes, Threshold has been applied to reduce malfunction. checked the continuity of the user operating to make natural tune possible.
+ - stabilize and remove many warning messages - by Pullrequest and merge
+ - Changed cw keying method. removed the original code and applied Ron's code and Improved compatibility with original hardware and CAT commnication. It can be used without modification of hardware.
+
+0.28
+ - Fixed CAT problem with hamlib on Linux
+ - restore Protocol autorecovery logic
+
+0.27
+ (First alpha test version, This will be renamed to the major version 1.0)
+ - Dual VFO Dial Lock (vfoA Dial lock)
+ - Support Ham band on uBITX
+ default Hamband is regeion1 but customize by uBITX Manager Software
+ - Advanced ham band options (Tx control) for use in all countries. You can adjust it yourself.
+ - Convenience of band movement
+
+0.26
+ - only Beta tester released & source code share
+ - find a bug on none initial eeprom uBITX - Fixed (Check -> initialized & compatible original source code)
+ - change the version number 0.26 -> 0.27
+ - Prevent overflow bugs
+ - bug with linux based Hamlib (raspberry pi), It was perfect for the 0.224 version, but there was a problem for the 0.25 version.
+ On Windows, ham deluxe, wsjt-x, jt65-hf, and fldigi were successfully run. Problem with Raspberry pi.
+
+0.25
+ - Beta Version Released
+ http://www.hamskey.com/2018/01/release-beta-version-of-cat-support.html
+ - Added CAT Protocol for uBITX
+ - Modified the default usb carrier value used when the setting is wrong.
+ - Fixed a routine to repair when the CAT protocol was interrupted.
+
+0.24
+ - Program optimization
+ reduce usage ram rate (string with M() optins)
+ - Optimized CAT protocol for wsjt-x, fldigi
+
+0.23
+ - added delay_background() , replace almost delay() to delay_background for prevent timeout
+ - cat library compatible with FT-817 Command
+ switch VFOA / VFOB,
+ Read Write CW Speed
+ Read Write CW Delay Time
+ Read Write CW Pitch (with sidetone)
+ All of these can be controlled by Hamradio deluxe.
+
+ - modified cat libray function for protocol for CAT communication is not broken in CW or TX mode
+ - Ability to change CW Delay
+ - Added Dial Lock function
+ - Add functions CW Start dely (TX -> CW interval)
+ - Automatic storage of VFO frequency
+ It was implemented by storing it only once when the frequency stays 10 seconds or more after the change.
+ (protect eeprom life)
+
+
+0.22
+ - fixed screen Update Problem
+ - Frequency Display Problem - Problems occur below 1Mhz
+ - added function Enhanced CAT communication
+ - replace ubitx_cat.ino to cat_libs.ino
+ - Save mode when switching to VFOA / VFOB
+
+
+0.21
+ - fixed the cw side tone configuration.
+ - Fix the error that the frequency is over.
+ - fixed frequency display (alignment, point)
+
+
+0.20
+ - original uBITX software (Ashhar Farhan)
+
+## Original README.md
+uBITX firmware, written for the Raduino/Arduino control of uBITX transceigers
+
+Copyright (C) 2017, Ashhar Farhan
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see .
diff --git a/VersionInfo.txt b/VersionInfo.txt
new file mode 100644
index 0000000..d3f0eae
--- /dev/null
+++ b/VersionInfo.txt
@@ -0,0 +1,30 @@
+This file will guide you to change the source code file.
+For Windows-based Arduino IDE users, the directory name and the Main source file name must be the same.
+You do not need to learn github to download .hex files or source code that I release.
+However, if you want to see what I'm doing right now, you should use the github homepage.
+
+You do not need to learn git to suggest source code. If you give me an e-mail, I will correct it at any time.
+If you have not joined the BITX Group, join group. There will be discussions on various topics every day.
+I am getting a lot of hints from the group.
+
+Ian KD8CEC
+kd8cec@gmail.com
+==================================================================
+Files modified in Version1.08 Beta
+
+1.Delted Files.
+
+2.Added Files
+
+3.Modified Files
+ - ubitx_20.ino
+ - ubitx_ui.ino
+ - cat_libs.ino
+ - ubitx.h
+ - ubitx_eemap.h
+ - ubitx_lcd_1602.ino
+ - ubitx_lcd_1602Dual.ino
+ - ubitx_lcd_2004.ino
+ - ubitx_wspr.ino
+
+
\ No newline at end of file
diff --git a/ubitx_20/cat_libs.ino b/ubitx_20/cat_libs.ino
new file mode 100644
index 0000000..70e2422
--- /dev/null
+++ b/ubitx_20/cat_libs.ino
@@ -0,0 +1,1032 @@
+/*************************************************************************
+ KD8CEC's CAT Library for uBITX and HAM
+ This source code is written for uBITX, but it can also be used on other radios.
+
+ The CAT protocol is used by many radios to provide remote control to comptuers through
+ the serial port.
+ it is based on FT-817, uBITX's only protocol has been added and will be added in the future.
+ In addition, simple things such as FT-857 frequency control and PTT control can also be
+ transmitted to the FT-857 protocol.
+
+ This code refers to the following code.
+ - FT857D CAT Library, by Pavel Milanes, CO7WT, pavelmc@gmail.com
+ https://github.com/pavelmc/FT857d/
+ - Ham Radio Control Libraries, https://sourceforge.net/projects/hamlib/
+ - Not found protocols decription were analyzed using an RS-232 analyzer.
+ using FT-817 and
+ - http://www.ka7oei.com/ft817_meow.html <-- It was a great help here.
+
+-----------------------------------------------------------------------------
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see .
+
+**************************************************************************/
+
+#include
+
+#include "ubitx.h"
+
+//for broken protocol
+#define CAT_RECEIVE_TIMEOUT 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
+
+/*
+ * KC4UPR - IOP update, 2020-05-03
+ *
+ * Send the current mode to the I/O Processor.
+ */
+void iopSendMode(char cw_mode, char is_usb, char digi_mode)
+{
+ rig_mode mode;
+
+ // NOTE: eventually, add sideband (is_usb) to all of these.
+ if (cw_mode > 0) {
+ mode = rig_mode::cw;
+ } else if (digi_mode > 0) {
+ //mode = (is_usb ? RIG_MODE_DGU : RIG_MODE_DGL);
+ mode = rig_mode::digi;
+// } else if (is_test) {
+// mode = (is_usb ? RIG_MODE_TTU : RIG_MODE_TTL);
+ } else {
+ mode = rig_mode::ssb; //(is_usb ? usb : lsb);
+ }
+ sendIOPModeCommand(mode);
+}
+
+unsigned int skipTimeCount = 0;
+byte CAT_BUFF[34];
+byte CAT_SNDBUFF[34];
+
+byte error_buf[17];
+
+void SendCatData(byte sendCount)
+{
+ // KC4UPR--uBITX IOP: Adding an additional byte at the beginning that
+ // indicates that this is a "CAT mode" transmission. Extra byte includes
+ // a prefix, as well as the number of bytes being sent.
+ //
+ // NOTE: Need to do some error checking at some point to ensure we don't
+ // try to send more than 15 bytes!!!
+ Serial.write(prefixAndLengthToByte(CAT_PREFIX, sendCount));
+
+ for (byte i = 0; i < sendCount; i++)
+ Serial.write(CAT_BUFF[i]);
+ //Serial.flush();
+}
+
+//PROTOCOL : 0x01
+//Computer ->(frequency)-> TRCV CAT_BUFF
+void CatSetFreq(byte fromType)
+{
+ //CAT_BUFF
+ byte i;
+ unsigned long tempFreq = 0;
+
+ if (fromType == 2 || fromType == 3) {
+ Serial.write(ACK);
+ return;
+ }
+
+ //2 digit in 1 byte (4 bit + 4bit) * 4.5 byte
+ for (i = 0; i < 4; i++)
+ {
+ tempFreq *= 10;
+ tempFreq += CAT_BUFF[i] >> 4;
+ tempFreq *= 10;
+ tempFreq += CAT_BUFF[i] & 0x0f;
+ }
+
+ tempFreq *= 10;
+ tempFreq += CAT_BUFF[4] >> 4;
+
+ if (!inTx && (frequency != tempFreq))
+ {
+ //Check Frequency Range
+ if (tempFreq >= LOWEST_FREQ_DIAL && tempFreq <= HIGHEST_FREQ_DIAL)
+ {
+ setFrequency(tempFreq);
+ updateDisplay();
+ }
+ else
+ {
+ //KD8CEC
+ //Remark for rduce program size, if you need, you can remove remark,
+ //however alomost rig control software available 1.0 ~ 50Mhz
+ //printLine(0, "OUT OF RANGE!!!");
+ //delay_background(300, 0);
+ }
+ }
+
+ Serial.write(ACK);
+}
+
+//#define BCD_LEN 9
+//PROTOCOL : 0x03
+//Computer <-(frequency)-> TRCV CAT_BUFF
+//void CatGetFreqMode(unsigned long freq, byte fromType)
+void CatGetFreqMode(unsigned long freq) //for remove warning messages
+{
+ int i;
+ byte tmpValue;
+ unsigned BCD_LEN = 9;
+
+ if (BCD_LEN & 1) {
+ CAT_BUFF[BCD_LEN / 2] &= 0x0f;
+ CAT_BUFF[BCD_LEN / 2] |= (freq % 10) << 4;
+
+ freq /= 10;
+ }
+ for (i = (BCD_LEN / 2) - 1; i >= 0; i--) {
+ tmpValue = freq % 10;
+ freq /= 10;
+ tmpValue |= (freq % 10) << 4;
+ freq /= 10;
+ CAT_BUFF[i] = tmpValue;
+ }
+
+ //Mode Check
+ if (cwMode == 0)
+ {
+ if (isUSB)
+ CAT_BUFF[4] = CAT_MODE_USB;
+ else
+ CAT_BUFF[4] = CAT_MODE_LSB;
+ }
+ else if (cwMode == 1)
+ {
+ CAT_BUFF[4] = CAT_MODE_CW;
+ }
+ else
+ {
+ CAT_BUFF[4] = CAT_MODE_CW;
+ }
+
+ SendCatData(5);
+}
+
+//void CatSetSplit(boolean isSplit, byte fromType)
+void CatSetSplit(boolean isSplit) //for remove warning messages
+{
+ if (isSplit)
+ splitOn = 1;
+ else
+ splitOn = 0;
+
+ Serial.write(ACK);
+}
+
+void CatSetPTT(boolean isPTTOn, byte fromType)
+{
+ // KC4UPR - so I think this means, that if we're currently processing a CW keyer (auto/manual),
+ // not to accept a CAT command in the middle of it.
+ if ((!inTx) && (fromType == 2 || fromType == 3)) {
+ Serial.write(ACK);
+ return;
+ }
+
+ // Set PTT Mode
+ if (isPTTOn)
+ {
+ if (!inTx)
+ {
+ txCAT = true;
+
+ // KC4UPR - added the next line to tell the IOP we're transmitting
+ sendIOPStartTxCommand();
+
+ startTx(TX_SSB, 1);
+ //Exit menu, Memory Keyer... ETC
+ //if (isCWAutoMode > 0) {
+ // isCWAutoMode = 0;
+ // printLineF2(F("AutoKey Exit/CAT"));
+ // //delay_background(1000, 0);
+ //}
+ }
+ }
+ else
+ {
+ if (inTx)
+ {
+ // KC4UPR - added the next line to tell the IOP we're not transmitting
+ sendIOPStopTxCommand();
+
+ stopTx();
+ txCAT = false;
+ }
+ }
+
+ Serial.write(ACK);
+}
+
+void CatVFOToggle(boolean isSendACK, byte fromType)
+{
+ if (fromType != 2 && fromType != 3) {
+ menuVfoToggle(1);
+ }
+
+ if (isSendACK)
+ Serial.write(ACK); //Time
+}
+
+void CatSetMode(byte tmpMode, byte fromType)
+{
+ if (fromType == 2 || fromType == 3) {
+ Serial.write(ACK);
+ return;
+ }
+
+ if (!inTx)
+ {
+ switch(tmpMode) {
+ case CAT_MODE_CW:
+ cwMode = 2; // should be CWU
+ break;
+
+ case CAT_MODE_CWR:
+ cwMode = 1; // should be CWL
+ break;
+
+ case CAT_MODE_USB:
+ cwMode = 0;
+ digiMode = 0;
+ isUSB = true;
+ break;
+
+ case CAT_MODE_LSB:
+ cwMode = 0;
+ digiMode = 0;
+ isUSB = false;
+ break;
+
+ case CAT_MODE_DIG:
+ cwMode = 0;
+ digiMode = 1;
+ isUSB = true; // DGU - but need to eventually use the FT-817 customization
+ }
+ iopSendMode(cwMode, isUSB, digiMode);
+ setFrequency(frequency);
+ updateDisplay();
+ }
+
+ Serial.write(ACK);
+}
+
+//Read EEProm by uBITX Manager Software
+//void ReadEEPRom(byte fromType)
+void ReadEEPRom() //for remove warnings.
+{
+ //5BYTES
+ //CAT_BUFF[0] [1] [2] [3] [4] //4 COMMAND
+ //0, 1 START ADDRESS
+ uint16_t eepromStartIndex = CAT_BUFF[0] + CAT_BUFF[1] * 256;
+ uint16_t eepromReadLength = CAT_BUFF[2] + CAT_BUFF[3] * 256;;
+ byte checkSum = 0;
+ byte read1Byte = 0;
+
+ // KC4UPR--uBITX IOP: Adding an additional byte at the beginning that
+ // indicates that this is a "Memory Manager mode" transmission.
+ // Then we repeat some of the CAT_BUFF data.
+ Serial.write(prefixAndLengthToByte(RAD_EEPROM_READ_PREFIX, 5));
+ Serial.write(CAT_BUFF[0]);
+ Serial.write(CAT_BUFF[1]);
+ Serial.write(CAT_BUFF[2]);
+ Serial.write(CAT_BUFF[3]);
+
+ Serial.write(0x02); //STX
+ checkSum = 0x02;
+ //I2C Scanner
+ //Magic Key Start 59414, Length : 48583
+ //if (eepromStartIndex == 59414 && eepromReadLength == 48583)
+ if (CAT_BUFF[0] == 0x16 && CAT_BUFF[1] == 0xe8)
+ {
+ for (uint8_t i = 1; i < 127; i++)
+ {
+ Wire.beginTransmission(i);
+ read1Byte = Wire.endTransmission();
+ if (read1Byte == 0)
+ {
+ Serial.write(i);
+ }
+ else
+ {
+ Serial.write(0);
+ }
+ }
+ }
+ else
+ {
+ for (uint16_t i = 0; i < eepromReadLength; i++)
+ {
+ read1Byte = EEPROM.read(eepromStartIndex + i);
+ checkSum += read1Byte;
+ Serial.write(read1Byte);
+ }
+ }
+
+ Serial.write(checkSum);
+ Serial.write(ACK);
+}
+
+//Write just proecess 1byes
+//void WriteEEPRom(byte fromType)
+void WriteEEPRom(void) //for remove warning
+{
+ //5BYTES
+ uint16_t eepromStartIndex = CAT_BUFF[0] + CAT_BUFF[1] * 256;
+ byte write1Byte = CAT_BUFF[2];
+
+ // KC4UPR--uBITX IOP: Adding an additional byte at the beginning that
+ // indicates that this is a "Memory Manager mode" transmission.
+ //
+ // Also indicates that we are going to be sending two bytes of data.
+ Serial.write(prefixAndLengthToByte(RAD_EEPROM_WRITE_PREFIX, 2));
+
+ //Check Checksum
+ if (CAT_BUFF[3] != ((CAT_BUFF[0] + CAT_BUFF[1] + CAT_BUFF[2]) % 256))
+ {
+ Serial.write(0x56); //CHECK SUM ERROR
+ Serial.write(ACK);
+ }
+ else
+ {
+ //Special Command
+ if (eepromStartIndex == 13131) //Magic Key
+ {
+ if (write1Byte == 0x51) //Restart
+ {
+ asm volatile (" jmp 0");
+ }
+ }
+ else
+ {
+ EEPROM.write(eepromStartIndex, write1Byte);
+ }
+
+ Serial.write(0x77); //OK
+ Serial.write(ACK);
+ }
+}
+
+//void ReadEEPRom_FT817(byte fromType)
+void ReadEEPRom_FT817(void) //for remove warnings
+{
+ byte temp0 = CAT_BUFF[0];
+ byte temp1 = CAT_BUFF[1];
+
+ CAT_BUFF[0] = 0;
+ CAT_BUFF[1] = 0;
+
+ switch (temp1)
+ {
+ case 0x45 : //
+ if (temp0 == 0x03)
+ {
+ CAT_BUFF[0] = 0x00;
+ CAT_BUFF[1] = 0xD0;
+ }
+ break;
+ case 0x47 : //
+ if (temp0 == 0x03)
+ {
+ CAT_BUFF[0] = 0xDC;
+ CAT_BUFF[1] = 0xE0;
+ }
+ break;
+ case 0x55 :
+ //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")
+ //3 :
+ //4 : Home Select 0 = (Not HOME), 1 = HOME memory
+ //5 : Memory/MTUNE select 0 = Memory, 1 = MTUNE
+ //6 :
+ //7 : MEM/VFO Select 0 = Memory, 1 = VFO (A or B - see bit 0)
+ CAT_BUFF[0] = 0x80 + (vfoActive == VFO_B ? 1 : 0);
+ CAT_BUFF[1] = 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_BUFF[0] = 0xC0;
+ CAT_BUFF[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_BUFF[0] = 0xB2;
+ CAT_BUFF[1] = 0x42;
+ break;
+ case 0x5E :
+ //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_BUFF[0] = sideTonePitch;
+ CAT_BUFF[1] = 0x25;
+ break;
+ case 0x61 : //Sidetone (Volume) (#44)
+ CAT_BUFF[0] = sideToneSub;
+ CAT_BUFF[1] = 0x08;
+ 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_BUFF[0] = 0x32;
+ CAT_BUFF[1] = 0x08;
+ break;
+ case 0x60 : //CW Delay (10-2500 ms) (#17) From 1 to 250 (decimal) with each step representing 10 ms
+ CAT_BUFF[0] = cwDelayTime;
+ CAT_BUFF[1] = 0x32;
+ break;
+ case 0x62 : //
+ //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_BUFF[0] = 1200 / cwSpeed - 4;
+ CAT_BUFF[1] = 0xB2;
+ 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_BUFF[0] = 0xB2;
+ CAT_BUFF[1] = 0xA5;
+ break;
+ case 0x64 : //
+ break;
+ case 0x67 : //6-0 SSB Mic (#46) Contains 0-100 (decimal) as displayed
+ CAT_BUFF[0] = 0xB2;
+ CAT_BUFF[1] = 0xB2;
+ break; case 0x69 : //FM Mic (#29) Contains 0-100 (decimal) as displayed
+ case 0x78 :
+ if (cwMode == 0)
+ {
+ if (isUSB)
+ CAT_BUFF[0] = CAT_MODE_USB;
+ else
+ CAT_BUFF[0] = CAT_MODE_LSB;
+ }
+ else if (cwMode == 1)
+ {
+ CAT_BUFF[0] = CAT_MODE_CW;
+ }
+ else if (cwMode == 2)
+ {
+ CAT_BUFF[0] = CAT_MODE_CW;
+ }
+
+ if (CAT_BUFF[0] != 0) CAT_BUFF[0] = 1 << 5;
+ 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_BUFF[0] = 0x00;
+ CAT_BUFF[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_BUFF[0] = (splitOn ? 0xFF : 0x7F);
+ break;
+ case 0xB3 : //
+ CAT_BUFF[0] = 0x00;
+ CAT_BUFF[1] = 0x4D;
+ break;
+
+ }
+
+ // sent the data
+ SendCatData(2);
+}
+
+void WriteEEPRom_FT817(byte fromType)
+{
+ //byte temp0 = CAT_BUFF[0];
+ byte temp1 = CAT_BUFF[1];
+
+ CAT_BUFF[0] = 0;
+ CAT_BUFF[1] = 0;
+
+ if (fromType == 2 || fromType == 3) {
+ SendCatData(2);
+ Serial.write(ACK);
+ return;
+ }
+ switch (temp1)
+ {
+ case 0x55 :
+ //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")
+ //3 :
+ //4 : Home Select 0 = (Not HOME), 1 = HOME memory
+ //5 : Memory/MTUNE select 0 = Memory, 1 = MTUNE
+ //6 :
+ //7 : MEM/VFO Select 0 = Memory, 1 = VFO (A or B - see bit 0)
+ if (CAT_BUFF[2] & 0x01) //vfoB
+ {
+ //nowVFO Check
+ if (vfoActive != VFO_B)
+ {
+ CatVFOToggle(false, fromType);
+ }
+ }
+ else
+ {
+ //vfoA
+ if (vfoActive != VFO_A)
+ {
+ CatVFOToggle(false, fromType);
+ }
+ }
+ 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_BUFF[0] = 0xC0;
+ CAT_BUFF[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_BUFF[0] = 0xB2;
+ CAT_BUFF[1] = 0x42;
+ break;
+ */
+ case 0x5E :
+ //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
+ sideTonePitch = (CAT_BUFF[2] & 0x0F);
+
+ if (sideTonePitch != 0 || sideToneSub != 0)
+ {
+ sideTone = (sideTonePitch * 50 + 300) + sideToneSub;
+ printLineF2(F("Sidetone set! CAT"));
+ EEPROM.put(CW_SIDETONE, sideTone);
+ delay(300); //If timeout errors occur in the calling software, remove them
+ clearLine2();
+ }
+ break;
+
+ case 0x61 : //Sidetone (Volume) (#44)
+ sideToneSub = (CAT_BUFF[2] & 0x7F);
+ if (sideTonePitch != 0 || sideToneSub != 0)
+ {
+ sideTone = (sideTonePitch * 50 + 300) + sideToneSub;
+ printLineF2(F("Sidetone set! CAT"));
+ EEPROM.put(CW_SIDETONE, sideTone);
+ delay(300); //If timeout errors occur in the calling software, remove them
+ clearLine2();
+ line2DisplayStatus = 0;
+ }
+ 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_BUFF[0] = 0x32;
+ CAT_BUFF[1] = 0x08;
+ break;
+ */
+ case 0x60 : //CW Delay (10-2500 ms) (#17) From 1 to 250 (decimal) with each step representing 10 ms
+ //CAT_BUFF[0] = 0x19;
+ cwDelayTime = CAT_BUFF[2];
+ printLineF2(F("CW Speed set!"));
+ EEPROM.put(CW_DELAY, cwDelayTime);
+ delay(300);
+ clearLine2();
+ break;
+ case 0x62 : //
+ //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
+ cwSpeed = 1200 / ((CAT_BUFF[2] & 0x3F) + 4);
+ printLineF2(F("CW Speed set!"));
+ EEPROM.put(CW_SPEED, cwSpeed);
+ delay(300);
+ clearLine2();
+
+ 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_BUFF[0] = 0xB2;
+ CAT_BUFF[1] = 0xA5;
+ break;
+ case 0x64 : //
+ //CAT_BUFF[0] = 0xA5;
+ //CAT_BUFF[1] = 0x00;
+ break;
+ case 0x67 : //6-0 SSB Mic (#46) Contains 0-100 (decimal) as displayed
+ CAT_BUFF[0] = 0xB2;
+ CAT_BUFF[1] = 0xB2;
+ //break; case 0x69 : //FM Mic (#29) Contains 0-100 (decimal) as displayed
+ //CAT_BUFF[0] = 0x32;
+ //CAT_BUFF[1] = 0x32;
+ //break;
+ case 0x78 :
+ CAT_BUFF[0] = catGetMode();
+ // check, it must be a bit argument
+ if (CAT_BUFF[0] != 0) CAT_BUFF[0] = 1<<5;
+ 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_BUFF[0] = 0x00;
+ CAT_BUFF[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_BUFF[0] = (isSplitOn ? 0xFF : 0x7F);
+ break;
+ case 0xB3 : //
+ CAT_BUFF[0] = 0x00;
+ CAT_BUFF[1] = 0x4D;
+ break;
+ */
+ }
+
+ // sent the data
+ SendCatData(2);
+ Serial.write(ACK);
+}
+
+const byte anlogPinIndex[6] = {A0, A1, A2, A3, A6, A7};
+
+//Read ADC Value by uBITX Manager Software
+void ReadADCValue(void)
+{
+ //ADC MAP for uBITX
+ int readedADCValue;
+ //5BYTES
+ //CAT_BUFF[0] [1] [2] [3] [4] //4 COMMAND
+ //0 READ ADDRESS
+ readedADCValue = analogRead(anlogPinIndex[CAT_BUFF[0]]);
+ CAT_BUFF[0] = readedADCValue >> 8;
+ CAT_BUFF[1] = readedADCValue;
+ SendCatData(2);
+ Serial.write(ACK);
+}
+
+void SetIFSValue(void)
+{
+ //Set IFShift Value
+ isIFShift = CAT_BUFF[0];
+ ifShiftValue = CAT_BUFF[1] + CAT_BUFF[2] * 256;
+ setFrequency(frequency);
+ SetCarrierFreq();
+ updateLine2Buffer(1); //option, perhap not need
+ Serial.write(ACK);
+}
+
+//void CatRxStatus(byte fromType)
+void CatRxStatus(void) //for remove warning
+{
+ byte sMeterValue = 0;
+
+ /*
+ http://www.ka7oei.com/ft817_meow.html
+ Command E7 - Read Receiver Status: This command returns one byte. Its contents are valid only when the '817 is in receive mode and it should be ignored when transmitting.
+ The lower 4 bits (0-3) of this byte indicate the current S-meter reading. 00 refers to an S-Zero reading, 04 = S4, 09 = S9, 0A = "10 over," 0B = "20 over" and so on up to 0F.
+ Bit 4 contains no useful information.
+ Bit 5 is 0 in non-FM modes, and it is 0 if the discriminator is centered (within 3.5 kHz for standard FM) when in the FM, FMN, or PKT modes, and 1 if the receiver is off-frequency.
+ Bit 6 is 0 if the CTCSS or DCS is turned off (or in a mode where it is not available.) It is also 0 if there is a signal being receive and the correct CTCSS tone or DCS code is being decoded.
+ It is 1 if there is a signal and the CTCSS/DCS decoding is enable, but the wrong CTCSS tone, DCS code, or no CTCSS/DCS is present.
+ Bit 7 is 0 if there is a signal present, or 1 if the receiver is squelched.
+ */
+ // The lower 4 bits (0-3) of this byte indicate the current S-meter reading. 00 refers to an S-Zero reading, 04 = S4, 09 = S9, 0A = "10 over," 0B = "20 over" and so on up to 0F.
+ //0~8
+ switch (scaledSMeter)
+ {
+ case 8 : sMeterValue = 0x0B;
+ break;
+ case 7 : sMeterValue = 0x0A;
+ break;
+ case 6 : sMeterValue = 0x09;
+ break;
+ case 5 : sMeterValue = 0x07;
+ break;
+ case 4 : sMeterValue = 0x05;
+ break;
+ case 3 : sMeterValue = 0x04;
+ break;
+ case 2 : sMeterValue = 0x02;
+ break;
+ case 1 : sMeterValue = 0x01;
+ break;
+ }
+
+/*
+ sMeterValue = (scaledSMeter * 2) -1;
+ if (sMeterValue > 0)
+ sMeterValue--;
+*/
+
+ CAT_BUFF[0] = sMeterValue & 0b00001111;
+ SendCatData(1);
+}
+
+
+//void CatTxStatus(byte fromType)
+void CatTxStatus(void) //for remove warning
+{
+ boolean isHighSWR = false;
+ boolean isSplitOn = false;
+
+ /*
+ Inverted -> *ptt = ((p->tx_status & 0x80) == 0); <-- souce code in ft817.c (hamlib)
+ */
+ CAT_BUFF[0] = ((inTx ? 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
+
+ SendCatData(1);
+}
+
+unsigned long rxBufferArriveTime = 0;
+byte rxBufferCheckCount = 0;
+
+//Prevent Stack Overflow
+byte isProcessCheck_Cat = 0;
+
+//char iopStatusWindow[4] = " "; // may need to move this if it's not visible to ubitx_lcd_1602
+char iopMenuDisplay[17] = " ";
+
+// KC4UPR - these are used to delay the display of the Smeter, if the
+// IOP status has recently been displayed, to give time to see it.
+// Put these here because of how Arduino IDE puts .ino files together.
+//#define SMETER_DELAY_TIME 5000
+//bool displaySmeter = true;
+//int delaySmeter;
+int stateTopLine = 0;
+
+//fromType normal : 0, TX : 1, CW_STRAIGHT : 2, CW_PADDLE : 3, CW_AUTOMODE : 4
+//if cw mode, no delay
+void Check_Cat(byte fromType)
+{
+ byte i;
+
+ static PrefixID readPrefix;
+ static uint8_t readLength;
+ static IOPMessage msg;
+ static bool read_in_progress = false;
+
+ //Check Serial Port Buffer
+ if (Serial.available() == 0 && !read_in_progress)
+ {
+ //Set Buffer Clear status
+ //rxBufferCheckCount = 0;
+ return;
+ }
+ // KC4UPR - IOP update: changed this to 6 characters, because we're going to have a
+ // first character which defines if this is CAT or IOP.
+ else // if (Serial.available() < 6) //5)
+ {
+ if (!read_in_progress) {
+ byte first = Serial.read();
+ readPrefix = byteToPrefix(first);
+ readLength = byteToLength(first);
+
+ rxBufferCheckCount = Serial.available();
+ rxBufferArriveTime = millis() + CAT_RECEIVE_TIMEOUT;
+
+ read_in_progress = true;
+ }
+
+ if (Serial.available() < readLength) { // not ready to read everything yet (not enough bytes)
+
+ if (rxBufferCheckCount < Serial.available()) { // increase buffer count, slow arrival
+ rxBufferCheckCount = Serial.available();
+ rxBufferArriveTime = millis() + CAT_RECEIVE_TIMEOUT; // update time for timeout
+
+ } else if (rxBufferArriveTime < millis()) { // timeout, so clear buffer
+ for (i = 0; i < Serial.available(); i++)
+ rxBufferCheckCount = Serial.read();
+ rxBufferCheckCount = 0;
+ read_in_progress = false;
+
+ }
+
+ return;
+ }
+
+ for (int i = 0; i < readLength; i++) {
+ CAT_BUFF[i] = Serial.read();
+ }
+ }
+
+ // KC4UPR: I don't understand why this is here or how/when it will ever get called, but I will leave
+ // it alone for now.
+ if (isProcessCheck_Cat == 1)
+ return;
+
+ isProcessCheck_Cat = 1;
+
+ if (readPrefix == IOP_PREFIX) {
+ recvIOPMessage(msg, CAT_BUFF, readLength); // not super robust... if IOP ever sends more (or less) than a 5 (6) byte message
+ // following assumes it's a status message, 4 chars (including trailing null, which I'm ignoring...
+ switch(msg.id) {
+ /*case IOP_SSB_STATUS_MSG:
+ case IOP_DGT_STATUS_MSG:
+ case IOP_CW_STATUS_MSG:
+ case IOP_TEST_STATUS_MSG:
+ iopStatusWindow[0] = msg.data[0];
+ iopStatusWindow[1] = msg.data[1];
+ iopStatusWindow[2] = msg.data[2];
+ displaySmeter = false;
+ delaySmeter = millis() + SMETER_DELAY_TIME;
+ break;*/
+
+ case IOP_MODE_REQUEST:
+ iopSendMode(cwMode, isUSB, digiMode);
+ break;
+
+ case IOP_MENU_DISPLAY_MSG:
+ for (int i = 0; i < 16; i++) {
+ iopMenuDisplay[i] = msg.data[i];
+ }
+ stateTopLine = 2;
+ sprintf(error_buf, "# recv'd: %3d", readLength);
+ sendIOPDebugMessage(error_buf);
+ sendIOPDebugMessage(iopMenuDisplay);
+ break;
+
+ case IOP_MENU_INACTIVE_MSG:
+ stateTopLine = 0;
+ line2DisplayStatus = 0; // trying to force an update
+ break;
+ }
+
+ } else if (readPrefix == CAT_PREFIX) {
+
+ //reference : http://www.ka7oei.com/ft817_meow.html
+ switch(CAT_BUFF[4])
+ {
+ //The stability has not been verified and there seems to be no need. so i remarked codes,
+ //if you need, unmark lines
+ /*
+ case 0x00 : //Lock On
+ if (isDialLock == 1) //This command returns 00 if it was unlocked, and F0 if already locked.
+ CAT_BUFF[0] = 0xF0;
+ else {
+ CAT_BUFF[0] = 0x00;
+ setDialLock(1, fromType);
+ }
+ Serial.write(CAT_BUFF[0]); //Time
+ break;
+ case 0x80 : //Lock Off
+ if (isDialLock == 0) //This command returns 00 if the '817 was already locked, and F0 (HEX) if already unlocked.
+ CAT_BUFF[0] = 0xF0;
+ else {
+ CAT_BUFF[0] = 0x00;
+ setDialLock(0, fromType);
+ }
+ Serial.write(CAT_BUFF[0]); //Time
+ break;
+ */
+
+ case 0x01 : //Set Frequency
+ CatSetFreq(fromType);
+ break;
+
+ case 0x02 : //Split On
+ case 0x82: //Split Off
+ CatSetSplit(CAT_BUFF[4] == 0x02);
+ break;
+
+ case 0x03 : //Read Frequency and mode
+ CatGetFreqMode(frequency);
+ break;
+
+ case 0x07 : //Set Operating Mode
+ CatSetMode(CAT_BUFF[0], fromType);
+ break;
+
+ case 0x08 : //Set PTT_ON
+ case 0x88: //Set PTT Off
+ CatSetPTT(CAT_BUFF[4] == 0x08, fromType);
+ break;
+
+ case 0x81: //Toggle VFO
+ CatVFOToggle(true, fromType);
+ break;
+
+ case 0xDB: //Read uBITX EEPROM Data
+ ReadEEPRom(); //Call by uBITX Manager Program
+ break;
+ case 0xBB: //Read FT-817 EEPROM Data (for comfirtable)
+ ReadEEPRom_FT817();
+ break;
+
+ case 0xDC: //Write uBITX EEPROM Data
+ WriteEEPRom(); //Call by uBITX Manager Program
+ break;
+ case 0xBC: //Write FT-817 EEPROM Data (for comfirtable)
+ WriteEEPRom_FT817(fromType);
+ break;
+
+ case 0xDD: //Read uBITX ADC Data
+ ReadADCValue(); //Call by uBITX Manager Program
+ break;
+
+ case 0xDE: //IF-Shift Control by CAT
+ SetIFSValue(); //
+ break;
+
+ case 0xE7 : //Read RX Status
+ CatRxStatus();
+ break;
+ case 0xF7: //Read TX Status
+ CatTxStatus();
+ break;
+ default:
+ /*
+ char buff[16];
+ sprintf(buff, "DEFAULT : %x", CAT_BUFF[4]);
+ printLine2(buff);
+ */
+ Serial.write(ACK);
+ break;
+ } //end of switch
+ }
+
+ isProcessCheck_Cat = 0;
+ read_in_progress = false;
+}
+
+void Init_Cat(long baud, int portConfig)
+{
+ Serial.begin(baud, portConfig);
+ Serial.flush();
+
+ // At start, immediately send mode to IOP. Currently, IOP has no way to
+ // request the mode.
+ // Moving this to main setup loop. Here, it may actually occur before the Raduino knows its mode!
+ //iopSendMode(cwMode, isUSB, digiMode, isTest);
+}
diff --git a/ubitx_20/ubitx.h b/ubitx_20/ubitx.h
new file mode 100644
index 0000000..8a97063
--- /dev/null
+++ b/ubitx_20/ubitx.h
@@ -0,0 +1,335 @@
+/*************************************************************************
+ header file for C++ by KD8CEC
+-----------------------------------------------------------------------------
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see .
+**************************************************************************/
+#ifndef _UBITX_HEADER__
+#define _UBITX_HEADER__
+
+#include //for Linux, On Linux it is case sensitive.
+
+//==============================================================================
+// Compile Option
+//==============================================================================
+//Ubitx Board Version
+#define UBITX_BOARD_VERSION 2 //v1 ~ v4 : 4, v5: 5
+
+//Depending on the type of LCD mounted on the uBITX, uncomment one of the options below.
+//You must select only one.
+//#define UBITX_DISPLAY_LCD1602P //LCD mounted on unmodified uBITX (Parallel)
+//#define UBITX_DISPLAY_LCD1602I //I2C type 16 x 02 LCD
+//#define UBITX_DISPLAY_LCD1602I_DUAL //I2C type 16 x02 LCD Dual
+//#define UBITX_DISPLAY_LCD2004P //24 x 04 LCD (Parallel)
+//#define UBITX_DISPLAY_LCD2004I //I2C type 24 x 04 LCD
+#define UBITX_DISPLAY_NEXTION //NEXTION LCD
+
+//#define UBITX_DISPLAY_NEXTION_SAFE //Only EEProm Write 770~775
+#define I2C_LCD_MASTER_ADDRESS_DEFAULT 0x27 //0x27 //DEFAULT, if Set I2C Address by uBITX Manager, read from EEProm
+#define I2C_LCD_SECOND_ADDRESS_DEFAULT 0x3F //0x27 //only using Dual LCD Mode
+
+//Select betwen Analog S-Meter and DSP (I2C) Meter
+#define USE_I2CSMETER
+
+#define EXTEND_KEY_GROUP1 //MODE, BAND(-), BAND(+), STEP
+//#define EXTEND_KEY_GROUP2 //Numeric (0~9), Point(.), Enter //Not supported in Version 1.0x
+
+//Custom LPF Filter Mod
+//#define USE_CUSTOM_LPF_FILTER //LPF FILTER MOD
+
+//#define ENABLE_FACTORYALIGN
+//#define FACTORY_RECOVERY_BOOTUP //Whether to enter Factory Recovery mode by pressing FKey and turning on power
+//#define ENABLE_ADCMONITOR //Starting with Version 1.07, you can read ADC values directly from uBITX Manager. So this function is not necessary.
+
+extern byte I2C_LCD_MASTER_ADDRESS; //0x27 //if Set I2C Address by uBITX Manager, read from EEProm
+extern byte I2C_LCD_SECOND_ADDRESS; //only using Dual LCD Mode
+#define SMeterLatency 3 //1 is 0.25 sec
+
+//==============================================================================
+// User Select feather list
+//==============================================================================
+//Enable all features
+#define FN_BAND 1 //592
+#define FN_VFO_TOGGLE 1 //78
+#define FN_MODE 1 //20
+#define FN_RIT 1 //58
+#define FN_SPLIT 1 //62
+#define FN_IFSHIFT 1 //238
+#define FN_ATT 1 //128
+#define FN_CW_SPEED 1 //152
+#define FN_VFOTOMEM 1 //254
+#define FN_MEMTOVFO 1 //188
+#define FN_MEMORYKEYER 0 //156
+#define FN_WSPR 1 //1044
+#define FN_SDRMODE 1 //68
+#define FN_CALIBRATION 1 //666
+#define FN_CARRIER 1 //382
+#define FN_CWCARRIER 1 //346
+#define FN_CWTONE 1 //148
+#define FN_CWDELAY 1 //98
+#define FN_TXCWDELAY 1 //94
+#define FN_KEYTYPE 1 //168
+#define FN_ADCMONITOR 1 //516
+#define FN_TXONOFF 1 //58
+
+/*
+//Test Configuration (88%)
+#define FN_BAND 0 //592
+#define FN_VFO_TOGGLE 0 //78
+#define FN_MODE 0 //20
+#define FN_RIT 0 //58
+#define FN_SPLIT 0 //62
+#define FN_IFSHIFT 0 //238
+#define FN_ATT 0 //128
+#define FN_CW_SPEED 1 //152
+#define FN_VFOTOMEM 0 //254
+#define FN_MEMTOVFO 0 //188
+#define FN_MEMORYKEYER 1 //156
+#define FN_WSPR 0 //1044
+#define FN_SDRMODE 1 //68
+#define FN_CALIBRATION 1 //666
+#define FN_CARRIER 1 //382
+#define FN_CWCARRIER 1 //346
+#define FN_CWTONE 1 //148
+#define FN_CWDELAY 1 //98
+#define FN_TXCWDELAY 1 //94
+#define FN_KEYTYPE 1 //168
+#define FN_ADCMONITOR 1 //516
+#define FN_TXONOFF 1 //58
+*/
+
+/*
+//Recommended Character LCD Developer 87%
+#define FN_BAND 1 //592
+#define FN_VFO_TOGGLE 1 //78
+#define FN_MODE 1 //20
+#define FN_RIT 1 //58
+#define FN_SPLIT 1 //62
+#define FN_IFSHIFT 1 //238
+#define FN_ATT 0 //128
+#define FN_CW_SPEED 0 //152 //using MM
+#define FN_VFOTOMEM 1 //254
+#define FN_MEMTOVFO 1 //188
+#define FN_MEMORYKEYER 1 //156
+#define FN_WSPR 1 //1044
+#define FN_SDRMODE 1 //68
+#define FN_CALIBRATION 0 //667 //using MM
+#define FN_CARRIER 0 //382 //using MM
+#define FN_CWCARRIER 0 //346 //using MM
+#define FN_CWTONE 0 //148 //using MM
+#define FN_CWDELAY 0 //98 //using MM
+#define FN_TXCWDELAY 0 //94 //using MM
+#define FN_KEYTYPE 0 //168 //using MM
+#define FN_ADCMONITOR 0 //516 //using MM
+#define FN_TXONOFF 1 //58
+*/
+
+/*
+//Recommended for Nextion, TJC LCD 88%
+#define FN_BAND 1 //600
+#define FN_VFO_TOGGLE 1 //90
+#define FN_MODE 1 //318
+#define FN_RIT 1 //62
+#define FN_SPLIT 1 //2
+#define FN_IFSHIFT 1 //358
+#define FN_ATT 1 //250
+#define FN_CW_SPEED 0 //286
+#define FN_VFOTOMEM 0 //276
+#define FN_MEMTOVFO 0 //234
+#define FN_MEMORYKEYER 1 //168
+#define FN_WSPR 1 //1130
+#define FN_SDRMODE 1 //70
+#define FN_CALIBRATION 0 //790
+#define FN_CARRIER 0 //500
+#define FN_CWCARRIER 0 //464
+#define FN_CWTONE 0 //158
+#define FN_CWDELAY 0 //108
+#define FN_TXCWDELAY 0 //106
+#define FN_KEYTYPE 0 //294
+#define FN_ADCMONITOR 0 //526 //not available with Nextion or Serial UI
+#define FN_TXONOFF 1 //70
+*/
+//==============================================================================
+// End of User Select Mode and Compil options
+//==============================================================================
+
+#ifdef UBITX_DISPLAY_LCD1602I
+ #define USE_I2C_LCD
+#elif defined(UBITX_DISPLAY_LCD1602I_DUAL)
+ #define USE_I2C_LCD
+#elif defined(UBITX_DISPLAY_LCD2004I)
+ #define USE_I2C_LCD
+#endif
+
+#ifdef UBITX_DISPLAY_NEXTION
+ #define USE_SW_SERIAL
+ #undef ENABLE_ADCMONITOR
+ #undef FACTORY_RECOVERY_BOOTUP
+#elif defined(UBITX_CONTROL_MCU)
+ #define USE_SW_SERIAL
+ #undef ENABLE_ADCMONITOR
+ #undef FACTORY_RECOVERY_BOOTUP
+#endif
+
+
+//==============================================================================
+// Hardware, Define PIN Usage
+//==============================================================================
+/**
+ * We need to carefully pick assignment of pin for various purposes.
+ * There are two sets of completely programmable pins on the Raduino.
+ * First, on the top of the board, in line with the LCD connector is an 8-pin connector
+ * that is largely meant for analog inputs and front-panel control. It has a regulated 5v output,
+ * ground and six pins. Each of these six pins can be individually programmed
+ * either as an analog input, a digital input or a digital output.
+ * The pins are assigned as follows (left to right, display facing you):
+ * Pin 1 (Violet), A7, SPARE => Analog S-Meter
+ * Pin 2 (Blue), A6, KEYER (DATA)
+ * Pin 3 (Green), +5v
+ * Pin 4 (Yellow), Gnd
+ * Pin 5 (Orange), A3, PTT
+ * Pin 6 (Red), A2, F BUTTON
+ * Pin 7 (Brown), A1, ENC B
+ * Pin 8 (Black), A0, ENC A
+ *Note: A5, A4 are wired to the Si5351 as I2C interface
+ * *
+ * Though, this can be assigned anyway, for this application of the Arduino, we will make the following
+ * assignment
+ * A2 will connect to the PTT line, which is the usually a part of the mic connector
+ * A3 is connected to a push button that can momentarily ground this line. This will be used for RIT/Bandswitching, etc.
+ * A6 is to implement a keyer, it is reserved and not yet implemented
+ * A7 is connected to a center pin of good quality 100K or 10K linear potentiometer with the two other ends connected to
+ * ground and +5v lines available on the connector. This implments the tuning mechanism
+ */
+#define ENC_A (A0)
+#define ENC_B (A1)
+#define FBUTTON (A2)
+#define PTT (A3)
+#define ANALOG_KEYER (A6)
+#define ANALOG_SPARE (A7)
+#define ANALOG_SMETER (A7) //by KD8CEC
+
+/**
+ * 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 CLK2 GND GND CLK1 GND GND CLK0 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
+ */
+#define TX_RX (7) //Relay
+#define CW_TONE (6)
+#define TX_LPF_A (5) //Relay
+#define TX_LPF_B (4) //Relay
+#define TX_LPF_C (3) //Relay
+#define CW_KEY (2)
+
+//******************************************************
+//DSP (I2C) Meter
+//******************************************************
+//S-Meter Address
+#define I2CMETER_ADDR 0x58
+//VALUE TYPE============================================
+//Signal
+#define I2CMETER_CALCS 0x59 //Calculated Signal Meter
+#define I2CMETER_UNCALCS 0x58 //Uncalculated Signal Meter
+
+//Power
+#define I2CMETER_CALCP 0x57 //Calculated Power Meter
+#define I2CMETER_UNCALCP 0x56 //UnCalculated Power Meter
+
+//SWR
+#define I2CMETER_CALCR 0x55 //Calculated SWR Meter
+#define I2CMETER_UNCALCR 0x54 //Uncalculated SWR Meter
+
+//==============================================================================
+// for public, Variable, functions
+//==============================================================================
+#define WSPR_BAND_COUNT 3
+#define TX_SSB 0
+#define TX_CW 1
+#define printLineF1(x) (printLineF(1, x))
+#define printLineF2(x) (printLineF(0, x))
+
+//0x00 : None, 0x01 : MODE, 0x02:BAND+, 0x03:BAND-, 0x04:TUNE_STEP, 0x05:VFO Toggle, 0x06:SplitOn/Off, 0x07:TX/ON-OFF, 0x08:SDR Mode On / Off, 0x09:Rit Toggle
+#define FUNCTION_KEY_ADC 80 //MODE, BAND(-), BAND(+), STEP
+#define FKEY_PRESS 0x78
+#define FKEY_MODE 0x01
+#define FKEY_BANDUP 0x02
+#define FKEY_BANDDOWN 0x03
+#define FKEY_STEP 0x04
+#define FKEY_VFOCHANGE 0x05
+#define FKEY_SPLIT 0x06
+#define FKEY_TXOFF 0x07
+#define FKEY_SDRMODE 0x08
+#define FKEY_RIT 0x09
+
+#define FKEY_ENTER 0x0A
+#define FKEY_POINT 0x0B
+#define FKEY_DELETE 0x0C
+#define FKEY_CANCEL 0x0D
+
+#define FKEY_NUM0 0x10
+#define FKEY_NUM1 0x11
+#define FKEY_NUM2 0x12
+#define FKEY_NUM3 0x13
+#define FKEY_NUM4 0x14
+#define FKEY_NUM5 0x15
+#define FKEY_NUM6 0x16
+#define FKEY_NUM7 0x17
+#define FKEY_NUM8 0x18
+#define FKEY_NUM9 0x19
+
+#define FKEY_TYPE_MAX 0x1F
+
+extern uint8_t SI5351BX_ADDR; //change typical -> variable at Version 1.097, address read from eeprom, default value is 0x60
+ //EEProm Address : 63
+extern unsigned long frequency;
+extern byte WsprMSGCount;
+extern byte sMeterLevels[9];
+extern int currentSMeter; //ADC Value for S.Meter
+extern byte scaledSMeter; //Calculated S.Meter Level
+
+extern byte KeyValues[16][3]; //Set : Start Value, End Value, Key Type, 16 Set (3 * 16 = 48)
+extern byte TriggerBySW; //Action Start from Nextion LCD, Other MCU
+
+extern void printLine1(const char *c);
+extern void printLine2(const char *c);
+extern void printLineF(char linenmbr, const __FlashStringHelper *c);
+extern void printLineFromEEPRom(char linenmbr, char lcdColumn, byte eepromStartIndex, byte eepromEndIndex, char offsetType);
+extern byte delay_background(unsigned delayTime, byte fromType);
+extern int btnDown(void);
+extern char c[40];
+extern char b[40];
+extern int enc_read(void);
+extern void si5351bx_init(void);
+extern void si5351bx_setfreq(uint8_t clknum, uint32_t fout);
+extern void si5351_set_calibration(int32_t cal);
+extern void initOscillators(void);
+extern void Set_WSPR_Param(void);
+extern void TXSubFreq(unsigned long P2);
+
+extern void startTx(byte txMode, byte isDisplayUpdate);
+extern void stopTx(void);
+extern void setTXFilters(unsigned long freq);
+
+extern void SendWSPRManage(void);
+extern char byteToChar(byte srcByte);
+extern void DisplayCallsign(byte callSignLength);
+extern void DisplayVersionInfo(const char* fwVersionInfo);
+
+//I2C Signal Meter, Version 1.097
+extern int GetI2CSmeterValue(int valueType); //ubitx_ui.ino
+
+#endif //end of if header define
diff --git a/ubitx_20/ubitx_20.ino b/ubitx_20/ubitx_20.ino
new file mode 100644
index 0000000..dc39af8
--- /dev/null
+++ b/ubitx_20/ubitx_20.ino
@@ -0,0 +1,1539 @@
+//Firmware Version
+//+ : This symbol identifies the firmware.
+// It was originally called 'CEC V1.072' but it is too long to waste the LCD window.
+// I do not want to make this Firmware users's uBITX messy with my callsign.
+// Putting one alphabet in front of 'v' has a different meaning.
+// So I put + in the sense that it was improved one by one based on Original Firmware.
+// This firmware has been gradually changed based on the original firmware created by Farhan, Jack, Jerry and others.
+
+#define FIRMWARE_VERSION_INFO F("+v1.200")
+#define FIRMWARE_VERSION_NUM 0x04 //1st Complete Project : 1 (Version 1.061), 2st Project : 2, 1.08: 3, 1.09 : 4
+
+/**
+ Cat Suppoort uBITX CEC Version
+ This firmware has been gradually changed based on the original firmware created by Farhan, Jack, Jerry and others.
+ Most features(TX, Frequency Range, Ham Band, TX Control, CW delay, start Delay... more) have been added by KD8CEC.
+ My wish is to keep the original author's Comment as long as the meaning does not change much, even if the code looks a bit long.
+ Ian KD8CEC
+
+ Original source comment -------------------------------------------------------------
+ * 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 16x2 LCD display and
+ * an Si5351a frequency synthesizer. This board is manufactured by Paradigm Ecomm 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,
+ * callsign 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
+#include
+#include "ubitx.h"
+#include "ubitx_eemap.h"
+
+/**
+ * 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 12 Mhz has a ladder crystal filter. If a second oscillator is used at
+ * 57 Mhz, the signal is subtracted FROM the oscillator, inverting a second time, and arrives
+ * at the 12 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
+ * 12 MHz where its fifth harmonic beats with the arduino's 16 Mhz oscillator's fourth harmonic
+ */
+
+// the second oscillator should ideally be at 57 MHz, however, the crystal filter's center frequency
+// is shifted down a little due to the loading from the impedance matching L-networks on either sides
+
+#if UBITX_BOARD_VERSION == 5
+//For Test //45005000
+ //#define SECOND_OSC_USB (56064200l)
+ //#define SECOND_OSC_LSB (33945800l)
+
+/*
+ //For Test //4500000
+ #define SECOND_OSC_USB (56059200l)
+ #define SECOND_OSC_LSB (33940800l)
+*/
+
+/*
+ //For Test // V1.121 44991500(LSB), 44998500 (USB), abs : 7k
+ #define SECOND_OSC_USB (56057700l)
+ #define SECOND_OSC_LSB (33932300l)
+*/
+
+ //==============================================================================================================================
+ //For Test // V1.200 V1.122 45002500 (LSB), 45002000 (USB) (Change Default BFO Frequency 11056xxx, adjust bfo and ifshift ), abs: 0.5k
+ //Best, Test 3 uBITX V5
+ //Last Value, If more data is collected, it can be changed to a better value.
+ #define SECOND_OSC_USB (56058700l)
+ #define SECOND_OSC_LSB (33945800l)
+
+ //Not used, Just comment (Default)
+ #define INIT_USB_FREQ (11056500l)
+ //-----------------------------------------------------------------------------------------------------------------------------
+#else
+ #define SECOND_OSC_USB (56995000l)
+ #define SECOND_OSC_LSB (32995000l)
+ //these are the two default USB and LSB frequencies. The best frequencies depend upon your individual taste and filter shape
+ //Not used, Just comment (Default)
+ #define INIT_USB_FREQ (11996500l)
+#endif
+
+
+// limits the tuning and working range of the ubitx between 3 MHz and 30 MHz
+#define LOWEST_FREQ (3000000l)
+#define HIGHEST_FREQ (30000000l)
+
+//When the frequency is moved by the dial, the maximum value by KD8CEC
+#define LOWEST_FREQ_DIAL (3000l)
+#define HIGHEST_FREQ_DIAL (60000000l)
+
+char ritOn = 0;
+char vfoActive = VFO_A;
+int8_t meter_reading = 0; // a -1 on meter makes it invisible
+unsigned long vfoA=7150000L, vfoB=14200000L, sideTone=800, usbCarrier, cwmCarrier;
+unsigned long vfoA_eeprom, vfoB_eeprom; //for protect eeprom life
+unsigned long frequency, ritRxFrequency, ritTxFrequency; //frequency is the current frequency on the dial
+
+unsigned int cwSpeed = 100; //this is actuall the dot period in milliseconds
+extern int32_t calibration;
+
+//for store the mode in eeprom
+byte vfoA_mode=0, vfoB_mode = 0; //0: default, 1:not use, 2:LSB, 3:USB, 4:CW, 5:AM, 6:FM
+byte vfoA_mode_eeprom, vfoB_mode_eeprom; //for protect eeprom life
+
+//KD8CEC
+//for AutoSave and protect eeprom life
+byte saveIntervalSec = 10; //second
+unsigned long saveCheckTime = 0;
+unsigned long saveCheckFreq = 0;
+
+byte cwDelayTime = 60;
+byte delayBeforeCWStartTime = 50;
+
+//sideTonePitch + sideToneSub = sideTone
+byte sideTonePitch=0;
+byte sideToneSub = 0;
+
+//DialLock
+byte isDialLock = 0; //000000[0]vfoB [0]vfoA 0Bit : A, 1Bit : B
+byte isTxType = 0; //000000[0 - isSplit] [0 - isTXStop]
+long arTuneStep[5];
+byte tuneStepIndex; //default Value 0, start Offset is 0 because of check new user
+
+byte commonOption0 = 0;
+byte displayOption1 = 0;
+byte displayOption2 = 0;
+
+//CW ADC Range
+int cwAdcSTFrom = 0;
+int cwAdcSTTo = 0;
+int cwAdcDotFrom = 0;
+int cwAdcDotTo = 0;
+int cwAdcDashFrom = 0;
+int cwAdcDashTo = 0;
+int cwAdcBothFrom = 0;
+int cwAdcBothTo = 0;
+byte cwKeyType = 0; //0: straight, 1 : iambica, 2: iambicb
+bool Iambic_Key = true;
+#define IAMBICB 0x10 // 0 for Iambic A, 1 for Iambic B
+unsigned char keyerControl = IAMBICB;
+
+byte isShiftDisplayCWFreq = 1; //Display Frequency
+int shiftDisplayAdjustVal = 0; //
+
+//Variables for auto cw mode
+//byte isCWAutoMode = 0; //0 : none, 1 : CW_AutoMode_Menu_Selection, 2 : CW_AutoMode Sending
+//byte cwAutoTextCount = 0; //cwAutoText Count
+//byte beforeCWTextIndex = 255; //when auto cw start, always beforeCWTextIndex = 255, (for first time check)
+//byte cwAutoDialType = 0; //0 : CW Text Change, 1 : Frequency Tune
+
+//#define AUTO_CW_RESERVE_MAX 3
+//byte autoCWSendReserv[AUTO_CW_RESERVE_MAX]; //Reserve CW Auto Send
+//byte autoCWSendReservCount = 0; //Reserve CW Text Cound
+//byte sendingCWTextIndex = 0; //cw auto seding Text Index
+
+byte userCallsignLength = 0; //7 : display callsign at system startup, 6~0 : callsign length (range : 1~18)
+
+/**
+ * Raduino needs to keep track of current state of the transceiver. These are a few variables that do it
+ */
+boolean txCAT = false; //turned on if the transmitting due to a CAT command
+char inTx = 0; //it is set to 1 if in transmit mode (whatever the reason : cw, ptt or cat)
+char splitOn = 0; //working split, uses VFO B as the transmit frequency
+char keyDown = 0; //in cw mode, denotes the carrier is being transmitted
+
+char isUSB = 0; //upper sideband was selected, this is reset to the default for the
+//char isTest = 0; // two-tone test mode
+char cwMode = 0; //compatible original source, and extend mode //if cwMode == 0, mode check : isUSB, cwMode > 0, mode Check : cwMode
+ //iscwMode = 0 : ssbmode, 1 :cwl, 2 : cwu, 3 : cwn (none tx)
+char digiMode = 0; // 0: normal uBITX behavior (transmit LSB/USB when PTT is depressed)
+ // 1: user-defined digital mode via USB audio input; DTU (default) USB if isUSB; DTL LSB if !isUSB
+ // Effect of non-zero digiMode on ubitx software: should disable the physical PTT inputs (front/back),
+ // but might need to consider physical PTT as a way to stop CAT-based transmission. Also affects the mode
+ // info sent to the IOP.
+
+ //frequency when it crosses the frequency border of 10 MHz
+byte menuOn = 0; //set to 1 when the menu is being displayed, if a menu item sets it to zero, the menu is exited
+unsigned long cwTimeout = 0; //milliseconds to go before the cw transmit line is released and the radio goes back to rx mode
+unsigned long dbgCount = 0; //not used now
+unsigned char txFilter = 0; //which of the four transmit filters are in use
+boolean modeCalibrate = false;//this mode of menus shows extended menus to calibrate the oscillators and choose the proper
+ //beat frequency
+
+byte advancedFreqOption1; //255 : Bit0: use IFTune_Value, Bit1 : use Stored enabled SDR Mode, Bit2~Bit3 : dynamic sdr frequency, bit 7: IFTune_Value Reverse for DIY uBITX
+byte attLevel = 0; //ATT : RF Gain Control (Receive) <-- IF1 Shift, 0 : Off, ShiftValue is attLevel * 100; attLevel 150 = 15K
+byte if1TuneValue = 0; //0 : OFF, IF1 + if1TuneValue * 100; // + - 12500;
+byte sdrModeOn = 0; //SDR MODE ON / OFF
+unsigned long SDR_Center_Freq; //
+
+unsigned long beforeIdle_ProcessTime = 0; //for check Idle time
+byte line2DisplayStatus = 0; //0:Clear, 1 : menu, 1: DisplayFrom Idle,
+char lcdMeter[17];
+byte sMeterLevels[9];
+
+//Current ADC Value for S.Meter, and S Meter Level
+int currentSMeter = 0;
+byte scaledSMeter = 0;
+
+byte I2C_LCD_MASTER_ADDRESS; //0x27 //if Set I2C Address by uBITX Manager, read from EEProm
+byte I2C_LCD_SECOND_ADDRESS; //only using Dual LCD Mode
+
+byte KeyValues[16][3];
+
+byte isIFShift = 0; //1 = ifShift, 2 extend
+int ifShiftValue = 0; //
+
+byte TriggerBySW = 0; //Action Start from Nextion LCD, Other MCU
+
+//Use Custom Filter
+//#define CUST_LPF_ENABLED 48
+//#define CUST_LPF_START 49
+char isCustomFilter = 0;
+char isCustomFilter_A7 = 0;
+char CustFilters[7][2];
+
+/**
+ * Below are the basic functions that control the uBitx. Understanding the functions before
+ * you start hacking around
+ */
+
+//Ham Band
+#define MAX_LIMIT_RANGE 10 //because limited eeprom size
+byte useHamBandCount = 0; //0 use full range frequency
+byte tuneTXType = 0; //0 : use full range, 1 : just Change Dial speed, 2 : just ham band change, but can general band by tune, 3 : only ham band (just support 0, 2 (0.26 version))
+ //100 : use full range but not TX on general band, 101 : just change dial speed but.. 2 : jut... but.. 3 : only ham band (just support 100, 102 (0.26 version))
+unsigned int hamBandRange[MAX_LIMIT_RANGE][2]; // = //Khz because reduce use memory
+
+//-1 : not found, 0 ~ 9 : Hamband index
+char getIndexHambanBbyFreq(unsigned long f)
+{
+ f = f / 1000;
+ for (byte i = 0; i < useHamBandCount; i++)
+ if (hamBandRange[i][0] <= f && f < hamBandRange[i][1])
+ return i;
+
+ return -1;
+}
+
+//when Band change step = just hamband
+//moveDirection : 1 = next, -1 : prior
+void setNextHamBandFreq(unsigned long f, char moveDirection)
+{
+ unsigned long resultFreq = 0;
+ byte loadMode = 0;
+ char findedIndex = getIndexHambanBbyFreq(f);
+
+ if (findedIndex == -1) { //out of hamband
+ f = f / 1000;
+ for (byte i = 0; i < useHamBandCount -1; i++) {
+ if (hamBandRange[i][1] <= f && f < hamBandRange[i + 1][0]) {
+ findedIndex = i + moveDirection;
+ //return (unsigned long)(hamBandRange[i + 1][0]) * 1000;
+ }
+ } //end of for
+ }
+ else if (((moveDirection == 1) && (findedIndex < useHamBandCount -1)) || //Next
+ ((moveDirection == -1) && (findedIndex > 0)) ) { //Prior
+ findedIndex += moveDirection;
+ }
+ else
+ findedIndex = -1;
+
+ if (findedIndex == -1)
+ findedIndex = (moveDirection == 1 ? 0 : useHamBandCount -1);
+
+ EEPROM.get(HAM_BAND_FREQS + 4 * findedIndex, resultFreq);
+
+ //loadMode = (byte)(resultFreq >> 30);
+ //resultFreq = resultFreq & 0x3FFFFFFF;
+ loadMode = (byte)(resultFreq >> 29);
+ resultFreq = resultFreq & 0x1FFFFFFF;
+
+ if ((resultFreq / 1000) < hamBandRange[(unsigned char)findedIndex][0] || (resultFreq / 1000) > hamBandRange[(unsigned char)findedIndex][1])
+ resultFreq = (unsigned long)(hamBandRange[(unsigned char)findedIndex][0]) * 1000;
+
+ byteToMode(loadMode, 1);
+ setFrequency(resultFreq);
+}
+
+void saveBandFreqByIndex(unsigned long f, unsigned long mode, char bandIndex) {
+ if (bandIndex >= 0)
+ //EEPROM.put(HAM_BAND_FREQS + 4 * bandIndex, (f & 0x3FFFFFFF) | (mode << 30) );
+ EEPROM.put(HAM_BAND_FREQS + 4 * bandIndex, (f & 0x1FFFFFFF) | (mode << 29) );
+}
+
+/*
+ KD8CEC
+ When using the basic delay of the Arduino, the program freezes.
+ When the delay is used, the program will generate an error because it is not communicating,
+ so Create a new delay function that can do background processing.
+ */
+unsigned long delayBeforeTime = 0;
+byte delay_background(unsigned delayTime, byte fromType){ //fromType : 4 autoCWKey -> Check Paddle
+ delayBeforeTime = millis();
+
+ /*
+ * KC4UPR - IOP review, 2020-05-03
+ *
+ * I don't see anything in here that is either important to, or will adversely affect, IOP
+ * operation. I'm not planning on using the uBITX autokeyer (since all keying will be in the
+ * IOP), so neither getPaddle() nor autoSendPTTCheck() will be issues. I do need to look into
+ * overall CAT operation, in general.
+ *
+ * UPDATE: Fixed getPaddle() to be compatible.
+ */
+
+ while (millis() - delayBeforeTime <= delayTime) {
+
+ if (fromType == 4)
+ {
+ //CHECK PADDLE
+ if (getPaddle() != 0) //Interrupt : Stop cw Auto mode by Paddle -> Change Auto to Manual
+ return 1;
+
+ //Check PTT while auto Sending
+ //autoSendPTTCheck();
+
+ Check_Cat(3);
+ }
+ else
+ {
+ //Background Work
+ Check_Cat(fromType);
+ }
+ }
+
+ return 0;
+}
+
+
+/**
+ * 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){
+#ifdef USE_CUSTOM_LPF_FILTER
+ freq = freq / 1000000UL;
+ for (byte i = 0; i < 7; i++) {
+ if (freq >= CustFilters[i][0])
+ {
+ char aIn = CustFilters[i][1];
+ digitalWrite(TX_LPF_A, aIn & 0x01);
+ digitalWrite(TX_LPF_B, aIn & 0x02);
+ digitalWrite(TX_LPF_C, aIn & 0x04);
+
+ if (isCustomFilter_A7 == 1)
+ {
+ digitalWrite(10, aIn & 0x08);
+ digitalWrite(11, aIn & 0x10);
+ digitalWrite(12, aIn & 0x20);
+ digitalWrite(13, aIn & 0x40);
+ }
+ return;
+ }
+ } //end of for
+#else
+
+ #if UBITX_BOARD_VERSION == 5
+ 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);
+ }
+ #else
+ 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, 1);
+ digitalWrite(TX_LPF_B, 1);
+ digitalWrite(TX_LPF_C, 0);
+ }
+ else {
+ digitalWrite(TX_LPF_A, 1);
+ digitalWrite(TX_LPF_B, 1);
+ digitalWrite(TX_LPF_C, 1);
+ }
+ #endif
+
+
+#endif
+}
+
+/**
+ * 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 f){
+ f = (f / arTuneStep[tuneStepIndex -1]) * arTuneStep[tuneStepIndex -1];
+ setTXFilters(f);
+
+ unsigned long appliedCarrier = ((cwMode == 0 ? usbCarrier : cwmCarrier) + (isIFShift && (inTx == 0) ? ifShiftValue : 0));
+ int appliedTuneValue = 0;
+
+ //applied if tune
+ //byte advancedFreqOption1; //255 : Bit0: use IFTune_Value, Bit1 : use Stored enabled SDR Mode, Bit2 : dynamic sdr frequency0, Bit3 : dynamic sdr frequency1, bit 7: IFTune_Value Reverse for DIY uBITX
+ if ((advancedFreqOption1 & 0x01) != 0x00)
+ {
+ appliedTuneValue = if1TuneValue;
+
+ //In the LSB state, the optimum reception value was found. To apply to USB, 3Khz decrease is required.
+ if (sdrModeOn && (inTx == 0))
+ appliedTuneValue -= 15; //decrease 1.55Khz
+
+ //if (isUSB)
+ if (cwMode == 2 || (cwMode == 0 && (isUSB)))
+ appliedTuneValue -= 30; //decrease 3Khz
+ }
+
+ //if1Tune RX, TX Enabled, ATT : only RX Mode
+ //The IF Tune shall be measured at the LSB. Then, move the 3Khz down for USB.
+ long if1AdjustValue = ((inTx == 0) ? (attLevel * 100) : 0) + (appliedTuneValue * 100); //if1Tune RX, TX Enabled, ATT : only RX Mode //5600
+
+ //for DIY uBITX (custom filter)
+ if ((advancedFreqOption1 & 0x80) != 0x00) //Reverse IF Tune (- Value for DIY uBITX)
+ if1AdjustValue *= -1;
+
+ if (sdrModeOn && (inTx == 0)) //IF SDR MODE
+ {
+ //Fixed Frequency SDR (Default Frequency : 32Mhz, available change sdr Frequency by uBITX Manager)
+ //Dynamic Frequency is for SWL without cat
+
+ //byte advancedFreqOption1; //255 : Bit0: use IFTune_Value, Bit1 : use Stored enabled SDR Mode, Bit2 : dynamic sdr frequency0, Bit3 : dynamic sdr frequency1, bit 7: IFTune_Value Reverse for DIY uBITX
+ long moveFrequency = 0;
+ //7 6 5 4 3 2 1 0
+ // _ _ <-- SDR Freuqncy Option
+ byte sdrOption = (advancedFreqOption1 >> 2) & 0x03;
+
+ if (sdrOption == 1) // SDR Frequency + frequenc
+ {
+ //example : offset Freq : 20 Mhz and frequency = 7.080 => 27.080 Mhz
+ //example : offset Freq : 0 Mhz and frequency = 7.080 => 7.080 Mhz
+ //for available HF, SDR
+ moveFrequency = f;
+ }
+ else if (sdrOption == 2) //Mhz move
+ {
+ //Offset Frequency + Mhz,
+ //Example : Offset Frequency : 30Mhz and current Frequncy is 7.080 => 37.080Mhz
+ // Offset Frequency : 30Mhz and current Frequncy is 14.074 => 34.074Mhz
+ moveFrequency = (f % 10000000);
+ }
+ else if (sdrOption == 3) //Khz move
+ {
+ //Offset Frequency + Khz,
+ //Example : Offset Frequency : 30Mhz and current Frequncy is 7.080 => 30.080Mhz
+ // Offset Frequency : 30Mhz and current Frequncy is 14.074 => 30.074Mhz
+ moveFrequency = (f % 1000000);
+ }
+
+#if UBITX_BOARD_VERSION == 5
+ si5351bx_setfreq(2, 45002000 + if1AdjustValue + f);
+ si5351bx_setfreq(1, 45002000
+ + if1AdjustValue
+ + SDR_Center_Freq
+ //+ ((advancedFreqOption1 & 0x04) == 0x00 ? 0 : (f % 10000000))
+ + moveFrequency);
+ // + 2390); //RTL-SDR Frequency Error, Do not add another SDR because the error is different. V1.3
+#else
+ si5351bx_setfreq(2, 44991500 + if1AdjustValue + f);
+ si5351bx_setfreq(1, 44991500
+ + if1AdjustValue
+ + SDR_Center_Freq
+ //+ ((advancedFreqOption1 & 0x04) == 0x00 ? 0 : (f % 10000000))
+ + moveFrequency );
+ //+ 2390); Do not add another SDR because the error is different. V1.3
+#endif
+ }
+ else
+ {
+ if (cwMode == 1 || (cwMode == 0 && (!isUSB))) //cwl or lsb
+ {
+ //CWL(cwMode == 1) or LSB (cwMode == 0 && (!isUSB))
+ si5351bx_setfreq(2, SECOND_OSC_LSB + if1AdjustValue + appliedCarrier + f);
+ si5351bx_setfreq(1, SECOND_OSC_LSB + if1AdjustValue);
+ }
+ else //cwu or usb
+ {
+ //CWU (cwMode == 2) or USB (cwMode == 0 and isUSB)
+ si5351bx_setfreq(2, SECOND_OSC_USB + if1AdjustValue - appliedCarrier + f);
+ si5351bx_setfreq(1, SECOND_OSC_USB + if1AdjustValue);
+ }
+ }
+
+ frequency = f;
+}
+
+/**
+ * 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
+ */
+void startTx(byte txMode, byte isDisplayUpdate){
+ //Check Hamband only TX //Not found Hamband index by now frequency
+ if (tuneTXType >= 100 && getIndexHambanBbyFreq(ritOn ? ritTxFrequency : frequency) == -1) {
+ //no message
+ return;
+ }
+
+ if ((isTxType & 0x01) != 0x01)
+ digitalWrite(TX_RX, 1);
+
+ inTx = 1;
+
+ if (ritOn){
+ //save the current as the rx frequency
+ ritRxFrequency = frequency;
+ setFrequency(ritTxFrequency);
+ }
+ else
+ {
+ if (splitOn == 1)
+ {
+ FrequencyToVFO(1); //Save current Frequency and Mode to eeprom
+ if (vfoActive == VFO_B)
+ {
+ vfoActive = VFO_A;
+ frequency = vfoA;
+ byteToMode(vfoA_mode, 0);
+ }
+ else if (vfoActive == VFO_A)
+ {
+ vfoActive = VFO_B;
+ frequency = vfoB;
+ byteToMode(vfoB_mode, 0);
+ }
+ }
+
+ setFrequency(frequency);
+ } //end of else
+
+ SetCarrierFreq();
+
+ if (txMode == TX_CW){
+ //turn off the second local oscillator and the bfo
+ si5351bx_setfreq(0, 0);
+ si5351bx_setfreq(1, 0);
+
+ //shif 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 (cwMode == 0)
+ {
+ if (isUSB)
+ si5351bx_setfreq(2, frequency + sideTone);
+ else
+ si5351bx_setfreq(2, frequency - sideTone);
+ }
+ else if (cwMode == 1) //CWL
+ {
+ si5351bx_setfreq(2, frequency - sideTone);
+ }
+ else //CWU
+ {
+ si5351bx_setfreq(2, frequency + sideTone);
+ }
+ }
+
+ //reduce latency time when begin of CW mode
+ if (isDisplayUpdate == 1)
+ updateDisplay();
+}
+
+void stopTx(void){
+ inTx = 0;
+
+ digitalWrite(TX_RX, 0); //turn off the tx
+ SetCarrierFreq();
+
+ if (ritOn)
+ setFrequency(ritRxFrequency);
+ else
+ {
+ if (splitOn == 1) {
+ //vfo Change
+ if (vfoActive == VFO_B){
+ vfoActive = VFO_A;
+ frequency = vfoA;
+ byteToMode(vfoA_mode, 0);
+ }
+ else if (vfoActive == VFO_A){
+ vfoActive = VFO_B;
+ frequency = vfoB;
+ byteToMode(vfoB_mode, 0);
+ }
+ }
+
+ setFrequency(frequency);
+ } //end of else
+
+ updateDisplay();
+}
+
+/**
+ * ritEnable is called with a frequency parameter that determines
+ * what the tx frequency will be
+ */
+void ritEnable(unsigned long f){
+ ritOn = 1;
+ //save the non-rit frequency back into the VFO memory
+ //as RIT is a temporary shift, this is not saved to EEPROM
+ ritTxFrequency = f;
+}
+
+// this is called by the RIT menu routine
+void ritDisable(){
+ if (ritOn){
+ ritOn = 0;
+ setFrequency(ritTxFrequency);
+ 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(){
+ /*
+ * KC4UPR - note that some of this is superfluous now that checkPTT() is only executed
+ * in SSB mode, and cwKeyer is only executed in CW mode...
+ */
+ //we don't check for ptt when transmitting cw
+ if (cwTimeout > 0)
+ return;
+
+ if (digitalRead(PTT) == 0 && inTx == 0){
+ startTx(TX_SSB, 1);
+ delay(50); //debounce the PTT
+ }
+
+ if (digitalRead(PTT) == 1 && inTx == 1)
+ stopTx();
+}
+#ifdef EXTEND_KEY_GROUP1
+void checkButton(){
+ char currentBandIndex = -1;
+
+ //only if the button is pressed
+ int keyStatus = getBtnStatus();
+ if (keyStatus == -1)
+ return;
+
+ delay(50);
+ keyStatus = getBtnStatus(); //will be remove 3 lines
+ if (keyStatus == -1)
+ return;
+
+ if (keyStatus == FKEY_PRESS) //Menu Key
+ {
+ //for touch screen
+#ifdef USE_SW_SERIAL
+ SetSWActivePage(1);
+ doMenu();
+
+ //if (isCWAutoMode == 0)
+ SetSWActivePage(0);
+#else
+ doMenu();
+#endif
+ }
+ else if (keyStatus <= FKEY_TYPE_MAX) //EXTEND KEY GROUP #1
+ {
+
+ switch(keyStatus)
+ {
+ case FKEY_MODE :
+ if (cwMode == 1)
+ {
+ cwMode = 2;
+ }
+ else if (cwMode == 2)
+ {
+ cwMode = 0;
+ isUSB = 0;
+ }
+ else if (isUSB == 0)
+ {
+ isUSB = 1;
+ }
+ else
+ {
+ cwMode = 1;
+ }
+ break;
+ case FKEY_BANDUP :
+ case FKEY_BANDDOWN :
+ //Save Band Information
+ if (tuneTXType == 2 || tuneTXType == 3 || tuneTXType == 102 || tuneTXType == 103) { //only ham band move
+ currentBandIndex = getIndexHambanBbyFreq(frequency);
+
+ if (currentBandIndex >= 0) {
+ saveBandFreqByIndex(frequency, modeToByte(), currentBandIndex);
+ }
+ }
+ setNextHamBandFreq(frequency, keyStatus == FKEY_BANDDOWN ? -1 : 1); //Prior Band
+ break;
+
+ case FKEY_STEP :
+ if (++tuneStepIndex > 5)
+ tuneStepIndex = 1;
+
+ EEPROM.put(TUNING_STEP, tuneStepIndex);
+ printLine2ClearAndUpdate();
+ break;
+
+ case FKEY_VFOCHANGE :
+ menuVfoToggle(1); //Vfo Toggle
+ break;
+
+ case FKEY_SPLIT :
+ menuSplitOnOff(1);
+ break;
+ case FKEY_TXOFF:
+ menuTxOnOff(1, 0x01);
+ break;
+ case FKEY_SDRMODE :
+ menuSDROnOff(1);
+ break;
+ case FKEY_RIT :
+ menuRitToggle(1);
+ break;
+ }
+
+ FrequencyToVFO(1);
+ SetCarrierFreq();
+ setFrequency(frequency);
+ //delay_background(delayTime, 0);
+ updateDisplay();
+ }
+
+ //wait for the button to go up again
+ while(keyStatus == getBtnStatus()) {
+ delay(10);
+ Check_Cat(0);
+ }
+ //delay(50);//debounce
+}
+
+#else
+void checkButton(){
+ //only if the button is pressed
+ if (!btnDown())
+ return;
+ delay(50);
+ if (!btnDown()) //debounce
+ return;
+
+ doMenu();
+
+ //wait for the button to go up again
+ while(btnDown()) {
+ delay(10);
+ Check_Cat(0);
+ }
+ //delay(50);//debounce
+}
+#endif
+
+/************************************
+Replace function by KD8CEC
+prevent error controls
+applied Threshold for reduct errors, dial Lock, dynamic Step
+ *************************************/
+byte threshold = 2; //noe action for count
+unsigned long lastEncInputtime = 0;
+int encodedSumValue = 0;
+unsigned long lastTunetime = 0; //if continous moving, skip threshold processing
+byte lastMovedirection = 0; //0 : stop, 1 : cw, 2 : ccw
+
+//#define skipThresholdTime 70
+#define encodeTimeOut 1000
+
+void doTuningWithThresHold(){
+ int s = 0;
+ unsigned long prev_freq;
+
+ if ((vfoActive == VFO_A && ((isDialLock & 0x01) == 0x01)) ||
+ (vfoActive == VFO_B && ((isDialLock & 0x02) == 0x02)))
+ return;
+
+ s = enc_read();
+
+ //if time is exceeded, it is recognized as an error,
+ //ignore exists values, because of errors
+ if (s == 0) {
+ if (encodedSumValue != 0 && (millis() - encodeTimeOut) > lastEncInputtime)
+ encodedSumValue = 0;
+
+ lastMovedirection = 0;
+ return;
+ }
+ lastEncInputtime = millis();
+
+ //for check moving direction
+ encodedSumValue += (s > 0 ? 1 : -1);
+
+ //check threshold and operator actions (hold dial speed = continous moving, skip threshold check)
+ //not use continues changing by Threshold
+ //if ((lastTunetime < (millis() - skipThresholdTime)) && ((encodedSumValue * encodedSumValue) <= (threshold * threshold)))
+ if (((encodedSumValue * encodedSumValue) <= (threshold * threshold)))
+ return;
+
+ lastTunetime = millis();
+
+ //Valid Action without noise
+ encodedSumValue = 0;
+
+ prev_freq = frequency;
+ //incdecValue = tuningStep * s;
+ //frequency += (arTuneStep[tuneStepIndex -1] * s * (s * s < 10 ? 1 : 3)); //appield weight (s is speed)
+ frequency += (arTuneStep[tuneStepIndex -1] * s); //appield weight (s is speed) //if want need more increase size, change step size
+
+ if (prev_freq < 10000000l && frequency > 10000000l)
+ isUSB = true;
+
+ if (prev_freq > 10000000l && frequency < 10000000l)
+ isUSB = false;
+
+ setFrequency(frequency);
+ updateDisplay();
+}
+
+/**
+ * RIT only steps back and forth by 100 hz at a time
+ */
+void doRIT(){
+ int knob = enc_read();
+ unsigned long old_freq = frequency;
+
+ if (knob < 0)
+ frequency -= (arTuneStep[tuneStepIndex -1]); //
+ else if (knob > 0)
+ frequency += (arTuneStep[tuneStepIndex -1]); //
+
+ if (old_freq != frequency){
+ setFrequency(frequency);
+ updateDisplay();
+ }
+}
+
+/*
+ save Frequency and mode to eeprom for Auto Save with protected eeprom cycle, by kd8cec
+ */
+void storeFrequencyAndMode(byte saveType)
+{
+ //freqType : 0 Both (vfoA and vfoB), 1 : vfoA, 2 : vfoB
+ if (saveType == 0 || saveType == 1) //vfoA
+ {
+ if (vfoA != vfoA_eeprom) {
+ EEPROM.put(VFO_A, vfoA);
+ vfoA_eeprom = vfoA;
+ }
+
+ if (vfoA_mode != vfoA_mode_eeprom) {
+ EEPROM.put(VFO_A_MODE, vfoA_mode);
+ vfoA_mode_eeprom = vfoA_mode;
+ }
+ }
+
+ if (saveType == 0 || saveType == 2) //vfoB
+ {
+ if (vfoB != vfoB_eeprom) {
+ EEPROM.put(VFO_B, vfoB);
+ vfoB_eeprom = vfoB;
+ }
+
+ if (vfoB_mode != vfoB_mode_eeprom) {
+ EEPROM.put(VFO_B_MODE, vfoB_mode);
+ vfoB_mode_eeprom = vfoB_mode;
+ }
+ }
+}
+
+//calculate step size from 1 byte, compatible uBITX Manager, by KD8CEC
+unsigned int byteToSteps(byte srcByte) {
+ byte powerVal = (byte)(srcByte >> 6);
+ unsigned int baseVal = srcByte & 0x3F;
+
+ if (powerVal == 1)
+ return baseVal * 10;
+ else if (powerVal == 2)
+ return baseVal * 100;
+ else if (powerVal == 3)
+ return baseVal * 1000;
+ else
+ return baseVal;
+}
+
+
+/**
+ * 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(){
+ //read the settings from the eeprom and restore them
+ //if the readings are off, then set defaults
+ //for original source Section ===========================
+ EEPROM.get(MASTER_CAL, calibration);
+ EEPROM.get(USB_CAL, usbCarrier);
+ EEPROM.get(VFO_A, vfoA);
+ EEPROM.get(VFO_B, vfoB);
+ EEPROM.get(CW_SIDETONE, sideTone);
+ EEPROM.get(CW_SPEED, cwSpeed);
+ //End of original code
+
+ //----------------------------------------------------------------
+ //Add Lines by KD8CEC
+ //for custom source Section =============================
+ //ID & Version Check from EEProm
+ //if found different firmware, erase eeprom (32
+ #define FIRMWAR_ID_ADDR 776 //776 : 0x59, 777 :0x58, 778 : 0x68 : Id Number, if not found id, erase eeprom(32~1023) for prevent system error.
+ if (EEPROM.read(FIRMWAR_ID_ADDR) != 0x59 ||
+ EEPROM.read(FIRMWAR_ID_ADDR + 1) != 0x58 ||
+ EEPROM.read(FIRMWAR_ID_ADDR + 2) != 0x68 ) {
+
+ printLineF(1, F("Init EEProm..."));
+ //initial all eeprom
+ for (unsigned int i = 64; i < 1024; i++) //protect Master_cal, usb_cal
+ EEPROM.write(i, 0);
+
+ //Write Firmware ID
+ EEPROM.write(FIRMWAR_ID_ADDR, 0x59);
+ EEPROM.write(FIRMWAR_ID_ADDR + 1, 0x58);
+ EEPROM.write(FIRMWAR_ID_ADDR + 2, 0x68);
+ }
+
+ //Version Write for Memory Management Software
+ if (EEPROM.read(VERSION_ADDRESS) != FIRMWARE_VERSION_NUM)
+ EEPROM.write(VERSION_ADDRESS, FIRMWARE_VERSION_NUM);
+
+ //SI5351 I2C Address
+ //I2C_ADDR_SI5351
+ SI5351BX_ADDR = EEPROM.read(I2C_ADDR_SI5351);
+ if (SI5351BX_ADDR < 0x10 || SI5351BX_ADDR > 0xF0)
+ {
+ SI5351BX_ADDR = 0x60;
+ }
+
+
+ //Backup Calibration Setting from Factory Setup
+ //Check Factory Setting Backup Y/N
+ if (EEPROM.read(FACTORY_BACKUP_YN) != 0x13) {
+ EEPROM.write(FACTORY_BACKUP_YN, 0x13); //Set Backup Y/N
+
+ for (unsigned int i = 0; i < 32; i++) //factory setting range
+ EEPROM.write(FACTORY_VALUES + i, EEPROM.read(i)); //0~31 => 65~96
+ }
+
+ EEPROM.get(CW_CAL, cwmCarrier);
+
+ //for Save VFO_A_MODE to eeprom
+ //0: default, 1:not use, 2:LSB, 3:USB, 4:CW, 5:AM, 6:FM
+ EEPROM.get(VFO_A_MODE, vfoA_mode);
+ EEPROM.get(VFO_B_MODE, vfoB_mode);
+
+ //CW DelayTime
+ EEPROM.get(CW_DELAY, cwDelayTime);
+
+ //CW interval between TX and CW Start
+ EEPROM.get(CW_START, delayBeforeCWStartTime);
+ EEPROM.get(CW_KEY_TYPE, cwKeyType);
+ if (cwKeyType > 2)
+ cwKeyType = 0;
+
+ if (cwKeyType == 0)
+ Iambic_Key = false;
+ else
+ {
+ Iambic_Key = true;
+ if (cwKeyType == 1)
+ keyerControl &= ~IAMBICB;
+ else
+ keyerControl |= IAMBICB;
+ }
+
+ EEPROM.get(COMMON_OPTION0, commonOption0);
+ EEPROM.get(DISPLAY_OPTION1, displayOption1);
+ EEPROM.get(DISPLAY_OPTION2, displayOption2);
+
+ for (byte i = 0; i < 8; i++) {
+ sMeterLevels[i + 1] = EEPROM.read(S_METER_LEVELS + i);
+ }
+
+ //KeyValues
+ for (byte i = 0; i < 16; i++) {
+ KeyValues[i][0] = EEPROM.read(EXTENDED_KEY_RANGE + (i * 3)); //RANGE : Start Value
+ KeyValues[i][1] = EEPROM.read(EXTENDED_KEY_RANGE + (i * 3) + 1); //RANGE : End Value
+ KeyValues[i][2] = EEPROM.read(EXTENDED_KEY_RANGE + (i * 3) + 2); //KEY TYPE
+ }
+
+#ifdef USE_CUSTOM_LPF_FILTER
+ //Custom Filters
+ EEPROM.get(CUST_LPF_ENABLED, isCustomFilter);
+ if (isCustomFilter == 0x58)
+ {
+ isCustomFilter_A7 = 1;
+ }
+ isCustomFilter = (isCustomFilter == 0x58 || isCustomFilter == 0x57);
+
+ for (byte i = 0; i < 7; i++) {
+ CustFilters[i][0] = EEPROM.read(CUST_LPF_START + (i * 2)); //LPF (To) Mhz
+ CustFilters[i][1] = EEPROM.read(CUST_LPF_START + (i * 2) + 1); //Enabled I/O
+ }
+//char isCustomFilter = 0;
+//char isCustomFilter_A7 = 0;
+//char CustFilters[2][7];
+#endif
+
+ //User callsign information
+ if (EEPROM.read(USER_CALLSIGN_KEY) == 0x59)
+ userCallsignLength = EEPROM.read(USER_CALLSIGN_LEN); //MAXIMUM 18 LENGTH
+
+ //Ham Band Count
+ EEPROM.get(HAM_BAND_COUNT, useHamBandCount);
+ EEPROM.get(TX_TUNE_TYPE, tuneTXType);
+
+ byte findedValidValueCount = 0;
+
+ //Read band Information
+ for (byte i = 0; i < useHamBandCount; i++) {
+ unsigned int tmpReadValue = 0;
+ EEPROM.get(HAM_BAND_RANGE + 4 * i, tmpReadValue);
+ hamBandRange[i][0] = tmpReadValue;
+
+ if (tmpReadValue > 1 && tmpReadValue < 55000)
+ findedValidValueCount++;
+
+ EEPROM.get(HAM_BAND_RANGE + 4 * i + 2, tmpReadValue);
+ hamBandRange[i][1] = tmpReadValue;
+ }
+
+ //Check Value Range and default Set for new users
+ if ((3 < tuneTXType && tuneTXType < 100) || 103 < tuneTXType || useHamBandCount < 1 || findedValidValueCount < 5)
+ {
+ tuneTXType = 2;
+ //if empty band Information, auto insert default region 2 frequency range
+ //This part is made temporary for people who have difficulty setting up, so can remove it when you run out of memory.
+ useHamBandCount = 10;
+ hamBandRange[0][0] = 1810; hamBandRange[0][1] = 2000;
+ hamBandRange[1][0] = 3500; hamBandRange[1][1] = 3800;
+ hamBandRange[2][0] = 5351; hamBandRange[2][1] = 5367;
+ hamBandRange[3][0] = 7000; hamBandRange[3][1] = 7300; //region 2
+ hamBandRange[4][0] = 10100; hamBandRange[4][1] = 10150;
+ hamBandRange[5][0] = 14000; hamBandRange[5][1] = 14350;
+ hamBandRange[6][0] = 18068; hamBandRange[6][1] = 18168;
+ hamBandRange[7][0] = 21000; hamBandRange[7][1] = 21450;
+ hamBandRange[8][0] = 24890; hamBandRange[8][1] = 24990;
+ hamBandRange[9][0] = 28000; hamBandRange[9][1] = 29700;
+ }
+
+
+ //Read Tuning Step Index, and steps
+ findedValidValueCount = 0;
+ EEPROM.get(TUNING_STEP, tuneStepIndex);
+ for (byte i = 0; i < 5; i++) {
+ arTuneStep[i] = byteToSteps(EEPROM.read(TUNING_STEP + i + 1));
+ if (arTuneStep[i] >= 1 && arTuneStep[i] <= 60000) //Maximum 650 for check valid Value
+ findedValidValueCount++;
+ }
+
+ //Check Value Range and default Set for new users
+ if (findedValidValueCount < 5)
+ {
+ //Default Setting
+ arTuneStep[0] = 10;
+ arTuneStep[1] = 50;
+ arTuneStep[2] = 100;
+ arTuneStep[3] = 500;
+ arTuneStep[4] = 1000;
+ }
+
+ if (tuneStepIndex == 0) //New User
+ tuneStepIndex = 3;
+
+ //CW Key ADC Range ======= adjust set value for reduce cw keying error
+ //by KD8CEC
+ unsigned int tmpMostBits = 0;
+ tmpMostBits = EEPROM.read(CW_ADC_MOST_BIT1);
+ cwAdcSTFrom = EEPROM.read(CW_ADC_ST_FROM) | ((tmpMostBits & 0x03) << 8);
+ cwAdcSTTo = EEPROM.read(CW_ADC_ST_TO) | ((tmpMostBits & 0x0C) << 6);
+ cwAdcDotFrom = EEPROM.read(CW_ADC_DOT_FROM) | ((tmpMostBits & 0x30) << 4);
+ cwAdcDotTo = EEPROM.read(CW_ADC_DOT_TO) | ((tmpMostBits & 0xC0) << 2);
+
+ tmpMostBits = EEPROM.read(CW_ADC_MOST_BIT2);
+ cwAdcDashFrom = EEPROM.read(CW_ADC_DASH_FROM) | ((tmpMostBits & 0x03) << 8);
+ cwAdcDashTo = EEPROM.read(CW_ADC_DASH_TO) | ((tmpMostBits & 0x0C) << 6);
+ cwAdcBothFrom = EEPROM.read(CW_ADC_BOTH_FROM) | ((tmpMostBits & 0x30) << 4);
+ cwAdcBothTo = EEPROM.read(CW_ADC_BOTH_TO) | ((tmpMostBits & 0xC0) << 2);
+
+ //Display Type for CW mode
+ isShiftDisplayCWFreq = EEPROM.read(CW_DISPLAY_SHIFT);
+
+ //Enable / Diable Check for CW Display Cofiguration Group
+ if ((commonOption0 & 0x80) != 0x00)
+ {
+ //Adjust CW Mode Freq
+ shiftDisplayAdjustVal = (isShiftDisplayCWFreq & 0x3F) * 10;
+
+ //check Minus
+ if ((isShiftDisplayCWFreq & 0x40) == 0x40)
+ shiftDisplayAdjustVal = shiftDisplayAdjustVal * -1;
+
+ //Shift Display Check (Default : 0)
+ if ((isShiftDisplayCWFreq & 0x80) == 0) //Enabled
+ isShiftDisplayCWFreq = 1;
+ else //Disabled
+ isShiftDisplayCWFreq = 0;
+ }
+
+ //Stored IF Shift Option
+ if ((commonOption0 & 0x40) != 0x00)
+ {
+ EEPROM.get(IF_SHIFTVALUE, ifShiftValue);
+ isIFShift = ifShiftValue != 0;
+ }
+
+ //Advanced Freq control
+ EEPROM.get(ADVANCED_FREQ_OPTION1, advancedFreqOption1);
+
+ //byte advancedFreqOption1; //255 : Bit0: use IFTune_Value, Bit1 : use Stored enabled SDR Mode, Bit2 : dynamic sdr frequency0, Bit3 : dynamic sdr frequency1, bit 7: IFTune_Value Reverse for DIY uBITX
+ if ((advancedFreqOption1 & 0x01) != 0x00)
+ {
+ EEPROM.get(IF1_CAL, if1TuneValue);
+
+ //Stored Enabled SDR Mode
+ if ((advancedFreqOption1 & 0x02) != 0x00)
+ {
+ EEPROM.get(ENABLE_SDR, sdrModeOn);
+ }
+ }
+
+ EEPROM.get(SDR_FREQUNCY, SDR_Center_Freq);
+ //if (SDR_Center_Freq == 0)
+ // SDR_Center_Freq = 32000000;
+
+ //default Value (for original hardware)
+ if (cwAdcSTFrom >= cwAdcSTTo)
+ {
+ cwAdcSTFrom = 0;
+ cwAdcSTTo = 50;
+ }
+
+ if (cwAdcBothFrom >= cwAdcBothTo)
+ {
+ cwAdcBothFrom = 51;
+ cwAdcBothTo = 300;
+ }
+
+ if (cwAdcDotFrom >= cwAdcDotTo)
+ {
+ cwAdcDotFrom = 301;
+ cwAdcDotTo = 600;
+ }
+ if (cwAdcDashFrom >= cwAdcDashTo)
+ {
+ cwAdcDashFrom = 601;
+ cwAdcDashTo = 800;
+ }
+ //end of CW Keying Variables
+
+ if (cwDelayTime < 1 || cwDelayTime > 250)
+ cwDelayTime = 60;
+
+ if (vfoA_mode < 2)
+ vfoA_mode = 2;
+
+ if (vfoB_mode < 2)
+ vfoB_mode = 3;
+
+
+#if UBITX_BOARD_VERSION == 5
+ //original code with modified by kd8cec
+ if (usbCarrier > 11060000l || usbCarrier < 11048000l)
+ usbCarrier = 11052000l;
+
+ if (cwmCarrier > 11060000l || cwmCarrier < 11048000l)
+ cwmCarrier = 11052000l;
+#else
+ //original code with modified by kd8cec
+ if (usbCarrier > 12010000l || usbCarrier < 11990000l)
+ usbCarrier = 11997000l;
+
+ if (cwmCarrier > 12010000l || cwmCarrier < 11990000l)
+ cwmCarrier = 11997000l;
+#endif
+
+ if (vfoA > 35000000l || 3500000l > vfoA) {
+ vfoA = 7150000l;
+ vfoA_mode = 2; //LSB
+ }
+
+ if (vfoB > 35000000l || 3500000l > vfoB) {
+ vfoB = 14150000l;
+ vfoB_mode = 3; //USB
+ }
+ //end of original code section
+
+ //for protect eeprom life by KD8CEC
+ vfoA_eeprom = vfoA;
+ vfoB_eeprom = vfoB;
+ vfoA_mode_eeprom = vfoA_mode;
+ vfoB_mode_eeprom = vfoB_mode;
+
+ if (sideTone < 100 || 2000 < sideTone)
+ sideTone = 800;
+ if (cwSpeed < 10 || 1000 < cwSpeed)
+ cwSpeed = 100;
+
+ if (sideTone < 300 || sideTone > 1000) {
+ sideTonePitch = 0;
+ sideToneSub = 0;;
+ }
+ else{
+ sideTonePitch = (sideTone - 300) / 50;
+ sideToneSub = sideTone % 50;
+ }
+}
+
+void initPorts(){
+ analogReference(DEFAULT);
+
+ //??
+ pinMode(ENC_A, INPUT_PULLUP);
+ pinMode(ENC_B, INPUT_PULLUP);
+ pinMode(FBUTTON, INPUT_PULLUP);
+
+ //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(ANALOG_SMETER, INPUT); //by KD8CEC
+
+#ifdef USE_CUSTOM_LPF_FILTER
+ if (isCustomFilter_A7)
+ {
+ pinMode(10, OUTPUT);
+ pinMode(11, OUTPUT);
+ pinMode(12, OUTPUT);
+ pinMode(13, OUTPUT);
+ }
+#endif
+
+ 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);
+}
+
+//Recovery Factory Setting Values
+void factory_Recovery()
+{
+ if (EEPROM.read(FACTORY_BACKUP_YN) != 0x13)
+ return;
+
+ if (digitalRead(PTT) == 0) //Do not proceed if PTT is pressed to prevent malfunction.
+ return;
+
+ printLineF2(F("Factory Recovery"));
+ delay(2000);
+ if (!btnDown())
+ return;
+
+ printLineF2(F("IF you continue"));
+ printLineF1(F("release the key"));
+ delay(2000);
+ if (btnDown())
+ return;
+
+ printLineF1(F("Press Key PTT"));
+ delay(2000);
+ if (digitalRead(PTT) == 0)
+ {
+ for (unsigned int i = 0; i < 32; i++) //factory setting range
+ EEPROM.write(i, EEPROM.read(FACTORY_VALUES + i)); //65~96 => 0~31
+
+ //printLineF2(F("CompleteRecovery"));
+ printLineF1(F("Power Reset!"));
+ while(1); //Hold
+ }
+}
+
+
+void setup()
+{
+ /*
+ //Init EEProm for Fault EEProm TEST and Factory Reset
+ //please remove remark for others.
+ //for (int i = 0; i < 1024; i++)
+ for (int i = 16; i < 1024; i++) //protect Master_cal, usb_cal
+ EEPROM.write(i, 0xFF);
+ lcd.begin(16, 2);
+ printLineF(1, F("Complete Erase"));
+ sleep(1000);
+ //while(1);
+ //end section of test
+ */
+
+ //Load I2C LCD Address for I2C LCD
+ //I2C LCD Parametere
+#ifdef USE_I2C_LCD
+ EEPROM.get(I2C_LCD_MASTER, I2C_LCD_MASTER_ADDRESS);
+ EEPROM.get(I2C_LCD_SECOND, I2C_LCD_SECOND_ADDRESS);
+
+ if (I2C_LCD_MASTER_ADDRESS < 0x10 || I2C_LCD_MASTER_ADDRESS > 0xF0)
+ I2C_LCD_MASTER_ADDRESS = I2C_LCD_MASTER_ADDRESS_DEFAULT;
+
+ if (I2C_LCD_SECOND_ADDRESS < 0x10 || I2C_LCD_SECOND_ADDRESS > 0xF0)
+ I2C_LCD_SECOND_ADDRESS = I2C_LCD_SECOND_ADDRESS_DEFAULT;
+#endif
+
+ //Serial.begin(9600);
+ LCD_Init();
+ //printLineF(1, FIRMWARE_VERSION_INFO);
+ DisplayVersionInfo(FIRMWARE_VERSION_INFO);
+
+ Init_Cat(38400, SERIAL_8N1);
+ initSettings();
+ initPorts();
+
+#ifdef USE_SW_SERIAL
+// if (userCallsignLength > 0 && ((userCallsignLength & 0x80) == 0x80))
+// {
+ userCallsignLength = userCallsignLength & 0x7F;
+// }
+#else
+//for Chracter LCD
+ if (userCallsignLength > 0 && ((userCallsignLength & 0x80) == 0x80))
+ {
+ userCallsignLength = userCallsignLength & 0x7F;
+ DisplayCallsign(userCallsignLength);
+ }
+ else {
+ printLineF(0, F("uBITX v0.20"));
+ delay(500);
+ clearLine2();
+ }
+#endif
+
+#ifdef FACTORY_RECOVERY_BOOTUP
+ if (btnDown())
+ factory_Recovery();
+#endif
+
+ byteToMode(vfoA_mode, 0);
+ initOscillators();
+
+ frequency = vfoA;
+ saveCheckFreq = frequency; //for auto save frequency
+ setFrequency(vfoA);
+
+#ifdef USE_SW_SERIAL
+ SendUbitxData();
+#endif
+
+ updateDisplay();
+
+#ifdef ENABLE_FACTORYALIGN
+ if (btnDown())
+ factory_alignment();
+#endif
+
+ iopSendMode(cwMode, isUSB, digiMode);
+
+}
+
+//Auto save Frequency and Mode with Protected eeprom life by KD8CEC
+void checkAutoSaveFreqMode()
+{
+ //when tx or ritOn, disable auto save
+ if (inTx || ritOn)
+ return;
+
+ //detect change frequency
+ if (saveCheckFreq != frequency)
+ {
+ saveCheckTime = millis();
+ saveCheckFreq = frequency;
+ }
+ else if (saveCheckTime != 0)
+ {
+ //check time for Frequency auto save
+ if (millis() - saveCheckTime > saveIntervalSec * 1000)
+ {
+ FrequencyToVFO(1);
+ saveCheckTime = 0; //for reduce cpu use rate
+ }
+ }
+}
+
+void loop(){
+ /*
+ * KC4UPR - IOP update, 2020-05-03
+ *
+ * Getting rid of the autokeyer code... not planning on using, since any autokeying
+ * would actually be done by the IOP. We'll check the PTT, but only in SSB mode
+ * (same line as CW, so it would be caught by cwKeyer() in CW mode).
+ *
+ * Only check the CW keyer if we are in one of the CW modes. Why? Because we
+ * are using the same input for PTT and CW.
+ */
+// if (isCWAutoMode == 0){ //when CW AutoKey Mode, disable this process
+// if (!txCAT)
+// checkPTT();
+// checkButton();
+// }
+// else
+// controlAutoCW();
+ // KC4UPR: Note, implementation below leaves no manual way to abort TX due to CAT. May
+ // want to add in a way to interrupt CAT transmission with a PTT/CW event.
+ if (!txCAT) {
+ if (cwMode == 0)
+ checkPTT();
+ else
+ cwKeyer();
+ checkButton();
+ }
+
+//cwKeyer();
+
+ //tune only when not tranmsitting
+ if (!inTx){
+ //if (isCWAutoMode == 0 || cwAutoDialType == 1)
+ //{
+ if (ritOn)
+ doRIT();
+ else
+ doTuningWithThresHold();
+ //}
+
+ // KC4UPR: Updated to 100 msec (instead of 250 msec) for improved display responsiveness)
+ if (beforeIdle_ProcessTime < millis() - 100) {
+ idle_process();
+ checkAutoSaveFreqMode(); //move here form out scope for reduce cpu use rate
+ beforeIdle_ProcessTime = millis();
+ }
+ } //end of check TX Status
+
+ //we check CAT after the encoder as it might put the radio into TX
+ Check_Cat(inTx? 1 : 0);
+
+ //for SEND SW Serial
+ #ifdef USE_SW_SERIAL
+ SWS_Process();
+ #endif
+}
diff --git a/ubitx_20/ubitx_eemap.h b/ubitx_20/ubitx_eemap.h
new file mode 100644
index 0000000..c86ac1d
--- /dev/null
+++ b/ubitx_20/ubitx_eemap.h
@@ -0,0 +1,152 @@
+/*************************************************************************
+ header file for EEProm Address Map by KD8CEC
+ It must be protected to protect the factory calibrated calibration.
+-----------------------------------------------------------------------------
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see .
+**************************************************************************/
+#ifndef _UBITX_EEPOM_HEADER__
+#define _UBITX_EEPOM_HEADER__
+
+//==============================================================================
+// Factory-shipped EEProm address
+// (factory Firmware)
+// Address : 0 ~ 31
+//==============================================================================
+#define MASTER_CAL 0
+#define LSB_CAL 4
+#define USB_CAL 8
+#define SIDE_TONE 12
+//these are ids of the vfos as well as their offset into the eeprom storage, don't change these 'magic' values
+#define VFO_A 16
+#define VFO_B 20
+#define CW_SIDETONE 24
+#define CW_SPEED 28
+
+//==============================================================================
+// The spare space available in the original firmware #1
+// Address : 32 ~ 62
+//==============================================================================
+#define RESERVE_FOR_FACTORY1 32
+
+//==============================================================================
+// custom LPF Filter
+// 48 : Using Custom LPF Filter (48 = 0x57 or 0x58 => Using Custom LPF Filter, 0x58 = using A7 IO
+// 49, 50 : LPF1 (49 : MHz (~ Mhz), 50 : Enabled PIN
+// 51, 52 : LPF2
+// 53, 54 : LPF3
+// 55, 56 : LPF4
+// 57, 58 : LPF5
+// 59, 60 : LPF6
+// 61, 62 : LPF7
+//==============================================================================
+#define CUST_LPF_ENABLED 48
+#define CUST_LPF_START 49
+
+//SI5351 I2C Address (Version 1.097)
+#define I2C_ADDR_SI5351 63
+
+//==============================================================================
+// The spare space available in the original firmware #2
+// (Enabled if the EEProm address is insufficient)
+// Address : 64 ~ 100
+//==============================================================================
+#define RESERVE_FOR_FACTORY2 64 //use Factory backup from Version 1.075
+#define FACTORY_BACKUP_YN 64 //Check Backup //Magic : 0x13
+#define FACTORY_VALUES 65 //65 ~ 65 + 32
+
+//==============================================================================
+// KD8CEC EEPROM MAP
+// Address : 101 ~ 1023
+// 256 is the base address
+// 256 ~ 1023 (EEProm Section #1)
+// 255 ~ 101 (EEProm Section #2)
+//==============================================================================
+
+//0x00 : None, 0x01 : MODE, 0x02:BAND+, 0x03:BAND-, 0x04:TUNE_STEP, 0x05:VFO Toggle, 0x06:SplitOn/Off, 0x07:TX/ON-OFF, 0x08:SDR Mode On / Off, 0x09:Rit Toggle
+#define EXTENDED_KEY_RANGE 140 //Extended Key => Set : Start Value, End Value, Key Type, 16 Set (3 * 16 = 48)
+
+#define I2C_LCD_MASTER 190
+#define I2C_LCD_SECOND 191
+
+#define S_METER_LEVELS 230 //LEVEL0 ~ LEVEL7
+
+#define ADVANCED_FREQ_OPTION1 240 //Bit0: use IFTune_Value, Bit1 : use Stored enabled SDR Mode, Bit2 : dynamic sdr frequency
+#define IF1_CAL 241
+#define ENABLE_SDR 242
+#define SDR_FREQUNCY 243
+#define CW_CAL 252
+
+#define VFO_A_MODE 256
+#define VFO_B_MODE 257
+#define CW_DELAY 258
+#define CW_START 259
+#define HAM_BAND_COUNT 260 //
+#define TX_TUNE_TYPE 261 //
+#define HAM_BAND_RANGE 262 //FROM (2BYTE) TO (2BYTE) * 10 = 40byte
+#define HAM_BAND_FREQS 302 //40, 1 BAND = 4Byte most bit is mode
+#define TUNING_STEP 342 //TUNING STEP * 6 (index 1 + STEPS 5) //1STEP :
+
+//for reduce cw key error, eeprom address
+#define CW_ADC_MOST_BIT1 348 //most 2bits of DOT_TO , DOT_FROM, ST_TO, ST_FROM
+#define CW_ADC_ST_FROM 349 //CW ADC Range STRAIGHT KEY from (Lower 8 bit)
+#define CW_ADC_ST_TO 350 //CW ADC Range STRAIGHT KEY to (Lower 8 bit)
+#define CW_ADC_DOT_FROM 351 //CW ADC Range DOT from (Lower 8 bit)
+#define CW_ADC_DOT_TO 352 //CW ADC Range DOT to (Lower 8 bit)
+
+#define CW_ADC_MOST_BIT2 353 //most 2bits of BOTH_TO, BOTH_FROM, DASH_TO, DASH_FROM
+#define CW_ADC_DASH_FROM 354 //CW ADC Range DASH from (Lower 8 bit)
+#define CW_ADC_DASH_TO 355 //CW ADC Range DASH to (Lower 8 bit)
+#define CW_ADC_BOTH_FROM 356 //CW ADC Range BOTH from (Lower 8 bit)
+#define CW_ADC_BOTH_TO 357 //CW ADC Range BOTH to (Lower 8 bit)
+#define CW_KEY_TYPE 358
+#define CW_DISPLAY_SHIFT 359 //Transmits on CWL, CWU Mode, LCD Frequency shifts Sidetone Frequency.
+ //(7:Enable / Disable //0: enable, 1:disable, (default is applied shift)
+ //6 : 0 : Adjust Pulus, 1 : Adjust Minus
+ //0~5: Adjust Value : * 10 = Adjust Value (0~300)
+#define COMMON_OPTION0 360 //0: Confirm : CW Frequency Shift
+ //1 : IF Shift Save
+#define IF_SHIFTVALUE 363
+
+#define DISPLAY_OPTION1 361 //Display Option1
+#define DISPLAY_OPTION2 362 //Display Option2
+
+#define WSPR_COUNT 443 //WSPR_MESSAGE_COUNT
+#define WSPR_MESSAGE1 444 //
+#define WSPR_MESSAGE2 490 //
+#define WSPR_MESSAGE3 536 //
+#define WSPR_MESSAGE4 582 //
+
+#define CHANNEL_FREQ 630 //Channel 1 ~ 20, 1 Channel = 4 bytes
+#define CHANNEL_DESC 710 //Channel 1 ~ 20, 1 Channel = 4 bytes
+#define EXTERNAL_DEVICE_OPT1 770 //for External Deivce 4byte
+#define EXTERNAL_DEVICE_OPT2 774 //for External Deivce 2byte
+
+//Check Firmware type and version
+#define FIRMWAR_ID_ADDR 776 //776 : 0x59, 777 :0x58, 778 : 0x68 : Id Number, if not found id, erase eeprom(32~1023) for prevent system error.
+#define VERSION_ADDRESS 779 //check Firmware version
+//USER INFORMATION
+#define USER_CALLSIGN_KEY 780 //0x59
+#define USER_CALLSIGN_LEN 781 //1BYTE (OPTION + LENGTH) + CALLSIGN (MAXIMUM 18)
+#define USER_CALLSIGN_DAT 782 //CALL SIGN DATA //direct EEPROM to LCD basic offset
+
+//AUTO KEY STRUCTURE
+//AUTO KEY USE 800 ~ 1023
+#define CW_AUTO_MAGIC_KEY 800 //0x73
+#define CW_AUTO_COUNT 801 //0 ~ 255
+#define CW_AUTO_DATA 803 //[INDEX, INDEX, INDEX,DATA,DATA, DATA (Positon offset is CW_AUTO_DATA
+#define CW_DATA_OFSTADJ CW_AUTO_DATA - USER_CALLSIGN_DAT //offset adjust for ditect eeprom to lcd (basic offset is USER_CALLSIGN_DAT
+#define CW_STATION_LEN 1023 //value range : 4 ~ 30
+
+#endif //end of if header define
+
diff --git a/ubitx_20/ubitx_factory_alignment.ino b/ubitx_20/ubitx_factory_alignment.ino
new file mode 100644
index 0000000..4f3e504
--- /dev/null
+++ b/ubitx_20/ubitx_factory_alignment.ino
@@ -0,0 +1,102 @@
+#include "ubitx.h"
+
+/**
+ * This procedure is only for those who have a signal generator/transceiver tuned to exactly 7.150 and a dummy load
+ */
+
+void btnWaitForClick(){
+ while(!btnDown())
+ delay(50);
+ while(btnDown())
+ delay(50);
+ delay(50);
+}
+
+void factory_alignment(){
+
+ factoryCalibration(1);
+ line2DisplayStatus = 1;
+
+ if (calibration == 0){
+ printLine2("Setup Aborted");
+ return;
+ }
+
+ //move it away to 7.160 for an LSB signal
+ setFrequency(7160000l);
+ updateDisplay();
+ printLine2("#2 BFO");
+ delay(1000);
+
+#if UBITX_BOARD_VERSION == 5
+ usbCarrier = 11053000l;
+ menuSetupCarrier(1);
+ if (usbCarrier == 11053000l){
+ printLine2("Setup Aborted");
+ return;
+ }
+
+#else
+ usbCarrier = 11994999l;
+ menuSetupCarrier(1);
+ if (usbCarrier == 11994999l){
+ printLine2("Setup Aborted");
+ return;
+ }
+
+#endif
+
+
+
+ printLine2("#3:Test 3.5MHz");
+ cwMode = 0;
+ isUSB = false;
+ setFrequency(3500000l);
+ updateDisplay();
+
+ while (!btnDown()){
+ checkPTT();
+ delay(100);
+ }
+
+ btnWaitForClick();
+ printLine2("#4:Test 7MHz");
+
+ setFrequency(7150000l);
+ updateDisplay();
+ while (!btnDown()){
+ checkPTT();
+ delay(100);
+ }
+
+ btnWaitForClick();
+ printLine2("#5:Test 14MHz");
+
+ cwMode = 0;
+ isUSB = true;
+ setFrequency(14000000l);
+ updateDisplay();
+ while (!btnDown()){
+ checkPTT();
+ delay(100);
+ }
+
+ btnWaitForClick();
+ printLine2("#6:Test 28MHz");
+
+ setFrequency(28000000l);
+ updateDisplay();
+ while (!btnDown()){
+ checkPTT();
+ delay(100);
+ }
+
+ printLine2("Alignment done");
+ delay(1000);
+
+ cwMode = 0;
+ isUSB = false;
+ setFrequency(7150000l);
+ updateDisplay();
+
+}
diff --git a/ubitx_20/ubitx_keyer.ino b/ubitx_20/ubitx_keyer.ino
new file mode 100644
index 0000000..691b1d0
--- /dev/null
+++ b/ubitx_20/ubitx_keyer.ino
@@ -0,0 +1,403 @@
+/**
+ CW Keyer
+ CW Key logic change with ron's code (ubitx_keyer.cpp)
+ Ron's logic has been modified to work with the original uBITX by KD8CEC
+
+ Original Comment ----------------------------------------------------------------------------
+ * The CW keyer handles either a straight key or an iambic / paddle key.
+ * They all use just one analog input line. This is how it works.
+ * The analog line has the internal pull-up resistor enabled.
+ * When a straight key is connected, it shorts the pull-up resistor, analog input is 0 volts
+ * When a paddle is connected, the dot and the dash are connected to the analog pin through
+ * a 10K and a 2.2K resistors. These produce a 4v and a 2v input to the analog pins.
+ * So, the readings are as follows :
+ * 0v - straight key
+ * 1-2.5 v - paddle dot
+ * 2.5 to 4.5 v - paddle dash
+ * 2.0 to 0.5 v - dot and dash pressed
+ *
+ * The keyer is written to transparently handle all these cases
+ *
+ * Generating CW
+ * The CW is cleanly generated by unbalancing the front-end mixer
+ * and putting the local oscillator directly at the CW transmit frequency.
+ * The sidetone, generated by the Arduino is injected into the volume control
+ */
+
+
+// in milliseconds, this is the parameter that determines how long the tx will hold between cw key downs
+//#define CW_TIMEOUT (600l) //Change to CW Delaytime for value save to eeprom
+#define PADDLE_DOT 1
+#define PADDLE_DASH 2
+#define PADDLE_BOTH 3
+#define PADDLE_STRAIGHT 4
+
+//we store the last padde's character
+//to alternatively send dots and dashes
+//when both are simultaneously pressed
+char lastPaddle = 0;
+
+//reads the analog keyer pin and reports the paddle
+byte getPaddle(){
+ /*
+ * KC4UPR - IOP update, 2020-05-03
+ *
+ * Modifying this for the uBITX IOP. Big picture:
+ *
+ * (1) It uses the PTT input line.
+ *
+ * (2) It's always "straight key" mode (the IOP provides the keyer).
+ */
+
+ if (digitalRead(PTT) == 1) // key/PTT is up
+ return 0;
+ else
+ return PADDLE_STRAIGHT;
+
+/*
+ int paddle = analogRead(ANALOG_KEYER);
+
+ if (paddle > 800) // above 4v is up
+ return 0;
+
+ if (paddle > 600) // 4-3v is dot
+ return PADDLE_DASH;
+ else if (paddle > 300) //1-2v is dash
+ return PADDLE_DOT;
+ else if (paddle > 50)
+ return PADDLE_BOTH; //both are between 1 and 2v
+ else
+ return PADDLE_STRAIGHT; //less than 1v is the straight key
+*/
+}
+
+/**
+ * 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(){
+ keyDown = 1; //tracks the CW_KEY
+ tone(CW_TONE, (int)sideTone);
+ digitalWrite(CW_KEY, 1);
+
+ //Modified by KD8CEC, for CW Delay Time save to eeprom
+ //cwTimeout = millis() + CW_TIMEOUT;
+ cwTimeout = millis() + cwDelayTime * 10;
+}
+
+/**
+ * Stops the cw carrier transmission along with the sidetone
+ * Pushes the cwTimeout further into the future
+ */
+void cwKeyUp(){
+ keyDown = 0; //tracks the CW_KEY
+ noTone(CW_TONE);
+ digitalWrite(CW_KEY, 0);
+
+ //Modified by KD8CEC, for CW Delay Time save to eeprom
+ //cwTimeout = millis() + CW_TIMEOUT;
+ cwTimeout = millis() + cwDelayTime * 10;
+}
+
+//Variables for Ron's new logic
+#define DIT_L 0x01 // DIT latch
+#define DAH_L 0x02 // DAH latch
+#define DIT_PROC 0x04 // DIT is being processed
+#define PDLSWAP 0x08 // 0 for normal, 1 for swap
+#define IAMBICB 0x10 // 0 for Iambic A, 1 for Iambic B
+enum KSTYPE {IDLE, CHK_DIT, CHK_DAH, KEYED_PREP, KEYED, INTER_ELEMENT };
+static unsigned long ktimer;
+unsigned char keyerState = IDLE;
+
+//Below is a test to reduce the keying error. do not delete lines
+//create by KD8CEC for compatible with new CW Logic
+char update_PaddleLatch(byte isUpdateKeyState) {
+ /*
+ * KC4UPR - IOP update, 2020-05-03
+ *
+ * Modifying this for the uBITX IOP. Big picture:
+ *
+ * No iambic keyer. It's always "straight key" based on the IOP.
+ *
+ * It uses the PTT line.
+ */
+ return (digitalRead(PTT) ? 0 : DIT_L);
+/*
+ unsigned char tmpKeyerControl = 0;
+ int paddle = analogRead(ANALOG_KEYER);
+
+ if (paddle >= cwAdcDashFrom && paddle <= cwAdcDashTo)
+ tmpKeyerControl |= DAH_L;
+ else if (paddle >= cwAdcDotFrom && paddle <= cwAdcDotTo)
+ tmpKeyerControl |= DIT_L;
+ else if (paddle >= cwAdcBothFrom && paddle <= cwAdcBothTo)
+ tmpKeyerControl |= (DAH_L | DIT_L) ;
+ else
+ {
+ if (Iambic_Key)
+ tmpKeyerControl = 0 ;
+ else if (paddle >= cwAdcSTFrom && paddle <= cwAdcSTTo)
+ tmpKeyerControl = DIT_L ;
+ else
+ tmpKeyerControl = 0 ;
+ }
+
+ if (isUpdateKeyState == 1)
+ keyerControl |= tmpKeyerControl;
+
+ return tmpKeyerControl;
+*/
+}
+
+/*****************************************************************************
+// New logic, by RON
+// modified by KD8CEC
+******************************************************************************/
+void cwKeyer(void){
+ /*
+ * KC4UPR - IOP update, 2020-05-03
+ *
+ * Modifying this for the uBITX IOP. Big picture:
+ *
+ * No iambic keyer. It's always "straight key" based on the IOP.
+ */
+// lastPaddle = 0;
+// bool continue_loop = true;
+// unsigned tmpKeyControl = 0;
+//
+// if( Iambic_Key ) {
+// while(continue_loop) {
+// switch (keyerState) {
+// case IDLE:
+// tmpKeyControl = update_PaddleLatch(0);
+// if ( tmpKeyControl == DAH_L || tmpKeyControl == DIT_L ||
+// tmpKeyControl == (DAH_L | DIT_L) || (keyerControl & 0x03)) {
+// update_PaddleLatch(1);
+// keyerState = CHK_DIT;
+// }else{
+// if (0 < cwTimeout && cwTimeout < millis()){
+// cwTimeout = 0;
+// stopTx();
+// }
+// continue_loop = false;
+// }
+// break;
+//
+// case CHK_DIT:
+// if (keyerControl & DIT_L) {
+// keyerControl |= DIT_PROC;
+// ktimer = cwSpeed;
+// keyerState = KEYED_PREP;
+// }else{
+// keyerState = CHK_DAH;
+// }
+// break;
+//
+// case CHK_DAH:
+// if (keyerControl & DAH_L) {
+// ktimer = cwSpeed*3;
+// keyerState = KEYED_PREP;
+// }else{
+// keyerState = IDLE;
+// }
+// break;
+//
+// case KEYED_PREP:
+// //modified KD8CEC
+// /*
+// ktimer += millis(); // set ktimer to interval end time
+// keyerControl &= ~(DIT_L + DAH_L); // clear both paddle latch bits
+// keyerState = KEYED; // next state
+// if (!inTx){
+// //DelayTime Option
+// delay_background(delayBeforeCWStartTime * 2, 2);
+//
+// keyDown = 0;
+// cwTimeout = millis() + cwDelayTime * 10; //+ CW_TIMEOUT;
+// startTx(TX_CW, 1);
+// }
+// */
+// if (!inTx){
+// //DelayTime Option
+// delay_background(delayBeforeCWStartTime * 2, 2);
+//
+// keyDown = 0;
+// cwTimeout = millis() + cwDelayTime * 10; //+ CW_TIMEOUT;
+// startTx(TX_CW, 1);
+// }
+// ktimer += millis(); // set ktimer to interval end time
+// keyerControl &= ~(DIT_L + DAH_L); // clear both paddle latch bits
+// keyerState = KEYED; // next state
+//
+// cwKeydown();
+// break;
+//
+// case KEYED:
+// if (millis() > ktimer) { // are we at end of key down ?
+// cwKeyUp();
+// ktimer = millis() + cwSpeed; // inter-element time
+// keyerState = INTER_ELEMENT; // next state
+// }else if (keyerControl & IAMBICB) {
+// update_PaddleLatch(1); // early paddle latch in Iambic B mode
+// }
+// break;
+//
+// case INTER_ELEMENT:
+// // Insert time between dits/dahs
+// update_PaddleLatch(1); // latch paddle state
+// if (millis() > ktimer) { // are we at end of inter-space ?
+// if (keyerControl & DIT_PROC) { // was it a dit or dah ?
+// keyerControl &= ~(DIT_L + DIT_PROC); // clear two bits
+// keyerState = CHK_DAH; // dit done, check for dah
+// }else{
+// keyerControl &= ~(DAH_L); // clear dah latch
+// keyerState = IDLE; // go idle
+// }
+// }
+// break;
+// }
+//
+// Check_Cat(2);
+// } //end of while
+// }
+// else{
+ while(1){
+ if (update_PaddleLatch(0) == DIT_L) {
+ // if we are here, it is only because the key is pressed
+ if (!inTx){
+ //DelayTime Option
+ delay_background(delayBeforeCWStartTime * 2, 2);
+
+ keyDown = 0;
+ cwTimeout = millis() + cwDelayTime * 10; //+ CW_TIMEOUT;
+ startTx(TX_CW, 1);
+ }
+ cwKeydown();
+
+ while ( update_PaddleLatch(0) == DIT_L )
+ delay_background(1, 3);
+
+ cwKeyUp();
+ }
+ else{
+ if (0 < cwTimeout && cwTimeout < millis()){
+ cwTimeout = 0;
+ keyDown = 0;
+ stopTx();
+ }
+ //if (!cwTimeout) //removed by KD8CEC
+ // return;
+ // got back to the beginning of the loop, if no further activity happens on straight key
+ // we will time out, and return out of this routine
+ //delay(5);
+ //delay_background(5, 3); //removed by KD8CEC
+ //continue; //removed by KD8CEC
+ return; //Tx stop control by Main Loop
+ }
+
+ Check_Cat(2);
+ } //end of while
+// } //end of elese
+}
+
+
+//=======================================================================================
+//Before logic
+//by Farhan and modified by KD8CEC
+//======================================================================================
+
+/**
+ * The keyer handles the straight key as well as the iambic key
+ * This module keeps looping until the user stops sending cw
+ * if the cwTimeout is set to 0, then it means, we have to exit the keyer loop
+ * Each time the key is hit the cwTimeout is pushed to a time in the future by cwKeyDown()
+ */
+ /*
+void cwKeyer(){
+ byte paddle;
+ lastPaddle = 0;
+
+ while(1){
+ paddle = getPaddle();
+
+ // do nothing if the paddle has not been touched, unless
+ // we are in the cw mode and we have timed out
+ if (!paddle){
+ //modifed by KD8CEC for auto CW Send
+ if (isCWAutoMode > 1) //if while auto cw sending, dont stop tx by paddle position
+ return;
+
+ if (0 < cwTimeout && cwTimeout < millis()){
+ cwTimeout = 0;
+ keyDown = 0;
+ stopTx();
+ }
+
+ if (!cwTimeout)
+ return;
+
+ Check_Cat(2); //for uBITX on Raspberry pi, when straight keying, disconnect / test complete
+ continue;
+ }
+
+ //if while auto cw send, stop auto cw
+ //but isAutoCWHold for Manual Keying with cwAutoSend
+ if (isCWAutoMode > 1 && isAutoCWHold == 0)
+ isCWAutoMode = 1; //read status
+
+ //Remoark Debug code / Serial Use by CAT Protocol
+ //Serial.print("paddle:");Serial.println(paddle);
+ // if we are here, it is only because the key or the paddle is pressed
+ if (!inTx){
+ keyDown = 0;
+ //Modified by KD8CEC, for CW Delay Time save to eeprom
+ //cwTimeout = millis() + CW_TIMEOUT;
+ cwTimeout = millis() + cwDelayTime * 10;
+
+ startTx(TX_CW, 0); //disable updateDisplay Command for reduce latency time
+ updateDisplay();
+
+ //DelayTime Option
+ delay_background(delayBeforeCWStartTime * 2, 2);
+ }
+
+ // star the transmission)
+ // we store the transmitted character in the lastPaddle
+ cwKeydown();
+ if (paddle == PADDLE_DOT){
+ //delay(cwSpeed);
+ delay_background(cwSpeed, 3);
+ lastPaddle = PADDLE_DOT;
+ }
+ else if (paddle == PADDLE_DASH){
+ //delay(cwSpeed * 3);
+ delay_background(cwSpeed * 3, 3);
+ lastPaddle = PADDLE_DASH;
+ }
+ else if (paddle == PADDLE_BOTH){ //both paddles down
+ //depending upon what was sent last, send the other
+ if (lastPaddle == PADDLE_DOT) {
+ //delay(cwSpeed * 3);
+ delay_background(cwSpeed * 3, 3);
+ lastPaddle = PADDLE_DASH;
+ }else{
+ //delay(cwSpeed);
+ delay_background(cwSpeed, 3);
+ lastPaddle = PADDLE_DOT;
+ }
+ }
+ else if (paddle == PADDLE_STRAIGHT){
+ while (getPaddle() == PADDLE_STRAIGHT) {
+ delay(1);
+ Check_Cat(2);
+ }
+ lastPaddle = PADDLE_STRAIGHT;
+ }
+ cwKeyUp();
+ //introduce a dot long gap between characters if the keyer was used
+ if (lastPaddle != PADDLE_STRAIGHT)
+ delay(cwSpeed);
+ }
+}
+*/
diff --git a/ubitx_20/ubitx_lcd.h b/ubitx_20/ubitx_lcd.h
new file mode 100644
index 0000000..bc477d3
--- /dev/null
+++ b/ubitx_20/ubitx_lcd.h
@@ -0,0 +1,64 @@
+/*************************************************************************
+ header file for LCD by KD8CEC
+-----------------------------------------------------------------------------
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see .
+**************************************************************************/
+#ifndef _UBITX_LCD_HEADER__
+#define _UBITX_LCD_HEADER__
+
+//Common Defines *********************************************************
+#define LCD_CLEARDISPLAY 0x01
+#define LCD_RETURNHOME 0x02
+#define LCD_ENTRYMODESET 0x04
+#define LCD_DISPLAYCONTROL 0x08
+#define LCD_CURSORSHIFT 0x10
+#define LCD_FUNCTIONSET 0x20
+#define LCD_SETCGRAMADDR 0x40
+#define LCD_SETDDRAMADDR 0x80
+
+// flags for display entry mode
+#define LCD_ENTRYRIGHT 0x00
+#define LCD_ENTRYLEFT 0x02
+#define LCD_ENTRYSHIFTINCREMENT 0x01
+#define LCD_ENTRYSHIFTDECREMENT 0x00
+
+// flags for display on/off control
+#define LCD_DISPLAYON 0x04
+#define LCD_DISPLAYOFF 0x00
+#define LCD_CURSORON 0x02
+#define LCD_CURSOROFF 0x00
+#define LCD_BLINKON 0x01
+#define LCD_BLINKOFF 0x00
+
+// flags for display/cursor shift
+#define LCD_DISPLAYMOVE 0x08
+#define LCD_CURSORMOVE 0x00
+#define LCD_MOVERIGHT 0x04
+#define LCD_MOVELEFT 0x00
+
+// flags for function set
+#define LCD_8BITMODE 0x10
+#define LCD_4BITMODE 0x00
+#define LCD_2LINE 0x08
+#define LCD_1LINE 0x00
+#define LCD_5x10DOTS 0x04
+#define LCD_5x8DOTS 0x00
+
+// flags for backlight control
+#define LCD_BACKLIGHT 0x08
+#define LCD_NOBACKLIGHT 0x00
+
+#endif //end of if header define
+
+
diff --git a/ubitx_20/ubitx_lcd_1602.ino b/ubitx_20/ubitx_lcd_1602.ino
new file mode 100644
index 0000000..41c169b
--- /dev/null
+++ b/ubitx_20/ubitx_lcd_1602.ino
@@ -0,0 +1,823 @@
+/*************************************************************************
+ KD8CEC's uBITX Display Routine for LCD1602 Parrel
+ 1.This is the display code for the default LCD mounted in uBITX.
+ 2.Some functions moved from uBITX_Ui.
+-----------------------------------------------------------------------------
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see .
+
+**************************************************************************/
+#include "ubitx.h"
+#include "ubitx_lcd.h"
+
+//========================================================================
+//Begin of TinyLCD Library by KD8CEC
+//========================================================================
+
+#ifdef UBITX_DISPLAY_LCD1602P
+/*************************************************************************
+ LCD1602_TINY Library for 16 x 2 LCD
+ Referecnce Source : LiquidCrystal.cpp
+ KD8CEC
+
+ This source code is modified version for small program memory
+ from Arduino LiquidCrystal Library
+
+ I wrote this code myself, so there is no license restriction.
+ So this code allows anyone to write with confidence.
+ But keep it as long as the original author of the code.
+ DE Ian KD8CEC
+**************************************************************************/
+
+#define LCD_Command(x) (LCD_Send(x, LOW))
+#define LCD_Write(x) (LCD_Send(x, HIGH))
+
+#define UBITX_DISPLAY_LCD1602_BASE
+
+//Define connected PIN
+#define LCD_PIN_RS 8
+#define LCD_PIN_EN 9
+uint8_t LCD_PIN_DAT[4] = {10, 11, 12, 13};
+
+void write4bits(uint8_t value)
+{
+ for (int i = 0; i < 4; i++)
+ digitalWrite(LCD_PIN_DAT[i], (value >> i) & 0x01);
+
+ digitalWrite(LCD_PIN_EN, LOW);
+ delayMicroseconds(1);
+ digitalWrite(LCD_PIN_EN, HIGH);
+ delayMicroseconds(1); // enable pulse must be >450ns
+ digitalWrite(LCD_PIN_EN, LOW);
+ delayMicroseconds(100); // commands need > 37us to settle
+}
+
+void LCD_Send(uint8_t value, uint8_t mode)
+{
+ digitalWrite(LCD_PIN_RS, mode);
+ write4bits(value>>4);
+ write4bits(value);
+}
+
+void LCD1602_Init()
+{
+ pinMode(LCD_PIN_RS, OUTPUT);
+ pinMode(LCD_PIN_EN, OUTPUT);
+ for (int i = 0; i < 4; i++)
+ pinMode(LCD_PIN_DAT[i], OUTPUT);
+
+ delayMicroseconds(50);
+
+ // Now we pull both RS and R/W low to begin commands
+ digitalWrite(LCD_PIN_RS, LOW);
+ digitalWrite(LCD_PIN_EN, LOW);
+
+ // we start in 8bit mode, try to set 4 bit mode
+ write4bits(0x03);
+ delayMicroseconds(4500); // wait min 4.1ms
+
+ // second try
+ write4bits(0x03);
+ delayMicroseconds(4500); // wait min 4.1ms
+
+ // third go!
+ write4bits(0x03);
+ delayMicroseconds(150);
+
+ // finally, set to 4-bit interface
+ write4bits(0x02);
+
+ // finally, set # lines, font size, etc.
+ LCD_Command(LCD_FUNCTIONSET | LCD_4BITMODE | LCD_1LINE | LCD_5x8DOTS | LCD_2LINE);
+
+ // turn the display on with no cursor or blinking default
+ LCD_Command(LCD_DISPLAYCONTROL | LCD_DISPLAYON | LCD_CURSOROFF | LCD_BLINKOFF);
+
+ // clear it off
+ LCD_Command(LCD_CLEARDISPLAY); // clear display, set cursor position to zero
+ delayMicroseconds(2000); // this command takes a long time!
+
+ LCD_Command(LCD_ENTRYMODESET | LCD_ENTRYLEFT | LCD_ENTRYSHIFTDECREMENT);
+}
+
+#endif
+//========================================================================
+//End of TinyLCD Library by KD8CEC
+//========================================================================
+
+
+//========================================================================
+//Begin of I2CTinyLCD Library by KD8CEC
+//========================================================================
+#ifdef UBITX_DISPLAY_LCD1602I
+#include
+/*************************************************************************
+ I2C Tiny LCD Library
+ Referecnce Source : LiquidCrystal_I2C.cpp // Based on the work by DFRobot
+ KD8CEC
+
+ This source code is modified version for small program memory
+ from Arduino LiquidCrystal_I2C Library
+
+ I wrote this code myself, so there is no license restriction.
+ So this code allows anyone to write with confidence.
+ But keep it as long as the original author of the code.
+ Ian KD8CEC
+**************************************************************************/
+#define UBITX_DISPLAY_LCD1602_BASE
+
+#define En B00000100 // Enable bit
+#define Rw B00000010 // Read/Write bit
+#define Rs B00000001 // Register select bit
+
+#define LCD_Command(x) (LCD_Send(x, 0))
+#define LCD_Write(x) (LCD_Send(x, Rs))
+
+uint8_t _Addr;
+uint8_t _displayfunction;
+uint8_t _displaycontrol;
+uint8_t _displaymode;
+uint8_t _numlines;
+uint8_t _cols;
+uint8_t _rows;
+uint8_t _backlightval;
+
+#define printIIC(args) Wire.write(args)
+
+void expanderWrite(uint8_t _data)
+{
+ Wire.beginTransmission(_Addr);
+ printIIC((int)(_data) | _backlightval);
+ Wire.endTransmission();
+}
+
+void pulseEnable(uint8_t _data){
+ expanderWrite(_data | En); // En high
+ delayMicroseconds(1); // enable pulse must be >450ns
+
+ expanderWrite(_data & ~En); // En low
+ delayMicroseconds(50); // commands need > 37us to settle
+}
+
+void write4bits(uint8_t value)
+{
+ expanderWrite(value);
+ pulseEnable(value);
+}
+
+void LCD_Send(uint8_t value, uint8_t mode)
+{
+ uint8_t highnib=value&0xf0;
+ uint8_t lownib=(value<<4)&0xf0;
+ write4bits((highnib)|mode);
+ write4bits((lownib)|mode);
+}
+
+
+// Turn the (optional) backlight off/on
+void noBacklight(void) {
+ _backlightval=LCD_NOBACKLIGHT;
+ expanderWrite(0);
+}
+
+void backlight(void) {
+ _backlightval=LCD_BACKLIGHT;
+ expanderWrite(0);
+}
+
+void LCD1602_Init()
+{
+ //I2C Init
+ _Addr = I2C_LCD_MASTER_ADDRESS;
+ _cols = 16;
+ _rows = 2;
+ _backlightval = LCD_NOBACKLIGHT;
+ Wire.begin();
+
+ delay(50);
+
+ // Now we pull both RS and R/W low to begin commands
+ expanderWrite(_backlightval); // reset expanderand turn backlight off (Bit 8 =1)
+ delay(1000);
+ //put the LCD into 4 bit mode
+ // this is according to the hitachi HD44780 datasheet
+ // figure 24, pg 46
+
+ // we start in 8bit mode, try to set 4 bit mode
+ write4bits(0x03 << 4);
+ delayMicroseconds(4500); // wait min 4.1ms
+
+ // second try
+ write4bits(0x03 << 4);
+ delayMicroseconds(4500); // wait min 4.1ms
+
+ // third go!
+ write4bits(0x03 << 4);
+ delayMicroseconds(150);
+
+ // finally, set to 4-bit interface
+ write4bits(0x02 << 4);
+
+ // finally, set # lines, font size, etc.
+ LCD_Command(LCD_FUNCTIONSET | LCD_4BITMODE | LCD_1LINE | LCD_5x8DOTS | LCD_2LINE);
+
+ // turn the display on with no cursor or blinking default
+ LCD_Command(LCD_DISPLAYCONTROL | LCD_DISPLAYON | LCD_CURSOROFF | LCD_BLINKOFF);
+
+ // clear it off
+ LCD_Command(LCD_CLEARDISPLAY); // clear display, set cursor position to zero
+ //delayMicroseconds(2000); // this command takes a long time!
+ delayMicroseconds(1000); // this command takes a long time!
+
+ LCD_Command(LCD_ENTRYMODESET | LCD_ENTRYLEFT | LCD_ENTRYSHIFTDECREMENT);
+
+ backlight();
+}
+
+/*
+void LCD_Print(const char *c)
+{
+ for (uint8_t i = 0; i < strlen(c); i++)
+ {
+ if (*(c + i) == 0x00) return;
+ LCD_Write(*(c + i));
+ }
+}
+
+void LCD_SetCursor(uint8_t col, uint8_t row)
+{
+ LCD_Command(LCD_SETDDRAMADDR | (col + row * 0x40)); //0 : 0x00, 1 : 0x40, only for 16 x 2 lcd
+}
+
+void LCD_CreateChar(uint8_t location, uint8_t charmap[])
+{
+ location &= 0x7; // we only have 8 locations 0-7
+ LCD_Command(LCD_SETCGRAMADDR | (location << 3));
+ for (int i=0; i<8; i++)
+ LCD_Write(charmap[i]);
+}
+*/
+#endif
+//========================================================================
+//End of I2CTinyLCD Library by KD8CEC
+//========================================================================
+
+
+//========================================================================
+// 16 X 02 LCD Routines
+//Begin of Display Base Routines (Init, printLine..)
+//========================================================================
+#ifdef UBITX_DISPLAY_LCD1602_BASE
+
+//SWR GRAPH, DrawMeter and drawingMeter Logic function by VK2ETA
+#define OPTION_SKINNYBARS
+
+char c[40], b[40];
+char printBuff[2][17]; //mirrors what is showing on the two lines of the display
+
+
+void LCD_Print(const char *c)
+{
+ for (uint8_t i = 0; i < strlen(c); i++)
+ {
+ if (*(c + i) == 0x00) return;
+ LCD_Write(*(c + i));
+ }
+}
+
+void LCD_SetCursor(uint8_t col, uint8_t row)
+{
+ LCD_Command(LCD_SETDDRAMADDR | (col + row * 0x40)); //0 : 0x00, 1 : 0x40, only for 16 x 2 lcd
+}
+
+void LCD_CreateChar(uint8_t location, uint8_t charmap[])
+{
+ location &= 0x7; // we only have 8 locations 0-7
+ LCD_Command(LCD_SETCGRAMADDR | (location << 3));
+ for (int i=0; i<8; i++)
+ LCD_Write(charmap[i]);
+}
+
+void LCD_Init(void)
+{
+ LCD1602_Init();
+ initMeter(); //for Meter Display
+}
+
+// The generic routine to display one line on the LCD
+void printLine(unsigned char linenmbr, const char *c) {
+ if ((displayOption1 & 0x01) == 0x01)
+ linenmbr = (linenmbr == 0 ? 1 : 0); //Line Toggle
+ if (strcmp(c, printBuff[linenmbr])) { // only refresh the display when there was a change
+ LCD_SetCursor(0, linenmbr); // place the cursor at the beginning of the selected line
+ LCD_Print(c);
+ strcpy(printBuff[linenmbr], c);
+
+ for (byte i = strlen(c); i < 16; i++) { // add white spaces until the end of the 16 characters line is reached
+ LCD_Write(' ');
+ }
+ }
+}
+
+void printLineF(char linenmbr, const __FlashStringHelper *c)
+{
+ int i;
+ char tmpBuff[17];
+ PGM_P p = reinterpret_cast(c);
+
+ for (i = 0; i < 17; i++){
+ unsigned char fChar = pgm_read_byte(p++);
+ tmpBuff[i] = fChar;
+ if (fChar == 0)
+ break;
+ }
+
+ printLine(linenmbr, tmpBuff);
+}
+
+#define LCD_MAX_COLUMN 16
+void printLineFromEEPRom(char linenmbr, char lcdColumn, byte eepromStartIndex, byte eepromEndIndex, char offsetTtype) {
+ if ((displayOption1 & 0x01) == 0x01)
+ linenmbr = (linenmbr == 0 ? 1 : 0); //Line Toggle
+
+ LCD_SetCursor(lcdColumn, linenmbr);
+
+ for (byte i = eepromStartIndex; i <= eepromEndIndex; i++)
+ {
+ if (++lcdColumn <= LCD_MAX_COLUMN)
+ LCD_Write(EEPROM.read((offsetTtype == 0 ? USER_CALLSIGN_DAT : WSPR_MESSAGE1) + i));
+ else
+ break;
+ }
+
+ for (byte i = lcdColumn; i < 16; i++) //Right Padding by Space
+ LCD_Write(' ');
+}
+
+// short cut to print to the first line
+void printLine1(const char *c)
+{
+ printLine(1,c);
+}
+// short cut to print to the first line
+void printLine2(const char *c)
+{
+ printLine(0,c);
+}
+
+void clearLine2()
+{
+ printLine2("");
+ line2DisplayStatus = 0;
+}
+
+// short cut to print to the first line
+void printLine1Clear(){
+ printLine(1,"");
+}
+// short cut to print to the first line
+void printLine2Clear(){
+ printLine(0, "");
+}
+
+void printLine2ClearAndUpdate(){
+ printLine(0, "");
+ line2DisplayStatus = 0;
+ updateDisplay();
+}
+
+//==================================================================================
+//End of Display Base Routines
+//==================================================================================
+
+
+//==================================================================================
+//Begin of User Interface Routines
+//==================================================================================
+
+//Main Display
+// this builds up the top line of the display with frequency and mode
+void updateDisplay() {
+ // tks Jack Purdum W8TEE
+ // replaced fsprint commmands by str commands for code size reduction
+ // replace code for Frequency numbering error (alignment, point...) by KD8CEC
+ int i;
+ unsigned long tmpFreq = frequency; //
+
+ memset(c, 0, sizeof(c));
+
+ if (inTx){
+ //if (isCWAutoMode == 2) {
+ // for (i = 0; i < 4; i++)
+ // c[3-i] = (i < autoCWSendReservCount ? byteToChar(autoCWSendReserv[i]) : ' ');
+
+ //display Sending Index
+ // c[4] = byteToChar(sendingCWTextIndex);
+ // c[5] = '=';
+ //}
+ //else {
+ if (cwTimeout > 0)
+ strcpy(c, " CW:");
+ else
+ strcpy(c, " TX:");
+ //}
+ }
+ else {
+ if (ritOn)
+ strcpy(c, "RIT ");
+ else {
+ if (cwMode == 0)
+ {
+ if (digiMode == 1) {
+// if (isUSB)
+ strcpy(c, "DIG ");
+// else
+// strcpy(c, "DGL ");
+// } else if (isTest == 1) {
+// if (isUSB)
+// strcpy(c, "TTU ");
+// else
+// strcpy(c, "TTL ");
+ } else {
+ if (isUSB)
+ strcpy(c, "USB ");
+ else
+ strcpy(c, "LSB ");
+ }
+ }
+ else if (cwMode == 1)
+ {
+ strcpy(c, "CWR ");
+ }
+ else
+ {
+ strcpy(c, "CW ");
+ }
+ }
+ if (vfoActive == VFO_A) // VFO A is active
+ strcat(c, "A:");
+ else
+ strcat(c, "B:");
+ }
+
+ //Fixed by Mitani Massaru (JE4SMQ)
+ if (isShiftDisplayCWFreq == 1)
+ {
+ if (cwMode == 1) //CWL
+ tmpFreq = tmpFreq - sideTone + shiftDisplayAdjustVal;
+ else if (cwMode == 2) //CWU
+ tmpFreq = tmpFreq + sideTone + shiftDisplayAdjustVal;
+ }
+
+ //display frequency
+ for (int i = 15; i >= 6; i--) {
+ if (tmpFreq > 0) {
+ if (i == 12 || i == 8) c[i] = '.';
+ else {
+ c[i] = tmpFreq % 10 + 0x30;
+ tmpFreq /= 10;
+ }
+ }
+ else
+ c[i] = ' ';
+ }
+
+ //remarked by KD8CEC
+ //already RX/TX status display, and over index (16 x 2 LCD)
+ //if (inTx)
+ // strcat(c, " TX");
+ printLine(1, c);
+
+ byte diplayVFOLine = 1;
+ if ((displayOption1 & 0x01) == 0x01)
+ diplayVFOLine = 0;
+
+ if ((vfoActive == VFO_A && ((isDialLock & 0x01) == 0x01)) ||
+ (vfoActive == VFO_B && ((isDialLock & 0x02) == 0x02))) {
+ LCD_SetCursor(5,diplayVFOLine);
+ LCD_Write((uint8_t)0);
+ }
+ //else if (isCWAutoMode == 2){
+ // LCD_SetCursor(5,diplayVFOLine);
+ // LCD_Write(0x7E);
+ //}
+ else
+ {
+ LCD_SetCursor(5,diplayVFOLine);
+ LCD_Write(':');
+ }
+}
+
+char line2Buffer[17];
+//KD8CEC 200Hz ST
+//L14.150 200Hz ST
+//U14.150 +150khz
+int freqScrollPosition = 0;
+
+//Example Line2 Optinal Display
+//immediate execution, not call by scheulder
+//warning : unused parameter 'displayType' <-- ignore, this is reserve
+void updateLine2Buffer(char displayType)
+{
+ unsigned long tmpFreq = 0;
+
+ if (stateTopLine == 2) {
+ strncpy(line2Buffer, iopMenuDisplay, 16);
+ stateTopLine = 1;
+ }
+ if (stateTopLine == 1) {
+ return;
+ }
+
+ if (ritOn)
+ {
+ strcpy(line2Buffer, "RitTX:");
+
+ //display frequency
+ tmpFreq = ritTxFrequency;
+
+ //Fixed by Mitani Massaru (JE4SMQ)
+ if (isShiftDisplayCWFreq == 1)
+ {
+ if (cwMode == 1) //CWL
+ tmpFreq = tmpFreq - sideTone + shiftDisplayAdjustVal;
+ else if (cwMode == 2) //CWU
+ tmpFreq = tmpFreq + sideTone + shiftDisplayAdjustVal;
+ }
+
+ for (int i = 15; i >= 6; i--) {
+ if (tmpFreq > 0) {
+ if (i == 12 || i == 8) line2Buffer[i] = '.';
+ else {
+ line2Buffer[i] = tmpFreq % 10 + 0x30;
+ tmpFreq /= 10;
+ }
+ }
+ else
+ line2Buffer[i] = ' ';
+ }
+
+ return;
+ } //end of ritOn display
+
+ //other VFO display
+ if (vfoActive == VFO_B)
+ {
+ tmpFreq = vfoA;
+ }
+ else
+ {
+ tmpFreq = vfoB;
+ }
+
+ // EXAMPLE 1 & 2
+ //U14.150.100
+ //display frequency
+ for (int i = 9; i >= 0; i--) {
+ if (tmpFreq > 0) {
+ if (i == 2 || i == 6) line2Buffer[i] = '.';
+ else {
+ line2Buffer[i] = tmpFreq % 10 + 0x30;
+ tmpFreq /= 10;
+ }
+ }
+ else
+ line2Buffer[i] = ' ';
+ }
+
+ //EXAMPLE #1
+ if ((displayOption1 & 0x04) == 0x00) //none scroll display
+ line2Buffer[6] = 'M';
+ else
+ {
+ //example #2
+ if (freqScrollPosition++ > 18) //none scroll display time
+ {
+ line2Buffer[6] = 'M';
+ if (freqScrollPosition > 25)
+ freqScrollPosition = -1;
+ }
+ else //scroll frequency
+ {
+ line2Buffer[10] = 'H';
+ line2Buffer[11] = 'z';
+
+ if (freqScrollPosition < 7)
+ {
+ for (int i = 11; i >= 0; i--)
+ if (i - (7 - freqScrollPosition) >= 0)
+ line2Buffer[i] = line2Buffer[i - (7 - freqScrollPosition)];
+ else
+ line2Buffer[i] = ' ';
+ }
+ else
+ {
+ for (int i = 0; i < 11; i++)
+ if (i + (freqScrollPosition - 7) <= 11)
+ line2Buffer[i] = line2Buffer[i + (freqScrollPosition - 7)];
+ else
+ line2Buffer[i] = ' ';
+ }
+ }
+ } //scroll
+
+ line2Buffer[7] = ' ';
+
+ if (isIFShift)
+ {
+// if (isDirectCall == 1)
+// for (int i = 0; i < 16; i++)
+// line2Buffer[i] = ' ';
+
+ //IFShift Offset Value
+ line2Buffer[8] = 'I';
+ line2Buffer[9] = 'F';
+
+ line2Buffer[10] = ifShiftValue >= 0 ? '+' : 0;
+ line2Buffer[11] = 0;
+ line2Buffer[12] = ' ';
+
+ //11, 12, 13, 14, 15
+ memset(b, 0, sizeof(b));
+ ltoa(ifShiftValue, b, DEC);
+ strncat(line2Buffer, b, 5);
+
+ //if (isDirectCall == 1) //if call by encoder (not scheduler), immediate print value
+ printLine2(line2Buffer);
+ } // end of display IF
+ else // step & Key Type display
+ {
+ //if (isDirectCall != 0)
+ // return;
+
+ memset(&line2Buffer[8], ' ', 8);
+ //Step
+ long tmpStep = arTuneStep[tuneStepIndex -1];
+
+ byte isStepKhz = 0;
+ if (tmpStep >= 1000)
+ {
+ isStepKhz = 2;
+ }
+
+ for (int i = 10; i >= 8 - isStepKhz; i--) {
+ if (tmpStep > 0) {
+ line2Buffer[i + isStepKhz] = tmpStep % 10 + 0x30;
+ tmpStep /= 10;
+ }
+ else
+ line2Buffer[i +isStepKhz] = ' ';
+ }
+
+ if (isStepKhz == 0)
+ {
+ line2Buffer[11] = 'H';
+ line2Buffer[12] = 'z';
+ }
+
+ line2Buffer[13] = ' ';
+
+ // KC4UPR: Replacing these all with IOP status
+ //line2Buffer[13] = iopStatusWindow[0];
+ //line2Buffer[14] = iopStatusWindow[1];
+ //line2Buffer[15] = iopStatusWindow[2];
+ //Check CW Key cwKeyType = 0; //0: straight, 1 : iambica, 2: iambicb
+ if (sdrModeOn == 1)
+ {
+ line2Buffer[13] = 'S';
+ line2Buffer[14] = 'D';
+ line2Buffer[15] = 'R';
+ }
+ else if (cwKeyType == 0)
+ {
+ line2Buffer[14] = 'S';
+ line2Buffer[15] = 'T';
+ }
+ else if (cwKeyType == 1)
+ {
+ line2Buffer[14] = 'I';
+ line2Buffer[15] = 'A';
+ }
+ else
+ {
+ line2Buffer[14] = 'I';
+ line2Buffer[15] = 'B';
+ }
+ }
+}
+
+//meterType : 0 = S.Meter, 1 : P.Meter
+void DisplayMeter(byte meterType, byte meterValue, char drawPosition)
+{
+ if (meterType == 0 || meterType == 1 || meterType == 2)
+ {
+ drawMeter(meterValue);
+ int lineNumber = 0;
+ if ((displayOption1 & 0x01) == 0x01)
+ lineNumber = 1;
+
+ LCD_SetCursor(drawPosition, lineNumber);
+
+ LCD_Write(lcdMeter[0]);
+ LCD_Write(lcdMeter[1]);
+ LCD_Write(lcdMeter[2]);
+ }
+}
+
+char checkCount = 0;
+char checkCountSMeter = 0;
+
+void idle_process()
+{
+ //space for user graphic display
+ if (menuOn == 0)
+ {
+ if ((displayOption1 & 0x10) == 0x10) //always empty topline
+ return;
+
+ //if line2DisplayStatus == 0 <-- this condition is clear Line, you can display any message
+ if (line2DisplayStatus == 0 || (((displayOption1 & 0x04) == 0x04) && line2DisplayStatus == 2) || stateTopLine > 0) {
+ if (checkCount++ > 1)
+ {
+ updateLine2Buffer(0); //call by scheduler
+ printLine2(line2Buffer);
+ line2DisplayStatus = 2;
+ checkCount = 0;
+ }
+ }
+
+ if (stateTopLine > 0) {
+ return;
+ }
+
+ /*if (!displaySmeter) {
+ if (delaySmeter < millis()) {
+ displaySmeter = true;
+ }
+ }*/
+
+ //S-Meter Display
+ if (((displayOption1 & 0x08) == 0x08 && (sdrModeOn == 0)) && (++checkCountSMeter > SMeterLatency))
+ {
+ int newSMeter;
+
+#ifdef USE_I2CSMETER
+ scaledSMeter = GetI2CSmeterValue(I2CMETER_CALCS);
+#else
+ //VK2ETA S-Meter from MAX9814 TC pin / divide 4 by KD8CEC for reduce EEPromSize
+ newSMeter = analogRead(ANALOG_SMETER) / 4;
+
+ //Faster attack, Slower release
+ //currentSMeter = (newSMeter > currentSMeter ? ((currentSMeter * 3 + newSMeter * 7) + 5) / 10 : ((currentSMeter * 7 + newSMeter * 3) + 5) / 10) / 4;
+ currentSMeter = newSMeter;
+
+ scaledSMeter = 0;
+ for (byte s = 8; s >= 1; s--) {
+ if (currentSMeter > sMeterLevels[s]) {
+ scaledSMeter = s;
+ break;
+ }
+ }
+#endif
+
+ DisplayMeter(0, scaledSMeter, 13);
+ checkCountSMeter = 0; //Reset Latency time
+ } //end of S-Meter
+
+ }
+}
+
+//AutoKey LCD Display Routine
+void Display_AutoKeyTextIndex(byte textIndex)
+{
+ byte diplayAutoCWLine = 0;
+
+ if ((displayOption1 & 0x01) == 0x01)
+ diplayAutoCWLine = 1;
+ LCD_SetCursor(0, diplayAutoCWLine);
+ LCD_Write(byteToChar(textIndex));
+ LCD_Write(':');
+}
+
+void DisplayCallsign(byte callSignLength)
+{
+ printLineFromEEPRom(0, 0, 0, userCallsignLength -1, 0); //eeprom to lcd use offset (USER_CALLSIGN_DAT)
+ //delay(500);
+}
+
+void DisplayVersionInfo(const __FlashStringHelper * fwVersionInfo)
+{
+ printLineF(1, fwVersionInfo);
+}
+
+#endif
diff --git a/ubitx_20/ubitx_menu.ino b/ubitx_20/ubitx_menu.ino
new file mode 100644
index 0000000..53b0f98
--- /dev/null
+++ b/ubitx_20/ubitx_menu.ino
@@ -0,0 +1,1783 @@
+/*
+This source code started with Farhan's original source. The license rules are followed as well.
+Calibration related functions kept the original source except for the minor ones.
+The part is collected in the last minute of this source.
+Ian KD8CEC
+ */
+
+#include "ubitx.h"
+#include "ubitx_eemap.h"
+
+//Current Frequency and mode to active VFO by KD8CEC
+void FrequencyToVFO(byte isSaveFreq)
+{
+ //Save Frequency & Mode Information
+ if (vfoActive == VFO_A)
+ {
+ vfoA = frequency;
+ vfoA_mode = modeToByte();
+
+ if (isSaveFreq)
+ storeFrequencyAndMode(1);
+ }
+ else
+ {
+ vfoB = frequency;
+ vfoB_mode = modeToByte();
+
+ if (isSaveFreq)
+ storeFrequencyAndMode(2);
+ }
+}
+
+//Commonly called functions when exiting menus by KD8CEC
+void menuClearExit(int delayTime)
+{
+ if (delayTime > 0)
+ delay_background(delayTime, 0);
+
+ printLine2ClearAndUpdate();
+ menuOn = 0;
+}
+
+//Ham band or general band movement by KD8CEC
+void menuBand(int btn){
+ int knob = 0;
+ int stepChangeCount = 0;
+ byte btnPressCount = 0;
+
+ if (!btn){
+ printLineF2(F("Band Select?"));
+ return;
+ }
+
+ //printLineF2(F("Press to confirm"));
+ //wait for the button menu select button to be lifted)
+ while (btnDown()) {
+ delay_background(50, 0);
+ if (btnPressCount++ > 20) {
+ btnPressCount = 0;
+ if (tuneTXType > 0) { //Just toggle 0 <-> 2, if tuneTXType is 100, 100 -> 0 -> 2
+ tuneTXType = 0;
+ printLineF2(F("General"));
+ }
+ else {
+ tuneTXType = 2;
+ printLineF2(F("Ham band"));
+ }
+ delay_background(1000, 0);
+ printLine2ClearAndUpdate();
+ }
+ }
+ printLineF2(F("Press to confirm"));
+
+ char currentBandIndex = -1;
+ //Save Band Information
+ if (tuneTXType == 2 || tuneTXType == 3 || tuneTXType == 102 || tuneTXType == 103) { //only ham band move
+ //Get Now Band Index
+ currentBandIndex = getIndexHambanBbyFreq(frequency);
+
+ if (currentBandIndex >= 0) {
+ //Save Frequency to Band Frequncy Record
+ saveBandFreqByIndex(frequency, modeToByte(), currentBandIndex);
+ }
+ }
+
+ //delay(50);
+ ritDisable();
+
+ while(!btnDown()){
+ knob = enc_read();
+ if (knob != 0){
+ if (tuneTXType == 2 || tuneTXType == 3 || tuneTXType == 102 || tuneTXType == 103) { //only ham band move
+ if (knob < 0) {
+ if (stepChangeCount-- < -3) {
+ setNextHamBandFreq(frequency, -1); //Prior Band
+ stepChangeCount = 0;
+ }
+ }
+ else if (knob > 0) {
+ if (stepChangeCount++ > 3) {
+ setNextHamBandFreq(frequency, 1); //Next Band
+ stepChangeCount = 0;
+ }
+ }
+ } //end of only ham band move
+ else { //original source
+ if (knob < 0 && frequency > 3000000l)
+ setFrequency(frequency - 200000l);
+ if (knob > 0 && frequency < 30000000l)
+ setFrequency(frequency + 200000l);
+
+ if (frequency > 10000000l)
+ isUSB = true;
+ else
+ isUSB = false;
+ }
+
+ updateDisplay();
+ }
+
+ delay_background(20, 0);
+ }
+
+ FrequencyToVFO(1);
+ menuClearExit(500);
+}
+
+//Convert Mode, Number by KD8CEC
+//0: default, 1:not use, 2:LSB, 3:USB, 4:CWL, 5:CWU, 6:FM
+// Updated: KC4UPR - 6: DIG
+byte modeToByte() {
+ if (cwMode == 0)
+ {
+ if (digiMode > 0) {
+ return 6;
+ }
+ if (isUSB)
+ return 3;
+ else
+ return 2;
+ }
+ else if (cwMode == 1)
+ {
+ return 4;
+ }
+ else
+ {
+ return 5;
+ }
+}
+
+//Convert Number to Mode by KD8CEC
+//autoSetModebyFreq : 0
+//autoSetModebyFreq : 1, if (modValue is not set, set mode by frequency)
+void byteToMode(byte modeValue, byte autoSetModebyFreq){
+ //isTest = false; // test never settable from EEPROM
+ isUSB = false;
+ cwMode = 0;
+ digiMode = 0;
+
+ if (autoSetModebyFreq == 1 && (modeValue == 0)) {
+ isUSB = (frequency > 10000000l) ? true : false;
+ } else {
+ switch(modeValue) {
+ // LSB by default, so we can skip this case
+ //case 2: // LSB
+ //break;
+
+ case 3: // USB
+ isUSB = true;
+ break;
+
+ case 4: // CWL
+ cwMode = 1;
+ break;
+
+ case 5: // CWU
+ cwMode = 2;
+ break;
+
+ case 6: // DIG
+ digiMode = 1;
+ isUSB = true;
+ break;
+ }
+ }
+/* if (modeValue == 4) {
+ cwMode = 1; digiMode = 0;
+ } else if (modeValue == 5) {
+ cwMode = 2; digiMode = 0;
+ } else {
+ cwMode = 0;
+ if (modeValue == 3) {
+ isUSB = 1; digiMode = 0;
+ } else if (modeValue == 6) {
+ isUSB =
+ }
+ else if (autoSetModebyFreq == 1 && (modeValue == 0))
+ isUSB = (frequency > 10000000l) ? true : false;
+ else
+ isUSB = 0;
+ }*/
+}
+
+
+//Memory to VFO, VFO to Memory by KD8CEC
+void menuCHMemory(int btn, byte isMemoryToVfo){
+ int knob = 0;
+ int selectChannel = 0;
+ byte isDisplayInfo = 1;
+ int moveStep = 0;
+ unsigned long resultFreq, tmpFreq = 0;
+ byte loadMode = 0;
+
+ if (!btn){
+ if (isMemoryToVfo == 1)
+ printLineF2(F("Channel To VFO?"));
+ else
+ printLineF2(F("VFO To Channel?"));
+ }
+ else {
+ delay_background(500, 0);
+
+ while(!btnDown()){
+ if (isDisplayInfo == 1) {
+ //Display Channel info *********************************
+ memset(c, 0, sizeof(c));
+
+ if (selectChannel >= 20 || selectChannel <=-1)
+ {
+ //strcpy(c, "Exit setup?");
+ strcpy(c, "Exit?");
+ }
+ else
+ {
+ //Read Frequency from eeprom
+ EEPROM.get(CHANNEL_FREQ + 4 * selectChannel, resultFreq);
+
+ loadMode = (byte)(resultFreq >> 29);
+ resultFreq = resultFreq & 0x1FFFFFFF;
+
+ //display channel description
+ if (selectChannel < 10 && EEPROM.read(CHANNEL_DESC + 6 * selectChannel) == 0x03) { //0x03 is display Chnnel Name
+ //display Channel Name
+ for (int i = 0; i < 5; i++)
+ c[i] = EEPROM.read(CHANNEL_DESC + 6 * selectChannel + i + 1);
+
+ c[5] = ':';
+ }
+ else {
+ //Display frequency
+ //1 LINE : Channel Information : CH00
+ strcpy(c, "CH");
+ if (selectChannel < 9)
+ c[2] = '0';
+
+ ltoa(selectChannel + 1, b, 10);
+ strcat(c, b); //append channel Number;
+ strcat(c, " :"); //append channel Number;
+ }
+
+ //display frequency
+ tmpFreq = resultFreq;
+ for (int i = 15; i >= 6; i--) {
+ if (tmpFreq > 0) {
+ if (i == 12 || i == 8) c[i] = '.';
+ else {
+ c[i] = tmpFreq % 10 + 0x30;
+ tmpFreq /= 10;
+ }
+ }
+ else
+ c[i] = ' ';
+ }
+ }
+
+ printLine2(c);
+
+ isDisplayInfo = 0;
+ }
+
+ knob = enc_read();
+
+ if (knob != 0)
+ {
+ moveStep += (knob > 0 ? 1 : -1);
+ if (moveStep < -3) {
+ if (selectChannel > -1)
+ selectChannel--;
+
+ isDisplayInfo = 1;
+ moveStep = 0;
+ }
+ else if (moveStep > 3) {
+ if (selectChannel < 20)
+ selectChannel++;
+
+ isDisplayInfo = 1;
+ moveStep = 0;
+ }
+ }
+
+ Check_Cat(0); //To prevent disconnections
+ } //end of while (knob)
+
+ if (selectChannel < 20 && selectChannel >= 0)
+ {
+ if (isMemoryToVfo == 1)
+ {
+ if (resultFreq > 3000 && resultFreq < 60000000)
+ {
+ byteToMode(loadMode, 1);
+ setFrequency(resultFreq);
+ }
+ }
+ else
+ {
+ //Save current Frequency to Channel (selectChannel)
+ EEPROM.put(CHANNEL_FREQ + 4 * selectChannel, (frequency & 0x1FFFFFFF) | (((unsigned long)modeToByte()) << 29) );
+ printLine2("Saved Frequency");
+ }
+ }
+
+ menuClearExit(500);
+ }
+}
+
+//Analog pin monitoring with CW Key and function keys connected.
+//by KD8CEC
+#ifdef ENABLE_ADCMONITOR
+void menuADCMonitor(int btn){
+ int adcPinA0 = 0; //A0(BLACK, EncoderA)
+ int adcPinA1 = 0; //A1(BROWN, EncoderB)
+ int adcPinA2 = 0; //A2(RED, Function Key)
+ int adcPinA3 = 0; //A3(ORANGE, CW Key)
+ int adcPinA6 = 0; //A6(BLUE, Ptt)
+ int adcPinA7 = 0; //A7(VIOLET, Spare)
+ unsigned long pressKeyTime = 0;
+
+ if (!btn){
+ printLineF2(F("ADC Line Monitor"));
+ return;
+ }
+
+ printLineF2(F("Exit:Long PTT"));
+ delay_background(2000, 0);
+ printLineF1(F("A0 A1 A2"));
+ printLineF2(F("A3 A6 A7"));
+ delay_background(3000, 0);
+
+ while (true) {
+ adcPinA0 = analogRead(A0); //A0(BLACK, EncoderA)
+ adcPinA1 = analogRead(A1); //A1(BROWN, EncoderB)
+ adcPinA2 = analogRead(A2); //A2(RED, Function Key)
+ adcPinA3 = analogRead(A3); //A3(PTT)
+ adcPinA6 = analogRead(A6); //A6(KEYER)
+ adcPinA7 = analogRead(A7); //A7(VIOLET, Spare)
+
+ if (adcPinA3 < 50) {
+ if (pressKeyTime == 0)
+ pressKeyTime = millis();
+ else if (pressKeyTime < (millis() - 3000))
+ break;
+ }
+ else
+ pressKeyTime = 0;
+
+ ltoa(adcPinA0, c, 10);
+ //strcat(b, c);
+ strcpy(b, c);
+ strcat(b, ", ");
+
+ ltoa(adcPinA1, c, 10);
+ strcat(b, c);
+ strcat(b, ", ");
+
+ ltoa(adcPinA2, c, 10);
+ strcat(b, c);
+
+ printLine1(b);
+
+ //strcpy(b, " ");
+ ltoa(adcPinA3, c, 10);
+ strcpy(b, c);
+ strcat(b, ", ");
+
+ ltoa(adcPinA6, c, 10);
+ strcat(b, c);
+ strcat(b, ", ");
+
+ ltoa(adcPinA7, c, 10);
+ strcat(b, c);
+ printLine2(b);
+
+ delay_background(200, 0);
+ } //end of while
+
+ menuClearExit(0);
+}
+#endif
+
+//VFO Toggle and save VFO Information, modified by KD8CEC
+void menuVfoToggle(int btn)
+{
+ if (!btn){
+ if (vfoActive == VFO_A)
+ printLineF2(F("Select VFO B?"));
+ else
+ printLineF2(F("Select VFO A?"));
+ }
+ else {
+ FrequencyToVFO(1);
+
+ if (vfoActive == VFO_B){
+ vfoActive = VFO_A;
+ frequency = vfoA;
+ saveCheckFreq = frequency;
+ byteToMode(vfoA_mode, 0);
+ }
+ else {
+ vfoActive = VFO_B;
+ frequency = vfoB;
+ saveCheckFreq = frequency;
+ byteToMode(vfoB_mode, 0);
+ }
+
+ ritDisable();
+ setFrequency(frequency);
+
+#ifdef USE_SW_SERIAL
+ menuOn = 0;
+#else
+ //Only Clear And Delay for Character LCD
+ menuClearExit(0);
+#endif
+
+ }
+}
+
+
+//Split communication using VFOA and VFOB by KD8CEC
+void menuSplitOnOff(int btn){
+ if (!btn){
+ if (splitOn == 0)
+ printLineF2(F("Split On?"));
+ else
+ printLineF2(F("Split Off?"));
+ }
+ else {
+ if (splitOn == 1){
+ splitOn = 0;
+ printLineF2(F("SPT Off"));
+ }
+ else {
+ splitOn = 1;
+ if (ritOn == 1)
+ ritOn = 0;
+ printLineF2(F("SPT On"));
+ }
+
+#ifdef USE_SW_SERIAL
+ menuOn = 0;
+#else
+//Only Clear And Delay for Character LCD
+ menuClearExit(500);
+#endif
+ }
+}
+
+//Function to disbled transmission
+//by KD8CEC
+void menuTxOnOff(int btn, byte optionType){
+ if (!btn){
+ if ((isTxType & optionType) == 0)
+ printLineF2(F("TX OFF?"));
+ else
+ printLineF2(F("TX ON?"));
+ }
+ else {
+ if ((isTxType & optionType) == 0){
+ isTxType |= optionType;
+ printLineF2(F("TX OFF"));
+ }
+ else {
+ isTxType &= ~(optionType);
+ printLineF2(F("TX ON"));
+ }
+
+#ifdef USE_SW_SERIAL
+ menuOn = 0;
+#else
+ //Only Clear And Delay for Character LCD
+ menuClearExit(500);
+#endif
+ }
+}
+
+//Toggle SDR MODE
+void menuSDROnOff(int btn)
+{
+ if (!btn){
+ if (sdrModeOn == 0)
+ printLineF2(F("SDR Mode On?"));
+ else
+ printLineF2(F("SDR Mode Off?"));
+ }
+ else {
+ if (sdrModeOn == 1){
+ sdrModeOn = 0;
+ printLineF2(F("SPK MODE"));
+ }
+ else {
+ sdrModeOn = 1;
+
+ if (ritOn == 1)
+ ritOn = 0;
+
+ if (splitOn == 1)
+ splitOn = 0;
+
+ printLineF2(F("SDR MODE"));
+ }
+
+ EEPROM.put(ENABLE_SDR, sdrModeOn);
+ setFrequency(frequency);
+ SetCarrierFreq();
+
+#ifdef USE_SW_SERIAL
+ menuOn = 0;
+#else
+ //Only Clear And Delay for Character LCD
+ menuClearExit(500);
+#endif
+ }
+}
+
+void displayEmptyData(void){
+ printLineF2(F("Empty data"));
+ delay_background(2000, 0);
+}
+
+/*
+//Builtin CW Keyer Logic by KD8CEC
+void menuCWAutoKey(int btn){
+ if (!btn){
+ printLineF2(F("Memory Keyer"));
+ return;
+ }
+
+ //Check CW_AUTO_MAGIC_KEY and CW Text Count
+ EEPROM.get(CW_AUTO_COUNT, cwAutoTextCount);
+ if (EEPROM.read(CW_AUTO_MAGIC_KEY) != 0x73 || cwAutoTextCount < 1)
+ {
+ displayEmptyData();
+ return;
+ }
+
+ printLineF1(F("PTT to Send"));
+ delay_background(500, 0);
+ beforeCWTextIndex = 255; //255 value is for start check
+ isCWAutoMode = 1;
+ updateDisplay();
+ menuOn = 0;
+}
+*/
+
+//Standalone WSPR Beacone
+void menuWSPRSend(int btn){
+ if (!btn){
+ printLineF2(F("WSPR Beacon"));
+ return;
+ }
+
+ WsprMSGCount = EEPROM.read(WSPR_COUNT);
+
+ if (WsprMSGCount < 1)
+ {
+ displayEmptyData();
+ return;
+ }
+
+ SendWSPRManage();
+ menuClearExit(1000);
+}
+
+//Append by KD8CEC
+void menuSetupCWCarrier(int btn){
+ int knob = 0;
+ unsigned long prevCarrier;
+
+ if (!btn){
+ printLineF2(F("Set CW RX BFO"));
+ return;
+ }
+
+ prevCarrier = cwmCarrier;
+ printLineF1(F("PTT to confirm. "));
+ delay_background(1000, 0);
+
+ si5351bx_setfreq(0, cwmCarrier);
+ printCarrierFreq(cwmCarrier);
+
+ //disable all clock 1 and clock 2
+ while (digitalRead(PTT) == HIGH && !btnDown())
+ {
+ knob = enc_read();
+
+ if (knob > 0)
+ cwmCarrier -= 5;
+ else if (knob < 0)
+ cwmCarrier += 5;
+ else
+ continue; //don't update the frequency or the display
+
+ si5351bx_setfreq(0, cwmCarrier);
+ printCarrierFreq(cwmCarrier);
+
+ delay_background(100, 0);
+ }
+
+ //save the setting
+ if (digitalRead(PTT) == LOW){
+ printLineF2(F("Carrier set!"));
+ EEPROM.put(CW_CAL, cwmCarrier);
+ delay_background(1000, 0);
+ }
+ else
+ cwmCarrier = prevCarrier;
+
+ if (cwMode == 0)
+ si5351bx_setfreq(0, usbCarrier); //set back the carrier oscillator anyway, cw tx switches it off
+ else
+ si5351bx_setfreq(0, cwmCarrier); //set back the carrier oscillator anyway, cw tx switches it off
+
+ setFrequency(frequency);
+ menuClearExit(0);
+}
+
+//=======================================================
+//BEGIN OF STANDARD TUNE SETUP for reduce Program Memory
+// by KD8CEC
+//=======================================================
+//valueType 0 : Normal
+// 1 : CW Change -> Generate Tone
+// 2 : IF Shift Setup -> SetFrequency, Set SideTone
+// 5 : ATT
+// 11 : Select Mode (different display type)
+//knobSensitivity : 1 ~
+int getValueByKnob(int valueType, int targetValue, int minKnobValue, int maxKnobValue, int incStep, const char* displayTitle, int knobSensitivity)
+{
+ int knob;
+ int moveDetectStep = 0;
+ char isInitDisplay = 1;
+ delay_background(300, 0); //Default Delay
+
+ if (valueType < 10)
+ {
+ strcpy(b, "Press, set ");
+ strcat(b, displayTitle);
+ printLine1(b);
+ }
+
+ while(!btnDown())
+ {
+ knob = enc_read();
+ if (knob != 0 || isInitDisplay == 1)
+ {
+ isInitDisplay = 0;
+
+ /*
+ //Program Size : 29424 (95%)
+ if (targetValue > minKnobValue && knob < 0)
+ targetValue -= incStep;
+ if (targetValue < maxKnobValue && knob > 0)
+ targetValue += incStep;
+ */
+
+ //Program Size : 29560 (increase 135 byte from avobe codes), but a lot of usable functions
+ moveDetectStep += (knob > 0 ? 1 : -1);
+ if (moveDetectStep < (knobSensitivity * -1)) {
+ if (targetValue > minKnobValue)
+ targetValue -= incStep;
+
+ moveDetectStep = 0;
+ }
+ else if (moveDetectStep > knobSensitivity) {
+ if (targetValue < maxKnobValue)
+ targetValue += incStep;
+
+ moveDetectStep = 0;
+ }
+
+ if (valueType == 11) //Mode Select
+ {
+ int tmpCol = targetValue * 4;
+ if (tmpCol >= 16) {
+ tmpCol -= 16;
+ strcpy(b, &displayTitle[16]);
+ } else {
+ strcpy(b, displayTitle);
+ }
+ b[tmpCol] = '>';
+ } else {
+ strcpy(b, displayTitle);
+
+ /*
+ else if (valueType == 4) //CW Key Type Select
+ {
+ if (targetValue == 0)
+ strcat(b, "Straight");
+ else if (targetValue == 1)
+ strcat(b, "IAMBICA");
+ else if (targetValue == 2)
+ strcat(b, "IAMBICB");
+ }
+ */
+ strcat(b, ":");
+ itoa(targetValue,c, 10);
+ strcat(b, c);
+ }
+
+ printLine2(b);
+
+ if (valueType == 1) //Generate Side Tone
+ {
+ tone(CW_TONE, targetValue);
+ }
+ else if (valueType == 2 || valueType == 5 ) // 2:IFS, 5:ATT
+ {
+ if (valueType == 2)
+ ifShiftValue = targetValue;
+ else
+ attLevel = targetValue;
+
+#ifdef USE_SW_SERIAL
+ menuOn=2;
+ updateDisplay();
+#endif
+ setFrequency(frequency);
+ SetCarrierFreq();
+ }
+ }
+
+ Check_Cat(0); //To prevent disconnections
+ }
+
+ return targetValue;
+}
+
+void menuCWSpeed(int btn){
+ int wpm;
+
+ wpm = 1200/cwSpeed;
+
+ if (!btn){
+ strcpy(b, "CW:");
+ itoa(wpm,c, 10);
+ strcat(b, c);
+ strcat(b, "WPM Change?");
+ printLine2(b);
+ return;
+ }
+
+ //printLineF1(F("Press to set WPM"));
+ //strcpy(b, "WPM:");
+ //itoa(wpm,c, 10);
+ //strcat(b, c);
+ //printLine2(b);
+ //delay_background(300, 0);
+
+ wpm = getValueByKnob(0, wpm, 3, 50, 1, "WPM", 3);
+
+ /*
+ while(!btnDown()){
+
+ knob = enc_read();
+ if (knob != 0){
+ if (wpm > 3 && knob < 0)
+ wpm--;
+ if (wpm < 50 && knob > 0)
+ wpm++;
+
+ strcpy(b, "WPM:");
+ itoa(wpm,c, 10);
+ strcat(b, c);
+ printLine2(b);
+ }
+
+ Check_Cat(0); //To prevent disconnections
+ }
+ */
+
+
+ //save the setting
+ //printLineF2(F("CW Speed set!"));
+ cwSpeed = 1200 / wpm;
+ EEPROM.put(CW_SPEED, cwSpeed);
+ //menuClearExit(1000);
+#ifdef USE_SW_SERIAL
+ menuOn = 0;
+#else
+ //Only Clear And Delay for Character LCD
+ menuClearExit(1000);
+#endif
+
+}
+
+//Modified by KD8CEC
+void menuSetupCwTone(int btn){
+ //int prev_sideTone;
+
+ if (!btn){
+ printLineF2(F("Change CW Tone"));
+ return;
+ }
+
+ //prev_sideTone = sideTone;
+ //printLineF1(F("Tune CW tone"));
+ //printLineF2(F("PTT to confirm."));
+ //printLineF1(F("Press to set WPM"));
+ //delay_background(1000, 0);
+ //tone(CW_TONE, sideTone);
+
+ sideTone = getValueByKnob(1, sideTone, 100, 2000, 10, "Tone", 2); //1 : Generate Tone, targetValue, minKnobValue, maxKnobValue, stepSize
+
+ noTone(CW_TONE);
+
+ printLineF2(F("Sidetone set!"));
+ EEPROM.put(CW_SIDETONE, sideTone);
+
+ //delay_background(2000, 0);
+ //menuClearExit(0);
+#ifdef USE_SW_SERIAL
+ menuOn = 0;
+#else
+ //Only Clear And Delay for Character LCD
+ delay_background(2000, 0);
+ menuClearExit(0);
+#endif
+ }
+
+//Modified by KD8CEC
+void menuSetupCwDelay(int btn){
+ //int knob = 0;
+ int tmpCWDelay = cwDelayTime * 10;
+
+ if (!btn){
+ printLineF2(F("CW TX->RX Delay"));
+ return;
+ }
+
+ tmpCWDelay = getValueByKnob(0, tmpCWDelay, 3, 2500, 10, "Delay", 2); //0 : Generate Tone, targetValue, minKnobValue, maxKnobValue, stepSize
+
+ //save the setting
+ cwDelayTime = tmpCWDelay / 10;
+ EEPROM.put(CW_DELAY, cwDelayTime);
+ //menuClearExit(1000);
+#ifdef USE_SW_SERIAL
+ menuOn = 0;
+#else
+ //Only Clear And Delay for Character LCD
+ menuClearExit(1000);
+#endif
+}
+
+//CW Time delay by KD8CEC
+void menuSetupTXCWInterval(int btn){
+ //char needDisplayInformation = 1;
+ //int knob = 0;
+ int tmpTXCWInterval = delayBeforeCWStartTime * 2;
+
+ if (!btn){
+ printLineF2(F("CW Start Delay"));
+ return;
+ }
+
+ //printLineF1(F("Press, set Delay"));
+ //delay_background(300, 0);
+
+ tmpTXCWInterval = getValueByKnob(0, tmpTXCWInterval, 0, 500, 2, "Delay", 2); //0 : Generate Tone, targetValue, minKnobValue, maxKnobValue, stepSize
+
+ delayBeforeCWStartTime = tmpTXCWInterval / 2;
+ EEPROM.put(CW_START, delayBeforeCWStartTime);
+ //menuClearExit(1000);
+
+#ifdef USE_SW_SERIAL
+ menuOn = 0;
+#else
+ //Only Clear And Delay for Character LCD
+ menuClearExit(1000);
+#endif
+
+}
+
+//IF Shift function, BFO Change like RIT, by KD8CEC
+void menuIFSSetup(int btn){
+ //int knob = 0;
+ //char needApplyChangeValue = 1;
+
+ if (!btn){
+ if (isIFShift == 1)
+ printLineF2(F("IF Shift Change?"));
+ else
+ printLineF2(F("IF Shift On?"));
+ }
+ else
+ {
+ isIFShift = 1;
+
+ ifShiftValue = getValueByKnob(2, ifShiftValue, -20000, 20000, 50, "IFS", 2); //2 : IF Setup (updateLine2Buffer(1), SetFrequency), targetValue, minKnobValue, maxKnobValue, stepSize
+ delay_background(500, 0); //for check Long Press function key
+
+ if (btnDown() || ifShiftValue == 0)
+ {
+ isIFShift = 0;
+ ifShiftValue = 0;
+ setFrequency(frequency);
+ SetCarrierFreq();
+ }
+
+ //Store IF Shiift
+ EEPROM.put(IF_SHIFTVALUE, ifShiftValue);
+ //menuClearExit(0);
+#ifdef USE_SW_SERIAL
+ menuOn = 0;
+#else
+ //Only Clear And Delay for Character LCD
+ menuClearExit(0);
+#endif
+ }
+}
+
+//ATT SETUP (IF1(45MHZ) SHIFT), by KD8CEC
+void menuATTSetup(int btn){
+ //int knob = 0;
+ //char needApplyChangeValue = 1;
+
+ if (!btn){
+ if (attLevel != 0)
+ printLineF2(F("ATT Change?"));
+ else
+ printLineF2(F("ATT On?"));
+ }
+ else
+ {
+ attLevel = getValueByKnob(5, attLevel, 0, 250, 5, "ATT", 2); //2 : (SetFrequency), targetValue, minKnobValue, maxKnobValue, stepSize
+ delay_background(500, 0); //for check Long Press function key
+
+ if (btnDown() || attLevel == 0)
+ {
+ attLevel = 0;
+ setFrequency(frequency);
+ //SetCarrierFreq();
+ }
+ //menuClearExit(0);
+
+#ifdef USE_SW_SERIAL
+ menuOn = 0;
+#else
+ //Only Clear And Delay for Character LCD
+ menuClearExit(0);
+#endif
+
+ }
+}
+
+//Functions for CWL and CWU by KD8CEC
+void menuSelectMode(int btn){
+ //int knob = 0;
+ int selectModeType = 0;
+ int beforeMode = 0;
+ //int moveStep = 0;
+
+ if (!btn){
+ printLineF2(F("Select Mode?"));
+ }
+ else
+ {
+ //LSB, USB, CWL, CWU, DGL, DGU, TTL, TTU
+ if (cwMode == 0) {
+ if (isUSB == 0) {
+ selectModeType = 0; // LSB
+ } else {
+ selectModeType = 1; // USB
+ }
+ // modify if digital mode is set
+ if (digiMode > 0) {
+
+ selectModeType = 4;
+ }
+ } else if (cwMode == 2) {
+ selectModeType = 2; // CW
+ } else {
+ selectModeType = 3; // CWR
+ }
+ /*if (cwMode == 0 && isUSB == 0)
+ selectModeType = 0;
+ else if (cwMode == 0 && isUSB == 1)
+ selectModeType = 1;
+ else if (cwMode == 1)
+ selectModeType = 2;
+ else
+ selectModeType = 3;*/
+
+ beforeMode = selectModeType;
+ selectModeType = getValueByKnob(11, selectModeType, 0, 4, 1, " LSB USB CW CWR DIG", 4); //3 : Select Mode, targetValue, minKnobValue, maxKnobValue, stepSize
+
+ if (beforeMode != selectModeType)
+ {
+ //printLineF1(F("Changed Mode"));
+ if (selectModeType == 0) {
+ cwMode = 0; isUSB = 0; digiMode = 0; //isTest = 0;
+ } else if (selectModeType == 1) {
+ cwMode = 0; isUSB = 1; digiMode = 0; //isTest = 0;
+ } else if (selectModeType == 2) {
+ cwMode = 2; digiMode = 0; //isTest = 0;
+ } else if (selectModeType == 3) {
+ cwMode = 1; digiMode = 0; //isTest = 0;
+ } else if (selectModeType == 4) {
+ cwMode = 0; isUSB = 1; digiMode = 1; //isTest = 0;
+// } else if (selectModeType == 5) {
+// cwMode = 0; isUSB = 1; digiMode = 1; isTest = 0;
+// } else if (selectModeType == 6) {
+// cwMode = 0; isUSB = 0; digiMode = 0; isTest = 1;
+// } else if (selectModeType == 7) {
+// cwMode = 0; isUSB = 1; digiMode = 0; isTest = 1;
+ }
+ // KC4UPR: sending mode to IOP
+ iopSendMode(cwMode, isUSB, digiMode);
+
+ FrequencyToVFO(1);
+ }
+
+ SetCarrierFreq();
+ setFrequency(frequency);
+ //menuClearExit(500);
+#ifdef USE_SW_SERIAL
+ menuOn = 0;
+#else
+ //Only Clear And Delay for Character LCD
+ menuClearExit(500);
+#endif
+ }
+}
+
+//Select CW Key Type by KD8CEC
+void menuSetupKeyType(int btn){
+ //int knob = 0;
+ int selectedKeyType = 0;
+ //int moveStep = 0;
+ if (!btn){
+ printLineF2(F("Change Key Type?"));
+ }
+ else {
+ selectedKeyType = cwKeyType;
+
+ //selectedKeyType = getValueByKnob(12, selectedKeyType, 0, 2, 1, " KEY:", 5); //4 : Select Key Type, targetValue, minKnobValue, maxKnobValue, stepSize
+ selectedKeyType = getValueByKnob(11, selectedKeyType, 0, 2, 1, " ST IA IB", 5); //4 : Select Key Type, targetValue, minKnobValue, maxKnobValue, stepSize
+
+ printLineF2(F("CW Key Type set!"));
+ cwKeyType = selectedKeyType;
+ EEPROM.put(CW_KEY_TYPE, cwKeyType);
+
+ if (cwKeyType == 0)
+ Iambic_Key = false;
+ else
+ {
+ Iambic_Key = true;
+ if (cwKeyType == 1)
+ keyerControl &= ~IAMBICB;
+ else
+ keyerControl |= IAMBICB;
+ }
+
+ //menuClearExit(1000);
+#ifdef USE_SW_SERIAL
+ menuOn = 0;
+#else
+ //Only Clear And Delay for Character LCD
+ menuClearExit(1000);
+#endif
+
+ }
+}
+
+//=====================================================
+//END OF STANDARD Set by Knob for reduce Program Memory
+//=====================================================
+
+
+//Lock Dial move by KD8CEC
+void setDialLock(byte tmpLock, byte fromMode) {
+ if (tmpLock == 1)
+ isDialLock |= (vfoActive == VFO_A ? 0x01 : 0x02);
+ else
+ isDialLock &= ~(vfoActive == VFO_A ? 0x01 : 0x02);
+
+ if (fromMode == 2 || fromMode == 3) return;
+
+ //delay_background(1000, 0);
+ printLine2ClearAndUpdate();
+}
+
+byte btnDownTimeCount;
+
+#define PRESS_ADJUST_TUNE 20 //1000msec 20 * 50 = 1000milisec
+#define PRESS_LOCK_CONTROL 40 //2000msec 40 * 50 = 2000milisec
+
+//Modified by KD8CEC
+void doMenu(){
+ int select=0, i,btnState;
+ char isNeedDisplay = 0;
+
+ //for DialLock On/Off function
+ btnDownTimeCount = 0;
+
+ //wait for the button to be raised up
+
+ //Appened Lines by KD8CEC for Adjust Tune step and Set Dial lock
+ while(btnDown()){
+ delay_background(50, 0);
+
+ if (btnDownTimeCount++ == (PRESS_ADJUST_TUNE)) { //Set Tune Step
+ printLineF2(F("Set Tune Step?"));
+ }
+ else if (btnDownTimeCount > (PRESS_LOCK_CONTROL)) { //check long time Down Button -> 2.5 Second => Lock
+ if (vfoActive == VFO_A)
+ setDialLock((isDialLock & 0x01) == 0x01 ? 0 : 1, 0); //Reverse Dial lock
+ else
+ setDialLock((isDialLock & 0x02) == 0x02 ? 0 : 1, 0); //Reverse Dial lock
+ return;
+ }
+ }
+ delay(50); //debounce
+
+ //ADJUST TUNE STEP
+ if (btnDownTimeCount > PRESS_ADJUST_TUNE)
+ {
+ printLineF1(F("Press to set"));
+ isNeedDisplay = 1; //check to need display for display current value
+
+ while (!btnDown())
+ {
+ delay_background(50, 0);
+
+ if (isNeedDisplay) {
+ strcpy(b, "Tune Step:");
+ itoa(arTuneStep[tuneStepIndex -1], c, 10);
+ strcat(b, c);
+ printLine2(b);
+ isNeedDisplay = 0;
+ }
+
+ i = enc_read();
+
+ if (i != 0) {
+ select += (i > 0 ? 1 : -1);
+
+ if (select * select >= 25) { //Threshold 5 * 5 = 25
+ if (select < 0) {
+ if (tuneStepIndex > 1)
+ tuneStepIndex--;
+ }
+ else {
+ if (tuneStepIndex < 5)
+ tuneStepIndex++;
+ }
+ select = 0;
+ isNeedDisplay = 1;
+ }
+ }
+ } //end of while
+
+ EEPROM.put(TUNING_STEP, tuneStepIndex);
+ delay_background(500, 0);
+ printLine2ClearAndUpdate();
+ return;
+ } //set tune step
+
+ //Below codes are origial code with modified by KD8CEC
+ menuOn = 2;
+ TriggerBySW = 0; //Nextion LCD and Other MCU
+
+ //*********************************************************************************
+ // New type menu for developer by KD8CEC
+ // Selectable menu
+ // Version : 1.097 ~
+ //*********************************************************************************
+#ifndef ENABLE_ADCMONITOR
+ #define FN_ADCMONITOR 0
+#endif
+
+ #define FN_DEFAULT_MENU 2 //Setup Onff / Exit
+ #define FN_DEFAULT_SETUP 1 //Exit
+
+ #define FN_BAND_IDX (FN_BAND -1) //0 or -1
+ #define FN_VFO_TOGGLE_IDX (FN_BAND_IDX + FN_VFO_TOGGLE)
+ #define FN_MODE_IDX (FN_VFO_TOGGLE_IDX + FN_MODE)
+ #define FN_RIT_IDX (FN_MODE_IDX + FN_RIT)
+ #define FN_IFSHIFT_IDX (FN_RIT_IDX + FN_IFSHIFT)
+ #define FN_ATT_IDX (FN_IFSHIFT_IDX + FN_ATT)
+ #define FN_CW_SPEED_IDX (FN_ATT_IDX + FN_CW_SPEED)
+ #define FN_SPLIT_IDX (FN_CW_SPEED_IDX + FN_SPLIT)
+ #define FN_VFOTOMEM_IDX (FN_SPLIT_IDX + FN_VFOTOMEM)
+ #define FN_MEMTOVFO_IDX (FN_VFOTOMEM_IDX + FN_MEMTOVFO)
+ #define FN_MEMORYKEYER_IDX (FN_MEMTOVFO_IDX + FN_MEMORYKEYER)
+ #define FN_WSPR_IDX (FN_MEMORYKEYER_IDX + FN_WSPR)
+ #define FN_SDRMODE_IDX (FN_WSPR_IDX + FN_SDRMODE)
+ #define FN_SETUP_IDX (FN_SDRMODE_IDX + 1)
+ #define FN_EXIT_IDX (FN_SETUP_IDX + 1)
+ #define FN_CALIBRATION_IDX (FN_EXIT_IDX + FN_CALIBRATION)
+ #define FN_CARRIER_IDX (FN_CALIBRATION_IDX + FN_CARRIER)
+ #define FN_CWCARRIER_IDX (FN_CARRIER_IDX + FN_CWCARRIER)
+ #define FN_CWTONE_IDX (FN_CWCARRIER_IDX + FN_CWTONE)
+ #define FN_CWDELAY_IDX (FN_CWTONE_IDX + FN_CWDELAY)
+ #define FN_TXCWDELAY_IDX (FN_CWDELAY_IDX + FN_TXCWDELAY)
+ #define FN_KEYTYPE_IDX (FN_TXCWDELAY_IDX + FN_KEYTYPE)
+ #define FN_ADCMONITOR_IDX (FN_KEYTYPE_IDX + FN_ADCMONITOR)
+ #define FN_TXONOFF_IDX (FN_ADCMONITOR_IDX + FN_TXONOFF)
+
+ #define FN_MENU_COUNT (FN_DEFAULT_MENU + FN_BAND + FN_VFO_TOGGLE + FN_MODE + FN_RIT + FN_IFSHIFT + FN_ATT + FN_CW_SPEED + FN_SPLIT + FN_VFOTOMEM + FN_MEMTOVFO + FN_MEMORYKEYER + FN_WSPR + FN_SDRMODE)
+ #define FN_SETUP_COUNT (FN_DEFAULT_SETUP + FN_CALIBRATION + FN_CARRIER + FN_CWCARRIER + FN_CWTONE + FN_CWDELAY + FN_TXCWDELAY + FN_KEYTYPE + FN_ADCMONITOR + FN_TXONOFF)
+ #define FN_STEP_COUNT (FN_MENU_COUNT + FN_SETUP_COUNT)
+
+ while (menuOn){
+ i = enc_read();
+ btnState = btnDown();
+
+ if (i > 0){
+ if (modeCalibrate && select + i < FN_STEP_COUNT * 10)
+ select += i;
+ else if (!modeCalibrate && select + i < FN_MENU_COUNT * 10)
+ select += i;
+ }
+ else if (i < 0 && select - i >= -10)
+ select += i;
+
+ switch (select / 10)
+ {
+#if FN_BAND == 1
+ case FN_BAND_IDX :
+ menuBand(btnState);
+ break;
+#endif
+#if FN_VFO_TOGGLE == 1
+ case FN_VFO_TOGGLE_IDX :
+ menuVfoToggle(btnState);
+ break;
+#endif
+#if FN_MODE == 1
+ case FN_MODE_IDX :
+ menuSelectMode(btnState);
+ break;
+#endif
+#if FN_RIT == 1
+ case FN_RIT_IDX :
+ menuRitToggle(btnState);
+ break;
+#endif
+#if FN_IFSHIFT == 1
+ case FN_IFSHIFT_IDX :
+ menuIFSSetup(btnState);
+ break;
+#endif
+#if FN_ATT == 1
+ case FN_ATT_IDX :
+ menuATTSetup(btnState);
+ break;
+#endif
+#if FN_CW_SPEED == 1
+ case FN_CW_SPEED_IDX :
+ menuCWSpeed(btnState);
+ break;
+#endif
+#if FN_SPLIT == 1
+ case FN_SPLIT_IDX :
+ menuSplitOnOff(btnState); //SplitOn / off
+ break;
+#endif
+#if FN_VFOTOMEM == 1
+ case FN_VFOTOMEM_IDX :
+ menuCHMemory(btnState, 0); //VFO to Memroy
+ break;
+#endif
+#if FN_MEMTOVFO == 1
+ case FN_MEMTOVFO_IDX :
+ menuCHMemory(btnState, 1); //Memory to VFO
+ break;
+#endif
+#if FN_MEMORYKEYER == 1
+ case FN_MEMORYKEYER_IDX :
+ menuCWAutoKey(btnState);
+ break;
+#endif
+#if FN_WSPR == 1
+ case FN_WSPR_IDX :
+ menuWSPRSend(btnState);
+ break;
+#endif
+#if FN_SDRMODE == 1
+ case FN_SDRMODE_IDX :
+ menuSDROnOff(btnState);
+ break;
+#endif
+ case FN_SETUP_IDX :
+ menuSetup(btnState);
+ break;
+ case FN_EXIT_IDX :
+ menuExit(btnState);
+ break;
+
+#if FN_CALIBRATION == 1
+ case FN_CALIBRATION_IDX :
+ menuSetupCalibration(btnState); //crystal
+ break;
+#endif
+#if FN_CARRIER == 1
+ case FN_CARRIER_IDX :
+ menuSetupCarrier(btnState); //ssb
+ break;
+#endif
+#if FN_CWCARRIER == 1
+ case FN_CWCARRIER_IDX :
+ menuSetupCWCarrier(btnState); //cw
+ break;
+#endif
+#if FN_CWTONE == 1
+ case FN_CWTONE_IDX :
+ menuSetupCwTone(btnState);
+ break;
+#endif
+#if FN_CWDELAY == 1
+ case FN_CWDELAY_IDX :
+ menuSetupCwDelay(btnState);
+ break;
+#endif
+#if FN_TXCWDELAY == 1
+ case FN_TXCWDELAY_IDX :
+ menuSetupTXCWInterval(btnState);
+ break;
+#endif
+#if FN_KEYTYPE == 1
+ case FN_KEYTYPE_IDX :
+ menuSetupKeyType(btnState);
+ break;
+#endif
+#if FN_ADCMONITOR == 1
+ case FN_ADCMONITOR_IDX :
+ menuADCMonitor(btnState);
+ break;
+#endif
+#if FN_TXONOFF == 1
+ case FN_TXONOFF_IDX :
+ menuTxOnOff(btnState, 0x01); //TX OFF / ON
+ break;
+#endif
+ default :
+ menuExit(btnState); break;
+ } //end of switch
+ Check_Cat(0); //To prevent disconnections
+ } //end of while
+
+ //****************************************************************************
+ //Before change menu type (Version : ~ 0.95)
+ //****************************************************************************
+ /*
+ while (menuOn){
+ i = enc_read();
+ btnState = btnDown();
+
+ if (i > 0){
+ if (modeCalibrate && select + i < 250)
+ select += i;
+ else if (!modeCalibrate && select + i < 150)
+ select += i;
+ }
+ else if (i < 0 && select - i >= -10)
+ select += i;
+
+ //if -> switch : reduce program memory 200byte
+ switch (select / 10)
+ {
+ case 0 :
+ menuBand(btnState);
+ break;
+ case 1 :
+ menuVfoToggle(btnState);
+ break;
+ case 2 :
+ menuSelectMode(btnState);
+ break;
+ case 3 :
+ menuRitToggle(btnState);
+ break;
+ case 4 :
+ menuIFSSetup(btnState);
+ break;
+ case 5 :
+ menuATTSetup(btnState);
+ break;
+ case 6 :
+ menuCWSpeed(btnState);
+ break;
+ case 7 :
+ menuSplitOnOff(btnState); //SplitOn / off
+ break;
+ case 8 :
+ menuCHMemory(btnState, 0); //VFO to Memroy
+ break;
+ case 9 :
+ menuCHMemory(btnState, 1); //Memory to VFO
+ break;
+ case 10 :
+ menuCWAutoKey(btnState);
+ break;
+ case 11 :
+ menuWSPRSend(btnState);
+ break;
+ case 12 :
+ menuSDROnOff(btnState);
+ break;
+ case 13 :
+ menuSetup(btnState);
+ break;
+ case 14 :
+ menuExit(btnState);
+ break;
+ case 15 :
+ menuSetupCalibration(btnState); //crystal
+ break;
+ case 16 :
+ menuSetupCarrier(btnState); //ssb
+ break;
+ case 17 :
+ menuSetupCWCarrier(btnState); //cw
+ break;
+ case 18 :
+ menuSetupCwTone(btnState);
+ break;
+ case 19 :
+ menuSetupCwDelay(btnState);
+ break;
+ case 20 :
+ menuSetupTXCWInterval(btnState);
+ break;
+ case 21 :
+ menuSetupKeyType(btnState);
+ break;
+#ifdef ENABLE_ADCMONITOR
+ case 22 :
+ menuADCMonitor(btnState);
+ break;
+ case 23 :
+#else
+ case 22 :
+#endif
+ menuTxOnOff(btnState, 0x01); //TX OFF / ON
+ break;
+ default :
+ menuExit(btnState); break;
+ } //end of case
+ Check_Cat(0); //To prevent disconnections
+ } //end of while
+ */
+}
+
+//*************************************************************************************
+//Original Source Part
+//The code below is the original source part that I kept unchanged for compatibility.
+//By KD8CEC
+//*************************************************************************************
+
+/**
+ Original source comment
+ * Menus
+ * The Radio menus are accessed by tapping on the function button.
+ * - The main loop() constantly looks for a button press and calls doMenu() when it detects
+ * a function button press.
+ * - As the encoder is rotated, at every 10th pulse, the next or the previous menu
+ * item is displayed. Each menu item is controlled by it's own function.
+ * - Eache menu function may be called to display itself
+ * - Each of these menu routines is called with a button parameter.
+ * - The btn flag denotes if the menu itme was clicked on or not.
+ * - If the menu item is clicked on, then it is selected,
+ * - If the menu item is NOT clicked on, then the menu's prompt is to be displayed
+ */
+
+/**
+ * The calibration routines are not normally shown in the menu as they are rarely used
+ * They can be enabled by choosing this menu option
+ */
+void menuSetup(int btn){
+ if (!btn)
+ {
+ if (!modeCalibrate)
+ printLineF2(F("Setup On?"));
+ else
+ printLineF2(F("Setup Off?"));
+ }
+ else
+ {
+ modeCalibrate = ! modeCalibrate;
+ //menuClearExit(1000);
+
+#ifdef USE_SW_SERIAL
+ menuOn = 0;
+#else
+ //Only Clear And Delay for Character LCD
+ menuClearExit(1000);
+#endif
+ }
+}
+
+void menuExit(int btn){
+ if (!btn){
+ printLineF2(F("Exit Menu?"));
+ }
+ else
+ menuClearExit(0);
+}
+
+//modified for reduce used flash memory by KD8CEC
+void menuRitToggle(int btn){
+ if (!btn){
+ if (ritOn == 1)
+ printLineF2(F("RIT Off?"));
+ else
+ printLineF2(F("RIT On?"));
+ }
+ else {
+ if (ritOn == 0){
+ //printLineF2(F("RIT is ON"));
+ //enable RIT so the current frequency is used at transmit
+ ritEnable(frequency);
+ }
+ else{
+ //printLineF2(F("RIT is OFF"));
+ ritDisable();
+ }
+
+ //menuClearExit(500);
+#ifdef USE_SW_SERIAL
+ menuOn = 0;
+#else
+ //Only Clear And Delay for Character LCD
+ menuClearExit(500);
+#endif
+
+ }
+}
+
+/**
+ * Take a deep breath, math(ematics) ahead
+ * The 25 mhz oscillator is multiplied by 35 to run the vco at 875 mhz
+ * This is divided by a number to generate different frequencies.
+ * If we divide it by 875, we will get 1 mhz signal
+ * So, if the vco is shifted up by 875 hz, the generated frequency of 1 mhz is shifted by 1 hz (875/875)
+ * At 12 Mhz, the carrier will needed to be shifted down by 12 hz for every 875 hz of shift up of the vco
+ *
+ */
+
+ //this is used by the si5351 routines in the ubitx_5351 file
+extern int32_t calibration;
+extern uint32_t si5351bx_vcoa;
+
+void factoryCalibration(int btn){
+ int knob = 0;
+
+ //keep clear of any previous button press
+ while (btnDown())
+ delay(100);
+ delay(100);
+
+ if (!btn){
+ printLineF2(F("Set Calibration?"));
+ return;
+ }
+
+ calibration = 0;
+
+ cwMode = 0;
+ isUSB = true;
+
+ //turn off the second local oscillator and the bfo
+ si5351_set_calibration(calibration);
+ startTx(TX_CW, 1);
+ si5351bx_setfreq(2, 10000000l);
+
+ strcpy(b, "#1 10 MHz cal:");
+ ltoa(calibration/8750, c, 10);
+ strcat(b, c);
+ printLine2(b);
+
+ while (!btnDown())
+ {
+
+ if (digitalRead(PTT) == LOW && !keyDown)
+ cwKeydown();
+ if (digitalRead(PTT) == HIGH && keyDown)
+ cwKeyUp();
+
+ knob = enc_read();
+
+ if (knob > 0)
+ calibration += 875;
+ else if (knob < 0)
+ calibration -= 875;
+ else
+ continue; //don't update the frequency or the display
+
+ si5351_set_calibration(calibration);
+ si5351bx_setfreq(2, 10000000l);
+ strcpy(b, "#1 10 MHz cal:");
+ ltoa(calibration/8750, c, 10);
+ strcat(b, c);
+ printLine2(b);
+ }
+
+ cwTimeout = 0;
+ keyDown = 0;
+ stopTx();
+
+ printLineF2(F("Calibration set!"));
+ EEPROM.put(MASTER_CAL, calibration);
+ initOscillators();
+ setFrequency(frequency);
+ updateDisplay();
+
+ while(btnDown())
+ delay(50);
+
+ menuClearExit(100);
+}
+
+void menuSetupCalibration(int btn){
+ int knob = 0;
+ int32_t prev_calibration;
+
+ if (!btn){
+ printLineF2(F("Set Calibration?"));
+ return;
+ }
+
+ printLineF1(F("Set to Zero-beat,"));
+ printLineF2(F("press PTT to save"));
+ delay_background(1000, 0);
+
+ prev_calibration = calibration;
+ calibration = 0;
+ si5351_set_calibration(calibration);
+ setFrequency(frequency);
+
+ strcpy(b, "cal:");
+ ltoa(calibration/8750, c, 10);
+ strcat(b, c);
+ printLine2(b);
+
+ while (digitalRead(PTT) == HIGH && !btnDown())
+ {
+ knob = enc_read();
+
+ if (knob > 0){
+ calibration += 8750;
+ usbCarrier += 120;
+ }
+ else if (knob < 0){
+ calibration -= 8750;
+ usbCarrier -= 120;
+ }
+ else
+ continue; //don't update the frequency or the display
+
+ si5351_set_calibration(calibration);
+ si5351bx_setfreq(0, usbCarrier);
+ setFrequency(frequency);
+
+ strcpy(b, "cal:");
+ ltoa(calibration/8750, c, 10);
+ strcat(b, c);
+ printLine2(b);
+ }
+
+ //save the setting
+ if (digitalRead(PTT) == LOW){
+ printLineF1(F("Calibration set!"));
+ printLineF2(F("Set Carrier now"));
+ EEPROM.put(MASTER_CAL, calibration);
+ delay_background(2000, 0);
+ }
+ else
+ calibration = prev_calibration;
+
+ initOscillators();
+ //si5351_set_calibration(calibration);
+ setFrequency(frequency);
+ //printLine2ClearAndUpdate();
+ //menuOn = 0;
+ menuClearExit(0);
+}
+
+void printCarrierFreq(unsigned long freq){
+
+ memset(c, 0, sizeof(c));
+ memset(b, 0, sizeof(b));
+
+ ultoa(freq, b, DEC);
+
+ strncat(c, b, 2);
+ strcat(c, ".");
+ strncat(c, &b[2], 3);
+ strcat(c, ".");
+ strncat(c, &b[5], 3);
+ printLine2(c);
+}
+
+//modified by KD8CEC (just 1 line remarked //usbCarrier = ...
+void menuSetupCarrier(int btn){
+ int knob = 0;
+ unsigned long prevCarrier;
+
+ if (!btn){
+ printLineF2(F("Set the BFO"));
+ return;
+ }
+
+ prevCarrier = usbCarrier;
+ printLineF1(F("Tune to best Signal"));
+ printLineF1(F("PTT to confirm. "));
+ delay_background(1000, 0);
+
+ //usbCarrier = 11995000l; //Remarked by KD8CEC, Suggest from many user, if entry routine factoryrest
+ /*
+ //for uBITX V5.0, but not used by KD8CEC, if you want default value of carrier on Calibration, delete remark symbols
+#if UBITX_BOARD_VERSION == 5
+ usbCarrier = 11053000l;
+#else
+ usbCarrier = 11995000l;
+#endif
+ */
+
+
+ si5351bx_setfreq(0, usbCarrier);
+ printCarrierFreq(usbCarrier);
+
+ //disable all clock 1 and clock 2
+ while (digitalRead(PTT) == HIGH && !btnDown())
+ {
+ knob = enc_read();
+
+ if (knob > 0)
+ usbCarrier -= 5;
+ else if (knob < 0)
+ usbCarrier += 5;
+ else
+ continue; //don't update the frequency or the display
+
+ si5351bx_setfreq(0, usbCarrier);
+ printCarrierFreq(usbCarrier);
+
+ Check_Cat(0); //To prevent disconnections
+ delay(100);
+ }
+
+ //save the setting
+ if (digitalRead(PTT) == LOW){
+ printLineF2(F("Carrier set!"));
+ EEPROM.put(USB_CAL, usbCarrier);
+ delay_background(1000, 0);
+ }
+ else
+ usbCarrier = prevCarrier;
+
+ //si5351bx_setfreq(0, usbCarrier);
+ if (cwMode == 0)
+ si5351bx_setfreq(0, usbCarrier); //set back the carrier oscillator anyway, cw tx switches it off
+ else
+ si5351bx_setfreq(0, cwmCarrier); //set back the carrier oscillator anyway, cw tx switches it off
+
+ setFrequency(frequency);
+ //printLine2ClearAndUpdate();
+ //menuOn = 0;
+ menuClearExit(0);
+}
diff --git a/ubitx_20/ubitx_si5351.ino b/ubitx_20/ubitx_si5351.ino
new file mode 100644
index 0000000..e230bfd
--- /dev/null
+++ b/ubitx_20/ubitx_si5351.ino
@@ -0,0 +1,190 @@
+/************************************************************************************
+ * KD8CEC
+ * kd8cec@gmail.com http://www.hamskey.com
+ *
+ * Merge two SI5351 Librarys
+ * KE7ER's fixed vco and variable Clocks Configure values
+ * G3ZIL's fixed Clock Configure Value and variable VCO
+ * * I have combined the two libraries above. All licenses follow the above library.
+ *
+ * PLL-A is generated by fixing 850Mhz clock. All output clocks use PLL-A to
+ * generate the frequency. This is the method used in QRP radios such as uBITX.
+ * When switching to WSPR transmission mode, PLL-B operates for the base frequency to transmit WSPR.
+ * The output clock channel that controls the frequency is connected to the PLL-B.
+ * The WSPR protocol is generated by changing the clock of the PLL-B.
+ ************************************************************************************/
+#include "ubitx.h"
+
+// ************* SI5315 routines - tks Jerry Gaffke, KE7ER ***********************
+// An minimalist standalone set of Si5351 routines.
+// VCOA is fixed at 875mhz, VCOB not used.
+// The output msynth dividers are used to generate 3 independent clocks
+// with 1hz resolution to any frequency between 4khz and 109mhz.
+
+// Usage:
+// Call si5351bx_init() once at startup with no args;
+// Call si5351bx_setfreq(clknum, freq) each time one of the
+// three output CLK pins is to be updated to a new frequency.
+// A freq of 0 serves to shut down that output clock.
+
+// The global variable si5351bx_vcoa starts out equal to the nominal VCOA
+// frequency of 25mhz*35 = 875000000 Hz. To correct for 25mhz crystal errors,
+// the user can adjust this value. The vco frequency will not change but
+// the number used for the (a+b/c) output msynth calculations is affected.
+// Example: We call for a 5mhz signal, but it measures to be 5.001mhz.
+// So the actual vcoa frequency is 875mhz*5.001/5.000 = 875175000 Hz,
+// To correct for this error: si5351bx_vcoa=875175000;
+
+// Most users will never need to generate clocks below 500khz.
+// But it is possible to do so by loading a value between 0 and 7 into
+// the global variable si5351bx_rdiv, be sure to return it to a value of 0
+// before setting some other CLK output pin. The affected clock will be
+// divided down by a power of two defined by 2**si5351_rdiv
+// A value of zero gives a divide factor of 1, a value of 7 divides by 128.
+// This lightweight method is a reasonable compromise for a seldom used feature.
+
+
+#define BB0(x) ((uint8_t)x) // Bust int32 into Bytes
+#define BB1(x) ((uint8_t)(x>>8))
+#define BB2(x) ((uint8_t)(x>>16))
+
+//#define SI5351BX_ADDR 0x60 // I2C address of Si5351 (typical)
+uint8_t SI5351BX_ADDR; // I2C address of Si5351 (variable from Version 1.097)
+#define SI5351BX_XTALPF 2 // 1:6pf 2:8pf 3:10pf
+
+// If using 27mhz crystal, set XTAL=27000000, MSA=33. Then vco=891mhz
+#define SI5351BX_XTAL 25000000 // Crystal freq in Hz
+#define SI5351BX_MSA 35 // VCOA is at 25mhz*35 = 875mhz
+
+// User program may have reason to poke new values into these 3 RAM variables
+uint32_t si5351bx_vcoa = (SI5351BX_XTAL*SI5351BX_MSA); // 25mhzXtal calibrate
+uint8_t si5351bx_rdiv = 0; // 0-7, CLK pin sees fout/(2**rdiv)
+
+#if UBITX_BOARD_VERSION == 5
+uint8_t si5351bx_drive[3] = {3, 3, 3}; // 0=2ma 1=4ma 2=6ma 3=8ma for CLK 0,1,2
+#else
+uint8_t si5351bx_drive[3] = {1, 1, 1}; // 0=2ma 1=4ma 2=6ma 3=8ma for CLK 0,1,2
+#endif
+
+uint8_t si5351bx_clken = 0xFF; // Private, all CLK output drivers off
+int32_t calibration = 0;
+
+void i2cWrite(uint8_t reg, uint8_t val) { // write reg via i2c
+ Wire.beginTransmission(SI5351BX_ADDR);
+ Wire.write(reg);
+ Wire.write(val);
+ Wire.endTransmission();
+}
+
+void i2cWriten(uint8_t reg, uint8_t *vals, uint8_t vcnt) { // write array
+ Wire.beginTransmission(SI5351BX_ADDR);
+ Wire.write(reg);
+ while (vcnt--) Wire.write(*vals++);
+ Wire.endTransmission();
+}
+
+uint8_t si5351Val[8] = {0, 1, 0, 0, 0, 0, 0, 0}; //for reduce program memory size
+
+void si5351bx_init() { // Call once at power-up, start PLLA
+ uint32_t msxp1;
+ Wire.begin();
+ i2cWrite(149, 0); // SpreadSpectrum off
+ i2cWrite(3, si5351bx_clken); // Disable all CLK output drivers
+ i2cWrite(183, SI5351BX_XTALPF << 6); // Set 25mhz crystal load capacitance
+ msxp1 = 128 * SI5351BX_MSA - 512; // and msxp2=0, msxp3=1, not fractional
+ //uint8_t vals[8] = {0, 1, BB2(msxp1), BB1(msxp1), BB0(msxp1), 0, 0, 0};
+ si5351Val[2] = BB2(msxp1);
+ si5351Val[3] = BB1(msxp1);
+ si5351Val[4] = BB0(msxp1);
+
+ i2cWriten(26, si5351Val, 8); // Write to 8 PLLA msynth regs
+ i2cWrite(177, 0x20); // Reset PLLA (0x80 resets PLLB)
+
+
+#if UBITX_BOARD_VERSION == 5
+ //why? TODO : CHECK by KD8CEC
+ //initializing the ppl2 as well
+ i2cWriten(34, si5351Val, 8); // Write to 8 PLLA msynth regs
+ i2cWrite(177, 0xa0); // Reset PLLA & PPLB (0x80 resets PLLB)
+#else
+ //
+#endif
+
+
+}
+
+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
+ si5351bx_clken |= 1 << clknum; // shut down the clock
+ else {
+ msa = si5351bx_vcoa / fout; // Integer part of vco/fout
+ msb = si5351bx_vcoa % fout; // Fractional part of vco/fout
+ msc = fout; // Divide by 2 till fits in reg
+ while (msc & 0xfff00000) {
+ msb = msb >> 1;
+ msc = msc >> 1;
+ }
+ msxp1 = (128 * msa + 128 * msb / msc - 512) | (((uint32_t)si5351bx_rdiv) << 20);
+ msxp2 = 128 * msb - 128 * msb / msc * msc; // msxp3 == msc;
+ msxp3p2top = (((msc & 0x0F0000) << 4) | msxp2); // 2 top nibbles
+ uint8_t vals[8] = { BB1(msc), BB0(msc), BB2(msxp1), BB1(msxp1),
+ BB0(msxp1), BB2(msxp3p2top), BB1(msxp2), BB0(msxp2)
+ };
+ i2cWriten(42 + (clknum * 8), vals, 8); // Write to 8 msynth regs
+ i2cWrite(16 + clknum, 0x0C | si5351bx_drive[clknum]); // use local msynth
+ si5351bx_clken &= ~(1 << clknum); // Clear bit to enable clock
+ }
+ i2cWrite(3, si5351bx_clken); // Enable/disable clock
+}
+
+void si5351_set_calibration(int32_t cal){
+ si5351bx_vcoa = (SI5351BX_XTAL * SI5351BX_MSA) + cal; // apply the calibration correction factor
+ si5351bx_setfreq(0, usbCarrier);
+}
+
+void SetCarrierFreq()
+{
+ unsigned long appliedCarrier = ((cwMode == 0 ? usbCarrier : cwmCarrier) + (isIFShift && (inTx == 0) ? ifShiftValue : 0));
+ //si5351bx_setfreq(0, (sdrModeOn ? 0 : appliedCarrier));
+ si5351bx_setfreq(0, ((sdrModeOn && (inTx == 0)) ? 0 : appliedCarrier)); //found bug by KG4GEK
+
+
+ /*
+ if (cwMode == 0)
+ si5351bx_setfreq(0, usbCarrier + (isIFShift ? ifShiftValue : 0));
+ else
+ si5351bx_setfreq(0, cwmCarrier + (isIFShift ? ifShiftValue : 0));
+ */
+}
+
+void initOscillators(){
+ //initialize the SI5351
+ si5351bx_init();
+ si5351bx_vcoa = (SI5351BX_XTAL * SI5351BX_MSA) + calibration; // apply the calibration correction factor
+ SetCarrierFreq();
+}
+
+//============================================================
+// ADD FUNCTIONS by KD8CEC
+//============================================================
+uint8_t Wspr_Reg1[8] = {0xFF,0xFE, 0x00, 0, 0, 0, 0, 0}; //3, 4, 5, 6, 7
+uint8_t Wspr_Reg2[8] = {0, 1, 0, 0, 0, 0, 0, 0}; //2, 3, 4
+
+void Set_WSPR_Param(void)
+{
+ i2cWrite(18, 128);
+ i2cWriten(34, Wspr_Reg1, 8);
+ i2cWriten(58, Wspr_Reg2, 8);
+ i2cWrite(177, 128);
+ i2cWrite(18, 111);
+
+ si5351bx_clken &= ~(1 << 2);
+ i2cWrite(3, si5351bx_clken);
+}
+
+void TXSubFreq(unsigned long P2)
+{
+ i2cWrite(40, (P2 & 65280) >> 8);
+ i2cWrite(41, P2 & 255);
+}
diff --git a/ubitx_20/ubitx_ui.ino b/ubitx_20/ubitx_ui.ino
new file mode 100644
index 0000000..0aa7c30
--- /dev/null
+++ b/ubitx_20/ubitx_ui.ino
@@ -0,0 +1,299 @@
+/**
+ * The user interface of the ubitx consists of the encoder, the push-button on top of it
+ * and the 16x2 LCD display.
+ * The upper line of the display is constantly used to display frequency and status
+ * of the radio. Occasionally, it is used to provide a two-line information that is
+ * quickly cleared up.
+ */
+
+
+/*
+const PROGMEM uint8_t meters_bitmap[] = {
+ B10000, B10000, B10000, B10000, B10000, B10000, B10000, B10000 , //custom 1
+ B11000, B11000, B11000, B11000, B11000, B11000, B11000, B11000 , //custom 2
+ B11100, B11100, B11100, B11100, B11100, B11100, B11100, B11100 , //custom 3
+ B11110, B11110, B11110, B11110, B11110, B11110, B11110, B11110 , //custom 4
+ B11111, B11111, B11111, B11111, B11111, B11111, B11111, B11111 , //custom 5
+ B01000, B11100, B01000, B00000, B10111, B10101, B10101, B10111 //custom 6
+};
+*/
+
+//SWR GRAPH, DrawMeter and drawingMeter Logic function by VK2ETA
+
+#ifdef OPTION_SKINNYBARS //We want skninny bars with more text
+//VK2ETA modded "Skinny" bitmaps
+const PROGMEM uint8_t meters_bitmap[] = {
+ // B01110, B10001, B10001, B11111, B11011, B11011, B11111, B00000, //Padlock Symbol, for merging. Not working, see below
+ B00000, B00000, B00000, B00000, B00000, B00000, B00000, B10000, //shortest bar
+ B00000, B00000, B00000, B00000, B00000, B00000, B00100, B10100,
+ B00000, B00000, B00000, B00000, B00000, B00001, B00101, B10101,
+ B00000, B00000, B00000, B00000, B10000, B10000, B10000, B10000,
+ B00000, B00000, B00000, B00100, B10100, B10100, B10100, B10100,
+ B00000, B00000, B00001, B00101, B10101, B10101, B10101, B10101, //tallest bar
+ B00000, B00010, B00111, B00010, B01000, B11100, B01000, B00000, // ++ sign
+};
+#else
+//VK2ETA "Fat" bars, easy to read, with less text
+const PROGMEM uint8_t meters_bitmap[] = {
+ // B01110, B10001, B10001, B11111, B11011, B11011, B11111, B00000, //Padlock Symbol, for merging. Not working, see below
+ B00000, B00000, B00000, B00000, B00000, B00000, B00000, B11111, //shortest bar
+ B00000, B00000, B00000, B00000, B00000, B00000, B11111, B11111,
+ B00000, B00000, B00000, B00000, B00000, B11111, B11111, B11111,
+ B00000, B00000, B00000, B00000, B11111, B11111, B11111, B11111,
+ B00000, B00000, B00000, B11111, B11111, B11111, B11111, B11111,
+ B00000, B00000, B11111, B11111, B11111, B11111, B11111, B11111, //tallest bar
+ B00000, B00010, B00111, B00010, B01000, B11100, B01000, B00000, // ++ sign
+};
+#endif //OPTION_SKINNYBARS
+PGM_P p_metes_bitmap = reinterpret_cast(meters_bitmap);
+
+const PROGMEM uint8_t lock_bitmap[8] = {
+ 0b01110,
+ 0b10001,
+ 0b10001,
+ 0b11111,
+ 0b11011,
+ 0b11011,
+ 0b11111,
+ 0b00000};
+PGM_P plock_bitmap = reinterpret_cast(lock_bitmap);
+
+
+// initializes the custom characters
+// we start from char 1 as char 0 terminates the string!
+void initMeter(){
+ uint8_t tmpbytes[8];
+ byte i;
+
+ for (i = 0; i < 8; i++)
+ tmpbytes[i] = pgm_read_byte(plock_bitmap + i);
+ LCD_CreateChar(0, tmpbytes);
+
+ for (i = 0; i < 8; i++)
+ tmpbytes[i] = pgm_read_byte(p_metes_bitmap + i);
+ LCD_CreateChar(1, tmpbytes);
+
+ for (i = 0; i < 8; i++)
+ tmpbytes[i] = pgm_read_byte(p_metes_bitmap + i + 8);
+ LCD_CreateChar(2, tmpbytes);
+
+ for (i = 0; i < 8; i++)
+ tmpbytes[i] = pgm_read_byte(p_metes_bitmap + i + 16);
+ LCD_CreateChar(3, tmpbytes);
+
+ for (i = 0; i < 8; i++)
+ tmpbytes[i] = pgm_read_byte(p_metes_bitmap + i + 24);
+ LCD_CreateChar(4, tmpbytes);
+
+ for (i = 0; i < 8; i++)
+ tmpbytes[i] = pgm_read_byte(p_metes_bitmap + i + 32);
+ LCD_CreateChar(5, tmpbytes);
+
+ for (i = 0; i < 8; i++)
+ tmpbytes[i] = pgm_read_byte(p_metes_bitmap + i + 40);
+ LCD_CreateChar(6, tmpbytes);
+
+ for (i = 0; i < 8; i++)
+ tmpbytes[i] = pgm_read_byte(p_metes_bitmap + i + 48);
+ LCD_CreateChar(7, tmpbytes);
+}
+
+
+//by KD8CEC
+//0 ~ 25 : 30 over : + 10
+/*
+void drawMeter(int needle) {
+ //5Char + O over
+ int i;
+
+ for (i = 0; i < 5; i++) {
+ if (needle >= 5)
+ lcdMeter[i] = 5; //full
+ else if (needle > 0)
+ lcdMeter[i] = needle; //full
+ else //0
+ lcdMeter[i] = 0x20;
+
+ needle -= 5;
+ }
+
+ if (needle > 0)
+ lcdMeter[5] = 6;
+ else
+ lcdMeter[5] = 0x20;
+}
+*/
+//VK2ETA meter for S.Meter, power and SWR
+void drawMeter(int needle)
+{
+#ifdef OPTION_SKINNYBARS
+ //Fill buffer with growing set of bars, up to needle value
+ lcdMeter[0] = 0x20;
+ lcdMeter[1] = 0x20;
+ for (int i = 0; i < 6; i++) {
+ if (needle > i)
+ lcdMeter[i / 3] = byte(i + 1); //Custom characters above
+ //else if (i == 1 || i == 4) {
+ // lcdMeter[i / 3] = 0x20; //blank
+ //}
+ }
+
+ if (needle > 7) {
+ lcdMeter[2] = byte(7); //Custom character "++"
+ } else if (needle > 6) {
+ lcdMeter[2] = '+'; //"+"
+ } else lcdMeter[2] = 0x20;
+
+
+#else //Must be "fat" bars
+ //Fill buffer with growing set of bars, up to needle value
+ for (int i = 0; i < 6; i++) {
+ if (needle > i)
+ lcdMeter[i] = byte(i + 1); //Custom characters above
+ else
+ lcdMeter[i] = 0x20; //blank
+ }
+
+ if (needle > 7) {
+ lcdMeter[6] = byte(7); //Custom character "++"
+ } else if (needle > 6) {
+ lcdMeter[6] = '+'; //"+"
+ } else lcdMeter[6] = 0x20;
+
+#endif //OPTION_FATBARS
+}
+
+
+
+ char byteToChar(byte srcByte){
+ if (srcByte < 10)
+ return 0x30 + srcByte;
+ else
+ return 'A' + srcByte - 10;
+}
+
+//returns true if the button is pressed
+int btnDown(void){
+#ifdef EXTEND_KEY_GROUP1
+ if (analogRead(FBUTTON) > FUNCTION_KEY_ADC)
+ return 0;
+ else
+ return 1;
+
+#else
+ if (digitalRead(FBUTTON) == HIGH)
+ return 0;
+ else
+ return 1;
+#endif
+}
+
+#ifdef EXTEND_KEY_GROUP1
+int getBtnStatus(void){
+ int readButtonValue = analogRead(FBUTTON);
+
+ if (analogRead(FBUTTON) < FUNCTION_KEY_ADC)
+ return FKEY_PRESS;
+ else
+ {
+ readButtonValue = readButtonValue / 4;
+ //return FKEY_VFOCHANGE;
+ for (int i = 0; i < 16; i++)
+ if (KeyValues[i][2] != 0 && KeyValues[i][0] <= readButtonValue && KeyValues[i][1] >= readButtonValue)
+ return KeyValues[i][2];
+ //return i;
+ }
+
+ return -1;
+}
+#endif
+
+int enc_prev_state = 3;
+
+/**
+ * The A7 And A6 are purely analog lines on the Arduino Nano
+ * These need to be pulled up externally using two 10 K resistors
+ *
+ * There are excellent pages on the Internet about how these encoders work
+ * and how they should be used. We have elected to use the simplest way
+ * to use these encoders without the complexity of interrupts etc to
+ * keep it understandable.
+ *
+ * The enc_state returns a two-bit number such that each bit reflects the current
+ * value of each of the two phases of the encoder
+ *
+ * The enc_read returns the number of net pulses counted over 50 msecs.
+ * If the puluses are -ve, they were anti-clockwise, if they are +ve, the
+ * were in the clockwise directions. Higher the pulses, greater the speed
+ * at which the enccoder was spun
+ */
+
+byte enc_state (void) {
+ return (analogRead(ENC_A) > 500 ? 1 : 0) + (analogRead(ENC_B) > 500 ? 2: 0);
+}
+
+int enc_read(void) {
+ int result = 0;
+ byte newState;
+ int enc_speed = 0;
+
+ unsigned long start_at = millis();
+
+ while (millis() - start_at < 50) { // check if the previous state was stable
+ newState = enc_state(); // Get current state
+
+ if (newState != enc_prev_state)
+ delay (1);
+
+ if (enc_state() != newState || newState == enc_prev_state)
+ continue;
+ //these transitions point to the encoder being rotated anti-clockwise
+ if ((enc_prev_state == 0 && newState == 2) ||
+ (enc_prev_state == 2 && newState == 3) ||
+ (enc_prev_state == 3 && newState == 1) ||
+ (enc_prev_state == 1 && newState == 0)){
+ result--;
+ }
+ //these transitions point o the enccoder being rotated clockwise
+ if ((enc_prev_state == 0 && newState == 1) ||
+ (enc_prev_state == 1 && newState == 3) ||
+ (enc_prev_state == 3 && newState == 2) ||
+ (enc_prev_state == 2 && newState == 0)){
+ result++;
+ }
+ enc_prev_state = newState; // Record state for next pulse interpretation
+ enc_speed++;
+ delay(1);
+ }
+ return(result);
+}
+
+//===================================================================
+//I2C Signal Meter, Version 1.097
+//===================================================================
+
+// 0xA0 ~ 0xCF : CW Decode Mode + 100Hz ~
+// 0xD0 ~ 0xF3 : RTTY Decode Mode + 100Hz ~
+// 0x10 ~ 0x30 : Spectrum Mode
+int GetI2CSmeterValue(int valueType)
+{
+ if (valueType > 0)
+ {
+ Wire.beginTransmission(I2CMETER_ADDR); //j : S-Meter
+ Wire.write(valueType); //Y : Get Value Type
+ Wire.endTransmission();
+ }
+
+ Wire.requestFrom(I2CMETER_ADDR, 1);
+
+ if (Wire.available() > 0)
+ {
+ return Wire.read();
+ }
+ else
+ {
+ return 0;
+ }
+}
+
+
diff --git a/ubitx_20/ubitx_wspr.ino b/ubitx_20/ubitx_wspr.ino
new file mode 100644
index 0000000..a9281f6
--- /dev/null
+++ b/ubitx_20/ubitx_wspr.ino
@@ -0,0 +1,198 @@
+/**********************************************************************************
+WSPR SENDER for uBITX by KD8CEC
+Some of the code that sends WSPR referenced the code in G3ZIL.
+Thanks to G3ZIL for sharing great code.
+
+Due to the limited memory of uBITX, I have implemented at least only a few of the codes in uBITX.
+
+Thanks for testing
+-----------------------------------------------------------------------------
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see .
+**********************************************************************************/
+
+#include
+#include "ubitx.h"
+
+//begin of test
+byte WsprToneCode[164];
+
+unsigned long lastTime=0;
+unsigned long TX_MSNB_P2; // Si5351 register MSNB_P2 PLLB for Tx
+unsigned long TX_P2; // Variable values for MSNB_P2 which defines the frequencies for the data
+
+extern int enc_read(void);
+
+byte WsprMSGCount = 0;
+
+#define WSPR_BAND1 401
+
+extern uint8_t Wspr_Reg1[8]; //3, 4, 5, 6, 7
+extern uint8_t Wspr_Reg2[8]; //2, 3, 4
+
+void SendWSPRManage()
+{
+ int knob = 0;
+ byte knobPosition = 0;
+ //char isNeedDisplayInfo = 0;
+ char nowSelectedIndex = 0;
+ char nowWsprStep = 0; //0 : select Message, 1 : select band, 2 : send
+ char selectedWsprMessageIndex = -1;
+ char selectedWsprBandIndex = -1;
+
+ unsigned long WsprTXFreq = 0;
+ unsigned int WsprMultiChan = 0;
+ //unsigned long prevFreq;
+ byte loopIndex;
+
+ delay_background(500, 0);
+
+ //Readed WsprMSGCount, WsprTone
+ while(1)
+ {
+ knob = enc_read();
+
+ if (knobPosition > 0 && knob < 0)
+ knobPosition--;
+ else if (knob > 0 && (knobPosition <= (nowWsprStep == 0 ? WsprMSGCount : WSPR_BAND_COUNT) * 10 -2))
+ knobPosition++;
+
+ nowSelectedIndex = knobPosition / 10;
+
+ if (nowWsprStep == 0) //select Message status
+ {
+ //printLineF2(F("WSPR:"));
+
+ if (selectedWsprMessageIndex != nowSelectedIndex)
+ {
+ selectedWsprMessageIndex = nowSelectedIndex;
+ int wsprMessageBuffIndex = selectedWsprMessageIndex * 46;
+
+ printLineF2(F("WSPR:"));
+ //Display WSPR Name tag
+ printLineFromEEPRom(0, 6, wsprMessageBuffIndex, wsprMessageBuffIndex + 4, 1);
+
+ //Load WSPR Tonecode
+ //Read Tone Code
+ for (int i = 0; i < 41; i++)
+ {
+ byte readData = EEPROM.read(WSPR_MESSAGE1 + 5 + (wsprMessageBuffIndex) + i); //NAME TAG 5, MESSAGE 41 = 46
+ WsprToneCode[i * 4 + 0] = readData & 3;
+ WsprToneCode[i * 4 + 1] = (readData >> 2) & 3;
+ WsprToneCode[i * 4 + 2] = (readData >> 4) & 3;
+ WsprToneCode[i * 4 + 3] = (readData >> 6) & 3;
+ }
+ }
+ else if (btnDown())
+ {
+ nowWsprStep = 1; //Change Status to Select Band
+ knobPosition = 0;
+ nowSelectedIndex = 0;
+ delay_background(500, 0);
+ }
+ }
+ else if (nowWsprStep == 1)
+ {
+ //printLineF2(F("Select Band"));
+ if (selectedWsprBandIndex != nowSelectedIndex)
+ {
+ selectedWsprBandIndex = nowSelectedIndex;
+ int bandBuffIndex = WSPR_BAND1 + selectedWsprBandIndex * 14;
+
+ EEPROM.get(bandBuffIndex, WsprTXFreq);
+ EEPROM.get(bandBuffIndex + 4, WsprMultiChan);
+
+ for (loopIndex = 3; loopIndex < 8; loopIndex++)
+ Wspr_Reg1[loopIndex] = EEPROM.read(bandBuffIndex + loopIndex + 3);
+
+ //2, 3, 4
+ for (loopIndex = 2; loopIndex < 5; loopIndex++)
+ Wspr_Reg2[loopIndex] = EEPROM.read(bandBuffIndex + loopIndex + 9);
+
+ TX_MSNB_P2 = ((unsigned long)Wspr_Reg1[5] & 0x0F) << 16 | ((unsigned long)Wspr_Reg1[6]) << 8 | Wspr_Reg1[7];
+ }
+
+ if (digitalRead(PTT) == 0)
+ strcpy(c, "SEND: ");
+ else
+ strcpy(c, "PTT-> ");
+
+ //ltoa(WsprTXFreq, b, DEC);
+ //strcat(c, b);
+
+ //display frequency, Frequency to String for KD8CEC
+ unsigned long tmpFreq = WsprTXFreq;
+ for (int i = 15; i >= 6; i--) {
+ if (tmpFreq > 0) {
+ if (i == 12 || i == 8) c[i] = '.';
+ else {
+ c[i] = tmpFreq % 10 + 0x30;
+ tmpFreq /= 10;
+ }
+ }
+ else
+ c[i] = ' ';
+ }
+
+ printLine1(c);
+
+#ifdef USE_SW_SERIAL
+ SWS_Process();
+ if ((digitalRead(PTT) == 0) || (TriggerBySW == 1))
+ {
+ TriggerBySW = 0;
+#else
+ if (digitalRead(PTT) == 0)
+ {
+#endif
+ //SEND WSPR
+ //If you need to consider the Rit and Sprite modes, uncomment them below.
+ //remark = To reduce the size of the program
+ //prevFreq = frequency;
+ //frequency = WsprTXFreq;
+ startTx(TX_CW, 0);
+ setTXFilters(WsprTXFreq);
+
+ //Start WSPR
+ Set_WSPR_Param();
+ digitalWrite(CW_KEY, 1);
+
+ for (int i = 0; i < 162; i++)
+ { // Now this is the message loop
+ lastTime = millis(); // Store away the time when the last message symbol was sent
+ TX_P2 = TX_MSNB_P2 + WsprMultiChan * WsprToneCode[i]; // This represents the 1.46 Hz shift and is correct only for the bands specified in the array
+ TXSubFreq(TX_P2); // TX at the appropriate channel frequency for....
+
+ //if (btnDown())
+ // break;
+
+ while (millis() < lastTime + 683){} // .... 0,683 seconds
+ }
+
+ digitalWrite(CW_KEY, 0);
+ stopTx(); //call setFrequency -> recovery TX Filter
+ //frequency = prevFreq;
+
+ selectedWsprBandIndex = -1;
+ } //end of PTT Check
+ else if (btnDown())
+ {
+ return;
+ }
+
+ } //end of status check
+
+ //delay_background(50, 1);
+ } //end of while
+}
+
diff --git a/ubitx_wiring.png b/ubitx_wiring.png
new file mode 100644
index 0000000..49f903e
Binary files /dev/null and b/ubitx_wiring.png differ
diff --git a/ubitxmanager ubuntu.odt b/ubitxmanager ubuntu.odt
new file mode 100644
index 0000000..514b20b
Binary files /dev/null and b/ubitxmanager ubuntu.odt differ
diff --git a/ubitxv3.pdf b/ubitxv3.pdf
new file mode 100644
index 0000000..9b7990e
Binary files /dev/null and b/ubitxv3.pdf differ