From b83b0d24dc30d2418890f2b9192c4350f479d4d9 Mon Sep 17 00:00:00 2001 From: Ashhar Farhan Date: Fri, 3 Mar 2017 01:05:30 +0530 Subject: [PATCH 001/173] Update LICENSE.md --- LICENSE.md | 674 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 674 insertions(+) create mode 100644 LICENSE.md 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 +. From 37aaaf4e89c70048ee7bb796c071111cdff377d7 Mon Sep 17 00:00:00 2001 From: Ashhar Farhan Date: Fri, 3 Mar 2017 01:06:45 +0530 Subject: [PATCH 002/173] Add files via upload --- control_lcd_45mhz_if_v4.ino | 899 ++++++++++++++++++++++++++++++++++++ 1 file changed, 899 insertions(+) create mode 100644 control_lcd_45mhz_if_v4.ino diff --git a/control_lcd_45mhz_if_v4.ino b/control_lcd_45mhz_if_v4.ino new file mode 100644 index 0000000..f4eb989 --- /dev/null +++ b/control_lcd_45mhz_if_v4.ino @@ -0,0 +1,899 @@ +/** + * This source file is under General Public License version 3. + * + * 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 firt 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 . + */ + +#include + +/** + * 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 +Si5351 si5351; +/** + * The Radiono board is the size of a standard 16x2 LCD panel. It has three connectors: + * + * First, is an 8 pin connector that provides +5v, GND and six analog input pins that can also be + * configured to be used as digital input or output pins. These are referred to as A0,A1,A2, + * A3,A6 and A7 pins. The A4 and A5 pins are missing from this connector as they are used to + * talk to the Si5351 over I2C protocol. + * + * Second is a 16 pin LCD connector. This connector is meant specifically for the standard 16x2 + * LCD display in 4 bit mode. The 4 bit mode requires 4 data lines and two control lines to work: + * Lines used are : RESET, ENABLE, D4, D5, D6, D7 + * We include the library and declare the configuration of the LCD panel too + */ + +#include +LiquidCrystal lcd(8,9,10,11,12,13); + +/** + * The Arduino, unlike C/C++ on a regular computer with gigabytes of RAM, has very little memory. + * We have to be very careful with variables that are declared inside the functions as they are + * created in a memory region called the stack. The stack has just a few bytes of space on the Arduino + * if you declare large strings inside functions, they can easily exceed the capacity of the stack + * and mess up your programs. + * We circumvent this by declaring a few global buffers as kitchen counters where we can + * slice and dice our strings. These strings are mostly used to control the display or handle + * the input and output from the USB port. We must keep a count of the bytes used while reading + * the serial port as we can easily run out of buffer space. This is done in the serial_in_count variable. + */ +char serial_in[32], c[30], b[30], printBuff[32]; +int count = 0; +unsigned char serial_in_count = 0; + +/** + * 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: + * A0, A1, A2, A3, +5v, GND, A6, A7 + * (while holding the board up so that back of the board faces you) + * + * 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 S_METER (A0) +#define PTT (A1) +#define FBUTTON (A3) +#define ANALOG_KEYER (A7) +#define ANALOG_TUNING (A6) + +/** + * The second set of 16 pins on the bottom connector are have the three clock outputs and the digital lines to control the rig. + * This assignment is as follows : + * Pin 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 + * GND +5V CLK0 GND GND CLK1 GND GND CLK2 GND D2 D3 D4 D5 D6 D7 + * These too are flexible with what you may do with them, for the Raduino, we use them to : + * - TX_RX line : Switches between Transmit and Receive after sensing the PTT or the morse keyer + * - CW_KEY line : turns on the carrier for CW + */ + +#define TX_RX (7) +#define CW_TONE (6) +#define TX_LPF_SEL (5) +#define CW_KEY (4) + +/** + * The raduino has a number of timing parameters, all specified in milliseconds + * CW_TIMEOUT : how many milliseconds between consecutive keyup and keydowns before switching back to receive? + * The next set of three parameters determine what is a tap, a double tap and a hold time for the funciton button + * TAP_DOWN_MILLIS : upper limit of how long a tap can be to be considered as a button_tap + * TAP_UP_MILLIS : upper limit of how long a gap can be between two taps of a button_double_tap + * TAP_HOLD_MILIS : many milliseconds of the buttonb being down before considering it to be a button_hold + */ + +#define TAP_UP_MILLIS (500) +#define TAP_DOWN_MILLIS (600) +#define TAP_HOLD_MILLIS (2000) +#define CW_TIMEOUT (2000l) // in milliseconds, this is the parameter that determines how long the tx will hold between cw key downs + +/** + * The Raduino supports two VFOs : A and B and receiver incremental tuning (RIT). + * we define a variables to hold the frequency of the two VFOs, RITs + * the rit offset as well as status of the RIT + */ +#define VFO_A 0 +#define VFO_B 1 +char ritOn = 0; +char vfoActive = VFO_A; +unsigned long vfoA=7100000L, vfoB=14200000L, ritA, ritB, sideTone=800, lsbCarrier, usbCarrier; + +#define MASTER_CAL 0 +#define LSB_CAL 4 +#define USB_CAL 8 +#define SIDE_TONE 12 + +/** + * Raduino needs to keep track of current state of the transceiver. These are a few variables that do it + */ +char inTx = 0; +char keyDown = 0; +char isUSB = 0; +unsigned long cwTimeout = 0; +unsigned char txFilter = 0; + +/** Tuning Mechanism of the Raduino + * We use a linear pot that has two ends connected to +5 and the ground. the middle wiper + * is connected to ANALOG_TUNNING pin. Depending upon the position of the wiper, the + * reading can be anywhere from 0 to 1024. + * The tuning control works in steps of 50Hz each for every increment between 50 and 950. + * Hence the turning the pot fully from one end to the other will cover 50 x 900 = 45 KHz. + * At the two ends, that is, the tuning starts slowly stepping up or down in 10 KHz steps + * To stop the scanning the pot is moved back from the edge. + * To rapidly change from one band to another, you press the function button and then + * move the tuning pot. Now, instead of 50 Hz, the tuning is in steps of 50 KHz allowing you + * rapidly use it like a 'bandset' control. + * To implement this, we fix a 'base frequency' to which we add the offset that the pot + * points to. We also store the previous position to know if we need to wake up and change + * the frequency. + */ + +unsigned long baseTune = 7100000L; +int old_knob = 0; + +#define FIRST_IF (45000000l) +#define SECOND_OSC (57000000l) +#define INIT_USB_FREQ (11996500l) +#define INIT_LSB_FREQ (11998500l) +#define LOWEST_FREQ (3000000l) +#define HIGHEST_FREQ (30000000l) + +long frequency, stepSize=100000; + +/** + * The raduino can be booted into multiple modes: + * MODE_NORMAL : works the radio normally + * MODE_CALIBRATION : used to calibrate Raduino. + * To enter this mode, hold the function button down and power up. Tune to exactly 10 MHz on clock0 and release the function button + */ + #define MODE_NORMAL (0) + #define MODE_CALIBRATE (1) + char mode = MODE_NORMAL; + +char meter[17]; +byte s_meter[] = { + B0,B0,B0,B0,B0,B00000,B0,B10101, + B0,B0,B0,B0,B10000,B10000,B0,B10101, + B0,B0,B0,B0,B11000,B11000,B0,B10101, + B0,B0,B0,B0,B11100,B11100,B0,B10101, + B0,B0,B0,B0,B11110,B11110,B0,B10101, + B0,B0,B0,B0,B11111,B11111,B0,B10101 +}; + +void setupSmeter(){ + lcd.createChar(1, s_meter); + lcd.createChar(2, s_meter + 8); + lcd.createChar(3, s_meter + 16); + lcd.createChar(4, s_meter + 24); + lcd.createChar(5, s_meter + 32); + lcd.createChar(6, s_meter + 40); +} + +/* display routines */ +void printLine1(char *c){ + if (strcmp(c, printBuff)){ + lcd.setCursor(0, 0); + lcd.print(c); + strcpy(printBuff, c); + count++; + } +} + +void printLine2(char *c){ + if (strlen(c) > 16) + c[16] = 0; + lcd.setCursor(0, 1); + lcd.print(c); +} + +void displayFrequency(unsigned long f){ + int mhz, khz, hz; + + mhz = f / 1000000l; + khz = (f % 1000000l)/1000; + hz = f % 1000l; + sprintf(b, "[%02d.%03d.%03d]", mhz, khz, hz); + printLine1(b); +} + +void updateMeter(){ + int16_t best, i, s; + + best = 0; + //take 100 readings, take the peaks + for (i = 0; i < 100; i++){ + s = analogRead(S_METER); + if (s > best) + best = s; + } + //now, use the s to get the signal + s = best *2; + sprintf(meter, "%3d", s); + for (i = 3; i < 14; i++){ + if (s >= 5) + meter[i] = 6; + else if (s > 0) + meter[i] = 1 + s; + else + meter[i] = 1; + s = s - 5; + } + meter[i] = 0; + printLine2(meter); +} + +/** + * Defines for menus + */ +byte menuOn = 0; + +int ritToggle(int btn){ + if (!btn){ + if (ritOn == 1) + printLine2("RIT:On, Off? "); + else + printLine2("RIT:Off, On? "); + } + else { + if (ritOn == 0){ + ritOn = 1; + printLine2("RIT is On. "); + } + else { + ritOn = 0; + printLine2("RIT Is Off. "); + } + } +} + +int vfoToggle(int btn){ + + if (!btn){ + if (vfoActive == VFO_A) + printLine2("Select VFO B? "); + else + printLine2("Select VFO A? "); + } + else { + if (vfoActive == VFO_A){ + vfoActive = VFO_A; + printLine1("Selected VFO A "); + frequency = vfoA; + } + else { + vfoActive = VFO_B; + printLine1("Selected VFO B "); + frequency = vfoB; + } + setFrequency(frequency); + updateDisplay(); + resetBasefrequency(); + } +} + +/** + * Load the new correction and resets the clock 0 to 10 MHz + */ + +void recalibrate(int32_t correction){ + + si5351.set_correction(correction); + + si5351.set_pll(SI5351_PLL_FIXED, SI5351_PLLA); + si5351.set_pll(SI5351_PLL_FIXED, SI5351_PLLB); + + si5351.output_enable(SI5351_CLK0, 1); + si5351.output_enable(SI5351_CLK1, 0); + si5351.output_enable(SI5351_CLK2, 0); + + si5351.set_freq(1000000000ULL, SI5351_CLK0); +} + +void calibrateMaster(int btn){ + int knob = 0; + int32_t correction; + + if (!btn){ + printLine2("Set Calibration?"); + return; + } + printLine1("Set to 10.000.000"); + printLine2("PTT to confirm. "); + delay(2000); + + recalibrate(0); + + //disable all clock 1 and clock 2 + while(digitalRead(PTT) == HIGH && !btnDown()){ + knob = analogRead(ANALOG_TUNING); + correction = (knob - 500) * 500ULL; + recalibrate(correction); + //abort if this button is down + if (btnDown()){ + //re-enable the clock1 and clock 2 + break; + } + sprintf(c, "%3d ", knob); + printLine2(c); + } + + //save the setting + if (digitalRead(PTT) == LOW){ + printLine2("Calibration set!"); + EEPROM.put(MASTER_CAL, correction); + delay(2000); + } + else { + EEPROM.get(MASTER_CAL, correction); + } + + si5351.set_correction(correction); + si5351.set_pll(SI5351_PLL_FIXED, SI5351_PLLA); + si5351.set_pll(SI5351_PLL_FIXED, SI5351_PLLB); + + si5351.output_enable(SI5351_CLK0, 1); + si5351.output_enable(SI5351_CLK1, 1); + si5351.output_enable(SI5351_CLK2, 1); + + resetBasefrequency(); + setFrequency(frequency); + updateDisplay(); + menuOn = 0; +} + +void setBFO(int btn, byte isLSB){ + int knob = 0; + int32_t lsb_Freq; + + if (!btn){ + if (isLSB) + printLine2("Set LSB Carrier "); + else + printLine2("Set USB Carrier "); + return; + } + if (isLSB) + printLine1("Tune to best LSB"); + else + printLine1("Tune to best USB"); + printLine2("PTT to confirm. "); + delay(2000); + + //disable all clock 1 and clock 2 + while(digitalRead(PTT) == HIGH && !btnDown()){ + knob = analogRead(ANALOG_TUNING); + if (isLSB){ + lsbCarrier = 12000000l - knob * 10; + si5351.set_freq(lsbCarrier * 100ULL, SI5351_CLK0); + } + else { + usbCarrier = 12000000l - knob * 10; + si5351.set_freq(usbCarrier * 100ULL, SI5351_CLK0); + } + //abort if this button is down + if (btnDown()){ + break; + } + sprintf(c, "%3d ", knob); + printLine2(c); + } + //save the setting + if (digitalRead(PTT) == LOW){ + printLine2("Carrier set! "); + if (isLSB) + EEPROM.put(LSB_CAL, lsbCarrier); + else + EEPROM.put(USB_CAL, usbCarrier); + delay(2000); + } + else { + EEPROM.get(LSB_CAL, lsbCarrier); + EEPROM.get(USB_CAL, usbCarrier); + } + + resetBasefrequency(); + setFrequency(frequency); + updateDisplay(); + menuOn = 0; +} + +void resetBasefrequency(){ + int knob = analogRead(ANALOG_TUNING); + baseTune = frequency - (50l * knob); +} + +int exitMenu(int btn){ + + if (!btn){ + printLine2("Exit Menu? "); + } + else{ + menuOn = 0; + resetBasefrequency(); + } +} + +void doMenu(){ + int select, btnState; + menuOn = 1; + + while(menuOn){ + select = analogRead(ANALOG_TUNING); + //downscale the selection + select = (select-50)/50; + btnState = btnDown(); + delay(200); + switch(select){ + case 0: + ritToggle(btnState); + break; + case 1: + vfoToggle(btnState); + break; + case 2: + calibrateMaster(btnState); + break; + case 3: + setBFO(btnState, 1); + break; + case 4: + setBFO(btnState, 0); + break; + + default: + exitMenu(btnState); + break; + } + } +} + +void updateDisplay(){ + sprintf(b, "%8ld", frequency); + sprintf(c, "%s:%.2s.%.3s.%1s", vfoActive == VFO_A ? "A" : "B" , b, b+2, b+5); + if (isUSB) + strcat(c, " USB"); + else + strcat(c, " LSB"); + + if (inTx) + strcat(c, " TX"); + else if (ritOn) + strcat(c, " +R"); + else + strcat(c, " "); + + + printLine1(c); +/* sprintf(c, "%s %s %d", isLSB ? "LSB" : "USB", inTx ? " TX" : " RX", digitalRead(FBUTTON)); + printLine2(c); */ +} + +void setSidebandAndTXFilters(unsigned long freq){ + + if (freq > 10000000L){ + isUSB = 1; + si5351.set_freq(usbCarrier * 100ULL, SI5351_CLK0); + digitalWrite(TX_LPF_SEL, 1); + } + else{ + isUSB = 0; + digitalWrite(TX_LPF_SEL, 0); + si5351.set_freq(lsbCarrier * 100ULL, SI5351_CLK0); + } +} + +void setFrequency(unsigned long f){ + uint64_t osc_f; + + setSidebandAndTXFilters(f); + + if (isUSB) + si5351.set_freq((SECOND_OSC - usbCarrier + f) * 100ULL, SI5351_CLK2); + else + si5351.set_freq((SECOND_OSC - lsbCarrier + f) * 100ULL, SI5351_CLK2); + + frequency = f; +} + +void checkTX(){ + + //we don't check for ptt when transmitting cw + if (cwTimeout > 0) + return; + + if (digitalRead(PTT) == 0 && inTx == 0){ + inTx = 1; + digitalWrite(TX_RX, 1); + updateDisplay(); + } + + if (digitalRead(PTT) == 1 && inTx == 1){ + inTx = 0; + digitalWrite(TX_RX, 0); + updateDisplay(); + } +} + +/* +byte prevWasDot = 0; + +void keyer(){ + int key = analogRead(ANALOG_KEYER); + + if (key < 50) //straight key + keyDown(); + else if (key < 300) // both + if (prevWasDot){ + keyDown(dotPeriod * 3); + prevWasDot = 0; + } + else { + keyDown(dotPeriod); + prevWasDot = 1; + } + else if (key < 600){ //dash + keyDown(dotPeriod * 3); + prevWasDot = 0; + } + else if (key > 900){ //dot + keyUp(); + } +} +*/ + + +void checkCW2(){ + + if (keyDown == 0 && analogRead(ANALOG_KEYER) < 50){ + //switch to transmit mode if we are not already in it + if (inTx == 0){ + digitalWrite(TX_RX, 1); +// selectTransmitFilter(); + //give the relays a few ms to settle the T/R relays + delay(50); + } + inTx = 1; + keyDown = 1; + tone(CW_TONE, (int)sideTone); + updateDisplay(); + } + + //reset the timer as long as the key is down + if (keyDown == 1){ + cwTimeout = CW_TIMEOUT + millis(); + } + + //if we have a keyup + if (keyDown == 1 && analogRead(ANALOG_KEYER) > 150){ + keyDown = 0; + noTone(CW_TONE); + cwTimeout = millis() + CW_TIMEOUT; + } + + //if we are in cw-mode and have a keyuup for a longish time + if (cwTimeout > 0 && inTx == 1 && cwTimeout < millis()){ + //move the radio back to receive + digitalWrite(TX_RX, 0); + inTx = 0; + cwTimeout = 0; + updateDisplay(); + } +} + +void checkCW3(){ + + if (keyDown == 0 && analogRead(ANALOG_KEYER) < 50){ + //switch to transmit mode if we are not already in it + if (inTx == 0){ + + if (isUSB) + si5351.set_freq((frequency + sideTone) * 100ULL, SI5351_CLK2); + else + si5351.set_freq((frequency - sideTone) * 100ULL, SI5351_CLK2); + //switch off the second oscillator and the bfo + si5351.output_enable(SI5351_CLK0, 0); + si5351.output_enable(SI5351_CLK1, 0); + si5351.output_enable(SI5351_CLK2, 1); + + digitalWrite(TX_RX, 1); +// selectTransmitFilter(); + //give the relays a few ms to settle the T/R relays + delay(50); + } + inTx = 1; + keyDown = 1; + tone(CW_TONE, (int)sideTone); + digitalWrite(CW_KEY, 1); + + updateDisplay(); + } + + //reset the timer as long as the key is down + if (keyDown == 1){ + cwTimeout = CW_TIMEOUT + millis(); + } + + //if we have a keyup + if (keyDown == 1 && analogRead(ANALOG_KEYER) > 150){ + keyDown = 0; + noTone(CW_TONE); + digitalWrite(CW_KEY, 0); + cwTimeout = millis() + CW_TIMEOUT; + } + + //if we are in cw-mode and have a keyuup for a longish time + if (cwTimeout > 0 && inTx == 1 && cwTimeout < millis()){ + //move the radio back to receive + digitalWrite(TX_RX, 0); + inTx = 0; + cwTimeout = 0; + updateDisplay(); + + //switch off the second oscillator and the bfo + si5351.output_enable(SI5351_CLK0, 1); + si5351.output_enable(SI5351_CLK1, 1); + si5351.output_enable(SI5351_CLK2, 1); + setFrequency(frequency); + } +} + + +void checkCW(){ + + if (keyDown == 0 && analogRead(ANALOG_KEYER) < 50){ + //switch to transmit mode if we are not already in it + if (inTx == 0){ + digitalWrite(TX_RX, 1); + delay(50); + inTx = 1; + keyDown = 1; + } + if (isUSB) + si5351.set_freq((frequency + sideTone) * 100ULL, SI5351_CLK2); + else + si5351.set_freq((frequency - sideTone) * 100ULL, SI5351_CLK2); + //switch off the second oscillator and the bfo + si5351.output_enable(SI5351_CLK0, 0); + si5351.output_enable(SI5351_CLK1, 0); + si5351.output_enable(SI5351_CLK2, 1); + + digitalWrite(CW_KEY, 1); + tone(CW_TONE, sideTone); + updateDisplay(); + } + + //reset the timer as long as the key is down + if (keyDown == 1){ + cwTimeout = CW_TIMEOUT + millis(); + } + + //if we have a keyup + if (keyDown == 1 && analogRead(ANALOG_KEYER) > 150){ + keyDown = 0; + noTone(CW_TONE); + digitalWrite(CW_KEY, 0); + cwTimeout = millis() + CW_TIMEOUT; + } + + //if we are in cw-mode and have a keyuup for a longish time + if (cwTimeout > 0 && inTx == 1 && cwTimeout < millis()){ + //move the radio back to receive + digitalWrite(TX_RX, 0); + inTx = 0; + cwTimeout = 0; + //switch off the second oscillator and the bfo + si5351.output_enable(SI5351_CLK0, 1); + si5351.output_enable(SI5351_CLK1, 1); + si5351.output_enable(SI5351_CLK2, 1); + setFrequency(frequency); + updateDisplay(); + } +} + +int btnDown(){ + if (digitalRead(FBUTTON) == HIGH) + return 0; + else + return 1; +} + +void checkButton(){ + int i, t1, t2, knob, new_knob, duration; + + //only if the button is pressed + if (!btnDown()) + return; + + //wait for 50 ms before declaring the button to be really down + delay(50); + if (!btnDown()) + return; + + t1 = millis(); + knob = analogRead(ANALOG_TUNING); + duration = 0; + + /* keep measuring how long the duration of btn down has been to a max of 3 seconds */ + while (btnDown() && duration < 3000){ + /* if the tuning knob is moved while the btn is down, + then track the bandset until the button is up and return + */ + new_knob = analogRead(ANALOG_TUNING); + if (abs(new_knob - knob) > 10){ + int count = 0; + /* track the tuning and return */ + while (btnDown()){ + frequency = baseTune = ((analogRead(ANALOG_TUNING) * 30000l) + 1000000l); + setFrequency(frequency); + updateDisplay(); + count++; + delay(200); + } + delay(1000); + return; + } /* end of handling the bandset */ + + delay(100); + duration += 100; + } + + if (duration < 1000) { + printLine2("Menu."); + doMenu(); + } +} + +void doTuning(){ + unsigned long newFreq; + + int knob = analogRead(ANALOG_TUNING); + unsigned long old_freq = frequency; + + if (knob < 10 && frequency > LOWEST_FREQ) { + baseTune = baseTune - 1000l; + frequency = baseTune; + updateDisplay(); + setFrequency(frequency); + delay(50); + } + else if (knob > 1010 && frequency < HIGHEST_FREQ) { + baseTune = baseTune + 1000l; + frequency = baseTune + 50000l; + setFrequency(frequency); + updateDisplay(); + delay(50); + } + // in the middle, it is business as usual + else if (knob != old_knob){ + frequency = baseTune + (50l * knob); + old_knob = knob; + setFrequency(frequency); + updateDisplay(); + } +} + + +void setup() +{ + int32_t cal; + + lcd.begin(16, 2); + setupSmeter(); + printBuff[0] = 0; + printLine1("HFuino v0.01 "); + printLine2(" "); + + EEPROM.get(MASTER_CAL, cal); + EEPROM.get(LSB_CAL, lsbCarrier); + EEPROM.get(USB_CAL, usbCarrier); + //set the lsb and usb to defaults + if (lsbCarrier == 0) + lsbCarrier = INIT_LSB_FREQ; + if (usbCarrier == 0) + usbCarrier = INIT_USB_FREQ; + + // Start serial and initialize the Si5351 + Serial.begin(9600); + analogReference(DEFAULT); + Serial.println("*HFuino v0.01\n"); + Serial.println("*Searching Si5351\n"); + + //configure the function button to use the external pull-up + pinMode(FBUTTON, INPUT); + digitalWrite(FBUTTON, HIGH); + + pinMode(PTT, INPUT); + digitalWrite(PTT, HIGH); + + digitalWrite(ANALOG_KEYER, HIGH); + + pinMode(CW_TONE, OUTPUT); + digitalWrite(CW_TONE, 0); + pinMode(TX_RX,OUTPUT); + digitalWrite(TX_RX, 0); + pinMode(TX_LPF_SEL, OUTPUT); + digitalWrite(TX_LPF_SEL, 0); + pinMode(CW_KEY, OUTPUT); + digitalWrite(CW_KEY, 0); + EEPROM.get(0,cal); + + si5351.init(SI5351_CRYSTAL_LOAD_8PF,25000000l,0); + + si5351.set_correction(cal); + si5351.set_pll(SI5351_PLL_FIXED, SI5351_PLLA); + si5351.set_pll(SI5351_PLL_FIXED, SI5351_PLLB); + + si5351.output_enable(SI5351_CLK0, 1); + si5351.output_enable(SI5351_CLK1, 1); + si5351.output_enable(SI5351_CLK2, 1); + // printLine1("check freq "); + // si5351.set_freq(1000000000l, SI5351_CLK2); + + // delay(20000); + + si5351.set_freq(lsbCarrier * 100ULL, SI5351_CLK0); + si5351.set_freq(SECOND_OSC * 100ULL, SI5351_CLK1); + si5351.set_freq(5900000000l, SI5351_CLK2); + printLine2(b); + delay(2000); + + // Set CLK0 to output 7 MHz with a fixed PLL frequency + + si5351.drive_strength(SI5351_CLK0, SI5351_DRIVE_8MA); + si5351.drive_strength(SI5351_CLK1, SI5351_DRIVE_8MA); + si5351.drive_strength(SI5351_CLK2, SI5351_DRIVE_8MA); + + + Serial.println("*Si5350 ON"); + delay(10); +} + +void loop(){ + + //generateCW(10000); + //the order of testing first for cw and then for ptt is important. + checkCW3(); + checkTX(); + checkButton(); + //tune only when not tranmsitting + if (!inTx) + doTuning(); + updateMeter(); + delay(50); +} + From e481ea2a2457fc4e7be7a6a4bc9a0fba12bf2cde Mon Sep 17 00:00:00 2001 From: Ashhar Farhan Date: Thu, 7 Dec 2017 10:18:43 +0530 Subject: [PATCH 003/173] The ubitx production sktech, wireup and circuit This is the snap of the circuit, wiring instructions for the ubitx pcb. the sketch may change slightly for factory alignment but the rest will remain the same. --- ubitx_20/ubitx_20.ino | 558 ++++++++++++++++++++++++++ ubitx_20/ubitx_cat.ino | 231 +++++++++++ ubitx_20/ubitx_factory_alignment.ino | 87 ++++ ubitx_20/ubitx_keyer.ino | 155 ++++++++ ubitx_20/ubitx_menu.ino | 572 +++++++++++++++++++++++++++ ubitx_20/ubitx_si5351.ino | 116 ++++++ ubitx_20/ubitx_ui.ino | 230 +++++++++++ ubitx_wiring.png | Bin 0 -> 72490 bytes ubitxv3.pdf | Bin 0 -> 246788 bytes 9 files changed, 1949 insertions(+) create mode 100644 ubitx_20/ubitx_20.ino create mode 100644 ubitx_20/ubitx_cat.ino create mode 100644 ubitx_20/ubitx_factory_alignment.ino create mode 100644 ubitx_20/ubitx_keyer.ino create mode 100644 ubitx_20/ubitx_menu.ino create mode 100644 ubitx_20/ubitx_si5351.ino create mode 100644 ubitx_20/ubitx_ui.ino create mode 100644 ubitx_wiring.png create mode 100644 ubitxv3.pdf diff --git a/ubitx_20/ubitx_20.ino b/ubitx_20/ubitx_20.ino new file mode 100644 index 0000000..c6aab85 --- /dev/null +++ b/ubitx_20/ubitx_20.ino @@ -0,0 +1,558 @@ +/** + * 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 + +/** + 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. + + We no longer use the standard SI5351 library because of its huge overhead due to many unused + features consuming a lot of program space. Instead of depending on an external library we now use + Jerry Gaffke's, KE7ER, lightweight standalone mimimalist "si5351bx" routines (see further down the + code). Here are some defines and declarations used by Jerry's routines: +*/ + + +/** + * 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 + * 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) + +/** + * The Raduino board is the size of a standard 16x2 LCD panel. It has three connectors: + * + * First, is an 8 pin connector that provides +5v, GND and six analog input pins that can also be + * configured to be used as digital input or output pins. These are referred to as A0,A1,A2, + * A3,A6 and A7 pins. The A4 and A5 pins are missing from this connector as they are used to + * talk to the Si5351 over I2C protocol. + * + * Second is a 16 pin LCD connector. This connector is meant specifically for the standard 16x2 + * LCD display in 4 bit mode. The 4 bit mode requires 4 data lines and two control lines to work: + * Lines used are : RESET, ENABLE, D4, D5, D6, D7 + * We include the library and declare the configuration of the LCD panel too + */ + +#include +LiquidCrystal lcd(8,9,10,11,12,13); + +/** + * The Arduino, unlike C/C++ on a regular computer with gigabytes of RAM, has very little memory. + * We have to be very careful with variables that are declared inside the functions as they are + * created in a memory region called the stack. The stack has just a few bytes of space on the Arduino + * if you declare large strings inside functions, they can easily exceed the capacity of the stack + * and mess up your programs. + * We circumvent this by declaring a few global buffers as kitchen counters where we can + * slice and dice our strings. These strings are mostly used to control the display or handle + * the input and output from the USB port. We must keep a count of the bytes used while reading + * the serial port as we can easily run out of buffer space. This is done in the serial_in_count variable. + */ +char c[30], b[30]; +char printBuff[2][17]; //mirrors what is showing on the two lines of the display +int count = 0; //to generally count ticks, loops, etc + +/** + * The second set of 16 pins on the Raduino's bottom connector are have the three clock outputs and the digital lines to control the rig. + * This assignment is as follows : + * Pin 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 + * GND +5V CLK0 GND GND CLK1 GND GND CLK2 GND D2 D3 D4 D5 D6 D7 + * These too are flexible with what you may do with them, for the Raduino, we use them to : + * - TX_RX line : Switches between Transmit and Receive after sensing the PTT or the morse keyer + * - CW_KEY line : turns on the carrier for CW + */ + +#define TX_RX (7) +#define CW_TONE (6) +#define TX_LPF_A (5) +#define TX_LPF_B (4) +#define TX_LPF_C (3) +#define CW_KEY (2) + +/** + * These are the indices where these user changable settinngs are stored in the EEPROM + */ +#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 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 +#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 +#define INIT_USB_FREQ (11996500l) +// limits the tuning and working range of the ubitx between 3 MHz and 30 MHz +#define LOWEST_FREQ (3000000l) +#define HIGHEST_FREQ (30000000l) + +//we directly generate the CW by programmin the Si5351 to the cw tx frequency, hence, both are different modes +//these are the parameter passed to startTx +#define TX_SSB 0 +#define TX_CW 1 + +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; +unsigned long frequency, ritRxFrequency, ritTxFrequency; //frequency is the current frequency on the dial + +int cwSpeed = 100; //this is actuall the dot period in milliseconds +extern int32_t calibration; + +/** + * 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, (NOT IMPLEMENTED YET) +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 + //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 +/** + * Below are the basic functions that control the uBitx. Understanding the functions before + * you start hacking around + */ + +/** + * Select the properly tx harmonic filters + * The four harmonic filters use only three relays + * the four LPFs cover 30-21 Mhz, 18 - 14 Mhz, 7-10 MHz and 3.5 to 5 Mhz + * Briefly, it works like this, + * - When KT1 is OFF, the 'off' position routes the PA output through the 30 MHz LPF + * - When KT1 is ON, it routes the PA output to KT2. Which is why you will see that + * the KT1 is on for the three other cases. + * - When the KT1 is ON and KT2 is off, the off position of KT2 routes the PA output + * to 18 MHz LPF (That also works for 14 Mhz) + * - When KT1 is On, KT2 is On, it routes the PA output to KT3 + * - KT3, when switched on selects the 7-10 Mhz filter + * - KT3 when switched off selects the 3.5-5 Mhz filter + * See the circuit to understand this + */ + +void setTXFilters(unsigned long freq){ + + if (freq > 21000000L){ // the default filter is with 35 MHz cut-off + digitalWrite(TX_LPF_A, 0); + digitalWrite(TX_LPF_B, 0); + digitalWrite(TX_LPF_C, 0); + } + else if (freq >= 14000000L){ //thrown the KT1 relay on, the 30 MHz LPF is bypassed and the 14-18 MHz LPF is allowd to go through + digitalWrite(TX_LPF_A, 1); + digitalWrite(TX_LPF_B, 0); + digitalWrite(TX_LPF_C, 0); + } + else if (freq > 7000000L){ + digitalWrite(TX_LPF_A, 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); + } +} + +/** + * 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){ + uint64_t osc_f; + + setTXFilters(f); + + if (isUSB){ + si5351bx_setfreq(2, SECOND_OSC_USB - usbCarrier + f); + si5351bx_setfreq(1, SECOND_OSC_USB); + } + else{ + si5351bx_setfreq(2, SECOND_OSC_LSB + usbCarrier + f); + si5351bx_setfreq(1, SECOND_OSC_LSB); + } + + 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){ + unsigned long tx_freq = 0; + digitalWrite(TX_RX, 1); + inTx = 1; + + if (ritOn){ + //save the current as the rx frequency + ritRxFrequency = frequency; + setFrequency(ritTxFrequency); + } + + 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 (isUSB) + si5351bx_setfreq(2, frequency + sideTone); + else + si5351bx_setfreq(2, frequency - sideTone); + } + updateDisplay(); +} + +void stopTx(){ + inTx = 0; + + digitalWrite(TX_RX, 0); //turn off the tx + si5351bx_setfreq(0, usbCarrier); //set back the carrier oscillator anyway, cw tx switches it off + + if (ritOn) + setFrequency(ritRxFrequency); + else + setFrequency(frequency); + + 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(){ + //we don't check for ptt when transmitting cw + if (cwTimeout > 0) + return; + + if (digitalRead(PTT) == 0 && inTx == 0){ + startTx(TX_SSB); + delay(50); //debounce the PTT + } + + if (digitalRead(PTT) == 1 && inTx == 1) + stopTx(); +} + +void checkButton(){ + int i, t1, t2, knob, new_knob; + + //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); + delay(50);//debounce +} + + +/** + * The tuning jumps by 50 Hz on each step when you tune slowly + * As you spin the encoder faster, the jump size also increases + * This way, you can quickly move to another band by just spinning the + * tuning knob + */ + +void doTuning(){ + int s; + unsigned long prev_freq; + + s = enc_read(); + if (s){ + prev_freq = frequency; + + if (s > 10) + frequency += 200000l; + if (s > 7) + frequency += 10000l; + else if (s > 4) + frequency += 1000l; + else if (s > 2) + frequency += 500; + else if (s > 0) + frequency += 50l; + else if (s > -2) + frequency -= 50l; + else if (s > -4) + frequency -= 500l; + else if (s > -7) + frequency -= 1000l; + else if (s > -9) + frequency -= 10000l; + else + frequency -= 200000l; + + 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(){ + unsigned long newFreq; + + int knob = enc_read(); + unsigned long old_freq = frequency; + + if (knob < 0) + frequency -= 100l; + else if (knob > 0) + frequency += 100; + + if (old_freq != frequency){ + setFrequency(frequency); + updateDisplay(); + } +} + +/** + * The settings are read from EEPROM. The first time around, the values may not be + * present or out of range, in this case, some intelligent defaults are copied into the + * variables. + */ +void initSettings(){ + //read the settings from the eeprom and restore them + //if the readings are off, then set defaults + 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); + if (usbCarrier > 12010000l || usbCarrier < 11990000l) + usbCarrier = 11997000l; + if (vfoA > 35000000l || 3500000l > vfoA) + vfoA = 7150000l; + if (vfoB > 35000000l || 3500000l > vfoB) + vfoB = 14150000l; + if (sideTone < 100 || 2000 < sideTone) + sideTone = 800; + if (cwSpeed < 10 || 1000 < cwSpeed) + cwSpeed = 100; + +} + +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(CW_TONE, OUTPUT); + digitalWrite(CW_TONE, 0); + + pinMode(TX_RX,OUTPUT); + digitalWrite(TX_RX, 0); + + pinMode(TX_LPF_A, OUTPUT); + pinMode(TX_LPF_B, OUTPUT); + pinMode(TX_LPF_C, OUTPUT); + digitalWrite(TX_LPF_A, 0); + digitalWrite(TX_LPF_B, 0); + digitalWrite(TX_LPF_C, 0); + + pinMode(CW_KEY, OUTPUT); + digitalWrite(CW_KEY, 0); +} + +void setup() +{ + Serial.begin(9600); + + lcd.begin(16, 2); + + //we print this line so this shows up even if the raduino + //crashes later in the code + printLine1("uBITX v0.20"); + delay(500); + + initMeter(); //not used in this build + initSettings(); + initPorts(); + initOscillators(); + + frequency = vfoA; + setFrequency(vfoA); + updateDisplay(); + + if (btnDown()) + factory_alignment(); +} + + +/** + * The loop checks for keydown, ptt, function button and tuning. + */ + +byte flasher = 0; +void loop(){ + + cwKeyer(); + if (!txCAT) + checkPTT(); + checkButton(); + + //tune only when not tranmsitting + if (!inTx){ + if (ritOn) + doRIT(); + else + doTuning(); + } + + //we check CAT after the encoder as it might put the radio into TX + checkCAT(); +} diff --git a/ubitx_20/ubitx_cat.ino b/ubitx_20/ubitx_cat.ino new file mode 100644 index 0000000..687595c --- /dev/null +++ b/ubitx_20/ubitx_cat.ino @@ -0,0 +1,231 @@ +/** + * The CAT protocol is used by many radios to provide remote control to comptuers through + * the serial port. + * + * This is very much a work in progress. Parts of this code have been liberally + * borrowed from other GPLicensed works like hamlib. + * + * WARNING : This is an unstable version and it has worked with fldigi, + * it gives time out error with WSJTX 1.8.0 + */ + +// The next 4 functions are needed to implement the CAT protocol, which +// uses 4-bit BCD formatting. +// +byte setHighNibble(byte b,byte v) { + // Clear the high nibble + b &= 0x0f; + // Set the high nibble + return b | ((v & 0x0f) << 4); +} + +byte setLowNibble(byte b,byte v) { + // Clear the low nibble + b &= 0xf0; + // Set the low nibble + return b | (v & 0x0f); +} + +byte getHighNibble(byte b) { + return (b >> 4) & 0x0f; +} + +byte getLowNibble(byte b) { + return b & 0x0f; +} + +// Takes a number and produces the requested number of decimal digits, staring +// from the least significant digit. +// +void getDecimalDigits(unsigned long number,byte* result,int digits) { + for (int i = 0; i < digits; i++) { + // "Mask off" (in a decimal sense) the LSD and return it + result[i] = number % 10; + // "Shift right" (in a decimal sense) + number /= 10; + } +} + +// Takes a frequency and writes it into the CAT command buffer in BCD form. +// +void writeFreq(unsigned long freq,byte* cmd) { + // Convert the frequency to a set of decimal digits. We are taking 9 digits + // so that we can get up to 999 MHz. But the protocol doesn't care about the + // LSD (1's place), so we ignore that digit. + byte digits[9]; + getDecimalDigits(freq,digits,9); + // Start from the LSB and get each nibble + cmd[3] = setLowNibble(cmd[3],digits[1]); + cmd[3] = setHighNibble(cmd[3],digits[2]); + cmd[2] = setLowNibble(cmd[2],digits[3]); + cmd[2] = setHighNibble(cmd[2],digits[4]); + cmd[1] = setLowNibble(cmd[1],digits[5]); + cmd[1] = setHighNibble(cmd[1],digits[6]); + cmd[0] = setLowNibble(cmd[0],digits[7]); + cmd[0] = setHighNibble(cmd[0],digits[8]); +} + +// This function takes a frquency that is encoded using 4 bytes of BCD +// representation and turns it into an long measured in Hz. +// +// [12][34][56][78] = 123.45678? Mhz +// +unsigned long readFreq(byte* cmd) { + // Pull off each of the digits + byte d7 = getHighNibble(cmd[0]); + byte d6 = getLowNibble(cmd[0]); + byte d5 = getHighNibble(cmd[1]); + byte d4 = getLowNibble(cmd[1]); + byte d3 = getHighNibble(cmd[2]); + byte d2 = getLowNibble(cmd[2]); + byte d1 = getHighNibble(cmd[3]); + byte d0 = getLowNibble(cmd[3]); + return + (unsigned long)d7 * 100000000L + + (unsigned long)d6 * 10000000L + + (unsigned long)d5 * 1000000L + + (unsigned long)d4 * 100000L + + (unsigned long)d3 * 10000L + + (unsigned long)d2 * 1000L + + (unsigned long)d1 * 100L + + (unsigned long)d0 * 10L; +} + +/** + * Responds to all the cat commands, emulates FT-817 + */ + +void processCATCommand(byte* cmd) { + byte response[5]; + + // Debugging code, enable it to fix the cat implementation + + count++; + if (cmd[4] == 0x00){ + response[0]=0; + Serial.write(response, 1); + } + else if (cmd[4] == 0x01) { + unsigned long f = readFreq(cmd); + setFrequency(f); + updateDisplay(); + //sprintf(b, "set:%ld", f); + //printLine2(b); + + } + // Get frequency + else if (cmd[4] == 0x03){ + writeFreq(frequency,response); // Put the frequency into the buffer + if (isUSB) + response[4] = 0x01; //USB + else + response[4] = 0x00; //LSB + Serial.write(response,5); + printLine2("cat:getfreq"); + } + else if (cmd[4] == 0x07){ // set mode + if (cmd[0] == 0x00 || cmd[0] == 0x03) + isUSB = 0; + else + isUSB = 1; + response[0] = 0x00; + Serial.write(response, 1); + setFrequency(frequency); + //printLine2("cat: mode changed"); + //updateDisplay(); + } + else if (cmd[4] == 0x88){ + if (inTx){ + stopTx(); + txCAT = false; + } + else + response[0] = 0xf0; + printLine2("tx > rx"); + Serial.write(response,1); + } + else if (cmd[4] == 0x08) { // PTT On + if (!inTx) { + response[0] = 0; + txCAT = true; + startTx(TX_SSB); + updateDisplay(); + } else { + response[0] = 0xf0; + } + Serial.write(response,1); + printLine2("rx > tx"); + } + // Read TX keyed state + else if (cmd[4] == 0x10) { + if (!inTx) { + response[0] = 0; + } else { + response[0] = 0xf0; + } + Serial.write(response,1); + printLine2("cat;0x10"); + } + // PTT Off + else if (cmd[4] == 0x88) { + byte resBuf[0]; + if (inTx) { + response[0] = 0; + } else { + response[0] = 0xf0; + } + Serial.write(response,1); + printLine2("cat;0x88"); + //keyed = false; + //digitalWrite(13,LOW); + } + // Read receiver status + else if (cmd[4] == 0xe7) { + response[0] = 0x09; + Serial.write(response,1); + printLine2("cat;0xe7"); + } + else if (cmd[4] == 0xf5){ + + } + // Read receiver status + else if (cmd[4] == 0xf7) { + response[0] = 0x00; + if (inTx) { + response[0] = response[0] | 0xf0; + } + Serial.write(response,1); + printLine2("cat;0xf7"); + } + else { + //somehow, get this to print the four bytes + ultoa(*((unsigned long *)cmd), c, 16); + itoa(cmd[4], b, 16); + strcat(b, ":"); + strcat(b, c); + printLine2(b); + response[0] = 0x00; + Serial.write(response[0]); + } + +} + + + +void checkCAT(){ + static byte cat[5]; + byte i; + + if (Serial.available() < 5) + return; + + cat[4] = cat[3]; + cat[3] = cat[2]; + cat[2] = cat[0]; + for (i = 0; i < 5; i++) + cat[i] = Serial.read(); + + processCATCommand(cat); +} + + diff --git a/ubitx_20/ubitx_factory_alignment.ino b/ubitx_20/ubitx_factory_alignment.ino new file mode 100644 index 0000000..77c5e16 --- /dev/null +++ b/ubitx_20/ubitx_factory_alignment.ino @@ -0,0 +1,87 @@ + +/** + * 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); + + 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); + + usbCarrier = 11994999l; + menuSetupCarrier(1); + + if (usbCarrier == 11994999l){ + printLine2("Setup Aborted"); + return; + } + + + printLine2("#3:Test 3.5MHz"); + 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"); + + 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); + + 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..3a9c86f --- /dev/null +++ b/ubitx_20/ubitx_keyer.ino @@ -0,0 +1,155 @@ +/** + * CW Keyer + * + * 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) +#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(){ + 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); + cwTimeout = millis() + CW_TIMEOUT; +} + +/** + * 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); + cwTimeout = millis() + CW_TIMEOUT; +} + +/** + * 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){ + if (0 < cwTimeout && cwTimeout < millis()){ + cwTimeout = 0; + keyDown = 0; + stopTx(); + } + + if (!cwTimeout) + return; + + //if a paddle was used (not a straight key) we should extend the space to be a full dash + //by adding two more dots long space (one has already been added at the end of the dot or dash) + if (cwTimeout > 0 && lastPaddle != PADDLE_STRAIGHT) + delay(cwSpeed * 2); + + // got back to the begining of the loop, if no further activity happens on the paddle or the straight key + // we will time out, and return out of this routine + delay(5); + continue; + } + + 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; + cwTimeout = millis() + CW_TIMEOUT; + startTx(TX_CW); + updateDisplay(); + } + + // star the transmission) + // we store the transmitted character in the lastPaddle + cwKeydown(); + if (paddle == PADDLE_DOT){ + delay(cwSpeed); + lastPaddle = PADDLE_DOT; + } + else if (paddle == PADDLE_DASH){ + delay(cwSpeed * 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); + lastPaddle = PADDLE_DASH; + }else{ + delay(cwSpeed); + lastPaddle = PADDLE_DOT; + } + } + else if (paddle == PADDLE_STRAIGHT){ + while (getPaddle() == PADDLE_STRAIGHT) + delay(1); + 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_menu.ino b/ubitx_20/ubitx_menu.ino new file mode 100644 index 0000000..fff0378 --- /dev/null +++ b/ubitx_20/ubitx_menu.ino @@ -0,0 +1,572 @@ +/** 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 + */ + + + +int menuBand(int btn){ + int knob = 0; + int band; + unsigned long offset; + + // band = frequency/1000000l; + // offset = frequency % 1000000l; + + if (!btn){ + printLine2("Band Select?"); + return; + } + + printLine2("Press to confirm"); + //wait for the button menu select button to be lifted) + while (btnDown()) + delay(50); + delay(50); + ritDisable(); + + while(!btnDown()){ + + knob = enc_read(); + if (knob != 0){ + /* + if (band > 3 && knob < 0) + band--; + if (band < 30 && knob > 0) + band++; + if (band > 10) + isUSB = true; + else + isUSB = false; + setFrequency(((unsigned long)band * 1000000l) + offset); */ + 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(20); + } + + while(btnDown()) + delay(50); + delay(50); + + printLine2(""); + updateDisplay(); + menuOn = 0; +} + +void menuVfoToggle(int btn){ + + if (!btn){ + if (vfoActive == VFO_A) + printLine2("Select VFO B? "); + else + printLine2("Select VFO A? "); + } + else { + if (vfoActive == VFO_B){ + vfoB = frequency; + EEPROM.put(VFO_B, frequency); + vfoActive = VFO_A; + printLine2("Selected VFO A "); + frequency = vfoA; + } + else { + vfoA = frequency; + EEPROM.put(VFO_A, frequency); + vfoActive = VFO_B; + printLine2("Selected VFO B "); + frequency = vfoB; + } + + ritDisable(); + setFrequency(frequency); + if (frequency >= 10000000l) + isUSB = true; + else + isUSB = false; + updateDisplay(); + printLine2(""); + delay(1000); + //exit the menu + menuOn = 0; + } +} + +void menuRitToggle(int btn){ + if (!btn){ + if (ritOn == 1) + printLine2("RIT:On, Off? "); + else + printLine2("RIT:Off, On? "); + } + else { + if (ritOn == 0){ + printLine2("RIT is ON"); + //enable RIT so the current frequency is used at transmit + ritEnable(frequency); + } + else{ + printLine2("RIT is OFF"); + ritDisable(); + } + menuOn = 0; + delay(500); + printLine2(""); + updateDisplay(); + } +} + +void menuSidebandToggle(int btn){ + if (!btn){ + if (isUSB == true) + printLine2("Select LSB?"); + else + printLine2("Select USB?"); + } + else { + if (isUSB == true){ + isUSB = false; + printLine2("LSB Selected"); + delay(500); + printLine2(""); + } + else { + isUSB = true; + printLine2("USB Selected"); + delay(500); + printLine2(""); + } + + updateDisplay(); + menuOn = 0; + } +} + +/** + * 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) + printLine2("Setup On?"); + else + printLine2("Setup Off?"); + }else { + if (!modeCalibrate){ + modeCalibrate = true; + printLine2("Setup:On "); + } + else { + modeCalibrate = false; + printLine2("Setup:Off "); + } + delay(2000); + printLine2(""); + menuOn = 0; + } +} + +void menuExit(int btn){ + + if (!btn){ + printLine2("Exit Menu? "); + } + else{ + printLine2("Exiting menu"); + delay(300); + printLine2(""); + updateDisplay(); + menuOn = 0; + } +} + +int menuCWSpeed(int btn){ + int knob = 0; + int wpm; + + wpm = 1200/cwSpeed; + + if (!btn){ + strcpy(b, "CW:"); + itoa(wpm,c, 10); + strcat(b, c); + strcat(b, "WPM Change?"); + printLine2(b); + return; + } + + printLine1("Press PTT to set"); + strcpy(b, "WPM:"); + itoa(wpm,c, 10); + strcat(b, c); + printLine2(b); + delay(300); + + while(!btnDown() && digitalRead(PTT) == HIGH){ + + 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); + } + //abort if this button is down + if (btnDown()) + //re-enable the clock1 and clock 2 + break; + } + + //save the setting + if (digitalRead(PTT) == LOW){ + printLine2("CW Speed set!"); + cwSpeed = 1200/wpm; + EEPROM.put(CW_SPEED, cwSpeed); + delay(2000); + } + printLine2(""); + menuOn = 0; +} + + + +/** + * 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; + +int factoryCalibration(int btn){ + int knob = 0; + int32_t prev_calibration; + + + //keep clear of any previous button press + while (btnDown()) + delay(100); + delay(100); + + if (!btn){ + printLine2("Set Calibration?"); + return 0; + } + + prev_calibration = calibration; + calibration = 0; + + isUSB = true; + + //turn off the second local oscillator and the bfo + si5351_set_calibration(calibration); + startTx(TX_CW); + 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(); + + printLine2("Calibration set!"); + EEPROM.put(MASTER_CAL, calibration); + initOscillators(); + setFrequency(frequency); + updateDisplay(); + + while(btnDown()) + delay(50); + delay(100); +} + +int menuSetupCalibration(int btn){ + int knob = 0; + int32_t prev_calibration; + + if (!btn){ + printLine2("Set Calibration?"); + return 0; + } + + printLine1("Set to Zero-beat,"); + printLine2("press PTT to save"); + delay(1000); + + 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){ + printLine1("Calibration set!"); + printLine2("Set Carrier now"); + EEPROM.put(MASTER_CAL, calibration); + delay(2000); + } + else + calibration = prev_calibration; + + printLine2(""); + initOscillators(); + //si5351_set_calibration(calibration); + setFrequency(frequency); + updateDisplay(); + menuOn = 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], 1); + printLine2(c); +} + +void menuSetupCarrier(int btn){ + int knob = 0; + unsigned long prevCarrier; + + if (!btn){ + printLine2("Set the BFO"); + return; + } + + prevCarrier = usbCarrier; + printLine1("Tune to best Signal"); + printLine2("PTT to confirm. "); + delay(1000); + + usbCarrier = 11995000l; + 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 -= 50; + else if (knob < 0) + usbCarrier += 50; + else + continue; //don't update the frequency or the display + + si5351bx_setfreq(0, usbCarrier); + printCarrierFreq(usbCarrier); + + delay(100); + } + + //save the setting + if (digitalRead(PTT) == LOW){ + printLine2("Carrier set! "); + EEPROM.put(USB_CAL, usbCarrier); + delay(1000); + } + else + usbCarrier = prevCarrier; + + si5351bx_setfreq(0, usbCarrier); + setFrequency(frequency); + updateDisplay(); + printLine2(""); + menuOn = 0; +} + +void menuSetupCwTone(int btn){ + int knob = 0; + int prev_sideTone; + + if (!btn){ + printLine2("Change CW Tone"); + return; + } + + prev_sideTone = sideTone; + printLine1("Tune CW tone"); + printLine2("PTT to confirm. "); + delay(1000); + tone(CW_TONE, sideTone); + + //disable all clock 1 and clock 2 + while (digitalRead(PTT) == LOW || !btnDown()) + { + knob = enc_read(); + + if (knob > 0 && sideTone < 2000) + sideTone += 10; + else if (knob < 0 && sideTone > 100 ) + sideTone -= 10; + else + continue; //don't update the frequency or the display + + tone(CW_TONE, sideTone); + itoa(sideTone, b, 10); + printLine2(b); + + delay(100); + } + noTone(CW_TONE); + //save the setting + if (digitalRead(PTT) == LOW){ + printLine2("Sidetone set! "); + EEPROM.put(CW_SIDETONE, usbCarrier); + delay(2000); + } + else + sideTone = prev_sideTone; + + printLine2(""); + updateDisplay(); + menuOn = 0; + } + +void doMenu(){ + int select=0, i,btnState; + + //wait for the button to be raised up + while(btnDown()) + delay(50); + delay(50); //debounce + + menuOn = 2; + + while (menuOn){ + i = enc_read(); + btnState = btnDown(); + + if (i > 0){ + if (modeCalibrate && select + i < 110) + select += i; + if (!modeCalibrate && select + i < 70) + select += i; + } + if (i < 0 && select - i >= 0) + select += i; //caught ya, i is already -ve here, so you add it + + if (select < 10) + menuBand(btnState); + else if (select < 20) + menuRitToggle(btnState); + else if (select < 30) + menuVfoToggle(btnState); + else if (select < 40) + menuSidebandToggle(btnState); + else if (select < 50) + menuCWSpeed(btnState); + else if (select < 60) + menuSetup(btnState); + else if (select < 70 && !modeCalibrate) + menuExit(btnState); + else if (select < 80 && modeCalibrate) + menuSetupCalibration(btnState); //crystal + else if (select < 90 && modeCalibrate) + menuSetupCarrier(btnState); //lsb + else if (select < 100 && modeCalibrate) + menuSetupCwTone(btnState); + else if (select < 110 && modeCalibrate) + menuExit(btnState); + } + + //debounce the button + while(btnDown()) + delay(50); + delay(50); +} + diff --git a/ubitx_20/ubitx_si5351.ino b/ubitx_20/ubitx_si5351.ino new file mode 100644 index 0000000..a5d3ed4 --- /dev/null +++ b/ubitx_20/ubitx_si5351.ino @@ -0,0 +1,116 @@ +// ************* 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) +#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) +uint8_t si5351bx_drive[3] = {1, 1, 1}; // 0=2ma 1=4ma 2=6ma 3=8ma for CLK 0,1,2 +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(); +} + + +void si5351bx_init() { // Call once at power-up, start PLLA + uint8_t reg; 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}; + i2cWriten(26, vals, 8); // Write to 8 PLLA msynth regs + i2cWrite(177, 0x20); // Reset PLLA (0x80 resets PLLB) + // for (reg=16; reg<=23; reg++) i2cWrite(reg, 0x80); // Powerdown CLK's + // i2cWrite(187, 0); // No fannout of clkin, xtal, ms0, ms4 +} + +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 initOscillators(){ + //initialize the SI5351 + si5351bx_init(); + si5351bx_vcoa = (SI5351BX_XTAL * SI5351BX_MSA) + calibration; // apply the calibration correction factor + si5351bx_setfreq(0, usbCarrier); +} + + + diff --git a/ubitx_20/ubitx_ui.ino b/ubitx_20/ubitx_ui.ino new file mode 100644 index 0000000..dbf513b --- /dev/null +++ b/ubitx_20/ubitx_ui.ino @@ -0,0 +1,230 @@ +/** + * 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. + */ + +//returns true if the button is pressed +int btnDown(){ + if (digitalRead(FBUTTON) == HIGH) + return 0; + else + return 1; +} + +/** + * Meter (not used in this build for anything) + * the meter is drawn using special characters. Each character is composed of 5 x 8 matrix. + * The s_meter array holds the definition of the these characters. + * each line of the array is is one character such that 5 bits of every byte + * makes up one line of pixels of the that character (only 5 bits are used) + * The current reading of the meter is assembled in the string called meter + */ + +char meter[17]; + +byte s_meter_bitmap[] = { + B00000,B00000,B00000,B00000,B00000,B00100,B00100,B11011, + B10000,B10000,B10000,B10000,B10100,B10100,B10100,B11011, + B01000,B01000,B01000,B01000,B01100,B01100,B01100,B11011, + B00100,B00100,B00100,B00100,B00100,B00100,B00100,B11011, + B00010,B00010,B00010,B00010,B00110,B00110,B00110,B11011, + B00001,B00001,B00001,B00001,B00101,B00101,B00101,B11011 +}; + + + +// initializes the custom characters +// we start from char 1 as char 0 terminates the string! +void initMeter(){ + lcd.createChar(1, s_meter_bitmap); + lcd.createChar(2, s_meter_bitmap + 8); + lcd.createChar(3, s_meter_bitmap + 16); + lcd.createChar(4, s_meter_bitmap + 24); + lcd.createChar(5, s_meter_bitmap + 32); + lcd.createChar(6, s_meter_bitmap + 40); +} + +/** + * The meter is drawn with special characters. + * character 1 is used to simple draw the blocks of the scale of the meter + * characters 2 to 6 are used to draw the needle in positions 1 to within the block + * This displays a meter from 0 to 100, -1 displays nothing + */ +void drawMeter(int8_t needle){ + int16_t best, i, s; + + if (needle < 0) + return; + + s = (needle * 4)/10; + for (i = 0; i < 8; i++){ + if (s >= 5) + meter[i] = 1; + else if (s >= 0) + meter[i] = 2 + s; + else + meter[i] = 1; + s = s - 5; + } + if (needle >= 40) + meter[i-1] = 6; + meter[i] = 0; +} + +// The generic routine to display one line on the LCD +void printLine(char linenmbr, char *c) { + 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.print(' '); + } + } +} + +// short cut to print to the first line +void printLine1(char *c){ + printLine(1,c); +} +// short cut to print to the first line +void printLine2(char *c){ + printLine(0,c); +} + +// 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 + + memset(c, 0, sizeof(c)); + memset(b, 0, sizeof(b)); + + ultoa(frequency, b, DEC); + + if (inTx){ + if (cwTimeout > 0) + strcpy(c, " CW:"); + else + strcpy(c, " TX:"); + } + else { + if (ritOn) + strcpy(c, "RIT "); + else { + if (isUSB) + strcpy(c, "USB "); + else + strcpy(c, "LSB "); + } + if (vfoActive == VFO_A) // VFO A is active + strcat(c, "A:"); + else + strcat(c, "B:"); + } + + + + //one mhz digit if less than 10 M, two digits if more + if (frequency < 10000000l){ + c[6] = ' '; + c[7] = b[0]; + strcat(c, "."); + strncat(c, &b[1], 3); + strcat(c, "."); + strncat(c, &b[4], 3); + } + else { + strncat(c, b, 2); + strcat(c, "."); + strncat(c, &b[2], 3); + strcat(c, "."); + strncat(c, &b[5], 3); + } + + if (inTx) + strcat(c, " TX"); + printLine(1, c); + +/* + //now, the second line + memset(c, 0, sizeof(c)); + memset(b, 0, sizeof(b)); + + if (inTx) + strcat(c, "TX "); + else if (ritOn) + strcpy(c, "RIT"); + + strcpy(c, " \xff"); + drawMeter(meter_reading); + strcat(c, meter); + strcat(c, "\xff"); + printLine2(c);*/ +} + +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; + + long stop_by = millis() + 50; + + while (millis() < stop_by) { // 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); +} + + diff --git a/ubitx_wiring.png b/ubitx_wiring.png new file mode 100644 index 0000000000000000000000000000000000000000..49f903ebeaf72e97ef050c2922602871d43fb515 GIT binary patch literal 72490 zcmeFZRali@)IGWZ2>}5`T0lh_q&o$qq(Qoo?(UW@k?sN8n-acfYY_tTD$NlOP!>kypt0$PftRm6)iI90URj2fk#G5Wp*#ZH8#z#|v8l zF$E;>&kgBEAo%^Im8gm>1cE^Z{etnWmcRlp;@N#wwv)FsuyfS0(T6xXI({~`Ftydw zvC{u+X=9jl#ElPuyoZPheNk{qK3H^gi~jo5dOB>t#xrX-MgRl*;nSxKM_43r#4r3X zUpj*gyKVS}eFES+EO(~Q zv(d=#1Y}Y)mV!the*UG+$~kIH`NYAO)@wRl&OnyaNYnH;(E0wVjPHZ@#)F&GpFhzt zIM_BEY&HMlZVI>@88amb?p&Nt=Y~=s+%@*p>+?0a4Gmn!2a6#As)Mn;QL~)-$b8}W zU&|CjqF%rXAbqg#eF1k#QC0e@o71sNJv(1!X=*7|$?@Q?O!eLZM^}yI@R)qAvfJ7V z$bGT8j!CZ604kcnaSS!&owSsJO?UVJ@=Klm&Jf5 z&a=T4hIo71-KqU?F!9)Le~$Gak@&%4X1__%ME8mm@P?(A&+F5A1$k>+RmkfYt{1^O zcLG;HUjO?|CRp%=y~&Q_nJ;1%N0Qgd4GrsTX1Ij85^dz%jW1m&e_}WizeK?AZ9zo) zbdqEl{}uz%8dz;EMoY0`BZY>Lu3XTmp8c`nFH)FPKvNwDgiM_D7XMX3XD7Rga&%42 z#RTQi@yKe9O5j#c`na|3sqWRozLpU(vJsyL_cg8R&u>N)iWU~}$||zSF<%|j-zlLJ zel%!D@Mzv9O7``^W%g`-0WW|w+M4*xCWi7RA+ZmvR+OT0PXU+1iIJ%2?c3&D)xm?$ z8lu!RFIN1H7GZAgW*C?`38%=&pv(L(ktN!wiu*M_aw|y+7n94TxOk;%>d{#CgUa$d zzM;F9nE3W?_K|V%J#&=uqP!l-nzmab`d4NWlY2==OcfRFokM7GBQgSMPCm&uPFIbO ztbFKr0y9z4j5q5MvWbbTZAd>c)`oI_Exx!UuS)k?jH8Q+j|%|z^q!cQMpB|SI!cg$ ziz~QN6?pGyRTXVXl%{eZOrPf^v8aSlw#>-nV9`VBX0$vzyXG6FhMOyG$xVsfFaoX)6G|P-v%Q7% zi-|aeI>^(~WvF&<&-&RNkJR@KQ9g7mX3oOH^-HB~X*D>m>+7{Iz`*SsuEMl-#p7-r z%?J32cajVGkIc^A=F}UGk2^cxJAmjs6+T{CSk8cBNJ!u=w2z`Es(=})r`v-oj|}UqpT|KCWTgVJl7d) zy?%3%>PkUDTU2=jXF7G_a=c`gzzFY`Y3Adx)fF~?g1&t+kl<#$fn+^9`v)7F(el|N z{1a)UtU||aC@MrMJLl{mmX?2(>M2u=d)wl-%iddafAsDP|Jl&Q88*ky)gB(9YRwPT zurLq;OG~onTXN%EDQkz@GyKWj%Q@3p@MPbVaa~f5s#EDe?9?0JpsG)g_69X;YQB?{ zGFZGl2g#u8TM^mc?*(VGJ_H&=gPomy!AnF$AYka5-CZGL$SAvqtKL;XFG=RBH~Dox zLNwGhoQ>zJljfiaG-77PpHLlT4UH~X6$L{$qe=J#9tfObEk@vvJ6%V|!w(xm*4{pOe4=&5@>NB;G z>mk^u=AuYyX`V!agwLsL)p!lgOx?|Q`NHOV%=n7Z@m_{$yml!(4aI_Q1OgibD$o!< zzQKHH<3P619q?+p{BHM^zql4=rLEP9f(cc<>9_Z1Ecg%wVd1x?RS==<+;u%Yc}>Hm zR__N~Je7=;sp{&B>#MDk$%0GXRE;=?(e-%jvvVo z^fPyWMAG<6fYy28QXX%M&DsP&DAXQXj$owKE zkJ7-w;e}+9e_q%tHIUKIG@Q=Nkat>P znLFr(NW`zEj{08hjUPrP{-6ch7X#VX-M^!(tI9L8qU4?Hmb6!Xh$H7veUDMw)2%2M zrK9;YFo_i-JKNE{Tgl8A61;PC6rLv+B%DjqP#dop8*6mwGKPX;T~_{)fexL`)buUC zfW>E`7&E>3*Db!0)WU1JyK)8Ao6#G;RFZqcJS}WvIEodEzfYLF)9~zn&pe0vZM{Ct zI=y!|w=&&J>FnIHKGt;;InCAibmR7Xrp8ohomHAvqpx^c%kZjdVt}o$-YJsq@j=Gb zWy;90t*CG}we6&h$w_C+hqLCxPYha(iX^;JHa5ePS1qt5OmY-R)_G4TGs8n@d|VC4 z`rd3Wuw<$oR_({Di>YDHve~ba$&meNkZq@GE(v=d>rW1>wo5y8 zVq&S4O(q3X#TaWy$>5fq`_(M|HqqaK-v<$5rjv5gju--gccTYB-djU6e-D&)3V*3o zAtM;<8QQR3(saK;(;L~7nP)%gkJma;aAj3cGX1=9l|(Vrw>A;MXLEBty!-TU{@kEV zUwWP5cych0(=To#sLKIK%_Pu&`Jv+{#U|I2-XLFG@04YZ=-k>Uc(NYx`(?Gx=xHFmvuSWM_hC%Mzd}fT`lDuv%j9=F$ao7 z5R0g(henEjr|b2=&y)oAd?tA=aY4Cue`K+qud#K$KFT=~53y^Y3?wLkzCFmjD^Ajwp@aTa70-F0BWdPr^bQHJi431WxsW&GhgqVoEweK%QeO=vY?@C*Ip2CA( zKlak$GI3QNC7tE+5aQ}8?&7ZjF-v;-gE*}liw-1Y@ox!_+i4*4G|tx0`MSH$+V8%h zGhasY3aDZ+C700NtG5@@;8Neya=&LRH=obJEyoa8U`W)U|K-jQd$o>;dDXEY?0z)0 zy&dT)Xp>YWpJU~GZBa=|sy)I?0(h;Hf|1c1OUakT)n`%TV%$y`6{ask9=gLjJr8Ei_GPJ+V6F0YB*Nw= zHn!!RXjg~qot27ZqEn@=IzzDfBqvO0@_Pzr77pv}f^zHa+D8BRkP8SpTOF!GLSw|M zyE;#e47Mcg*UBqyuR2~{0oz9^hC~X;dVJK^l9nF&lP&va^7pT#{<4i9cP_D;KPW~` zI1;0OHdL`V(B4>*0ZV_ac!|I|nD})s2y^EZ!pOdCLIV9-gY)Ke#rhlNF+JFIB@h4M z^i;^f{q^)a)zTLPme&NTjK5r{@V^;~(5P`t+}s))>h`b`cW0Ymqvp&-kC83Z3%?9l zt2SRB80J%=nJ}$40(R)xql6( z$%BhpgfHk{PhdiTlHe}p_I?+sxqzHyu7$- zDx)9)SnW?0`O~s^9!*UF-^d9mpe|EU!II-bKRaDL2W3UGj>F2hV8G77frMvCc>)oN z-uhV-8H^lA<&~wai*t$Ahx6y+!>OCo?*;}{?3Ufb=&8W%b5;Ibd{Go|rGHT#p~7-x z98RO&HKEZ@^#T@hzZFaPDmgA*;=-R$a&%CQv)t;#(3dYWRThIc;ZLtp zc?M?7e92iYx5|E%zw4fs_IPkQSveC-95VcIve+BdIN##w?4B*~g$^B^?D2{~`tcMI zfV`Jhc0$&I1k;n-#=n6>CyoyAOWrQdN8DpKg_w#$s+)xz8Q&J zHCO2avq12yUOgoc^R<1~bU(098UQrqRNrlvbPf&5=P7N=$2F0C~Uvj}&c4EA-#Z7JJuJcgpH= zb3k3wt0z4K%$t?JoILG4Gw~5!Nl~XlIitTA;5eisa61$(!CQnz52PRfIQMZGL+P=W2U? zCQnLjDv$J`XvPL{DD8H|NM7q*;Q>TJ3ZS>bQ%FGIgGJvn#{M z&Ib%+g&O^qQDkjZ*NgVn?uxA)^=qp_j%x>A-~+L_o$zqTB=AJ;|4JQBrhgqyYUQm>80ACz6 z3t|sLvS@Yy6}NJ({Bd1a7#H;ppByDW6oeL`EwAozRWck)q$_Dcd>Gg4a~mR?#{ItK z@nzBCLj2XV)?~G_P463Kcz83njmWd|4md=LfgbI_j-XBbuL%i418Kvxrj!wW;7PkC zK&Bn(Cf<%POl`~6;*&5}xJZ#1dpd0Ct+&ckv`2HlB0+mfIU(SzoO7j3Nim%; zSn)+)^AOzP_6meY5#@D>PPN@qps2C2FLE}7p4A1M6{gz@0!fDD%4DV%HrAE6>ZG2W z$WJbc;Hu_5np}4BMM-j4XR|X38+$z+4fgS39UZK`gZau2bRSl8Y{1iJ<}oK}uW(ic zAl@%|reQ7@n@ns|y+!8ayNVEY&%pqt^*d%4daI2PI`)&*fSD@e_P@t1B8QuuCD%*e zBI2|V(!2XhM%H6ECM!Qe_u9L$zis?t7N141KX2s4L!-#>Ht;ofsWnNRQt^VK95J}2 zz=TGY1?_}s)n5roP$RGLSiKkIdrsaY5td1gnT%fqSN+iO3ZDg+zNV%-%h7UdC>f@; zz|+&;Xi+WxqlIMXgR4K7Kq) ztSHp5Yg(H^ZfBzxb|{7VkE11v`F!HCn=ec}sWo}XaJXUN^UQzw zmpM-u1z{+SZcXdC{->s8YiH+AupHH9y}HLs@7}!&O}H_cyhK5#rYthLcOpRri~RF%R_1`jz~LcvOKjwq zi+X!?9=pJQmCVrw2JDE4;|?82@8DqZ8!f}IQD^20oDLY`)HIi!^9&9LlJN%7`TnFU zP&qwVE9FR;f32~YYIICb<)ngob45je9Oh{+`9NUcUzuV$+RYNj<;@erwAI-Lwk0@( z6#&agIXFHMb9F_y->;1}uz?IAbhtaK{b*XL&7aFeZ!|*Ld|ymhq?QyD_k5mq%Fc<8 zsop+aecZe+IbA$uwh-Tz`Bm&-F$rpfkqmFn&)~8P{_D$b?W1%p9X++W^=<$W_6MMg z==|sa^-rng^ifXz^}{yb!{t!}4H}Bx*w$dJzaNVzF@d@1YAO%MCvwx1OO^r|kLO1z zuvYNC8GCnMzq+tm4(X0&{!lCw)w@1=_wX=)gi`UZySs3&FWOx1;iUb=-4#zz)cBXT z!7Zg#Jp45l^BrR z^pK~OJ~ZF=NQ;~N;$@v1;Fpags#5)4ZM%Zt;6N1|Y&SWjRY?()<{_T#Xi1Rj+!eOe zG+A5!MCU!~o%B4kiAFr6H}=Q2($caGh0bXtFA5$Ft!razkKMKU7qN$EhF|~*jpjo5 zYZ5BYG!m$7{DNYz5+5Fr^$k64{RZN3iMX8KkbJ+?>hwnE5gHrKGIKuS=%UrqJa#v$ z)Yfi=sJ(fk>-2c(MC37phlq&P9lm+Kl@u4xn2m+}RUqav&2=bcJJlBPJ$1o>Qlpe1 zf1ZNE_g7xTo)VjbY)$dJO=})DTwGX7OF0X!C4_1<2B(+Py>C!~?-^MIVAG^OHymd& z7sX30r%y)_Vfp1%v6#@6AWz>l-;!zF-2KKRjtGm8Zo0}H)x=a*#w|0H#yCCgyx6gZ z73ucnet!7c72&v1P%@{Wwyf<15_Vw4dE425FB%%6^TjBxxOhG<;_dnHBS5(-DyqT3 zR8x429pRG&`m5g1eb9b~?|HT5%a=cCT-{1fPd~utT_cDp33%%NxhFoj#iHT1rm`(> z)KJRL+%4E*6(`Nkj=J<<=sw?ME#3d6$IW+DTP_v<_Nt@fZKN*Vmz#2{3(Fl@tMiga z(rv9aWU!Lv>5mQ3gjdk?>1sMu^dXA^PK{~)tbFajY@)2LlIb!((;%U;!qD1i8jU0- z_`Ky&xFG$#OOtA>li}vh=!MIp{X&ByU5n@OLfwz>Gq~fsh&$I=)9uZD?3zFaOYbZt21S8Q^l{| zf9eQsX3-SW3(qc$&1bkC=R&b}DqLX9Nv zfB?$8yll727~&|hD&6hul+h^iu3izp@)(s!n|I&ec_m1Ot_e_}~CGF(-0Fy=IQ$3*9lL2Rc zXYLId=3ei*I-H%EJ?!qxHpBVi;z9}XeuaS~WVusGqor1PSeh@pTu;Qo@2scC2f5)5 zh`T`l>PjuXBehDT>r|fpem6w)oKNHde1^ka6H~DaEe}3^QSyE+r(6yTl~d`wAghlQ zC9xzV@XR*zB*jpV8;x%vxiooWQIDBKlef;P_Ewvxh)PZUivJ7KCq+$DLZD^;2t4OK zRsKm16gA+oh)Z$41<1hZp*xIZZCo19QIH-o0xGt-2x2Yc6cG5Ik7wI^NWbkh@rBhJ zwEp`S7)f%7$1#?g{EqhdZdxC@p=q^a#4z~viJQJ*k%;U zw@Q6lEUCd&J|v3QY^UnU@gtMY(juCE zO=ruH5WUh8ItbU=`tI6ZUy0%Ho0P~e1c-<(bq$yk`QM!%h38do9DzZ3A1hf1*c?8w z&y>%h{zOFEV6@Wy1pT4gfj3nsX4o0b48;d0+AQ0|a224rxhdW&xykn!-PaehYflI& z9wb>)gX-!~wK5;c2yOwe#eH8Y=x?AXOzSRI?QH(} zLj2Mtb-uo;(aqhC86JO7@-m4Ax3>6CN9*3?uJ+%%+{ax+&}2|Qe} zN}54>oxxWF3G1?lwhjk9WR!Ua=p!XspL%=spTk3GRxWmZ=7|HL_k)t^en;;yN#)`B zU_Hfq^Ii#^c&kgR3v7tVH&<%SFOdcGErvQS_o)XJD14_x(nzDKiow)R48MSLQDWTI-{N?R^HtKzSOyiGrx1xr&i z=-p`~%+E7vAE_fW-hbwZqpPqvP+q8a(C_hq$5m5SPgt=U687dVQ~Z{p*nhve`Zgx^ zY$iui{W;OTqI%2Q3o@0=r)ACslEhyHWC6hwE5c~$;LJ=W>MHf59UPh&biP!<46@jp%6l zTtn&l+Im>f8AT#hr5*S2fjHF}N)K2l8`4iL6PrdQ+3qv%!T#$EiP~Lyd?C&2>|Lfe zJXXA@BXyWv@qe@c+&m=8)ihOe^{PeQ0QHGNamoE*zulf)=*o)T%`G7rWBDDt7nj42 z^t_V5{GW-ifwX<$wl}TPcHYJe&G;Cl*adFgSZk5*2 ztPf7EN}k048z5Pen@$w@Z4Y6c?G;!<-`wmcZM-vimz~YR_r>{qOM;Q<8;AABw1EM+ z&zdxAYn^NO(^|DBRHjq$>7zTRXN;Q#CB?Ax9*rcR2AOu@{?EbR{`pgCvcQf!KDKi< zzyz8LDxWQ81?Cc%B*)Ug%DG%u>x_LOx8sCsYm0q>7?&-<%&JK5*-VF=m9GThz0szg zDj9}oZ;0NXolO-D#&g$Ukry~WnU*-mlH=er{#g@~B_oqebrl1pxyC2mHdr6Sp-)zQ zZ#|N@@cD2iTtyzuSgy_68!F#N)Ksu2rC|HgFq5^>hG;^H~TWDy12L|zKbm2|O zq&{6m@SPCRr;FWbI?E~@m9jZB5+)K;$^{BUTc^S1%;rhyF(23}(wKdq@1 zwJj_oWM92`!SYi$0dHrb_qSz>dG~rm_sWq4;66=2SjyzBOymhX-BAbhFbUJIkPAn6 ztmNEDbFYefG^eKWNRKr&umI;9&+vr@I3J3F?yt<`khxDh?o100?_s*I_MWd>%x}B- zbdBe)kl+yKMzR7}G&K$W{4rv8qK_z2s{plt?qa=H*NrW-7VGDLM_@Et{dYDjj){4s z)r6lSEch^#61GI_*Cw-=(|ji{ldd;i{0@zpqbB0W^K=+bM}LrOxl5pPR-qdh1gtKDlZ53;+I_fzl&Gn30RE# zTL^Epr*ZtRij?2IGUIYe$nX$+X@^{RSPDfqF1DTdkg7~;>2Y(0Tgf z$L0ohsiPB%Z$PN2hGG+=LQu_lqR9o)(i_wqHTO=(XmQ>HUKeA{z)^@EO1cErM`?pb zIxt+-4g61NfVxxCOklo_{HCt{Qnd)i)RdHvOBLg-o#A$7B=pD*jgmh?!wYpvHy7hV zfE;OGUWNr5jex*Qa;di~J4OQm#CMEl@fz}uRbND*msl${pyz*nid0si9cSA@=tvt407lbVSvT1HulX#%Ow?hk@I^+b>Lsk!`{Wi&ku1y z%J$KIOtDjW&EP?|Br{~_t(LesCE~5dt7Nm@vAfrM{r;WQ;`J6839Y(gaPS2iTa+qK z1$ew1$pnzBu>;UHBja-0=jTIt-6tfHcSNn>n?m8r$u?+zbv3B0J|$I%MK5(vw0BurG)(O!dRFzh2%*AwP|ahPJgj*Mp?q# ztklZx6AR<-hd-YR7Z#1NaCOn{k0@y2BSA?$S9yMaf`mfNEhu7D~9^y3s53camytJp>Ayz2T%s0f+`D zE$ch=`W}H9KU))-)k_qTABv;_56tP7uyA$Te9vvqQNw+y?jhRmxju&jesg4VH%7HKuQpIfVD@jP z-7^@33i_Gq=qQE~ANO<<*KBp-(4CEnymY&Wv^+#qh`_fW^lGk*F5$A{kBSQ@i}~*C z{Kdv5Y>%9fL*U+e9m#c%5}^F`Y1iZI?hv}EeDo$w*D&AnZKge-W7u0J_KJ=oH=Tsi z|6Jq#uM)Vg)oGTN3=U>q3vCbCCv_=NtUTO){=TGVVbKw%Rh=h)e_13Og@%VmaK2J) z9?!*oa`N=ZO-Rr`URE}9H%0TY{PJ@1ER{!_UVC%x-=Aeq!{__s>3sBXLQeYq3**MS zOSt#feOMASN!?roz^x?(vlvENJU?X#r+lE34#>(1_I$o)#3UDH(C_1TgUM250sKz- z{z`=1I&;msLnM3y6W1vkRH%Ce_ccYMj+iqyH}odEa-p;|ef|El>Vbev%~Zsj{;aWc zzm>FE^14u6Bca@$nt_1rcU-)(x%;x~QCqVHmxM)pj0{E03+bdTzrmz^dVFYz!RqP3 zNAT|b^F^<&-%XTz$;#TNT4QWt(xGPQ%=Dv&lGPIJ&o>-uKtCja{b&8Krs?FmWD)f+WM@$NWO5l*# z%KvR_oCRnDB)Y?~CnwC*!|oE(DVU)f`3g#k5gp5tk2C|p?)NseRvAdya&4tLSIVkWPnUv% zMi<)r845PThG{^K2=7&0i>o0f7E=dr@x#D!*mu4rYdo5E3-E3KW*WDSHIjXOow=#< z-A1*|_ff!JV2g#lr_#utz~gEoTMz@yDS%paa*Cg-r^UpC2f4)5OLMA4*j-XPUvb=e z>=R_*pbyQpr{2nY$+FQq6QRvZj~|lSqH}V7@?0wTF#>-|hK1{8_ap+*ke~d)YH#LsZa5)zbQLil5k>!J z<@iL^&GX0Z@TR=N?r(H4RfHH`DO!tic+f{>0@cV^a})gTuBx1z_-q+T05G^-7yHWQ z_l8**z_L^fca>Y~bsp2=;9zbI1nEss+;a}HGygQOm5KCi;bTH4F1CG?lFuD8-kac@ zCcfH?voEFw=tG~Ha&)}<@HH~B-Njxul)9!?a!CJ%Q^0xrxo2qVcS;fIr;dDqI?!!t zZZ=SFRHlKlHAtiCpg07zyrH^=q)GkHBGvHluA6%`dQT5nx~2_p@ofGQx_KR|Tbq|4 zRZg;viGIR`7H$xEdH9kh?Cz&W=eO8taY);!4OmZx}WYaf%TA^*zK2B zS{mNzDQRsj?)m*OVbY)!?63a-07Uz2<6KznuNMK0}&BfrR8$t8?-M>FA=KM8+*cUoW$)o@0?s1L4}ruKQw*- zv=l&ccX4*IlF`a9hm5R@LpWIY>lZ$s=Tn>SkGEJ1Rr2Jpp9nGlh_)Xa6Q8Xh4@gc< zjZb_!{^kifJDm2MT^WMTsJPnl+gFbd11D#U`m^NA3yKN~F5qX`VuUMbhFRh>NI8NkfQ zc)`wj)^Xf|S?$D-q^Gyf_!gQEqJzX{(MJx<%>B;Rba?=*92nfcvddrYDO#}N=Y8$mc<7hGXkL4+C0Sp7dU8{vAl@KBTE<{ z}_?x@frui1@XHSD1Y%Z?v zDX=~xGSr}`xj7Nkf+m9$$IlfO8VG=oEp>II_wcCVI!@1EcBaRe$j9=V3qCd)_@0md zC@?&36gb$IS(8hyp`pPbJ7tm71q8#*T-H7bDO~5Ri7amR$4!tz9#75xBrg4pms6zJ zG@>0nqHTicRcjT)56YmXeZKaL3k=+mXmF^x4sS74GG#TR=oe-OaTyysXlRn&>+8K; zZ9$s#7s)GgOzw*O)5zRVxO|0yPH}#{D-dzdBqWsW%rFo&xnKbf;m+CFS}Te5!pgDa zYL;A14v5`5{&+!LY8#krZf%HAQ48a&UjhKgVPjM2cJc*wbX23XTy899@U>J5)q9?% z=)-xgMThq%j?_vynD{1sA>S6ya3*TV(7$;Qa>W+O=bV^L7ypQhJ~3EsPA>lP9s=bm zltq!G{rwmkB?;DSZIp>I+mHI=OfL>(aBw`hJc%I01O%+yCyKtjD*SEN?P%(^vQre|GKEu1wTbNu;l zQ2cw?76!}ya8C6WYgX6F5T_cJr4rh5uNN@dD}1`RniumyJ$N5BB) z>{nXJ`mCXzEEQJcZH`irXJ=e2zSCw5J2Hwrn?^_kU2z=mbR2N6r#e?QXZ%;-Ke|>w zopy%Y>2@?FOCq7@K!5v8Phh%Y5%4F&;m!V9W!16y_AKGyXV>2!Y-57gT8ea_#D<}* z4dZH*w5q)FUqtPp;Y@`2o%E_?jjrl*?{WKUIq|L4yLo=r{Ob-kwk6AuloaV)L6n%$Xrc5}`u1 zNeoX<5L~aB+_W6iS4mo>|RHsGKL1)SLl52B#r)lFS3PY7W(hmU`#PF z$B>b&Dyj{Dh7U*$-$uvks`@6wbj!;DW!6Bhp#O=qo*vLhn_{6tBz8u;Q`yV}5B9c% z_X)7EKz&nto}C@@22Z8+>9I}dD;A^E2_>|{=H+lWP-mA{JEgVoYSci`_)L4n_rwa} zvzoA>p`k>yIFPxYO2mCu#wAcvo7SfP1(ajewd|iaH_~M&N|~5I=Q46x_06$odoJ`Np0E??d_p=Z{CQHnCQ$`m#64+aM-IUEMb_lMg!mSzrD4- zJMHF(iWLUzXK96Pn8RyGWkn4+7+3+eTD35j%#-Q{7i=^$GXc-_PEvfWA&UIA8o?9` zB(4RO2@@<|gxNDd0z;5qmeD-d#Kqw)Emb|kFaPfqAfaKQV13m0U5aREo%y&Qa?XYU z{|$^0zfh)EWgfj__h5g^XgD@jn-~YZ(!;^QNsJO09|6wA#-_X8wb#OwSU>1Lwtp;ojr>RFxaA>?3^!8trQtZQ)} zd?EoD%gCd;=>!(2YN9=x%Rx_0GIik2cwCgrHm9#1oo6Drr-$CnZQy1%SK;n5oD zqEkQuW0X=!oEcA?rMwt0DTUJuI`Gh2WOB3Q&}Ax9tteA5N>an?Wch?nSaN>A~3QA^8pL>G5;nMI3<&W z(>$LrDNx?p;Y-ejxXI>WKPIk-TYMfu!KQaURCRH7I&rJ90J9NWK+2X>Zwd`w*@<8* z_DV}>U7;7C(b1Jo%|$7rB=>Lc{y4Y)U{;^*>}$5F9sNq4qV2<|h;xNBF?Jez8(Vio z1<&X32_K)FqeK7;sKC(G7D%=#b_ci!@4<);%k~h=FAa6|r(?@-;_XvVg3j)aD_k}# z6@vs{ZBF7aPFDPIi31c~JIwe~63Kyv$4k)Zg;m5!pQ;^gNGlkL@U+|SI?9v3H6Fli zG&gs?FKJ1vR|B_fp`#@L8(}dg#3wC%#*7cvyKHt=LainB=Yo3i#@5;XYz2gd%RacR zx%nV8Vmogz$#Qn9)%hG3P@p=}e3Gqj=-R70J^9w_&>~}@&AW%#!>DkzN|e@Pur<;& z0WRR?4&cO=e;og*=F%p&hfWn+JVF7vB~TZZfCt(~E0dZ{RMF@sS6feLggdoHuklmR zqNs+7fzlcYP_B@}?g$YrRn=#>S~&1D?*Z1^-JfOx13OF0EyQ$H^-~RCqDg?c_~yXu zfzuH>W&s@;Lxh8a!JVsT337IRuy4O=-G3Y1gfG=M4CX^}8o8n5M|(gPEoI(S9W|Hw zR`L^4EK1zeymIlD7AYgk@9bPa9{zdwlw4$z7IW=Txud-zpok6K7AA`cMWUjD28Uvy zXgZNYOHD14iE#(yI#rzyuAjBYF6N#chl!a3U4h~NP_I~fKn47Hdt<@wS8_}U3Xi7C zOZQjhLz;J|JObkLV;R#OE(HDEhm!qQxO*Ve6^ObeGPOdR^9BoX7Q#*Bl>%p@e=eak`Rnzdt zSI5Jve?CD{kx)p$O#**m;dGOlkOZe1P@wAihO7g^)*1-gM+dOV?RU}5ur%AO;e|5OyUBt~cFN%bhSu`i(Rbc9sU z9YMZ5mVGuHz;_?&SEP2XKVL0Wq&)hQ$Mi|{!U7iT1F-4zv|*W{sD+CQBiJ}uj7;r- zElCCRgt_eNkoJG2v38d=pO~PhQ*ErER)ltqhl-WkB8T}9TP}M*+n~;I@_ep(ND?3?}_|dx-7&zs7i` zmOIdrauPx!o}ajYNO<>b(UINrjxr+zr{ev`%rOUO+pd=IVl zL73L`Y$cT`4p|~_+GaXmmL>~;u2F^6>OGio=x%;WgvMwkLzU3~C{x~u-QI$o2!P06 z-_=7?67Y&>Xq3Se*|@Kd$$}J6PWqcxylnPG%$nY(>>UJ(YJH=SU*K2_tAI-{YI1j;4SR0(&|pSQz=OiO|Mg=GM~;`!;{zL#|68pNjsM}q*FKZ~M-EuW z{U5Sk>_77U{}BfNFCWjvMz%)ebi1TljH79TC@00!JGMen59eZP56l3t>dH=j2RiBx zjyG0h4m%?gi&z584B%F3;Wmw#?d)wybh}VNAAA&02F~x_LvX06J@B|xGa@6?`wl`X zMi#6VKa}2SBQR7JP?cwVTZK4XlvPo|MEnYW;wKL{5a)jmoapo$z|s12dArRZpEu)j z`VoS_Z%KbL%X6)5t~*wl0**uhu3;Te!NSzOG!QCKRNZ(00ApxOAK5_+MkWX7TLg0b ziWF$83q|#Rcwpjbc}_)9Q9(UBh0*pDfL-m6J)Pd{mz)>-AXM?e6#CSc7bn`ifv`fx;gZYNWW>cJP zCPs?MedoOhK8n%Z#J>TcZy^B6Qq|gyMs9$6VK5@&MQuEIA=xfG^Xu!kM;l^{!NCxt zX1lS^ZP4`Hv0>LYak`nUnn-m!(Us?nj(Z;jG;hK@$ zn=GY53UpAVJMsb_FFQtffd(<6*ByBY3d zW1}*I#k=Jc?%tEl{scZJ`X@BBuo2e}LblNK=?G&RgZ#McFv!E;K>&74$Gt5 z&&ukhrPQ2tWBxx{fYrq&SIQ|9=PD(-t|Ha1fD@5N6+s$(@oD!L2JM28r{`0xQ{=4Y zQxF*0SzTI!+|13Lso62Bt*t>bf=@~c<>+qh^e{TbHyNT_s?lf z_u;{7CdBMdi~75wk>Nf*@C`GiIA&#zqwyvoL3ugkU7suy8JwdUgM#?EnCcw%R; zz8<7gNd8)K4e(pMeS~=vSq|5sT$^TVBZYgVvt?~ym|ADMt*NT2`X(+)P}HS9eoYwz z|D~T@H(d86m~qza!9Z|-ICWq6^abO^mz(V%Q}w|PJy_^3MjJv5?~#1oBZVbXAiMyt zWJF?idO=@dw#mc=2VndZmU;^Z4)7W23DV_RFq|F74?zJ(2X z3$-+9R6Rf{`~NZL3W{%+uc94ZxWj3skiJBR#4 z4BKIPkAxnH^75sd!xk#UbE#2G$Cwg=S#8mjmPCs5Pf)576?_+>dPdjo-0ZijuE@&v zgI5*HrNI9#8LA4)_u-EgXY`{h2W`Rwn|e*&poR$j|pVOfq92Aq0P;4c(} zh3SLS9K1qVk76<$QKC{|FnPQ9?%k7L5e|_ZSRW$JJb3tbp@A!TLn+2)*X@4U+3FIa zD@OsaLt9(Q{k0TG=()AY@2cSuG3hOdsPt8oO42KtvB4l|Z*J-sUdz=Si^Sz6Oki*C z>g;EYQWgdsa5Lb%5D-Y{8Dt0uH(gz4_f@8a_)Iynmzl##1$D{MKNXflR-EjZm7=0l z7_}kpLJTK8!2?5GZ3CfEqoXmg4{UKf-X0P6nW06EE%j(20c=p< zPf~Cn93Ee9y}Sd-(q;jW1#T?1U^$qvX$CnG_F zy{jWdYVX|FUcMU~IJ(|?C@2pJ9ua?o(yxWcg^%~rE5y%_0)a@UjDurfwx*=drmjeb z0muX+B1&#JJ~BlG4oGm$&wdXTreL=C&#q*5cifA0wlM{UR!^3bP4`XPYA# z(I!Tm64Cn;}ueR(Eml-SARv>M(v^~ih!t;w9<`qmq>Rr zbayulp&}sN-7Qiwl;lWvcT0oB&>iRI{noeEIe)1&_j5Q!ZZ=fJ=U>(!<>D=0vBavZ^58XJkuh=@2hTm2^Sl?LTokg}sC zZL*kH+V==E*`3LAsPi2M__iB-+|KZ)Xmolkq22v!hYJmLAE0MGi@eKvv^2Dax(4sA z?H2REmPgjrWg}^E+0qriohByGVo_3(b>n2h#r>T0L-Hc^;qxPMlkwkgZ-)U*1F$P* zK-8++-CmypH&b>7Fth1 z>-SpQmcP)T?8?d2-OW4izQkA$UT9#Mn4(?_!26&+(d_x=;G#@dM^3&I^hp91eRzI8 z?GAn)fnXcD;Um1qOl|bxEA9+ynQUM4wU;hkadu9I`w1z{%p`5?#A@sGQ@Fb~9~{+h z2Bd}7@qnmGVRv!hDi~|VWff&9U1$h_MKwQmD)^j?H|R?-^QdUVI@o2mLZ{`;4O#~eE)`isf{EycAF}bwO=AZ3a4PPo??0__UVS6_)3tNKgrbBx9Vz}8m0cX!$z_> zU8mkuzG|_lu7N>5UydA?-G|Wu3~OU!h#xr(BL*-_zzEkod$u|OqXg%3f*ud`xr{<# zX?68r9qt<^XC55lfrhR~4Xu46LnW58&j%JTR@R@eT@x2}PAV$i&`0sg5ZjxfyE_+H zW!u2$5$T-#^74r=HnJHCiZ{E%JKY^_ixA~(VI^O@e-k-R^}Re<-1-uN%env z$wK0kPf@j;X|J(0#$Du7@T&B;pg&_!xYlQPwQX=%(TkfqN?*CPIu83GlKsMpl4)tN z54Hety3+-{z3N$9{!aIUhV4Bo6_+01-|Bjn(}b}a5XJFbk#F?;G|7gSE{#fRr&I} z14K*=HZ`}{8F)6)C{M-Zo_%L^1q4qFzn1v2UbnS*-j*5PUG6V<=4+ID*~fm&zR)N* zw6oZMFeqGYK^ke=;8J<69~g4Vrv0eoK>sN;RZ7CAj$U=D5VQZw?!aJsT!K{UzF<=5 zR`u}SPwbQAf_iyEeW@N}hne|KSoq*zLSTtLvne*NI)nBef_Y%RPGz#5>j{c8LS3(840_E7#u{kZ<7lpOT~&g{5N;c@sxi4*O)iy z6=3<5hd8YBluK)jAevtF?0?UfOiQ6Y7l&5N!2rDP2^cpUO(gfeb^Tew%U3yo&YU%p;Xr;mnzMcUA z9%hB3n^TRVj-&O_ugK5xN^;072Q&Som-K%tB3!v}ozM4T-PDeKgb46y>F@N2j{PS~ zW0jANcW|JTEJ`UrNh5ynJqI`Qo2;<|`_Ui1i z`kfcj_q#au4IP)XQLZ@b2LYBtY50ARluFoqk_kK#9;$AfAYUK9!l6~V%hRb5Mgl6Y_#PB_nA*E4 z0SE>APg8ts0uaDPVBjt~YEcz`b(ZjCJ@}xg{a%6hTpDisnV9&k?WNj1v`hv5;TT|p z`x8ZjR%nltJ;CiUH!`MY98;m`W}y4ICFIBqA;4!DYReskIcnGZx~b@Lm>5a^3Cxqg zPyE^H)gd8gT?0oVmX>tn(cVZ=8ugO8bE=JIL z>IjH$^*8M-tter~WDMqzOK@{Ln{~i8UlJL_r4j(Me)^MxBh1i*#`orI&KKT*q77T1YJq#E8A^AlTmM9E^&h!}5@&N4ziZ z)4{}i924`sxA&*!WNYi!MONnTz%1J&aYsiJa*ei)MEkRu_hz_sF3b`rP#4n8-TmyR z>WVHa+oeNDx)&C)%7-XU@_8#cNfq+IJw(uu)+%fn4c2)gM(MX}PW0o(_v4Rt;KXvj^67LrrQM80io-+ob z8-kYqr7pRz%-%kpbsEg+#M=*U66VAA#xz|fL`J59jU#@c_{O*4_9^Y8h2&sgTSx0- zYEu&xW5CB*@zuS6 zds*+%B?$?GM|;-~a6xkY*;mwULue z+T9;)INcq-_+??S50LnUz_m?B7F&3agN6nZHRXuu1YJKmdbe6J{!#%3^Vu#feRBcg zIz%b=r;m+GZ6MJxikBC<<;d5paaADYH=;skX>O})N`Oe2PE8rj^K>rD&xg<&t(`NezFSzfbu|fbC9Mll~x7qg<@jDBs7)EW`S;JX74wrEuGoTEGfxg zm!M$(?(ml`KDW5)j1FX!Uq9UKrP&F5>%EgPzI^fKcE~DWmXOO3dWi8Or+6AmD(iS8 z%mzN@*dV1%MG|ELE&_3iV730ke1`;JfLYH4p*EF5!KY%%re9H%)CzAh zl6#bynIQqMgf*bho7h-XIR%L##ShaR0q+^Lxv+#4ESY z{u+4k@Tk@2&SK$qj_KBSYu)$KYk1Toi8Ku@DcJ)#KI+MAINTDb&nuVP=Y8R4M4Tf`Cnrt+hAzt+h)FjWoaP z2xa&wD$}37Sx;D0)aZBu#CjM=E?e)HCjS~`CB|Zb@EyYH@a{!#%8tS(u^!*EM(CAG z#$siVd4pTN-|>da!TZyV8et8jU+wI-_u-)N8J3@>gg%pF-}jou?YLLyCPVI zXYlVmj|t{?DMivVh?)7I30NZ|-U2pBB}KCK))xB{BhoRuwp_|Z(;laE53vp)qbdzY z^LR0h=ND&FKoo7$?C9_R`n4uLVIRTfpT+UG!t?$7&~)NVbdr8dJZ2a$dti4HIYP35 zVxV!Y&2k5cK+gfA2P>&uyaoFSq#=P+JfeNH`}>0v$t$+*C~`(VD*OgA8bT2}|tLOBCfSGHX* z`!O)KH~uPC=0g0dm_#cGb7~;Zdh0Ko4K$>TU z#r5ZI?3_?EUtG1gH}sBw&fotMz!Qjj5N1Q%jdz*a@qA-xH3t(biDk3%4Y&T~rJCm; z)A_^U7o-qKue&~TyxK0L*J2~9geBEkCzjyzYg}GqMJM;bvPJ0=N&%z*mI@LJd}@nF_vAQu(HVUS2TsEatpz9@}FUJn(2WXzO{gh?O%dt_U!L7;By~DXE3UgORddw;60DS z2x2O9UGpV5R^&Vw+yDtwbo611YM>5V0vevx-xp0Of5)ZBXd|E7=IlQ5qUyEsRj|8YRerIz4mp# z;|HYRj;i8Cd;5oK-G-3Oa`V*{U?TpgY=|gpDu0yF*1n02nSO))O}c1pU9ZVR_tmkC z^arIPhYxJV5|YpHu-^RQWHF2`EG$9hXBQymQ&E|PXd|2ACC9R`z^=5AOt5iB2U@-W zFrILE`CjUc%3O8q?Ndw1ePAv@)XP?{Rvcm~q*vb#j7eXwKy?<9yR|jolAA#x$H`F< z#JszEp|kx7J$bIQXiP?UsA#@wuh+zOcd7QWiHvS0romrE>b~vaGUfDnjW4)|ymdjYmmEd_qc!m$=(^J@6icMus;(J3RdfIe!P0wd6 zKukPb+JcETr>(7l-kPIGbFsW420Zo7N%KW>Di>s9Qvw79_wMU205W=6EuPE9B*>L` zulxJ+GV!zO_~_U6#C%lNO%HzP>}XS?(8vft`;Vm~2V?m%u$3hM#-dJKkS3p@tXP;Go!I^Gfzkn8ce@d}o$CU1_RiH+CQbc@5Wu$;X6>xW^-#Pn! z3z`6oS}PvY8jgYjd(49o2ApV3$w#EWr{ogP88hK4XXm#jE+=rO{>|cvlHV@D^2O?h znPY{3g48NxVTn0D@;E&(yaL)@_qxrp-W;W0t3eP~>4u=vFQrl_H9NRE-9^JRpAN); zUAaFk%vXb!#X|ehj@W;jW)(LH4D?as@n%N-si4lxvnSq!xPM?PPEwicm?z@HiVfNP zSlz?K)Y;jdr#x2RpA3oDz_HGWr$FyTyez1;4g#0L`{Uz66TN;8E!z&MOCf)hk(SAX zAG#FY6f3gc%mIE6C4cKhGJGPEpxtx z5cQN}qgEhBp`@wR`{lr)na4e%5AY$oGqJT-CnI(Hdh+r_ z862U&-n)JKcF>+70H}6vR*R^TIvs)(sMMu_Mk>(2Uf<|^2t2C|k)=RiJZbE_XGZg} z&UtQsDyK*W3Lf&#V`R_u;rCjangA4LGhYh`2Abi8CurBX=1 zxfjnA?4N^Yg3_2ANG#PZI>x%JjZOSH!W#BV0+Y&qu9FB869g60Bt){{Pa*yr4_|Up zHlHcvp?H=@U1`g-yEbqQ}srC6%Fr)8}S>sNSia0!}`$YsB?JLec3&I>PYUJ%)ZNXrz56_}Nj4^2;& z(#v~s)GgHs^)pce&+u0SwNrOZ*aT&}|MNw9$$e>BQ;?MP z)8th#-$nxucQ*-zOiqb^g@P>ll!^-e-(8);N6p~#c?j`>z|UsH#KP~gY8^~&ms(ZhX-@!ZPKK_lTo&@t>4wQ|I^54_h6OQJHrFZq8 zDM*8ilTSJ?OT0z34d%-YFDp9W8pFzr%m1AK_V>xt*Rkp0rA4%hdvjAoLmxqA6586@ z&A^c7I#$5HSdm*YHD<>L^liY?0Ne~h7g>ur^|PpYvh%C;=Kdi}E*Y7EJ|ed(J7Z3J zv%QPM(~Q>Y&2qRp-_uH+bGGv!2(Wv=r~G665PwBcX#xj@X5;~f$v^g1Lv z)33oxZ8BA6_`rF=HV4^ofTVl*`_pATx(O>KI&4=#{KbMv3zx-~rM`qcn2N5UA={q6Tp`}yZ-#8qXu7R@R=8j`Q7te_z#GYc#Ej?US9? zdrdq0B?aFuay#Pze1naAxH+hll+ZcgbKM;g^7FJ3lzQsiEI(U8B&i5^-}}OHbAH#^ zla8}K7qj!`cQhY*9ED31wxpr&XuGFHQJSz;tPxylCcT*QlYlvV}P})3Ycmdhw@shV_Z?*M0JSr198c&~gzd<cIEe?`A_}9!JN|jq}y{ z`El6TNEkvJd|K2+glR~mr}kw~ zXMP&BngQPxhl{HWic_ePj~xqxzP)n3V&^N6GrnHyF{M7?_0IkXs$2UvGo3fa6#DM= z`UiM!d#JzdEzr~7b?1n+UtZCJ zQ0`_5>7Sh~m3|;cUSe*JI-QxT+idTI*$rofP=Amx4X3kkGuqO&}Wm{uoVTrBh=tTW-OTT zvu2q@BNJ1#!ds^2A6FGOYf*uAa_aP0AxpinaCTbK+mxQ9%zE$?`ClI0;xQKtY1>yA z&$3_qE9Na-GG65Wc)pu`d$N6^`8(?CxN40;x^y3(+3L?=a10GiOgsJpm7t{t1kCTw zn?%Uvd6qK0h#b2WhvXZjuE^v0YHOO46G^leWQ;VD<|PzT6grKP;3!KYBVRRLGa*fJ z*~R&YxB}_rmQ3%DjhUo)emBv%_KhO<=SzT*^;vmxyW~eIRZIykgID9hW2(tXie91) z`hORW8+vtLGrm09ZfU7X=Z+5TON@PI+}@T`!mx64DT{^_8H-7MWt zZ|{X~T9=Plome2fY&v;k#wy;*`hhJM6u5#Vm$k7?uJ_^NE0CuYV30k#3NI<~IGV12 zp|M4!MtyGXZwa4ONEQDZh;hSW^A=@eV{6Ja1})&Lu{~T)NgM@OjnNW&{g`sS{D!dY z7>t2|?aP(W-88}UxIe;!K-W5k!qCDlA}6B!mVIX~(ah&MCNoVISTf6&7G7hmmxO^! zFjL8xPhi(H)XOL=cGSJ#cf2fug1oD`L12VIakowfpm7Sld{QZ(t zzR$4j?crXnO(t)=UU?%KJxSK;YUG&Lt|m4R-&?1o>gC;M(5h+o{3|@@cTQY1Ri3L< ze3-skL7g=?$}P>o`buduMVjnXGn4(_p_C6%_?WdaZL~0Cs+z zOZ9J>McIBH+`4aQcn{MQP(Z357`2ph#L)KrP0*xq*S|2e%?+60mSTa;g7o;|>V@y& zQ(!u>A_ZGl%Cdi0$G&+h2~KPw&}sT2lVIT5)oxIYI#>BD$ZkOqpRm%o6CIYDTjAtv zJlYu!Tgg>O%L+);=zxFWYGM?hFiZ@6lWZdMqPaKhyTPyo>1!q5Abmm^E-n<`i}%Pv zH&JocQ#1^j9-?JMbG5OK$5b`xyoQ>3r)SV}-CbVy1e~Rvcnt?)!L+2YLfXOJk3-$x z@vqNrZ*Q7#Yn|b-MNI+)XJ?;oP7?<`&U<1<*w{a1D;gM?OPn08o*J9lHy2WBo@*@8YIXyj+bZE%0 zozbV%_@#!@@gZ&?{cqQcJ?`lVo(~Va*LGM#>1h(nTW#&w%(zdMO0SVRIXT0CzrFFh z6f%X$cqV9~J#5iHcrQ#jrSp8Q$+n^b&qXpi*NeR-|0v z(~B)$t|=m7Mb|MA_S>s=G>7a6MANKp3^5dkItK5+VX?x*>@3sDXKSVqBF%3WoE=pT zVPzY^##4z)mMZG6Ln4bLYWdS2)}1S{{lOSz0gvJDP3@A?>6E@{2dUSk>BiG14j0u^ zNnpVyZRcRFE1w{YUt+{XD+PVt{xU(mD^_3$sogC#Y3vZYEy zni+a<(@vE;|MSN*@!9-#RWy|)x-mEyk4J+IL)fM76Y%K|{Xt3;@i*z1b=mqnpgZPM zuaNR-cY6%Bf-nJB8tJm92fNqCuEx#1Yj&Y&6h<|WqC~N}<}kOw(``vt#`IEn)M_UV zy2D~jcehj_fq4`gYs%q@yoFCNx;|r0&nR)gVsi?>c3QXAZ=OuFX3JjFgVw}3aK(37@XQ`;}DDZ*KTsL*0F~6!ZMWZt|)(R&G z00kX2Sk&q+G!v9UlmQ1m*A}w{7<+|s$ZMNrtU$4TGNDAf)?yJZq*#Oa2U}Y8_E7>Q z17Og|hl=XbF<-wVU_YGJs;4X_YwbHcsf8$?#`oj4#5D4!uv-Ua*YU8O?HTv|b_CtA zD}t6WB_3!R8l9llc=`HVql&C3EG)_dd-%2AZAb9=QdgayA8&tJaCAPU&ywde8taOv z+;^ZEp@bnN5p&KOP=u3*YA9fBx$8Vw{>|&et%Cz8(8PPH;WHEMDYxV zabROtO3XLV8DJftLi6uL7w;c}^b)VR-V8&EwXnHVUY9p#q4a}5daO*sICKG+-TCRp zy2V2>_k}tcEyg)2*>}t=(QKJMfkP6ckB*#&!XwQA=y+;rxqEU(wNeiM7YChQY)ZSl zab_haoBrTJGcz*c+I!m$MZ9*s;eL3Z$zUvSBJUgO6f%96ocxli4~|w2MT0(6*A^o9 z5W`@^!-LGzwA(HXeKQc|c%yoUFd4~t3YLqUnWt_=Ny!%5%ZujpLIhKRRcnk!i5`n1Q@ zp&q~N2Z>M;{M^4ma|aob8%X@pwpP37t6z5S3IAZEUA~{K79F+tP-kouRkCFP*^xgw zH(xziPMV%IGB8s=Y=rR(+$^;AypPF&QoPQ|LBe8t7ymey%LuSM4x5wdMcl#H!0`0 z+OA2?i$}zrUh-9Ea+c=c{Hy6n&oWQmFBjLeC7zUCJfmt2rAH$Et}Te8{dh&4_n>Z@D&mIps99$`wVN?f#v zh!z2`#Zsx9uXlE2ItNl=leQB-aVnadg&``1=gmH8R|ley3MBPXZH*Ir2cYctddWw_ zF-ULfVzC@94=mm9vunhE>(!skyY36M3BQhRP)rtyrGllFAok_T%?&>L3Ygb>Pk-k^ zB@tB9LK(H2f9Ek*qvbyfdi|V)k;K-UQssz=x*bZe24fLvM1VfSW|_ zhfodKd5Uu;Z}noGdm2aRuZ0GBUJ_Avw8V!FO)HBPfKlv&8o_z^Nf6gS(R0_V zLA@=WwTplP#(2qkG|8D+Qm#}($5_$w#p>4;+9bajI^^=)rLx zr9jg+D!G$`6PwoYB2Lpv4AfTL|BC4kzDr6gv-#c0Jw`tEJd)xAlAKRDIj`2%;#PK2 z+hmx@xP8H}(qcdH3eYwha(-QYylB z*|q3BSWY;HR+3nP64fUn9>NsV#mPKu->RyV_57Of^?lYuVq+g!RVRG^PUf~Jz1bBY z;P@7Tq@WhQ1j{F;QyYkUq)PXc2}_>EBKyr-XT+PL!o9gM4@48qix=!3o{-UOf7GW> z2`0(9!Ai$M^Oywx))NTq8?M z?&kx>$KTl4p3?h>=PQ43HhV6USkpc+@m%V$*kvZ9j`g*uDCG~~BI<=oqK~3zkV>jx zG(4x^Phn!#DJ#R%`*)H7k*uduldJq)kxmB}6nmjn8>w8PZ*pF*<8xj<>v`~?V z`Shrm%HDqN?k3C03E3YAR5|l7-X7KTI)9nj@?-31O8ov@{p)DHF)Z$1_cQ>1cTT7IQUDF?+MgAH1q?nwoNx zc#H9aUYfERHoE2yP0#+TG?pNE@l-b1NM+j{Ah*}psz@XTMmZ%Gd#~a z)Mf^nd0&DmlC%sZ5Q7crBE`&>BP3Rf8-DjckKbEdF7B|aiFm`U> zAok0%0zdQOwsNQ0wfMQh5YonwyTM$|Pl2n@&}xROUG^{cw_n)UoZfQ($?1fjF5Pz? zu12Dom?a#wW4XyD84P@Xo#5?WeP+w;+vwau9@zSkv9r_9-aj<-eGun_kdW@zuP-hx zR=2GDz+3^N$!FDF=l>?YwFU7VP8&~2$uk*VpRtF8RO$Tw`=z+}^`_5$i#Xcnho&2k zcK*veV&>*8)m36JHnYIR=UhW;UGAc5%GSJa=hv|?Jp;w zJ=l)r=#}l6Z-eROzwC6i>s{H3&)8lFFf!@F?haj3leb4|N?EKYDYA<;W=)NATYo+? zzzb+<5~=nREe@d9IC}oPo16zCXJ$ql7$^;RbOCy1D7I1e?#$W6g$bKkKSo*_x}+Rq z%g|7+A?iN^%OayI7s3}Ws(_Z$ADC%{Y^GhCd1iI{q& zHEe5v4~b?+V)$koT7May^51%{nV2WSY_$pSm^aie7oy&Ca1GpJ#l(z*X%G<0^?b~V(mx6YY|K@0l} zg@}jj2Q$R1~Z+mbFoxL|OEFGVwWmvmJty2Tx$)`fx)C5o!N~3AfW|D4*Mwe4ow}LPC0tt`#eU&<^JXA%-7$4 zVq$+knqL84|D$pY0TMkuP^+);K7*j8SJ*etpxYx}mFe^tgHK>(m7a!ChNq<|7syo_ zwq?S&atE)@IH(1h?8D(guXUmCtDx?@q`q(E3=Gbt``|aM5hDIhhJS@%$sak|ivzyC z<9G7CslZ9+O&+J`WGYLd&P2~wDeNQSjsnsvo#>=?0TeAjVL=WpG4wANhTN&~W5-qT zN2OZ2YeM(I*|yrob_;YygJ~ZcoBWgcG|r2^@VOTp_AyJ9=**-X9O!-ebYe7C;6@~r z(E;9vcU27z-UxyOA>nVRHuF%L!#>+^hy+>!hNi7Hu+5y*x}ZX9eGldTRR$iSNh6ij#Sxa zwzZXG)bHNGdyjypuBaRnKu2H-Q8=uMPk4MT%&eKhUwVa$OQ#1d#RuAC?H!?YC0g;I zus83QpC1bJjNNcMVRkjsvoiyOPOPt!u?OwltFm>iJQccJLmdI#3!dNlu7vL`90Bv)*Y zFHWyBTrp8r1d!{hxT5s+mozs21G*LKFSAsOlBz7R63WU>L*R)gC)oVAE^iA8q=5=r z5--Pd0F(|7tujDMtrFD9NqPp{HxBFBm<+wv9I;x>+Ck#aT=NMWmt=mXZBFMqLI(aezj#;dN_opCv$q*E^S=*gLGhpuy%o+Y%YO zph>UWq^6E_0^{g1GTuT|1@SS?Z&lRy0dbG?mfiBBmgIBX!9mUXx}IL*(uBODw0Jt4 zgiu9BmM(8I5AM3d z!=x!w_>^!Ag?!^V$&8W`rpdzq3n-92Fx!!lRn@RML(GhYVX;ET3kws0T>R}bYC5`6 zWgVS!X@zVWLfQ(ILU>|2Ut-_=9iBo%Ky{aySwcdjS`-k=d|3h+t7$C%${&Pjel%lV z0TMg@>FI)JZ*k%958#7`4XTBq#8PTY+iogWv2_{en zajCL(cB@i#aP;%c84#Vb%|3tuAgYZZYJrKNT{WKK7K7FbFyPMFc*5Rvr)aqy$kPx) zGksIu>wAk_d_g|(-llxRtY7esBYuA&$k^w|q;$@6t_4r;-3mx*#qNN{(CaJf`t90m zvGe!Uo#oq$={$){j)1@W0OkXF4!>h!)E|uPiX6LaI=33o1Jw`p!Cinz}k?} zv%cv|VBBf^pQPULbOm-^!2<&}xxbID1N-#fvqggwou1(p0+N9DKK+IHJ-i!3Ou8X+ z#*xuDIARwq{w^sTp?^+yOcCb<@p#CXbnfn}R$W-rO?<@qOKDohI4^)GxE8Rhd0E0T zUS6o=;!hsGdZV0m{Gs}nVg<1AFFLkEObNLPkk*Nx<8U+vW{HZ6qXq|C&2C@JhvB~` z;!&PF_ML%oCoqNw{0HW4Nl*a3O#9rEpN|_}y~M21o(oWq4AB z0rU9SK@pwTWoWz(c{#Jv9BWm@GZNd>WVpoLKD{DfdzhxYD&d?KRu=7djIGlA=;~u#|5B$%;jBp zUzP~^-5S=4w@=;4m;#Z{t?L#1zPV)jhjG^fMF`B)#og_R$MRFzl2WPP9!l!oYa_Y+ zO-@e*kda6I{Vn+%$?*IxMAW^(Fl@koHNGOyswkPFFF%M)B_uZMAI8rldiCe5NQIs; zmYtv~KmVOET!NUd^E0Ik$iEOJenFmLHAB}m8=Ay-FDx4p?ECT*&4Z7*T>e^sjX$jq z$Qy#U-{?4S_G(vu*0|KWf5%0Jye@X*%} zOekF+pZ)Txw_tRksR214K#KykwGEBltRH&k*#&#~&D=VIl%w~B)X&QR4_(A8*Z6W< zy4CG1DMwhvJ1GN$EP%oGHMKSNeoU98qYB*t(JV2xZpqu4S@=j%EbY!@U2HO>ccSH?0j+qwK$JmoR zq)s727UV`tNcrD_6p^a9>uywzqQgzoIZbar#o$n+na@?!gRDv>RNfr)UGNCtGMNCz zhC;uow+M=NCZoce;k@Nu92m40HP=H1p-MIkc6cw!` zEgumm0Ob`PAjd!+Zfd~SuBpn+67Ktgj1R^yNJ|ALH2WZHR{g-|9%u0sso}*#7EeeB z({3Vgy;~|Tb4eB;1SZwByni|gb!XDCVSJ2@-DEa`_<~87ZYRe8(A*G_FPBsMEj$P9GR#1g$ybaaZh?wu?JXDV2(!R>8Isz1dDBHbP2 z7RA&XH;cwhZ;d3pCfj*88}!|p{^ZG{c(NCozj7<+D+r-R3OTd|HZWSM8H&`J9;$1~ z8FpiK)Zk9L-IQrD47aCB!u9OiP4kb>2hX2%k0OvN3^oqC6!zzN*bvFkWFCa40;J;c z(BUc%K({FCU0_7qC%gIYbj*=p4Gp$eT8F3fB}!%lA4+Nz(%x0_s^{Dvt-Ij!#H>ZV zQaTbGE0|ze?BCtPKiqG~A+hiWqTkB$w?n2oud&<_8FWKrnYzZy=(38G zOFLVZGUffzCD?l6rMtBYS zJr*80<>t^x$&d?i;zCJDqk5v~JA8a_sMM5(L2um3YwT}zx#TZ>eRG7)9}Nl0j8B%* z4D=rm2$AF$(F&Lieo|2;lWyjx4*KnLz4doh1#6dD-90}UKY@t}o03{XxAtGwSc3Py zCAb9;J$Zx#X~+CXg;*vOYI^B}SD=6x-hw`QgiCbaWC_8-;vw}O#;LB~-{skY z!_5Lq);(1IsgR9-xp$!S-FuON(fmP(sK&(`niwioHnEQhuh?)T8yeaWalIsWc3zw4d%}oH(Qq-qm3FZy#8OdFbZ7#8 zd!kxYtBe$_6L8Eh+aDx`HI z+c(o{2_F6x354Hrl8A_SJb7B-I$MRJ_fqtzueCLb5Z`gXa%h^xoe7t?L(NHLef_-( z%}_WSv%b=%*%RQnri5Ik4$8FO9e;t0#VblZ%;TJ9qH;_ zSOVDO`mU~10R!7xW6QI)sgX0XHC@B_>~j|>;=AmkH_uAVXl@V>sn2*k7{~xp4f)>Z zx+tLj=I@4E1_F+lEGf~$jE{eeMXhIjuozBg<^OV?M4Av%zGF53E!tGv`-GYTX+4!1l#7)ZvUG{1pOj$m8)dsL{nQ_` z_M~u-#f%wN?B6Z^>N0s^BY1U+JLq%GYB@l_;PR!m-@{YG-~KG~c`R<^x+G$tAW z$5hSVHPvsTv)a=fZE~$A)P`jD4oSo(20?;e_D?q^>znt#BIdT*+3v}=hd=!@+7a+D zsEmc?xid9#z@U8()H9lZAVfm(RXRW;^X8cqK9dY6%?Dp;JR7VonR3NhF4hx^6pJoa z{V@+x`KGdLkQZugEH%KgXZQ&|A|wRN2(qIii}?Sc=_{bJ+M=xy1SAxtJEXg$OB(4$ z8cFGHq&ua%ySux)F5TTocmA9A{^uBXT<^v2c)oM?-fOM7=9;rS00BcBfOk!D7;fg% zCOmCz7^b0t>IsR1_qXlMn>#ZtZsTd_EVQ9GTTV8Yec!FD(18zHg`u@v9 z(}n&Q(>X(yhgGn#0?UahC*{EtPMDu(U}0|nGdrg4s+}pVPJIkc1RDs?o{Uf3`OwC%TZe? zmYjf7n#|*srzyC|!~|YX&+z$yn#i)9mda{67ZhLIuWmD&xsv|1&0G;rFI;o=mZIt= zPJImP3r0V<@7g&p@BvLsZy5aw4K2%#;PrYg#_;(RhUK`lo`H$2Uqo`ktoGz#sA&1( zCePJ%=f7Ne_m_qIy!pQxpL}4|87S;bW;dkitiGEopPSk3p_TwmP{PB}}1u_~^wa`e)9S;|-N zogi$<$#I{agc@bIB5+linPJYH_lal#l+!sbqqyRA!fZJGM5jV^07Ug!Q@Ql^b_`^d zjZK2*dV*NWf$fO($s7_FJ-yi>v2h_qww;x+?g1U8R{XeFtD0pG3y$aQyK@1s6`CFQD&3s9LCll5 zhOuDteka{>8D3$rEhgCfLrm-`cH+p-!^>)GzOtDoN+c+h2Bpb?hkkx39i5wJ93d@j z4_aZ0&leh@3;{b#3$xK8=WHy1u$So9M5#n0+kWUI+?HH9{mz(Wc2p~WP? zY;P}R=kU>6RZ0Ss0KAF*um{ zzFpfl@<@c0LqvNmr0f2&7s$|cRs7oUPF*qHxI6Dkx=o(qVOAji`O_C3{OHFIs!Y?l z%hFjM7A0B=RB~v`Q-m!0h%j1=<9{*;E6v)Uaq-4Kl$9Cz-LA=?yIeI;xII}H@vo4o zMND`76cE^K>mDCZ$;t5o1%(MGx{FhSt|k3RoQiUuV6iznS}B3zJgT=WOEDegBvHJe zn3yFE4Qg0f?_7SCKmo_C1U)H}NepcS-|ClAWf@_=hRf6CMA4r=l_1v6HduT5iUPxw z7~S12?IDZ$=wxMOe^^+Mn$))SaZSjt0PUWE9j0)CQ&P2C%l6UrNl`9=oEFPih&m?MI`iNp;V#|&|q62U>gEZ*6$D~!OODign9dCEz;l5$> zq;jTaH#GceND?6Go~^rI%1cd+0t=?1{#ARsrTNi0gDU%1Y9R6#pdUd(RzyklUu0q- zP_xjS>YVtD#CI&FuMeC+s5tG5f)m+oa*8_C=E_EoEDV*=WMy5C>)qQ?noPgBaB~+y ze$&(lt*j}*j);^SK0*!oq^dZra~H4TI5n?lSN=HIH0T><;w-^*z7Z#U6$#HchV|{{CvJ$WSnv49g<+ zd5mkc-^*=U$P%(3ScQVxHDmDBvi>A44jx!f>-GkdWE9zKD99)otOibMW?k1TV2aVw z?W#$CLNSGADs6F!%zwBRq`cUTQ&R4e2tc~50F$h(t?G6P+{tNUZ4K;@nwp97cnfVW zi!A8Dk_*&|#S8AwoafNcc2za{b>;MQadBDW1lqD4;LlbK4V~Fq>4cZl=Y6H#V(BZN zoRmD6m6~R>^X@oF!0f83Vo;z;JpLy|5P5$(;jl8Emv75X8N^hIu0l<1Xm=W(Tzj{8 zkZEk}OG2JqSfXE$jDsTdo?l?b(PDFV)Q!`jf=`*=+~jK#g_}-*setFHKWf#7P+)$;D-M zVKwUXDQ`fm%+QFrSQP1@xxtEsD>?MP|6uyf#*dc|=8hlv*e<`9L=bkIZ~6WH2FcG~ zN1=ANhlj5vqLBihh}aj-GLZn`bW}Rla;-dtWXJgB+Vy7J`p$|7SVrqJVez;oRa<2$ zjFvQWT)D8j)IgLf>pVF%C&37UTxD~Qz6@!Ec4+t-msi8Hx|$IL5Ul0btq2~ct&g`N zYt=Q1XTL5D;7Jz0C+B}9@b{P3lAHTu59FYMyl7U$6JKq&#_NWM`xA)a6Qq}!kltht$d~qFe2QjB z2`nthwYrabe%@_kV?w*z3>Wd*6vdzS#;9jZC}oR}o=RHmf|hN%(Z(Ql|1A$@=8!z$ z;#$?!Y!3pthwfVcUIFav(x%tX_+vN#7+zRRdZDA+hxUSHGynQS-*`6`2$hWmp){ze zPIh;CrI%@J6t20WkRDl>k6K1>cf|uq)w8kG9GgyLW%18ljDny?pHqlfT`iZAirkE%lk%Kn#F3-nlIB@3GCWajFAy8m0GDJhlN+I0w@%qDia7-R=IVV8^!I4?7os`Z+D3r5PaC&~)|E4xhzL{vTs*M4 z-Z;|L6FiHhc4M_j;lF?7a*aDBlh#jgjV0xpn~TTzgts{#h9il_D1i%zd*}D}S5|l0 z3Q@jKZqu>;kEx=2yYd;z)%Lq1N#;|3+?=eY$j}25h%6c$!W!M(TkbnvHM}*=%K8;$Fl>UyUZQl^@lefYeAWqinmLtkY*vpXO*E1fJNVoX^~r@8ry zk5BjVvN0`Q)Z>eA!Y48^8(>Oo%*%WAZ$5JBptM-bk&)O(w-ntqQj_c5*jme=9&lJ5 zUrSdB$KMfGM$ZSj&l*CvgEu|lcEFT*(YZ;ZPwMj^BRT6_iN!UyhSo zt$&yKN<8em;rG6(3X+j6ek!}4bS%Yq9-@#nGgFv_HX@TM;XKiZbK}q?wEB`mi|KQ~ z?P0M61u7E^;)kv4xfSOfG!<0%bJJaIx5%+1mKCrvJvzN*`2KYPcT#1vtAoWFGc7G> zP<;MP#S3g~gjAWcy*hRdYWqVQ6u2Let#BC;3aWab9ZybaOlaAjDkIR= z=@D`($@t8~gv#jV`1c!@0w9B@rxDXD5VOA^%lF(SSIagliZi~i_hQ9?qDR6XK>Oh8pDGd(af)xJS!A5W|2>p=jXn8xieoj%bR}0j2?((!MbCARQaczt@u`3}b0hKR`>r zw&7HwwP15{(1t6|ryIVoYBtPAVadSL)pKL$u?y|VJ zNnfS%)Giufv5tO=_(3OTK-wKxL764-;x((4I=?a-BGM`Q&&`5Bc4?xc_$07v=r8=hj)iqpT9?Z+HC52 zhe?epd2?e}RnK-fBt8p-db%6;J)jQ~Zi?eju|}7j_Q}5GfcV*MR(z^WwKQBm}*J;^I!78n@Ws zkt(K#SvC0yILO(r%OenF%F3aaXTK{nONgeXCV>`S1|}PuBCP`V=AMde>U%;eHVh9@ zQPi-+n_31ormq9t*Gw~aY?w67&4g%CLid*wgI(}3 z{Upf_xommZ;MiswN7)bIp+~_mAPmnPvQNdX`lx_Vo3FeLsCg{Rpt#PcaLPq=Ez`=x! zAps^t;L}krB>K1mM2w&SLVah(VQbW zNsh@!ZVuJ%z%Q(B*RdEt3jvqyj6uEeg6#D{g$l5dds8?*jh-%l7>;jVKQJDOB=#dA zQ|^tqIyC4EAaE$9vRw{k)Dd%3ncF=zk0x6HwsH|7YmaB$bOk$`TrEwFi8Y04IYPtW zC7qogOdW?rlsn@kM*ASLSN@jEW*d3fE>u)v+X1GB|A4dFN;Pb%7+Y(Q{9u2+OthAK zSCuEk*Q#pO79%8&VinFhN_XIL)vAzhqD78@7OA2&rn1$4x$OB4W@oAB(Jz^eBPC$~ zNg+?RK)G6f9DljA)VXJEEyGOBnz6dhvfTP;uUL@;4V`IeF5bL$^FvY=SWOlZPPTOLFSW!z= z6{Y53bE6xOXgxzdwwS@co2bv+Eu23!R|i$ac?oNsVr;CN{RU)WZmt_XDv7u|=pL2; z=?+jOlq7yD6kZ_s=T}i7uUulaw`AE`S@Cwzicxdq3&A&!4NFQEMp(p?+~$Q zgXiCQ$aTP`Oh~YKxTys$@Hir2CY$VyC)>Nq>-(~fLDVct-4mItLZVzO$y3@X>;BT@ z-vUWLAW-MJI@; zs1g>Vq4=x`X)SA2VMR(^yn7v-Eed@fKmcDTES$AHT%$(xyBlUBGL_43)pYj5uf)Wx z@joGMvEp}2ojzS1>pC0y;^HAY>c!0f09Ya->YBekf^MHF_LGW?q~&$=lO``|b5q@K zbg8X)xG><)w`>_PQm7HWZLO^Iem$J&`U|dbt=I1hpIRn-0g!?K6cj(t({_v(hqDaX zlF7g*j@YQLi144#d3pb+?YEi4R&4H1CfO0HN&O!eU~Q=@XEfQ*7adp$9!&aQrc20R zpr5YNnrOEYeo)WO@=YsNK`6)Lp?YYVG4#+#SqG-FU5CT*>NM&sfv)wq=TkEMaPQv5 z`%1)%WB=hJ%hRa1eEo5TTT$HU>hed4V~xW$%QrN%ul%S}&>8Y3**CY`|Crr*~MT2W2oh?vWq&^YKGFC#TWe%%%+8< z5Vh_RO8UL(YNLaH+dYfLDsWady0-cx`BVR4H+bR*(tjvvcL?(c@V~HYcLxj4S74+| znZ58XV!#FskMHFYi$cuvx;l)SsUSY5 zq&nwvYU!$ehp?EPrO;=Zl}sXez;uk>&idLktsV?6D{SypKQ@-p{79M3$dIXh(dTM| zz++;ePh{Hh1wFc@73*-f&lG5taj?dnl1rKV{WYnj+W22`EVU(*;fNBbH2|PH3DC*J zM6?thbxD_|xKDZtiLY+(XR>&ziHKvV2u|A1v?ehB_Rw}gh$-7bJb_d@$`Dq!cUMG1I+c4}&2&0M(!fn%(!6re|9f1t|{ zNL$;GfYtu8sN?#*KODwq0>YuM=j<186FtI*^#k{3#34`vMQN=Jag^rxO2@?w7%;&A znQ1CRT>xNgPum_PQ=sB%R&=a0d5Po`x8~%8zavtN7##0B!#4eCNCrGlzN^e7naedc z{?DQUTxsvn&W zHTSVl#KbA^d5o>^t-jg|LUCiTAz^m?*0#C?!M`rpnBoTp#|^Ibl~ZpW6jUjJ?qc8l zB`GS}0Rt#n+S^H=AJv)Mk1$2-v3i?{SIyk$i1kK zRr>W~uH+i8R9DrcaH$ALhQhbvb8|~#GW8@mJsbCx5cs@E@l+YU{I>`mvpO?=g z%`+VDW0_GB1!@=!M4QcaU;)k+7#K-hZFL=n(FmzK)=)9|CYppiji#qx)zmjU8ZUdK$e(V9|qxHKj|{!~u6teK0u zwm%K3s^{8YO!rvx@@Rm7+jkX}Rbbj}NvEZ6s)*XDkQXSf@J)%z8PIvPu(=b!M8f4} zrXXs;%#%jBl9j?SpbVN%PF_8pi$b8^9`*+Hh6pRIev_lXoT{Y;Sx62#hF*^B`rf){ z_JEnETP!?EZk2S#ehEGg=42x?peG9BWw&N14(-pCn>Z|DgS0=a>=8%2yji$EyiYQD zmZ6(OOaB|N$}_Y-M62<=DQoUY-76zEg?V{m)&$g(j4&KicRC~eK&qB0)JIe3YZg&2 zwb9HTp^PC32153Pj&zS*6NfsA`@mzfEL&SID*8^(bsaK`RhlNqUwSFxQn^3%OFe1j zl*;O2Hg#cOV41;(uD?=$u2Z`03mh3m)AX|PaO2LM@hb%JYXp4u#r>{=Vj&zI{TT2d z`)~j00soh5;MVrb!XgXtU4eKecdZkna$g_2$2l|xGt&pnDxs?p&Vg>9sR-U701w(d z@8Ue+;w{{sb!Saof&i{DG|EzqFO(K& zEA@VVIgZwg1zq(w<<_H#Tl^8x^2y4~G@Y||YV=fzqEVW0UtAyqHU z-4NRL^vud^k8e8hS0N?s(9v=7+yV>YgdT6mNz>PH0&X!tW`p`$EN=}w%@63SAb5l@9h5X!vG%c+5CtCTQ1d~pW_nYIuxECh@fUggmlQNCjUnJ zK_8HkUfDVr0xc8F!5y$ovJkO6@faCVXOq8tvbN3k3B)o42QU+m^FV-f|Mba?&zT9d zYBu{VEXHCF)n01tF_!cXK#Jo7INUBkv7~Dt@M|qLIRb(}JPm*GRAx~L>dZ1#c}vT# zZTey<&W5Tgs_8e7>jyExfNESWZ^mYKQj}4d!fBvCpqySqr@PjaoAj&JCU~isGr0(t zMNoHnz~vQ;IDvKdIadCm-V4fk{?sGP?rr;Wdw9qVzeg7l0e8L+o!NIrAzLi$VZn>X z$xtPMv3vKx%3kK{+oK)dY=H>Dg7h-Isk>ZKdO-FS8?R`&cTLI+Q;s?g0Bt28x{^o13!xmBk+4SP&=U zoRNRH7#(At5<|(_2R$ zzB%3NAB+~p5CE^x$KxSr3jt%c!EsJR=yP)VctExYRt%dQ1v|F~?+-v*M@3!NH4aM8 z&fdpVkx^GK<34{6LLCUjm;Yt((^73-jCetf98UUFYBodum6h$-0!o*ak=lettBVOs#c7kYh0_>~K%RJva@ zTjbYLAFN`g8tqWE6)R6G8F3~n2&Tf<7S|$qcy-6&rWiX-YXVWi-hB;!B#L20t!J{%e z>$Kxwjt1||e%6`b{5JnJf-jf}#$w}_DRq&rh2FA>!xZ)>5|XpgyK|QE*5hNavj^#@ zy9c`jrWpZRAbczEJ9PH2jrD7mm(dnK5)y#iSgrWk{QM%n2%dCx7yc|J%?1YaE=XEH zrdh7RC>zJaY18?LtDp!PQ(M zsW{THl4sc;ejFTBcu@DVWYUU?WhNu!MXMv*CCI>ny8qD7{4KN1&ldA3-{F8 ztcum%+s}4J^L8})4Vb~h!NCPRETi;w0EqSMXF4x;gEcWh1Qu~qSS66I)qW4n_-l$R zgGzjPc&tpy`n_XRdY-hw!ppOMd~D|5VMYZiRW5V25UO{pv@c-ia6dnu+G*FqBk1XY zh1r6PIe#eaO$$zhFR{rI^yK-Rb4A6av~^!KcIjbZ>JETVGqdQGjig>+SdcLyI6nLx zPnJ6_NuA-q+g$A8`GvT=SqUnfWpR7&cnp-57K-A64l%lNICxVeaGF@VZ2&t$JkD19 zO9#|Ra|`{RCgUl9V&tlo?n{Sxz(AX@fCh~XDK!E;7ngr?Gjofrk(_z?J2@|x(Mfc{ zj8ePmYCXB}w2v*?k6T-njEuh$njbZ>6McP!TzAVb@QOykNi$h!eO<}GWTN8#-CPni z*umh#v7D=o<2ipsB7ncf z*l;c?vamuPi1h07V{6>hX5@a#2s^Z^Guo!w^CuBBDvm|Cqdlngz)}gg)%VCzS<2;1Cl#zjfHzi@1D%rCjB3{G|u5C?Scz z1WlrO_Xo;xTy{b%C=lpNck5hXfVpDj?0450&~l68=gXfjR_Zx{fxZXRIg48>wUL=)vsGSeZ}HZb@D$7E zT2q?&+K_{#x=IeJ!|;^q)(yaY1^aV5E`H98Dn#=MlS&Pa)EK*1^Vkd9 z>OZ~t`?dxd9rdYJ>-9tdkh-w_2-lJ#?U)h~w^-5p z8?}MD{0@MmvFfdfIa|}jTk(<9)!UN49atd!t9!dy0I2xpYTC+`0n{or_;6g3N; z8*=GJ+S#yra>Ciz*~U+7oJ&Ko4+yR|C0md$)Qod;7$|;_2*kvF&Q8n!HkMJq-usdo zFPc$IO$HAQN|Q_r3txJ5hx!1Aey|}EXG?Ji!mvq;)PG58;ggAz-czB0f* zO{Mbw9;ku0pF**alDwEKD=3T`J>2$-(UIQ`#WwTAG!F^#@HR;2t=g(Ee!255Y&HDQNN;Ug6DgJcLF!wK|2y7?%&k-g*;X% zw!jcnRpm%{Ls6@(=NgSsJFuTBEC4QT5p>J^2n-A_y8JkohHBJ;YZp1wstJ*hyjd;u z`Nz}jZ&t9;dUvsy#Xuy#-WeVNGuoyc96j4Rj2*qb>-Vo9J8Rc!AN%0w=)%RpSZAuj zDxI!%okv)s$q$Y|SAW+bOo+cf4&;z)KNr~86Z{bG63f&P)*%F_6?h?2Gkv=jNT+p7 zox>rpML0-!(&C$=Tm4u^8=bi$l2_icTX+DlNH$BmN@q5rm^6VKAKC-}4tr``-_-MC z249 zgHlrrlY(#n_Z*>8mQiZ&Zc9uo5`;L?l#)2O82F*h?W~jR_MU|^sU`|p3-?>Wg5Dq7 z^RMB%yWGcnbI0zMe!ic#4}yRm)+>jtf6JjM5mcVTQ6I_mmI&$sYO|UOO^Lc^Ckq#t< zE$+vdZ;1t{#Xx%>bepPex_n_SK=2Th|3xHw{l~ND+v*7(XCUV*SW?{ETY?~|?K7Rb zqx|a43p{*J7OGDlP3eiwRkqTYV5)|P*rQ^E&ZY}TPi_I4=w|=I4G#Rnl2Q!QZ%@?U zVnD68z5aCXw6uW5ZS=o5COJ2C2;d#}RmY9g9-C=#xF}A~6IWF)prK5aFj|rEq|QIN z`Sf*E;;>m&*RlRaJO+}mvhv6nmaF<%^^T*p1LfiDuONwc_t^ddTJwnEe62aYja&{^ zl*B%{@7~#!wJm6mj_r1a;a~wrLjM5#rY%wrC{0Z`JNwq`y%v>u9E|>ye@l#POj+$A z2=us|?>jcNTAhr~bU=;7s6=&dvDqVHYKLfLZEVNU;c;#9HQh!0ioGN>%(+OLFYRzH zV#?UK#q|)C541sCdf)Z~#|aj2{arvpf)`56y8mN*1H6pNt#=o)I*5q6llGDNL@*eO`bo6uu#H|^jxa>{r(gA6Om&Y|O zLL{QED0Nk%R7x_*ehIdz3EY(!HC>)4%2AJ6Dqz@Xtgir_L(@Fm8erDtfy4fHd@OCp z5(>7?`B*nnt`;{+SzaJqyq6rf(`CEhJ-P3R$bG?JW)7mJD5!pZ$HwN*@(WP{T0vr&)Va|dYVG-QCeHhs;aj@vusiELoRPSIf-o6Oq7P=^foUz;)StC(_L0? z#0r%q(!@y*H<(6NQa#extwo!mcsEbA6%=%g(|P9;lXUlvN7l(gTlkW}H&WJY>H%Hk zZ?Un6htokj&yQP9u)5@-+cq?zp`f@dtzHoksw9F4lNJR#POElP2Nb_f91OBCZidE` zeelpXhK3L^DvB9@2NM>u1H?YQg-is;9?Y|v+MQY=!9z1=+ zxKiS}hqI+Af37B%+)qGa0hlX{O%n0SgB~t7G=}1&^dIpxs?85xo}bvAt0?Y!U2op{ zgr+aNA>XjkPgYm5)&;@azvWnq4QW8S@dSp-xY}!+d=gNQ z4L94-2{>3Qli%5bmb!h?(iHdY5by8z(3Qp>t3UjZ-u02o-rw04iui4|g4GkMcR z3*VIZD|SyOh-4ubHg61o4LmN;03HXIy|X+1IhK>3T~x4sT5I?y#aJzhw8~{4Tp#ce z6hYp_GY941sUQwPf`WJJ6)a}J-U!{X314mCHx`B-i;I(o7Jv(S3~f$Qn;&0DtOrV% zi6DmpOSvhKLjMA6@{ybCSVXV(4~;uJ+_viw0Pr~<=k^K7H7Jg-&n^gTxv*(DKCURO z|FniWIL;#~+6(m;Eu8uvBwTLu9*dN?XUYlO z!^59!u8RsP`6lb>(KzwS@wh|`Rk1*XdSY;{+V|#PlTPRW#FUHuG#i?cp z1d_?e?Xo#6r%Nx`Hy|l%rJ1AccVuTxS28e(KsgZp9j?yY3=F_V`%T7|n?CKJp~2kJ zmKS_xA%0;F8W}YJ96Q^O(`mnhRhR0kT{eXr`unQLXhH#x0d&k3%{YzC%XYVAl~1`q zvWK;h8`4QA4l0~H$J4PS)*5AAU~dSDF+UUg5N1mKj%p~Fvt!S9rJVIZbNUsVrAVpj ze^-MAB7myhlQ?5N3EbB&ZTFmJzUNd6(B*H-IC(a!seai%9KzTeuXb3hVI`2Ej91W7 z4U+zhi2dyrxemy9+S_jRpIL$2ilzA!X=6k3?7~Ejj9=QBgG*00;q-K^=;;nJHh64y z*1nDu9o>e6GKl=~hJ`{qM>%`)U>+4~a5jI`*oxPM*(t{q4pzK;qnj4fbUa&gIF;Su=xvtn1Do$pWD z>%L3c1Zxj_+E#XG2Y{0}Q|K{`aUgcwHj)Q_a-Ls&1MF5m!4Yv&7cczDw?7iGYQ={v z-SLSeUa0JGwDW}EbQnO$;-q(;6Ehu20_B^2N{;o)zyq+Y%YGtNaKR$lWuDeC< zCq+|T58yfM9-VKU+gcG8R{X7<%ColXg!J);_zvO$;6Vc5$=z_vPvQ6q}D zJX`nU_j%*=fGkh;rzLuflJabZpP7PPO~pEBN@?-h_=ZJB+KadO8yPSfUmAkev2_#d z6kfg}8%q+1l9v9y)8{Sv!1ASDFd*Uns2p&>ph~G6n6qq+-oT&M)Rcs3Jy=`EV}_r2 zRjdFa0yrEYXt@AhVoyy+#)Hl@1x;9J!pW9T(}U~G(qIsrmR8obV_(n2pBX9vdHFl{ z3;idMQel_W571F>)%(5&d3wp^3uzj zR6FY}&Mj`+AKKRb1lYGi(x|0=%GC%jlvm9^kW~PrIo#Hi6sNr&I9@KtGHJ9n5c&Z7 z4uo!@TIHAD(-fcpiTk(IKh^&1U&X20B9^_uFC(-2_^3db63 zdBn6{3`?Ldn=k#eb!W|6ZjU!aMFrxr-qJ{-j@2><)6|6Aqz^|9(D!Zb zVb4j;X5{F2@}`>eY@o{z=ypDEaKBAx`9RQM2D6h#h_s2S1lp^yGUpRA;)KR5D%+euGVz~1f<@#g0vA(eCs$r zb*)a<@W%}djHO~xR%}Y?^*7YE&hUba|5vZDcGaR>`%F4r#|SlE$9Y4e))zV*+_X z(CS+Ms@7H8D32^6boWfZP^L~&giOfz6sDBDmR&K%hdET;$I)Aq${VgMKRq@Ds-^9B z1rhDp#4q9DEaft}c~#)d0}Dp(z#af09PV2%U>^(6MFWs~8}NJ2D(m;qA`R>WR z*#pmey91nE=NUK?5+`?#%B_JygHR@+-5|106#%T=P_e}U%^5UaK(jGJ*sr3ix~hVa z(RhN!G`zgL;4mp5I_ks4Sx3VxOh}Cnp+U+8~2UqU&=!6BD4~ zz2!xS4)*5O8HcM4?9wW(3Cg%UYSqt^tF6M&-(PN*Ic$c80W+%y06jRkQ=ebi-Ej+= z+}|Ke4uIYCJo}-d(^#wjpry+L5G)9a_khob91;wymCfsxbpP<;Gy7v85g6_-)*h#G z#S_t9JxE@j|4NEL0wCUhfWj=?>Jk+94@4ty<)&oY*jm@s#Bh>|i!Jv9V64Zy7XV`;7~0!bx?k0^ zvhH|@ym;}r{#zRSq4;@eECExiFuV=p%Ei!pEj&?vp~$8OP@9x;?bJH}r=|iBYQUCf zP^KuDIQYqBUsQk@3}{PWN(8DrmfEzg)%q9{;BvwxoYNNPuP)R!IH7!Ey`s%ip9co(dV6o3KYd$lmHD}*8ZOx1;~t1R6QGD z1W=ZjY|9EjdIXM;B}}%u`q%^SkQ~QT^kJvPMH^7IfF|Ak)dPAj?i~mF=hqQtTHZZc z{G+LW{)a2$+9yX8@;WtiLbHXcytNp}52Pd~)FgV}H^X&b$YCi~DMxYyxIn^j%P0?# z32?Tz?g=g!jQF2lcOk?8+;c=!a_5DHgDZ`r7Zdw#kkMmjtMBheRKt{|J#jElQszF7 z{W8=9!rAg0^w@*9_1{qJ6Ie2(aj*Xd$r}}^nc9UvKz2gGMmap#757X@r;|CYcPPV- zJ;&O|zs3K^(04&x{#yBhYyKLKbwdc8cn^k2IxMayLqp2rB^%|X&ET$DBmQTS1k^`Q zy>A~_3eT8}w%PREf1VN$3|ei94L&4O1cTPwBEmsYt-wO=AsGIBYueFLJxZP7M`1RL z8pC;^*9Yoc+Ja^B{pR~H_5)`VJ+k;?gyys-G_uS)2Fy;x&ff7-3r@W2UHt9k`1ZcYi`9i@8 z4Qj)E+C6NtcU#Lx+G~G8mXeF4;3#0yXO&9$en{YP2By78@HvI#Y?zW4e>MOtI_nRi zl6>Ty!XP}7(c|&Lhg!pYmZKTHKFk;yZY$3(r)?o%uJOD(pxSv;=z&IaF^lmkfg>7f zoTuM(XJ;T*{Gv>od~cD($Hp%xNaOy1++`$fbPHJDSsutk#?kef@5@FNC*OPvXv>1s z6ox*?m)YWSWra%jNUJ(Fs~b;~Qu)#pzq9=p4S#GD0mG!stFY8O1oAbnR5OH%FZ~Q8 zyL&`}``o(kv-utdGE%7MkvxNnl_Sy6`w}Af`bq?0v+B(S3oMLi0gCqe6|7GWDm*Ai zOkgXap;j9V=q3;`d)sG6C8}B5U~=vU*B|VN48oRT)BV#VzH~487SNb}ClSBNV*r0~ zSmd?C!9gJ<^>IiQs2-he2@WJ%%BLg+OJ@^hK>G(uW)ix;y4#d#7AM9UX|;ud$>OYS zC~arIQ55p5sow)3L75DcK?73Bn0H+WIVs$fF1Wkl;fQ~>KKhsZ*+F1WZ1 z0{Z)~+S)!g^=FBkXw)hyKuwi$d=#3$WB$LW4ElBwvr%lHi@32dZif@x9|1_ww*RmX zy&?VEUO3aX`fXoA=n;q3{(DAe^7FO;{EA?rzreQMW*tIKz3xmBIIy_hX39C#eL8j)ievW{TJEt=G~_( zwW8DY42&F0hYD}AE!Px46LnUbGNr59z-E7&&X+D-`YrtbF7L3qis<~WmWvS`F37O* zb;psrDO7hLFwTPj`u*M6M;BVPp1*Y2>NMrjyq}IdbO%h!Ylkq|o#cgWY(5C0*Pd7w z@*L5D#eYiN`!-wmehMdsPGHsNLOVAp8*|x_Ae>PBFf5fbEJGqSMG?!=!r@O-MV}q>HGJT z%G>mw`iRR$zrVTQJFUaN-UVH<~r9skLDMMukxx|5P}mXx5{>mOMbKXY=Z z`beuNHT2Ia2PGz^&}oj@-dWKD-f{y}(%bYV)^Q54MV<%uJ-gm4&PgGWwFhA@xW3!P z5A2&8k&%Rrj+tTuCl@W94o?O!*EzmqNj`Tx*L-ULW_9iLp`f}O-CJ=z_xEl55#eBg zUCjWJ4c(AOfeR{tC7_4t7wRMG?6t+$-S(Ox3N z6Q#*m_xFN3MQLf5Is`fBu%#w6b4R2RGA>Q0qR2@>z%9{pIt;Wk7{q00G1} z-&)X}6U*LM>@q*?Tfd#;#Vp6b`!TSfz=n$IJtS1AXbJIH$dZUJA`=25FZe)<0Tv0J!Kq>``_->uo=Tf6yGX zzSnC!$HPn%auoruikMED4md&#b9(t49ANK`h2??yNeqlFWoR@*{og+%6`36^zO}lj zR+z?XkGcZ+)yhAvC+Q3>U_mhI#~a!5oO0Saj<7UXdNo1V=5ipo&^162NB+AUA zE1ugwRCFSGxMLtaR`14-($}5GY}0JR zTu=7W_EN@y+N@okjAM~pE3G>ARHhGy(YOPp1@Bt z5Axx<*)u}s1GAxTbaZxdG*Fff4jv9Lk@4 zhk|r6unrccf+gbN2S%pt2bi#W`l$A2X%RhMt5MMk6B#ml|Jfq%Ev}{^V5W z^~b|Cdvf0)$$V}V)sF<$57Oaz=Z8ZbHiQBSC3KS615z}y%|e7a9BD%%&c;3kkwm2M zQrX!fzvXj*w~tQ>jWGJgTrh_-RlKFU9r&TrX}Mgk+{`17?+~EpbUs`+r6P5AU4RX@ z>4#z0JprEA51?UnWngZ${T^Bsa(&lOQa3Bg;orzY9c^lz`6&2T*qk=~pXtA`xtm)N zmghYvV-I@2e%wX29Mh2%dVde6h;w23F52FbmV3NEj zx$-M^4$)WWOF3=V!yHxZO!4PQV`-f#qQq}z&+^nLpY9mnYxX=o5|}ty|Ep!NoYc8R z9uk+K-zlg+D^em3lGl?D#67IyNZB=07poM1t%#iC^}Ix1W&ZRQ@? zntKnf@HvV=&O)j;8sSmT?4snw2@q)c>WUL?a^y%==A;ef$TT&EWOSeEwLL~V=H_BP z;RzY`G{r<_c6IWHS5{j52p0OvpG}$SYjZoEfrC|1@i#h7xoa>w6^!HA+*y6X!XnMd zFW#RX^aegnz(-6SY9cR;11#Y9AK?b3#m)HZ9bRT&E>ow=7v1eHldV)enaQJ#8VYuC zeM9aclRnZjB!uzcfb=WoXI~TK&hN}RB+Oky^TC93gb?27tRsJ1J(OSO6Y|S9H+6;n z7Fz_~qX^|0zZOm7slJp=-H+uh$UgFg>Z$8M z@no~N^aI@BR`Y4pJJY(z-VA=VJW1>ACCg|`?s;@=ZRQwp+S7J|@z<9?{#u2 zNEW>gW*6gC7LeRZ(Shn}MK#JwGNEF8!gq@4#HsvCc30L28NTax>$hvniD0bu(f!FMy+U5@3WKxjeRowdjxVCC50rFue_d`X(1=NCx*7D4-2_Kz{Lj70m~J z7nx9?|9Df!ef9Ta#-oUb5ikGkiE@cHfoEf5X#Ltvj-|FH777X_4(t2%b;3PAjODY- zsXtD9w;B@5D-z7R&wG3pS10DZm3ocbHwn@1(w$Mjx7Y4Ry#W8$cg)NyYb!}CgOUmB zvZu`s@B+49z29Yy;c~@wCtDu2%@oe4NvDk%fWO|;qAD>c2vtErGXM`uwTHUA2b|BO z)DptqJ+V|(se*&QNv64dw6(=DoqHgT1x#>n!GNkn?t5&ob*gm!A5B*s7gZN+!JtLD zyHiR)S_J8k?(Xg`LE@tYq)Qs4ySriN?gr^@2Hx?#-}{#tX71d3&OU3ez4u!F#m^b? z{2N_eK@&v0(?v2`a3~cUo4v0tE@#v=Rnu9r+P)f#*?-s}TA6h%Ub)6c7c~~E*z3#C ztl!vgl{YfczO0$>kXrdq?zVPCVV=*yXF;0C-WyeEZhG=dRK+!%{gxi?S~}sL+KQjx z^SPdh>+-Xm^FlZ2Ghcbs>nF!cJr~}D=LUns*j&Qh!dYJ_bMqM|X%y<(&$p+uJ~p?S zI+>*d`SC7{sAHeB6Gt#e?7{v%kkQs=Yir9Bsdj5i)@e}(y3z(<51|p49>T=M#Xnss zv6GPG6YZ-T&&~lAK!%=?VX&*Amz){)Mh>qc6a zkK!ic=rQP>J$0BdHSzZBe4$T;$tc9r4YL>El}b}Bay`HGBEMhLcyGPlap+rRa@1gNkfkYvVPO8F6^>)x z%D%LG&C7_zXXUY~@HYG3l@D)hW{kLTtro?- zg@w(dd_&Bq$?8^yEcBd|Erf%e2lT&}Wt@OXN*4dW+hW>-;0088_XcTQFri|RReRwq z-t!G??~sw{m+B8)`HrRszq~L%H|}X-VCRv6_*z<;mL+nm-m00%ROI7@4355hXriN`o6ikhsv$f67KMC{1Rw|kdh#Ow^fMa&~#!sq`VoI2_axy=0E+2^3E9b!HdS#GkbZc*hbwXb(>zXfvh2=D3s z-5kqNA~PBp`@%DAtEE|I$Ag~d{+l@~!VB?YaH5jOKkK!wih~>3h}QZ2+n%*G%A?r~ z-USknviV3IX|xg#Bx}eJ;3xxN|5?(wcZBK#j~8Jy5_`kZ?_?j@7~&J!Uq&||$^5{n zWx~7$IC)I9He+`OFBlk8#WErUeB&@Q{*0FLQ>owrD4`Rvw4i-8%*K$H$JDInqU2T^ zA++n++{5hxgCN1S`D6&P5qS7$fix7%eX$BJjgTF)L?-ohO}jP7)RYR5U|OVR z>C!LB;-L2DAqw#G<||QUk~h^^Zn43D6Xw6`#T$-rsCA?s;QUWssI|875oau(_0{U%+4EgoOnU=BU*0D|$95Z5tT0Q|;>m z4pe^TUN-2>~H4wu3UyQe?(PFrQUQbEF@vl^3L50ycITo z7G)*-$kQLa2hefp^KGJ@tI<@xGQe79V;MKVCwyRlPB?3v)#HK_6#l`ecXSQhmOdh4 z@7+VnzTfRR8#TEw;^%f>E}-Ts0v-!43y!`8eU6@xmCwSuKH;i6<;k<2`P)~onR$F$ zI5chUom5o~-vZB{TCfST216rNSnc&KSjiHl-4Ez-1@E_Ux`X#;X9@8oV}_~!onOhx zHDdl9#Mq@<@PC))QpNUPRW{ zTeL2VpasH5Cvkw^M?<5Cggj#s<>3ix@Cr@Ox)d9(AY~ILDZ0OO#>F+t&XE=xH!=)! zaGhmdI0WAVctDUqQGA+DFzR<4jzGb}v1H{e&8$~7%?|@Ob$^l?g}Z(VV2ntWVhqH0 zBjR~~^cz4h$s+jQrwc|D(J*55OnBm@hG$FnVL%3t&#%s%0+cVNNl&K-Uwyy2!hlxJ zmdbkE8Sv?P(x(6cTzdz$UB8#`t?u_Om+;y;krpe9zc#v$?cKOHwgir6Pl>&_}cmK6qng?E|HBR z?qbRAct~d{>&IIlC!V!@?(;ZAdN|P?M$9)f2Md@A=4+vs+jf$NOi;oIuc+{@v$fHJ z;wmFkiZ8}wp7v(?nV!$-X;4wqBfe+ZUU6$ZxNZacU!DTb{kw&~UUxS>F50&fZ3#mP zaLEsA&5cdj%EfF6gB3t-p|t`nR5aj6vPPp7VOOHw0|t?IjKvg@gDV%81R=x4c3h12 zV<~Au!Zmlb5P!>mM*YrOczVsM|B1c=s46KneCAC6*>n^F9ErY}?TX=4JR>&TYjN9E zs@89b17_Vj!`1-5g5k&wV}njsJG*~POU2?*AJtjM0Unr`SgEYE(Dv&}{NPeL#|2D` zhwskS_GgAue4!=DLTf=F6^V#^*K7xFp{&-{fn*R|?d45j$H{<%*xemjyA^bWvDFjS zo{=RQ%Eoqed$zt^s z)||oKJVp{moSkpvC>0DaG5o23{P~HNak@&3__ILyVmF3T#$ql?p{?!RlDhEeoF2#D znM3}Y8`{cxq0sPQdK&pQF-^_TtW+9=FPPK;{_x6g6F?387(5rzX0JtZjW@g;Zac{|ZH9Ilo|0=TRwXa4N7M>9C&Ef<2y4A18 z>3sP{*gRX5_3XQ2WlgUA1jXoRvWOZk?&^C~u_D#W-i2y&+r1{}`R3RBI=kp1ZSJdM z3nP)A;mB*iE@wKOkDdvAO_WemLV^SkgKRE;DCOk-C4cAFoM7Nr(+Tif1d&-LR zr~+X9Ps+-`R8btL;kFghVKJ;kd7n2n@~_kux1~jwGLvv|UAefKxfaN!Nl36N#}Q5g zYnzd0sD_K`+Iykgv-AGi$9gAME*h{rT3V{`@qjpK@5IF44EkShh0z$M?o9O&-<{-; zi@;Q{vKx;5qJP*rg@;e1=dQHbU%Y=SEc|Zj)9a-g`beE-_PezgexN_cYCUfT%tds(N*!-P6 z<_AA{s=nL>o0-mP{WIaiZGGR&^+%m%O+!Npa_YRmdG)u4bMOF;p#^H9cYY^R-pq-{ z03!T$*4EpiVhrF|sRF4vh?tOMOv$;j1Yiv^7KHp3s6(&-k`V;>?s@eumX?SZh}O&V z!RdS)2&iomqN3a_EixK)knlO(@C`7OZ^!dAB@f&3(iU5+<{~X5E1L)CCqPcaJ-o>h zTpnufohDY|Np{DOCzPL!)|Dr{6%4jjO$RuLY+2;X7q*y~uMq?)DZ6*0@GM}md6$an z7mcaq+A5JydzA++WF+tEuff-764j%HxWU0V4-_Kv?g)H2CT+a0z%33SA5Kd(xz_Gd z_W$n%hzg6Nb%}mU;yC~ZQ>!@~L&*bdo0OIk84=#mNXG;Pro1#X{bk}G=sVQP*oe$8v|8zClPZfrj0?{IMYPByOg`J3G8yLxi3-leCN zP<^_iFg0Z#O&inF3P4dl~By_@J7;7-vjDcr9xIg8= zxu{b4`?Ku7bj~Xk10=Hm7y*{Cr$hsNz_=L#10lQUlmq|$S2)FMA|?iT+9PF3OFO916_J)_>71#x z{$m6vh|$qL0HTHm&;o(8jrDb}=bt%=7W*P#H zF2waEj!Ld_AnfKsr7UHAXOzrlC01(id#}SQ!405VevXz60(E#m5B3C6hg9AVnZ_c= zEqK~6#x2VIxghAjIj+3DEwAPph_JkYG#C8^yYT+f?zgh#@b+_})J6#D)BfZR(N-T1 z5V1d5>P)rkGccm;9+Db`Cv}HKBQ4)9Dxg?s7YbjfPCvhy6%eFjm_#E;k&h<9_N#8d zO_u2b9EtNGPB7rlii7SiDPirMKN_B{UNo~H1=#z&N`Ecs&ITKID?86)K}+c)M$jbb zBKPLCth%-w5p?dlpduHIS(9W9`pK~<%>i}`Wu?Znb9nd{z#I$B+@O`3$b4y5MhIhl z!=P(4{TMKBxCo`{8?s*lFve{%nc}7Xv65fImnY2EEBlG?KaH#E-R8?$IPV)OoUnK?#jrLlI~w*6>+HC7oW6_(WM#dS*T z_r6v!bldXPSh~5+Zc4zF^93g6p|kOCI*vG-t$s>w=Jb#C#j+R*!n;ZHzea84A@V(< zn&+&ZMz#9>jCO7K{o`4#fD;Bjj?SA?>B&zpQw52BqJ}oiTQh1a!^HBE3RT9EC_oXX z$iWK&$weWt*4kC~Z6g4Dl$aRB;P?N9o&GeC*=Zd7yfrv#3`9X!K^aRvL~vOYw@5a$ zF7!^a!1bP{uI{Dmplv`__8gz>)g=z|RXVr1jZ+TLyU5JBN-Xt|aRc$=b zU3izA%uu5V@`L~T_fYWf<7Hp@6pFKgFQa>?6=(tXnReYEx~qHXQ&?%n z;lXBC=UD8`NA-KDvzbN+XlPL1-6wDEnMtW8e8BDEJ9Ve9;a!kVZQ(Sqy5l$)lN!#& z&fl=yA8LNRX7gJW@yqdl`s=V$(EGi2D{p>`XPfk26TfMMGcdC>`@1`xl)Q`r&KcnR zw(;YA3nO!}qD9PbZ*{-}g>XQ5It!u)BKR~FO)e0u5HSF9j?4h02>ei3CnLfQN?DUW zrSrKVY^hdA1qeeA*TwFKTOft>II28+I`zviNf1VIVRRHWt{q~An$i39Lv`9C(F1wO z0MB4f!Ru0`zrfDY7u70tdy;SkAXwsugn=ClP~%wtJ3$1{$_C(j@%U8VyaHrQCIZ}> zEW~8C?oa^z=j&O*0gZjP{=*$$f#W4DptXxUTXNpL&}gdh4nT{44@Xue9OqxfxF-U? zR!{8D%ok&0bZ$EwBV(#8BXMLXoCGkFbMFC?x+G*EZv$f}ua@favQ$MvPzAdSsRE+>liag1;oCknV5TB z2LjH>{^71^|7Q}CC(u_J1H^1HPS^iRfM2gRDHS&zNKJeBtyp!NF&-f0oY~oz`g2fM z03H{wHa7C28J?f{LO@S}!zW=~V4vih*OBv5W@d^XvE^fz z3I#4WCdS{AGIY}u1iwMH9Wm_eAXxG7y<{vb&_;AYL2r*6>*T=zH^QTxpq$1=-zp$$ zDL<>%)RPK*aG+sR01g6eBbnt(UwG)~Q2hKj05V=bwpk>*EsN~Bat>qv^Za;HXww_c zSSzs#^XEUr43x{xcDn+GLuF%A#h)o*^Qcw#e{A(=C%V8}oR8-I3{_oE(9UtQv5%u9 zSw>bUgo$!o=l#gZd9L2MKUul|z=X0^-qr>5ImlO6)$#B*j0%=c*WD#}eWEptBhDO)MGzr#o&ew0~R(@;q<%w!USBZ%p$`S{Dj z0{|wn`1s6@JeA@>X_=lT6$@j{v{bFqrg#qzyyR3hO&T_;z=W0Nxsr+Z-$%Ck_Wf*4 zz+il}X*|!VzG(Sk@FT^Rz`CcY$mIN$4-H7iV~>0rR5t2(!*b88)R zjYifnSBskwdOA2LrLFCpKAZkRkissM*)^>UAtq+4tNK$RW9!YY%B)4v2o)?kdHkHC zm{n$oZ?+iEIPdADcJGUxQkHV#a(f_THASm7Ge#JO0EcoEeSe96KffR2^0tZsJH`D6j26~r9i|_ zE=Lj%GsO|WOgpZ0eFe>Q`QnWW_I2i;Yzi(Ek_gBELaA7H4>8}#0!n!N-V!mF9(i4S#c`A~~vw z#g`DX%oyKm%VeW^xwMW}`AqhHc1lVG(C`A@4;t)5U#@X=c1OCqCSopuR0=fQh9dTh z2v6gaj|g{;<`W6NY`l+*S?A}bks()g^CeRD$n zG^aOAG#nMx2OLDK(-#(UbsIhppRDY&+mYN44&H$w*kKX@IvLs0OkAdqg(+7qGsi+X z&rfk+9daC1kNr1aTRbVKu{P#+23s9P(%aLQ-{rgd6QFDa(WBK!u&6fEh=3=R!tUN- zh|uZJp&j5J3A66zh3C0+cb>!-KPG=VT}fFW^zh$md26u9XBe2bpz8W$HBd`O`6CC^ z(R~dmY-nUY7f-AcU@4QbR7y&szN4l+f%A7FytNEV6(7U6-fJUx`>(92a_9d7xFuj$ zzM$U;^ZDs~YrC&Mr$qqrhl%bp1IyA^$y`Ncq_-)t7)Rbd$ybNv*2H`XPr12{*9(u2 zajrW^;3iJFPEx_xK3~2cdh-4+awbuU9xF|+IYDT5tl#VJH?T`Ty?L_^@Sp<)o;i-? zvWx*Fd6D`2zyZ4OqHwYL8Ia5ZJz-=lwe zTK*oPPCiB1wyr%r`aNd7-QK3W(Eb{HzN@RTxOklRNm^SP==sd?dq9l;B0SyZLkEmB zEf=a_b!*4Jf%~ztFQoGIYy3^-%Sqhnn>X0kgAcxKTHLnjJU<$9jDnyo^^8AYF7M8~;AG6g{r=v%7 zh%$PfGENd;uQ))FF4UEllBc};ZdDI7qYqVg)z7L-uoI5YC#6k%>o2c&1}j)MPlh(^ zLxQR~T3=3Iq)suh0>R@l1Gcun4X%hD)~J8g$u@HQN~OO1lHg+@4f6d8f+KlLS~0-ZP-1e8kX<43<8ejsM`)M9da!wx z3SJY>t<6~&b|DrL*E`bD9HFOgAJ7`Q`}=>zGin|B%~_IDQAJaVJL0i+oV}nbc-_W& zd2!HveyP`-S##^FwaVN1BZ2md81n~Ypm2zH3UuhG6#!SZsA^O47_QTg%+zn+2HSpV zm1wljg7mb8Jv1TB{C+91$b35WUG+OsXF6fK{oVGT{_hw%3CWFHkITPLJ%Q}~Q{7`Z z3`P8LD!x+uf4cZ%zYM>RdrnpUK*{)kG1Rp^bSM*%LnWl9XQtqZW1*IvDhp{BjPu+I&CX~?_c%g61Wl`nbUHqq8~_FUAcjxS7VEIAYXo|9db@|T?gCevZ` zr}967*0-)vxlz!AYKhg^@f^gmIp4-<%IqAPCUxO7C?8RLaoSAA^R7r+Y_hSvxFzHd zYxL8vJBC9@)a+Yow>giEkmDNPI|C*io#GaGrFeY&P}zWh-`{k0TP?R2EZ9eVpxE@d zU+fQZQ?;R|ZDs5;Cqh=^v?2?>f)8UxN$Oftwg=OeI$CNVKK=*tQM~)(r6#=^%`R2D z{L|QbcevX>S4|Z&?PzY>GW)gh4V8OnKh&7IR@aIr9bcHBX$|f2hlesqtq{8@T$MwY zk8eTWG1Gq$J$=LBkO7J3B%yZQ0RL%e^rPhJr2gjz*u9PiKbli;%z)6mQ;*N_pO7z6 zbO;H9lOSMfL7z~iixmMVlZul#{LX!2yLu#P^pL$|Ht4PWgBRu}I!WYSM+Se*)xA{t zV8nma`$u_}t?z%`zja6btFS{g(a>G+ACG-n%vwtxDSkthg*>O;*Qr{YC--15iqnuD zY%2*h=Q}s>Y~zQgTbonKZ-ScDK5ipD+%xCk{^sn^egzV<_f(OZvc8V&T)Mi2SYe)o z7d8SVq&_%fX3!3Nl$XtR=Ong#&*Ldeho>v<78_=TKQq?EYauWpVb$j=4u{*4AQZj| z-OecA2PGBXJMy4djCa8Gy05~(^dc5>-`g+%h=go-%RhvfUk0gj))*8KYCyIy@CiPXmx$T9$lJ&BLs zGd~9~(-Eb~Q7<*UZgk>jaeN_ERy3z$i)lhJXw~)j>erH6@e>$&!K^slw)9}deqlKr z(`x!;xTAWnT4!m3n5Ad8-BV>}e?047hZ(|Qy+io)Fz;u7GMNyw)~1wGmye56{j}}! z93iz^iJGx9nux_PATKvtzr^vW0TMfz^aCDtJe$z5h2zTHm}dUsIR0u~*K|5~a( ze#``+RK2a`WRDB0c*eTHIkeH=|0p`tV5!!srlx9hMz@uf8zc2Ce#%--w4&m-??vgv zK^7JOIX(49&B&WeBfXmuqF}45=*W^K6ydt{ccbIN;bvrPv9Zx`eflJMe;q)y7k_E%fj0>Y^o9)0m3iC1sD zP>ozv)8RpSduR*)y6ElO2)n6Z9dz`PR(t=w=Xk9Sv@mw(Mtlg7^}A(^ZVPS~hPgT$ zquZ@$qm9j1-!+RwEscAk=<_Za9KnDuJrBN`+&<95{m00@H2#ecyA-s|F5_iyf~7`F77AvfX==wnGwdK4((N z_)o_L0$(02qNo3~&eDjol&d)iQsxBq^bEOO z79&sbIg`m3Z5NM!gY!%vJvNsJD#CxdxqET&In-T@@Jk{=HcMoTLq}g)KW4|0l|nrT^8^TD(f#DuP!52M3xodcODto(Uiuc>(*3{0lk3(1()Mz>+ zXuAIjDiMb3|Avid7bv;WQBU&M%D#MzE6-8b4<35|w6{ulaTiUp{7u%fDvZ7AerMwT z%G2b-iXL6N_-9{70=CEKIO4ePs->NiGcl^r=8gH)@<~tXj?xy2U|aq#ifsXA0p5rE z*dJT&{hov?3TWdN2|jF*cPy}Q{mhI-?byk5bGY9N6M7?C!dwi|BKxRA!<9gl+1#~g zyLGBT1C^rk%DgAttxs&`!U3fw$jjDy-}H}&3qH@0VPHq^Fvi~!m(GOr-@Hill*50j zf4~*86QNA8v3nW)d(xaWMv{&D&qL{-fuYU2$I~28P3gYcuQ^z2 z*G@JW-t>T8&m)xsZJ0-fQw73aOU{dCk{B85DKSzqLd8sK`5c8}p6d;_I{^a7@11g0 zbvawh$qwdXEwE*uuFsLpr!AWVp&#YIuzQnaq|{iV>6C|Sum5KQPn@+H{HV2@AAg&- zE$Sk*#yd(G<+BtGoFAda6mjP>|DoS4t-W}uy?K4TE@MdHm6L65VG;y9H2^=7CND`Y zUu1k(YlU)i(X|SJ=pR>G?nB1o%Q@E56IrWRt!|R152WwP;Rhn+4Hs8Whl$#&EZ#q6 z_=IO5n{k+SiwF8NC;z7d4-(3xg(Y^2y+n|AhOA|Jr@JbNv$@|FW7EJ_v6n3@T`tzy z%-7mRMi$U|;WFtDWe7Au5l;ZK%$UGrp>m01(9ivWnHjoD`*6Y;!^SaEk$*(|dD^(l^i*kgk+})|1Qu@H9b_Mr4R+K#@-G zV4cjdwL1^T8_d+9#A~iVz+j>F^nhl=ce?v|!;<#&%#nI!x_N%ZkACvu^8TJblM+L! zgWew6of^g~D2qC~a&=~80FEe5PtkX-A^0aTvAlz=AUoztps*1mGCIO^tZL52DYu}> zdNXp&>UF8WX8r4@>&<4Fl}BResyB|&@WuTzH9J1BX@r68UpZS6p6<@Cj0d^0I>2(g zOkOB*>_2pn!5?xP{E-25HF#9xxS!v&X>A%(HFL zDNq@{m!fW$O$nSjrvB}FbHjh2c2C)VfI<2CTHpHf=j1i7#>R=^k&(}(6N6DvW>7E|sz16g!N@&Nrzt8#Sb{e)-a#@|zuJ zlsT#8QES$fQ2x*WYA#TsmtOM-8Hxwz4B!ri(}#zZIDDT}03~a1WeyL_oAZ6VoB(ds zA5N&_f3jck(MpugYw_koXjqbYjjB`}hGmU^i86cgp(d4gNdK5M5Q>buIor$)@N=-O zR2--23Q}E^4_tDQioUt;2aP%ws!v-r`l zZBrcnFvGZleDq}Ttf-@JBP~koT41~Xn#TKs8Y8jQ~njnq!lIq4iXw(9u zIll%5PJn+y$CP}tA+uI(W^ge zPB-ThZAggF%ksFa#iZu-Y2RIGX0_LimFO{4{N*;@l{z;3Fva!66eJ=dZ1D1Dm+SqA zT2SPP$7rWRQM{p+SBtVF7Ju_O>C0Al6@SLc$sI96h==9IL;1XsPP5!h?xrjoo9^A3 zW(@BY9{WkC`FJxR1M}?tne#ruJ_>{C_`@0xWAO}!i1$xUa^7dK#QE~Hj<>cT$KDyD zqGbds-f*B9543WfT}+w~fxVHPn|pp#s*77?WLJ5TwIAqP8^cKG7UxSJ5V*DX_o^ol zo&gO32>y1t9sR!UevjGaL`x7K)*X&p?E=Buj3xnH2q{FjtNx$fxI69d>6DOu`tfsY z&dFk>e7lE+pvmI)D?g2S_Ney_=HOo0xZ5Dd&geagAA*;j{E@f7V5EH4WG{N8)X0NB zN0NiPfU6)0oVmjkW&H3N-uRECAG8Q*d_|DPWBv6Gk=ZH+iH%kS-UtcYjdUi z6{#-5mq3ZniUjJ5AB4Y@|>0HgH~BG&ezC$lV;`jk{0UC&C{L&O)iXT7Z}n?lwu}3Q)ORw#$GA0 z1Yx$G%FnJ+hOgC@YF!KYEip!s8c(VFz`ziQONt6BXDp2{mcL=Q*Y8cfd4j&H@5*B^ zDCTxDlE0S;Pr<-$L>#mx;w5yVh0m?3`jHMc_(LZ1h)kuki}BO!*n;4>NB;3+MuGtE zjMvgT$?Tv)fp$uHmUl>F-Z#m%j{C<~)AYbcY$*aCByd|8SjL644ek*nzB z5>4LyF({vo$3rchD(|%@L!!~lWaA`1TqJpP5CR+lV`!jwn^$yK4FpL(ohKv$Ly8Hr9pj_aFMYVZk*V3b#8*^DQ|_(VU% z$jeXhgKoj6_U|7q8CO3vLuIVVUgyw7=lrs!vGYZ?(|2YeQ=7oVfc) zQ(Nk*i0V$3Pd#S2+?%GZsKX{{Zl_!Nupn0(6(ypDH>0q@K=D%^}}imxLU- zghniTzTKQHeqvwi6Dy%HhXX<1|8~6XiQhfJ7eiXu67F+ymUULi=CZb{T)L(SPi<9T zd!qLjG53gP^}`Kl=+hj>(TB?d$D`Kr>oLRP!|f0&1Ux>`HIHQj1FEeVU082^s-L>H zGg^cswRWV0a4KoK1p3<&UWSIAXM35%c$`Bdp>M2hOj@3*2rDZE<|}iEPflbxV58$x zIikyaGC>OS(|woXH*(EK22RaLI9bWdCH;cBSA9{~{-22(E^mWW!! zSHQvQhqBT_fKK^(#9y?lyLi=jt=g{AVPMNSgClfvnIB7oXTI)+On-Tt9aHB``hR+U zy?$5bRXI5zV0`tB#+p#Xr2NQ$eXu#H1AP*IzdOIAmknW=Hp53rPAUJE;1C+eAVn;_99u+Z)wEqEmy zo_8^Uv&jBHxyi75wEy*(zrZKxpeB^9C?-v^-B)(tj|)+GA3|rOP*jCYmlJ>0jgPg~ znxCx$;GvUTj^=&lrlxt`)QA;N#G;e@7(07L(LMJ|({3Xu6*Kyqk2oS>UymjkJvX3M3QXey!;hgXT zlP|&bikE_UE#TMtlgDP!-R`iqxXN4hwi{AF$CFsH-aS&_?gySDhE#FE!HO~6f-OiT z*j)OSX5EFSiCGNFcugK(H~`#3zIk2+fu7xQ#EXu-+Jf7JDm8d)XKtT`!xSnmu@h-G zx9EiqX7kX%H~6vK2lk^r1uBWZu2$%Knuh_O}6Lo6c>IsCWIWAo2c)h2pT(Z%g*ht!mN|CgAJ;Sb7Ys((Lfsx zFGA?rVvWyQ_5KDAk>4rf!^^3M2v@n-zp2QYqlx6kJzwU;tLzWNK99&%*+%!1{qUip zT{S+B70s>8HgQxj>&qJT$v;~&_2L+*00`?y_O(ae8} zM)?DyH1r>_#+hS=!=n10%WgfDWHpY6@CK?C6pX)qt*rBMTYj9i%kK^T8+Vuj+E*-< zu(2H;rsNTQUqE3nudl`&-_e-eX*NL3dS-{(k%y0_z3^qF_OMkPHG&C})Br_()_CA4 z<`a;r-!mP5oS|i_tlS*hUE_fczLycYQ;H1vQ?DogJFc2pC*4NfOE6!X^{4jt=dUrbCs+AF50vQapiH zqYwK*r>VkVoGhc&AxB|*v>ZA5cx{X&I)cLSmK=_&k{{+;_RZ^&&t2zEY0pQrg?*Fs zjiGEJf;x&@c16_nW`@HuG8MO%Idqgcsx~$!M6X)#+g-3dLW@LC1baYW{fUh)F1O4) zEATNAck`vlRfqm9Qdb61urfvG;3j*Bp%OmsH>m!mu+q%dS1=bB?yo52kt6VBv$5XMIaPdxRoejRn9A!biT(N47 zDnm?avVp0Y_`^(>=1jHUhSzqVn%ztA?EBk-VWl`L7g7`6T$=s zzhdJFrLd{tWXVMwRi@Bb29J~d;Y@hakB|X*sXw=EHxD;L_eQIPwM})0Pl*Q&oGM`V zQyN&B^;`AUE?u8w=1KEZdh=L*y`EJV`{XRq&Link6fb3)&Ksm!YC3NQYIJ+S@AjV- zk3p4_!)EQ4Q%=;-d(O3`%9LG3i&#Dlv1rD{A>_SUO4z+EyOPoalV*>IbmHutpk!7S zC~@L7SmT3!2vr|pN*Ye~FSdpOF2$CluG*CM!#7b2o5g5L%s-@!~jY<-4+!{JJ^rV|{<#;)A= zsUgISYr{G!%A%4A=%9)e4=*_eLSb&-@A`J)F~X8(oxjQ8FYy7q{-PO3ZnK0opHt2lSN6LY$_j}fJ`ljGfWy4$OYi*@0?>w~W{-_YzpL1C** z|9Wsv?{ZB%Iu-}FKc_o{o8^freF=P)?rLrLL!X%GJE8#*=;P&Vo6nP;0~<~EdZt(e z8S{rIn*zB??^E6Np=(Ti3pv!6Pu6&yZEe3mCl@I)Qc`A`%;^3U*4#8Jm{=fzPGuHW zaF+VsEzeiJd3?A4%7Jpf<=sEYN8|r!?-dYA;56$HSs+Fg1ahB>#qkzabFmnN$e(vB zjh>jOgL;dKWPqN>z;_rN6O)N``?L9i%vdxWG4KTuVDu8a3~{PG9~l@kn{OtyY5XJ6^YwMhol@phYMxl5lDNNONq>da*|y?%if0rTFB! zr!3!L527h5UPsq2ERsP;k7>Qr?~(|?jSVI_S(CQI2an3O48Nti$jJUESo__sw{>a! zgR9kM8WkQKl-lL_zM9HCo72kAoXnAJ8ymmWOXmp))YDR=z(XF^ChsQx$iCirKtPaG zWBP8GMs~A|L;%Lz{u9FGZGo8tg2D6ilk=JWzxx~_bRQ^*h=}rc5xcot7*|g(J0RdM zx}%BD_bfIxykP&S{oJ-nbRp(nl<5)eyD-LC2ZRBrk-lHtEg)%0PTAf zY?!rq>nAV`36YiBOkdVU1;&Imt#t`%eUV$`FR?RFUKrYOPO|R?m6Z`EzIpHiV4+h1 z%O4=VZzJ;BHRQ)qYG%$d{ z`|SQ1M#0}-UEAuX^E(7GYM}c&By}+~ymXybo*x7Ie&>yxW~p{b`Ce2p4jMzdbEp~U zdAarQ7+HFD8p3h~(z1umC$D}tG^MC`crXo)+OoYGKP|2Q+w9GQtfYu=b8S=agl@qi zVrWQL_-C^o2;TXvwXNMlIdDIq;SwDM4=s+C_`kmIj-ezfF4?YI*YAQtiz*iIfV+ht z4W`@ekz}We(3M@!PB5IR-!=9rXpikJD2g);8WFo0jBD{)hq)&CRCTxOb9;USyy(3j zZK~wb%vS}Cpq`4WUeO^Bx3uhJ-!Y2gBc8kyjLo<+_a1V@Jx)%Vc~q1{Ol+8E5?QME zAo#Dmy(JLZt3FTOFKU9fUAFf}bK4Cw(UBd^sCoE|k>H>ZFZ$AYzl%h1r+Y1d@NQ?v zVJj&;rqit2%!mu62_C-M@jw||dg|>lKE@YieKOcGGX)*-3rXYW40 zD|Ac@jKxc3(ZxvECkE!oqfNtuHZ#Oi-3iG*DA2Lz~=%=(A{rRn@+4*FWnWMHq0xjP20zta7l z zhKbd)7^20sk&==$aJSmJpsg;kcE+Wvash8{-0|Z+X@vi0(!ly(a}bYldF&3jf1yY_$HsWW74u3%u-UR#DS)`|7g6-g|>9=+vBZ&i_2^KeZBijOKu>zm?wDy za*eNF`Pq#Ty!J*Yt!}JcGkJatvYyqetH&nvRSpg9HooC%P6ux-85pr)Bi*6>0cTLX zw6MK6_AgLmJwZ%$Q(0lZC-z4g&FxRY@bkGc(f%oJuYS3BJh6J_r=<@~&&%tG=L>vE zVuke!51Kw%@n92Zm9@GR>o1OS%6fh!IU$+!Zmjv6D_oYZZ;QK1{b)NW@2@s=^nZc= zw9N0)t*`eSbm>?9bHbaN(t;G78&|`jZqI{JT_+bposX7SMyjRRZt8eopZ86A{NT9R72p3J5*oSy;wgt9!Yh5|W@55KK4wpS`Plwru}4+t+)(?bv+nywYX`;mwK5+&%dB z{dg(v!t$pgc*?ak2W6k@$#!pD*C$}V|5<$awQE7a;qL1<81T$`rdV68oM^`L$k~}u zV`G$|G~4!_Zv0u&QX6LY{Mey(;OdpDg0G&ppUqo-rgZ!5qsQA{ESQ~rQ}^A{iHsk$ zexG}{_~OO7$+L_6o==FJaPn!}vfYi3LyrUN3`Xu?v2D@3zRY_U+ylgzo{sAq{=Yi*Yr^(r%um@ps_lDkwM8*7)iPDSoHxB9 z=ScF~xbBVSC;%({7%o?WeIC9Psf&xH_OjF zyYVwQNB7>TOV!hN?UoUo`2NC)hK`Po>A?~LNG+^H2N#zkr!{#x+#~8sY%8CCil|P} zZ9gYlRBoxHG`H(eLrI0?PsuZPO{T9p9V%lBJhbF{u;#{TJ9g^`PV{r$%lTwl&F*)# z`%l!d{NCm(|D)*?)0)!oUB_qg2p1kKbYXSfdh+9~Lo>P4KbZdx{_%5zSie(c)adVSGi$Dg4tOMr{r9c-E|KU@G@5c!dt zS2gW~K>2iKpnHIU1aiHt8rF0Gl4@lH8)~4Sr1a>i2%C$G%bqz9m*Pk~7@GdcH~8<& U`!9B{ivb8cUHx3vIVCg!0OGZ#lmGw# literal 0 HcmV?d00001 diff --git a/ubitxv3.pdf b/ubitxv3.pdf new file mode 100644 index 0000000000000000000000000000000000000000..9b7990ebb8241c7bfb9cb27e9b62cdeb0345d531 GIT binary patch literal 246788 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

kfq#4TmT&Y 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 literal 0 HcmV?d00001 From d0ac62605c9502f2c601610ad4480d9ead0af0ff Mon Sep 17 00:00:00 2001 From: Ashhar Farhan Date: Thu, 7 Dec 2017 10:22:46 +0530 Subject: [PATCH 004/173] Create README.md Added the licensing --- README.md | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) create mode 100644 README.md diff --git a/README.md b/README.md new file mode 100644 index 0000000..7750d58 --- /dev/null +++ b/README.md @@ -0,0 +1,17 @@ + +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 . From 91f3ecdf224a8429ed82ab6734cb0284108bf2bc Mon Sep 17 00:00:00 2001 From: Ashhar Farhan Date: Thu, 7 Dec 2017 10:34:45 +0530 Subject: [PATCH 005/173] Old Code for the prototype uBITX, no longer used --- control_lcd_45mhz_if_v4.ino | 2 ++ 1 file changed, 2 insertions(+) diff --git a/control_lcd_45mhz_if_v4.ino b/control_lcd_45mhz_if_v4.ino index f4eb989..0a8b2dd 100644 --- a/control_lcd_45mhz_if_v4.ino +++ b/control_lcd_45mhz_if_v4.ino @@ -1,4 +1,6 @@ /** + * This is source code for the scratch built ubitx, it is no longer used for the kits. + * * This source file is under General Public License version 3. * * Most source code are meant to be understood by the compilers and the computers. From ae8d03601d652f9004ed662016bd717158508a57 Mon Sep 17 00:00:00 2001 From: Ashhar Farhan Date: Thu, 7 Dec 2017 10:39:50 +0530 Subject: [PATCH 006/173] Delete control_lcd_45mhz_if_v4.ino --- control_lcd_45mhz_if_v4.ino | 901 ------------------------------------ 1 file changed, 901 deletions(-) delete mode 100644 control_lcd_45mhz_if_v4.ino diff --git a/control_lcd_45mhz_if_v4.ino b/control_lcd_45mhz_if_v4.ino deleted file mode 100644 index 0a8b2dd..0000000 --- a/control_lcd_45mhz_if_v4.ino +++ /dev/null @@ -1,901 +0,0 @@ -/** - * This is source code for the scratch built ubitx, it is no longer used for the kits. - * - * This source file is under General Public License version 3. - * - * 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 firt 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 . - */ - -#include - -/** - * 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 -Si5351 si5351; -/** - * The Radiono board is the size of a standard 16x2 LCD panel. It has three connectors: - * - * First, is an 8 pin connector that provides +5v, GND and six analog input pins that can also be - * configured to be used as digital input or output pins. These are referred to as A0,A1,A2, - * A3,A6 and A7 pins. The A4 and A5 pins are missing from this connector as they are used to - * talk to the Si5351 over I2C protocol. - * - * Second is a 16 pin LCD connector. This connector is meant specifically for the standard 16x2 - * LCD display in 4 bit mode. The 4 bit mode requires 4 data lines and two control lines to work: - * Lines used are : RESET, ENABLE, D4, D5, D6, D7 - * We include the library and declare the configuration of the LCD panel too - */ - -#include -LiquidCrystal lcd(8,9,10,11,12,13); - -/** - * The Arduino, unlike C/C++ on a regular computer with gigabytes of RAM, has very little memory. - * We have to be very careful with variables that are declared inside the functions as they are - * created in a memory region called the stack. The stack has just a few bytes of space on the Arduino - * if you declare large strings inside functions, they can easily exceed the capacity of the stack - * and mess up your programs. - * We circumvent this by declaring a few global buffers as kitchen counters where we can - * slice and dice our strings. These strings are mostly used to control the display or handle - * the input and output from the USB port. We must keep a count of the bytes used while reading - * the serial port as we can easily run out of buffer space. This is done in the serial_in_count variable. - */ -char serial_in[32], c[30], b[30], printBuff[32]; -int count = 0; -unsigned char serial_in_count = 0; - -/** - * 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: - * A0, A1, A2, A3, +5v, GND, A6, A7 - * (while holding the board up so that back of the board faces you) - * - * 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 S_METER (A0) -#define PTT (A1) -#define FBUTTON (A3) -#define ANALOG_KEYER (A7) -#define ANALOG_TUNING (A6) - -/** - * The second set of 16 pins on the bottom connector are have the three clock outputs and the digital lines to control the rig. - * This assignment is as follows : - * Pin 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 - * GND +5V CLK0 GND GND CLK1 GND GND CLK2 GND D2 D3 D4 D5 D6 D7 - * These too are flexible with what you may do with them, for the Raduino, we use them to : - * - TX_RX line : Switches between Transmit and Receive after sensing the PTT or the morse keyer - * - CW_KEY line : turns on the carrier for CW - */ - -#define TX_RX (7) -#define CW_TONE (6) -#define TX_LPF_SEL (5) -#define CW_KEY (4) - -/** - * The raduino has a number of timing parameters, all specified in milliseconds - * CW_TIMEOUT : how many milliseconds between consecutive keyup and keydowns before switching back to receive? - * The next set of three parameters determine what is a tap, a double tap and a hold time for the funciton button - * TAP_DOWN_MILLIS : upper limit of how long a tap can be to be considered as a button_tap - * TAP_UP_MILLIS : upper limit of how long a gap can be between two taps of a button_double_tap - * TAP_HOLD_MILIS : many milliseconds of the buttonb being down before considering it to be a button_hold - */ - -#define TAP_UP_MILLIS (500) -#define TAP_DOWN_MILLIS (600) -#define TAP_HOLD_MILLIS (2000) -#define CW_TIMEOUT (2000l) // in milliseconds, this is the parameter that determines how long the tx will hold between cw key downs - -/** - * The Raduino supports two VFOs : A and B and receiver incremental tuning (RIT). - * we define a variables to hold the frequency of the two VFOs, RITs - * the rit offset as well as status of the RIT - */ -#define VFO_A 0 -#define VFO_B 1 -char ritOn = 0; -char vfoActive = VFO_A; -unsigned long vfoA=7100000L, vfoB=14200000L, ritA, ritB, sideTone=800, lsbCarrier, usbCarrier; - -#define MASTER_CAL 0 -#define LSB_CAL 4 -#define USB_CAL 8 -#define SIDE_TONE 12 - -/** - * Raduino needs to keep track of current state of the transceiver. These are a few variables that do it - */ -char inTx = 0; -char keyDown = 0; -char isUSB = 0; -unsigned long cwTimeout = 0; -unsigned char txFilter = 0; - -/** Tuning Mechanism of the Raduino - * We use a linear pot that has two ends connected to +5 and the ground. the middle wiper - * is connected to ANALOG_TUNNING pin. Depending upon the position of the wiper, the - * reading can be anywhere from 0 to 1024. - * The tuning control works in steps of 50Hz each for every increment between 50 and 950. - * Hence the turning the pot fully from one end to the other will cover 50 x 900 = 45 KHz. - * At the two ends, that is, the tuning starts slowly stepping up or down in 10 KHz steps - * To stop the scanning the pot is moved back from the edge. - * To rapidly change from one band to another, you press the function button and then - * move the tuning pot. Now, instead of 50 Hz, the tuning is in steps of 50 KHz allowing you - * rapidly use it like a 'bandset' control. - * To implement this, we fix a 'base frequency' to which we add the offset that the pot - * points to. We also store the previous position to know if we need to wake up and change - * the frequency. - */ - -unsigned long baseTune = 7100000L; -int old_knob = 0; - -#define FIRST_IF (45000000l) -#define SECOND_OSC (57000000l) -#define INIT_USB_FREQ (11996500l) -#define INIT_LSB_FREQ (11998500l) -#define LOWEST_FREQ (3000000l) -#define HIGHEST_FREQ (30000000l) - -long frequency, stepSize=100000; - -/** - * The raduino can be booted into multiple modes: - * MODE_NORMAL : works the radio normally - * MODE_CALIBRATION : used to calibrate Raduino. - * To enter this mode, hold the function button down and power up. Tune to exactly 10 MHz on clock0 and release the function button - */ - #define MODE_NORMAL (0) - #define MODE_CALIBRATE (1) - char mode = MODE_NORMAL; - -char meter[17]; -byte s_meter[] = { - B0,B0,B0,B0,B0,B00000,B0,B10101, - B0,B0,B0,B0,B10000,B10000,B0,B10101, - B0,B0,B0,B0,B11000,B11000,B0,B10101, - B0,B0,B0,B0,B11100,B11100,B0,B10101, - B0,B0,B0,B0,B11110,B11110,B0,B10101, - B0,B0,B0,B0,B11111,B11111,B0,B10101 -}; - -void setupSmeter(){ - lcd.createChar(1, s_meter); - lcd.createChar(2, s_meter + 8); - lcd.createChar(3, s_meter + 16); - lcd.createChar(4, s_meter + 24); - lcd.createChar(5, s_meter + 32); - lcd.createChar(6, s_meter + 40); -} - -/* display routines */ -void printLine1(char *c){ - if (strcmp(c, printBuff)){ - lcd.setCursor(0, 0); - lcd.print(c); - strcpy(printBuff, c); - count++; - } -} - -void printLine2(char *c){ - if (strlen(c) > 16) - c[16] = 0; - lcd.setCursor(0, 1); - lcd.print(c); -} - -void displayFrequency(unsigned long f){ - int mhz, khz, hz; - - mhz = f / 1000000l; - khz = (f % 1000000l)/1000; - hz = f % 1000l; - sprintf(b, "[%02d.%03d.%03d]", mhz, khz, hz); - printLine1(b); -} - -void updateMeter(){ - int16_t best, i, s; - - best = 0; - //take 100 readings, take the peaks - for (i = 0; i < 100; i++){ - s = analogRead(S_METER); - if (s > best) - best = s; - } - //now, use the s to get the signal - s = best *2; - sprintf(meter, "%3d", s); - for (i = 3; i < 14; i++){ - if (s >= 5) - meter[i] = 6; - else if (s > 0) - meter[i] = 1 + s; - else - meter[i] = 1; - s = s - 5; - } - meter[i] = 0; - printLine2(meter); -} - -/** - * Defines for menus - */ -byte menuOn = 0; - -int ritToggle(int btn){ - if (!btn){ - if (ritOn == 1) - printLine2("RIT:On, Off? "); - else - printLine2("RIT:Off, On? "); - } - else { - if (ritOn == 0){ - ritOn = 1; - printLine2("RIT is On. "); - } - else { - ritOn = 0; - printLine2("RIT Is Off. "); - } - } -} - -int vfoToggle(int btn){ - - if (!btn){ - if (vfoActive == VFO_A) - printLine2("Select VFO B? "); - else - printLine2("Select VFO A? "); - } - else { - if (vfoActive == VFO_A){ - vfoActive = VFO_A; - printLine1("Selected VFO A "); - frequency = vfoA; - } - else { - vfoActive = VFO_B; - printLine1("Selected VFO B "); - frequency = vfoB; - } - setFrequency(frequency); - updateDisplay(); - resetBasefrequency(); - } -} - -/** - * Load the new correction and resets the clock 0 to 10 MHz - */ - -void recalibrate(int32_t correction){ - - si5351.set_correction(correction); - - si5351.set_pll(SI5351_PLL_FIXED, SI5351_PLLA); - si5351.set_pll(SI5351_PLL_FIXED, SI5351_PLLB); - - si5351.output_enable(SI5351_CLK0, 1); - si5351.output_enable(SI5351_CLK1, 0); - si5351.output_enable(SI5351_CLK2, 0); - - si5351.set_freq(1000000000ULL, SI5351_CLK0); -} - -void calibrateMaster(int btn){ - int knob = 0; - int32_t correction; - - if (!btn){ - printLine2("Set Calibration?"); - return; - } - printLine1("Set to 10.000.000"); - printLine2("PTT to confirm. "); - delay(2000); - - recalibrate(0); - - //disable all clock 1 and clock 2 - while(digitalRead(PTT) == HIGH && !btnDown()){ - knob = analogRead(ANALOG_TUNING); - correction = (knob - 500) * 500ULL; - recalibrate(correction); - //abort if this button is down - if (btnDown()){ - //re-enable the clock1 and clock 2 - break; - } - sprintf(c, "%3d ", knob); - printLine2(c); - } - - //save the setting - if (digitalRead(PTT) == LOW){ - printLine2("Calibration set!"); - EEPROM.put(MASTER_CAL, correction); - delay(2000); - } - else { - EEPROM.get(MASTER_CAL, correction); - } - - si5351.set_correction(correction); - si5351.set_pll(SI5351_PLL_FIXED, SI5351_PLLA); - si5351.set_pll(SI5351_PLL_FIXED, SI5351_PLLB); - - si5351.output_enable(SI5351_CLK0, 1); - si5351.output_enable(SI5351_CLK1, 1); - si5351.output_enable(SI5351_CLK2, 1); - - resetBasefrequency(); - setFrequency(frequency); - updateDisplay(); - menuOn = 0; -} - -void setBFO(int btn, byte isLSB){ - int knob = 0; - int32_t lsb_Freq; - - if (!btn){ - if (isLSB) - printLine2("Set LSB Carrier "); - else - printLine2("Set USB Carrier "); - return; - } - if (isLSB) - printLine1("Tune to best LSB"); - else - printLine1("Tune to best USB"); - printLine2("PTT to confirm. "); - delay(2000); - - //disable all clock 1 and clock 2 - while(digitalRead(PTT) == HIGH && !btnDown()){ - knob = analogRead(ANALOG_TUNING); - if (isLSB){ - lsbCarrier = 12000000l - knob * 10; - si5351.set_freq(lsbCarrier * 100ULL, SI5351_CLK0); - } - else { - usbCarrier = 12000000l - knob * 10; - si5351.set_freq(usbCarrier * 100ULL, SI5351_CLK0); - } - //abort if this button is down - if (btnDown()){ - break; - } - sprintf(c, "%3d ", knob); - printLine2(c); - } - //save the setting - if (digitalRead(PTT) == LOW){ - printLine2("Carrier set! "); - if (isLSB) - EEPROM.put(LSB_CAL, lsbCarrier); - else - EEPROM.put(USB_CAL, usbCarrier); - delay(2000); - } - else { - EEPROM.get(LSB_CAL, lsbCarrier); - EEPROM.get(USB_CAL, usbCarrier); - } - - resetBasefrequency(); - setFrequency(frequency); - updateDisplay(); - menuOn = 0; -} - -void resetBasefrequency(){ - int knob = analogRead(ANALOG_TUNING); - baseTune = frequency - (50l * knob); -} - -int exitMenu(int btn){ - - if (!btn){ - printLine2("Exit Menu? "); - } - else{ - menuOn = 0; - resetBasefrequency(); - } -} - -void doMenu(){ - int select, btnState; - menuOn = 1; - - while(menuOn){ - select = analogRead(ANALOG_TUNING); - //downscale the selection - select = (select-50)/50; - btnState = btnDown(); - delay(200); - switch(select){ - case 0: - ritToggle(btnState); - break; - case 1: - vfoToggle(btnState); - break; - case 2: - calibrateMaster(btnState); - break; - case 3: - setBFO(btnState, 1); - break; - case 4: - setBFO(btnState, 0); - break; - - default: - exitMenu(btnState); - break; - } - } -} - -void updateDisplay(){ - sprintf(b, "%8ld", frequency); - sprintf(c, "%s:%.2s.%.3s.%1s", vfoActive == VFO_A ? "A" : "B" , b, b+2, b+5); - if (isUSB) - strcat(c, " USB"); - else - strcat(c, " LSB"); - - if (inTx) - strcat(c, " TX"); - else if (ritOn) - strcat(c, " +R"); - else - strcat(c, " "); - - - printLine1(c); -/* sprintf(c, "%s %s %d", isLSB ? "LSB" : "USB", inTx ? " TX" : " RX", digitalRead(FBUTTON)); - printLine2(c); */ -} - -void setSidebandAndTXFilters(unsigned long freq){ - - if (freq > 10000000L){ - isUSB = 1; - si5351.set_freq(usbCarrier * 100ULL, SI5351_CLK0); - digitalWrite(TX_LPF_SEL, 1); - } - else{ - isUSB = 0; - digitalWrite(TX_LPF_SEL, 0); - si5351.set_freq(lsbCarrier * 100ULL, SI5351_CLK0); - } -} - -void setFrequency(unsigned long f){ - uint64_t osc_f; - - setSidebandAndTXFilters(f); - - if (isUSB) - si5351.set_freq((SECOND_OSC - usbCarrier + f) * 100ULL, SI5351_CLK2); - else - si5351.set_freq((SECOND_OSC - lsbCarrier + f) * 100ULL, SI5351_CLK2); - - frequency = f; -} - -void checkTX(){ - - //we don't check for ptt when transmitting cw - if (cwTimeout > 0) - return; - - if (digitalRead(PTT) == 0 && inTx == 0){ - inTx = 1; - digitalWrite(TX_RX, 1); - updateDisplay(); - } - - if (digitalRead(PTT) == 1 && inTx == 1){ - inTx = 0; - digitalWrite(TX_RX, 0); - updateDisplay(); - } -} - -/* -byte prevWasDot = 0; - -void keyer(){ - int key = analogRead(ANALOG_KEYER); - - if (key < 50) //straight key - keyDown(); - else if (key < 300) // both - if (prevWasDot){ - keyDown(dotPeriod * 3); - prevWasDot = 0; - } - else { - keyDown(dotPeriod); - prevWasDot = 1; - } - else if (key < 600){ //dash - keyDown(dotPeriod * 3); - prevWasDot = 0; - } - else if (key > 900){ //dot - keyUp(); - } -} -*/ - - -void checkCW2(){ - - if (keyDown == 0 && analogRead(ANALOG_KEYER) < 50){ - //switch to transmit mode if we are not already in it - if (inTx == 0){ - digitalWrite(TX_RX, 1); -// selectTransmitFilter(); - //give the relays a few ms to settle the T/R relays - delay(50); - } - inTx = 1; - keyDown = 1; - tone(CW_TONE, (int)sideTone); - updateDisplay(); - } - - //reset the timer as long as the key is down - if (keyDown == 1){ - cwTimeout = CW_TIMEOUT + millis(); - } - - //if we have a keyup - if (keyDown == 1 && analogRead(ANALOG_KEYER) > 150){ - keyDown = 0; - noTone(CW_TONE); - cwTimeout = millis() + CW_TIMEOUT; - } - - //if we are in cw-mode and have a keyuup for a longish time - if (cwTimeout > 0 && inTx == 1 && cwTimeout < millis()){ - //move the radio back to receive - digitalWrite(TX_RX, 0); - inTx = 0; - cwTimeout = 0; - updateDisplay(); - } -} - -void checkCW3(){ - - if (keyDown == 0 && analogRead(ANALOG_KEYER) < 50){ - //switch to transmit mode if we are not already in it - if (inTx == 0){ - - if (isUSB) - si5351.set_freq((frequency + sideTone) * 100ULL, SI5351_CLK2); - else - si5351.set_freq((frequency - sideTone) * 100ULL, SI5351_CLK2); - //switch off the second oscillator and the bfo - si5351.output_enable(SI5351_CLK0, 0); - si5351.output_enable(SI5351_CLK1, 0); - si5351.output_enable(SI5351_CLK2, 1); - - digitalWrite(TX_RX, 1); -// selectTransmitFilter(); - //give the relays a few ms to settle the T/R relays - delay(50); - } - inTx = 1; - keyDown = 1; - tone(CW_TONE, (int)sideTone); - digitalWrite(CW_KEY, 1); - - updateDisplay(); - } - - //reset the timer as long as the key is down - if (keyDown == 1){ - cwTimeout = CW_TIMEOUT + millis(); - } - - //if we have a keyup - if (keyDown == 1 && analogRead(ANALOG_KEYER) > 150){ - keyDown = 0; - noTone(CW_TONE); - digitalWrite(CW_KEY, 0); - cwTimeout = millis() + CW_TIMEOUT; - } - - //if we are in cw-mode and have a keyuup for a longish time - if (cwTimeout > 0 && inTx == 1 && cwTimeout < millis()){ - //move the radio back to receive - digitalWrite(TX_RX, 0); - inTx = 0; - cwTimeout = 0; - updateDisplay(); - - //switch off the second oscillator and the bfo - si5351.output_enable(SI5351_CLK0, 1); - si5351.output_enable(SI5351_CLK1, 1); - si5351.output_enable(SI5351_CLK2, 1); - setFrequency(frequency); - } -} - - -void checkCW(){ - - if (keyDown == 0 && analogRead(ANALOG_KEYER) < 50){ - //switch to transmit mode if we are not already in it - if (inTx == 0){ - digitalWrite(TX_RX, 1); - delay(50); - inTx = 1; - keyDown = 1; - } - if (isUSB) - si5351.set_freq((frequency + sideTone) * 100ULL, SI5351_CLK2); - else - si5351.set_freq((frequency - sideTone) * 100ULL, SI5351_CLK2); - //switch off the second oscillator and the bfo - si5351.output_enable(SI5351_CLK0, 0); - si5351.output_enable(SI5351_CLK1, 0); - si5351.output_enable(SI5351_CLK2, 1); - - digitalWrite(CW_KEY, 1); - tone(CW_TONE, sideTone); - updateDisplay(); - } - - //reset the timer as long as the key is down - if (keyDown == 1){ - cwTimeout = CW_TIMEOUT + millis(); - } - - //if we have a keyup - if (keyDown == 1 && analogRead(ANALOG_KEYER) > 150){ - keyDown = 0; - noTone(CW_TONE); - digitalWrite(CW_KEY, 0); - cwTimeout = millis() + CW_TIMEOUT; - } - - //if we are in cw-mode and have a keyuup for a longish time - if (cwTimeout > 0 && inTx == 1 && cwTimeout < millis()){ - //move the radio back to receive - digitalWrite(TX_RX, 0); - inTx = 0; - cwTimeout = 0; - //switch off the second oscillator and the bfo - si5351.output_enable(SI5351_CLK0, 1); - si5351.output_enable(SI5351_CLK1, 1); - si5351.output_enable(SI5351_CLK2, 1); - setFrequency(frequency); - updateDisplay(); - } -} - -int btnDown(){ - if (digitalRead(FBUTTON) == HIGH) - return 0; - else - return 1; -} - -void checkButton(){ - int i, t1, t2, knob, new_knob, duration; - - //only if the button is pressed - if (!btnDown()) - return; - - //wait for 50 ms before declaring the button to be really down - delay(50); - if (!btnDown()) - return; - - t1 = millis(); - knob = analogRead(ANALOG_TUNING); - duration = 0; - - /* keep measuring how long the duration of btn down has been to a max of 3 seconds */ - while (btnDown() && duration < 3000){ - /* if the tuning knob is moved while the btn is down, - then track the bandset until the button is up and return - */ - new_knob = analogRead(ANALOG_TUNING); - if (abs(new_knob - knob) > 10){ - int count = 0; - /* track the tuning and return */ - while (btnDown()){ - frequency = baseTune = ((analogRead(ANALOG_TUNING) * 30000l) + 1000000l); - setFrequency(frequency); - updateDisplay(); - count++; - delay(200); - } - delay(1000); - return; - } /* end of handling the bandset */ - - delay(100); - duration += 100; - } - - if (duration < 1000) { - printLine2("Menu."); - doMenu(); - } -} - -void doTuning(){ - unsigned long newFreq; - - int knob = analogRead(ANALOG_TUNING); - unsigned long old_freq = frequency; - - if (knob < 10 && frequency > LOWEST_FREQ) { - baseTune = baseTune - 1000l; - frequency = baseTune; - updateDisplay(); - setFrequency(frequency); - delay(50); - } - else if (knob > 1010 && frequency < HIGHEST_FREQ) { - baseTune = baseTune + 1000l; - frequency = baseTune + 50000l; - setFrequency(frequency); - updateDisplay(); - delay(50); - } - // in the middle, it is business as usual - else if (knob != old_knob){ - frequency = baseTune + (50l * knob); - old_knob = knob; - setFrequency(frequency); - updateDisplay(); - } -} - - -void setup() -{ - int32_t cal; - - lcd.begin(16, 2); - setupSmeter(); - printBuff[0] = 0; - printLine1("HFuino v0.01 "); - printLine2(" "); - - EEPROM.get(MASTER_CAL, cal); - EEPROM.get(LSB_CAL, lsbCarrier); - EEPROM.get(USB_CAL, usbCarrier); - //set the lsb and usb to defaults - if (lsbCarrier == 0) - lsbCarrier = INIT_LSB_FREQ; - if (usbCarrier == 0) - usbCarrier = INIT_USB_FREQ; - - // Start serial and initialize the Si5351 - Serial.begin(9600); - analogReference(DEFAULT); - Serial.println("*HFuino v0.01\n"); - Serial.println("*Searching Si5351\n"); - - //configure the function button to use the external pull-up - pinMode(FBUTTON, INPUT); - digitalWrite(FBUTTON, HIGH); - - pinMode(PTT, INPUT); - digitalWrite(PTT, HIGH); - - digitalWrite(ANALOG_KEYER, HIGH); - - pinMode(CW_TONE, OUTPUT); - digitalWrite(CW_TONE, 0); - pinMode(TX_RX,OUTPUT); - digitalWrite(TX_RX, 0); - pinMode(TX_LPF_SEL, OUTPUT); - digitalWrite(TX_LPF_SEL, 0); - pinMode(CW_KEY, OUTPUT); - digitalWrite(CW_KEY, 0); - EEPROM.get(0,cal); - - si5351.init(SI5351_CRYSTAL_LOAD_8PF,25000000l,0); - - si5351.set_correction(cal); - si5351.set_pll(SI5351_PLL_FIXED, SI5351_PLLA); - si5351.set_pll(SI5351_PLL_FIXED, SI5351_PLLB); - - si5351.output_enable(SI5351_CLK0, 1); - si5351.output_enable(SI5351_CLK1, 1); - si5351.output_enable(SI5351_CLK2, 1); - // printLine1("check freq "); - // si5351.set_freq(1000000000l, SI5351_CLK2); - - // delay(20000); - - si5351.set_freq(lsbCarrier * 100ULL, SI5351_CLK0); - si5351.set_freq(SECOND_OSC * 100ULL, SI5351_CLK1); - si5351.set_freq(5900000000l, SI5351_CLK2); - printLine2(b); - delay(2000); - - // Set CLK0 to output 7 MHz with a fixed PLL frequency - - si5351.drive_strength(SI5351_CLK0, SI5351_DRIVE_8MA); - si5351.drive_strength(SI5351_CLK1, SI5351_DRIVE_8MA); - si5351.drive_strength(SI5351_CLK2, SI5351_DRIVE_8MA); - - - Serial.println("*Si5350 ON"); - delay(10); -} - -void loop(){ - - //generateCW(10000); - //the order of testing first for cw and then for ptt is important. - checkCW3(); - checkTX(); - checkButton(); - //tune only when not tranmsitting - if (!inTx) - doTuning(); - updateMeter(); - delay(50); -} - From 8176b50f488e3babe08ffc1dedc8b65a2d6de917 Mon Sep 17 00:00:00 2001 From: phdlee Date: Wed, 10 Jan 2018 11:29:25 +0900 Subject: [PATCH 007/173] add revision history --- README.md | 55 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 55 insertions(+) diff --git a/README.md b/README.md index 7750d58..66b1ddd 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,59 @@ +#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 +---------------------------------------------------------------------------- +## REVISION RECORD +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 From f563e74a4e7a5909c90a36c69cd91ba881df3e52 Mon Sep 17 00:00:00 2001 From: phdlee Date: Wed, 10 Jan 2018 11:34:15 +0900 Subject: [PATCH 008/173] beta 0.25 commit --- ubitx_20/cat_libs.ino | 754 +++++++++++++++++++++++++++++++++++++++ ubitx_20/cw_autokey.ino | 406 +++++++++++++++++++++ ubitx_20/ubitx_20.ino | 428 ++++++++++++++++++++-- ubitx_20/ubitx_cat.ino | 231 ------------ ubitx_20/ubitx_keyer.ino | 55 ++- ubitx_20/ubitx_menu.ino | 405 +++++++++++++++------ ubitx_20/ubitx_ui.ino | 185 ++++++++-- 7 files changed, 2044 insertions(+), 420 deletions(-) create mode 100644 ubitx_20/cat_libs.ino create mode 100644 ubitx_20/cw_autokey.ino delete mode 100644 ubitx_20/ubitx_cat.ino diff --git a/ubitx_20/cat_libs.ino b/ubitx_20/cat_libs.ino new file mode 100644 index 0000000..e9375c4 --- /dev/null +++ b/ubitx_20/cat_libs.ino @@ -0,0 +1,754 @@ +/************************************************************************* + 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 . + +**************************************************************************/ +#define printLineF1(x) (printLineF(1, x)) +#define printLineF2(x) (printLineF(0, x)) + +//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 + +unsigned int skipTimeCount = 0; +byte CAT_BUFF[5]; +byte CAT_SNDBUFF[5]; + +void SendCatData(byte 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) +{ + 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 (isUSB) + CAT_BUFF[4] = CAT_MODE_USB; + else + CAT_BUFF[4] = CAT_MODE_LSB; + + SendCatData(5); +} + +void CatSetSplit(boolean isSplit, byte fromType) +{ + + Serial.write(ACK); +} + +void CatSetPTT(boolean isPTTOn, byte fromType) +{ + if (fromType == 2 || fromType == 3) { + Serial.write(ACK); + return; + } + + // Set PTT Mode + if (isPTTOn) + { + if (!inTx) + { + txCAT = true; + + 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) + { + 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) + { + if (tmpMode == CAT_MODE_USB) + { + isUSB = true; + } + else + { + isUSB = false; + } + + setFrequency(frequency); + updateDisplay(); + } + + Serial.write(ACK); +} + +//Read EEProm by uBITX Manager Software +void ReadEEPRom(byte fromType) +{ + //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; + + Serial.write(0x02); //STX + checkSum = 0x02; + 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) +{ + //5BYTES + uint16_t eepromStartIndex = CAT_BUFF[0] + CAT_BUFF[1] * 256; + byte write1Byte = CAT_BUFF[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 + { + EEPROM.write(eepromStartIndex, write1Byte); + Serial.write(0x77); //OK + Serial.write(ACK); + } +} + +void ReadEEPRom_FT817(byte fromType) +{ + 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 (isUSB) + CAT_BUFF[0] = CAT_MODE_USB; + else + CAT_BUFF[0] = CAT_MODE_LSB; + + 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); +} + +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(500); + printLine2(""); + } + 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(500); + printLine2(""); + } + 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(500); + printLine2(""); + 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(500); + printLine2(""); + + 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); +} + +void CatRxStatus(byte fromType) +{ + byte sMeterValue = 1; + + /* + 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. + CAT_BUFF[0] = sMeterValue & 0b00001111; + SendCatData(1); +} + + +void CatTxStatus(byte fromType) +{ + 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; + +//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; + + //Check Serial Port Buffer + if (Serial.available() == 0) + { + //Set Buffer Clear status + rxBufferCheckCount = 0; + return; + } + else if (Serial.available() < 5) + { + //First Arrived + if (rxBufferCheckCount == 0) + { + rxBufferCheckCount = Serial.available(); + rxBufferArriveTime = millis() + CAT_RECEIVE_TIMEOUT; //Set time for timeout + } + else if (rxBufferArriveTime < millis()) //timeout + { + //Clear Buffer + for (i = 0; i < Serial.available(); i++) + rxBufferCheckCount = Serial.read(); + + rxBufferCheckCount = 0; + } + else if (rxBufferCheckCount < Serial.available()) //increase buffer count, slow arrived + { + rxBufferCheckCount = Serial.available(); + rxBufferArriveTime = millis() + CAT_RECEIVE_TIMEOUT; //Set time for timeout + } + + return; + } + + //Arived CAT DATA + for (i = 0; i < 5; i++) + CAT_BUFF[i] = Serial.read(); + + if (isProcessCheck_Cat == 1) + return; + + isProcessCheck_Cat = 1; + + //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, fromType); + break; + + case 0x03 : //Read Frequency and mode + CatGetFreqMode(frequency, fromType); + 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(fromType); //Call by uBITX Manager Program + break; + case 0xBB: //Read FT-817 EEPROM Data (for comfirtable) + ReadEEPRom_FT817(fromType); + break; + + case 0xDC: //Write uBITX EEPROM Data + WriteEEPRom(fromType); //Call by uBITX Manager Program + break; + case 0xBC: //Write FT-817 EEPROM Data (for comfirtable) + WriteEEPRom_FT817(fromType); + break; + + case 0xE7 : //Read RX Status + CatRxStatus(fromType); + break; + case 0xF7: //Read TX Status + CatTxStatus(fromType); + break; + default: + /* + char buff[16]; + sprintf(buff, "DEFAULT : %x", CAT_BUFF[4]); + printLine2(buff); + */ + Serial.write(ACK); + break; + } //end of switch + + isProcessCheck_Cat = 0; +} + +void Init_Cat(long baud, int portConfig) +{ + Serial.begin(baud, portConfig); + Serial.flush(); +} + diff --git a/ubitx_20/cw_autokey.ino b/ubitx_20/cw_autokey.ino new file mode 100644 index 0000000..7c6cc75 --- /dev/null +++ b/ubitx_20/cw_autokey.ino @@ -0,0 +1,406 @@ +/************************************************************************* + This source code is written for All amateur radio operator, + I have not had amateur radio communication for a long time. CW has been + around for a long time, and I do not know what kind of keyer and keying + software is fashionable. So I implemented the functions I need mainly. + + To minimize the use of memory space, we used bitwise operations. + For the alphabet, I put Morsecode in 1 byte. The front 4Bit is the length + and the 4Bit is the Morse code. Because the number is fixed in length, + there is no separate length information. The 5Bit on the right side is + the Morse code. + + 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. +----------------------------------------------------------------------------- + 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 + +//27 + 10 + 18 + 1(SPACE) = //56 +const PROGMEM uint8_t cwAZTable[27] = {0b00100100 , 0b01001000 , 0b01001010 , 0b00111000 , 0b00010000, 0b01000010, 0b00111100, 0b01000000 , //A ~ H +0b00100000, 0b01000111 ,0b00111010, 0b01000100, 0b00101100, 0b00101000 , 0b00111110, 0b01000110, 0b01001101, 0b00110100, //I ~ R +0b00110000, 0b00011000, 0b00110010, 0b01000001, 0b00110110, 0b01001001, 0b01001011, 0b00111000}; //S ~ Z +PGM_P pCwAZTable = reinterpret_cast(cwAZTable); + +const PROGMEM uint8_t cw09Table[27] = {0b00011111, 0b00001111, 0b00000111, 0b00000011, 0b00000001, 0b00000000, 0b00010000, 0b00011000, 0b00011100, 0b00011110}; +PGM_P pcw09Table = reinterpret_cast(cw09Table); + +//# : AR, ~:BT, [:AS, ]:SK, ^:KN +const PROGMEM uint8_t cwSymbolIndex[] = {'.', ',', '?', '"', '!', '/', '(', ')', '&', ':', ';', '=', '+', '-', '_', '\'', '@', '#', '~', '[', ']', '^' }; +PGM_P pCwSymbolIndex = reinterpret_cast(cwSymbolIndex); + +const PROGMEM uint8_t cwSymbolTable[] = {0b11010101, 0b11110011, 0b11001100, 0b11011110, 0b11101011, 0b10100100, 0b10101100, 0b11101101, 0b10010000, 0b11111000, 0b11101010, 0b10100010, 0b10010100, 0b11100001, 0b11001101, 0b11010010, 0b11011010, 0b10010100, 0b10100010, 0b10010000, 0b11000101, 0b10101100}; +PGM_P pCwSymbolTable = reinterpret_cast(cwSymbolTable); +////const PROGMEM uint8_t cwSymbolLength[] = {6, 6, 6, 6, 6, 5, 5, 6, 5, 6, 6, 5, 5, 6, 6, 6, 6, 5, 5, 5, 6, 5}; + +// ":(Start"), ':(End "), >: My callsign, <:QSO Callsign (Second Callsign), #:AR, ~:BT, [:AS, ]:SK + +byte knobPosition = 0; +//byte cwTextData[30]; //Maximum 30 Remarked by KD8CE -> Direct Read EEPROM +byte autoCWSendCharEndIndex = 0; +byte autoCWSendCharIndex = 0; +unsigned long autoCWbeforeTime = 0; //for interval time between chars +byte pttBeforeStatus = 1; //PTT : default high +byte isKeyStatusAfterCWStart = 0; //0 : Init, 1 : Keyup after auto CW Start, 2 : Keydown after +byte selectedCWTextIndex = 0; +unsigned long autoCWKeydownCheckTime = 0; //for interval time between chars +byte changeReserveStatus = 0; +byte isAutoCWHold = 0; //auto CW Pause => Manual Keying => auto + +void autoSendPTTCheck() +{ + if (isCWAutoMode == 2) { //Sending Mode + //check PTT Button + //short Press => reservation or cancel + //long Press => Hold + if (digitalRead(PTT) == LOW) + { + //if (isKeyStatusAfterCWStart == 0) //Yet Press PTT from start TX + //{ + //} + + if (isKeyStatusAfterCWStart == 1) //while auto cw send, ptt up and ptt down again + { + //Start Time + autoCWKeydownCheckTime = millis() + 200; //Long push time + isKeyStatusAfterCWStart = 2; //Change status => ptt down agian + } + else if (isKeyStatusAfterCWStart == 2 && autoCWKeydownCheckTime < millis()) + { + //Hold Mode + isAutoCWHold = 1; + isKeyStatusAfterCWStart = 3; + } + else if (isKeyStatusAfterCWStart == 3) + { + autoCWKeydownCheckTime = millis() + 200; + } + } + else + { + //PTT UP + if (isKeyStatusAfterCWStart == 2) //0 (down before cw start) -> 1 (up while cw sending) -> 2 (down while cw sending) + { + if (autoCWKeydownCheckTime > millis()) //Short : Reservation or cancel Next Text + { + if (autoCWSendReservCount == 0 || + (autoCWSendReservCount < AUTO_CW_RESERVE_MAX && + autoCWSendReserv[autoCWSendReservCount - 1] != selectedCWTextIndex)) + { + //Reserve + autoCWSendReserv[autoCWSendReservCount++] = selectedCWTextIndex; + changeReserveStatus = 1; + } + else if (autoCWSendReservCount > 0 && autoCWSendReserv[autoCWSendReservCount - 1] == selectedCWTextIndex) + { + autoCWSendReservCount--; + changeReserveStatus = 1; + } + } // end of Short Key up + } + else if (isKeyStatusAfterCWStart == 3) //play from Hold (pause Auto CW Send) + { + isAutoCWHold = 0; + } + + isKeyStatusAfterCWStart = 1; //Change status => ptt up (while cw send mode) + } //end of PTT UP + } +} + +//Send 1 char +void sendCWChar(char cwKeyChar) +{ + byte sendBuff[7]; + byte i, j, charLength; + byte tmpChar; + + //For Macrofunction + //replace > and < to My callsign, qso callsign, use recursive function call + if (cwKeyChar == '>' || cwKeyChar == '<') + { + uint16_t callsignStartIndex = 0; + uint16_t callsignEndIndex = 0; + + if (cwKeyChar == '>') //replace my callsign + { + if (userCallsignLength > 0) + { + callsignStartIndex = 0; + callsignEndIndex = userCallsignLength; + } + } + else if (cwKeyChar == '<') //replace qso callsign + { + //ReadLength + callsignEndIndex = EEPROM.read(CW_STATION_LEN); + if (callsignEndIndex > 0) + { + callsignStartIndex = CW_STATION_LEN - callsignEndIndex - USER_CALLSIGN_DAT; + callsignEndIndex = callsignStartIndex + callsignEndIndex; + } + } + + if (callsignStartIndex == 0 && callsignEndIndex == 0) + return; + + for (uint16_t i = callsignStartIndex; i <= callsignEndIndex; i++) + { + sendCWChar(EEPROM.read(USER_CALLSIGN_DAT + i)); + autoSendPTTCheck(); //for reserve and cancel next CW Text + if (changeReserveStatus == 1) + { + changeReserveStatus = 0; + updateDisplay(); + } + + if (i < callsignEndIndex) delay_background(cwSpeed * 3, 4); // + } + + return; + } + else if (cwKeyChar >= 'A' && cwKeyChar <= 'Z') //Encode Char by KD8CEC + { + tmpChar = pgm_read_byte(pCwAZTable + (cwKeyChar - 'A')); + charLength = (tmpChar >> 4) & 0x0F; + for (i = 0; i < charLength; i++) + sendBuff[i] = (tmpChar << i) & 0x08; + } + else if (cwKeyChar >= '0' && cwKeyChar <= '9') + { + charLength = 5; + for (i = 0; i < charLength; i++) + sendBuff[i] = (pgm_read_byte(pcw09Table + (cwKeyChar - '0')) << i) & 0x10; + } + else if (cwKeyChar == ' ') + { + charLength = 0; + delay_background(cwSpeed * 4, 4); //7 -> basic interval is 3 + } + else if (cwKeyChar == '$') //7 digit + { + charLength = 7; + for (i = 0; i < 7; i++) + sendBuff[i] = (0b00010010 << i) & 0x80; //...1..1 + } + else + { + //symbol + for (i = 0; i < 22; i++) + { + if (pgm_read_byte(pCwSymbolIndex + i) == cwKeyChar) + { + tmpChar = pgm_read_byte(pCwSymbolTable + i); + charLength = ((tmpChar >> 6) & 0x03) + 3; + + for (j = 0; j < charLength; j++) + sendBuff[j] = (tmpChar << j + 2) & 0x80; + + break; + } + } + } + + for (i = 0; i < charLength; i++) + { + cwKeydown(); + if (sendBuff[i] == 0) + delay_background(cwSpeed, 4); + else + delay_background(cwSpeed * 3, 4); + cwKeyUp(); + if (i != charLength -1) + delay_background(cwSpeed, 4); + } +} + +/* +void sendAutoCW(int cwSendLength, char *sendString) +{ + byte i; + + if (!inTx){ + keyDown = 0; + cwTimeout = millis() + cwDelayTime * 10; + startTx(TX_CW, 0); //disable updateDisplay Command for reduce latency time + updateDisplay(); + + delay_background(delayBeforeCWStartTime * 2, 2); + } + + for (i = 0; i < cwSendLength; i++) + { + sendCWChar(sendString[i]); + if (i != cwSendLength -1) delay_background(cwSpeed * 3, 3); + } + + delay_background(cwDelayTime * 10, 2); + stopTx(); +} +*/ +byte isNeedScroll = 0; +unsigned long scrollDispayTime = 0; +#define scrollSpeed 500 +byte displayScrolStep = 0; + +int controlAutoCW(){ + int knob = 0; + byte i; + + byte cwStartIndex, cwEndIndex; + + if (cwAutoDialType == 0) + knob = enc_read(); + + if (knob != 0 || beforeCWTextIndex == 255 || isNeedScroll == 1){ //start display + if (knobPosition > 0 && knob < 0) + knobPosition--; + if (knobPosition < cwAutoTextCount * 10 -1 && knob > 0) + knobPosition++; + + selectedCWTextIndex = knobPosition / 10; + + if ((beforeCWTextIndex != selectedCWTextIndex) || + (isNeedScroll == 1 && beforeCWTextIndex == selectedCWTextIndex && scrollDispayTime < millis())) { + //Read CW Text Data Position From EEProm + EEPROM.get(CW_AUTO_DATA + (selectedCWTextIndex * 2), cwStartIndex); + EEPROM.get(CW_AUTO_DATA + (selectedCWTextIndex * 2 + 1), cwEndIndex); + + if (beforeCWTextIndex == selectedCWTextIndex) + { + if (++displayScrolStep > cwEndIndex - cwStartIndex) + displayScrolStep = 0; + } + else + { + displayScrolStep = 0; + } + + printLineFromEEPRom(0, 2, cwStartIndex + displayScrolStep + CW_DATA_OFSTADJ, cwEndIndex + CW_DATA_OFSTADJ); + + lcd.setCursor(0,0); + lcd.write(byteToChar(selectedCWTextIndex)); + lcd.write(':'); + isNeedScroll = (cwEndIndex - cwStartIndex) > 14 ? 1 : 0; + scrollDispayTime = millis() + scrollSpeed; + beforeCWTextIndex = selectedCWTextIndex; + } + } //end of check knob + + if (isCWAutoMode == 1) { //ready status + if (digitalRead(PTT) == LOW) //PTT Down : Start Auto CW or DialMode Change + { + if (pttBeforeStatus == 1) //High to Low Change + { + autoCWbeforeTime = millis() + 500; //Long push time + pttBeforeStatus = 0; + } + else if (autoCWbeforeTime < millis()) //while press PTT, OK Long push then Send Auto CW Text + { + sendingCWTextIndex = selectedCWTextIndex; + + //Information about Auto Send CW Text + autoCWSendCharEndIndex = cwEndIndex; //length of CW Text //ianlee + autoCWSendCharIndex = cwStartIndex; //position of Sending Char //ianlee + + isCWAutoMode = 2; //auto sending start + autoCWbeforeTime = 0; //interval between chars, 0 = always send + isKeyStatusAfterCWStart = 0; //Init PTT Key status + autoCWSendReservCount = 0; //Init Reserve Count + isAutoCWHold = 0; + if (!inTx){ //if not TX Status, change RX -> TX + keyDown = 0; + startTx(TX_CW, 0); //disable updateDisplay Command for reduce latency time + updateDisplay(); + + delay_background(delayBeforeCWStartTime * 2, 2); //for External AMP or personal situation + } + } + } + else if (pttBeforeStatus == 0 && autoCWbeforeTime > 0) //while reade status LOW -> HIGH (before Auto send Before) + { + pttBeforeStatus = 1; //HIGH + if (autoCWbeforeTime > millis()) //short Press -> ? DialModeChange + { + cwAutoDialType = (cwAutoDialType == 1 ? 0 : 1); //Invert DialMode between select CW Text and Frequency Tune + if (cwAutoDialType == 0) + printLineF1(F("Dial:Select Text")); + else + printLineF1(F("Dial:Freq Tune")); + + delay_background(1000, 0); + updateDisplay(); + } + } + } //end of isCWAutoMode == 1 condition + + if (isCWAutoMode == 2) { //Sending Mode + autoSendPTTCheck(); + + //check interval time, if you want adjust interval between chars, modify below + if (isAutoCWHold == 0 && (millis() - autoCWbeforeTime > cwSpeed * 3)) + { + sendCWChar(EEPROM.read(CW_AUTO_DATA + autoCWSendCharIndex++)); + + if (autoCWSendCharIndex > autoCWSendCharEndIndex) { //finish auto cw send + //check reserve status + if (autoCWSendReservCount > 0) + { + //prepare + sendingCWTextIndex = autoCWSendReserv[0]; + + for (i = 0; i < AUTO_CW_RESERVE_MAX -1; i++) + autoCWSendReserv[i] = autoCWSendReserv[i + 1]; + + EEPROM.get(CW_AUTO_DATA + (sendingCWTextIndex * 2), cwStartIndex); + EEPROM.get(CW_AUTO_DATA + (sendingCWTextIndex * 2 + 1), cwEndIndex); + + //Information about Auto Send CW Text + autoCWSendCharEndIndex = cwEndIndex; //length of CW Text //ianlee + autoCWSendCharIndex = cwStartIndex; //position of Sending Char //ianlee + autoCWSendReservCount--; //Decrease + + sendCWChar(' '); //APPLY SPACE between CW Texts + changeReserveStatus = 1; + } + else + { + isCWAutoMode = 1; //ready status + delay_background(cwDelayTime * 10, 2); + stopTx(); + } + } + + autoCWbeforeTime = millis(); + + if (changeReserveStatus == 1) + { + changeReserveStatus = 0; + updateDisplay(); + } + } + } + + //abort if this button is down + if (btnDown()) + { + isCWAutoMode = 0; //dsiable Auto CW Mode + printLine2ClearAndUpdate(); + delay_background(1000, 0); + } +} + diff --git a/ubitx_20/ubitx_20.ino b/ubitx_20/ubitx_20.ino index c6aab85..9c0760b 100644 --- a/ubitx_20/ubitx_20.ino +++ b/ubitx_20/ubitx_20.ino @@ -96,6 +96,8 @@ #include LiquidCrystal lcd(8,9,10,11,12,13); +#define VERSION_NUM 0x01 //for KD8CEC'S firmware and for memory management software + /** * The Arduino, unlike C/C++ on a regular computer with gigabytes of RAM, has very little memory. * We have to be very careful with variables that are declared inside the functions as they are @@ -141,6 +143,26 @@ int count = 0; //to generally count ticks, loops, etc #define CW_SIDETONE 24 #define CW_SPEED 28 +//AT328 has 1KBytes EEPROM +#define VFO_A_MODE 256 +#define VFO_B_MODE 257 +#define CW_DELAY 258 +#define CW_START 259 + +// +#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 /** * 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, @@ -168,6 +190,10 @@ int count = 0; //to generally count ticks, loops, etc #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) + //we directly generate the CW by programmin the Si5351 to the cw tx frequency, hence, both are different modes //these are the parameter passed to startTx #define TX_SSB 0 @@ -177,11 +203,47 @@ 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; +unsigned long vfoA_eeprom, vfoB_eeprom; //for protect eeprom life unsigned long frequency, ritRxFrequency, ritTxFrequency; //frequency is the current frequency on the dial 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; + +bool isSplitOn = false; +byte cwDelayTime = 60; +byte delayBeforeCWStartTime = 50; + +//sideTonePitch + sideToneSub = sideTone +byte sideTonePitch=0; +byte sideToneSub = 0; + +//DialLock +byte isDialLock = 0; +byte isTxOff = 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 */ @@ -202,6 +264,41 @@ boolean modeCalibrate = false;//this mode of menus shows extended menus to calib * you start hacking around */ +/* + 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(); + + 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 @@ -257,7 +354,10 @@ void setTXFilters(unsigned long freq){ void setFrequency(unsigned long f){ uint64_t osc_f; - + + //1 digits discarded + f = (f / 50) * 50; + setTXFilters(f); if (isUSB){ @@ -278,9 +378,12 @@ void setFrequency(unsigned long f){ * Note: In cw mode, doesnt key the radio, only puts it in tx mode */ -void startTx(byte txMode){ - unsigned long tx_freq = 0; - digitalWrite(TX_RX, 1); +void startTx(byte txMode, byte isDisplayUpdate){ + unsigned long tx_freq = 0; + + if (isTxOff != 1) + digitalWrite(TX_RX, 1); + inTx = 1; if (ritOn){ @@ -302,7 +405,10 @@ void startTx(byte txMode){ else si5351bx_setfreq(2, frequency - sideTone); } - updateDisplay(); + + //reduce latency time when begin of CW mode + if (isDisplayUpdate == 1) + updateDisplay(); } void stopTx(){ @@ -355,7 +461,7 @@ void checkPTT(){ return; if (digitalRead(PTT) == 0 && inTx == 0){ - startTx(TX_SSB); + startTx(TX_SSB, 1); delay(50); //debounce the PTT } @@ -374,9 +480,12 @@ void checkButton(){ return; doMenu(); + //wait for the button to go up again - while(btnDown()) + while(btnDown()) { delay(10); + Check_Cat(0); + } delay(50);//debounce } @@ -389,33 +498,46 @@ void checkButton(){ */ void doTuning(){ - int s; + int s = 0; unsigned long prev_freq; + int incdecValue = 0; + + if (isDialLock == 1) + return; + + if (isCWAutoMode == 0 || cwAutoDialType == 1) + s = enc_read(); - s = enc_read(); if (s){ prev_freq = frequency; if (s > 10) - frequency += 200000l; + incdecValue = 200000l; if (s > 7) - frequency += 10000l; + incdecValue = 10000l; else if (s > 4) - frequency += 1000l; + incdecValue = 1000l; else if (s > 2) - frequency += 500; + incdecValue = 500; else if (s > 0) - frequency += 50l; + incdecValue = 50l; else if (s > -2) - frequency -= 50l; + incdecValue = -50l; else if (s > -4) - frequency -= 500l; + incdecValue = -500l; else if (s > -7) - frequency -= 1000l; + incdecValue = -1000l; else if (s > -9) - frequency -= 10000l; + incdecValue = -10000l; else - frequency -= 200000l; + incdecValue = -200000l; + + if (incdecValue > 0 && frequency + incdecValue > HIGHEST_FREQ_DIAL) + frequency = HIGHEST_FREQ_DIAL; + else if (incdecValue < 0 && frequency < -incdecValue + LOWEST_FREQ_DIAL) //for compute and compare based integer type. + frequency = LOWEST_FREQ_DIAL; + else + frequency += incdecValue; if (prev_freq < 10000000l && frequency > 10000000l) isUSB = true; @@ -448,6 +570,39 @@ void doRIT(){ } } +/** + save Frequency and mode to eeprom + */ +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; + } + } +} + /** * 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 @@ -462,17 +617,67 @@ void initSettings(){ EEPROM.get(VFO_B, vfoB); EEPROM.get(CW_SIDETONE, sideTone); EEPROM.get(CW_SPEED, cwSpeed); + + //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); + + //User callsign information + if (EEPROM.read(USER_CALLSIGN_KEY) == 0x59) + userCallsignLength = EEPROM.read(USER_CALLSIGN_LEN); //MAXIMUM 18 LENGTH + + //Version Write for Memory Management Software + if (EEPROM.read(VERSION_ADDRESS) != VERSION_NUM) + EEPROM.write(VERSION_ADDRESS, VERSION_NUM); + + if (cwDelayTime < 1 || cwDelayTime > 250) + cwDelayTime = 60; + + if (vfoA_mode < 2) + vfoA_mode = 2; + + if (vfoB_mode < 2) + vfoB_mode = 3; + if (usbCarrier > 12010000l || usbCarrier < 11990000l) - usbCarrier = 11997000l; - if (vfoA > 35000000l || 3500000l > vfoA) + usbCarrier = 11995000l; + + if (vfoA > 35000000l || 3500000l > vfoA) { vfoA = 7150000l; - if (vfoB > 35000000l || 3500000l > vfoB) + vfoA_mode = 2; + } + + if (vfoB > 35000000l || 3500000l > vfoB) { vfoB = 14150000l; + vfoB_mode = 3; + } + + //for protect eeprom life + 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(){ @@ -510,41 +715,185 @@ void initPorts(){ void setup() { - Serial.begin(9600); - + //Init EEProm for Fault EEProm TEST and Factory Reset + /* + for (int i = 0; i < 1024; i++) + EEPROM.write(i, 0); + */ + //Serial.begin(9600); lcd.begin(16, 2); - //we print this line so this shows up even if the raduino - //crashes later in the code - printLine1("uBITX v0.20"); - delay(500); - + Init_Cat(38400, SERIAL_8N1); initMeter(); //not used in this build initSettings(); + + printLineF(1, F("CECBT v0.25")); + if (userCallsignLength > 0 && ((userCallsignLength & 0x80) == 0x80)) + { + userCallsignLength = userCallsignLength & 0x7F; + printLineFromEEPRom(0, 0, 0, userCallsignLength -1); //eeprom to lcd use offset (USER_CALLSIGN_DAT) + } + else + { + printLineF(0, F("uBITX v0.20")); + delay_background(500, 0); + printLine2(""); + } + initPorts(); initOscillators(); frequency = vfoA; + saveCheckFreq = frequency; //for auto save frequency + byteToMode(vfoA_mode); setFrequency(vfoA); updateDisplay(); if (btnDown()) factory_alignment(); + +/* + //This is for auto key test + EEPROM.put(CW_AUTO_MAGIC_KEY, 0x73); //MAGIC KEY + EEPROM.put(CW_AUTO_COUNT, 3); //WORD COUNT + EEPROM.put(CW_AUTO_DATA + 0, 6); // 0 word begin postion / CQCQ TEST K + EEPROM.put(CW_AUTO_DATA + 1, 33); // 0 word end postion / CQCQ TEST K + EEPROM.put(CW_AUTO_DATA + 2, 34); //1 word begin position / LOL LOL + EEPROM.put(CW_AUTO_DATA + 3, 40); //1 word end position / LOL LOL + EEPROM.put(CW_AUTO_DATA + 4, 41); //2 word begin position / /?![]789 + EEPROM.put(CW_AUTO_DATA + 5, 48); //2 word end position / /?![]789 + + EEPROM.put(CW_AUTO_DATA + 6, 'C'); // + EEPROM.put(CW_AUTO_DATA + 7, 'Q'); // + EEPROM.put(CW_AUTO_DATA + 8, 'C'); // + EEPROM.put(CW_AUTO_DATA + 9, 'Q'); // + EEPROM.put(CW_AUTO_DATA + 10, ' '); // + EEPROM.put(CW_AUTO_DATA + 11, 'D'); // + EEPROM.put(CW_AUTO_DATA + 12, 'E'); // + EEPROM.put(CW_AUTO_DATA + 13, ' '); // + EEPROM.put(CW_AUTO_DATA + 14, 'K'); // + EEPROM.put(CW_AUTO_DATA + 15, 'D'); // + EEPROM.put(CW_AUTO_DATA + 16, '8'); // + EEPROM.put(CW_AUTO_DATA + 17, 'C'); // + EEPROM.put(CW_AUTO_DATA + 18, 'E'); // + EEPROM.put(CW_AUTO_DATA + 19, 'C'); // + EEPROM.put(CW_AUTO_DATA + 20, ' '); // + EEPROM.put(CW_AUTO_DATA + 21, 'E'); // + EEPROM.put(CW_AUTO_DATA + 22, 'M'); // + EEPROM.put(CW_AUTO_DATA + 23, '3'); // + EEPROM.put(CW_AUTO_DATA + 24, '7'); // + EEPROM.put(CW_AUTO_DATA + 25, ' '); // + EEPROM.put(CW_AUTO_DATA + 26, 'D'); // + EEPROM.put(CW_AUTO_DATA + 27, 'E'); // + EEPROM.put(CW_AUTO_DATA + 28, ' '); // + EEPROM.put(CW_AUTO_DATA + 29, 'C'); // + EEPROM.put(CW_AUTO_DATA + 30, 'E'); // + EEPROM.put(CW_AUTO_DATA + 31, 'C'); // + EEPROM.put(CW_AUTO_DATA + 32, ' '); // + EEPROM.put(CW_AUTO_DATA + 33, 'K'); // +*/ + +/* + EEPROM.put(CW_AUTO_DATA + 34, '<'); // + EEPROM.put(CW_AUTO_DATA + 35, ' '); // + EEPROM.put(CW_AUTO_DATA + 36, '>'); // + EEPROM.put(CW_AUTO_DATA + 37, ' '); // + EEPROM.put(CW_AUTO_DATA + 38, '7'); // + EEPROM.put(CW_AUTO_DATA + 39, '3'); // + EEPROM.put(CW_AUTO_DATA + 40, 'K'); // + + EEPROM.put(CW_AUTO_DATA + 41, 'C'); // + EEPROM.put(CW_AUTO_DATA + 42, 'Q'); // + EEPROM.put(CW_AUTO_DATA + 43, ' '); // + EEPROM.put(CW_AUTO_DATA + 44, '>'); // start " + EEPROM.put(CW_AUTO_DATA + 45, ' '); // end " + EEPROM.put(CW_AUTO_DATA + 46, '>'); // + EEPROM.put(CW_AUTO_DATA + 47, ' '); // + EEPROM.put(CW_AUTO_DATA + 48, 'K'); // +*/ + +/* + //This is for auto key test2 + //USER CALL SIGN + EEPROM.put(USER_CALLSIGN_KEY, 0x59); //MAGIC KEY + //EEPROM.put(USER_CALLSIGN_LEN, 10); //WORD COUNT + EEPROM.put(USER_CALLSIGN_LEN, 10 + 0x80); //WORD COUNT + + EEPROM.put(USER_CALLSIGN_DAT + 1, 'K'); // + EEPROM.put(USER_CALLSIGN_DAT + 2, 'D'); // + EEPROM.put(USER_CALLSIGN_DAT + 3, '8'); // + EEPROM.put(USER_CALLSIGN_DAT + 4, 'C'); // + EEPROM.put(USER_CALLSIGN_DAT + 5, 'E'); // + EEPROM.put(USER_CALLSIGN_DAT + 6, 'C'); // + EEPROM.put(USER_CALLSIGN_DAT + 7, '/'); // + EEPROM.put(USER_CALLSIGN_DAT + 8, 'A'); // + EEPROM.put(USER_CALLSIGN_DAT + 9, 'B'); // + EEPROM.put(USER_CALLSIGN_DAT + 10, 'C'); // + + //CW QSO CALLSIGN + EEPROM.put(CW_STATION_LEN, 6); // + EEPROM.put(CW_STATION_LEN - 6 + 0 , 'A'); // + EEPROM.put(CW_STATION_LEN - 6 + 1 , 'B'); // + EEPROM.put(CW_STATION_LEN - 6 + 2 , '1'); // + EEPROM.put(CW_STATION_LEN - 6 + 3 , 'C'); // + EEPROM.put(CW_STATION_LEN - 6 + 4 , 'D'); // + EEPROM.put(CW_STATION_LEN - 6 + 5 , 'E'); // +*/ + } /** * The loop checks for keydown, ptt, function button and tuning. */ - +//for debug +int dbgCnt = 0; byte flasher = 0; -void loop(){ - - cwKeyer(); - if (!txCAT) - checkPTT(); - checkButton(); +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) + { + if (vfoActive == VFO_A) + { + vfoA = frequency; + vfoA_mode = modeToByte(); + storeFrequencyAndMode(1); + } + else + { + vfoB = frequency; + vfoB_mode = modeToByte(); + storeFrequencyAndMode(2); + } + } + } +} + +void loop(){ + if (isCWAutoMode == 0){ //when CW AutoKey Mode, disable this process + if (!txCAT) + checkPTT(); + checkButton(); + } + else + controlAutoCW(); + + cwKeyer(); + //tune only when not tranmsitting if (!inTx){ if (ritOn) @@ -552,7 +901,8 @@ void loop(){ else doTuning(); } - + //we check CAT after the encoder as it might put the radio into TX - checkCAT(); + Check_Cat(inTx? 1 : 0); + checkAutoSaveFreqMode(); } diff --git a/ubitx_20/ubitx_cat.ino b/ubitx_20/ubitx_cat.ino deleted file mode 100644 index 687595c..0000000 --- a/ubitx_20/ubitx_cat.ino +++ /dev/null @@ -1,231 +0,0 @@ -/** - * The CAT protocol is used by many radios to provide remote control to comptuers through - * the serial port. - * - * This is very much a work in progress. Parts of this code have been liberally - * borrowed from other GPLicensed works like hamlib. - * - * WARNING : This is an unstable version and it has worked with fldigi, - * it gives time out error with WSJTX 1.8.0 - */ - -// The next 4 functions are needed to implement the CAT protocol, which -// uses 4-bit BCD formatting. -// -byte setHighNibble(byte b,byte v) { - // Clear the high nibble - b &= 0x0f; - // Set the high nibble - return b | ((v & 0x0f) << 4); -} - -byte setLowNibble(byte b,byte v) { - // Clear the low nibble - b &= 0xf0; - // Set the low nibble - return b | (v & 0x0f); -} - -byte getHighNibble(byte b) { - return (b >> 4) & 0x0f; -} - -byte getLowNibble(byte b) { - return b & 0x0f; -} - -// Takes a number and produces the requested number of decimal digits, staring -// from the least significant digit. -// -void getDecimalDigits(unsigned long number,byte* result,int digits) { - for (int i = 0; i < digits; i++) { - // "Mask off" (in a decimal sense) the LSD and return it - result[i] = number % 10; - // "Shift right" (in a decimal sense) - number /= 10; - } -} - -// Takes a frequency and writes it into the CAT command buffer in BCD form. -// -void writeFreq(unsigned long freq,byte* cmd) { - // Convert the frequency to a set of decimal digits. We are taking 9 digits - // so that we can get up to 999 MHz. But the protocol doesn't care about the - // LSD (1's place), so we ignore that digit. - byte digits[9]; - getDecimalDigits(freq,digits,9); - // Start from the LSB and get each nibble - cmd[3] = setLowNibble(cmd[3],digits[1]); - cmd[3] = setHighNibble(cmd[3],digits[2]); - cmd[2] = setLowNibble(cmd[2],digits[3]); - cmd[2] = setHighNibble(cmd[2],digits[4]); - cmd[1] = setLowNibble(cmd[1],digits[5]); - cmd[1] = setHighNibble(cmd[1],digits[6]); - cmd[0] = setLowNibble(cmd[0],digits[7]); - cmd[0] = setHighNibble(cmd[0],digits[8]); -} - -// This function takes a frquency that is encoded using 4 bytes of BCD -// representation and turns it into an long measured in Hz. -// -// [12][34][56][78] = 123.45678? Mhz -// -unsigned long readFreq(byte* cmd) { - // Pull off each of the digits - byte d7 = getHighNibble(cmd[0]); - byte d6 = getLowNibble(cmd[0]); - byte d5 = getHighNibble(cmd[1]); - byte d4 = getLowNibble(cmd[1]); - byte d3 = getHighNibble(cmd[2]); - byte d2 = getLowNibble(cmd[2]); - byte d1 = getHighNibble(cmd[3]); - byte d0 = getLowNibble(cmd[3]); - return - (unsigned long)d7 * 100000000L + - (unsigned long)d6 * 10000000L + - (unsigned long)d5 * 1000000L + - (unsigned long)d4 * 100000L + - (unsigned long)d3 * 10000L + - (unsigned long)d2 * 1000L + - (unsigned long)d1 * 100L + - (unsigned long)d0 * 10L; -} - -/** - * Responds to all the cat commands, emulates FT-817 - */ - -void processCATCommand(byte* cmd) { - byte response[5]; - - // Debugging code, enable it to fix the cat implementation - - count++; - if (cmd[4] == 0x00){ - response[0]=0; - Serial.write(response, 1); - } - else if (cmd[4] == 0x01) { - unsigned long f = readFreq(cmd); - setFrequency(f); - updateDisplay(); - //sprintf(b, "set:%ld", f); - //printLine2(b); - - } - // Get frequency - else if (cmd[4] == 0x03){ - writeFreq(frequency,response); // Put the frequency into the buffer - if (isUSB) - response[4] = 0x01; //USB - else - response[4] = 0x00; //LSB - Serial.write(response,5); - printLine2("cat:getfreq"); - } - else if (cmd[4] == 0x07){ // set mode - if (cmd[0] == 0x00 || cmd[0] == 0x03) - isUSB = 0; - else - isUSB = 1; - response[0] = 0x00; - Serial.write(response, 1); - setFrequency(frequency); - //printLine2("cat: mode changed"); - //updateDisplay(); - } - else if (cmd[4] == 0x88){ - if (inTx){ - stopTx(); - txCAT = false; - } - else - response[0] = 0xf0; - printLine2("tx > rx"); - Serial.write(response,1); - } - else if (cmd[4] == 0x08) { // PTT On - if (!inTx) { - response[0] = 0; - txCAT = true; - startTx(TX_SSB); - updateDisplay(); - } else { - response[0] = 0xf0; - } - Serial.write(response,1); - printLine2("rx > tx"); - } - // Read TX keyed state - else if (cmd[4] == 0x10) { - if (!inTx) { - response[0] = 0; - } else { - response[0] = 0xf0; - } - Serial.write(response,1); - printLine2("cat;0x10"); - } - // PTT Off - else if (cmd[4] == 0x88) { - byte resBuf[0]; - if (inTx) { - response[0] = 0; - } else { - response[0] = 0xf0; - } - Serial.write(response,1); - printLine2("cat;0x88"); - //keyed = false; - //digitalWrite(13,LOW); - } - // Read receiver status - else if (cmd[4] == 0xe7) { - response[0] = 0x09; - Serial.write(response,1); - printLine2("cat;0xe7"); - } - else if (cmd[4] == 0xf5){ - - } - // Read receiver status - else if (cmd[4] == 0xf7) { - response[0] = 0x00; - if (inTx) { - response[0] = response[0] | 0xf0; - } - Serial.write(response,1); - printLine2("cat;0xf7"); - } - else { - //somehow, get this to print the four bytes - ultoa(*((unsigned long *)cmd), c, 16); - itoa(cmd[4], b, 16); - strcat(b, ":"); - strcat(b, c); - printLine2(b); - response[0] = 0x00; - Serial.write(response[0]); - } - -} - - - -void checkCAT(){ - static byte cat[5]; - byte i; - - if (Serial.available() < 5) - return; - - cat[4] = cat[3]; - cat[3] = cat[2]; - cat[2] = cat[0]; - for (i = 0; i < 5; i++) - cat[i] = Serial.read(); - - processCATCommand(cat); -} - - diff --git a/ubitx_20/ubitx_keyer.ino b/ubitx_20/ubitx_keyer.ino index 3a9c86f..5d2b668 100644 --- a/ubitx_20/ubitx_keyer.ino +++ b/ubitx_20/ubitx_keyer.ino @@ -23,7 +23,7 @@ // in milliseconds, this is the parameter that determines how long the tx will hold between cw key downs -#define CW_TIMEOUT (600l) +//#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 @@ -61,7 +61,10 @@ void cwKeydown(){ keyDown = 1; //tracks the CW_KEY tone(CW_TONE, (int)sideTone); digitalWrite(CW_KEY, 1); - cwTimeout = millis() + CW_TIMEOUT; + + //Modified by KD8CEC, for CW Delay Time save to eeprom + //cwTimeout = millis() + CW_TIMEOUT; + cwTimeout = millis() + cwDelayTime * 10; } /** @@ -72,7 +75,10 @@ void cwKeyUp(){ keyDown = 0; //tracks the CW_KEY noTone(CW_TONE); digitalWrite(CW_KEY, 0); - cwTimeout = millis() + CW_TIMEOUT; + + //Modified by KD8CEC, for CW Delay Time save to eeprom + //cwTimeout = millis() + CW_TIMEOUT; + cwTimeout = millis() + cwDelayTime * 10; } /** @@ -92,6 +98,10 @@ void cwKeyer(){ // 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; @@ -103,48 +113,69 @@ void cwKeyer(){ //if a paddle was used (not a straight key) we should extend the space to be a full dash //by adding two more dots long space (one has already been added at the end of the dot or dash) + /* if (cwTimeout > 0 && lastPaddle != PADDLE_STRAIGHT) - delay(cwSpeed * 2); + delay_background(cwSpeed * 2, 3); + //delay(cwSpeed * 2); // got back to the begining of the loop, if no further activity happens on the paddle or the straight key // we will time out, and return out of this routine delay(5); + */ continue; } - Serial.print("paddle:");Serial.println(paddle); + //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; - cwTimeout = millis() + CW_TIMEOUT; - startTx(TX_CW); + //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(cwSpeed); + delay_background(cwSpeed, 3); lastPaddle = PADDLE_DOT; } else if (paddle == PADDLE_DASH){ - delay(cwSpeed * 3); + //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(cwSpeed * 3); + delay_background(cwSpeed * 3, 3); lastPaddle = PADDLE_DASH; }else{ - delay(cwSpeed); + //delay(cwSpeed); + delay_background(cwSpeed, 3); lastPaddle = PADDLE_DOT; } } else if (paddle == PADDLE_STRAIGHT){ - while (getPaddle() == PADDLE_STRAIGHT) + while (getPaddle() == PADDLE_STRAIGHT) { delay(1); + Check_Cat(2); + } lastPaddle = PADDLE_STRAIGHT; } cwKeyUp(); diff --git a/ubitx_20/ubitx_menu.ino b/ubitx_20/ubitx_menu.ino index fff0378..a242ea3 100644 --- a/ubitx_20/ubitx_menu.ino +++ b/ubitx_20/ubitx_menu.ino @@ -10,8 +10,8 @@ * - 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 */ - - +#define printLineF1(x) (printLineF(1, x)) +#define printLineF2(x) (printLineF(0, x)) int menuBand(int btn){ int knob = 0; @@ -22,14 +22,17 @@ int menuBand(int btn){ // offset = frequency % 1000000l; if (!btn){ - printLine2("Band Select?"); + printLineF2(F("Band Select?")); return; } - printLine2("Press to confirm"); + printLineF2(F("Press to confirm")); //wait for the button menu select button to be lifted) - while (btnDown()) + while (btnDown()) { delay(50); + Check_Cat(0); //To prevent disconnections + } + delay(50); ritDisable(); @@ -58,50 +61,72 @@ int menuBand(int btn){ updateDisplay(); } delay(20); + Check_Cat(0); //To prevent disconnections } - while(btnDown()) + while(btnDown()) { delay(50); + Check_Cat(0); //To prevent disconnections + } + delay(50); - printLine2(""); - updateDisplay(); + printLine2ClearAndUpdate(); menuOn = 0; } -void menuVfoToggle(int btn){ - +//0: default, 1:not use, 2:LSB, 3:USB, 4:CW, 5:AM, 6:FM +byte modeToByte(){ + if (isUSB) + return 3; + else + return 2; +} + +void byteToMode(byte modeValue){ + if (modeValue == 3) + isUSB = 1; + else + isUSB = 0; +} + +void menuVfoToggle(int btn) +{ if (!btn){ if (vfoActive == VFO_A) - printLine2("Select VFO B? "); + printLineF2(F("Select VFO B?")); else - printLine2("Select VFO A? "); + printLineF2(F("Select VFO A?")); } else { if (vfoActive == VFO_B){ vfoB = frequency; - EEPROM.put(VFO_B, frequency); + vfoB_mode = modeToByte(); + storeFrequencyAndMode(2); //vfoB -> eeprom + vfoActive = VFO_A; - printLine2("Selected VFO A "); frequency = vfoA; + saveCheckFreq = frequency; + byteToMode(vfoA_mode); + printLineF2(F("Selected VFO A")); } else { vfoA = frequency; - EEPROM.put(VFO_A, frequency); + vfoA_mode = modeToByte(); + storeFrequencyAndMode(1); //vfoA -> eeprom + vfoActive = VFO_B; - printLine2("Selected VFO B "); frequency = vfoB; + saveCheckFreq = frequency; + byteToMode(vfoB_mode); + printLineF2(F("Selected VFO B")); } - + ritDisable(); - setFrequency(frequency); - if (frequency >= 10000000l) - isUSB = true; - else - isUSB = false; - updateDisplay(); - printLine2(""); - delay(1000); + + //updateDisplay(); + delay_background(500, 0); + printLine2ClearAndUpdate(); //exit the menu menuOn = 0; } @@ -110,49 +135,67 @@ void menuVfoToggle(int btn){ void menuRitToggle(int btn){ if (!btn){ if (ritOn == 1) - printLine2("RIT:On, Off? "); + printLineF2(F("RIT:On, Off?")); else - printLine2("RIT:Off, On? "); + printLineF2(F("RIT:Off, On?")); } else { if (ritOn == 0){ - printLine2("RIT is ON"); + printLineF2(F("RIT is ON")); //enable RIT so the current frequency is used at transmit ritEnable(frequency); } else{ - printLine2("RIT is OFF"); + printLineF2(F("RIT is OFF")); ritDisable(); } menuOn = 0; - delay(500); - printLine2(""); - updateDisplay(); + delay_background(500, 0); + printLine2ClearAndUpdate(); } } void menuSidebandToggle(int btn){ if (!btn){ if (isUSB == true) - printLine2("Select LSB?"); + printLineF2(F("Select LSB?")); else - printLine2("Select USB?"); + printLineF2(F("Select USB?")); } else { if (isUSB == true){ isUSB = false; - printLine2("LSB Selected"); - delay(500); - printLine2(""); + printLineF2(F("LSB Selected")); } else { isUSB = true; - printLine2("USB Selected"); - delay(500); - printLine2(""); + printLineF2(F("USB Selected")); } - - updateDisplay(); + setFrequency(frequency); + delay_background(500, 0); + printLine2ClearAndUpdate(); + menuOn = 0; + } +} + +void menuTxOnOff(int btn){ + if (!btn){ + if (isTxOff == 0) + printLineF2(F("TX OFF?")); + else + printLineF2(F("TX ON?")); + } + else { + if (isTxOff == 0){ + isTxOff = 1; + printLineF2(F("TX OFF!")); + } + else { + isTxOff = 0; + printLineF2(F("TX ON!")); + } + delay_background(500, 0); + printLine2ClearAndUpdate(); menuOn = 0; } } @@ -164,20 +207,20 @@ void menuSidebandToggle(int btn){ void menuSetup(int btn){ if (!btn){ if (!modeCalibrate) - printLine2("Setup On?"); + printLineF2(F("Setup On?")); else - printLine2("Setup Off?"); + printLineF2(F("Setup Off?")); }else { if (!modeCalibrate){ modeCalibrate = true; - printLine2("Setup:On "); + printLineF2(F("Setup:On")); } else { modeCalibrate = false; - printLine2("Setup:Off "); + printLineF2(F("Setup:Off")); } - delay(2000); - printLine2(""); + delay_background(2000, 0); + printLine2Clear(); menuOn = 0; } } @@ -185,13 +228,10 @@ void menuSetup(int btn){ void menuExit(int btn){ if (!btn){ - printLine2("Exit Menu? "); + printLineF2(F("Exit Menu?")); } else{ - printLine2("Exiting menu"); - delay(300); - printLine2(""); - updateDisplay(); + printLine2ClearAndUpdate(); menuOn = 0; } } @@ -211,12 +251,12 @@ int menuCWSpeed(int btn){ return; } - printLine1("Press PTT to set"); + printLineF1(F("Press PTT to set")); strcpy(b, "WPM:"); itoa(wpm,c, 10); strcat(b, c); printLine2(b); - delay(300); + delay_background(300, 0); while(!btnDown() && digitalRead(PTT) == HIGH){ @@ -236,19 +276,139 @@ int menuCWSpeed(int btn){ if (btnDown()) //re-enable the clock1 and clock 2 break; + + Check_Cat(0); //To prevent disconnections } //save the setting if (digitalRead(PTT) == LOW){ - printLine2("CW Speed set!"); + printLineF2(F("CW Speed set!")); cwSpeed = 1200/wpm; EEPROM.put(CW_SPEED, cwSpeed); - delay(2000); + delay_background(2000, 0); } - printLine2(""); + printLine2ClearAndUpdate(); menuOn = 0; } +int menuCWAutoKey(int btn){ + if (!btn){ + printLineF2(F("CW AutoKey Mode?")); + 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) + { + printLineF2(F("Empty CW data")); + delay_background(2000, 0); + return; + } + + printLineF1(F("Press PTT to Send")); + delay_background(500, 0); + updateDisplay(); + beforeCWTextIndex = 255; //255 value is for start check + isCWAutoMode = 1; + menuOn = 0; +} + +int menuSetupCwDelay(int btn){ + int knob = 0; + int tmpCWDelay = cwDelayTime * 10; + + if (!btn){ + strcpy(b, "CW TX->RX Delay"); + printLine2(b); + return; + } + + printLineF1(F("Press PTT to set")); + strcpy(b, "DELAY:"); + itoa(tmpCWDelay,c, 10); + strcat(b, c); + printLine2(b); + delay_background(300, 0); + + while(!btnDown() && digitalRead(PTT) == HIGH){ + knob = enc_read(); + if (knob != 0){ + if (tmpCWDelay > 3 && knob < 0) + tmpCWDelay -= 10; + if (tmpCWDelay < 2500 && knob > 0) + tmpCWDelay += 10; + + strcpy(b, "DELAY:"); + itoa(tmpCWDelay,c, 10); + strcat(b, c); + printLine2(b); + } + //abort if this button is down + if (btnDown()) + break; + + Check_Cat(0); //To prevent disconnections + } + + //save the setting + if (digitalRead(PTT) == LOW){ + printLineF2(F("CW Delay set!")); + cwDelayTime = tmpCWDelay / 10; + EEPROM.put(CW_DELAY, cwDelayTime); + delay_background(2000, 0); + } + printLine2ClearAndUpdate(); + menuOn = 0; +} + +int menuSetupTXCWInterval(int btn){ + int knob = 0; + int tmpTXCWInterval = delayBeforeCWStartTime * 2; + + if (!btn){ + strcpy(b, "CW Start Delay"); + printLine2(b); + return; + } + + printLineF1(F("Press PTT to set")); + strcpy(b, "Start Delay:"); + itoa(tmpTXCWInterval,c, 10); + strcat(b, c); + printLine2(b); + delay_background(300, 0); + + while(!btnDown() && digitalRead(PTT) == HIGH){ + knob = enc_read(); + if (knob != 0){ + if (tmpTXCWInterval > 0 && knob < 0) + tmpTXCWInterval -= 2; + if (tmpTXCWInterval < 500 && knob > 0) + tmpTXCWInterval += 2; + + strcpy(b, "Start Delay:"); + itoa(tmpTXCWInterval,c, 10); + strcat(b, c); + printLine2(b); + } + //abort if this button is down + if (btnDown()) + break; + + Check_Cat(0); //To prevent disconnections + } + + //save the setting + if (digitalRead(PTT) == LOW){ + printLineF2(F("CW Start set!")); + delayBeforeCWStartTime = tmpTXCWInterval / 2; + EEPROM.put(CW_START, delayBeforeCWStartTime); + delay_background(2000, 0); + } + printLine2ClearAndUpdate(); + menuOn = 0; +} /** @@ -276,7 +436,7 @@ int factoryCalibration(int btn){ delay(100); if (!btn){ - printLine2("Set Calibration?"); + printLineF2(F("Set Calibration?")); return 0; } @@ -287,7 +447,7 @@ int factoryCalibration(int btn){ //turn off the second local oscillator and the bfo si5351_set_calibration(calibration); - startTx(TX_CW); + startTx(TX_CW, 1); si5351bx_setfreq(2, 10000000l); strcpy(b, "#1 10 MHz cal:"); @@ -324,10 +484,10 @@ int factoryCalibration(int btn){ keyDown = 0; stopTx(); - printLine2("Calibration set!"); + printLineF2(F("Calibration set!")); EEPROM.put(MASTER_CAL, calibration); initOscillators(); - setFrequency(frequency); + setFrequency(frequency); updateDisplay(); while(btnDown()) @@ -340,13 +500,13 @@ int menuSetupCalibration(int btn){ int32_t prev_calibration; if (!btn){ - printLine2("Set Calibration?"); + printLineF2(F("Set Calibration?")); return 0; } - printLine1("Set to Zero-beat,"); - printLine2("press PTT to save"); - delay(1000); + printLineF1(F("Set to Zero-beat,")); + printLineF2(F("press PTT to save")); + delay_background(1000, 0); prev_calibration = calibration; calibration = 0; @@ -385,19 +545,18 @@ int menuSetupCalibration(int btn){ //save the setting if (digitalRead(PTT) == LOW){ - printLine1("Calibration set!"); - printLine2("Set Carrier now"); + printLineF1(F("Calibration set!")); + printLineF2(F("Set Carrier now")); EEPROM.put(MASTER_CAL, calibration); - delay(2000); + delay_background(2000, 0); } else calibration = prev_calibration; - printLine2(""); initOscillators(); //si5351_set_calibration(calibration); setFrequency(frequency); - updateDisplay(); + printLine2ClearAndUpdate(); menuOn = 0; } @@ -422,14 +581,14 @@ void menuSetupCarrier(int btn){ unsigned long prevCarrier; if (!btn){ - printLine2("Set the BFO"); + printLineF2(F("Set the BFO")); return; } prevCarrier = usbCarrier; - printLine1("Tune to best Signal"); - printLine2("PTT to confirm. "); - delay(1000); + printLineF1(F("Tune to best Signal")); + printLineF1(F("PTT to confirm. ")); + delay_background(1000, 0); usbCarrier = 11995000l; si5351bx_setfreq(0, usbCarrier); @@ -449,23 +608,23 @@ void menuSetupCarrier(int btn){ si5351bx_setfreq(0, usbCarrier); printCarrierFreq(usbCarrier); - + + Check_Cat(0); //To prevent disconnections delay(100); } //save the setting if (digitalRead(PTT) == LOW){ - printLine2("Carrier set! "); + printLineF2(F("Carrier set!")); EEPROM.put(USB_CAL, usbCarrier); - delay(1000); + delay_background(1000, 0); } else usbCarrier = prevCarrier; si5351bx_setfreq(0, usbCarrier); setFrequency(frequency); - updateDisplay(); - printLine2(""); + printLine2ClearAndUpdate(); menuOn = 0; } @@ -474,18 +633,18 @@ void menuSetupCwTone(int btn){ int prev_sideTone; if (!btn){ - printLine2("Change CW Tone"); + printLineF2(F("Change CW Tone")); return; } prev_sideTone = sideTone; - printLine1("Tune CW tone"); - printLine2("PTT to confirm. "); - delay(1000); + printLineF1(F("Tune CW tone")); + printLineF2(F("PTT to confirm.")); + delay_background(1000, 0); tone(CW_TONE, sideTone); //disable all clock 1 and clock 2 - while (digitalRead(PTT) == LOW || !btnDown()) + while (digitalRead(PTT) == HIGH && !btnDown()) { knob = enc_read(); @@ -501,30 +660,58 @@ void menuSetupCwTone(int btn){ printLine2(b); delay(100); + Check_Cat(0); //To prevent disconnections } noTone(CW_TONE); //save the setting if (digitalRead(PTT) == LOW){ - printLine2("Sidetone set! "); + printLineF2(F("Sidetone set!")); EEPROM.put(CW_SIDETONE, usbCarrier); - delay(2000); + delay_background(2000, 0); } else sideTone = prev_sideTone; - printLine2(""); - updateDisplay(); + printLine2ClearAndUpdate(); menuOn = 0; } +void setDialLock(byte tmpLock, byte fromMode) { + isDialLock = tmpLock; + + if (fromMode == 2 || fromMode == 3) return; + + if (isDialLock == 1) + printLineF2(F("Dial Lock ON")); + else + printLineF2(F("Dial Lock OFF")); + + delay_background(1000, 0); + printLine2ClearAndUpdate(); +} + +int btnDownTimeCount; + void doMenu(){ int select=0, i,btnState; - - //wait for the button to be raised up - while(btnDown()) - delay(50); - delay(50); //debounce + //for DialLock On/Off function + btnDownTimeCount = 0; + + //wait for the button to be raised up + while(btnDown()){ + delay(50); + Check_Cat(0); //To prevent disconnections + + //btnDownTimeCount++; + //check long time Down Button -> 3 Second + if (btnDownTimeCount++ > (2000 / 50)) { + setDialLock(isDialLock == 1 ? 0 : 1, 0); //Reverse Dialo lock + return; + } + } + delay(50); //debounce + menuOn = 2; while (menuOn){ @@ -532,9 +719,9 @@ void doMenu(){ btnState = btnDown(); if (i > 0){ - if (modeCalibrate && select + i < 110) + if (modeCalibrate && select + i < 150) select += i; - if (!modeCalibrate && select + i < 70) + if (!modeCalibrate && select + i < 80) select += i; } if (i < 0 && select - i >= 0) @@ -551,22 +738,34 @@ void doMenu(){ else if (select < 50) menuCWSpeed(btnState); else if (select < 60) + menuCWAutoKey(btnState); + else if (select < 70) menuSetup(btnState); - else if (select < 70 && !modeCalibrate) + else if (select < 80 && !modeCalibrate) menuExit(btnState); - else if (select < 80 && modeCalibrate) - menuSetupCalibration(btnState); //crystal else if (select < 90 && modeCalibrate) - menuSetupCarrier(btnState); //lsb + menuSetupCalibration(btnState); //crystal else if (select < 100 && modeCalibrate) - menuSetupCwTone(btnState); + menuSetupCarrier(btnState); //lsb else if (select < 110 && modeCalibrate) - menuExit(btnState); + menuSetupCwTone(btnState); + else if (select < 120 && modeCalibrate) + menuSetupCwDelay(btnState); + else if (select < 130 && modeCalibrate) + menuSetupTXCWInterval(btnState); + else if (select < 140 && modeCalibrate) + menuTxOnOff(btnState); + else if (select < 150 && modeCalibrate) + menuExit(btnState); + + Check_Cat(0); //To prevent disconnections } //debounce the button - while(btnDown()) + while(btnDown()){ delay(50); + Check_Cat(0); //To prevent disconnections + } delay(50); } diff --git a/ubitx_20/ubitx_ui.ino b/ubitx_20/ubitx_ui.ino index dbf513b..20fc69e 100644 --- a/ubitx_20/ubitx_ui.ino +++ b/ubitx_20/ubitx_ui.ino @@ -5,6 +5,8 @@ * of the radio. Occasionally, it is used to provide a two-line information that is * quickly cleared up. */ +//#define printLineF1(x) (printLineF(1, x)) +//#define printLineF2(x) (printLineF(0, x)) //returns true if the button is pressed int btnDown(){ @@ -23,9 +25,9 @@ int btnDown(){ * The current reading of the meter is assembled in the string called meter */ -char meter[17]; +//char meter[17]; -byte s_meter_bitmap[] = { +const PROGMEM uint8_t s_meter_bitmap[] = { B00000,B00000,B00000,B00000,B00000,B00100,B00100,B11011, B10000,B10000,B10000,B10000,B10100,B10100,B10100,B11011, B01000,B01000,B01000,B01000,B01100,B01100,B01100,B11011, @@ -33,18 +35,53 @@ byte s_meter_bitmap[] = { B00010,B00010,B00010,B00010,B00110,B00110,B00110,B11011, B00001,B00001,B00001,B00001,B00101,B00101,B00101,B11011 }; +PGM_P ps_meter_bitmap = reinterpret_cast(s_meter_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(){ - lcd.createChar(1, s_meter_bitmap); - lcd.createChar(2, s_meter_bitmap + 8); - lcd.createChar(3, s_meter_bitmap + 16); - lcd.createChar(4, s_meter_bitmap + 24); - lcd.createChar(5, s_meter_bitmap + 32); - lcd.createChar(6, s_meter_bitmap + 40); + 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(ps_meter_bitmap + i); + lcd.createChar(1, tmpbytes); + + for (i = 0; i < 8; i++) + tmpbytes[i] = pgm_read_byte(ps_meter_bitmap + i + 8); + lcd.createChar(2, tmpbytes); + + for (i = 0; i < 8; i++) + tmpbytes[i] = pgm_read_byte(ps_meter_bitmap + i + 16); + lcd.createChar(3, tmpbytes); + + for (i = 0; i < 8; i++) + tmpbytes[i] = pgm_read_byte(ps_meter_bitmap + i + 24); + lcd.createChar(4, tmpbytes); + + for (i = 0; i < 8; i++) + tmpbytes[i] = pgm_read_byte(ps_meter_bitmap + i + 28); + lcd.createChar(5, tmpbytes); + + for (i = 0; i < 8; i++) + tmpbytes[i] = pgm_read_byte(ps_meter_bitmap + i + 32); + lcd.createChar(6, tmpbytes); } /** @@ -53,6 +90,8 @@ void initMeter(){ * characters 2 to 6 are used to draw the needle in positions 1 to within the block * This displays a meter from 0 to 100, -1 displays nothing */ + + /* void drawMeter(int8_t needle){ int16_t best, i, s; @@ -73,6 +112,7 @@ void drawMeter(int8_t needle){ meter[i-1] = 6; meter[i] = 0; } +*/ // The generic routine to display one line on the LCD void printLine(char linenmbr, char *c) { @@ -87,6 +127,38 @@ void printLine(char linenmbr, char *c) { } } +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) { + lcd.setCursor(lcdColumn, linenmbr); + + for (byte i = eepromStartIndex; i <= eepromEndIndex; i++) + { + if (++lcdColumn <= LCD_MAX_COLUMN) + lcd.write(EEPROM.read(USER_CALLSIGN_DAT + 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(char *c){ printLine(1,c); @@ -96,21 +168,54 @@ void printLine2(char *c){ printLine(0,c); } +// 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, ""); + updateDisplay(); +} + +//012...89ABC...Z +char byteToChar(byte srcByte){ + if (srcByte < 10) + return 0x30 + srcByte; + else + return 'A' + srcByte - 10; +} + // 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)); - memset(b, 0, sizeof(b)); - - ultoa(frequency, b, DEC); if (inTx){ - if (cwTimeout > 0) - strcpy(c, " CW:"); - else - strcpy(c, " TX:"); + 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) @@ -127,29 +232,39 @@ void updateDisplay() { strcat(c, "B:"); } - - - //one mhz digit if less than 10 M, two digits if more - if (frequency < 10000000l){ - c[6] = ' '; - c[7] = b[0]; - strcat(c, "."); - strncat(c, &b[1], 3); - strcat(c, "."); - strncat(c, &b[4], 3); - } - else { - strncat(c, b, 2); - strcat(c, "."); - strncat(c, &b[2], 3); - strcat(c, "."); - strncat(c, &b[5], 3); + //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] = ' '; } - if (inTx) - strcat(c, " TX"); + //remarked by KD8CEC + //already RX/TX status display, and over index (16 x 2 LCD) + //if (inTx) + // strcat(c, " TX"); printLine(1, c); + if (isDialLock == 1) { + lcd.setCursor(5,1); + lcd.write((uint8_t)0); + } + else if (isCWAutoMode == 2){ + lcd.setCursor(5,1); + lcd.write(0x7E); + } + else + { + lcd.setCursor(5,1); + lcd.write(":"); + } + /* //now, the second line memset(c, 0, sizeof(c)); From 7ef9c29fa87f9e07b51ff8319218eb664eb174e7 Mon Sep 17 00:00:00 2001 From: Qi Wenmin Date: Wed, 10 Jan 2018 12:00:53 +0800 Subject: [PATCH 009/173] Fix the delay condition bug when overflow The original expression will cause bug when overflow. --- ubitx_20/ubitx_20.ino | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ubitx_20/ubitx_20.ino b/ubitx_20/ubitx_20.ino index 9c0760b..369739a 100644 --- a/ubitx_20/ubitx_20.ino +++ b/ubitx_20/ubitx_20.ino @@ -275,7 +275,7 @@ unsigned long delayBeforeTime = 0; byte delay_background(unsigned delayTime, byte fromType){ //fromType : 4 autoCWKey -> Check Paddle delayBeforeTime = millis(); - while (millis() <= delayBeforeTime + delayTime) { + while (millis() - delayBeforeTime <= delayTime) { if (fromType == 4) { From 8551ff1b68606e0935eb604e2ab2bb936e01c710 Mon Sep 17 00:00:00 2001 From: phdlee Date: Thu, 11 Jan 2018 17:40:00 +0900 Subject: [PATCH 010/173] Update README.md --- README.md | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/README.md b/README.md index 66b1ddd..cb6b0dd 100644 --- a/README.md +++ b/README.md @@ -4,6 +4,21 @@ This project is based on https://github.com/afarhan/ubitx and all copyright is i The copyright information of the original is below. KD8CEC +---------------------------------------------------------------------------- +Prepared or finished tasks for the next version + - Prevent overflow bugs [from pullrequest, history check] : complete + - Hamlib bug (raspberry pi), It was perfect for the 0.224 version, but there was a problem for the 0.25 version. + Found by Beta Tester very thanks. + On Windows, ham deluxe, wsjt-x, jt65-hf, and fldigi were successfully run. Problem with Raspberry pi. + As a result of the analysis, when the serial port is initialized and used immediately, problems occur in Linux and Raspberry pi. -> Resolution (Complete) + + - No TX on non-ham band request - This may be a prohibited item depending on the country. + Plan to change for uBITX Manager for free countries - Icom, yaesu, kenwood are mostly jumper in circuit. + Only those who need to lock themselves, Other users remain unchanged + so, Available in most countries around the world. + - I have heard that Beta testers want DialLock to distinguish between VFOA and VFOB (Complete) + - Convenience of band movement added (ing) + ---------------------------------------------------------------------------- ## REVISION RECORD 0.25 From 90655e03b89c7f328a9b1a636986db134a199832 Mon Sep 17 00:00:00 2001 From: phdlee Date: Fri, 12 Jan 2018 09:51:58 +0900 Subject: [PATCH 011/173] Update README.md add status of project --- README.md | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index cb6b0dd..9b2b5eb 100644 --- a/README.md +++ b/README.md @@ -15,10 +15,17 @@ Prepared or finished tasks for the next version - No TX on non-ham band request - This may be a prohibited item depending on the country. Plan to change for uBITX Manager for free countries - Icom, yaesu, kenwood are mostly jumper in circuit. Only those who need to lock themselves, Other users remain unchanged - so, Available in most countries around the world. + so, Available in most countries around the world. (Complete) - I have heard that Beta testers want DialLock to distinguish between VFOA and VFOB (Complete) - - Convenience of band movement added (ing) + - Convenience of band movement added (ing - need idea...) + - User Interface on LCD -> Option by user (yet - need idea) + - Include WSPR Beacone function - (considerd about include functions or create other version) + complete experiment + need solve : Big code size (over 100%, then remove some functions for experment) + need replace Si5351 Library for multisynth (increase risk and need more beta tester) + W3PM sent me his wonderful source - using BITX, GPS + ---------------------------------------------------------------------------- ## REVISION RECORD 0.25 From f9050ebb110f42fae29a8b6365dec20bf97ffb0c Mon Sep 17 00:00:00 2001 From: phdlee Date: Fri, 12 Jan 2018 09:54:38 +0900 Subject: [PATCH 012/173] for 0.26version commit1 --- ubitx_20/cat_libs.ino | 2 + ubitx_20/ubitx_20.ino | 105 +++++++++++++++++++++++++++++++++++++--- ubitx_20/ubitx_menu.ino | 75 +++++++++++++++++++++------- ubitx_20/ubitx_ui.ino | 3 +- 4 files changed, 161 insertions(+), 24 deletions(-) diff --git a/ubitx_20/cat_libs.ino b/ubitx_20/cat_libs.ino index e9375c4..26c79d3 100644 --- a/ubitx_20/cat_libs.ino +++ b/ubitx_20/cat_libs.ino @@ -629,6 +629,7 @@ void Check_Cat(byte fromType) } else if (Serial.available() < 5) { + /* //First Arrived if (rxBufferCheckCount == 0) { @@ -648,6 +649,7 @@ void Check_Cat(byte fromType) rxBufferCheckCount = Serial.available(); rxBufferArriveTime = millis() + CAT_RECEIVE_TIMEOUT; //Set time for timeout } + */ return; } diff --git a/ubitx_20/ubitx_20.ino b/ubitx_20/ubitx_20.ino index 9c0760b..5b2851c 100644 --- a/ubitx_20/ubitx_20.ino +++ b/ubitx_20/ubitx_20.ino @@ -148,8 +148,12 @@ int count = 0; //to generally count ticks, loops, etc #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 -// +//Check Firmware type and version #define VERSION_ADDRESS 779 //check Firmware version //USER INFORMATION #define USER_CALLSIGN_KEY 780 //0x59 @@ -228,7 +232,7 @@ byte sideTonePitch=0; byte sideToneSub = 0; //DialLock -byte isDialLock = 0; +byte isDialLock = 0; //000000[0]vfoB [0]vfoA 0Bit : A, 1Bit : B byte isTxOff = 0; //Variables for auto cw mode @@ -264,6 +268,69 @@ boolean modeCalibrate = false;//this mode of menus shows extended menus to calib * 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 + //100 : use full range but not TX on general band, 101 : just change dial speed but.. 2 : jut... but.. 3 : only ham band +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; + + if ((resultFreq / 1000) < hamBandRange[findedIndex][0] || (resultFreq / 1000) > hamBandRange[findedIndex][1]) + resultFreq = (unsigned long)(hamBandRange[findedIndex][0]) * 1000; + + setFrequency(resultFreq); + byteWithFreqToMode(loadMode); +} + +void saveBandFreqByIndex(unsigned long f, unsigned long mode, byte bandIndex) { + if (bandIndex >= 0) + EEPROM.put(HAM_BAND_FREQS + 4 * bandIndex, (f & 0x3FFFFFFF) | (mode << 30) ); +} + + /* KD8CEC When using the basic delay of the Arduino, the program freezes. @@ -381,6 +448,12 @@ void setFrequency(unsigned long f){ void startTx(byte txMode, byte isDisplayUpdate){ unsigned long tx_freq = 0; + //Check Hamband only TX //Not found Hamband index by now frequency + if (tuneTXType >= 100 && getIndexHambanBbyFreq(ritOn ? ritTxFrequency : frequency) == -1) { + //no message + return; + } + if (isTxOff != 1) digitalWrite(TX_RX, 1); @@ -502,7 +575,8 @@ void doTuning(){ unsigned long prev_freq; int incdecValue = 0; - if (isDialLock == 1) + if ((vfoActive == VFO_A && ((isDialLock & 0x01) == 0x01)) || + (vfoActive == VFO_B && ((isDialLock & 0x02) == 0x02))) return; if (isCWAutoMode == 0 || cwAutoDialType == 1) @@ -636,6 +710,23 @@ void initSettings(){ //Version Write for Memory Management Software if (EEPROM.read(VERSION_ADDRESS) != VERSION_NUM) EEPROM.write(VERSION_ADDRESS, VERSION_NUM); + + //Ham Band Count + EEPROM.get(HAM_BAND_COUNT, useHamBandCount); + EEPROM.get(TX_TUNE_TYPE, tuneTXType); + + + if ((3 < tuneTXType && 100 < tuneTXType) || 103 < tuneTXType || useHamBandCount < 1) + tuneTXType = 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; + EEPROM.get(HAM_BAND_RANGE + 4 * i + 2, tmpReadValue); + hamBandRange[i][1] = tmpReadValue; + } if (cwDelayTime < 1 || cwDelayTime > 250) cwDelayTime = 60; @@ -715,11 +806,13 @@ void initPorts(){ void setup() { - //Init EEProm for Fault EEProm TEST and Factory Reset /* - for (int i = 0; i < 1024; i++) + //Init EEProm for Fault EEProm TEST and Factory Reset + //for (int i = 0; i < 1024; i++) + for (int i = 16; i < 1024; i++) //protect Master_cal, usb_cal EEPROM.write(i, 0); */ + //Serial.begin(9600); lcd.begin(16, 2); @@ -736,7 +829,7 @@ void setup() else { printLineF(0, F("uBITX v0.20")); - delay_background(500, 0); + delay(500); printLine2(""); } diff --git a/ubitx_20/ubitx_menu.ino b/ubitx_20/ubitx_menu.ino index a242ea3..c8778d7 100644 --- a/ubitx_20/ubitx_menu.ino +++ b/ubitx_20/ubitx_menu.ino @@ -15,12 +15,8 @@ int menuBand(int btn){ int knob = 0; - int band; - unsigned long offset; + int stepChangeCount = 0; - // band = frequency/1000000l; - // offset = frequency % 1000000l; - if (!btn){ printLineF2(F("Band Select?")); return; @@ -33,6 +29,18 @@ int menuBand(int btn){ Check_Cat(0); //To prevent disconnections } + byte 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(); @@ -50,16 +58,35 @@ int menuBand(int btn){ else isUSB = false; setFrequency(((unsigned long)band * 1000000l) + offset); */ - if (knob < 0 && frequency > 3000000l) - setFrequency(frequency - 200000l); - if (knob > 0 && frequency < 30000000l) - setFrequency(frequency + 200000l); - if (frequency > 10000000l) - isUSB = true; - else - isUSB = false; + 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; + } + } + } + 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(20); Check_Cat(0); //To prevent disconnections } @@ -89,6 +116,14 @@ void byteToMode(byte modeValue){ else isUSB = 0; } +void byteWithFreqToMode(byte modeValue){ + if (modeValue == 3) + isUSB = 1; + else if (modeValue == 0) //Not Set + isUSB = (frequency > 10000000l) ? true : false; + else + isUSB = 0; +} void menuVfoToggle(int btn) { @@ -677,11 +712,14 @@ void menuSetupCwTone(int btn){ } void setDialLock(byte tmpLock, byte fromMode) { - isDialLock = tmpLock; - + if (tmpLock == 1) + isDialLock |= (vfoActive == VFO_A ? 0x01 : 0x02); + else + isDialLock &= ~(vfoActive == VFO_A ? 0x01 : 0x02); + if (fromMode == 2 || fromMode == 3) return; - if (isDialLock == 1) + if (tmpLock == 1) printLineF2(F("Dial Lock ON")); else printLineF2(F("Dial Lock OFF")); @@ -706,7 +744,10 @@ void doMenu(){ //btnDownTimeCount++; //check long time Down Button -> 3 Second if (btnDownTimeCount++ > (2000 / 50)) { - setDialLock(isDialLock == 1 ? 0 : 1, 0); //Reverse Dialo 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; } } diff --git a/ubitx_20/ubitx_ui.ino b/ubitx_20/ubitx_ui.ino index 20fc69e..a4c4798 100644 --- a/ubitx_20/ubitx_ui.ino +++ b/ubitx_20/ubitx_ui.ino @@ -251,7 +251,8 @@ void updateDisplay() { // strcat(c, " TX"); printLine(1, c); - if (isDialLock == 1) { + if ((vfoActive == VFO_A && ((isDialLock & 0x01) == 0x01)) || + (vfoActive == VFO_B && ((isDialLock & 0x02) == 0x02))) { lcd.setCursor(5,1); lcd.write((uint8_t)0); } From 2b08a76fbfb983e4500c619733508beff86da228 Mon Sep 17 00:00:00 2001 From: phdlee Date: Fri, 12 Jan 2018 10:16:59 +0900 Subject: [PATCH 013/173] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 9b2b5eb..23db191 100644 --- a/README.md +++ b/README.md @@ -23,7 +23,7 @@ Prepared or finished tasks for the next version - Include WSPR Beacone function - (considerd about include functions or create other version) complete experiment need solve : Big code size (over 100%, then remove some functions for experment) - need replace Si5351 Library for multisynth (increase risk and need more beta tester) + need replace Si5351 Library (increase risk and need more beta tester) W3PM sent me his wonderful source - using BITX, GPS ---------------------------------------------------------------------------- From b2d3e3a6f811bcc5e03eec9cce3f6f13e77d7c79 Mon Sep 17 00:00:00 2001 From: phdlee Date: Fri, 12 Jan 2018 19:58:20 +0900 Subject: [PATCH 014/173] cat 38400 to 9600 --- ubitx_20/ubitx_20.ino | 105 ++++++------------------------------------ 1 file changed, 14 insertions(+), 91 deletions(-) diff --git a/ubitx_20/ubitx_20.ino b/ubitx_20/ubitx_20.ino index 5b2851c..90617a7 100644 --- a/ubitx_20/ubitx_20.ino +++ b/ubitx_20/ubitx_20.ino @@ -812,10 +812,11 @@ void setup() for (int i = 16; i < 1024; i++) //protect Master_cal, usb_cal EEPROM.write(i, 0); */ - //Serial.begin(9600); lcd.begin(16, 2); + //remark for John test + /* Init_Cat(38400, SERIAL_8N1); initMeter(); //not used in this build initSettings(); @@ -829,9 +830,20 @@ void setup() else { printLineF(0, F("uBITX v0.20")); - delay(500); + delay(500); //< -- replace from delay_background(500, 0) //johns bug report / on raspberry printLine2(""); } + */ + //replace above to below (before initSettings(); position) + + printLine2("CECBT v0.27"); + printLine1("uBITX v0.20"); + delay(500); + printLine2(""); + + Init_Cat(9600, SERIAL_8N1); + initMeter(); //not used in this build + initSettings(); initPorts(); initOscillators(); @@ -844,95 +856,6 @@ void setup() if (btnDown()) factory_alignment(); - -/* - //This is for auto key test - EEPROM.put(CW_AUTO_MAGIC_KEY, 0x73); //MAGIC KEY - EEPROM.put(CW_AUTO_COUNT, 3); //WORD COUNT - EEPROM.put(CW_AUTO_DATA + 0, 6); // 0 word begin postion / CQCQ TEST K - EEPROM.put(CW_AUTO_DATA + 1, 33); // 0 word end postion / CQCQ TEST K - EEPROM.put(CW_AUTO_DATA + 2, 34); //1 word begin position / LOL LOL - EEPROM.put(CW_AUTO_DATA + 3, 40); //1 word end position / LOL LOL - EEPROM.put(CW_AUTO_DATA + 4, 41); //2 word begin position / /?![]789 - EEPROM.put(CW_AUTO_DATA + 5, 48); //2 word end position / /?![]789 - - EEPROM.put(CW_AUTO_DATA + 6, 'C'); // - EEPROM.put(CW_AUTO_DATA + 7, 'Q'); // - EEPROM.put(CW_AUTO_DATA + 8, 'C'); // - EEPROM.put(CW_AUTO_DATA + 9, 'Q'); // - EEPROM.put(CW_AUTO_DATA + 10, ' '); // - EEPROM.put(CW_AUTO_DATA + 11, 'D'); // - EEPROM.put(CW_AUTO_DATA + 12, 'E'); // - EEPROM.put(CW_AUTO_DATA + 13, ' '); // - EEPROM.put(CW_AUTO_DATA + 14, 'K'); // - EEPROM.put(CW_AUTO_DATA + 15, 'D'); // - EEPROM.put(CW_AUTO_DATA + 16, '8'); // - EEPROM.put(CW_AUTO_DATA + 17, 'C'); // - EEPROM.put(CW_AUTO_DATA + 18, 'E'); // - EEPROM.put(CW_AUTO_DATA + 19, 'C'); // - EEPROM.put(CW_AUTO_DATA + 20, ' '); // - EEPROM.put(CW_AUTO_DATA + 21, 'E'); // - EEPROM.put(CW_AUTO_DATA + 22, 'M'); // - EEPROM.put(CW_AUTO_DATA + 23, '3'); // - EEPROM.put(CW_AUTO_DATA + 24, '7'); // - EEPROM.put(CW_AUTO_DATA + 25, ' '); // - EEPROM.put(CW_AUTO_DATA + 26, 'D'); // - EEPROM.put(CW_AUTO_DATA + 27, 'E'); // - EEPROM.put(CW_AUTO_DATA + 28, ' '); // - EEPROM.put(CW_AUTO_DATA + 29, 'C'); // - EEPROM.put(CW_AUTO_DATA + 30, 'E'); // - EEPROM.put(CW_AUTO_DATA + 31, 'C'); // - EEPROM.put(CW_AUTO_DATA + 32, ' '); // - EEPROM.put(CW_AUTO_DATA + 33, 'K'); // -*/ - -/* - EEPROM.put(CW_AUTO_DATA + 34, '<'); // - EEPROM.put(CW_AUTO_DATA + 35, ' '); // - EEPROM.put(CW_AUTO_DATA + 36, '>'); // - EEPROM.put(CW_AUTO_DATA + 37, ' '); // - EEPROM.put(CW_AUTO_DATA + 38, '7'); // - EEPROM.put(CW_AUTO_DATA + 39, '3'); // - EEPROM.put(CW_AUTO_DATA + 40, 'K'); // - - EEPROM.put(CW_AUTO_DATA + 41, 'C'); // - EEPROM.put(CW_AUTO_DATA + 42, 'Q'); // - EEPROM.put(CW_AUTO_DATA + 43, ' '); // - EEPROM.put(CW_AUTO_DATA + 44, '>'); // start " - EEPROM.put(CW_AUTO_DATA + 45, ' '); // end " - EEPROM.put(CW_AUTO_DATA + 46, '>'); // - EEPROM.put(CW_AUTO_DATA + 47, ' '); // - EEPROM.put(CW_AUTO_DATA + 48, 'K'); // -*/ - -/* - //This is for auto key test2 - //USER CALL SIGN - EEPROM.put(USER_CALLSIGN_KEY, 0x59); //MAGIC KEY - //EEPROM.put(USER_CALLSIGN_LEN, 10); //WORD COUNT - EEPROM.put(USER_CALLSIGN_LEN, 10 + 0x80); //WORD COUNT - - EEPROM.put(USER_CALLSIGN_DAT + 1, 'K'); // - EEPROM.put(USER_CALLSIGN_DAT + 2, 'D'); // - EEPROM.put(USER_CALLSIGN_DAT + 3, '8'); // - EEPROM.put(USER_CALLSIGN_DAT + 4, 'C'); // - EEPROM.put(USER_CALLSIGN_DAT + 5, 'E'); // - EEPROM.put(USER_CALLSIGN_DAT + 6, 'C'); // - EEPROM.put(USER_CALLSIGN_DAT + 7, '/'); // - EEPROM.put(USER_CALLSIGN_DAT + 8, 'A'); // - EEPROM.put(USER_CALLSIGN_DAT + 9, 'B'); // - EEPROM.put(USER_CALLSIGN_DAT + 10, 'C'); // - - //CW QSO CALLSIGN - EEPROM.put(CW_STATION_LEN, 6); // - EEPROM.put(CW_STATION_LEN - 6 + 0 , 'A'); // - EEPROM.put(CW_STATION_LEN - 6 + 1 , 'B'); // - EEPROM.put(CW_STATION_LEN - 6 + 2 , '1'); // - EEPROM.put(CW_STATION_LEN - 6 + 3 , 'C'); // - EEPROM.put(CW_STATION_LEN - 6 + 4 , 'D'); // - EEPROM.put(CW_STATION_LEN - 6 + 5 , 'E'); // -*/ - } From 9781ef086b85177ae1794d368f09dcf11e30855d Mon Sep 17 00:00:00 2001 From: phdlee Date: Sat, 13 Jan 2018 10:58:47 +0900 Subject: [PATCH 015/173] Update README.md --- README.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/README.md b/README.md index 23db191..fd065d2 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,7 @@ +#IMPORTANT INFORMATION +Beta 0.26 is still in testing. +If you need the source, you can download the 0.25 version or the 0.26 release one day later. + #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. From b9b8f4b46fac10edc08ba39e1bf76cc2716d2feb Mon Sep 17 00:00:00 2001 From: phdlee Date: Sat, 13 Jan 2018 16:19:23 +0900 Subject: [PATCH 016/173] bug fix 0.26 --- ubitx_20/ubitx_20.ino | 58 ++++++++++++++++++++++++++++--------- ubitx_20/ubitx_menu.ino | 64 +++++++++++++++++++++++++++++++++++++---- 2 files changed, 102 insertions(+), 20 deletions(-) diff --git a/ubitx_20/ubitx_20.ino b/ubitx_20/ubitx_20.ino index 90617a7..ec77aa8 100644 --- a/ubitx_20/ubitx_20.ino +++ b/ubitx_20/ubitx_20.ino @@ -154,6 +154,7 @@ int count = 0; //to generally count ticks, loops, etc #define HAM_BAND_FREQS 302 //40, 1 BAND = 4Byte most bit is mode //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 @@ -233,7 +234,8 @@ byte sideToneSub = 0; //DialLock byte isDialLock = 0; //000000[0]vfoB [0]vfoA 0Bit : A, 1Bit : B -byte isTxOff = 0; +byte isTxType = 0; //000000[0 - isSplit] [0 - isTXStop] + //Variables for auto cw mode byte isCWAutoMode = 0; //0 : none, 1 : CW_AutoMode_Menu_Selection, 2 : CW_AutoMode Sending @@ -271,8 +273,8 @@ boolean modeCalibrate = false;//this mode of menus shows extended menus to calib //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 - //100 : use full range but not TX on general band, 101 : just change dial speed but.. 2 : jut... but.. 3 : only ham band +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 @@ -454,9 +456,9 @@ void startTx(byte txMode, byte isDisplayUpdate){ return; } - if (isTxOff != 1) + if ((isTxType & 0x01) != 0x01) digitalWrite(TX_RX, 1); - + inTx = 1; if (ritOn){ @@ -685,6 +687,7 @@ void storeFrequencyAndMode(byte saveType) 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); @@ -692,6 +695,30 @@ void initSettings(){ EEPROM.get(CW_SIDETONE, sideTone); EEPROM.get(CW_SPEED, cwSpeed); + //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 = 32; 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) != VERSION_NUM) + EEPROM.write(VERSION_ADDRESS, VERSION_NUM); + + //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); @@ -707,10 +734,6 @@ void initSettings(){ if (EEPROM.read(USER_CALLSIGN_KEY) == 0x59) userCallsignLength = EEPROM.read(USER_CALLSIGN_LEN); //MAXIMUM 18 LENGTH - //Version Write for Memory Management Software - if (EEPROM.read(VERSION_ADDRESS) != VERSION_NUM) - EEPROM.write(VERSION_ADDRESS, VERSION_NUM); - //Ham Band Count EEPROM.get(HAM_BAND_COUNT, useHamBandCount); EEPROM.get(TX_TUNE_TYPE, tuneTXType); @@ -808,20 +831,27 @@ 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, 0); + EEPROM.write(i, 0xFF); + lcd.begin(16, 2); + printLineF(1, F("Complete Erase")); + sleep(1000); + //while(1); + //end section of test */ + //Serial.begin(9600); + delay(100); lcd.begin(16, 2); //remark for John test - /* Init_Cat(38400, SERIAL_8N1); initMeter(); //not used in this build initSettings(); - printLineF(1, F("CECBT v0.25")); + printLineF(1, F("CECBT v0.263")); if (userCallsignLength > 0 && ((userCallsignLength & 0x80) == 0x80)) { userCallsignLength = userCallsignLength & 0x7F; @@ -833,9 +863,8 @@ void setup() delay(500); //< -- replace from delay_background(500, 0) //johns bug report / on raspberry printLine2(""); } - */ //replace above to below (before initSettings(); position) - +/* printLine2("CECBT v0.27"); printLine1("uBITX v0.20"); delay(500); @@ -844,6 +873,7 @@ void setup() Init_Cat(9600, SERIAL_8N1); initMeter(); //not used in this build initSettings(); + */ initPorts(); initOscillators(); diff --git a/ubitx_20/ubitx_menu.ino b/ubitx_20/ubitx_menu.ino index c8778d7..1cfe580 100644 --- a/ubitx_20/ubitx_menu.ino +++ b/ubitx_20/ubitx_menu.ino @@ -16,6 +16,7 @@ int menuBand(int btn){ int knob = 0; int stepChangeCount = 0; + byte btnPressCount = 0; if (!btn){ printLineF2(F("Band Select?")); @@ -27,6 +28,33 @@ int menuBand(int btn){ while (btnDown()) { delay(50); Check_Cat(0); //To prevent disconnections + if (btnPressCount++ > 20) { + btnPressCount = 0; + if (tuneTXType > 0) { //Just toggle 0 <-> 2, if tuneTXType is 100, 100 -> 0 -> 2 + tuneTXType = 0; + printLineF2(F("Full range mode")); + } + else { + tuneTXType = 2; + //if empty band Information, auto insert default region 1 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] = 7200; + 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; + printLineF2(F("Ham band mode")); + } + delay_background(1000, 0); + printLine2ClearAndUpdate(); + printLineF2(F("Press to confirm")); + } } byte currentBandIndex = -1; @@ -213,20 +241,20 @@ void menuSidebandToggle(int btn){ } } -void menuTxOnOff(int btn){ +void menuTxOnOff(int btn, byte optionType){ if (!btn){ - if (isTxOff == 0) + if ((isTxType & optionType) == 0) printLineF2(F("TX OFF?")); else printLineF2(F("TX ON?")); } else { - if (isTxOff == 0){ - isTxOff = 1; + if ((isTxType & optionType) == 0){ + isTxType |= optionType; printLineF2(F("TX OFF!")); } else { - isTxOff = 0; + isTxType &= ~(optionType); printLineF2(F("TX ON!")); } delay_background(500, 0); @@ -235,6 +263,30 @@ void menuTxOnOff(int btn){ } } +/* +void menuSplitOnOff(int btn){ + if (!btn){ + if ((isTxType & 0x02) == 0) + printLineF2(F("Split OFF?")); + else + printLineF2(F("Split ON?")); + } + else { + if ((isTxType & 0x02) == 0){ + isTxType |= 0x02; + printLineF2(F("Split OFF!")); + } + else { + isTxType &= ~(0x02); + printLineF2(F("Split ON!")); + } + delay_background(500, 0); + printLine2ClearAndUpdate(); + menuOn = 0; + } +} +*/ + /** * The calibration routines are not normally shown in the menu as they are rarely used * They can be enabled by choosing this menu option @@ -795,7 +847,7 @@ void doMenu(){ else if (select < 130 && modeCalibrate) menuSetupTXCWInterval(btnState); else if (select < 140 && modeCalibrate) - menuTxOnOff(btnState); + menuTxOnOff(btnState, 0x01); //TX OFF / ON else if (select < 150 && modeCalibrate) menuExit(btnState); From 924db221f4a668bd28dfb07206486260508973c3 Mon Sep 17 00:00:00 2001 From: phdlee Date: Sat, 13 Jan 2018 19:42:39 +0900 Subject: [PATCH 017/173] bug fix 0.26_2 --- ubitx_20/ubitx_20.ino | 28 ++++++----------------- ubitx_20/ubitx_menu.ino | 50 ++++++++++++----------------------------- 2 files changed, 21 insertions(+), 57 deletions(-) diff --git a/ubitx_20/ubitx_20.ino b/ubitx_20/ubitx_20.ino index ec77aa8..fa644a1 100644 --- a/ubitx_20/ubitx_20.ino +++ b/ubitx_20/ubitx_20.ino @@ -327,7 +327,7 @@ void setNextHamBandFreq(unsigned long f, char moveDirection) byteWithFreqToMode(loadMode); } -void saveBandFreqByIndex(unsigned long f, unsigned long mode, byte bandIndex) { +void saveBandFreqByIndex(unsigned long f, unsigned long mode, char bandIndex) { if (bandIndex >= 0) EEPROM.put(HAM_BAND_FREQS + 4 * bandIndex, (f & 0x3FFFFFFF) | (mode << 30) ); } @@ -739,7 +739,7 @@ void initSettings(){ EEPROM.get(TX_TUNE_TYPE, tuneTXType); - if ((3 < tuneTXType && 100 < tuneTXType) || 103 < tuneTXType || useHamBandCount < 1) + if ((3 < tuneTXType && tuneTXType < 100) || 103 < tuneTXType || useHamBandCount < 1) tuneTXType = 0; //Read band Information @@ -843,37 +843,23 @@ void setup() */ //Serial.begin(9600); - delay(100); lcd.begin(16, 2); + printLineF(1, F("CECBT v0.27")); - //remark for John test Init_Cat(38400, SERIAL_8N1); initMeter(); //not used in this build initSettings(); - printLineF(1, F("CECBT v0.263")); - if (userCallsignLength > 0 && ((userCallsignLength & 0x80) == 0x80)) - { + if (userCallsignLength > 0 && ((userCallsignLength & 0x80) == 0x80)) { userCallsignLength = userCallsignLength & 0x7F; printLineFromEEPRom(0, 0, 0, userCallsignLength -1); //eeprom to lcd use offset (USER_CALLSIGN_DAT) + delay(500); } - else - { + else { printLineF(0, F("uBITX v0.20")); - delay(500); //< -- replace from delay_background(500, 0) //johns bug report / on raspberry + delay(500); printLine2(""); } - //replace above to below (before initSettings(); position) -/* - printLine2("CECBT v0.27"); - printLine1("uBITX v0.20"); - delay(500); - printLine2(""); - - Init_Cat(9600, SERIAL_8N1); - initMeter(); //not used in this build - initSettings(); - */ initPorts(); initOscillators(); diff --git a/ubitx_20/ubitx_menu.ino b/ubitx_20/ubitx_menu.ino index 1cfe580..0437e41 100644 --- a/ubitx_20/ubitx_menu.ino +++ b/ubitx_20/ubitx_menu.ino @@ -38,17 +38,19 @@ int menuBand(int btn){ tuneTXType = 2; //if empty band Information, auto insert default region 1 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] = 7200; - 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; + if (useHamBandCount < 1) { + 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] = 7200; + 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; + } printLineF2(F("Ham band mode")); } delay_background(1000, 0); @@ -57,7 +59,7 @@ int menuBand(int btn){ } } - byte currentBandIndex = -1; + char currentBandIndex = -1; //Save Band Information if (tuneTXType == 2 || tuneTXType == 3 || tuneTXType == 102 || tuneTXType == 103) { //only ham band move //Get Now Band Index @@ -263,30 +265,6 @@ void menuTxOnOff(int btn, byte optionType){ } } -/* -void menuSplitOnOff(int btn){ - if (!btn){ - if ((isTxType & 0x02) == 0) - printLineF2(F("Split OFF?")); - else - printLineF2(F("Split ON?")); - } - else { - if ((isTxType & 0x02) == 0){ - isTxType |= 0x02; - printLineF2(F("Split OFF!")); - } - else { - isTxType &= ~(0x02); - printLineF2(F("Split ON!")); - } - delay_background(500, 0); - printLine2ClearAndUpdate(); - menuOn = 0; - } -} -*/ - /** * The calibration routines are not normally shown in the menu as they are rarely used * They can be enabled by choosing this menu option From 3e60728727c4c4e2321c62fc6859a8229fef2ab4 Mon Sep 17 00:00:00 2001 From: phdlee Date: Sat, 13 Jan 2018 22:27:23 +0900 Subject: [PATCH 018/173] Update README.md --- README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index fd065d2..8409181 100644 --- a/README.md +++ b/README.md @@ -21,14 +21,14 @@ Prepared or finished tasks for the next version Only those who need to lock themselves, Other users remain unchanged so, Available in most countries around the world. (Complete) - I have heard that Beta testers want DialLock to distinguish between VFOA and VFOB (Complete) - - Convenience of band movement added (ing - need idea...) + - Convenience of band movement added (Complete) - - User Interface on LCD -> Option by user (yet - need idea) - - Include WSPR Beacone function - (considerd about include functions or create other version) + - User Interface on LCD -> Option by user (not need) + - Include WSPR Beacone function - (implement other new repository) complete experiment need solve : Big code size (over 100%, then remove some functions for experment) need replace Si5351 Library (increase risk and need more beta tester) - W3PM sent me his wonderful source - using BITX, GPS + W3PM sent me his wonderful source - using BITX, GPS ---------------------------------------------------------------------------- ## REVISION RECORD From 16304efacd4231f9b298e1b53626ab42846506a2 Mon Sep 17 00:00:00 2001 From: phdlee Date: Sun, 14 Jan 2018 14:51:23 +0900 Subject: [PATCH 019/173] Update README.md --- README.md | 57 ++++++++++++++++++++++++++++++++++++++++--------------- 1 file changed, 42 insertions(+), 15 deletions(-) diff --git a/README.md b/README.md index 8409181..aef5bc1 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,29 @@ #IMPORTANT INFORMATION -Beta 0.26 is still in testing. -If you need the source, you can download the 0.25 version or the 0.26 release one day later. +Beta 0.26 and Beta 0.261, Beta 0.262, Beta 0.27 is complete test +You can download and use it. + +#NOTICE +I received uBITX a month ago and found that many features are required, and began coding with the idea of implementing minimal functionality as a general hf transceiver rather than an experimental device. + +Such as +Diallock for uBITX's sensitive encoders +built in softare Memory keyer and cw options control for CW communication +Implementation of CAT communication protocol for Digital Communication (as FT8, JT65, etc) +Delay Options for external Linear. + +Most of the basic functions of the HF transceiver I thought were implemented. +The minimum basic specification for uBITX to operate as a radio, I think it is finished. +So I will release the 0.27 version and if I do not see the bug anymore, I will try to change the version name to 1.0. +Now uBITX is an HF radio and will be able to join you in your happy hams life. +Based on this source, you can use it by adding functions. + +I am going to do a new project based on this source, linking with WSPR, WSJT-X and so on. +Of course, this repository is still running. If you have any bugs or ideas, please feel free to email me. + +http://www.hamskey.com + +DE KD8CEC +kd8cec@gmail.com #uBITX uBITX firmware, written for the Raduino/Arduino control of uBITX transceivers @@ -10,19 +33,7 @@ The copyright information of the original is below. KD8CEC ---------------------------------------------------------------------------- Prepared or finished tasks for the next version - - Prevent overflow bugs [from pullrequest, history check] : complete - - Hamlib bug (raspberry pi), It was perfect for the 0.224 version, but there was a problem for the 0.25 version. - Found by Beta Tester very thanks. - On Windows, ham deluxe, wsjt-x, jt65-hf, and fldigi were successfully run. Problem with Raspberry pi. - As a result of the analysis, when the serial port is initialized and used immediately, problems occur in Linux and Raspberry pi. -> Resolution (Complete) - - - No TX on non-ham band request - This may be a prohibited item depending on the country. - Plan to change for uBITX Manager for free countries - Icom, yaesu, kenwood are mostly jumper in circuit. - Only those who need to lock themselves, Other users remain unchanged - so, Available in most countries around the world. (Complete) - - I have heard that Beta testers want DialLock to distinguish between VFOA and VFOB (Complete) - - Convenience of band movement added (Complete) - + - Most of them are implemented and included in version 0.27. - User Interface on LCD -> Option by user (not need) - Include WSPR Beacone function - (implement other new repository) complete experiment @@ -32,6 +43,22 @@ Prepared or finished tasks for the next version ---------------------------------------------------------------------------- ## REVISION RECORD +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 From bcf80f851db1a0dc5f21c84d10108c38ad7a8d8c Mon Sep 17 00:00:00 2001 From: phdlee Date: Sun, 14 Jan 2018 14:51:46 +0900 Subject: [PATCH 020/173] Update README.md --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index aef5bc1..84c1f69 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ #IMPORTANT INFORMATION -Beta 0.26 and Beta 0.261, Beta 0.262, Beta 0.27 is complete test -You can download and use it. +- Beta 0.26 and Beta 0.261, Beta 0.262, Beta 0.27 is complete test +- You can download and use it. #NOTICE I received uBITX a month ago and found that many features are required, and began coding with the idea of implementing minimal functionality as a general hf transceiver rather than an experimental device. From a6ad381c24536db0c9c621df5b6f5b2de224d3d4 Mon Sep 17 00:00:00 2001 From: phdlee Date: Sun, 14 Jan 2018 14:52:22 +0900 Subject: [PATCH 021/173] Update README.md --- README.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 84c1f69..ffa9825 100644 --- a/README.md +++ b/README.md @@ -5,11 +5,11 @@ #NOTICE I received uBITX a month ago and found that many features are required, and began coding with the idea of implementing minimal functionality as a general hf transceiver rather than an experimental device. -Such as -Diallock for uBITX's sensitive encoders -built in softare Memory keyer and cw options control for CW communication -Implementation of CAT communication protocol for Digital Communication (as FT8, JT65, etc) -Delay Options for external Linear. + +- Diallock for uBITX's sensitive encoders +- built in softare Memory keyer and cw options control for CW communication +- Implementation of CAT communication protocol for Digital Communication (as FT8, JT65, etc) +- Delay Options for external Linear. Most of the basic functions of the HF transceiver I thought were implemented. The minimum basic specification for uBITX to operate as a radio, I think it is finished. From 45a84790618315e81900e95e82b158033565d1fe Mon Sep 17 00:00:00 2001 From: phdlee Date: Sun, 14 Jan 2018 14:52:58 +0900 Subject: [PATCH 022/173] Update README.md --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index ffa9825..22f906c 100644 --- a/README.md +++ b/README.md @@ -5,11 +5,12 @@ #NOTICE I received uBITX a month ago and found that many features are required, and began coding with the idea of implementing minimal functionality as a general hf transceiver rather than an experimental device. - +- fixed bugs... - Diallock for uBITX's sensitive encoders - built in softare Memory keyer and cw options control for CW communication - Implementation of CAT communication protocol for Digital Communication (as FT8, JT65, etc) - Delay Options for external Linear. +- and more... Most of the basic functions of the HF transceiver I thought were implemented. The minimum basic specification for uBITX to operate as a radio, I think it is finished. From 95e5c1dfe5c19cd2b7fd708e081672c5434d6d70 Mon Sep 17 00:00:00 2001 From: phdlee Date: Sun, 14 Jan 2018 14:53:28 +0900 Subject: [PATCH 023/173] Update README.md --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 22f906c..79f9917 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,10 @@ #IMPORTANT INFORMATION +---------------------------------------------------------------------------- - Beta 0.26 and Beta 0.261, Beta 0.262, Beta 0.27 is complete test - You can download and use it. #NOTICE +---------------------------------------------------------------------------- I received uBITX a month ago and found that many features are required, and began coding with the idea of implementing minimal functionality as a general hf transceiver rather than an experimental device. - fixed bugs... From 587d4854c375e656471210673c0ab6e17d7ee540 Mon Sep 17 00:00:00 2001 From: phdlee Date: Wed, 17 Jan 2018 14:05:20 +0900 Subject: [PATCH 024/173] change delaytimes via cat --- ubitx_20/cat_libs.ino | 17 +++++++---------- ubitx_20/ubitx_menu.ino | 9 +++++---- 2 files changed, 12 insertions(+), 14 deletions(-) diff --git a/ubitx_20/cat_libs.ino b/ubitx_20/cat_libs.ino index 26c79d3..b0aae87 100644 --- a/ubitx_20/cat_libs.ino +++ b/ubitx_20/cat_libs.ino @@ -181,7 +181,7 @@ void CatSetPTT(boolean isPTTOn, byte fromType) void CatVFOToggle(boolean isSendACK, byte fromType) { if (fromType != 2 && fromType != 3) { - menuVfoToggle(1); + menuVfoToggle(1, 0); } if (isSendACK) @@ -470,8 +470,8 @@ void WriteEEPRom_FT817(byte fromType) sideTone = (sideTonePitch * 50 + 300) + sideToneSub; printLineF2(F("Sidetone set! CAT")); EEPROM.put(CW_SIDETONE, sideTone); - delay(500); - printLine2(""); + delay(300); //If timeout errors occur in the calling software, remove them + printLine2(""); //Ham radio deluxe is the only one that supports this feature yet. and ham radio deluxe has wait time as greater than 500ms } break; @@ -482,8 +482,8 @@ void WriteEEPRom_FT817(byte fromType) sideTone = (sideTonePitch * 50 + 300) + sideToneSub; printLineF2(F("Sidetone set! CAT")); EEPROM.put(CW_SIDETONE, sideTone); - delay(500); - printLine2(""); + delay(300); //If timeout errors occur in the calling software, remove them + printLine2(""); //Ham radio deluxe is the only one that supports this feature yet. and ham radio deluxe has wait time as greater than 500ms } break; @@ -502,7 +502,7 @@ void WriteEEPRom_FT817(byte fromType) cwDelayTime = CAT_BUFF[2]; printLineF2(F("CW Speed set!")); EEPROM.put(CW_DELAY, cwDelayTime); - delay(500); + delay(300); printLine2(""); break; case 0x62 : // @@ -511,7 +511,7 @@ void WriteEEPRom_FT817(byte fromType) cwSpeed = 1200 / ((CAT_BUFF[2] & 0x3F) + 4); printLineF2(F("CW Speed set!")); EEPROM.put(CW_SPEED, cwSpeed); - delay(500); + delay(300); printLine2(""); break; @@ -629,7 +629,6 @@ void Check_Cat(byte fromType) } else if (Serial.available() < 5) { - /* //First Arrived if (rxBufferCheckCount == 0) { @@ -649,8 +648,6 @@ void Check_Cat(byte fromType) rxBufferCheckCount = Serial.available(); rxBufferArriveTime = millis() + CAT_RECEIVE_TIMEOUT; //Set time for timeout } - */ - return; } diff --git a/ubitx_20/ubitx_menu.ino b/ubitx_20/ubitx_menu.ino index 0437e41..02388c4 100644 --- a/ubitx_20/ubitx_menu.ino +++ b/ubitx_20/ubitx_menu.ino @@ -155,7 +155,7 @@ void byteWithFreqToMode(byte modeValue){ isUSB = 0; } -void menuVfoToggle(int btn) +void menuVfoToggle(int btn, char isUseDelayTime) { if (!btn){ if (vfoActive == VFO_A) @@ -189,8 +189,9 @@ void menuVfoToggle(int btn) ritDisable(); - //updateDisplay(); - delay_background(500, 0); + if (isUseDelayTime == 1) //Found Issue in wsjt-x Linux 32bit + delay_background(500, 0); + printLine2ClearAndUpdate(); //exit the menu menuOn = 0; @@ -803,7 +804,7 @@ void doMenu(){ else if (select < 20) menuRitToggle(btnState); else if (select < 30) - menuVfoToggle(btnState); + menuVfoToggle(btnState, 1); else if (select < 40) menuSidebandToggle(btnState); else if (select < 50) From 209cd3a49c6c33c2f5e2dbd52232833d51364205 Mon Sep 17 00:00:00 2001 From: Qi Wenmin Date: Wed, 17 Jan 2018 14:42:15 +0800 Subject: [PATCH 025/173] Fixed most compilation warnings and a delay issue * Fixed most compilation warnings (Compiler warning level: All) * Fixed a delay issue in enc_read function. --- ubitx_20/cat_libs.ino | 2 +- ubitx_20/cw_autokey.ino | 8 ++++++-- ubitx_20/ubitx_20.ino | 18 +++++------------- ubitx_20/ubitx_menu.ino | 21 +++++++++------------ ubitx_20/ubitx_si5351.ino | 2 +- ubitx_20/ubitx_ui.ino | 10 +++++----- 6 files changed, 27 insertions(+), 34 deletions(-) diff --git a/ubitx_20/cat_libs.ino b/ubitx_20/cat_libs.ino index 26c79d3..9c16a61 100644 --- a/ubitx_20/cat_libs.ino +++ b/ubitx_20/cat_libs.ino @@ -398,7 +398,7 @@ void ReadEEPRom_FT817(byte fromType) void WriteEEPRom_FT817(byte fromType) { - byte temp0 = CAT_BUFF[0]; + //byte temp0 = CAT_BUFF[0]; byte temp1 = CAT_BUFF[1]; CAT_BUFF[0] = 0; diff --git a/ubitx_20/cw_autokey.ino b/ubitx_20/cw_autokey.ino index 7c6cc75..3dfbb3b 100644 --- a/ubitx_20/cw_autokey.ino +++ b/ubitx_20/cw_autokey.ino @@ -208,10 +208,14 @@ void sendCWChar(char cwKeyChar) charLength = ((tmpChar >> 6) & 0x03) + 3; for (j = 0; j < charLength; j++) - sendBuff[j] = (tmpChar << j + 2) & 0x80; + sendBuff[j] = (tmpChar << (j + 2)) & 0x80; break; } + else + { + charLength = 0; + } } } @@ -257,7 +261,7 @@ unsigned long scrollDispayTime = 0; #define scrollSpeed 500 byte displayScrolStep = 0; -int controlAutoCW(){ +void controlAutoCW(){ int knob = 0; byte i; diff --git a/ubitx_20/ubitx_20.ino b/ubitx_20/ubitx_20.ino index 2507a30..5a2b8b6 100644 --- a/ubitx_20/ubitx_20.ino +++ b/ubitx_20/ubitx_20.ino @@ -211,7 +211,7 @@ unsigned long vfoA=7150000L, vfoB=14200000L, sideTone=800, usbCarrier; unsigned long vfoA_eeprom, vfoB_eeprom; //for protect eeprom life unsigned long frequency, ritRxFrequency, ritTxFrequency; //frequency is the current frequency on the dial -int cwSpeed = 100; //this is actuall the dot period in milliseconds +unsigned int cwSpeed = 100; //this is actuall the dot period in milliseconds extern int32_t calibration; //for store the mode in eeprom @@ -320,8 +320,8 @@ void setNextHamBandFreq(unsigned long f, char moveDirection) loadMode = (byte)(resultFreq >> 30); resultFreq = resultFreq & 0x3FFFFFFF; - if ((resultFreq / 1000) < hamBandRange[findedIndex][0] || (resultFreq / 1000) > hamBandRange[findedIndex][1]) - resultFreq = (unsigned long)(hamBandRange[findedIndex][0]) * 1000; + if ((resultFreq / 1000) < hamBandRange[(unsigned char)findedIndex][0] || (resultFreq / 1000) > hamBandRange[(unsigned char)findedIndex][1]) + resultFreq = (unsigned long)(hamBandRange[(unsigned char)findedIndex][0]) * 1000; setFrequency(resultFreq); byteWithFreqToMode(loadMode); @@ -422,8 +422,6 @@ void setTXFilters(unsigned long freq){ */ void setFrequency(unsigned long f){ - uint64_t osc_f; - //1 digits discarded f = (f / 50) * 50; @@ -448,8 +446,6 @@ void setFrequency(unsigned long f){ */ void startTx(byte txMode, byte isDisplayUpdate){ - unsigned long tx_freq = 0; - //Check Hamband only TX //Not found Hamband index by now frequency if (tuneTXType >= 100 && getIndexHambanBbyFreq(ritOn ? ritTxFrequency : frequency) == -1) { //no message @@ -545,8 +541,6 @@ void checkPTT(){ } void checkButton(){ - int i, t1, t2, knob, new_knob; - //only if the button is pressed if (!btnDown()) return; @@ -575,7 +569,7 @@ void checkButton(){ void doTuning(){ int s = 0; unsigned long prev_freq; - int incdecValue = 0; + long incdecValue = 0; if ((vfoActive == VFO_A && ((isDialLock & 0x01) == 0x01)) || (vfoActive == VFO_B && ((isDialLock & 0x02) == 0x02))) @@ -610,7 +604,7 @@ void doTuning(){ if (incdecValue > 0 && frequency + incdecValue > HIGHEST_FREQ_DIAL) frequency = HIGHEST_FREQ_DIAL; - else if (incdecValue < 0 && frequency < -incdecValue + LOWEST_FREQ_DIAL) //for compute and compare based integer type. + else if (incdecValue < 0 && frequency < (unsigned long)(-incdecValue + LOWEST_FREQ_DIAL)) //for compute and compare based integer type. frequency = LOWEST_FREQ_DIAL; else frequency += incdecValue; @@ -630,8 +624,6 @@ void doTuning(){ * RIT only steps back and forth by 100 hz at a time */ void doRIT(){ - unsigned long newFreq; - int knob = enc_read(); unsigned long old_freq = frequency; diff --git a/ubitx_20/ubitx_menu.ino b/ubitx_20/ubitx_menu.ino index 0437e41..f8c43fb 100644 --- a/ubitx_20/ubitx_menu.ino +++ b/ubitx_20/ubitx_menu.ino @@ -13,7 +13,7 @@ #define printLineF1(x) (printLineF(1, x)) #define printLineF2(x) (printLineF(0, x)) -int menuBand(int btn){ +void menuBand(int btn){ int knob = 0; int stepChangeCount = 0; byte btnPressCount = 0; @@ -301,7 +301,7 @@ void menuExit(int btn){ } } -int menuCWSpeed(int btn){ +void menuCWSpeed(int btn){ int knob = 0; int wpm; @@ -356,7 +356,7 @@ int menuCWSpeed(int btn){ menuOn = 0; } -int menuCWAutoKey(int btn){ +void menuCWAutoKey(int btn){ if (!btn){ printLineF2(F("CW AutoKey Mode?")); return; @@ -379,7 +379,7 @@ int menuCWAutoKey(int btn){ menuOn = 0; } -int menuSetupCwDelay(int btn){ +void menuSetupCwDelay(int btn){ int knob = 0; int tmpCWDelay = cwDelayTime * 10; @@ -427,7 +427,7 @@ int menuSetupCwDelay(int btn){ menuOn = 0; } -int menuSetupTXCWInterval(int btn){ +void menuSetupTXCWInterval(int btn){ int knob = 0; int tmpTXCWInterval = delayBeforeCWStartTime * 2; @@ -490,10 +490,8 @@ int menuSetupTXCWInterval(int btn){ extern int32_t calibration; extern uint32_t si5351bx_vcoa; -int factoryCalibration(int btn){ +void factoryCalibration(int btn){ int knob = 0; - int32_t prev_calibration; - //keep clear of any previous button press while (btnDown()) @@ -502,10 +500,9 @@ int factoryCalibration(int btn){ if (!btn){ printLineF2(F("Set Calibration?")); - return 0; + return; } - prev_calibration = calibration; calibration = 0; isUSB = true; @@ -560,13 +557,13 @@ int factoryCalibration(int btn){ delay(100); } -int menuSetupCalibration(int btn){ +void menuSetupCalibration(int btn){ int knob = 0; int32_t prev_calibration; if (!btn){ printLineF2(F("Set Calibration?")); - return 0; + return; } printLineF1(F("Set to Zero-beat,")); diff --git a/ubitx_20/ubitx_si5351.ino b/ubitx_20/ubitx_si5351.ino index a5d3ed4..b437ad9 100644 --- a/ubitx_20/ubitx_si5351.ino +++ b/ubitx_20/ubitx_si5351.ino @@ -62,7 +62,7 @@ void i2cWriten(uint8_t reg, uint8_t *vals, uint8_t vcnt) { // write array void si5351bx_init() { // Call once at power-up, start PLLA - uint8_t reg; uint32_t msxp1; + uint32_t msxp1; Wire.begin(); i2cWrite(149, 0); // SpreadSpectrum off i2cWrite(3, si5351bx_clken); // Disable all CLK output drivers diff --git a/ubitx_20/ubitx_ui.ino b/ubitx_20/ubitx_ui.ino index a4c4798..f309fcc 100644 --- a/ubitx_20/ubitx_ui.ino +++ b/ubitx_20/ubitx_ui.ino @@ -115,7 +115,7 @@ void drawMeter(int8_t needle){ */ // The generic routine to display one line on the LCD -void printLine(char linenmbr, char *c) { +void printLine(unsigned char linenmbr, const char *c) { 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); @@ -160,11 +160,11 @@ void printLineFromEEPRom(char linenmbr, char lcdColumn, byte eepromStartIndex, b } // short cut to print to the first line -void printLine1(char *c){ +void printLine1(const char *c){ printLine(1,c); } // short cut to print to the first line -void printLine2(char *c){ +void printLine2(const char *c){ printLine(0,c); } @@ -312,9 +312,9 @@ int enc_read(void) { byte newState; int enc_speed = 0; - long stop_by = millis() + 50; + unsigned long start_at = millis(); - while (millis() < stop_by) { // check if the previous state was stable + while (millis() - start_at < 50) { // check if the previous state was stable newState = enc_state(); // Get current state if (newState != enc_prev_state) From 2fa824750131a548dd784130091923a0ca7cef87 Mon Sep 17 00:00:00 2001 From: phdlee Date: Sat, 20 Jan 2018 22:05:04 +0900 Subject: [PATCH 026/173] v0.29 prepare --- README.md | 56 +++++++++++++++++++++++++++++++++++++++ ubitx_20/cat_libs.ino | 2 +- ubitx_20/cw_autokey.ino | 8 ++++-- ubitx_20/ubitx_20.ino | 20 +++++--------- ubitx_20/ubitx_menu.ino | 21 +++++++-------- ubitx_20/ubitx_si5351.ino | 2 +- ubitx_20/ubitx_ui.ino | 10 +++---- 7 files changed, 84 insertions(+), 35 deletions(-) diff --git a/README.md b/README.md index 66b1ddd..79f9917 100644 --- a/README.md +++ b/README.md @@ -1,11 +1,67 @@ +#IMPORTANT INFORMATION +---------------------------------------------------------------------------- +- Beta 0.26 and Beta 0.261, Beta 0.262, Beta 0.27 is complete test +- You can download and use it. + +#NOTICE +---------------------------------------------------------------------------- +I received uBITX a month ago and found that many features are required, and began coding with the idea of implementing minimal functionality as a general hf transceiver rather than an experimental device. + +- fixed bugs... +- Diallock for uBITX's sensitive encoders +- built in softare Memory keyer and cw options control for CW communication +- Implementation of CAT communication protocol for Digital Communication (as FT8, JT65, etc) +- Delay Options for external Linear. +- and more... + +Most of the basic functions of the HF transceiver I thought were implemented. +The minimum basic specification for uBITX to operate as a radio, I think it is finished. +So I will release the 0.27 version and if I do not see the bug anymore, I will try to change the version name to 1.0. +Now uBITX is an HF radio and will be able to join you in your happy hams life. +Based on this source, you can use it by adding functions. + +I am going to do a new project based on this source, linking with WSPR, WSJT-X and so on. +Of course, this repository is still running. If you have any bugs or ideas, please feel free to email me. + +http://www.hamskey.com + +DE 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 + - Most of them are implemented and included in version 0.27. + - User Interface on LCD -> Option by user (not need) + - Include WSPR Beacone function - (implement other new repository) + complete experiment + need solve : Big code size (over 100%, then remove some functions for experment) + need replace Si5351 Library (increase risk and need more beta tester) + W3PM sent me his wonderful source - using BITX, GPS + ---------------------------------------------------------------------------- ## REVISION RECORD +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 diff --git a/ubitx_20/cat_libs.ino b/ubitx_20/cat_libs.ino index b0aae87..b4c67ec 100644 --- a/ubitx_20/cat_libs.ino +++ b/ubitx_20/cat_libs.ino @@ -398,7 +398,7 @@ void ReadEEPRom_FT817(byte fromType) void WriteEEPRom_FT817(byte fromType) { - byte temp0 = CAT_BUFF[0]; + //byte temp0 = CAT_BUFF[0]; byte temp1 = CAT_BUFF[1]; CAT_BUFF[0] = 0; diff --git a/ubitx_20/cw_autokey.ino b/ubitx_20/cw_autokey.ino index 7c6cc75..3dfbb3b 100644 --- a/ubitx_20/cw_autokey.ino +++ b/ubitx_20/cw_autokey.ino @@ -208,10 +208,14 @@ void sendCWChar(char cwKeyChar) charLength = ((tmpChar >> 6) & 0x03) + 3; for (j = 0; j < charLength; j++) - sendBuff[j] = (tmpChar << j + 2) & 0x80; + sendBuff[j] = (tmpChar << (j + 2)) & 0x80; break; } + else + { + charLength = 0; + } } } @@ -257,7 +261,7 @@ unsigned long scrollDispayTime = 0; #define scrollSpeed 500 byte displayScrolStep = 0; -int controlAutoCW(){ +void controlAutoCW(){ int knob = 0; byte i; diff --git a/ubitx_20/ubitx_20.ino b/ubitx_20/ubitx_20.ino index fa644a1..5a2b8b6 100644 --- a/ubitx_20/ubitx_20.ino +++ b/ubitx_20/ubitx_20.ino @@ -211,7 +211,7 @@ unsigned long vfoA=7150000L, vfoB=14200000L, sideTone=800, usbCarrier; unsigned long vfoA_eeprom, vfoB_eeprom; //for protect eeprom life unsigned long frequency, ritRxFrequency, ritTxFrequency; //frequency is the current frequency on the dial -int cwSpeed = 100; //this is actuall the dot period in milliseconds +unsigned int cwSpeed = 100; //this is actuall the dot period in milliseconds extern int32_t calibration; //for store the mode in eeprom @@ -320,8 +320,8 @@ void setNextHamBandFreq(unsigned long f, char moveDirection) loadMode = (byte)(resultFreq >> 30); resultFreq = resultFreq & 0x3FFFFFFF; - if ((resultFreq / 1000) < hamBandRange[findedIndex][0] || (resultFreq / 1000) > hamBandRange[findedIndex][1]) - resultFreq = (unsigned long)(hamBandRange[findedIndex][0]) * 1000; + if ((resultFreq / 1000) < hamBandRange[(unsigned char)findedIndex][0] || (resultFreq / 1000) > hamBandRange[(unsigned char)findedIndex][1]) + resultFreq = (unsigned long)(hamBandRange[(unsigned char)findedIndex][0]) * 1000; setFrequency(resultFreq); byteWithFreqToMode(loadMode); @@ -344,7 +344,7 @@ unsigned long delayBeforeTime = 0; byte delay_background(unsigned delayTime, byte fromType){ //fromType : 4 autoCWKey -> Check Paddle delayBeforeTime = millis(); - while (millis() <= delayBeforeTime + delayTime) { + while (millis() - delayBeforeTime <= delayTime) { if (fromType == 4) { @@ -422,8 +422,6 @@ void setTXFilters(unsigned long freq){ */ void setFrequency(unsigned long f){ - uint64_t osc_f; - //1 digits discarded f = (f / 50) * 50; @@ -448,8 +446,6 @@ void setFrequency(unsigned long f){ */ void startTx(byte txMode, byte isDisplayUpdate){ - unsigned long tx_freq = 0; - //Check Hamband only TX //Not found Hamband index by now frequency if (tuneTXType >= 100 && getIndexHambanBbyFreq(ritOn ? ritTxFrequency : frequency) == -1) { //no message @@ -545,8 +541,6 @@ void checkPTT(){ } void checkButton(){ - int i, t1, t2, knob, new_knob; - //only if the button is pressed if (!btnDown()) return; @@ -575,7 +569,7 @@ void checkButton(){ void doTuning(){ int s = 0; unsigned long prev_freq; - int incdecValue = 0; + long incdecValue = 0; if ((vfoActive == VFO_A && ((isDialLock & 0x01) == 0x01)) || (vfoActive == VFO_B && ((isDialLock & 0x02) == 0x02))) @@ -610,7 +604,7 @@ void doTuning(){ if (incdecValue > 0 && frequency + incdecValue > HIGHEST_FREQ_DIAL) frequency = HIGHEST_FREQ_DIAL; - else if (incdecValue < 0 && frequency < -incdecValue + LOWEST_FREQ_DIAL) //for compute and compare based integer type. + else if (incdecValue < 0 && frequency < (unsigned long)(-incdecValue + LOWEST_FREQ_DIAL)) //for compute and compare based integer type. frequency = LOWEST_FREQ_DIAL; else frequency += incdecValue; @@ -630,8 +624,6 @@ void doTuning(){ * RIT only steps back and forth by 100 hz at a time */ void doRIT(){ - unsigned long newFreq; - int knob = enc_read(); unsigned long old_freq = frequency; diff --git a/ubitx_20/ubitx_menu.ino b/ubitx_20/ubitx_menu.ino index 02388c4..3b58636 100644 --- a/ubitx_20/ubitx_menu.ino +++ b/ubitx_20/ubitx_menu.ino @@ -13,7 +13,7 @@ #define printLineF1(x) (printLineF(1, x)) #define printLineF2(x) (printLineF(0, x)) -int menuBand(int btn){ +void menuBand(int btn){ int knob = 0; int stepChangeCount = 0; byte btnPressCount = 0; @@ -302,7 +302,7 @@ void menuExit(int btn){ } } -int menuCWSpeed(int btn){ +void menuCWSpeed(int btn){ int knob = 0; int wpm; @@ -357,7 +357,7 @@ int menuCWSpeed(int btn){ menuOn = 0; } -int menuCWAutoKey(int btn){ +void menuCWAutoKey(int btn){ if (!btn){ printLineF2(F("CW AutoKey Mode?")); return; @@ -380,7 +380,7 @@ int menuCWAutoKey(int btn){ menuOn = 0; } -int menuSetupCwDelay(int btn){ +void menuSetupCwDelay(int btn){ int knob = 0; int tmpCWDelay = cwDelayTime * 10; @@ -428,7 +428,7 @@ int menuSetupCwDelay(int btn){ menuOn = 0; } -int menuSetupTXCWInterval(int btn){ +void menuSetupTXCWInterval(int btn){ int knob = 0; int tmpTXCWInterval = delayBeforeCWStartTime * 2; @@ -491,10 +491,8 @@ int menuSetupTXCWInterval(int btn){ extern int32_t calibration; extern uint32_t si5351bx_vcoa; -int factoryCalibration(int btn){ +void factoryCalibration(int btn){ int knob = 0; - int32_t prev_calibration; - //keep clear of any previous button press while (btnDown()) @@ -503,10 +501,9 @@ int factoryCalibration(int btn){ if (!btn){ printLineF2(F("Set Calibration?")); - return 0; + return; } - prev_calibration = calibration; calibration = 0; isUSB = true; @@ -561,13 +558,13 @@ int factoryCalibration(int btn){ delay(100); } -int menuSetupCalibration(int btn){ +void menuSetupCalibration(int btn){ int knob = 0; int32_t prev_calibration; if (!btn){ printLineF2(F("Set Calibration?")); - return 0; + return; } printLineF1(F("Set to Zero-beat,")); diff --git a/ubitx_20/ubitx_si5351.ino b/ubitx_20/ubitx_si5351.ino index a5d3ed4..b437ad9 100644 --- a/ubitx_20/ubitx_si5351.ino +++ b/ubitx_20/ubitx_si5351.ino @@ -62,7 +62,7 @@ void i2cWriten(uint8_t reg, uint8_t *vals, uint8_t vcnt) { // write array void si5351bx_init() { // Call once at power-up, start PLLA - uint8_t reg; uint32_t msxp1; + uint32_t msxp1; Wire.begin(); i2cWrite(149, 0); // SpreadSpectrum off i2cWrite(3, si5351bx_clken); // Disable all CLK output drivers diff --git a/ubitx_20/ubitx_ui.ino b/ubitx_20/ubitx_ui.ino index a4c4798..f309fcc 100644 --- a/ubitx_20/ubitx_ui.ino +++ b/ubitx_20/ubitx_ui.ino @@ -115,7 +115,7 @@ void drawMeter(int8_t needle){ */ // The generic routine to display one line on the LCD -void printLine(char linenmbr, char *c) { +void printLine(unsigned char linenmbr, const char *c) { 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); @@ -160,11 +160,11 @@ void printLineFromEEPRom(char linenmbr, char lcdColumn, byte eepromStartIndex, b } // short cut to print to the first line -void printLine1(char *c){ +void printLine1(const char *c){ printLine(1,c); } // short cut to print to the first line -void printLine2(char *c){ +void printLine2(const char *c){ printLine(0,c); } @@ -312,9 +312,9 @@ int enc_read(void) { byte newState; int enc_speed = 0; - long stop_by = millis() + 50; + unsigned long start_at = millis(); - while (millis() < stop_by) { // check if the previous state was stable + while (millis() - start_at < 50) { // check if the previous state was stable newState = enc_state(); // Get current state if (newState != enc_prev_state) From b1cc5eb98a9554db2b6fa328525e94d852fa7351 Mon Sep 17 00:00:00 2001 From: phdlee Date: Mon, 22 Jan 2018 02:11:35 +0900 Subject: [PATCH 027/173] Update README.md --- README.md | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 79f9917..37ae25e 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,10 @@ #IMPORTANT INFORMATION ---------------------------------------------------------------------------- -- Beta 0.26 and Beta 0.261, Beta 0.262, Beta 0.27 is complete test -- You can download and use it. +-Working on version 0.29 now. Download the source from the release section rather than the master branch version. + Master version is working now. + +- Beta 0.26 and Beta 0.261, Beta 0.262,0.27 is complete test, 0.28 is tested. +- You can download and use it (Release section). #NOTICE ---------------------------------------------------------------------------- From b6bc264332d9f2ffe30e7c647292897e098dd75a Mon Sep 17 00:00:00 2001 From: phdlee Date: Mon, 22 Jan 2018 18:11:15 +0900 Subject: [PATCH 028/173] Update README.md --- README.md | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/README.md b/README.md index 37ae25e..348fb1b 100644 --- a/README.md +++ b/README.md @@ -6,6 +6,15 @@ - Beta 0.26 and Beta 0.261, Beta 0.262,0.27 is complete test, 0.28 is tested. - You can download and use it (Release section). +- Current work list (for Version 0.29) + 1.Testing CAT Control with Software using hamlib on Linux + 2.BFO setting based on current value - complete + 3.Select Tune Step - Testing + 4.Change Tune control type, Do not keep the original source - Complete + - Coded differently after clearing the original source + - Prevent malfunction by applying threshold + 5.stabilize and remove many warning messages - by Pullrequest and merge + #NOTICE ---------------------------------------------------------------------------- I received uBITX a month ago and found that many features are required, and began coding with the idea of implementing minimal functionality as a general hf transceiver rather than an experimental device. From 032e7f919fc68619b929d8ac0e7b18049ae46399 Mon Sep 17 00:00:00 2001 From: phdlee Date: Mon, 22 Jan 2018 18:21:55 +0900 Subject: [PATCH 029/173] Update README.md --- README.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/README.md b/README.md index 348fb1b..37d06f3 100644 --- a/README.md +++ b/README.md @@ -58,6 +58,10 @@ Prepared or finished tasks for the next version ---------------------------------------------------------------------------- ## REVISION RECORD +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) From d1e72b3bd579fb31a2dfbc75a3b5e11ac0d1acff Mon Sep 17 00:00:00 2001 From: phdlee Date: Mon, 22 Jan 2018 18:24:29 +0900 Subject: [PATCH 030/173] Update README.md --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 37d06f3..7136b22 100644 --- a/README.md +++ b/README.md @@ -14,6 +14,8 @@ - Coded differently after clearing the original source - Prevent malfunction by applying threshold 5.stabilize and remove many warning messages - by Pullrequest and merge + 6.Study on improvement method for cw keying - need idea + - set ADC Range value #NOTICE ---------------------------------------------------------------------------- From a1f941f965e160b6d2f21e8ce7d3e87e835456fb Mon Sep 17 00:00:00 2001 From: phdlee Date: Mon, 22 Jan 2018 18:25:41 +0900 Subject: [PATCH 031/173] Update README.md --- README.md | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index 7136b22..a56a178 100644 --- a/README.md +++ b/README.md @@ -6,15 +6,15 @@ - Beta 0.26 and Beta 0.261, Beta 0.262,0.27 is complete test, 0.28 is tested. - You can download and use it (Release section). -- Current work list (for Version 0.29) - 1.Testing CAT Control with Software using hamlib on Linux - 2.BFO setting based on current value - complete - 3.Select Tune Step - Testing - 4.Change Tune control type, Do not keep the original source - Complete +# Current work list (for Version 0.29) + -Testing CAT Control with Software using hamlib on Linux + -BFO setting based on current value - complete + -Select Tune Step - Testing + -Change Tune control type, Do not keep the original source - Complete - Coded differently after clearing the original source - Prevent malfunction by applying threshold - 5.stabilize and remove many warning messages - by Pullrequest and merge - 6.Study on improvement method for cw keying - need idea + -stabilize and remove many warning messages - by Pullrequest and merge + -Study on improvement method for cw keying - need idea - set ADC Range value #NOTICE From e61e45d3dd8eaf55a76dabeb66e4e842df00318c Mon Sep 17 00:00:00 2001 From: phdlee Date: Mon, 22 Jan 2018 18:26:22 +0900 Subject: [PATCH 032/173] Update README.md --- README.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index a56a178..a4e603a 100644 --- a/README.md +++ b/README.md @@ -7,14 +7,14 @@ - You can download and use it (Release section). # Current work list (for Version 0.29) - -Testing CAT Control with Software using hamlib on Linux - -BFO setting based on current value - complete - -Select Tune Step - Testing - -Change Tune control type, Do not keep the original source - Complete + 1 Testing CAT Control with Software using hamlib on Linux + 2 BFO setting based on current value - complete + 3 Select Tune Step - Testing + 4 Change Tune control type, Do not keep the original source - Complete - Coded differently after clearing the original source - Prevent malfunction by applying threshold - -stabilize and remove many warning messages - by Pullrequest and merge - -Study on improvement method for cw keying - need idea + 5 stabilize and remove many warning messages - by Pullrequest and merge + 6 Study on improvement method for cw keying - need idea - set ADC Range value #NOTICE From 4d61cf4de95ce38d1a9f8e87ad1f8c8436d9e700 Mon Sep 17 00:00:00 2001 From: phdlee Date: Mon, 22 Jan 2018 19:46:50 +0900 Subject: [PATCH 033/173] freq tunes, and set defualt values --- ubitx_20/ubitx_20.ino | 142 +++++++++++++++++++++++++-------------- ubitx_20/ubitx_keyer.ino | 2 + ubitx_20/ubitx_menu.ino | 88 ++++++++++++++++++------ 3 files changed, 161 insertions(+), 71 deletions(-) diff --git a/ubitx_20/ubitx_20.ino b/ubitx_20/ubitx_20.ino index 5a2b8b6..784fb65 100644 --- a/ubitx_20/ubitx_20.ino +++ b/ubitx_20/ubitx_20.ino @@ -152,6 +152,7 @@ int count = 0; //to generally count ticks, loops, etc #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) //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. @@ -235,7 +236,8 @@ byte sideToneSub = 0; //DialLock byte isDialLock = 0; //000000[0]vfoB [0]vfoA 0Bit : A, 1Bit : B byte isTxType = 0; //000000[0 - isSplit] [0 - isTXStop] - +byte arTuneStep[5]; +byte tuneStepIndex; //Variables for auto cw mode byte isCWAutoMode = 0; //0 : none, 1 : CW_AutoMode_Menu_Selection, 2 : CW_AutoMode Sending @@ -423,7 +425,9 @@ void setTXFilters(unsigned long freq){ void setFrequency(unsigned long f){ //1 digits discarded - f = (f / 50) * 50; + //byte arTuneStep[] = {10, 20, 50, 100, 200}; + //byte tuneStepIndex = 2; + f = (f / arTuneStep[tuneStepIndex]) * arTuneStep[tuneStepIndex]; setTXFilters(f); @@ -559,13 +563,15 @@ void checkButton(){ } -/** - * The tuning jumps by 50 Hz on each step when you tune slowly - * As you spin the encoder faster, the jump size also increases - * This way, you can quickly move to another band by just spinning the - * tuning knob - */ - +/************************************ +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; +#define encodeTimeOut 1000 void doTuning(){ int s = 0; unsigned long prev_freq; @@ -578,46 +584,37 @@ void doTuning(){ if (isCWAutoMode == 0 || cwAutoDialType == 1) s = enc_read(); - if (s){ - prev_freq = frequency; - - if (s > 10) - incdecValue = 200000l; - if (s > 7) - incdecValue = 10000l; - else if (s > 4) - incdecValue = 1000l; - else if (s > 2) - incdecValue = 500; - else if (s > 0) - incdecValue = 50l; - else if (s > -2) - incdecValue = -50l; - else if (s > -4) - incdecValue = -500l; - else if (s > -7) - incdecValue = -1000l; - else if (s > -9) - incdecValue = -10000l; - else - incdecValue = -200000l; - - if (incdecValue > 0 && frequency + incdecValue > HIGHEST_FREQ_DIAL) - frequency = HIGHEST_FREQ_DIAL; - else if (incdecValue < 0 && frequency < (unsigned long)(-incdecValue + LOWEST_FREQ_DIAL)) //for compute and compare based integer type. - frequency = LOWEST_FREQ_DIAL; - else - frequency += incdecValue; - - if (prev_freq < 10000000l && frequency > 10000000l) - isUSB = true; - - if (prev_freq > 10000000l && frequency < 10000000l) - isUSB = false; - - setFrequency(frequency); - updateDisplay(); + //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; + return; } + lastEncInputtime = millis(); + + //for check moving direction + encodedSumValue += (s > 0 ? 1 : -1); + + //check threshold + if ((encodedSumValue * encodedSumValue) <= (threshold * threshold)) + return; + + //Valid Action without noise + encodedSumValue = 0; + + prev_freq = frequency; + //incdecValue = tuningStep * s; + frequency += (arTuneStep[tuneStepIndex] * s); + + if (prev_freq < 10000000l && frequency > 10000000l) + isUSB = true; + + if (prev_freq > 10000000l && frequency < 10000000l) + isUSB = false; + + setFrequency(frequency); + updateDisplay(); } /** @@ -730,19 +727,62 @@ void initSettings(){ EEPROM.get(HAM_BAND_COUNT, useHamBandCount); EEPROM.get(TX_TUNE_TYPE, tuneTXType); - - if ((3 < tuneTXType && tuneTXType < 100) || 103 < tuneTXType || useHamBandCount < 1) - tuneTXType = 0; + 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 1 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] = 7200; + 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] = EEPROM.read(TUNING_STEP + i + 1); + if (arTuneStep[i] >= 1 && arTuneStep[i] < 251) //Maximum 250 for check valid Value + findedValidValueCount++; + } + + //Check Value Range and default Set for new users + if (findedValidValueCount < 5) + { + //Default Setting + arTuneStep[0] = 10; + arTuneStep[1] = 20; + arTuneStep[2] = 50; + arTuneStep[3] = 100; + arTuneStep[4] = 200; + tuneStepIndex = 2; + } + if (cwDelayTime < 1 || cwDelayTime > 250) cwDelayTime = 60; diff --git a/ubitx_20/ubitx_keyer.ino b/ubitx_20/ubitx_keyer.ino index 5d2b668..67aa248 100644 --- a/ubitx_20/ubitx_keyer.ino +++ b/ubitx_20/ubitx_keyer.ino @@ -122,6 +122,8 @@ void cwKeyer(){ // we will time out, and return out of this routine delay(5); */ + + Check_Cat(2); //for uBITX on Raspberry pi, when straight keying, disconnect / test complete continue; } diff --git a/ubitx_20/ubitx_menu.ino b/ubitx_20/ubitx_menu.ino index 3b58636..52e3ae4 100644 --- a/ubitx_20/ubitx_menu.ino +++ b/ubitx_20/ubitx_menu.ino @@ -36,21 +36,6 @@ void menuBand(int btn){ } else { tuneTXType = 2; - //if empty band Information, auto insert default region 1 frequency range - //This part is made temporary for people who have difficulty setting up, so can remove it when you run out of memory. - if (useHamBandCount < 1) { - 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] = 7200; - 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; - } printLineF2(F("Ham band mode")); } delay_background(1000, 0); @@ -653,7 +638,8 @@ void menuSetupCarrier(int btn){ printLineF1(F("PTT to confirm. ")); delay_background(1000, 0); - usbCarrier = 11995000l; + //usbCarrier = 11995000l; //Remarked by KD8CEC, Suggest from many user, if entry routine factoryrest + si5351bx_setfreq(0, usbCarrier); printCarrierFreq(usbCarrier); @@ -756,22 +742,29 @@ void setDialLock(byte tmpLock, byte fromMode) { printLine2ClearAndUpdate(); } -int btnDownTimeCount; +unsigned int btnDownTimeCount; + +#define PRESS_ADJUST_TUNE 1000 +#define PRESS_LOCK_CONTROL 2000 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(50); Check_Cat(0); //To prevent disconnections - //btnDownTimeCount++; - //check long time Down Button -> 3 Second - if (btnDownTimeCount++ > (2000 / 50)) { + if (btnDownTimeCount++ == (PRESS_ADJUST_TUNE / 50)) { //Set Tune Step + printLineF2(F("Set Tune Step?")); + } + else if (btnDownTimeCount > (PRESS_LOCK_CONTROL / 50)) { //check long time Down Button -> 2.5 Second => Lock if (vfoActive == VFO_A) setDialLock((isDialLock & 0x01) == 0x01 ? 0 : 1, 0); //Reverse Dial lock else @@ -781,6 +774,61 @@ void doMenu(){ } delay(50); //debounce + //ADJUST TUNE STEP + if (btnDownTimeCount > (PRESS_ADJUST_TUNE / 50)) + { + printLineF1(F("Press Key to set")); + isNeedDisplay = 1; //check to need display for display current value + + while (digitalRead(PTT) == HIGH && !btnDown()) + { + Check_Cat(0); //To prevent disconnections + delay(50); //debounce + + if (isNeedDisplay) { + strcpy(b, "Tune Step:"); + itoa(arTuneStep[tuneStepIndex], 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 == 0) + tuneStepIndex = 4; + else + tuneStepIndex--; + } + else + { + if (tuneStepIndex == 4) + tuneStepIndex = 0; + else + tuneStepIndex++; + } + select = 0; + isNeedDisplay = 1; + } + } + } //end of while + + printLineF2(F("Changed Step!")); + //SAVE EEPROM + EEPROM.put(TUNING_STEP, tuneStepIndex); + delay_background(500, 0); + printLine2ClearAndUpdate(); + return; + } //set tune step + + //Below codes are origial code with modified by KD8CEC + //Select menu menuOn = 2; while (menuOn){ From bbb23bf817d93509f9ef014f036f4056fe1a9f8c Mon Sep 17 00:00:00 2001 From: phdlee Date: Mon, 22 Jan 2018 21:16:29 +0900 Subject: [PATCH 034/173] default set for new users --- ubitx_20/ubitx_20.ino | 14 +++++++------- ubitx_20/ubitx_menu.ino | 23 ++++++++++------------- 2 files changed, 17 insertions(+), 20 deletions(-) diff --git a/ubitx_20/ubitx_20.ino b/ubitx_20/ubitx_20.ino index 784fb65..a765fdb 100644 --- a/ubitx_20/ubitx_20.ino +++ b/ubitx_20/ubitx_20.ino @@ -237,7 +237,7 @@ byte sideToneSub = 0; byte isDialLock = 0; //000000[0]vfoB [0]vfoA 0Bit : A, 1Bit : B byte isTxType = 0; //000000[0 - isSplit] [0 - isTXStop] byte arTuneStep[5]; -byte tuneStepIndex; +byte tuneStepIndex; //default Value 0, start Offset is 0 because of check new user //Variables for auto cw mode byte isCWAutoMode = 0; //0 : none, 1 : CW_AutoMode_Menu_Selection, 2 : CW_AutoMode Sending @@ -424,10 +424,7 @@ void setTXFilters(unsigned long freq){ */ void setFrequency(unsigned long f){ - //1 digits discarded - //byte arTuneStep[] = {10, 20, 50, 100, 200}; - //byte tuneStepIndex = 2; - f = (f / arTuneStep[tuneStepIndex]) * arTuneStep[tuneStepIndex]; + f = (f / arTuneStep[tuneStepIndex -1]) * arTuneStep[tuneStepIndex -1]; setTXFilters(f); @@ -605,7 +602,7 @@ void doTuning(){ prev_freq = frequency; //incdecValue = tuningStep * s; - frequency += (arTuneStep[tuneStepIndex] * s); + frequency += (arTuneStep[tuneStepIndex -1] * s); if (prev_freq < 10000000l && frequency > 10000000l) isUSB = true; @@ -780,9 +777,12 @@ void initSettings(){ arTuneStep[2] = 50; arTuneStep[3] = 100; arTuneStep[4] = 200; - tuneStepIndex = 2; } + if (tuneStepIndex == 0) //New User + tuneStepIndex = 3; + + if (cwDelayTime < 1 || cwDelayTime > 250) cwDelayTime = 60; diff --git a/ubitx_20/ubitx_menu.ino b/ubitx_20/ubitx_menu.ino index 52e3ae4..37165d9 100644 --- a/ubitx_20/ubitx_menu.ino +++ b/ubitx_20/ubitx_menu.ino @@ -787,7 +787,7 @@ void doMenu(){ if (isNeedDisplay) { strcpy(b, "Tune Step:"); - itoa(arTuneStep[tuneStepIndex], c, 10); + itoa(arTuneStep[tuneStepIndex -1], c, 10); strcat(b, c); printLine2(b); isNeedDisplay = 0; @@ -799,18 +799,12 @@ void doMenu(){ select += (i > 0 ? 1 : -1); if (select * select >= 25) { //Threshold 5 * 5 = 25 - if (select < 0) - { - if (tuneStepIndex == 0) - tuneStepIndex = 4; - else + if (select < 0) { + if (tuneStepIndex > 1) tuneStepIndex--; } - else - { - if (tuneStepIndex == 4) - tuneStepIndex = 0; - else + else { + if (tuneStepIndex < 5) tuneStepIndex++; } select = 0; @@ -841,10 +835,13 @@ void doMenu(){ if (!modeCalibrate && select + i < 80) select += i; } - if (i < 0 && select - i >= 0) + //if (i < 0 && select - i >= 0) + if (i < 0 && select - i >= -10) select += i; //caught ya, i is already -ve here, so you add it - if (select < 10) + if (select < -5) + menuExit(btnState); + else if (select < 10) menuBand(btnState); else if (select < 20) menuRitToggle(btnState); From c7be3dcd39d2cb1c39ec4766cb43b42e9e70b59d Mon Sep 17 00:00:00 2001 From: phdlee Date: Wed, 24 Jan 2018 21:41:15 +0900 Subject: [PATCH 035/173] test for new cw keying logic --- ubitx_20/ubitx_keyer.ino | 225 ++++++++++++++++++++++++++++++++++++--- 1 file changed, 211 insertions(+), 14 deletions(-) diff --git a/ubitx_20/ubitx_keyer.ino b/ubitx_20/ubitx_keyer.ino index 67aa248..cf950b8 100644 --- a/ubitx_20/ubitx_keyer.ino +++ b/ubitx_20/ubitx_keyer.ino @@ -1,5 +1,7 @@ /** * CW Keyer + * CW Key logic change with ron's code (ubitx_keyer.cpp) <=== ********************************** + * The file you are working on. The code only applies and is still in testing. <==== *********** * * 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. @@ -34,7 +36,6 @@ //when both are simultaneously pressed char lastPaddle = 0; - //reads the analog keyer pin and reports the paddle byte getPaddle(){ int paddle = analogRead(ANALOG_KEYER); @@ -81,13 +82,218 @@ void cwKeyUp(){ cwTimeout = millis() + cwDelayTime * 10; } +/***************************************************************************** +// New logic, by RON +// modified by KD8CEC +******************************************************************************/ +#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 long ktimer; + +bool Iambic_Key = true; +unsigned char keyerControl = IAMBICB; +unsigned char keyerState = IDLE; + +//Below is a test to reduce the keying error. +/* +char update_PaddleLatch(byte isUpdateKeyState) { + int paddle = analogRead(ANALOG_KEYER); + unsigned char tmpKeyerControl; + + if (paddle > 800) // above 4v is up + tmpKeyerControl = 0; + //else if (paddle > 600) // 4-3v is DASH + else if (paddle > 693 && paddle < 700) // 4-3v is DASH + tmpKeyerControl |= DAH_L; + //else if (paddle > 300) //1-2v is DOT + else if (paddle > 323 && paddle < 328) //1-2v is DOT + tmpKeyerControl |= DIT_L; + //else if (paddle > 50) + else if (paddle > 280 && paddle < 290) + tmpKeyerControl |= (DAH_L | DIT_L) ; //both are between 1 and 2v + else + tmpKeyerControl = 0 ; //STRAIGHT KEY in original code + //keyerControl |= (DAH_L | DIT_L) ; //STRAIGHT KEY in original code + + if (isUpdateKeyState == 1) { + keyerControl |= tmpKeyerControl; + } + + byte buff[17]; + sprintf(buff, "Key : %d", paddle); + if (tmpKeyerControl > 0) + printLine2(buff); + + return tmpKeyerControl; + + //if (analogRead(ANALOG_DOT) < 600 ) keyerControl |= DIT_L; + //if (analogRead(ANALOG_DASH) < 600 ) keyerControl |= DAH_L; +} +*/ + +//create by KD8CEC for compatible with new CW Logic +char update_PaddleLatch(byte isUpdateKeyState) { + int paddle = analogRead(ANALOG_KEYER); + unsigned char tmpKeyerControl; + + if (paddle > 800) // above 4v is up + tmpKeyerControl = 0; + else if (paddle > 600) // 4-3v is DASH + tmpKeyerControl |= DAH_L; + else if (paddle > 300) //1-2v is DOT + tmpKeyerControl |= DIT_L; + else if (paddle > 50) + tmpKeyerControl |= (DAH_L | DIT_L) ; //both are between 1 and 2v + else + tmpKeyerControl = 0 ; //STRAIGHT KEY in original code + //keyerControl |= (DAH_L | DIT_L) ; //STRAIGHT KEY in original code + + if (isUpdateKeyState == 1) { + keyerControl |= tmpKeyerControl; + } + + return tmpKeyerControl; + //if (analogRead(ANALOG_DOT) < 600 ) keyerControl |= DIT_L; + //if (analogRead(ANALOG_DASH) < 600 ) keyerControl |= DAH_L; +} + +void cwKeyer(void){ + byte paddle; + lastPaddle = 0; + int dot,dash; + 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)) { + //DIT or DASH or current state DIT & DASH + //(analogRead(ANALOG_DOT) < 600) || //DIT + //(analogRead(ANALOG_DASH) < 600) || //DIT + // (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: + ktimer += millis(); // set ktimer to interval end time + keyerControl &= ~(DIT_L + DAH_L); // clear both paddle latch bits + keyerState = KEYED; // next state + if (!inTx){ + keyDown = 0; + cwTimeout = millis() + cwDelayTime * 10; //+ CW_TIMEOUT; + startTx(TX_CW, 0); + } + 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; + } +} //end of while + +}else{ + while(1){ + //if (analogRead(ANALOG_DOT) < 600){ + if (update_PaddleLatch(0) == DIT_L) { + // if we are here, it is only because the key is pressed + if (!inTx){ + keyDown = 0; + cwTimeout = millis() + cwDelayTime * 10; //+ CW_TIMEOUT; + startTx(TX_CW, 0); + } + // start the transmission) + cwKeydown(); + //while ( analogRead(ANALOG_DOT) < 600 ) delay(1); + while ( update_PaddleLatch(0) == DIT_L ) delay(1); + cwKeyUp(); + }else{ + if (0 < cwTimeout && cwTimeout < millis()){ + cwTimeout = 0; + keyDown = 0; + stopTx(); + } + if (!cwTimeout) + 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); + continue; + } +} //end of else +} +} + + + +//======================================================================================= +//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; @@ -111,18 +317,6 @@ void cwKeyer(){ if (!cwTimeout) return; - //if a paddle was used (not a straight key) we should extend the space to be a full dash - //by adding two more dots long space (one has already been added at the end of the dot or dash) - /* - if (cwTimeout > 0 && lastPaddle != PADDLE_STRAIGHT) - delay_background(cwSpeed * 2, 3); - //delay(cwSpeed * 2); - - // got back to the begining of the loop, if no further activity happens on the paddle or the straight key - // we will time out, and return out of this routine - delay(5); - */ - Check_Cat(2); //for uBITX on Raspberry pi, when straight keying, disconnect / test complete continue; } @@ -186,3 +380,6 @@ void cwKeyer(){ delay(cwSpeed); } } +*/ + + From 386a0b2d46fe64dfed44229f975ce7e37eb2f1fc Mon Sep 17 00:00:00 2001 From: phdlee Date: Thu, 25 Jan 2018 22:33:20 +0900 Subject: [PATCH 036/173] Update README.md --- README.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/README.md b/README.md index a4e603a..41f4bd9 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,10 @@ #IMPORTANT INFORMATION ---------------------------------------------------------------------------- +- 0.29 Version Test only download. + replace keyer logic to Ron's code + Currently, it does not work with straight keys for testing purposes. + If you are using a straight key, please download it up in a few days. + -Working on version 0.29 now. Download the source from the release section rather than the master branch version. Master version is working now. From 020b34e504db09d5dc83fee6b41ded35865ac853 Mon Sep 17 00:00:00 2001 From: phdlee Date: Thu, 25 Jan 2018 23:15:24 +0900 Subject: [PATCH 037/173] add menu for new Keyer logic --- ubitx_20/ubitx_keyer.ino | 21 ++++++++++---------- ubitx_20/ubitx_menu.ino | 41 ++++++++++++++++++++++++++++++++++++++-- 2 files changed, 50 insertions(+), 12 deletions(-) diff --git a/ubitx_20/ubitx_keyer.ino b/ubitx_20/ubitx_keyer.ino index cf950b8..d994315 100644 --- a/ubitx_20/ubitx_keyer.ino +++ b/ubitx_20/ubitx_keyer.ino @@ -95,7 +95,7 @@ enum KSTYPE {IDLE, CHK_DIT, CHK_DAH, KEYED_PREP, KEYED, INTER_ELEMENT }; static long ktimer; -bool Iambic_Key = true; +bool Iambic_Key = false; unsigned char keyerControl = IAMBICB; unsigned char keyerState = IDLE; @@ -150,16 +150,17 @@ char update_PaddleLatch(byte isUpdateKeyState) { else if (paddle > 50) tmpKeyerControl |= (DAH_L | DIT_L) ; //both are between 1 and 2v else - tmpKeyerControl = 0 ; //STRAIGHT KEY in original code - //keyerControl |= (DAH_L | DIT_L) ; //STRAIGHT KEY in original code - - if (isUpdateKeyState == 1) { - keyerControl |= tmpKeyerControl; + { //STRAIGHT KEY in original code + if (Iambic_Key) + tmpKeyerControl = 0 ; + else + tmpKeyerControl = DIT_L ; } + + if (isUpdateKeyState == 1) + keyerControl |= tmpKeyerControl; return tmpKeyerControl; - //if (analogRead(ANALOG_DOT) < 600 ) keyerControl |= DIT_L; - //if (analogRead(ANALOG_DASH) < 600 ) keyerControl |= DAH_L; } void cwKeyer(void){ @@ -217,7 +218,7 @@ while(continue_loop){ if (!inTx){ keyDown = 0; cwTimeout = millis() + cwDelayTime * 10; //+ CW_TIMEOUT; - startTx(TX_CW, 0); + startTx(TX_CW, 1); } cwKeydown(); break; @@ -256,7 +257,7 @@ while(continue_loop){ if (!inTx){ keyDown = 0; cwTimeout = millis() + cwDelayTime * 10; //+ CW_TIMEOUT; - startTx(TX_CW, 0); + startTx(TX_CW, 1); } // start the transmission) cwKeydown(); diff --git a/ubitx_20/ubitx_menu.ino b/ubitx_20/ubitx_menu.ino index 37165d9..117dca7 100644 --- a/ubitx_20/ubitx_menu.ino +++ b/ubitx_20/ubitx_menu.ino @@ -229,6 +229,41 @@ void menuSidebandToggle(int btn){ } } +void menuSetupKeyType(int btn){ + if (!btn && digitalRead(PTT) == HIGH){ + if (Iambic_Key) + printLineF2(F("Key: Straight?")); + else + printLineF2(F("Key: Fn=A, PTT=B")); + } + else { + if (Iambic_Key) + { + printLineF2(F("Straight Key!")); + Iambic_Key = false; + } + else + { + Iambic_Key = true; + if (btn) + { + keyerControl &= ~IAMBICB; + printLineF2(F("IAMBICA Key!")); + } + else + { + keyerControl |= IAMBICB; + printLineF2(F("IAMBICB Key!")); + } + } + + delay_background(500, 0); + printLine2ClearAndUpdate(); + menuOn = 0; + } +} + + void menuTxOnOff(int btn, byte optionType){ if (!btn){ if ((isTxType & optionType) == 0) @@ -830,7 +865,7 @@ void doMenu(){ btnState = btnDown(); if (i > 0){ - if (modeCalibrate && select + i < 150) + if (modeCalibrate && select + i < 160) select += i; if (!modeCalibrate && select + i < 80) select += i; @@ -868,8 +903,10 @@ void doMenu(){ else if (select < 130 && modeCalibrate) menuSetupTXCWInterval(btnState); else if (select < 140 && modeCalibrate) - menuTxOnOff(btnState, 0x01); //TX OFF / ON + menuSetupKeyType(btnState); else if (select < 150 && modeCalibrate) + menuTxOnOff(btnState, 0x01); //TX OFF / ON + else if (select < 160 && modeCalibrate) menuExit(btnState); Check_Cat(0); //To prevent disconnections From 981db341dbb8d3ae997f420903f5e1878dbc2e0b Mon Sep 17 00:00:00 2001 From: phdlee Date: Thu, 25 Jan 2018 23:31:47 +0900 Subject: [PATCH 038/173] change defautl key type --- ubitx_20/ubitx_keyer.ino | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ubitx_20/ubitx_keyer.ino b/ubitx_20/ubitx_keyer.ino index d994315..99dd5af 100644 --- a/ubitx_20/ubitx_keyer.ino +++ b/ubitx_20/ubitx_keyer.ino @@ -95,7 +95,7 @@ enum KSTYPE {IDLE, CHK_DIT, CHK_DAH, KEYED_PREP, KEYED, INTER_ELEMENT }; static long ktimer; -bool Iambic_Key = false; +bool Iambic_Key = true; unsigned char keyerControl = IAMBICB; unsigned char keyerState = IDLE; From 4e15f2150cd4277d27ec7272a51d90f0e47421cd Mon Sep 17 00:00:00 2001 From: phdlee Date: Thu, 25 Jan 2018 23:39:33 +0900 Subject: [PATCH 039/173] Update README.md --- README.md | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 41f4bd9..4f3072e 100644 --- a/README.md +++ b/README.md @@ -1,13 +1,11 @@ #IMPORTANT INFORMATION ---------------------------------------------------------------------------- -- 0.29 Version Test only download. +- 0.296 Version Test only download. almost complete replace keyer logic to Ron's code - Currently, it does not work with straight keys for testing purposes. - If you are using a straight key, please download it up in a few days. + Currently, default key type is IAMBICB + If you are using a straight key or IAMBICB, you can change key type at setup menu. + default Band select is Ham Band mode, if you want common type, long press function key at band select menu --Working on version 0.29 now. Download the source from the release section rather than the master branch version. - Master version is working now. - - Beta 0.26 and Beta 0.261, Beta 0.262,0.27 is complete test, 0.28 is tested. - You can download and use it (Release section). From db543c43e18163b085d0292e703ad09877c05845 Mon Sep 17 00:00:00 2001 From: phdlee Date: Fri, 26 Jan 2018 18:23:52 +0900 Subject: [PATCH 040/173] Add Comment --- ubitx_20/cat_libs.ino | 1 + ubitx_20/cw_autokey.ino | 3 +++ ubitx_20/ubitx_20.ino | 6 ++++++ ubitx_20/ubitx_keyer.ino | 11 ++++++----- 4 files changed, 16 insertions(+), 5 deletions(-) diff --git a/ubitx_20/cat_libs.ino b/ubitx_20/cat_libs.ino index b4c67ec..7a29dd8 100644 --- a/ubitx_20/cat_libs.ino +++ b/ubitx_20/cat_libs.ino @@ -1,4 +1,5 @@ /************************************************************************* + 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 diff --git a/ubitx_20/cw_autokey.ino b/ubitx_20/cw_autokey.ino index 3dfbb3b..7f20461 100644 --- a/ubitx_20/cw_autokey.ino +++ b/ubitx_20/cw_autokey.ino @@ -1,4 +1,6 @@ /************************************************************************* + KD8CEC'S Memory Keyer for HAM + This source code is written for All amateur radio operator, I have not had amateur radio communication for a long time. CW has been around for a long time, and I do not know what kind of keyer and keying @@ -13,6 +15,7 @@ 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 ----------------------------------------------------------------------------- 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 diff --git a/ubitx_20/ubitx_20.ino b/ubitx_20/ubitx_20.ino index a765fdb..f5d986e 100644 --- a/ubitx_20/ubitx_20.ino +++ b/ubitx_20/ubitx_20.ino @@ -1,4 +1,10 @@ /** + Since KD8CEC Version 0.29, most of the original code is no longer available. + Most features(TX, Frequency Range, Ham Band, TX Control, CW delay, start Delay... more) have been added by KD8CEC. + However, the license rules are subject to the original source rules. + DE Ian KD8CEC + + Original source comment ------------------------------------------------------------- * This source file is under General Public License version 3. * * This verision uses a built-in Si5351 library diff --git a/ubitx_20/ubitx_keyer.ino b/ubitx_20/ubitx_keyer.ino index 99dd5af..bae1396 100644 --- a/ubitx_20/ubitx_keyer.ino +++ b/ubitx_20/ubitx_keyer.ino @@ -1,8 +1,9 @@ /** - * CW Keyer - * CW Key logic change with ron's code (ubitx_keyer.cpp) <=== ********************************** - * The file you are working on. The code only applies and is still in testing. <==== *********** - * + 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. @@ -163,6 +164,7 @@ char update_PaddleLatch(byte isUpdateKeyState) { return tmpKeyerControl; } +//This function is Ron's Logic. void cwKeyer(void){ byte paddle; lastPaddle = 0; @@ -282,7 +284,6 @@ while(continue_loop){ } - //======================================================================================= //Before logic //by Farhan and modified by KD8CEC From 4506ff1c1bf7b81680df762ad3298b43798341cb Mon Sep 17 00:00:00 2001 From: phdlee Date: Fri, 26 Jan 2018 21:47:15 +0900 Subject: [PATCH 041/173] for Reduce CW Keying error --- ubitx_20/cw_autokey.ino | 2 +- ubitx_20/ubitx_20.ino | 69 ++++++++++++++++++++++++-- ubitx_20/ubitx_keyer.ino | 40 +++++++++------- ubitx_20/ubitx_menu.ino | 101 ++++++++++++++++++++++++++++++++++++++- 4 files changed, 190 insertions(+), 22 deletions(-) diff --git a/ubitx_20/cw_autokey.ino b/ubitx_20/cw_autokey.ino index 7f20461..01c2564 100644 --- a/ubitx_20/cw_autokey.ino +++ b/ubitx_20/cw_autokey.ino @@ -1,5 +1,5 @@ /************************************************************************* - KD8CEC'S Memory Keyer for HAM + KD8CEC's Memory Keyer for HAM This source code is written for All amateur radio operator, I have not had amateur radio communication for a long time. CW has been diff --git a/ubitx_20/ubitx_20.ino b/ubitx_20/ubitx_20.ino index f5d986e..fc791a4 100644 --- a/ubitx_20/ubitx_20.ino +++ b/ubitx_20/ubitx_20.ino @@ -158,7 +158,17 @@ int count = 0; //to generally count ticks, loops, etc #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) +#define TUNING_STEP 342 //TUNING STEP * 6 (index 1 + STEPS 5) + +//for reduce cw key error, eeprom address +#define CW_ADC_ST_FROM 348 //CW ADC Range STRAIGHT KEY from +#define CW_ADC_ST_TO 349 //CW ADC Range STRAIGHT KEY to +#define CW_ADC_DOT_FROM 350 //CW ADC Range DOT from +#define CW_ADC_DOT_TO 351 //CW ADC Range DOT to +#define CW_ADC_DASH_FROM 352 //CW ADC Range DASH from +#define CW_ADC_DASH_TO 353 //CW ADC Range DASH to +#define CW_ADC_BOTH_FROM 354 //CW ADC Range BOTH from +#define CW_ADC_BOTH_TO 355 //CW ADC Range BOTH to //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. @@ -245,6 +255,16 @@ byte isTxType = 0; //000000[0 - isSplit] [0 - isTXStop] byte arTuneStep[5]; byte tuneStepIndex; //default Value 0, start Offset is 0 because of check new user +//CW ADC Range +byte cwAdcSTFrom = 0; +byte cwAdcSTTo = 0; +byte cwAdcDotFrom = 0; +byte cwAdcDotTo = 0; +byte cwAdcDashtFrom = 0; +byte cwAdcDashTo = 0; +byte cwAdcBothFrom = 0; +byte cwAdcBothTo = 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 @@ -686,7 +706,10 @@ void initSettings(){ 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 @@ -787,6 +810,44 @@ void initSettings(){ if (tuneStepIndex == 0) //New User tuneStepIndex = 3; + + //CW Key ADC Range ======= adjust set value for reduce cw keying error + //by KD8CEC + EEPROM.get(CW_ADC_ST_FROM, cwAdcSTFrom); + EEPROM.get(CW_ADC_ST_TO, cwAdcSTTo); + + EEPROM.get(CW_ADC_DOT_FROM, cwAdcDotFrom); + EEPROM.get(CW_ADC_DOT_TO, cwAdcDotTo); + + EEPROM.get(CW_ADC_DASH_FROM, cwAdcDashtFrom); + EEPROM.get(CW_ADC_DASH_TO, cwAdcDashTo); + + EEPROM.get(CW_ADC_BOTH_FROM, cwAdcBothFrom); + EEPROM.get(CW_ADC_BOTH_TO, cwAdcBothTo); + + //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 (cwAdcDashtFrom >= cwAdcDashTo) + { + cwAdcDashtFrom = 601; + cwAdcDashTo = 800; + } if (cwDelayTime < 1 || cwDelayTime > 250) @@ -798,6 +859,7 @@ void initSettings(){ if (vfoB_mode < 2) vfoB_mode = 3; + //original code with modified by kd8cec if (usbCarrier > 12010000l || usbCarrier < 11990000l) usbCarrier = 11995000l; @@ -810,8 +872,9 @@ void initSettings(){ vfoB = 14150000l; vfoB_mode = 3; } + //end of original code section - //for protect eeprom life + //for protect eeprom life by KD8CEC vfoA_eeprom = vfoA; vfoB_eeprom = vfoB; vfoA_mode_eeprom = vfoA_mode; diff --git a/ubitx_20/ubitx_keyer.ino b/ubitx_20/ubitx_keyer.ino index bae1396..0611f58 100644 --- a/ubitx_20/ubitx_keyer.ino +++ b/ubitx_20/ubitx_keyer.ino @@ -83,41 +83,45 @@ void cwKeyUp(){ cwTimeout = millis() + cwDelayTime * 10; } -/***************************************************************************** -// New logic, by RON -// modified by KD8CEC -******************************************************************************/ +//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 long ktimer; - bool Iambic_Key = true; unsigned char keyerControl = IAMBICB; unsigned char keyerState = IDLE; -//Below is a test to reduce the keying error. -/* +//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) { int paddle = analogRead(ANALOG_KEYER); unsigned char tmpKeyerControl; - if (paddle > 800) // above 4v is up - tmpKeyerControl = 0; + //if (paddle > 800) // above 4v is up + // tmpKeyerControl = 0; //else if (paddle > 600) // 4-3v is DASH - else if (paddle > 693 && paddle < 700) // 4-3v is DASH + if (paddle > cwAdcDashtFrom && cwAdcDashTo < 700) // 4-3v is DASH tmpKeyerControl |= DAH_L; //else if (paddle > 300) //1-2v is DOT - else if (paddle > 323 && paddle < 328) //1-2v is DOT + else if (paddle > cwAdcDotFrom && paddle < cwAdcDotTo) //1-2v is DOT tmpKeyerControl |= DIT_L; //else if (paddle > 50) - else if (paddle > 280 && paddle < 290) + else if (paddle > cwAdcBothFrom && paddle < cwAdcBothTo) tmpKeyerControl |= (DAH_L | DIT_L) ; //both are between 1 and 2v - else + else + { + if (Iambic_Key) + tmpKeyerControl = 0 ; + else if (paddle > cwAdcSTFrom && paddle < cwAdcSTTo) + tmpKeyerControl = DIT_L ; + else + tmpKeyerControl = 0 ; + } + tmpKeyerControl = 0 ; //STRAIGHT KEY in original code //keyerControl |= (DAH_L | DIT_L) ; //STRAIGHT KEY in original code @@ -135,8 +139,8 @@ char update_PaddleLatch(byte isUpdateKeyState) { //if (analogRead(ANALOG_DOT) < 600 ) keyerControl |= DIT_L; //if (analogRead(ANALOG_DASH) < 600 ) keyerControl |= DAH_L; } -*/ +/* //create by KD8CEC for compatible with new CW Logic char update_PaddleLatch(byte isUpdateKeyState) { int paddle = analogRead(ANALOG_KEYER); @@ -163,8 +167,12 @@ char update_PaddleLatch(byte isUpdateKeyState) { return tmpKeyerControl; } +*/ -//This function is Ron's Logic. +/***************************************************************************** +// New logic, by RON +// modified by KD8CEC +******************************************************************************/ void cwKeyer(void){ byte paddle; lastPaddle = 0; diff --git a/ubitx_20/ubitx_menu.ino b/ubitx_20/ubitx_menu.ino index 117dca7..ffd2394 100644 --- a/ubitx_20/ubitx_menu.ino +++ b/ubitx_20/ubitx_menu.ino @@ -13,6 +13,7 @@ #define printLineF1(x) (printLineF(1, x)) #define printLineF2(x) (printLineF(0, x)) +//Ham band move by KD8CEC void menuBand(int btn){ int knob = 0; int stepChangeCount = 0; @@ -117,6 +118,7 @@ void menuBand(int btn){ menuOn = 0; } +//Convert Mode, Number by KD8CEC //0: default, 1:not use, 2:LSB, 3:USB, 4:CW, 5:AM, 6:FM byte modeToByte(){ if (isUSB) @@ -125,12 +127,15 @@ byte modeToByte(){ return 2; } +//Convert Number to Mode by KD8CEC void byteToMode(byte modeValue){ if (modeValue == 3) isUSB = 1; else isUSB = 0; } + +//Convert Number to Mode by KD8CEC void byteWithFreqToMode(byte modeValue){ if (modeValue == 3) isUSB = 1; @@ -140,6 +145,7 @@ void byteWithFreqToMode(byte modeValue){ isUSB = 0; } +//VFO Toggle and save VFO Information, modified by KD8CEC void menuVfoToggle(int btn, char isUseDelayTime) { if (!btn){ @@ -229,6 +235,7 @@ void menuSidebandToggle(int btn){ } } +//Select CW Key Type by KD8CEC void menuSetupKeyType(int btn){ if (!btn && digitalRead(PTT) == HIGH){ if (Iambic_Key) @@ -263,7 +270,88 @@ void menuSetupKeyType(int btn){ } } +//Analog pin monitoring with CW Key and function keys connected. +//by KD8CEC +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(ORANGE, CW Key) + adcPinA6 = analogRead(A6); //A6(BLUE, Ptt) + adcPinA7 = analogRead(A7); //A7(VIOLET, Spare) +/* + sprintf(c, "%4d %4d %4d", adcPinA0, adcPinA1, adcPinA2); + printLine1(c); + sprintf(c, "%4d %4d %4d", adcPinA3, adcPinA6, adcPinA7); + printLine2(c); +*/ + + if (adcPinA6 < 10) { + 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 + + printLine2ClearAndUpdate(); + menuOn = 0; +} + +//Function to disbled transmission +//by KD8CEC void menuTxOnOff(int btn, byte optionType){ if (!btn){ if ((isTxType & optionType) == 0) @@ -377,6 +465,7 @@ void menuCWSpeed(int btn){ menuOn = 0; } +//Builtin CW Keyer Logic by KD8CEC void menuCWAutoKey(int btn){ if (!btn){ printLineF2(F("CW AutoKey Mode?")); @@ -400,6 +489,7 @@ void menuCWAutoKey(int btn){ menuOn = 0; } +//Modified by KD8CEC void menuSetupCwDelay(int btn){ int knob = 0; int tmpCWDelay = cwDelayTime * 10; @@ -448,6 +538,7 @@ void menuSetupCwDelay(int btn){ menuOn = 0; } +//CW Time delay by KD8CEC void menuSetupTXCWInterval(int btn){ int knob = 0; int tmpTXCWInterval = delayBeforeCWStartTime * 2; @@ -659,6 +750,7 @@ void printCarrierFreq(unsigned long freq){ printLine2(c); } +//modified by KD8CEC (just 1 line remarked //usbCarrier = ... void menuSetupCarrier(int btn){ int knob = 0; unsigned long prevCarrier; @@ -712,6 +804,7 @@ void menuSetupCarrier(int btn){ menuOn = 0; } +//Modified by KD8CEC void menuSetupCwTone(int btn){ int knob = 0; int prev_sideTone; @@ -760,6 +853,7 @@ void menuSetupCwTone(int btn){ menuOn = 0; } +//Lock Dial move by KD8CEC void setDialLock(byte tmpLock, byte fromMode) { if (tmpLock == 1) isDialLock |= (vfoActive == VFO_A ? 0x01 : 0x02); @@ -782,6 +876,7 @@ unsigned int btnDownTimeCount; #define PRESS_ADJUST_TUNE 1000 #define PRESS_LOCK_CONTROL 2000 +//Modified by KD8CEC void doMenu(){ int select=0, i,btnState; char isNeedDisplay = 0; @@ -865,7 +960,7 @@ void doMenu(){ btnState = btnDown(); if (i > 0){ - if (modeCalibrate && select + i < 160) + if (modeCalibrate && select + i < 170) select += i; if (!modeCalibrate && select + i < 80) select += i; @@ -905,8 +1000,10 @@ void doMenu(){ else if (select < 140 && modeCalibrate) menuSetupKeyType(btnState); else if (select < 150 && modeCalibrate) - menuTxOnOff(btnState, 0x01); //TX OFF / ON + menuADCMonitor(btnState); else if (select < 160 && modeCalibrate) + menuTxOnOff(btnState, 0x01); //TX OFF / ON + else if (select < 170 && modeCalibrate) menuExit(btnState); Check_Cat(0); //To prevent disconnections From cc7dd752e639749eafe61032f9d718a1e16d5ed9 Mon Sep 17 00:00:00 2001 From: phdlee Date: Sat, 27 Jan 2018 16:39:54 +0900 Subject: [PATCH 042/173] add function adjust CW ADC Range --- ubitx_20/ubitx_20.ino | 62 +++++++++++++++++++++------------------- ubitx_20/ubitx_keyer.ino | 58 +++---------------------------------- 2 files changed, 37 insertions(+), 83 deletions(-) diff --git a/ubitx_20/ubitx_20.ino b/ubitx_20/ubitx_20.ino index fc791a4..164dc48 100644 --- a/ubitx_20/ubitx_20.ino +++ b/ubitx_20/ubitx_20.ino @@ -161,14 +161,17 @@ int count = 0; //to generally count ticks, loops, etc #define TUNING_STEP 342 //TUNING STEP * 6 (index 1 + STEPS 5) //for reduce cw key error, eeprom address -#define CW_ADC_ST_FROM 348 //CW ADC Range STRAIGHT KEY from -#define CW_ADC_ST_TO 349 //CW ADC Range STRAIGHT KEY to -#define CW_ADC_DOT_FROM 350 //CW ADC Range DOT from -#define CW_ADC_DOT_TO 351 //CW ADC Range DOT to -#define CW_ADC_DASH_FROM 352 //CW ADC Range DASH from -#define CW_ADC_DASH_TO 353 //CW ADC Range DASH to -#define CW_ADC_BOTH_FROM 354 //CW ADC Range BOTH from -#define CW_ADC_BOTH_TO 355 //CW ADC Range BOTH to +#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) //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. @@ -256,14 +259,14 @@ byte arTuneStep[5]; byte tuneStepIndex; //default Value 0, start Offset is 0 because of check new user //CW ADC Range -byte cwAdcSTFrom = 0; -byte cwAdcSTTo = 0; -byte cwAdcDotFrom = 0; -byte cwAdcDotTo = 0; -byte cwAdcDashtFrom = 0; -byte cwAdcDashTo = 0; -byte cwAdcBothFrom = 0; -byte cwAdcBothTo = 0; +int cwAdcSTFrom = 0; +int cwAdcSTTo = 0; +int cwAdcDotFrom = 0; +int cwAdcDotTo = 0; +int cwAdcDashFrom = 0; +int cwAdcDashTo = 0; +int cwAdcBothFrom = 0; +int cwAdcBothTo = 0; //Variables for auto cw mode byte isCWAutoMode = 0; //0 : none, 1 : CW_AutoMode_Menu_Selection, 2 : CW_AutoMode Sending @@ -813,17 +816,18 @@ void initSettings(){ //CW Key ADC Range ======= adjust set value for reduce cw keying error //by KD8CEC - EEPROM.get(CW_ADC_ST_FROM, cwAdcSTFrom); - EEPROM.get(CW_ADC_ST_TO, cwAdcSTTo); - - EEPROM.get(CW_ADC_DOT_FROM, cwAdcDotFrom); - EEPROM.get(CW_ADC_DOT_TO, cwAdcDotTo); + 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); - EEPROM.get(CW_ADC_DASH_FROM, cwAdcDashtFrom); - EEPROM.get(CW_ADC_DASH_TO, cwAdcDashTo); - - EEPROM.get(CW_ADC_BOTH_FROM, cwAdcBothFrom); - EEPROM.get(CW_ADC_BOTH_TO, cwAdcBothTo); + 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); //default Value (for original hardware) if (cwAdcSTFrom >= cwAdcSTTo) @@ -843,13 +847,13 @@ void initSettings(){ cwAdcDotFrom = 301; cwAdcDotTo = 600; } - if (cwAdcDashtFrom >= cwAdcDashTo) + if (cwAdcDashFrom >= cwAdcDashTo) { - cwAdcDashtFrom = 601; + cwAdcDashFrom = 601; cwAdcDashTo = 800; } + //end of CW Keying Variables - if (cwDelayTime < 1 || cwDelayTime > 250) cwDelayTime = 60; diff --git a/ubitx_20/ubitx_keyer.ino b/ubitx_20/ubitx_keyer.ino index 0611f58..0c67876 100644 --- a/ubitx_20/ubitx_keyer.ino +++ b/ubitx_20/ubitx_keyer.ino @@ -98,20 +98,15 @@ 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) { - int paddle = analogRead(ANALOG_KEYER); unsigned char tmpKeyerControl; + int paddle = analogRead(ANALOG_KEYER); - //if (paddle > 800) // above 4v is up - // tmpKeyerControl = 0; - //else if (paddle > 600) // 4-3v is DASH - if (paddle > cwAdcDashtFrom && cwAdcDashTo < 700) // 4-3v is DASH + if (paddle > cwAdcDashFrom && paddle < cwAdcDashTo) tmpKeyerControl |= DAH_L; - //else if (paddle > 300) //1-2v is DOT - else if (paddle > cwAdcDotFrom && paddle < cwAdcDotTo) //1-2v is DOT + else if (paddle > cwAdcDotFrom && paddle < cwAdcDotTo) tmpKeyerControl |= DIT_L; - //else if (paddle > 50) else if (paddle > cwAdcBothFrom && paddle < cwAdcBothTo) - tmpKeyerControl |= (DAH_L | DIT_L) ; //both are between 1 and 2v + tmpKeyerControl |= (DAH_L | DIT_L) ; else { if (Iambic_Key) @@ -122,52 +117,11 @@ char update_PaddleLatch(byte isUpdateKeyState) { tmpKeyerControl = 0 ; } - tmpKeyerControl = 0 ; //STRAIGHT KEY in original code - //keyerControl |= (DAH_L | DIT_L) ; //STRAIGHT KEY in original code - - if (isUpdateKeyState == 1) { - keyerControl |= tmpKeyerControl; - } - - byte buff[17]; - sprintf(buff, "Key : %d", paddle); - if (tmpKeyerControl > 0) - printLine2(buff); - - return tmpKeyerControl; - - //if (analogRead(ANALOG_DOT) < 600 ) keyerControl |= DIT_L; - //if (analogRead(ANALOG_DASH) < 600 ) keyerControl |= DAH_L; -} - -/* -//create by KD8CEC for compatible with new CW Logic -char update_PaddleLatch(byte isUpdateKeyState) { - int paddle = analogRead(ANALOG_KEYER); - unsigned char tmpKeyerControl; - - if (paddle > 800) // above 4v is up - tmpKeyerControl = 0; - else if (paddle > 600) // 4-3v is DASH - tmpKeyerControl |= DAH_L; - else if (paddle > 300) //1-2v is DOT - tmpKeyerControl |= DIT_L; - else if (paddle > 50) - tmpKeyerControl |= (DAH_L | DIT_L) ; //both are between 1 and 2v - else - { //STRAIGHT KEY in original code - if (Iambic_Key) - tmpKeyerControl = 0 ; - else - tmpKeyerControl = DIT_L ; - } - if (isUpdateKeyState == 1) keyerControl |= tmpKeyerControl; return tmpKeyerControl; } -*/ /***************************************************************************** // New logic, by RON @@ -187,10 +141,6 @@ while(continue_loop){ tmpKeyControl = update_PaddleLatch(0); if ( tmpKeyControl == DAH_L || tmpKeyControl == DIT_L || tmpKeyControl == (DAH_L | DIT_L) || (keyerControl & 0x03)) { - //DIT or DASH or current state DIT & DASH - //(analogRead(ANALOG_DOT) < 600) || //DIT - //(analogRead(ANALOG_DASH) < 600) || //DIT - // (keyerControl & 0x03)) { update_PaddleLatch(1); keyerState = CHK_DIT; }else{ From 8d4c788e1176f5735e30618c98764ba2d92031b3 Mon Sep 17 00:00:00 2001 From: phdlee Date: Sat, 27 Jan 2018 18:05:08 +0900 Subject: [PATCH 043/173] 1st Test new CW Keyer and add cat message processing --- ubitx_20/ubitx_20.ino | 21 ++-- ubitx_20/ubitx_keyer.ino | 206 ++++++++++++++++++++------------------- 2 files changed, 121 insertions(+), 106 deletions(-) diff --git a/ubitx_20/ubitx_20.ino b/ubitx_20/ubitx_20.ino index 164dc48..03e617e 100644 --- a/ubitx_20/ubitx_20.ino +++ b/ubitx_20/ubitx_20.ino @@ -597,8 +597,13 @@ 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 100 #define encodeTimeOut 1000 -void doTuning(){ + +void doTuningWithThresHold(){ int s = 0; unsigned long prev_freq; long incdecValue = 0; @@ -615,6 +620,8 @@ void doTuning(){ if (s == 0) { if (encodedSumValue != 0 && (millis() - encodeTimeOut) > lastEncInputtime) encodedSumValue = 0; + + lastMovedirection = 0; return; } lastEncInputtime = millis(); @@ -622,23 +629,25 @@ void doTuning(){ //for check moving direction encodedSumValue += (s > 0 ? 1 : -1); - //check threshold - if ((encodedSumValue * encodedSumValue) <= (threshold * threshold)) + //check threshold and operator actions (hold dial speed = continous moving, skip threshold check) + if ((lastTunetime < millis() - skipThresholdTime) && ((encodedSumValue * encodedSumValue) <= (threshold * threshold))) return; + lastTunetime = millis(); + //Valid Action without noise encodedSumValue = 0; prev_freq = frequency; //incdecValue = tuningStep * s; - frequency += (arTuneStep[tuneStepIndex -1] * s); + frequency += (arTuneStep[tuneStepIndex -1] * s * (s * s < 10 ? 1 : 3)); //appield weight (s is speed) if (prev_freq < 10000000l && frequency > 10000000l) isUSB = true; if (prev_freq > 10000000l && frequency < 10000000l) isUSB = false; - + setFrequency(frequency); updateDisplay(); } @@ -1036,7 +1045,7 @@ void loop(){ if (ritOn) doRIT(); else - doTuning(); + doTuningWithThresHold(); } //we check CAT after the encoder as it might put the radio into TX diff --git a/ubitx_20/ubitx_keyer.ino b/ubitx_20/ubitx_keyer.ino index 0c67876..65e86ec 100644 --- a/ubitx_20/ubitx_keyer.ino +++ b/ubitx_20/ubitx_keyer.ino @@ -133,112 +133,118 @@ void cwKeyer(void){ int dot,dash; 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( 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: + ktimer += millis(); // set ktimer to interval end time + keyerControl &= ~(DIT_L + DAH_L); // clear both paddle latch bits + keyerState = KEYED; // next state + if (!inTx){ + keyDown = 0; + cwTimeout = millis() + cwDelayTime * 10; //+ CW_TIMEOUT; + startTx(TX_CW, 1); + } + 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(3); + } //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){ + 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(); } - continue_loop = false; + if (!cwTimeout) + 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); + continue; } - 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: - ktimer += millis(); // set ktimer to interval end time - keyerControl &= ~(DIT_L + DAH_L); // clear both paddle latch bits - keyerState = KEYED; // next state - if (!inTx){ - keyDown = 0; - cwTimeout = millis() + cwDelayTime * 10; //+ CW_TIMEOUT; - startTx(TX_CW, 1); - } - 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; - } -} //end of while - -}else{ - while(1){ - //if (analogRead(ANALOG_DOT) < 600){ - if (update_PaddleLatch(0) == DIT_L) { - // if we are here, it is only because the key is pressed - if (!inTx){ - keyDown = 0; - cwTimeout = millis() + cwDelayTime * 10; //+ CW_TIMEOUT; - startTx(TX_CW, 1); - } - // start the transmission) - cwKeydown(); - //while ( analogRead(ANALOG_DOT) < 600 ) delay(1); - while ( update_PaddleLatch(0) == DIT_L ) delay(1); - cwKeyUp(); - }else{ - if (0 < cwTimeout && cwTimeout < millis()){ - cwTimeout = 0; - keyDown = 0; - stopTx(); - } - if (!cwTimeout) - 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); - continue; - } -} //end of else -} + Check_Cat(2); + } //end of while + } //end of elese } From 1a2f5b4fdea0a00b0964e4d165973965e30bb504 Mon Sep 17 00:00:00 2001 From: phdlee Date: Sat, 27 Jan 2018 18:33:51 +0900 Subject: [PATCH 044/173] Update README.md --- README.md | 31 ++++++++++++++++--------------- 1 file changed, 16 insertions(+), 15 deletions(-) diff --git a/README.md b/README.md index 4f3072e..7ca4040 100644 --- a/README.md +++ b/README.md @@ -1,24 +1,11 @@ #IMPORTANT INFORMATION ---------------------------------------------------------------------------- -- 0.296 Version Test only download. almost complete - replace keyer logic to Ron's code - Currently, default key type is IAMBICB - If you are using a straight key or IAMBICB, you can change key type at setup menu. - default Band select is Ham Band mode, if you want common type, long press function key at band select menu - +- 0.30 Version Test only download. almost complete - Beta 0.26 and Beta 0.261, Beta 0.262,0.27 is complete test, 0.28 is tested. - You can download and use it (Release section). -# Current work list (for Version 0.29) +# Current work list (for Version 0.31) 1 Testing CAT Control with Software using hamlib on Linux - 2 BFO setting based on current value - complete - 3 Select Tune Step - Testing - 4 Change Tune control type, Do not keep the original source - Complete - - Coded differently after clearing the original source - - Prevent malfunction by applying threshold - 5 stabilize and remove many warning messages - by Pullrequest and merge - 6 Study on improvement method for cw keying - need idea - - set ADC Range value #NOTICE ---------------------------------------------------------------------------- @@ -63,6 +50,20 @@ Prepared or finished tasks for the next version ---------------------------------------------------------------------------- ## REVISION RECORD +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 From ee23827def3d78c1fbac3cde7aed1ccd6b82c82d Mon Sep 17 00:00:00 2001 From: phdlee Date: Sat, 27 Jan 2018 18:38:18 +0900 Subject: [PATCH 045/173] rename version to 0.30 --- ubitx_20/ubitx_20.ino | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ubitx_20/ubitx_20.ino b/ubitx_20/ubitx_20.ino index 03e617e..7ae6077 100644 --- a/ubitx_20/ubitx_20.ino +++ b/ubitx_20/ubitx_20.ino @@ -958,7 +958,7 @@ void setup() //Serial.begin(9600); lcd.begin(16, 2); - printLineF(1, F("CECBT v0.27")); + printLineF(1, F("CECBT v0.30")); Init_Cat(38400, SERIAL_8N1); initMeter(); //not used in this build From 282c196f63cb22281f23e345c4e569c4f8f1f874 Mon Sep 17 00:00:00 2001 From: phdlee Date: Mon, 29 Jan 2018 18:38:48 +0900 Subject: [PATCH 046/173] fixed cw adc range bug --- ubitx_20/cat_libs.ino | 9 ++-- ubitx_20/cw_autokey.ino | 6 ++- ubitx_20/ubitx_20.ino | 46 +++++++++++++++++-- ubitx_20/ubitx_factory_alignment.ino | 1 + ubitx_20/ubitx_idle.ino | 30 +++++++++++++ ubitx_20/ubitx_keyer.ino | 10 ++--- ubitx_20/ubitx_menu.ino | 67 +++++++++++++++++++++++++++- ubitx_20/ubitx_ui.ino | 23 ++++++++-- 8 files changed, 174 insertions(+), 18 deletions(-) create mode 100644 ubitx_20/ubitx_idle.ino diff --git a/ubitx_20/cat_libs.ino b/ubitx_20/cat_libs.ino index 7a29dd8..dec5998 100644 --- a/ubitx_20/cat_libs.ino +++ b/ubitx_20/cat_libs.ino @@ -472,7 +472,7 @@ void WriteEEPRom_FT817(byte fromType) printLineF2(F("Sidetone set! CAT")); EEPROM.put(CW_SIDETONE, sideTone); delay(300); //If timeout errors occur in the calling software, remove them - printLine2(""); //Ham radio deluxe is the only one that supports this feature yet. and ham radio deluxe has wait time as greater than 500ms + clearLine2(); } break; @@ -484,7 +484,8 @@ void WriteEEPRom_FT817(byte fromType) printLineF2(F("Sidetone set! CAT")); EEPROM.put(CW_SIDETONE, sideTone); delay(300); //If timeout errors occur in the calling software, remove them - printLine2(""); //Ham radio deluxe is the only one that supports this feature yet. and ham radio deluxe has wait time as greater than 500ms + clearLine2(); + line2DisplayStatus = 0; } break; @@ -504,7 +505,7 @@ void WriteEEPRom_FT817(byte fromType) printLineF2(F("CW Speed set!")); EEPROM.put(CW_DELAY, cwDelayTime); delay(300); - printLine2(""); + 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) @@ -513,7 +514,7 @@ void WriteEEPRom_FT817(byte fromType) printLineF2(F("CW Speed set!")); EEPROM.put(CW_SPEED, cwSpeed); delay(300); - printLine2(""); + clearLine2(); break; /* diff --git a/ubitx_20/cw_autokey.ino b/ubitx_20/cw_autokey.ino index 01c2564..8c9103e 100644 --- a/ubitx_20/cw_autokey.ino +++ b/ubitx_20/cw_autokey.ino @@ -298,8 +298,12 @@ void controlAutoCW(){ } printLineFromEEPRom(0, 2, cwStartIndex + displayScrolStep + CW_DATA_OFSTADJ, cwEndIndex + CW_DATA_OFSTADJ); + + byte diplayAutoCWLine = 0; + if ((displayOption1 & 0x01) == 0x01) + diplayAutoCWLine = 1; - lcd.setCursor(0,0); + lcd.setCursor(0, diplayAutoCWLine); lcd.write(byteToChar(selectedCWTextIndex)); lcd.write(':'); isNeedScroll = (cwEndIndex - cwStartIndex) > 14 ? 1 : 0; diff --git a/ubitx_20/ubitx_20.ino b/ubitx_20/ubitx_20.ino index 7ae6077..690fb56 100644 --- a/ubitx_20/ubitx_20.ino +++ b/ubitx_20/ubitx_20.ino @@ -84,6 +84,7 @@ #define PTT (A3) #define ANALOG_KEYER (A6) #define ANALOG_SPARE (A7) +#define ANALOG_SMETER (A7) //by KD8CEC /** * The Raduino board is the size of a standard 16x2 LCD panel. It has three connectors: @@ -172,6 +173,10 @@ int count = 0; //to generally count ticks, loops, etc #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 DISPLAY_OPTION1 361 //Display Option1 +#define DISPLAY_OPTION2 362 //Display Option2 //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. @@ -258,6 +263,9 @@ byte isTxType = 0; //000000[0 - isSplit] [0 - isTXStop] byte arTuneStep[5]; byte tuneStepIndex; //default Value 0, start Offset is 0 because of check new user +byte displayOption1 = 0; +byte displayOption2 = 0; + //CW ADC Range int cwAdcSTFrom = 0; int cwAdcSTTo = 0; @@ -267,6 +275,10 @@ 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; //Variables for auto cw mode byte isCWAutoMode = 0; //0 : none, 1 : CW_AutoMode_Menu_Selection, 2 : CW_AutoMode Sending @@ -296,6 +308,10 @@ 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 + +unsigned long beforeIdle_ProcessTime = 0; //for check Idle time +byte line2DisplayStatus = 0; //0:Clear, 1 : menu, 1: DisplayFrom Idle, + /** * Below are the basic functions that control the uBitx. Understanding the functions before * you start hacking around @@ -756,6 +772,24 @@ void initSettings(){ //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(DISPLAY_OPTION1, displayOption1); + EEPROM.get(DISPLAY_OPTION2, displayOption2); //User callsign information if (EEPROM.read(USER_CALLSIGN_KEY) == 0x59) @@ -923,6 +957,7 @@ void initPorts(){ pinMode(PTT, INPUT_PULLUP); pinMode(ANALOG_KEYER, INPUT_PULLUP); + pinMode(ANALOG_SMETER, INPUT); //by KD8CEC pinMode(CW_TONE, OUTPUT); digitalWrite(CW_TONE, 0); @@ -958,7 +993,7 @@ void setup() //Serial.begin(9600); lcd.begin(16, 2); - printLineF(1, F("CECBT v0.30")); + printLineF(1, F("CECBT v0.31")); Init_Cat(38400, SERIAL_8N1); initMeter(); //not used in this build @@ -972,7 +1007,7 @@ void setup() else { printLineF(0, F("uBITX v0.20")); delay(500); - printLine2(""); + clearLine2(); } initPorts(); @@ -1046,7 +1081,12 @@ void loop(){ doRIT(); else doTuningWithThresHold(); - } + + if (isCWAutoMode == 0 && beforeIdle_ProcessTime < millis() - 200) { + idle_process(); + 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); diff --git a/ubitx_20/ubitx_factory_alignment.ino b/ubitx_20/ubitx_factory_alignment.ino index 77c5e16..b73a1c0 100644 --- a/ubitx_20/ubitx_factory_alignment.ino +++ b/ubitx_20/ubitx_factory_alignment.ino @@ -14,6 +14,7 @@ void btnWaitForClick(){ void factory_alignment(){ factoryCalibration(1); + line2DisplayStatus = 1; if (calibration == 0){ printLine2("Setup Aborted"); diff --git a/ubitx_20/ubitx_idle.ino b/ubitx_20/ubitx_idle.ino new file mode 100644 index 0000000..dff2aad --- /dev/null +++ b/ubitx_20/ubitx_idle.ino @@ -0,0 +1,30 @@ +/************************************************************************* + KD8CEC's uBITX Idle time Processing + Functions that run at times that do not affect TX, CW, and CAT + It is called in 1/10 time unit. +----------------------------------------------------------------------------- + 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 . + +**************************************************************************/ + + +void idle_process() +{ + //space for user graphic display + if (menuOn == 0) + { + //if line2DisplayStatus == 0 <-- this condition is clear Line, you can display any message + } +} + diff --git a/ubitx_20/ubitx_keyer.ino b/ubitx_20/ubitx_keyer.ino index 65e86ec..04163af 100644 --- a/ubitx_20/ubitx_keyer.ino +++ b/ubitx_20/ubitx_keyer.ino @@ -91,8 +91,6 @@ void cwKeyUp(){ #define IAMBICB 0x10 // 0 for Iambic A, 1 for Iambic B enum KSTYPE {IDLE, CHK_DIT, CHK_DAH, KEYED_PREP, KEYED, INTER_ELEMENT }; static long ktimer; -bool Iambic_Key = true; -unsigned char keyerControl = IAMBICB; unsigned char keyerState = IDLE; //Below is a test to reduce the keying error. do not delete lines @@ -101,17 +99,17 @@ char update_PaddleLatch(byte isUpdateKeyState) { unsigned char tmpKeyerControl; int paddle = analogRead(ANALOG_KEYER); - if (paddle > cwAdcDashFrom && paddle < cwAdcDashTo) + if (paddle >= cwAdcDashFrom && paddle <= cwAdcDashTo) tmpKeyerControl |= DAH_L; - else if (paddle > cwAdcDotFrom && paddle < cwAdcDotTo) + else if (paddle >= cwAdcDotFrom && paddle <= cwAdcDotTo) tmpKeyerControl |= DIT_L; - else if (paddle > cwAdcBothFrom && paddle < cwAdcBothTo) + else if (paddle >= cwAdcBothFrom && paddle <= cwAdcBothTo) tmpKeyerControl |= (DAH_L | DIT_L) ; else { if (Iambic_Key) tmpKeyerControl = 0 ; - else if (paddle > cwAdcSTFrom && paddle < cwAdcSTTo) + else if (paddle >= cwAdcSTFrom && paddle <= cwAdcSTTo) tmpKeyerControl = DIT_L ; else tmpKeyerControl = 0 ; diff --git a/ubitx_20/ubitx_menu.ino b/ubitx_20/ubitx_menu.ino index ffd2394..5fa1e5a 100644 --- a/ubitx_20/ubitx_menu.ino +++ b/ubitx_20/ubitx_menu.ino @@ -235,6 +235,7 @@ void menuSidebandToggle(int btn){ } } +/* //Select CW Key Type by KD8CEC void menuSetupKeyType(int btn){ if (!btn && digitalRead(PTT) == HIGH){ @@ -269,6 +270,71 @@ void menuSetupKeyType(int btn){ menuOn = 0; } } +*/ + +//Select CW Key Type by KD8CEC +void menuSetupKeyType(int btn){ + int knob = 0; + int selectedKeyType = 0; + int moveStep = 0; + if (!btn && digitalRead(PTT) == HIGH){ + printLineF2(F("Change Key Type?")); + } + else { + printLineF2(F("Press PTT to set")); + delay_background(500, 0); + selectedKeyType = cwKeyType; + while(!btnDown() && digitalRead(PTT) == HIGH){ + + //Display Key Type + if (selectedKeyType == 0) + printLineF1(F("Straight")); + else if (selectedKeyType == 1) + printLineF1(F("IAMBICA")); + else if (selectedKeyType == 2) + printLineF1(F("IAMBICB")); + + knob = enc_read(); + + if (knob != 0) + { + moveStep += (knob > 0 ? 1 : -1); + if (selectedKeyType > 0 && moveStep < -3) { + selectedKeyType--; + moveStep = 0; + } + else if (selectedKeyType < 2 && moveStep > 3) { + selectedKeyType++; + moveStep = 0; + } + } + + Check_Cat(0); //To prevent disconnections + } + + //save the setting + if (digitalRead(PTT) == LOW){ + 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; + } + delay_background(2000, 0); + } + + printLine2ClearAndUpdate(); + menuOn = 0; + } +} //Analog pin monitoring with CW Key and function keys connected. //by KD8CEC @@ -734,7 +800,6 @@ void menuSetupCalibration(int btn){ menuOn = 0; } - void printCarrierFreq(unsigned long freq){ memset(c, 0, sizeof(c)); diff --git a/ubitx_20/ubitx_ui.ino b/ubitx_20/ubitx_ui.ino index f309fcc..85229db 100644 --- a/ubitx_20/ubitx_ui.ino +++ b/ubitx_20/ubitx_ui.ino @@ -116,6 +116,9 @@ void drawMeter(int8_t needle){ // 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); @@ -145,6 +148,9 @@ void printLineF(char linenmbr, const __FlashStringHelper *c) #define LCD_MAX_COLUMN 16 void printLineFromEEPRom(char linenmbr, char lcdColumn, byte eepromStartIndex, byte eepromEndIndex) { + if ((displayOption1 & 0x01) == 0x01) + linenmbr = (linenmbr == 0 ? 1 : 0); //Line Toggle + lcd.setCursor(lcdColumn, linenmbr); for (byte i = eepromStartIndex; i <= eepromEndIndex; i++) @@ -168,6 +174,12 @@ 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,""); @@ -179,6 +191,7 @@ void printLine2Clear(){ void printLine2ClearAndUpdate(){ printLine(0, ""); + line2DisplayStatus = 0; updateDisplay(); } @@ -251,18 +264,22 @@ void updateDisplay() { // 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,1); + lcd.setCursor(5,diplayVFOLine); lcd.write((uint8_t)0); } else if (isCWAutoMode == 2){ - lcd.setCursor(5,1); + lcd.setCursor(5,diplayVFOLine); lcd.write(0x7E); } else { - lcd.setCursor(5,1); + lcd.setCursor(5,diplayVFOLine); lcd.write(":"); } From a49d5e85b829b6f1165d498cf1a535b020a5e12f Mon Sep 17 00:00:00 2001 From: phdlee Date: Mon, 29 Jan 2018 22:49:30 +0900 Subject: [PATCH 047/173] line2 display sample1 --- ubitx_20/ubitx_20.ino | 5 +- ubitx_20/ubitx_idle.ino | 101 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 103 insertions(+), 3 deletions(-) diff --git a/ubitx_20/ubitx_20.ino b/ubitx_20/ubitx_20.ino index 690fb56..4311683 100644 --- a/ubitx_20/ubitx_20.ino +++ b/ubitx_20/ubitx_20.ino @@ -379,7 +379,6 @@ void saveBandFreqByIndex(unsigned long f, unsigned long mode, char bandIndex) { EEPROM.put(HAM_BAND_FREQS + 4 * bandIndex, (f & 0x3FFFFFFF) | (mode << 30) ); } - /* KD8CEC When using the basic delay of the Arduino, the program freezes. @@ -824,7 +823,7 @@ void initSettings(){ 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] = 7200; + hamBandRange[3][0] = 7000; hamBandRange[3][1] = 7300; //region 1 hamBandRange[4][0] = 10100; hamBandRange[4][1] = 10150; hamBandRange[5][0] = 14000; hamBandRange[5][1] = 14350; hamBandRange[6][0] = 18068; hamBandRange[6][1] = 18168; @@ -1082,7 +1081,7 @@ void loop(){ else doTuningWithThresHold(); - if (isCWAutoMode == 0 && beforeIdle_ProcessTime < millis() - 200) { + if (isCWAutoMode == 0 && beforeIdle_ProcessTime < millis() - 500) { idle_process(); beforeIdle_ProcessTime = millis(); } diff --git a/ubitx_20/ubitx_idle.ino b/ubitx_20/ubitx_idle.ino index dff2aad..39583e4 100644 --- a/ubitx_20/ubitx_idle.ino +++ b/ubitx_20/ubitx_idle.ino @@ -17,7 +17,103 @@ along with this program. If not, see . **************************************************************************/ +byte line2Buffer[17]; +//KD8CEC 200Hz ST +//L14.150 200Hz ST +//U14.150 +150khz +//Example Line2 Optinal Display +void updateLine2Buffer() +{ + unsigned long tmpFreq = 0; + + if (ritOn) + { + line2Buffer[0] = 'R'; + line2Buffer[1] = 'i'; + line2Buffer[2] = 't'; + line2Buffer[3] = 'T'; + line2Buffer[4] = 'X'; + line2Buffer[5] = ':'; + + //display frequency + tmpFreq = ritTxFrequency; + 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; + } + + if (vfoActive == VFO_B) + { + tmpFreq = vfoA; + //line2Buffer[0] = 'A'; + } + else + { + tmpFreq = vfoB; + //line2Buffer[0] = 'B'; + } + + //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] = ' '; + } + + line2Buffer[6] = 'k'; + + line2Buffer[7] = ' '; + + //Step + byte tmpStep = arTuneStep[tuneStepIndex -1]; + for (int i = 10; i >= 8; i--) { + if (tmpStep > 0) { + line2Buffer[i] = tmpStep % 10 + 0x30; + tmpStep /= 10; + } + else + line2Buffer[i] = ' '; + } + line2Buffer[11] = 'H'; + line2Buffer[12] = 'z'; + + line2Buffer[13] = ' '; + //if ( + //Check CW Key cwKeyType = 0; //0: straight, 1 : iambica, 2: iambicb + 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'; + } +} void idle_process() { @@ -25,6 +121,11 @@ void idle_process() if (menuOn == 0) { //if line2DisplayStatus == 0 <-- this condition is clear Line, you can display any message + //if (line2DisplayStatus == 0) { + updateLine2Buffer(); + printLine2(line2Buffer); + line2DisplayStatus = 2; + //} } } From 4f634a8277e0d7e803732210d3fe8b1a5b27f03c Mon Sep 17 00:00:00 2001 From: phdlee Date: Mon, 29 Jan 2018 23:02:46 +0900 Subject: [PATCH 048/173] line2 display example1.1 --- ubitx_20/ubitx_idle.ino | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/ubitx_20/ubitx_idle.ino b/ubitx_20/ubitx_idle.ino index 39583e4..7807f7a 100644 --- a/ubitx_20/ubitx_idle.ino +++ b/ubitx_20/ubitx_idle.ino @@ -17,7 +17,7 @@ along with this program. If not, see . **************************************************************************/ -byte line2Buffer[17]; +byte line2Buffer[16]; //KD8CEC 200Hz ST //L14.150 200Hz ST //U14.150 +150khz @@ -121,11 +121,11 @@ void idle_process() if (menuOn == 0) { //if line2DisplayStatus == 0 <-- this condition is clear Line, you can display any message - //if (line2DisplayStatus == 0) { + if (line2DisplayStatus == 0) { updateLine2Buffer(); printLine2(line2Buffer); line2DisplayStatus = 2; - //} + } } } From 3a306429ea01304aceb11ade6c7b33514e5a2621 Mon Sep 17 00:00:00 2001 From: phdlee Date: Tue, 30 Jan 2018 00:00:43 +0900 Subject: [PATCH 049/173] display exaam (scroll freq) #2 --- ubitx_20/ubitx_idle.ino | 40 +++++++++++++++++++++++++++++++++++++--- 1 file changed, 37 insertions(+), 3 deletions(-) diff --git a/ubitx_20/ubitx_idle.ino b/ubitx_20/ubitx_idle.ino index 7807f7a..f85ba52 100644 --- a/ubitx_20/ubitx_idle.ino +++ b/ubitx_20/ubitx_idle.ino @@ -21,7 +21,7 @@ byte line2Buffer[16]; //KD8CEC 200Hz ST //L14.150 200Hz ST //U14.150 +150khz - +int freqScrollPosition = 0; //Example Line2 Optinal Display void updateLine2Buffer() { @@ -64,6 +64,7 @@ void updateLine2Buffer() //line2Buffer[0] = 'B'; } + // EXAMPLE 1 & 2 //U14.150.100 //display frequency for (int i = 9; i >= 0; i--) { @@ -78,8 +79,41 @@ void updateLine2Buffer() line2Buffer[i] = ' '; } - line2Buffer[6] = 'k'; + //EXAMPLE #1 + if ((displayOption1 & 0x04) == 0x00) + line2Buffer[6] = 'k'; + else + { + //example #2 + if (freqScrollPosition++ > 18) + { + line2Buffer[6] = 'k'; + if (freqScrollPosition > 25) + freqScrollPosition = -1; + } + else + { + 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] = ' '; + } + } + } line2Buffer[7] = ' '; //Step @@ -121,7 +155,7 @@ void idle_process() if (menuOn == 0) { //if line2DisplayStatus == 0 <-- this condition is clear Line, you can display any message - if (line2DisplayStatus == 0) { + if (line2DisplayStatus == 0 || (((displayOption1 & 0x04) == 0x04) && line2DisplayStatus == 2)) { updateLine2Buffer(); printLine2(line2Buffer); line2DisplayStatus = 2; From 98c26730c6b64bc2b7b2991f5b9f41bc5a492c21 Mon Sep 17 00:00:00 2001 From: phdlee Date: Tue, 30 Jan 2018 12:13:52 +0900 Subject: [PATCH 050/173] display test and split TX/RX added --- ubitx_20/cat_libs.ino | 2 +- ubitx_20/ubitx_20.ino | 34 +++++++++++++++++++++++++++++++--- ubitx_20/ubitx_idle.ino | 2 +- ubitx_20/ubitx_menu.ino | 32 ++++++++++++++++++++++++++++++-- 4 files changed, 63 insertions(+), 7 deletions(-) diff --git a/ubitx_20/cat_libs.ino b/ubitx_20/cat_libs.ino index dec5998..b82f639 100644 --- a/ubitx_20/cat_libs.ino +++ b/ubitx_20/cat_libs.ino @@ -384,7 +384,7 @@ void ReadEEPRom_FT817(byte fromType) //7A 6 ? ? //7A 7 SPL On/Off 0 = Off, 1 = On - CAT_BUFF[0] = (isSplitOn ? 0xFF : 0x7F); + CAT_BUFF[0] = (splitOn ? 0xFF : 0x7F); break; case 0xB3 : // CAT_BUFF[0] = 0x00; diff --git a/ubitx_20/ubitx_20.ino b/ubitx_20/ubitx_20.ino index 4311683..cc5119a 100644 --- a/ubitx_20/ubitx_20.ino +++ b/ubitx_20/ubitx_20.ino @@ -249,7 +249,6 @@ byte saveIntervalSec = 10; //second unsigned long saveCheckTime = 0; unsigned long saveCheckFreq = 0; -bool isSplitOn = false; byte cwDelayTime = 60; byte delayBeforeCWStartTime = 50; @@ -298,7 +297,7 @@ byte userCallsignLength = 0; //7 : display callsign at system startup, 6~0 : */ 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, (NOT IMPLEMENTED YET) +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 //frequency when it crosses the frequency border of 10 MHz @@ -507,6 +506,21 @@ void startTx(byte txMode, byte isDisplayUpdate){ ritRxFrequency = frequency; setFrequency(ritTxFrequency); } + else if (splitOn == 1) { + if (vfoActive == VFO_B) { + vfoActive = VFO_A; + frequency = vfoA; + byteToMode(vfoA_mode); + } + else if (vfoActive == VFO_A){ + vfoActive = VFO_B; + frequency = vfoB; + byteToMode(vfoB_mode); + } + + setFrequency(frequency); + } //end of else + if (txMode == TX_CW){ //turn off the second local oscillator and the bfo @@ -535,6 +549,20 @@ void stopTx(){ if (ritOn) setFrequency(ritRxFrequency); + else if (splitOn == 1) { + //vfo Change + if (vfoActive == VFO_B){ + vfoActive = VFO_A; + frequency = vfoA; + byteToMode(vfoA_mode); + } + else if (vfoActive == VFO_A){ + vfoActive = VFO_B; + frequency = vfoB; + byteToMode(vfoB_mode); + } + setFrequency(frequency); + } //end of else else setFrequency(frequency); @@ -992,7 +1020,7 @@ void setup() //Serial.begin(9600); lcd.begin(16, 2); - printLineF(1, F("CECBT v0.31")); + printLineF(1, F("CECBT v0.32")); Init_Cat(38400, SERIAL_8N1); initMeter(); //not used in this build diff --git a/ubitx_20/ubitx_idle.ino b/ubitx_20/ubitx_idle.ino index f85ba52..55f2fdf 100644 --- a/ubitx_20/ubitx_idle.ino +++ b/ubitx_20/ubitx_idle.ino @@ -98,7 +98,7 @@ void updateLine2Buffer() if (freqScrollPosition < 7) { - for (int i = 11; i > 0; i--) + for (int i = 11; i >= 0; i--) if (i - (7 - freqScrollPosition) >= 0) line2Buffer[i] = line2Buffer[i - (7 - freqScrollPosition)]; else diff --git a/ubitx_20/ubitx_menu.ino b/ubitx_20/ubitx_menu.ino index 5fa1e5a..e7f5670 100644 --- a/ubitx_20/ubitx_menu.ino +++ b/ubitx_20/ubitx_menu.ino @@ -235,6 +235,32 @@ void menuSidebandToggle(int btn){ } } +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("Split Off!")); + } + else { + splitOn = 1; + if (ritOn == 1) + ritOn = 0; + printLineF2(F("Split On!")); + } + delay_background(500, 0); + printLine2ClearAndUpdate(); + menuOn = 0; + } +} + + + /* //Select CW Key Type by KD8CEC void menuSetupKeyType(int btn){ @@ -1025,7 +1051,7 @@ void doMenu(){ btnState = btnDown(); if (i > 0){ - if (modeCalibrate && select + i < 170) + if (modeCalibrate && select + i < 180) select += i; if (!modeCalibrate && select + i < 80) select += i; @@ -1067,8 +1093,10 @@ void doMenu(){ else if (select < 150 && modeCalibrate) menuADCMonitor(btnState); else if (select < 160 && modeCalibrate) - menuTxOnOff(btnState, 0x01); //TX OFF / ON + menuSplitOnOff(btnState); //TX OFF / ON else if (select < 170 && modeCalibrate) + menuTxOnOff(btnState, 0x01); //TX OFF / ON + else if (select < 180 && modeCalibrate) menuExit(btnState); Check_Cat(0); //To prevent disconnections From 0d9ec08bd7120f5d75ba3c7626d41753e65d0893 Mon Sep 17 00:00:00 2001 From: phdlee Date: Tue, 30 Jan 2018 13:20:52 +0900 Subject: [PATCH 051/173] Added CWL, CWU Mode, need test --- ubitx_20/cat_libs.ino | 44 ++++- ubitx_20/ubitx_20.ino | 77 +++++++-- ubitx_20/ubitx_factory_alignment.ino | 3 + ubitx_20/ubitx_menu.ino | 250 ++++++++++++++++++++++++--- ubitx_20/ubitx_si5351.ino | 6 +- ubitx_20/ubitx_ui.ino | 17 +- 6 files changed, 340 insertions(+), 57 deletions(-) diff --git a/ubitx_20/cat_libs.ino b/ubitx_20/cat_libs.ino index b82f639..85a4111 100644 --- a/ubitx_20/cat_libs.ino +++ b/ubitx_20/cat_libs.ino @@ -130,10 +130,21 @@ void CatGetFreqMode(unsigned long freq, byte fromType) } //Mode Check - if (isUSB) - CAT_BUFF[4] = CAT_MODE_USB; + 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_LSB; + { + CAT_BUFF[4] = CAT_MODE_CW; + } SendCatData(5); } @@ -198,12 +209,18 @@ void CatSetMode(byte tmpMode, byte fromType) if (!inTx) { - if (tmpMode == CAT_MODE_USB) + if (tmpMode == CAT_MODE_CW) { + cwMode = 1; + } + else if (tmpMode == CAT_MODE_USB) + { + cwMode = 0; isUSB = true; } else { + cwMode = 0; isUSB = false; } @@ -358,10 +375,21 @@ void ReadEEPRom_FT817(byte fromType) CAT_BUFF[1] = 0xB2; break; case 0x69 : //FM Mic (#29) Contains 0-100 (decimal) as displayed case 0x78 : - if (isUSB) - CAT_BUFF[0] = CAT_MODE_USB; - else - CAT_BUFF[0] = CAT_MODE_LSB; + 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; diff --git a/ubitx_20/ubitx_20.ino b/ubitx_20/ubitx_20.ino index cc5119a..e927cea 100644 --- a/ubitx_20/ubitx_20.ino +++ b/ubitx_20/ubitx_20.ino @@ -151,6 +151,7 @@ int count = 0; //to generally count ticks, loops, etc #define CW_SPEED 28 //AT328 has 1KBytes EEPROM +#define CW_CAL 252 #define VFO_A_MODE 256 #define VFO_B_MODE 257 #define CW_DELAY 258 @@ -232,7 +233,7 @@ int count = 0; //to generally count ticks, loops, etc 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; +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 @@ -300,6 +301,10 @@ char inTx = 0; //it is set to 1 if in transmit mode (whatever the 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 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) + //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 @@ -363,8 +368,10 @@ void setNextHamBandFreq(unsigned long f, char moveDirection) EEPROM.get(HAM_BAND_FREQS + 4 * findedIndex, resultFreq); - loadMode = (byte)(resultFreq >> 30); - resultFreq = resultFreq & 0x3FFFFFFF; + //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; @@ -375,7 +382,8 @@ void setNextHamBandFreq(unsigned long f, char moveDirection) 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 & 0x3FFFFFFF) | (mode << 30) ); + EEPROM.put(HAM_BAND_FREQS + 4 * bandIndex, (f & 0x1FFFFFFF) | (mode << 29) ); } /* @@ -471,13 +479,27 @@ void setFrequency(unsigned long f){ setTXFilters(f); - if (isUSB){ - si5351bx_setfreq(2, SECOND_OSC_USB - usbCarrier + f); - si5351bx_setfreq(1, SECOND_OSC_USB); + if (cwMode == 0) + { + if (isUSB){ + si5351bx_setfreq(2, SECOND_OSC_USB - usbCarrier + f); + si5351bx_setfreq(1, SECOND_OSC_USB); + } + else{ + si5351bx_setfreq(2, SECOND_OSC_LSB + usbCarrier + f); + si5351bx_setfreq(1, SECOND_OSC_LSB); + } } - else{ - si5351bx_setfreq(2, SECOND_OSC_LSB + usbCarrier + f); - si5351bx_setfreq(1, SECOND_OSC_LSB); + else + { + if (cwMode == 1){ //CWL + si5351bx_setfreq(2, SECOND_OSC_LSB + cwmCarrier + f); + si5351bx_setfreq(1, SECOND_OSC_LSB); + } + else{ //CWU + si5351bx_setfreq(2, SECOND_OSC_USB - cwmCarrier + f); + si5351bx_setfreq(1, SECOND_OSC_USB); + } } frequency = f; @@ -530,10 +552,22 @@ void startTx(byte txMode, byte isDisplayUpdate){ //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 (isUSB) - si5351bx_setfreq(2, frequency + sideTone); - else - si5351bx_setfreq(2, 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 @@ -545,7 +579,11 @@ void stopTx(){ inTx = 0; digitalWrite(TX_RX, 0); //turn off the tx - si5351bx_setfreq(0, usbCarrier); //set back the carrier oscillator anyway, cw tx switches it off + + 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 if (ritOn) setFrequency(ritRxFrequency); @@ -788,6 +826,7 @@ void initSettings(){ if (EEPROM.read(VERSION_ADDRESS) != VERSION_NUM) EEPROM.write(VERSION_ADDRESS, VERSION_NUM); + 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 @@ -936,6 +975,9 @@ void initSettings(){ //original code with modified by kd8cec if (usbCarrier > 12010000l || usbCarrier < 11990000l) usbCarrier = 11995000l; + + if (cwmCarrier > 12010000l || cwmCarrier < 11990000l) + cwmCarrier = 11995000l; if (vfoA > 35000000l || 3500000l > vfoA) { vfoA = 7150000l; @@ -1020,7 +1062,7 @@ void setup() //Serial.begin(9600); lcd.begin(16, 2); - printLineF(1, F("CECBT v0.32")); + printLineF(1, F("CECBT v0.33")); Init_Cat(38400, SERIAL_8N1); initMeter(); //not used in this build @@ -1038,11 +1080,12 @@ void setup() } initPorts(); + + byteToMode(vfoA_mode); initOscillators(); frequency = vfoA; saveCheckFreq = frequency; //for auto save frequency - byteToMode(vfoA_mode); setFrequency(vfoA); updateDisplay(); diff --git a/ubitx_20/ubitx_factory_alignment.ino b/ubitx_20/ubitx_factory_alignment.ino index b73a1c0..8178a20 100644 --- a/ubitx_20/ubitx_factory_alignment.ino +++ b/ubitx_20/ubitx_factory_alignment.ino @@ -37,6 +37,7 @@ void factory_alignment(){ printLine2("#3:Test 3.5MHz"); + cwMode = 0; isUSB = false; setFrequency(3500000l); updateDisplay(); @@ -59,6 +60,7 @@ void factory_alignment(){ btnWaitForClick(); printLine2("#5:Test 14MHz"); + cwMode = 0; isUSB = true; setFrequency(14000000l); updateDisplay(); @@ -80,6 +82,7 @@ void factory_alignment(){ printLine2("Alignment done"); delay(1000); + cwMode = 0; isUSB = false; setFrequency(7150000l); updateDisplay(); diff --git a/ubitx_20/ubitx_menu.ino b/ubitx_20/ubitx_menu.ino index e7f5670..29cc43e 100644 --- a/ubitx_20/ubitx_menu.ino +++ b/ubitx_20/ubitx_menu.ino @@ -119,30 +119,56 @@ void menuBand(int btn){ } //Convert Mode, Number by KD8CEC -//0: default, 1:not use, 2:LSB, 3:USB, 4:CW, 5:AM, 6:FM +//0: default, 1:not use, 2:LSB, 3:USB, 4:CWL, 5:CWU, 6:FM byte modeToByte(){ - if (isUSB) - return 3; + if (cwMode == 0) + { + if (isUSB) + return 3; + else + return 2; + } + else if (cwMode == 1) + { + return 4; + } else - return 2; + { + return 5; + } } //Convert Number to Mode by KD8CEC void byteToMode(byte modeValue){ - if (modeValue == 3) - isUSB = 1; + if (modeValue == 4) + cwMode = 1; + else if (modeValue == 5) + cwMode = 2; else - isUSB = 0; + { + cwMode = 0; + if (modeValue == 3) + isUSB = 1; + else + isUSB = 0; + } } //Convert Number to Mode by KD8CEC void byteWithFreqToMode(byte modeValue){ - if (modeValue == 3) - isUSB = 1; - else if (modeValue == 0) //Not Set - isUSB = (frequency > 10000000l) ? true : false; - else - isUSB = 0; + if (modeValue == 4) + cwMode = 1; + else if (modeValue == 5) + cwMode = 2; + else { + cwMode = 0; + if (modeValue == 3) + isUSB = 1; + else if (modeValue == 0) //Not Set + isUSB = (frequency > 10000000l) ? true : false; + else + isUSB = 0; + } } //VFO Toggle and save VFO Information, modified by KD8CEC @@ -212,6 +238,7 @@ void menuRitToggle(int btn){ } } +/* void menuSidebandToggle(int btn){ if (!btn){ if (isUSB == true) @@ -220,6 +247,7 @@ void menuSidebandToggle(int btn){ printLineF2(F("Select USB?")); } else { + cwMode = 0; if (isUSB == true){ isUSB = false; printLineF2(F("LSB Selected")); @@ -234,6 +262,108 @@ void menuSidebandToggle(int btn){ menuOn = 0; } } +*/ +void menuSelectMode(int btn){ + int knob = 0; + int selectModeType = 0; + int beforeMode = 0; + int moveStep = 0; + + if (!btn){ + printLineF2(F("Select Mode?")); + } + else { + delay_background(500, 0); + + //LSB, USB, CWL, CWU + 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; + + while(!btnDown() && digitalRead(PTT) == HIGH){ + //Display Mode Name + if (selectModeType == 0) + printLineF1(F("LSB")); + else if (selectModeType == 1) + printLineF1(F("USB")); + else if (selectModeType == 2) + printLineF1(F("CWL")); + else if (selectModeType == 3) + printLineF1(F("CWU")); + + knob = enc_read(); + + if (knob != 0) + { + moveStep += (knob > 0 ? 1 : -1); + if (moveStep < -3) { + if (selectModeType > 0) + selectModeType--; + + moveStep = 0; + } + else if (moveStep > 3) { + if (selectModeType < 3) + selectModeType++; + + moveStep = 0; + } + } + + Check_Cat(0); //To prevent disconnections + } + + if (beforeMode != selectModeType) { + printLineF1(F("Changed Mode")); + + if (selectModeType == 0) { + cwMode = 0; isUSB = 0; + } + else if (selectModeType == 1) { + cwMode = 0; isUSB = 1; + } + else if (selectModeType == 2) { + cwMode = 1; + } + else if (selectModeType == 3) { + cwMode = 2; + } + + //Save Frequency & Mode Information + if (vfoActive == VFO_A) + { + vfoA = frequency; + vfoA_mode = modeToByte(); + storeFrequencyAndMode(1); + } + else + { + vfoB = frequency; + vfoB_mode = modeToByte(); + storeFrequencyAndMode(2); + } + } + + 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); + delay_background(500, 0); + printLine2ClearAndUpdate(); + menuOn = 0; + } +} + + void menuSplitOnOff(int btn){ if (!btn){ @@ -325,12 +455,14 @@ void menuSetupKeyType(int btn){ if (knob != 0) { moveStep += (knob > 0 ? 1 : -1); - if (selectedKeyType > 0 && moveStep < -3) { - selectedKeyType--; + if (moveStep < -3) { + if (selectedKeyType > 0) + selectedKeyType--; moveStep = 0; } - else if (selectedKeyType < 2 && moveStep > 3) { - selectedKeyType++; + else if (moveStep > 3) { + if (selectedKeyType < 2) + selectedKeyType++; moveStep = 0; } } @@ -709,6 +841,7 @@ void factoryCalibration(int btn){ calibration = 0; + cwMode = 0; isUSB = true; //turn off the second local oscillator and the bfo @@ -837,7 +970,7 @@ void printCarrierFreq(unsigned long freq){ strcat(c, "."); strncat(c, &b[2], 3); strcat(c, "."); - strncat(c, &b[5], 1); + strncat(c, &b[5], 3); printLine2(c); } @@ -889,12 +1022,71 @@ void menuSetupCarrier(int btn){ else usbCarrier = prevCarrier; - si5351bx_setfreq(0, usbCarrier); + //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; } +//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); + + Check_Cat(0); //To prevent disconnections + delay(100); + } + + //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); + printLine2ClearAndUpdate(); + menuOn = 0; +} //Modified by KD8CEC void menuSetupCwTone(int btn){ int knob = 0; @@ -1051,7 +1243,7 @@ void doMenu(){ btnState = btnDown(); if (i > 0){ - if (modeCalibrate && select + i < 180) + if (modeCalibrate && select + i < 190) select += i; if (!modeCalibrate && select + i < 80) select += i; @@ -1069,7 +1261,7 @@ void doMenu(){ else if (select < 30) menuVfoToggle(btnState, 1); else if (select < 40) - menuSidebandToggle(btnState); + menuSelectMode(btnState); else if (select < 50) menuCWSpeed(btnState); else if (select < 60) @@ -1083,20 +1275,22 @@ void doMenu(){ else if (select < 100 && modeCalibrate) menuSetupCarrier(btnState); //lsb else if (select < 110 && modeCalibrate) - menuSetupCwTone(btnState); + menuSetupCWCarrier(btnState); //lsb else if (select < 120 && modeCalibrate) - menuSetupCwDelay(btnState); + menuSetupCwTone(btnState); else if (select < 130 && modeCalibrate) - menuSetupTXCWInterval(btnState); + menuSetupCwDelay(btnState); else if (select < 140 && modeCalibrate) - menuSetupKeyType(btnState); + menuSetupTXCWInterval(btnState); else if (select < 150 && modeCalibrate) - menuADCMonitor(btnState); + menuSetupKeyType(btnState); else if (select < 160 && modeCalibrate) - menuSplitOnOff(btnState); //TX OFF / ON + menuADCMonitor(btnState); else if (select < 170 && modeCalibrate) - menuTxOnOff(btnState, 0x01); //TX OFF / ON + menuSplitOnOff(btnState); //SplitOn / off else if (select < 180 && modeCalibrate) + menuTxOnOff(btnState, 0x01); //TX OFF / ON + else if (select < 190 && modeCalibrate) menuExit(btnState); Check_Cat(0); //To prevent disconnections diff --git a/ubitx_20/ubitx_si5351.ino b/ubitx_20/ubitx_si5351.ino index b437ad9..9e79e51 100644 --- a/ubitx_20/ubitx_si5351.ino +++ b/ubitx_20/ubitx_si5351.ino @@ -109,7 +109,11 @@ void initOscillators(){ //initialize the SI5351 si5351bx_init(); si5351bx_vcoa = (SI5351BX_XTAL * SI5351BX_MSA) + calibration; // apply the calibration correction factor - si5351bx_setfreq(0, usbCarrier); + + if (cwMode == 0) + si5351bx_setfreq(0, usbCarrier); + else + si5351bx_setfreq(0, cwmCarrier); } diff --git a/ubitx_20/ubitx_ui.ino b/ubitx_20/ubitx_ui.ino index 85229db..71d35cd 100644 --- a/ubitx_20/ubitx_ui.ino +++ b/ubitx_20/ubitx_ui.ino @@ -234,10 +234,21 @@ void updateDisplay() { if (ritOn) strcpy(c, "RIT "); else { - if (isUSB) - strcpy(c, "USB "); + if (cwMode == 0) + { + if (isUSB) + strcpy(c, "USB "); + else + strcpy(c, "LSB "); + } + else if (cwMode == 1) + { + strcpy(c, "CWL "); + } else - strcpy(c, "LSB "); + { + strcpy(c, "CWU "); + } } if (vfoActive == VFO_A) // VFO A is active strcat(c, "A:"); From 5eca64d2a9c6f7f9d35840133be3fbf21cb8aa01 Mon Sep 17 00:00:00 2001 From: phdlee Date: Tue, 30 Jan 2018 17:44:15 +0900 Subject: [PATCH 052/173] vfo changed buf fixed, added BFO feature with Mike --- ubitx_20/ubitx_20.ino | 36 ++++-- ubitx_20/ubitx_idle.ino | 256 +++++++++++++++++++++++--------------- ubitx_20/ubitx_menu.ino | 89 ++++++++----- ubitx_20/ubitx_si5351.ino | 4 +- ubitx_20/ubitx_ui.ino | 70 +++++++---- 5 files changed, 296 insertions(+), 159 deletions(-) diff --git a/ubitx_20/ubitx_20.ino b/ubitx_20/ubitx_20.ino index e927cea..2ce915d 100644 --- a/ubitx_20/ubitx_20.ino +++ b/ubitx_20/ubitx_20.ino @@ -315,6 +315,10 @@ boolean modeCalibrate = false;//this mode of menus shows extended menus to calib unsigned long beforeIdle_ProcessTime = 0; //for check Idle time byte line2DisplayStatus = 0; //0:Clear, 1 : menu, 1: DisplayFrom Idle, +char lcdMeter[17]; + +byte isIFShift = 0; //1 = ifShift, 2 extend +long ifShiftValue = 0; // /** * Below are the basic functions that control the uBitx. Understanding the functions before @@ -482,22 +486,22 @@ void setFrequency(unsigned long f){ if (cwMode == 0) { if (isUSB){ - si5351bx_setfreq(2, SECOND_OSC_USB - usbCarrier + f); + si5351bx_setfreq(2, SECOND_OSC_USB - usbCarrier + f + (isIFShift ? ifShiftValue : 0)); si5351bx_setfreq(1, SECOND_OSC_USB); } else{ - si5351bx_setfreq(2, SECOND_OSC_LSB + usbCarrier + f); + si5351bx_setfreq(2, SECOND_OSC_LSB + usbCarrier + f + (isIFShift ? ifShiftValue : 0)); si5351bx_setfreq(1, SECOND_OSC_LSB); } } else { if (cwMode == 1){ //CWL - si5351bx_setfreq(2, SECOND_OSC_LSB + cwmCarrier + f); + si5351bx_setfreq(2, SECOND_OSC_LSB + cwmCarrier + f + (isIFShift ? ifShiftValue : 0)); si5351bx_setfreq(1, SECOND_OSC_LSB); } else{ //CWU - si5351bx_setfreq(2, SECOND_OSC_USB - cwmCarrier + f); + si5351bx_setfreq(2, SECOND_OSC_USB - cwmCarrier + f + (isIFShift ? ifShiftValue : 0)); si5351bx_setfreq(1, SECOND_OSC_USB); } } @@ -581,9 +585,9 @@ void stopTx(){ digitalWrite(TX_RX, 0); //turn off the tx if (cwMode == 0) - si5351bx_setfreq(0, usbCarrier); //set back the carrier oscillator anyway, cw tx switches it off + si5351bx_setfreq(0, usbCarrier + (isIFShift ? ifShiftValue : 0)); //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 + si5351bx_setfreq(0, cwmCarrier + (isIFShift ? ifShiftValue : 0)); //set back the carrier oscillator anyway, cw tx switches it off if (ritOn) setFrequency(ritRxFrequency); @@ -751,6 +755,22 @@ void doRIT(){ } } +void doIFShift(){ + int knob = enc_read(); + unsigned long old_freq = frequency; + + if (knob != 0) + { + if (knob < 0) + ifShiftValue -= 1l; + else if (knob > 0) + ifShiftValue += 1; + + updateLine2Buffer(1); + setFrequency(frequency); + } +} + /** save Frequency and mode to eeprom */ @@ -1149,10 +1169,12 @@ void loop(){ if (!inTx){ if (ritOn) doRIT(); + else if (isIFShift) + doIFShift(); else doTuningWithThresHold(); - if (isCWAutoMode == 0 && beforeIdle_ProcessTime < millis() - 500) { + if (isCWAutoMode == 0 && beforeIdle_ProcessTime < millis() - 250) { idle_process(); beforeIdle_ProcessTime = millis(); } diff --git a/ubitx_20/ubitx_idle.ino b/ubitx_20/ubitx_idle.ino index 55f2fdf..b96e567 100644 --- a/ubitx_20/ubitx_idle.ino +++ b/ubitx_20/ubitx_idle.ino @@ -23,24 +23,55 @@ byte line2Buffer[16]; //U14.150 +150khz int freqScrollPosition = 0; //Example Line2 Optinal Display -void updateLine2Buffer() +//immediate execution, not call by scheulder +void updateLine2Buffer(char isDirectCall) { unsigned long tmpFreq = 0; - - if (ritOn) + if (isDirectCall == 0) { - line2Buffer[0] = 'R'; - line2Buffer[1] = 'i'; - line2Buffer[2] = 't'; - line2Buffer[3] = 'T'; - line2Buffer[4] = 'X'; - line2Buffer[5] = ':'; - + if (ritOn) + { + line2Buffer[0] = 'R'; + line2Buffer[1] = 'i'; + line2Buffer[2] = 't'; + line2Buffer[3] = 'T'; + line2Buffer[4] = 'X'; + line2Buffer[5] = ':'; + + //display frequency + tmpFreq = ritTxFrequency; + 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; + } + + if (vfoActive == VFO_B) + { + tmpFreq = vfoA; + //line2Buffer[0] = 'A'; + } + else + { + tmpFreq = vfoB; + //line2Buffer[0] = 'B'; + } + + // EXAMPLE 1 & 2 + //U14.150.100 //display frequency - tmpFreq = ritTxFrequency; - for (int i = 15; i >= 6; i--) { + for (int i = 9; i >= 0; i--) { if (tmpFreq > 0) { - if (i == 12 || i == 8) line2Buffer[i] = '.'; + if (i == 2 || i == 6) line2Buffer[i] = '.'; else { line2Buffer[i] = tmpFreq % 10 + 0x30; tmpFreq /= 10; @@ -49,106 +80,124 @@ void updateLine2Buffer() else line2Buffer[i] = ' '; } - - return; - } - if (vfoActive == VFO_B) - { - tmpFreq = vfoA; - //line2Buffer[0] = 'A'; - } - else - { - tmpFreq = vfoB; - //line2Buffer[0] = 'B'; - } - - // 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) - line2Buffer[6] = 'k'; - else - { - //example #2 - if (freqScrollPosition++ > 18) - { + //EXAMPLE #1 + if ((displayOption1 & 0x04) == 0x00) line2Buffer[6] = 'k'; - if (freqScrollPosition > 25) - freqScrollPosition = -1; - } else { - line2Buffer[10] = 'H'; - line2Buffer[11] = 'z'; - - if (freqScrollPosition < 7) + //example #2 + if (freqScrollPosition++ > 18) { - for (int i = 11; i >= 0; i--) - if (i - (7 - freqScrollPosition) >= 0) - line2Buffer[i] = line2Buffer[i - (7 - freqScrollPosition)]; - else - line2Buffer[i] = ' '; + line2Buffer[6] = 'k'; + if (freqScrollPosition > 25) + freqScrollPosition = -1; } else { - for (int i = 0; i < 11; i++) - if (i + (freqScrollPosition - 7) <= 11) - line2Buffer[i] = line2Buffer[i + (freqScrollPosition - 7)]; - else - line2Buffer[i] = ' '; + 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] = ' '; + } } } - } - line2Buffer[7] = ' '; + line2Buffer[7] = ' '; + } //check direct call by encoder - //Step - byte tmpStep = arTuneStep[tuneStepIndex -1]; - for (int i = 10; i >= 8; i--) { - if (tmpStep > 0) { - line2Buffer[i] = tmpStep % 10 + 0x30; - tmpStep /= 10; - } - else - line2Buffer[i] = ' '; - } - line2Buffer[11] = 'H'; - line2Buffer[12] = 'z'; + + if (isIFShift) + { + //IFShift Offset Value + line2Buffer[8] = 'I'; + line2Buffer[9] = 'F'; + + line2Buffer[10] = ifShiftValue >= 0 ? '+' : 0; + line2Buffer[11] = 0; + line2Buffer[12] = ' '; + line2Buffer[13] = ' '; + line2Buffer[14] = ' '; + line2Buffer[15] = ' '; + + //11, 12, 13, 14, 15 + memset(b, 0, sizeof(b)); + ltoa(ifShiftValue, b, DEC); + strncat(line2Buffer, b, 5); - line2Buffer[13] = ' '; - //if ( - //Check CW Key cwKeyType = 0; //0: straight, 1 : iambica, 2: iambicb - if (cwKeyType == 0) - { - line2Buffer[14] = 'S'; - line2Buffer[15] = 'T'; - } - else if (cwKeyType == 1) - { - line2Buffer[14] = 'I'; - line2Buffer[15] = 'A'; + if (isDirectCall == 1) //if call by encoder (not scheduler), immediate print value + printLine2(line2Buffer); } else { - line2Buffer[14] = 'I'; - line2Buffer[15] = 'B'; + if (isDirectCall != 0) + return; + + //Step + byte tmpStep = arTuneStep[tuneStepIndex -1]; + for (int i = 10; i >= 8; i--) { + if (tmpStep > 0) { + line2Buffer[i] = tmpStep % 10 + 0x30; + tmpStep /= 10; + } + else + line2Buffer[i] = ' '; + } + line2Buffer[11] = 'H'; + line2Buffer[12] = 'z'; + + line2Buffer[13] = ' '; + //if ( + //Check CW Key cwKeyType = 0; //0: straight, 1 : iambica, 2: iambicb + 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) +{ + drawMeter(meterValue); //call original source code + int lineNumber = 0; + if ((displayOption1 & 0x01) == 0x01) + lineNumber = 1; + + lcd.setCursor(drawPosition, lineNumber); + + for (int i = 0; i < 6; i++) //meter 5 + +db 1 = 6 + lcd.write(lcdMeter[i]); + +} + +byte testValue = 0; +char checkCount = 0; void idle_process() { //space for user graphic display @@ -156,9 +205,20 @@ void idle_process() { //if line2DisplayStatus == 0 <-- this condition is clear Line, you can display any message if (line2DisplayStatus == 0 || (((displayOption1 & 0x04) == 0x04) && line2DisplayStatus == 2)) { - updateLine2Buffer(); - printLine2(line2Buffer); - line2DisplayStatus = 2; + if (checkCount++ > 1) + { + updateLine2Buffer(0); //call by scheduler + printLine2(line2Buffer); + line2DisplayStatus = 2; + checkCount = 0; + } + + //EX for Meters + /* + DisplayMeter(0, testValue++, 7); + if (testValue > 30) + testValue = 0; + */ } } } diff --git a/ubitx_20/ubitx_menu.ino b/ubitx_20/ubitx_menu.ino index 29cc43e..06823ca 100644 --- a/ubitx_20/ubitx_menu.ino +++ b/ubitx_20/ubitx_menu.ino @@ -205,10 +205,11 @@ void menuVfoToggle(int btn, char isUseDelayTime) } ritDisable(); + setFrequency(frequency); if (isUseDelayTime == 1) //Found Issue in wsjt-x Linux 32bit delay_background(500, 0); - + printLine2ClearAndUpdate(); //exit the menu menuOn = 0; @@ -238,6 +239,29 @@ void menuRitToggle(int btn){ } } +void menuIFSToggle(int btn){ + if (!btn){ + if (isIFShift == 1) + printLineF2(F("IF Shift:On, Off?")); + else + printLineF2(F("IF Shift:Off, On?")); + } + else { + if (isIFShift == 0){ + printLineF2(F("IF Shift is ON")); + isIFShift = 1; + } + else{ + printLineF2(F("IF Shift is OFF")); + isIFShift = 0; + } + menuOn = 0; + delay_background(500, 0); + printLine2ClearAndUpdate(); + } +} + + /* void menuSidebandToggle(int btn){ if (!btn){ @@ -286,9 +310,10 @@ void menuSelectMode(int btn){ selectModeType = 3; beforeMode = selectModeType; - + while(!btnDown() && digitalRead(PTT) == HIGH){ //Display Mode Name + printLineF1(F("LSB USB CWL CWU")); if (selectModeType == 0) printLineF1(F("LSB")); else if (selectModeType == 1) @@ -1190,7 +1215,7 @@ void doMenu(){ //ADJUST TUNE STEP if (btnDownTimeCount > (PRESS_ADJUST_TUNE / 50)) { - printLineF1(F("Press Key to set")); + printLineF1(F("Press to set step")); isNeedDisplay = 1; //check to need display for display current value while (digitalRead(PTT) == HIGH && !btnDown()) @@ -1243,9 +1268,9 @@ void doMenu(){ btnState = btnDown(); if (i > 0){ - if (modeCalibrate && select + i < 190) + if (modeCalibrate && select + i < 200) select += i; - if (!modeCalibrate && select + i < 80) + if (!modeCalibrate && select + i < 100) select += i; } //if (i < 0 && select - i >= 0) @@ -1257,40 +1282,42 @@ void doMenu(){ else if (select < 10) menuBand(btnState); else if (select < 20) - menuRitToggle(btnState); - else if (select < 30) menuVfoToggle(btnState, 1); - else if (select < 40) + else if (select < 30) menuSelectMode(btnState); + else if (select < 40) + menuRitToggle(btnState); else if (select < 50) - menuCWSpeed(btnState); + menuIFSToggle(btnState); else if (select < 60) - menuCWAutoKey(btnState); + menuCWSpeed(btnState); else if (select < 70) - menuSetup(btnState); - else if (select < 80 && !modeCalibrate) - menuExit(btnState); - else if (select < 90 && modeCalibrate) - menuSetupCalibration(btnState); //crystal - else if (select < 100 && modeCalibrate) - menuSetupCarrier(btnState); //lsb - else if (select < 110 && modeCalibrate) - menuSetupCWCarrier(btnState); //lsb - else if (select < 120 && modeCalibrate) - menuSetupCwTone(btnState); - else if (select < 130 && modeCalibrate) - menuSetupCwDelay(btnState); - else if (select < 140 && modeCalibrate) - menuSetupTXCWInterval(btnState); - else if (select < 150 && modeCalibrate) - menuSetupKeyType(btnState); - else if (select < 160 && modeCalibrate) - menuADCMonitor(btnState); - else if (select < 170 && modeCalibrate) menuSplitOnOff(btnState); //SplitOn / off + else if (select < 80) + menuCWAutoKey(btnState); + else if (select < 90) + menuSetup(btnState); + else if (select < 100) + menuExit(btnState); + else if (select < 110 && modeCalibrate) + menuSetupCalibration(btnState); //crystal + else if (select < 120 && modeCalibrate) + menuSetupCarrier(btnState); //lsb + else if (select < 130 && modeCalibrate) + menuSetupCWCarrier(btnState); //lsb + else if (select < 140 && modeCalibrate) + menuSetupCwTone(btnState); + else if (select < 150 && modeCalibrate) + menuSetupCwDelay(btnState); + else if (select < 160 && modeCalibrate) + menuSetupTXCWInterval(btnState); + else if (select < 170 && modeCalibrate) + menuSetupKeyType(btnState); else if (select < 180 && modeCalibrate) - menuTxOnOff(btnState, 0x01); //TX OFF / ON + menuADCMonitor(btnState); else if (select < 190 && modeCalibrate) + menuTxOnOff(btnState, 0x01); //TX OFF / ON + else if (select < 200 && modeCalibrate) menuExit(btnState); Check_Cat(0); //To prevent disconnections diff --git a/ubitx_20/ubitx_si5351.ino b/ubitx_20/ubitx_si5351.ino index 9e79e51..8098819 100644 --- a/ubitx_20/ubitx_si5351.ino +++ b/ubitx_20/ubitx_si5351.ino @@ -111,9 +111,9 @@ void initOscillators(){ si5351bx_vcoa = (SI5351BX_XTAL * SI5351BX_MSA) + calibration; // apply the calibration correction factor if (cwMode == 0) - si5351bx_setfreq(0, usbCarrier); + si5351bx_setfreq(0, usbCarrier + (isIFShift ? ifShiftValue : 0)); else - si5351bx_setfreq(0, cwmCarrier); + si5351bx_setfreq(0, cwmCarrier + (isIFShift ? ifShiftValue : 0)); } diff --git a/ubitx_20/ubitx_ui.ino b/ubitx_20/ubitx_ui.ino index 71d35cd..f415268 100644 --- a/ubitx_20/ubitx_ui.ino +++ b/ubitx_20/ubitx_ui.ino @@ -25,8 +25,8 @@ int btnDown(){ * The current reading of the meter is assembled in the string called meter */ -//char meter[17]; +/* const PROGMEM uint8_t s_meter_bitmap[] = { B00000,B00000,B00000,B00000,B00000,B00100,B00100,B11011, B10000,B10000,B10000,B10000,B10100,B10100,B10100,B11011, @@ -35,7 +35,18 @@ const PROGMEM uint8_t s_meter_bitmap[] = { B00010,B00010,B00010,B00010,B00110,B00110,B00110,B11011, B00001,B00001,B00001,B00001,B00101,B00101,B00101,B11011 }; -PGM_P ps_meter_bitmap = reinterpret_cast(s_meter_bitmap); +*/ + +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 +}; + +PGM_P p_metes_bitmap = reinterpret_cast(meters_bitmap); const PROGMEM uint8_t lock_bitmap[8] = { 0b01110, @@ -60,38 +71,56 @@ void initMeter(){ lcd.createChar(0, tmpbytes); for (i = 0; i < 8; i++) - tmpbytes[i] = pgm_read_byte(ps_meter_bitmap + 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(ps_meter_bitmap + i + 8); + 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(ps_meter_bitmap + i + 16); + 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(ps_meter_bitmap + i + 24); + 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(ps_meter_bitmap + i + 28); + 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(ps_meter_bitmap + i + 32); + tmpbytes[i] = pgm_read_byte(p_metes_bitmap + i + 40); lcd.createChar(6, tmpbytes); } -/** - * The meter is drawn with special characters. - * character 1 is used to simple draw the blocks of the scale of the meter - * characters 2 to 6 are used to draw the needle in positions 1 to within the block - * This displays a meter from 0 to 100, -1 displays nothing - */ +//by KD8CEC +//0 ~ 25 : 30 over : + 10 +void drawMeter(int needle) { + //5Char + O over + int drawCharLength = needle / 5; + int drawCharLengthLast = needle % 5; + 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; +} + +/* void drawMeter(int8_t needle){ int16_t best, i, s; @@ -101,19 +130,18 @@ void drawMeter(int8_t needle){ s = (needle * 4)/10; for (i = 0; i < 8; i++){ if (s >= 5) - meter[i] = 1; + lcdMeter[i] = 1; else if (s >= 0) - meter[i] = 2 + s; + lcdMeter[i] = 2 + s; else - meter[i] = 1; + lcdMeter[i] = 1; s = s - 5; } if (needle >= 40) - meter[i-1] = 6; - meter[i] = 0; + lcdMeter[i-1] = 6; + lcdMeter[i] = 0; } */ - // The generic routine to display one line on the LCD void printLine(unsigned char linenmbr, const char *c) { if ((displayOption1 & 0x01) == 0x01) From 4830db78cbdfef403f2fb923d5705b63bbeca4e7 Mon Sep 17 00:00:00 2001 From: phdlee Date: Tue, 30 Jan 2018 18:43:08 +0900 Subject: [PATCH 053/173] change IF Shift setup type --- ubitx_20/ubitx_20.ino | 6 ++-- ubitx_20/ubitx_idle.ino | 36 ++++++++++++++-------- ubitx_20/ubitx_menu.ino | 68 +++++++++++++++++++++++++++++++++++++++-- 3 files changed, 94 insertions(+), 16 deletions(-) diff --git a/ubitx_20/ubitx_20.ino b/ubitx_20/ubitx_20.ino index 2ce915d..bf4c9c5 100644 --- a/ubitx_20/ubitx_20.ino +++ b/ubitx_20/ubitx_20.ino @@ -755,6 +755,7 @@ void doRIT(){ } } +/* void doIFShift(){ int knob = enc_read(); unsigned long old_freq = frequency; @@ -770,6 +771,7 @@ void doIFShift(){ setFrequency(frequency); } } +*/ /** save Frequency and mode to eeprom @@ -1169,8 +1171,8 @@ void loop(){ if (!inTx){ if (ritOn) doRIT(); - else if (isIFShift) - doIFShift(); + //else if (isIFShift) + // doIFShift(); else doTuningWithThresHold(); diff --git a/ubitx_20/ubitx_idle.ino b/ubitx_20/ubitx_idle.ino index b96e567..2e1c782 100644 --- a/ubitx_20/ubitx_idle.ino +++ b/ubitx_20/ubitx_idle.ino @@ -122,22 +122,34 @@ void updateLine2Buffer(char isDirectCall) 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] = ' '; - line2Buffer[13] = ' '; - line2Buffer[14] = ' '; - line2Buffer[15] = ' '; - - //11, 12, 13, 14, 15 - memset(b, 0, sizeof(b)); - ltoa(ifShiftValue, b, DEC); - strncat(line2Buffer, b, 5); + if (ifShiftValue == 0) + { + line2Buffer[10] = 'S'; + line2Buffer[11] = ':'; + line2Buffer[12] = 'O'; + line2Buffer[13] = 'F'; + line2Buffer[14] = 'F'; + } + else + { + 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); } diff --git a/ubitx_20/ubitx_menu.ino b/ubitx_20/ubitx_menu.ino index 06823ca..82b1f7a 100644 --- a/ubitx_20/ubitx_menu.ino +++ b/ubitx_20/ubitx_menu.ino @@ -239,6 +239,7 @@ void menuRitToggle(int btn){ } } +/* void menuIFSToggle(int btn){ if (!btn){ if (isIFShift == 1) @@ -260,6 +261,69 @@ void menuIFSToggle(int btn){ printLine2ClearAndUpdate(); } } +*/ +void menuIFSToggle(int btn){ + int knob = 0; + char needApplyChangeValue = 1; + + if (!btn){ + if (isIFShift == 1) + printLineF2(F("IF Shift Change?")); + else + printLineF2(F("IF Shift:Off, On?")); + } + else { + if (isIFShift == 0){ + printLineF2(F("IF Shift is ON")); + delay_background(500, 0); + isIFShift = 1; + } + + delay_background(500, 0); + updateLine2Buffer(1); + setFrequency(frequency); + + //Off or Change Value + while(!btnDown() && digitalRead(PTT) == HIGH){ + if (needApplyChangeValue ==1) + { + updateLine2Buffer(1); + setFrequency(frequency); + + if (cwMode == 0) + si5351bx_setfreq(0, usbCarrier + (isIFShift ? ifShiftValue : 0)); //set back the carrier oscillator anyway, cw tx switches it off + else + si5351bx_setfreq(0, cwmCarrier + (isIFShift ? ifShiftValue : 0)); //set back the carrier oscillator anyway, cw tx switches it off + + needApplyChangeValue = 0; + } + + knob = enc_read(); + if (knob != 0){ + if (knob < 0) + ifShiftValue -= 1l; + else if (knob > 0) + ifShiftValue += 1; + + needApplyChangeValue = 1; + } + } + + delay_background(500, 0); //for check Long Press function key + + if (btnDown() || digitalRead(PTT) == LOW || ifShiftValue == 0) + { + isIFShift = 0; + printLineF2(F("IF Shift is OFF")); + setFrequency(frequency); + delay_background(500, 0); + } + + menuOn = 0; + //delay_background(500, 0); + printLine2ClearAndUpdate(); + } +} /* @@ -377,9 +441,9 @@ void menuSelectMode(int btn){ } if (cwMode == 0) - si5351bx_setfreq(0, usbCarrier); //set back the carrier oscillator anyway, cw tx switches it off + si5351bx_setfreq(0, usbCarrier + (isIFShift ? ifShiftValue : 0)); //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 + si5351bx_setfreq(0, cwmCarrier + (isIFShift ? ifShiftValue : 0)); //set back the carrier oscillator anyway, cw tx switches it off setFrequency(frequency); delay_background(500, 0); From 85832de03461aab83cee3c2c3019f610ab390739 Mon Sep 17 00:00:00 2001 From: phdlee Date: Tue, 30 Jan 2018 20:02:49 +0900 Subject: [PATCH 054/173] change confirmation key PTT->function key for easy interface --- ubitx_20/ubitx_menu.ino | 55 ++++++++++++++++++----------------------- 1 file changed, 24 insertions(+), 31 deletions(-) diff --git a/ubitx_20/ubitx_menu.ino b/ubitx_20/ubitx_menu.ino index 82b1f7a..61ff5be 100644 --- a/ubitx_20/ubitx_menu.ino +++ b/ubitx_20/ubitx_menu.ino @@ -452,8 +452,6 @@ void menuSelectMode(int btn){ } } - - void menuSplitOnOff(int btn){ if (!btn){ if (splitOn == 0) @@ -478,8 +476,6 @@ void menuSplitOnOff(int btn){ } } - - /* //Select CW Key Type by KD8CEC void menuSetupKeyType(int btn){ @@ -522,14 +518,14 @@ void menuSetupKeyType(int btn){ int knob = 0; int selectedKeyType = 0; int moveStep = 0; - if (!btn && digitalRead(PTT) == HIGH){ + if (!btn){ printLineF2(F("Change Key Type?")); } else { - printLineF2(F("Press PTT to set")); + printLineF2(F("Press to set Key")); delay_background(500, 0); selectedKeyType = cwKeyType; - while(!btnDown() && digitalRead(PTT) == HIGH){ + while(!btnDown()){ //Display Key Type if (selectedKeyType == 0) @@ -559,24 +555,21 @@ void menuSetupKeyType(int btn){ Check_Cat(0); //To prevent disconnections } - //save the setting - if (digitalRead(PTT) == LOW){ - printLineF2(F("CW Key Type set!")); - cwKeyType = selectedKeyType; - EEPROM.put(CW_KEY_TYPE, cwKeyType); + printLineF2(F("CW Key Type set!")); + cwKeyType = selectedKeyType; + EEPROM.put(CW_KEY_TYPE, cwKeyType); - if (cwKeyType == 0) - Iambic_Key = false; + if (cwKeyType == 0) + Iambic_Key = false; + else + { + Iambic_Key = true; + if (cwKeyType = 1) + keyerControl &= ~IAMBICB; else - { - Iambic_Key = true; - if (cwKeyType = 1) - keyerControl &= ~IAMBICB; - else - keyerControl |= IAMBICB; - } - delay_background(2000, 0); + keyerControl |= IAMBICB; } + delay_background(2000, 0); printLine2ClearAndUpdate(); menuOn = 0; @@ -738,7 +731,7 @@ void menuCWSpeed(int btn){ return; } - printLineF1(F("Press PTT to set")); + printLineF1(F("Press to set WPm")); strcpy(b, "WPM:"); itoa(wpm,c, 10); strcat(b, c); @@ -768,12 +761,12 @@ void menuCWSpeed(int btn){ } //save the setting - if (digitalRead(PTT) == LOW){ + //if (digitalRead(PTT) == LOW){ printLineF2(F("CW Speed set!")); cwSpeed = 1200/wpm; EEPROM.put(CW_SPEED, cwSpeed); delay_background(2000, 0); - } + //} printLine2ClearAndUpdate(); menuOn = 0; } @@ -813,7 +806,7 @@ void menuSetupCwDelay(int btn){ return; } - printLineF1(F("Press PTT to set")); + printLineF1(F("Press, set Delay")); strcpy(b, "DELAY:"); itoa(tmpCWDelay,c, 10); strcat(b, c); @@ -841,12 +834,12 @@ void menuSetupCwDelay(int btn){ } //save the setting - if (digitalRead(PTT) == LOW){ + //if (digitalRead(PTT) == LOW){ printLineF2(F("CW Delay set!")); cwDelayTime = tmpCWDelay / 10; EEPROM.put(CW_DELAY, cwDelayTime); delay_background(2000, 0); - } + //} printLine2ClearAndUpdate(); menuOn = 0; } @@ -862,7 +855,7 @@ void menuSetupTXCWInterval(int btn){ return; } - printLineF1(F("Press PTT to set")); + printLineF1(F("Press, set Delay")); strcpy(b, "Start Delay:"); itoa(tmpTXCWInterval,c, 10); strcat(b, c); @@ -890,12 +883,12 @@ void menuSetupTXCWInterval(int btn){ } //save the setting - if (digitalRead(PTT) == LOW){ + //if (digitalRead(PTT) == LOW){ printLineF2(F("CW Start set!")); delayBeforeCWStartTime = tmpTXCWInterval / 2; EEPROM.put(CW_START, delayBeforeCWStartTime); delay_background(2000, 0); - } + //} printLine2ClearAndUpdate(); menuOn = 0; } From 4745790dfa50977b0a1716ca160aa0c650a05845 Mon Sep 17 00:00:00 2001 From: phdlee Date: Wed, 31 Jan 2018 10:44:23 +0900 Subject: [PATCH 055/173] fixed Key select bug --- ubitx_20/ubitx_20.ino | 2 +- ubitx_20/ubitx_menu.ino | 5 ++++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/ubitx_20/ubitx_20.ino b/ubitx_20/ubitx_20.ino index bf4c9c5..1ba7d95 100644 --- a/ubitx_20/ubitx_20.ino +++ b/ubitx_20/ubitx_20.ino @@ -869,7 +869,7 @@ void initSettings(){ else { Iambic_Key = true; - if (cwKeyType = 1) + if (cwKeyType == 1) keyerControl &= ~IAMBICB; else keyerControl |= IAMBICB; diff --git a/ubitx_20/ubitx_menu.ino b/ubitx_20/ubitx_menu.ino index 61ff5be..c09ca6f 100644 --- a/ubitx_20/ubitx_menu.ino +++ b/ubitx_20/ubitx_menu.ino @@ -87,6 +87,8 @@ void menuBand(int btn){ stepChangeCount = 0; } } + + //setFrequency(frequency + 200000l); } else { //original source if (knob < 0 && frequency > 3000000l) @@ -525,6 +527,7 @@ void menuSetupKeyType(int btn){ printLineF2(F("Press to set Key")); delay_background(500, 0); selectedKeyType = cwKeyType; + while(!btnDown()){ //Display Key Type @@ -564,7 +567,7 @@ void menuSetupKeyType(int btn){ else { Iambic_Key = true; - if (cwKeyType = 1) + if (cwKeyType == 1) keyerControl &= ~IAMBICB; else keyerControl |= IAMBICB; From c8879e0e596d0fb6273d7ee7331038e1ed88b5b8 Mon Sep 17 00:00:00 2001 From: phdlee Date: Wed, 31 Jan 2018 12:12:58 +0900 Subject: [PATCH 056/173] Update README.md --- README.md | 27 +++++++++++++++++++-------- 1 file changed, 19 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index 7ca4040..4a3e96f 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,8 @@ #IMPORTANT INFORMATION ---------------------------------------------------------------------------- -- 0.30 Version Test only download. almost complete +- 0.33 Version Test only download. almost complete - Beta 0.26 and Beta 0.261, Beta 0.262,0.27 is complete test, 0.28 is tested. +- 0.31 is tested but has not critical bug - You can download and use it (Release section). # Current work list (for Version 0.31) @@ -11,13 +12,6 @@ ---------------------------------------------------------------------------- I received uBITX a month ago and found that many features are required, and began coding with the idea of implementing minimal functionality as a general hf transceiver rather than an experimental device. -- fixed bugs... -- Diallock for uBITX's sensitive encoders -- built in softare Memory keyer and cw options control for CW communication -- Implementation of CAT communication protocol for Digital Communication (as FT8, JT65, etc) -- Delay Options for external Linear. -- and more... - Most of the basic functions of the HF transceiver I thought were implemented. The minimum basic specification for uBITX to operate as a radio, I think it is finished. So I will release the 0.27 version and if I do not see the bug anymore, I will try to change the version name to 1.0. @@ -50,6 +44,23 @@ Prepared or finished tasks for the next version ---------------------------------------------------------------------------- ## REVISION RECORD +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. From 55cfeeb924b039c54dfeb6cbbb55a5a04b1c906e Mon Sep 17 00:00:00 2001 From: phdlee Date: Wed, 31 Jan 2018 12:13:44 +0900 Subject: [PATCH 057/173] Update README.md --- README.md | 3 --- 1 file changed, 3 deletions(-) diff --git a/README.md b/README.md index 4a3e96f..9074023 100644 --- a/README.md +++ b/README.md @@ -5,9 +5,6 @@ - 0.31 is tested but has not critical bug - You can download and use it (Release section). -# Current work list (for Version 0.31) - 1 Testing CAT Control with Software using hamlib on Linux - #NOTICE ---------------------------------------------------------------------------- I received uBITX a month ago and found that many features are required, and began coding with the idea of implementing minimal functionality as a general hf transceiver rather than an experimental device. From 3d019cdd44a98e643552c10777e49aac033bd4cd Mon Sep 17 00:00:00 2001 From: phdlee Date: Wed, 31 Jan 2018 17:53:20 +0900 Subject: [PATCH 058/173] change IF Shift Step 1 -> 50Hz --- ubitx_20/ubitx_menu.ino | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ubitx_20/ubitx_menu.ino b/ubitx_20/ubitx_menu.ino index c09ca6f..0e64dd3 100644 --- a/ubitx_20/ubitx_menu.ino +++ b/ubitx_20/ubitx_menu.ino @@ -303,9 +303,9 @@ void menuIFSToggle(int btn){ knob = enc_read(); if (knob != 0){ if (knob < 0) - ifShiftValue -= 1l; + ifShiftValue -= 50l; else if (knob > 0) - ifShiftValue += 1; + ifShiftValue += 50; needApplyChangeValue = 1; } From d229a10092b39a3f752ed5b472a42f79908c1e91 Mon Sep 17 00:00:00 2001 From: phdlee Date: Fri, 2 Feb 2018 20:49:00 +0900 Subject: [PATCH 059/173] change tune step size and fixed bug --- ubitx_20/ubitx_20.ino | 24 ++++++++++++++++++++---- ubitx_20/ubitx_menu.ino | 2 +- 2 files changed, 21 insertions(+), 5 deletions(-) diff --git a/ubitx_20/ubitx_20.ino b/ubitx_20/ubitx_20.ino index 1ba7d95..098e2af 100644 --- a/ubitx_20/ubitx_20.ino +++ b/ubitx_20/ubitx_20.ino @@ -160,7 +160,8 @@ int count = 0; //to generally count ticks, loops, etc #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) +#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 @@ -260,7 +261,7 @@ byte sideToneSub = 0; //DialLock byte isDialLock = 0; //000000[0]vfoB [0]vfoA 0Bit : A, 1Bit : B byte isTxType = 0; //000000[0 - isSplit] [0 - isTXStop] -byte arTuneStep[5]; +long arTuneStep[5]; byte tuneStepIndex; //default Value 0, start Offset is 0 because of check new user byte displayOption1 = 0; @@ -806,6 +807,21 @@ void storeFrequencyAndMode(byte saveType) } } +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 @@ -926,8 +942,8 @@ void initSettings(){ findedValidValueCount = 0; EEPROM.get(TUNING_STEP, tuneStepIndex); for (byte i = 0; i < 5; i++) { - arTuneStep[i] = EEPROM.read(TUNING_STEP + i + 1); - if (arTuneStep[i] >= 1 && arTuneStep[i] < 251) //Maximum 250 for check valid Value + arTuneStep[i] = byteToSteps(EEPROM.read(TUNING_STEP + i + 1)); + if (arTuneStep[i] >= 1 && arTuneStep[i] <= 60000) //Maximum 650 for check valid Value findedValidValueCount++; } diff --git a/ubitx_20/ubitx_menu.ino b/ubitx_20/ubitx_menu.ino index 0e64dd3..298701a 100644 --- a/ubitx_20/ubitx_menu.ino +++ b/ubitx_20/ubitx_menu.ino @@ -1211,7 +1211,7 @@ void menuSetupCwTone(int btn){ //save the setting if (digitalRead(PTT) == LOW){ printLineF2(F("Sidetone set!")); - EEPROM.put(CW_SIDETONE, usbCarrier); + EEPROM.put(CW_SIDETONE, sideTone); delay_background(2000, 0); } else From dd68b38454643c69e31050b800eca3981eb811c5 Mon Sep 17 00:00:00 2001 From: phdlee Date: Sat, 3 Feb 2018 16:35:27 +0900 Subject: [PATCH 060/173] Optimize codes --- ubitx_20/ubitx_20.ino | 44 +--- ubitx_20/ubitx_idle.ino | 56 +++-- ubitx_20/ubitx_menu.ino | 504 ++++++++++++++++++---------------------- ubitx_20/ubitx_ui.ino | 1 - 4 files changed, 276 insertions(+), 329 deletions(-) diff --git a/ubitx_20/ubitx_20.ino b/ubitx_20/ubitx_20.ino index 098e2af..f36c00e 100644 --- a/ubitx_20/ubitx_20.ino +++ b/ubitx_20/ubitx_20.ino @@ -382,7 +382,7 @@ void setNextHamBandFreq(unsigned long f, char moveDirection) resultFreq = (unsigned long)(hamBandRange[(unsigned char)findedIndex][0]) * 1000; setFrequency(resultFreq); - byteWithFreqToMode(loadMode); + byteToMode(loadMode, 1); } void saveBandFreqByIndex(unsigned long f, unsigned long mode, char bandIndex) { @@ -537,12 +537,12 @@ void startTx(byte txMode, byte isDisplayUpdate){ if (vfoActive == VFO_B) { vfoActive = VFO_A; frequency = vfoA; - byteToMode(vfoA_mode); + byteToMode(vfoA_mode, 0); } else if (vfoActive == VFO_A){ vfoActive = VFO_B; frequency = vfoB; - byteToMode(vfoB_mode); + byteToMode(vfoB_mode, 0); } setFrequency(frequency); @@ -597,12 +597,12 @@ void stopTx(){ if (vfoActive == VFO_B){ vfoActive = VFO_A; frequency = vfoA; - byteToMode(vfoA_mode); + byteToMode(vfoA_mode, 0); } else if (vfoActive == VFO_A){ vfoActive = VFO_B; frequency = vfoB; - byteToMode(vfoB_mode); + byteToMode(vfoB_mode, 0); } setFrequency(frequency); } //end of else @@ -755,27 +755,8 @@ void doRIT(){ updateDisplay(); } } - /* -void doIFShift(){ - int knob = enc_read(); - unsigned long old_freq = frequency; - - if (knob != 0) - { - if (knob < 0) - ifShiftValue -= 1l; - else if (knob > 0) - ifShiftValue += 1; - - updateLine2Buffer(1); - setFrequency(frequency); - } -} -*/ - -/** - save Frequency and mode to eeprom + save Frequency and mode to eeprom for Auto Save with protected eeprom cycle, by kd8cec */ void storeFrequencyAndMode(byte saveType) { @@ -807,6 +788,7 @@ void storeFrequencyAndMode(byte saveType) } } +//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; @@ -1019,12 +1001,12 @@ void initSettings(){ if (vfoA > 35000000l || 3500000l > vfoA) { vfoA = 7150000l; - vfoA_mode = 2; + vfoA_mode = 2; //LSB } if (vfoB > 35000000l || 3500000l > vfoB) { vfoB = 14150000l; - vfoB_mode = 3; + vfoB_mode = 3; //USB } //end of original code section @@ -1100,7 +1082,7 @@ void setup() //Serial.begin(9600); lcd.begin(16, 2); - printLineF(1, F("CECBT v0.33")); + printLineF(1, F("CECBT v0.35")); Init_Cat(38400, SERIAL_8N1); initMeter(); //not used in this build @@ -1119,7 +1101,7 @@ void setup() initPorts(); - byteToMode(vfoA_mode); + byteToMode(vfoA_mode, 0); initOscillators(); frequency = vfoA; @@ -1132,13 +1114,11 @@ void setup() } -/** - * The loop checks for keydown, ptt, function button and tuning. - */ //for debug int dbgCnt = 0; byte flasher = 0; +//Auto save Frequency and Mode with Protected eeprom life by KD8CEC void checkAutoSaveFreqMode() { //when tx or ritOn, disable auto save diff --git a/ubitx_20/ubitx_idle.ino b/ubitx_20/ubitx_idle.ino index 2e1c782..974aef6 100644 --- a/ubitx_20/ubitx_idle.ino +++ b/ubitx_20/ubitx_idle.ino @@ -17,7 +17,7 @@ along with this program. If not, see . **************************************************************************/ -byte line2Buffer[16]; +char line2Buffer[16]; //KD8CEC 200Hz ST //L14.150 200Hz ST //U14.150 +150khz @@ -53,8 +53,11 @@ void updateLine2Buffer(char isDirectCall) } return; - } - + } //end of ritOn display + + //====================================================== + //other VFO display + //====================================================== if (vfoActive == VFO_B) { tmpFreq = vfoA; @@ -82,23 +85,23 @@ void updateLine2Buffer(char isDirectCall) } //EXAMPLE #1 - if ((displayOption1 & 0x04) == 0x00) + if ((displayOption1 & 0x04) == 0x00) //none scroll display line2Buffer[6] = 'k'; else { //example #2 - if (freqScrollPosition++ > 18) + if (freqScrollPosition++ > 18) //none scroll display time { line2Buffer[6] = 'k'; if (freqScrollPosition > 25) freqScrollPosition = -1; } - else + else //scroll frequency { line2Buffer[10] = 'H'; line2Buffer[11] = 'z'; - if (freqScrollPosition < 7) + if (freqScrollPosition < 7) { for (int i = 11; i >= 0; i--) if (i - (7 - freqScrollPosition) >= 0) @@ -115,10 +118,10 @@ void updateLine2Buffer(char isDirectCall) line2Buffer[i] = ' '; } } - } + } //scroll + line2Buffer[7] = ' '; } //check direct call by encoder - if (isIFShift) { @@ -152,24 +155,38 @@ void updateLine2Buffer(char isDirectCall) if (isDirectCall == 1) //if call by encoder (not scheduler), immediate print value printLine2(line2Buffer); - } - else + } // end of display IF + else // step display { if (isDirectCall != 0) return; - + + memset(&line2Buffer[8], ' ', 8); //Step - byte tmpStep = arTuneStep[tuneStepIndex -1]; - for (int i = 10; i >= 8; i--) { + 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] = tmpStep % 10 + 0x30; + line2Buffer[i + isStepKhz] = tmpStep % 10 + 0x30; tmpStep /= 10; } else - line2Buffer[i] = ' '; + line2Buffer[i +isStepKhz] = ' '; + } + //if (isStepKhz == 1) + // line2Buffer[10] = 'k'; + + if (isStepKhz == 0) + { + line2Buffer[11] = 'H'; + line2Buffer[12] = 'z'; } - line2Buffer[11] = 'H'; - line2Buffer[12] = 'z'; line2Buffer[13] = ' '; //if ( @@ -215,6 +232,9 @@ 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)) { if (checkCount++ > 1) diff --git a/ubitx_20/ubitx_menu.ino b/ubitx_20/ubitx_menu.ino index 298701a..12fabfd 100644 --- a/ubitx_20/ubitx_menu.ino +++ b/ubitx_20/ubitx_menu.ino @@ -13,6 +13,36 @@ #define printLineF1(x) (printLineF(1, x)) #define printLineF2(x) (printLineF(0, x)) +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); + } +} + +void menuClearExit(int delayTime) +{ + if (delayTime > 0) + delay_background(delayTime, 0); + + printLine2ClearAndUpdate(); + menuOn = 0; +} + //Ham band move by KD8CEC void menuBand(int btn){ int knob = 0; @@ -27,8 +57,7 @@ void menuBand(int btn){ printLineF2(F("Press to confirm")); //wait for the button menu select button to be lifted) while (btnDown()) { - delay(50); - Check_Cat(0); //To prevent disconnections + delay_background(50, 0); if (btnPressCount++ > 20) { btnPressCount = 0; if (tuneTXType > 0) { //Just toggle 0 <-> 2, if tuneTXType is 100, 100 -> 0 -> 2 @@ -57,23 +86,13 @@ void menuBand(int btn){ } } - delay(50); + //delay(50); ritDisable(); while(!btnDown()){ knob = enc_read(); if (knob != 0){ - /* - if (band > 3 && knob < 0) - band--; - if (band < 30 && knob > 0) - band++; - if (band > 10) - isUSB = true; - else - isUSB = false; - setFrequency(((unsigned long)band * 1000000l) + offset); */ if (tuneTXType == 2 || tuneTXType == 3 || tuneTXType == 102 || tuneTXType == 103) { //only ham band move if (knob < 0) { if (stepChangeCount-- < -3) { @@ -87,9 +106,7 @@ void menuBand(int btn){ stepChangeCount = 0; } } - - //setFrequency(frequency + 200000l); - } + } //end of only ham band move else { //original source if (knob < 0 && frequency > 3000000l) setFrequency(frequency - 200000l); @@ -105,19 +122,21 @@ void menuBand(int btn){ updateDisplay(); } - delay(20); - Check_Cat(0); //To prevent disconnections + delay_background(20, 0); } +/* while(btnDown()) { delay(50); Check_Cat(0); //To prevent disconnections } - - delay(50); - - printLine2ClearAndUpdate(); - menuOn = 0; +*/ + FrequencyToVFO(1); + + //printLine2ClearAndUpdate(); + //delay_background(500, 0); + //menuOn = 0; + menuClearExit(500); } //Convert Mode, Number by KD8CEC @@ -141,7 +160,9 @@ byte modeToByte(){ } //Convert Number to Mode by KD8CEC -void byteToMode(byte modeValue){ +//autoSetModebyFreq : 0 +//autoSetModebyFreq : 1, if (modValue is not set, set mode by frequency) +void byteToMode(byte modeValue, byte autoSetModebyFreq){ if (modeValue == 4) cwMode = 1; else if (modeValue == 5) @@ -151,11 +172,14 @@ void byteToMode(byte modeValue){ cwMode = 0; if (modeValue == 3) isUSB = 1; + else if (autoSetModebyFreq == 1 && (modeValue == 0)) + isUSB = (frequency > 10000000l) ? true : false; else isUSB = 0; } } +/* //Convert Number to Mode by KD8CEC void byteWithFreqToMode(byte modeValue){ if (modeValue == 4) @@ -172,99 +196,9 @@ void byteWithFreqToMode(byte modeValue){ isUSB = 0; } } - -//VFO Toggle and save VFO Information, modified by KD8CEC -void menuVfoToggle(int btn, char isUseDelayTime) -{ - if (!btn){ - if (vfoActive == VFO_A) - printLineF2(F("Select VFO B?")); - else - printLineF2(F("Select VFO A?")); - } - else { - if (vfoActive == VFO_B){ - vfoB = frequency; - vfoB_mode = modeToByte(); - storeFrequencyAndMode(2); //vfoB -> eeprom - - vfoActive = VFO_A; - frequency = vfoA; - saveCheckFreq = frequency; - byteToMode(vfoA_mode); - printLineF2(F("Selected VFO A")); - } - else { - vfoA = frequency; - vfoA_mode = modeToByte(); - storeFrequencyAndMode(1); //vfoA -> eeprom - - vfoActive = VFO_B; - frequency = vfoB; - saveCheckFreq = frequency; - byteToMode(vfoB_mode); - printLineF2(F("Selected VFO B")); - } - - ritDisable(); - setFrequency(frequency); - - if (isUseDelayTime == 1) //Found Issue in wsjt-x Linux 32bit - delay_background(500, 0); - - printLine2ClearAndUpdate(); - //exit the menu - menuOn = 0; - } -} - -void menuRitToggle(int btn){ - if (!btn){ - if (ritOn == 1) - printLineF2(F("RIT:On, Off?")); - else - printLineF2(F("RIT:Off, 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(); - } - menuOn = 0; - delay_background(500, 0); - printLine2ClearAndUpdate(); - } -} - -/* -void menuIFSToggle(int btn){ - if (!btn){ - if (isIFShift == 1) - printLineF2(F("IF Shift:On, Off?")); - else - printLineF2(F("IF Shift:Off, On?")); - } - else { - if (isIFShift == 0){ - printLineF2(F("IF Shift is ON")); - isIFShift = 1; - } - else{ - printLineF2(F("IF Shift is OFF")); - isIFShift = 0; - } - menuOn = 0; - delay_background(500, 0); - printLine2ClearAndUpdate(); - } -} */ -void menuIFSToggle(int btn){ + +void menuIFSSetup(int btn){ int knob = 0; char needApplyChangeValue = 1; @@ -275,18 +209,18 @@ void menuIFSToggle(int btn){ printLineF2(F("IF Shift:Off, On?")); } else { - if (isIFShift == 0){ - printLineF2(F("IF Shift is ON")); - delay_background(500, 0); - isIFShift = 1; - } + //if (isIFShift == 0){ + //printLineF2(F("IF Shift is ON")); + //delay_background(500, 0); + isIFShift = 1; + //} delay_background(500, 0); updateLine2Buffer(1); setFrequency(frequency); //Off or Change Value - while(!btnDown() && digitalRead(PTT) == HIGH){ + while(!btnDown() ){ if (needApplyChangeValue ==1) { updateLine2Buffer(1); @@ -313,7 +247,7 @@ void menuIFSToggle(int btn){ delay_background(500, 0); //for check Long Press function key - if (btnDown() || digitalRead(PTT) == LOW || ifShiftValue == 0) + if (btnDown() || ifShiftValue == 0) { isIFShift = 0; printLineF2(F("IF Shift is OFF")); @@ -321,38 +255,12 @@ void menuIFSToggle(int btn){ delay_background(500, 0); } - menuOn = 0; - //delay_background(500, 0); - printLine2ClearAndUpdate(); + //menuOn = 0; + //printLine2ClearAndUpdate(); + menuClearExit(0); } } - -/* -void menuSidebandToggle(int btn){ - if (!btn){ - if (isUSB == true) - printLineF2(F("Select LSB?")); - else - printLineF2(F("Select USB?")); - } - else { - cwMode = 0; - if (isUSB == true){ - isUSB = false; - printLineF2(F("LSB Selected")); - } - else { - isUSB = true; - printLineF2(F("USB Selected")); - } - setFrequency(frequency); - delay_background(500, 0); - printLine2ClearAndUpdate(); - menuOn = 0; - } -} -*/ void menuSelectMode(int btn){ int knob = 0; int selectModeType = 0; @@ -412,8 +320,7 @@ void menuSelectMode(int btn){ } if (beforeMode != selectModeType) { - printLineF1(F("Changed Mode")); - + //printLineF1(F("Changed Mode")); if (selectModeType == 0) { cwMode = 0; isUSB = 0; } @@ -427,19 +334,7 @@ void menuSelectMode(int btn){ cwMode = 2; } - //Save Frequency & Mode Information - if (vfoActive == VFO_A) - { - vfoA = frequency; - vfoA_mode = modeToByte(); - storeFrequencyAndMode(1); - } - else - { - vfoB = frequency; - vfoB_mode = modeToByte(); - storeFrequencyAndMode(2); - } + FrequencyToVFO(1); } if (cwMode == 0) @@ -448,73 +343,14 @@ void menuSelectMode(int btn){ si5351bx_setfreq(0, cwmCarrier + (isIFShift ? ifShiftValue : 0)); //set back the carrier oscillator anyway, cw tx switches it off setFrequency(frequency); - delay_background(500, 0); - printLine2ClearAndUpdate(); - menuOn = 0; - } -} -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("Split Off!")); - } - else { - splitOn = 1; - if (ritOn == 1) - ritOn = 0; - printLineF2(F("Split On!")); - } - delay_background(500, 0); - printLine2ClearAndUpdate(); - menuOn = 0; + //delay_background(500, 0); + //printLine2ClearAndUpdate(); + //menuOn = 0; + menuClearExit(500); } } -/* -//Select CW Key Type by KD8CEC -void menuSetupKeyType(int btn){ - if (!btn && digitalRead(PTT) == HIGH){ - if (Iambic_Key) - printLineF2(F("Key: Straight?")); - else - printLineF2(F("Key: Fn=A, PTT=B")); - } - else { - if (Iambic_Key) - { - printLineF2(F("Straight Key!")); - Iambic_Key = false; - } - else - { - Iambic_Key = true; - if (btn) - { - keyerControl &= ~IAMBICB; - printLineF2(F("IAMBICA Key!")); - } - else - { - keyerControl |= IAMBICB; - printLineF2(F("IAMBICB Key!")); - } - } - - delay_background(500, 0); - printLine2ClearAndUpdate(); - menuOn = 0; - } -} -*/ - //Select CW Key Type by KD8CEC void menuSetupKeyType(int btn){ int knob = 0; @@ -572,10 +408,11 @@ void menuSetupKeyType(int btn){ else keyerControl |= IAMBICB; } - delay_background(2000, 0); - - printLine2ClearAndUpdate(); - menuOn = 0; + + //delay_background(2000, 0); + //printLine2ClearAndUpdate(); + //menuOn = 0; + menuClearExit(1000); } } @@ -655,10 +492,109 @@ void menuADCMonitor(int btn){ delay_background(200, 0); } //end of while - printLine2ClearAndUpdate(); - menuOn = 0; + //printLine2ClearAndUpdate(); + //menuOn = 0; + menuClearExit(0); } +//VFO Toggle and save VFO Information, modified by KD8CEC +void menuVfoToggle(int btn, char isUseDelayTime) +{ + if (!btn){ + if (vfoActive == VFO_A) + printLineF2(F("Select VFO B?")); + else + printLineF2(F("Select VFO A?")); + } + else { + FrequencyToVFO(1); + + if (vfoActive == VFO_B){ + //vfoB = frequency; + //vfoB_mode = modeToByte(); + //storeFrequencyAndMode(2); //vfoB -> eeprom + + vfoActive = VFO_A; + frequency = vfoA; + saveCheckFreq = frequency; + byteToMode(vfoA_mode, 0); + //printLineF2(F("Selected VFO A")); + } + else { + //vfoA = frequency; + //vfoA_mode = modeToByte(); + //storeFrequencyAndMode(1); //vfoA -> eeprom + + vfoActive = VFO_B; + frequency = vfoB; + saveCheckFreq = frequency; + byteToMode(vfoB_mode, 0); + //printLineF2(F("Selected VFO B")); + } + + ritDisable(); + setFrequency(frequency); + + //if (isUseDelayTime == 1) //Found Issue in wsjt-x Linux 32bit + // delay_background(500, 0); + + //printLine2ClearAndUpdate(); + //menuOn = 0; + menuClearExit(0); + } +} + +void menuRitToggle(int btn){ + if (!btn){ + if (ritOn == 1) + printLineF2(F("RIT:On, Off?")); + else + printLineF2(F("RIT:Off, 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(); + } + //delay_background(500, 0); + //printLine2ClearAndUpdate(); + //menuOn = 0; + menuClearExit(500); + } +} + +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("Split Off!")); + } + else { + splitOn = 1; + if (ritOn == 1) + ritOn = 0; + printLineF2(F("Split On!")); + } + + //delay_background(500, 0); + //printLine2ClearAndUpdate(); + //menuOn = 0; + menuClearExit(500); + } +} + + //Function to disbled transmission //by KD8CEC void menuTxOnOff(int btn, byte optionType){ @@ -677,9 +613,11 @@ void menuTxOnOff(int btn, byte optionType){ isTxType &= ~(optionType); printLineF2(F("TX ON!")); } - delay_background(500, 0); - printLine2ClearAndUpdate(); - menuOn = 0; + + //delay_background(500, 0); + //printLine2ClearAndUpdate(); + //menuOn = 0; + menuClearExit(500); } } @@ -702,21 +640,19 @@ void menuSetup(int btn){ modeCalibrate = false; printLineF2(F("Setup:Off")); } - delay_background(2000, 0); - printLine2Clear(); - menuOn = 0; + //delay_background(2000, 0); + //printLine2Clear(); + //menuOn = 0; + menuClearExit(1000); } } void menuExit(int btn){ - if (!btn){ printLineF2(F("Exit Menu?")); } - else{ - printLine2ClearAndUpdate(); - menuOn = 0; - } + else + menuClearExit(0); } void menuCWSpeed(int btn){ @@ -768,10 +704,11 @@ void menuCWSpeed(int btn){ printLineF2(F("CW Speed set!")); cwSpeed = 1200/wpm; EEPROM.put(CW_SPEED, cwSpeed); - delay_background(2000, 0); //} - printLine2ClearAndUpdate(); - menuOn = 0; + //delay_background(2000, 0); + //printLine2ClearAndUpdate(); + //menuOn = 0; + menuClearExit(1000); } //Builtin CW Keyer Logic by KD8CEC @@ -841,10 +778,11 @@ void menuSetupCwDelay(int btn){ printLineF2(F("CW Delay set!")); cwDelayTime = tmpCWDelay / 10; EEPROM.put(CW_DELAY, cwDelayTime); - delay_background(2000, 0); + //delay_background(2000, 0); //} - printLine2ClearAndUpdate(); - menuOn = 0; + //printLine2ClearAndUpdate(); + //menuOn = 0; + menuClearExit(1000); } //CW Time delay by KD8CEC @@ -890,10 +828,11 @@ void menuSetupTXCWInterval(int btn){ printLineF2(F("CW Start set!")); delayBeforeCWStartTime = tmpTXCWInterval / 2; EEPROM.put(CW_START, delayBeforeCWStartTime); - delay_background(2000, 0); + //delay_background(2000, 0); //} - printLine2ClearAndUpdate(); - menuOn = 0; + //printLine2ClearAndUpdate(); + //menuOn = 0; + menuClearExit(1000); } @@ -976,7 +915,8 @@ void factoryCalibration(int btn){ while(btnDown()) delay(50); - delay(100); + + menuClearExit(100); } void menuSetupCalibration(int btn){ @@ -1040,8 +980,9 @@ void menuSetupCalibration(int btn){ initOscillators(); //si5351_set_calibration(calibration); setFrequency(frequency); - printLine2ClearAndUpdate(); - menuOn = 0; + //printLine2ClearAndUpdate(); + //menuOn = 0; + menuClearExit(0); } void printCarrierFreq(unsigned long freq){ @@ -1114,8 +1055,9 @@ void menuSetupCarrier(int btn){ si5351bx_setfreq(0, cwmCarrier); //set back the carrier oscillator anyway, cw tx switches it off setFrequency(frequency); - printLine2ClearAndUpdate(); - menuOn = 0; + //printLine2ClearAndUpdate(); + //menuOn = 0; + menuClearExit(0); } //Append by KD8CEC @@ -1150,8 +1092,9 @@ void menuSetupCWCarrier(int btn){ si5351bx_setfreq(0, cwmCarrier); printCarrierFreq(cwmCarrier); - Check_Cat(0); //To prevent disconnections - delay(100); + //Check_Cat(0); //To prevent disconnections + //delay(100); + delay_background(100, 0); } //save the setting @@ -1168,10 +1111,12 @@ void menuSetupCWCarrier(int btn){ else si5351bx_setfreq(0, cwmCarrier); //set back the carrier oscillator anyway, cw tx switches it off - setFrequency(frequency); - printLine2ClearAndUpdate(); - menuOn = 0; + setFrequency(frequency); + //printLine2ClearAndUpdate(); + //menuOn = 0; + menuClearExit(0); } + //Modified by KD8CEC void menuSetupCwTone(int btn){ int knob = 0; @@ -1204,8 +1149,9 @@ void menuSetupCwTone(int btn){ itoa(sideTone, b, 10); printLine2(b); - delay(100); - Check_Cat(0); //To prevent disconnections + //delay(100); + //Check_Cat(0); //To prevent disconnections + delay_background(100, 0); } noTone(CW_TONE); //save the setting @@ -1217,8 +1163,9 @@ void menuSetupCwTone(int btn){ else sideTone = prev_sideTone; - printLine2ClearAndUpdate(); - menuOn = 0; + //printLine2ClearAndUpdate(); + //menuOn = 0; + menuClearExit(0); } //Lock Dial move by KD8CEC @@ -1256,8 +1203,9 @@ void doMenu(){ //Appened Lines by KD8CEC for Adjust Tune step and Set Dial lock while(btnDown()){ - delay(50); - Check_Cat(0); //To prevent disconnections + //delay(50); + //Check_Cat(0); //To prevent disconnections + delay_background(50, 0); if (btnDownTimeCount++ == (PRESS_ADJUST_TUNE / 50)) { //Set Tune Step printLineF2(F("Set Tune Step?")); @@ -1280,8 +1228,9 @@ void doMenu(){ while (digitalRead(PTT) == HIGH && !btnDown()) { - Check_Cat(0); //To prevent disconnections - delay(50); //debounce + //Check_Cat(0); //To prevent disconnections + //delay(50); //debounce + delay_background(50, 0); if (isNeedDisplay) { strcpy(b, "Tune Step:"); @@ -1348,7 +1297,7 @@ void doMenu(){ else if (select < 40) menuRitToggle(btnState); else if (select < 50) - menuIFSToggle(btnState); + menuIFSSetup(btnState); else if (select < 60) menuCWSpeed(btnState); else if (select < 70) @@ -1385,9 +1334,8 @@ void doMenu(){ //debounce the button while(btnDown()){ - delay(50); - Check_Cat(0); //To prevent disconnections + delay_background(50, 0); //To prevent disconnections } - delay(50); + //delay(50); } diff --git a/ubitx_20/ubitx_ui.ino b/ubitx_20/ubitx_ui.ino index f415268..5259bf1 100644 --- a/ubitx_20/ubitx_ui.ino +++ b/ubitx_20/ubitx_ui.ino @@ -235,7 +235,6 @@ char byteToChar(byte srcByte){ 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; // From 60777178a84273b2f1cb1fa03797b5659cb66d29 Mon Sep 17 00:00:00 2001 From: phdlee Date: Sat, 3 Feb 2018 17:07:11 +0900 Subject: [PATCH 061/173] TX Check in auto keysend --- ubitx_20/cw_autokey.ino | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/ubitx_20/cw_autokey.ino b/ubitx_20/cw_autokey.ino index 8c9103e..c9dba8b 100644 --- a/ubitx_20/cw_autokey.ino +++ b/ubitx_20/cw_autokey.ino @@ -365,6 +365,11 @@ void controlAutoCW(){ //check interval time, if you want adjust interval between chars, modify below if (isAutoCWHold == 0 && (millis() - autoCWbeforeTime > cwSpeed * 3)) { + if (!inTx){ //if not TX Status, change RX -> TX + keyDown = 0; + startTx(TX_CW, 0); //disable updateDisplay Command for reduce latency time + } + sendCWChar(EEPROM.read(CW_AUTO_DATA + autoCWSendCharIndex++)); if (autoCWSendCharIndex > autoCWSendCharEndIndex) { //finish auto cw send From 57cd385b8a06b0bc4124ab601a5cdbad7ebe0e3b Mon Sep 17 00:00:00 2001 From: phdlee Date: Mon, 5 Feb 2018 15:07:25 +0900 Subject: [PATCH 062/173] add vfo to channel, channel to vfo --- ubitx_20/ubitx_20.ino | 4 + ubitx_20/ubitx_menu.ino | 169 +++++++++++++++++++++++++++++++++++----- 2 files changed, 155 insertions(+), 18 deletions(-) diff --git a/ubitx_20/ubitx_20.ino b/ubitx_20/ubitx_20.ino index f36c00e..20abc2e 100644 --- a/ubitx_20/ubitx_20.ino +++ b/ubitx_20/ubitx_20.ino @@ -180,6 +180,10 @@ int count = 0; //to generally count ticks, loops, etc #define DISPLAY_OPTION1 361 //Display Option1 #define DISPLAY_OPTION2 362 //Display Option2 +#define CHANNEL_FREQ 630 //Channel 1 ~ 20, 1 Channel = 4 bytes +#define CHANNEL_DESC 710 //Channel 1 ~ 20, 1 Channel = 4 bytes +#define RESERVE3 770 //Reserve3 between Channel and Firmware id check + //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 diff --git a/ubitx_20/ubitx_menu.ino b/ubitx_20/ubitx_menu.ino index 12fabfd..b16d584 100644 --- a/ubitx_20/ubitx_menu.ino +++ b/ubitx_20/ubitx_menu.ino @@ -13,6 +13,7 @@ #define printLineF1(x) (printLineF(1, x)) #define printLineF2(x) (printLineF(0, x)) +//Current Frequency and mode to active VFO by KD8CEC void FrequencyToVFO(byte isSaveFreq) { //Save Frequency & Mode Information @@ -34,6 +35,7 @@ void FrequencyToVFO(byte isSaveFreq) } } +//Commonly called functions when exiting menus by KD8CEC void menuClearExit(int delayTime) { if (delayTime > 0) @@ -43,7 +45,7 @@ void menuClearExit(int delayTime) menuOn = 0; } -//Ham band move by KD8CEC +//Ham band or general band movement by KD8CEC void menuBand(int btn){ int knob = 0; int stepChangeCount = 0; @@ -198,6 +200,7 @@ void byteWithFreqToMode(byte modeValue){ } */ +//IF Shift function, BFO Change like RIT, by KD8CEC void menuIFSSetup(int btn){ int knob = 0; char needApplyChangeValue = 1; @@ -261,6 +264,7 @@ void menuIFSSetup(int btn){ } } +//Functions for CWL and CWU by KD8CEC void menuSelectMode(int btn){ int knob = 0; int selectModeType = 0; @@ -351,6 +355,131 @@ void menuSelectMode(int btn){ } } + +//Memory to VFO, VFO to Memory by KD8CEC +//select between MtoV and VtoM by isMemoryToVfo +void menuCHMemory(int btn, byte isMemoryToVfo){ + int knob = 0; + int selectChannel = 0; + byte isDisplayInfo = 1; + byte isCancel = 0; + int moveStep = 0; + unsigned long resultFreq, tmpFreq = 0; + byte loadMode = 0; + + if (!btn){ + if (isMemoryToVfo == 1) + printLine2("Channel To VFO?"); + else + printLine2("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?"); + } + 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) == 0x33) { //0x33 is display Chnnel Name + //display Channel Name + for (int i = 0; i < 5; i++) + c[i] = EEPROM.read(CHANNEL_DESC + 6 * selectChannel + i + 1); + } + 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; + } + /* + if (selectChannel < 10) + printLineFromEEPRom(0, 4, 0, userCallsignLength -1); //eeprom to lcd use offset (USER_CALLSIGN_DAT) + */ + + //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) + setFrequency(resultFreq); + byteToMode(loadMode, 1); + } + else + { + //Save current Frequency to Channel (selectChannel) + EEPROM.put(CHANNEL_FREQ + 4 * selectChannel, (frequency & 0x1FFFFFFF) | (modeToByte() << 29) ); + printLine2("Saved Frequency"); + } + } + + menuClearExit(500); + } +} + + //Select CW Key Type by KD8CEC void menuSetupKeyType(int btn){ int knob = 0; @@ -568,6 +697,7 @@ void menuRitToggle(int btn){ } } +//Split communication using VFOA and VFOB by KD8CEC void menuSplitOnOff(int btn){ if (!btn){ if (splitOn == 0) @@ -1269,7 +1399,6 @@ void doMenu(){ } //set tune step //Below codes are origial code with modified by KD8CEC - //Select menu menuOn = 2; while (menuOn){ @@ -1277,9 +1406,9 @@ void doMenu(){ btnState = btnDown(); if (i > 0){ - if (modeCalibrate && select + i < 200) + if (modeCalibrate && select + i < 220) select += i; - if (!modeCalibrate && select + i < 100) + if (!modeCalibrate && select + i < 120) select += i; } //if (i < 0 && select - i >= 0) @@ -1301,32 +1430,36 @@ void doMenu(){ else if (select < 60) menuCWSpeed(btnState); else if (select < 70) - menuSplitOnOff(btnState); //SplitOn / off + menuSplitOnOff(btnState); //SplitOn / off else if (select < 80) - menuCWAutoKey(btnState); + menuCHMemory(btnState, 0); //VFO to Memroy else if (select < 90) - menuSetup(btnState); + menuCHMemory(btnState, 1); //Memory to VFO else if (select < 100) + menuCWAutoKey(btnState); + else if (select < 110) + menuSetup(btnState); + else if (select < 120) menuExit(btnState); - else if (select < 110 && modeCalibrate) - menuSetupCalibration(btnState); //crystal - else if (select < 120 && modeCalibrate) - menuSetupCarrier(btnState); //lsb else if (select < 130 && modeCalibrate) - menuSetupCWCarrier(btnState); //lsb + menuSetupCalibration(btnState); //crystal else if (select < 140 && modeCalibrate) - menuSetupCwTone(btnState); + menuSetupCarrier(btnState); //lsb else if (select < 150 && modeCalibrate) - menuSetupCwDelay(btnState); + menuSetupCWCarrier(btnState); //lsb else if (select < 160 && modeCalibrate) - menuSetupTXCWInterval(btnState); + menuSetupCwTone(btnState); else if (select < 170 && modeCalibrate) - menuSetupKeyType(btnState); + menuSetupCwDelay(btnState); else if (select < 180 && modeCalibrate) - menuADCMonitor(btnState); + menuSetupTXCWInterval(btnState); else if (select < 190 && modeCalibrate) - menuTxOnOff(btnState, 0x01); //TX OFF / ON + menuSetupKeyType(btnState); else if (select < 200 && modeCalibrate) + menuADCMonitor(btnState); + else if (select < 210 && modeCalibrate) + menuTxOnOff(btnState, 0x01); //TX OFF / ON + else if (select < 220 && modeCalibrate) menuExit(btnState); Check_Cat(0); //To prevent disconnections From 14888bb7d777f9f03e7509b1fe0025c45f4fd2fe Mon Sep 17 00:00:00 2001 From: phdlee Date: Mon, 5 Feb 2018 16:46:37 +0900 Subject: [PATCH 063/173] change channel name display code --- ubitx_20/ubitx_menu.ino | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/ubitx_20/ubitx_menu.ino b/ubitx_20/ubitx_menu.ino index b16d584..08d9bb6 100644 --- a/ubitx_20/ubitx_menu.ino +++ b/ubitx_20/ubitx_menu.ino @@ -394,10 +394,12 @@ void menuCHMemory(int btn, byte isMemoryToVfo){ resultFreq = resultFreq & 0x1FFFFFFF; //display channel description - if (selectChannel < 10 && EEPROM.read(CHANNEL_DESC + 6 * selectChannel) == 0x33) { //0x33 is display Chnnel Name + 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 From 3b4aaa664cf9e6efb88c0fda3548021fca21ccd0 Mon Sep 17 00:00:00 2001 From: phdlee Date: Tue, 6 Feb 2018 16:13:05 +0900 Subject: [PATCH 064/173] version0.35 --- ubitx_20/cat_libs.ino | 43 +++++++++++++++++++++++++--------------- ubitx_20/ubitx_20.ino | 1 - ubitx_20/ubitx_idle.ino | 20 ++++++++++--------- ubitx_20/ubitx_keyer.ino | 6 ++---- ubitx_20/ubitx_menu.ino | 7 +++---- ubitx_20/ubitx_ui.ino | 2 -- 6 files changed, 43 insertions(+), 36 deletions(-) diff --git a/ubitx_20/cat_libs.ino b/ubitx_20/cat_libs.ino index 85a4111..cb26bcc 100644 --- a/ubitx_20/cat_libs.ino +++ b/ubitx_20/cat_libs.ino @@ -109,7 +109,8 @@ void CatSetFreq(byte fromType) //#define BCD_LEN 9 //PROTOCOL : 0x03 //Computer <-(frequency)-> TRCV CAT_BUFF -void CatGetFreqMode(unsigned long freq, byte fromType) +//void CatGetFreqMode(unsigned long freq, byte fromType) +void CatGetFreqMode(unsigned long freq) //for remove warning messages { int i; byte tmpValue; @@ -149,9 +150,14 @@ void CatGetFreqMode(unsigned long freq, byte fromType) SendCatData(5); } -void CatSetSplit(boolean isSplit, byte fromType) +//void CatSetSplit(boolean isSplit, byte fromType) +void CatSetSplit(boolean isSplit) //for remove warning messages { - + if (isSplit) + splitOn = 1; + else + splitOn = 0; + Serial.write(ACK); } @@ -193,7 +199,7 @@ void CatSetPTT(boolean isPTTOn, byte fromType) void CatVFOToggle(boolean isSendACK, byte fromType) { if (fromType != 2 && fromType != 3) { - menuVfoToggle(1, 0); + menuVfoToggle(1); } if (isSendACK) @@ -232,7 +238,8 @@ void CatSetMode(byte tmpMode, byte fromType) } //Read EEProm by uBITX Manager Software -void ReadEEPRom(byte fromType) +//void ReadEEPRom(byte fromType) +void ReadEEPRom() //for remove warnings. { //5BYTES //CAT_BUFF[0] [1] [2] [3] [4] //4 COMMAND @@ -255,7 +262,8 @@ void ReadEEPRom(byte fromType) } //Write just proecess 1byes -void WriteEEPRom(byte fromType) +//void WriteEEPRom(byte fromType) +void WriteEEPRom(void) //for remove warning { //5BYTES uint16_t eepromStartIndex = CAT_BUFF[0] + CAT_BUFF[1] * 256; @@ -275,7 +283,8 @@ void WriteEEPRom(byte fromType) } } -void ReadEEPRom_FT817(byte fromType) +//void ReadEEPRom_FT817(byte fromType) +void ReadEEPRom_FT817(void) //for remove warnings { byte temp0 = CAT_BUFF[0]; byte temp1 = CAT_BUFF[1]; @@ -601,7 +610,8 @@ void WriteEEPRom_FT817(byte fromType) Serial.write(ACK); } -void CatRxStatus(byte fromType) +//void CatRxStatus(byte fromType) +void CatRxStatus(void) //for remove warning { byte sMeterValue = 1; @@ -621,7 +631,8 @@ void CatRxStatus(byte fromType) } -void CatTxStatus(byte fromType) +//void CatTxStatus(byte fromType) +void CatTxStatus(void) //for remove warning { boolean isHighSWR = false; boolean isSplitOn = false; @@ -722,11 +733,11 @@ void Check_Cat(byte fromType) case 0x02 : //Split On case 0x82: //Split Off - CatSetSplit(CAT_BUFF[4] == 0x02, fromType); + CatSetSplit(CAT_BUFF[4] == 0x02); break; case 0x03 : //Read Frequency and mode - CatGetFreqMode(frequency, fromType); + CatGetFreqMode(frequency); break; case 0x07 : //Set Operating Mode @@ -743,24 +754,24 @@ void Check_Cat(byte fromType) break; case 0xDB: //Read uBITX EEPROM Data - ReadEEPRom(fromType); //Call by uBITX Manager Program + ReadEEPRom(); //Call by uBITX Manager Program break; case 0xBB: //Read FT-817 EEPROM Data (for comfirtable) - ReadEEPRom_FT817(fromType); + ReadEEPRom_FT817(); break; case 0xDC: //Write uBITX EEPROM Data - WriteEEPRom(fromType); //Call by uBITX Manager Program + WriteEEPRom(); //Call by uBITX Manager Program break; case 0xBC: //Write FT-817 EEPROM Data (for comfirtable) WriteEEPRom_FT817(fromType); break; case 0xE7 : //Read RX Status - CatRxStatus(fromType); + CatRxStatus(); break; case 0xF7: //Read TX Status - CatTxStatus(fromType); + CatTxStatus(); break; default: /* diff --git a/ubitx_20/ubitx_20.ino b/ubitx_20/ubitx_20.ino index 20abc2e..d900638 100644 --- a/ubitx_20/ubitx_20.ino +++ b/ubitx_20/ubitx_20.ino @@ -696,7 +696,6 @@ byte lastMovedirection = 0; //0 : stop, 1 : cw, 2 : ccw void doTuningWithThresHold(){ int s = 0; unsigned long prev_freq; - long incdecValue = 0; if ((vfoActive == VFO_A && ((isDialLock & 0x01) == 0x01)) || (vfoActive == VFO_B && ((isDialLock & 0x02) == 0x02))) diff --git a/ubitx_20/ubitx_idle.ino b/ubitx_20/ubitx_idle.ino index 974aef6..178dcdf 100644 --- a/ubitx_20/ubitx_idle.ino +++ b/ubitx_20/ubitx_idle.ino @@ -213,16 +213,18 @@ void updateLine2Buffer(char isDirectCall) //meterType : 0 = S.Meter, 1 : P.Meter void DisplayMeter(byte meterType, byte meterValue, char drawPosition) { - drawMeter(meterValue); //call original source code - int lineNumber = 0; - if ((displayOption1 & 0x01) == 0x01) - lineNumber = 1; + if (meterType == 0 || meterType == 1 || meterType == 2) + { + drawMeter(meterValue); //call original source code + int lineNumber = 0; + if ((displayOption1 & 0x01) == 0x01) + lineNumber = 1; + + lcd.setCursor(drawPosition, lineNumber); - lcd.setCursor(drawPosition, lineNumber); - - for (int i = 0; i < 6; i++) //meter 5 + +db 1 = 6 - lcd.write(lcdMeter[i]); - + for (int i = 0; i < 6; i++) //meter 5 + +db 1 = 6 + lcd.write(lcdMeter[i]); + } } byte testValue = 0; diff --git a/ubitx_20/ubitx_keyer.ino b/ubitx_20/ubitx_keyer.ino index 04163af..a4c804d 100644 --- a/ubitx_20/ubitx_keyer.ino +++ b/ubitx_20/ubitx_keyer.ino @@ -90,13 +90,13 @@ void cwKeyUp(){ #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 long ktimer; +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) { - unsigned char tmpKeyerControl; + unsigned char tmpKeyerControl = 0; int paddle = analogRead(ANALOG_KEYER); if (paddle >= cwAdcDashFrom && paddle <= cwAdcDashTo) @@ -126,9 +126,7 @@ char update_PaddleLatch(byte isUpdateKeyState) { // modified by KD8CEC ******************************************************************************/ void cwKeyer(void){ - byte paddle; lastPaddle = 0; - int dot,dash; bool continue_loop = true; unsigned tmpKeyControl = 0; diff --git a/ubitx_20/ubitx_menu.ino b/ubitx_20/ubitx_menu.ino index 08d9bb6..4cf9303 100644 --- a/ubitx_20/ubitx_menu.ino +++ b/ubitx_20/ubitx_menu.ino @@ -362,7 +362,6 @@ void menuCHMemory(int btn, byte isMemoryToVfo){ int knob = 0; int selectChannel = 0; byte isDisplayInfo = 1; - byte isCancel = 0; int moveStep = 0; unsigned long resultFreq, tmpFreq = 0; byte loadMode = 0; @@ -472,7 +471,7 @@ void menuCHMemory(int btn, byte isMemoryToVfo){ else { //Save current Frequency to Channel (selectChannel) - EEPROM.put(CHANNEL_FREQ + 4 * selectChannel, (frequency & 0x1FFFFFFF) | (modeToByte() << 29) ); + EEPROM.put(CHANNEL_FREQ + 4 * selectChannel, (frequency & 0x1FFFFFFF) | (((unsigned long)modeToByte()) << 29) ); printLine2("Saved Frequency"); } } @@ -629,7 +628,7 @@ void menuADCMonitor(int btn){ } //VFO Toggle and save VFO Information, modified by KD8CEC -void menuVfoToggle(int btn, char isUseDelayTime) +void menuVfoToggle(int btn) { if (!btn){ if (vfoActive == VFO_A) @@ -1422,7 +1421,7 @@ void doMenu(){ else if (select < 10) menuBand(btnState); else if (select < 20) - menuVfoToggle(btnState, 1); + menuVfoToggle(btnState); else if (select < 30) menuSelectMode(btnState); else if (select < 40) diff --git a/ubitx_20/ubitx_ui.ino b/ubitx_20/ubitx_ui.ino index 5259bf1..34fe5ad 100644 --- a/ubitx_20/ubitx_ui.ino +++ b/ubitx_20/ubitx_ui.ino @@ -99,8 +99,6 @@ void initMeter(){ //0 ~ 25 : 30 over : + 10 void drawMeter(int needle) { //5Char + O over - int drawCharLength = needle / 5; - int drawCharLengthLast = needle % 5; int i; for (i = 0; i < 5; i++) { From c1d81d9d5bf715dd7626b28492fc45b850087efa Mon Sep 17 00:00:00 2001 From: phdlee Date: Thu, 8 Feb 2018 01:15:39 +0900 Subject: [PATCH 065/173] Update README.md --- README.md | 22 ++++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 9074023..09924b2 100644 --- a/README.md +++ b/README.md @@ -1,9 +1,7 @@ #IMPORTANT INFORMATION ---------------------------------------------------------------------------- -- 0.33 Version Test only download. almost complete -- Beta 0.26 and Beta 0.261, Beta 0.262,0.27 is complete test, 0.28 is tested. -- 0.31 is tested but has not critical bug -- You can download and use it (Release section). +- Now Release Version 1.0 on my blog (http://www.hamskey.com) +- You can download and compiled hex file and uBITX Manager application on my blog (http://www.hamskey.com) #NOTICE ---------------------------------------------------------------------------- @@ -31,8 +29,6 @@ The copyright information of the original is below. KD8CEC ---------------------------------------------------------------------------- Prepared or finished tasks for the next version - - Most of them are implemented and included in version 0.27. - - User Interface on LCD -> Option by user (not need) - Include WSPR Beacone function - (implement other new repository) complete experiment need solve : Big code size (over 100%, then remove some functions for experment) @@ -41,6 +37,20 @@ Prepared or finished tasks for the next version ---------------------------------------------------------------------------- ## REVISION RECORD +1.0 + - todo : 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. From a7684284d24a2b440adc25e52505343c7c2a75a0 Mon Sep 17 00:00:00 2001 From: phdlee Date: Thu, 8 Feb 2018 12:45:54 +0900 Subject: [PATCH 066/173] write eeprom cycle test and reconvery --- ubitx_20/ubitx_20.ino | 17 +++--- ubitx_20/ubitx_menu.ino | 121 +++++++++++++++++++--------------------- 2 files changed, 66 insertions(+), 72 deletions(-) diff --git a/ubitx_20/ubitx_20.ino b/ubitx_20/ubitx_20.ino index d900638..6ec6fdb 100644 --- a/ubitx_20/ubitx_20.ino +++ b/ubitx_20/ubitx_20.ino @@ -156,10 +156,10 @@ int count = 0; //to generally count ticks, loops, etc #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 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 : @@ -907,13 +907,13 @@ void initSettings(){ if ((3 < tuneTXType && tuneTXType < 100) || 103 < tuneTXType || useHamBandCount < 1 || findedValidValueCount < 5) { tuneTXType = 2; - //if empty band Information, auto insert default region 1 frequency range + //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 1 + 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; @@ -1085,7 +1085,7 @@ void setup() //Serial.begin(9600); lcd.begin(16, 2); - printLineF(1, F("CECBT v0.35")); + printLineF(1, F("CECBT v1.00")); Init_Cat(38400, SERIAL_8N1); initMeter(); //not used in this build @@ -1139,6 +1139,7 @@ void checkAutoSaveFreqMode() //check time for Frequency auto save if (millis() - saveCheckTime > saveIntervalSec * 1000) { + /* if (vfoActive == VFO_A) { vfoA = frequency; @@ -1151,6 +1152,8 @@ void checkAutoSaveFreqMode() vfoB_mode = modeToByte(); storeFrequencyAndMode(2); } + */ + FrequencyToVFO(1); } } } diff --git a/ubitx_20/ubitx_menu.ino b/ubitx_20/ubitx_menu.ino index 4cf9303..8ae6e53 100644 --- a/ubitx_20/ubitx_menu.ino +++ b/ubitx_20/ubitx_menu.ino @@ -64,7 +64,7 @@ void menuBand(int btn){ btnPressCount = 0; if (tuneTXType > 0) { //Just toggle 0 <-> 2, if tuneTXType is 100, 100 -> 0 -> 2 tuneTXType = 0; - printLineF2(F("Full range mode")); + printLineF2(F("General mode")); } else { tuneTXType = 2; @@ -289,18 +289,13 @@ void menuSelectMode(int btn){ beforeMode = selectModeType; - while(!btnDown() && digitalRead(PTT) == HIGH){ + while(!btnDown()){ //Display Mode Name - printLineF1(F("LSB USB CWL CWU")); - if (selectModeType == 0) - printLineF1(F("LSB")); - else if (selectModeType == 1) - printLineF1(F("USB")); - else if (selectModeType == 2) - printLineF1(F("CWL")); - else if (selectModeType == 3) - printLineF1(F("CWU")); - + memset(c, 0, sizeof(c)); + strcpy(c, " LSB USB CWL CWU"); + c[selectModeType * 4] = '>'; + printLine1(c); + knob = enc_read(); if (knob != 0) @@ -320,7 +315,8 @@ void menuSelectMode(int btn){ } } - Check_Cat(0); //To prevent disconnections + //Check_Cat(0); //To prevent disconnections + delay_background(50, 0); } if (beforeMode != selectModeType) { @@ -368,9 +364,9 @@ void menuCHMemory(int btn, byte isMemoryToVfo){ if (!btn){ if (isMemoryToVfo == 1) - printLine2("Channel To VFO?"); + printLineF2(F("Channel To VFO?")); else - printLine2("VFO To Channel?"); + printLineF2(F("VFO To Channel?")); } else { delay_background(500, 0); @@ -490,7 +486,7 @@ void menuSetupKeyType(int btn){ printLineF2(F("Change Key Type?")); } else { - printLineF2(F("Press to set Key")); + //printLineF2(F("Press to set Key")); //for reduce usable flash memory delay_background(500, 0); selectedKeyType = cwKeyType; @@ -538,10 +534,7 @@ void menuSetupKeyType(int btn){ else keyerControl |= IAMBICB; } - - //delay_background(2000, 0); - //printLine2ClearAndUpdate(); - //menuOn = 0; + menuClearExit(1000); } } @@ -572,18 +565,11 @@ void menuADCMonitor(int btn){ adcPinA0 = analogRead(A0); //A0(BLACK, EncoderA) adcPinA1 = analogRead(A1); //A1(BROWN, EncoderB) adcPinA2 = analogRead(A2); //A2(RED, Function Key) - adcPinA3 = analogRead(A3); //A3(ORANGE, CW Key) - adcPinA6 = analogRead(A6); //A6(BLUE, Ptt) + adcPinA3 = analogRead(A3); //A3(PTT) + adcPinA6 = analogRead(A6); //A6(KEYER) adcPinA7 = analogRead(A7); //A7(VIOLET, Spare) -/* - sprintf(c, "%4d %4d %4d", adcPinA0, adcPinA1, adcPinA2); - printLine1(c); - sprintf(c, "%4d %4d %4d", adcPinA3, adcPinA6, adcPinA7); - printLine2(c); -*/ - - if (adcPinA6 < 10) { + if (adcPinA3 < 50) { if (pressKeyTime == 0) pressKeyTime = millis(); else if (pressKeyTime < (millis() - 3000)) @@ -622,8 +608,6 @@ void menuADCMonitor(int btn){ delay_background(200, 0); } //end of while - //printLine2ClearAndUpdate(); - //menuOn = 0; menuClearExit(0); } @@ -648,7 +632,6 @@ void menuVfoToggle(int btn) frequency = vfoA; saveCheckFreq = frequency; byteToMode(vfoA_mode, 0); - //printLineF2(F("Selected VFO A")); } else { //vfoA = frequency; @@ -664,16 +647,11 @@ void menuVfoToggle(int btn) ritDisable(); setFrequency(frequency); - - //if (isUseDelayTime == 1) //Found Issue in wsjt-x Linux 32bit - // delay_background(500, 0); - - //printLine2ClearAndUpdate(); - //menuOn = 0; menuClearExit(0); } } +//modified for reduce used flash memory by KD8CEC void menuRitToggle(int btn){ if (!btn){ if (ritOn == 1) @@ -691,9 +669,7 @@ void menuRitToggle(int btn){ printLineF2(F("RIT is OFF")); ritDisable(); } - //delay_background(500, 0); - //printLine2ClearAndUpdate(); - //menuOn = 0; + menuClearExit(500); } } @@ -763,14 +739,18 @@ void menuSetup(int btn){ else printLineF2(F("Setup Off?")); }else { + modeCalibrate = ! modeCalibrate; + /* if (!modeCalibrate){ modeCalibrate = true; - printLineF2(F("Setup:On")); + //printLineF2(F("Setup:On")); } else { modeCalibrate = false; - printLineF2(F("Setup:Off")); + //printLineF2(F("Setup:Off")); } + */ + //delay_background(2000, 0); //printLine2Clear(); //menuOn = 0; @@ -801,14 +781,14 @@ void menuCWSpeed(int btn){ return; } - printLineF1(F("Press to set WPm")); + printLineF1(F("Press to set WPM")); strcpy(b, "WPM:"); itoa(wpm,c, 10); strcat(b, c); printLine2(b); delay_background(300, 0); - while(!btnDown() && digitalRead(PTT) == HIGH){ + while(!btnDown()){ knob = enc_read(); if (knob != 0){ @@ -872,8 +852,7 @@ void menuSetupCwDelay(int btn){ int tmpCWDelay = cwDelayTime * 10; if (!btn){ - strcpy(b, "CW TX->RX Delay"); - printLine2(b); + printLineF2(F("CW TX->RX Delay")); return; } @@ -884,7 +863,7 @@ void menuSetupCwDelay(int btn){ printLine2(b); delay_background(300, 0); - while(!btnDown() && digitalRead(PTT) == HIGH){ + while(!btnDown()){ knob = enc_read(); if (knob != 0){ if (tmpCWDelay > 3 && knob < 0) @@ -918,34 +897,47 @@ void menuSetupCwDelay(int btn){ //CW Time delay by KD8CEC void menuSetupTXCWInterval(int btn){ + char needDisplayInformation = 1; int knob = 0; int tmpTXCWInterval = delayBeforeCWStartTime * 2; if (!btn){ - strcpy(b, "CW Start Delay"); - printLine2(b); + printLineF2(F("CW Start Delay")); return; } printLineF1(F("Press, set Delay")); + /* strcpy(b, "Start Delay:"); itoa(tmpTXCWInterval,c, 10); strcat(b, c); printLine2(b); + */ delay_background(300, 0); - while(!btnDown() && digitalRead(PTT) == HIGH){ + while(!btnDown()){ + + if (needDisplayInformation == 1) { + strcpy(b, "Start Delay:"); + itoa(tmpTXCWInterval,c, 10); + strcat(b, c); + printLine2(b); + needDisplayInformation = 0; + } + knob = enc_read(); if (knob != 0){ if (tmpTXCWInterval > 0 && knob < 0) tmpTXCWInterval -= 2; if (tmpTXCWInterval < 500 && knob > 0) tmpTXCWInterval += 2; - + /* strcpy(b, "Start Delay:"); itoa(tmpTXCWInterval,c, 10); strcat(b, c); printLine2(b); + */ + needDisplayInformation = 1; } //abort if this button is down if (btnDown()) @@ -1280,8 +1272,6 @@ void menuSetupCwTone(int btn){ itoa(sideTone, b, 10); printLine2(b); - //delay(100); - //Check_Cat(0); //To prevent disconnections delay_background(100, 0); } noTone(CW_TONE); @@ -1307,20 +1297,23 @@ void setDialLock(byte tmpLock, byte fromMode) { isDialLock &= ~(vfoActive == VFO_A ? 0x01 : 0x02); if (fromMode == 2 || fromMode == 3) return; - + + //for reduce using flash memory + /* if (tmpLock == 1) printLineF2(F("Dial Lock ON")); else printLineF2(F("Dial Lock OFF")); + */ delay_background(1000, 0); printLine2ClearAndUpdate(); } -unsigned int btnDownTimeCount; +byte btnDownTimeCount; -#define PRESS_ADJUST_TUNE 1000 -#define PRESS_LOCK_CONTROL 2000 +#define PRESS_ADJUST_TUNE 20 //1000msec 20 * 50 = 1000milisec +#define PRESS_LOCK_CONTROL 40 //2000msec 40 * 50 = 2000milisec //Modified by KD8CEC void doMenu(){ @@ -1334,14 +1327,12 @@ void doMenu(){ //Appened Lines by KD8CEC for Adjust Tune step and Set Dial lock while(btnDown()){ - //delay(50); - //Check_Cat(0); //To prevent disconnections delay_background(50, 0); - if (btnDownTimeCount++ == (PRESS_ADJUST_TUNE / 50)) { //Set Tune Step + if (btnDownTimeCount++ == (PRESS_ADJUST_TUNE)) { //Set Tune Step printLineF2(F("Set Tune Step?")); } - else if (btnDownTimeCount > (PRESS_LOCK_CONTROL / 50)) { //check long time Down Button -> 2.5 Second => Lock + 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 @@ -1352,12 +1343,12 @@ void doMenu(){ delay(50); //debounce //ADJUST TUNE STEP - if (btnDownTimeCount > (PRESS_ADJUST_TUNE / 50)) + if (btnDownTimeCount > PRESS_ADJUST_TUNE) { printLineF1(F("Press to set step")); isNeedDisplay = 1; //check to need display for display current value - while (digitalRead(PTT) == HIGH && !btnDown()) + while (!btnDown()) { //Check_Cat(0); //To prevent disconnections //delay(50); //debounce From 1e9576ddc2656325bc2380d9597eecb9d03f91fa Mon Sep 17 00:00:00 2001 From: phdlee Date: Fri, 9 Feb 2018 01:11:48 +0900 Subject: [PATCH 067/173] fixed cat with cw key (IA, IB) --- ubitx_20/cat_libs.ino | 3 ++- ubitx_20/ubitx_20.ino | 2 +- ubitx_20/ubitx_keyer.ino | 2 +- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/ubitx_20/cat_libs.ino b/ubitx_20/cat_libs.ino index cb26bcc..c9dca39 100644 --- a/ubitx_20/cat_libs.ino +++ b/ubitx_20/cat_libs.ino @@ -163,7 +163,8 @@ void CatSetSplit(boolean isSplit) //for remove warning messages void CatSetPTT(boolean isPTTOn, byte fromType) { - if (fromType == 2 || fromType == 3) { + // + if ((!inTx) && (fromType == 2 || fromType == 3)) { Serial.write(ACK); return; } diff --git a/ubitx_20/ubitx_20.ino b/ubitx_20/ubitx_20.ino index 6ec6fdb..c0768e6 100644 --- a/ubitx_20/ubitx_20.ino +++ b/ubitx_20/ubitx_20.ino @@ -1085,7 +1085,7 @@ void setup() //Serial.begin(9600); lcd.begin(16, 2); - printLineF(1, F("CECBT v1.00")); + printLineF(1, F("CECBT v1.01")); Init_Cat(38400, SERIAL_8N1); initMeter(); //not used in this build diff --git a/ubitx_20/ubitx_keyer.ino b/ubitx_20/ubitx_keyer.ino index a4c804d..9aed3d3 100644 --- a/ubitx_20/ubitx_keyer.ino +++ b/ubitx_20/ubitx_keyer.ino @@ -204,7 +204,7 @@ void cwKeyer(void){ break; } - Check_Cat(3); + Check_Cat(2); } //end of while } else{ From a374297d49bbb66dd93ea38c355c91f1e30aacf4 Mon Sep 17 00:00:00 2001 From: phdlee Date: Fri, 9 Feb 2018 13:42:36 +0900 Subject: [PATCH 068/173] Update README.md --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 09924b2..0bdc85b 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,7 @@ #IMPORTANT INFORMATION ---------------------------------------------------------------------------- -- Now Release Version 1.0 on my blog (http://www.hamskey.com) +- A bug was found in version 1.0, When CW Keytype is set to IAMBCA and IAMBCB, there was a problem that switching to RX is not performed well when CAT communication is performed. If CW key type is straight, it works normally. This bug has been fixed and changed to version 1.01. +- Now Release Version 1.01 on my blog (http://www.hamskey.com) - You can download and compiled hex file and uBITX Manager application on my blog (http://www.hamskey.com) #NOTICE From ed767f2e3485d2c039c7049eaadf6cf7db0deb8a Mon Sep 17 00:00:00 2001 From: phdlee Date: Sat, 10 Feb 2018 13:29:30 +0900 Subject: [PATCH 069/173] CW Start Delay applied New CW Logic --- ubitx_20/ubitx_keyer.ino | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/ubitx_20/ubitx_keyer.ino b/ubitx_20/ubitx_keyer.ino index 9aed3d3..be8c2f6 100644 --- a/ubitx_20/ubitx_keyer.ino +++ b/ubitx_20/ubitx_keyer.ino @@ -172,6 +172,9 @@ void cwKeyer(void){ 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); @@ -212,6 +215,9 @@ void cwKeyer(void){ 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); From bbdd0947d3c167017ab8751185c77503c28bc7ca Mon Sep 17 00:00:00 2001 From: phdlee Date: Sat, 10 Feb 2018 13:31:51 +0900 Subject: [PATCH 070/173] Update README.md --- README.md | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 0bdc85b..a471f5c 100644 --- a/README.md +++ b/README.md @@ -38,8 +38,14 @@ Prepared or finished tasks for the next version ---------------------------------------------------------------------------- ## REVISION RECORD +1.02 (working) + - Applied CW Start Delay to New CW Key logic + +1.01 + - Fixed Cat problem with (IAMBIC A or B Selected) + 1.0 - - todo : rename 0.30 to 1.0 + - rename 0.30 to 1.0 0.35 - vfo to channel bug fixed (not saved mode -> fixed, channel has frequency and mode) From 04949cdb93b520e2afd4a3a85a08b37be5d98eee Mon Sep 17 00:00:00 2001 From: phdlee Date: Sat, 10 Feb 2018 13:41:12 +0900 Subject: [PATCH 071/173] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index a471f5c..5610f98 100644 --- a/README.md +++ b/README.md @@ -39,7 +39,7 @@ Prepared or finished tasks for the next version ---------------------------------------------------------------------------- ## REVISION RECORD 1.02 (working) - - Applied CW Start Delay to New CW Key logic + - 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.) 1.01 - Fixed Cat problem with (IAMBIC A or B Selected) From 81333e7af4fd85a9b01f2b81b5c2b81ca5178a1a Mon Sep 17 00:00:00 2001 From: phdlee Date: Sat, 10 Feb 2018 15:07:56 +0900 Subject: [PATCH 072/173] modified CW Key Logic for AutoKey and reduce cpu use rate, reduce program memory --- ubitx_20/ubitx_20.ino | 22 ++-------------------- ubitx_20/ubitx_keyer.ino | 9 +++++---- ubitx_20/ubitx_menu.ino | 4 ++-- 3 files changed, 9 insertions(+), 26 deletions(-) diff --git a/ubitx_20/ubitx_20.ino b/ubitx_20/ubitx_20.ino index c0768e6..6f81cb2 100644 --- a/ubitx_20/ubitx_20.ino +++ b/ubitx_20/ubitx_20.ino @@ -1116,11 +1116,6 @@ void setup() factory_alignment(); } - -//for debug -int dbgCnt = 0; -byte flasher = 0; - //Auto save Frequency and Mode with Protected eeprom life by KD8CEC void checkAutoSaveFreqMode() { @@ -1139,21 +1134,8 @@ void checkAutoSaveFreqMode() //check time for Frequency auto save if (millis() - saveCheckTime > saveIntervalSec * 1000) { - /* - if (vfoActive == VFO_A) - { - vfoA = frequency; - vfoA_mode = modeToByte(); - storeFrequencyAndMode(1); - } - else - { - vfoB = frequency; - vfoB_mode = modeToByte(); - storeFrequencyAndMode(2); - } - */ FrequencyToVFO(1); + saveCheckTime = 0; //for reduce cpu use rate } } } @@ -1180,11 +1162,11 @@ void loop(){ if (isCWAutoMode == 0 && beforeIdle_ProcessTime < millis() - 250) { 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); - checkAutoSaveFreqMode(); } diff --git a/ubitx_20/ubitx_keyer.ino b/ubitx_20/ubitx_keyer.ino index be8c2f6..308bf7d 100644 --- a/ubitx_20/ubitx_keyer.ino +++ b/ubitx_20/ubitx_keyer.ino @@ -235,13 +235,14 @@ void cwKeyer(void){ keyDown = 0; stopTx(); } - if (!cwTimeout) - return; + //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); - continue; + //delay_background(5, 3); //removed by KD8CEC + //continue; //removed by KD8CEC + return; //Tx stop control by Main Loop } Check_Cat(2); diff --git a/ubitx_20/ubitx_menu.ino b/ubitx_20/ubitx_menu.ino index 8ae6e53..4542a0c 100644 --- a/ubitx_20/ubitx_menu.ino +++ b/ubitx_20/ubitx_menu.ino @@ -1345,7 +1345,7 @@ void doMenu(){ //ADJUST TUNE STEP if (btnDownTimeCount > PRESS_ADJUST_TUNE) { - printLineF1(F("Press to set step")); + printLineF1(F("Press to set")); isNeedDisplay = 1; //check to need display for display current value while (!btnDown()) @@ -1382,7 +1382,7 @@ void doMenu(){ } } //end of while - printLineF2(F("Changed Step!")); + //printLineF2(F("Changed Step!")); //remarked for reduce program memory by KD8CEC //SAVE EEPROM EEPROM.put(TUNING_STEP, tuneStepIndex); delay_background(500, 0); From e532dccce78d6fffee18e3a7ea8e81a85ed36e11 Mon Sep 17 00:00:00 2001 From: phdlee Date: Sat, 10 Feb 2018 15:10:55 +0900 Subject: [PATCH 073/173] Update README.md --- README.md | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 5610f98..6129344 100644 --- a/README.md +++ b/README.md @@ -39,11 +39,13 @@ Prepared or finished tasks for the next version ---------------------------------------------------------------------------- ## REVISION RECORD 1.02 (working) - - 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.) - + - 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 From 277666f82fc08ec7eb56a1cd03c3579f861db196 Mon Sep 17 00:00:00 2001 From: phdlee Date: Sat, 10 Feb 2018 18:34:21 +0900 Subject: [PATCH 074/173] Konstantinos (SV1ONW) shared the usage of uBITX Manager on Linux. Konstantinos (SV1ONW) shared the usage of uBITX Manager on Linux. --- ubitxmanager ubuntu.odt | Bin 0 -> 24818 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 ubitxmanager ubuntu.odt diff --git a/ubitxmanager ubuntu.odt b/ubitxmanager ubuntu.odt new file mode 100644 index 0000000000000000000000000000000000000000..514b20b609a69f92b351ff6dbd200683e6662c1c GIT binary patch literal 24818 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 Date: Tue, 13 Feb 2018 19:54:19 +0900 Subject: [PATCH 075/173] Change RIT tune step (freq tune step) --- ubitx_20/ubitx_20.ino | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/ubitx_20/ubitx_20.ino b/ubitx_20/ubitx_20.ino index 6f81cb2..0e66cc8 100644 --- a/ubitx_20/ubitx_20.ino +++ b/ubitx_20/ubitx_20.ino @@ -749,9 +749,11 @@ void doRIT(){ unsigned long old_freq = frequency; if (knob < 0) - frequency -= 100l; + frequency -= (arTuneStep[tuneStepIndex -1]); // + //frequency -= 100l; else if (knob > 0) - frequency += 100; + frequency += (arTuneStep[tuneStepIndex -1]); // + //frequency += 100; if (old_freq != frequency){ setFrequency(frequency); From 4a6909f361e3bd3afbedcf8f264b32cdceef6675 Mon Sep 17 00:00:00 2001 From: phdlee Date: Thu, 22 Feb 2018 12:26:18 +0900 Subject: [PATCH 076/173] Change BFO Cal Step(50 to 5), Change CW Frequency Method --- ubitx_20/ubitx_20.ino | 23 +++++++++++++++++++++++ ubitx_20/ubitx_menu.ino | 4 ++-- ubitx_20/ubitx_ui.ino | 9 +++++++++ 3 files changed, 34 insertions(+), 2 deletions(-) diff --git a/ubitx_20/ubitx_20.ino b/ubitx_20/ubitx_20.ino index 0e66cc8..4c12b81 100644 --- a/ubitx_20/ubitx_20.ino +++ b/ubitx_20/ubitx_20.ino @@ -176,6 +176,10 @@ int count = 0; //to generally count ticks, loops, etc #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 DISPLAY_OPTION1 361 //Display Option1 #define DISPLAY_OPTION2 362 //Display Option2 @@ -285,6 +289,9 @@ 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 @@ -963,6 +970,22 @@ void initSettings(){ 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); + + //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; + //default Value (for original hardware) if (cwAdcSTFrom >= cwAdcSTTo) { diff --git a/ubitx_20/ubitx_menu.ino b/ubitx_20/ubitx_menu.ino index 4542a0c..d4011a3 100644 --- a/ubitx_20/ubitx_menu.ino +++ b/ubitx_20/ubitx_menu.ino @@ -1149,9 +1149,9 @@ void menuSetupCarrier(int btn){ knob = enc_read(); if (knob > 0) - usbCarrier -= 50; + usbCarrier -= 5; else if (knob < 0) - usbCarrier += 50; + usbCarrier += 5; else continue; //don't update the frequency or the display diff --git a/ubitx_20/ubitx_ui.ino b/ubitx_20/ubitx_ui.ino index 34fe5ad..56ccdbe 100644 --- a/ubitx_20/ubitx_ui.ino +++ b/ubitx_20/ubitx_ui.ino @@ -281,6 +281,15 @@ void updateDisplay() { 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) { From bf68dd6c266f0acf42d5ea9b77c2c0d3679c1d4c Mon Sep 17 00:00:00 2001 From: phdlee Date: Thu, 22 Feb 2018 13:27:51 +0900 Subject: [PATCH 077/173] Change Version Number --- ubitx_20/ubitx_20.ino | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ubitx_20/ubitx_20.ino b/ubitx_20/ubitx_20.ino index 4c12b81..a130eb4 100644 --- a/ubitx_20/ubitx_20.ino +++ b/ubitx_20/ubitx_20.ino @@ -1110,7 +1110,7 @@ void setup() //Serial.begin(9600); lcd.begin(16, 2); - printLineF(1, F("CECBT v1.01")); + printLineF(1, F("CECBT v1.03")); Init_Cat(38400, SERIAL_8N1); initMeter(); //not used in this build From fb2c9d2cc3ad22bbda88dc9c6b310be88a5c29f1 Mon Sep 17 00:00:00 2001 From: phdlee Date: Mon, 5 Mar 2018 12:51:14 +0900 Subject: [PATCH 078/173] Optimized from Version1.03 --- ubitx_20/ubitx_20.ino | 19 ++--- ubitx_20/ubitx_idle.ino | 19 +++-- ubitx_20/ubitx_menu.ino | 167 ++++++++++++-------------------------- ubitx_20/ubitx_si5351.ino | 11 ++- ubitx_20/ubitx_ui.ino | 16 ---- 5 files changed, 79 insertions(+), 153 deletions(-) diff --git a/ubitx_20/ubitx_20.ino b/ubitx_20/ubitx_20.ino index a130eb4..2beacab 100644 --- a/ubitx_20/ubitx_20.ino +++ b/ubitx_20/ubitx_20.ino @@ -408,7 +408,6 @@ void saveBandFreqByIndex(unsigned long f, unsigned long mode, char bandIndex) { 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(); @@ -526,7 +525,6 @@ void setFrequency(unsigned long f){ * 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) { @@ -682,7 +680,7 @@ void checkButton(){ delay(10); Check_Cat(0); } - delay(50);//debounce + //delay(50);//debounce } @@ -697,7 +695,7 @@ int encodedSumValue = 0; unsigned long lastTunetime = 0; //if continous moving, skip threshold processing byte lastMovedirection = 0; //0 : stop, 1 : cw, 2 : ccw -#define skipThresholdTime 100 +//#define skipThresholdTime 70 #define encodeTimeOut 1000 void doTuningWithThresHold(){ @@ -726,7 +724,9 @@ void doTuningWithThresHold(){ encodedSumValue += (s > 0 ? 1 : -1); //check threshold and operator actions (hold dial speed = continous moving, skip threshold check) - if ((lastTunetime < millis() - skipThresholdTime) && ((encodedSumValue * encodedSumValue) <= (threshold * threshold))) + //not use continues changing by Threshold + //if ((lastTunetime < (millis() - skipThresholdTime)) && ((encodedSumValue * encodedSumValue) <= (threshold * threshold))) + if (((encodedSumValue * encodedSumValue) <= (threshold * threshold))) return; lastTunetime = millis(); @@ -736,7 +736,8 @@ void doTuningWithThresHold(){ 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 * (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; @@ -757,16 +758,15 @@ void doRIT(){ if (knob < 0) frequency -= (arTuneStep[tuneStepIndex -1]); // - //frequency -= 100l; else if (knob > 0) frequency += (arTuneStep[tuneStepIndex -1]); // - //frequency += 100; if (old_freq != frequency){ setFrequency(frequency); updateDisplay(); } } + /* save Frequency and mode to eeprom for Auto Save with protected eeprom cycle, by kd8cec */ @@ -1060,7 +1060,6 @@ void initSettings(){ } void initPorts(){ - analogReference(DEFAULT); //?? @@ -1110,7 +1109,7 @@ void setup() //Serial.begin(9600); lcd.begin(16, 2); - printLineF(1, F("CECBT v1.03")); + printLineF(1, F("CECBT v1.04")); Init_Cat(38400, SERIAL_8N1); initMeter(); //not used in this build diff --git a/ubitx_20/ubitx_idle.ino b/ubitx_20/ubitx_idle.ino index 178dcdf..663a291 100644 --- a/ubitx_20/ubitx_idle.ino +++ b/ubitx_20/ubitx_idle.ino @@ -31,12 +31,15 @@ void updateLine2Buffer(char isDirectCall) { if (ritOn) { + strcpy(line2Buffer, "RitTX:"); + /* line2Buffer[0] = 'R'; line2Buffer[1] = 'i'; line2Buffer[2] = 't'; line2Buffer[3] = 'T'; line2Buffer[4] = 'X'; line2Buffer[5] = ':'; + */ //display frequency tmpFreq = ritTxFrequency; @@ -61,12 +64,10 @@ void updateLine2Buffer(char isDirectCall) if (vfoActive == VFO_B) { tmpFreq = vfoA; - //line2Buffer[0] = 'A'; } else { tmpFreq = vfoB; - //line2Buffer[0] = 'B'; } // EXAMPLE 1 & 2 @@ -133,16 +134,18 @@ void updateLine2Buffer(char isDirectCall) line2Buffer[8] = 'I'; line2Buffer[9] = 'F'; - if (ifShiftValue == 0) - { + //if (ifShiftValue == 0) + //{ + /* line2Buffer[10] = 'S'; line2Buffer[11] = ':'; line2Buffer[12] = 'O'; line2Buffer[13] = 'F'; line2Buffer[14] = 'F'; - } - else - { + */ + //} + //else + //{ line2Buffer[10] = ifShiftValue >= 0 ? '+' : 0; line2Buffer[11] = 0; line2Buffer[12] = ' '; @@ -151,7 +154,7 @@ void updateLine2Buffer(char isDirectCall) 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); diff --git a/ubitx_20/ubitx_menu.ino b/ubitx_20/ubitx_menu.ino index d4011a3..c531305 100644 --- a/ubitx_20/ubitx_menu.ino +++ b/ubitx_20/ubitx_menu.ino @@ -56,7 +56,7 @@ void menuBand(int btn){ return; } - printLineF2(F("Press to confirm")); + //printLineF2(F("Press to confirm")); //wait for the button menu select button to be lifted) while (btnDown()) { delay_background(50, 0); @@ -72,9 +72,9 @@ void menuBand(int btn){ } delay_background(1000, 0); printLine2ClearAndUpdate(); - printLineF2(F("Press to confirm")); } } + printLineF2(F("Press to confirm")); char currentBandIndex = -1; //Save Band Information @@ -92,7 +92,6 @@ void menuBand(int btn){ ritDisable(); while(!btnDown()){ - knob = enc_read(); if (knob != 0){ if (tuneTXType == 2 || tuneTXType == 3 || tuneTXType == 102 || tuneTXType == 103) { //only ham band move @@ -127,17 +126,7 @@ void menuBand(int btn){ delay_background(20, 0); } -/* - while(btnDown()) { - delay(50); - Check_Cat(0); //To prevent disconnections - } -*/ FrequencyToVFO(1); - - //printLine2ClearAndUpdate(); - //delay_background(500, 0); - //menuOn = 0; menuClearExit(500); } @@ -181,25 +170,6 @@ void byteToMode(byte modeValue, byte autoSetModebyFreq){ } } -/* -//Convert Number to Mode by KD8CEC -void byteWithFreqToMode(byte modeValue){ - if (modeValue == 4) - cwMode = 1; - else if (modeValue == 5) - cwMode = 2; - else { - cwMode = 0; - if (modeValue == 3) - isUSB = 1; - else if (modeValue == 0) //Not Set - isUSB = (frequency > 10000000l) ? true : false; - else - isUSB = 0; - } -} -*/ - //IF Shift function, BFO Change like RIT, by KD8CEC void menuIFSSetup(int btn){ int knob = 0; @@ -212,11 +182,7 @@ void menuIFSSetup(int btn){ printLineF2(F("IF Shift:Off, On?")); } else { - //if (isIFShift == 0){ - //printLineF2(F("IF Shift is ON")); - //delay_background(500, 0); isIFShift = 1; - //} delay_background(500, 0); updateLine2Buffer(1); @@ -255,7 +221,7 @@ void menuIFSSetup(int btn){ isIFShift = 0; printLineF2(F("IF Shift is OFF")); setFrequency(frequency); - delay_background(500, 0); + delay_background(1500, 0); } //menuOn = 0; @@ -343,17 +309,12 @@ void menuSelectMode(int btn){ si5351bx_setfreq(0, cwmCarrier + (isIFShift ? ifShiftValue : 0)); //set back the carrier oscillator anyway, cw tx switches it off setFrequency(frequency); - - //delay_background(500, 0); - //printLine2ClearAndUpdate(); - //menuOn = 0; menuClearExit(500); } } //Memory to VFO, VFO to Memory by KD8CEC -//select between MtoV and VtoM by isMemoryToVfo void menuCHMemory(int btn, byte isMemoryToVfo){ int knob = 0; int selectChannel = 0; @@ -407,10 +368,6 @@ void menuCHMemory(int btn, byte isMemoryToVfo){ strcat(c, b); //append channel Number; strcat(c, " :"); //append channel Number; } - /* - if (selectChannel < 10) - printLineFromEEPRom(0, 4, 0, userCallsignLength -1); //eeprom to lcd use offset (USER_CALLSIGN_DAT) - */ //display frequency tmpFreq = resultFreq; @@ -624,25 +581,16 @@ void menuVfoToggle(int btn) FrequencyToVFO(1); if (vfoActive == VFO_B){ - //vfoB = frequency; - //vfoB_mode = modeToByte(); - //storeFrequencyAndMode(2); //vfoB -> eeprom - vfoActive = VFO_A; frequency = vfoA; saveCheckFreq = frequency; byteToMode(vfoA_mode, 0); } else { - //vfoA = frequency; - //vfoA_mode = modeToByte(); - //storeFrequencyAndMode(1); //vfoA -> eeprom - vfoActive = VFO_B; frequency = vfoB; saveCheckFreq = frequency; byteToMode(vfoB_mode, 0); - //printLineF2(F("Selected VFO B")); } ritDisable(); @@ -661,12 +609,12 @@ void menuRitToggle(int btn){ } else { if (ritOn == 0){ - printLineF2(F("RIT is ON")); + //printLineF2(F("RIT is ON")); //enable RIT so the current frequency is used at transmit ritEnable(frequency); } else{ - printLineF2(F("RIT is OFF")); + //printLineF2(F("RIT is OFF")); ritDisable(); } @@ -694,9 +642,6 @@ void menuSplitOnOff(int btn){ printLineF2(F("Split On!")); } - //delay_background(500, 0); - //printLine2ClearAndUpdate(); - //menuOn = 0; menuClearExit(500); } } @@ -721,9 +666,6 @@ void menuTxOnOff(int btn, byte optionType){ printLineF2(F("TX ON!")); } - //delay_background(500, 0); - //printLine2ClearAndUpdate(); - //menuOn = 0; menuClearExit(500); } } @@ -751,9 +693,6 @@ void menuSetup(int btn){ } */ - //delay_background(2000, 0); - //printLine2Clear(); - //menuOn = 0; menuClearExit(1000); } } @@ -803,23 +742,18 @@ void menuCWSpeed(int btn){ printLine2(b); } //abort if this button is down - if (btnDown()) - //re-enable the clock1 and clock 2 - break; + //if (btnDown()) + //re-enable the clock1 and clock 2 + // break; Check_Cat(0); //To prevent disconnections } - //save the setting - //if (digitalRead(PTT) == LOW){ - printLineF2(F("CW Speed set!")); - cwSpeed = 1200/wpm; - EEPROM.put(CW_SPEED, cwSpeed); - //} - //delay_background(2000, 0); - //printLine2ClearAndUpdate(); - //menuOn = 0; - menuClearExit(1000); + //save the setting + //printLineF2(F("CW Speed set!")); + cwSpeed = 1200 / wpm; + EEPROM.put(CW_SPEED, cwSpeed); + menuClearExit(1000); } //Builtin CW Keyer Logic by KD8CEC @@ -884,14 +818,9 @@ void menuSetupCwDelay(int btn){ } //save the setting - //if (digitalRead(PTT) == LOW){ - printLineF2(F("CW Delay set!")); - cwDelayTime = tmpCWDelay / 10; - EEPROM.put(CW_DELAY, cwDelayTime); - //delay_background(2000, 0); - //} - //printLine2ClearAndUpdate(); - //menuOn = 0; + //printLineF2(F("CW Delay set!")); + cwDelayTime = tmpCWDelay / 10; + EEPROM.put(CW_DELAY, cwDelayTime); menuClearExit(1000); } @@ -940,21 +869,17 @@ void menuSetupTXCWInterval(int btn){ needDisplayInformation = 1; } //abort if this button is down - if (btnDown()) - break; + //if (btnDown()) + // break; Check_Cat(0); //To prevent disconnections } //save the setting - //if (digitalRead(PTT) == LOW){ - printLineF2(F("CW Start set!")); - delayBeforeCWStartTime = tmpTXCWInterval / 2; - EEPROM.put(CW_START, delayBeforeCWStartTime); - //delay_background(2000, 0); - //} - //printLine2ClearAndUpdate(); - //menuOn = 0; + //printLineF2(F("CW Start set!")); + delayBeforeCWStartTime = tmpTXCWInterval / 2; + EEPROM.put(CW_START, delayBeforeCWStartTime); + menuClearExit(1000); } @@ -1215,8 +1140,6 @@ void menuSetupCWCarrier(int btn){ si5351bx_setfreq(0, cwmCarrier); printCarrierFreq(cwmCarrier); - //Check_Cat(0); //To prevent disconnections - //delay(100); delay_background(100, 0); } @@ -1235,8 +1158,6 @@ void menuSetupCWCarrier(int btn){ si5351bx_setfreq(0, cwmCarrier); //set back the carrier oscillator anyway, cw tx switches it off setFrequency(frequency); - //printLine2ClearAndUpdate(); - //menuOn = 0; menuClearExit(0); } @@ -1284,8 +1205,6 @@ void menuSetupCwTone(int btn){ else sideTone = prev_sideTone; - //printLine2ClearAndUpdate(); - //menuOn = 0; menuClearExit(0); } @@ -1298,15 +1217,7 @@ void setDialLock(byte tmpLock, byte fromMode) { if (fromMode == 2 || fromMode == 3) return; - //for reduce using flash memory - /* - if (tmpLock == 1) - printLineF2(F("Dial Lock ON")); - else - printLineF2(F("Dial Lock OFF")); - */ - - delay_background(1000, 0); + //delay_background(1000, 0); printLine2ClearAndUpdate(); } @@ -1382,8 +1293,6 @@ void doMenu(){ } } //end of while - //printLineF2(F("Changed Step!")); //remarked for reduce program memory by KD8CEC - //SAVE EEPROM EEPROM.put(TUNING_STEP, tuneStepIndex); delay_background(500, 0); printLine2ClearAndUpdate(); @@ -1433,6 +1342,8 @@ void doMenu(){ menuSetup(btnState); else if (select < 120) menuExit(btnState); + + /* else if (select < 130 && modeCalibrate) menuSetupCalibration(btnState); //crystal else if (select < 140 && modeCalibrate) @@ -1453,14 +1364,40 @@ void doMenu(){ menuTxOnOff(btnState, 0x01); //TX OFF / ON else if (select < 220 && modeCalibrate) menuExit(btnState); + */ + + else if (modeCalibrate) + { + if (select < 130) + menuSetupCalibration(btnState); //crystal + else if (select < 140) + menuSetupCarrier(btnState); //lsb + else if (select < 150) + menuSetupCWCarrier(btnState); //lsb + else if (select < 160) + menuSetupCwTone(btnState); + else if (select < 170) + menuSetupCwDelay(btnState); + else if (select < 180) + menuSetupTXCWInterval(btnState); + else if (select < 190) + menuSetupKeyType(btnState); + else if (select < 200) + menuADCMonitor(btnState); + else if (select < 210) + menuTxOnOff(btnState, 0x01); //TX OFF / ON + else if (select < 220) + menuExit(btnState); + } Check_Cat(0); //To prevent disconnections } +/* //debounce the button while(btnDown()){ delay_background(50, 0); //To prevent disconnections } - //delay(50); +*/ } diff --git a/ubitx_20/ubitx_si5351.ino b/ubitx_20/ubitx_si5351.ino index 8098819..6d16af2 100644 --- a/ubitx_20/ubitx_si5351.ino +++ b/ubitx_20/ubitx_si5351.ino @@ -60,6 +60,7 @@ void i2cWriten(uint8_t reg, uint8_t *vals, uint8_t vcnt) { // write array 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; @@ -68,11 +69,13 @@ void si5351bx_init() { // Call once at power-up, start PLLA 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}; - i2cWriten(26, vals, 8); // Write to 8 PLLA msynth regs + //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) - // for (reg=16; reg<=23; reg++) i2cWrite(reg, 0x80); // Powerdown CLK's - // i2cWrite(187, 0); // No fannout of clkin, xtal, ms0, ms4 } void si5351bx_setfreq(uint8_t clknum, uint32_t fout) { // Set a CLK to fout Hz diff --git a/ubitx_20/ubitx_ui.ino b/ubitx_20/ubitx_ui.ino index 56ccdbe..ad55426 100644 --- a/ubitx_20/ubitx_ui.ino +++ b/ubitx_20/ubitx_ui.ino @@ -327,22 +327,6 @@ void updateDisplay() { lcd.setCursor(5,diplayVFOLine); lcd.write(":"); } - -/* - //now, the second line - memset(c, 0, sizeof(c)); - memset(b, 0, sizeof(b)); - - if (inTx) - strcat(c, "TX "); - else if (ritOn) - strcpy(c, "RIT"); - - strcpy(c, " \xff"); - drawMeter(meter_reading); - strcat(c, meter); - strcat(c, "\xff"); - printLine2(c);*/ } int enc_prev_state = 3; From a21dbe2fa5d4b057103385893d7e11994fd4660d Mon Sep 17 00:00:00 2001 From: phdlee Date: Mon, 5 Mar 2018 13:03:05 +0900 Subject: [PATCH 079/173] Update README.md --- README.md | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 6129344..de477e8 100644 --- a/README.md +++ b/README.md @@ -38,7 +38,15 @@ Prepared or finished tasks for the next version ---------------------------------------------------------------------------- ## REVISION RECORD -1.02 (working) +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) From a26978f573787529d7610be66045a3360e5e4d04 Mon Sep 17 00:00:00 2001 From: phdlee Date: Fri, 9 Mar 2018 22:02:10 +0900 Subject: [PATCH 080/173] Added WSPR and Reduce Program size --- ubitx_20/cw_autokey.ino | 2 +- ubitx_20/ubitx.h | 79 +++++++++++++++ ubitx_20/ubitx_20.ino | 8 +- ubitx_20/ubitx_idle.ino | 8 -- ubitx_20/ubitx_menu.ino | 205 +++++++++++++++++++++++--------------- ubitx_20/ubitx_si5351.ino | 40 ++++++++ ubitx_20/ubitx_ui.ino | 6 +- ubitx_20/ubitx_wspr.cpp | 190 +++++++++++++++++++++++++++++++++++ 8 files changed, 443 insertions(+), 95 deletions(-) create mode 100644 ubitx_20/ubitx.h create mode 100644 ubitx_20/ubitx_wspr.cpp diff --git a/ubitx_20/cw_autokey.ino b/ubitx_20/cw_autokey.ino index c9dba8b..93fc748 100644 --- a/ubitx_20/cw_autokey.ino +++ b/ubitx_20/cw_autokey.ino @@ -297,7 +297,7 @@ void controlAutoCW(){ displayScrolStep = 0; } - printLineFromEEPRom(0, 2, cwStartIndex + displayScrolStep + CW_DATA_OFSTADJ, cwEndIndex + CW_DATA_OFSTADJ); + printLineFromEEPRom(0, 2, cwStartIndex + displayScrolStep + CW_DATA_OFSTADJ, cwEndIndex + CW_DATA_OFSTADJ, 0); byte diplayAutoCWLine = 0; if ((displayOption1 & 0x01) == 0x01) diff --git a/ubitx_20/ubitx.h b/ubitx_20/ubitx.h new file mode 100644 index 0000000..0729320 --- /dev/null +++ b/ubitx_20/ubitx.h @@ -0,0 +1,79 @@ +/************************************************************************* + 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 . +**************************************************************************/ +#define WSPR_COUNT 443 //WSPR_MESSAGE_COUNT +#define WSPR_MESSAGE1 444 // +#define WSPR_MESSAGE2 490 // +#define WSPR_MESSAGE3 536 // +#define WSPR_MESSAGE4 582 // + +#define WSPR_BAND_COUNT 3 + +#define TX_SSB 0 +#define TX_CW 1 + + +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[30]; +extern char b[30]; + +extern unsigned long frequency; + +#define printLineF1(x) (printLineF(1, x)) +#define printLineF2(x) (printLineF(0, x)) + + +/** + * The second set of 16 pins on the Raduino's bottom connector are have the three clock outputs and the digital lines to control the rig. + * This assignment is as follows : + * Pin 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 + * GND +5V CLK0 GND GND CLK1 GND GND CLK2 GND D2 D3 D4 D5 D6 D7 + * These too are flexible with what you may do with them, for the Raduino, we use them to : + * - TX_RX line : Switches between Transmit and Receive after sensing the PTT or the morse keyer + * - CW_KEY line : turns on the carrier for CW + */ + +#define TX_RX (7) +#define CW_TONE (6) +#define TX_LPF_A (5) +#define TX_LPF_B (4) +#define TX_LPF_C (3) +#define CW_KEY (2) + +//we directly generate the CW by programmin the Si5351 to the cw tx frequency, hence, both are different modes +//these are the parameter passed to startTx +#define TX_SSB 0 +#define TX_CW 1 + +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 SendWSPRManage(void); +extern byte WsprMSGCount; + + diff --git a/ubitx_20/ubitx_20.ino b/ubitx_20/ubitx_20.ino index 2beacab..48d2746 100644 --- a/ubitx_20/ubitx_20.ino +++ b/ubitx_20/ubitx_20.ino @@ -38,6 +38,7 @@ */ #include #include +#include "ubitx.h" /** The main chip which generates upto three oscillators of various frequencies in the @@ -589,7 +590,7 @@ void startTx(byte txMode, byte isDisplayUpdate){ updateDisplay(); } -void stopTx(){ +void stopTx(void){ inTx = 0; digitalWrite(TX_RX, 0); //turn off the tx @@ -1109,7 +1110,8 @@ void setup() //Serial.begin(9600); lcd.begin(16, 2); - printLineF(1, F("CECBT v1.04")); + //printLineF(1, F("CECBT v1.05")); + printLineF(1, F("CE v1.05_W")); Init_Cat(38400, SERIAL_8N1); initMeter(); //not used in this build @@ -1117,7 +1119,7 @@ void setup() if (userCallsignLength > 0 && ((userCallsignLength & 0x80) == 0x80)) { userCallsignLength = userCallsignLength & 0x7F; - printLineFromEEPRom(0, 0, 0, userCallsignLength -1); //eeprom to lcd use offset (USER_CALLSIGN_DAT) + printLineFromEEPRom(0, 0, 0, userCallsignLength -1, 0); //eeprom to lcd use offset (USER_CALLSIGN_DAT) delay(500); } else { diff --git a/ubitx_20/ubitx_idle.ino b/ubitx_20/ubitx_idle.ino index 663a291..87aa25e 100644 --- a/ubitx_20/ubitx_idle.ino +++ b/ubitx_20/ubitx_idle.ino @@ -32,14 +32,6 @@ void updateLine2Buffer(char isDirectCall) if (ritOn) { strcpy(line2Buffer, "RitTX:"); - /* - line2Buffer[0] = 'R'; - line2Buffer[1] = 'i'; - line2Buffer[2] = 't'; - line2Buffer[3] = 'T'; - line2Buffer[4] = 'X'; - line2Buffer[5] = ':'; - */ //display frequency tmpFreq = ritTxFrequency; diff --git a/ubitx_20/ubitx_menu.ino b/ubitx_20/ubitx_menu.ino index c531305..0f678ca 100644 --- a/ubitx_20/ubitx_menu.ino +++ b/ubitx_20/ubitx_menu.ino @@ -10,8 +10,7 @@ * - 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 */ -#define printLineF1(x) (printLineF(1, x)) -#define printLineF2(x) (printLineF(0, x)) +#include "ubitx.h" //Current Frequency and mode to active VFO by KD8CEC void FrequencyToVFO(byte isSaveFreq) @@ -64,11 +63,13 @@ void menuBand(int btn){ btnPressCount = 0; if (tuneTXType > 0) { //Just toggle 0 <-> 2, if tuneTXType is 100, 100 -> 0 -> 2 tuneTXType = 0; - printLineF2(F("General mode")); + //printLineF2(F("General mode")); + printLineF2(F("General")); } else { tuneTXType = 2; - printLineF2(F("Ham band mode")); + //printLineF2(F("Ham band mode")); + printLineF2(F("Ham band")); } delay_background(1000, 0); printLine2ClearAndUpdate(); @@ -179,7 +180,7 @@ void menuIFSSetup(int btn){ if (isIFShift == 1) printLineF2(F("IF Shift Change?")); else - printLineF2(F("IF Shift:Off, On?")); + printLineF2(F("IF Shift On?")); } else { isIFShift = 1; @@ -219,9 +220,11 @@ void menuIFSSetup(int btn){ if (btnDown() || ifShiftValue == 0) { isIFShift = 0; - printLineF2(F("IF Shift is OFF")); + //printLineF2(F("IF Shift is OFF")); + //printLineF2(F("OFF")); + //clearLine2(); setFrequency(frequency); - delay_background(1500, 0); + //delay_background(1500, 0); } //menuOn = 0; @@ -339,7 +342,8 @@ void menuCHMemory(int btn, byte isMemoryToVfo){ if (selectChannel >= 20 || selectChannel <=-1) { - strcpy(c, "Exit setup?"); + //strcpy(c, "Exit setup?"); + strcpy(c, "Exit?"); } else { @@ -603,9 +607,9 @@ void menuVfoToggle(int btn) void menuRitToggle(int btn){ if (!btn){ if (ritOn == 1) - printLineF2(F("RIT:On, Off?")); + printLineF2(F("RIT Off?")); else - printLineF2(F("RIT:Off, On?")); + printLineF2(F("RIT On?")); } else { if (ritOn == 0){ @@ -633,20 +637,21 @@ void menuSplitOnOff(int btn){ else { if (splitOn == 1){ splitOn = 0; - printLineF2(F("Split Off!")); + //printLineF2(F("Split Off!")); + printLineF2(F("[OFF]")); } else { splitOn = 1; if (ritOn == 1) ritOn = 0; - printLineF2(F("Split On!")); + //printLineF2(F("Split On!")); + printLineF2(F("[ON]")); } menuClearExit(500); } } - //Function to disbled transmission //by KD8CEC void menuTxOnOff(int btn, byte optionType){ @@ -756,23 +761,29 @@ void menuCWSpeed(int btn){ menuClearExit(1000); } +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("CW AutoKey Mode?")); - return; + 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) { - printLineF2(F("Empty CW data")); - delay_background(2000, 0); + displayEmptyData(); return; } - printLineF1(F("Press PTT to Send")); + //printLineF1(F("Press PTT to Send")); + printLineF1(F("PTT to Send")); delay_background(500, 0); updateDisplay(); beforeCWTextIndex = 255; //255 value is for start check @@ -780,6 +791,27 @@ void menuCWAutoKey(int btn){ 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); +} + + + //Modified by KD8CEC void menuSetupCwDelay(int btn){ int knob = 0; @@ -836,12 +868,6 @@ void menuSetupTXCWInterval(int btn){ } printLineF1(F("Press, set Delay")); - /* - strcpy(b, "Start Delay:"); - itoa(tmpTXCWInterval,c, 10); - strcat(b, c); - printLine2(b); - */ delay_background(300, 0); while(!btnDown()){ @@ -1261,8 +1287,6 @@ void doMenu(){ while (!btnDown()) { - //Check_Cat(0); //To prevent disconnections - //delay(50); //debounce delay_background(50, 0); if (isNeedDisplay) { @@ -1307,42 +1331,87 @@ void doMenu(){ btnState = btnDown(); if (i > 0){ - if (modeCalibrate && select + i < 220) + if (modeCalibrate && select + i < 240) select += i; - if (!modeCalibrate && select + i < 120) + if (!modeCalibrate && select + i < 130) select += i; } - //if (i < 0 && select - i >= 0) + if (i < 0 && select - i >= -10) select += i; //caught ya, i is already -ve here, so you add it - if (select < -5) - menuExit(btnState); - else if (select < 10) - menuBand(btnState); - else if (select < 20) - menuVfoToggle(btnState); - else if (select < 30) - menuSelectMode(btnState); - else if (select < 40) - menuRitToggle(btnState); - else if (select < 50) - menuIFSSetup(btnState); - else if (select < 60) - menuCWSpeed(btnState); - else if (select < 70) - menuSplitOnOff(btnState); //SplitOn / off - else if (select < 80) - menuCHMemory(btnState, 0); //VFO to Memroy - else if (select < 90) - menuCHMemory(btnState, 1); //Memory to VFO - else if (select < 100) - menuCWAutoKey(btnState); - else if (select < 110) - menuSetup(btnState); - else if (select < 120) - menuExit(btnState); - + //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 : + menuCWSpeed(btnState); + break; + case 6 : + menuSplitOnOff(btnState); //SplitOn / off + break; + case 7 : + menuCHMemory(btnState, 0); //VFO to Memroy + break; + case 8 : + menuCHMemory(btnState, 1); //Memory to VFO + break; + case 9 : + menuCWAutoKey(btnState); + break; + case 10 : + menuWSPRSend(btnState); + break; + case 11 : + menuSetup(btnState); + break; + case 12 : + menuExit(btnState); + break; + case 13 : + menuSetupCalibration(btnState); //crystal + break; + case 14 : + menuSetupCarrier(btnState); //lsb + break; + case 15 : + menuSetupCWCarrier(btnState); //lsb + break; + case 16 : + menuSetupCwTone(btnState); + break; + case 17 : + menuSetupCwDelay(btnState); + break; + case 18 : + menuSetupTXCWInterval(btnState); + break; + case 19 : + menuSetupKeyType(btnState); + break; + case 20 : + menuADCMonitor(btnState); + break; + case 21 : + menuTxOnOff(btnState, 0x01); //TX OFF / ON + break; + default : + menuExit(btnState); break; + } /* else if (select < 130 && modeCalibrate) menuSetupCalibration(btnState); //crystal @@ -1366,30 +1435,6 @@ void doMenu(){ menuExit(btnState); */ - else if (modeCalibrate) - { - if (select < 130) - menuSetupCalibration(btnState); //crystal - else if (select < 140) - menuSetupCarrier(btnState); //lsb - else if (select < 150) - menuSetupCWCarrier(btnState); //lsb - else if (select < 160) - menuSetupCwTone(btnState); - else if (select < 170) - menuSetupCwDelay(btnState); - else if (select < 180) - menuSetupTXCWInterval(btnState); - else if (select < 190) - menuSetupKeyType(btnState); - else if (select < 200) - menuADCMonitor(btnState); - else if (select < 210) - menuTxOnOff(btnState, 0x01); //TX OFF / ON - else if (select < 220) - menuExit(btnState); - } - Check_Cat(0); //To prevent disconnections } diff --git a/ubitx_20/ubitx_si5351.ino b/ubitx_20/ubitx_si5351.ino index 6d16af2..8a1cb56 100644 --- a/ubitx_20/ubitx_si5351.ino +++ b/ubitx_20/ubitx_si5351.ino @@ -1,3 +1,19 @@ +/************************************************************************************ + * 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. + ************************************************************************************/ + // ************* SI5315 routines - tks Jerry Gaffke, KE7ER *********************** // An minimalist standalone set of Si5351 routines. @@ -119,5 +135,29 @@ void initOscillators(){ si5351bx_setfreq(0, cwmCarrier + (isIFShift ? ifShiftValue : 0)); } +//============================================================ +// 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 index ad55426..c5089ea 100644 --- a/ubitx_20/ubitx_ui.ino +++ b/ubitx_20/ubitx_ui.ino @@ -9,7 +9,7 @@ //#define printLineF2(x) (printLineF(0, x)) //returns true if the button is pressed -int btnDown(){ +int btnDown(void){ if (digitalRead(FBUTTON) == HIGH) return 0; else @@ -173,7 +173,7 @@ void printLineF(char linenmbr, const __FlashStringHelper *c) } #define LCD_MAX_COLUMN 16 -void printLineFromEEPRom(char linenmbr, char lcdColumn, byte eepromStartIndex, byte eepromEndIndex) { +void printLineFromEEPRom(char linenmbr, char lcdColumn, byte eepromStartIndex, byte eepromEndIndex, char offsetTtype) { if ((displayOption1 & 0x01) == 0x01) linenmbr = (linenmbr == 0 ? 1 : 0); //Line Toggle @@ -182,7 +182,7 @@ void printLineFromEEPRom(char linenmbr, char lcdColumn, byte eepromStartIndex, b for (byte i = eepromStartIndex; i <= eepromEndIndex; i++) { if (++lcdColumn <= LCD_MAX_COLUMN) - lcd.write(EEPROM.read(USER_CALLSIGN_DAT + i)); + lcd.write(EEPROM.read((offsetTtype == 0 ? USER_CALLSIGN_DAT : WSPR_MESSAGE1) + i)); else break; } diff --git a/ubitx_20/ubitx_wspr.cpp b/ubitx_20/ubitx_wspr.cpp new file mode 100644 index 0000000..2568378 --- /dev/null +++ b/ubitx_20/ubitx_wspr.cpp @@ -0,0 +1,190 @@ +/********************************************************************************** +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 +Beta Tester : + +----------------------------------------------------------------------------- + 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 +#include "ubitx.h" + +//begin of test +byte WsprToneCode[164]; + +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 PTT (A3) + +#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; + char 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; + + //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); + + /* + //3, 4, 5, 6, 7 + Wspr_Reg1[3] = EEPROM.read(bandBuffIndex + 6); + Wspr_Reg1[4] = EEPROM.read(bandBuffIndex + 7); + Wspr_Reg1[5] = EEPROM.read(bandBuffIndex + 8); + Wspr_Reg1[6] = EEPROM.read(bandBuffIndex + 9); + Wspr_Reg1[7] = EEPROM.read(bandBuffIndex + 10); + */ + for (loopIndex = 3; loopIndex < 8; loopIndex++) + Wspr_Reg1[loopIndex] = EEPROM.read(bandBuffIndex + loopIndex + 3); + + /* + Wspr_Reg2[2] = EEPROM.read(bandBuffIndex + 11); + Wspr_Reg2[3] = EEPROM.read(bandBuffIndex + 12); + Wspr_Reg2[4] = EEPROM.read(bandBuffIndex + 13); + */ + //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]; + } + + ltoa(WsprTXFreq, b, DEC); + if (digitalRead(PTT) == 0) + strcpy(c, "SEND:"); + else + strcpy(c, "PTT->"); + + strcat(c, b); + printLine1(c); + + if (digitalRead(PTT) == 0) + { + //printLineF1(F("Transmitting")); + //SEND WSPR + prevFreq = frequency; + frequency = WsprTXFreq; + startTx(TX_CW, 0); + + //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(); + frequency = prevFreq; + + selectedWsprBandIndex = -1; + } //end of PTT Check + else if (btnDown()) + { + return; + } + + } //end of status check + + //delay_background(50, 1); + } //end of while +} + From 94a3e5ca1bf4426ba20895395e760a6c1418b718 Mon Sep 17 00:00:00 2001 From: phdlee Date: Tue, 13 Mar 2018 01:17:06 +0900 Subject: [PATCH 081/173] Test and some mod about WSPR Calibration --- ubitx_20/ubitx.h | 1 + ubitx_20/ubitx_20.ino | 6 +++--- ubitx_20/ubitx_wspr.cpp | 11 +++++++---- 3 files changed, 11 insertions(+), 7 deletions(-) diff --git a/ubitx_20/ubitx.h b/ubitx_20/ubitx.h index 0729320..64fcd3f 100644 --- a/ubitx_20/ubitx.h +++ b/ubitx_20/ubitx.h @@ -72,6 +72,7 @@ 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 byte WsprMSGCount; diff --git a/ubitx_20/ubitx_20.ino b/ubitx_20/ubitx_20.ino index 48d2746..5e7d797 100644 --- a/ubitx_20/ubitx_20.ino +++ b/ubitx_20/ubitx_20.ino @@ -1023,10 +1023,10 @@ void initSettings(){ //original code with modified by kd8cec if (usbCarrier > 12010000l || usbCarrier < 11990000l) - usbCarrier = 11995000l; + usbCarrier = 11997000l; if (cwmCarrier > 12010000l || cwmCarrier < 11990000l) - cwmCarrier = 11995000l; + cwmCarrier = 11997000l; if (vfoA > 35000000l || 3500000l > vfoA) { vfoA = 7150000l; @@ -1111,7 +1111,7 @@ void setup() //Serial.begin(9600); lcd.begin(16, 2); //printLineF(1, F("CECBT v1.05")); - printLineF(1, F("CE v1.05_W")); + printLineF(1, F("CE v1.051W")); Init_Cat(38400, SERIAL_8N1); initMeter(); //not used in this build diff --git a/ubitx_20/ubitx_wspr.cpp b/ubitx_20/ubitx_wspr.cpp index 2568378..c87c1a7 100644 --- a/ubitx_20/ubitx_wspr.cpp +++ b/ubitx_20/ubitx_wspr.cpp @@ -151,8 +151,11 @@ void SendWSPRManage() { //printLineF1(F("Transmitting")); //SEND WSPR - prevFreq = frequency; - frequency = WsprTXFreq; + //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; + setTXFilters(WsprTXFreq); startTx(TX_CW, 0); //Start WSPR @@ -172,8 +175,8 @@ void SendWSPRManage() } digitalWrite(CW_KEY, 0); - stopTx(); - frequency = prevFreq; + stopTx(); //call setFrequency -> recovery TX Filter + //frequency = prevFreq; selectedWsprBandIndex = -1; } //end of PTT Check From 8326b1ade3bbbb3b54f50aacddf85751c528adb4 Mon Sep 17 00:00:00 2001 From: phdlee Date: Thu, 15 Mar 2018 21:00:42 +0900 Subject: [PATCH 082/173] bug fixed : cw start delay option --- ubitx_20/ubitx_keyer.ino | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/ubitx_20/ubitx_keyer.ino b/ubitx_20/ubitx_keyer.ino index 308bf7d..1ac1c2f 100644 --- a/ubitx_20/ubitx_keyer.ino +++ b/ubitx_20/ubitx_keyer.ino @@ -168,6 +168,8 @@ void cwKeyer(void){ 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 @@ -179,6 +181,19 @@ void cwKeyer(void){ 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; From f0409d641def6850ac37fb29e721ace94ef06aff Mon Sep 17 00:00:00 2001 From: phdlee Date: Sat, 17 Mar 2018 11:11:27 +0900 Subject: [PATCH 083/173] Auto key Bug fixed, LZ1LDO found bug --- ubitx_20/cw_autokey.ino | 2 +- ubitx_20/ubitx_20.ino | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/ubitx_20/cw_autokey.ino b/ubitx_20/cw_autokey.ino index 93fc748..4d2d93f 100644 --- a/ubitx_20/cw_autokey.ino +++ b/ubitx_20/cw_autokey.ino @@ -36,7 +36,7 @@ //27 + 10 + 18 + 1(SPACE) = //56 const PROGMEM uint8_t cwAZTable[27] = {0b00100100 , 0b01001000 , 0b01001010 , 0b00111000 , 0b00010000, 0b01000010, 0b00111100, 0b01000000 , //A ~ H 0b00100000, 0b01000111 ,0b00111010, 0b01000100, 0b00101100, 0b00101000 , 0b00111110, 0b01000110, 0b01001101, 0b00110100, //I ~ R -0b00110000, 0b00011000, 0b00110010, 0b01000001, 0b00110110, 0b01001001, 0b01001011, 0b00111000}; //S ~ Z +0b00110000, 0b00011000, 0b00110010, 0b01000001, 0b00110110, 0b01001001, 0b01001011, 0b01001100}; //S ~ Z PGM_P pCwAZTable = reinterpret_cast(cwAZTable); const PROGMEM uint8_t cw09Table[27] = {0b00011111, 0b00001111, 0b00000111, 0b00000011, 0b00000001, 0b00000000, 0b00010000, 0b00011000, 0b00011100, 0b00011110}; diff --git a/ubitx_20/ubitx_20.ino b/ubitx_20/ubitx_20.ino index 5e7d797..8f95241 100644 --- a/ubitx_20/ubitx_20.ino +++ b/ubitx_20/ubitx_20.ino @@ -1111,7 +1111,7 @@ void setup() //Serial.begin(9600); lcd.begin(16, 2); //printLineF(1, F("CECBT v1.05")); - printLineF(1, F("CE v1.051W")); + printLineF(1, F("CE v1.059W")); Init_Cat(38400, SERIAL_8N1); initMeter(); //not used in this build From bd52de59d2851bd9696f07d0d1d2d021a6b323b8 Mon Sep 17 00:00:00 2001 From: phdlee Date: Mon, 19 Mar 2018 10:13:30 +0900 Subject: [PATCH 084/173] bug fixed (found gereld) Autokey on Rit bug --- ubitx_20/ubitx_20.ino | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/ubitx_20/ubitx_20.ino b/ubitx_20/ubitx_20.ino index 8f95241..5f4516b 100644 --- a/ubitx_20/ubitx_20.ino +++ b/ubitx_20/ubitx_20.ino @@ -707,8 +707,7 @@ void doTuningWithThresHold(){ (vfoActive == VFO_B && ((isDialLock & 0x02) == 0x02))) return; - if (isCWAutoMode == 0 || cwAutoDialType == 1) - s = enc_read(); + s = enc_read(); //if time is exceeded, it is recognized as an error, //ignore exists values, because of errors @@ -1179,12 +1178,13 @@ void loop(){ //tune only when not tranmsitting if (!inTx){ - if (ritOn) - doRIT(); - //else if (isIFShift) - // doIFShift(); - else - doTuningWithThresHold(); + if (isCWAutoMode == 0 || cwAutoDialType == 1) + { + if (ritOn) + doRIT(); + else + doTuningWithThresHold(); + } if (isCWAutoMode == 0 && beforeIdle_ProcessTime < millis() - 250) { idle_process(); From ecd104b6860f0a1b58cc50e114c72d8d120c8530 Mon Sep 17 00:00:00 2001 From: phdlee Date: Mon, 19 Mar 2018 21:35:41 +0900 Subject: [PATCH 085/173] Fixed IF Shift Bug (USB and TX Mode) --- ubitx_20/ubitx_20.ino | 34 ++++++++++++++++++++++------------ ubitx_20/ubitx_menu.ino | 3 +++ ubitx_20/ubitx_si5351.ino | 19 ++++++++++++++----- 3 files changed, 39 insertions(+), 17 deletions(-) diff --git a/ubitx_20/ubitx_20.ino b/ubitx_20/ubitx_20.ino index 5f4516b..258240d 100644 --- a/ubitx_20/ubitx_20.ino +++ b/ubitx_20/ubitx_20.ino @@ -492,28 +492,29 @@ void setTXFilters(unsigned long freq){ 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)); + if (cwMode == 0) { if (isUSB){ - si5351bx_setfreq(2, SECOND_OSC_USB - usbCarrier + f + (isIFShift ? ifShiftValue : 0)); + si5351bx_setfreq(2, SECOND_OSC_USB - appliedCarrier + f); si5351bx_setfreq(1, SECOND_OSC_USB); } else{ - si5351bx_setfreq(2, SECOND_OSC_LSB + usbCarrier + f + (isIFShift ? ifShiftValue : 0)); + si5351bx_setfreq(2, SECOND_OSC_LSB + appliedCarrier + f); si5351bx_setfreq(1, SECOND_OSC_LSB); } } else { if (cwMode == 1){ //CWL - si5351bx_setfreq(2, SECOND_OSC_LSB + cwmCarrier + f + (isIFShift ? ifShiftValue : 0)); + si5351bx_setfreq(2, SECOND_OSC_LSB + appliedCarrier + f); si5351bx_setfreq(1, SECOND_OSC_LSB); } else{ //CWU - si5351bx_setfreq(2, SECOND_OSC_USB - cwmCarrier + f + (isIFShift ? ifShiftValue : 0)); + si5351bx_setfreq(2, SECOND_OSC_USB - appliedCarrier + f); si5351bx_setfreq(1, SECOND_OSC_USB); } } @@ -543,7 +544,9 @@ void startTx(byte txMode, byte isDisplayUpdate){ ritRxFrequency = frequency; setFrequency(ritTxFrequency); } - else if (splitOn == 1) { + else + { + if (splitOn == 1) { if (vfoActive == VFO_B) { vfoActive = VFO_A; frequency = vfoA; @@ -554,10 +557,12 @@ void startTx(byte txMode, byte isDisplayUpdate){ frequency = vfoB; byteToMode(vfoB_mode, 0); } + } - setFrequency(frequency); + setFrequency(frequency); } //end of else - + + SetCarrierFreq(); if (txMode == TX_CW){ //turn off the second local oscillator and the bfo @@ -595,14 +600,19 @@ void stopTx(void){ digitalWrite(TX_RX, 0); //turn off the tx +/* if (cwMode == 0) si5351bx_setfreq(0, usbCarrier + (isIFShift ? ifShiftValue : 0)); //set back the carrier oscillator anyway, cw tx switches it off else si5351bx_setfreq(0, cwmCarrier + (isIFShift ? ifShiftValue : 0)); //set back the carrier oscillator anyway, cw tx switches it off +*/ + SetCarrierFreq(); if (ritOn) setFrequency(ritRxFrequency); - else if (splitOn == 1) { + else + { + if (splitOn == 1) { //vfo Change if (vfoActive == VFO_B){ vfoActive = VFO_A; @@ -614,10 +624,10 @@ void stopTx(void){ frequency = vfoB; byteToMode(vfoB_mode, 0); } - setFrequency(frequency); - } //end of else - else + } + setFrequency(frequency); + } //end of else updateDisplay(); } diff --git a/ubitx_20/ubitx_menu.ino b/ubitx_20/ubitx_menu.ino index 0f678ca..0a9bdac 100644 --- a/ubitx_20/ubitx_menu.ino +++ b/ubitx_20/ubitx_menu.ino @@ -306,10 +306,13 @@ void menuSelectMode(int btn){ FrequencyToVFO(1); } + /* if (cwMode == 0) si5351bx_setfreq(0, usbCarrier + (isIFShift ? ifShiftValue : 0)); //set back the carrier oscillator anyway, cw tx switches it off else si5351bx_setfreq(0, cwmCarrier + (isIFShift ? ifShiftValue : 0)); //set back the carrier oscillator anyway, cw tx switches it off + */ + SetCarrierFreq(); setFrequency(frequency); menuClearExit(500); diff --git a/ubitx_20/ubitx_si5351.ino b/ubitx_20/ubitx_si5351.ino index 8a1cb56..87819f8 100644 --- a/ubitx_20/ubitx_si5351.ino +++ b/ubitx_20/ubitx_si5351.ino @@ -124,15 +124,24 @@ void si5351_set_calibration(int32_t cal){ si5351bx_setfreq(0, usbCarrier); } +void SetCarrierFreq() +{ + unsigned long appliedCarrier = ((cwMode == 0 ? usbCarrier : cwmCarrier) + (isIFShift && (inTx == 0) ? ifShiftValue : 0)); + si5351bx_setfreq(0, appliedCarrier); + + /* + 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 - - if (cwMode == 0) - si5351bx_setfreq(0, usbCarrier + (isIFShift ? ifShiftValue : 0)); - else - si5351bx_setfreq(0, cwmCarrier + (isIFShift ? ifShiftValue : 0)); + SetCarrierFreq(); } //============================================================ From d7858e35c3d9c5b9ede469a8e504e9271f94263e Mon Sep 17 00:00:00 2001 From: phdlee Date: Tue, 20 Mar 2018 17:06:28 +0900 Subject: [PATCH 086/173] if shift bfo modified --- ubitx_20/ubitx_menu.ino | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/ubitx_20/ubitx_menu.ino b/ubitx_20/ubitx_menu.ino index 0a9bdac..fc15625 100644 --- a/ubitx_20/ubitx_menu.ino +++ b/ubitx_20/ubitx_menu.ino @@ -195,11 +195,13 @@ void menuIFSSetup(int btn){ { updateLine2Buffer(1); setFrequency(frequency); - + /* if (cwMode == 0) si5351bx_setfreq(0, usbCarrier + (isIFShift ? ifShiftValue : 0)); //set back the carrier oscillator anyway, cw tx switches it off else si5351bx_setfreq(0, cwmCarrier + (isIFShift ? ifShiftValue : 0)); //set back the carrier oscillator anyway, cw tx switches it off + */ + SetCarrierFreq(); needApplyChangeValue = 0; } @@ -224,6 +226,7 @@ void menuIFSSetup(int btn){ //printLineF2(F("OFF")); //clearLine2(); setFrequency(frequency); + SetCarrierFreq(); //delay_background(1500, 0); } From 31a7f795692501624882b9ad5005aa35b24d7c1f Mon Sep 17 00:00:00 2001 From: phdlee Date: Tue, 20 Mar 2018 21:41:24 +0900 Subject: [PATCH 087/173] ifshift store, cw mode shift change --- ubitx_20/ubitx_20.ino | 45 +++++++++++++++++++++++++++++------------ ubitx_20/ubitx_menu.ino | 8 +++++--- 2 files changed, 37 insertions(+), 16 deletions(-) diff --git a/ubitx_20/ubitx_20.ino b/ubitx_20/ubitx_20.ino index 258240d..0e61328 100644 --- a/ubitx_20/ubitx_20.ino +++ b/ubitx_20/ubitx_20.ino @@ -181,6 +181,12 @@ int count = 0; //to generally count ticks, loops, etc //(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 @@ -273,6 +279,7 @@ 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; @@ -331,7 +338,7 @@ byte line2DisplayStatus = 0; //0:Clear, 1 : menu, 1: DisplayFrom Idle, char lcdMeter[17]; byte isIFShift = 0; //1 = ifShift, 2 extend -long ifShiftValue = 0; // +int ifShiftValue = 0; // /** * Below are the basic functions that control the uBitx. Understanding the functions before @@ -896,6 +903,7 @@ void initSettings(){ } + EEPROM.get(COMMON_OPTION0, commonOption0); EEPROM.get(DISPLAY_OPTION1, displayOption1); EEPROM.get(DISPLAY_OPTION2, displayOption2); @@ -983,18 +991,29 @@ void initSettings(){ //Display Type for CW mode isShiftDisplayCWFreq = EEPROM.read(CW_DISPLAY_SHIFT); - //Adjust CW Mode Freq - shiftDisplayAdjustVal = (isShiftDisplayCWFreq & 0x3F) * 10; + //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; + } - //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; + } //default Value (for original hardware) if (cwAdcSTFrom >= cwAdcSTTo) @@ -1120,7 +1139,7 @@ void setup() //Serial.begin(9600); lcd.begin(16, 2); //printLineF(1, F("CECBT v1.05")); - printLineF(1, F("CE v1.059W")); + printLineF(1, F("CE v1.06")); Init_Cat(38400, SERIAL_8N1); initMeter(); //not used in this build diff --git a/ubitx_20/ubitx_menu.ino b/ubitx_20/ubitx_menu.ino index fc15625..fece070 100644 --- a/ubitx_20/ubitx_menu.ino +++ b/ubitx_20/ubitx_menu.ino @@ -209,7 +209,7 @@ void menuIFSSetup(int btn){ knob = enc_read(); if (knob != 0){ if (knob < 0) - ifShiftValue -= 50l; + ifShiftValue -= 50; else if (knob > 0) ifShiftValue += 50; @@ -222,6 +222,7 @@ void menuIFSSetup(int btn){ if (btnDown() || ifShiftValue == 0) { isIFShift = 0; + ifShiftValue = 0; //printLineF2(F("IF Shift is OFF")); //printLineF2(F("OFF")); //clearLine2(); @@ -229,9 +230,10 @@ void menuIFSSetup(int btn){ SetCarrierFreq(); //delay_background(1500, 0); } + + //Store IF Shiift + EEPROM.put(IF_SHIFTVALUE, ifShiftValue); - //menuOn = 0; - //printLine2ClearAndUpdate(); menuClearExit(0); } } From 93727e6b22319d46278b9db886a9c71a22542d6e Mon Sep 17 00:00:00 2001 From: phdlee Date: Wed, 21 Mar 2018 14:20:09 +0900 Subject: [PATCH 088/173] fixed Cat in IFSifht setup routine --- ubitx_20/ubitx_menu.ino | 1 + 1 file changed, 1 insertion(+) diff --git a/ubitx_20/ubitx_menu.ino b/ubitx_20/ubitx_menu.ino index fece070..4b7d9be 100644 --- a/ubitx_20/ubitx_menu.ino +++ b/ubitx_20/ubitx_menu.ino @@ -215,6 +215,7 @@ void menuIFSSetup(int btn){ needApplyChangeValue = 1; } + Check_Cat(0); //To prevent disconnections } delay_background(500, 0); //for check Long Press function key From bad62ef728a52632637aebeac16c7a5d8e6a8247 Mon Sep 17 00:00:00 2001 From: phdlee Date: Sat, 24 Mar 2018 21:33:01 +0900 Subject: [PATCH 089/173] Change Version Name --- ubitx_20/ubitx_20.ino | 1 - 1 file changed, 1 deletion(-) diff --git a/ubitx_20/ubitx_20.ino b/ubitx_20/ubitx_20.ino index 0e61328..daaf4c8 100644 --- a/ubitx_20/ubitx_20.ino +++ b/ubitx_20/ubitx_20.ino @@ -1138,7 +1138,6 @@ void setup() //Serial.begin(9600); lcd.begin(16, 2); - //printLineF(1, F("CECBT v1.05")); printLineF(1, F("CE v1.06")); Init_Cat(38400, SERIAL_8N1); From 8f8850f4daa8a57946772a3a0f9d27c3ffdb3664 Mon Sep 17 00:00:00 2001 From: phdlee Date: Sun, 25 Mar 2018 03:17:04 +0900 Subject: [PATCH 090/173] Update ubitx_wspr.cpp --- ubitx_20/ubitx_wspr.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ubitx_20/ubitx_wspr.cpp b/ubitx_20/ubitx_wspr.cpp index c87c1a7..4352413 100644 --- a/ubitx_20/ubitx_wspr.cpp +++ b/ubitx_20/ubitx_wspr.cpp @@ -155,8 +155,8 @@ void SendWSPRManage() //remark = To reduce the size of the program //prevFreq = frequency; //frequency = WsprTXFreq; - setTXFilters(WsprTXFreq); startTx(TX_CW, 0); + setTXFilters(WsprTXFreq); //Start WSPR Set_WSPR_Param(); From dd6d4555a89b8937484490055de1ccae022982d5 Mon Sep 17 00:00:00 2001 From: phdlee Date: Sun, 25 Mar 2018 03:21:31 +0900 Subject: [PATCH 091/173] Update ubitx_20.ino --- ubitx_20/ubitx_20.ino | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ubitx_20/ubitx_20.ino b/ubitx_20/ubitx_20.ino index daaf4c8..79a575b 100644 --- a/ubitx_20/ubitx_20.ino +++ b/ubitx_20/ubitx_20.ino @@ -1138,7 +1138,7 @@ void setup() //Serial.begin(9600); lcd.begin(16, 2); - printLineF(1, F("CE v1.06")); + printLineF(1, F("CE v1.061")); Init_Cat(38400, SERIAL_8N1); initMeter(); //not used in this build From 075f585a1e08b3758d0f7c7eed75ea0950403584 Mon Sep 17 00:00:00 2001 From: phdlee Date: Thu, 29 Mar 2018 22:31:36 +0900 Subject: [PATCH 092/173] Update README.md --- README.md | 36 +++++++++++++++++++++++++----------- 1 file changed, 25 insertions(+), 11 deletions(-) diff --git a/README.md b/README.md index de477e8..74dbeeb 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,6 @@ #IMPORTANT INFORMATION ---------------------------------------------------------------------------- -- A bug was found in version 1.0, When CW Keytype is set to IAMBCA and IAMBCB, there was a problem that switching to RX is not performed well when CAT communication is performed. If CW key type is straight, it works normally. This bug has been fixed and changed to version 1.01. -- Now Release Version 1.01 on my blog (http://www.hamskey.com) +- Now Release Version 1.061 on my blog (http://www.hamskey.com) - You can download and compiled hex file and uBITX Manager application on my blog (http://www.hamskey.com) #NOTICE @@ -14,9 +13,6 @@ So I will release the 0.27 version and if I do not see the bug anymore, I will t Now uBITX is an HF radio and will be able to join you in your happy hams life. Based on this source, you can use it by adding functions. -I am going to do a new project based on this source, linking with WSPR, WSJT-X and so on. -Of course, this repository is still running. If you have any bugs or ideas, please feel free to email me. - http://www.hamskey.com DE KD8CEC @@ -30,14 +26,32 @@ The copyright information of the original is below. KD8CEC ---------------------------------------------------------------------------- Prepared or finished tasks for the next version - - Include WSPR Beacone function - (implement other new repository) - complete experiment - need solve : Big code size (over 100%, then remove some functions for experment) - need replace Si5351 Library (increase risk and need more beta tester) - W3PM sent me his wonderful source - using BITX, GPS - + - Reduce Program size + - uBITX with RTL-SDR + - Direct control for Student + ---------------------------------------------------------------------------- ## REVISION RECORD +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.04 - Optimized from Version1.03 - Reduce program size (97% -> 95%) From 5afcdf2583d2e1c4f8541bc8b1d721979c513c57 Mon Sep 17 00:00:00 2001 From: phdlee Date: Wed, 4 Apr 2018 20:22:42 +0900 Subject: [PATCH 093/173] Update README.md --- README.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/README.md b/README.md index 74dbeeb..2d4404f 100644 --- a/README.md +++ b/README.md @@ -32,6 +32,9 @@ Prepared or finished tasks for the next version ---------------------------------------------------------------------------- ## REVISION RECORD +1.07 (Working) + - BetaVersion for Reduce program size + 1.061 - Added WSPR You only need uBITX to use WSPR. No external devices are required. @@ -52,6 +55,10 @@ Prepared or finished tasks for the next version (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%) From 7aafed9e9548251f755bcdac1d0aea1b64aee6bb Mon Sep 17 00:00:00 2001 From: phdlee Date: Wed, 4 Apr 2018 20:29:27 +0900 Subject: [PATCH 094/173] rename ubitx_wspr.cpp to ubitx_wspr.ino --- ubitx_20/ubitx.h | 1 + ubitx_20/{ubitx_wspr.cpp => ubitx_wspr.ino} | 0 2 files changed, 1 insertion(+) rename ubitx_20/{ubitx_wspr.cpp => ubitx_wspr.ino} (100%) diff --git a/ubitx_20/ubitx.h b/ubitx_20/ubitx.h index 64fcd3f..93467c3 100644 --- a/ubitx_20/ubitx.h +++ b/ubitx_20/ubitx.h @@ -34,6 +34,7 @@ extern byte delay_background(unsigned delayTime, byte fromType); extern int btnDown(void); extern char c[30]; extern char b[30]; +extern int enc_read(void); extern unsigned long frequency; diff --git a/ubitx_20/ubitx_wspr.cpp b/ubitx_20/ubitx_wspr.ino similarity index 100% rename from ubitx_20/ubitx_wspr.cpp rename to ubitx_20/ubitx_wspr.ino From 11e47fdccce2695d8553aab52437ce734d865143 Mon Sep 17 00:00:00 2001 From: phdlee Date: Wed, 4 Apr 2018 22:20:04 +0900 Subject: [PATCH 095/173] Added Version Info at top of ubitx_20.ino --- ubitx_20/ubitx_20.ino | 26 +++++++++++--------------- 1 file changed, 11 insertions(+), 15 deletions(-) diff --git a/ubitx_20/ubitx_20.ino b/ubitx_20/ubitx_20.ino index 79a575b..edb2714 100644 --- a/ubitx_20/ubitx_20.ino +++ b/ubitx_20/ubitx_20.ino @@ -1,8 +1,12 @@ +//Firmware Version +#define FIRMWARE_VERSION_INFO F("CE v1.061") +#define FIRMWARE_VERSION_NUM 0x02 //1st Complete Project : 1 (Version 1.061), 2st Project : 2 + /** - Since KD8CEC Version 0.29, most of the original code is no longer available. + Cat Suppoort uBITX CEC Version Most features(TX, Frequency Range, Ham Band, TX Control, CW delay, start Delay... more) have been added by KD8CEC. However, the license rules are subject to the original source rules. - DE Ian KD8CEC + Ian KD8CEC Original source comment ------------------------------------------------------------- * This source file is under General Public License version 3. @@ -104,7 +108,6 @@ #include LiquidCrystal lcd(8,9,10,11,12,13); -#define VERSION_NUM 0x01 //for KD8CEC'S firmware and for memory management software /** * The Arduino, unlike C/C++ on a regular computer with gigabytes of RAM, has very little memory. @@ -606,13 +609,6 @@ void stopTx(void){ inTx = 0; digitalWrite(TX_RX, 0); //turn off the tx - -/* - if (cwMode == 0) - si5351bx_setfreq(0, usbCarrier + (isIFShift ? ifShiftValue : 0)); //set back the carrier oscillator anyway, cw tx switches it off - else - si5351bx_setfreq(0, cwmCarrier + (isIFShift ? ifShiftValue : 0)); //set back the carrier oscillator anyway, cw tx switches it off -*/ SetCarrierFreq(); if (ritOn) @@ -669,7 +665,7 @@ void ritDisable(){ * flip the T/R line to T and update the display to denote transmission */ -void checkPTT(){ +void checkPTT(){ //we don't check for ptt when transmitting cw if (cwTimeout > 0) return; @@ -678,7 +674,7 @@ void checkPTT(){ startTx(TX_SSB, 1); delay(50); //debounce the PTT } - + if (digitalRead(PTT) == 1 && inTx == 1) stopTx(); } @@ -872,8 +868,8 @@ void initSettings(){ } //Version Write for Memory Management Software - if (EEPROM.read(VERSION_ADDRESS) != VERSION_NUM) - EEPROM.write(VERSION_ADDRESS, VERSION_NUM); + if (EEPROM.read(VERSION_ADDRESS) != FIRMWARE_VERSION_NUM) + EEPROM.write(VERSION_ADDRESS, FIRMWARE_VERSION_NUM); EEPROM.get(CW_CAL, cwmCarrier); @@ -1138,7 +1134,7 @@ void setup() //Serial.begin(9600); lcd.begin(16, 2); - printLineF(1, F("CE v1.061")); + printLineF(1, FIRMWARE_VERSION_INFO); Init_Cat(38400, SERIAL_8N1); initMeter(); //not used in this build From 02f22d66e53bdac09cb891d4a1dc8f52be113a85 Mon Sep 17 00:00:00 2001 From: phdlee Date: Thu, 5 Apr 2018 09:50:29 +0900 Subject: [PATCH 096/173] Change Menu codes --- ubitx_20/ubitx_20.ino | 2 +- ubitx_20/ubitx_menu.ino | 1406 +++++++++++++++++++++------------------ 2 files changed, 749 insertions(+), 659 deletions(-) diff --git a/ubitx_20/ubitx_20.ino b/ubitx_20/ubitx_20.ino index edb2714..08b24d2 100644 --- a/ubitx_20/ubitx_20.ino +++ b/ubitx_20/ubitx_20.ino @@ -1,5 +1,5 @@ //Firmware Version -#define FIRMWARE_VERSION_INFO F("CE v1.061") +#define FIRMWARE_VERSION_INFO F("CE v1.070") #define FIRMWARE_VERSION_NUM 0x02 //1st Complete Project : 1 (Version 1.061), 2st Project : 2 /** diff --git a/ubitx_20/ubitx_menu.ino b/ubitx_20/ubitx_menu.ino index 4b7d9be..02486c4 100644 --- a/ubitx_20/ubitx_menu.ino +++ b/ubitx_20/ubitx_menu.ino @@ -1,15 +1,11 @@ -/** 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 +/* +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" //Current Frequency and mode to active VFO by KD8CEC @@ -171,160 +167,6 @@ void byteToMode(byte modeValue, byte autoSetModebyFreq){ } } -//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; - - delay_background(500, 0); - updateLine2Buffer(1); - setFrequency(frequency); - - //Off or Change Value - while(!btnDown() ){ - if (needApplyChangeValue ==1) - { - updateLine2Buffer(1); - setFrequency(frequency); - /* - if (cwMode == 0) - si5351bx_setfreq(0, usbCarrier + (isIFShift ? ifShiftValue : 0)); //set back the carrier oscillator anyway, cw tx switches it off - else - si5351bx_setfreq(0, cwmCarrier + (isIFShift ? ifShiftValue : 0)); //set back the carrier oscillator anyway, cw tx switches it off - */ - SetCarrierFreq(); - - needApplyChangeValue = 0; - } - - knob = enc_read(); - if (knob != 0){ - if (knob < 0) - ifShiftValue -= 50; - else if (knob > 0) - ifShiftValue += 50; - - needApplyChangeValue = 1; - } - Check_Cat(0); //To prevent disconnections - } - - delay_background(500, 0); //for check Long Press function key - - if (btnDown() || ifShiftValue == 0) - { - isIFShift = 0; - ifShiftValue = 0; - //printLineF2(F("IF Shift is OFF")); - //printLineF2(F("OFF")); - //clearLine2(); - setFrequency(frequency); - SetCarrierFreq(); - //delay_background(1500, 0); - } - - //Store IF Shiift - EEPROM.put(IF_SHIFTVALUE, ifShiftValue); - - menuClearExit(0); - } -} - -//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 { - delay_background(500, 0); - - //LSB, USB, CWL, CWU - 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; - - while(!btnDown()){ - //Display Mode Name - memset(c, 0, sizeof(c)); - strcpy(c, " LSB USB CWL CWU"); - c[selectModeType * 4] = '>'; - printLine1(c); - - knob = enc_read(); - - if (knob != 0) - { - moveStep += (knob > 0 ? 1 : -1); - if (moveStep < -3) { - if (selectModeType > 0) - selectModeType--; - - moveStep = 0; - } - else if (moveStep > 3) { - if (selectModeType < 3) - selectModeType++; - - moveStep = 0; - } - } - - //Check_Cat(0); //To prevent disconnections - delay_background(50, 0); - } - - if (beforeMode != selectModeType) { - //printLineF1(F("Changed Mode")); - if (selectModeType == 0) { - cwMode = 0; isUSB = 0; - } - else if (selectModeType == 1) { - cwMode = 0; isUSB = 1; - } - else if (selectModeType == 2) { - cwMode = 1; - } - else if (selectModeType == 3) { - cwMode = 2; - } - - FrequencyToVFO(1); - } - - /* - if (cwMode == 0) - si5351bx_setfreq(0, usbCarrier + (isIFShift ? ifShiftValue : 0)); //set back the carrier oscillator anyway, cw tx switches it off - else - si5351bx_setfreq(0, cwmCarrier + (isIFShift ? ifShiftValue : 0)); //set back the carrier oscillator anyway, cw tx switches it off - */ - SetCarrierFreq(); - - setFrequency(frequency); - menuClearExit(500); - } -} - //Memory to VFO, VFO to Memory by KD8CEC void menuCHMemory(int btn, byte isMemoryToVfo){ @@ -446,69 +288,6 @@ void menuCHMemory(int btn, byte isMemoryToVfo){ } } - -//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 { - //printLineF2(F("Press to set Key")); //for reduce usable flash memory - delay_background(500, 0); - selectedKeyType = cwKeyType; - - while(!btnDown()){ - - //Display Key Type - if (selectedKeyType == 0) - printLineF1(F("Straight")); - else if (selectedKeyType == 1) - printLineF1(F("IAMBICA")); - else if (selectedKeyType == 2) - printLineF1(F("IAMBICB")); - - knob = enc_read(); - - if (knob != 0) - { - moveStep += (knob > 0 ? 1 : -1); - if (moveStep < -3) { - if (selectedKeyType > 0) - selectedKeyType--; - moveStep = 0; - } - else if (moveStep > 3) { - if (selectedKeyType < 2) - selectedKeyType++; - moveStep = 0; - } - } - - Check_Cat(0); //To prevent disconnections - } - - 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); - } -} - //Analog pin monitoring with CW Key and function keys connected. //by KD8CEC void menuADCMonitor(int btn){ @@ -612,28 +391,6 @@ void menuVfoToggle(int btn) } } -//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); - } -} //Split communication using VFOA and VFOB by KD8CEC void menuSplitOnOff(int btn){ @@ -684,98 +441,11 @@ void menuTxOnOff(int btn, byte optionType){ } } -/** - * 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; - /* - if (!modeCalibrate){ - modeCalibrate = true; - //printLineF2(F("Setup:On")); - } - else { - modeCalibrate = false; - //printLineF2(F("Setup:Off")); - } - */ - - menuClearExit(1000); - } -} - -void menuExit(int btn){ - if (!btn){ - printLineF2(F("Exit Menu?")); - } - else - menuClearExit(0); -} - -void menuCWSpeed(int btn){ - int knob = 0; - 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); - - 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); - } - //abort if this button is down - //if (btnDown()) - //re-enable the clock1 and clock 2 - // break; - - Check_Cat(0); //To prevent disconnections - } - - //save the setting - //printLineF2(F("CW Speed set!")); - cwSpeed = 1200 / wpm; - EEPROM.put(CW_SPEED, cwSpeed); - menuClearExit(1000); -} - void displayEmptyData(void){ printLineF2(F("Empty data")); delay_background(2000, 0); } - //Builtin CW Keyer Logic by KD8CEC void menuCWAutoKey(int btn){ if (!btn){ @@ -791,7 +461,6 @@ void menuCWAutoKey(int btn){ return; } - //printLineF1(F("Press PTT to Send")); printLineF1(F("PTT to Send")); delay_background(500, 0); updateDisplay(); @@ -819,6 +488,256 @@ void menuWSPRSend(int btn){ 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 +// 3 : Select Mode (different display type) +//knobSensitivity : 1 ~ +int getValueByKnob(int valueType, int targetValue, int minKnobValue, int maxKnobValue, int incStep, char* displayTitle, int knobSensitivity) +{ + int knob; + int moveDetectStep = 0; + int negativeSensitivity; + char isInitDisplay = 1; + delay_background(300, 0); //Default Delay + + 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; + } + + strcpy(b, displayTitle); + + if (valueType == 3) //Mode Select + { + b[targetValue * 4] = '>'; + } + /* + 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"); + } + */ + else + { + itoa(targetValue,c, 10); + strcat(b, c); + } + + printLine2(b); + + if (valueType == 1) //Generate Side Tone + { + tone(CW_TONE, targetValue); + } + else if (valueType == 2) + { + ifShiftValue = targetValue; + setFrequency(frequency); + SetCarrierFreq(); + } + } + + Check_Cat(0); //To prevent disconnections + } + + return targetValue; +} + +void menuCWSpeed(int btn){ + int knob = 0; + 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); +} + +//Modified by KD8CEC +void menuSetupCwTone(int btn){ + int knob = 0; + 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, "", 2); //1 : Generate Tone, targetValue, minKnobValue, maxKnobValue, stepSize + + /* + //disable all clock 1 and clock 2 + while (digitalRead(PTT) == HIGH && !btnDown()) + { + knob = enc_read(); + + if (knob > 0 && sideTone < 2000) + sideTone += 10; + else if (knob < 0 && sideTone > 100 ) + sideTone -= 10; + else + continue; //don't update the frequency or the display + + tone(CW_TONE, sideTone); + itoa(sideTone, b, 10); + printLine2(b); + + delay_background(100, 0); + } + */ + + + noTone(CW_TONE); + + //save the setting + //if (digitalRead(PTT) == LOW){ + printLineF2(F("Sidetone set!")); + EEPROM.put(CW_SIDETONE, sideTone); + delay_background(2000, 0); + //} + //else + // sideTone = prev_sideTone; + + menuClearExit(0); + } + //Modified by KD8CEC @@ -832,12 +751,18 @@ void menuSetupCwDelay(int btn){ } printLineF1(F("Press, set Delay")); + + /* strcpy(b, "DELAY:"); itoa(tmpCWDelay,c, 10); strcat(b, c); printLine2(b); - delay_background(300, 0); + */ + //delay_background(300, 0); + tmpCWDelay = getValueByKnob(0, tmpCWDelay, 3, 2500, 10, "DELAY:", 2); //0 : Generate Tone, targetValue, minKnobValue, maxKnobValue, stepSize + +/* while(!btnDown()){ knob = enc_read(); if (knob != 0){ @@ -857,6 +782,7 @@ void menuSetupCwDelay(int btn){ Check_Cat(0); //To prevent disconnections } +*/ //save the setting //printLineF2(F("CW Delay set!")); @@ -877,8 +803,11 @@ void menuSetupTXCWInterval(int btn){ } printLineF1(F("Press, set Delay")); - delay_background(300, 0); + //delay_background(300, 0); + tmpTXCWInterval = getValueByKnob(0, tmpTXCWInterval, 0, 500, 2, "Start Delay:", 2); //0 : Generate Tone, targetValue, minKnobValue, maxKnobValue, stepSize + +/* while(!btnDown()){ if (needDisplayInformation == 1) { @@ -895,12 +824,7 @@ void menuSetupTXCWInterval(int btn){ tmpTXCWInterval -= 2; if (tmpTXCWInterval < 500 && knob > 0) tmpTXCWInterval += 2; - /* - strcpy(b, "Start Delay:"); - itoa(tmpTXCWInterval,c, 10); - strcat(b, c); - printLine2(b); - */ + needDisplayInformation = 1; } //abort if this button is down @@ -909,6 +833,8 @@ void menuSetupTXCWInterval(int btn){ Check_Cat(0); //To prevent disconnections } +*/ + //save the setting //printLineF2(F("CW Start set!")); @@ -918,6 +844,482 @@ void menuSetupTXCWInterval(int btn){ menuClearExit(1000); } +//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; + + //delay_background(500, 0); + //updateLine2Buffer(1); + //setFrequency(frequency); + + ifShiftValue = getValueByKnob(2, ifShiftValue, -20000, 20000, 50, "IFS:", 2); //2 : IF Setup (updateLine2Buffer(1), SetFrequency), targetValue, minKnobValue, maxKnobValue, stepSize + +/* + //Off or Change Value + while(!btnDown() ){ + if (needApplyChangeValue ==1) + { + updateLine2Buffer(1); + setFrequency(frequency); + SetCarrierFreq(); + needApplyChangeValue = 0; + } + + knob = enc_read(); + if (knob != 0){ + if (knob < 0) + ifShiftValue -= 50; + else if (knob > 0) + ifShiftValue += 50; + + needApplyChangeValue = 1; + } + Check_Cat(0); //To prevent disconnections + } +*/ + + 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); + } +} + + +//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 + 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; + + //delay_background(500, 0); + + selectModeType = getValueByKnob(3, selectModeType, 0, 3, 1, " LSB USB CWL CWU", 4); //3 : Select Mode, targetValue, minKnobValue, maxKnobValue, stepSize + +/* + while(!btnDown()){ + //Display Mode Name + memset(c, 0, sizeof(c)); + strcpy(c, " LSB USB CWL CWU"); + c[selectModeType * 4] = '>'; + printLine1(c); + + knob = enc_read(); + + if (knob != 0) + { + moveStep += (knob > 0 ? 1 : -1); + if (moveStep < -3) { + if (selectModeType > 0) + selectModeType--; + + moveStep = 0; + } + else if (moveStep > 3) { + if (selectModeType < 3) + selectModeType++; + + moveStep = 0; + } + } + + //Check_Cat(0); //To prevent disconnections + delay_background(50, 0); + } +*/ + + if (beforeMode != selectModeType) { + //printLineF1(F("Changed Mode")); + if (selectModeType == 0) { + cwMode = 0; isUSB = 0; + } + else if (selectModeType == 1) { + cwMode = 0; isUSB = 1; + } + else if (selectModeType == 2) { + cwMode = 1; + } + else if (selectModeType == 3) { + cwMode = 2; + } + + FrequencyToVFO(1); + } + + SetCarrierFreq(); + + setFrequency(frequency); + menuClearExit(500); + } +} + +//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 { + //printLineF2(F("Press to set Key")); //for reduce usable flash memory + //delay_background(500, 0); + selectedKeyType = cwKeyType; + + //selectedKeyType = getValueByKnob(4, selectedKeyType, 0, 2, 1, " KEY:", 5); //4 : Select Key Type, targetValue, minKnobValue, maxKnobValue, stepSize + selectedKeyType = getValueByKnob(3, selectedKeyType, 0, 2, 1, " ST IA IB", 5); //4 : Select Key Type, targetValue, minKnobValue, maxKnobValue, stepSize + + /* + while(!btnDown()){ + + //Display Key Type + if (selectedKeyType == 0) + printLineF1(F("Straight")); + else if (selectedKeyType == 1) + printLineF1(F("IAMBICA")); + else if (selectedKeyType == 2) + printLineF1(F("IAMBICB")); + + knob = enc_read(); + + if (knob != 0) + { + moveStep += (knob > 0 ? 1 : -1); + if (moveStep < -3) { + if (selectedKeyType > 0) + selectedKeyType--; + moveStep = 0; + } + else if (moveStep > 3) { + if (selectedKeyType < 2) + selectedKeyType++; + moveStep = 0; + } + } + + Check_Cat(0); //To prevent disconnections + } + */ + + + 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); + } +} + +//===================================================== +//END OF STANDARD Tune Setup 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; + + while (menuOn){ + i = enc_read(); + btnState = btnDown(); + + if (i > 0){ + if (modeCalibrate && select + i < 240) + select += i; + if (!modeCalibrate && select + i < 130) + select += i; + } + + if (i < 0 && select - i >= -10) + select += i; //caught ya, i is already -ve here, so you add it + + //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 : + menuCWSpeed(btnState); + break; + case 6 : + menuSplitOnOff(btnState); //SplitOn / off + break; + case 7 : + menuCHMemory(btnState, 0); //VFO to Memroy + break; + case 8 : + menuCHMemory(btnState, 1); //Memory to VFO + break; + case 9 : + menuCWAutoKey(btnState); + break; + case 10 : + menuWSPRSend(btnState); + break; + case 11 : + menuSetup(btnState); + break; + case 12 : + menuExit(btnState); + break; + case 13 : + menuSetupCalibration(btnState); //crystal + break; + case 14 : + menuSetupCarrier(btnState); //lsb + break; + case 15 : + menuSetupCWCarrier(btnState); //lsb + break; + case 16 : + menuSetupCwTone(btnState); + break; + case 17 : + menuSetupCwDelay(btnState); + break; + case 18 : + menuSetupTXCWInterval(btnState); + break; + case 19 : + menuSetupKeyType(btnState); + break; + case 20 : + menuADCMonitor(btnState); + break; + case 21 : + menuTxOnOff(btnState, 0x01); //TX OFF / ON + break; + default : + menuExit(btnState); break; + } + + Check_Cat(0); //To prevent disconnections + } +} + +//************************************************************************************* +//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); + } +} + +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); + } +} + + + /** * Take a deep breath, math(ematics) ahead @@ -1143,315 +1545,3 @@ void menuSetupCarrier(int btn){ menuClearExit(0); } -//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); -} - -//Modified by KD8CEC -void menuSetupCwTone(int btn){ - int knob = 0; - int prev_sideTone; - - if (!btn){ - printLineF2(F("Change CW Tone")); - return; - } - - prev_sideTone = sideTone; - printLineF1(F("Tune CW tone")); - printLineF2(F("PTT to confirm.")); - delay_background(1000, 0); - tone(CW_TONE, sideTone); - - //disable all clock 1 and clock 2 - while (digitalRead(PTT) == HIGH && !btnDown()) - { - knob = enc_read(); - - if (knob > 0 && sideTone < 2000) - sideTone += 10; - else if (knob < 0 && sideTone > 100 ) - sideTone -= 10; - else - continue; //don't update the frequency or the display - - tone(CW_TONE, sideTone); - itoa(sideTone, b, 10); - printLine2(b); - - delay_background(100, 0); - } - noTone(CW_TONE); - //save the setting - if (digitalRead(PTT) == LOW){ - printLineF2(F("Sidetone set!")); - EEPROM.put(CW_SIDETONE, sideTone); - delay_background(2000, 0); - } - else - sideTone = prev_sideTone; - - menuClearExit(0); - } - -//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; - - while (menuOn){ - i = enc_read(); - btnState = btnDown(); - - if (i > 0){ - if (modeCalibrate && select + i < 240) - select += i; - if (!modeCalibrate && select + i < 130) - select += i; - } - - if (i < 0 && select - i >= -10) - select += i; //caught ya, i is already -ve here, so you add it - - //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 : - menuCWSpeed(btnState); - break; - case 6 : - menuSplitOnOff(btnState); //SplitOn / off - break; - case 7 : - menuCHMemory(btnState, 0); //VFO to Memroy - break; - case 8 : - menuCHMemory(btnState, 1); //Memory to VFO - break; - case 9 : - menuCWAutoKey(btnState); - break; - case 10 : - menuWSPRSend(btnState); - break; - case 11 : - menuSetup(btnState); - break; - case 12 : - menuExit(btnState); - break; - case 13 : - menuSetupCalibration(btnState); //crystal - break; - case 14 : - menuSetupCarrier(btnState); //lsb - break; - case 15 : - menuSetupCWCarrier(btnState); //lsb - break; - case 16 : - menuSetupCwTone(btnState); - break; - case 17 : - menuSetupCwDelay(btnState); - break; - case 18 : - menuSetupTXCWInterval(btnState); - break; - case 19 : - menuSetupKeyType(btnState); - break; - case 20 : - menuADCMonitor(btnState); - break; - case 21 : - menuTxOnOff(btnState, 0x01); //TX OFF / ON - break; - default : - menuExit(btnState); break; - } - /* - else if (select < 130 && modeCalibrate) - menuSetupCalibration(btnState); //crystal - else if (select < 140 && modeCalibrate) - menuSetupCarrier(btnState); //lsb - else if (select < 150 && modeCalibrate) - menuSetupCWCarrier(btnState); //lsb - else if (select < 160 && modeCalibrate) - menuSetupCwTone(btnState); - else if (select < 170 && modeCalibrate) - menuSetupCwDelay(btnState); - else if (select < 180 && modeCalibrate) - menuSetupTXCWInterval(btnState); - else if (select < 190 && modeCalibrate) - menuSetupKeyType(btnState); - else if (select < 200 && modeCalibrate) - menuADCMonitor(btnState); - else if (select < 210 && modeCalibrate) - menuTxOnOff(btnState, 0x01); //TX OFF / ON - else if (select < 220 && modeCalibrate) - menuExit(btnState); - */ - - Check_Cat(0); //To prevent disconnections - } - -/* - //debounce the button - while(btnDown()){ - delay_background(50, 0); //To prevent disconnections - } -*/ -} - From 9c4b694ce22a2229438df87aa41e331af20e8144 Mon Sep 17 00:00:00 2001 From: phdlee Date: Thu, 5 Apr 2018 10:19:38 +0900 Subject: [PATCH 097/173] Update README.md --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 2d4404f..5fc01db 100644 --- a/README.md +++ b/README.md @@ -32,7 +32,8 @@ Prepared or finished tasks for the next version ---------------------------------------------------------------------------- ## REVISION RECORD -1.07 (Working) +1.07 (Working...) + - Please do not download it yet. The code will continue to change for the time being. - BetaVersion for Reduce program size 1.061 From e8d67920731cf72692e5f0462396d0495221bb2d Mon Sep 17 00:00:00 2001 From: phdlee Date: Thu, 5 Apr 2018 17:36:16 +0900 Subject: [PATCH 098/173] complete setup menu ui for reduce program memory --- ubitx_20/ubitx_idle.ino | 189 ++++++++++++++++++---------------------- ubitx_20/ubitx_menu.ino | 31 ++++--- 2 files changed, 104 insertions(+), 116 deletions(-) diff --git a/ubitx_20/ubitx_idle.ino b/ubitx_20/ubitx_idle.ino index 87aa25e..4b72950 100644 --- a/ubitx_20/ubitx_idle.ino +++ b/ubitx_20/ubitx_idle.ino @@ -24,50 +24,18 @@ char line2Buffer[16]; int freqScrollPosition = 0; //Example Line2 Optinal Display //immediate execution, not call by scheulder -void updateLine2Buffer(char isDirectCall) +void updateLine2Buffer(char displayType) { unsigned long tmpFreq = 0; - if (isDirectCall == 0) + if (ritOn) { - if (ritOn) - { - strcpy(line2Buffer, "RitTX:"); - - //display frequency - tmpFreq = ritTxFrequency; - 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 + strcpy(line2Buffer, "RitTX:"); - //====================================================== - //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--) { + tmpFreq = ritTxFrequency; + for (int i = 15; i >= 6; i--) { if (tmpFreq > 0) { - if (i == 2 || i == 6) line2Buffer[i] = '.'; + if (i == 12 || i == 8) line2Buffer[i] = '.'; else { line2Buffer[i] = tmpFreq % 10 + 0x30; tmpFreq /= 10; @@ -76,85 +44,101 @@ void updateLine2Buffer(char isDirectCall) else line2Buffer[i] = ' '; } - - //EXAMPLE #1 - if ((displayOption1 & 0x04) == 0x00) //none scroll display - line2Buffer[6] = 'k'; + + 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] = 'k'; + else + { + //example #2 + if (freqScrollPosition++ > 18) //none scroll display time { - //example #2 - if (freqScrollPosition++ > 18) //none scroll display time + line2Buffer[6] = 'k'; + if (freqScrollPosition > 25) + freqScrollPosition = -1; + } + else //scroll frequency + { + line2Buffer[10] = 'H'; + line2Buffer[11] = 'z'; + + if (freqScrollPosition < 7) { - line2Buffer[6] = 'k'; - if (freqScrollPosition > 25) - freqScrollPosition = -1; + for (int i = 11; i >= 0; i--) + if (i - (7 - freqScrollPosition) >= 0) + line2Buffer[i] = line2Buffer[i - (7 - freqScrollPosition)]; + else + line2Buffer[i] = ' '; } - else //scroll frequency + else { - 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] = ' '; - } + for (int i = 0; i < 11; i++) + if (i + (freqScrollPosition - 7) <= 11) + line2Buffer[i] = line2Buffer[i + (freqScrollPosition - 7)]; + else + line2Buffer[i] = ' '; } - } //scroll - - line2Buffer[7] = ' '; - } //check direct call by encoder + } + } //scroll + + line2Buffer[7] = ' '; if (isIFShift) { - if (isDirectCall == 1) - for (int i = 0; i < 16; i++) - line2Buffer[i] = ' '; +// if (isDirectCall == 1) +// for (int i = 0; i < 16; i++) +// line2Buffer[i] = ' '; //IFShift Offset Value line2Buffer[8] = 'I'; line2Buffer[9] = 'F'; - //if (ifShiftValue == 0) - //{ - /* - line2Buffer[10] = 'S'; - line2Buffer[11] = ':'; - line2Buffer[12] = 'O'; - line2Buffer[13] = 'F'; - line2Buffer[14] = 'F'; - */ - //} - //else - //{ - line2Buffer[10] = ifShiftValue >= 0 ? '+' : 0; - line2Buffer[11] = 0; - line2Buffer[12] = ' '; + 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); - //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); + //if (isDirectCall == 1) //if call by encoder (not scheduler), immediate print value + printLine2(line2Buffer); } // end of display IF - else // step display + else // step & Key Type display { - if (isDirectCall != 0) - return; + //if (isDirectCall != 0) + // return; memset(&line2Buffer[8], ' ', 8); //Step @@ -174,8 +158,6 @@ void updateLine2Buffer(char isDirectCall) else line2Buffer[i +isStepKhz] = ' '; } - //if (isStepKhz == 1) - // line2Buffer[10] = 'k'; if (isStepKhz == 0) { @@ -184,7 +166,7 @@ void updateLine2Buffer(char isDirectCall) } line2Buffer[13] = ' '; - //if ( + //Check CW Key cwKeyType = 0; //0: straight, 1 : iambica, 2: iambicb if (cwKeyType == 0) { @@ -202,7 +184,6 @@ void updateLine2Buffer(char isDirectCall) line2Buffer[15] = 'B'; } } - } //meterType : 0 = S.Meter, 1 : P.Meter diff --git a/ubitx_20/ubitx_menu.ino b/ubitx_20/ubitx_menu.ino index 02486c4..5d82b51 100644 --- a/ubitx_20/ubitx_menu.ino +++ b/ubitx_20/ubitx_menu.ino @@ -557,6 +557,13 @@ int getValueByKnob(int valueType, int targetValue, int minKnobValue, int maxKnob int negativeSensitivity; char isInitDisplay = 1; delay_background(300, 0); //Default Delay + + if (valueType < 3) + { + strcpy(b, "Press, set "); + strcat(b, displayTitle); + printLine1(b); + } while(!btnDown()) { @@ -607,6 +614,7 @@ int getValueByKnob(int valueType, int targetValue, int minKnobValue, int maxKnob */ else { + strcat(b, ":"); itoa(targetValue,c, 10); strcat(b, c); } @@ -646,14 +654,14 @@ void menuCWSpeed(int btn){ return; } - printLineF1(F("Press to set WPM")); + //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); + wpm = getValueByKnob(0, wpm, 3, 50, 1, "WPM", 3); /* while(!btnDown()){ @@ -694,13 +702,13 @@ void menuSetupCwTone(int btn){ } prev_sideTone = sideTone; - printLineF1(F("Tune CW tone")); + //printLineF1(F("Tune CW tone")); //printLineF2(F("PTT to confirm.")); - printLineF1(F("Press to set WPM")); + //printLineF1(F("Press to set WPM")); //delay_background(1000, 0); - tone(CW_TONE, sideTone); + //tone(CW_TONE, sideTone); - sideTone = getValueByKnob(1, sideTone, 100, 2000, 10, "", 2); //1 : Generate Tone, targetValue, minKnobValue, maxKnobValue, stepSize + sideTone = getValueByKnob(1, sideTone, 100, 2000, 10, "Tone", 2); //1 : Generate Tone, targetValue, minKnobValue, maxKnobValue, stepSize /* //disable all clock 1 and clock 2 @@ -750,8 +758,7 @@ void menuSetupCwDelay(int btn){ return; } - printLineF1(F("Press, set Delay")); - + //printLineF1(F("Press, set Delay")); /* strcpy(b, "DELAY:"); itoa(tmpCWDelay,c, 10); @@ -760,7 +767,7 @@ void menuSetupCwDelay(int btn){ */ //delay_background(300, 0); - tmpCWDelay = getValueByKnob(0, tmpCWDelay, 3, 2500, 10, "DELAY:", 2); //0 : Generate Tone, targetValue, minKnobValue, maxKnobValue, stepSize + tmpCWDelay = getValueByKnob(0, tmpCWDelay, 3, 2500, 10, "Delay", 2); //0 : Generate Tone, targetValue, minKnobValue, maxKnobValue, stepSize /* while(!btnDown()){ @@ -802,10 +809,10 @@ void menuSetupTXCWInterval(int btn){ return; } - printLineF1(F("Press, set Delay")); + //printLineF1(F("Press, set Delay")); //delay_background(300, 0); - tmpTXCWInterval = getValueByKnob(0, tmpTXCWInterval, 0, 500, 2, "Start Delay:", 2); //0 : Generate Tone, targetValue, minKnobValue, maxKnobValue, stepSize + tmpTXCWInterval = getValueByKnob(0, tmpTXCWInterval, 0, 500, 2, "Delay", 2); //0 : Generate Tone, targetValue, minKnobValue, maxKnobValue, stepSize /* while(!btnDown()){ @@ -863,7 +870,7 @@ void menuIFSSetup(int btn){ //updateLine2Buffer(1); //setFrequency(frequency); - ifShiftValue = getValueByKnob(2, ifShiftValue, -20000, 20000, 50, "IFS:", 2); //2 : IF Setup (updateLine2Buffer(1), SetFrequency), targetValue, minKnobValue, maxKnobValue, stepSize + ifShiftValue = getValueByKnob(2, ifShiftValue, -20000, 20000, 50, "IFS", 2); //2 : IF Setup (updateLine2Buffer(1), SetFrequency), targetValue, minKnobValue, maxKnobValue, stepSize /* //Off or Change Value From 1210f56cd1660ce3f382aca52d8cd35e64c8e60d Mon Sep 17 00:00:00 2001 From: phdlee Date: Thu, 5 Apr 2018 21:30:35 +0900 Subject: [PATCH 099/173] move display routine ui to idle --- ubitx_20/ubitx_idle.ino | 278 +++++++++++++++++++++++++++++++++++ ubitx_20/ubitx_ui.ino | 315 ---------------------------------------- 2 files changed, 278 insertions(+), 315 deletions(-) diff --git a/ubitx_20/ubitx_idle.ino b/ubitx_20/ubitx_idle.ino index 4b72950..bee7c2f 100644 --- a/ubitx_20/ubitx_idle.ino +++ b/ubitx_20/ubitx_idle.ino @@ -17,6 +17,284 @@ along with this program. If not, see . **************************************************************************/ + +//======================================================================== +//Display Routine +//======================================================================== +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 +}; + +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); +} + +//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; +} + + + +// 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.print(' '); + } + } +} + +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(); +} + +//012...89ABC...Z +char byteToChar(byte srcByte){ + if (srcByte < 10) + return 0x30 + srcByte; + else + return 'A' + srcByte - 10; +} + +// 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 (isUSB) + strcpy(c, "USB "); + else + strcpy(c, "LSB "); + } + else if (cwMode == 1) + { + strcpy(c, "CWL "); + } + else + { + strcpy(c, "CWU "); + } + } + 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[16]; //KD8CEC 200Hz ST //L14.150 200Hz ST diff --git a/ubitx_20/ubitx_ui.ino b/ubitx_20/ubitx_ui.ino index c5089ea..27d570f 100644 --- a/ubitx_20/ubitx_ui.ino +++ b/ubitx_20/ubitx_ui.ino @@ -5,8 +5,6 @@ * of the radio. Occasionally, it is used to provide a two-line information that is * quickly cleared up. */ -//#define printLineF1(x) (printLineF(1, x)) -//#define printLineF2(x) (printLineF(0, x)) //returns true if the button is pressed int btnDown(void){ @@ -16,319 +14,6 @@ int btnDown(void){ return 1; } -/** - * Meter (not used in this build for anything) - * the meter is drawn using special characters. Each character is composed of 5 x 8 matrix. - * The s_meter array holds the definition of the these characters. - * each line of the array is is one character such that 5 bits of every byte - * makes up one line of pixels of the that character (only 5 bits are used) - * The current reading of the meter is assembled in the string called meter - */ - - -/* -const PROGMEM uint8_t s_meter_bitmap[] = { - B00000,B00000,B00000,B00000,B00000,B00100,B00100,B11011, - B10000,B10000,B10000,B10000,B10100,B10100,B10100,B11011, - B01000,B01000,B01000,B01000,B01100,B01100,B01100,B11011, - B00100,B00100,B00100,B00100,B00100,B00100,B00100,B11011, - B00010,B00010,B00010,B00010,B00110,B00110,B00110,B11011, - B00001,B00001,B00001,B00001,B00101,B00101,B00101,B11011 -}; -*/ - -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 -}; - -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); -} - -//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; -} - -/* -void drawMeter(int8_t needle){ - int16_t best, i, s; - - if (needle < 0) - return; - - s = (needle * 4)/10; - for (i = 0; i < 8; i++){ - if (s >= 5) - lcdMeter[i] = 1; - else if (s >= 0) - lcdMeter[i] = 2 + s; - else - lcdMeter[i] = 1; - s = s - 5; - } - if (needle >= 40) - lcdMeter[i-1] = 6; - lcdMeter[i] = 0; -} -*/ -// 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.print(' '); - } - } -} - -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(); -} - -//012...89ABC...Z -char byteToChar(byte srcByte){ - if (srcByte < 10) - return 0x30 + srcByte; - else - return 'A' + srcByte - 10; -} - -// 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 (isUSB) - strcpy(c, "USB "); - else - strcpy(c, "LSB "); - } - else if (cwMode == 1) - { - strcpy(c, "CWL "); - } - else - { - strcpy(c, "CWU "); - } - } - 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(":"); - } -} - int enc_prev_state = 3; /** From 5f906a4497233c9f963eb5ceb3f4526e2185a4be Mon Sep 17 00:00:00 2001 From: phdlee Date: Thu, 5 Apr 2018 22:16:54 +0900 Subject: [PATCH 100/173] To Support various LCD Type --- ubitx_20/cw_autokey.ino | 15 +- ubitx_20/ubitx_20.ino | 33 +- ubitx_20/ubitx_lcd_1602i.ino | 561 ++++++++++++++++++ .../{ubitx_idle.ino => ubitx_lcd_1602p.ino} | 42 +- ubitx_20/ubitx_lcd_2404i.ino | 22 + ubitx_20/ubitx_lcd_2404p.ino | 22 + 6 files changed, 663 insertions(+), 32 deletions(-) create mode 100644 ubitx_20/ubitx_lcd_1602i.ino rename ubitx_20/{ubitx_idle.ino => ubitx_lcd_1602p.ino} (92%) create mode 100644 ubitx_20/ubitx_lcd_2404i.ino create mode 100644 ubitx_20/ubitx_lcd_2404p.ino diff --git a/ubitx_20/cw_autokey.ino b/ubitx_20/cw_autokey.ino index 4d2d93f..f5fcbde 100644 --- a/ubitx_20/cw_autokey.ino +++ b/ubitx_20/cw_autokey.ino @@ -299,13 +299,14 @@ void controlAutoCW(){ printLineFromEEPRom(0, 2, cwStartIndex + displayScrolStep + CW_DATA_OFSTADJ, cwEndIndex + CW_DATA_OFSTADJ, 0); - byte diplayAutoCWLine = 0; - if ((displayOption1 & 0x01) == 0x01) - diplayAutoCWLine = 1; - - lcd.setCursor(0, diplayAutoCWLine); - lcd.write(byteToChar(selectedCWTextIndex)); - lcd.write(':'); + //byte diplayAutoCWLine = 0; + //if ((displayOption1 & 0x01) == 0x01) + // diplayAutoCWLine = 1; + + Display_AutoKeyTextIndex(selectedCWTextIndex); + //lcd.setCursor(0, diplayAutoCWLine); + //lcd.write(byteToChar(selectedCWTextIndex)); + //lcd.write(':'); isNeedScroll = (cwEndIndex - cwStartIndex) > 14 ? 1 : 0; scrollDispayTime = millis() + scrollSpeed; beforeCWTextIndex = selectedCWTextIndex; diff --git a/ubitx_20/ubitx_20.ino b/ubitx_20/ubitx_20.ino index 08b24d2..fc2c584 100644 --- a/ubitx_20/ubitx_20.ino +++ b/ubitx_20/ubitx_20.ino @@ -2,6 +2,16 @@ #define FIRMWARE_VERSION_INFO F("CE v1.070") #define FIRMWARE_VERSION_NUM 0x02 //1st Complete Project : 1 (Version 1.061), 2st Project : 2 +//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 +#define UBITX_DISPLAY_LCD1602I //I2C type 16 x 02 LCD +//#define UBITX_DISPLAY_LCD2404P //24 x 04 LCD +//#define UBITX_DISPLAY_LCD2404I //I2C type 24 x 04 LCD + + + /** Cat Suppoort uBITX CEC Version Most features(TX, Frequency Range, Ham Band, TX Control, CW delay, start Delay... more) have been added by KD8CEC. @@ -91,24 +101,6 @@ #define ANALOG_SPARE (A7) #define ANALOG_SMETER (A7) //by KD8CEC -/** - * The Raduino board is the size of a standard 16x2 LCD panel. It has three connectors: - * - * First, is an 8 pin connector that provides +5v, GND and six analog input pins that can also be - * configured to be used as digital input or output pins. These are referred to as A0,A1,A2, - * A3,A6 and A7 pins. The A4 and A5 pins are missing from this connector as they are used to - * talk to the Si5351 over I2C protocol. - * - * Second is a 16 pin LCD connector. This connector is meant specifically for the standard 16x2 - * LCD display in 4 bit mode. The 4 bit mode requires 4 data lines and two control lines to work: - * Lines used are : RESET, ENABLE, D4, D5, D6, D7 - * We include the library and declare the configuration of the LCD panel too - */ - -#include -LiquidCrystal lcd(8,9,10,11,12,13); - - /** * The Arduino, unlike C/C++ on a regular computer with gigabytes of RAM, has very little memory. * We have to be very careful with variables that are declared inside the functions as they are @@ -120,8 +112,6 @@ LiquidCrystal lcd(8,9,10,11,12,13); * the input and output from the USB port. We must keep a count of the bytes used while reading * the serial port as we can easily run out of buffer space. This is done in the serial_in_count variable. */ -char c[30], b[30]; -char printBuff[2][17]; //mirrors what is showing on the two lines of the display int count = 0; //to generally count ticks, loops, etc /** @@ -1133,11 +1123,10 @@ void setup() */ //Serial.begin(9600); - lcd.begin(16, 2); + LCD_Init(); printLineF(1, FIRMWARE_VERSION_INFO); Init_Cat(38400, SERIAL_8N1); - initMeter(); //not used in this build initSettings(); if (userCallsignLength > 0 && ((userCallsignLength & 0x80) == 0x80)) { diff --git a/ubitx_20/ubitx_lcd_1602i.ino b/ubitx_20/ubitx_lcd_1602i.ino new file mode 100644 index 0000000..7b56eaf --- /dev/null +++ b/ubitx_20/ubitx_lcd_1602i.ino @@ -0,0 +1,561 @@ +/************************************************************************* + KD8CEC, _______ + uBITX Display Routine for LCD1602 I2C + + 1.Code for 16 x 2 LCD for I2C. + 2.Display related functions of uBITX. Some functions moved from uBITX_Ui. + 3.uBITX Idle time Processing + Functions that run at times that do not affect TX, CW, and CAT + It is called in 1/10 time unit. +----------------------------------------------------------------------------- + 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 . + +**************************************************************************/ +#ifdef UBITX_DISPLAY_LCD1602I + + + +/** + * The Raduino board is the size of a standard 16x2 LCD panel. It has three connectors: + * + * First, is an 8 pin connector that provides +5v, GND and six analog input pins that can also be + * configured to be used as digital input or output pins. These are referred to as A0,A1,A2, + * A3,A6 and A7 pins. The A4 and A5 pins are missing from this connector as they are used to + * talk to the Si5351 over I2C protocol. + * + * Second is a 16 pin LCD connector. This connector is meant specifically for the standard 16x2 + * LCD display in 4 bit mode. The 4 bit mode requires 4 data lines and two control lines to work: + * Lines used are : RESET, ENABLE, D4, D5, D6, D7 + * We include the library and declare the configuration of the LCD panel too + */ + +#include +LiquidCrystal lcd(8,9,10,11,12,13); + +char c[30], b[30]; +char printBuff[2][17]; //mirrors what is showing on the two lines of the display + +void LCD_Init(void) +{ + lcd.begin(16, 2); + initMeter(); //for Meter Display +} + +void Display_AutoKeyTextIndex(char textIndex) +{ + byte diplayAutoCWLine = 0; + + if ((displayOption1 & 0x01) == 0x01) + diplayAutoCWLine = 1; + lcd.setCursor(0, diplayAutoCWLine); + lcd.write(byteToChar(selectedCWTextIndex)); + lcd.write(':'); +} + + +//======================================================================== +//Display Routine +//======================================================================== +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 +}; + +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); +} + +//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; +} + + + +// 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.print(' '); + } + } +} + +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(); +} + +//012...89ABC...Z +char byteToChar(byte srcByte){ + if (srcByte < 10) + return 0x30 + srcByte; + else + return 'A' + srcByte - 10; +} + +// 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 (isUSB) + strcpy(c, "USB "); + else + strcpy(c, "LSB "); + } + else if (cwMode == 1) + { + strcpy(c, "CWL "); + } + else + { + strcpy(c, "CWU "); + } + } + 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[16]; +//KD8CEC 200Hz ST +//L14.150 200Hz ST +//U14.150 +150khz +int freqScrollPosition = 0; +//Example Line2 Optinal Display +//immediate execution, not call by scheulder +void updateLine2Buffer(char displayType) +{ + unsigned long tmpFreq = 0; + if (ritOn) + { + strcpy(line2Buffer, "RitTX:"); + + //display frequency + tmpFreq = ritTxFrequency; + 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] = 'k'; + else + { + //example #2 + if (freqScrollPosition++ > 18) //none scroll display time + { + line2Buffer[6] = 'k'; + 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] = ' '; + + //Check CW Key cwKeyType = 0; //0: straight, 1 : iambica, 2: iambicb + 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); //call original source code + int lineNumber = 0; + if ((displayOption1 & 0x01) == 0x01) + lineNumber = 1; + + lcd.setCursor(drawPosition, lineNumber); + + for (int i = 0; i < 6; i++) //meter 5 + +db 1 = 6 + lcd.write(lcdMeter[i]); + } +} + +byte testValue = 0; +char checkCount = 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)) { + if (checkCount++ > 1) + { + updateLine2Buffer(0); //call by scheduler + printLine2(line2Buffer); + line2DisplayStatus = 2; + checkCount = 0; + } + + //EX for Meters + /* + DisplayMeter(0, testValue++, 7); + if (testValue > 30) + testValue = 0; + */ + } + } +} + + +#endif diff --git a/ubitx_20/ubitx_idle.ino b/ubitx_20/ubitx_lcd_1602p.ino similarity index 92% rename from ubitx_20/ubitx_idle.ino rename to ubitx_20/ubitx_lcd_1602p.ino index bee7c2f..b37bb45 100644 --- a/ubitx_20/ubitx_idle.ino +++ b/ubitx_20/ubitx_lcd_1602p.ino @@ -1,7 +1,10 @@ /************************************************************************* - KD8CEC's uBITX Idle time Processing - Functions that run at times that do not affect TX, CW, and CAT - It is called in 1/10 time unit. + KD8CEC's uBITX Display Routine for LCD1602 Parrel + 1.This is the display code for the default LCD mounted in uBITX. + 2.Display related functions of uBITX. Some functions moved from uBITX_Ui. + 3.uBITX Idle time Processing + Functions that run at times that do not affect TX, CW, and CAT + It is called in 1/10 time unit. ----------------------------------------------------------------------------- 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 @@ -17,6 +20,37 @@ along with this program. If not, see . **************************************************************************/ +#ifdef UBITX_DISPLAY_LCD1602P + + +//========================================================================= +//Hardware Control routines +//========================================================================= + + +#include +LiquidCrystal lcd(8,9,10,11,12,13); + +char c[30], b[30]; +char printBuff[2][17]; //mirrors what is showing on the two lines of the display + +void LCD_Init(void) +{ + lcd.begin(16, 2); + initMeter(); //for Meter Display +} + +void Display_AutoKeyTextIndex(char textIndex) +{ + byte diplayAutoCWLine = 0; + + if ((displayOption1 & 0x01) == 0x01) + diplayAutoCWLine = 1; + lcd.setCursor(0, diplayAutoCWLine); + lcd.write(byteToChar(selectedCWTextIndex)); + lcd.write(':'); +} + //======================================================================== //Display Routine @@ -511,3 +545,5 @@ void idle_process() } } + +#endif diff --git a/ubitx_20/ubitx_lcd_2404i.ino b/ubitx_20/ubitx_lcd_2404i.ino new file mode 100644 index 0000000..bee57ab --- /dev/null +++ b/ubitx_20/ubitx_lcd_2404i.ino @@ -0,0 +1,22 @@ +/************************************************************************* + KD8CEC's uBITX Display Routine for LCD2404 I2C + uBITX Idle time Processing + Functions that run at times that do not affect TX, CW, and CAT + It is called in 1/10 time unit. +----------------------------------------------------------------------------- + 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/ubitx_20/ubitx_lcd_2404p.ino b/ubitx_20/ubitx_lcd_2404p.ino new file mode 100644 index 0000000..66c9868 --- /dev/null +++ b/ubitx_20/ubitx_lcd_2404p.ino @@ -0,0 +1,22 @@ +/************************************************************************* + KD8CEC's uBITX Display Routine for LCD2404 Parrel + uBITX Idle time Processing + Functions that run at times that do not affect TX, CW, and CAT + It is called in 1/10 time unit. +----------------------------------------------------------------------------- + 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 . + +**************************************************************************/ + + From d4ed0589e592ee13fe0aff64faf005c3773b74ce Mon Sep 17 00:00:00 2001 From: phdlee Date: Thu, 5 Apr 2018 22:57:07 +0900 Subject: [PATCH 101/173] Applied 1602 Tiny LCD Library for reduce program Memory --- ubitx_20/ubitx_lcd_1602i.ino | 81 +++++------ ubitx_20/ubitx_lcd_1602p.ino | 263 +++++++++++++++++++++++++++-------- ubitx_20/ubitx_ui.ino | 7 + 3 files changed, 247 insertions(+), 104 deletions(-) diff --git a/ubitx_20/ubitx_lcd_1602i.ino b/ubitx_20/ubitx_lcd_1602i.ino index 7b56eaf..c8546f0 100644 --- a/ubitx_20/ubitx_lcd_1602i.ino +++ b/ubitx_20/ubitx_lcd_1602i.ino @@ -25,48 +25,23 @@ #ifdef UBITX_DISPLAY_LCD1602I - -/** - * The Raduino board is the size of a standard 16x2 LCD panel. It has three connectors: - * - * First, is an 8 pin connector that provides +5v, GND and six analog input pins that can also be - * configured to be used as digital input or output pins. These are referred to as A0,A1,A2, - * A3,A6 and A7 pins. The A4 and A5 pins are missing from this connector as they are used to - * talk to the Si5351 over I2C protocol. - * - * Second is a 16 pin LCD connector. This connector is meant specifically for the standard 16x2 - * LCD display in 4 bit mode. The 4 bit mode requires 4 data lines and two control lines to work: - * Lines used are : RESET, ENABLE, D4, D5, D6, D7 - * We include the library and declare the configuration of the LCD panel too - */ - +//======================================================================== +//Begin of LCD Hardware define +//======================================================================== #include LiquidCrystal lcd(8,9,10,11,12,13); + +//======================================================================== +//End of LCD Hardware define +//======================================================================== + +//======================================================================== +//Begin of Display Base Routines (Init, printLine..) +//======================================================================== char c[30], b[30]; char printBuff[2][17]; //mirrors what is showing on the two lines of the display -void LCD_Init(void) -{ - lcd.begin(16, 2); - initMeter(); //for Meter Display -} - -void Display_AutoKeyTextIndex(char textIndex) -{ - byte diplayAutoCWLine = 0; - - if ((displayOption1 & 0x01) == 0x01) - diplayAutoCWLine = 1; - lcd.setCursor(0, diplayAutoCWLine); - lcd.write(byteToChar(selectedCWTextIndex)); - lcd.write(':'); -} - - -//======================================================================== -//Display Routine -//======================================================================== 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 @@ -125,6 +100,12 @@ void initMeter(){ lcd.createChar(6, tmpbytes); } +void LCD_Init(void) +{ + lcd.begin(16, 2); + initMeter(); //for Meter Display +} + //by KD8CEC //0 ~ 25 : 30 over : + 10 void drawMeter(int needle) { @@ -161,7 +142,7 @@ void printLine(unsigned char linenmbr, const char *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.print(' '); + lcd.write(' '); } } } @@ -230,14 +211,13 @@ void printLine2ClearAndUpdate(){ line2DisplayStatus = 0; updateDisplay(); } +//=================================================================================== +//End of Display Base Routines +//=================================================================================== -//012...89ABC...Z -char byteToChar(byte srcByte){ - if (srcByte < 10) - return 0x30 + srcByte; - else - return 'A' + srcByte - 10; -} +//=================================================================================== +//Begin of User Interface Routines +//=================================================================================== // this builds up the top line of the display with frequency and mode void updateDisplay() { @@ -335,7 +315,7 @@ void updateDisplay() { else { lcd.setCursor(5,diplayVFOLine); - lcd.write(":"); + lcd.write(':'); } } @@ -557,5 +537,16 @@ void idle_process() } } +void Display_AutoKeyTextIndex(char textIndex) +{ + byte diplayAutoCWLine = 0; + + if ((displayOption1 & 0x01) == 0x01) + diplayAutoCWLine = 1; + lcd.setCursor(0, diplayAutoCWLine); + lcd.write(byteToChar(selectedCWTextIndex)); + lcd.write(':'); +} + #endif diff --git a/ubitx_20/ubitx_lcd_1602p.ino b/ubitx_20/ubitx_lcd_1602p.ino index b37bb45..607d070 100644 --- a/ubitx_20/ubitx_lcd_1602p.ino +++ b/ubitx_20/ubitx_lcd_1602p.ino @@ -23,38 +23,166 @@ #ifdef UBITX_DISPLAY_LCD1602P -//========================================================================= -//Hardware Control routines -//========================================================================= +//======================================================================== +//Begin of TinyLCD Library by KD8CEC +//======================================================================== +/************************************************************************* + 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 connected PIN +#define LCD_PIN_RS 8 +#define LCD_PIN_EN 9 +uint8_t LCD_PIN_DAT[4] = {10, 11, 12, 13}; + +// commands +#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 + +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); +} + +void LCD_Print(const char *c) +{ + for (int 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]); +} +//======================================================================== +//End of TinyLCD Library by KD8CEC +//======================================================================== + +/* #include LiquidCrystal lcd(8,9,10,11,12,13); +*/ + +//======================================================================== +//Begin of Display Base Routines (Init, printLine..) +//======================================================================== char c[30], b[30]; char printBuff[2][17]; //mirrors what is showing on the two lines of the display -void LCD_Init(void) -{ - lcd.begin(16, 2); - initMeter(); //for Meter Display -} - -void Display_AutoKeyTextIndex(char textIndex) -{ - byte diplayAutoCWLine = 0; - - if ((displayOption1 & 0x01) == 0x01) - diplayAutoCWLine = 1; - lcd.setCursor(0, diplayAutoCWLine); - lcd.write(byteToChar(selectedCWTextIndex)); - lcd.write(':'); -} - - -//======================================================================== -//Display Routine -//======================================================================== 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 @@ -86,31 +214,37 @@ void initMeter(){ for (i = 0; i < 8; i++) tmpbytes[i] = pgm_read_byte(plock_bitmap + i); - lcd.createChar(0, tmpbytes); + LCD_CreateChar(0, tmpbytes); for (i = 0; i < 8; i++) tmpbytes[i] = pgm_read_byte(p_metes_bitmap + i); - lcd.createChar(1, tmpbytes); + LCD_CreateChar(1, tmpbytes); for (i = 0; i < 8; i++) tmpbytes[i] = pgm_read_byte(p_metes_bitmap + i + 8); - lcd.createChar(2, tmpbytes); + LCD_CreateChar(2, tmpbytes); for (i = 0; i < 8; i++) tmpbytes[i] = pgm_read_byte(p_metes_bitmap + i + 16); - lcd.createChar(3, tmpbytes); + LCD_CreateChar(3, tmpbytes); for (i = 0; i < 8; i++) tmpbytes[i] = pgm_read_byte(p_metes_bitmap + i + 24); - lcd.createChar(4, tmpbytes); + LCD_CreateChar(4, tmpbytes); for (i = 0; i < 8; i++) tmpbytes[i] = pgm_read_byte(p_metes_bitmap + i + 32); - lcd.createChar(5, tmpbytes); + LCD_CreateChar(5, tmpbytes); for (i = 0; i < 8; i++) tmpbytes[i] = pgm_read_byte(p_metes_bitmap + i + 40); - lcd.createChar(6, tmpbytes); + LCD_CreateChar(6, tmpbytes); +} + +void LCD_Init(void) +{ + LCD1602_Init(); + initMeter(); //for Meter Display } //by KD8CEC @@ -136,20 +270,17 @@ void drawMeter(int needle) { lcdMeter[5] = 0x20; } - - // 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); + 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.print(' '); + LCD_Write(' '); } } } @@ -175,26 +306,28 @@ void printLineFromEEPRom(char linenmbr, char lcdColumn, byte eepromStartIndex, b if ((displayOption1 & 0x01) == 0x01) linenmbr = (linenmbr == 0 ? 1 : 0); //Line Toggle - lcd.setCursor(lcdColumn, linenmbr); + 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)); + 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(' '); + LCD_Write(' '); } // short cut to print to the first line -void printLine1(const char *c){ +void printLine1(const char *c) +{ printLine(1,c); } // short cut to print to the first line -void printLine2(const char *c){ +void printLine2(const char *c) +{ printLine(0,c); } @@ -219,14 +352,16 @@ void printLine2ClearAndUpdate(){ updateDisplay(); } -//012...89ABC...Z -char byteToChar(byte srcByte){ - if (srcByte < 10) - return 0x30 + srcByte; - else - return 'A' + srcByte - 10; -} +//================================================================================== +//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 @@ -313,17 +448,17 @@ void updateDisplay() { if ((vfoActive == VFO_A && ((isDialLock & 0x01) == 0x01)) || (vfoActive == VFO_B && ((isDialLock & 0x02) == 0x02))) { - lcd.setCursor(5,diplayVFOLine); - lcd.write((uint8_t)0); + LCD_SetCursor(5,diplayVFOLine); + LCD_Write((uint8_t)0); } else if (isCWAutoMode == 2){ - lcd.setCursor(5,diplayVFOLine); - lcd.write(0x7E); + LCD_SetCursor(5,diplayVFOLine); + LCD_Write(0x7E); } else { - lcd.setCursor(5,diplayVFOLine); - lcd.write(":"); + LCD_SetCursor(5,diplayVFOLine); + LCD_Write(':'); } } @@ -360,9 +495,7 @@ void updateLine2Buffer(char displayType) return; } //end of ritOn display - //====================================================== //other VFO display - //====================================================== if (vfoActive == VFO_B) { tmpFreq = vfoA; @@ -508,10 +641,10 @@ void DisplayMeter(byte meterType, byte meterValue, char drawPosition) if ((displayOption1 & 0x01) == 0x01) lineNumber = 1; - lcd.setCursor(drawPosition, lineNumber); + LCD_SetCursor(drawPosition, lineNumber); for (int i = 0; i < 6; i++) //meter 5 + +db 1 = 6 - lcd.write(lcdMeter[i]); + LCD_Write(lcdMeter[i]); } } @@ -545,5 +678,17 @@ void idle_process() } } +//AutoKey LCD Display Routine +void Display_AutoKeyTextIndex(char textIndex) +{ + byte diplayAutoCWLine = 0; + + if ((displayOption1 & 0x01) == 0x01) + diplayAutoCWLine = 1; + LCD_SetCursor(0, diplayAutoCWLine); + LCD_Write(byteToChar(selectedCWTextIndex)); + LCD_Write(':'); +} + #endif diff --git a/ubitx_20/ubitx_ui.ino b/ubitx_20/ubitx_ui.ino index 27d570f..4907282 100644 --- a/ubitx_20/ubitx_ui.ino +++ b/ubitx_20/ubitx_ui.ino @@ -6,6 +6,13 @@ * quickly cleared up. */ + 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){ if (digitalRead(FBUTTON) == HIGH) From 23f1b7cd5cb7ee31966cd02c25708088f7620e55 Mon Sep 17 00:00:00 2001 From: phdlee Date: Fri, 6 Apr 2018 21:43:36 +0900 Subject: [PATCH 102/173] Added IF Tune, ATT, SDR Functions --- ubitx_20/ubitx_20.ino | 38 ++++++++++- ubitx_20/ubitx_menu.ino | 129 ++++++++++++++++++++++++++++---------- ubitx_20/ubitx_si5351.ino | 2 +- 3 files changed, 134 insertions(+), 35 deletions(-) diff --git a/ubitx_20/ubitx_20.ino b/ubitx_20/ubitx_20.ino index fc2c584..3b674b7 100644 --- a/ubitx_20/ubitx_20.ino +++ b/ubitx_20/ubitx_20.ino @@ -5,11 +5,12 @@ //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 -#define UBITX_DISPLAY_LCD1602I //I2C type 16 x 02 LCD +#define UBITX_DISPLAY_LCD1602P //LCD mounted on unmodified uBITX +//#define UBITX_DISPLAY_LCD1602I //I2C type 16 x 02 LCD //#define UBITX_DISPLAY_LCD2404P //24 x 04 LCD //#define UBITX_DISPLAY_LCD2404I //I2C type 24 x 04 LCD +//#define ENABLE_FACTORYALIGN /** @@ -326,6 +327,11 @@ 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 attLevel = 0; //ATT : RF Gain Control (Receive) <-- IF1 Shift, 0 : Off, ShiftValue is attLevel * 100; attLevel 150 = 15K +char if1TuneValue = 0; //0 : OFF, IF1 + if1TuneValue * 100; // + - 12500; +byte sdrModeOn = 0; //SDR MODE ON / OFF +unsigned long SDR_Center_Freq = 32000000; + unsigned long beforeIdle_ProcessTime = 0; //for check Idle time byte line2DisplayStatus = 0; //0:Clear, 1 : menu, 1: DisplayFrom Idle, char lcdMeter[17]; @@ -495,7 +501,32 @@ void setFrequency(unsigned long f){ setTXFilters(f); unsigned long appliedCarrier = ((cwMode == 0 ? usbCarrier : cwmCarrier) + (isIFShift && (inTx == 0) ? ifShiftValue : 0)); + long if1AdjustValue = ((inTx == 0) ? (attLevel * 200) : 0) + (if1TuneValue * 50); //if1Tune RX, TX Enabled, ATT : only RX Mode + if (sdrModeOn && (inTx == 0)) //IF SDR + { + si5351bx_setfreq(2, 44999500 + if1AdjustValue + f); + si5351bx_setfreq(1, 44999500 + if1AdjustValue + SDR_Center_Freq + 2390); + } + else + { + if (cwMode == 1 || (cwMode == 0 && (!isUSB))) + { + //CWL(cwMode == 1) or LSB (cwMode == 0 && (!isUSB)) + si5351bx_setfreq(2, SECOND_OSC_LSB + if1AdjustValue + appliedCarrier + f); + //si5351bx_setfreq(1, SECOND_OSC_LSB + if1AdjustValue - (sdrModeOn ? (SDR_Center_Freq- usbCarrier) : 0)); + si5351bx_setfreq(1, SECOND_OSC_LSB + if1AdjustValue); + } + else + { + //CWU (cwMode == 2) or LSB (cwMode == 0 and isUSB) + si5351bx_setfreq(2, SECOND_OSC_USB + if1AdjustValue - appliedCarrier + f); + //si5351bx_setfreq(1, SECOND_OSC_USB + if1AdjustValue + (sdrModeOn ? (SDR_Center_Freq- usbCarrier) : 0)); //Increase LO Frequency => 1198500 -> 32Mhz + si5351bx_setfreq(1, SECOND_OSC_USB + if1AdjustValue); //Increase LO Frequency => 1198500 -> 32Mhz + } + } + + /* if (cwMode == 0) { if (isUSB){ @@ -518,6 +549,7 @@ void setFrequency(unsigned long f){ si5351bx_setfreq(1, SECOND_OSC_USB); } } + */ frequency = f; } @@ -1150,8 +1182,10 @@ void setup() setFrequency(vfoA); updateDisplay(); +#ifdef ENABLE_FACTORYALIGN if (btnDown()) factory_alignment(); +#endif } //Auto save Frequency and Mode with Protected eeprom life by KD8CEC diff --git a/ubitx_20/ubitx_menu.ino b/ubitx_20/ubitx_menu.ino index 5d82b51..07160ee 100644 --- a/ubitx_20/ubitx_menu.ino +++ b/ubitx_20/ubitx_menu.ino @@ -441,6 +441,37 @@ void menuTxOnOff(int btn, byte optionType){ } } +//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("[OFF]")); + } + else { + sdrModeOn = 1; + + if (ritOn == 1) + ritOn = 0; + + if (splitOn == 1) + splitOn = 0; + + printLineF2(F("[ON]")); + } + + setFrequency(frequency); + menuClearExit(500); + } +} + void displayEmptyData(void){ printLineF2(F("Empty data")); delay_background(2000, 0); @@ -548,9 +579,10 @@ void menuSetupCWCarrier(int btn){ //valueType 0 : Normal // 1 : CW Change -> Generate Tone // 2 : IF Shift Setup -> SetFrequency, Set SideTone -// 3 : Select Mode (different display type) +// 5 : ATT +// 11 : Select Mode (different display type) //knobSensitivity : 1 ~ -int getValueByKnob(int valueType, int targetValue, int minKnobValue, int maxKnobValue, int incStep, char* displayTitle, int knobSensitivity) +int getValueByKnob(int valueType, int targetValue, int minKnobValue, int maxKnobValue, int incStep, const char* displayTitle, int knobSensitivity) { int knob; int moveDetectStep = 0; @@ -558,7 +590,7 @@ int getValueByKnob(int valueType, int targetValue, int minKnobValue, int maxKnob char isInitDisplay = 1; delay_background(300, 0); //Default Delay - if (valueType < 3) + if (valueType < 10) { strcpy(b, "Press, set "); strcat(b, displayTitle); @@ -597,7 +629,7 @@ int getValueByKnob(int valueType, int targetValue, int minKnobValue, int maxKnob strcpy(b, displayTitle); - if (valueType == 3) //Mode Select + if (valueType == 11) //Mode Select { b[targetValue * 4] = '>'; } @@ -625,9 +657,13 @@ int getValueByKnob(int valueType, int targetValue, int minKnobValue, int maxKnob { tone(CW_TONE, targetValue); } - else if (valueType == 2) + else if (valueType == 2 || valueType == 5 ) // 2:IFS, 5:ATT { - ifShiftValue = targetValue; + if (valueType == 2) + ifShiftValue = targetValue; + else + attLevel = targetValue; + setFrequency(frequency); SetCarrierFreq(); } @@ -912,6 +948,31 @@ void menuIFSSetup(int btn){ } } +//ATT SETUP (IF1(45MHZ) SHIFT), by KD8CEC +void menuATTSetup(int btn){ + int knob = 0; + char needApplyChangeValue = 1; + + if (!btn){ + if (isIFShift == 1) + printLineF2(F("ATT Change?")); + else + printLineF2(F("ATT On?")); + } + else + { + attLevel = getValueByKnob(5, attLevel, 0, 200, 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); + } +} //Functions for CWL and CWU by KD8CEC void menuSelectMode(int btn){ @@ -939,7 +1000,7 @@ void menuSelectMode(int btn){ //delay_background(500, 0); - selectModeType = getValueByKnob(3, selectModeType, 0, 3, 1, " LSB USB CWL CWU", 4); //3 : Select Mode, targetValue, minKnobValue, maxKnobValue, stepSize + selectModeType = getValueByKnob(11, selectModeType, 0, 3, 1, " LSB USB CWL CWU", 4); //3 : Select Mode, targetValue, minKnobValue, maxKnobValue, stepSize /* while(!btnDown()){ @@ -1011,8 +1072,8 @@ void menuSetupKeyType(int btn){ //delay_background(500, 0); selectedKeyType = cwKeyType; - //selectedKeyType = getValueByKnob(4, selectedKeyType, 0, 2, 1, " KEY:", 5); //4 : Select Key Type, targetValue, minKnobValue, maxKnobValue, stepSize - selectedKeyType = getValueByKnob(3, selectedKeyType, 0, 2, 1, " ST IA IB", 5); //4 : Select Key Type, targetValue, minKnobValue, maxKnobValue, stepSize + //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 /* while(!btnDown()){ @@ -1170,14 +1231,13 @@ void doMenu(){ if (i > 0){ if (modeCalibrate && select + i < 240) select += i; - if (!modeCalibrate && select + i < 130) + else if (!modeCalibrate && select + i < 150) select += i; } + else if (i < 0 && select - i >= -10) + select += i; - if (i < 0 && select - i >= -10) - select += i; //caught ya, i is already -ve here, so you add it - - //if -> switch reduce program memory 200byte + //if -> switch : reduce program memory 200byte switch (select / 10) { case 0 : @@ -1196,60 +1256,65 @@ void doMenu(){ menuIFSSetup(btnState); break; case 5 : - menuCWSpeed(btnState); + menuATTSetup(btnState); break; case 6 : - menuSplitOnOff(btnState); //SplitOn / off + menuCWSpeed(btnState); break; case 7 : - menuCHMemory(btnState, 0); //VFO to Memroy + menuSplitOnOff(btnState); //SplitOn / off break; case 8 : - menuCHMemory(btnState, 1); //Memory to VFO + menuCHMemory(btnState, 0); //VFO to Memroy break; case 9 : - menuCWAutoKey(btnState); + menuCHMemory(btnState, 1); //Memory to VFO break; case 10 : - menuWSPRSend(btnState); + menuCWAutoKey(btnState); break; case 11 : - menuSetup(btnState); + menuWSPRSend(btnState); break; case 12 : - menuExit(btnState); + menuSDROnOff(btnState); break; case 13 : - menuSetupCalibration(btnState); //crystal + menuSetup(btnState); break; case 14 : - menuSetupCarrier(btnState); //lsb + menuExit(btnState); break; case 15 : - menuSetupCWCarrier(btnState); //lsb + menuSetupCalibration(btnState); //crystal break; case 16 : - menuSetupCwTone(btnState); + menuSetupCarrier(btnState); //ssb break; case 17 : - menuSetupCwDelay(btnState); + menuSetupCWCarrier(btnState); //cw break; case 18 : + menuSetupCwTone(btnState); + break; + case 19 : + menuSetupCwDelay(btnState); + break; + case 20 : menuSetupTXCWInterval(btnState); break; - case 19 : + case 21 : menuSetupKeyType(btnState); break; - case 20 : + case 22 : menuADCMonitor(btnState); break; - case 21 : + case 23 : menuTxOnOff(btnState, 0x01); //TX OFF / ON break; default : menuExit(btnState); break; - } - + } Check_Cat(0); //To prevent disconnections } } diff --git a/ubitx_20/ubitx_si5351.ino b/ubitx_20/ubitx_si5351.ino index 87819f8..6587db6 100644 --- a/ubitx_20/ubitx_si5351.ino +++ b/ubitx_20/ubitx_si5351.ino @@ -127,7 +127,7 @@ void si5351_set_calibration(int32_t cal){ void SetCarrierFreq() { unsigned long appliedCarrier = ((cwMode == 0 ? usbCarrier : cwmCarrier) + (isIFShift && (inTx == 0) ? ifShiftValue : 0)); - si5351bx_setfreq(0, appliedCarrier); + si5351bx_setfreq(0, (sdrModeOn ? 0 : appliedCarrier)); /* if (cwMode == 0) From 689cfda09e064752950519dd1a95bc55cd7a260c Mon Sep 17 00:00:00 2001 From: phdlee Date: Sat, 7 Apr 2018 21:32:01 +0900 Subject: [PATCH 103/173] Add Support SDR Receiver and improve ATT --- ubitx_20/cat_libs.ino | 36 ++++++++++++++++++++++++ ubitx_20/ubitx_20.ino | 61 +++++++++++++++++++++++++++++++++++------ ubitx_20/ubitx_menu.ino | 13 +++++++-- 3 files changed, 99 insertions(+), 11 deletions(-) diff --git a/ubitx_20/cat_libs.ino b/ubitx_20/cat_libs.ino index c9dca39..2285d52 100644 --- a/ubitx_20/cat_libs.ino +++ b/ubitx_20/cat_libs.ino @@ -611,6 +611,34 @@ void WriteEEPRom_FT817(byte fromType) 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 { @@ -768,6 +796,14 @@ void Check_Cat(byte fromType) 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; diff --git a/ubitx_20/ubitx_20.ino b/ubitx_20/ubitx_20.ino index 3b674b7..13a871d 100644 --- a/ubitx_20/ubitx_20.ino +++ b/ubitx_20/ubitx_20.ino @@ -10,7 +10,9 @@ //#define UBITX_DISPLAY_LCD2404P //24 x 04 LCD //#define UBITX_DISPLAY_LCD2404I //I2C type 24 x 04 LCD -//#define ENABLE_FACTORYALIGN +//Compile Option +#define ENABLE_FACTORYALIGN +#define ENABLE_ADCMONITOR //Starting with Version 1.07, you can read ADC values directly from uBITX Manager. So this function is not necessary. /** @@ -145,7 +147,12 @@ int count = 0; //to generally count ticks, loops, etc #define CW_SIDETONE 24 #define CW_SPEED 28 -//AT328 has 1KBytes EEPROM +//KD8CEC EEPROM MAP +#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 @@ -326,11 +333,12 @@ 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 : dynamic sdr frequency byte attLevel = 0; //ATT : RF Gain Control (Receive) <-- IF1 Shift, 0 : Off, ShiftValue is attLevel * 100; attLevel 150 = 15K char if1TuneValue = 0; //0 : OFF, IF1 + if1TuneValue * 100; // + - 12500; byte sdrModeOn = 0; //SDR MODE ON / OFF -unsigned long SDR_Center_Freq = 32000000; +unsigned long SDR_Center_Freq; //DEFAULT Frequency : 32000000 unsigned long beforeIdle_ProcessTime = 0; //for check Idle time byte line2DisplayStatus = 0; //0:Clear, 1 : menu, 1: DisplayFrom Idle, @@ -501,12 +509,30 @@ void setFrequency(unsigned long f){ setTXFilters(f); unsigned long appliedCarrier = ((cwMode == 0 ? usbCarrier : cwmCarrier) + (isIFShift && (inTx == 0) ? ifShiftValue : 0)); - long if1AdjustValue = ((inTx == 0) ? (attLevel * 200) : 0) + (if1TuneValue * 50); //if1Tune RX, TX Enabled, ATT : only RX Mode + long if1AdjustValue = ((inTx == 0) ? (attLevel * 100) : 0) + (if1TuneValue * 100); //if1Tune RX, TX Enabled, ATT : only RX Mode if (sdrModeOn && (inTx == 0)) //IF SDR { - si5351bx_setfreq(2, 44999500 + if1AdjustValue + f); - si5351bx_setfreq(1, 44999500 + if1AdjustValue + SDR_Center_Freq + 2390); + //Fixed Frequency SDR (Default Frequency : 32Mhz, available change sdr Frequency by uBITX Manager) + //Dynamic Frequency is for SWL without cat + //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 + + //Dynamic Frequency + //if (advancedFreqOption1 & 0x04 != 0x00) + // if1AdjustValue += (f % 10000000); + + si5351bx_setfreq(2, 44991500 + if1AdjustValue + f); + si5351bx_setfreq(1, 44991500 + + if1AdjustValue + + SDR_Center_Freq + + ((advancedFreqOption1 & 0x04) == 0x00 ? 0 : (f % 10000000)) + + 2390); + /* + si5351bx_setfreq(2, 44999500 + f); + si5351bx_setfreq(1, 44999500 + SDR_Center_Freq + 2390); + */ } else { @@ -880,7 +906,7 @@ void initSettings(){ printLineF(1, F("Init EEProm...")); //initial all eeprom - for (unsigned int i = 32; i < 1024; i++) //protect Master_cal, usb_cal + for (unsigned int i = 64; i < 1024; i++) //protect Master_cal, usb_cal EEPROM.write(i, 0); //Write Firmware ID @@ -1033,6 +1059,25 @@ void initSettings(){ isIFShift = ifShiftValue != 0; } + //Advanced Freq control + EEPROM.get(ADVANCED_FREQ_OPTION1, advancedFreqOption1); + + //use Advanced Frequency Control + 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) { diff --git a/ubitx_20/ubitx_menu.ino b/ubitx_20/ubitx_menu.ino index 07160ee..094e594 100644 --- a/ubitx_20/ubitx_menu.ino +++ b/ubitx_20/ubitx_menu.ino @@ -290,6 +290,7 @@ void menuCHMemory(int btn, byte isMemoryToVfo){ //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) @@ -359,6 +360,7 @@ void menuADCMonitor(int btn){ menuClearExit(0); } +#endif //VFO Toggle and save VFO Information, modified by KD8CEC void menuVfoToggle(int btn) @@ -467,6 +469,7 @@ void menuSDROnOff(int btn) printLineF2(F("[ON]")); } + EEPROM.put(ENABLE_SDR, sdrModeOn); setFrequency(frequency); menuClearExit(500); } @@ -954,14 +957,14 @@ void menuATTSetup(int btn){ char needApplyChangeValue = 1; if (!btn){ - if (isIFShift == 1) + if (attLevel != 0) printLineF2(F("ATT Change?")); else printLineF2(F("ATT On?")); } else { - attLevel = getValueByKnob(5, attLevel, 0, 200, 5, "ATT", 2); //2 : (SetFrequency), targetValue, minKnobValue, maxKnobValue, stepSize + 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) @@ -1229,7 +1232,7 @@ void doMenu(){ btnState = btnDown(); if (i > 0){ - if (modeCalibrate && select + i < 240) + if (modeCalibrate && select + i < 250) select += i; else if (!modeCalibrate && select + i < 150) select += i; @@ -1306,10 +1309,14 @@ void doMenu(){ 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 : From 34be2d08452c16a7bd93a1a0d5abb8276e07acbd Mon Sep 17 00:00:00 2001 From: phdlee Date: Mon, 9 Apr 2018 23:35:13 +0900 Subject: [PATCH 104/173] Improve receive perforamnce for USB, CWU, custom uBITX --- ubitx_20/ubitx_20.ino | 89 +++++++++++++++++++++++++++++++------------ 1 file changed, 64 insertions(+), 25 deletions(-) diff --git a/ubitx_20/ubitx_20.ino b/ubitx_20/ubitx_20.ino index 13a871d..f3ccbf0 100644 --- a/ubitx_20/ubitx_20.ino +++ b/ubitx_20/ubitx_20.ino @@ -1,5 +1,5 @@ //Firmware Version -#define FIRMWARE_VERSION_INFO F("CE v1.070") +#define FIRMWARE_VERSION_INFO F("CE v1.071") #define FIRMWARE_VERSION_NUM 0x02 //1st Complete Project : 1 (Version 1.061), 2st Project : 2 //Depending on the type of LCD mounted on the uBITX, uncomment one of the options below. @@ -334,9 +334,9 @@ 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 : dynamic sdr 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 -char if1TuneValue = 0; //0 : OFF, IF1 + if1TuneValue * 100; // + - 12500; +byte if1TuneValue = 0; //0 : OFF, IF1 + if1TuneValue * 100; // + - 12500; byte sdrModeOn = 0; //SDR MODE ON / OFF unsigned long SDR_Center_Freq; //DEFAULT Frequency : 32000000 @@ -509,46 +509,85 @@ void setFrequency(unsigned long f){ setTXFilters(f); unsigned long appliedCarrier = ((cwMode == 0 ? usbCarrier : cwmCarrier) + (isIFShift && (inTx == 0) ? ifShiftValue : 0)); - long if1AdjustValue = ((inTx == 0) ? (attLevel * 100) : 0) + (if1TuneValue * 100); //if1Tune RX, TX Enabled, ATT : only RX Mode + int appliedTuneValue = 0; - if (sdrModeOn && (inTx == 0)) //IF SDR + //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 - //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 - //Dynamic Frequency - //if (advancedFreqOption1 & 0x04 != 0x00) - // if1AdjustValue += (f % 10000000); + //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) //Khzz 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); + } si5351bx_setfreq(2, 44991500 + if1AdjustValue + f); si5351bx_setfreq(1, 44991500 + if1AdjustValue + SDR_Center_Freq - + ((advancedFreqOption1 & 0x04) == 0x00 ? 0 : (f % 10000000)) + //+ ((advancedFreqOption1 & 0x04) == 0x00 ? 0 : (f % 10000000)) + + moveFrequency + 2390); - /* - si5351bx_setfreq(2, 44999500 + f); - si5351bx_setfreq(1, 44999500 + SDR_Center_Freq + 2390); - */ } else { - if (cwMode == 1 || (cwMode == 0 && (!isUSB))) + 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 - (sdrModeOn ? (SDR_Center_Freq- usbCarrier) : 0)); si5351bx_setfreq(1, SECOND_OSC_LSB + if1AdjustValue); } - else + else //cwu or usb { - //CWU (cwMode == 2) or LSB (cwMode == 0 and isUSB) + //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 + (sdrModeOn ? (SDR_Center_Freq- usbCarrier) : 0)); //Increase LO Frequency => 1198500 -> 32Mhz - si5351bx_setfreq(1, SECOND_OSC_USB + if1AdjustValue); //Increase LO Frequency => 1198500 -> 32Mhz + si5351bx_setfreq(1, SECOND_OSC_USB + if1AdjustValue); } } @@ -1062,13 +1101,13 @@ void initSettings(){ //Advanced Freq control EEPROM.get(ADVANCED_FREQ_OPTION1, advancedFreqOption1); - //use Advanced Frequency Control - if (advancedFreqOption1 & 0x01 != 0x00) + //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) + if ((advancedFreqOption1 & 0x02) != 0x00) { EEPROM.get(ENABLE_SDR, sdrModeOn); } From d72181603987234dc6d809978131f181ff6198a6 Mon Sep 17 00:00:00 2001 From: phdlee Date: Thu, 12 Apr 2018 22:08:43 +0900 Subject: [PATCH 105/173] LCD Work step1 --- ubitx_20/ubitx.h | 113 ++- ubitx_20/ubitx_20.ino | 189 +---- ubitx_20/ubitx_eemap.h | 118 +++ ubitx_20/ubitx_lcd_1602i.ino | 47 +- ubitx_20/ubitx_lcd_1602iian.ino | 753 ++++++++++++++++++ ubitx_20/ubitx_lcd_1602p.ino | 28 +- ...bitx_lcd_2404i.ino => ubitx_lcd_2004i.ino} | 0 ubitx_20/ubitx_lcd_2004p.ino | 702 ++++++++++++++++ ubitx_20/ubitx_lcd_2404p.ino | 22 - ubitx_20/ubitx_menu.ino | 32 +- ubitx_20/ubitx_wspr.ino | 9 +- 11 files changed, 1747 insertions(+), 266 deletions(-) create mode 100644 ubitx_20/ubitx_eemap.h create mode 100644 ubitx_20/ubitx_lcd_1602iian.ino rename ubitx_20/{ubitx_lcd_2404i.ino => ubitx_lcd_2004i.ino} (100%) create mode 100644 ubitx_20/ubitx_lcd_2004p.ino delete mode 100644 ubitx_20/ubitx_lcd_2404p.ino diff --git a/ubitx_20/ubitx.h b/ubitx_20/ubitx.h index 93467c3..41d3555 100644 --- a/ubitx_20/ubitx.h +++ b/ubitx_20/ubitx.h @@ -14,32 +14,58 @@ You should have received a copy of the GNU General Public License along with this program. If not, see . **************************************************************************/ -#define WSPR_COUNT 443 //WSPR_MESSAGE_COUNT -#define WSPR_MESSAGE1 444 // -#define WSPR_MESSAGE2 490 // -#define WSPR_MESSAGE3 536 // -#define WSPR_MESSAGE4 582 // +#include //for Linux, On Linux it is case sensitive. -#define WSPR_BAND_COUNT 3 +//============================================================================== +// Compile Option +//============================================================================== +#define ENABLE_FACTORYALIGN +#define ENABLE_ADCMONITOR //Starting with Version 1.07, you can read ADC values directly from uBITX Manager. So this function is not necessary. -#define TX_SSB 0 -#define TX_CW 1 +//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 +//#define UBITX_DISPLAY_LCD1602I //I2C type 16 x 02 LCD +//#define UBITX_DISPLAY_LCD1602I_CUST //I2C type 16 x 02 Custom Tiny Library +#define UBITX_DISPLAY_LCD2004P //24 x 04 LCD +//#define UBITX_DISPLAY_LCD2004I //I2C type 24 x 04 LCD - -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[30]; -extern char b[30]; -extern int enc_read(void); - -extern unsigned long frequency; - -#define printLineF1(x) (printLineF(1, x)) -#define printLineF2(x) (printLineF(0, x)) +//============================================================================== +// 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 + * 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 /** @@ -51,19 +77,34 @@ extern unsigned long frequency; * - 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) -#define TX_RX (7) -#define CW_TONE (6) -#define TX_LPF_A (5) -#define TX_LPF_B (4) -#define TX_LPF_C (3) -#define CW_KEY (2) +//============================================================================== +// 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)) -//we directly generate the CW by programmin the Si5351 to the cw tx frequency, hence, both are different modes -//these are the parameter passed to startTx -#define TX_SSB 0 -#define TX_CW 1 +extern unsigned long frequency; +extern byte WsprMSGCount; +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[30]; +extern char b[30]; +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); @@ -76,6 +117,6 @@ extern void stopTx(void); extern void setTXFilters(unsigned long freq); extern void SendWSPRManage(void); -extern byte WsprMSGCount; - - +extern char byteToChar(byte srcByte); +extern void DisplayCallsign(byte callSignLength); +extern void DisplayVersionInfo(const char* fwVersionInfo); diff --git a/ubitx_20/ubitx_20.ino b/ubitx_20/ubitx_20.ino index f3ccbf0..5b7b13a 100644 --- a/ubitx_20/ubitx_20.ino +++ b/ubitx_20/ubitx_20.ino @@ -1,20 +1,7 @@ //Firmware Version -#define FIRMWARE_VERSION_INFO F("CE v1.071") +#define FIRMWARE_VERSION_INFO F("CEC v1.071") #define FIRMWARE_VERSION_NUM 0x02 //1st Complete Project : 1 (Version 1.061), 2st Project : 2 -//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 -//#define UBITX_DISPLAY_LCD1602I //I2C type 16 x 02 LCD -//#define UBITX_DISPLAY_LCD2404P //24 x 04 LCD -//#define UBITX_DISPLAY_LCD2404I //I2C type 24 x 04 LCD - -//Compile Option -#define ENABLE_FACTORYALIGN -#define ENABLE_ADCMONITOR //Starting with Version 1.07, you can read ADC values directly from uBITX Manager. So this function is not necessary. - - /** Cat Suppoort uBITX CEC Version Most features(TX, Frequency Range, Ham Band, TX Control, CW delay, start Delay... more) have been added by KD8CEC. @@ -56,161 +43,8 @@ #include #include #include "ubitx.h" +#include "ubitx_eemap.h" -/** - 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. - - We no longer use the standard SI5351 library because of its huge overhead due to many unused - features consuming a lot of program space. Instead of depending on an external library we now use - Jerry Gaffke's, KE7ER, lightweight standalone mimimalist "si5351bx" routines (see further down the - code). Here are some defines and declarations used by Jerry's routines: -*/ - - -/** - * 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 - * 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 Arduino, unlike C/C++ on a regular computer with gigabytes of RAM, has very little memory. - * We have to be very careful with variables that are declared inside the functions as they are - * created in a memory region called the stack. The stack has just a few bytes of space on the Arduino - * if you declare large strings inside functions, they can easily exceed the capacity of the stack - * and mess up your programs. - * We circumvent this by declaring a few global buffers as kitchen counters where we can - * slice and dice our strings. These strings are mostly used to control the display or handle - * the input and output from the USB port. We must keep a count of the bytes used while reading - * the serial port as we can easily run out of buffer space. This is done in the serial_in_count variable. - */ -int count = 0; //to generally count ticks, loops, etc - -/** - * The second set of 16 pins on the Raduino's bottom connector are have the three clock outputs and the digital lines to control the rig. - * This assignment is as follows : - * Pin 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 - * GND +5V CLK0 GND GND CLK1 GND GND CLK2 GND D2 D3 D4 D5 D6 D7 - * These too are flexible with what you may do with them, for the Raduino, we use them to : - * - TX_RX line : Switches between Transmit and Receive after sensing the PTT or the morse keyer - * - CW_KEY line : turns on the carrier for CW - */ - -#define TX_RX (7) -#define CW_TONE (6) -#define TX_LPF_A (5) -#define TX_LPF_B (4) -#define TX_LPF_C (3) -#define CW_KEY (2) - -/** - * These are the indices where these user changable settinngs are stored in the EEPROM - */ -#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 - -//KD8CEC EEPROM MAP -#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 CHANNEL_FREQ 630 //Channel 1 ~ 20, 1 Channel = 4 bytes -#define CHANNEL_DESC 710 //Channel 1 ~ 20, 1 Channel = 4 bytes -#define RESERVE3 770 //Reserve3 between Channel and Firmware id check - -//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 /** * 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, @@ -242,11 +76,6 @@ int count = 0; //to generally count ticks, loops, etc #define LOWEST_FREQ_DIAL (3000l) #define HIGHEST_FREQ_DIAL (60000000l) -//we directly generate the CW by programmin the Si5351 to the cw tx frequency, hence, both are different modes -//these are the parameter passed to startTx -#define TX_SSB 0 -#define TX_CW 1 - char ritOn = 0; char vfoActive = VFO_A; int8_t meter_reading = 0; // a -1 on meter makes it invisible @@ -338,7 +167,7 @@ byte advancedFreqOption1; //255 : Bit0: use IFTune_Value, Bit1 : use Stored 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; //DEFAULT Frequency : 32000000 +unsigned long SDR_Center_Freq; // unsigned long beforeIdle_ProcessTime = 0; //for check Idle time byte line2DisplayStatus = 0; //0:Clear, 1 : menu, 1: DisplayFrom Idle, @@ -1114,8 +943,8 @@ void initSettings(){ } EEPROM.get(SDR_FREQUNCY, SDR_Center_Freq); - if (SDR_Center_Freq == 0) - SDR_Center_Freq = 32000000; + //if (SDR_Center_Freq == 0) + // SDR_Center_Freq = 32000000; //default Value (for original hardware) if (cwAdcSTFrom >= cwAdcSTTo) @@ -1240,15 +1069,17 @@ void setup() //Serial.begin(9600); LCD_Init(); - printLineF(1, FIRMWARE_VERSION_INFO); + //printLineF(1, FIRMWARE_VERSION_INFO); + DisplayVersionInfo(FIRMWARE_VERSION_INFO); Init_Cat(38400, SERIAL_8N1); initSettings(); if (userCallsignLength > 0 && ((userCallsignLength & 0x80) == 0x80)) { userCallsignLength = userCallsignLength & 0x7F; - printLineFromEEPRom(0, 0, 0, userCallsignLength -1, 0); //eeprom to lcd use offset (USER_CALLSIGN_DAT) - delay(500); + //printLineFromEEPRom(0, 0, 0, userCallsignLength -1, 0); //eeprom to lcd use offset (USER_CALLSIGN_DAT) + //delay(500); + DisplayCallsign(userCallsignLength); } else { printLineF(0, F("uBITX v0.20")); diff --git a/ubitx_20/ubitx_eemap.h b/ubitx_20/ubitx_eemap.h new file mode 100644 index 0000000..dc10487 --- /dev/null +++ b/ubitx_20/ubitx_eemap.h @@ -0,0 +1,118 @@ +/************************************************************************* + 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 . +**************************************************************************/ +//============================================================================== +// 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 ~ 63 +//============================================================================== +#define RESERVE_FOR_FACTORY1 32 + +//============================================================================== +// The spare space available in the original firmware #2 +// (Enabled if the EEProm address is insufficient) +// Address : 64 ~ 100 +//============================================================================== +#define RESERVE_FOR_FACTORY2 64 + +//============================================================================== +// KD8CEC EEPROM MAP +// Address : 101 ~ 1023 +// 256 is the base address +// 256 ~ 1023 (EEProm Section #1) +// 255 ~ 101 (EEProm Section #2) +//============================================================================== +#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 RESERVE3 770 //Reserve3 between Channel and Firmware id check + +//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 + diff --git a/ubitx_20/ubitx_lcd_1602i.ino b/ubitx_20/ubitx_lcd_1602i.ino index c8546f0..21def44 100644 --- a/ubitx_20/ubitx_lcd_1602i.ino +++ b/ubitx_20/ubitx_lcd_1602i.ino @@ -27,9 +27,27 @@ //======================================================================== //Begin of LCD Hardware define +//The I2C LCD I ordered did not arrive yet. +//I referenced the I2C LCD control in the thread at the link below. +////https://groups.io/g/BITX20/topic/variation_on_ian_s_kd8cec/16657839?p=,,,20,0,0,0::recentpostdate%2Fsticky,,,20,2,0,16657839 +//In particular, I referenced the sample code of John (VK2ETA) and K9HZ. Jack, W8TEE, Nick //======================================================================== -#include -LiquidCrystal lcd(8,9,10,11,12,13); +//#include +//LiquidCrystal lcd(8,9,10,11,12,13); + +//K9HZ's Code +//#include +//LiquidCrystal_I2C lcd(0x3F, 2, 1, 0, 4, 5, 6, 7, 3, POSITIVE); // + +//Jack(W8TEE) Code +//LiquidCrystal_I2C lcd(0x3F); // I2C address + +//John(VK2ETA) Code +#include +#define I2C_DISPLAY_ADDRESS 0x27 +LiquidCrystal_I2C lcd(I2C_DISPLAY_ADDRESS,16,2); // set the LCD as a 16 chars and 2 line display + +//LiquidCrystal_I2C lcd(0x27,16,2) //======================================================================== @@ -337,6 +355,16 @@ void updateLine2Buffer(char displayType) //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] = '.'; @@ -537,16 +565,27 @@ void idle_process() } } -void Display_AutoKeyTextIndex(char textIndex) +void Display_AutoKeyTextIndex(byte textIndex) { byte diplayAutoCWLine = 0; if ((displayOption1 & 0x01) == 0x01) diplayAutoCWLine = 1; lcd.setCursor(0, diplayAutoCWLine); - lcd.write(byteToChar(selectedCWTextIndex)); + 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_lcd_1602iian.ino b/ubitx_20/ubitx_lcd_1602iian.ino new file mode 100644 index 0000000..9d11bb4 --- /dev/null +++ b/ubitx_20/ubitx_lcd_1602iian.ino @@ -0,0 +1,753 @@ +/************************************************************************* + KD8CEC's uBITX Display Routine for LCD1602 Parrel + 1.This is the display code for the default LCD mounted in uBITX. + 2.Display related functions of uBITX. Some functions moved from uBITX_Ui. + 3.uBITX Idle time Processing + Functions that run at times that do not affect TX, CW, and CAT + It is called in 1/10 time unit. +----------------------------------------------------------------------------- + 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 . + +**************************************************************************/ +#ifdef UBITX_DISPLAY_LCD1602I_CUST +#include + +//======================================================================== +//Begin of TinyLCD Library by KD8CEC +//======================================================================== +/************************************************************************* + 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. + Ian KD8CEC +**************************************************************************/ +#define I2C_DISPLAY_ADDRESS 0x27 + +#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)) + +//Define connected PIN +//#define LCD_PIN_RS 8 +//#define LCD_PIN_EN 9 +//uint8_t LCD_PIN_DAT[4] = {10, 11, 12, 13}; + +uint8_t _Addr; +uint8_t _displayfunction; +uint8_t _displaycontrol; +uint8_t _displaymode; +uint8_t _numlines; +uint8_t _cols; +uint8_t _rows; +uint8_t _backlightval; + +// commands +#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 + +#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); +} + +void LCD1602_Init() +{ + //I2C Init + _Addr; + _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); +} + +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]); +} +//======================================================================== +//End of TinyLCD Library by KD8CEC +//======================================================================== + +/* +#include +LiquidCrystal lcd(8,9,10,11,12,13); +*/ + + +//======================================================================== +//Begin of Display Base Routines (Init, printLine..) +//======================================================================== +char c[30], b[30]; +char printBuff[2][17]; //mirrors what is showing on the two lines of the display + +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 +}; + +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); +} + +void LCD_Init(void) +{ + LCD1602_Init(); + initMeter(); //for Meter Display +} + +//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; +} + +// 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 (isUSB) + strcpy(c, "USB "); + else + strcpy(c, "LSB "); + } + else if (cwMode == 1) + { + strcpy(c, "CWL "); + } + else + { + strcpy(c, "CWU "); + } + } + 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[16]; +//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 (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] = 'k'; + else + { + //example #2 + if (freqScrollPosition++ > 18) //none scroll display time + { + line2Buffer[6] = 'k'; + 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] = ' '; + + //Check CW Key cwKeyType = 0; //0: straight, 1 : iambica, 2: iambicb + 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); //call original source code + int lineNumber = 0; + if ((displayOption1 & 0x01) == 0x01) + lineNumber = 1; + + LCD_SetCursor(drawPosition, lineNumber); + + for (int i = 0; i < 6; i++) //meter 5 + +db 1 = 6 + LCD_Write(lcdMeter[i]); + } +} + +byte testValue = 0; +char checkCount = 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)) { + if (checkCount++ > 1) + { + updateLine2Buffer(0); //call by scheduler + printLine2(line2Buffer); + line2DisplayStatus = 2; + checkCount = 0; + } + + //EX for Meters + /* + DisplayMeter(0, testValue++, 7); + if (testValue > 30) + testValue = 0; + */ + } + } +} + +//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_lcd_1602p.ino b/ubitx_20/ubitx_lcd_1602p.ino index 607d070..b20cba5 100644 --- a/ubitx_20/ubitx_lcd_1602p.ino +++ b/ubitx_20/ubitx_lcd_1602p.ino @@ -148,7 +148,7 @@ void LCD1602_Init() void LCD_Print(const char *c) { - for (int i = 0; i < strlen(c); i++) + for (uint8_t i = 0; i < strlen(c); i++) { if (*(c + i) == 0x00) return; LCD_Write(*(c + i)); @@ -469,8 +469,10 @@ char line2Buffer[16]; //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; @@ -480,6 +482,16 @@ void updateLine2Buffer(char displayType) //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] = '.'; @@ -679,16 +691,26 @@ void idle_process() } //AutoKey LCD Display Routine -void Display_AutoKeyTextIndex(char textIndex) +void Display_AutoKeyTextIndex(byte textIndex) { byte diplayAutoCWLine = 0; if ((displayOption1 & 0x01) == 0x01) diplayAutoCWLine = 1; LCD_SetCursor(0, diplayAutoCWLine); - LCD_Write(byteToChar(selectedCWTextIndex)); + 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_lcd_2404i.ino b/ubitx_20/ubitx_lcd_2004i.ino similarity index 100% rename from ubitx_20/ubitx_lcd_2404i.ino rename to ubitx_20/ubitx_lcd_2004i.ino diff --git a/ubitx_20/ubitx_lcd_2004p.ino b/ubitx_20/ubitx_lcd_2004p.ino new file mode 100644 index 0000000..7cec500 --- /dev/null +++ b/ubitx_20/ubitx_lcd_2004p.ino @@ -0,0 +1,702 @@ +/************************************************************************* + KD8CEC's uBITX Display Routine for LCD2004 Parrel + 1.This is the display code for the default LCD mounted in uBITX. + 2.Display related functions of uBITX. Some functions moved from uBITX_Ui. + 3.uBITX Idle time Processing + Functions that run at times that do not affect TX, CW, and CAT + It is called in 1/10 time unit. +----------------------------------------------------------------------------- + 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 . + +**************************************************************************/ +#ifdef UBITX_DISPLAY_LCD2004P + + +//======================================================================== +//Begin of TinyLCD Library by KD8CEC +//======================================================================== +/************************************************************************* + LCD2004TINY Library for 20 x 4 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 connected PIN +#define LCD_PIN_RS 8 +#define LCD_PIN_EN 9 +uint8_t LCD_PIN_DAT[4] = {10, 11, 12, 13}; + +// commands +#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 + +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 LCD2004_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); +} + +void LCD_Print(const char *c) +{ + for (uint8_t i = 0; i < strlen(c); i++) + { + if (*(c + i) == 0x00) return; + LCD_Write(*(c + i)); + } +} + +const int row_offsets[] = { 0x00, 0x40, 0x14, 0x54 }; +void LCD_SetCursor(uint8_t col, uint8_t row) +{ + LCD_Command(LCD_SETDDRAMADDR | (col + row_offsets[row])); //0 : 0x00, 1 : 0x40, only for 20 x 4 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]); +} +//======================================================================== +//End of TinyLCD Library by KD8CEC +//======================================================================== + +/* +#include +LiquidCrystal lcd(8,9,10,11,12,13); +*/ + + +//======================================================================== +//Begin of Display Base Routines (Init, printLine..) +//======================================================================== +char c[30], b[30]; +char printBuff[4][20]; //mirrors what is showing on the two lines of the display + +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 +}; + +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); +} + +void LCD_Init(void) +{ + LCD2004_Init(); + initMeter(); //for Meter Display +} + +//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; +} + +// 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 < 20; i++) { // add white spaces until the end of the 20 characters line is reached + LCD_Write(' '); + } + } +} + +void printLineF(char linenmbr, const __FlashStringHelper *c) +{ + int i; + char tmpBuff[20]; + 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 20 +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 < 20; i++) //Right Padding by Space + LCD_Write(' '); +} + +// short cut to print to the first line +void printLine1(const char *c) +{ + printLine(2,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(2,""); +} +// 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 (isUSB) + strcpy(c, "USB "); + else + strcpy(c, "LSB "); + } + else if (cwMode == 1) + { + strcpy(c, "CWL "); + } + else + { + strcpy(c, "CWU "); + } + } + 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 (20 x 4 LCD) + //if (inTx) + // strcat(c, " TX"); + + c[16] = ' '; + c[17] = 'H'; + c[18] = 'z'; + + printLine(2, c); + + byte diplayVFOLine = 2; + 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[20]; +//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) +{ + //Second Frequency Display + unsigned long tmpFreq = 0; + + 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 + else + { + + //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] = ' '; + } + + + line2Buffer[6] = 'k'; + line2Buffer[7] = ' '; + line2Buffer[8] = 'S'; + //strcat(line2Buffer, "k SDR:"); + + if (sdrModeOn == 0) + { + line2Buffer[9] = 'P'; + line2Buffer[10] = 'K'; + } + else + { + line2Buffer[9] = 'D'; + line2Buffer[10] = 'R'; + } + + line2Buffer[11] = ' '; + + //IFShift Offset Value + line2Buffer[12] = 'I'; + line2Buffer[13] = 'F'; + + line2Buffer[14] = ifShiftValue >= 0 ? '+' : 0; + line2Buffer[15] = 0; + line2Buffer[16] = ' '; + + //15, 16, 17, 18, 19 + memset(b, 0, sizeof(b)); + ltoa(ifShiftValue, b, DEC); + strncat(line2Buffer, b, 5); + } //end of else + + printLine(1, line2Buffer); + + memset(line2Buffer, ' ', 20); + { + //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] = ' '; + + //Check CW Key cwKeyType = 0; //0: straight, 1 : iambica, 2: iambicb + 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); //call original source code + int lineNumber = 0; + if ((displayOption1 & 0x01) == 0x01) + lineNumber = 1; + + LCD_SetCursor(drawPosition, lineNumber); + + for (int i = 0; i < 6; i++) //meter 5 + +db 1 = 6 + LCD_Write(lcdMeter[i]); + } +} + +byte testValue = 0; +char checkCount = 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)) { + if (checkCount++ > 1) + { + updateLine2Buffer(0); //call by scheduler + printLine2(line2Buffer); + line2DisplayStatus = 2; + checkCount = 0; + } + + //EX for Meters + /* + DisplayMeter(0, testValue++, 7); + if (testValue > 30) + testValue = 0; + */ + } + } +} + +//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(3, 12, 0, userCallsignLength -1, 0); //eeprom to lcd use offset (USER_CALLSIGN_DAT) + //delay(500); +} + +void DisplayVersionInfo(const __FlashStringHelper * fwVersionInfo) +{ + printLineF(3, fwVersionInfo); +} + + +#endif diff --git a/ubitx_20/ubitx_lcd_2404p.ino b/ubitx_20/ubitx_lcd_2404p.ino deleted file mode 100644 index 66c9868..0000000 --- a/ubitx_20/ubitx_lcd_2404p.ino +++ /dev/null @@ -1,22 +0,0 @@ -/************************************************************************* - KD8CEC's uBITX Display Routine for LCD2404 Parrel - uBITX Idle time Processing - Functions that run at times that do not affect TX, CW, and CAT - It is called in 1/10 time unit. ------------------------------------------------------------------------------ - 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/ubitx_20/ubitx_menu.ino b/ubitx_20/ubitx_menu.ino index 094e594..bddaea6 100644 --- a/ubitx_20/ubitx_menu.ino +++ b/ubitx_20/ubitx_menu.ino @@ -2,11 +2,11 @@ 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) @@ -471,6 +471,7 @@ void menuSDROnOff(int btn) EEPROM.put(ENABLE_SDR, sdrModeOn); setFrequency(frequency); + SetCarrierFreq(); menuClearExit(500); } } @@ -589,7 +590,6 @@ int getValueByKnob(int valueType, int targetValue, int minKnobValue, int maxKnob { int knob; int moveDetectStep = 0; - int negativeSensitivity; char isInitDisplay = 1; delay_background(300, 0); //Default Delay @@ -679,7 +679,6 @@ int getValueByKnob(int valueType, int targetValue, int minKnobValue, int maxKnob } void menuCWSpeed(int btn){ - int knob = 0; int wpm; wpm = 1200/cwSpeed; @@ -732,15 +731,14 @@ void menuCWSpeed(int btn){ //Modified by KD8CEC void menuSetupCwTone(int btn){ - int knob = 0; - int prev_sideTone; + //int prev_sideTone; if (!btn){ printLineF2(F("Change CW Tone")); return; } - prev_sideTone = sideTone; + //prev_sideTone = sideTone; //printLineF1(F("Tune CW tone")); //printLineF2(F("PTT to confirm.")); //printLineF1(F("Press to set WPM")); @@ -789,7 +787,7 @@ void menuSetupCwTone(int btn){ //Modified by KD8CEC void menuSetupCwDelay(int btn){ - int knob = 0; + //int knob = 0; int tmpCWDelay = cwDelayTime * 10; if (!btn){ @@ -839,8 +837,8 @@ void menuSetupCwDelay(int btn){ //CW Time delay by KD8CEC void menuSetupTXCWInterval(int btn){ - char needDisplayInformation = 1; - int knob = 0; + //char needDisplayInformation = 1; + //int knob = 0; int tmpTXCWInterval = delayBeforeCWStartTime * 2; if (!btn){ @@ -892,8 +890,8 @@ void menuSetupTXCWInterval(int btn){ //IF Shift function, BFO Change like RIT, by KD8CEC void menuIFSSetup(int btn){ - int knob = 0; - char needApplyChangeValue = 1; + //int knob = 0; + //char needApplyChangeValue = 1; if (!btn){ if (isIFShift == 1) @@ -953,8 +951,8 @@ void menuIFSSetup(int btn){ //ATT SETUP (IF1(45MHZ) SHIFT), by KD8CEC void menuATTSetup(int btn){ - int knob = 0; - char needApplyChangeValue = 1; + //int knob = 0; + //char needApplyChangeValue = 1; if (!btn){ if (attLevel != 0) @@ -979,10 +977,10 @@ void menuATTSetup(int btn){ //Functions for CWL and CWU by KD8CEC void menuSelectMode(int btn){ - int knob = 0; + //int knob = 0; int selectModeType = 0; int beforeMode = 0; - int moveStep = 0; + //int moveStep = 0; if (!btn){ printLineF2(F("Select Mode?")); @@ -1064,9 +1062,9 @@ void menuSelectMode(int btn){ //Select CW Key Type by KD8CEC void menuSetupKeyType(int btn){ - int knob = 0; + //int knob = 0; int selectedKeyType = 0; - int moveStep = 0; + //int moveStep = 0; if (!btn){ printLineF2(F("Change Key Type?")); } diff --git a/ubitx_20/ubitx_wspr.ino b/ubitx_20/ubitx_wspr.ino index 4352413..8ac28b5 100644 --- a/ubitx_20/ubitx_wspr.ino +++ b/ubitx_20/ubitx_wspr.ino @@ -23,14 +23,13 @@ Beta Tester : along with this program. If not, see . **********************************************************************************/ -#include #include #include "ubitx.h" //begin of test byte WsprToneCode[164]; -long lastTime=0; +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 @@ -48,7 +47,7 @@ void SendWSPRManage() { int knob = 0; byte knobPosition = 0; - char isNeedDisplayInfo = 0; + //char isNeedDisplayInfo = 0; char nowSelectedIndex = 0; char nowWsprStep = 0; //0 : select Message, 1 : select band, 2 : send char selectedWsprMessageIndex = -1; @@ -56,8 +55,8 @@ void SendWSPRManage() unsigned long WsprTXFreq = 0; unsigned int WsprMultiChan = 0; - unsigned long prevFreq; - char loopIndex; + //unsigned long prevFreq; + byte loopIndex; delay_background(500, 0); From 0e245fc488ceb3aa27059971a3d27632ecc1934a Mon Sep 17 00:00:00 2001 From: phdlee Date: Mon, 16 Apr 2018 23:56:32 +0900 Subject: [PATCH 106/173] Add 20x04LCD and S.Meter --- ubitx_20/ubitx.h | 5 +- ubitx_20/ubitx_20.ino | 21 +- ubitx_20/ubitx_eemap.h | 3 + ubitx_20/ubitx_lcd_1602i.ino | 4 +- ubitx_20/ubitx_lcd_1602iian.ino | 4 +- ubitx_20/ubitx_lcd_1602p.ino | 133 ++++------- ubitx_20/ubitx_lcd_2004p.ino | 391 ++++++++++++++++---------------- ubitx_20/ubitx_menu.ino | 2 - ubitx_20/ubitx_ui.ino | 147 ++++++++++++ 9 files changed, 417 insertions(+), 293 deletions(-) diff --git a/ubitx_20/ubitx.h b/ubitx_20/ubitx.h index 41d3555..51bdf19 100644 --- a/ubitx_20/ubitx.h +++ b/ubitx_20/ubitx.h @@ -24,10 +24,10 @@ //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 +#define UBITX_DISPLAY_LCD1602P //LCD mounted on unmodified uBITX //#define UBITX_DISPLAY_LCD1602I //I2C type 16 x 02 LCD //#define UBITX_DISPLAY_LCD1602I_CUST //I2C type 16 x 02 Custom Tiny Library -#define UBITX_DISPLAY_LCD2004P //24 x 04 LCD +//#define UBITX_DISPLAY_LCD2004P //24 x 04 LCD //#define UBITX_DISPLAY_LCD2004I //I2C type 24 x 04 LCD //============================================================================== @@ -95,6 +95,7 @@ extern unsigned long frequency; extern byte WsprMSGCount; +extern byte sMeterLevels[9]; extern void printLine1(const char *c); extern void printLine2(const char *c); diff --git a/ubitx_20/ubitx_20.ino b/ubitx_20/ubitx_20.ino index 5b7b13a..b80245e 100644 --- a/ubitx_20/ubitx_20.ino +++ b/ubitx_20/ubitx_20.ino @@ -1,11 +1,19 @@ //Firmware Version -#define FIRMWARE_VERSION_INFO F("CEC v1.071") +//+ : 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.072") #define FIRMWARE_VERSION_NUM 0x02 //1st Complete Project : 1 (Version 1.061), 2st Project : 2 /** 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. - However, the license rules are subject to the original source rules. + 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 ------------------------------------------------------------- @@ -172,6 +180,7 @@ 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]; byte isIFShift = 0; //1 = ifShift, 2 extend int ifShiftValue = 0; // @@ -183,8 +192,8 @@ int ifShiftValue = 0; // //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)) +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 @@ -819,6 +828,10 @@ void initSettings(){ 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); + } + //User callsign information if (EEPROM.read(USER_CALLSIGN_KEY) == 0x59) userCallsignLength = EEPROM.read(USER_CALLSIGN_LEN); //MAXIMUM 18 LENGTH diff --git a/ubitx_20/ubitx_eemap.h b/ubitx_20/ubitx_eemap.h index dc10487..56855c8 100644 --- a/ubitx_20/ubitx_eemap.h +++ b/ubitx_20/ubitx_eemap.h @@ -50,6 +50,9 @@ // 256 ~ 1023 (EEProm Section #1) // 255 ~ 101 (EEProm Section #2) //============================================================================== +#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 diff --git a/ubitx_20/ubitx_lcd_1602i.ino b/ubitx_20/ubitx_lcd_1602i.ino index 21def44..2fbfd87 100644 --- a/ubitx_20/ubitx_lcd_1602i.ino +++ b/ubitx_20/ubitx_lcd_1602i.ino @@ -409,13 +409,13 @@ void updateLine2Buffer(char displayType) //EXAMPLE #1 if ((displayOption1 & 0x04) == 0x00) //none scroll display - line2Buffer[6] = 'k'; + line2Buffer[6] = 'M'; else { //example #2 if (freqScrollPosition++ > 18) //none scroll display time { - line2Buffer[6] = 'k'; + line2Buffer[6] = 'M'; if (freqScrollPosition > 25) freqScrollPosition = -1; } diff --git a/ubitx_20/ubitx_lcd_1602iian.ino b/ubitx_20/ubitx_lcd_1602iian.ino index 9d11bb4..0512339 100644 --- a/ubitx_20/ubitx_lcd_1602iian.ino +++ b/ubitx_20/ubitx_lcd_1602iian.ino @@ -570,13 +570,13 @@ void updateLine2Buffer(char displayType) //EXAMPLE #1 if ((displayOption1 & 0x04) == 0x00) //none scroll display - line2Buffer[6] = 'k'; + line2Buffer[6] = 'M'; else { //example #2 if (freqScrollPosition++ > 18) //none scroll display time { - line2Buffer[6] = 'k'; + line2Buffer[6] = 'M'; if (freqScrollPosition > 25) freqScrollPosition = -1; } diff --git a/ubitx_20/ubitx_lcd_1602p.ino b/ubitx_20/ubitx_lcd_1602p.ino index b20cba5..8b2f82b 100644 --- a/ubitx_20/ubitx_lcd_1602p.ino +++ b/ubitx_20/ubitx_lcd_1602p.ino @@ -175,7 +175,8 @@ void LCD_CreateChar(uint8_t location, uint8_t charmap[]) #include LiquidCrystal lcd(8,9,10,11,12,13); */ - +//SWR GRAPH, DrawMeter and drawingMeter Logic function by VK2ETA +#define OPTION_SKINNYBARS //======================================================================== //Begin of Display Base Routines (Init, printLine..) @@ -183,93 +184,12 @@ LiquidCrystal lcd(8,9,10,11,12,13); char c[30], b[30]; char printBuff[2][17]; //mirrors what is showing on the two lines of the display -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 -}; - -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); -} - void LCD_Init(void) { LCD1602_Init(); initMeter(); //for Meter Display } -//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; -} - // The generic routine to display one line on the LCD void printLine(unsigned char linenmbr, const char *c) { if ((displayOption1 & 0x01) == 0x01) @@ -534,13 +454,13 @@ void updateLine2Buffer(char displayType) //EXAMPLE #1 if ((displayOption1 & 0x04) == 0x00) //none scroll display - line2Buffer[6] = 'k'; + line2Buffer[6] = 'M'; else { //example #2 if (freqScrollPosition++ > 18) //none scroll display time { - line2Buffer[6] = 'k'; + line2Buffer[6] = 'M'; if (freqScrollPosition > 25) freqScrollPosition = -1; } @@ -625,7 +545,13 @@ void updateLine2Buffer(char displayType) line2Buffer[13] = ' '; //Check CW Key cwKeyType = 0; //0: straight, 1 : iambica, 2: iambicb - if (cwKeyType == 0) + if (sdrModeOn == 1) + { + line2Buffer[13] = 'S'; + line2Buffer[14] = 'D'; + line2Buffer[15] = 'R'; + } + else if (cwKeyType == 0) { line2Buffer[14] = 'S'; line2Buffer[15] = 'T'; @@ -648,20 +574,26 @@ void DisplayMeter(byte meterType, byte meterValue, char drawPosition) { if (meterType == 0 || meterType == 1 || meterType == 2) { - drawMeter(meterValue); //call original source code + drawMeter(meterValue); int lineNumber = 0; if ((displayOption1 & 0x01) == 0x01) lineNumber = 1; LCD_SetCursor(drawPosition, lineNumber); - for (int i = 0; i < 6; i++) //meter 5 + +db 1 = 6 - LCD_Write(lcdMeter[i]); + //for (int i = 0; i <26; i++) //meter 5 + +db 1 = 6 + LCD_Write(lcdMeter[0]); + LCD_Write(lcdMeter[1]); } } byte testValue = 0; char checkCount = 0; + +int currentSMeter = 0; +//byte sMeterLevels[] = {0, 1, 4, 10, 18, 35, 63, 91, 117}; //John's default Value (divide / 4) +byte scaledSMeter = 0; + void idle_process() { //space for user graphic display @@ -679,6 +611,7 @@ void idle_process() line2DisplayStatus = 2; checkCount = 0; } + } //EX for Meters /* @@ -686,7 +619,29 @@ void idle_process() if (testValue > 30) testValue = 0; */ - } + + //S-Meter Display + if ((displayOption1 & 0x08) == 0x08 && (sdrModeOn == 0)) + { + int newSMeter; + + //VK2ETA S-Meter from MAX9814 TC pin / divide 4 by KD8CEC for reduce EEPromSize + newSMeter = analogRead(ANALOG_SMETER); + + //Faster attack, Slower release + currentSMeter = (newSMeter > currentSMeter ? ((currentSMeter * 3 + newSMeter * 7) + 5) / 10 : ((currentSMeter * 7 + newSMeter * 3) + 5) / 10) / 4; + + scaledSMeter = 0; + for (byte s = 8; s >= 1; s--) { + if (currentSMeter > sMeterLevels[s]) { + scaledSMeter = s; + break; + } + } + + DisplayMeter(0, scaledSMeter, 14); + } //end of S-Meter + } } diff --git a/ubitx_20/ubitx_lcd_2004p.ino b/ubitx_20/ubitx_lcd_2004p.ino index 7cec500..fb29ec0 100644 --- a/ubitx_20/ubitx_lcd_2004p.ino +++ b/ubitx_20/ubitx_lcd_2004p.ino @@ -22,7 +22,6 @@ **************************************************************************/ #ifdef UBITX_DISPLAY_LCD2004P - //======================================================================== //Begin of TinyLCD Library by KD8CEC //======================================================================== @@ -176,7 +175,8 @@ void LCD_CreateChar(uint8_t location, uint8_t charmap[]) #include LiquidCrystal lcd(8,9,10,11,12,13); */ - +//SWR GRAPH, DrawMeter and drawingMeter Logic function by VK2ETA +//#define OPTION_SKINNYBARS //======================================================================== //Begin of Display Base Routines (Init, printLine..) @@ -184,92 +184,12 @@ LiquidCrystal lcd(8,9,10,11,12,13); char c[30], b[30]; char printBuff[4][20]; //mirrors what is showing on the two lines of the display -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 -}; - -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); -} - void LCD_Init(void) { LCD2004_Init(); initMeter(); //for Meter Display } -//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; -} // The generic routine to display one line on the LCD void printLine(unsigned char linenmbr, const char *c) { @@ -289,10 +209,10 @@ void printLine(unsigned char linenmbr, const char *c) { void printLineF(char linenmbr, const __FlashStringHelper *c) { int i; - char tmpBuff[20]; + char tmpBuff[21]; PGM_P p = reinterpret_cast(c); - for (i = 0; i < 17; i++){ + for (i = 0; i < 21; i++){ unsigned char fChar = pgm_read_byte(p++); tmpBuff[i] = fChar; if (fChar == 0) @@ -324,7 +244,7 @@ void printLineFromEEPRom(char linenmbr, char lcdColumn, byte eepromStartIndex, b // short cut to print to the first line void printLine1(const char *c) { - printLine(2,c); + printLine(1,c); } // short cut to print to the first line void printLine2(const char *c) @@ -340,7 +260,7 @@ void clearLine2() // short cut to print to the first line void printLine1Clear(){ - printLine(2,""); + printLine(1,""); } // short cut to print to the first line void printLine2Clear(){ @@ -368,6 +288,7 @@ 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 + // i also Very TNX Purdum for good source code int i; unsigned long tmpFreq = frequency; // @@ -409,6 +330,7 @@ void updateDisplay() { strcpy(c, "CWU "); } } + if (vfoActive == VFO_A) // VFO A is active strcat(c, "A:"); else @@ -437,18 +359,18 @@ void updateDisplay() { c[i] = ' '; } + if (sdrModeOn) + strcat(c, " SDR"); + else + strcat(c, " SPK"); + //remarked by KD8CEC //already RX/TX status display, and over index (20 x 4 LCD) //if (inTx) // strcat(c, " TX"); + printLine(1, c); - c[16] = ' '; - c[17] = 'H'; - c[18] = 'z'; - - printLine(2, c); - - byte diplayVFOLine = 2; + byte diplayVFOLine = 1; if ((displayOption1 & 0x01) == 0x01) diplayVFOLine = 0; @@ -481,9 +403,7 @@ int freqScrollPosition = 0; //warning : unused parameter 'displayType' <-- ignore, this is reserve void updateLine2Buffer(char displayType) { - //Second Frequency Display unsigned long tmpFreq = 0; - if (ritOn) { strcpy(line2Buffer, "RitTX:"); @@ -512,77 +432,61 @@ void updateLine2Buffer(char displayType) line2Buffer[i] = ' '; } - //return; + return; } //end of ritOn display - else + + //other VFO display + if (vfoActive == VFO_B) { - - //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; - } + 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] = ' '; - } - - - line2Buffer[6] = 'k'; - line2Buffer[7] = ' '; - line2Buffer[8] = 'S'; - //strcat(line2Buffer, "k SDR:"); - - if (sdrModeOn == 0) - { - line2Buffer[9] = 'P'; - line2Buffer[10] = 'K'; } else - { - line2Buffer[9] = 'D'; - line2Buffer[10] = 'R'; - } - - line2Buffer[11] = ' '; - + line2Buffer[i] = ' '; + } + + memset(&line2Buffer[10], ' ', 10); + + if (isIFShift) + { + line2Buffer[6] = 'M'; + line2Buffer[7] = ' '; //IFShift Offset Value - line2Buffer[12] = 'I'; - line2Buffer[13] = 'F'; + line2Buffer[8] = 'I'; + line2Buffer[9] = 'F'; - line2Buffer[14] = ifShiftValue >= 0 ? '+' : 0; - line2Buffer[15] = 0; - line2Buffer[16] = ' '; - - //15, 16, 17, 18, 19 + 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); - } //end of else - printLine(1, line2Buffer); - - memset(line2Buffer, ' ', 20); + for (int i = 12; i < 17; i++) + { + if (line2Buffer[i] == 0) + line2Buffer[i] = ' '; + } + } // end of display IF + else // step & Key Type display { - //if (isDirectCall != 0) - // return; - - memset(&line2Buffer[8], ' ', 8); //Step long tmpStep = arTuneStep[tuneStepIndex -1]; @@ -592,7 +496,7 @@ void updateLine2Buffer(char displayType) isStepKhz = 2; } - for (int i = 10; i >= 8 - isStepKhz; i--) { + for (int i = 14; i >= 12 - isStepKhz; i--) { if (tmpStep > 0) { line2Buffer[i + isStepKhz] = tmpStep % 10 + 0x30; tmpStep /= 10; @@ -603,29 +507,30 @@ void updateLine2Buffer(char displayType) if (isStepKhz == 0) { - line2Buffer[11] = 'H'; - line2Buffer[12] = 'z'; + line2Buffer[15] = 'H'; + line2Buffer[16] = 'z'; } - - line2Buffer[13] = ' '; - - //Check CW Key cwKeyType = 0; //0: straight, 1 : iambica, 2: iambicb - 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'; - } } + + line2Buffer[17] = ' '; + + //Check CW Key cwKeyType = 0; //0: straight, 1 : iambica, 2: iambicb + if (cwKeyType == 0) + { + line2Buffer[18] = 'S'; + line2Buffer[19] = 'T'; + } + else if (cwKeyType == 1) + { + line2Buffer[18] = 'I'; + line2Buffer[19] = 'A'; + } + else + { + line2Buffer[18] = 'I'; + line2Buffer[19] = 'B'; + } + } //meterType : 0 = S.Meter, 1 : P.Meter @@ -633,20 +538,93 @@ void DisplayMeter(byte meterType, byte meterValue, char drawPosition) { if (meterType == 0 || meterType == 1 || meterType == 2) { - drawMeter(meterValue); //call original source code - int lineNumber = 0; - if ((displayOption1 & 0x01) == 0x01) - lineNumber = 1; + drawMeter(meterValue); + //int lineNumber = 0; + //if ((displayOption1 & 0x01) == 0x01) + //lineNumber = 1; - LCD_SetCursor(drawPosition, lineNumber); - + LCD_SetCursor(drawPosition, 2); + LCD_Write('S'); + LCD_Write(':'); for (int i = 0; i < 6; i++) //meter 5 + +db 1 = 6 LCD_Write(lcdMeter[i]); } } + +//meterType : 0 = S.Meter, 1 = Forward Power Meter, 2 = SWR Meter +void DisplayMeter(byte meterType, int meterValue, char drawPosition) +{ + +#ifdef OPTION_SKINNYBARS //We want skinny meter bars with more text/numbers + memcpy(&(line2Buffer[drawPosition]), " ", 8); //Blank that section of 8 characters first + if (meterType == 0) { //SWR meter + drawMeter(meterValue); //Only 2 characters + line2Buffer[drawPosition] = 'S'; + byte sValue = round((float)meterValue * 1.5); //6 bars available only to show 9 S values + sValue = sValue > 9 ? 9 : sValue; //Max S9 + line2Buffer[drawPosition + 1] = '0' + sValue; //0 to 9 + memcpy(&(line2Buffer[drawPosition + 2]), lcdMeter, 2); //Copy the S-Meter bars + //Add the +10, +20, etc... + if (meterValue > 6) { + //We are over S9 + line2Buffer[drawPosition + 4] = '+'; + line2Buffer[drawPosition + 5] = '0' + meterValue - 6; //1,2,3 etc... + line2Buffer[drawPosition + 6] = '0'; + } + } else if (meterType == 1) { //Forward Power + drawMeter(round((float)meterValue / 40)); //4 watts per bar + //meterValue contains power value x 10 (one decimal point) + line2Buffer[drawPosition] = 'P'; + meterValue = meterValue > 999 ? 999 : meterValue; //Limit to 99.9 watts!!!! + //Remove decimal value and divide by 10 + meterValue = round((float)meterValue / 10); + if (meterValue < 10) { + line2Buffer[drawPosition + 1] = ' '; + line2Buffer[drawPosition + 2] = '0' + meterValue; //0 to 9 + } else { + line2Buffer[drawPosition + 1] = '0' + meterValue / 10; + line2Buffer[drawPosition + 2] = '0' + (meterValue - ((meterValue / 10) * 10)); + } + line2Buffer[drawPosition + 3] = 'W'; + memcpy(&(line2Buffer[drawPosition + 4]), lcdMeter, 2); //Copy the S-Meter bars + } else { //SWR + drawMeter((int)(((float)meterValue - 21) / 100)); //no bar = < 1.2, then 1 bar = 1.2 to 2.2, 2 bars = 2.2 to 3.2, etc... + //meterValue contains SWR x 100 (two decimal point) + memcpy(&(line2Buffer[drawPosition]), "SWR", 3); + meterValue = round((float)meterValue / 10); //We now have swr x 10 (1 decimal point) + if (meterValue < 100) { //10 to 99, no decimal point + //Draw the decimal value + line2Buffer[drawPosition + 3] = '0' + meterValue / 10; + line2Buffer[drawPosition + 4] = '.'; + line2Buffer[drawPosition + 5] = '0' + (meterValue - ((meterValue / 10) * 10)); + } else { + memcpy(&(line2Buffer[drawPosition + 3]), "10+", 3); //over 10 + } + memcpy(&(line2Buffer[drawPosition + 6]), lcdMeter, 2); //Copy the S-Meter bars + } +#else //We want fat bars, easy to read, with less text/numbers + //Serial.print("In displaymeter, meterValue: "); Serial.println(meterValue); + drawMeter(meterValue); + //Always line 2 + char sym = 'S'; + if (meterType == 1) sym = 'P'; + else if (meterType == 2) sym = 'R'; //For SWR + line2Buffer[drawPosition] = sym; + memcpy(&(line2Buffer[drawPosition + 1]), lcdMeter, 7); +#endif //OPTION_SKINNYBARS + +} + + byte testValue = 0; char checkCount = 0; + +int currentSMeter = 0; +//int sMeterLevels[] = {0, 5, 17, 41, 74, 140, 255, 365, 470}; +byte scaledSMeter = 0; + +//execute interval : 0.25sec void idle_process() { //space for user graphic display @@ -664,14 +642,45 @@ void idle_process() line2DisplayStatus = 2; checkCount = 0; } - - //EX for Meters - /* - DisplayMeter(0, testValue++, 7); - if (testValue > 30) - testValue = 0; - */ } + + //EX for Meters + /* + DisplayMeter(0, testValue++, 0); + if (testValue > 30) + testValue = 0; + */ + + //Sample + //DisplayMeter(0, analogRead(ANALOG_SMETER) / 30, 0); + //DisplayMeter(0, analogRead(ANALOG_SMETER) / 10, 0); + //delay_background(10, 0); + //DisplayMeter(0, analogRead(ANALOG_SMETER), 0); + //if (testValue > 30) + // testValue = 0; + + //S-Meter Display + if ((displayOption1 & 0x08) == 0x08 && (sdrModeOn == 0)) + { + int newSMeter; + + //VK2ETA S-Meter from MAX9814 TC pin + newSMeter = analogRead(ANALOG_SMETER); + + //Faster attack, Slower release + currentSMeter = (newSMeter > currentSMeter ? ((currentSMeter * 3 + newSMeter * 7) + 5) / 10 : ((currentSMeter * 7 + newSMeter * 3) + 5) / 10); + + scaledSMeter = 0; + for (byte s = 8; s >= 1; s--) { + if (currentSMeter > sMeterLevels[s]) { + scaledSMeter = s; + break; + } + } + + DisplayMeter(0, scaledSMeter, 0); + } //end of S-Meter + } } @@ -689,8 +698,7 @@ void Display_AutoKeyTextIndex(byte textIndex) void DisplayCallsign(byte callSignLength) { - printLineFromEEPRom(3, 12, 0, userCallsignLength -1, 0); //eeprom to lcd use offset (USER_CALLSIGN_DAT) - //delay(500); + printLineFromEEPRom(3, 20 - userCallsignLength, 0, userCallsignLength -1, 0); //eeprom to lcd use offset (USER_CALLSIGN_DAT) } void DisplayVersionInfo(const __FlashStringHelper * fwVersionInfo) @@ -698,5 +706,4 @@ void DisplayVersionInfo(const __FlashStringHelper * fwVersionInfo) printLineF(3, fwVersionInfo); } - #endif diff --git a/ubitx_20/ubitx_menu.ino b/ubitx_20/ubitx_menu.ino index bddaea6..eac44b0 100644 --- a/ubitx_20/ubitx_menu.ino +++ b/ubitx_20/ubitx_menu.ino @@ -59,12 +59,10 @@ void menuBand(int btn){ btnPressCount = 0; if (tuneTXType > 0) { //Just toggle 0 <-> 2, if tuneTXType is 100, 100 -> 0 -> 2 tuneTXType = 0; - //printLineF2(F("General mode")); printLineF2(F("General")); } else { tuneTXType = 2; - //printLineF2(F("Ham band mode")); printLineF2(F("Ham band")); } delay_background(1000, 0); diff --git a/ubitx_20/ubitx_ui.ino b/ubitx_20/ubitx_ui.ino index 4907282..e2fad8d 100644 --- a/ubitx_20/ubitx_ui.ino +++ b/ubitx_20/ubitx_ui.ino @@ -6,6 +6,153 @@ * 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(6, 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 + 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 + } + } +#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] = 0x2B; //"+" + } else lcdMeter[6] = 0x20; +#endif //OPTION_FATBARS +} + + + char byteToChar(byte srcByte){ if (srcByte < 10) return 0x30 + srcByte; From 11b6fbc1f45c54aa6b9d9051829b5d920da0ac7f Mon Sep 17 00:00:00 2001 From: Richard Neese Date: Mon, 16 Apr 2018 21:56:20 -0400 Subject: [PATCH 107/173] Tuning step change changed tuning steps to 10/50/100/500/1000 --- ubitx_20/ubitx_20.ino | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/ubitx_20/ubitx_20.ino b/ubitx_20/ubitx_20.ino index 13a871d..0ed66be 100644 --- a/ubitx_20/ubitx_20.ino +++ b/ubitx_20/ubitx_20.ino @@ -1,5 +1,5 @@ //Firmware Version -#define FIRMWARE_VERSION_INFO F("CE v1.070") +#define FIRMWARE_VERSION_INFO F("CE v1.071") #define FIRMWARE_VERSION_NUM 0x02 //1st Complete Project : 1 (Version 1.061), 2st Project : 2 //Depending on the type of LCD mounted on the uBITX, uncomment one of the options below. @@ -1008,10 +1008,10 @@ void initSettings(){ { //Default Setting arTuneStep[0] = 10; - arTuneStep[1] = 20; - arTuneStep[2] = 50; - arTuneStep[3] = 100; - arTuneStep[4] = 200; + arTuneStep[1] = 50; + arTuneStep[2] = 100; + arTuneStep[3] = 500; + arTuneStep[4] = 1000; } if (tuneStepIndex == 0) //New User From f600c185412bca98e752c94efcd623b5a0a7f1b0 Mon Sep 17 00:00:00 2001 From: phdlee Date: Tue, 17 Apr 2018 21:26:29 +0900 Subject: [PATCH 108/173] add extended Keys (mode, band, tunestep) and i2clcd working --- ubitx_20/ubitx.h | 15 ++++- ubitx_20/ubitx_20.ino | 113 +++++++++++++++++++++++++++++++- ubitx_20/ubitx_eemap.h | 2 +- ubitx_20/ubitx_lcd_1602i.ino | 91 ++----------------------- ubitx_20/ubitx_lcd_1602iian.ino | 98 +++++---------------------- ubitx_20/ubitx_ui.ino | 25 +++++++ 6 files changed, 173 insertions(+), 171 deletions(-) diff --git a/ubitx_20/ubitx.h b/ubitx_20/ubitx.h index 51bdf19..606799c 100644 --- a/ubitx_20/ubitx.h +++ b/ubitx_20/ubitx.h @@ -24,12 +24,15 @@ //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 +//#define UBITX_DISPLAY_LCD1602P //LCD mounted on unmodified uBITX //#define UBITX_DISPLAY_LCD1602I //I2C type 16 x 02 LCD -//#define UBITX_DISPLAY_LCD1602I_CUST //I2C type 16 x 02 Custom Tiny Library +#define UBITX_DISPLAY_LCD1602I_CUST //I2C type 16 x 02 Custom Tiny Library //#define UBITX_DISPLAY_LCD2004P //24 x 04 LCD //#define UBITX_DISPLAY_LCD2004I //I2C type 24 x 04 LCD +//#define EXTEND_KEY_GROUP1 //MODE, BAND(-), BAND(+), STEP +//#define EXTEND_KEY_GROUP2 //Numeric (0~9), Point(.), Enter //Not supported in Version 1.0x + //============================================================================== // Hardware, Define PIN Usage //============================================================================== @@ -93,9 +96,17 @@ #define printLineF1(x) (printLineF(1, x)) #define printLineF2(x) (printLineF(0, x)) +#define FUNCTION_KEY_ADC 80 //MODE, BAND(-), BAND(+), STEP +#define FKEY_PRESS 120 +#define FKEY_MODE 0 +#define FKEY_BANDUP 1 +#define FKEY_BANDDOWN 2 +#define FKEY_STEP 3 + extern unsigned long frequency; extern byte WsprMSGCount; extern byte sMeterLevels[9]; +extern int KeyValues[16][2]; //ADC value Ranges for Extend Key extern void printLine1(const char *c); extern void printLine2(const char *c); diff --git a/ubitx_20/ubitx_20.ino b/ubitx_20/ubitx_20.ino index b80245e..34b99fb 100644 --- a/ubitx_20/ubitx_20.ino +++ b/ubitx_20/ubitx_20.ino @@ -182,6 +182,31 @@ byte line2DisplayStatus = 0; //0:Clear, 1 : menu, 1: DisplayFrom Idle, char lcdMeter[17]; byte sMeterLevels[9]; +int KeyValues[16][2]; +/*= { + {1023, 1025}, //1 + {707, 711}, //5 + {570, 574}, //9 + {493, 500}, //13 + + {932, 936}, //2 + {860, 864}, //3 + {800, 805}, //4 + + {672, 676}, //6 + {642, 646}, //7 + {616, 620}, //8 + + {552, 556}, //10 + {535, 539}, //11 + {520, 524}, //12 + + {438, 442}, //14 + {403, 407}, //15 + {378, 382} //16 +}; +*/ + byte isIFShift = 0; //1 = ifShift, 2 extend int ifShiftValue = 0; // @@ -603,7 +628,87 @@ void checkPTT(){ if (digitalRead(PTT) == 1 && inTx == 1) stopTx(); } +#ifdef EXTEND_KEY_GROUP1 +void checkButton(){ + //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 + doMenu(); + else if (keyStatus <= FKEY_STEP) //EXTEND KEY GROUP #1 + { + if (keyStatus == FKEY_MODE) //Press Mode Key + { + if (cwMode == 1) + { + cwMode = 2; + } + else if (cwMode == 2) + { + cwMode = 0; + isUSB = 0; + } + else if (isUSB == 0) + { + isUSB = 1; + } + else + { + cwMode = 1; + } + } + //else if (keyStatus == FKEY_BANDDOWN) //Press Mode Key + //{ + // setNextHamBandFreq(frequency, -1); //Prior Band + //} + else if (keyStatus == FKEY_BANDUP || keyStatus == FKEY_BANDDOWN) //Press Mode Key + { + char currentBandIndex = -1; + + //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 + } + else if (keyStatus == FKEY_STEP) //FKEY_BANDUP + { + if (++tuneStepIndex > 5) + tuneStepIndex = 1; + + EEPROM.put(TUNING_STEP, tuneStepIndex); + printLine2ClearAndUpdate(); + } + + 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()) @@ -621,7 +726,7 @@ void checkButton(){ } //delay(50);//debounce } - +#endif /************************************ Replace function by KD8CEC @@ -832,6 +937,12 @@ void initSettings(){ 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 * 2)); + KeyValues[i][1] = EEPROM.read(EXTENDED_KEY_RANGE + (i * 2) + 1); + } + //User callsign information if (EEPROM.read(USER_CALLSIGN_KEY) == 0x59) userCallsignLength = EEPROM.read(USER_CALLSIGN_LEN); //MAXIMUM 18 LENGTH diff --git a/ubitx_20/ubitx_eemap.h b/ubitx_20/ubitx_eemap.h index 56855c8..d87a869 100644 --- a/ubitx_20/ubitx_eemap.h +++ b/ubitx_20/ubitx_eemap.h @@ -50,9 +50,9 @@ // 256 ~ 1023 (EEProm Section #1) // 255 ~ 101 (EEProm Section #2) //============================================================================== +#define EXTENDED_KEY_RANGE 196 //Extended Key, KEY RANGE (MODE, BAND+, BAND-, TUNE_STEP, NUM0~NUM9, POINT, ENTER #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 diff --git a/ubitx_20/ubitx_lcd_1602i.ino b/ubitx_20/ubitx_lcd_1602i.ino index 2fbfd87..ecf363e 100644 --- a/ubitx_20/ubitx_lcd_1602i.ino +++ b/ubitx_20/ubitx_lcd_1602i.ino @@ -44,7 +44,7 @@ //John(VK2ETA) Code #include -#define I2C_DISPLAY_ADDRESS 0x27 +#define I2C_DISPLAY_ADDRESS 0x3F //0x27 LiquidCrystal_I2C lcd(I2C_DISPLAY_ADDRESS,16,2); // set the LCD as a 16 chars and 2 line display //LiquidCrystal_I2C lcd(0x27,16,2) @@ -60,95 +60,13 @@ LiquidCrystal_I2C lcd(I2C_DISPLAY_ADDRESS,16,2); // set the LCD as a 16 chars a char c[30], b[30]; char printBuff[2][17]; //mirrors what is showing on the two lines of the display -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 -}; - -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); -} - void LCD_Init(void) { lcd.begin(16, 2); initMeter(); //for Meter Display + lcd.backlight(); } -//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; -} - - - // The generic routine to display one line on the LCD void printLine(unsigned char linenmbr, const char *c) { if ((displayOption1 & 0x01) == 0x01) @@ -164,7 +82,10 @@ void printLine(unsigned char linenmbr, const char *c) { } } } - +void LCD_CreateChar(int aaa, int bbb) +{ + +} void printLineF(char linenmbr, const __FlashStringHelper *c) { int i; diff --git a/ubitx_20/ubitx_lcd_1602iian.ino b/ubitx_20/ubitx_lcd_1602iian.ino index 0512339..f551300 100644 --- a/ubitx_20/ubitx_lcd_1602iian.ino +++ b/ubitx_20/ubitx_lcd_1602iian.ino @@ -39,7 +39,7 @@ But keep it as long as the original author of the code. Ian KD8CEC **************************************************************************/ -#define I2C_DISPLAY_ADDRESS 0x27 +#define I2C_DISPLAY_ADDRESS 0x3F //0x27 #define En B00000100 // Enable bit #define Rw B00000010 // Read/Write bit @@ -135,10 +135,22 @@ void LCD_Send(uint8_t value, uint8_t 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; + _Addr = I2C_DISPLAY_ADDRESS; _cols = 16; _rows = 2; _backlightval = LCD_NOBACKLIGHT; @@ -180,6 +192,8 @@ void LCD1602_Init() delayMicroseconds(1000); // this command takes a long time! LCD_Command(LCD_ENTRYMODESET | LCD_ENTRYLEFT | LCD_ENTRYSHIFTDECREMENT); + + backlight(); } void LCD_Print(const char *c) @@ -219,92 +233,12 @@ LiquidCrystal lcd(8,9,10,11,12,13); char c[30], b[30]; char printBuff[2][17]; //mirrors what is showing on the two lines of the display -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 -}; - -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); -} - void LCD_Init(void) { LCD1602_Init(); initMeter(); //for Meter Display } -//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; -} // The generic routine to display one line on the LCD void printLine(unsigned char linenmbr, const char *c) { diff --git a/ubitx_20/ubitx_ui.ino b/ubitx_20/ubitx_ui.ino index e2fad8d..faf8f53 100644 --- a/ubitx_20/ubitx_ui.ino +++ b/ubitx_20/ubitx_ui.ino @@ -162,12 +162,37 @@ void drawMeter(int needle) //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 + { + for (int i = 0; i < 16; i++) + if (KeyValues[i][0] <= readButtonValue && KeyValues[i][1] >= readButtonValue) + return i; + } + + return -1; +} +#endif + int enc_prev_state = 3; /** From 5611e1c0ffa258ecc89e1a7be6ec0a4de730dead Mon Sep 17 00:00:00 2001 From: phdlee Date: Wed, 18 Apr 2018 20:22:52 +0900 Subject: [PATCH 109/173] lcdtest and extended ubutton tested --- ubitx_20/ubitx.h | 19 +- ubitx_20/ubitx_20.ino | 8 +- ...ubitx_lcd_1602p.ino => ubitx_lcd_1602.ino} | 238 +++++- ubitx_20/ubitx_lcd_1602i.ino | 512 ------------- ubitx_20/ubitx_lcd_1602iian.ino | 687 ------------------ ...ubitx_lcd_2004p.ino => ubitx_lcd_2004.ino} | 218 +++++- ubitx_20/ubitx_lcd_2004i.ino | 22 - 7 files changed, 397 insertions(+), 1307 deletions(-) rename ubitx_20/{ubitx_lcd_1602p.ino => ubitx_lcd_1602.ino} (78%) delete mode 100644 ubitx_20/ubitx_lcd_1602i.ino delete mode 100644 ubitx_20/ubitx_lcd_1602iian.ino rename ubitx_20/{ubitx_lcd_2004p.ino => ubitx_lcd_2004.ino} (82%) delete mode 100644 ubitx_20/ubitx_lcd_2004i.ino diff --git a/ubitx_20/ubitx.h b/ubitx_20/ubitx.h index 606799c..5ea7620 100644 --- a/ubitx_20/ubitx.h +++ b/ubitx_20/ubitx.h @@ -19,19 +19,22 @@ //============================================================================== // Compile Option //============================================================================== -#define ENABLE_FACTORYALIGN -#define ENABLE_ADCMONITOR //Starting with Version 1.07, you can read ADC values directly from uBITX Manager. So this function is not necessary. - //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 //#define UBITX_DISPLAY_LCD1602I //I2C type 16 x 02 LCD -#define UBITX_DISPLAY_LCD1602I_CUST //I2C type 16 x 02 Custom Tiny Library -//#define UBITX_DISPLAY_LCD2004P //24 x 04 LCD -//#define UBITX_DISPLAY_LCD2004I //I2C type 24 x 04 LCD +#define UBITX_DISPLAY_LCD2004P //24 x 04 LCD (Parallel) +//#define UBITX_DISPLAY_LCD2004I //I2C type 24 x 04 LCD -//#define EXTEND_KEY_GROUP1 //MODE, BAND(-), BAND(+), STEP -//#define EXTEND_KEY_GROUP2 //Numeric (0~9), Point(.), Enter //Not supported in Version 1.0x +#define I2C_DISPLAY_ADDRESS 0x3F //0x27 //DEFAULT, if Set I2C Address by uBITX Manager, read from EEProm + +//#define EXTEND_KEY_GROUP1 //MODE, BAND(-), BAND(+), STEP +//#define EXTEND_KEY_GROUP2 //Numeric (0~9), Point(.), Enter //Not supported in Version 1.0x + +#define ENABLE_FACTORYALIGN +#define ENABLE_ADCMONITOR //Starting with Version 1.07, you can read ADC values directly from uBITX Manager. So this function is not necessary. + +#define SMeterLatency 3 //1 is 0.25 sec //============================================================================== // Hardware, Define PIN Usage diff --git a/ubitx_20/ubitx_20.ino b/ubitx_20/ubitx_20.ino index 34b99fb..9ceb79a 100644 --- a/ubitx_20/ubitx_20.ino +++ b/ubitx_20/ubitx_20.ino @@ -1000,10 +1000,10 @@ void initSettings(){ { //Default Setting arTuneStep[0] = 10; - arTuneStep[1] = 20; - arTuneStep[2] = 50; - arTuneStep[3] = 100; - arTuneStep[4] = 200; + arTuneStep[1] = 50; + arTuneStep[2] = 100; + arTuneStep[3] = 500; + arTuneStep[4] = 1000; } if (tuneStepIndex == 0) //New User diff --git a/ubitx_20/ubitx_lcd_1602p.ino b/ubitx_20/ubitx_lcd_1602.ino similarity index 78% rename from ubitx_20/ubitx_lcd_1602p.ino rename to ubitx_20/ubitx_lcd_1602.ino index 8b2f82b..3162318 100644 --- a/ubitx_20/ubitx_lcd_1602p.ino +++ b/ubitx_20/ubitx_lcd_1602.ino @@ -20,34 +20,8 @@ along with this program. If not, see . **************************************************************************/ -#ifdef UBITX_DISPLAY_LCD1602P - -//======================================================================== -//Begin of TinyLCD Library by KD8CEC -//======================================================================== -/************************************************************************* - 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 connected PIN -#define LCD_PIN_RS 8 -#define LCD_PIN_EN 9 -uint8_t LCD_PIN_DAT[4] = {10, 11, 12, 13}; - -// commands +//Common Defines ********************************************************* #define LCD_CLEARDISPLAY 0x01 #define LCD_RETURNHOME 0x02 #define LCD_ENTRYMODESET 0x04 @@ -85,6 +59,39 @@ uint8_t LCD_PIN_DAT[4] = {10, 11, 12, 13}; #define LCD_5x10DOTS 0x04 #define LCD_5x8DOTS 0x00 +// flags for backlight control +#define LCD_BACKLIGHT 0x08 +#define LCD_NOBACKLIGHT 0x00 + +//======================================================================== +//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++) @@ -167,20 +174,176 @@ void LCD_CreateChar(uint8_t location, uint8_t charmap[]) for (int i=0; i<8; i++) LCD_Write(charmap[i]); } +#endif //======================================================================== //End of TinyLCD Library by KD8CEC //======================================================================== -/* -#include -LiquidCrystal lcd(8,9,10,11,12,13); -*/ + +//======================================================================== +//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_DISPLAY_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 -//======================================================================== -//Begin of Display Base Routines (Init, printLine..) -//======================================================================== char c[30], b[30]; char printBuff[2][17]; //mirrors what is showing on the two lines of the display @@ -589,9 +752,9 @@ void DisplayMeter(byte meterType, byte meterValue, char drawPosition) byte testValue = 0; char checkCount = 0; +char checkCountSMeter = 0; int currentSMeter = 0; -//byte sMeterLevels[] = {0, 1, 4, 10, 18, 35, 63, 91, 117}; //John's default Value (divide / 4) byte scaledSMeter = 0; void idle_process() @@ -621,10 +784,11 @@ void idle_process() */ //S-Meter Display - if ((displayOption1 & 0x08) == 0x08 && (sdrModeOn == 0)) + if (((displayOption1 & 0x08) == 0x08 && (sdrModeOn == 0)) && (++checkCountSMeter > SMeterLatency)) { int newSMeter; - + checkCountSMeter = 0; //Reset Latency time + //VK2ETA S-Meter from MAX9814 TC pin / divide 4 by KD8CEC for reduce EEPromSize newSMeter = analogRead(ANALOG_SMETER); diff --git a/ubitx_20/ubitx_lcd_1602i.ino b/ubitx_20/ubitx_lcd_1602i.ino deleted file mode 100644 index ecf363e..0000000 --- a/ubitx_20/ubitx_lcd_1602i.ino +++ /dev/null @@ -1,512 +0,0 @@ -/************************************************************************* - KD8CEC, _______ - uBITX Display Routine for LCD1602 I2C - - 1.Code for 16 x 2 LCD for I2C. - 2.Display related functions of uBITX. Some functions moved from uBITX_Ui. - 3.uBITX Idle time Processing - Functions that run at times that do not affect TX, CW, and CAT - It is called in 1/10 time unit. ------------------------------------------------------------------------------ - 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 . - -**************************************************************************/ -#ifdef UBITX_DISPLAY_LCD1602I - - -//======================================================================== -//Begin of LCD Hardware define -//The I2C LCD I ordered did not arrive yet. -//I referenced the I2C LCD control in the thread at the link below. -////https://groups.io/g/BITX20/topic/variation_on_ian_s_kd8cec/16657839?p=,,,20,0,0,0::recentpostdate%2Fsticky,,,20,2,0,16657839 -//In particular, I referenced the sample code of John (VK2ETA) and K9HZ. Jack, W8TEE, Nick -//======================================================================== -//#include -//LiquidCrystal lcd(8,9,10,11,12,13); - -//K9HZ's Code -//#include -//LiquidCrystal_I2C lcd(0x3F, 2, 1, 0, 4, 5, 6, 7, 3, POSITIVE); // - -//Jack(W8TEE) Code -//LiquidCrystal_I2C lcd(0x3F); // I2C address - -//John(VK2ETA) Code -#include -#define I2C_DISPLAY_ADDRESS 0x3F //0x27 -LiquidCrystal_I2C lcd(I2C_DISPLAY_ADDRESS,16,2); // set the LCD as a 16 chars and 2 line display - -//LiquidCrystal_I2C lcd(0x27,16,2) - - -//======================================================================== -//End of LCD Hardware define -//======================================================================== - -//======================================================================== -//Begin of Display Base Routines (Init, printLine..) -//======================================================================== -char c[30], b[30]; -char printBuff[2][17]; //mirrors what is showing on the two lines of the display - -void LCD_Init(void) -{ - lcd.begin(16, 2); - initMeter(); //for Meter Display - lcd.backlight(); -} - -// 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 LCD_CreateChar(int aaa, int bbb) -{ - -} -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 -//=================================================================================== - -// 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 (isUSB) - strcpy(c, "USB "); - else - strcpy(c, "LSB "); - } - else if (cwMode == 1) - { - strcpy(c, "CWL "); - } - else - { - strcpy(c, "CWU "); - } - } - 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[16]; -//KD8CEC 200Hz ST -//L14.150 200Hz ST -//U14.150 +150khz -int freqScrollPosition = 0; -//Example Line2 Optinal Display -//immediate execution, not call by scheulder -void updateLine2Buffer(char displayType) -{ - unsigned long tmpFreq = 0; - 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] = ' '; - - //Check CW Key cwKeyType = 0; //0: straight, 1 : iambica, 2: iambicb - 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); //call original source code - int lineNumber = 0; - if ((displayOption1 & 0x01) == 0x01) - lineNumber = 1; - - lcd.setCursor(drawPosition, lineNumber); - - for (int i = 0; i < 6; i++) //meter 5 + +db 1 = 6 - lcd.write(lcdMeter[i]); - } -} - -byte testValue = 0; -char checkCount = 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)) { - if (checkCount++ > 1) - { - updateLine2Buffer(0); //call by scheduler - printLine2(line2Buffer); - line2DisplayStatus = 2; - checkCount = 0; - } - - //EX for Meters - /* - DisplayMeter(0, testValue++, 7); - if (testValue > 30) - testValue = 0; - */ - } - } -} - -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_lcd_1602iian.ino b/ubitx_20/ubitx_lcd_1602iian.ino deleted file mode 100644 index f551300..0000000 --- a/ubitx_20/ubitx_lcd_1602iian.ino +++ /dev/null @@ -1,687 +0,0 @@ -/************************************************************************* - KD8CEC's uBITX Display Routine for LCD1602 Parrel - 1.This is the display code for the default LCD mounted in uBITX. - 2.Display related functions of uBITX. Some functions moved from uBITX_Ui. - 3.uBITX Idle time Processing - Functions that run at times that do not affect TX, CW, and CAT - It is called in 1/10 time unit. ------------------------------------------------------------------------------ - 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 . - -**************************************************************************/ -#ifdef UBITX_DISPLAY_LCD1602I_CUST -#include - -//======================================================================== -//Begin of TinyLCD Library by KD8CEC -//======================================================================== -/************************************************************************* - 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. - Ian KD8CEC -**************************************************************************/ -#define I2C_DISPLAY_ADDRESS 0x3F //0x27 - -#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)) - -//Define connected PIN -//#define LCD_PIN_RS 8 -//#define LCD_PIN_EN 9 -//uint8_t LCD_PIN_DAT[4] = {10, 11, 12, 13}; - -uint8_t _Addr; -uint8_t _displayfunction; -uint8_t _displaycontrol; -uint8_t _displaymode; -uint8_t _numlines; -uint8_t _cols; -uint8_t _rows; -uint8_t _backlightval; - -// commands -#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 - -#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_DISPLAY_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]); -} -//======================================================================== -//End of TinyLCD Library by KD8CEC -//======================================================================== - -/* -#include -LiquidCrystal lcd(8,9,10,11,12,13); -*/ - - -//======================================================================== -//Begin of Display Base Routines (Init, printLine..) -//======================================================================== -char c[30], b[30]; -char printBuff[2][17]; //mirrors what is showing on the two lines of the display - -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 (isUSB) - strcpy(c, "USB "); - else - strcpy(c, "LSB "); - } - else if (cwMode == 1) - { - strcpy(c, "CWL "); - } - else - { - strcpy(c, "CWU "); - } - } - 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[16]; -//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 (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] = ' '; - - //Check CW Key cwKeyType = 0; //0: straight, 1 : iambica, 2: iambicb - 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); //call original source code - int lineNumber = 0; - if ((displayOption1 & 0x01) == 0x01) - lineNumber = 1; - - LCD_SetCursor(drawPosition, lineNumber); - - for (int i = 0; i < 6; i++) //meter 5 + +db 1 = 6 - LCD_Write(lcdMeter[i]); - } -} - -byte testValue = 0; -char checkCount = 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)) { - if (checkCount++ > 1) - { - updateLine2Buffer(0); //call by scheduler - printLine2(line2Buffer); - line2DisplayStatus = 2; - checkCount = 0; - } - - //EX for Meters - /* - DisplayMeter(0, testValue++, 7); - if (testValue > 30) - testValue = 0; - */ - } - } -} - -//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_lcd_2004p.ino b/ubitx_20/ubitx_lcd_2004.ino similarity index 82% rename from ubitx_20/ubitx_lcd_2004p.ino rename to ubitx_20/ubitx_lcd_2004.ino index fb29ec0..8e552a6 100644 --- a/ubitx_20/ubitx_lcd_2004p.ino +++ b/ubitx_20/ubitx_lcd_2004.ino @@ -20,33 +20,8 @@ along with this program. If not, see . **************************************************************************/ -#ifdef UBITX_DISPLAY_LCD2004P -//======================================================================== -//Begin of TinyLCD Library by KD8CEC -//======================================================================== -/************************************************************************* - LCD2004TINY Library for 20 x 4 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 connected PIN -#define LCD_PIN_RS 8 -#define LCD_PIN_EN 9 -uint8_t LCD_PIN_DAT[4] = {10, 11, 12, 13}; - -// commands +//Common Defines ********************************************************* #define LCD_CLEARDISPLAY 0x01 #define LCD_RETURNHOME 0x02 #define LCD_ENTRYMODESET 0x04 @@ -84,6 +59,38 @@ uint8_t LCD_PIN_DAT[4] = {10, 11, 12, 13}; #define LCD_5x10DOTS 0x04 #define LCD_5x8DOTS 0x00 +// flags for backlight control +#define LCD_BACKLIGHT 0x08 +#define LCD_NOBACKLIGHT 0x00 + +//======================================================================== +//Begin of TinyLCD Library by KD8CEC +//======================================================================== + +#ifdef UBITX_DISPLAY_LCD2004P +/************************************************************************* + LCD2004TINY Library for 20 x 4 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_LCD2004_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++) @@ -144,6 +151,152 @@ void LCD2004_Init() LCD_Command(LCD_ENTRYMODESET | LCD_ENTRYLEFT | LCD_ENTRYSHIFTDECREMENT); } +#endif +//======================================================================== +//End of TinyLCD Library by KD8CEC +//======================================================================== + + + +//======================================================================== +//Begin of I2CTinyLCD Library by KD8CEC +//======================================================================== +#ifdef UBITX_DISPLAY_LCD2004I + +#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_LCD2004_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 LCD2004_Init() +{ + //I2C Init + _Addr = I2C_DISPLAY_ADDRESS; + _cols = 20; + _rows = 4; + _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(); +} +#endif +//======================================================================== +//End of I2CTinyLCD Library by KD8CEC +//======================================================================== + + +//======================================================================== +// 20 X 04 LCD Routines +//Begin of Display Base Routines (Init, printLine..) +//======================================================================== +#ifdef UBITX_DISPLAY_LCD2004_BASE void LCD_Print(const char *c) { @@ -167,20 +320,10 @@ void LCD_CreateChar(uint8_t location, uint8_t charmap[]) for (int i=0; i<8; i++) LCD_Write(charmap[i]); } -//======================================================================== -//End of TinyLCD Library by KD8CEC -//======================================================================== -/* -#include -LiquidCrystal lcd(8,9,10,11,12,13); -*/ //SWR GRAPH, DrawMeter and drawingMeter Logic function by VK2ETA //#define OPTION_SKINNYBARS -//======================================================================== -//Begin of Display Base Routines (Init, printLine..) -//======================================================================== char c[30], b[30]; char printBuff[4][20]; //mirrors what is showing on the two lines of the display @@ -623,6 +766,7 @@ char checkCount = 0; int currentSMeter = 0; //int sMeterLevels[] = {0, 5, 17, 41, 74, 140, 255, 365, 470}; byte scaledSMeter = 0; +char checkCountSMeter = 0; //execute interval : 0.25sec void idle_process() @@ -660,7 +804,7 @@ void idle_process() // testValue = 0; //S-Meter Display - if ((displayOption1 & 0x08) == 0x08 && (sdrModeOn == 0)) + if (((displayOption1 & 0x08) == 0x08 && (sdrModeOn == 0)) && (++checkCountSMeter > SMeterLatency)) { int newSMeter; diff --git a/ubitx_20/ubitx_lcd_2004i.ino b/ubitx_20/ubitx_lcd_2004i.ino deleted file mode 100644 index bee57ab..0000000 --- a/ubitx_20/ubitx_lcd_2004i.ino +++ /dev/null @@ -1,22 +0,0 @@ -/************************************************************************* - KD8CEC's uBITX Display Routine for LCD2404 I2C - uBITX Idle time Processing - Functions that run at times that do not affect TX, CW, and CAT - It is called in 1/10 time unit. ------------------------------------------------------------------------------ - 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 . - -**************************************************************************/ - - From 0aafe32e27b5d4e4a05356705969dc179b5494fc Mon Sep 17 00:00:00 2001 From: phdlee Date: Sat, 21 Apr 2018 16:09:21 +0900 Subject: [PATCH 110/173] Added Dual LCD --- ubitx_20/ubitx.h | 17 +- ubitx_20/ubitx_20.ino | 20 +- ubitx_20/ubitx_eemap.h | 3 + ubitx_20/ubitx_lcd_1602.ino | 51 ++- ubitx_20/ubitx_lcd_1602Dual.ino | 770 ++++++++++++++++++++++++++++++++ ubitx_20/ubitx_lcd_2004.ino | 6 +- ubitx_20/ubitx_wspr.ino | 1 - 7 files changed, 838 insertions(+), 30 deletions(-) create mode 100644 ubitx_20/ubitx_lcd_1602Dual.ino diff --git a/ubitx_20/ubitx.h b/ubitx_20/ubitx.h index 5ea7620..f3c399f 100644 --- a/ubitx_20/ubitx.h +++ b/ubitx_20/ubitx.h @@ -23,10 +23,12 @@ //You must select only one. //#define UBITX_DISPLAY_LCD1602P //LCD mounted on unmodified uBITX //#define UBITX_DISPLAY_LCD1602I //I2C type 16 x 02 LCD -#define UBITX_DISPLAY_LCD2004P //24 x 04 LCD (Parallel) +#define UBITX_DISPLAY_LCD1602I_DUAL +//#define UBITX_DISPLAY_LCD2004P //24 x 04 LCD (Parallel) //#define UBITX_DISPLAY_LCD2004I //I2C type 24 x 04 LCD -#define I2C_DISPLAY_ADDRESS 0x3F //0x27 //DEFAULT, if Set I2C Address by uBITX Manager, read from EEProm +#define I2C_LCD_MASTER_ADDRESS_DEFAULT 0x3F //0x27 //DEFAULT, if Set I2C Address by uBITX Manager, read from EEProm +#define I2C_LCD_SECOND_ADDRESS_DEFAULT 0x27 //0x27 //only using Dual LCD Mode //#define EXTEND_KEY_GROUP1 //MODE, BAND(-), BAND(+), STEP //#define EXTEND_KEY_GROUP2 //Numeric (0~9), Point(.), Enter //Not supported in Version 1.0x @@ -34,8 +36,19 @@ #define ENABLE_FACTORYALIGN #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 +#ifdef defined(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 + //============================================================================== // Hardware, Define PIN Usage //============================================================================== diff --git a/ubitx_20/ubitx_20.ino b/ubitx_20/ubitx_20.ino index 9ceb79a..5902aef 100644 --- a/ubitx_20/ubitx_20.ino +++ b/ubitx_20/ubitx_20.ino @@ -6,7 +6,7 @@ // 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.072") +#define FIRMWARE_VERSION_INFO F("+v1.073") #define FIRMWARE_VERSION_NUM 0x02 //1st Complete Project : 1 (Version 1.061), 2st Project : 2 /** @@ -182,6 +182,10 @@ byte line2DisplayStatus = 0; //0:Clear, 1 : menu, 1: DisplayFrom Idle, char lcdMeter[17]; byte sMeterLevels[9]; +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 + + int KeyValues[16][2]; /*= { {1023, 1025}, //1 @@ -927,7 +931,6 @@ void initSettings(){ else keyerControl |= IAMBICB; } - EEPROM.get(COMMON_OPTION0, commonOption0); EEPROM.get(DISPLAY_OPTION1, displayOption1); @@ -1190,6 +1193,19 @@ void setup() //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(); diff --git a/ubitx_20/ubitx_eemap.h b/ubitx_20/ubitx_eemap.h index d87a869..a2cd8a7 100644 --- a/ubitx_20/ubitx_eemap.h +++ b/ubitx_20/ubitx_eemap.h @@ -50,6 +50,9 @@ // 256 ~ 1023 (EEProm Section #1) // 255 ~ 101 (EEProm Section #2) //============================================================================== +#define I2C_LCD_MASTER 190 +#define I2C_LCD_SECOND 191 +//RESERVE 192 ~ 195 #define EXTENDED_KEY_RANGE 196 //Extended Key, KEY RANGE (MODE, BAND+, BAND-, TUNE_STEP, NUM0~NUM9, POINT, ENTER #define S_METER_LEVELS 230 //LEVEL0 ~ LEVEL7 diff --git a/ubitx_20/ubitx_lcd_1602.ino b/ubitx_20/ubitx_lcd_1602.ino index 3162318..9d23192 100644 --- a/ubitx_20/ubitx_lcd_1602.ino +++ b/ubitx_20/ubitx_lcd_1602.ino @@ -20,6 +20,7 @@ along with this program. If not, see . **************************************************************************/ +#include "ubitx.h" //Common Defines ********************************************************* #define LCD_CLEARDISPLAY 0x01 @@ -153,27 +154,6 @@ void LCD1602_Init() LCD_Command(LCD_ENTRYMODESET | LCD_ENTRYLEFT | LCD_ENTRYSHIFTDECREMENT); } -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 TinyLCD Library by KD8CEC @@ -262,7 +242,7 @@ void backlight(void) { void LCD1602_Init() { //I2C Init - _Addr = I2C_DISPLAY_ADDRESS; + _Addr = I2C_LCD_MASTER_ADDRESS; _cols = 16; _rows = 2; _backlightval = LCD_NOBACKLIGHT; @@ -308,6 +288,7 @@ void LCD1602_Init() backlight(); } +/* void LCD_Print(const char *c) { for (uint8_t i = 0; i < strlen(c); i++) @@ -329,6 +310,7 @@ void LCD_CreateChar(uint8_t location, uint8_t charmap[]) for (int i=0; i<8; i++) LCD_Write(charmap[i]); } +*/ #endif //======================================================================== //End of I2CTinyLCD Library by KD8CEC @@ -347,6 +329,29 @@ void LCD_CreateChar(uint8_t location, uint8_t charmap[]) char c[30], b[30]; 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(); @@ -787,7 +792,6 @@ void idle_process() if (((displayOption1 & 0x08) == 0x08 && (sdrModeOn == 0)) && (++checkCountSMeter > SMeterLatency)) { int newSMeter; - checkCountSMeter = 0; //Reset Latency time //VK2ETA S-Meter from MAX9814 TC pin / divide 4 by KD8CEC for reduce EEPromSize newSMeter = analogRead(ANALOG_SMETER); @@ -804,6 +808,7 @@ void idle_process() } DisplayMeter(0, scaledSMeter, 14); + checkCountSMeter = 0; //Reset Latency time } //end of S-Meter } diff --git a/ubitx_20/ubitx_lcd_1602Dual.ino b/ubitx_20/ubitx_lcd_1602Dual.ino new file mode 100644 index 0000000..a8105c7 --- /dev/null +++ b/ubitx_20/ubitx_lcd_1602Dual.ino @@ -0,0 +1,770 @@ +/************************************************************************* + KD8CEC's uBITX Display Routine for LCD1602 Dual LCD by KD8CEC + 1.This is the display code for the default LCD mounted in uBITX. + 2.Display related functions of uBITX. Some functions moved from uBITX_Ui. + 3.uBITX Idle time Processing + Functions that run at times that do not affect TX, CW, and CAT + It is called in 1/10 time unit. +----------------------------------------------------------------------------- + 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" + +//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 + +//======================================================================== +//Begin of I2CTinyLCD Library for Dual LCD by KD8CEC +//======================================================================== +#ifdef UBITX_DISPLAY_LCD1602I_DUAL + +#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_Dual_Init() +{ + //I2C Init + _cols = 16; + _rows = 2; + _backlightval = LCD_NOBACKLIGHT; + Wire.begin(); + + delay(50); + + // Now we pull both RS and R/W low to begin commands + _Addr = I2C_LCD_MASTER_ADDRESS; + expanderWrite(_backlightval); // reset expanderand turn backlight off (Bit 8 =1) + _Addr = I2C_LCD_SECOND_ADDRESS; + 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 + + _Addr = I2C_LCD_MASTER_ADDRESS; + // 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(); + + + _Addr = I2C_LCD_SECOND_ADDRESS; + // 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(); + + //Change to Default LCD (Master) + _Addr = I2C_LCD_MASTER_ADDRESS; +} + + +//======================================================================== +// 16 X 02 LCD Routines +//Begin of Display Base Routines (Init, printLine..) +//======================================================================== + +void LCD_Print(const char *c) +{ + for (uint8_t i = 0; i < strlen(c); i++) + { + if (*(c + i) == 0x00) return; + LCD_Write(*(c + i)); + } +} + +const int row_offsets[] = { 0x00, 0x40, 0x14, 0x54 }; +void LCD_SetCursor(uint8_t col, uint8_t row) +{ + LCD_Command(LCD_SETDDRAMADDR | (col + row_offsets[row])); //0 : 0x00, 1 : 0x40, only for 20 x 4 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]); +} + +//SWR GRAPH, DrawMeter and drawingMeter Logic function by VK2ETA +//#define OPTION_SKINNYBARS + +char c[30], b[30]; +char printBuff[4][20]; //mirrors what is showing on the two lines of the display + +void LCD_Init(void) +{ + LCD1602_Dual_Init(); + + _Addr = I2C_LCD_SECOND_ADDRESS; + initMeter(); //for Meter Display //when dual LCD, S.Meter on second LCD + _Addr = I2C_LCD_MASTER_ADDRESS; +} + + +// 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 < 20; i++) { // add white spaces until the end of the 20 characters line is reached + LCD_Write(' '); + } + } +} + +void printLineF(char linenmbr, const __FlashStringHelper *c) +{ + int i; + char tmpBuff[21]; + PGM_P p = reinterpret_cast(c); + + for (i = 0; i < 21; i++){ + unsigned char fChar = pgm_read_byte(p++); + tmpBuff[i] = fChar; + if (fChar == 0) + break; + } + + printLine(linenmbr, tmpBuff); +} + +#define LCD_MAX_COLUMN 20 +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 < 20; 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 + // i also Very TNX Purdum for good source code + 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 (isUSB) + strcpy(c, "USB "); + else + strcpy(c, "LSB "); + } + else if (cwMode == 1) + { + strcpy(c, "CWL "); + } + else + { + strcpy(c, "CWU "); + } + } + + 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) + 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[20]; +//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 (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] = ' '; + } + + memset(&line2Buffer[10], ' ', 10); + + if (isIFShift) + { + line2Buffer[6] = 'M'; + line2Buffer[7] = ' '; + //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); + + for (int i = 12; i < 17; i++) + { + if (line2Buffer[i] == 0) + line2Buffer[i] = ' '; + } + } // end of display IF + else // step & Key Type display + { + //Step + long tmpStep = arTuneStep[tuneStepIndex -1]; + + byte isStepKhz = 0; + if (tmpStep >= 1000) + { + isStepKhz = 2; + } + + for (int i = 13; i >= 11 - isStepKhz; i--) { + if (tmpStep > 0) { + line2Buffer[i + isStepKhz] = tmpStep % 10 + 0x30; + tmpStep /= 10; + } + else + line2Buffer[i +isStepKhz] = ' '; + } + + if (isStepKhz == 0) + { + line2Buffer[14] = 'H'; + line2Buffer[15] = 'z'; + } + } + + //line2Buffer[17] = ' '; + /* ianlee + //Check CW Key cwKeyType = 0; //0: straight, 1 : iambica, 2: iambicb + if (cwKeyType == 0) + { + line2Buffer[18] = 'S'; + line2Buffer[19] = 'T'; + } + else if (cwKeyType == 1) + { + line2Buffer[18] = 'I'; + line2Buffer[19] = 'A'; + } + else + { + line2Buffer[18] = 'I'; + line2Buffer[19] = '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); + + LCD_SetCursor(drawPosition, 0); + LCD_Write('S'); + + LCD_Write(':'); + for (int i = 0; i < 6; i++) //meter 5 + +db 1 = 6 + LCD_Write(lcdMeter[i]); + } +} + + +byte testValue = 0; +char checkCount = 0; + +int currentSMeter = 0; +byte scaledSMeter = 0; +char checkCountSMeter = 0; + +char beforeKeyType = -1; +char displaySDRON = 0; + +//execute interval : 0.25sec +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)) { + if (checkCount++ > 1) + { + updateLine2Buffer(0); //call by scheduler + printLine2(line2Buffer); + line2DisplayStatus = 2; + checkCount = 0; + + //check change CW Key Type + if (beforeKeyType != cwKeyType) + { + _Addr = I2C_LCD_SECOND_ADDRESS; + LCD_SetCursor(10, 0); + LCD_Write('K'); + LCD_Write('E'); + LCD_Write('Y'); + LCD_Write(':'); + + //Check CW Key cwKeyType = 0; //0: straight, 1 : iambica, 2: iambicb + if (cwKeyType == 0) + { + LCD_Write('S'); + LCD_Write('T'); + } + else if (cwKeyType == 1) + { + LCD_Write('I'); + LCD_Write('A'); + } + else + { + LCD_Write('I'); + LCD_Write('B'); + } + + beforeKeyType = cwKeyType; + _Addr = I2C_LCD_MASTER_ADDRESS; + } //Display Second Screen + + } + } + + //EX for Meters + + //S-Meter Display + _Addr = I2C_LCD_SECOND_ADDRESS; + if (sdrModeOn == 1) + { + if (displaySDRON == 0) //once display + { + displaySDRON = 1; + LCD_SetCursor(0, 0); + LCD_Write('S'); + LCD_Write('D'); + LCD_Write('R'); + LCD_Write(' '); + LCD_Write('M'); + LCD_Write('O'); + LCD_Write('D'); + LCD_Write('E'); + } + } + else if (((displayOption1 & 0x08) == 0x08) && (++checkCountSMeter > 3)) + { + int newSMeter; + displaySDRON = 0; + + //VK2ETA S-Meter from MAX9814 TC pin + newSMeter = analogRead(ANALOG_SMETER); + + //Faster attack, Slower release + currentSMeter = (newSMeter > currentSMeter ? ((currentSMeter * 3 + newSMeter * 7) + 5) / 10 : ((currentSMeter * 7 + newSMeter * 3) + 5) / 10); + + scaledSMeter = 0; + for (byte s = 8; s >= 1; s--) { + if (currentSMeter > sMeterLevels[s]) { + scaledSMeter = s; + break; + } + } + + DisplayMeter(0, scaledSMeter, 0); + + checkCountSMeter = 0; + } //end of S-Meter + _Addr = I2C_LCD_MASTER_ADDRESS; + + + } +} + +//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) +{ + _Addr = I2C_LCD_SECOND_ADDRESS; + printLineFromEEPRom(1, 16 - userCallsignLength, 0, userCallsignLength -1, 0); //eeprom to lcd use offset (USER_CALLSIGN_DAT) + _Addr = I2C_LCD_MASTER_ADDRESS; +} + +void DisplayVersionInfo(const __FlashStringHelper * fwVersionInfo) +{ + _Addr = I2C_LCD_SECOND_ADDRESS; + printLineF(1, fwVersionInfo); + _Addr = I2C_LCD_MASTER_ADDRESS; +} + +#endif diff --git a/ubitx_20/ubitx_lcd_2004.ino b/ubitx_20/ubitx_lcd_2004.ino index 8e552a6..e74b7c4 100644 --- a/ubitx_20/ubitx_lcd_2004.ino +++ b/ubitx_20/ubitx_lcd_2004.ino @@ -20,6 +20,7 @@ along with this program. If not, see . **************************************************************************/ +#include "ubitx.h" //Common Defines ********************************************************* #define LCD_CLEARDISPLAY 0x01 @@ -241,7 +242,7 @@ void backlight(void) { void LCD2004_Init() { //I2C Init - _Addr = I2C_DISPLAY_ADDRESS; + _Addr = I2C_LCD_MASTER_ADDRESS; _cols = 20; _rows = 4; _backlightval = LCD_NOBACKLIGHT; @@ -807,7 +808,7 @@ void idle_process() if (((displayOption1 & 0x08) == 0x08 && (sdrModeOn == 0)) && (++checkCountSMeter > SMeterLatency)) { int newSMeter; - + //VK2ETA S-Meter from MAX9814 TC pin newSMeter = analogRead(ANALOG_SMETER); @@ -823,6 +824,7 @@ void idle_process() } DisplayMeter(0, scaledSMeter, 0); + checkCountSMeter = 0; //Reset Latency time } //end of S-Meter } diff --git a/ubitx_20/ubitx_wspr.ino b/ubitx_20/ubitx_wspr.ino index 8ac28b5..eb85147 100644 --- a/ubitx_20/ubitx_wspr.ino +++ b/ubitx_20/ubitx_wspr.ino @@ -36,7 +36,6 @@ unsigned long TX_P2; // Variable values for MSNB_P2 which defines the frequen extern int enc_read(void); byte WsprMSGCount = 0; -#define PTT (A3) #define WSPR_BAND1 401 From 5b13ede65b6f571e41a820096fd65679e019bfda Mon Sep 17 00:00:00 2001 From: phdlee Date: Mon, 23 Apr 2018 08:29:19 +0900 Subject: [PATCH 111/173] test of extended key and dual lcd --- ubitx_20/ubitx.h | 10 +++++----- ubitx_20/ubitx_20.ino | 2 +- ubitx_20/ubitx_lcd_1602Dual.ino | 3 ++- ubitx_20/ubitx_ui.ino | 2 ++ 4 files changed, 10 insertions(+), 7 deletions(-) diff --git a/ubitx_20/ubitx.h b/ubitx_20/ubitx.h index f3c399f..20872b8 100644 --- a/ubitx_20/ubitx.h +++ b/ubitx_20/ubitx.h @@ -21,16 +21,16 @@ //============================================================================== //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 +#define UBITX_DISPLAY_LCD1602P //LCD mounted on unmodified uBITX //#define UBITX_DISPLAY_LCD1602I //I2C type 16 x 02 LCD -#define UBITX_DISPLAY_LCD1602I_DUAL +//#define UBITX_DISPLAY_LCD1602I_DUAL //#define UBITX_DISPLAY_LCD2004P //24 x 04 LCD (Parallel) //#define UBITX_DISPLAY_LCD2004I //I2C type 24 x 04 LCD #define I2C_LCD_MASTER_ADDRESS_DEFAULT 0x3F //0x27 //DEFAULT, if Set I2C Address by uBITX Manager, read from EEProm #define I2C_LCD_SECOND_ADDRESS_DEFAULT 0x27 //0x27 //only using Dual LCD Mode -//#define EXTEND_KEY_GROUP1 //MODE, BAND(-), BAND(+), STEP +#define EXTEND_KEY_GROUP1 //MODE, BAND(-), BAND(+), STEP //#define EXTEND_KEY_GROUP2 //Numeric (0~9), Point(.), Enter //Not supported in Version 1.0x #define ENABLE_FACTORYALIGN @@ -41,7 +41,7 @@ extern byte I2C_LCD_SECOND_ADDRESS; //only using Dual LCD Mode #define SMeterLatency 3 //1 is 0.25 sec -#ifdef defined(UBITX_DISPLAY_LCD1602I) +#ifdef UBITX_DISPLAY_LCD1602I #define USE_I2C_LCD #elif defined(UBITX_DISPLAY_LCD1602I_DUAL) #define USE_I2C_LCD @@ -122,7 +122,7 @@ extern byte I2C_LCD_SECOND_ADDRESS; //only using Dual LCD Mode extern unsigned long frequency; extern byte WsprMSGCount; extern byte sMeterLevels[9]; -extern int KeyValues[16][2]; //ADC value Ranges for Extend Key +extern byte KeyValues[16][2]; //ADC value Ranges for Extend Key extern void printLine1(const char *c); extern void printLine2(const char *c); diff --git a/ubitx_20/ubitx_20.ino b/ubitx_20/ubitx_20.ino index 5902aef..2b716e4 100644 --- a/ubitx_20/ubitx_20.ino +++ b/ubitx_20/ubitx_20.ino @@ -186,7 +186,7 @@ byte I2C_LCD_MASTER_ADDRESS; //0x27 //if Set I2C Address by uBITX Manage byte I2C_LCD_SECOND_ADDRESS; //only using Dual LCD Mode -int KeyValues[16][2]; +byte KeyValues[16][2]; /*= { {1023, 1025}, //1 {707, 711}, //5 diff --git a/ubitx_20/ubitx_lcd_1602Dual.ino b/ubitx_20/ubitx_lcd_1602Dual.ino index a8105c7..8c698ac 100644 --- a/ubitx_20/ubitx_lcd_1602Dual.ino +++ b/ubitx_20/ubitx_lcd_1602Dual.ino @@ -721,7 +721,8 @@ void idle_process() newSMeter = analogRead(ANALOG_SMETER); //Faster attack, Slower release - currentSMeter = (newSMeter > currentSMeter ? ((currentSMeter * 3 + newSMeter * 7) + 5) / 10 : ((currentSMeter * 7 + newSMeter * 3) + 5) / 10); + //currentSMeter = (newSMeter > currentSMeter ? ((currentSMeter * 3 + newSMeter * 7) + 5) / 10 : ((currentSMeter * 7 + newSMeter * 3) + 5) / 10); + currentSMeter = (currentSMeter * 3 + newSMeter * 7) / 10; //remarked becaused of have already Latency time scaledSMeter = 0; for (byte s = 8; s >= 1; s--) { diff --git a/ubitx_20/ubitx_ui.ino b/ubitx_20/ubitx_ui.ino index faf8f53..57fa1a9 100644 --- a/ubitx_20/ubitx_ui.ino +++ b/ubitx_20/ubitx_ui.ino @@ -184,6 +184,8 @@ int getBtnStatus(void){ return FKEY_PRESS; else { + readButtonValue = readButtonValue / 4; + for (int i = 0; i < 16; i++) if (KeyValues[i][0] <= readButtonValue && KeyValues[i][1] >= readButtonValue) return i; From 6be127d811be4d02769f53816d43d494b7317a7b Mon Sep 17 00:00:00 2001 From: phdlee Date: Mon, 23 Apr 2018 21:40:45 +0900 Subject: [PATCH 112/173] test lcd --- ubitx_20/ubitx_lcd_1602.ino | 2 +- ubitx_20/ubitx_lcd_2004.ino | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/ubitx_20/ubitx_lcd_1602.ino b/ubitx_20/ubitx_lcd_1602.ino index 9d23192..46e50a0 100644 --- a/ubitx_20/ubitx_lcd_1602.ino +++ b/ubitx_20/ubitx_lcd_1602.ino @@ -552,7 +552,7 @@ void updateDisplay() { -char line2Buffer[16]; +char line2Buffer[17]; //KD8CEC 200Hz ST //L14.150 200Hz ST //U14.150 +150khz diff --git a/ubitx_20/ubitx_lcd_2004.ino b/ubitx_20/ubitx_lcd_2004.ino index e74b7c4..f895a8b 100644 --- a/ubitx_20/ubitx_lcd_2004.ino +++ b/ubitx_20/ubitx_lcd_2004.ino @@ -326,7 +326,7 @@ void LCD_CreateChar(uint8_t location, uint8_t charmap[]) //#define OPTION_SKINNYBARS char c[30], b[30]; -char printBuff[4][20]; //mirrors what is showing on the two lines of the display +char printBuff[4][21]; //mirrors what is showing on the two lines of the display void LCD_Init(void) { From 6add092391d0636b851ee479410ad46a57d19ece Mon Sep 17 00:00:00 2001 From: phdlee Date: Tue, 24 Apr 2018 17:26:34 +0900 Subject: [PATCH 113/173] Extended key (select key type) --- ubitx_20/cat_libs.ino | 14 +++- ubitx_20/ubitx.h | 38 ++++++++-- ubitx_20/ubitx_20.ino | 149 +++++++++++++++++++++++--------------- ubitx_20/ubitx_eemap.h | 7 +- ubitx_20/ubitx_menu.ino | 16 ++-- ubitx_20/ubitx_si5351.ino | 5 +- ubitx_20/ubitx_ui.ino | 5 +- 7 files changed, 154 insertions(+), 80 deletions(-) diff --git a/ubitx_20/cat_libs.ino b/ubitx_20/cat_libs.ino index 2285d52..3ae0844 100644 --- a/ubitx_20/cat_libs.ino +++ b/ubitx_20/cat_libs.ino @@ -278,7 +278,19 @@ void WriteEEPRom(void) //for remove warning } else { - EEPROM.write(eepromStartIndex, write1Byte); + //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); } diff --git a/ubitx_20/ubitx.h b/ubitx_20/ubitx.h index 20872b8..734c4d0 100644 --- a/ubitx_20/ubitx.h +++ b/ubitx_20/ubitx.h @@ -34,7 +34,7 @@ //#define EXTEND_KEY_GROUP2 //Numeric (0~9), Point(.), Enter //Not supported in Version 1.0x #define ENABLE_FACTORYALIGN -#define ENABLE_ADCMONITOR //Starting with Version 1.07, you can read ADC values directly from uBITX Manager. So this function is not necessary. +//#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 @@ -112,17 +112,41 @@ extern byte I2C_LCD_SECOND_ADDRESS; //only using Dual LCD Mode #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 120 -#define FKEY_MODE 0 -#define FKEY_BANDUP 1 -#define FKEY_BANDDOWN 2 -#define FKEY_STEP 3 +#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 unsigned long frequency; extern byte WsprMSGCount; extern byte sMeterLevels[9]; -extern byte KeyValues[16][2]; //ADC value Ranges for Extend Key +extern byte KeyValues[16][3]; //Set : Start Value, End Value, Key Type, 16 Set (3 * 16 = 48) extern void printLine1(const char *c); extern void printLine2(const char *c); diff --git a/ubitx_20/ubitx_20.ino b/ubitx_20/ubitx_20.ino index 2b716e4..00d509d 100644 --- a/ubitx_20/ubitx_20.ino +++ b/ubitx_20/ubitx_20.ino @@ -185,31 +185,7 @@ byte sMeterLevels[9]; 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][2]; -/*= { - {1023, 1025}, //1 - {707, 711}, //5 - {570, 574}, //9 - {493, 500}, //13 - - {932, 936}, //2 - {860, 864}, //3 - {800, 805}, //4 - - {672, 676}, //6 - {642, 646}, //7 - {616, 620}, //8 - - {552, 556}, //10 - {535, 539}, //11 - {520, 524}, //12 - - {438, 442}, //14 - {403, 407}, //15 - {378, 382} //16 -}; -*/ +byte KeyValues[16][3]; byte isIFShift = 0; //1 = ifShift, 2 extend int ifShiftValue = 0; // @@ -426,7 +402,7 @@ void setFrequency(unsigned long f){ // Offset Frequency : 30Mhz and current Frequncy is 14.074 => 34.074Mhz moveFrequency = (f % 10000000); } - else if (sdrOption == 3) //Khzz move + else if (sdrOption == 3) //Khz move { //Offset Frequency + Khz, //Example : Offset Frequency : 30Mhz and current Frequncy is 7.080 => 30.080Mhz @@ -458,31 +434,6 @@ void setFrequency(unsigned long f){ } } - /* - if (cwMode == 0) - { - if (isUSB){ - si5351bx_setfreq(2, SECOND_OSC_USB - appliedCarrier + f); - si5351bx_setfreq(1, SECOND_OSC_USB); - } - else{ - si5351bx_setfreq(2, SECOND_OSC_LSB + appliedCarrier + f); - si5351bx_setfreq(1, SECOND_OSC_LSB); - } - } - else - { - if (cwMode == 1){ //CWL - si5351bx_setfreq(2, SECOND_OSC_LSB + appliedCarrier + f); - si5351bx_setfreq(1, SECOND_OSC_LSB); - } - else{ //CWU - si5351bx_setfreq(2, SECOND_OSC_USB - appliedCarrier + f); - si5351bx_setfreq(1, SECOND_OSC_USB); - } - } - */ - frequency = f; } @@ -634,6 +585,8 @@ void checkPTT(){ } #ifdef EXTEND_KEY_GROUP1 void checkButton(){ + char currentBandIndex = -1; + //only if the button is pressed int keyStatus = getBtnStatus(); if (keyStatus == -1) @@ -646,8 +599,69 @@ void checkButton(){ if (keyStatus == FKEY_PRESS) //Menu Key doMenu(); - else if (keyStatus <= FKEY_STEP) //EXTEND KEY GROUP #1 + 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; + } + /* if (keyStatus == FKEY_MODE) //Press Mode Key { if (cwMode == 1) @@ -668,10 +682,6 @@ void checkButton(){ cwMode = 1; } } - //else if (keyStatus == FKEY_BANDDOWN) //Press Mode Key - //{ - // setNextHamBandFreq(frequency, -1); //Prior Band - //} else if (keyStatus == FKEY_BANDUP || keyStatus == FKEY_BANDDOWN) //Press Mode Key { @@ -696,6 +706,28 @@ void checkButton(){ EEPROM.put(TUNING_STEP, tuneStepIndex); printLine2ClearAndUpdate(); } + + else if (keyStatus == FKEY_VFOCHANGE) + { + menuVfoToggle(1); //Vfo Toggle + } + else if (keyStatus == FKEY_SPLIT) + { + menuSplitOnOff(1); + } + else if (keyStatus == FKEY_TXOFF) + { + menuTxOnOff(1, 0x01); + } + else if (keyStatus == FKEY_SDRMODE) + { + menuSDROnOff(1); + } + else if (keyStatus == FKEY_RIT) + { + menuRitToggle(1); + } + */ FrequencyToVFO(1); SetCarrierFreq(); @@ -942,8 +974,9 @@ void initSettings(){ //KeyValues for (byte i = 0; i < 16; i++) { - KeyValues[i][0] = EEPROM.read(EXTENDED_KEY_RANGE + (i * 2)); - KeyValues[i][1] = EEPROM.read(EXTENDED_KEY_RANGE + (i * 2) + 1); + 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 } //User callsign information diff --git a/ubitx_20/ubitx_eemap.h b/ubitx_20/ubitx_eemap.h index a2cd8a7..2bdb32d 100644 --- a/ubitx_20/ubitx_eemap.h +++ b/ubitx_20/ubitx_eemap.h @@ -50,10 +50,13 @@ // 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 -//RESERVE 192 ~ 195 -#define EXTENDED_KEY_RANGE 196 //Extended Key, KEY RANGE (MODE, BAND+, BAND-, TUNE_STEP, NUM0~NUM9, POINT, ENTER + #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 diff --git a/ubitx_20/ubitx_menu.ino b/ubitx_20/ubitx_menu.ino index eac44b0..2af9627 100644 --- a/ubitx_20/ubitx_menu.ino +++ b/ubitx_20/ubitx_menu.ino @@ -403,15 +403,15 @@ void menuSplitOnOff(int btn){ else { if (splitOn == 1){ splitOn = 0; - //printLineF2(F("Split Off!")); - printLineF2(F("[OFF]")); + printLineF2(F("SPT Off")); + //printLineF2(F("[OFF]")); } else { splitOn = 1; if (ritOn == 1) ritOn = 0; - //printLineF2(F("Split On!")); - printLineF2(F("[ON]")); + printLineF2(F("SPT On")); + //printLineF2(F("[ON]")); } menuClearExit(500); @@ -430,11 +430,11 @@ void menuTxOnOff(int btn, byte optionType){ else { if ((isTxType & optionType) == 0){ isTxType |= optionType; - printLineF2(F("TX OFF!")); + printLineF2(F("TX OFF")); } else { isTxType &= ~(optionType); - printLineF2(F("TX ON!")); + printLineF2(F("TX ON")); } menuClearExit(500); @@ -453,7 +453,7 @@ void menuSDROnOff(int btn) else { if (sdrModeOn == 1){ sdrModeOn = 0; - printLineF2(F("[OFF]")); + printLineF2(F("SPK MODE")); } else { sdrModeOn = 1; @@ -464,7 +464,7 @@ void menuSDROnOff(int btn) if (splitOn == 1) splitOn = 0; - printLineF2(F("[ON]")); + printLineF2(F("SDR MODE")); } EEPROM.put(ENABLE_SDR, sdrModeOn); diff --git a/ubitx_20/ubitx_si5351.ino b/ubitx_20/ubitx_si5351.ino index 6587db6..2708b36 100644 --- a/ubitx_20/ubitx_si5351.ino +++ b/ubitx_20/ubitx_si5351.ino @@ -15,7 +15,6 @@ ************************************************************************************/ // ************* 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 @@ -127,7 +126,9 @@ void si5351_set_calibration(int32_t cal){ void SetCarrierFreq() { unsigned long appliedCarrier = ((cwMode == 0 ? usbCarrier : cwmCarrier) + (isIFShift && (inTx == 0) ? ifShiftValue : 0)); - si5351bx_setfreq(0, (sdrModeOn ? 0 : appliedCarrier)); + //si5351bx_setfreq(0, (sdrModeOn ? 0 : appliedCarrier)); + si5351bx_setfreq(0, ((sdrModeOn && (inTx == 0)) ? 0 : appliedCarrier)); //found bug by KG4GEK + /* if (cwMode == 0) diff --git a/ubitx_20/ubitx_ui.ino b/ubitx_20/ubitx_ui.ino index 57fa1a9..c303daa 100644 --- a/ubitx_20/ubitx_ui.ino +++ b/ubitx_20/ubitx_ui.ino @@ -185,10 +185,11 @@ int getBtnStatus(void){ else { readButtonValue = readButtonValue / 4; - + //return FKEY_VFOCHANGE; for (int i = 0; i < 16; i++) if (KeyValues[i][0] <= readButtonValue && KeyValues[i][1] >= readButtonValue) - return i; + return KeyValues[i][2]; + //return i; } return -1; From e961cd8ac9d437f489329fff56556a039c2c9065 Mon Sep 17 00:00:00 2001 From: phdlee Date: Tue, 24 Apr 2018 18:00:41 +0900 Subject: [PATCH 114/173] reduce compiller warning --- ubitx_20/ubitx.h | 7 ++++ ubitx_20/ubitx_eemap.h | 5 +++ ubitx_20/ubitx_lcd.h | 64 +++++++++++++++++++++++++++++++++ ubitx_20/ubitx_lcd_1602.ino | 43 +--------------------- ubitx_20/ubitx_lcd_1602Dual.ino | 43 +--------------------- ubitx_20/ubitx_lcd_2004.ino | 43 +--------------------- 6 files changed, 79 insertions(+), 126 deletions(-) create mode 100644 ubitx_20/ubitx_lcd.h diff --git a/ubitx_20/ubitx.h b/ubitx_20/ubitx.h index 734c4d0..d1ca476 100644 --- a/ubitx_20/ubitx.h +++ b/ubitx_20/ubitx.h @@ -14,6 +14,9 @@ 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. //============================================================================== @@ -172,3 +175,7 @@ extern void SendWSPRManage(void); extern char byteToChar(byte srcByte); extern void DisplayCallsign(byte callSignLength); extern void DisplayVersionInfo(const char* fwVersionInfo); + +#endif //end of if header define + + diff --git a/ubitx_20/ubitx_eemap.h b/ubitx_20/ubitx_eemap.h index 2bdb32d..200ba44 100644 --- a/ubitx_20/ubitx_eemap.h +++ b/ubitx_20/ubitx_eemap.h @@ -15,6 +15,9 @@ 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) @@ -125,3 +128,5 @@ #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_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 index 46e50a0..4315da4 100644 --- a/ubitx_20/ubitx_lcd_1602.ino +++ b/ubitx_20/ubitx_lcd_1602.ino @@ -21,48 +21,7 @@ **************************************************************************/ #include "ubitx.h" - -//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 +#include "ubitx_lcd.h" //======================================================================== //Begin of TinyLCD Library by KD8CEC diff --git a/ubitx_20/ubitx_lcd_1602Dual.ino b/ubitx_20/ubitx_lcd_1602Dual.ino index 8c698ac..e06a0cc 100644 --- a/ubitx_20/ubitx_lcd_1602Dual.ino +++ b/ubitx_20/ubitx_lcd_1602Dual.ino @@ -21,48 +21,7 @@ **************************************************************************/ #include "ubitx.h" - -//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 +#include "ubitx_lcd.h" //======================================================================== //Begin of I2CTinyLCD Library for Dual LCD by KD8CEC diff --git a/ubitx_20/ubitx_lcd_2004.ino b/ubitx_20/ubitx_lcd_2004.ino index f895a8b..d470db7 100644 --- a/ubitx_20/ubitx_lcd_2004.ino +++ b/ubitx_20/ubitx_lcd_2004.ino @@ -21,48 +21,7 @@ **************************************************************************/ #include "ubitx.h" - -//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 +#include "ubitx_lcd.h" //======================================================================== //Begin of TinyLCD Library by KD8CEC From 12984486a6cc4d3d663f4cd5b970c3fd1fe37b1c Mon Sep 17 00:00:00 2001 From: phdlee Date: Thu, 3 May 2018 16:20:09 +0900 Subject: [PATCH 115/173] Modfied SMeter and CAT --- VersionInfo.txt | 27 +++++++++++ ubitx_20/cat_libs.ino | 33 +++++++++++-- ubitx_20/ubitx.h | 7 ++- ubitx_20/ubitx_20.ino | 6 ++- ubitx_20/ubitx_lcd_1602.ino | 22 +++------ ubitx_20/ubitx_lcd_1602Dual.ino | 13 ++---- ubitx_20/ubitx_lcd_2004.ino | 82 ++------------------------------- ubitx_20/ubitx_ui.ino | 22 +++++++-- 8 files changed, 100 insertions(+), 112 deletions(-) create mode 100644 VersionInfo.txt diff --git a/VersionInfo.txt b/VersionInfo.txt new file mode 100644 index 0000000..7167345 --- /dev/null +++ b/VersionInfo.txt @@ -0,0 +1,27 @@ +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.074 Beta + +1.Delted Files. + +2.Added Files + +3.Modified Files + - ubitx_20.ino + - ubitx_ui.ino + - cat_libs.ino + - ubitx.h + - ubitx_eemap.h + + + \ No newline at end of file diff --git a/ubitx_20/cat_libs.ino b/ubitx_20/cat_libs.ino index 3ae0844..3e71855 100644 --- a/ubitx_20/cat_libs.ino +++ b/ubitx_20/cat_libs.ino @@ -31,8 +31,8 @@ along with this program. If not, see . **************************************************************************/ -#define printLineF1(x) (printLineF(1, x)) -#define printLineF2(x) (printLineF(0, x)) + +#include "ubitx.h" //for broken protocol #define CAT_RECEIVE_TIMEOUT 500 @@ -654,7 +654,7 @@ void SetIFSValue(void) //void CatRxStatus(byte fromType) void CatRxStatus(void) //for remove warning { - byte sMeterValue = 1; + byte sMeterValue = 0; /* http://www.ka7oei.com/ft817_meow.html @@ -667,6 +667,33 @@ void CatRxStatus(void) //for remove warning 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); } diff --git a/ubitx_20/ubitx.h b/ubitx_20/ubitx.h index d1ca476..09334e5 100644 --- a/ubitx_20/ubitx.h +++ b/ubitx_20/ubitx.h @@ -36,8 +36,8 @@ #define EXTEND_KEY_GROUP1 //MODE, BAND(-), BAND(+), STEP //#define EXTEND_KEY_GROUP2 //Numeric (0~9), Point(.), Enter //Not supported in Version 1.0x -#define ENABLE_FACTORYALIGN -//#define ENABLE_ADCMONITOR //Starting with Version 1.07, you can read ADC values directly from uBITX Manager. So this function is not necessary. +//#define ENABLE_FACTORYALIGN +#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 @@ -149,6 +149,9 @@ extern byte I2C_LCD_SECOND_ADDRESS; //only using Dual LCD Mode 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 void printLine1(const char *c); diff --git a/ubitx_20/ubitx_20.ino b/ubitx_20/ubitx_20.ino index 00d509d..764e52d 100644 --- a/ubitx_20/ubitx_20.ino +++ b/ubitx_20/ubitx_20.ino @@ -6,7 +6,7 @@ // 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.073") +#define FIRMWARE_VERSION_INFO F("+v1.074") #define FIRMWARE_VERSION_NUM 0x02 //1st Complete Project : 1 (Version 1.061), 2st Project : 2 /** @@ -182,6 +182,10 @@ 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 diff --git a/ubitx_20/ubitx_lcd_1602.ino b/ubitx_20/ubitx_lcd_1602.ino index 4315da4..a6c1c3a 100644 --- a/ubitx_20/ubitx_lcd_1602.ino +++ b/ubitx_20/ubitx_lcd_1602.ino @@ -708,19 +708,15 @@ void DisplayMeter(byte meterType, byte meterValue, char drawPosition) LCD_SetCursor(drawPosition, lineNumber); - //for (int i = 0; i <26; i++) //meter 5 + +db 1 = 6 LCD_Write(lcdMeter[0]); LCD_Write(lcdMeter[1]); + LCD_Write(lcdMeter[2]); } } -byte testValue = 0; char checkCount = 0; char checkCountSMeter = 0; -int currentSMeter = 0; -byte scaledSMeter = 0; - void idle_process() { //space for user graphic display @@ -740,24 +736,18 @@ void idle_process() } } - //EX for Meters - /* - DisplayMeter(0, testValue++, 7); - if (testValue > 30) - testValue = 0; - */ - //S-Meter Display if (((displayOption1 & 0x08) == 0x08 && (sdrModeOn == 0)) && (++checkCountSMeter > SMeterLatency)) { int newSMeter; //VK2ETA S-Meter from MAX9814 TC pin / divide 4 by KD8CEC for reduce EEPromSize - newSMeter = analogRead(ANALOG_SMETER); + 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 > 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]) { @@ -766,7 +756,7 @@ void idle_process() } } - DisplayMeter(0, scaledSMeter, 14); + DisplayMeter(0, scaledSMeter, 13); checkCountSMeter = 0; //Reset Latency time } //end of S-Meter diff --git a/ubitx_20/ubitx_lcd_1602Dual.ino b/ubitx_20/ubitx_lcd_1602Dual.ino index e06a0cc..478f031 100644 --- a/ubitx_20/ubitx_lcd_1602Dual.ino +++ b/ubitx_20/ubitx_lcd_1602Dual.ino @@ -583,17 +583,13 @@ void DisplayMeter(byte meterType, byte meterValue, char drawPosition) LCD_Write('S'); LCD_Write(':'); - for (int i = 0; i < 6; i++) //meter 5 + +db 1 = 6 + for (int i = 0; i < 7; i++) LCD_Write(lcdMeter[i]); } } -byte testValue = 0; char checkCount = 0; - -int currentSMeter = 0; -byte scaledSMeter = 0; char checkCountSMeter = 0; char beforeKeyType = -1; @@ -676,12 +672,13 @@ void idle_process() int newSMeter; displaySDRON = 0; - //VK2ETA S-Meter from MAX9814 TC pin - newSMeter = analogRead(ANALOG_SMETER); + //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); - currentSMeter = (currentSMeter * 3 + newSMeter * 7) / 10; //remarked becaused of have already Latency time + //currentSMeter = (currentSMeter * 3 + newSMeter * 7) / 10; //remarked becaused of have already Latency time + currentSMeter = newSMeter; scaledSMeter = 0; for (byte s = 8; s >= 1; s--) { diff --git a/ubitx_20/ubitx_lcd_2004.ino b/ubitx_20/ubitx_lcd_2004.ino index d470db7..5a0aa0d 100644 --- a/ubitx_20/ubitx_lcd_2004.ino +++ b/ubitx_20/ubitx_lcd_2004.ino @@ -642,90 +642,16 @@ 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, 2); LCD_Write('S'); LCD_Write(':'); - for (int i = 0; i < 6; i++) //meter 5 + +db 1 = 6 + for (int i = 0; i < 7; i++) //meter 5 + +db 1 = 6 LCD_Write(lcdMeter[i]); } } - -//meterType : 0 = S.Meter, 1 = Forward Power Meter, 2 = SWR Meter -void DisplayMeter(byte meterType, int meterValue, char drawPosition) -{ - -#ifdef OPTION_SKINNYBARS //We want skinny meter bars with more text/numbers - memcpy(&(line2Buffer[drawPosition]), " ", 8); //Blank that section of 8 characters first - if (meterType == 0) { //SWR meter - drawMeter(meterValue); //Only 2 characters - line2Buffer[drawPosition] = 'S'; - byte sValue = round((float)meterValue * 1.5); //6 bars available only to show 9 S values - sValue = sValue > 9 ? 9 : sValue; //Max S9 - line2Buffer[drawPosition + 1] = '0' + sValue; //0 to 9 - memcpy(&(line2Buffer[drawPosition + 2]), lcdMeter, 2); //Copy the S-Meter bars - //Add the +10, +20, etc... - if (meterValue > 6) { - //We are over S9 - line2Buffer[drawPosition + 4] = '+'; - line2Buffer[drawPosition + 5] = '0' + meterValue - 6; //1,2,3 etc... - line2Buffer[drawPosition + 6] = '0'; - } - } else if (meterType == 1) { //Forward Power - drawMeter(round((float)meterValue / 40)); //4 watts per bar - //meterValue contains power value x 10 (one decimal point) - line2Buffer[drawPosition] = 'P'; - meterValue = meterValue > 999 ? 999 : meterValue; //Limit to 99.9 watts!!!! - //Remove decimal value and divide by 10 - meterValue = round((float)meterValue / 10); - if (meterValue < 10) { - line2Buffer[drawPosition + 1] = ' '; - line2Buffer[drawPosition + 2] = '0' + meterValue; //0 to 9 - } else { - line2Buffer[drawPosition + 1] = '0' + meterValue / 10; - line2Buffer[drawPosition + 2] = '0' + (meterValue - ((meterValue / 10) * 10)); - } - line2Buffer[drawPosition + 3] = 'W'; - memcpy(&(line2Buffer[drawPosition + 4]), lcdMeter, 2); //Copy the S-Meter bars - } else { //SWR - drawMeter((int)(((float)meterValue - 21) / 100)); //no bar = < 1.2, then 1 bar = 1.2 to 2.2, 2 bars = 2.2 to 3.2, etc... - //meterValue contains SWR x 100 (two decimal point) - memcpy(&(line2Buffer[drawPosition]), "SWR", 3); - meterValue = round((float)meterValue / 10); //We now have swr x 10 (1 decimal point) - if (meterValue < 100) { //10 to 99, no decimal point - //Draw the decimal value - line2Buffer[drawPosition + 3] = '0' + meterValue / 10; - line2Buffer[drawPosition + 4] = '.'; - line2Buffer[drawPosition + 5] = '0' + (meterValue - ((meterValue / 10) * 10)); - } else { - memcpy(&(line2Buffer[drawPosition + 3]), "10+", 3); //over 10 - } - memcpy(&(line2Buffer[drawPosition + 6]), lcdMeter, 2); //Copy the S-Meter bars - } -#else //We want fat bars, easy to read, with less text/numbers - //Serial.print("In displaymeter, meterValue: "); Serial.println(meterValue); - drawMeter(meterValue); - //Always line 2 - char sym = 'S'; - if (meterType == 1) sym = 'P'; - else if (meterType == 2) sym = 'R'; //For SWR - line2Buffer[drawPosition] = sym; - memcpy(&(line2Buffer[drawPosition + 1]), lcdMeter, 7); -#endif //OPTION_SKINNYBARS - -} - - -byte testValue = 0; char checkCount = 0; - -int currentSMeter = 0; -//int sMeterLevels[] = {0, 5, 17, 41, 74, 140, 255, 365, 470}; -byte scaledSMeter = 0; char checkCountSMeter = 0; //execute interval : 0.25sec @@ -769,10 +695,12 @@ void idle_process() int newSMeter; //VK2ETA S-Meter from MAX9814 TC pin - newSMeter = analogRead(ANALOG_SMETER); + newSMeter = analogRead(ANALOG_SMETER) / 4; //Faster attack, Slower release - currentSMeter = (newSMeter > currentSMeter ? ((currentSMeter * 3 + newSMeter * 7) + 5) / 10 : ((currentSMeter * 7 + newSMeter * 3) + 5) / 10); + //currentSMeter = (newSMeter > currentSMeter ? ((currentSMeter * 3 + newSMeter * 7) + 5) / 10 : ((currentSMeter * 7 + newSMeter * 3) + 5) / 10); + //currentSMeter = ((currentSMeter * 7 + newSMeter * 3) + 5) / 10; + currentSMeter = newSMeter; scaledSMeter = 0; for (byte s = 8; s >= 1; s--) { diff --git a/ubitx_20/ubitx_ui.ino b/ubitx_20/ubitx_ui.ino index c303daa..cf52eb0 100644 --- a/ubitx_20/ubitx_ui.ino +++ b/ubitx_20/ubitx_ui.ino @@ -95,7 +95,7 @@ void initMeter(){ for (i = 0; i < 8; i++) tmpbytes[i] = pgm_read_byte(p_metes_bitmap + i + 48); - LCD_CreateChar(6, tmpbytes); + LCD_CreateChar(7, tmpbytes); } @@ -128,13 +128,23 @@ 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 - } + //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++) { @@ -143,11 +153,13 @@ void drawMeter(int needle) else lcdMeter[i] = 0x20; //blank } + if (needle > 7) { lcdMeter[6] = byte(7); //Custom character "++" } else if (needle > 6) { - lcdMeter[6] = 0x2B; //"+" + lcdMeter[6] = '+'; //"+" } else lcdMeter[6] = 0x20; + #endif //OPTION_FATBARS } From 51f690ef85fd0bb40a5a0ad5c54efd7d3befade9 Mon Sep 17 00:00:00 2001 From: phdlee Date: Thu, 3 May 2018 17:57:06 +0900 Subject: [PATCH 116/173] change frequency display in WSPR Menu --- ubitx_20/ubitx_wspr.ino | 37 +++++++++++++++++++------------------ 1 file changed, 19 insertions(+), 18 deletions(-) diff --git a/ubitx_20/ubitx_wspr.ino b/ubitx_20/ubitx_wspr.ino index eb85147..5765aca 100644 --- a/ubitx_20/ubitx_wspr.ino +++ b/ubitx_20/ubitx_wspr.ino @@ -113,22 +113,9 @@ void SendWSPRManage() EEPROM.get(bandBuffIndex, WsprTXFreq); EEPROM.get(bandBuffIndex + 4, WsprMultiChan); - /* - //3, 4, 5, 6, 7 - Wspr_Reg1[3] = EEPROM.read(bandBuffIndex + 6); - Wspr_Reg1[4] = EEPROM.read(bandBuffIndex + 7); - Wspr_Reg1[5] = EEPROM.read(bandBuffIndex + 8); - Wspr_Reg1[6] = EEPROM.read(bandBuffIndex + 9); - Wspr_Reg1[7] = EEPROM.read(bandBuffIndex + 10); - */ for (loopIndex = 3; loopIndex < 8; loopIndex++) Wspr_Reg1[loopIndex] = EEPROM.read(bandBuffIndex + loopIndex + 3); - /* - Wspr_Reg2[2] = EEPROM.read(bandBuffIndex + 11); - Wspr_Reg2[3] = EEPROM.read(bandBuffIndex + 12); - Wspr_Reg2[4] = EEPROM.read(bandBuffIndex + 13); - */ //2, 3, 4 for (loopIndex = 2; loopIndex < 5; loopIndex++) Wspr_Reg2[loopIndex] = EEPROM.read(bandBuffIndex + loopIndex + 9); @@ -136,18 +123,32 @@ void SendWSPRManage() TX_MSNB_P2 = ((unsigned long)Wspr_Reg1[5] & 0x0F) << 16 | ((unsigned long)Wspr_Reg1[6]) << 8 | Wspr_Reg1[7]; } - ltoa(WsprTXFreq, b, DEC); if (digitalRead(PTT) == 0) - strcpy(c, "SEND:"); + strcpy(c, "SEND: "); else - strcpy(c, "PTT->"); + 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] = ' '; + } - strcat(c, b); printLine1(c); if (digitalRead(PTT) == 0) { - //printLineF1(F("Transmitting")); //SEND WSPR //If you need to consider the Rit and Sprite modes, uncomment them below. //remark = To reduce the size of the program From 1d28f3e7e9dca8d608b4683808bb14747de444b2 Mon Sep 17 00:00:00 2001 From: phdlee Date: Thu, 3 May 2018 21:23:10 +0900 Subject: [PATCH 117/173] update versioninfo.txt --- VersionInfo.txt | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/VersionInfo.txt b/VersionInfo.txt index 7167345..581a34a 100644 --- a/VersionInfo.txt +++ b/VersionInfo.txt @@ -22,6 +22,9 @@ Files modified in Version1.074 Beta - 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 From 70fc6aeba87129db4f1465b7946a228a305eccd1 Mon Sep 17 00:00:00 2001 From: phdlee Date: Mon, 7 May 2018 13:56:46 +0900 Subject: [PATCH 118/173] Add Factory Recovery function --- ubitx_20/ubitx.h | 5 +++-- ubitx_20/ubitx_20.ino | 50 +++++++++++++++++++++++++++++++++++++++-- ubitx_20/ubitx_eemap.h | 4 +++- ubitx_20/ubitx_menu.ino | 3 +-- 4 files changed, 55 insertions(+), 7 deletions(-) diff --git a/ubitx_20/ubitx.h b/ubitx_20/ubitx.h index 09334e5..09153f1 100644 --- a/ubitx_20/ubitx.h +++ b/ubitx_20/ubitx.h @@ -24,10 +24,10 @@ //============================================================================== //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 +//#define UBITX_DISPLAY_LCD1602P //LCD mounted on unmodified uBITX //#define UBITX_DISPLAY_LCD1602I //I2C type 16 x 02 LCD //#define UBITX_DISPLAY_LCD1602I_DUAL -//#define UBITX_DISPLAY_LCD2004P //24 x 04 LCD (Parallel) +#define UBITX_DISPLAY_LCD2004P //24 x 04 LCD (Parallel) //#define UBITX_DISPLAY_LCD2004I //I2C type 24 x 04 LCD #define I2C_LCD_MASTER_ADDRESS_DEFAULT 0x3F //0x27 //DEFAULT, if Set I2C Address by uBITX Manager, read from EEProm @@ -37,6 +37,7 @@ //#define EXTEND_KEY_GROUP2 //Numeric (0~9), Point(.), Enter //Not supported in Version 1.0x //#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 diff --git a/ubitx_20/ubitx_20.ino b/ubitx_20/ubitx_20.ino index 764e52d..1199d97 100644 --- a/ubitx_20/ubitx_20.ino +++ b/ubitx_20/ubitx_20.ino @@ -6,8 +6,8 @@ // 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.074") -#define FIRMWARE_VERSION_NUM 0x02 //1st Complete Project : 1 (Version 1.061), 2st Project : 2 +#define FIRMWARE_VERSION_INFO F("+v1.075") +#define FIRMWARE_VERSION_NUM 0x03 //1st Complete Project : 1 (Version 1.061), 2st Project : 2 /** Cat Suppoort uBITX CEC Version @@ -941,6 +941,15 @@ void initSettings(){ if (EEPROM.read(VERSION_ADDRESS) != FIRMWARE_VERSION_NUM) EEPROM.write(VERSION_ADDRESS, FIRMWARE_VERSION_NUM); + //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 @@ -1216,6 +1225,37 @@ void initPorts(){ digitalWrite(CW_KEY, 0); } +//Recovery Factory Setting Values +void factory_Recovery() +{ + if (EEPROM.read(FACTORY_BACKUP_YN) != 0x13) + return; + + printLineF2(F("FactoryRecovery?")); + 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() { /* @@ -1266,6 +1306,11 @@ void setup() initPorts(); +#ifdef FACTORY_RECOVERY_BOOTUP + if (btnDown()) + factory_Recovery(); +#endif + byteToMode(vfoA_mode, 0); initOscillators(); @@ -1278,6 +1323,7 @@ void setup() if (btnDown()) factory_alignment(); #endif + } //Auto save Frequency and Mode with Protected eeprom life by KD8CEC diff --git a/ubitx_20/ubitx_eemap.h b/ubitx_20/ubitx_eemap.h index 200ba44..e799187 100644 --- a/ubitx_20/ubitx_eemap.h +++ b/ubitx_20/ubitx_eemap.h @@ -44,7 +44,9 @@ // (Enabled if the EEProm address is insufficient) // Address : 64 ~ 100 //============================================================================== -#define RESERVE_FOR_FACTORY2 64 +#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 diff --git a/ubitx_20/ubitx_menu.ino b/ubitx_20/ubitx_menu.ino index 2af9627..79f4ad7 100644 --- a/ubitx_20/ubitx_menu.ino +++ b/ubitx_20/ubitx_menu.ino @@ -1106,7 +1106,6 @@ void menuSetupKeyType(int btn){ } */ - printLineF2(F("CW Key Type set!")); cwKeyType = selectedKeyType; EEPROM.put(CW_KEY_TYPE, cwKeyType); @@ -1127,7 +1126,7 @@ void menuSetupKeyType(int btn){ } //===================================================== -//END OF STANDARD Tune Setup for reduce Program Memory +//END OF STANDARD Set by Knob for reduce Program Memory //===================================================== From 76d5c362d05911ca13013aec991d4d36cad2eb48 Mon Sep 17 00:00:00 2001 From: phdlee Date: Mon, 7 May 2018 14:08:22 +0900 Subject: [PATCH 119/173] complete test for Factory Recovery --- ubitx_20/ubitx.h | 4 ++-- ubitx_20/ubitx_20.ino | 7 +++++-- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/ubitx_20/ubitx.h b/ubitx_20/ubitx.h index 09153f1..f60095d 100644 --- a/ubitx_20/ubitx.h +++ b/ubitx_20/ubitx.h @@ -24,10 +24,10 @@ //============================================================================== //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 +#define UBITX_DISPLAY_LCD1602P //LCD mounted on unmodified uBITX //#define UBITX_DISPLAY_LCD1602I //I2C type 16 x 02 LCD //#define UBITX_DISPLAY_LCD1602I_DUAL -#define UBITX_DISPLAY_LCD2004P //24 x 04 LCD (Parallel) +//#define UBITX_DISPLAY_LCD2004P //24 x 04 LCD (Parallel) //#define UBITX_DISPLAY_LCD2004I //I2C type 24 x 04 LCD #define I2C_LCD_MASTER_ADDRESS_DEFAULT 0x3F //0x27 //DEFAULT, if Set I2C Address by uBITX Manager, read from EEProm diff --git a/ubitx_20/ubitx_20.ino b/ubitx_20/ubitx_20.ino index 1199d97..487faf4 100644 --- a/ubitx_20/ubitx_20.ino +++ b/ubitx_20/ubitx_20.ino @@ -1230,8 +1230,11 @@ void factory_Recovery() { if (EEPROM.read(FACTORY_BACKUP_YN) != 0x13) return; - - printLineF2(F("FactoryRecovery?")); + + if (digitalRead(PTT) == 0) //Do not proceed if PTT is pressed to prevent malfunction. + return; + + printLineF2(F("Factory Recovery")); delay(2000); if (!btnDown()) return; From 6a2369bc27872b96c4b74c05e237711a4d33783e Mon Sep 17 00:00:00 2001 From: phdlee Date: Wed, 9 May 2018 16:53:40 +0900 Subject: [PATCH 120/173] Fixed Band Select Bug --- ubitx_20/ubitx_20.ino | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ubitx_20/ubitx_20.ino b/ubitx_20/ubitx_20.ino index 487faf4..216d34c 100644 --- a/ubitx_20/ubitx_20.ino +++ b/ubitx_20/ubitx_20.ino @@ -254,8 +254,8 @@ void setNextHamBandFreq(unsigned long f, char moveDirection) if ((resultFreq / 1000) < hamBandRange[(unsigned char)findedIndex][0] || (resultFreq / 1000) > hamBandRange[(unsigned char)findedIndex][1]) resultFreq = (unsigned long)(hamBandRange[(unsigned char)findedIndex][0]) * 1000; - setFrequency(resultFreq); byteToMode(loadMode, 1); + setFrequency(resultFreq); } void saveBandFreqByIndex(unsigned long f, unsigned long mode, char bandIndex) { From 65d21aba77fab333c15d08286497aafc4c8c0a9a Mon Sep 17 00:00:00 2001 From: phdlee Date: Wed, 9 May 2018 16:53:49 +0900 Subject: [PATCH 121/173] Fixed Band Select Bug --- VersionInfo.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/VersionInfo.txt b/VersionInfo.txt index 581a34a..4ab4930 100644 --- a/VersionInfo.txt +++ b/VersionInfo.txt @@ -10,7 +10,7 @@ I am getting a lot of hints from the group. Ian KD8CEC kd8cec@gmail.com ================================================================== -Files modified in Version1.074 Beta +Files modified in Version1.075 Beta 1.Delted Files. @@ -25,6 +25,6 @@ Files modified in Version1.074 Beta - ubitx_lcd_1602.ino - ubitx_lcd_1602Dual.ino - ubitx_lcd_2004.ino - -ubitx_wspr.ino + - ubitx_wspr.ino \ No newline at end of file From 83dc1de18ef0f0463a79662a8c3a57f408f7e5d7 Mon Sep 17 00:00:00 2001 From: phdlee Date: Tue, 22 May 2018 11:37:10 +0900 Subject: [PATCH 122/173] fixed mode change --- ubitx_20/ubitx_20.ino | 2 +- ubitx_20/ubitx_menu.ino | 6 ++++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/ubitx_20/ubitx_20.ino b/ubitx_20/ubitx_20.ino index 216d34c..025fd9f 100644 --- a/ubitx_20/ubitx_20.ino +++ b/ubitx_20/ubitx_20.ino @@ -6,7 +6,7 @@ // 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.075") +#define FIRMWARE_VERSION_INFO F("+v1.080") #define FIRMWARE_VERSION_NUM 0x03 //1st Complete Project : 1 (Version 1.061), 2st Project : 2 /** diff --git a/ubitx_20/ubitx_menu.ino b/ubitx_20/ubitx_menu.ino index 79f4ad7..42bf7c0 100644 --- a/ubitx_20/ubitx_menu.ino +++ b/ubitx_20/ubitx_menu.ino @@ -271,8 +271,10 @@ void menuCHMemory(int btn, byte isMemoryToVfo){ if (isMemoryToVfo == 1) { if (resultFreq > 3000 && resultFreq < 60000000) - setFrequency(resultFreq); - byteToMode(loadMode, 1); + { + byteToMode(loadMode, 1); + setFrequency(resultFreq); + } } else { From 8a6e01e2898e3b2b4627f7aa255fb39c131eaedf Mon Sep 17 00:00:00 2001 From: phdlee Date: Wed, 23 May 2018 15:07:37 +0900 Subject: [PATCH 123/173] improve external switch check routine --- ubitx_20/ubitx_ui.ino | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ubitx_20/ubitx_ui.ino b/ubitx_20/ubitx_ui.ino index cf52eb0..aa3bae9 100644 --- a/ubitx_20/ubitx_ui.ino +++ b/ubitx_20/ubitx_ui.ino @@ -199,7 +199,7 @@ int getBtnStatus(void){ readButtonValue = readButtonValue / 4; //return FKEY_VFOCHANGE; for (int i = 0; i < 16; i++) - if (KeyValues[i][0] <= readButtonValue && KeyValues[i][1] >= readButtonValue) + if (KeyValues[i][2] != 0 && KeyValues[i][0] <= readButtonValue && KeyValues[i][1] >= readButtonValue) return KeyValues[i][2]; //return i; } From b375b7e9e4d247cda300dbaf23fc287c10aa1f38 Mon Sep 17 00:00:00 2001 From: phdlee Date: Wed, 23 May 2018 15:20:10 +0900 Subject: [PATCH 124/173] modified some comments --- ubitx_20/ubitx_lcd_1602.ino | 5 +---- ubitx_20/ubitx_lcd_1602Dual.ino | 9 +++------ ubitx_20/ubitx_lcd_2004.ino | 9 +++------ 3 files changed, 7 insertions(+), 16 deletions(-) diff --git a/ubitx_20/ubitx_lcd_1602.ino b/ubitx_20/ubitx_lcd_1602.ino index a6c1c3a..67ca38c 100644 --- a/ubitx_20/ubitx_lcd_1602.ino +++ b/ubitx_20/ubitx_lcd_1602.ino @@ -1,10 +1,7 @@ /************************************************************************* KD8CEC's uBITX Display Routine for LCD1602 Parrel 1.This is the display code for the default LCD mounted in uBITX. - 2.Display related functions of uBITX. Some functions moved from uBITX_Ui. - 3.uBITX Idle time Processing - Functions that run at times that do not affect TX, CW, and CAT - It is called in 1/10 time unit. + 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 diff --git a/ubitx_20/ubitx_lcd_1602Dual.ino b/ubitx_20/ubitx_lcd_1602Dual.ino index 478f031..211b3ef 100644 --- a/ubitx_20/ubitx_lcd_1602Dual.ino +++ b/ubitx_20/ubitx_lcd_1602Dual.ino @@ -1,10 +1,7 @@ /************************************************************************* - KD8CEC's uBITX Display Routine for LCD1602 Dual LCD by KD8CEC - 1.This is the display code for the default LCD mounted in uBITX. - 2.Display related functions of uBITX. Some functions moved from uBITX_Ui. - 3.uBITX Idle time Processing - Functions that run at times that do not affect TX, CW, and CAT - It is called in 1/10 time unit. + KD8CEC's uBITX Display Routine for LCD1602 Dual LCD + 1.This is the display code for the 16x02 Dual LCD + 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 diff --git a/ubitx_20/ubitx_lcd_2004.ino b/ubitx_20/ubitx_lcd_2004.ino index 5a0aa0d..2a5a2da 100644 --- a/ubitx_20/ubitx_lcd_2004.ino +++ b/ubitx_20/ubitx_lcd_2004.ino @@ -1,10 +1,7 @@ /************************************************************************* - KD8CEC's uBITX Display Routine for LCD2004 Parrel - 1.This is the display code for the default LCD mounted in uBITX. - 2.Display related functions of uBITX. Some functions moved from uBITX_Ui. - 3.uBITX Idle time Processing - Functions that run at times that do not affect TX, CW, and CAT - It is called in 1/10 time unit. + KD8CEC's uBITX Display Routine for LCD2004 Parrel & I2C + 1.This is the display code for the 20x04 LCD + 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 From 67cdd14945075035de03b8ae91d9fee8af7e8f95 Mon Sep 17 00:00:00 2001 From: phdlee Date: Wed, 23 May 2018 15:28:00 +0900 Subject: [PATCH 125/173] modified comments --- ubitx_20/ubitx.h | 29 ++++++++++++++--------------- 1 file changed, 14 insertions(+), 15 deletions(-) diff --git a/ubitx_20/ubitx.h b/ubitx_20/ubitx.h index f60095d..18d7be4 100644 --- a/ubitx_20/ubitx.h +++ b/ubitx_20/ubitx.h @@ -24,26 +24,26 @@ //============================================================================== //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 -//#define UBITX_DISPLAY_LCD1602I //I2C type 16 x 02 LCD -//#define UBITX_DISPLAY_LCD1602I_DUAL -//#define UBITX_DISPLAY_LCD2004P //24 x 04 LCD (Parallel) +#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 I2C_LCD_MASTER_ADDRESS_DEFAULT 0x3F //0x27 //DEFAULT, if Set I2C Address by uBITX Manager, read from EEProm #define I2C_LCD_SECOND_ADDRESS_DEFAULT 0x27 //0x27 //only using Dual LCD Mode -#define EXTEND_KEY_GROUP1 //MODE, BAND(-), BAND(+), STEP -//#define EXTEND_KEY_GROUP2 //Numeric (0~9), Point(.), Enter //Not supported in Version 1.0x +#define EXTEND_KEY_GROUP1 //MODE, BAND(-), BAND(+), STEP +//#define EXTEND_KEY_GROUP2 //Numeric (0~9), Point(.), Enter //Not supported in Version 1.0x //#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. +#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 +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 +#define SMeterLatency 3 //1 is 0.25 sec #ifdef UBITX_DISPLAY_LCD1602I #define USE_I2C_LCD @@ -90,7 +90,6 @@ extern byte I2C_LCD_SECOND_ADDRESS; //only using Dual LCD Mode #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 : @@ -150,10 +149,10 @@ extern byte I2C_LCD_SECOND_ADDRESS; //only using Dual LCD Mode 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 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 KeyValues[16][3]; //Set : Start Value, End Value, Key Type, 16 Set (3 * 16 = 48) extern void printLine1(const char *c); extern void printLine2(const char *c); From 7c8088f7537eff831def298bbd95c78f5920740c Mon Sep 17 00:00:00 2001 From: phdlee Date: Wed, 23 May 2018 15:58:50 +0900 Subject: [PATCH 126/173] add comment --- VersionInfo.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/VersionInfo.txt b/VersionInfo.txt index 4ab4930..d3f0eae 100644 --- a/VersionInfo.txt +++ b/VersionInfo.txt @@ -10,7 +10,7 @@ I am getting a lot of hints from the group. Ian KD8CEC kd8cec@gmail.com ================================================================== -Files modified in Version1.075 Beta +Files modified in Version1.08 Beta 1.Delted Files. From 2e8c97f19b0e6611445e08f3df04d8ba49db891c Mon Sep 17 00:00:00 2001 From: phdlee Date: Wed, 23 May 2018 18:32:01 +0900 Subject: [PATCH 127/173] Update README.md --- README.md | 40 ++++++++++++++++++++++------------------ 1 file changed, 22 insertions(+), 18 deletions(-) diff --git a/README.md b/README.md index 5fc01db..1eab8f4 100644 --- a/README.md +++ b/README.md @@ -1,21 +1,12 @@ -#IMPORTANT INFORMATION ----------------------------------------------------------------------------- -- Now Release Version 1.061 on my blog (http://www.hamskey.com) -- You can download and compiled hex file and uBITX Manager application on my blog (http://www.hamskey.com) - #NOTICE ---------------------------------------------------------------------------- -I received uBITX a month ago and found that many features are required, and began coding with the idea of implementing minimal functionality as a general hf transceiver rather than an experimental device. - -Most of the basic functions of the HF transceiver I thought were implemented. -The minimum basic specification for uBITX to operate as a radio, I think it is finished. -So I will release the 0.27 version and if I do not see the bug anymore, I will try to change the version name to 1.0. -Now uBITX is an HF radio and will be able to join you in your happy hams life. -Based on this source, you can use it by adding functions. +- Now Release Version 1.08 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 -DE KD8CEC +Ian KD8CEC kd8cec@gmail.com #uBITX @@ -26,15 +17,28 @@ The copyright information of the original is below. KD8CEC ---------------------------------------------------------------------------- Prepared or finished tasks for the next version - - Reduce Program size - - uBITX with RTL-SDR + - Nextion LCD + - Add TTS module + - Remote control on another MCU - Direct control for Student ---------------------------------------------------------------------------- ## REVISION RECORD -1.07 (Working...) - - Please do not download it yet. The code will continue to change for the time being. - - BetaVersion for Reduce program size +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 + - 1.071, 1.073, 1.075 is Beta + - Features implemented in the beta version have been applied to Version 1.08 above. 1.061 - Added WSPR From c6b020fa70b1c8dfea8930b3406b259a613ae60a Mon Sep 17 00:00:00 2001 From: phdlee Date: Wed, 23 May 2018 18:32:55 +0900 Subject: [PATCH 128/173] Update README.md --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 1eab8f4..38fc97b 100644 --- a/README.md +++ b/README.md @@ -36,8 +36,8 @@ Prepared or finished tasks for the next version - 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 - - 1.071, 1.073, 1.075 is Beta +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 From e81413fa0219a10ad19dd18fe6a0981b552a3685 Mon Sep 17 00:00:00 2001 From: phdlee Date: Sat, 9 Jun 2018 17:13:26 +0900 Subject: [PATCH 129/173] Support Nextion LCD --- ubitx_20/cw_autokey.ino | 21 +- ubitx_20/softserial_tiny.cpp | 354 ++++++++++ ubitx_20/ubitx.h | 15 +- ubitx_20/ubitx_20.ino | 25 +- ubitx_20/ubitx_lcd_nextion.ino | 1108 ++++++++++++++++++++++++++++++++ ubitx_20/ubitx_menu.ino | 8 +- ubitx_20/ubitx_wspr.ino | 3 +- 7 files changed, 1516 insertions(+), 18 deletions(-) create mode 100644 ubitx_20/softserial_tiny.cpp create mode 100644 ubitx_20/ubitx_lcd_nextion.ino diff --git a/ubitx_20/cw_autokey.ino b/ubitx_20/cw_autokey.ino index f5fcbde..58cda6b 100644 --- a/ubitx_20/cw_autokey.ino +++ b/ubitx_20/cw_autokey.ino @@ -296,18 +296,19 @@ void controlAutoCW(){ { displayScrolStep = 0; } - + +#ifdef USE_SW_SERIAL + //Not need Scroll + //Display_AutoKeyTextIndex(selectedCWTextIndex); + SendCommand1Num('w', selectedCWTextIndex); //Index + SendEEPromData('a', cwStartIndex + CW_DATA_OFSTADJ, cwEndIndex + CW_DATA_OFSTADJ, 0) ; //Data + SendCommand1Num('y', 1); //Send YN + isNeedScroll = 0; +#else printLineFromEEPRom(0, 2, cwStartIndex + displayScrolStep + CW_DATA_OFSTADJ, cwEndIndex + CW_DATA_OFSTADJ, 0); - - //byte diplayAutoCWLine = 0; - //if ((displayOption1 & 0x01) == 0x01) - // diplayAutoCWLine = 1; - - Display_AutoKeyTextIndex(selectedCWTextIndex); - //lcd.setCursor(0, diplayAutoCWLine); - //lcd.write(byteToChar(selectedCWTextIndex)); - //lcd.write(':'); isNeedScroll = (cwEndIndex - cwStartIndex) > 14 ? 1 : 0; + Display_AutoKeyTextIndex(selectedCWTextIndex); +#endif scrollDispayTime = millis() + scrollSpeed; beforeCWTextIndex = selectedCWTextIndex; } diff --git a/ubitx_20/softserial_tiny.cpp b/ubitx_20/softserial_tiny.cpp new file mode 100644 index 0000000..b0d1216 --- /dev/null +++ b/ubitx_20/softserial_tiny.cpp @@ -0,0 +1,354 @@ +/* +KD8CEC, Ian Lee +----------------------------------------------------------------------- +It is a library rewritten in C format based on SoftwareSerial.c. +I tried to use as much as possible without modifying the SoftwareSerial. +But eventually I had to modify the code. + +I rewrote it in C for the following reasons. + - Problems occurred when increasing Program Size and Program Memory + - We had to reduce the program size. + Of course, Software Serial is limited to one. + - reduce the steps for transmitting and receiving + +useage +extern void SWSerial_Begin(long speedBaud); +extern void SWSerial_Write(uint8_t b); +extern int SWSerial_Available(void); +extern int SWSerial_Read(void); +extern void SWSerial_Print(uint8_t *b); + +If you use Softwreserial library instead of this library, you can modify the code as shown below. +I kept the function name of SoftwareSerial so you only need to modify a few lines of code. + +define top of source code +#include +SoftwareSerial sSerial(10, 11); // RX, TX + +replace source code +SWSerial_Begin to sSerial.begin +SWSerial_Write to sSerial.write +SWSerial_Available to sSerial.available +SWSerial_Read to sSerial.read + +----------------------------------------------------------------------- +License +All licenses for the source code are subject to the license of the original source SoftwareSerial Library. +However, if you use or modify this code, please keep the all comments in this source code. +KD8CEC +----------------------------------------------------------------------- +License from SoftwareSerial +----------------------------------------------------------------------- +SoftwareSerial.cpp (formerly NewSoftSerial.cpp) - +Multi-instance software serial library for Arduino/Wiring +-- Interrupt-driven receive and other improvements by ladyada + (http://ladyada.net) +-- Tuning, circular buffer, derivation from class Print/Stream, + multi-instance support, porting to 8MHz processors, + various optimizations, PROGMEM delay tables, inverse logic and + direct port writing by Mikal Hart (http://www.arduiniana.org) +-- Pin change interrupt macros by Paul Stoffregen (http://www.pjrc.com) +-- 20MHz processor support by Garrett Mace (http://www.macetech.com) +-- ATmega1280/2560 support by Brett Hagman (http://www.roguerobotics.com/) + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library 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 +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +The latest version of this library can always be found at +http://arduiniana.org. + */ +#include + +//================================================================ +//Public Variable +//================================================================ +#define TX_PIN 9 +#define RX_PIN 8 +#define _SS_MAX_RX_BUFF 20 // RX buffer size +#define PRINT_MAX_LENGTH 30 + +//================================================================ +//Internal Variable from SoftwareSerial.c and SoftwareSerial.h +//================================================================ +//variable from softwareserial.c and softwareserial.h +static uint8_t swr_receive_buffer[_SS_MAX_RX_BUFF]; + +volatile uint8_t *_transmitPortRegister; //Write Port Register +uint8_t transmit_RegMask; //use Mask bit 1 +uint8_t transmit_InvMask; //use mask bit 0 + +volatile uint8_t *_receivePortRegister; //Read Port Register +uint8_t _receiveBitMask; + +//delay value for Bit +uint16_t _tx_delay; + +//delay value for Receive +uint16_t _rx_delay_stopbit; +uint16_t _rx_delay_centering; +uint16_t _rx_delay_intrabit; + +//Customize for uBITX Protocol +int8_t receiveIndex = 0; +int8_t receivedCommandLength = 0; +int8_t ffCount = 0; + +//Values for Receive Buffer +//uint16_t _buffer_overflow; +//static volatile uint8_t _receive_buffer_head; +//static volatile uint8_t _receive_buffer_tail; + +//Values for Interrupt (check Start Bit) +volatile uint8_t *_pcint_maskreg; +uint8_t _pcint_maskvalue; + +//================================================================ +//Internal Function from SoftwareSerial.c +//================================================================ +uint16_t subtract_cap(uint16_t num, uint16_t sub) +{ + if (num > sub) + return num - sub; + else + return 1; +} + +inline void tunedDelay(uint16_t delay) +{ + _delay_loop_2(delay); +} + +void setRxIntMsk(bool enable) +{ + if (enable) + *_pcint_maskreg |= _pcint_maskvalue; + else + *_pcint_maskreg &= ~_pcint_maskvalue; +} + +uint8_t rx_pin_read() +{ + return *_receivePortRegister & _receiveBitMask; +} + +// +// The receive routine called by the interrupt handler +// +void softSerail_Recv() +{ +#if GCC_VERSION < 40302 +// Work-around for avr-gcc 4.3.0 OSX version bug +// Preserve the registers that the compiler misses +// (courtesy of Arduino forum user *etracer*) + asm volatile( + "push r18 \n\t" + "push r19 \n\t" + "push r20 \n\t" + "push r21 \n\t" + "push r22 \n\t" + "push r23 \n\t" + "push r26 \n\t" + "push r27 \n\t" + ::); +#endif + + uint8_t d = 0; + + // If RX line is high, then we don't see any start bit + // so interrupt is probably not for us + if (!rx_pin_read()) //Start Bit + { + // Disable further interrupts during reception, this prevents + // triggering another interrupt directly after we return, which can + // cause problems at higher baudrates. + setRxIntMsk(false); + + // Wait approximately 1/2 of a bit width to "center" the sample + tunedDelay(_rx_delay_centering); + + // Read each of the 8 bits + for (uint8_t i=8; i > 0; --i) + { + tunedDelay(_rx_delay_intrabit); + d >>= 1; + + if (rx_pin_read()) + d |= 0x80; + } + + if (receivedCommandLength == 0) //check Already Command + { + //Set Received Data + swr_receive_buffer[receiveIndex++] = d; + + //Finded Command + if (d == 0x73 && ffCount > 1 && receiveIndex > 6) + { + receivedCommandLength = receiveIndex; + receiveIndex = 0; + ffCount = 0; + } + else if (receiveIndex > _SS_MAX_RX_BUFF) + { + //Buffer Overflow + receiveIndex = 0; + ffCount = 0; + } + else if (d == 0xFF) + { + ffCount++; + } + else + { + ffCount = 0; + } + } + + // skip the stop bit + tunedDelay(_rx_delay_stopbit); + + // Re-enable interrupts when we're sure to be inside the stop bit + setRxIntMsk(true); + } + +#if GCC_VERSION < 40302 +// Work-around for avr-gcc 4.3.0 OSX version bug +// Restore the registers that the compiler misses + asm volatile( + "pop r27 \n\t" + "pop r26 \n\t" + "pop r23 \n\t" + "pop r22 \n\t" + "pop r21 \n\t" + "pop r20 \n\t" + "pop r19 \n\t" + "pop r18 \n\t" + ::); +#endif +} + +ISR(PCINT0_vect) +{ + softSerail_Recv(); +} + +//================================================================ +//Public Function from SoftwareSerial.c and modified and create +//================================================================ +// Read data from buffer +void SWSerial_Read(uint8_t * receive_cmdBuffer) +{ + for (int i = 0; i < receivedCommandLength; i++) + receive_cmdBuffer[i] = swr_receive_buffer[i]; +} + + +/* +int SWSerial_Read(void) +{ + // Empty buffer? + if (_receive_buffer_head == _receive_buffer_tail) + return -1; + + // Read from "head" + uint8_t d = _receive_buffer[_receive_buffer_head]; // grab next byte + _receive_buffer_head = (_receive_buffer_head + 1) % _SS_MAX_RX_BUFF; + return d; +} + +int SWSerial_Available(void) +{ + return (_receive_buffer_tail + _SS_MAX_RX_BUFF - _receive_buffer_head) % _SS_MAX_RX_BUFF; +} +*/ + +void SWSerial_Write(uint8_t b) +{ + volatile uint8_t *reg = _transmitPortRegister; + uint8_t oldSREG = SREG; + uint16_t delay = _tx_delay; + + cli(); // turn off interrupts for a clean txmit + + // Write the start bit + *reg &= transmit_InvMask; + + tunedDelay(delay); + + // Write each of the 8 bits + for (uint8_t i = 8; i > 0; --i) + { + if (b & 1) // choose bit + *reg |= transmit_RegMask; // send 1 + else + *reg &= transmit_InvMask; // send 0 + + tunedDelay(delay); + b >>= 1; + } + + // restore pin to natural state + *reg |= transmit_RegMask; + + SREG = oldSREG; // turn interrupts back on + tunedDelay(_tx_delay); +} + +void SWSerial_Print(uint8_t *b) +{ + for (int i = 0; i < PRINT_MAX_LENGTH; i++) + { + if (b[i] == 0x00) + break; + else + SWSerial_Write(b[i]); + } +} + +void SWSerial_Begin(long speedBaud) +{ + //INT TX_PIN + digitalWrite(TX_PIN, HIGH); + pinMode(TX_PIN, OUTPUT); + transmit_RegMask = digitalPinToBitMask(TX_PIN); //use Bit 1 + transmit_InvMask = ~digitalPinToBitMask(TX_PIN); //use Bit 0 + _transmitPortRegister = portOutputRegister(digitalPinToPort(TX_PIN)); + + //INIT RX_PIN + pinMode(RX_PIN, INPUT); + digitalWrite(RX_PIN, HIGH); // pullup for normal logic! + _receiveBitMask = digitalPinToBitMask(RX_PIN); + _receivePortRegister = portInputRegister(digitalPinToPort(RX_PIN)); + + //Set Values + uint16_t bit_delay = (F_CPU / speedBaud) / 4; + _tx_delay = subtract_cap(bit_delay, 15 / 4); + + if (digitalPinToPCICR(RX_PIN)) + { + _rx_delay_centering = subtract_cap(bit_delay / 2, (4 + 4 + 75 + 17 - 23) / 4); + _rx_delay_intrabit = subtract_cap(bit_delay, 23 / 4); + _rx_delay_stopbit = subtract_cap(bit_delay * 3 / 4, (37 + 11) / 4); + *digitalPinToPCICR(RX_PIN) |= _BV(digitalPinToPCICRbit(RX_PIN)); + _pcint_maskreg = digitalPinToPCMSK(RX_PIN); + _pcint_maskvalue = _BV(digitalPinToPCMSKbit(RX_PIN)); + + tunedDelay(_tx_delay); // if we were low this establishes the end + } + + //Start Listen + //_buffer_overflow = false; + //_receive_buffer_head = _receive_buffer_tail = 0; + setRxIntMsk(true); +} diff --git a/ubitx_20/ubitx.h b/ubitx_20/ubitx.h index 18d7be4..6556372 100644 --- a/ubitx_20/ubitx.h +++ b/ubitx_20/ubitx.h @@ -24,14 +24,17 @@ //============================================================================== //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_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_CONTROL_MCU //CONTROL MCU -#define I2C_LCD_MASTER_ADDRESS_DEFAULT 0x3F //0x27 //DEFAULT, if Set I2C Address by uBITX Manager, read from EEProm -#define I2C_LCD_SECOND_ADDRESS_DEFAULT 0x27 //0x27 //only using Dual LCD Mode + +#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 #define EXTEND_KEY_GROUP1 //MODE, BAND(-), BAND(+), STEP //#define EXTEND_KEY_GROUP2 //Numeric (0~9), Point(.), Enter //Not supported in Version 1.0x @@ -53,6 +56,12 @@ extern byte I2C_LCD_SECOND_ADDRESS; //only using Dual LCD Mode #define USE_I2C_LCD #endif +#ifdef UBITX_DISPLAY_NEXTION + #define USE_SW_SERIAL +#elif defined(UBITX_CONTROL_MCU) + #define USE_SW_SERIAL +#endif + //============================================================================== // Hardware, Define PIN Usage //============================================================================== diff --git a/ubitx_20/ubitx_20.ino b/ubitx_20/ubitx_20.ino index 025fd9f..523bcc8 100644 --- a/ubitx_20/ubitx_20.ino +++ b/ubitx_20/ubitx_20.ino @@ -6,7 +6,7 @@ // 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.080") +#define FIRMWARE_VERSION_INFO F("+v1.091") #define FIRMWARE_VERSION_NUM 0x03 //1st Complete Project : 1 (Version 1.061), 2st Project : 2 /** @@ -602,7 +602,18 @@ void checkButton(){ 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 { @@ -1294,6 +1305,7 @@ void setup() Init_Cat(38400, SERIAL_8N1); initSettings(); + initPorts(); if (userCallsignLength > 0 && ((userCallsignLength & 0x80) == 0x80)) { userCallsignLength = userCallsignLength & 0x7F; @@ -1307,7 +1319,6 @@ void setup() clearLine2(); } - initPorts(); #ifdef FACTORY_RECOVERY_BOOTUP if (btnDown()) @@ -1320,6 +1331,11 @@ void setup() frequency = vfoA; saveCheckFreq = frequency; //for auto save frequency setFrequency(vfoA); + +#ifdef USE_SW_SERIAL + SendUbitxData(); +#endif + updateDisplay(); #ifdef ENABLE_FACTORYALIGN @@ -1383,4 +1399,9 @@ void loop(){ //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_lcd_nextion.ino b/ubitx_20/ubitx_lcd_nextion.ino new file mode 100644 index 0000000..cae6744 --- /dev/null +++ b/ubitx_20/ubitx_lcd_nextion.ino @@ -0,0 +1,1108 @@ +/************************************************************************* + KD8CEC's uBITX Display Routine for LCD2004 Parrel + 1.This is the display code for the default LCD mounted in uBITX. + 2.Display related functions of uBITX. Some functions moved from uBITX_Ui. + 3.uBITX Idle time Processing + Functions that run at times that do not affect TX, CW, and CAT + It is called in 1/10 time unit. +----------------------------------------------------------------------------- + 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 Nextion LCD Library by KD8CEC +//======================================================================== +#ifdef UBITX_DISPLAY_NEXTION +/************************************************************************* + Nextion Library for 20 x 4 LCD + KD8CEC +**************************************************************************/ +//#include +//SoftwareSerial softSerial(8, 9); // RX, TX +extern void SWSerial_Begin(long speedBaud); +extern void SWSerial_Write(uint8_t b); +extern int SWSerial_Available(void); +extern int SWSerial_Read(void); +extern void SWSerial_Print(uint8_t *b); + +#define TEXT_LINE_LENGTH 20 +char softBuffLines[2][TEXT_LINE_LENGTH + 1]; +char softBuffSended[2][TEXT_LINE_LENGTH + 1]; +char softBuffTemp[TEXT_LINE_LENGTH + 1]; //for STR Command + +char c[30], b[30]; +char softBuff[20]; +char softTemp[20]; + +void LCD2004_Init() +{ + SWSerial_Begin(9600); + memset(softBuffLines[0], ' ', TEXT_LINE_LENGTH); + softBuffLines[0][TEXT_LINE_LENGTH + 1] = 0x00; + memset(softBuffLines[1], ' ', TEXT_LINE_LENGTH); + softBuffLines[1][TEXT_LINE_LENGTH + 1] = 0x00; +} + +void LCD_CreateChar(uint8_t location, uint8_t charmap[]) +{ +} + +void LCD_Init(void) +{ + LCD2004_Init(); + initMeter(); //for Meter Display +} + +//=================================================================== +//Begin of Nextion LCD Protocol +// +// v0~v9, va~vz : Numeric (Transceiver -> Nextion LCD) +// s0~s9 : String (Text) (Transceiver -> Nextion LCD) +// vlSendxxx, vloxxx: Reserve for Nextion (Nextion LCD -> Transceiver) +// +//=================================================================== +#define CMD_NOW_DISP '0' //c0 +char L_nowdisp = -1; //Sended nowdisp + +#define CMD_VFO_TYPE 'v' //cv +char L_vfoActive; //vfoActive + +#define CMD_CURR_FREQ 'c' //vc +unsigned long L_vfoCurr; //vfoA +#define CMD_CURR_MODE 'c' //cc +byte L_vfoCurr_mode; //vfoA_mode + +#define CMD_VFOA_FREQ 'a' //va +unsigned long L_vfoA; //vfoA +#define CMD_VFOA_MODE 'a' //ca +byte L_vfoA_mode; //vfoA_mode + +#define CMD_VFOB_FREQ 'b' //vb +unsigned long L_vfoB; //vfoB +#define CMD_VFOB_MODE 'b' //cb +byte L_vfoB_mode; //vfoB_mode + +#define CMD_IS_RIT 'r' //cr +char L_ritOn; +#define CMD_RIT_FREQ 'r' //vr +unsigned long L_ritTxFrequency; //ritTxFrequency + +#define CMD_IS_TX 't' //ct +char L_inTx; + +#define CMD_IS_DIALLOCK 'l' //cl +byte L_isDialLock; //byte isDialLock + +#define CMD_IS_SPLIT 's' //cs +byte L_Split; //isTxType +#define CMD_IS_TXSTOP 'x' //cx +byte L_TXStop; //isTxType + +#define CMD_TUNEINDEX 'n' //cn +byte L_tuneStepIndex; //byte tuneStepIndex + +#define CMD_SMETER 'p' //cs +byte L_scaledSMeter; //scaledSMeter + +#define CMD_SIDE_TONE 't' //vt +unsigned long L_sideTone; //sideTone +#define CMD_KEY_TYPE 'k' //ck +byte L_cwKeyType; //L_cwKeyType 0: straight, 1 : iambica, 2: iambicb + +#define CMD_CW_SPEED 's' //vs +unsigned int L_cwSpeed; //cwSpeed + +#define CMD_CW_DELAY 'y' //vy +byte L_cwDelayTime; //cwDelayTime + +#define CMD_CW_STARTDELAY 'e' //ve +byte L_delayBeforeCWStartTime; //byte delayBeforeCWStartTime + +#define CMD_ATT_LEVEL 'f' //vf +byte L_attLevel; + +byte L_isIFShift; //1 = ifShift, 2 extend +#define CMD_IS_IFSHIFT 'i' //ci + +int L_ifShiftValue; +#define CMD_IFSHIFT_VALUE 'i' //vi + +byte L_sdrModeOn; +#define CMD_SDR_MODE 'j' //cj + +#define CMD_UBITX_INFO 'm' //cm Complete Send uBITX Information + +//Once Send Data, When boot +//arTuneStep, When boot, once send +//long arTuneStep[5]; +#define CMD_AR_TUNE1 '1' //v1 +#define CMD_AR_TUNE2 '2' //v2 +#define CMD_AR_TUNE3 '3' //v3 +#define CMD_AR_TUNE4 '4' //v4 +#define CMD_AR_TUNE5 '5' //v5 + + +#define CMD_IS_CW_SHIFT_DISPLAY 'h' //ch +byte L_isShiftDisplayCWFreq; //byte isShiftDisplayCWFreq + +#define CMD_CW_SHIFT_ADJUST 'h' //vh +int L_shiftDisplayAdjustVal; //int shiftDisplayAdjustVal + +//0:CW Display Shift Confirm, 1 : IFshift save +#define CMD_COMM_OPTION 'o' //vo +byte L_commonOption0; //byte commonOption0 + +//0:Line Toggle, 1 : Always display Callsign, 2 : scroll display, 3 : s.meter +#define CMD_DISP_OPTION1 'p' //vp +byte L_displayOption1; //byte displayOption1 +#define CMD_DISP_OPTION2 'q' //vq +byte L_displayOption2; //byte displayOption2 (Reserve) + +#define CMD_TEXT_LINE0 '0' //s0 +#define CMD_TEXT_LINE1 '1' //s1 + +#define CMD_CW_TEXT 'a' //sa +#define CMD_CALLSIGN 'c' //sc +#define CMD_VERSION 'v' //sv + +char nowdisp = 0; + +#define SWS_HEADER_CHAR_TYPE 'c' //1Byte Protocol Prefix +#define SWS_HEADER_INT_TYPE 'v' //Numeric Protocol Prefex +#define SWS_HEADER_STR_TYPE 's' //for TEXT Line compatiable Character LCD Control + +//Control must have prefix 'v' or 's' +char softSTRHeader[14] = {'p', 'a', 'g', 'e', '0', '.', 's', '0', '.', 't', 'x', 't', '=', '\"'}; +char softINTHeader[13] = {'p', 'a', 'g', 'e', '0', '.', 'v', '0', '.', 'v', 'a', 'l', '='}; + +//send data for Nextion LCD +void SendHeader(char varType, char varIndex) +{ + if (varType == SWS_HEADER_STR_TYPE) + { + softSTRHeader[7] = varIndex; + for (int i = 0; i < 14; i++) + SWSerial_Write(softSTRHeader[i]); + } + else + { + softINTHeader[7] = varIndex; + for (int i = 0; i < 13; i++) + SWSerial_Write(softINTHeader[i]); + } +} + +void SendCommandUL(char varIndex, unsigned long sendValue) +{ + SendHeader(SWS_HEADER_INT_TYPE, varIndex); + + memset(softTemp, 0, 20); + ultoa(sendValue, softTemp, DEC); + SWSerial_Print(softTemp); + SWSerial_Write(0xff); + SWSerial_Write(0xff); + SWSerial_Write(0xff); +} + +void SendCommandL(char varIndex, long sendValue) +{ + SendHeader(SWS_HEADER_INT_TYPE, varIndex); + + memset(softTemp, 0, 20); + ltoa(sendValue, softTemp, DEC); + SWSerial_Print(softTemp); + SWSerial_Write(0xff); + SWSerial_Write(0xff); + SWSerial_Write(0xff); +} + +void SendCommandStr(char varIndex, char* sendValue) +{ + SendHeader(SWS_HEADER_STR_TYPE, varIndex); + + SWSerial_Print(sendValue); + SWSerial_Write('\"'); + SWSerial_Write(0xFF); + SWSerial_Write(0xFF); + SWSerial_Write(0xFF); +} + +//Send String data with duplicate check +void SendTextLineBuff(char lineNumber) +{ + //Check Duplicated data + if (strcmp(softBuffLines[lineNumber], softBuffSended[lineNumber])) + { + SendHeader(SWS_HEADER_STR_TYPE, lineNumber + 0x30); //s0.txt, s1.txt + + SWSerial_Print(softBuffLines[lineNumber]); + SWSerial_Write('\"'); + SWSerial_Write(0xFF); + SWSerial_Write(0xFF); + SWSerial_Write(0xFF); + + strcpy(softBuffSended[lineNumber], softBuffLines[lineNumber]); + } +} + +void SendTextLineStr(char lineNumber, char* sendValue) +{ + int i = 0; + for (i = 0; i < 16; i++) + { + if (sendValue[i] == 0x00) + break; + else + softBuffLines[lineNumber][i] = sendValue[i]; + } + + for (;i < 20; i++) + { + softBuffLines[lineNumber][i] = ' '; + } + + softBuffLines[lineNumber][TEXT_LINE_LENGTH + 1] = 0x00; + SendTextLineBuff(lineNumber); +} + +void SendEEPromData(char varIndex, int eepromStartIndex, int eepromEndIndex, char offsetTtype) +{ + SendHeader(SWS_HEADER_STR_TYPE, varIndex); + + for (int i = eepromStartIndex; i <= eepromEndIndex; i++) + { + SWSerial_Write(EEPROM.read((offsetTtype == 0 ? USER_CALLSIGN_DAT : WSPR_MESSAGE1) + i)); + } + + SWSerial_Write('\"'); + SWSerial_Write(0xFF); + SWSerial_Write(0xFF); + SWSerial_Write(0xFF); +} + +char softBuff1Num[17] = {'p', 'a', 'g', 'e', '0', '.', 'c', '0', '.', 'v', 'a', 'l', '=', 0, 0xFF, 0xFF, 0xFF}; +void SendCommand1Num(char varType, char sendValue) //0~9 : Mode, nowDisp, ActiveVFO, IsDialLock, IsTxtType, IsSplitType +{ + softBuff1Num[7] = varType; + softBuff1Num[13] = sendValue + 0x30; + + for (int i = 0; i < 17; i++) + SWSerial_Write(softBuff1Num[i]); +} + +void SetSWActivePage(char newPageIndex) +{ + if (L_nowdisp != newPageIndex) + { + L_nowdisp = newPageIndex; + SendCommand1Num(CMD_NOW_DISP, L_nowdisp); + } +} +//=================================================================== +//End of Nextion LCD Protocol +//=================================================================== + +// The generic routine to display one line on the LCD +void printLine(unsigned char linenmbr, const char *c) { + SendTextLineStr(linenmbr, c); +} + +void printLineF(char linenmbr, const __FlashStringHelper *c) +{ + int i; + char tmpBuff[21]; + PGM_P p = reinterpret_cast(c); + + for (i = 0; i < 21; i++){ + unsigned char fChar = pgm_read_byte(p++); + tmpBuff[i] = fChar; + if (fChar == 0) + break; + } + + printLine(linenmbr, tmpBuff); +} + +#define LCD_MAX_COLUMN 20 +void printLineFromEEPRom(char linenmbr, char lcdColumn, byte eepromStartIndex, byte eepromEndIndex, char offsetTtype) +{ + int colIndex = lcdColumn; + for (byte i = eepromStartIndex; i <= eepromEndIndex; i++) + { + if (++lcdColumn <= LCD_MAX_COLUMN) + softBuffLines[linenmbr][colIndex++] = EEPROM.read((offsetTtype == 0 ? USER_CALLSIGN_DAT : WSPR_MESSAGE1) + i); + else + break; + } + + SendTextLineBuff(linenmbr); +} + +// 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 for Nextion LCD +//unsigned long +byte nowPageIndex = 0; + +//sendType == 1 not check different +void sendUIData(int sendType) +{ + char nowActiveVFO = vfoActive == VFO_A ? 0 : 1; + + //#define CMD_VFO_TYPE 'v' //cv + if (L_vfoActive != nowActiveVFO) + { + L_vfoActive = nowActiveVFO; + SendCommand1Num(CMD_VFO_TYPE, L_vfoActive); + } + + //#define CMD_CURR_FREQ 'c' //vc + if (L_vfoCurr != frequency) + { + L_vfoCurr = frequency; + SendCommandUL(CMD_CURR_FREQ, frequency); + } + + //#define CMD_CURR_MODE 'c' //cc + byte vfoCurr_mode = modeToByte(); + if (L_vfoCurr_mode != vfoCurr_mode) + { + L_vfoCurr_mode = vfoCurr_mode; + SendCommand1Num(CMD_CURR_MODE, L_vfoCurr_mode); + } + + //if auto cw key mode, exit + //if (isCWAutoMode != 0 || menuOn != 0) + if (isCWAutoMode != 0) + return; + + //nowPageIndex = 0; + if (menuOn==0) + { + if (sendType == 0) + { + SetSWActivePage(0); + } + else + { + SetSWActivePage(0); + } + } + else + { + //Text Line Mode + SetSWActivePage(1); + } + + //#define CMD_VFOA_FREQ 'a' //va + //VFOA + if (L_vfoA != vfoA) + { + L_vfoA = vfoA; + SendCommandUL(CMD_VFOA_FREQ, L_vfoA); + } + + //#define CMD_VFOA_MODE 'a' //ca + if (L_vfoA_mode != vfoA_mode) + { + L_vfoA_mode = vfoA_mode; + SendCommand1Num(CMD_VFOA_MODE, L_vfoA_mode); + } + + //#define CMD_VFOB_FREQ 'b' //vb + //VFOB + if (L_vfoB != vfoB) + { + L_vfoB = vfoB; + SendCommandUL(CMD_VFOB_FREQ, L_vfoB); + } + + //#define CMD_VFOB_MODE 'b' //cb + if (L_vfoB_mode != vfoB_mode) + { + L_vfoB_mode = vfoB_mode; + SendCommand1Num(CMD_VFOB_MODE, L_vfoB_mode); + } + + //byte isDialLock = ((isTxType & 0x01) == 0x01) ? 1 : 0; + if (L_isDialLock != isDialLock) + { + L_isDialLock = isDialLock; + SendCommand1Num(CMD_IS_DIALLOCK, L_isDialLock); + } + + //#define CMD_IS_RIT 'r' //cr + if (L_ritOn != ritOn) + { + L_ritOn = ritOn; + SendCommand1Num(CMD_IS_RIT, L_ritOn); + } + + //#define CMD_RIT_FREQ 'r' //vr + //unsigned long L_ritTxFrequency; //ritTxFrequency + if (L_ritTxFrequency != ritTxFrequency) + { + L_ritTxFrequency = ritTxFrequency; + SendCommandUL(CMD_RIT_FREQ, L_ritTxFrequency); + } + + //#define CMD_IS_TX 't' //ct + //char L_inTx; + if (L_inTx != inTx) + { + L_inTx = inTx; + SendCommand1Num(CMD_IS_TX, L_inTx); + } + + //#define CMD_IS_DIALLOCK 'l' //cl + //byte L_isDialLock; //byte isDialLock + if (L_isDialLock != isDialLock) + { + L_isDialLock = isDialLock; + SendCommand1Num(CMD_IS_DIALLOCK, L_isDialLock); + } + + //#define CMD_IS_SPLIT 's' //cs + //byte L_Split; //isTxType + if (L_Split != splitOn) + { + L_Split = splitOn; + SendCommand1Num(CMD_IS_SPLIT, L_Split); + } + + + //#define CMD_IS_TXSTOP 'x' //cx + byte isTXStop = ((isTxType & 0x01) == 0x01); + if (L_TXStop != isTXStop) + { + L_TXStop = isTXStop; + SendCommand1Num(CMD_IS_TXSTOP, L_TXStop); + } + + //#define CMD_TUNEINDEX 'n' //cn + if (L_tuneStepIndex != tuneStepIndex) + { + L_tuneStepIndex = tuneStepIndex; + SendCommand1Num(CMD_TUNEINDEX, L_tuneStepIndex); + } + + //#define CMD_SMETER 'p' //cp + if (L_scaledSMeter != scaledSMeter) + { + L_scaledSMeter = scaledSMeter; + SendCommand1Num(CMD_SMETER, L_scaledSMeter); + } + + //#define CMD_SIDE_TONE 't' //vt + if (L_sideTone != sideTone) + { + L_sideTone = sideTone; + SendCommandL(CMD_SIDE_TONE, L_sideTone); + } + + //#define CMD_KEY_TYPE 'k' //ck + if (L_cwKeyType != cwKeyType) + { + L_cwKeyType = cwKeyType; + SendCommand1Num(CMD_KEY_TYPE, L_cwKeyType); + } + + //#define CMD_CW_SPEED 's' //vs + if (L_cwSpeed != cwSpeed) + { + L_cwSpeed = cwSpeed; + SendCommandL(CMD_CW_SPEED, L_cwSpeed); + } + + //#define CMD_CW_DELAY 'y' //vy + if (L_cwDelayTime != cwDelayTime) + { + L_cwDelayTime = cwDelayTime; + SendCommandL(CMD_CW_DELAY, L_cwDelayTime); + } + + //#define CMD_CW_STARTDELAY 'e' //ve + if (L_delayBeforeCWStartTime != delayBeforeCWStartTime) + { + L_delayBeforeCWStartTime = delayBeforeCWStartTime; + SendCommandL(CMD_CW_STARTDELAY, L_delayBeforeCWStartTime); + } + + //#define CMD_ATT_LEVEL 'f' //vf + if (L_attLevel != attLevel) + { + L_attLevel = attLevel; + SendCommandL(CMD_ATT_LEVEL, L_attLevel); + } + + //#define CMD_IS_IFSHIFT 'i' + if (L_isIFShift != isIFShift) + { + L_isIFShift = isIFShift; + SendCommand1Num(CMD_IS_IFSHIFT, L_isIFShift); + } + + //#define CMD_IFSHIFT_VALUE 'i' + if (L_ifShiftValue != ifShiftValue) + { + L_ifShiftValue = ifShiftValue; + SendCommandL(CMD_IFSHIFT_VALUE, L_ifShiftValue); + } + + //#define CMD_SDR_MODE 'j' //cj + if (L_sdrModeOn != sdrModeOn) + { + L_sdrModeOn = sdrModeOn; + SendCommand1Num(CMD_SDR_MODE, L_sdrModeOn); + } +} + +void updateDisplay() { + //clearLine1(); + sendUIData(0); //UI + + /* + 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 (isUSB) + strcpy(c, "USB "); + else + strcpy(c, "LSB "); + } + else if (cwMode == 1) + { + strcpy(c, "CWL "); + } + else + { + strcpy(c, "CWU "); + } + } + + 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] = ' '; + } + + if (sdrModeOn) + strcat(c, " SDR"); + else + strcat(c, " SPK"); + + //remarked by KD8CEC + //already RX/TX status display, and over index (20 x 4 LCD) + //if (inTx) + // strcat(c, " TX"); + printLine(1, c); + + byte diplayVFOLine = 1; + if ((displayOption1 & 0x01) == 0x01) + diplayVFOLine = 0; + */ +} + +// tern static uint8_t swr_receive_buffer[20]; +extern uint8_t receivedCommandLength; +extern void SWSerial_Read(uint8_t * receive_cmdBuffer); +//extern void byteToMode(byte modeValue, byte autoSetModebyFreq); +uint8_t swr_buffer[20]; + +#define TS_CMD_MODE 1 +#define TS_CMD_FREQ 2 +#define TS_CMD_BAND 3 +#define TS_CMD_VFO 4 +#define TS_CMD_SPLIT 5 +#define TS_CMD_RIT 6 +#define TS_CMD_TXSTOP 7 +#define TS_CMD_SDR 8 +#define TS_CMD_LOCK 9 //Dial Lock +#define TS_CMD_ATT 10 //ATT +#define TS_CMD_IFS 11 //IFS Enabled +#define TS_CMD_IFSVALUE 12 //IFS VALUE + +//SoftwareSerial_Process +void SWS_Process(void) +{ + //Received Command from touch screen + if (receivedCommandLength > 0) + { + SWSerial_Read(swr_buffer); + + int8_t comandLength = receivedCommandLength; + int8_t commandStartIndex = -1; + receivedCommandLength = 0; + + //Data Process + //comandLength //Find start Length + for (int i = 0; i < comandLength - 3; i++) + { + if (swr_buffer[i] == 0x59 && swr_buffer[i+ 1] == 0x58 && swr_buffer[i + 2] == 0x68) + { + commandStartIndex = i; + break; + } + } //end of for + + if (commandStartIndex != -1) + { + //Complete received command from touch screen + uint8_t commandType = swr_buffer[commandStartIndex + 3]; + +/* +#define TS_CMD_MODE 1 +#define TS_CMD_FREQ 2 +#define TS_CMD_BAND 3 + */ +#define TS_CMD_BAND 3 + + if (commandType == TS_CMD_MODE) + { + byteToMode(swr_buffer[commandStartIndex + 4], 1); + } + else if (commandType == TS_CMD_FREQ) + { + unsigned long *tempFreq; + tempFreq = (unsigned long *)(&swr_buffer[commandStartIndex + 4]); + frequency = *tempFreq; + } + else if (commandType == TS_CMD_BAND) + { + char currentBandIndex = -1; + 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, swr_buffer[commandStartIndex + 4] == 1 ? -1 : 1); //Prior Band + } + else if (commandType == TS_CMD_VFO) + { + menuVfoToggle(1); //Vfo Toggle + } + else if (commandType == TS_CMD_SPLIT) + { + menuSplitOnOff(1); + } + else if (commandType == TS_CMD_RIT) + { + menuRitToggle(1); + } + else if (commandType == TS_CMD_TXSTOP) + { + menuTxOnOff(1, 0x01); + } + else if (commandType == TS_CMD_SDR) + { + menuSDROnOff(1); + } + else if (commandType == TS_CMD_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 + } + else if (commandType == TS_CMD_ATT) + { + attLevel = swr_buffer[commandStartIndex + 4]; + } + else if (commandType == TS_CMD_IFS) + { + isIFShift = isIFShift ? 0 : 1; //Toggle + } + else if (commandType == TS_CMD_IFSVALUE) + { + ifShiftValue = *(long *)(&swr_buffer[commandStartIndex + 4]); + } + + setFrequency(frequency); + SetCarrierFreq(); + updateDisplay(); + } + } +} + +void updateLine2Buffer(char displayType) +{ + +} + +/* +char line2Buffer[20]; +//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) +{ + return; + unsigned long tmpFreq = 0; + 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] = ' '; + } + + memset(&line2Buffer[10], ' ', 10); + + if (isIFShift) + { + line2Buffer[6] = 'M'; + line2Buffer[7] = ' '; + //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); + + for (int i = 12; i < 17; i++) + { + if (line2Buffer[i] == 0) + line2Buffer[i] = ' '; + } + } // end of display IF + else // step & Key Type display + { + //Step + long tmpStep = arTuneStep[tuneStepIndex -1]; + + byte isStepKhz = 0; + if (tmpStep >= 1000) + { + isStepKhz = 2; + } + + for (int i = 14; i >= 12 - isStepKhz; i--) { + if (tmpStep > 0) { + line2Buffer[i + isStepKhz] = tmpStep % 10 + 0x30; + tmpStep /= 10; + } + else + line2Buffer[i +isStepKhz] = ' '; + } + + if (isStepKhz == 0) + { + line2Buffer[15] = 'H'; + line2Buffer[16] = 'z'; + } + } + + line2Buffer[17] = ' '; + + //Check CW Key cwKeyType = 0; //0: straight, 1 : iambica, 2: iambicb + if (cwKeyType == 0) + { + line2Buffer[18] = 'S'; + line2Buffer[19] = 'T'; + } + else if (cwKeyType == 1) + { + line2Buffer[18] = 'I'; + line2Buffer[19] = 'A'; + } + else + { + line2Buffer[18] = 'I'; + line2Buffer[19] = 'B'; + } + +} + +*/ + +char checkCount = 0; +char checkCountSMeter = 0; + +//execute interval : 0.25sec +void idle_process() +{ + sendUIData(1); + + //S-Meter Display + if (((displayOption1 & 0x08) == 0x08 && (sdrModeOn == 0)) && (++checkCountSMeter > SMeterLatency)) + { + int newSMeter; + + //VK2ETA S-Meter from MAX9814 TC pin + newSMeter = analogRead(ANALOG_SMETER) / 4; + + //Faster attack, Slower release + //currentSMeter = (newSMeter > currentSMeter ? ((currentSMeter * 3 + newSMeter * 7) + 5) / 10 : ((currentSMeter * 7 + newSMeter * 3) + 5) / 10); + //currentSMeter = ((currentSMeter * 7 + newSMeter * 3) + 5) / 10; + currentSMeter = newSMeter; + + scaledSMeter = 0; + for (byte s = 8; s >= 1; s--) { + if (currentSMeter > sMeterLevels[s]) { + scaledSMeter = s; + break; + } + } + + checkCountSMeter = 0; //Reset Latency time + } //end of S-Meter + + /* + return; + //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)) { + if (checkCount++ > 1) + { + updateLine2Buffer(0); //call by scheduler + printLine2(line2Buffer); + line2DisplayStatus = 2; + checkCount = 0; + } + } + + //S-Meter Display + if (((displayOption1 & 0x08) == 0x08 && (sdrModeOn == 0)) && (++checkCountSMeter > SMeterLatency)) + { + int newSMeter; + + //VK2ETA S-Meter from MAX9814 TC pin + newSMeter = analogRead(ANALOG_SMETER) / 4; + + //Faster attack, Slower release + //currentSMeter = (newSMeter > currentSMeter ? ((currentSMeter * 3 + newSMeter * 7) + 5) / 10 : ((currentSMeter * 7 + newSMeter * 3) + 5) / 10); + //currentSMeter = ((currentSMeter * 7 + newSMeter * 3) + 5) / 10; + currentSMeter = newSMeter; + + scaledSMeter = 0; + for (byte s = 8; s >= 1; s--) { + if (currentSMeter > sMeterLevels[s]) { + scaledSMeter = s; + break; + } + } + + checkCountSMeter = 0; //Reset Latency time + } //end of S-Meter + } + */ +} + +//When boot time, send data +void SendUbitxData(void) +{ + SendCommandL(CMD_AR_TUNE1, arTuneStep[0]); + SendCommandL(CMD_AR_TUNE2, arTuneStep[1]); + SendCommandL(CMD_AR_TUNE3, arTuneStep[2]); + SendCommandL(CMD_AR_TUNE4, arTuneStep[3]); + SendCommandL(CMD_AR_TUNE5, arTuneStep[4]); + + SendCommandL(CMD_IS_CW_SHIFT_DISPLAY, isShiftDisplayCWFreq); + SendCommandL(CMD_CW_SHIFT_ADJUST, shiftDisplayAdjustVal); + SendCommandL(CMD_COMM_OPTION, commonOption0); + SendCommandL(CMD_DISP_OPTION1, displayOption1); + SendCommandL(CMD_DISP_OPTION2, displayOption2); + + SendCommandStr(CMD_VERSION, "+v1.0N1"); //Version + SendEEPromData(CMD_CALLSIGN, 0, userCallsignLength -1, 0); + + //Complte Send Info + SendCommand1Num(CMD_UBITX_INFO, 1); + + //Page Init + L_nowdisp = 0; + SendCommand1Num(CMD_NOW_DISP, L_nowdisp); +} + + +//AutoKey LCD Display Routine +void Display_AutoKeyTextIndex(byte textIndex) +{ + byte diplayAutoCWLine = 0; + + if ((displayOption1 & 0x01) == 0x01) + diplayAutoCWLine = 1; + //LCD_SetCursor(0, diplayAutoCWLine); + + softBuffLines[diplayAutoCWLine][0] = byteToChar(textIndex); + softBuffLines[diplayAutoCWLine][1] = ':'; + + SendTextLineBuff(diplayAutoCWLine); + //LCD_Write(byteToChar(textIndex)); + //LCD_Write(':'); +} + +//not use with Nextion LCD +void DisplayCallsign(byte callSignLength) +{ +} + +//Not use with Nextion LCD +void DisplayVersionInfo(const __FlashStringHelper * fwVersionInfo) +{ +} + +#endif diff --git a/ubitx_20/ubitx_menu.ino b/ubitx_20/ubitx_menu.ino index 42bf7c0..5452901 100644 --- a/ubitx_20/ubitx_menu.ino +++ b/ubitx_20/ubitx_menu.ino @@ -498,9 +498,9 @@ void menuCWAutoKey(int btn){ printLineF1(F("PTT to Send")); delay_background(500, 0); - updateDisplay(); beforeCWTextIndex = 255; //255 value is for start check isCWAutoMode = 1; + updateDisplay(); menuOn = 0; } @@ -666,7 +666,11 @@ int getValueByKnob(int valueType, int targetValue, int minKnobValue, int maxKnob ifShiftValue = targetValue; else attLevel = targetValue; - + +#ifdef USE_SW_SERIAL + menuOn=2; + updateDisplay(); +#endif setFrequency(frequency); SetCarrierFreq(); } diff --git a/ubitx_20/ubitx_wspr.ino b/ubitx_20/ubitx_wspr.ino index 5765aca..2e65c14 100644 --- a/ubitx_20/ubitx_wspr.ino +++ b/ubitx_20/ubitx_wspr.ino @@ -73,13 +73,14 @@ void SendWSPRManage() if (nowWsprStep == 0) //select Message status { - printLineF2(F("WSPR:")); + //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); From 72ccd3b0e4807b159b31f2ce0e4984281b6c4276 Mon Sep 17 00:00:00 2001 From: phdlee Date: Sat, 9 Jun 2018 18:26:11 +0900 Subject: [PATCH 130/173] modified protocol for nextion lcd --- ubitx_20/softserial_tiny.cpp | 23 +-- ubitx_20/ubitx.h | 2 + ubitx_20/ubitx_lcd_nextion.ino | 367 ++++----------------------------- 3 files changed, 40 insertions(+), 352 deletions(-) diff --git a/ubitx_20/softserial_tiny.cpp b/ubitx_20/softserial_tiny.cpp index b0d1216..55cee48 100644 --- a/ubitx_20/softserial_tiny.cpp +++ b/ubitx_20/softserial_tiny.cpp @@ -1,4 +1,5 @@ /* +Softserial for Nextion LCD and Control MCU KD8CEC, Ian Lee ----------------------------------------------------------------------- It is a library rewritten in C format based on SoftwareSerial.c. @@ -253,26 +254,6 @@ void SWSerial_Read(uint8_t * receive_cmdBuffer) receive_cmdBuffer[i] = swr_receive_buffer[i]; } - -/* -int SWSerial_Read(void) -{ - // Empty buffer? - if (_receive_buffer_head == _receive_buffer_tail) - return -1; - - // Read from "head" - uint8_t d = _receive_buffer[_receive_buffer_head]; // grab next byte - _receive_buffer_head = (_receive_buffer_head + 1) % _SS_MAX_RX_BUFF; - return d; -} - -int SWSerial_Available(void) -{ - return (_receive_buffer_tail + _SS_MAX_RX_BUFF - _receive_buffer_head) % _SS_MAX_RX_BUFF; -} -*/ - void SWSerial_Write(uint8_t b) { volatile uint8_t *reg = _transmitPortRegister; @@ -348,7 +329,5 @@ void SWSerial_Begin(long speedBaud) } //Start Listen - //_buffer_overflow = false; - //_receive_buffer_head = _receive_buffer_tail = 0; setRxIntMsk(true); } diff --git a/ubitx_20/ubitx.h b/ubitx_20/ubitx.h index 6556372..1a09bf1 100644 --- a/ubitx_20/ubitx.h +++ b/ubitx_20/ubitx.h @@ -58,8 +58,10 @@ extern byte I2C_LCD_SECOND_ADDRESS; //only using Dual LCD Mode #ifdef UBITX_DISPLAY_NEXTION #define USE_SW_SERIAL + #undef ENABLE_ADCMONITOR #elif defined(UBITX_CONTROL_MCU) #define USE_SW_SERIAL + #undef ENABLE_ADCMONITOR #endif //============================================================================== diff --git a/ubitx_20/ubitx_lcd_nextion.ino b/ubitx_20/ubitx_lcd_nextion.ino index cae6744..8041001 100644 --- a/ubitx_20/ubitx_lcd_nextion.ino +++ b/ubitx_20/ubitx_lcd_nextion.ino @@ -1,10 +1,5 @@ /************************************************************************* - KD8CEC's uBITX Display Routine for LCD2004 Parrel - 1.This is the display code for the default LCD mounted in uBITX. - 2.Display related functions of uBITX. Some functions moved from uBITX_Ui. - 3.uBITX Idle time Processing - Functions that run at times that do not affect TX, CW, and CAT - It is called in 1/10 time unit. + KD8CEC's uBITX Display Routine for Nextion LCD ----------------------------------------------------------------------------- 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 @@ -28,11 +23,9 @@ //======================================================================== #ifdef UBITX_DISPLAY_NEXTION /************************************************************************* - Nextion Library for 20 x 4 LCD + Nextion Library for uBItX KD8CEC **************************************************************************/ -//#include -//SoftwareSerial softSerial(8, 9); // RX, TX extern void SWSerial_Begin(long speedBaud); extern void SWSerial_Write(uint8_t b); extern int SWSerial_Available(void); @@ -57,10 +50,6 @@ void LCD2004_Init() softBuffLines[1][TEXT_LINE_LENGTH + 1] = 0x00; } -void LCD_CreateChar(uint8_t location, uint8_t charmap[]) -{ -} - void LCD_Init(void) { LCD2004_Init(); @@ -179,6 +168,19 @@ byte L_displayOption2; //byte displayOption2 (Reserve) #define CMD_CALLSIGN 'c' //sc #define CMD_VERSION 'v' //sv +#define TS_CMD_MODE 1 +#define TS_CMD_FREQ 2 +#define TS_CMD_BAND 3 +#define TS_CMD_VFO 4 +#define TS_CMD_SPLIT 5 +#define TS_CMD_RIT 6 +#define TS_CMD_TXSTOP 7 +#define TS_CMD_SDR 8 +#define TS_CMD_LOCK 9 //Dial Lock +#define TS_CMD_ATT 10 //ATT +#define TS_CMD_IFS 11 //IFS Enabled +#define TS_CMD_IFSVALUE 12 //IFS VALUE + char nowdisp = 0; #define SWS_HEADER_CHAR_TYPE 'c' //1Byte Protocol Prefix @@ -186,22 +188,22 @@ char nowdisp = 0; #define SWS_HEADER_STR_TYPE 's' //for TEXT Line compatiable Character LCD Control //Control must have prefix 'v' or 's' -char softSTRHeader[14] = {'p', 'a', 'g', 'e', '0', '.', 's', '0', '.', 't', 'x', 't', '=', '\"'}; -char softINTHeader[13] = {'p', 'a', 'g', 'e', '0', '.', 'v', '0', '.', 'v', 'a', 'l', '='}; +char softSTRHeader[11] = {'p', 'm', '.', 's', '0', '.', 't', 'x', 't', '=', '\"'}; +char softINTHeader[10] = {'p', 'm', '.', 'v', '0', '.', 'v', 'a', 'l', '='}; //send data for Nextion LCD void SendHeader(char varType, char varIndex) { if (varType == SWS_HEADER_STR_TYPE) { - softSTRHeader[7] = varIndex; - for (int i = 0; i < 14; i++) + softSTRHeader[4] = varIndex; + for (int i = 0; i < 11; i++) SWSerial_Write(softSTRHeader[i]); } else { - softINTHeader[7] = varIndex; - for (int i = 0; i < 13; i++) + softINTHeader[4] = varIndex; + for (int i = 0; i < 10; i++) SWSerial_Write(softINTHeader[i]); } } @@ -294,13 +296,13 @@ void SendEEPromData(char varIndex, int eepromStartIndex, int eepromEndIndex, cha SWSerial_Write(0xFF); } -char softBuff1Num[17] = {'p', 'a', 'g', 'e', '0', '.', 'c', '0', '.', 'v', 'a', 'l', '=', 0, 0xFF, 0xFF, 0xFF}; +char softBuff1Num[14] = {'p', 'm', '.', 'c', '0', '.', 'v', 'a', 'l', '=', 0, 0xFF, 0xFF, 0xFF}; void SendCommand1Num(char varType, char sendValue) //0~9 : Mode, nowDisp, ActiveVFO, IsDialLock, IsTxtType, IsSplitType { - softBuff1Num[7] = varType; - softBuff1Num[13] = sendValue + 0x30; + softBuff1Num[4] = varType; + softBuff1Num[10] = sendValue + 0x30; - for (int i = 0; i < 17; i++) + for (int i = 0; i < 14; i++) SWSerial_Write(softBuff1Num[i]); } @@ -609,95 +611,7 @@ void sendUIData(int sendType) } void updateDisplay() { - //clearLine1(); sendUIData(0); //UI - - /* - 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 (isUSB) - strcpy(c, "USB "); - else - strcpy(c, "LSB "); - } - else if (cwMode == 1) - { - strcpy(c, "CWL "); - } - else - { - strcpy(c, "CWU "); - } - } - - 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] = ' '; - } - - if (sdrModeOn) - strcat(c, " SDR"); - else - strcat(c, " SPK"); - - //remarked by KD8CEC - //already RX/TX status display, and over index (20 x 4 LCD) - //if (inTx) - // strcat(c, " TX"); - printLine(1, c); - - byte diplayVFOLine = 1; - if ((displayOption1 & 0x01) == 0x01) - diplayVFOLine = 0; - */ } // tern static uint8_t swr_receive_buffer[20]; @@ -706,19 +620,6 @@ extern void SWSerial_Read(uint8_t * receive_cmdBuffer); //extern void byteToMode(byte modeValue, byte autoSetModebyFreq); uint8_t swr_buffer[20]; -#define TS_CMD_MODE 1 -#define TS_CMD_FREQ 2 -#define TS_CMD_BAND 3 -#define TS_CMD_VFO 4 -#define TS_CMD_SPLIT 5 -#define TS_CMD_RIT 6 -#define TS_CMD_TXSTOP 7 -#define TS_CMD_SDR 8 -#define TS_CMD_LOCK 9 //Dial Lock -#define TS_CMD_ATT 10 //ATT -#define TS_CMD_IFS 11 //IFS Enabled -#define TS_CMD_IFSVALUE 12 //IFS VALUE - //SoftwareSerial_Process void SWS_Process(void) { @@ -747,13 +648,6 @@ void SWS_Process(void) //Complete received command from touch screen uint8_t commandType = swr_buffer[commandStartIndex + 3]; -/* -#define TS_CMD_MODE 1 -#define TS_CMD_FREQ 2 -#define TS_CMD_BAND 3 - */ -#define TS_CMD_BAND 3 - if (commandType == TS_CMD_MODE) { byteToMode(swr_buffer[commandStartIndex + 4], 1); @@ -767,10 +661,12 @@ void SWS_Process(void) else if (commandType == TS_CMD_BAND) { char currentBandIndex = -1; - if (tuneTXType == 2 || tuneTXType == 3 || tuneTXType == 102 || tuneTXType == 103) { //only ham band move + if (tuneTXType == 2 || tuneTXType == 3 || tuneTXType == 102 || tuneTXType == 103) + { //only ham band move currentBandIndex = getIndexHambanBbyFreq(frequency); - if (currentBandIndex >= 0) { + if (currentBandIndex >= 0) + { saveBandFreqByIndex(frequency, modeToByte(), currentBandIndex); } } @@ -823,156 +719,6 @@ void SWS_Process(void) } } -void updateLine2Buffer(char displayType) -{ - -} - -/* -char line2Buffer[20]; -//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) -{ - return; - unsigned long tmpFreq = 0; - 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] = ' '; - } - - memset(&line2Buffer[10], ' ', 10); - - if (isIFShift) - { - line2Buffer[6] = 'M'; - line2Buffer[7] = ' '; - //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); - - for (int i = 12; i < 17; i++) - { - if (line2Buffer[i] == 0) - line2Buffer[i] = ' '; - } - } // end of display IF - else // step & Key Type display - { - //Step - long tmpStep = arTuneStep[tuneStepIndex -1]; - - byte isStepKhz = 0; - if (tmpStep >= 1000) - { - isStepKhz = 2; - } - - for (int i = 14; i >= 12 - isStepKhz; i--) { - if (tmpStep > 0) { - line2Buffer[i + isStepKhz] = tmpStep % 10 + 0x30; - tmpStep /= 10; - } - else - line2Buffer[i +isStepKhz] = ' '; - } - - if (isStepKhz == 0) - { - line2Buffer[15] = 'H'; - line2Buffer[16] = 'z'; - } - } - - line2Buffer[17] = ' '; - - //Check CW Key cwKeyType = 0; //0: straight, 1 : iambica, 2: iambicb - if (cwKeyType == 0) - { - line2Buffer[18] = 'S'; - line2Buffer[19] = 'T'; - } - else if (cwKeyType == 1) - { - line2Buffer[18] = 'I'; - line2Buffer[19] = 'A'; - } - else - { - line2Buffer[18] = 'I'; - line2Buffer[19] = 'B'; - } - -} - -*/ - char checkCount = 0; char checkCountSMeter = 0; @@ -1004,51 +750,6 @@ void idle_process() checkCountSMeter = 0; //Reset Latency time } //end of S-Meter - - /* - return; - //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)) { - if (checkCount++ > 1) - { - updateLine2Buffer(0); //call by scheduler - printLine2(line2Buffer); - line2DisplayStatus = 2; - checkCount = 0; - } - } - - //S-Meter Display - if (((displayOption1 & 0x08) == 0x08 && (sdrModeOn == 0)) && (++checkCountSMeter > SMeterLatency)) - { - int newSMeter; - - //VK2ETA S-Meter from MAX9814 TC pin - newSMeter = analogRead(ANALOG_SMETER) / 4; - - //Faster attack, Slower release - //currentSMeter = (newSMeter > currentSMeter ? ((currentSMeter * 3 + newSMeter * 7) + 5) / 10 : ((currentSMeter * 7 + newSMeter * 3) + 5) / 10); - //currentSMeter = ((currentSMeter * 7 + newSMeter * 3) + 5) / 10; - currentSMeter = newSMeter; - - scaledSMeter = 0; - for (byte s = 8; s >= 1; s--) { - if (currentSMeter > sMeterLevels[s]) { - scaledSMeter = s; - break; - } - } - - checkCountSMeter = 0; //Reset Latency time - } //end of S-Meter - } - */ } //When boot time, send data @@ -1091,8 +792,14 @@ void Display_AutoKeyTextIndex(byte textIndex) softBuffLines[diplayAutoCWLine][1] = ':'; SendTextLineBuff(diplayAutoCWLine); - //LCD_Write(byteToChar(textIndex)); - //LCD_Write(':'); +} + +void LCD_CreateChar(uint8_t location, uint8_t charmap[]) +{ +} + +void updateLine2Buffer(char displayType) +{ } //not use with Nextion LCD From 152b63a9edb9001a19834d249fc507027fc9f1d2 Mon Sep 17 00:00:00 2001 From: phdlee Date: Mon, 11 Jun 2018 22:06:42 +0900 Subject: [PATCH 131/173] Added SW Trigger, Spectrum Protocol, Get ADC Protocol --- ubitx_20/ubitx.h | 2 + ubitx_20/ubitx_20.ino | 4 +- ubitx_20/ubitx_lcd_nextion.ino | 123 +++++++++++++++++++++++++++++++-- ubitx_20/ubitx_menu.ino | 1 + ubitx_20/ubitx_wspr.ino | 9 ++- 5 files changed, 131 insertions(+), 8 deletions(-) diff --git a/ubitx_20/ubitx.h b/ubitx_20/ubitx.h index 1a09bf1..23f626f 100644 --- a/ubitx_20/ubitx.h +++ b/ubitx_20/ubitx.h @@ -64,6 +64,7 @@ extern byte I2C_LCD_SECOND_ADDRESS; //only using Dual LCD Mode #undef ENABLE_ADCMONITOR #endif + //============================================================================== // Hardware, Define PIN Usage //============================================================================== @@ -164,6 +165,7 @@ 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); diff --git a/ubitx_20/ubitx_20.ino b/ubitx_20/ubitx_20.ino index 523bcc8..8892311 100644 --- a/ubitx_20/ubitx_20.ino +++ b/ubitx_20/ubitx_20.ino @@ -192,7 +192,9 @@ byte I2C_LCD_SECOND_ADDRESS; //only using Dual LCD Mode byte KeyValues[16][3]; byte isIFShift = 0; //1 = ifShift, 2 extend -int ifShiftValue = 0; // +int ifShiftValue = 0; // + +byte TriggerBySW = 0; //Action Start from Nextion LCD, Other MCU /** * Below are the basic functions that control the uBitx. Understanding the functions before diff --git a/ubitx_20/ubitx_lcd_nextion.ino b/ubitx_20/ubitx_lcd_nextion.ino index 8041001..c538968 100644 --- a/ubitx_20/ubitx_lcd_nextion.ino +++ b/ubitx_20/ubitx_lcd_nextion.ino @@ -176,10 +176,15 @@ byte L_displayOption2; //byte displayOption2 (Reserve) #define TS_CMD_RIT 6 #define TS_CMD_TXSTOP 7 #define TS_CMD_SDR 8 -#define TS_CMD_LOCK 9 //Dial Lock -#define TS_CMD_ATT 10 //ATT -#define TS_CMD_IFS 11 //IFS Enabled -#define TS_CMD_IFSVALUE 12 //IFS VALUE +#define TS_CMD_LOCK 9 //Dial Lock +#define TS_CMD_ATT 10 //ATT +#define TS_CMD_IFS 11 //IFS Enabled +#define TS_CMD_IFSVALUE 12 //IFS VALUE +#define TS_CMD_STARTADC 13 +#define TS_CMD_STOPADC 14 +#define TS_CMD_SPECTRUMOPT 15 //Option for Spectrum +#define TS_CMD_SPECTRUM 16 //Get Spectrum Value +#define TS_CMD_SWTRIG 21 //SW Action Trigger for WSPR and more char nowdisp = 0; @@ -190,6 +195,7 @@ char nowdisp = 0; //Control must have prefix 'v' or 's' char softSTRHeader[11] = {'p', 'm', '.', 's', '0', '.', 't', 'x', 't', '=', '\"'}; char softINTHeader[10] = {'p', 'm', '.', 'v', '0', '.', 'v', 'a', 'l', '='}; +const byte ADCIndex[6] = {A0, A1, A2, A3, A6, A7}; //send data for Nextion LCD void SendHeader(char varType, char varIndex) @@ -614,6 +620,60 @@ void updateDisplay() { sendUIData(0); //UI } +uint8_t SpectrumHeader[11]={'p', 'm', '.', 's', 'h', '.', 't', 'x', 't', '=', '"'}; +uint8_t SpectrumFooter[4]={'"', 0xFF, 0xFF, 0xFF}; + +char HexCodes[16] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f', }; +void sendSpectrumData(unsigned long startFreq, unsigned long incStep, int scanCount, int delayTime, int sendCount) +{ + unsigned long beforFreq = frequency; + unsigned long k; + uint8_t adcBytes[200]; //Maximum 200 Step + + //Voltage drop + //scanResult[0] = analogRead(ANALOG_SMETER); + //adcBytes[0] = analogRead(ANALOG_SMETER); + //delay(10); + int readedValue = 0; + + for (int si = 0; si < sendCount; si++) + //while(1) + { + for (int i = 0; i < 11; i++) + SWSerial_Write(SpectrumHeader[i]); + + for (k = 0; k < scanCount; k ++) + { + //Sampling Range + //setScanFreq(startFreq + k * incStep); + setFrequency(startFreq + (k * incStep)); + + //Wait time for charging + delay(delayTime); + + //ADC + readedValue = analogRead(ANALOG_SMETER); + if (readedValue>255) + { + readedValue=255; + } + SWSerial_Write(HexCodes[readedValue >> 4]); + SWSerial_Write(HexCodes[readedValue & 0xf]); + } + + for (int i = 0; i < 4; i++) + SWSerial_Write(SpectrumFooter[i]); + + } //end of for +} + +//sendSpectrumData(unsigned long startFreq, unsigned int incStep, int scanCount, int delayTime, int sendCount) +//sendSpectrumData(frequency - (1000L * 50), 1000, 100, 0, 10); +int spectrumSendCount = 10; //count of full scan and Send +int spectrumDelayTime = 0; //Scan interval time +int spectrumScanCount = 100; //Maximum 200 +unsigned long spectrumIncStep = 1000; //Increaase Step + // tern static uint8_t swr_receive_buffer[20]; extern uint8_t receivedCommandLength; extern void SWSerial_Read(uint8_t * receive_cmdBuffer); @@ -711,6 +771,57 @@ void SWS_Process(void) { ifShiftValue = *(long *)(&swr_buffer[commandStartIndex + 4]); } + else if (commandType == TS_CMD_STARTADC) + { + int startIndex = swr_buffer[commandStartIndex + 4]; + int endIndex = swr_buffer[commandStartIndex + 5]; + int adcCheckInterval = swr_buffer[commandStartIndex + 6] * 10; + int nowCheckIndex = startIndex; + + while(1 == 1) + { + if (receivedCommandLength > 0) + { + //receivedCommandLength = 0; + break; + } + + //SendCommandL('x', analogRead(ADCIndex[swr_buffer[commandStartIndex + 4]])); + SendCommandL('n', nowCheckIndex); //Index Input + SendCommandL('x', analogRead(ADCIndex[nowCheckIndex++])); + + if (nowCheckIndex > endIndex) + nowCheckIndex = startIndex; + + delay(adcCheckInterval); + } //end of while + } + else if (commandType == TS_CMD_STOPADC) + { + //None Action + return; + } + else if (commandType == TS_CMD_SPECTRUM) + { + //sendSpectrumData(unsigned long startFreq, unsigned int incStep, int scanCount, int delayTime, int sendCount) + //sendSpectrumData(frequency - (1000L * 50), 1000, 100, 0, 10); + //*(long *)(&swr_buffer[commandStartIndex + 4]) + //sendSpectrumData(*(long *)(&swr_buffer[commandStartIndex + 4]), spectrumIncStep, spectrumScanCount, spectrumDelayTime, spectrumSendCount); + sendSpectrumData(*(long *)(&swr_buffer[commandStartIndex + 4]), 1000, 100, 0, 32); + } + else if (commandType == TS_CMD_SPECTRUMOPT) + { + //sendSpectrumData(unsigned long startFreq, unsigned int incStep, int scanCount, int delayTime, int sendCount) + //sendSpectrumData(frequency - (1000L * 50), 1000, 100, 0, 10); + spectrumSendCount = swr_buffer[commandStartIndex + 4]; //count of full scan and Send + spectrumDelayTime = swr_buffer[commandStartIndex + 5]; //Scan interval time + spectrumScanCount = swr_buffer[commandStartIndex + 6]; //Maximum 120 + spectrumIncStep = swr_buffer[commandStartIndex + 7] * 10; //Increaase Step + } + else if (commandType == TS_CMD_SWTRIG) + { + TriggerBySW = 1; //Action Trigger by Software + } setFrequency(frequency); SetCarrierFreq(); @@ -725,8 +836,6 @@ char checkCountSMeter = 0; //execute interval : 0.25sec void idle_process() { - sendUIData(1); - //S-Meter Display if (((displayOption1 & 0x08) == 0x08 && (sdrModeOn == 0)) && (++checkCountSMeter > SMeterLatency)) { @@ -750,6 +859,8 @@ void idle_process() checkCountSMeter = 0; //Reset Latency time } //end of S-Meter + + sendUIData(1); } //When boot time, send data diff --git a/ubitx_20/ubitx_menu.ino b/ubitx_20/ubitx_menu.ino index 5452901..bca69ef 100644 --- a/ubitx_20/ubitx_menu.ino +++ b/ubitx_20/ubitx_menu.ino @@ -1227,6 +1227,7 @@ void doMenu(){ //Below codes are origial code with modified by KD8CEC menuOn = 2; + TriggerBySW = 0; //Nextion LCD and Other MCU while (menuOn){ i = enc_read(); diff --git a/ubitx_20/ubitx_wspr.ino b/ubitx_20/ubitx_wspr.ino index 2e65c14..824f9f7 100644 --- a/ubitx_20/ubitx_wspr.ino +++ b/ubitx_20/ubitx_wspr.ino @@ -147,9 +147,16 @@ void SendWSPRManage() } 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 From 3050374504532ca062c43c95c1641e4eb278128d Mon Sep 17 00:00:00 2001 From: phdlee Date: Tue, 12 Jun 2018 00:30:50 +0900 Subject: [PATCH 132/173] Second Deploy for Nextion LCD --- ubitx_20/ubitx.h | 5 ++--- ubitx_20/ubitx_lcd_nextion.ino | 2 +- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/ubitx_20/ubitx.h b/ubitx_20/ubitx.h index 23f626f..0ccdd6b 100644 --- a/ubitx_20/ubitx.h +++ b/ubitx_20/ubitx.h @@ -24,15 +24,14 @@ //============================================================================== //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_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 //NEXTION LCD //#define UBITX_CONTROL_MCU //CONTROL MCU - #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 diff --git a/ubitx_20/ubitx_lcd_nextion.ino b/ubitx_20/ubitx_lcd_nextion.ino index c538968..9f78862 100644 --- a/ubitx_20/ubitx_lcd_nextion.ino +++ b/ubitx_20/ubitx_lcd_nextion.ino @@ -878,7 +878,7 @@ void SendUbitxData(void) SendCommandL(CMD_DISP_OPTION1, displayOption1); SendCommandL(CMD_DISP_OPTION2, displayOption2); - SendCommandStr(CMD_VERSION, "+v1.0N1"); //Version + SendCommandStr(CMD_VERSION, "+v1.092"); //Version SendEEPromData(CMD_CALLSIGN, 0, userCallsignLength -1, 0); //Complte Send Info From 9da71429cb4c63ed4fd951f0339e4fcaf3eff152 Mon Sep 17 00:00:00 2001 From: phdlee Date: Wed, 13 Jun 2018 23:15:58 +0900 Subject: [PATCH 133/173] added protocol --- ubitx_20/cw_autokey.ino | 24 -------------- ubitx_20/ubitx.h | 6 ++-- ubitx_20/ubitx_20.ino | 2 -- ubitx_20/ubitx_lcd_nextion.ino | 59 +++++++++++++++++++++++++++++++--- 4 files changed, 59 insertions(+), 32 deletions(-) diff --git a/ubitx_20/cw_autokey.ino b/ubitx_20/cw_autokey.ino index 58cda6b..9bf838e 100644 --- a/ubitx_20/cw_autokey.ino +++ b/ubitx_20/cw_autokey.ino @@ -235,30 +235,6 @@ void sendCWChar(char cwKeyChar) } } -/* -void sendAutoCW(int cwSendLength, char *sendString) -{ - byte i; - - if (!inTx){ - keyDown = 0; - cwTimeout = millis() + cwDelayTime * 10; - startTx(TX_CW, 0); //disable updateDisplay Command for reduce latency time - updateDisplay(); - - delay_background(delayBeforeCWStartTime * 2, 2); - } - - for (i = 0; i < cwSendLength; i++) - { - sendCWChar(sendString[i]); - if (i != cwSendLength -1) delay_background(cwSpeed * 3, 3); - } - - delay_background(cwDelayTime * 10, 2); - stopTx(); -} -*/ byte isNeedScroll = 0; unsigned long scrollDispayTime = 0; #define scrollSpeed 500 diff --git a/ubitx_20/ubitx.h b/ubitx_20/ubitx.h index 0ccdd6b..2dbebf8 100644 --- a/ubitx_20/ubitx.h +++ b/ubitx_20/ubitx.h @@ -24,12 +24,12 @@ //============================================================================== //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_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 //NEXTION LCD //#define UBITX_CONTROL_MCU //CONTROL MCU #define I2C_LCD_MASTER_ADDRESS_DEFAULT 0x27 //0x27 //DEFAULT, if Set I2C Address by uBITX Manager, read from EEProm @@ -58,9 +58,11 @@ extern byte I2C_LCD_SECOND_ADDRESS; //only using Dual LCD Mode #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 diff --git a/ubitx_20/ubitx_20.ino b/ubitx_20/ubitx_20.ino index 8892311..c0ab129 100644 --- a/ubitx_20/ubitx_20.ino +++ b/ubitx_20/ubitx_20.ino @@ -1311,8 +1311,6 @@ void setup() if (userCallsignLength > 0 && ((userCallsignLength & 0x80) == 0x80)) { userCallsignLength = userCallsignLength & 0x7F; - //printLineFromEEPRom(0, 0, 0, userCallsignLength -1, 0); //eeprom to lcd use offset (USER_CALLSIGN_DAT) - //delay(500); DisplayCallsign(userCallsignLength); } else { diff --git a/ubitx_20/ubitx_lcd_nextion.ino b/ubitx_20/ubitx_lcd_nextion.ino index 9f78862..023fa4e 100644 --- a/ubitx_20/ubitx_lcd_nextion.ino +++ b/ubitx_20/ubitx_lcd_nextion.ino @@ -185,6 +185,9 @@ byte L_displayOption2; //byte displayOption2 (Reserve) #define TS_CMD_SPECTRUMOPT 15 //Option for Spectrum #define TS_CMD_SPECTRUM 16 //Get Spectrum Value #define TS_CMD_SWTRIG 21 //SW Action Trigger for WSPR and more +#define TS_CMD_READMEM 31 //Read EEProm +#define TS_CMD_WRITEMEM 32 //Write EEProm +#define TS_CMD_FACTORYRESET 33 //Factory Reset char nowdisp = 0; @@ -782,13 +785,11 @@ void SWS_Process(void) { if (receivedCommandLength > 0) { - //receivedCommandLength = 0; break; } - //SendCommandL('x', analogRead(ADCIndex[swr_buffer[commandStartIndex + 4]])); - SendCommandL('n', nowCheckIndex); //Index Input SendCommandL('x', analogRead(ADCIndex[nowCheckIndex++])); + SendCommandL('n', nowCheckIndex); //Index Input if (nowCheckIndex > endIndex) nowCheckIndex = startIndex; @@ -805,7 +806,6 @@ void SWS_Process(void) { //sendSpectrumData(unsigned long startFreq, unsigned int incStep, int scanCount, int delayTime, int sendCount) //sendSpectrumData(frequency - (1000L * 50), 1000, 100, 0, 10); - //*(long *)(&swr_buffer[commandStartIndex + 4]) //sendSpectrumData(*(long *)(&swr_buffer[commandStartIndex + 4]), spectrumIncStep, spectrumScanCount, spectrumDelayTime, spectrumSendCount); sendSpectrumData(*(long *)(&swr_buffer[commandStartIndex + 4]), 1000, 100, 0, 32); } @@ -822,6 +822,39 @@ void SWS_Process(void) { TriggerBySW = 1; //Action Trigger by Software } + else if (commandType == TS_CMD_READMEM || commandType == TS_CMD_WRITEMEM) //Read Mem + { + uint16_t eepromIndex = *(uint16_t *)(&swr_buffer[commandStartIndex + 4]); + byte eepromData = swr_buffer[commandStartIndex + 6]; + byte eepromCheckSum = swr_buffer[commandStartIndex + 7]; + + //Check Checksum + if (eepromCheckSum == (swr_buffer[commandStartIndex + 4] + swr_buffer[commandStartIndex + 5] + swr_buffer[commandStartIndex + 6])) + { + if (commandType == TS_CMD_WRITEMEM) + { + if (eepromIndex > 64) + EEPROM.write(eepromIndex, eepromData); + } + else + { + SendCommandL('x', EEPROM.read(eepromIndex)); + } + } + else + { + eepromIndex = -2; + } + SendCommandL('n', eepromIndex); //Index Input + } + else if (commandType == TS_CMD_FACTORYRESET) + { + if (*(unsigned long *)&swr_buffer[commandStartIndex + 4] == 1497712748) + { + for (unsigned int i = 0; i < 32; i++) //factory setting range + EEPROM.write(i, EEPROM.read(FACTORY_VALUES + i)); //65~96 => 0~31 + } + } setFrequency(frequency); SetCarrierFreq(); @@ -881,6 +914,24 @@ void SendUbitxData(void) SendCommandStr(CMD_VERSION, "+v1.092"); //Version SendEEPromData(CMD_CALLSIGN, 0, userCallsignLength -1, 0); + /* + //Frequency of Bands + for (int i = 0; i < 11; i++) + SWSerial_Write(SpectrumHeader[i]); + + byte *tmpByte; + tmpByte = (byte *)hamBandRange; + for (byte i = 0; i < (useHamBandCount -1) * 4; i++) + { + SWSerial_Write(HexCodes[*tmpByte >> 4]); + SWSerial_Write(HexCodes[*tmpByte & 0xf]); + tmpByte++; + } + + for (int i = 0; i < 4; i++) + SWSerial_Write(SpectrumFooter[i]); + */ + //Complte Send Info SendCommand1Num(CMD_UBITX_INFO, 1); From edadce7d896ce3d83db2050c93aa390fb8ce0c23 Mon Sep 17 00:00:00 2001 From: phdlee Date: Fri, 15 Jun 2018 10:34:52 +0900 Subject: [PATCH 134/173] add protocol about eeprom --- ubitx_20/ubitx_20.ino | 4 +- ubitx_20/ubitx_lcd_nextion.ino | 111 ++++++++++++++++++++++----------- 2 files changed, 77 insertions(+), 38 deletions(-) diff --git a/ubitx_20/ubitx_20.ino b/ubitx_20/ubitx_20.ino index c0ab129..670f3d8 100644 --- a/ubitx_20/ubitx_20.ino +++ b/ubitx_20/ubitx_20.ino @@ -6,8 +6,8 @@ // 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.091") -#define FIRMWARE_VERSION_NUM 0x03 //1st Complete Project : 1 (Version 1.061), 2st Project : 2 +#define FIRMWARE_VERSION_INFO F("+v1.093") +#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 diff --git a/ubitx_20/ubitx_lcd_nextion.ino b/ubitx_20/ubitx_lcd_nextion.ino index 023fa4e..0df8ccd 100644 --- a/ubitx_20/ubitx_lcd_nextion.ino +++ b/ubitx_20/ubitx_lcd_nextion.ino @@ -188,6 +188,7 @@ byte L_displayOption2; //byte displayOption2 (Reserve) #define TS_CMD_READMEM 31 //Read EEProm #define TS_CMD_WRITEMEM 32 //Write EEProm #define TS_CMD_FACTORYRESET 33 //Factory Reset +#define TS_CMD_UBITX_REBOOT 95 //Reboot char nowdisp = 0; @@ -623,11 +624,24 @@ void updateDisplay() { sendUIData(0); //UI } -uint8_t SpectrumHeader[11]={'p', 'm', '.', 's', 'h', '.', 't', 'x', 't', '=', '"'}; -uint8_t SpectrumFooter[4]={'"', 0xFF, 0xFF, 0xFF}; +#define RESPONSE_SPECTRUM 0 +#define RESPONSE_EEPROM 1 +#define RESPONSE_EEPROM_HEX 0 +#define RESPONSE_EEPROM_STR 1 + +uint8_t ResponseHeader[11]={'p', 'm', '.', 's', 'h', '.', 't', 'x', 't', '=', '"'}; +uint8_t ResponseFooter[4]={'"', 0xFF, 0xFF, 0xFF}; char HexCodes[16] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f', }; -void sendSpectrumData(unsigned long startFreq, unsigned long incStep, int scanCount, int delayTime, int sendCount) +//void sendSpectrumData(unsigned long startFreq, unsigned long incStep, int scanCount, int delayTime, int sendCount) +//sendResponseData(RESPONSE_EEPROM, 0, eepromIndex, eepromReadLength, eepromDataType, 1); +//protocol Type : 0 - Spectrum, 1 : EEProm +//startFreq : Spectrum - Frequency, EEProm - 0 +//sendOption1 : Spectrum - 1 Step Frequency, EEProm - EEProm Start Address +//scanCount : Spectrum - 1 Set Length, EEProm - Read Length +//sendOption2 : Spectrum - Value offset (because support various S-Meter), EEProm - EEProm Response DataType (0:HEX, 1:String) +//sendCount : Spectrum - All scan set count, EEProm - always 1 +void sendResponseData(int protocolType, unsigned long startFreq, unsigned int sendOption1, int readCount, int sendOption2, int sendCount) //Spectrum and EEProm Data { unsigned long beforFreq = frequency; unsigned long k; @@ -640,32 +654,45 @@ void sendSpectrumData(unsigned long startFreq, unsigned long incStep, int scanCo int readedValue = 0; for (int si = 0; si < sendCount; si++) - //while(1) { for (int i = 0; i < 11; i++) - SWSerial_Write(SpectrumHeader[i]); + SWSerial_Write(ResponseHeader[i]); - for (k = 0; k < scanCount; k ++) + for (k = 0; k < readCount; k ++) { - //Sampling Range - //setScanFreq(startFreq + k * incStep); - setFrequency(startFreq + (k * incStep)); - - //Wait time for charging - delay(delayTime); - - //ADC - readedValue = analogRead(ANALOG_SMETER); - if (readedValue>255) + if (protocolType == RESPONSE_SPECTRUM) { - readedValue=255; + //Spectrum Data + //Sampling Range + setFrequency(startFreq + (k * sendOption1)); + //Wait time for charging + delay(sendOption2); + + //ADC + readedValue = analogRead(ANALOG_SMETER); + if (readedValue>255) + { + readedValue=255; + } + } + else + { + readedValue = EEPROM.read(k + sendOption1); + } + + if (protocolType == RESPONSE_EEPROM && sendOption2 == RESPONSE_EEPROM_STR) //None HEX + { + SWSerial_Write(readedValue); + } + else + { + SWSerial_Write(HexCodes[readedValue >> 4]); + SWSerial_Write(HexCodes[readedValue & 0xf]); } - SWSerial_Write(HexCodes[readedValue >> 4]); - SWSerial_Write(HexCodes[readedValue & 0xf]); } for (int i = 0; i < 4; i++) - SWSerial_Write(SpectrumFooter[i]); + SWSerial_Write(ResponseFooter[i]); } //end of for } @@ -673,9 +700,9 @@ void sendSpectrumData(unsigned long startFreq, unsigned long incStep, int scanCo //sendSpectrumData(unsigned long startFreq, unsigned int incStep, int scanCount, int delayTime, int sendCount) //sendSpectrumData(frequency - (1000L * 50), 1000, 100, 0, 10); int spectrumSendCount = 10; //count of full scan and Send -int spectrumDelayTime = 0; //Scan interval time +int spectrumOffset = 0; //offset position int spectrumScanCount = 100; //Maximum 200 -unsigned long spectrumIncStep = 1000; //Increaase Step +unsigned int spectrumIncStep = 1000; //Increaase Step // tern static uint8_t swr_receive_buffer[20]; extern uint8_t receivedCommandLength; @@ -807,14 +834,14 @@ void SWS_Process(void) //sendSpectrumData(unsigned long startFreq, unsigned int incStep, int scanCount, int delayTime, int sendCount) //sendSpectrumData(frequency - (1000L * 50), 1000, 100, 0, 10); //sendSpectrumData(*(long *)(&swr_buffer[commandStartIndex + 4]), spectrumIncStep, spectrumScanCount, spectrumDelayTime, spectrumSendCount); - sendSpectrumData(*(long *)(&swr_buffer[commandStartIndex + 4]), 1000, 100, 0, 32); + sendResponseData(RESPONSE_SPECTRUM, *(long *)(&swr_buffer[commandStartIndex + 4]), spectrumIncStep, spectrumScanCount, spectrumOffset, spectrumSendCount); } else if (commandType == TS_CMD_SPECTRUMOPT) { //sendSpectrumData(unsigned long startFreq, unsigned int incStep, int scanCount, int delayTime, int sendCount) //sendSpectrumData(frequency - (1000L * 50), 1000, 100, 0, 10); spectrumSendCount = swr_buffer[commandStartIndex + 4]; //count of full scan and Send - spectrumDelayTime = swr_buffer[commandStartIndex + 5]; //Scan interval time + spectrumOffset = swr_buffer[commandStartIndex + 5]; //Scan interval time spectrumScanCount = swr_buffer[commandStartIndex + 6]; //Maximum 120 spectrumIncStep = swr_buffer[commandStartIndex + 7] * 10; //Increaase Step } @@ -822,7 +849,15 @@ void SWS_Process(void) { TriggerBySW = 1; //Action Trigger by Software } - else if (commandType == TS_CMD_READMEM || commandType == TS_CMD_WRITEMEM) //Read Mem + else if (commandType == TS_CMD_READMEM ) //Read Mem + { + uint16_t eepromIndex = *(uint16_t *)(&swr_buffer[commandStartIndex + 4]); + byte eepromReadLength = swr_buffer[commandStartIndex + 6]; + byte eepromDataType = swr_buffer[commandStartIndex + 7]; //0 : Hex, 1 : String + + sendResponseData(RESPONSE_EEPROM, 0, eepromIndex, eepromReadLength, eepromDataType, 1); + } + else if (commandType == TS_CMD_WRITEMEM) //Write Mem { uint16_t eepromIndex = *(uint16_t *)(&swr_buffer[commandStartIndex + 4]); byte eepromData = swr_buffer[commandStartIndex + 6]; @@ -831,15 +866,8 @@ void SWS_Process(void) //Check Checksum if (eepromCheckSum == (swr_buffer[commandStartIndex + 4] + swr_buffer[commandStartIndex + 5] + swr_buffer[commandStartIndex + 6])) { - if (commandType == TS_CMD_WRITEMEM) - { if (eepromIndex > 64) EEPROM.write(eepromIndex, eepromData); - } - else - { - SendCommandL('x', EEPROM.read(eepromIndex)); - } } else { @@ -847,14 +875,25 @@ void SWS_Process(void) } SendCommandL('n', eepromIndex); //Index Input } - else if (commandType == TS_CMD_FACTORYRESET) + else if (commandType == TS_CMD_FACTORYRESET || commandType == TS_CMD_UBITX_REBOOT) { if (*(unsigned long *)&swr_buffer[commandStartIndex + 4] == 1497712748) { - for (unsigned int i = 0; i < 32; i++) //factory setting range - EEPROM.write(i, EEPROM.read(FACTORY_VALUES + i)); //65~96 => 0~31 + if (commandType == TS_CMD_UBITX_REBOOT) + { + asm volatile (" jmp 0"); + } + else + { + for (unsigned int i = 0; i < 32; i++) //factory setting range + EEPROM.write(i, EEPROM.read(FACTORY_VALUES + i)); //65~96 => 0~31 + } } } + //else if (commandType == TS_CMD_UBITX_REBOOT) + //{ + //asm volatile (" jmp 0"); + //} setFrequency(frequency); SetCarrierFreq(); @@ -911,7 +950,7 @@ void SendUbitxData(void) SendCommandL(CMD_DISP_OPTION1, displayOption1); SendCommandL(CMD_DISP_OPTION2, displayOption2); - SendCommandStr(CMD_VERSION, "+v1.092"); //Version + SendCommandStr(CMD_VERSION, "+v1.093"); //Version SendEEPromData(CMD_CALLSIGN, 0, userCallsignLength -1, 0); /* From c602fdde7cb0c910ce91b8156e59b741475f9b8a Mon Sep 17 00:00:00 2001 From: phdlee Date: Fri, 15 Jun 2018 20:48:51 +0900 Subject: [PATCH 135/173] Add EEProm Read by Nextion LCD Reversed order --- ubitx_20/ubitx_lcd_nextion.ino | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/ubitx_20/ubitx_lcd_nextion.ino b/ubitx_20/ubitx_lcd_nextion.ino index 0df8ccd..6f228b7 100644 --- a/ubitx_20/ubitx_lcd_nextion.ino +++ b/ubitx_20/ubitx_lcd_nextion.ino @@ -626,8 +626,9 @@ void updateDisplay() { #define RESPONSE_SPECTRUM 0 #define RESPONSE_EEPROM 1 -#define RESPONSE_EEPROM_HEX 0 -#define RESPONSE_EEPROM_STR 1 +#define RESPONSE_EEPROM_HEX_F 89 //C Language order +#define RESPONSE_EEPROM_HEX_R 72 //Nextion order (Reverse) +#define RESPONSE_EEPROM_STR 87 //String uint8_t ResponseHeader[11]={'p', 'm', '.', 's', 'h', '.', 't', 'x', 't', '=', '"'}; uint8_t ResponseFooter[4]={'"', 0xFF, 0xFF, 0xFF}; @@ -677,7 +678,7 @@ void sendResponseData(int protocolType, unsigned long startFreq, unsigned int se } else { - readedValue = EEPROM.read(k + sendOption1); + readedValue = EEPROM.read(((sendOption2 == RESPONSE_EEPROM_HEX_R) ? (readCount - k - 1) : k) + sendOption1); } if (protocolType == RESPONSE_EEPROM && sendOption2 == RESPONSE_EEPROM_STR) //None HEX From 0e13dd0267dbb72e11c3dfe4e196ffdeab321a43 Mon Sep 17 00:00:00 2001 From: phdlee Date: Fri, 15 Jun 2018 21:43:56 +0900 Subject: [PATCH 136/173] modified Checksum logic for Nextion LCD --- ubitx_20/ubitx_lcd_nextion.ino | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/ubitx_20/ubitx_lcd_nextion.ino b/ubitx_20/ubitx_lcd_nextion.ino index 6f228b7..b1212bb 100644 --- a/ubitx_20/ubitx_lcd_nextion.ino +++ b/ubitx_20/ubitx_lcd_nextion.ino @@ -865,7 +865,8 @@ void SWS_Process(void) byte eepromCheckSum = swr_buffer[commandStartIndex + 7]; //Check Checksum - if (eepromCheckSum == (swr_buffer[commandStartIndex + 4] + swr_buffer[commandStartIndex + 5] + swr_buffer[commandStartIndex + 6])) + //if (eepromCheckSum == (swr_buffer[commandStartIndex + 4] + swr_buffer[commandStartIndex + 5] + swr_buffer[commandStartIndex + 6])) + if (eepromCheckSum == (swr_buffer[commandStartIndex + 4] + swr_buffer[commandStartIndex + 5])) { if (eepromIndex > 64) EEPROM.write(eepromIndex, eepromData); From 82177199c44421ea2db51628ac234916b44fd565 Mon Sep 17 00:00:00 2001 From: phdlee Date: Sat, 16 Jun 2018 21:45:55 +0900 Subject: [PATCH 137/173] Increase Buffer for SW Serial and Modified Protocol for EEProm --- ubitx_20/softserial_tiny.cpp | 3 ++- ubitx_20/ubitx.h | 2 ++ ubitx_20/ubitx_lcd_nextion.ino | 49 ++++++++++++++++++++++------------ 3 files changed, 36 insertions(+), 18 deletions(-) diff --git a/ubitx_20/softserial_tiny.cpp b/ubitx_20/softserial_tiny.cpp index 55cee48..b742ca7 100644 --- a/ubitx_20/softserial_tiny.cpp +++ b/ubitx_20/softserial_tiny.cpp @@ -32,6 +32,7 @@ SWSerial_Write to sSerial.write SWSerial_Available to sSerial.available SWSerial_Read to sSerial.read +KD8CEC, Ian Lee ----------------------------------------------------------------------- License All licenses for the source code are subject to the license of the original source SoftwareSerial Library. @@ -76,7 +77,7 @@ http://arduiniana.org. //================================================================ #define TX_PIN 9 #define RX_PIN 8 -#define _SS_MAX_RX_BUFF 20 // RX buffer size +#define _SS_MAX_RX_BUFF 35 // RX buffer size #define PRINT_MAX_LENGTH 30 //================================================================ diff --git a/ubitx_20/ubitx.h b/ubitx_20/ubitx.h index 2dbebf8..fe31e85 100644 --- a/ubitx_20/ubitx.h +++ b/ubitx_20/ubitx.h @@ -32,9 +32,11 @@ #define UBITX_DISPLAY_NEXTION //NEXTION LCD //#define UBITX_CONTROL_MCU //CONTROL MCU +//#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 + #define EXTEND_KEY_GROUP1 //MODE, BAND(-), BAND(+), STEP //#define EXTEND_KEY_GROUP2 //Numeric (0~9), Point(.), Enter //Not supported in Version 1.0x diff --git a/ubitx_20/ubitx_lcd_nextion.ino b/ubitx_20/ubitx_lcd_nextion.ino index b1212bb..ac40929 100644 --- a/ubitx_20/ubitx_lcd_nextion.ino +++ b/ubitx_20/ubitx_lcd_nextion.ino @@ -41,7 +41,7 @@ char c[30], b[30]; char softBuff[20]; char softTemp[20]; -void LCD2004_Init() +void LCDNextion_Init() { SWSerial_Begin(9600); memset(softBuffLines[0], ' ', TEXT_LINE_LENGTH); @@ -52,8 +52,8 @@ void LCD2004_Init() void LCD_Init(void) { - LCD2004_Init(); - initMeter(); //for Meter Display + LCDNextion_Init(); + //initMeter(); //for Meter Display } //=================================================================== @@ -187,7 +187,7 @@ byte L_displayOption2; //byte displayOption2 (Reserve) #define TS_CMD_SWTRIG 21 //SW Action Trigger for WSPR and more #define TS_CMD_READMEM 31 //Read EEProm #define TS_CMD_WRITEMEM 32 //Write EEProm -#define TS_CMD_FACTORYRESET 33 //Factory Reset +#define TS_CMD_FACTORYRESET 85 //Factory Reset #define TS_CMD_UBITX_REBOOT 95 //Reboot char nowdisp = 0; @@ -630,10 +630,10 @@ void updateDisplay() { #define RESPONSE_EEPROM_HEX_R 72 //Nextion order (Reverse) #define RESPONSE_EEPROM_STR 87 //String -uint8_t ResponseHeader[11]={'p', 'm', '.', 's', 'h', '.', 't', 'x', 't', '=', '"'}; -uint8_t ResponseFooter[4]={'"', 0xFF, 0xFF, 0xFF}; +const uint8_t ResponseHeader[11]={'p', 'm', '.', 's', 'h', '.', 't', 'x', 't', '=', '"'}; +const uint8_t ResponseFooter[4]={'"', 0xFF, 0xFF, 0xFF}; -char HexCodes[16] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f', }; +const char HexCodes[16] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f', }; //void sendSpectrumData(unsigned long startFreq, unsigned long incStep, int scanCount, int delayTime, int sendCount) //sendResponseData(RESPONSE_EEPROM, 0, eepromIndex, eepromReadLength, eepromDataType, 1); //protocol Type : 0 - Spectrum, 1 : EEProm @@ -860,16 +860,35 @@ void SWS_Process(void) } else if (commandType == TS_CMD_WRITEMEM) //Write Mem { + /* + Address : 2 byte int + Length : Data Length + Checksum : (Addr0+Addr1+Len) %256 + Data : Variable (Max 23) + */ uint16_t eepromIndex = *(uint16_t *)(&swr_buffer[commandStartIndex + 4]); - byte eepromData = swr_buffer[commandStartIndex + 6]; - byte eepromCheckSum = swr_buffer[commandStartIndex + 7]; + byte writeLength = swr_buffer[commandStartIndex + 6]; + byte writeCheckSum = swr_buffer[commandStartIndex + 7]; //Check Checksum - //if (eepromCheckSum == (swr_buffer[commandStartIndex + 4] + swr_buffer[commandStartIndex + 5] + swr_buffer[commandStartIndex + 6])) - if (eepromCheckSum == (swr_buffer[commandStartIndex + 4] + swr_buffer[commandStartIndex + 5])) + if (writeCheckSum == (swr_buffer[commandStartIndex + 4] + swr_buffer[commandStartIndex + 5] + swr_buffer[commandStartIndex + 6])) + //if (writeCheckSum == (swr_buffer[commandStartIndex + 4] + swr_buffer[commandStartIndex + 5] + writeLength)) { - if (eepromIndex > 64) - EEPROM.write(eepromIndex, eepromData); + //if (eepromIndex > 64) //Safe #1 +#ifdef UBITX_DISPLAY_NEXTION_SAFE + //Safe #2 + if (eepromIndex < 770 || eepromIndex > 775 ) + { + eepromIndex = -2; + } + else +#else + if (1 == 1) +#endif + { + for (int i = 0; i < writeLength; i++) + EEPROM.write(eepromIndex + i , swr_buffer[commandStartIndex + 8 + i]); + } } else { @@ -892,10 +911,6 @@ void SWS_Process(void) } } } - //else if (commandType == TS_CMD_UBITX_REBOOT) - //{ - //asm volatile (" jmp 0"); - //} setFrequency(frequency); SetCarrierFreq(); From c73fffb25b9e2cf500788c09879f6dbc5db90881 Mon Sep 17 00:00:00 2001 From: phdlee Date: Sat, 16 Jun 2018 23:03:47 +0900 Subject: [PATCH 138/173] Modified Spectrum Protocol --- ubitx_20/ubitx_lcd_nextion.ino | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/ubitx_20/ubitx_lcd_nextion.ino b/ubitx_20/ubitx_lcd_nextion.ino index ac40929..9db365b 100644 --- a/ubitx_20/ubitx_lcd_nextion.ino +++ b/ubitx_20/ubitx_lcd_nextion.ino @@ -667,11 +667,18 @@ void sendResponseData(int protocolType, unsigned long startFreq, unsigned int se //Sampling Range setFrequency(startFreq + (k * sendOption1)); //Wait time for charging - delay(sendOption2); + //delay(10); //ADC readedValue = analogRead(ANALOG_SMETER); - if (readedValue>255) + readedValue -= (sendOption2 * 3); //0 ~ 765 + //Down Scale + readedValue /= 2; + if (readedValue < 0) + { + readedValue = 0; + } + else if (readedValue>255) { readedValue=255; } @@ -835,16 +842,18 @@ void SWS_Process(void) //sendSpectrumData(unsigned long startFreq, unsigned int incStep, int scanCount, int delayTime, int sendCount) //sendSpectrumData(frequency - (1000L * 50), 1000, 100, 0, 10); //sendSpectrumData(*(long *)(&swr_buffer[commandStartIndex + 4]), spectrumIncStep, spectrumScanCount, spectrumDelayTime, spectrumSendCount); + unsigned long beforeFreq = frequency; sendResponseData(RESPONSE_SPECTRUM, *(long *)(&swr_buffer[commandStartIndex + 4]), spectrumIncStep, spectrumScanCount, spectrumOffset, spectrumSendCount); + frequency = beforeFreq; } else if (commandType == TS_CMD_SPECTRUMOPT) { //sendSpectrumData(unsigned long startFreq, unsigned int incStep, int scanCount, int delayTime, int sendCount) //sendSpectrumData(frequency - (1000L * 50), 1000, 100, 0, 10); - spectrumSendCount = swr_buffer[commandStartIndex + 4]; //count of full scan and Send - spectrumOffset = swr_buffer[commandStartIndex + 5]; //Scan interval time - spectrumScanCount = swr_buffer[commandStartIndex + 6]; //Maximum 120 - spectrumIncStep = swr_buffer[commandStartIndex + 7] * 10; //Increaase Step + spectrumSendCount = swr_buffer[commandStartIndex + 4]; //count of full scan and Send + spectrumOffset = swr_buffer[commandStartIndex + 5]; //Scan interval time + spectrumScanCount = swr_buffer[commandStartIndex + 6]; //Maximum 120 + spectrumIncStep = swr_buffer[commandStartIndex + 7] * 20; //Increaase Step } else if (commandType == TS_CMD_SWTRIG) { From 22bb9ee11201731049ec27c2a6d0a05a0bb94eeb Mon Sep 17 00:00:00 2001 From: phdlee Date: Sun, 17 Jun 2018 01:19:37 +0900 Subject: [PATCH 139/173] Release 1.093Beta --- ubitx_20/ubitx_lcd_nextion.ino | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ubitx_20/ubitx_lcd_nextion.ino b/ubitx_20/ubitx_lcd_nextion.ino index 9db365b..d78247d 100644 --- a/ubitx_20/ubitx_lcd_nextion.ino +++ b/ubitx_20/ubitx_lcd_nextion.ino @@ -823,8 +823,8 @@ void SWS_Process(void) break; } - SendCommandL('x', analogRead(ADCIndex[nowCheckIndex++])); SendCommandL('n', nowCheckIndex); //Index Input + SendCommandL('x', analogRead(ADCIndex[nowCheckIndex++])); if (nowCheckIndex > endIndex) nowCheckIndex = startIndex; From fe44c703c5f8bb12f8f88d262888a2e3bb4cab8d Mon Sep 17 00:00:00 2001 From: phdlee Date: Tue, 19 Jun 2018 11:17:58 +0900 Subject: [PATCH 140/173] define eeprom map for external device and send eeprom data to nextion lcd --- ubitx_20/ubitx_20.ino | 2 +- ubitx_20/ubitx_eemap.h | 3 ++- ubitx_20/ubitx_lcd_nextion.ino | 9 ++++++--- 3 files changed, 9 insertions(+), 5 deletions(-) diff --git a/ubitx_20/ubitx_20.ino b/ubitx_20/ubitx_20.ino index 670f3d8..5d31c8d 100644 --- a/ubitx_20/ubitx_20.ino +++ b/ubitx_20/ubitx_20.ino @@ -6,7 +6,7 @@ // 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.093") +#define FIRMWARE_VERSION_INFO F("+v1.094") #define FIRMWARE_VERSION_NUM 0x04 //1st Complete Project : 1 (Version 1.061), 2st Project : 2, 1.08: 3, 1.09 : 4 /** diff --git a/ubitx_20/ubitx_eemap.h b/ubitx_20/ubitx_eemap.h index e799187..0e15e2b 100644 --- a/ubitx_20/ubitx_eemap.h +++ b/ubitx_20/ubitx_eemap.h @@ -112,7 +112,8 @@ #define CHANNEL_FREQ 630 //Channel 1 ~ 20, 1 Channel = 4 bytes #define CHANNEL_DESC 710 //Channel 1 ~ 20, 1 Channel = 4 bytes -#define RESERVE3 770 //Reserve3 between Channel and Firmware id check +#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. diff --git a/ubitx_20/ubitx_lcd_nextion.ino b/ubitx_20/ubitx_lcd_nextion.ino index d78247d..5aa46ac 100644 --- a/ubitx_20/ubitx_lcd_nextion.ino +++ b/ubitx_20/ubitx_lcd_nextion.ino @@ -969,14 +969,17 @@ void SendUbitxData(void) SendCommandL(CMD_AR_TUNE3, arTuneStep[2]); SendCommandL(CMD_AR_TUNE4, arTuneStep[3]); SendCommandL(CMD_AR_TUNE5, arTuneStep[4]); - + SendCommandL(CMD_IS_CW_SHIFT_DISPLAY, isShiftDisplayCWFreq); SendCommandL(CMD_CW_SHIFT_ADJUST, shiftDisplayAdjustVal); SendCommandL(CMD_COMM_OPTION, commonOption0); SendCommandL(CMD_DISP_OPTION1, displayOption1); - SendCommandL(CMD_DISP_OPTION2, displayOption2); - SendCommandStr(CMD_VERSION, "+v1.093"); //Version + unsigned long nextionDisplayOption; + EEPROM.get(EXTERNAL_DEVICE_OPT1, nextionDisplayOption); + SendCommandUL(CMD_DISP_OPTION2, nextionDisplayOption); + + SendCommandStr(CMD_VERSION, "+v1.094"); //Version SendEEPromData(CMD_CALLSIGN, 0, userCallsignLength -1, 0); /* From dd43ba4c335decd676b25fda08566aedcb41a5d4 Mon Sep 17 00:00:00 2001 From: phdlee Date: Thu, 28 Jun 2018 23:29:06 +0900 Subject: [PATCH 141/173] Modified about Loopback protocol --- ubitx_20/ubitx_20.ino | 12 +- ubitx_20/ubitx_lcd_nextion.ino | 25 ++- ubitx_20/ubitx_menu.ino | 314 +++++++++++---------------------- 3 files changed, 135 insertions(+), 216 deletions(-) diff --git a/ubitx_20/ubitx_20.ino b/ubitx_20/ubitx_20.ino index 5d31c8d..6e2f910 100644 --- a/ubitx_20/ubitx_20.ino +++ b/ubitx_20/ubitx_20.ino @@ -6,7 +6,7 @@ // 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.094") +#define FIRMWARE_VERSION_INFO F("+v1.095") #define FIRMWARE_VERSION_NUM 0x04 //1st Complete Project : 1 (Version 1.061), 2st Project : 2, 1.08: 3, 1.09 : 4 /** @@ -467,13 +467,17 @@ void startTx(byte txMode, byte isDisplayUpdate){ } else { - if (splitOn == 1) { - if (vfoActive == VFO_B) { + 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){ + else if (vfoActive == VFO_A) + { vfoActive = VFO_B; frequency = vfoB; byteToMode(vfoB_mode, 0); diff --git a/ubitx_20/ubitx_lcd_nextion.ino b/ubitx_20/ubitx_lcd_nextion.ino index 5aa46ac..06340ae 100644 --- a/ubitx_20/ubitx_lcd_nextion.ino +++ b/ubitx_20/ubitx_lcd_nextion.ino @@ -184,9 +184,16 @@ byte L_displayOption2; //byte displayOption2 (Reserve) #define TS_CMD_STOPADC 14 #define TS_CMD_SPECTRUMOPT 15 //Option for Spectrum #define TS_CMD_SPECTRUM 16 //Get Spectrum Value +#define TS_CMD_TUNESTEP 17 //Get Spectrum Value #define TS_CMD_SWTRIG 21 //SW Action Trigger for WSPR and more #define TS_CMD_READMEM 31 //Read EEProm #define TS_CMD_WRITEMEM 32 //Write EEProm +#define TS_CMD_LOOPBACK0 74 //Loopback1 (Response to Loopback Channgel) +#define TS_CMD_LOOPBACK1 75 //Loopback2 (Response to Loopback Channgel) +#define TS_CMD_LOOPBACK2 76 //Loopback3 (Response to Loopback Channgel) +#define TS_CMD_LOOPBACK3 77 //Loopback4 (Response to Loopback Channgel) +#define TS_CMD_LOOPBACK4 78 //Loopback5 (Response to Loopback Channgel) +#define TS_CMD_LOOPBACK5 79 //Loopback6 (Response to Loopback Channgel) #define TS_CMD_FACTORYRESET 85 //Factory Reset #define TS_CMD_UBITX_REBOOT 95 //Reboot @@ -776,7 +783,7 @@ void SWS_Process(void) } else if (commandType == TS_CMD_SPLIT) { - menuSplitOnOff(1); + menuSplitOnOff(10); } else if (commandType == TS_CMD_RIT) { @@ -855,6 +862,10 @@ void SWS_Process(void) spectrumScanCount = swr_buffer[commandStartIndex + 6]; //Maximum 120 spectrumIncStep = swr_buffer[commandStartIndex + 7] * 20; //Increaase Step } + else if (commandType == TS_CMD_TUNESTEP) //Set Tune Step + { + tuneStepIndex = swr_buffer[commandStartIndex + 4]; //Tune Step Index + } else if (commandType == TS_CMD_SWTRIG) { TriggerBySW = 1; //Action Trigger by Software @@ -903,7 +914,14 @@ void SWS_Process(void) { eepromIndex = -2; } - SendCommandL('n', eepromIndex); //Index Input + SendCommandL('n', eepromIndex); //Index Input + } + //else if (TS_CMD_LOOPBACK0 <= commandType && commandType <= TS_CMD_LOOPBACK5) //Loop back Channel 0 ~ 5 Loop back Channel 1~5 : Reserve + else if (TS_CMD_LOOPBACK0 == commandType) //Loop back Channel 0 ~ 5 + { + SendCommandUL('v', *(unsigned long *)&swr_buffer[commandStartIndex + 4]); //Return data + SendCommandUL('g', commandType); //Index Input + //return; } else if (commandType == TS_CMD_FACTORYRESET || commandType == TS_CMD_UBITX_REBOOT) { @@ -911,6 +929,7 @@ void SWS_Process(void) { if (commandType == TS_CMD_UBITX_REBOOT) { + FrequencyToVFO(1); //Save current Frequency and Mode to eeprom asm volatile (" jmp 0"); } else @@ -979,7 +998,7 @@ void SendUbitxData(void) EEPROM.get(EXTERNAL_DEVICE_OPT1, nextionDisplayOption); SendCommandUL(CMD_DISP_OPTION2, nextionDisplayOption); - SendCommandStr(CMD_VERSION, "+v1.094"); //Version + SendCommandStr(CMD_VERSION, "+v1.095"); //Version SendEEPromData(CMD_CALLSIGN, 0, userCallsignLength -1, 0); /* diff --git a/ubitx_20/ubitx_menu.ino b/ubitx_20/ubitx_menu.ino index bca69ef..80055c4 100644 --- a/ubitx_20/ubitx_menu.ino +++ b/ubitx_20/ubitx_menu.ino @@ -389,7 +389,14 @@ void menuVfoToggle(int btn) ritDisable(); setFrequency(frequency); - menuClearExit(0); + +#ifdef USE_SW_SERIAL + menuOn = 0; +#else + //Only Clear And Delay for Character LCD + menuClearExit(0); +#endif + } } @@ -406,17 +413,20 @@ void menuSplitOnOff(int btn){ if (splitOn == 1){ splitOn = 0; printLineF2(F("SPT Off")); - //printLineF2(F("[OFF]")); } else { splitOn = 1; if (ritOn == 1) ritOn = 0; printLineF2(F("SPT On")); - //printLineF2(F("[ON]")); } - + +#ifdef USE_SW_SERIAL + menuOn = 0; +#else +//Only Clear And Delay for Character LCD menuClearExit(500); +#endif } } @@ -438,8 +448,13 @@ void menuTxOnOff(int btn, byte optionType){ isTxType &= ~(optionType); printLineF2(F("TX ON")); } - + +#ifdef USE_SW_SERIAL + menuOn = 0; +#else + //Only Clear And Delay for Character LCD menuClearExit(500); +#endif } } @@ -472,7 +487,13 @@ void menuSDROnOff(int btn) 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 } } @@ -730,7 +751,14 @@ void menuCWSpeed(int btn){ //printLineF2(F("CW Speed set!")); cwSpeed = 1200 / wpm; EEPROM.put(CW_SPEED, cwSpeed); - menuClearExit(1000); + //menuClearExit(1000); +#ifdef USE_SW_SERIAL + menuOn = 0; +#else + //Only Clear And Delay for Character LCD + menuClearExit(1000); +#endif + } //Modified by KD8CEC @@ -751,44 +779,22 @@ void menuSetupCwTone(int btn){ sideTone = getValueByKnob(1, sideTone, 100, 2000, 10, "Tone", 2); //1 : Generate Tone, targetValue, minKnobValue, maxKnobValue, stepSize - /* - //disable all clock 1 and clock 2 - while (digitalRead(PTT) == HIGH && !btnDown()) - { - knob = enc_read(); - - if (knob > 0 && sideTone < 2000) - sideTone += 10; - else if (knob < 0 && sideTone > 100 ) - sideTone -= 10; - else - continue; //don't update the frequency or the display - - tone(CW_TONE, sideTone); - itoa(sideTone, b, 10); - printLine2(b); - - delay_background(100, 0); - } - */ - - noTone(CW_TONE); - //save the setting - //if (digitalRead(PTT) == LOW){ - printLineF2(F("Sidetone set!")); - EEPROM.put(CW_SIDETONE, sideTone); - delay_background(2000, 0); - //} - //else - // sideTone = prev_sideTone; - - menuClearExit(0); + 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; @@ -799,44 +805,18 @@ void menuSetupCwDelay(int btn){ return; } - //printLineF1(F("Press, set Delay")); - /* - strcpy(b, "DELAY:"); - itoa(tmpCWDelay,c, 10); - strcat(b, c); - printLine2(b); - */ - //delay_background(300, 0); - tmpCWDelay = getValueByKnob(0, tmpCWDelay, 3, 2500, 10, "Delay", 2); //0 : Generate Tone, targetValue, minKnobValue, maxKnobValue, stepSize -/* - while(!btnDown()){ - knob = enc_read(); - if (knob != 0){ - if (tmpCWDelay > 3 && knob < 0) - tmpCWDelay -= 10; - if (tmpCWDelay < 2500 && knob > 0) - tmpCWDelay += 10; - - strcpy(b, "DELAY:"); - itoa(tmpCWDelay,c, 10); - strcat(b, c); - printLine2(b); - } - //abort if this button is down - if (btnDown()) - break; - - Check_Cat(0); //To prevent disconnections - } -*/ - //save the setting - //printLineF2(F("CW Delay set!")); cwDelayTime = tmpCWDelay / 10; EEPROM.put(CW_DELAY, cwDelayTime); - menuClearExit(1000); + //menuClearExit(1000); +#ifdef USE_SW_SERIAL + menuOn = 0; +#else + //Only Clear And Delay for Character LCD + menuClearExit(1000); +#endif } //CW Time delay by KD8CEC @@ -855,41 +835,17 @@ void menuSetupTXCWInterval(int btn){ tmpTXCWInterval = getValueByKnob(0, tmpTXCWInterval, 0, 500, 2, "Delay", 2); //0 : Generate Tone, targetValue, minKnobValue, maxKnobValue, stepSize -/* - while(!btnDown()){ - - if (needDisplayInformation == 1) { - strcpy(b, "Start Delay:"); - itoa(tmpTXCWInterval,c, 10); - strcat(b, c); - printLine2(b); - needDisplayInformation = 0; - } - - knob = enc_read(); - if (knob != 0){ - if (tmpTXCWInterval > 0 && knob < 0) - tmpTXCWInterval -= 2; - if (tmpTXCWInterval < 500 && knob > 0) - tmpTXCWInterval += 2; - - needDisplayInformation = 1; - } - //abort if this button is down - //if (btnDown()) - // break; - - Check_Cat(0); //To prevent disconnections - } -*/ - - - //save the setting - //printLineF2(F("CW Start set!")); 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 - menuClearExit(1000); } //IF Shift function, BFO Change like RIT, by KD8CEC @@ -907,36 +863,7 @@ void menuIFSSetup(int btn){ { isIFShift = 1; - //delay_background(500, 0); - //updateLine2Buffer(1); - //setFrequency(frequency); - ifShiftValue = getValueByKnob(2, ifShiftValue, -20000, 20000, 50, "IFS", 2); //2 : IF Setup (updateLine2Buffer(1), SetFrequency), targetValue, minKnobValue, maxKnobValue, stepSize - -/* - //Off or Change Value - while(!btnDown() ){ - if (needApplyChangeValue ==1) - { - updateLine2Buffer(1); - setFrequency(frequency); - SetCarrierFreq(); - needApplyChangeValue = 0; - } - - knob = enc_read(); - if (knob != 0){ - if (knob < 0) - ifShiftValue -= 50; - else if (knob > 0) - ifShiftValue += 50; - - needApplyChangeValue = 1; - } - Check_Cat(0); //To prevent disconnections - } -*/ - delay_background(500, 0); //for check Long Press function key if (btnDown() || ifShiftValue == 0) @@ -949,7 +876,13 @@ void menuIFSSetup(int btn){ //Store IF Shiift EEPROM.put(IF_SHIFTVALUE, ifShiftValue); - menuClearExit(0); + //menuClearExit(0); +#ifdef USE_SW_SERIAL + menuOn = 0; +#else + //Only Clear And Delay for Character LCD + menuClearExit(0); +#endif } } @@ -975,7 +908,15 @@ void menuATTSetup(int btn){ setFrequency(frequency); //SetCarrierFreq(); } + //menuClearExit(0); + +#ifdef USE_SW_SERIAL + menuOn = 0; +#else + //Only Clear And Delay for Character LCD menuClearExit(0); +#endif + } } @@ -1002,44 +943,10 @@ void menuSelectMode(int btn){ selectModeType = 3; beforeMode = selectModeType; - - //delay_background(500, 0); - selectModeType = getValueByKnob(11, selectModeType, 0, 3, 1, " LSB USB CWL CWU", 4); //3 : Select Mode, targetValue, minKnobValue, maxKnobValue, stepSize -/* - while(!btnDown()){ - //Display Mode Name - memset(c, 0, sizeof(c)); - strcpy(c, " LSB USB CWL CWU"); - c[selectModeType * 4] = '>'; - printLine1(c); - - knob = enc_read(); - - if (knob != 0) - { - moveStep += (knob > 0 ? 1 : -1); - if (moveStep < -3) { - if (selectModeType > 0) - selectModeType--; - - moveStep = 0; - } - else if (moveStep > 3) { - if (selectModeType < 3) - selectModeType++; - - moveStep = 0; - } - } - - //Check_Cat(0); //To prevent disconnections - delay_background(50, 0); - } -*/ - - if (beforeMode != selectModeType) { + if (beforeMode != selectModeType) + { //printLineF1(F("Changed Mode")); if (selectModeType == 0) { cwMode = 0; isUSB = 0; @@ -1058,9 +965,14 @@ void menuSelectMode(int btn){ } SetCarrierFreq(); - setFrequency(frequency); + //menuClearExit(500); +#ifdef USE_SW_SERIAL + menuOn = 0; +#else + //Only Clear And Delay for Character LCD menuClearExit(500); +#endif } } @@ -1073,45 +985,11 @@ void menuSetupKeyType(int btn){ printLineF2(F("Change Key Type?")); } else { - //printLineF2(F("Press to set Key")); //for reduce usable flash memory - //delay_background(500, 0); 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 - /* - while(!btnDown()){ - - //Display Key Type - if (selectedKeyType == 0) - printLineF1(F("Straight")); - else if (selectedKeyType == 1) - printLineF1(F("IAMBICA")); - else if (selectedKeyType == 2) - printLineF1(F("IAMBICB")); - - knob = enc_read(); - - if (knob != 0) - { - moveStep += (knob > 0 ? 1 : -1); - if (moveStep < -3) { - if (selectedKeyType > 0) - selectedKeyType--; - moveStep = 0; - } - else if (moveStep > 3) { - if (selectedKeyType < 2) - selectedKeyType++; - moveStep = 0; - } - } - - Check_Cat(0); //To prevent disconnections - } - */ - printLineF2(F("CW Key Type set!")); cwKeyType = selectedKeyType; EEPROM.put(CW_KEY_TYPE, cwKeyType); @@ -1127,7 +1005,14 @@ void menuSetupKeyType(int btn){ keyerControl |= IAMBICB; } + //menuClearExit(1000); +#ifdef USE_SW_SERIAL + menuOn = 0; +#else + //Only Clear And Delay for Character LCD menuClearExit(1000); +#endif + } } @@ -1364,7 +1249,14 @@ void menuSetup(int btn){ else { modeCalibrate = ! modeCalibrate; + //menuClearExit(1000); + +#ifdef USE_SW_SERIAL + menuOn = 0; +#else + //Only Clear And Delay for Character LCD menuClearExit(1000); +#endif } } @@ -1395,13 +1287,17 @@ void menuRitToggle(int btn){ 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 From 1ce889eef04e2624a672aac9ac00d5e7df58cb24 Mon Sep 17 00:00:00 2001 From: phdlee Date: Thu, 5 Jul 2018 19:18:22 +0900 Subject: [PATCH 142/173] Release v1.095 Beta --- ubitx_20/ubitx_lcd_nextion.ino | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/ubitx_20/ubitx_lcd_nextion.ino b/ubitx_20/ubitx_lcd_nextion.ino index 06340ae..db587dc 100644 --- a/ubitx_20/ubitx_lcd_nextion.ino +++ b/ubitx_20/ubitx_lcd_nextion.ino @@ -761,7 +761,10 @@ void SWS_Process(void) { unsigned long *tempFreq; tempFreq = (unsigned long *)(&swr_buffer[commandStartIndex + 4]); - frequency = *tempFreq; + //if (*tempFreq > 3000) //for loss protcol + //{ + frequency = *tempFreq; + //} } else if (commandType == TS_CMD_BAND) { @@ -989,7 +992,7 @@ void SendUbitxData(void) SendCommandL(CMD_AR_TUNE4, arTuneStep[3]); SendCommandL(CMD_AR_TUNE5, arTuneStep[4]); - SendCommandL(CMD_IS_CW_SHIFT_DISPLAY, isShiftDisplayCWFreq); + SendCommand1Num(CMD_IS_CW_SHIFT_DISPLAY, isShiftDisplayCWFreq); SendCommandL(CMD_CW_SHIFT_ADJUST, shiftDisplayAdjustVal); SendCommandL(CMD_COMM_OPTION, commonOption0); SendCommandL(CMD_DISP_OPTION1, displayOption1); From 41548163cf9b57ac67575f470e192bbb2ba8c798 Mon Sep 17 00:00:00 2001 From: phdlee Date: Tue, 17 Jul 2018 11:21:24 +0900 Subject: [PATCH 143/173] Support I2C S-Meter --- ubitx_20/ubitx_20.ino | 2 +- ubitx_20/ubitx_lcd_nextion.ino | 62 ++++++++++++++++++++++++++++++++-- 2 files changed, 60 insertions(+), 4 deletions(-) diff --git a/ubitx_20/ubitx_20.ino b/ubitx_20/ubitx_20.ino index 6e2f910..0281112 100644 --- a/ubitx_20/ubitx_20.ino +++ b/ubitx_20/ubitx_20.ino @@ -6,7 +6,7 @@ // 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.095") +#define FIRMWARE_VERSION_INFO F("+v1.097") #define FIRMWARE_VERSION_NUM 0x04 //1st Complete Project : 1 (Version 1.061), 2st Project : 2, 1.08: 3, 1.09 : 4 /** diff --git a/ubitx_20/ubitx_lcd_nextion.ino b/ubitx_20/ubitx_lcd_nextion.ino index db587dc..1ed8436 100644 --- a/ubitx_20/ubitx_lcd_nextion.ino +++ b/ubitx_20/ubitx_lcd_nextion.ino @@ -56,6 +56,53 @@ void LCD_Init(void) //initMeter(); //for Meter Display } +//=================================================================== +//I2C Signal Meter, Version 1.097 +// +//=================================================================== + +#define USE_I2CSMETER +//S-Meter Address +#define I2CMETER_ADDR 0x6A +//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 + +// 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); + for (int i = 0; i < 100; i++) + { + if (Wire.available() > 0) + { + return Wire.read(); + } + else + { + delay(1); + } + } +} + //=================================================================== //Begin of Nextion LCD Protocol // @@ -675,6 +722,10 @@ void sendResponseData(int protocolType, unsigned long startFreq, unsigned int se setFrequency(startFreq + (k * sendOption1)); //Wait time for charging //delay(10); + +#ifdef USE_I2CSMETER + readedValue = GetI2CSmeterValue(I2CMETER_UNCALCS); +#else //ADC readedValue = analogRead(ANALOG_SMETER); @@ -689,6 +740,7 @@ void sendResponseData(int protocolType, unsigned long startFreq, unsigned int se { readedValue=255; } +#endif } else { @@ -960,7 +1012,10 @@ void idle_process() 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 newSMeter = analogRead(ANALOG_SMETER) / 4; @@ -976,7 +1031,8 @@ void idle_process() break; } } - + +#endif checkCountSMeter = 0; //Reset Latency time } //end of S-Meter @@ -1001,7 +1057,7 @@ void SendUbitxData(void) EEPROM.get(EXTERNAL_DEVICE_OPT1, nextionDisplayOption); SendCommandUL(CMD_DISP_OPTION2, nextionDisplayOption); - SendCommandStr(CMD_VERSION, "+v1.095"); //Version + SendCommandStr(CMD_VERSION, "+v1.097"); //Version SendEEPromData(CMD_CALLSIGN, 0, userCallsignLength -1, 0); /* From b984f62dfd6b2652e7165b69cc1bba3e8c467811 Mon Sep 17 00:00:00 2001 From: phdlee Date: Tue, 17 Jul 2018 20:13:06 +0900 Subject: [PATCH 144/173] Add I2C Scan, Change DSP Meter I2C --- ubitx_20/cat_libs.ino | 31 +++++++++++++++++++++++++++---- ubitx_20/ubitx.h | 3 ++- ubitx_20/ubitx_20.ino | 9 +++++++++ ubitx_20/ubitx_eemap.h | 5 ++++- ubitx_20/ubitx_lcd_nextion.ino | 2 +- ubitx_20/ubitx_si5351.ino | 3 ++- 6 files changed, 45 insertions(+), 8 deletions(-) diff --git a/ubitx_20/cat_libs.ino b/ubitx_20/cat_libs.ino index 3e71855..608af79 100644 --- a/ubitx_20/cat_libs.ino +++ b/ubitx_20/cat_libs.ino @@ -252,12 +252,35 @@ void ReadEEPRom() //for remove warnings. Serial.write(0x02); //STX checkSum = 0x02; - for (uint16_t i = 0; i < eepromReadLength; i++) + //I2C Scanner + //Magic Key Start 59414, Length : 48583 + //if (eepromStartIndex == 59414 && eepromReadLength == 48583) + if (CAT_BUFF[0] == 0x16 && CAT_BUFF[1] == 0xe8) { - read1Byte = EEPROM.read(eepromStartIndex + i); - checkSum += read1Byte; - Serial.write(read1Byte); + 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); } diff --git a/ubitx_20/ubitx.h b/ubitx_20/ubitx.h index fe31e85..0c7f3e2 100644 --- a/ubitx_20/ubitx.h +++ b/ubitx_20/ubitx.h @@ -30,7 +30,6 @@ //#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_CONTROL_MCU //CONTROL MCU //#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 @@ -161,6 +160,8 @@ extern byte I2C_LCD_SECOND_ADDRESS; //only using Dual LCD Mode #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]; diff --git a/ubitx_20/ubitx_20.ino b/ubitx_20/ubitx_20.ino index 0281112..06f8d2d 100644 --- a/ubitx_20/ubitx_20.ino +++ b/ubitx_20/ubitx_20.ino @@ -958,6 +958,15 @@ void initSettings(){ 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) { diff --git a/ubitx_20/ubitx_eemap.h b/ubitx_20/ubitx_eemap.h index 0e15e2b..cc37111 100644 --- a/ubitx_20/ubitx_eemap.h +++ b/ubitx_20/ubitx_eemap.h @@ -35,10 +35,13 @@ //============================================================================== // The spare space available in the original firmware #1 -// Address : 32 ~ 63 +// Address : 32 ~ 62 //============================================================================== #define RESERVE_FOR_FACTORY1 32 +//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) diff --git a/ubitx_20/ubitx_lcd_nextion.ino b/ubitx_20/ubitx_lcd_nextion.ino index 1ed8436..2fc746f 100644 --- a/ubitx_20/ubitx_lcd_nextion.ino +++ b/ubitx_20/ubitx_lcd_nextion.ino @@ -63,7 +63,7 @@ void LCD_Init(void) #define USE_I2CSMETER //S-Meter Address -#define I2CMETER_ADDR 0x6A +#define I2CMETER_ADDR 0x58 //VALUE TYPE============================================ //Signal #define I2CMETER_CALCS 0x59 //Calculated Signal Meter diff --git a/ubitx_20/ubitx_si5351.ino b/ubitx_20/ubitx_si5351.ino index 2708b36..049cf0b 100644 --- a/ubitx_20/ubitx_si5351.ino +++ b/ubitx_20/ubitx_si5351.ino @@ -47,7 +47,8 @@ #define BB1(x) ((uint8_t)(x>>8)) #define BB2(x) ((uint8_t)(x>>16)) -#define SI5351BX_ADDR 0x60 // I2C address of Si5351 (typical) +//#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 From c27bbf1b6bcd8db4374006ec577c47fdf48202f4 Mon Sep 17 00:00:00 2001 From: phdlee Date: Tue, 17 Jul 2018 20:41:17 +0900 Subject: [PATCH 145/173] Apply DSP meter to all lcd types --- ubitx_20/ubitx.h | 25 ++++++++++++- ubitx_20/ubitx_lcd_1602.ino | 4 +++ ubitx_20/ubitx_lcd_1602Dual.ino | 5 ++- ubitx_20/ubitx_lcd_2004.ino | 6 +++- ubitx_20/ubitx_lcd_nextion.ino | 63 ++++++--------------------------- ubitx_20/ubitx_ui.ino | 30 ++++++++++++++++ 6 files changed, 77 insertions(+), 56 deletions(-) diff --git a/ubitx_20/ubitx.h b/ubitx_20/ubitx.h index 0c7f3e2..d7c5118 100644 --- a/ubitx_20/ubitx.h +++ b/ubitx_20/ubitx.h @@ -29,12 +29,14 @@ //#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 //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 @@ -120,6 +122,24 @@ extern byte I2C_LCD_SECOND_ADDRESS; //only using Dual LCD Mode #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 //============================================================================== @@ -196,6 +216,9 @@ 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_lcd_1602.ino b/ubitx_20/ubitx_lcd_1602.ino index 67ca38c..76f2e5b 100644 --- a/ubitx_20/ubitx_lcd_1602.ino +++ b/ubitx_20/ubitx_lcd_1602.ino @@ -738,6 +738,9 @@ void idle_process() { 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; @@ -752,6 +755,7 @@ void idle_process() break; } } +#endif DisplayMeter(0, scaledSMeter, 13); checkCountSMeter = 0; //Reset Latency time diff --git a/ubitx_20/ubitx_lcd_1602Dual.ino b/ubitx_20/ubitx_lcd_1602Dual.ino index 211b3ef..48598cb 100644 --- a/ubitx_20/ubitx_lcd_1602Dual.ino +++ b/ubitx_20/ubitx_lcd_1602Dual.ino @@ -669,6 +669,9 @@ void idle_process() int newSMeter; displaySDRON = 0; +#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; @@ -684,9 +687,9 @@ void idle_process() break; } } +#endif DisplayMeter(0, scaledSMeter, 0); - checkCountSMeter = 0; } //end of S-Meter _Addr = I2C_LCD_MASTER_ADDRESS; diff --git a/ubitx_20/ubitx_lcd_2004.ino b/ubitx_20/ubitx_lcd_2004.ino index 2a5a2da..06d44fa 100644 --- a/ubitx_20/ubitx_lcd_2004.ino +++ b/ubitx_20/ubitx_lcd_2004.ino @@ -690,7 +690,10 @@ void idle_process() 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 newSMeter = analogRead(ANALOG_SMETER) / 4; @@ -706,6 +709,7 @@ void idle_process() break; } } +#endif DisplayMeter(0, scaledSMeter, 0); checkCountSMeter = 0; //Reset Latency time diff --git a/ubitx_20/ubitx_lcd_nextion.ino b/ubitx_20/ubitx_lcd_nextion.ino index 2fc746f..23d70ab 100644 --- a/ubitx_20/ubitx_lcd_nextion.ino +++ b/ubitx_20/ubitx_lcd_nextion.ino @@ -1,5 +1,8 @@ /************************************************************************* KD8CEC's uBITX Display Routine for Nextion LCD + + Uses the default protocol of Nextion LCD. + Do not assign a 2 byte address to Nextion LCD. ----------------------------------------------------------------------------- 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 @@ -56,53 +59,6 @@ void LCD_Init(void) //initMeter(); //for Meter Display } -//=================================================================== -//I2C Signal Meter, Version 1.097 -// -//=================================================================== - -#define USE_I2CSMETER -//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 - -// 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); - for (int i = 0; i < 100; i++) - { - if (Wire.available() > 0) - { - return Wire.read(); - } - else - { - delay(1); - } - } -} - //=================================================================== //Begin of Nextion LCD Protocol // @@ -678,6 +634,9 @@ void updateDisplay() { sendUIData(0); //UI } +//**************************************************************** +// Spectrum for Range scan and Band Scan +//**************************************************************** #define RESPONSE_SPECTRUM 0 #define RESPONSE_EEPROM 1 #define RESPONSE_EEPROM_HEX_F 89 //C Language order @@ -686,8 +645,8 @@ void updateDisplay() { const uint8_t ResponseHeader[11]={'p', 'm', '.', 's', 'h', '.', 't', 'x', 't', '=', '"'}; const uint8_t ResponseFooter[4]={'"', 0xFF, 0xFF, 0xFF}; - const char HexCodes[16] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f', }; + //void sendSpectrumData(unsigned long startFreq, unsigned long incStep, int scanCount, int delayTime, int sendCount) //sendResponseData(RESPONSE_EEPROM, 0, eepromIndex, eepromReadLength, eepromDataType, 1); //protocol Type : 0 - Spectrum, 1 : EEProm @@ -764,17 +723,15 @@ void sendResponseData(int protocolType, unsigned long startFreq, unsigned int se } //end of for } -//sendSpectrumData(unsigned long startFreq, unsigned int incStep, int scanCount, int delayTime, int sendCount) -//sendSpectrumData(frequency - (1000L * 50), 1000, 100, 0, 10); +//**************************************************************** +//Receive command and processing from External device (LCD or MCU) +//**************************************************************** int spectrumSendCount = 10; //count of full scan and Send int spectrumOffset = 0; //offset position int spectrumScanCount = 100; //Maximum 200 unsigned int spectrumIncStep = 1000; //Increaase Step - -// tern static uint8_t swr_receive_buffer[20]; extern uint8_t receivedCommandLength; extern void SWSerial_Read(uint8_t * receive_cmdBuffer); -//extern void byteToMode(byte modeValue, byte autoSetModebyFreq); uint8_t swr_buffer[20]; //SoftwareSerial_Process diff --git a/ubitx_20/ubitx_ui.ino b/ubitx_20/ubitx_ui.ino index aa3bae9..91b271c 100644 --- a/ubitx_20/ubitx_ui.ino +++ b/ubitx_20/ubitx_ui.ino @@ -268,4 +268,34 @@ int enc_read(void) { 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); + for (int i = 0; i < 100; i++) + { + if (Wire.available() > 0) + { + return Wire.read(); + } + else + { + delay(1); + } + } +} + From 4ee3631db0ee79902753d1c366d15ad64414334f Mon Sep 17 00:00:00 2001 From: phdlee Date: Tue, 17 Jul 2018 21:10:45 +0900 Subject: [PATCH 146/173] Change menu type (selectable functions) --- ubitx_20/ubitx.h | 85 +++++++++++++++++- ubitx_20/ubitx_20.ino | 67 -------------- ubitx_20/ubitx_menu.ino | 190 +++++++++++++++++++++++++++++++++++++++- ubitx_20/ubitx_wspr.ino | 2 - 4 files changed, 270 insertions(+), 74 deletions(-) diff --git a/ubitx_20/ubitx.h b/ubitx_20/ubitx.h index d7c5118..4dafcb0 100644 --- a/ubitx_20/ubitx.h +++ b/ubitx_20/ubitx.h @@ -36,7 +36,7 @@ #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 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 @@ -47,9 +47,90 @@ 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 1 //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 + +/* +//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) diff --git a/ubitx_20/ubitx_20.ino b/ubitx_20/ubitx_20.ino index 06f8d2d..95d3519 100644 --- a/ubitx_20/ubitx_20.ino +++ b/ubitx_20/ubitx_20.ino @@ -682,74 +682,7 @@ void checkButton(){ menuRitToggle(1); break; } - /* - if (keyStatus == FKEY_MODE) //Press Mode Key - { - if (cwMode == 1) - { - cwMode = 2; - } - else if (cwMode == 2) - { - cwMode = 0; - isUSB = 0; - } - else if (isUSB == 0) - { - isUSB = 1; - } - else - { - cwMode = 1; - } - } - else if (keyStatus == FKEY_BANDUP || keyStatus == FKEY_BANDDOWN) //Press Mode Key - { - char currentBandIndex = -1; - - //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 - } - else if (keyStatus == FKEY_STEP) //FKEY_BANDUP - { - if (++tuneStepIndex > 5) - tuneStepIndex = 1; - - EEPROM.put(TUNING_STEP, tuneStepIndex); - printLine2ClearAndUpdate(); - } - - else if (keyStatus == FKEY_VFOCHANGE) - { - menuVfoToggle(1); //Vfo Toggle - } - else if (keyStatus == FKEY_SPLIT) - { - menuSplitOnOff(1); - } - else if (keyStatus == FKEY_TXOFF) - { - menuTxOnOff(1, 0x01); - } - else if (keyStatus == FKEY_SDRMODE) - { - menuSDROnOff(1); - } - else if (keyStatus == FKEY_RIT) - { - menuRitToggle(1); - } - */ - FrequencyToVFO(1); SetCarrierFreq(); setFrequency(frequency); diff --git a/ubitx_20/ubitx_menu.ino b/ubitx_20/ubitx_menu.ino index 80055c4..9bcd126 100644 --- a/ubitx_20/ubitx_menu.ino +++ b/ubitx_20/ubitx_menu.ino @@ -1113,7 +1113,190 @@ void doMenu(){ //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(); @@ -1208,9 +1391,10 @@ void doMenu(){ break; default : menuExit(btnState); break; - } + } //end of case Check_Cat(0); //To prevent disconnections - } + } //end of while + */ } //************************************************************************************* diff --git a/ubitx_20/ubitx_wspr.ino b/ubitx_20/ubitx_wspr.ino index 824f9f7..a9281f6 100644 --- a/ubitx_20/ubitx_wspr.ino +++ b/ubitx_20/ubitx_wspr.ino @@ -6,8 +6,6 @@ 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 -Beta Tester : - ----------------------------------------------------------------------------- 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 From 7c1ee29500ad10b3d05125a00db08f900e0d2ee6 Mon Sep 17 00:00:00 2001 From: phdlee Date: Sat, 28 Jul 2018 18:53:28 +0900 Subject: [PATCH 147/173] Before Release V1.096 --- ubitx_20/softserial_tiny.cpp | 2 +- ubitx_20/ubitx.h | 26 ++++++++++++++++++++ ubitx_20/ubitx_20.ino | 6 +++-- ubitx_20/ubitx_lcd_nextion.ino | 44 +++++++++++++++++++++++++++------- ubitx_20/ubitx_ui.ino | 17 +++++++------ 5 files changed, 74 insertions(+), 21 deletions(-) diff --git a/ubitx_20/softserial_tiny.cpp b/ubitx_20/softserial_tiny.cpp index b742ca7..4738e87 100644 --- a/ubitx_20/softserial_tiny.cpp +++ b/ubitx_20/softserial_tiny.cpp @@ -103,7 +103,7 @@ uint16_t _rx_delay_intrabit; //Customize for uBITX Protocol int8_t receiveIndex = 0; -int8_t receivedCommandLength = 0; +uint8_t receivedCommandLength = 0; int8_t ffCount = 0; //Values for Receive Buffer diff --git a/ubitx_20/ubitx.h b/ubitx_20/ubitx.h index 4dafcb0..f954d78 100644 --- a/ubitx_20/ubitx.h +++ b/ubitx_20/ubitx.h @@ -76,6 +76,32 @@ extern byte I2C_LCD_SECOND_ADDRESS; //only using Dual LCD Mode #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 diff --git a/ubitx_20/ubitx_20.ino b/ubitx_20/ubitx_20.ino index 95d3519..c6e45be 100644 --- a/ubitx_20/ubitx_20.ino +++ b/ubitx_20/ubitx_20.ino @@ -1,4 +1,4 @@ -//Firmware Version + //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. @@ -1255,6 +1255,8 @@ void setup() initSettings(); initPorts(); +#ifndef USE_SW_SERIAL +//for Chracter LCD if (userCallsignLength > 0 && ((userCallsignLength & 0x80) == 0x80)) { userCallsignLength = userCallsignLength & 0x7F; DisplayCallsign(userCallsignLength); @@ -1264,7 +1266,7 @@ void setup() delay(500); clearLine2(); } - +#endif #ifdef FACTORY_RECOVERY_BOOTUP if (btnDown()) diff --git a/ubitx_20/ubitx_lcd_nextion.ino b/ubitx_20/ubitx_lcd_nextion.ino index 23d70ab..cbac8ac 100644 --- a/ubitx_20/ubitx_lcd_nextion.ino +++ b/ubitx_20/ubitx_lcd_nextion.ino @@ -56,7 +56,6 @@ void LCDNextion_Init() void LCD_Init(void) { LCDNextion_Init(); - //initMeter(); //for Meter Display } //=================================================================== @@ -113,16 +112,16 @@ byte L_scaledSMeter; //scaledSMeter #define CMD_SIDE_TONE 't' //vt unsigned long L_sideTone; //sideTone #define CMD_KEY_TYPE 'k' //ck -byte L_cwKeyType; //L_cwKeyType 0: straight, 1 : iambica, 2: iambicb +byte L_cwKeyType = -1; //L_cwKeyType 0: straight, 1 : iambica, 2: iambicb #define CMD_CW_SPEED 's' //vs unsigned int L_cwSpeed; //cwSpeed #define CMD_CW_DELAY 'y' //vy -byte L_cwDelayTime; //cwDelayTime +byte L_cwDelayTime=-1; //cwDelayTime #define CMD_CW_STARTDELAY 'e' //ve -byte L_delayBeforeCWStartTime; //byte delayBeforeCWStartTime +byte L_delayBeforeCWStartTime=-1; //byte delayBeforeCWStartTime #define CMD_ATT_LEVEL 'f' //vf byte L_attLevel; @@ -188,6 +187,9 @@ byte L_displayOption2; //byte displayOption2 (Reserve) #define TS_CMD_SPECTRUMOPT 15 //Option for Spectrum #define TS_CMD_SPECTRUM 16 //Get Spectrum Value #define TS_CMD_TUNESTEP 17 //Get Spectrum Value +#define TS_CMD_WPM 18 //Set WPM +#define TS_CMD_KEYTYPE 19 //Set KeyType + #define TS_CMD_SWTRIG 21 //SW Action Trigger for WSPR and more #define TS_CMD_READMEM 31 //Read EEProm #define TS_CMD_WRITEMEM 32 //Write EEProm @@ -316,7 +318,7 @@ void SendEEPromData(char varIndex, int eepromStartIndex, int eepromEndIndex, cha SWSerial_Write(0xFF); } -char softBuff1Num[14] = {'p', 'm', '.', 'c', '0', '.', 'v', 'a', 'l', '=', 0, 0xFF, 0xFF, 0xFF}; +uint8_t softBuff1Num[14] = {'p', 'm', '.', 'c', '0', '.', 'v', 'a', 'l', '=', 0, 0xFF, 0xFF, 0xFF}; void SendCommand1Num(char varType, char sendValue) //0~9 : Mode, nowDisp, ActiveVFO, IsDialLock, IsTxtType, IsSplitType { softBuff1Num[4] = varType; @@ -876,7 +878,27 @@ void SWS_Process(void) } else if (commandType == TS_CMD_TUNESTEP) //Set Tune Step { - tuneStepIndex = swr_buffer[commandStartIndex + 4]; //Tune Step Index + tuneStepIndex = swr_buffer[commandStartIndex + 4]; //Tune Step Index + } + else if (commandType == TS_CMD_WPM) //Set WPM + { + cwSpeed = swr_buffer[commandStartIndex + 4]; // + } + else if (commandType == TS_CMD_KEYTYPE) //Set Key Type + { + cwKeyType = swr_buffer[commandStartIndex + 4]; + + //for reduce program memory + Iambic_Key = cwKeyType != 0; + //if (cwKeyType == 0) + // Iambic_Key = false; + //else + //Iambic_Key = true; + if (cwKeyType == 1) + keyerControl &= ~IAMBICB; + else + keyerControl |= IAMBICB; + //} } else if (commandType == TS_CMD_SWTRIG) { @@ -968,11 +990,11 @@ void idle_process() //S-Meter Display if (((displayOption1 & 0x08) == 0x08 && (sdrModeOn == 0)) && (++checkCountSMeter > SMeterLatency)) { - int newSMeter; - #ifdef USE_I2CSMETER scaledSMeter = GetI2CSmeterValue(I2CMETER_CALCS); #else + int newSMeter; + //VK2ETA S-Meter from MAX9814 TC pin newSMeter = analogRead(ANALOG_SMETER) / 4; @@ -999,6 +1021,10 @@ void idle_process() //When boot time, send data void SendUbitxData(void) { + //Wait for ready other device (LCD, DSP and more) + //delay(500); + delay_background(500, 2); + SendCommandL(CMD_AR_TUNE1, arTuneStep[0]); SendCommandL(CMD_AR_TUNE2, arTuneStep[1]); SendCommandL(CMD_AR_TUNE3, arTuneStep[2]); @@ -1014,7 +1040,7 @@ void SendUbitxData(void) EEPROM.get(EXTERNAL_DEVICE_OPT1, nextionDisplayOption); SendCommandUL(CMD_DISP_OPTION2, nextionDisplayOption); - SendCommandStr(CMD_VERSION, "+v1.097"); //Version + SendCommandStr(CMD_VERSION, (char *)("+v1.097")); //Version SendEEPromData(CMD_CALLSIGN, 0, userCallsignLength -1, 0); /* diff --git a/ubitx_20/ubitx_ui.ino b/ubitx_20/ubitx_ui.ino index 91b271c..8ab1579 100644 --- a/ubitx_20/ubitx_ui.ino +++ b/ubitx_20/ubitx_ui.ino @@ -285,17 +285,16 @@ int GetI2CSmeterValue(int valueType) } Wire.requestFrom(I2CMETER_ADDR, 1); - for (int i = 0; i < 100; i++) + + if (Wire.available() > 0) { - if (Wire.available() > 0) - { - return Wire.read(); - } - else - { - delay(1); - } + return Wire.read(); } + //else + //{ + // delay(10); + // return Wire.read(); + //} } From e79dbdbbe7312a4cc90b07e6c9919ef1fbd988cd Mon Sep 17 00:00:00 2001 From: phdlee Date: Mon, 6 Aug 2018 13:59:55 +0900 Subject: [PATCH 148/173] for Release Version 1.097 --- ubitx_20/ubitx.h | 8 ++--- ubitx_20/ubitx_20.ino | 10 ++++-- ubitx_20/ubitx_lcd_nextion.ino | 61 +++++++++++++++++----------------- ubitx_20/ubitx_ui.ino | 9 +++-- 4 files changed, 47 insertions(+), 41 deletions(-) diff --git a/ubitx_20/ubitx.h b/ubitx_20/ubitx.h index f954d78..fedb46e 100644 --- a/ubitx_20/ubitx.h +++ b/ubitx_20/ubitx.h @@ -28,8 +28,8 @@ //#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_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 @@ -187,7 +187,7 @@ extern byte I2C_LCD_SECOND_ADDRESS; //only using Dual LCD Mode * 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 + * Pin 1 (Violet), A7, SPARE => Analog S-Meter * Pin 2 (Blue), A6, KEYER (DATA) * Pin 3 (Green), +5v * Pin 4 (Yellow), Gnd @@ -217,7 +217,7 @@ extern byte I2C_LCD_SECOND_ADDRESS; //only using Dual LCD Mode * The second set of 16 pins on the Raduino's bottom connector are have the three clock outputs and the digital lines to control the rig. * This assignment is as follows : * Pin 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 - * GND +5V CLK0 GND GND CLK1 GND GND CLK2 GND D2 D3 D4 D5 D6 D7 + * 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 diff --git a/ubitx_20/ubitx_20.ino b/ubitx_20/ubitx_20.ino index c6e45be..c9b235f 100644 --- a/ubitx_20/ubitx_20.ino +++ b/ubitx_20/ubitx_20.ino @@ -1255,9 +1255,15 @@ void setup() initSettings(); initPorts(); -#ifndef USE_SW_SERIAL +#ifdef USE_SW_SERIAL +// if (userCallsignLength > 0 && ((userCallsignLength & 0x80) == 0x80)) +// { + userCallsignLength = userCallsignLength & 0x7F; +// } +#else //for Chracter LCD - if (userCallsignLength > 0 && ((userCallsignLength & 0x80) == 0x80)) { + if (userCallsignLength > 0 && ((userCallsignLength & 0x80) == 0x80)) + { userCallsignLength = userCallsignLength & 0x7F; DisplayCallsign(userCallsignLength); } diff --git a/ubitx_20/ubitx_lcd_nextion.ino b/ubitx_20/ubitx_lcd_nextion.ino index cbac8ac..096b86b 100644 --- a/ubitx_20/ubitx_lcd_nextion.ino +++ b/ubitx_20/ubitx_lcd_nextion.ino @@ -230,16 +230,37 @@ void SendHeader(char varType, char varIndex) } } +#define INT_ETX 0 +#define STR_ETX 1 +#define TMP_ETX 2 +//Send 0xFF, 0xFF, 0xFF +//etxType : INT_ETX = 0xFF, 0xFF, 0xFF +// STR_ETX = ", 0xFF, 0xFF, 0xFF +// TEMP_ETX = softTemp, 0xFF, 0xFF, 0xff + +void SendCommandETX(char etxType) +{ + if (etxType == 2) + { + SWSerial_Print(softTemp); + } + else if (etxType == 1) + { + SWSerial_Print("\""); + } + + SWSerial_Write(0xff); + SWSerial_Write(0xff); + SWSerial_Write(0xff); +} + void SendCommandUL(char varIndex, unsigned long sendValue) { SendHeader(SWS_HEADER_INT_TYPE, varIndex); memset(softTemp, 0, 20); ultoa(sendValue, softTemp, DEC); - SWSerial_Print(softTemp); - SWSerial_Write(0xff); - SWSerial_Write(0xff); - SWSerial_Write(0xff); + SendCommandETX(TMP_ETX); } void SendCommandL(char varIndex, long sendValue) @@ -248,10 +269,7 @@ void SendCommandL(char varIndex, long sendValue) memset(softTemp, 0, 20); ltoa(sendValue, softTemp, DEC); - SWSerial_Print(softTemp); - SWSerial_Write(0xff); - SWSerial_Write(0xff); - SWSerial_Write(0xff); + SendCommandETX(TMP_ETX); } void SendCommandStr(char varIndex, char* sendValue) @@ -259,10 +277,7 @@ void SendCommandStr(char varIndex, char* sendValue) SendHeader(SWS_HEADER_STR_TYPE, varIndex); SWSerial_Print(sendValue); - SWSerial_Write('\"'); - SWSerial_Write(0xFF); - SWSerial_Write(0xFF); - SWSerial_Write(0xFF); + SendCommandETX(STR_ETX); } //Send String data with duplicate check @@ -274,10 +289,7 @@ void SendTextLineBuff(char lineNumber) SendHeader(SWS_HEADER_STR_TYPE, lineNumber + 0x30); //s0.txt, s1.txt SWSerial_Print(softBuffLines[lineNumber]); - SWSerial_Write('\"'); - SWSerial_Write(0xFF); - SWSerial_Write(0xFF); - SWSerial_Write(0xFF); + SendCommandETX(STR_ETX); strcpy(softBuffSended[lineNumber], softBuffLines[lineNumber]); } @@ -312,10 +324,7 @@ void SendEEPromData(char varIndex, int eepromStartIndex, int eepromEndIndex, cha SWSerial_Write(EEPROM.read((offsetTtype == 0 ? USER_CALLSIGN_DAT : WSPR_MESSAGE1) + i)); } - SWSerial_Write('\"'); - SWSerial_Write(0xFF); - SWSerial_Write(0xFF); - SWSerial_Write(0xFF); + SendCommandETX(STR_ETX); } uint8_t softBuff1Num[14] = {'p', 'm', '.', 'c', '0', '.', 'v', 'a', 'l', '=', 0, 0xFF, 0xFF, 0xFF}; @@ -646,7 +655,6 @@ void updateDisplay() { #define RESPONSE_EEPROM_STR 87 //String const uint8_t ResponseHeader[11]={'p', 'm', '.', 's', 'h', '.', 't', 'x', 't', '=', '"'}; -const uint8_t ResponseFooter[4]={'"', 0xFF, 0xFF, 0xFF}; const char HexCodes[16] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f', }; //void sendSpectrumData(unsigned long startFreq, unsigned long incStep, int scanCount, int delayTime, int sendCount) @@ -718,10 +726,8 @@ void sendResponseData(int protocolType, unsigned long startFreq, unsigned int se SWSerial_Write(HexCodes[readedValue & 0xf]); } } - - for (int i = 0; i < 4; i++) - SWSerial_Write(ResponseFooter[i]); - + + SendCommandETX(STR_ETX); } //end of for } @@ -995,12 +1001,7 @@ void idle_process() #else int newSMeter; - //VK2ETA S-Meter from MAX9814 TC pin newSMeter = analogRead(ANALOG_SMETER) / 4; - - //Faster attack, Slower release - //currentSMeter = (newSMeter > currentSMeter ? ((currentSMeter * 3 + newSMeter * 7) + 5) / 10 : ((currentSMeter * 7 + newSMeter * 3) + 5) / 10); - //currentSMeter = ((currentSMeter * 7 + newSMeter * 3) + 5) / 10; currentSMeter = newSMeter; scaledSMeter = 0; diff --git a/ubitx_20/ubitx_ui.ino b/ubitx_20/ubitx_ui.ino index 8ab1579..0aa7c30 100644 --- a/ubitx_20/ubitx_ui.ino +++ b/ubitx_20/ubitx_ui.ino @@ -290,11 +290,10 @@ int GetI2CSmeterValue(int valueType) { return Wire.read(); } - //else - //{ - // delay(10); - // return Wire.read(); - //} + else + { + return 0; + } } From 9ff8365c3f7bd49ee67a88b30f23b1764218298a Mon Sep 17 00:00:00 2001 From: phdlee Date: Fri, 7 Sep 2018 23:37:23 +0900 Subject: [PATCH 149/173] Add Custom LPF Filter and Changed Version Number --- ubitx_20/ubitx.h | 9 ++++--- ubitx_20/ubitx_20.ino | 48 ++++++++++++++++++++++++++++++++-- ubitx_20/ubitx_eemap.h | 14 ++++++++++ ubitx_20/ubitx_lcd_nextion.ino | 2 +- 4 files changed, 67 insertions(+), 6 deletions(-) diff --git a/ubitx_20/ubitx.h b/ubitx_20/ubitx.h index fedb46e..de44003 100644 --- a/ubitx_20/ubitx.h +++ b/ubitx_20/ubitx.h @@ -28,19 +28,22 @@ //#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_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 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. diff --git a/ubitx_20/ubitx_20.ino b/ubitx_20/ubitx_20.ino index c9b235f..00412f1 100644 --- a/ubitx_20/ubitx_20.ino +++ b/ubitx_20/ubitx_20.ino @@ -6,7 +6,7 @@ // 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.097") +#define FIRMWARE_VERSION_INFO F("+v1.100") #define FIRMWARE_VERSION_NUM 0x04 //1st Complete Project : 1 (Version 1.061), 2st Project : 2, 1.08: 3, 1.09 : 4 /** @@ -195,6 +195,13 @@ 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 @@ -317,7 +324,24 @@ byte delay_background(unsigned delayTime, byte fromType){ //fromType : 4 autoCWK */ 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(A7, aIn & 0x08); + } + return; + } + } //end of for +#else if (freq > 21000000L){ // the default filter is with 35 MHz cut-off digitalWrite(TX_LPF_A, 0); digitalWrite(TX_LPF_B, 0); @@ -338,6 +362,8 @@ void setTXFilters(unsigned long freq){ digitalWrite(TX_LPF_B, 1); digitalWrite(TX_LPF_C, 1); } + +#endif } /** @@ -951,6 +977,24 @@ void initSettings(){ 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 diff --git a/ubitx_20/ubitx_eemap.h b/ubitx_20/ubitx_eemap.h index cc37111..c86ac1d 100644 --- a/ubitx_20/ubitx_eemap.h +++ b/ubitx_20/ubitx_eemap.h @@ -39,6 +39,20 @@ //============================================================================== #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 diff --git a/ubitx_20/ubitx_lcd_nextion.ino b/ubitx_20/ubitx_lcd_nextion.ino index 096b86b..8cb035c 100644 --- a/ubitx_20/ubitx_lcd_nextion.ino +++ b/ubitx_20/ubitx_lcd_nextion.ino @@ -1041,7 +1041,7 @@ void SendUbitxData(void) EEPROM.get(EXTERNAL_DEVICE_OPT1, nextionDisplayOption); SendCommandUL(CMD_DISP_OPTION2, nextionDisplayOption); - SendCommandStr(CMD_VERSION, (char *)("+v1.097")); //Version + SendCommandStr(CMD_VERSION, (char *)("+v1.100")); //Version SendEEPromData(CMD_CALLSIGN, 0, userCallsignLength -1, 0); /* From c34e798313c0a1df8ee573322ce6d52448e66e89 Mon Sep 17 00:00:00 2001 From: phdlee Date: Tue, 11 Sep 2018 18:09:22 +0900 Subject: [PATCH 150/173] Update README.md --- README.md | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/README.md b/README.md index 38fc97b..35e9177 100644 --- a/README.md +++ b/README.md @@ -24,6 +24,27 @@ Prepared or finished tasks for the next version ---------------------------------------------------------------------------- ## REVISION RECORD +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) From 450f57ae0f28c64b8006c2fec190151dc366ba5d Mon Sep 17 00:00:00 2001 From: phdlee Date: Sat, 22 Sep 2018 18:56:23 +0900 Subject: [PATCH 151/173] Added Custom LPF Control --- ubitx_20/ubitx_20.ino | 19 ++++++++++++++++--- ubitx_20/ubitx_lcd_nextion.ino | 2 +- 2 files changed, 17 insertions(+), 4 deletions(-) diff --git a/ubitx_20/ubitx_20.ino b/ubitx_20/ubitx_20.ino index 00412f1..2b23fb0 100644 --- a/ubitx_20/ubitx_20.ino +++ b/ubitx_20/ubitx_20.ino @@ -6,7 +6,7 @@ // 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.100") +#define FIRMWARE_VERSION_INFO F("+v1.110") #define FIRMWARE_VERSION_NUM 0x04 //1st Complete Project : 1 (Version 1.061), 2st Project : 2, 1.08: 3, 1.09 : 4 /** @@ -327,7 +327,7 @@ 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]) + if (freq >= CustFilters[i][0]) { char aIn = CustFilters[i][1]; digitalWrite(TX_LPF_A, aIn & 0x01); @@ -336,7 +336,10 @@ void setTXFilters(unsigned long freq){ if (isCustomFilter_A7 == 1) { - digitalWrite(A7, aIn & 0x08); + digitalWrite(10, aIn & 0x08); + digitalWrite(11, aIn & 0x10); + digitalWrite(12, aIn & 0x20); + digitalWrite(13, aIn & 0x40); } return; } @@ -1211,6 +1214,16 @@ void initPorts(){ 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); diff --git a/ubitx_20/ubitx_lcd_nextion.ino b/ubitx_20/ubitx_lcd_nextion.ino index 8cb035c..fdec271 100644 --- a/ubitx_20/ubitx_lcd_nextion.ino +++ b/ubitx_20/ubitx_lcd_nextion.ino @@ -1041,7 +1041,7 @@ void SendUbitxData(void) EEPROM.get(EXTERNAL_DEVICE_OPT1, nextionDisplayOption); SendCommandUL(CMD_DISP_OPTION2, nextionDisplayOption); - SendCommandStr(CMD_VERSION, (char *)("+v1.100")); //Version + SendCommandStr(CMD_VERSION, (char *)("+v1.110")); //Version SendEEPromData(CMD_CALLSIGN, 0, userCallsignLength -1, 0); /* From 2c075d52369bb655595f66e1ec46e7d4a277d550 Mon Sep 17 00:00:00 2001 From: phdlee Date: Fri, 15 Feb 2019 19:32:07 +0900 Subject: [PATCH 152/173] for uBITX v5 --- ubitx_20/softserial_tiny.cpp | 2 +- ubitx_20/ubitx.h | 5 +- ubitx_20/ubitx_20.ino | 94 ++++++++++++++++++++-------- ubitx_20/ubitx_factory_alignment.ino | 15 ++++- ubitx_20/ubitx_lcd_nextion.ino | 2 +- ubitx_20/ubitx_menu.ino | 10 ++- ubitx_20/ubitx_si5351.ino | 22 ++++++- 7 files changed, 115 insertions(+), 35 deletions(-) diff --git a/ubitx_20/softserial_tiny.cpp b/ubitx_20/softserial_tiny.cpp index 4738e87..6023c67 100644 --- a/ubitx_20/softserial_tiny.cpp +++ b/ubitx_20/softserial_tiny.cpp @@ -70,7 +70,7 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA The latest version of this library can always be found at http://arduiniana.org. */ -#include +#include //================================================================ //Public Variable diff --git a/ubitx_20/ubitx.h b/ubitx_20/ubitx.h index de44003..2eb279d 100644 --- a/ubitx_20/ubitx.h +++ b/ubitx_20/ubitx.h @@ -22,6 +22,9 @@ //============================================================================== // Compile Option //============================================================================== +//Ubitx Board Version +#define UBITX_BOARD_VERSION 4 //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) @@ -330,5 +333,3 @@ extern void DisplayVersionInfo(const char* fwVersionInfo); 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 index 2b23fb0..cdbe69b 100644 --- a/ubitx_20/ubitx_20.ino +++ b/ubitx_20/ubitx_20.ino @@ -6,7 +6,7 @@ // 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.110") +#define FIRMWARE_VERSION_INFO F("+v1.120") #define FIRMWARE_VERSION_NUM 0x04 //1st Complete Project : 1 (Version 1.061), 2st Project : 2, 1.08: 3, 1.09 : 4 /** @@ -72,10 +72,19 @@ // 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 -#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 -#define INIT_USB_FREQ (11996500l) + +#if UBITX_BOARD_VERSION == 5 + #define SECOND_OSC_USB (56064200l) + #define SECOND_OSC_LSB (33945800l) + #define INIT_USB_FREQ (11059200l) +#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 + #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) @@ -345,26 +354,51 @@ void setTXFilters(unsigned long freq){ } } //end of for #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); - } + + #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 } @@ -1159,12 +1193,22 @@ void initSettings(){ 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; diff --git a/ubitx_20/ubitx_factory_alignment.ino b/ubitx_20/ubitx_factory_alignment.ino index 8178a20..4f3e504 100644 --- a/ubitx_20/ubitx_factory_alignment.ino +++ b/ubitx_20/ubitx_factory_alignment.ino @@ -1,3 +1,4 @@ +#include "ubitx.h" /** * This procedure is only for those who have a signal generator/transceiver tuned to exactly 7.150 and a dummy load @@ -27,14 +28,25 @@ void factory_alignment(){ 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; @@ -88,4 +100,3 @@ void factory_alignment(){ updateDisplay(); } - diff --git a/ubitx_20/ubitx_lcd_nextion.ino b/ubitx_20/ubitx_lcd_nextion.ino index fdec271..786708e 100644 --- a/ubitx_20/ubitx_lcd_nextion.ino +++ b/ubitx_20/ubitx_lcd_nextion.ino @@ -1041,7 +1041,7 @@ void SendUbitxData(void) EEPROM.get(EXTERNAL_DEVICE_OPT1, nextionDisplayOption); SendCommandUL(CMD_DISP_OPTION2, nextionDisplayOption); - SendCommandStr(CMD_VERSION, (char *)("+v1.110")); //Version + SendCommandStr(CMD_VERSION, (char *)("+v1.120")); //Version SendEEPromData(CMD_CALLSIGN, 0, userCallsignLength -1, 0); /* diff --git a/ubitx_20/ubitx_menu.ino b/ubitx_20/ubitx_menu.ino index 9bcd126..502bd94 100644 --- a/ubitx_20/ubitx_menu.ino +++ b/ubitx_20/ubitx_menu.ino @@ -1662,6 +1662,15 @@ void menuSetupCarrier(int btn){ 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); @@ -1705,4 +1714,3 @@ void menuSetupCarrier(int btn){ //menuOn = 0; menuClearExit(0); } - diff --git a/ubitx_20/ubitx_si5351.ino b/ubitx_20/ubitx_si5351.ino index 049cf0b..e230bfd 100644 --- a/ubitx_20/ubitx_si5351.ino +++ b/ubitx_20/ubitx_si5351.ino @@ -13,6 +13,7 @@ * 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. @@ -58,7 +59,13 @@ uint8_t SI5351BX_ADDR; // I2C address of Si5351 (variable f // 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; @@ -92,6 +99,18 @@ void si5351bx_init() { // Call once at power-up, start PLLA 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 @@ -169,6 +188,3 @@ void TXSubFreq(unsigned long P2) i2cWrite(40, (P2 & 65280) >> 8); i2cWrite(41, P2 & 255); } - - - From 05de66a038a752e17e8b58d1cf971e93f0204f7b Mon Sep 17 00:00:00 2001 From: phdlee Date: Tue, 2 Apr 2019 23:09:18 +0900 Subject: [PATCH 153/173] uBITX V5 suppoort and SDR Frequency Change --- ubitx_20/ubitx.h | 8 +++---- ubitx_20/ubitx_20.ino | 44 ++++++++++++++++++++++++++++++---- ubitx_20/ubitx_lcd_nextion.ino | 2 +- 3 files changed, 44 insertions(+), 10 deletions(-) diff --git a/ubitx_20/ubitx.h b/ubitx_20/ubitx.h index 2eb279d..958e4c4 100644 --- a/ubitx_20/ubitx.h +++ b/ubitx_20/ubitx.h @@ -23,23 +23,23 @@ // Compile Option //============================================================================== //Ubitx Board Version -#define UBITX_BOARD_VERSION 4 //v1 ~ v4 : 4, v5: 5 +#define UBITX_BOARD_VERSION 5 //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_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 //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 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 diff --git a/ubitx_20/ubitx_20.ino b/ubitx_20/ubitx_20.ino index cdbe69b..2066939 100644 --- a/ubitx_20/ubitx_20.ino +++ b/ubitx_20/ubitx_20.ino @@ -6,7 +6,7 @@ // 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.120") +#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 /** @@ -74,13 +74,37 @@ // is shifted down a little due to the loading from the impedance matching L-networks on either sides #if UBITX_BOARD_VERSION == 5 - #define SECOND_OSC_USB (56064200l) +//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) - #define INIT_USB_FREQ (11059200l) + + //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 @@ -479,13 +503,23 @@ void setFrequency(unsigned long f){ 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); + + moveFrequency ); + //+ 2390); Do not add another SDR because the error is different. V1.3 +#endif } else { diff --git a/ubitx_20/ubitx_lcd_nextion.ino b/ubitx_20/ubitx_lcd_nextion.ino index 786708e..4c0a91a 100644 --- a/ubitx_20/ubitx_lcd_nextion.ino +++ b/ubitx_20/ubitx_lcd_nextion.ino @@ -1041,7 +1041,7 @@ void SendUbitxData(void) EEPROM.get(EXTERNAL_DEVICE_OPT1, nextionDisplayOption); SendCommandUL(CMD_DISP_OPTION2, nextionDisplayOption); - SendCommandStr(CMD_VERSION, (char *)("+v1.120")); //Version + SendCommandStr(CMD_VERSION, (char *)("+v1.122")); //Version SendEEPromData(CMD_CALLSIGN, 0, userCallsignLength -1, 0); /* From f25bf575565d9e600ed2c4ebe909534f2d267821 Mon Sep 17 00:00:00 2001 From: phdlee Date: Tue, 2 Apr 2019 23:18:54 +0900 Subject: [PATCH 154/173] Update README.md --- README.md | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 35e9177..2f0bf65 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ #NOTICE ---------------------------------------------------------------------------- -- Now Release Version 1.08 on my blog (http://www.hamskey.com) +- 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) @@ -24,6 +24,12 @@ Prepared or finished tasks for the next version ---------------------------------------------------------------------------- ## 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) From 395dd424594659080eb91cf6c2da59c0abf27552 Mon Sep 17 00:00:00 2001 From: phdlee Date: Tue, 2 Apr 2019 23:20:04 +0900 Subject: [PATCH 155/173] Update README.md --- README.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/README.md b/README.md index 2f0bf65..a4bdd90 100644 --- a/README.md +++ b/README.md @@ -17,9 +17,7 @@ The copyright information of the original is below. KD8CEC ---------------------------------------------------------------------------- Prepared or finished tasks for the next version - - Nextion LCD - Add TTS module - - Remote control on another MCU - Direct control for Student ---------------------------------------------------------------------------- From a4d9f6e6c5f9ed5f485c5c87ffc027fc22949df2 Mon Sep 17 00:00:00 2001 From: phdlee Date: Sat, 6 Apr 2019 16:35:46 +0900 Subject: [PATCH 156/173] changed version number for nextion lcd protocol --- ubitx_20/ubitx.h | 6 +++--- ubitx_20/ubitx_lcd_nextion.ino | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/ubitx_20/ubitx.h b/ubitx_20/ubitx.h index 958e4c4..7c17b11 100644 --- a/ubitx_20/ubitx.h +++ b/ubitx_20/ubitx.h @@ -23,16 +23,16 @@ // Compile Option //============================================================================== //Ubitx Board Version -#define UBITX_BOARD_VERSION 5 //v1 ~ v4 : 4, v5: 5 +#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_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 //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 diff --git a/ubitx_20/ubitx_lcd_nextion.ino b/ubitx_20/ubitx_lcd_nextion.ino index 4c0a91a..62667af 100644 --- a/ubitx_20/ubitx_lcd_nextion.ino +++ b/ubitx_20/ubitx_lcd_nextion.ino @@ -1041,7 +1041,7 @@ void SendUbitxData(void) EEPROM.get(EXTERNAL_DEVICE_OPT1, nextionDisplayOption); SendCommandUL(CMD_DISP_OPTION2, nextionDisplayOption); - SendCommandStr(CMD_VERSION, (char *)("+v1.122")); //Version + SendCommandStr(CMD_VERSION, (char *)("+v1.200")); //Version SendEEPromData(CMD_CALLSIGN, 0, userCallsignLength -1, 0); /* From 494bfa409e3bf073a70babfbe433fa74e4397180 Mon Sep 17 00:00:00 2001 From: Rob French Date: Tue, 28 Apr 2020 21:45:35 -0500 Subject: [PATCH 157/173] Made some changes to disable the keyer so it doesn't go into transmit mode when I start up. Doesn't seem to compile, however... --- ubitx_20/ubitx.h | 4 ++-- ubitx_20/ubitx_keyer.ino | 6 ++---- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/ubitx_20/ubitx.h b/ubitx_20/ubitx.h index 958e4c4..3dbb4fb 100644 --- a/ubitx_20/ubitx.h +++ b/ubitx_20/ubitx.h @@ -48,8 +48,8 @@ //#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. +//#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 diff --git a/ubitx_20/ubitx_keyer.ino b/ubitx_20/ubitx_keyer.ino index 1ac1c2f..e3477fb 100644 --- a/ubitx_20/ubitx_keyer.ino +++ b/ubitx_20/ubitx_keyer.ino @@ -98,7 +98,7 @@ unsigned char keyerState = IDLE; char update_PaddleLatch(byte isUpdateKeyState) { unsigned char tmpKeyerControl = 0; int paddle = analogRead(ANALOG_KEYER); - +/* KC4UPR: temporarily disabling keyer while doing ubitx_iop development. if (paddle >= cwAdcDashFrom && paddle <= cwAdcDashTo) tmpKeyerControl |= DAH_L; else if (paddle >= cwAdcDotFrom && paddle <= cwAdcDotTo) @@ -120,7 +120,7 @@ char update_PaddleLatch(byte isUpdateKeyState) { return tmpKeyerControl; } - +*/ /***************************************************************************** // New logic, by RON // modified by KD8CEC @@ -365,5 +365,3 @@ void cwKeyer(){ } } */ - - From 913f1d078166c48b93701ddc5eeb05004a54f1e6 Mon Sep 17 00:00:00 2001 From: Rob French Date: Tue, 28 Apr 2020 21:56:47 -0500 Subject: [PATCH 158/173] Misc small changes. --- ubitx_20/ubitx_keyer.ino | 6 ++++-- ubitx_20/ubitx_lcd_nextion.ino | 2 +- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/ubitx_20/ubitx_keyer.ino b/ubitx_20/ubitx_keyer.ino index e3477fb..c38d2e3 100644 --- a/ubitx_20/ubitx_keyer.ino +++ b/ubitx_20/ubitx_keyer.ino @@ -98,7 +98,9 @@ unsigned char keyerState = IDLE; char update_PaddleLatch(byte isUpdateKeyState) { unsigned char tmpKeyerControl = 0; int paddle = analogRead(ANALOG_KEYER); -/* KC4UPR: temporarily disabling keyer while doing ubitx_iop development. + + return 0; // KC4UPR: temporarily disabling keyer while doing ubitx_iop development. + if (paddle >= cwAdcDashFrom && paddle <= cwAdcDashTo) tmpKeyerControl |= DAH_L; else if (paddle >= cwAdcDotFrom && paddle <= cwAdcDotTo) @@ -120,7 +122,7 @@ char update_PaddleLatch(byte isUpdateKeyState) { return tmpKeyerControl; } -*/ + /***************************************************************************** // New logic, by RON // modified by KD8CEC diff --git a/ubitx_20/ubitx_lcd_nextion.ino b/ubitx_20/ubitx_lcd_nextion.ino index 4c0a91a..62667af 100644 --- a/ubitx_20/ubitx_lcd_nextion.ino +++ b/ubitx_20/ubitx_lcd_nextion.ino @@ -1041,7 +1041,7 @@ void SendUbitxData(void) EEPROM.get(EXTERNAL_DEVICE_OPT1, nextionDisplayOption); SendCommandUL(CMD_DISP_OPTION2, nextionDisplayOption); - SendCommandStr(CMD_VERSION, (char *)("+v1.122")); //Version + SendCommandStr(CMD_VERSION, (char *)("+v1.200")); //Version SendEEPromData(CMD_CALLSIGN, 0, userCallsignLength -1, 0); /* From 44c6c868380a33106cc482118189a5c4fb4316ff Mon Sep 17 00:00:00 2001 From: Rob French Date: Sun, 3 May 2020 22:00:02 -0500 Subject: [PATCH 159/173] Updated CAT to support interoperability with uBITX I/O Processor (IOP). --- ubitx_20/cat_libs.ino | 29 ++++++++++++++++++++++++++++- 1 file changed, 28 insertions(+), 1 deletion(-) diff --git a/ubitx_20/cat_libs.ino b/ubitx_20/cat_libs.ino index 608af79..e467f1b 100644 --- a/ubitx_20/cat_libs.ino +++ b/ubitx_20/cat_libs.ino @@ -48,6 +48,11 @@ #define CAT_MODE_FMN 0x88 #define ACK 0 +// KC4UPR--uBITX IOP: prefixes to determine "mode" of serial transmission +#define CAT_PREFIX 0xC0 +#define IOP_PREFIX 0xD0 +#define EEPROM_READ_PREFIX 0xE0 +#define EEPROM_WRITE_PREFIX 0xF0 unsigned int skipTimeCount = 0; byte CAT_BUFF[5]; @@ -55,6 +60,14 @@ byte CAT_SNDBUFF[5]; 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(CAT_PREFIX | sendCount); + for (byte i = 0; i < sendCount; i++) Serial.write(CAT_BUFF[i]); //Serial.flush(); @@ -250,6 +263,15 @@ void ReadEEPRom() //for remove warnings. 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(EEPROM_READ_PREFIX); + 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 @@ -293,6 +315,12 @@ void WriteEEPRom(void) //for remove warning 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(EEPROM_WRITE_PREFIX | 2); + //Check Checksum if (CAT_BUFF[3] != ((CAT_BUFF[0] + CAT_BUFF[1] + CAT_BUFF[2]) % 256)) { @@ -890,4 +918,3 @@ void Init_Cat(long baud, int portConfig) Serial.begin(baud, portConfig); Serial.flush(); } - From 1aa9ce1bd6c5e28515bac891a94f4871a283e13a Mon Sep 17 00:00:00 2001 From: Rob French Date: Sun, 3 May 2020 23:04:02 -0500 Subject: [PATCH 160/173] In theory, the code has now been modified to allow CW transmission when used with the IOP. However, at the moment, there is no way to put the IOP into CW mode. Intended behavior: PTT/Key in SSB = transmit (PTT) PTT/Key in CW = key down --- ubitx_20/ubitx_20.ino | 48 ++++++-- ubitx_20/ubitx_keyer.ino | 240 ++++++++++++++++++++++----------------- 2 files changed, 178 insertions(+), 110 deletions(-) diff --git a/ubitx_20/ubitx_20.ino b/ubitx_20/ubitx_20.ino index 2066939..c78da97 100644 --- a/ubitx_20/ubitx_20.ino +++ b/ubitx_20/ubitx_20.ino @@ -316,6 +316,17 @@ 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) @@ -678,6 +689,10 @@ void ritDisable(){ */ 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; @@ -1459,15 +1474,34 @@ void checkAutoSaveFreqMode() } void loop(){ - if (isCWAutoMode == 0){ //when CW AutoKey Mode, disable this process - if (!txCAT) + /* + * 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(); - checkButton(); + else + cwKeyer(); + checkButton(); } - else - controlAutoCW(); - - cwKeyer(); + +//cwKeyer(); //tune only when not tranmsitting if (!inTx){ diff --git a/ubitx_20/ubitx_keyer.ino b/ubitx_20/ubitx_keyer.ino index c38d2e3..691b1d0 100644 --- a/ubitx_20/ubitx_keyer.ino +++ b/ubitx_20/ubitx_keyer.ino @@ -39,6 +39,22 @@ 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 @@ -52,6 +68,7 @@ byte getPaddle(){ return PADDLE_BOTH; //both are between 1 and 2v else return PADDLE_STRAIGHT; //less than 1v is the straight key +*/ } /** @@ -96,11 +113,20 @@ 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); - return 0; // KC4UPR: temporarily disabling keyer while doing ubitx_iop development. - if (paddle >= cwAdcDashFrom && paddle <= cwAdcDashTo) tmpKeyerControl |= DAH_L; else if (paddle >= cwAdcDotFrom && paddle <= cwAdcDotTo) @@ -121,6 +147,7 @@ char update_PaddleLatch(byte isUpdateKeyState) { keyerControl |= tmpKeyerControl; return tmpKeyerControl; +*/ } /***************************************************************************** @@ -128,106 +155,113 @@ char update_PaddleLatch(byte isUpdateKeyState) { // modified by KD8CEC ******************************************************************************/ void cwKeyer(void){ - 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{ + /* + * 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 @@ -264,7 +298,7 @@ void cwKeyer(void){ Check_Cat(2); } //end of while - } //end of elese +// } //end of elese } From 7ba147b06dd918816ae3598fcba0a92d98d774ad Mon Sep 17 00:00:00 2001 From: Rob French Date: Sun, 3 May 2020 23:30:44 -0500 Subject: [PATCH 161/173] Added ability to send a command to the IOP, specifically a mode change command (to SSB, DIGI, or CW; although DIGI is currently not actually possible). --- ubitx_20/cat_libs.ino | 26 ++++++++++++++++++++++++++ ubitx_20/ubitx_menu.ino | 2 ++ 2 files changed, 28 insertions(+) diff --git a/ubitx_20/cat_libs.ino b/ubitx_20/cat_libs.ino index e467f1b..13b3ab4 100644 --- a/ubitx_20/cat_libs.ino +++ b/ubitx_20/cat_libs.ino @@ -54,6 +54,32 @@ #define EEPROM_READ_PREFIX 0xE0 #define EEPROM_WRITE_PREFIX 0xF0 +#define IOP_MODE_COMMAND 0x00 +#define IOP_MODE_SSB 0x01 +#define IOP_MODE_DIGI 0x02 +#define IOP_MODE_CW 0x03 + +/* + * KC4UPR - IOP update, 2020-05-03 + * + * Send the current mode to the I/O Processor. + * + * NOTE: This all needs to be expanded to include digital... + * + * NOTE: Not yet called from CAT, only from menu... + */ +void iopSendMode(char is_cw, char is_usb) +{ + byte mode; + + Serial.write(IOP_PREFIX | 2); + Serial.write(IOP_MODE_COMMAND); + if (is_cw) + mode = IOP_MODE_CW; + else + mode = IOP_MODE_SSB; +} + unsigned int skipTimeCount = 0; byte CAT_BUFF[5]; byte CAT_SNDBUFF[5]; diff --git a/ubitx_20/ubitx_menu.ino b/ubitx_20/ubitx_menu.ino index 502bd94..4bec378 100644 --- a/ubitx_20/ubitx_menu.ino +++ b/ubitx_20/ubitx_menu.ino @@ -960,6 +960,8 @@ void menuSelectMode(int btn){ else if (selectModeType == 3) { cwMode = 2; } + // KC4UPR: sending mode to IOP + iopSendMode(cwMode, isUSB); FrequencyToVFO(1); } From c8b2110052c00d79d4ec8f18fdd90ba82794c950 Mon Sep 17 00:00:00 2001 From: Rob French Date: Fri, 8 May 2020 00:16:59 -0500 Subject: [PATCH 162/173] Successful communication between Raduino and IOP. Digi modes working. Have a high-pitched whine in RX audio, however (IOP problem, not Raduino). --- ubitx_20/cat_libs.ino | 44 +++++++++++--- ubitx_20/ubitx.h | 4 +- ubitx_20/ubitx_20.ino | 7 ++- ubitx_20/ubitx_lcd_1602.ino | 17 ++++-- ubitx_20/ubitx_menu.ino | 112 +++++++++++++++++++++++++++--------- 5 files changed, 141 insertions(+), 43 deletions(-) diff --git a/ubitx_20/cat_libs.ino b/ubitx_20/cat_libs.ino index 13b3ab4..354f5e0 100644 --- a/ubitx_20/cat_libs.ino +++ b/ubitx_20/cat_libs.ino @@ -54,30 +54,49 @@ #define EEPROM_READ_PREFIX 0xE0 #define EEPROM_WRITE_PREFIX 0xF0 -#define IOP_MODE_COMMAND 0x00 -#define IOP_MODE_SSB 0x01 -#define IOP_MODE_DIGI 0x02 -#define IOP_MODE_CW 0x03 +#define IOP_MODE_COMMAND 0x00 +#define IOP_START_TX_COMMAND 0x01 +#define IOP_STOP_TX_COMMAND 0x02 +#define IOP_MODE_SSB 0x00 +#define IOP_MODE_DIGI 0x01 +#define IOP_MODE_CW 0x02 /* * KC4UPR - IOP update, 2020-05-03 * * Send the current mode to the I/O Processor. * - * NOTE: This all needs to be expanded to include digital... - * * NOTE: Not yet called from CAT, only from menu... */ -void iopSendMode(char is_cw, char is_usb) +void iopSendMode(char cw_mode, char is_usb, char digi_mode) { byte mode; Serial.write(IOP_PREFIX | 2); Serial.write(IOP_MODE_COMMAND); - if (is_cw) + if (cw_mode > 0) mode = IOP_MODE_CW; + else if (digi_mode > 0) + mode = IOP_MODE_DIGI; else mode = IOP_MODE_SSB; + Serial.write(mode); +} + +// Used to tell the IOP that we're transmitting, when it came via +// CAT (otherwise, IOP is the one who tells Raduino to start TX!). +void iopStartTx() +{ + Serial.write(IOP_PREFIX | 1); + Serial.write(IOP_START_TX_COMMAND); +} + +// Used to tell the IOP to stop transmitting, when it came via +// CAT (otherwise, IOP is the one who tells Raduino to stop TX!). +void iopStopTx() +{ + Serial.write(IOP_PREFIX | 1); + Serial.write(IOP_STOP_TX_COMMAND); } unsigned int skipTimeCount = 0; @@ -202,7 +221,8 @@ void CatSetSplit(boolean isSplit) //for remove warning messages 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; @@ -215,6 +235,9 @@ void CatSetPTT(boolean isPTTOn, byte fromType) { txCAT = true; + // KC4UPR - added the next line to tell the IOP we're transmitting + iopStartTx(); + startTx(TX_SSB, 1); //Exit menu, Memory Keyer... ETC if (isCWAutoMode > 0) { @@ -228,6 +251,9 @@ void CatSetPTT(boolean isPTTOn, byte fromType) { if (inTx) { + // KC4UPR - added the next line to tell the IOP we're not transmitting + iopStopTx(); + stopTx(); txCAT = false; } diff --git a/ubitx_20/ubitx.h b/ubitx_20/ubitx.h index 3dbb4fb..427a99e 100644 --- a/ubitx_20/ubitx.h +++ b/ubitx_20/ubitx.h @@ -310,8 +310,8 @@ 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[30]; -extern char b[30]; +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); diff --git a/ubitx_20/ubitx_20.ino b/ubitx_20/ubitx_20.ino index c78da97..4fb2bcd 100644 --- a/ubitx_20/ubitx_20.ino +++ b/ubitx_20/ubitx_20.ino @@ -191,10 +191,15 @@ boolean txCAT = false; //turned on if the transmitting due to a CAT comma 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 isUSB = 0; //upper sideband was selected, this is reset to the default for the 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 diff --git a/ubitx_20/ubitx_lcd_1602.ino b/ubitx_20/ubitx_lcd_1602.ino index 76f2e5b..1f669ad 100644 --- a/ubitx_20/ubitx_lcd_1602.ino +++ b/ubitx_20/ubitx_lcd_1602.ino @@ -282,7 +282,7 @@ void LCD_CreateChar(uint8_t location, uint8_t charmap[]) //SWR GRAPH, DrawMeter and drawingMeter Logic function by VK2ETA #define OPTION_SKINNYBARS -char c[30], b[30]; +char c[40], b[40]; char printBuff[2][17]; //mirrors what is showing on the two lines of the display @@ -438,10 +438,17 @@ void updateDisplay() { else { if (cwMode == 0) { - if (isUSB) - strcpy(c, "USB "); - else - strcpy(c, "LSB "); + if (digiMode == 1) { + if (isUSB) + strcpy(c, "DGU "); + else + strcpy(c, "DGL "); + } else { + if (isUSB) + strcpy(c, "USB "); + else + strcpy(c, "LSB "); + } } else if (cwMode == 1) { diff --git a/ubitx_20/ubitx_menu.ino b/ubitx_20/ubitx_menu.ino index 4bec378..dd5a4cb 100644 --- a/ubitx_20/ubitx_menu.ino +++ b/ubitx_20/ubitx_menu.ino @@ -131,9 +131,9 @@ byte modeToByte(){ if (cwMode == 0) { if (isUSB) - return 3; + return 3 + (digiMode > 0 ? 3 + digiMode : 0); else - return 2; + return 2 + (digiMode > 0 ? 3 + digiMode : 0); } else if (cwMode == 1) { @@ -149,20 +149,56 @@ byte modeToByte(){ //autoSetModebyFreq : 0 //autoSetModebyFreq : 1, if (modValue is not set, set mode by frequency) void byteToMode(byte modeValue, byte autoSetModebyFreq){ - if (modeValue == 4) - cwMode = 1; - else if (modeValue == 5) - cwMode = 2; - else - { + 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: // DGL + digiMode = 1; + break; + + case 7: // DGU + isUSB = true; + digiMode = 1; + break; + } + } +/* if (modeValue == 4) { + cwMode = 1; digiMode = 0; + } else if (modeValue == 5) { + cwMode = 2; digiMode = 0; + } else { cwMode = 0; - if (modeValue == 3) - isUSB = 1; + 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; - } + }*/ } @@ -651,12 +687,19 @@ int getValueByKnob(int valueType, int targetValue, int minKnobValue, int maxKnob moveDetectStep = 0; } - strcpy(b, displayTitle); - if (valueType == 11) //Mode Select { - b[targetValue * 4] = '>'; - } + 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 { @@ -668,8 +711,6 @@ int getValueByKnob(int valueType, int targetValue, int minKnobValue, int maxKnob strcat(b, "IAMBICB"); } */ - else - { strcat(b, ":"); itoa(targetValue,c, 10); strcat(b, c); @@ -932,36 +973,55 @@ void menuSelectMode(int btn){ } else { - //LSB, USB, CWL, CWU - if (cwMode == 0 && isUSB == 0) + //LSB, USB, CWL, CWU, DGL, DGU + if (cwMode == 0) { + if (isUSB == 0) { + selectModeType = 0; // LSB + } else { + selectModeType = 1; // USB + } + // modify if digital mode is set + if (digiMode > 0) { + selectModeType += (3 + digiMode); + } + } else if (cwMode == 1) { + selectModeType = 2; // CWL + } else { + selectModeType = 3; // CWU + } + /*if (cwMode == 0 && isUSB == 0) selectModeType = 0; else if (cwMode == 0 && isUSB == 1) selectModeType = 1; else if (cwMode == 1) selectModeType = 2; else - selectModeType = 3; + selectModeType = 3;*/ beforeMode = selectModeType; - selectModeType = getValueByKnob(11, selectModeType, 0, 3, 1, " LSB USB CWL CWU", 4); //3 : Select Mode, targetValue, minKnobValue, maxKnobValue, stepSize + selectModeType = getValueByKnob(11, selectModeType, 0, 5, 1, " LSB USB CWL CWU DGL DGU ", 4); //3 : Select Mode, targetValue, minKnobValue, maxKnobValue, stepSize if (beforeMode != selectModeType) { //printLineF1(F("Changed Mode")); if (selectModeType == 0) { - cwMode = 0; isUSB = 0; + cwMode = 0; isUSB = 0; digiMode = 0; } else if (selectModeType == 1) { - cwMode = 0; isUSB = 1; + cwMode = 0; isUSB = 1; digiMode = 0; } else if (selectModeType == 2) { - cwMode = 1; + cwMode = 1; digiMode = 0; } else if (selectModeType == 3) { - cwMode = 2; + cwMode = 2; digiMode = 0; + } else if (selectModeType == 4) { + cwMode = 0; isUSB = 0; digiMode = 1; + } else if (selectModeType == 5) { + cwMode = 0; isUSB = 1; digiMode = 1; } // KC4UPR: sending mode to IOP - iopSendMode(cwMode, isUSB); + iopSendMode(cwMode, isUSB, digiMode); FrequencyToVFO(1); } From 681e01d019e7779fd046ff133b730bc20cdcd5c0 Mon Sep 17 00:00:00 2001 From: Rob French Date: Sat, 16 May 2020 23:51:46 -0500 Subject: [PATCH 163/173] Updates to be compatible with iopcomm.h/iopcomm.cpp, and with the new two-tone test mode. --- ubitx_20/cat_libs.ino | 100 ++++++++++++++++-------------------- ubitx_20/ubitx_20.ino | 3 +- ubitx_20/ubitx_lcd_1602.ino | 5 ++ ubitx_20/ubitx_menu.ino | 45 ++++++++++------ 4 files changed, 81 insertions(+), 72 deletions(-) diff --git a/ubitx_20/cat_libs.ino b/ubitx_20/cat_libs.ino index 354f5e0..eb39c43 100644 --- a/ubitx_20/cat_libs.ino +++ b/ubitx_20/cat_libs.ino @@ -32,6 +32,8 @@ **************************************************************************/ +#include + #include "ubitx.h" //for broken protocol @@ -48,55 +50,25 @@ #define CAT_MODE_FMN 0x88 #define ACK 0 -// KC4UPR--uBITX IOP: prefixes to determine "mode" of serial transmission -#define CAT_PREFIX 0xC0 -#define IOP_PREFIX 0xD0 -#define EEPROM_READ_PREFIX 0xE0 -#define EEPROM_WRITE_PREFIX 0xF0 - -#define IOP_MODE_COMMAND 0x00 -#define IOP_START_TX_COMMAND 0x01 -#define IOP_STOP_TX_COMMAND 0x02 -#define IOP_MODE_SSB 0x00 -#define IOP_MODE_DIGI 0x01 -#define IOP_MODE_CW 0x02 /* * KC4UPR - IOP update, 2020-05-03 * * Send the current mode to the I/O Processor. - * - * NOTE: Not yet called from CAT, only from menu... */ -void iopSendMode(char cw_mode, char is_usb, char digi_mode) +void iopSendMode(char cw_mode, char is_usb, char digi_mode, char is_test) { byte mode; - Serial.write(IOP_PREFIX | 2); - Serial.write(IOP_MODE_COMMAND); if (cw_mode > 0) - mode = IOP_MODE_CW; + mode = MODE_CW; else if (digi_mode > 0) - mode = IOP_MODE_DIGI; + mode = MODE_DIGI; + else if (is_test) + mode = MODE_TEST; else - mode = IOP_MODE_SSB; - Serial.write(mode); -} - -// Used to tell the IOP that we're transmitting, when it came via -// CAT (otherwise, IOP is the one who tells Raduino to start TX!). -void iopStartTx() -{ - Serial.write(IOP_PREFIX | 1); - Serial.write(IOP_START_TX_COMMAND); -} - -// Used to tell the IOP to stop transmitting, when it came via -// CAT (otherwise, IOP is the one who tells Raduino to stop TX!). -void iopStopTx() -{ - Serial.write(IOP_PREFIX | 1); - Serial.write(IOP_STOP_TX_COMMAND); + mode = MODE_SSB; + sendIOPModeCommand(mode); } unsigned int skipTimeCount = 0; @@ -111,7 +83,7 @@ void SendCatData(byte sendCount) // // NOTE: Need to do some error checking at some point to ensure we don't // try to send more than 15 bytes!!! - Serial.write(CAT_PREFIX | sendCount); + Serial.write(prefixAndLengthToByte(CAT_PREFIX, sendCount)); for (byte i = 0; i < sendCount; i++) Serial.write(CAT_BUFF[i]); @@ -236,7 +208,7 @@ void CatSetPTT(boolean isPTTOn, byte fromType) txCAT = true; // KC4UPR - added the next line to tell the IOP we're transmitting - iopStartTx(); + sendIOPStartTxCommand(); startTx(TX_SSB, 1); //Exit menu, Memory Keyer... ETC @@ -252,7 +224,7 @@ void CatSetPTT(boolean isPTTOn, byte fromType) if (inTx) { // KC4UPR - added the next line to tell the IOP we're not transmitting - iopStopTx(); + sendIOPStopTxCommand(); stopTx(); txCAT = false; @@ -281,21 +253,33 @@ void CatSetMode(byte tmpMode, byte fromType) if (!inTx) { - if (tmpMode == CAT_MODE_CW) - { - cwMode = 1; - } - else if (tmpMode == CAT_MODE_USB) - { - cwMode = 0; - isUSB = true; - } - else - { - cwMode = 0; - isUSB = false; - } + 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, isTest); setFrequency(frequency); updateDisplay(); } @@ -318,7 +302,7 @@ void ReadEEPRom() //for remove warnings. // 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(EEPROM_READ_PREFIX); + Serial.write(prefixAndLengthToByte(RAD_EEPROM_READ_PREFIX, 5)); Serial.write(CAT_BUFF[0]); Serial.write(CAT_BUFF[1]); Serial.write(CAT_BUFF[2]); @@ -371,7 +355,7 @@ void WriteEEPRom(void) //for remove warning // indicates that this is a "Memory Manager mode" transmission. // // Also indicates that we are going to be sending two bytes of data. - Serial.write(EEPROM_WRITE_PREFIX | 2); + Serial.write(prefixAndLengthToByte(RAD_EEPROM_WRITE_PREFIX, 2)); //Check Checksum if (CAT_BUFF[3] != ((CAT_BUFF[0] + CAT_BUFF[1] + CAT_BUFF[2]) % 256)) @@ -969,4 +953,8 @@ 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. + iopSendMode(cwMode, isUSB, digiMode, isTest); } diff --git a/ubitx_20/ubitx_20.ino b/ubitx_20/ubitx_20.ino index 4fb2bcd..fc9cfee 100644 --- a/ubitx_20/ubitx_20.ino +++ b/ubitx_20/ubitx_20.ino @@ -1,4 +1,4 @@ - //Firmware Version +//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. @@ -193,6 +193,7 @@ char splitOn = 0; //working split, uses VFO B as the transmit freque 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) diff --git a/ubitx_20/ubitx_lcd_1602.ino b/ubitx_20/ubitx_lcd_1602.ino index 1f669ad..f4fb70a 100644 --- a/ubitx_20/ubitx_lcd_1602.ino +++ b/ubitx_20/ubitx_lcd_1602.ino @@ -443,6 +443,11 @@ void updateDisplay() { strcpy(c, "DGU "); else strcpy(c, "DGL "); + } else if (isTest == 1) { + if (isUSB) + strcpy(c, "TTU "); + else + strcpy(c, "TTL "); } else { if (isUSB) strcpy(c, "USB "); diff --git a/ubitx_20/ubitx_menu.ino b/ubitx_20/ubitx_menu.ino index dd5a4cb..d177b61 100644 --- a/ubitx_20/ubitx_menu.ino +++ b/ubitx_20/ubitx_menu.ino @@ -149,6 +149,7 @@ byte modeToByte(){ //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; @@ -181,6 +182,15 @@ void byteToMode(byte modeValue, byte autoSetModebyFreq){ isUSB = true; digiMode = 1; break; +/* + case 8: // TTL + isUSB = false; + break; + + case 9: // TTU + isUSB = true; + break; +*/ } } /* if (modeValue == 4) { @@ -973,7 +983,7 @@ void menuSelectMode(int btn){ } else { - //LSB, USB, CWL, CWU, DGL, DGU + //LSB, USB, CWL, CWU, DGL, DGU, TTL, TTU if (cwMode == 0) { if (isUSB == 0) { selectModeType = 0; // LSB @@ -983,6 +993,10 @@ void menuSelectMode(int btn){ // modify if digital mode is set if (digiMode > 0) { selectModeType += (3 + digiMode); + + // modify if two-tone test mode is set + } else if (isTest > 0) { + selectModeType += 5; } } else if (cwMode == 1) { selectModeType = 2; // CWL @@ -999,29 +1013,30 @@ void menuSelectMode(int btn){ selectModeType = 3;*/ beforeMode = selectModeType; - selectModeType = getValueByKnob(11, selectModeType, 0, 5, 1, " LSB USB CWL CWU DGL DGU ", 4); //3 : Select Mode, targetValue, minKnobValue, maxKnobValue, stepSize + selectModeType = getValueByKnob(11, selectModeType, 0, 7, 1, " LSB USB CWL CWU DGL DGU TTL TTU", 4); //3 : Select Mode, targetValue, minKnobValue, maxKnobValue, stepSize if (beforeMode != selectModeType) { //printLineF1(F("Changed Mode")); if (selectModeType == 0) { - cwMode = 0; isUSB = 0; digiMode = 0; - } - else if (selectModeType == 1) { - cwMode = 0; isUSB = 1; digiMode = 0; - } - else if (selectModeType == 2) { - cwMode = 1; digiMode = 0; - } - else if (selectModeType == 3) { - cwMode = 2; digiMode = 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 = 1; digiMode = 0; isTest = 0; + } else if (selectModeType == 3) { + cwMode = 2; digiMode = 0; isTest = 0; } else if (selectModeType == 4) { - cwMode = 0; isUSB = 0; digiMode = 1; + cwMode = 0; isUSB = 0; digiMode = 1; isTest = 0; } else if (selectModeType == 5) { - cwMode = 0; isUSB = 1; digiMode = 1; + 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); + iopSendMode(cwMode, isUSB, digiMode, isTest); FrequencyToVFO(1); } From 2f8fe7fb4c8a44bbf1042e68109d5ee32162e112 Mon Sep 17 00:00:00 2001 From: Rob French Date: Mon, 18 May 2020 08:15:28 -0500 Subject: [PATCH 164/173] Got the 5 second "DSP status menu" working. Got comms between the IOP and the Raduino working again. Now implements a wrapper around all IOP<=>Raduino comms (IOP prefix, CAT prefix...) --- ubitx_20/cat_libs.ino | 237 +++++++++++++++++++++--------------- ubitx_20/ubitx_lcd_1602.ino | 27 ++-- ubitx_20/ubitx_menu.ino | 2 +- 3 files changed, 160 insertions(+), 106 deletions(-) diff --git a/ubitx_20/cat_libs.ino b/ubitx_20/cat_libs.ino index eb39c43..08789ad 100644 --- a/ubitx_20/cat_libs.ino +++ b/ubitx_20/cat_libs.ino @@ -61,13 +61,13 @@ void iopSendMode(char cw_mode, char is_usb, char digi_mode, char is_test) byte mode; if (cw_mode > 0) - mode = MODE_CW; + mode = RIG_MODE_CW; else if (digi_mode > 0) - mode = MODE_DIGI; + mode = RIG_MODE_DIGI; else if (is_test) - mode = MODE_TEST; + mode = RIG_MODE_TEST; else - mode = MODE_SSB; + mode = RIG_MODE_SSB; sendIOPModeCommand(mode); } @@ -810,12 +810,25 @@ 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 + +// 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; + //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; + //Check Serial Port Buffer if (Serial.available() == 0) { @@ -823,7 +836,9 @@ void Check_Cat(byte fromType) rxBufferCheckCount = 0; return; } - else if (Serial.available() < 5) + // 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) { //First Arrived if (rxBufferCheckCount == 0) @@ -837,7 +852,7 @@ void Check_Cat(byte fromType) for (i = 0; i < Serial.available(); i++) rxBufferCheckCount = Serial.read(); - rxBufferCheckCount = 0; + rxBufferCheckCount = 0; } else if (rxBufferCheckCount < Serial.available()) //increase buffer count, slow arrived { @@ -848,103 +863,133 @@ void Check_Cat(byte fromType) } //Arived CAT DATA - for (i = 0; i < 5; i++) - CAT_BUFF[i] = Serial.read(); + // KC4UPR - IOP update - 6 characters; first character determines mode (CAT or IOP) + for (i = 0; i < 6; i++) { //5; i++) + if (i == 0) { + byte first = Serial.read(); + readPrefix = byteToPrefix(first); + readLength = byteToLength(first); + } else { + CAT_BUFF[i-1] = 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; + isProcessCheck_Cat = 1; - //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 + if (readPrefix == IOP_PREFIX) { + recvIOPMessage(msg, CAT_BUFF, 5); // not super robust... if IOP ever sends more 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_DIGI_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; + } + + } 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 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 + + case 0x02 : //Split On + case 0x82: //Split Off + CatSetSplit(CAT_BUFF[4] == 0x02); 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 + + 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; } diff --git a/ubitx_20/ubitx_lcd_1602.ino b/ubitx_20/ubitx_lcd_1602.ino index f4fb70a..7d58293 100644 --- a/ubitx_20/ubitx_lcd_1602.ino +++ b/ubitx_20/ubitx_lcd_1602.ino @@ -518,8 +518,6 @@ void updateDisplay() { } } - - char line2Buffer[17]; //KD8CEC 200Hz ST //L14.150 200Hz ST @@ -674,13 +672,18 @@ void updateLine2Buffer(char displayType) if (isStepKhz == 0) { - line2Buffer[11] = 'H'; - line2Buffer[12] = 'z'; + // KC4UPR: Getting rid of the "Hz" to unclutter the top line. + line2Buffer[11] = ' '; //'H'; + line2Buffer[12] = ' '; //'z'; } - line2Buffer[13] = ' '; - - //Check CW Key cwKeyType = 0; //0: straight, 1 : iambica, 2: iambicb + //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'; @@ -701,7 +704,7 @@ void updateLine2Buffer(char displayType) { line2Buffer[14] = 'I'; line2Buffer[15] = 'B'; - } + } */ } } @@ -745,8 +748,14 @@ void idle_process() } } + if (!displaySmeter) { + if (delaySmeter < millis()) { + displaySmeter = true; + } + } + //S-Meter Display - if (((displayOption1 & 0x08) == 0x08 && (sdrModeOn == 0)) && (++checkCountSMeter > SMeterLatency)) + if (((displayOption1 & 0x08) == 0x08 && (sdrModeOn == 0)) && (++checkCountSMeter > SMeterLatency) && displaySmeter) { int newSMeter; diff --git a/ubitx_20/ubitx_menu.ino b/ubitx_20/ubitx_menu.ino index d177b61..8dda453 100644 --- a/ubitx_20/ubitx_menu.ino +++ b/ubitx_20/ubitx_menu.ino @@ -996,7 +996,7 @@ void menuSelectMode(int btn){ // modify if two-tone test mode is set } else if (isTest > 0) { - selectModeType += 5; + selectModeType += 6; } } else if (cwMode == 1) { selectModeType = 2; // CWL From cc78a9f9a1e5ced47716c85b5f673f680514f9c7 Mon Sep 17 00:00:00 2001 From: Rob French Date: Mon, 25 May 2020 23:09:37 -0500 Subject: [PATCH 165/173] Updates to be compatible with the refactored IOP code. Compiles, but untested. --- ubitx_20/cat_libs.ino | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/ubitx_20/cat_libs.ino b/ubitx_20/cat_libs.ino index 08789ad..d6455e3 100644 --- a/ubitx_20/cat_libs.ino +++ b/ubitx_20/cat_libs.ino @@ -60,14 +60,15 @@ void iopSendMode(char cw_mode, char is_usb, char digi_mode, char is_test) { byte mode; - if (cw_mode > 0) - mode = RIG_MODE_CW; - else if (digi_mode > 0) - mode = RIG_MODE_DIGI; - else if (is_test) - mode = RIG_MODE_TEST; - else - mode = RIG_MODE_SSB; + if (cw_mode > 0) { + mode = (cw_mode == 1 ? RIG_MODE_CWL : RIG_MODE_CWU); + } else if (digi_mode > 0) { + mode = (is_usb ? RIG_MODE_DGU : RIG_MODE_DGL); + } else if (is_test) { + mode = (is_usb ? RIG_MODE_TTU : RIG_MODE_TTL); + } else { + mode = (is_usb ? RIG_MODE_USB : RIG_MODE_LSB); + } sendIOPModeCommand(mode); } @@ -887,7 +888,7 @@ void Check_Cat(byte fromType) // 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_DIGI_STATUS_MSG: + case IOP_DGT_STATUS_MSG: case IOP_CW_STATUS_MSG: case IOP_TEST_STATUS_MSG: iopStatusWindow[0] = msg.data[0]; From 5a6c8308d32ef4816cec5db48d96a94df015e6d1 Mon Sep 17 00:00:00 2001 From: Rob French Date: Tue, 26 May 2020 10:48:35 -0500 Subject: [PATCH 166/173] Quick update to support a mode request message from the IOP. Compiles, runs; not comprehensively tested. --- ubitx_20/cat_libs.ino | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/ubitx_20/cat_libs.ino b/ubitx_20/cat_libs.ino index d6455e3..e59f31c 100644 --- a/ubitx_20/cat_libs.ino +++ b/ubitx_20/cat_libs.ino @@ -884,7 +884,7 @@ void Check_Cat(byte fromType) isProcessCheck_Cat = 1; if (readPrefix == IOP_PREFIX) { - recvIOPMessage(msg, CAT_BUFF, 5); // not super robust... if IOP ever sends more than a 5 (6) byte message + recvIOPMessage(msg, CAT_BUFF, 5); // 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: @@ -897,6 +897,10 @@ void Check_Cat(byte fromType) displaySmeter = false; delaySmeter = millis() + SMETER_DELAY_TIME; break; + + case IOP_MODE_REQUEST: + iopSendMode(cwMode, isUSB, digiMode, isTest); + break; } } else if (readPrefix == CAT_PREFIX) { From bbf883d3c5e84c4d69099fd1a818e617d6ec137c Mon Sep 17 00:00:00 2001 From: Rob French Date: Sat, 6 Jun 2020 00:07:50 -0500 Subject: [PATCH 167/173] Added support for a 16-char display to be received from the IOP, with a selectable timeout. Verified the basics of it working. Subsequently updated with better timeout code. This compiles, but has not been tested. --- ubitx_20/cat_libs.ino | 38 +++++++++++++++++++++++++------------ ubitx_20/ubitx_lcd_1602.ino | 21 +++++++++++++++++++- 2 files changed, 46 insertions(+), 13 deletions(-) diff --git a/ubitx_20/cat_libs.ino b/ubitx_20/cat_libs.ino index e59f31c..c5fd8af 100644 --- a/ubitx_20/cat_libs.ino +++ b/ubitx_20/cat_libs.ino @@ -73,8 +73,8 @@ void iopSendMode(char cw_mode, char is_usb, char digi_mode, char is_test) } unsigned int skipTimeCount = 0; -byte CAT_BUFF[5]; -byte CAT_SNDBUFF[5]; +byte CAT_BUFF[34]; +byte CAT_SNDBUFF[34]; void SendCatData(byte sendCount) { @@ -812,6 +812,7 @@ byte rxBufferCheckCount = 0; 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. @@ -819,6 +820,8 @@ char iopStatusWindow[4] = " "; // may need to move this if it's not visible t #define SMETER_DELAY_TIME 5000 bool displaySmeter = true; int delaySmeter; +int delayTopLine = 0; +int stateTopLine = 0; //fromType normal : 0, TX : 1, CW_STRAIGHT : 2, CW_PADDLE : 3, CW_AUTOMODE : 4 //if cw mode, no delay @@ -865,16 +868,13 @@ void Check_Cat(byte fromType) //Arived CAT DATA // KC4UPR - IOP update - 6 characters; first character determines mode (CAT or IOP) - for (i = 0; i < 6; i++) { //5; i++) - if (i == 0) { - byte first = Serial.read(); - readPrefix = byteToPrefix(first); - readLength = byteToLength(first); - } else { - CAT_BUFF[i-1] = Serial.read(); - } + // Will adjust based on readlength + byte first = Serial.read(); + readPrefix = byteToPrefix(first); + readLength = byteToLength(first); + 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. @@ -884,7 +884,7 @@ void Check_Cat(byte fromType) isProcessCheck_Cat = 1; if (readPrefix == IOP_PREFIX) { - recvIOPMessage(msg, CAT_BUFF, 5); // not super robust... if IOP ever sends more (or less) than a 5 (6) byte message + 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: @@ -901,6 +901,20 @@ void Check_Cat(byte fromType) case IOP_MODE_REQUEST: iopSendMode(cwMode, isUSB, digiMode, isTest); break; + + case IOP_MENU_DISPLAY_MSG: + for (int i = 0; i < 16; i++) { + iopMenuDisplay[i] = msg.data[i+1]; + } + if (int8_t(msg.data[0]) == 0) { + stateTopLine = 4; + } else if (int8_t(msg.data[0]) < 0) { + stateTopLine = 0; + } else { // > 0 + delayTopLine = millis() + (int8_t(msg.data[0]) * 1000); + stateTopLine = 2; + } + break; } } else if (readPrefix == CAT_PREFIX) { diff --git a/ubitx_20/ubitx_lcd_1602.ino b/ubitx_20/ubitx_lcd_1602.ino index 7d58293..2fc2da5 100644 --- a/ubitx_20/ubitx_lcd_1602.ino +++ b/ubitx_20/ubitx_lcd_1602.ino @@ -530,6 +530,25 @@ int freqScrollPosition = 0; void updateLine2Buffer(char displayType) { unsigned long tmpFreq = 0; + + if ((stateTopLine == 2) || (stateTopLine == 4)) { + strcpy(line2Buffer, iopMenuDisplay); + if (stateTopLine == 4) { + stateTopLine = 3; + } else { + stateTopLine = 1; + } + } + if (stateTopLine == 3) { + return; + } else if (stateTopLine == 1) { + if (delayTopLine < millis()) { + stateTopLine = 0; + } else { + return; + } + } + if (ritOn) { strcpy(line2Buffer, "RitTX:"); @@ -738,7 +757,7 @@ void idle_process() return; //if line2DisplayStatus == 0 <-- this condition is clear Line, you can display any message - if (line2DisplayStatus == 0 || (((displayOption1 & 0x04) == 0x04) && line2DisplayStatus == 2)) { + if (line2DisplayStatus == 0 || (((displayOption1 & 0x04) == 0x04) && line2DisplayStatus == 2) || stateTopLine > 0) { if (checkCount++ > 1) { updateLine2Buffer(0); //call by scheduler From 10926b54ec1cd5cf9d275c7f5d92d2bf6af286b5 Mon Sep 17 00:00:00 2001 From: Rob French Date: Sat, 6 Jun 2020 22:25:11 -0500 Subject: [PATCH 168/173] Further functioning, but with bugs. Top line is working, but clear there is messed up interplay between between how IOP updates its menu and sends it to the Raduino, and how the Raduino updates its display. Needs to be worked on. Issues: - After deselecting the I/O menu (i.e. should go back to normal Raduino top line), it never does. But the I/O menu is frozen. So clearly the top line never gets updated again by the Raduino. Need to understand the logic it uses to actually refresh the display. - Random garbage (black boxes) on the top line--intermittently. --- ubitx_20/cat_libs.ino | 10 +++++----- ubitx_20/ubitx_lcd_1602.ino | 10 +++++++--- 2 files changed, 12 insertions(+), 8 deletions(-) diff --git a/ubitx_20/cat_libs.ino b/ubitx_20/cat_libs.ino index c5fd8af..1ced1e3 100644 --- a/ubitx_20/cat_libs.ino +++ b/ubitx_20/cat_libs.ino @@ -817,9 +817,9 @@ 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; +//#define SMETER_DELAY_TIME 5000 +//bool displaySmeter = true; +//int delaySmeter; int delayTopLine = 0; int stateTopLine = 0; @@ -887,7 +887,7 @@ void Check_Cat(byte fromType) 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_SSB_STATUS_MSG: case IOP_DGT_STATUS_MSG: case IOP_CW_STATUS_MSG: case IOP_TEST_STATUS_MSG: @@ -896,7 +896,7 @@ void Check_Cat(byte fromType) iopStatusWindow[2] = msg.data[2]; displaySmeter = false; delaySmeter = millis() + SMETER_DELAY_TIME; - break; + break;*/ case IOP_MODE_REQUEST: iopSendMode(cwMode, isUSB, digiMode, isTest); diff --git a/ubitx_20/ubitx_lcd_1602.ino b/ubitx_20/ubitx_lcd_1602.ino index 2fc2da5..afb0da5 100644 --- a/ubitx_20/ubitx_lcd_1602.ino +++ b/ubitx_20/ubitx_lcd_1602.ino @@ -767,14 +767,18 @@ void idle_process() } } - if (!displaySmeter) { + if (stateTopLine > 0) { + return; + } + + /*if (!displaySmeter) { if (delaySmeter < millis()) { displaySmeter = true; } - } + }*/ //S-Meter Display - if (((displayOption1 & 0x08) == 0x08 && (sdrModeOn == 0)) && (++checkCountSMeter > SMeterLatency) && displaySmeter) + if (((displayOption1 & 0x08) == 0x08 && (sdrModeOn == 0)) && (++checkCountSMeter > SMeterLatency)) { int newSMeter; From 20e1eda14064368c4ae6edba21a3c70644ade453 Mon Sep 17 00:00:00 2001 From: Rob French Date: Sun, 7 Jun 2020 08:56:06 -0500 Subject: [PATCH 169/173] Removed a bunch of files that I am not maintaining to be compatible with my Raduino/IOP mods. Increaesd the update rate for the display (hopefully). --- ubitx_20/cat_libs.ino | 10 +- ubitx_20/cw_autokey.ino | 400 ----------- ubitx_20/softserial_tiny.cpp | 334 ---------- ubitx_20/ubitx.h | 2 +- ubitx_20/ubitx_20.ino | 31 +- ubitx_20/ubitx_lcd_1602.ino | 41 +- ubitx_20/ubitx_lcd_1602Dual.ino | 727 -------------------- ubitx_20/ubitx_lcd_2004.ino | 743 --------------------- ubitx_20/ubitx_lcd_nextion.ino | 1107 ------------------------------- ubitx_20/ubitx_menu.ino | 2 + 10 files changed, 44 insertions(+), 3353 deletions(-) delete mode 100644 ubitx_20/cw_autokey.ino delete mode 100644 ubitx_20/softserial_tiny.cpp delete mode 100644 ubitx_20/ubitx_lcd_1602Dual.ino delete mode 100644 ubitx_20/ubitx_lcd_2004.ino delete mode 100644 ubitx_20/ubitx_lcd_nextion.ino diff --git a/ubitx_20/cat_libs.ino b/ubitx_20/cat_libs.ino index 1ced1e3..0be4e4b 100644 --- a/ubitx_20/cat_libs.ino +++ b/ubitx_20/cat_libs.ino @@ -213,11 +213,11 @@ void CatSetPTT(boolean isPTTOn, byte fromType) startTx(TX_SSB, 1); //Exit menu, Memory Keyer... ETC - if (isCWAutoMode > 0) { - isCWAutoMode = 0; - printLineF2(F("AutoKey Exit/CAT")); - //delay_background(1000, 0); - } + //if (isCWAutoMode > 0) { + // isCWAutoMode = 0; + // printLineF2(F("AutoKey Exit/CAT")); + // //delay_background(1000, 0); + //} } } else diff --git a/ubitx_20/cw_autokey.ino b/ubitx_20/cw_autokey.ino deleted file mode 100644 index 9bf838e..0000000 --- a/ubitx_20/cw_autokey.ino +++ /dev/null @@ -1,400 +0,0 @@ -/************************************************************************* - KD8CEC's Memory Keyer for HAM - - This source code is written for All amateur radio operator, - I have not had amateur radio communication for a long time. CW has been - around for a long time, and I do not know what kind of keyer and keying - software is fashionable. So I implemented the functions I need mainly. - - To minimize the use of memory space, we used bitwise operations. - For the alphabet, I put Morsecode in 1 byte. The front 4Bit is the length - and the 4Bit is the Morse code. Because the number is fixed in length, - there is no separate length information. The 5Bit on the right side is - the Morse code. - - 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 ------------------------------------------------------------------------------ - 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 - -//27 + 10 + 18 + 1(SPACE) = //56 -const PROGMEM uint8_t cwAZTable[27] = {0b00100100 , 0b01001000 , 0b01001010 , 0b00111000 , 0b00010000, 0b01000010, 0b00111100, 0b01000000 , //A ~ H -0b00100000, 0b01000111 ,0b00111010, 0b01000100, 0b00101100, 0b00101000 , 0b00111110, 0b01000110, 0b01001101, 0b00110100, //I ~ R -0b00110000, 0b00011000, 0b00110010, 0b01000001, 0b00110110, 0b01001001, 0b01001011, 0b01001100}; //S ~ Z -PGM_P pCwAZTable = reinterpret_cast(cwAZTable); - -const PROGMEM uint8_t cw09Table[27] = {0b00011111, 0b00001111, 0b00000111, 0b00000011, 0b00000001, 0b00000000, 0b00010000, 0b00011000, 0b00011100, 0b00011110}; -PGM_P pcw09Table = reinterpret_cast(cw09Table); - -//# : AR, ~:BT, [:AS, ]:SK, ^:KN -const PROGMEM uint8_t cwSymbolIndex[] = {'.', ',', '?', '"', '!', '/', '(', ')', '&', ':', ';', '=', '+', '-', '_', '\'', '@', '#', '~', '[', ']', '^' }; -PGM_P pCwSymbolIndex = reinterpret_cast(cwSymbolIndex); - -const PROGMEM uint8_t cwSymbolTable[] = {0b11010101, 0b11110011, 0b11001100, 0b11011110, 0b11101011, 0b10100100, 0b10101100, 0b11101101, 0b10010000, 0b11111000, 0b11101010, 0b10100010, 0b10010100, 0b11100001, 0b11001101, 0b11010010, 0b11011010, 0b10010100, 0b10100010, 0b10010000, 0b11000101, 0b10101100}; -PGM_P pCwSymbolTable = reinterpret_cast(cwSymbolTable); -////const PROGMEM uint8_t cwSymbolLength[] = {6, 6, 6, 6, 6, 5, 5, 6, 5, 6, 6, 5, 5, 6, 6, 6, 6, 5, 5, 5, 6, 5}; - -// ":(Start"), ':(End "), >: My callsign, <:QSO Callsign (Second Callsign), #:AR, ~:BT, [:AS, ]:SK - -byte knobPosition = 0; -//byte cwTextData[30]; //Maximum 30 Remarked by KD8CE -> Direct Read EEPROM -byte autoCWSendCharEndIndex = 0; -byte autoCWSendCharIndex = 0; -unsigned long autoCWbeforeTime = 0; //for interval time between chars -byte pttBeforeStatus = 1; //PTT : default high -byte isKeyStatusAfterCWStart = 0; //0 : Init, 1 : Keyup after auto CW Start, 2 : Keydown after -byte selectedCWTextIndex = 0; -unsigned long autoCWKeydownCheckTime = 0; //for interval time between chars -byte changeReserveStatus = 0; -byte isAutoCWHold = 0; //auto CW Pause => Manual Keying => auto - -void autoSendPTTCheck() -{ - if (isCWAutoMode == 2) { //Sending Mode - //check PTT Button - //short Press => reservation or cancel - //long Press => Hold - if (digitalRead(PTT) == LOW) - { - //if (isKeyStatusAfterCWStart == 0) //Yet Press PTT from start TX - //{ - //} - - if (isKeyStatusAfterCWStart == 1) //while auto cw send, ptt up and ptt down again - { - //Start Time - autoCWKeydownCheckTime = millis() + 200; //Long push time - isKeyStatusAfterCWStart = 2; //Change status => ptt down agian - } - else if (isKeyStatusAfterCWStart == 2 && autoCWKeydownCheckTime < millis()) - { - //Hold Mode - isAutoCWHold = 1; - isKeyStatusAfterCWStart = 3; - } - else if (isKeyStatusAfterCWStart == 3) - { - autoCWKeydownCheckTime = millis() + 200; - } - } - else - { - //PTT UP - if (isKeyStatusAfterCWStart == 2) //0 (down before cw start) -> 1 (up while cw sending) -> 2 (down while cw sending) - { - if (autoCWKeydownCheckTime > millis()) //Short : Reservation or cancel Next Text - { - if (autoCWSendReservCount == 0 || - (autoCWSendReservCount < AUTO_CW_RESERVE_MAX && - autoCWSendReserv[autoCWSendReservCount - 1] != selectedCWTextIndex)) - { - //Reserve - autoCWSendReserv[autoCWSendReservCount++] = selectedCWTextIndex; - changeReserveStatus = 1; - } - else if (autoCWSendReservCount > 0 && autoCWSendReserv[autoCWSendReservCount - 1] == selectedCWTextIndex) - { - autoCWSendReservCount--; - changeReserveStatus = 1; - } - } // end of Short Key up - } - else if (isKeyStatusAfterCWStart == 3) //play from Hold (pause Auto CW Send) - { - isAutoCWHold = 0; - } - - isKeyStatusAfterCWStart = 1; //Change status => ptt up (while cw send mode) - } //end of PTT UP - } -} - -//Send 1 char -void sendCWChar(char cwKeyChar) -{ - byte sendBuff[7]; - byte i, j, charLength; - byte tmpChar; - - //For Macrofunction - //replace > and < to My callsign, qso callsign, use recursive function call - if (cwKeyChar == '>' || cwKeyChar == '<') - { - uint16_t callsignStartIndex = 0; - uint16_t callsignEndIndex = 0; - - if (cwKeyChar == '>') //replace my callsign - { - if (userCallsignLength > 0) - { - callsignStartIndex = 0; - callsignEndIndex = userCallsignLength; - } - } - else if (cwKeyChar == '<') //replace qso callsign - { - //ReadLength - callsignEndIndex = EEPROM.read(CW_STATION_LEN); - if (callsignEndIndex > 0) - { - callsignStartIndex = CW_STATION_LEN - callsignEndIndex - USER_CALLSIGN_DAT; - callsignEndIndex = callsignStartIndex + callsignEndIndex; - } - } - - if (callsignStartIndex == 0 && callsignEndIndex == 0) - return; - - for (uint16_t i = callsignStartIndex; i <= callsignEndIndex; i++) - { - sendCWChar(EEPROM.read(USER_CALLSIGN_DAT + i)); - autoSendPTTCheck(); //for reserve and cancel next CW Text - if (changeReserveStatus == 1) - { - changeReserveStatus = 0; - updateDisplay(); - } - - if (i < callsignEndIndex) delay_background(cwSpeed * 3, 4); // - } - - return; - } - else if (cwKeyChar >= 'A' && cwKeyChar <= 'Z') //Encode Char by KD8CEC - { - tmpChar = pgm_read_byte(pCwAZTable + (cwKeyChar - 'A')); - charLength = (tmpChar >> 4) & 0x0F; - for (i = 0; i < charLength; i++) - sendBuff[i] = (tmpChar << i) & 0x08; - } - else if (cwKeyChar >= '0' && cwKeyChar <= '9') - { - charLength = 5; - for (i = 0; i < charLength; i++) - sendBuff[i] = (pgm_read_byte(pcw09Table + (cwKeyChar - '0')) << i) & 0x10; - } - else if (cwKeyChar == ' ') - { - charLength = 0; - delay_background(cwSpeed * 4, 4); //7 -> basic interval is 3 - } - else if (cwKeyChar == '$') //7 digit - { - charLength = 7; - for (i = 0; i < 7; i++) - sendBuff[i] = (0b00010010 << i) & 0x80; //...1..1 - } - else - { - //symbol - for (i = 0; i < 22; i++) - { - if (pgm_read_byte(pCwSymbolIndex + i) == cwKeyChar) - { - tmpChar = pgm_read_byte(pCwSymbolTable + i); - charLength = ((tmpChar >> 6) & 0x03) + 3; - - for (j = 0; j < charLength; j++) - sendBuff[j] = (tmpChar << (j + 2)) & 0x80; - - break; - } - else - { - charLength = 0; - } - } - } - - for (i = 0; i < charLength; i++) - { - cwKeydown(); - if (sendBuff[i] == 0) - delay_background(cwSpeed, 4); - else - delay_background(cwSpeed * 3, 4); - cwKeyUp(); - if (i != charLength -1) - delay_background(cwSpeed, 4); - } -} - -byte isNeedScroll = 0; -unsigned long scrollDispayTime = 0; -#define scrollSpeed 500 -byte displayScrolStep = 0; - -void controlAutoCW(){ - int knob = 0; - byte i; - - byte cwStartIndex, cwEndIndex; - - if (cwAutoDialType == 0) - knob = enc_read(); - - if (knob != 0 || beforeCWTextIndex == 255 || isNeedScroll == 1){ //start display - if (knobPosition > 0 && knob < 0) - knobPosition--; - if (knobPosition < cwAutoTextCount * 10 -1 && knob > 0) - knobPosition++; - - selectedCWTextIndex = knobPosition / 10; - - if ((beforeCWTextIndex != selectedCWTextIndex) || - (isNeedScroll == 1 && beforeCWTextIndex == selectedCWTextIndex && scrollDispayTime < millis())) { - //Read CW Text Data Position From EEProm - EEPROM.get(CW_AUTO_DATA + (selectedCWTextIndex * 2), cwStartIndex); - EEPROM.get(CW_AUTO_DATA + (selectedCWTextIndex * 2 + 1), cwEndIndex); - - if (beforeCWTextIndex == selectedCWTextIndex) - { - if (++displayScrolStep > cwEndIndex - cwStartIndex) - displayScrolStep = 0; - } - else - { - displayScrolStep = 0; - } - -#ifdef USE_SW_SERIAL - //Not need Scroll - //Display_AutoKeyTextIndex(selectedCWTextIndex); - SendCommand1Num('w', selectedCWTextIndex); //Index - SendEEPromData('a', cwStartIndex + CW_DATA_OFSTADJ, cwEndIndex + CW_DATA_OFSTADJ, 0) ; //Data - SendCommand1Num('y', 1); //Send YN - isNeedScroll = 0; -#else - printLineFromEEPRom(0, 2, cwStartIndex + displayScrolStep + CW_DATA_OFSTADJ, cwEndIndex + CW_DATA_OFSTADJ, 0); - isNeedScroll = (cwEndIndex - cwStartIndex) > 14 ? 1 : 0; - Display_AutoKeyTextIndex(selectedCWTextIndex); -#endif - scrollDispayTime = millis() + scrollSpeed; - beforeCWTextIndex = selectedCWTextIndex; - } - } //end of check knob - - if (isCWAutoMode == 1) { //ready status - if (digitalRead(PTT) == LOW) //PTT Down : Start Auto CW or DialMode Change - { - if (pttBeforeStatus == 1) //High to Low Change - { - autoCWbeforeTime = millis() + 500; //Long push time - pttBeforeStatus = 0; - } - else if (autoCWbeforeTime < millis()) //while press PTT, OK Long push then Send Auto CW Text - { - sendingCWTextIndex = selectedCWTextIndex; - - //Information about Auto Send CW Text - autoCWSendCharEndIndex = cwEndIndex; //length of CW Text //ianlee - autoCWSendCharIndex = cwStartIndex; //position of Sending Char //ianlee - - isCWAutoMode = 2; //auto sending start - autoCWbeforeTime = 0; //interval between chars, 0 = always send - isKeyStatusAfterCWStart = 0; //Init PTT Key status - autoCWSendReservCount = 0; //Init Reserve Count - isAutoCWHold = 0; - if (!inTx){ //if not TX Status, change RX -> TX - keyDown = 0; - startTx(TX_CW, 0); //disable updateDisplay Command for reduce latency time - updateDisplay(); - - delay_background(delayBeforeCWStartTime * 2, 2); //for External AMP or personal situation - } - } - } - else if (pttBeforeStatus == 0 && autoCWbeforeTime > 0) //while reade status LOW -> HIGH (before Auto send Before) - { - pttBeforeStatus = 1; //HIGH - if (autoCWbeforeTime > millis()) //short Press -> ? DialModeChange - { - cwAutoDialType = (cwAutoDialType == 1 ? 0 : 1); //Invert DialMode between select CW Text and Frequency Tune - if (cwAutoDialType == 0) - printLineF1(F("Dial:Select Text")); - else - printLineF1(F("Dial:Freq Tune")); - - delay_background(1000, 0); - updateDisplay(); - } - } - } //end of isCWAutoMode == 1 condition - - if (isCWAutoMode == 2) { //Sending Mode - autoSendPTTCheck(); - - //check interval time, if you want adjust interval between chars, modify below - if (isAutoCWHold == 0 && (millis() - autoCWbeforeTime > cwSpeed * 3)) - { - if (!inTx){ //if not TX Status, change RX -> TX - keyDown = 0; - startTx(TX_CW, 0); //disable updateDisplay Command for reduce latency time - } - - sendCWChar(EEPROM.read(CW_AUTO_DATA + autoCWSendCharIndex++)); - - if (autoCWSendCharIndex > autoCWSendCharEndIndex) { //finish auto cw send - //check reserve status - if (autoCWSendReservCount > 0) - { - //prepare - sendingCWTextIndex = autoCWSendReserv[0]; - - for (i = 0; i < AUTO_CW_RESERVE_MAX -1; i++) - autoCWSendReserv[i] = autoCWSendReserv[i + 1]; - - EEPROM.get(CW_AUTO_DATA + (sendingCWTextIndex * 2), cwStartIndex); - EEPROM.get(CW_AUTO_DATA + (sendingCWTextIndex * 2 + 1), cwEndIndex); - - //Information about Auto Send CW Text - autoCWSendCharEndIndex = cwEndIndex; //length of CW Text //ianlee - autoCWSendCharIndex = cwStartIndex; //position of Sending Char //ianlee - autoCWSendReservCount--; //Decrease - - sendCWChar(' '); //APPLY SPACE between CW Texts - changeReserveStatus = 1; - } - else - { - isCWAutoMode = 1; //ready status - delay_background(cwDelayTime * 10, 2); - stopTx(); - } - } - - autoCWbeforeTime = millis(); - - if (changeReserveStatus == 1) - { - changeReserveStatus = 0; - updateDisplay(); - } - } - } - - //abort if this button is down - if (btnDown()) - { - isCWAutoMode = 0; //dsiable Auto CW Mode - printLine2ClearAndUpdate(); - delay_background(1000, 0); - } -} - diff --git a/ubitx_20/softserial_tiny.cpp b/ubitx_20/softserial_tiny.cpp deleted file mode 100644 index 6023c67..0000000 --- a/ubitx_20/softserial_tiny.cpp +++ /dev/null @@ -1,334 +0,0 @@ -/* -Softserial for Nextion LCD and Control MCU -KD8CEC, Ian Lee ------------------------------------------------------------------------ -It is a library rewritten in C format based on SoftwareSerial.c. -I tried to use as much as possible without modifying the SoftwareSerial. -But eventually I had to modify the code. - -I rewrote it in C for the following reasons. - - Problems occurred when increasing Program Size and Program Memory - - We had to reduce the program size. - Of course, Software Serial is limited to one. - - reduce the steps for transmitting and receiving - -useage -extern void SWSerial_Begin(long speedBaud); -extern void SWSerial_Write(uint8_t b); -extern int SWSerial_Available(void); -extern int SWSerial_Read(void); -extern void SWSerial_Print(uint8_t *b); - -If you use Softwreserial library instead of this library, you can modify the code as shown below. -I kept the function name of SoftwareSerial so you only need to modify a few lines of code. - -define top of source code -#include -SoftwareSerial sSerial(10, 11); // RX, TX - -replace source code -SWSerial_Begin to sSerial.begin -SWSerial_Write to sSerial.write -SWSerial_Available to sSerial.available -SWSerial_Read to sSerial.read - -KD8CEC, Ian Lee ------------------------------------------------------------------------ -License -All licenses for the source code are subject to the license of the original source SoftwareSerial Library. -However, if you use or modify this code, please keep the all comments in this source code. -KD8CEC ------------------------------------------------------------------------ -License from SoftwareSerial ------------------------------------------------------------------------ -SoftwareSerial.cpp (formerly NewSoftSerial.cpp) - -Multi-instance software serial library for Arduino/Wiring --- Interrupt-driven receive and other improvements by ladyada - (http://ladyada.net) --- Tuning, circular buffer, derivation from class Print/Stream, - multi-instance support, porting to 8MHz processors, - various optimizations, PROGMEM delay tables, inverse logic and - direct port writing by Mikal Hart (http://www.arduiniana.org) --- Pin change interrupt macros by Paul Stoffregen (http://www.pjrc.com) --- 20MHz processor support by Garrett Mace (http://www.macetech.com) --- ATmega1280/2560 support by Brett Hagman (http://www.roguerobotics.com/) - -This library is free software; you can redistribute it and/or -modify it under the terms of the GNU Lesser General Public -License as published by the Free Software Foundation; either -version 2.1 of the License, or (at your option) any later version. - -This library 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 -Lesser General Public License for more details. - -You should have received a copy of the GNU Lesser General Public -License along with this library; if not, write to the Free Software -Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - -The latest version of this library can always be found at -http://arduiniana.org. - */ -#include - -//================================================================ -//Public Variable -//================================================================ -#define TX_PIN 9 -#define RX_PIN 8 -#define _SS_MAX_RX_BUFF 35 // RX buffer size -#define PRINT_MAX_LENGTH 30 - -//================================================================ -//Internal Variable from SoftwareSerial.c and SoftwareSerial.h -//================================================================ -//variable from softwareserial.c and softwareserial.h -static uint8_t swr_receive_buffer[_SS_MAX_RX_BUFF]; - -volatile uint8_t *_transmitPortRegister; //Write Port Register -uint8_t transmit_RegMask; //use Mask bit 1 -uint8_t transmit_InvMask; //use mask bit 0 - -volatile uint8_t *_receivePortRegister; //Read Port Register -uint8_t _receiveBitMask; - -//delay value for Bit -uint16_t _tx_delay; - -//delay value for Receive -uint16_t _rx_delay_stopbit; -uint16_t _rx_delay_centering; -uint16_t _rx_delay_intrabit; - -//Customize for uBITX Protocol -int8_t receiveIndex = 0; -uint8_t receivedCommandLength = 0; -int8_t ffCount = 0; - -//Values for Receive Buffer -//uint16_t _buffer_overflow; -//static volatile uint8_t _receive_buffer_head; -//static volatile uint8_t _receive_buffer_tail; - -//Values for Interrupt (check Start Bit) -volatile uint8_t *_pcint_maskreg; -uint8_t _pcint_maskvalue; - -//================================================================ -//Internal Function from SoftwareSerial.c -//================================================================ -uint16_t subtract_cap(uint16_t num, uint16_t sub) -{ - if (num > sub) - return num - sub; - else - return 1; -} - -inline void tunedDelay(uint16_t delay) -{ - _delay_loop_2(delay); -} - -void setRxIntMsk(bool enable) -{ - if (enable) - *_pcint_maskreg |= _pcint_maskvalue; - else - *_pcint_maskreg &= ~_pcint_maskvalue; -} - -uint8_t rx_pin_read() -{ - return *_receivePortRegister & _receiveBitMask; -} - -// -// The receive routine called by the interrupt handler -// -void softSerail_Recv() -{ -#if GCC_VERSION < 40302 -// Work-around for avr-gcc 4.3.0 OSX version bug -// Preserve the registers that the compiler misses -// (courtesy of Arduino forum user *etracer*) - asm volatile( - "push r18 \n\t" - "push r19 \n\t" - "push r20 \n\t" - "push r21 \n\t" - "push r22 \n\t" - "push r23 \n\t" - "push r26 \n\t" - "push r27 \n\t" - ::); -#endif - - uint8_t d = 0; - - // If RX line is high, then we don't see any start bit - // so interrupt is probably not for us - if (!rx_pin_read()) //Start Bit - { - // Disable further interrupts during reception, this prevents - // triggering another interrupt directly after we return, which can - // cause problems at higher baudrates. - setRxIntMsk(false); - - // Wait approximately 1/2 of a bit width to "center" the sample - tunedDelay(_rx_delay_centering); - - // Read each of the 8 bits - for (uint8_t i=8; i > 0; --i) - { - tunedDelay(_rx_delay_intrabit); - d >>= 1; - - if (rx_pin_read()) - d |= 0x80; - } - - if (receivedCommandLength == 0) //check Already Command - { - //Set Received Data - swr_receive_buffer[receiveIndex++] = d; - - //Finded Command - if (d == 0x73 && ffCount > 1 && receiveIndex > 6) - { - receivedCommandLength = receiveIndex; - receiveIndex = 0; - ffCount = 0; - } - else if (receiveIndex > _SS_MAX_RX_BUFF) - { - //Buffer Overflow - receiveIndex = 0; - ffCount = 0; - } - else if (d == 0xFF) - { - ffCount++; - } - else - { - ffCount = 0; - } - } - - // skip the stop bit - tunedDelay(_rx_delay_stopbit); - - // Re-enable interrupts when we're sure to be inside the stop bit - setRxIntMsk(true); - } - -#if GCC_VERSION < 40302 -// Work-around for avr-gcc 4.3.0 OSX version bug -// Restore the registers that the compiler misses - asm volatile( - "pop r27 \n\t" - "pop r26 \n\t" - "pop r23 \n\t" - "pop r22 \n\t" - "pop r21 \n\t" - "pop r20 \n\t" - "pop r19 \n\t" - "pop r18 \n\t" - ::); -#endif -} - -ISR(PCINT0_vect) -{ - softSerail_Recv(); -} - -//================================================================ -//Public Function from SoftwareSerial.c and modified and create -//================================================================ -// Read data from buffer -void SWSerial_Read(uint8_t * receive_cmdBuffer) -{ - for (int i = 0; i < receivedCommandLength; i++) - receive_cmdBuffer[i] = swr_receive_buffer[i]; -} - -void SWSerial_Write(uint8_t b) -{ - volatile uint8_t *reg = _transmitPortRegister; - uint8_t oldSREG = SREG; - uint16_t delay = _tx_delay; - - cli(); // turn off interrupts for a clean txmit - - // Write the start bit - *reg &= transmit_InvMask; - - tunedDelay(delay); - - // Write each of the 8 bits - for (uint8_t i = 8; i > 0; --i) - { - if (b & 1) // choose bit - *reg |= transmit_RegMask; // send 1 - else - *reg &= transmit_InvMask; // send 0 - - tunedDelay(delay); - b >>= 1; - } - - // restore pin to natural state - *reg |= transmit_RegMask; - - SREG = oldSREG; // turn interrupts back on - tunedDelay(_tx_delay); -} - -void SWSerial_Print(uint8_t *b) -{ - for (int i = 0; i < PRINT_MAX_LENGTH; i++) - { - if (b[i] == 0x00) - break; - else - SWSerial_Write(b[i]); - } -} - -void SWSerial_Begin(long speedBaud) -{ - //INT TX_PIN - digitalWrite(TX_PIN, HIGH); - pinMode(TX_PIN, OUTPUT); - transmit_RegMask = digitalPinToBitMask(TX_PIN); //use Bit 1 - transmit_InvMask = ~digitalPinToBitMask(TX_PIN); //use Bit 0 - _transmitPortRegister = portOutputRegister(digitalPinToPort(TX_PIN)); - - //INIT RX_PIN - pinMode(RX_PIN, INPUT); - digitalWrite(RX_PIN, HIGH); // pullup for normal logic! - _receiveBitMask = digitalPinToBitMask(RX_PIN); - _receivePortRegister = portInputRegister(digitalPinToPort(RX_PIN)); - - //Set Values - uint16_t bit_delay = (F_CPU / speedBaud) / 4; - _tx_delay = subtract_cap(bit_delay, 15 / 4); - - if (digitalPinToPCICR(RX_PIN)) - { - _rx_delay_centering = subtract_cap(bit_delay / 2, (4 + 4 + 75 + 17 - 23) / 4); - _rx_delay_intrabit = subtract_cap(bit_delay, 23 / 4); - _rx_delay_stopbit = subtract_cap(bit_delay * 3 / 4, (37 + 11) / 4); - *digitalPinToPCICR(RX_PIN) |= _BV(digitalPinToPCICRbit(RX_PIN)); - _pcint_maskreg = digitalPinToPCMSK(RX_PIN); - _pcint_maskvalue = _BV(digitalPinToPCMSKbit(RX_PIN)); - - tunedDelay(_tx_delay); // if we were low this establishes the end - } - - //Start Listen - setRxIntMsk(true); -} diff --git a/ubitx_20/ubitx.h b/ubitx_20/ubitx.h index 427a99e..f9d1d92 100644 --- a/ubitx_20/ubitx.h +++ b/ubitx_20/ubitx.h @@ -69,7 +69,7 @@ extern byte I2C_LCD_SECOND_ADDRESS; //only using Dual LCD Mode #define FN_CW_SPEED 1 //152 #define FN_VFOTOMEM 1 //254 #define FN_MEMTOVFO 1 //188 -#define FN_MEMORYKEYER 1 //156 +#define FN_MEMORYKEYER 0 //156 #define FN_WSPR 1 //1044 #define FN_SDRMODE 1 //68 #define FN_CALIBRATION 1 //666 diff --git a/ubitx_20/ubitx_20.ino b/ubitx_20/ubitx_20.ino index fc9cfee..deb97b8 100644 --- a/ubitx_20/ubitx_20.ino +++ b/ubitx_20/ubitx_20.ino @@ -172,15 +172,15 @@ 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 +//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 +//#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) @@ -342,7 +342,7 @@ byte delay_background(unsigned delayTime, byte fromType){ //fromType : 4 autoCWK return 1; //Check PTT while auto Sending - autoSendPTTCheck(); + //autoSendPTTCheck(); Check_Cat(3); } @@ -732,8 +732,8 @@ void checkButton(){ SetSWActivePage(1); doMenu(); - if (isCWAutoMode == 0) - SetSWActivePage(0); + //if (isCWAutoMode == 0) + SetSWActivePage(0); #else doMenu(); #endif @@ -1511,15 +1511,16 @@ void loop(){ //tune only when not tranmsitting if (!inTx){ - if (isCWAutoMode == 0 || cwAutoDialType == 1) - { + //if (isCWAutoMode == 0 || cwAutoDialType == 1) + //{ if (ritOn) doRIT(); else doTuningWithThresHold(); - } + //} - if (isCWAutoMode == 0 && beforeIdle_ProcessTime < millis() - 250) { + // 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(); diff --git a/ubitx_20/ubitx_lcd_1602.ino b/ubitx_20/ubitx_lcd_1602.ino index afb0da5..dbe1e15 100644 --- a/ubitx_20/ubitx_lcd_1602.ino +++ b/ubitx_20/ubitx_lcd_1602.ino @@ -417,20 +417,20 @@ void updateDisplay() { memset(c, 0, sizeof(c)); if (inTx){ - if (isCWAutoMode == 2) { - for (i = 0; i < 4; i++) - c[3-i] = (i < autoCWSendReservCount ? byteToChar(autoCWSendReserv[i]) : ' '); + //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 { + // c[4] = byteToChar(sendingCWTextIndex); + // c[5] = '='; + //} + //else { if (cwTimeout > 0) strcpy(c, " CW:"); else strcpy(c, " TX:"); - } + //} } else { if (ritOn) @@ -507,10 +507,10 @@ void updateDisplay() { LCD_SetCursor(5,diplayVFOLine); LCD_Write((uint8_t)0); } - else if (isCWAutoMode == 2){ - LCD_SetCursor(5,diplayVFOLine); - LCD_Write(0x7E); - } + //else if (isCWAutoMode == 2){ + // LCD_SetCursor(5,diplayVFOLine); + // LCD_Write(0x7E); + //} else { LCD_SetCursor(5,diplayVFOLine); @@ -691,18 +691,17 @@ void updateLine2Buffer(char displayType) if (isStepKhz == 0) { - // KC4UPR: Getting rid of the "Hz" to unclutter the top line. - line2Buffer[11] = ' '; //'H'; - line2Buffer[12] = ' '; //'z'; + line2Buffer[11] = 'H'; + line2Buffer[12] = 'z'; } - //line2Buffer[13] = ' '; + 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 + //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'; @@ -723,7 +722,7 @@ void updateLine2Buffer(char displayType) { line2Buffer[14] = 'I'; line2Buffer[15] = 'B'; - } */ + } } } diff --git a/ubitx_20/ubitx_lcd_1602Dual.ino b/ubitx_20/ubitx_lcd_1602Dual.ino deleted file mode 100644 index 48598cb..0000000 --- a/ubitx_20/ubitx_lcd_1602Dual.ino +++ /dev/null @@ -1,727 +0,0 @@ -/************************************************************************* - KD8CEC's uBITX Display Routine for LCD1602 Dual LCD - 1.This is the display code for the 16x02 Dual LCD - 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 I2CTinyLCD Library for Dual LCD by KD8CEC -//======================================================================== -#ifdef UBITX_DISPLAY_LCD1602I_DUAL - -#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_Dual_Init() -{ - //I2C Init - _cols = 16; - _rows = 2; - _backlightval = LCD_NOBACKLIGHT; - Wire.begin(); - - delay(50); - - // Now we pull both RS and R/W low to begin commands - _Addr = I2C_LCD_MASTER_ADDRESS; - expanderWrite(_backlightval); // reset expanderand turn backlight off (Bit 8 =1) - _Addr = I2C_LCD_SECOND_ADDRESS; - 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 - - _Addr = I2C_LCD_MASTER_ADDRESS; - // 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(); - - - _Addr = I2C_LCD_SECOND_ADDRESS; - // 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(); - - //Change to Default LCD (Master) - _Addr = I2C_LCD_MASTER_ADDRESS; -} - - -//======================================================================== -// 16 X 02 LCD Routines -//Begin of Display Base Routines (Init, printLine..) -//======================================================================== - -void LCD_Print(const char *c) -{ - for (uint8_t i = 0; i < strlen(c); i++) - { - if (*(c + i) == 0x00) return; - LCD_Write(*(c + i)); - } -} - -const int row_offsets[] = { 0x00, 0x40, 0x14, 0x54 }; -void LCD_SetCursor(uint8_t col, uint8_t row) -{ - LCD_Command(LCD_SETDDRAMADDR | (col + row_offsets[row])); //0 : 0x00, 1 : 0x40, only for 20 x 4 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]); -} - -//SWR GRAPH, DrawMeter and drawingMeter Logic function by VK2ETA -//#define OPTION_SKINNYBARS - -char c[30], b[30]; -char printBuff[4][20]; //mirrors what is showing on the two lines of the display - -void LCD_Init(void) -{ - LCD1602_Dual_Init(); - - _Addr = I2C_LCD_SECOND_ADDRESS; - initMeter(); //for Meter Display //when dual LCD, S.Meter on second LCD - _Addr = I2C_LCD_MASTER_ADDRESS; -} - - -// 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 < 20; i++) { // add white spaces until the end of the 20 characters line is reached - LCD_Write(' '); - } - } -} - -void printLineF(char linenmbr, const __FlashStringHelper *c) -{ - int i; - char tmpBuff[21]; - PGM_P p = reinterpret_cast(c); - - for (i = 0; i < 21; i++){ - unsigned char fChar = pgm_read_byte(p++); - tmpBuff[i] = fChar; - if (fChar == 0) - break; - } - - printLine(linenmbr, tmpBuff); -} - -#define LCD_MAX_COLUMN 20 -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 < 20; 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 - // i also Very TNX Purdum for good source code - 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 (isUSB) - strcpy(c, "USB "); - else - strcpy(c, "LSB "); - } - else if (cwMode == 1) - { - strcpy(c, "CWL "); - } - else - { - strcpy(c, "CWU "); - } - } - - 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) - 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[20]; -//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 (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] = ' '; - } - - memset(&line2Buffer[10], ' ', 10); - - if (isIFShift) - { - line2Buffer[6] = 'M'; - line2Buffer[7] = ' '; - //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); - - for (int i = 12; i < 17; i++) - { - if (line2Buffer[i] == 0) - line2Buffer[i] = ' '; - } - } // end of display IF - else // step & Key Type display - { - //Step - long tmpStep = arTuneStep[tuneStepIndex -1]; - - byte isStepKhz = 0; - if (tmpStep >= 1000) - { - isStepKhz = 2; - } - - for (int i = 13; i >= 11 - isStepKhz; i--) { - if (tmpStep > 0) { - line2Buffer[i + isStepKhz] = tmpStep % 10 + 0x30; - tmpStep /= 10; - } - else - line2Buffer[i +isStepKhz] = ' '; - } - - if (isStepKhz == 0) - { - line2Buffer[14] = 'H'; - line2Buffer[15] = 'z'; - } - } - - //line2Buffer[17] = ' '; - /* ianlee - //Check CW Key cwKeyType = 0; //0: straight, 1 : iambica, 2: iambicb - if (cwKeyType == 0) - { - line2Buffer[18] = 'S'; - line2Buffer[19] = 'T'; - } - else if (cwKeyType == 1) - { - line2Buffer[18] = 'I'; - line2Buffer[19] = 'A'; - } - else - { - line2Buffer[18] = 'I'; - line2Buffer[19] = '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); - - LCD_SetCursor(drawPosition, 0); - LCD_Write('S'); - - LCD_Write(':'); - for (int i = 0; i < 7; i++) - LCD_Write(lcdMeter[i]); - } -} - - -char checkCount = 0; -char checkCountSMeter = 0; - -char beforeKeyType = -1; -char displaySDRON = 0; - -//execute interval : 0.25sec -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)) { - if (checkCount++ > 1) - { - updateLine2Buffer(0); //call by scheduler - printLine2(line2Buffer); - line2DisplayStatus = 2; - checkCount = 0; - - //check change CW Key Type - if (beforeKeyType != cwKeyType) - { - _Addr = I2C_LCD_SECOND_ADDRESS; - LCD_SetCursor(10, 0); - LCD_Write('K'); - LCD_Write('E'); - LCD_Write('Y'); - LCD_Write(':'); - - //Check CW Key cwKeyType = 0; //0: straight, 1 : iambica, 2: iambicb - if (cwKeyType == 0) - { - LCD_Write('S'); - LCD_Write('T'); - } - else if (cwKeyType == 1) - { - LCD_Write('I'); - LCD_Write('A'); - } - else - { - LCD_Write('I'); - LCD_Write('B'); - } - - beforeKeyType = cwKeyType; - _Addr = I2C_LCD_MASTER_ADDRESS; - } //Display Second Screen - - } - } - - //EX for Meters - - //S-Meter Display - _Addr = I2C_LCD_SECOND_ADDRESS; - if (sdrModeOn == 1) - { - if (displaySDRON == 0) //once display - { - displaySDRON = 1; - LCD_SetCursor(0, 0); - LCD_Write('S'); - LCD_Write('D'); - LCD_Write('R'); - LCD_Write(' '); - LCD_Write('M'); - LCD_Write('O'); - LCD_Write('D'); - LCD_Write('E'); - } - } - else if (((displayOption1 & 0x08) == 0x08) && (++checkCountSMeter > 3)) - { - int newSMeter; - displaySDRON = 0; - -#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); - //currentSMeter = (currentSMeter * 3 + newSMeter * 7) / 10; //remarked becaused of have already Latency time - currentSMeter = newSMeter; - - scaledSMeter = 0; - for (byte s = 8; s >= 1; s--) { - if (currentSMeter > sMeterLevels[s]) { - scaledSMeter = s; - break; - } - } -#endif - - DisplayMeter(0, scaledSMeter, 0); - checkCountSMeter = 0; - } //end of S-Meter - _Addr = I2C_LCD_MASTER_ADDRESS; - - - } -} - -//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) -{ - _Addr = I2C_LCD_SECOND_ADDRESS; - printLineFromEEPRom(1, 16 - userCallsignLength, 0, userCallsignLength -1, 0); //eeprom to lcd use offset (USER_CALLSIGN_DAT) - _Addr = I2C_LCD_MASTER_ADDRESS; -} - -void DisplayVersionInfo(const __FlashStringHelper * fwVersionInfo) -{ - _Addr = I2C_LCD_SECOND_ADDRESS; - printLineF(1, fwVersionInfo); - _Addr = I2C_LCD_MASTER_ADDRESS; -} - -#endif diff --git a/ubitx_20/ubitx_lcd_2004.ino b/ubitx_20/ubitx_lcd_2004.ino deleted file mode 100644 index 06d44fa..0000000 --- a/ubitx_20/ubitx_lcd_2004.ino +++ /dev/null @@ -1,743 +0,0 @@ -/************************************************************************* - KD8CEC's uBITX Display Routine for LCD2004 Parrel & I2C - 1.This is the display code for the 20x04 LCD - 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_LCD2004P -/************************************************************************* - LCD2004TINY Library for 20 x 4 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_LCD2004_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 LCD2004_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_LCD2004I - -#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_LCD2004_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 LCD2004_Init() -{ - //I2C Init - _Addr = I2C_LCD_MASTER_ADDRESS; - _cols = 20; - _rows = 4; - _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(); -} -#endif -//======================================================================== -//End of I2CTinyLCD Library by KD8CEC -//======================================================================== - - -//======================================================================== -// 20 X 04 LCD Routines -//Begin of Display Base Routines (Init, printLine..) -//======================================================================== -#ifdef UBITX_DISPLAY_LCD2004_BASE - -void LCD_Print(const char *c) -{ - for (uint8_t i = 0; i < strlen(c); i++) - { - if (*(c + i) == 0x00) return; - LCD_Write(*(c + i)); - } -} - -const int row_offsets[] = { 0x00, 0x40, 0x14, 0x54 }; -void LCD_SetCursor(uint8_t col, uint8_t row) -{ - LCD_Command(LCD_SETDDRAMADDR | (col + row_offsets[row])); //0 : 0x00, 1 : 0x40, only for 20 x 4 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]); -} - -//SWR GRAPH, DrawMeter and drawingMeter Logic function by VK2ETA -//#define OPTION_SKINNYBARS - -char c[30], b[30]; -char printBuff[4][21]; //mirrors what is showing on the two lines of the display - -void LCD_Init(void) -{ - LCD2004_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 < 20; i++) { // add white spaces until the end of the 20 characters line is reached - LCD_Write(' '); - } - } -} - -void printLineF(char linenmbr, const __FlashStringHelper *c) -{ - int i; - char tmpBuff[21]; - PGM_P p = reinterpret_cast(c); - - for (i = 0; i < 21; i++){ - unsigned char fChar = pgm_read_byte(p++); - tmpBuff[i] = fChar; - if (fChar == 0) - break; - } - - printLine(linenmbr, tmpBuff); -} - -#define LCD_MAX_COLUMN 20 -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 < 20; 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 - // i also Very TNX Purdum for good source code - 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 (isUSB) - strcpy(c, "USB "); - else - strcpy(c, "LSB "); - } - else if (cwMode == 1) - { - strcpy(c, "CWL "); - } - else - { - strcpy(c, "CWU "); - } - } - - 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] = ' '; - } - - if (sdrModeOn) - strcat(c, " SDR"); - else - strcat(c, " SPK"); - - //remarked by KD8CEC - //already RX/TX status display, and over index (20 x 4 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[20]; -//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 (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] = ' '; - } - - memset(&line2Buffer[10], ' ', 10); - - if (isIFShift) - { - line2Buffer[6] = 'M'; - line2Buffer[7] = ' '; - //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); - - for (int i = 12; i < 17; i++) - { - if (line2Buffer[i] == 0) - line2Buffer[i] = ' '; - } - } // end of display IF - else // step & Key Type display - { - //Step - long tmpStep = arTuneStep[tuneStepIndex -1]; - - byte isStepKhz = 0; - if (tmpStep >= 1000) - { - isStepKhz = 2; - } - - for (int i = 14; i >= 12 - isStepKhz; i--) { - if (tmpStep > 0) { - line2Buffer[i + isStepKhz] = tmpStep % 10 + 0x30; - tmpStep /= 10; - } - else - line2Buffer[i +isStepKhz] = ' '; - } - - if (isStepKhz == 0) - { - line2Buffer[15] = 'H'; - line2Buffer[16] = 'z'; - } - } - - line2Buffer[17] = ' '; - - //Check CW Key cwKeyType = 0; //0: straight, 1 : iambica, 2: iambicb - if (cwKeyType == 0) - { - line2Buffer[18] = 'S'; - line2Buffer[19] = 'T'; - } - else if (cwKeyType == 1) - { - line2Buffer[18] = 'I'; - line2Buffer[19] = 'A'; - } - else - { - line2Buffer[18] = 'I'; - line2Buffer[19] = '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); - - LCD_SetCursor(drawPosition, 2); - LCD_Write('S'); - LCD_Write(':'); - for (int i = 0; i < 7; i++) //meter 5 + +db 1 = 6 - LCD_Write(lcdMeter[i]); - } -} - -char checkCount = 0; -char checkCountSMeter = 0; - -//execute interval : 0.25sec -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)) { - if (checkCount++ > 1) - { - updateLine2Buffer(0); //call by scheduler - printLine2(line2Buffer); - line2DisplayStatus = 2; - checkCount = 0; - } - } - - //EX for Meters - /* - DisplayMeter(0, testValue++, 0); - if (testValue > 30) - testValue = 0; - */ - - //Sample - //DisplayMeter(0, analogRead(ANALOG_SMETER) / 30, 0); - //DisplayMeter(0, analogRead(ANALOG_SMETER) / 10, 0); - //delay_background(10, 0); - //DisplayMeter(0, analogRead(ANALOG_SMETER), 0); - //if (testValue > 30) - // testValue = 0; - - //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 - newSMeter = analogRead(ANALOG_SMETER) / 4; - - //Faster attack, Slower release - //currentSMeter = (newSMeter > currentSMeter ? ((currentSMeter * 3 + newSMeter * 7) + 5) / 10 : ((currentSMeter * 7 + newSMeter * 3) + 5) / 10); - //currentSMeter = ((currentSMeter * 7 + newSMeter * 3) + 5) / 10; - currentSMeter = newSMeter; - - scaledSMeter = 0; - for (byte s = 8; s >= 1; s--) { - if (currentSMeter > sMeterLevels[s]) { - scaledSMeter = s; - break; - } - } -#endif - - DisplayMeter(0, scaledSMeter, 0); - 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(3, 20 - userCallsignLength, 0, userCallsignLength -1, 0); //eeprom to lcd use offset (USER_CALLSIGN_DAT) -} - -void DisplayVersionInfo(const __FlashStringHelper * fwVersionInfo) -{ - printLineF(3, fwVersionInfo); -} - -#endif diff --git a/ubitx_20/ubitx_lcd_nextion.ino b/ubitx_20/ubitx_lcd_nextion.ino deleted file mode 100644 index 62667af..0000000 --- a/ubitx_20/ubitx_lcd_nextion.ino +++ /dev/null @@ -1,1107 +0,0 @@ -/************************************************************************* - KD8CEC's uBITX Display Routine for Nextion LCD - - Uses the default protocol of Nextion LCD. - Do not assign a 2 byte address to Nextion LCD. ------------------------------------------------------------------------------ - 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 Nextion LCD Library by KD8CEC -//======================================================================== -#ifdef UBITX_DISPLAY_NEXTION -/************************************************************************* - Nextion Library for uBItX - KD8CEC -**************************************************************************/ -extern void SWSerial_Begin(long speedBaud); -extern void SWSerial_Write(uint8_t b); -extern int SWSerial_Available(void); -extern int SWSerial_Read(void); -extern void SWSerial_Print(uint8_t *b); - -#define TEXT_LINE_LENGTH 20 -char softBuffLines[2][TEXT_LINE_LENGTH + 1]; -char softBuffSended[2][TEXT_LINE_LENGTH + 1]; -char softBuffTemp[TEXT_LINE_LENGTH + 1]; //for STR Command - -char c[30], b[30]; -char softBuff[20]; -char softTemp[20]; - -void LCDNextion_Init() -{ - SWSerial_Begin(9600); - memset(softBuffLines[0], ' ', TEXT_LINE_LENGTH); - softBuffLines[0][TEXT_LINE_LENGTH + 1] = 0x00; - memset(softBuffLines[1], ' ', TEXT_LINE_LENGTH); - softBuffLines[1][TEXT_LINE_LENGTH + 1] = 0x00; -} - -void LCD_Init(void) -{ - LCDNextion_Init(); -} - -//=================================================================== -//Begin of Nextion LCD Protocol -// -// v0~v9, va~vz : Numeric (Transceiver -> Nextion LCD) -// s0~s9 : String (Text) (Transceiver -> Nextion LCD) -// vlSendxxx, vloxxx: Reserve for Nextion (Nextion LCD -> Transceiver) -// -//=================================================================== -#define CMD_NOW_DISP '0' //c0 -char L_nowdisp = -1; //Sended nowdisp - -#define CMD_VFO_TYPE 'v' //cv -char L_vfoActive; //vfoActive - -#define CMD_CURR_FREQ 'c' //vc -unsigned long L_vfoCurr; //vfoA -#define CMD_CURR_MODE 'c' //cc -byte L_vfoCurr_mode; //vfoA_mode - -#define CMD_VFOA_FREQ 'a' //va -unsigned long L_vfoA; //vfoA -#define CMD_VFOA_MODE 'a' //ca -byte L_vfoA_mode; //vfoA_mode - -#define CMD_VFOB_FREQ 'b' //vb -unsigned long L_vfoB; //vfoB -#define CMD_VFOB_MODE 'b' //cb -byte L_vfoB_mode; //vfoB_mode - -#define CMD_IS_RIT 'r' //cr -char L_ritOn; -#define CMD_RIT_FREQ 'r' //vr -unsigned long L_ritTxFrequency; //ritTxFrequency - -#define CMD_IS_TX 't' //ct -char L_inTx; - -#define CMD_IS_DIALLOCK 'l' //cl -byte L_isDialLock; //byte isDialLock - -#define CMD_IS_SPLIT 's' //cs -byte L_Split; //isTxType -#define CMD_IS_TXSTOP 'x' //cx -byte L_TXStop; //isTxType - -#define CMD_TUNEINDEX 'n' //cn -byte L_tuneStepIndex; //byte tuneStepIndex - -#define CMD_SMETER 'p' //cs -byte L_scaledSMeter; //scaledSMeter - -#define CMD_SIDE_TONE 't' //vt -unsigned long L_sideTone; //sideTone -#define CMD_KEY_TYPE 'k' //ck -byte L_cwKeyType = -1; //L_cwKeyType 0: straight, 1 : iambica, 2: iambicb - -#define CMD_CW_SPEED 's' //vs -unsigned int L_cwSpeed; //cwSpeed - -#define CMD_CW_DELAY 'y' //vy -byte L_cwDelayTime=-1; //cwDelayTime - -#define CMD_CW_STARTDELAY 'e' //ve -byte L_delayBeforeCWStartTime=-1; //byte delayBeforeCWStartTime - -#define CMD_ATT_LEVEL 'f' //vf -byte L_attLevel; - -byte L_isIFShift; //1 = ifShift, 2 extend -#define CMD_IS_IFSHIFT 'i' //ci - -int L_ifShiftValue; -#define CMD_IFSHIFT_VALUE 'i' //vi - -byte L_sdrModeOn; -#define CMD_SDR_MODE 'j' //cj - -#define CMD_UBITX_INFO 'm' //cm Complete Send uBITX Information - -//Once Send Data, When boot -//arTuneStep, When boot, once send -//long arTuneStep[5]; -#define CMD_AR_TUNE1 '1' //v1 -#define CMD_AR_TUNE2 '2' //v2 -#define CMD_AR_TUNE3 '3' //v3 -#define CMD_AR_TUNE4 '4' //v4 -#define CMD_AR_TUNE5 '5' //v5 - - -#define CMD_IS_CW_SHIFT_DISPLAY 'h' //ch -byte L_isShiftDisplayCWFreq; //byte isShiftDisplayCWFreq - -#define CMD_CW_SHIFT_ADJUST 'h' //vh -int L_shiftDisplayAdjustVal; //int shiftDisplayAdjustVal - -//0:CW Display Shift Confirm, 1 : IFshift save -#define CMD_COMM_OPTION 'o' //vo -byte L_commonOption0; //byte commonOption0 - -//0:Line Toggle, 1 : Always display Callsign, 2 : scroll display, 3 : s.meter -#define CMD_DISP_OPTION1 'p' //vp -byte L_displayOption1; //byte displayOption1 -#define CMD_DISP_OPTION2 'q' //vq -byte L_displayOption2; //byte displayOption2 (Reserve) - -#define CMD_TEXT_LINE0 '0' //s0 -#define CMD_TEXT_LINE1 '1' //s1 - -#define CMD_CW_TEXT 'a' //sa -#define CMD_CALLSIGN 'c' //sc -#define CMD_VERSION 'v' //sv - -#define TS_CMD_MODE 1 -#define TS_CMD_FREQ 2 -#define TS_CMD_BAND 3 -#define TS_CMD_VFO 4 -#define TS_CMD_SPLIT 5 -#define TS_CMD_RIT 6 -#define TS_CMD_TXSTOP 7 -#define TS_CMD_SDR 8 -#define TS_CMD_LOCK 9 //Dial Lock -#define TS_CMD_ATT 10 //ATT -#define TS_CMD_IFS 11 //IFS Enabled -#define TS_CMD_IFSVALUE 12 //IFS VALUE -#define TS_CMD_STARTADC 13 -#define TS_CMD_STOPADC 14 -#define TS_CMD_SPECTRUMOPT 15 //Option for Spectrum -#define TS_CMD_SPECTRUM 16 //Get Spectrum Value -#define TS_CMD_TUNESTEP 17 //Get Spectrum Value -#define TS_CMD_WPM 18 //Set WPM -#define TS_CMD_KEYTYPE 19 //Set KeyType - -#define TS_CMD_SWTRIG 21 //SW Action Trigger for WSPR and more -#define TS_CMD_READMEM 31 //Read EEProm -#define TS_CMD_WRITEMEM 32 //Write EEProm -#define TS_CMD_LOOPBACK0 74 //Loopback1 (Response to Loopback Channgel) -#define TS_CMD_LOOPBACK1 75 //Loopback2 (Response to Loopback Channgel) -#define TS_CMD_LOOPBACK2 76 //Loopback3 (Response to Loopback Channgel) -#define TS_CMD_LOOPBACK3 77 //Loopback4 (Response to Loopback Channgel) -#define TS_CMD_LOOPBACK4 78 //Loopback5 (Response to Loopback Channgel) -#define TS_CMD_LOOPBACK5 79 //Loopback6 (Response to Loopback Channgel) -#define TS_CMD_FACTORYRESET 85 //Factory Reset -#define TS_CMD_UBITX_REBOOT 95 //Reboot - -char nowdisp = 0; - -#define SWS_HEADER_CHAR_TYPE 'c' //1Byte Protocol Prefix -#define SWS_HEADER_INT_TYPE 'v' //Numeric Protocol Prefex -#define SWS_HEADER_STR_TYPE 's' //for TEXT Line compatiable Character LCD Control - -//Control must have prefix 'v' or 's' -char softSTRHeader[11] = {'p', 'm', '.', 's', '0', '.', 't', 'x', 't', '=', '\"'}; -char softINTHeader[10] = {'p', 'm', '.', 'v', '0', '.', 'v', 'a', 'l', '='}; -const byte ADCIndex[6] = {A0, A1, A2, A3, A6, A7}; - -//send data for Nextion LCD -void SendHeader(char varType, char varIndex) -{ - if (varType == SWS_HEADER_STR_TYPE) - { - softSTRHeader[4] = varIndex; - for (int i = 0; i < 11; i++) - SWSerial_Write(softSTRHeader[i]); - } - else - { - softINTHeader[4] = varIndex; - for (int i = 0; i < 10; i++) - SWSerial_Write(softINTHeader[i]); - } -} - -#define INT_ETX 0 -#define STR_ETX 1 -#define TMP_ETX 2 -//Send 0xFF, 0xFF, 0xFF -//etxType : INT_ETX = 0xFF, 0xFF, 0xFF -// STR_ETX = ", 0xFF, 0xFF, 0xFF -// TEMP_ETX = softTemp, 0xFF, 0xFF, 0xff - -void SendCommandETX(char etxType) -{ - if (etxType == 2) - { - SWSerial_Print(softTemp); - } - else if (etxType == 1) - { - SWSerial_Print("\""); - } - - SWSerial_Write(0xff); - SWSerial_Write(0xff); - SWSerial_Write(0xff); -} - -void SendCommandUL(char varIndex, unsigned long sendValue) -{ - SendHeader(SWS_HEADER_INT_TYPE, varIndex); - - memset(softTemp, 0, 20); - ultoa(sendValue, softTemp, DEC); - SendCommandETX(TMP_ETX); -} - -void SendCommandL(char varIndex, long sendValue) -{ - SendHeader(SWS_HEADER_INT_TYPE, varIndex); - - memset(softTemp, 0, 20); - ltoa(sendValue, softTemp, DEC); - SendCommandETX(TMP_ETX); -} - -void SendCommandStr(char varIndex, char* sendValue) -{ - SendHeader(SWS_HEADER_STR_TYPE, varIndex); - - SWSerial_Print(sendValue); - SendCommandETX(STR_ETX); -} - -//Send String data with duplicate check -void SendTextLineBuff(char lineNumber) -{ - //Check Duplicated data - if (strcmp(softBuffLines[lineNumber], softBuffSended[lineNumber])) - { - SendHeader(SWS_HEADER_STR_TYPE, lineNumber + 0x30); //s0.txt, s1.txt - - SWSerial_Print(softBuffLines[lineNumber]); - SendCommandETX(STR_ETX); - - strcpy(softBuffSended[lineNumber], softBuffLines[lineNumber]); - } -} - -void SendTextLineStr(char lineNumber, char* sendValue) -{ - int i = 0; - for (i = 0; i < 16; i++) - { - if (sendValue[i] == 0x00) - break; - else - softBuffLines[lineNumber][i] = sendValue[i]; - } - - for (;i < 20; i++) - { - softBuffLines[lineNumber][i] = ' '; - } - - softBuffLines[lineNumber][TEXT_LINE_LENGTH + 1] = 0x00; - SendTextLineBuff(lineNumber); -} - -void SendEEPromData(char varIndex, int eepromStartIndex, int eepromEndIndex, char offsetTtype) -{ - SendHeader(SWS_HEADER_STR_TYPE, varIndex); - - for (int i = eepromStartIndex; i <= eepromEndIndex; i++) - { - SWSerial_Write(EEPROM.read((offsetTtype == 0 ? USER_CALLSIGN_DAT : WSPR_MESSAGE1) + i)); - } - - SendCommandETX(STR_ETX); -} - -uint8_t softBuff1Num[14] = {'p', 'm', '.', 'c', '0', '.', 'v', 'a', 'l', '=', 0, 0xFF, 0xFF, 0xFF}; -void SendCommand1Num(char varType, char sendValue) //0~9 : Mode, nowDisp, ActiveVFO, IsDialLock, IsTxtType, IsSplitType -{ - softBuff1Num[4] = varType; - softBuff1Num[10] = sendValue + 0x30; - - for (int i = 0; i < 14; i++) - SWSerial_Write(softBuff1Num[i]); -} - -void SetSWActivePage(char newPageIndex) -{ - if (L_nowdisp != newPageIndex) - { - L_nowdisp = newPageIndex; - SendCommand1Num(CMD_NOW_DISP, L_nowdisp); - } -} -//=================================================================== -//End of Nextion LCD Protocol -//=================================================================== - -// The generic routine to display one line on the LCD -void printLine(unsigned char linenmbr, const char *c) { - SendTextLineStr(linenmbr, c); -} - -void printLineF(char linenmbr, const __FlashStringHelper *c) -{ - int i; - char tmpBuff[21]; - PGM_P p = reinterpret_cast(c); - - for (i = 0; i < 21; i++){ - unsigned char fChar = pgm_read_byte(p++); - tmpBuff[i] = fChar; - if (fChar == 0) - break; - } - - printLine(linenmbr, tmpBuff); -} - -#define LCD_MAX_COLUMN 20 -void printLineFromEEPRom(char linenmbr, char lcdColumn, byte eepromStartIndex, byte eepromEndIndex, char offsetTtype) -{ - int colIndex = lcdColumn; - for (byte i = eepromStartIndex; i <= eepromEndIndex; i++) - { - if (++lcdColumn <= LCD_MAX_COLUMN) - softBuffLines[linenmbr][colIndex++] = EEPROM.read((offsetTtype == 0 ? USER_CALLSIGN_DAT : WSPR_MESSAGE1) + i); - else - break; - } - - SendTextLineBuff(linenmbr); -} - -// 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 for Nextion LCD -//unsigned long -byte nowPageIndex = 0; - -//sendType == 1 not check different -void sendUIData(int sendType) -{ - char nowActiveVFO = vfoActive == VFO_A ? 0 : 1; - - //#define CMD_VFO_TYPE 'v' //cv - if (L_vfoActive != nowActiveVFO) - { - L_vfoActive = nowActiveVFO; - SendCommand1Num(CMD_VFO_TYPE, L_vfoActive); - } - - //#define CMD_CURR_FREQ 'c' //vc - if (L_vfoCurr != frequency) - { - L_vfoCurr = frequency; - SendCommandUL(CMD_CURR_FREQ, frequency); - } - - //#define CMD_CURR_MODE 'c' //cc - byte vfoCurr_mode = modeToByte(); - if (L_vfoCurr_mode != vfoCurr_mode) - { - L_vfoCurr_mode = vfoCurr_mode; - SendCommand1Num(CMD_CURR_MODE, L_vfoCurr_mode); - } - - //if auto cw key mode, exit - //if (isCWAutoMode != 0 || menuOn != 0) - if (isCWAutoMode != 0) - return; - - //nowPageIndex = 0; - if (menuOn==0) - { - if (sendType == 0) - { - SetSWActivePage(0); - } - else - { - SetSWActivePage(0); - } - } - else - { - //Text Line Mode - SetSWActivePage(1); - } - - //#define CMD_VFOA_FREQ 'a' //va - //VFOA - if (L_vfoA != vfoA) - { - L_vfoA = vfoA; - SendCommandUL(CMD_VFOA_FREQ, L_vfoA); - } - - //#define CMD_VFOA_MODE 'a' //ca - if (L_vfoA_mode != vfoA_mode) - { - L_vfoA_mode = vfoA_mode; - SendCommand1Num(CMD_VFOA_MODE, L_vfoA_mode); - } - - //#define CMD_VFOB_FREQ 'b' //vb - //VFOB - if (L_vfoB != vfoB) - { - L_vfoB = vfoB; - SendCommandUL(CMD_VFOB_FREQ, L_vfoB); - } - - //#define CMD_VFOB_MODE 'b' //cb - if (L_vfoB_mode != vfoB_mode) - { - L_vfoB_mode = vfoB_mode; - SendCommand1Num(CMD_VFOB_MODE, L_vfoB_mode); - } - - //byte isDialLock = ((isTxType & 0x01) == 0x01) ? 1 : 0; - if (L_isDialLock != isDialLock) - { - L_isDialLock = isDialLock; - SendCommand1Num(CMD_IS_DIALLOCK, L_isDialLock); - } - - //#define CMD_IS_RIT 'r' //cr - if (L_ritOn != ritOn) - { - L_ritOn = ritOn; - SendCommand1Num(CMD_IS_RIT, L_ritOn); - } - - //#define CMD_RIT_FREQ 'r' //vr - //unsigned long L_ritTxFrequency; //ritTxFrequency - if (L_ritTxFrequency != ritTxFrequency) - { - L_ritTxFrequency = ritTxFrequency; - SendCommandUL(CMD_RIT_FREQ, L_ritTxFrequency); - } - - //#define CMD_IS_TX 't' //ct - //char L_inTx; - if (L_inTx != inTx) - { - L_inTx = inTx; - SendCommand1Num(CMD_IS_TX, L_inTx); - } - - //#define CMD_IS_DIALLOCK 'l' //cl - //byte L_isDialLock; //byte isDialLock - if (L_isDialLock != isDialLock) - { - L_isDialLock = isDialLock; - SendCommand1Num(CMD_IS_DIALLOCK, L_isDialLock); - } - - //#define CMD_IS_SPLIT 's' //cs - //byte L_Split; //isTxType - if (L_Split != splitOn) - { - L_Split = splitOn; - SendCommand1Num(CMD_IS_SPLIT, L_Split); - } - - - //#define CMD_IS_TXSTOP 'x' //cx - byte isTXStop = ((isTxType & 0x01) == 0x01); - if (L_TXStop != isTXStop) - { - L_TXStop = isTXStop; - SendCommand1Num(CMD_IS_TXSTOP, L_TXStop); - } - - //#define CMD_TUNEINDEX 'n' //cn - if (L_tuneStepIndex != tuneStepIndex) - { - L_tuneStepIndex = tuneStepIndex; - SendCommand1Num(CMD_TUNEINDEX, L_tuneStepIndex); - } - - //#define CMD_SMETER 'p' //cp - if (L_scaledSMeter != scaledSMeter) - { - L_scaledSMeter = scaledSMeter; - SendCommand1Num(CMD_SMETER, L_scaledSMeter); - } - - //#define CMD_SIDE_TONE 't' //vt - if (L_sideTone != sideTone) - { - L_sideTone = sideTone; - SendCommandL(CMD_SIDE_TONE, L_sideTone); - } - - //#define CMD_KEY_TYPE 'k' //ck - if (L_cwKeyType != cwKeyType) - { - L_cwKeyType = cwKeyType; - SendCommand1Num(CMD_KEY_TYPE, L_cwKeyType); - } - - //#define CMD_CW_SPEED 's' //vs - if (L_cwSpeed != cwSpeed) - { - L_cwSpeed = cwSpeed; - SendCommandL(CMD_CW_SPEED, L_cwSpeed); - } - - //#define CMD_CW_DELAY 'y' //vy - if (L_cwDelayTime != cwDelayTime) - { - L_cwDelayTime = cwDelayTime; - SendCommandL(CMD_CW_DELAY, L_cwDelayTime); - } - - //#define CMD_CW_STARTDELAY 'e' //ve - if (L_delayBeforeCWStartTime != delayBeforeCWStartTime) - { - L_delayBeforeCWStartTime = delayBeforeCWStartTime; - SendCommandL(CMD_CW_STARTDELAY, L_delayBeforeCWStartTime); - } - - //#define CMD_ATT_LEVEL 'f' //vf - if (L_attLevel != attLevel) - { - L_attLevel = attLevel; - SendCommandL(CMD_ATT_LEVEL, L_attLevel); - } - - //#define CMD_IS_IFSHIFT 'i' - if (L_isIFShift != isIFShift) - { - L_isIFShift = isIFShift; - SendCommand1Num(CMD_IS_IFSHIFT, L_isIFShift); - } - - //#define CMD_IFSHIFT_VALUE 'i' - if (L_ifShiftValue != ifShiftValue) - { - L_ifShiftValue = ifShiftValue; - SendCommandL(CMD_IFSHIFT_VALUE, L_ifShiftValue); - } - - //#define CMD_SDR_MODE 'j' //cj - if (L_sdrModeOn != sdrModeOn) - { - L_sdrModeOn = sdrModeOn; - SendCommand1Num(CMD_SDR_MODE, L_sdrModeOn); - } -} - -void updateDisplay() { - sendUIData(0); //UI -} - -//**************************************************************** -// Spectrum for Range scan and Band Scan -//**************************************************************** -#define RESPONSE_SPECTRUM 0 -#define RESPONSE_EEPROM 1 -#define RESPONSE_EEPROM_HEX_F 89 //C Language order -#define RESPONSE_EEPROM_HEX_R 72 //Nextion order (Reverse) -#define RESPONSE_EEPROM_STR 87 //String - -const uint8_t ResponseHeader[11]={'p', 'm', '.', 's', 'h', '.', 't', 'x', 't', '=', '"'}; -const char HexCodes[16] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f', }; - -//void sendSpectrumData(unsigned long startFreq, unsigned long incStep, int scanCount, int delayTime, int sendCount) -//sendResponseData(RESPONSE_EEPROM, 0, eepromIndex, eepromReadLength, eepromDataType, 1); -//protocol Type : 0 - Spectrum, 1 : EEProm -//startFreq : Spectrum - Frequency, EEProm - 0 -//sendOption1 : Spectrum - 1 Step Frequency, EEProm - EEProm Start Address -//scanCount : Spectrum - 1 Set Length, EEProm - Read Length -//sendOption2 : Spectrum - Value offset (because support various S-Meter), EEProm - EEProm Response DataType (0:HEX, 1:String) -//sendCount : Spectrum - All scan set count, EEProm - always 1 -void sendResponseData(int protocolType, unsigned long startFreq, unsigned int sendOption1, int readCount, int sendOption2, int sendCount) //Spectrum and EEProm Data -{ - unsigned long beforFreq = frequency; - unsigned long k; - uint8_t adcBytes[200]; //Maximum 200 Step - - //Voltage drop - //scanResult[0] = analogRead(ANALOG_SMETER); - //adcBytes[0] = analogRead(ANALOG_SMETER); - //delay(10); - int readedValue = 0; - - for (int si = 0; si < sendCount; si++) - { - for (int i = 0; i < 11; i++) - SWSerial_Write(ResponseHeader[i]); - - for (k = 0; k < readCount; k ++) - { - if (protocolType == RESPONSE_SPECTRUM) - { - //Spectrum Data - //Sampling Range - setFrequency(startFreq + (k * sendOption1)); - //Wait time for charging - //delay(10); - -#ifdef USE_I2CSMETER - readedValue = GetI2CSmeterValue(I2CMETER_UNCALCS); -#else - - //ADC - readedValue = analogRead(ANALOG_SMETER); - readedValue -= (sendOption2 * 3); //0 ~ 765 - //Down Scale - readedValue /= 2; - if (readedValue < 0) - { - readedValue = 0; - } - else if (readedValue>255) - { - readedValue=255; - } -#endif - } - else - { - readedValue = EEPROM.read(((sendOption2 == RESPONSE_EEPROM_HEX_R) ? (readCount - k - 1) : k) + sendOption1); - } - - if (protocolType == RESPONSE_EEPROM && sendOption2 == RESPONSE_EEPROM_STR) //None HEX - { - SWSerial_Write(readedValue); - } - else - { - SWSerial_Write(HexCodes[readedValue >> 4]); - SWSerial_Write(HexCodes[readedValue & 0xf]); - } - } - - SendCommandETX(STR_ETX); - } //end of for -} - -//**************************************************************** -//Receive command and processing from External device (LCD or MCU) -//**************************************************************** -int spectrumSendCount = 10; //count of full scan and Send -int spectrumOffset = 0; //offset position -int spectrumScanCount = 100; //Maximum 200 -unsigned int spectrumIncStep = 1000; //Increaase Step -extern uint8_t receivedCommandLength; -extern void SWSerial_Read(uint8_t * receive_cmdBuffer); -uint8_t swr_buffer[20]; - -//SoftwareSerial_Process -void SWS_Process(void) -{ - //Received Command from touch screen - if (receivedCommandLength > 0) - { - SWSerial_Read(swr_buffer); - - int8_t comandLength = receivedCommandLength; - int8_t commandStartIndex = -1; - receivedCommandLength = 0; - - //Data Process - //comandLength //Find start Length - for (int i = 0; i < comandLength - 3; i++) - { - if (swr_buffer[i] == 0x59 && swr_buffer[i+ 1] == 0x58 && swr_buffer[i + 2] == 0x68) - { - commandStartIndex = i; - break; - } - } //end of for - - if (commandStartIndex != -1) - { - //Complete received command from touch screen - uint8_t commandType = swr_buffer[commandStartIndex + 3]; - - if (commandType == TS_CMD_MODE) - { - byteToMode(swr_buffer[commandStartIndex + 4], 1); - } - else if (commandType == TS_CMD_FREQ) - { - unsigned long *tempFreq; - tempFreq = (unsigned long *)(&swr_buffer[commandStartIndex + 4]); - //if (*tempFreq > 3000) //for loss protcol - //{ - frequency = *tempFreq; - //} - } - else if (commandType == TS_CMD_BAND) - { - char currentBandIndex = -1; - 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, swr_buffer[commandStartIndex + 4] == 1 ? -1 : 1); //Prior Band - } - else if (commandType == TS_CMD_VFO) - { - menuVfoToggle(1); //Vfo Toggle - } - else if (commandType == TS_CMD_SPLIT) - { - menuSplitOnOff(10); - } - else if (commandType == TS_CMD_RIT) - { - menuRitToggle(1); - } - else if (commandType == TS_CMD_TXSTOP) - { - menuTxOnOff(1, 0x01); - } - else if (commandType == TS_CMD_SDR) - { - menuSDROnOff(1); - } - else if (commandType == TS_CMD_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 - } - else if (commandType == TS_CMD_ATT) - { - attLevel = swr_buffer[commandStartIndex + 4]; - } - else if (commandType == TS_CMD_IFS) - { - isIFShift = isIFShift ? 0 : 1; //Toggle - } - else if (commandType == TS_CMD_IFSVALUE) - { - ifShiftValue = *(long *)(&swr_buffer[commandStartIndex + 4]); - } - else if (commandType == TS_CMD_STARTADC) - { - int startIndex = swr_buffer[commandStartIndex + 4]; - int endIndex = swr_buffer[commandStartIndex + 5]; - int adcCheckInterval = swr_buffer[commandStartIndex + 6] * 10; - int nowCheckIndex = startIndex; - - while(1 == 1) - { - if (receivedCommandLength > 0) - { - break; - } - - SendCommandL('n', nowCheckIndex); //Index Input - SendCommandL('x', analogRead(ADCIndex[nowCheckIndex++])); - - if (nowCheckIndex > endIndex) - nowCheckIndex = startIndex; - - delay(adcCheckInterval); - } //end of while - } - else if (commandType == TS_CMD_STOPADC) - { - //None Action - return; - } - else if (commandType == TS_CMD_SPECTRUM) - { - //sendSpectrumData(unsigned long startFreq, unsigned int incStep, int scanCount, int delayTime, int sendCount) - //sendSpectrumData(frequency - (1000L * 50), 1000, 100, 0, 10); - //sendSpectrumData(*(long *)(&swr_buffer[commandStartIndex + 4]), spectrumIncStep, spectrumScanCount, spectrumDelayTime, spectrumSendCount); - unsigned long beforeFreq = frequency; - sendResponseData(RESPONSE_SPECTRUM, *(long *)(&swr_buffer[commandStartIndex + 4]), spectrumIncStep, spectrumScanCount, spectrumOffset, spectrumSendCount); - frequency = beforeFreq; - } - else if (commandType == TS_CMD_SPECTRUMOPT) - { - //sendSpectrumData(unsigned long startFreq, unsigned int incStep, int scanCount, int delayTime, int sendCount) - //sendSpectrumData(frequency - (1000L * 50), 1000, 100, 0, 10); - spectrumSendCount = swr_buffer[commandStartIndex + 4]; //count of full scan and Send - spectrumOffset = swr_buffer[commandStartIndex + 5]; //Scan interval time - spectrumScanCount = swr_buffer[commandStartIndex + 6]; //Maximum 120 - spectrumIncStep = swr_buffer[commandStartIndex + 7] * 20; //Increaase Step - } - else if (commandType == TS_CMD_TUNESTEP) //Set Tune Step - { - tuneStepIndex = swr_buffer[commandStartIndex + 4]; //Tune Step Index - } - else if (commandType == TS_CMD_WPM) //Set WPM - { - cwSpeed = swr_buffer[commandStartIndex + 4]; // - } - else if (commandType == TS_CMD_KEYTYPE) //Set Key Type - { - cwKeyType = swr_buffer[commandStartIndex + 4]; - - //for reduce program memory - Iambic_Key = cwKeyType != 0; - //if (cwKeyType == 0) - // Iambic_Key = false; - //else - //Iambic_Key = true; - if (cwKeyType == 1) - keyerControl &= ~IAMBICB; - else - keyerControl |= IAMBICB; - //} - } - else if (commandType == TS_CMD_SWTRIG) - { - TriggerBySW = 1; //Action Trigger by Software - } - else if (commandType == TS_CMD_READMEM ) //Read Mem - { - uint16_t eepromIndex = *(uint16_t *)(&swr_buffer[commandStartIndex + 4]); - byte eepromReadLength = swr_buffer[commandStartIndex + 6]; - byte eepromDataType = swr_buffer[commandStartIndex + 7]; //0 : Hex, 1 : String - - sendResponseData(RESPONSE_EEPROM, 0, eepromIndex, eepromReadLength, eepromDataType, 1); - } - else if (commandType == TS_CMD_WRITEMEM) //Write Mem - { - /* - Address : 2 byte int - Length : Data Length - Checksum : (Addr0+Addr1+Len) %256 - Data : Variable (Max 23) - */ - uint16_t eepromIndex = *(uint16_t *)(&swr_buffer[commandStartIndex + 4]); - byte writeLength = swr_buffer[commandStartIndex + 6]; - byte writeCheckSum = swr_buffer[commandStartIndex + 7]; - - //Check Checksum - if (writeCheckSum == (swr_buffer[commandStartIndex + 4] + swr_buffer[commandStartIndex + 5] + swr_buffer[commandStartIndex + 6])) - //if (writeCheckSum == (swr_buffer[commandStartIndex + 4] + swr_buffer[commandStartIndex + 5] + writeLength)) - { - //if (eepromIndex > 64) //Safe #1 -#ifdef UBITX_DISPLAY_NEXTION_SAFE - //Safe #2 - if (eepromIndex < 770 || eepromIndex > 775 ) - { - eepromIndex = -2; - } - else -#else - if (1 == 1) -#endif - { - for (int i = 0; i < writeLength; i++) - EEPROM.write(eepromIndex + i , swr_buffer[commandStartIndex + 8 + i]); - } - } - else - { - eepromIndex = -2; - } - SendCommandL('n', eepromIndex); //Index Input - } - //else if (TS_CMD_LOOPBACK0 <= commandType && commandType <= TS_CMD_LOOPBACK5) //Loop back Channel 0 ~ 5 Loop back Channel 1~5 : Reserve - else if (TS_CMD_LOOPBACK0 == commandType) //Loop back Channel 0 ~ 5 - { - SendCommandUL('v', *(unsigned long *)&swr_buffer[commandStartIndex + 4]); //Return data - SendCommandUL('g', commandType); //Index Input - //return; - } - else if (commandType == TS_CMD_FACTORYRESET || commandType == TS_CMD_UBITX_REBOOT) - { - if (*(unsigned long *)&swr_buffer[commandStartIndex + 4] == 1497712748) - { - if (commandType == TS_CMD_UBITX_REBOOT) - { - FrequencyToVFO(1); //Save current Frequency and Mode to eeprom - asm volatile (" jmp 0"); - } - else - { - for (unsigned int i = 0; i < 32; i++) //factory setting range - EEPROM.write(i, EEPROM.read(FACTORY_VALUES + i)); //65~96 => 0~31 - } - } - } - - setFrequency(frequency); - SetCarrierFreq(); - updateDisplay(); - } - } -} - -char checkCount = 0; -char checkCountSMeter = 0; - -//execute interval : 0.25sec -void idle_process() -{ - //S-Meter Display - if (((displayOption1 & 0x08) == 0x08 && (sdrModeOn == 0)) && (++checkCountSMeter > SMeterLatency)) - { -#ifdef USE_I2CSMETER - scaledSMeter = GetI2CSmeterValue(I2CMETER_CALCS); -#else - int newSMeter; - - newSMeter = analogRead(ANALOG_SMETER) / 4; - currentSMeter = newSMeter; - - scaledSMeter = 0; - for (byte s = 8; s >= 1; s--) { - if (currentSMeter > sMeterLevels[s]) { - scaledSMeter = s; - break; - } - } - -#endif - checkCountSMeter = 0; //Reset Latency time - } //end of S-Meter - - sendUIData(1); -} - -//When boot time, send data -void SendUbitxData(void) -{ - //Wait for ready other device (LCD, DSP and more) - //delay(500); - delay_background(500, 2); - - SendCommandL(CMD_AR_TUNE1, arTuneStep[0]); - SendCommandL(CMD_AR_TUNE2, arTuneStep[1]); - SendCommandL(CMD_AR_TUNE3, arTuneStep[2]); - SendCommandL(CMD_AR_TUNE4, arTuneStep[3]); - SendCommandL(CMD_AR_TUNE5, arTuneStep[4]); - - SendCommand1Num(CMD_IS_CW_SHIFT_DISPLAY, isShiftDisplayCWFreq); - SendCommandL(CMD_CW_SHIFT_ADJUST, shiftDisplayAdjustVal); - SendCommandL(CMD_COMM_OPTION, commonOption0); - SendCommandL(CMD_DISP_OPTION1, displayOption1); - - unsigned long nextionDisplayOption; - EEPROM.get(EXTERNAL_DEVICE_OPT1, nextionDisplayOption); - SendCommandUL(CMD_DISP_OPTION2, nextionDisplayOption); - - SendCommandStr(CMD_VERSION, (char *)("+v1.200")); //Version - SendEEPromData(CMD_CALLSIGN, 0, userCallsignLength -1, 0); - - /* - //Frequency of Bands - for (int i = 0; i < 11; i++) - SWSerial_Write(SpectrumHeader[i]); - - byte *tmpByte; - tmpByte = (byte *)hamBandRange; - for (byte i = 0; i < (useHamBandCount -1) * 4; i++) - { - SWSerial_Write(HexCodes[*tmpByte >> 4]); - SWSerial_Write(HexCodes[*tmpByte & 0xf]); - tmpByte++; - } - - for (int i = 0; i < 4; i++) - SWSerial_Write(SpectrumFooter[i]); - */ - - //Complte Send Info - SendCommand1Num(CMD_UBITX_INFO, 1); - - //Page Init - L_nowdisp = 0; - SendCommand1Num(CMD_NOW_DISP, L_nowdisp); -} - - -//AutoKey LCD Display Routine -void Display_AutoKeyTextIndex(byte textIndex) -{ - byte diplayAutoCWLine = 0; - - if ((displayOption1 & 0x01) == 0x01) - diplayAutoCWLine = 1; - //LCD_SetCursor(0, diplayAutoCWLine); - - softBuffLines[diplayAutoCWLine][0] = byteToChar(textIndex); - softBuffLines[diplayAutoCWLine][1] = ':'; - - SendTextLineBuff(diplayAutoCWLine); -} - -void LCD_CreateChar(uint8_t location, uint8_t charmap[]) -{ -} - -void updateLine2Buffer(char displayType) -{ -} - -//not use with Nextion LCD -void DisplayCallsign(byte callSignLength) -{ -} - -//Not use with Nextion LCD -void DisplayVersionInfo(const __FlashStringHelper * fwVersionInfo) -{ -} - -#endif diff --git a/ubitx_20/ubitx_menu.ino b/ubitx_20/ubitx_menu.ino index 8dda453..4e4020d 100644 --- a/ubitx_20/ubitx_menu.ino +++ b/ubitx_20/ubitx_menu.ino @@ -548,6 +548,7 @@ void displayEmptyData(void){ delay_background(2000, 0); } +/* //Builtin CW Keyer Logic by KD8CEC void menuCWAutoKey(int btn){ if (!btn){ @@ -570,6 +571,7 @@ void menuCWAutoKey(int btn){ updateDisplay(); menuOn = 0; } +*/ //Standalone WSPR Beacone void menuWSPRSend(int btn){ From f588a89ee78137bc4633bb48031f03fd9909c739 Mon Sep 17 00:00:00 2001 From: Rob French Date: Sun, 7 Jun 2020 13:27:24 -0500 Subject: [PATCH 170/173] Working on the buffer issues. It looks like the issue is a failure (on the Raduino side) to check for availability of character in the Serial port, before reading the serial port. Note, need to see if this is a weakness on the Teensy (IOP) side as well. --- ubitx_20/cat_libs.ino | 29 ++++++++++++++++------------- ubitx_20/ubitx_20.ino | 2 ++ ubitx_20/ubitx_lcd_1602.ino | 18 ++++-------------- 3 files changed, 22 insertions(+), 27 deletions(-) diff --git a/ubitx_20/cat_libs.ino b/ubitx_20/cat_libs.ino index 0be4e4b..771bbe7 100644 --- a/ubitx_20/cat_libs.ino +++ b/ubitx_20/cat_libs.ino @@ -76,6 +76,8 @@ 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 @@ -811,7 +813,7 @@ 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 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 @@ -820,7 +822,6 @@ char iopMenuDisplay[17] = " "; //#define SMETER_DELAY_TIME 5000 //bool displaySmeter = true; //int delaySmeter; -int delayTopLine = 0; int stateTopLine = 0; //fromType normal : 0, TX : 1, CW_STRAIGHT : 2, CW_PADDLE : 3, CW_AUTOMODE : 4 @@ -871,7 +872,7 @@ void Check_Cat(byte fromType) // Will adjust based on readlength byte first = Serial.read(); readPrefix = byteToPrefix(first); - readLength = byteToLength(first); + readLength = byteToLength(first); for (int i = 0; i < readLength; i++) { CAT_BUFF[i] = Serial.read(); } @@ -904,17 +905,18 @@ void Check_Cat(byte fromType) case IOP_MENU_DISPLAY_MSG: for (int i = 0; i < 16; i++) { - iopMenuDisplay[i] = msg.data[i+1]; - } - if (int8_t(msg.data[0]) == 0) { - stateTopLine = 4; - } else if (int8_t(msg.data[0]) < 0) { - stateTopLine = 0; - } else { // > 0 - delayTopLine = millis() + (int8_t(msg.data[0]) * 1000); - stateTopLine = 2; + 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) { @@ -1020,5 +1022,6 @@ void Init_Cat(long baud, int portConfig) // At start, immediately send mode to IOP. Currently, IOP has no way to // request the mode. - iopSendMode(cwMode, isUSB, digiMode, isTest); + // 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_20.ino b/ubitx_20/ubitx_20.ino index deb97b8..f7ef20d 100644 --- a/ubitx_20/ubitx_20.ino +++ b/ubitx_20/ubitx_20.ino @@ -1453,6 +1453,8 @@ void setup() factory_alignment(); #endif + iopSendMode(cwMode, isUSB, digiMode, isTest); + } //Auto save Frequency and Mode with Protected eeprom life by KD8CEC diff --git a/ubitx_20/ubitx_lcd_1602.ino b/ubitx_20/ubitx_lcd_1602.ino index dbe1e15..2676000 100644 --- a/ubitx_20/ubitx_lcd_1602.ino +++ b/ubitx_20/ubitx_lcd_1602.ino @@ -531,22 +531,12 @@ void updateLine2Buffer(char displayType) { unsigned long tmpFreq = 0; - if ((stateTopLine == 2) || (stateTopLine == 4)) { - strcpy(line2Buffer, iopMenuDisplay); - if (stateTopLine == 4) { - stateTopLine = 3; - } else { - stateTopLine = 1; - } + if (stateTopLine == 2) { + strncpy(line2Buffer, iopMenuDisplay, 16); + stateTopLine = 1; } - if (stateTopLine == 3) { + if (stateTopLine == 1) { return; - } else if (stateTopLine == 1) { - if (delayTopLine < millis()) { - stateTopLine = 0; - } else { - return; - } } if (ritOn) From 1a1e92a709bf5d2129dc77e31a9faccd86083b9a Mon Sep 17 00:00:00 2001 From: Rob French Date: Sun, 7 Jun 2020 15:26:37 -0500 Subject: [PATCH 171/173] Working! Implemented a more reasonable serial reader. --- ubitx_20/cat_libs.ino | 65 ++++++++++++++++++++++--------------------- 1 file changed, 34 insertions(+), 31 deletions(-) diff --git a/ubitx_20/cat_libs.ino b/ubitx_20/cat_libs.ino index 771bbe7..0afb739 100644 --- a/ubitx_20/cat_libs.ino +++ b/ubitx_20/cat_libs.ino @@ -832,51 +832,53 @@ void Check_Cat(byte fromType) static PrefixID readPrefix; static uint8_t readLength; - static IOPMessage msg; + static IOPMessage msg; + static bool read_in_progress = false; //Check Serial Port Buffer - if (Serial.available() == 0) + if (Serial.available() == 0 && !read_in_progress) { //Set Buffer Clear status - rxBufferCheckCount = 0; + //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) + else // if (Serial.available() < 6) //5) { - //First Arrived - if (rxBufferCheckCount == 0) - { + if (!read_in_progress) { + byte first = Serial.read(); + readPrefix = byteToPrefix(first); + readLength = byteToLength(first); + rxBufferCheckCount = Serial.available(); - rxBufferArriveTime = millis() + CAT_RECEIVE_TIMEOUT; //Set time for timeout - } - else if (rxBufferArriveTime < millis()) //timeout - { - //Clear Buffer - for (i = 0; i < Serial.available(); i++) - rxBufferCheckCount = Serial.read(); + rxBufferArriveTime = millis() + CAT_RECEIVE_TIMEOUT; - rxBufferCheckCount = 0; + read_in_progress = true; } - else if (rxBufferCheckCount < Serial.available()) //increase buffer count, slow arrived - { - rxBufferCheckCount = Serial.available(); - rxBufferArriveTime = millis() + CAT_RECEIVE_TIMEOUT; //Set time for timeout + + 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(); } - return; } - - //Arived CAT DATA - // KC4UPR - IOP update - 6 characters; first character determines mode (CAT or IOP) - // Will adjust based on readlength - byte first = Serial.read(); - readPrefix = byteToPrefix(first); - readLength = byteToLength(first); - 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) @@ -1013,6 +1015,7 @@ void Check_Cat(byte fromType) } isProcessCheck_Cat = 0; + read_in_progress = false; } void Init_Cat(long baud, int portConfig) From 5e675107c1f0ea5617d5c043b42b1debd2154de0 Mon Sep 17 00:00:00 2001 From: Rob French Date: Sun, 14 Jun 2020 00:12:29 -0500 Subject: [PATCH 172/173] Changes to work with updated modes and such from iopcomm.h --- ubitx_20/cat_libs.ino | 17 +++++----- ubitx_20/ubitx_20.ino | 4 +-- ubitx_20/ubitx_lcd_1602.ino | 22 ++++++------- ubitx_20/ubitx_menu.ino | 66 +++++++++++++++---------------------- 4 files changed, 49 insertions(+), 60 deletions(-) diff --git a/ubitx_20/cat_libs.ino b/ubitx_20/cat_libs.ino index 0afb739..c5c1183 100644 --- a/ubitx_20/cat_libs.ino +++ b/ubitx_20/cat_libs.ino @@ -56,18 +56,19 @@ * * Send the current mode to the I/O Processor. */ -void iopSendMode(char cw_mode, char is_usb, char digi_mode, char is_test) +void iopSendMode(char cw_mode, char is_usb, char digi_mode) { byte mode; if (cw_mode > 0) { - mode = (cw_mode == 1 ? RIG_MODE_CWL : RIG_MODE_CWU); + mode = (cw_mode == 1 ? cwr : cw); } else if (digi_mode > 0) { - mode = (is_usb ? RIG_MODE_DGU : RIG_MODE_DGL); - } else if (is_test) { - mode = (is_usb ? RIG_MODE_TTU : RIG_MODE_TTL); + //mode = (is_usb ? RIG_MODE_DGU : RIG_MODE_DGL); + mode = dig; +// } else if (is_test) { +// mode = (is_usb ? RIG_MODE_TTU : RIG_MODE_TTL); } else { - mode = (is_usb ? RIG_MODE_USB : RIG_MODE_LSB); + mode = (is_usb ? usb : lsb); } sendIOPModeCommand(mode); } @@ -282,7 +283,7 @@ void CatSetMode(byte tmpMode, byte fromType) digiMode = 1; isUSB = true; // DGU - but need to eventually use the FT-817 customization } - iopSendMode(cwMode, isUSB, digiMode, isTest); + iopSendMode(cwMode, isUSB, digiMode); setFrequency(frequency); updateDisplay(); } @@ -902,7 +903,7 @@ void Check_Cat(byte fromType) break;*/ case IOP_MODE_REQUEST: - iopSendMode(cwMode, isUSB, digiMode, isTest); + iopSendMode(cwMode, isUSB, digiMode); break; case IOP_MENU_DISPLAY_MSG: diff --git a/ubitx_20/ubitx_20.ino b/ubitx_20/ubitx_20.ino index f7ef20d..dc39af8 100644 --- a/ubitx_20/ubitx_20.ino +++ b/ubitx_20/ubitx_20.ino @@ -193,7 +193,7 @@ char splitOn = 0; //working split, uses VFO B as the transmit freque 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 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) @@ -1453,7 +1453,7 @@ void setup() factory_alignment(); #endif - iopSendMode(cwMode, isUSB, digiMode, isTest); + iopSendMode(cwMode, isUSB, digiMode); } diff --git a/ubitx_20/ubitx_lcd_1602.ino b/ubitx_20/ubitx_lcd_1602.ino index 2676000..41c169b 100644 --- a/ubitx_20/ubitx_lcd_1602.ino +++ b/ubitx_20/ubitx_lcd_1602.ino @@ -439,15 +439,15 @@ void updateDisplay() { if (cwMode == 0) { if (digiMode == 1) { - if (isUSB) - strcpy(c, "DGU "); - else - strcpy(c, "DGL "); - } else if (isTest == 1) { - if (isUSB) - strcpy(c, "TTU "); - else - strcpy(c, "TTL "); +// 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 "); @@ -457,11 +457,11 @@ void updateDisplay() { } else if (cwMode == 1) { - strcpy(c, "CWL "); + strcpy(c, "CWR "); } else { - strcpy(c, "CWU "); + strcpy(c, "CW "); } } if (vfoActive == VFO_A) // VFO A is active diff --git a/ubitx_20/ubitx_menu.ino b/ubitx_20/ubitx_menu.ino index 4e4020d..53b0f98 100644 --- a/ubitx_20/ubitx_menu.ino +++ b/ubitx_20/ubitx_menu.ino @@ -127,13 +127,17 @@ void menuBand(int btn){ //Convert Mode, Number by KD8CEC //0: default, 1:not use, 2:LSB, 3:USB, 4:CWL, 5:CWU, 6:FM -byte modeToByte(){ +// Updated: KC4UPR - 6: DIG +byte modeToByte() { if (cwMode == 0) { + if (digiMode > 0) { + return 6; + } if (isUSB) - return 3 + (digiMode > 0 ? 3 + digiMode : 0); + return 3; else - return 2 + (digiMode > 0 ? 3 + digiMode : 0); + return 2; } else if (cwMode == 1) { @@ -149,7 +153,7 @@ byte modeToByte(){ //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 + //isTest = false; // test never settable from EEPROM isUSB = false; cwMode = 0; digiMode = 0; @@ -174,23 +178,10 @@ void byteToMode(byte modeValue, byte autoSetModebyFreq){ cwMode = 2; break; - case 6: // DGL + case 6: // DIG digiMode = 1; - break; - - case 7: // DGU - isUSB = true; - digiMode = 1; - break; -/* - case 8: // TTL - isUSB = false; - break; - - case 9: // TTU isUSB = true; break; -*/ } } /* if (modeValue == 4) { @@ -994,16 +985,13 @@ void menuSelectMode(int btn){ } // modify if digital mode is set if (digiMode > 0) { - selectModeType += (3 + digiMode); - - // modify if two-tone test mode is set - } else if (isTest > 0) { - selectModeType += 6; + + selectModeType = 4; } - } else if (cwMode == 1) { - selectModeType = 2; // CWL + } else if (cwMode == 2) { + selectModeType = 2; // CW } else { - selectModeType = 3; // CWU + selectModeType = 3; // CWR } /*if (cwMode == 0 && isUSB == 0) selectModeType = 0; @@ -1015,30 +1003,30 @@ void menuSelectMode(int btn){ selectModeType = 3;*/ beforeMode = selectModeType; - selectModeType = getValueByKnob(11, selectModeType, 0, 7, 1, " LSB USB CWL CWU DGL DGU TTL TTU", 4); //3 : Select Mode, targetValue, minKnobValue, maxKnobValue, stepSize + 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; + cwMode = 0; isUSB = 0; digiMode = 0; //isTest = 0; } else if (selectModeType == 1) { - cwMode = 0; isUSB = 1; digiMode = 0; isTest = 0; + cwMode = 0; isUSB = 1; digiMode = 0; //isTest = 0; } else if (selectModeType == 2) { - cwMode = 1; digiMode = 0; isTest = 0; + cwMode = 2; digiMode = 0; //isTest = 0; } else if (selectModeType == 3) { - cwMode = 2; digiMode = 0; isTest = 0; + cwMode = 1; digiMode = 0; //isTest = 0; } else if (selectModeType == 4) { - cwMode = 0; isUSB = 0; 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; + 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, isTest); + iopSendMode(cwMode, isUSB, digiMode); FrequencyToVFO(1); } From cdf3871385429193c04f7504ae3c65181d029637 Mon Sep 17 00:00:00 2001 From: Rob French Date: Sun, 14 Jun 2020 22:54:40 -0500 Subject: [PATCH 173/173] Updated to be compatible with some iopcomm changes. --- ubitx_20/cat_libs.ino | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/ubitx_20/cat_libs.ino b/ubitx_20/cat_libs.ino index c5c1183..70e2422 100644 --- a/ubitx_20/cat_libs.ino +++ b/ubitx_20/cat_libs.ino @@ -58,17 +58,18 @@ */ void iopSendMode(char cw_mode, char is_usb, char digi_mode) { - byte mode; - + rig_mode mode; + + // NOTE: eventually, add sideband (is_usb) to all of these. if (cw_mode > 0) { - mode = (cw_mode == 1 ? cwr : cw); + mode = rig_mode::cw; } else if (digi_mode > 0) { //mode = (is_usb ? RIG_MODE_DGU : RIG_MODE_DGL); - mode = dig; + mode = rig_mode::digi; // } else if (is_test) { // mode = (is_usb ? RIG_MODE_TTU : RIG_MODE_TTL); } else { - mode = (is_usb ? usb : lsb); + mode = rig_mode::ssb; //(is_usb ? usb : lsb); } sendIOPModeCommand(mode); }