├── LICENSE ├── README.md ├── ubitx_cat.ino ├── ubitx_factory_alignment.ino ├── ubitx_keyer.ino ├── ubitx_menu.ino ├── ubitx_si5351.ino ├── ubitx_ui.ino ├── ubitx_v5.1_code.ino └── ubitx_v5.pdf /LICENSE: -------------------------------------------------------------------------------- 1 | GNU GENERAL PUBLIC LICENSE 2 | Version 3, 29 June 2007 3 | 4 | Copyright (C) 2007 Free Software Foundation, Inc. 5 | Everyone is permitted to copy and distribute verbatim copies 6 | of this license document, but changing it is not allowed. 7 | 8 | Preamble 9 | 10 | The GNU General Public License is a free, copyleft license for 11 | software and other kinds of works. 12 | 13 | The licenses for most software and other practical works are designed 14 | to take away your freedom to share and change the works. By contrast, 15 | the GNU General Public License is intended to guarantee your freedom to 16 | share and change all versions of a program--to make sure it remains free 17 | software for all its users. We, the Free Software Foundation, use the 18 | GNU General Public License for most of our software; it applies also to 19 | any other work released this way by its authors. You can apply it to 20 | your programs, too. 21 | 22 | When we speak of free software, we are referring to freedom, not 23 | price. Our General Public Licenses are designed to make sure that you 24 | have the freedom to distribute copies of free software (and charge for 25 | them if you wish), that you receive source code or can get it if you 26 | want it, that you can change the software or use pieces of it in new 27 | free programs, and that you know you can do these things. 28 | 29 | To protect your rights, we need to prevent others from denying you 30 | these rights or asking you to surrender the rights. Therefore, you have 31 | certain responsibilities if you distribute copies of the software, or if 32 | you modify it: responsibilities to respect the freedom of others. 33 | 34 | For example, if you distribute copies of such a program, whether 35 | gratis or for a fee, you must pass on to the recipients the same 36 | freedoms that you received. You must make sure that they, too, receive 37 | or can get the source code. And you must show them these terms so they 38 | know their rights. 39 | 40 | Developers that use the GNU GPL protect your rights with two steps: 41 | (1) assert copyright on the software, and (2) offer you this License 42 | giving you legal permission to copy, distribute and/or modify it. 43 | 44 | For the developers' and authors' protection, the GPL clearly explains 45 | that there is no warranty for this free software. For both users' and 46 | authors' sake, the GPL requires that modified versions be marked as 47 | changed, so that their problems will not be attributed erroneously to 48 | authors of previous versions. 49 | 50 | Some devices are designed to deny users access to install or run 51 | modified versions of the software inside them, although the manufacturer 52 | can do so. This is fundamentally incompatible with the aim of 53 | protecting users' freedom to change the software. The systematic 54 | pattern of such abuse occurs in the area of products for individuals to 55 | use, which is precisely where it is most unacceptable. Therefore, we 56 | have designed this version of the GPL to prohibit the practice for those 57 | products. If such problems arise substantially in other domains, we 58 | stand ready to extend this provision to those domains in future versions 59 | of the GPL, as needed to protect the freedom of users. 60 | 61 | Finally, every program is threatened constantly by software patents. 62 | States should not allow patents to restrict development and use of 63 | software on general-purpose computers, but in those that do, we wish to 64 | avoid the special danger that patents applied to a free program could 65 | make it effectively proprietary. To prevent this, the GPL assures that 66 | patents cannot be used to render the program non-free. 67 | 68 | The precise terms and conditions for copying, distribution and 69 | modification follow. 70 | 71 | TERMS AND CONDITIONS 72 | 73 | 0. Definitions. 74 | 75 | "This License" refers to version 3 of the GNU General Public License. 76 | 77 | "Copyright" also means copyright-like laws that apply to other kinds of 78 | works, such as semiconductor masks. 79 | 80 | "The Program" refers to any copyrightable work licensed under this 81 | License. Each licensee is addressed as "you". "Licensees" and 82 | "recipients" may be individuals or organizations. 83 | 84 | To "modify" a work means to copy from or adapt all or part of the work 85 | in a fashion requiring copyright permission, other than the making of an 86 | exact copy. The resulting work is called a "modified version" of the 87 | earlier work or a work "based on" the earlier work. 88 | 89 | A "covered work" means either the unmodified Program or a work based 90 | on the Program. 91 | 92 | To "propagate" a work means to do anything with it that, without 93 | permission, would make you directly or secondarily liable for 94 | infringement under applicable copyright law, except executing it on a 95 | computer or modifying a private copy. Propagation includes copying, 96 | distribution (with or without modification), making available to the 97 | public, and in some countries other activities as well. 98 | 99 | To "convey" a work means any kind of propagation that enables other 100 | parties to make or receive copies. Mere interaction with a user through 101 | a computer network, with no transfer of a copy, is not conveying. 102 | 103 | An interactive user interface displays "Appropriate Legal Notices" 104 | to the extent that it includes a convenient and prominently visible 105 | feature that (1) displays an appropriate copyright notice, and (2) 106 | tells the user that there is no warranty for the work (except to the 107 | extent that warranties are provided), that licensees may convey the 108 | work under this License, and how to view a copy of this License. If 109 | the interface presents a list of user commands or options, such as a 110 | menu, a prominent item in the list meets this criterion. 111 | 112 | 1. Source Code. 113 | 114 | The "source code" for a work means the preferred form of the work 115 | for making modifications to it. "Object code" means any non-source 116 | form of a work. 117 | 118 | A "Standard Interface" means an interface that either is an official 119 | standard defined by a recognized standards body, or, in the case of 120 | interfaces specified for a particular programming language, one that 121 | is widely used among developers working in that language. 122 | 123 | The "System Libraries" of an executable work include anything, other 124 | than the work as a whole, that (a) is included in the normal form of 125 | packaging a Major Component, but which is not part of that Major 126 | Component, and (b) serves only to enable use of the work with that 127 | Major Component, or to implement a Standard Interface for which an 128 | implementation is available to the public in source code form. A 129 | "Major Component", in this context, means a major essential component 130 | (kernel, window system, and so on) of the specific operating system 131 | (if any) on which the executable work runs, or a compiler used to 132 | produce the work, or an object code interpreter used to run it. 133 | 134 | The "Corresponding Source" for a work in object code form means all 135 | the source code needed to generate, install, and (for an executable 136 | work) run the object code and to modify the work, including scripts to 137 | control those activities. However, it does not include the work's 138 | System Libraries, or general-purpose tools or generally available free 139 | programs which are used unmodified in performing those activities but 140 | which are not part of the work. For example, Corresponding Source 141 | includes interface definition files associated with source files for 142 | the work, and the source code for shared libraries and dynamically 143 | linked subprograms that the work is specifically designed to require, 144 | such as by intimate data communication or control flow between those 145 | subprograms and other parts of the work. 146 | 147 | The Corresponding Source need not include anything that users 148 | can regenerate automatically from other parts of the Corresponding 149 | Source. 150 | 151 | The Corresponding Source for a work in source code form is that 152 | same work. 153 | 154 | 2. Basic Permissions. 155 | 156 | All rights granted under this License are granted for the term of 157 | copyright on the Program, and are irrevocable provided the stated 158 | conditions are met. This License explicitly affirms your unlimited 159 | permission to run the unmodified Program. The output from running a 160 | covered work is covered by this License only if the output, given its 161 | content, constitutes a covered work. This License acknowledges your 162 | rights of fair use or other equivalent, as provided by copyright law. 163 | 164 | You may make, run and propagate covered works that you do not 165 | convey, without conditions so long as your license otherwise remains 166 | in force. You may convey covered works to others for the sole purpose 167 | of having them make modifications exclusively for you, or provide you 168 | with facilities for running those works, provided that you comply with 169 | the terms of this License in conveying all material for which you do 170 | not control copyright. Those thus making or running the covered works 171 | for you must do so exclusively on your behalf, under your direction 172 | and control, on terms that prohibit them from making any copies of 173 | your copyrighted material outside their relationship with you. 174 | 175 | Conveying under any other circumstances is permitted solely under 176 | the conditions stated below. Sublicensing is not allowed; section 10 177 | makes it unnecessary. 178 | 179 | 3. Protecting Users' Legal Rights From Anti-Circumvention Law. 180 | 181 | No covered work shall be deemed part of an effective technological 182 | measure under any applicable law fulfilling obligations under article 183 | 11 of the WIPO copyright treaty adopted on 20 December 1996, or 184 | similar laws prohibiting or restricting circumvention of such 185 | measures. 186 | 187 | When you convey a covered work, you waive any legal power to forbid 188 | circumvention of technological measures to the extent such circumvention 189 | is effected by exercising rights under this License with respect to 190 | the covered work, and you disclaim any intention to limit operation or 191 | modification of the work as a means of enforcing, against the work's 192 | users, your or third parties' legal rights to forbid circumvention of 193 | technological measures. 194 | 195 | 4. Conveying Verbatim Copies. 196 | 197 | You may convey verbatim copies of the Program's source code as you 198 | receive it, in any medium, provided that you conspicuously and 199 | appropriately publish on each copy an appropriate copyright notice; 200 | keep intact all notices stating that this License and any 201 | non-permissive terms added in accord with section 7 apply to the code; 202 | keep intact all notices of the absence of any warranty; and give all 203 | recipients a copy of this License along with the Program. 204 | 205 | You may charge any price or no price for each copy that you convey, 206 | and you may offer support or warranty protection for a fee. 207 | 208 | 5. Conveying Modified Source Versions. 209 | 210 | You may convey a work based on the Program, or the modifications to 211 | produce it from the Program, in the form of source code under the 212 | terms of section 4, provided that you also meet all of these conditions: 213 | 214 | a) The work must carry prominent notices stating that you modified 215 | it, and giving a relevant date. 216 | 217 | b) The work must carry prominent notices stating that it is 218 | released under this License and any conditions added under section 219 | 7. This requirement modifies the requirement in section 4 to 220 | "keep intact all notices". 221 | 222 | c) You must license the entire work, as a whole, under this 223 | License to anyone who comes into possession of a copy. This 224 | License will therefore apply, along with any applicable section 7 225 | additional terms, to the whole of the work, and all its parts, 226 | regardless of how they are packaged. This License gives no 227 | permission to license the work in any other way, but it does not 228 | invalidate such permission if you have separately received it. 229 | 230 | d) If the work has interactive user interfaces, each must display 231 | Appropriate Legal Notices; however, if the Program has interactive 232 | interfaces that do not display Appropriate Legal Notices, your 233 | work need not make them do so. 234 | 235 | A compilation of a covered work with other separate and independent 236 | works, which are not by their nature extensions of the covered work, 237 | and which are not combined with it such as to form a larger program, 238 | in or on a volume of a storage or distribution medium, is called an 239 | "aggregate" if the compilation and its resulting copyright are not 240 | used to limit the access or legal rights of the compilation's users 241 | beyond what the individual works permit. Inclusion of a covered work 242 | in an aggregate does not cause this License to apply to the other 243 | parts of the aggregate. 244 | 245 | 6. Conveying Non-Source Forms. 246 | 247 | You may convey a covered work in object code form under the terms 248 | of sections 4 and 5, provided that you also convey the 249 | machine-readable Corresponding Source under the terms of this License, 250 | in one of these ways: 251 | 252 | a) Convey the object code in, or embodied in, a physical product 253 | (including a physical distribution medium), accompanied by the 254 | Corresponding Source fixed on a durable physical medium 255 | customarily used for software interchange. 256 | 257 | b) Convey the object code in, or embodied in, a physical product 258 | (including a physical distribution medium), accompanied by a 259 | written offer, valid for at least three years and valid for as 260 | long as you offer spare parts or customer support for that product 261 | model, to give anyone who possesses the object code either (1) a 262 | copy of the Corresponding Source for all the software in the 263 | product that is covered by this License, on a durable physical 264 | medium customarily used for software interchange, for a price no 265 | more than your reasonable cost of physically performing this 266 | conveying of source, or (2) access to copy the 267 | Corresponding Source from a network server at no charge. 268 | 269 | c) Convey individual copies of the object code with a copy of the 270 | written offer to provide the Corresponding Source. This 271 | alternative is allowed only occasionally and noncommercially, and 272 | only if you received the object code with such an offer, in accord 273 | with subsection 6b. 274 | 275 | d) Convey the object code by offering access from a designated 276 | place (gratis or for a charge), and offer equivalent access to the 277 | Corresponding Source in the same way through the same place at no 278 | further charge. You need not require recipients to copy the 279 | Corresponding Source along with the object code. If the place to 280 | copy the object code is a network server, the Corresponding Source 281 | may be on a different server (operated by you or a third party) 282 | that supports equivalent copying facilities, provided you maintain 283 | clear directions next to the object code saying where to find the 284 | Corresponding Source. Regardless of what server hosts the 285 | Corresponding Source, you remain obligated to ensure that it is 286 | available for as long as needed to satisfy these requirements. 287 | 288 | e) Convey the object code using peer-to-peer transmission, provided 289 | you inform other peers where the object code and Corresponding 290 | Source of the work are being offered to the general public at no 291 | charge under subsection 6d. 292 | 293 | A separable portion of the object code, whose source code is excluded 294 | from the Corresponding Source as a System Library, need not be 295 | included in conveying the object code work. 296 | 297 | A "User Product" is either (1) a "consumer product", which means any 298 | tangible personal property which is normally used for personal, family, 299 | or household purposes, or (2) anything designed or sold for incorporation 300 | into a dwelling. In determining whether a product is a consumer product, 301 | doubtful cases shall be resolved in favor of coverage. For a particular 302 | product received by a particular user, "normally used" refers to a 303 | typical or common use of that class of product, regardless of the status 304 | of the particular user or of the way in which the particular user 305 | actually uses, or expects or is expected to use, the product. A product 306 | is a consumer product regardless of whether the product has substantial 307 | commercial, industrial or non-consumer uses, unless such uses represent 308 | the only significant mode of use of the product. 309 | 310 | "Installation Information" for a User Product means any methods, 311 | procedures, authorization keys, or other information required to install 312 | and execute modified versions of a covered work in that User Product from 313 | a modified version of its Corresponding Source. The information must 314 | suffice to ensure that the continued functioning of the modified object 315 | code is in no case prevented or interfered with solely because 316 | modification has been made. 317 | 318 | If you convey an object code work under this section in, or with, or 319 | specifically for use in, a User Product, and the conveying occurs as 320 | part of a transaction in which the right of possession and use of the 321 | User Product is transferred to the recipient in perpetuity or for a 322 | fixed term (regardless of how the transaction is characterized), the 323 | Corresponding Source conveyed under this section must be accompanied 324 | by the Installation Information. But this requirement does not apply 325 | if neither you nor any third party retains the ability to install 326 | modified object code on the User Product (for example, the work has 327 | been installed in ROM). 328 | 329 | The requirement to provide Installation Information does not include a 330 | requirement to continue to provide support service, warranty, or updates 331 | for a work that has been modified or installed by the recipient, or for 332 | the User Product in which it has been modified or installed. Access to a 333 | network may be denied when the modification itself materially and 334 | adversely affects the operation of the network or violates the rules and 335 | protocols for communication across the network. 336 | 337 | Corresponding Source conveyed, and Installation Information provided, 338 | in accord with this section must be in a format that is publicly 339 | documented (and with an implementation available to the public in 340 | source code form), and must require no special password or key for 341 | unpacking, reading or copying. 342 | 343 | 7. Additional Terms. 344 | 345 | "Additional permissions" are terms that supplement the terms of this 346 | License by making exceptions from one or more of its conditions. 347 | Additional permissions that are applicable to the entire Program shall 348 | be treated as though they were included in this License, to the extent 349 | that they are valid under applicable law. If additional permissions 350 | apply only to part of the Program, that part may be used separately 351 | under those permissions, but the entire Program remains governed by 352 | this License without regard to the additional permissions. 353 | 354 | When you convey a copy of a covered work, you may at your option 355 | remove any additional permissions from that copy, or from any part of 356 | it. (Additional permissions may be written to require their own 357 | removal in certain cases when you modify the work.) You may place 358 | additional permissions on material, added by you to a covered work, 359 | for which you have or can give appropriate copyright permission. 360 | 361 | Notwithstanding any other provision of this License, for material you 362 | add to a covered work, you may (if authorized by the copyright holders of 363 | that material) supplement the terms of this License with terms: 364 | 365 | a) Disclaiming warranty or limiting liability differently from the 366 | terms of sections 15 and 16 of this License; or 367 | 368 | b) Requiring preservation of specified reasonable legal notices or 369 | author attributions in that material or in the Appropriate Legal 370 | Notices displayed by works containing it; or 371 | 372 | c) Prohibiting misrepresentation of the origin of that material, or 373 | requiring that modified versions of such material be marked in 374 | reasonable ways as different from the original version; or 375 | 376 | d) Limiting the use for publicity purposes of names of licensors or 377 | authors of the material; or 378 | 379 | e) Declining to grant rights under trademark law for use of some 380 | trade names, trademarks, or service marks; or 381 | 382 | f) Requiring indemnification of licensors and authors of that 383 | material by anyone who conveys the material (or modified versions of 384 | it) with contractual assumptions of liability to the recipient, for 385 | any liability that these contractual assumptions directly impose on 386 | those licensors and authors. 387 | 388 | All other non-permissive additional terms are considered "further 389 | restrictions" within the meaning of section 10. If the Program as you 390 | received it, or any part of it, contains a notice stating that it is 391 | governed by this License along with a term that is a further 392 | restriction, you may remove that term. If a license document contains 393 | a further restriction but permits relicensing or conveying under this 394 | License, you may add to a covered work material governed by the terms 395 | of that license document, provided that the further restriction does 396 | not survive such relicensing or conveying. 397 | 398 | If you add terms to a covered work in accord with this section, you 399 | must place, in the relevant source files, a statement of the 400 | additional terms that apply to those files, or a notice indicating 401 | where to find the applicable terms. 402 | 403 | Additional terms, permissive or non-permissive, may be stated in the 404 | form of a separately written license, or stated as exceptions; 405 | the above requirements apply either way. 406 | 407 | 8. Termination. 408 | 409 | You may not propagate or modify a covered work except as expressly 410 | provided under this License. Any attempt otherwise to propagate or 411 | modify it is void, and will automatically terminate your rights under 412 | this License (including any patent licenses granted under the third 413 | paragraph of section 11). 414 | 415 | However, if you cease all violation of this License, then your 416 | license from a particular copyright holder is reinstated (a) 417 | provisionally, unless and until the copyright holder explicitly and 418 | finally terminates your license, and (b) permanently, if the copyright 419 | holder fails to notify you of the violation by some reasonable means 420 | prior to 60 days after the cessation. 421 | 422 | Moreover, your license from a particular copyright holder is 423 | reinstated permanently if the copyright holder notifies you of the 424 | violation by some reasonable means, this is the first time you have 425 | received notice of violation of this License (for any work) from that 426 | copyright holder, and you cure the violation prior to 30 days after 427 | your receipt of the notice. 428 | 429 | Termination of your rights under this section does not terminate the 430 | licenses of parties who have received copies or rights from you under 431 | this License. If your rights have been terminated and not permanently 432 | reinstated, you do not qualify to receive new licenses for the same 433 | material under section 10. 434 | 435 | 9. Acceptance Not Required for Having Copies. 436 | 437 | You are not required to accept this License in order to receive or 438 | run a copy of the Program. Ancillary propagation of a covered work 439 | occurring solely as a consequence of using peer-to-peer transmission 440 | to receive a copy likewise does not require acceptance. However, 441 | nothing other than this License grants you permission to propagate or 442 | modify any covered work. These actions infringe copyright if you do 443 | not accept this License. Therefore, by modifying or propagating a 444 | covered work, you indicate your acceptance of this License to do so. 445 | 446 | 10. Automatic Licensing of Downstream Recipients. 447 | 448 | Each time you convey a covered work, the recipient automatically 449 | receives a license from the original licensors, to run, modify and 450 | propagate that work, subject to this License. You are not responsible 451 | for enforcing compliance by third parties with this License. 452 | 453 | An "entity transaction" is a transaction transferring control of an 454 | organization, or substantially all assets of one, or subdividing an 455 | organization, or merging organizations. If propagation of a covered 456 | work results from an entity transaction, each party to that 457 | transaction who receives a copy of the work also receives whatever 458 | licenses to the work the party's predecessor in interest had or could 459 | give under the previous paragraph, plus a right to possession of the 460 | Corresponding Source of the work from the predecessor in interest, if 461 | the predecessor has it or can get it with reasonable efforts. 462 | 463 | You may not impose any further restrictions on the exercise of the 464 | rights granted or affirmed under this License. For example, you may 465 | not impose a license fee, royalty, or other charge for exercise of 466 | rights granted under this License, and you may not initiate litigation 467 | (including a cross-claim or counterclaim in a lawsuit) alleging that 468 | any patent claim is infringed by making, using, selling, offering for 469 | sale, or importing the Program or any portion of it. 470 | 471 | 11. Patents. 472 | 473 | A "contributor" is a copyright holder who authorizes use under this 474 | License of the Program or a work on which the Program is based. The 475 | work thus licensed is called the contributor's "contributor version". 476 | 477 | A contributor's "essential patent claims" are all patent claims 478 | owned or controlled by the contributor, whether already acquired or 479 | hereafter acquired, that would be infringed by some manner, permitted 480 | by this License, of making, using, or selling its contributor version, 481 | but do not include claims that would be infringed only as a 482 | consequence of further modification of the contributor version. For 483 | purposes of this definition, "control" includes the right to grant 484 | patent sublicenses in a manner consistent with the requirements of 485 | this License. 486 | 487 | Each contributor grants you a non-exclusive, worldwide, royalty-free 488 | patent license under the contributor's essential patent claims, to 489 | make, use, sell, offer for sale, import and otherwise run, modify and 490 | propagate the contents of its contributor version. 491 | 492 | In the following three paragraphs, a "patent license" is any express 493 | agreement or commitment, however denominated, not to enforce a patent 494 | (such as an express permission to practice a patent or covenant not to 495 | sue for patent infringement). To "grant" such a patent license to a 496 | party means to make such an agreement or commitment not to enforce a 497 | patent against the party. 498 | 499 | If you convey a covered work, knowingly relying on a patent license, 500 | and the Corresponding Source of the work is not available for anyone 501 | to copy, free of charge and under the terms of this License, through a 502 | publicly available network server or other readily accessible means, 503 | then you must either (1) cause the Corresponding Source to be so 504 | available, or (2) arrange to deprive yourself of the benefit of the 505 | patent license for this particular work, or (3) arrange, in a manner 506 | consistent with the requirements of this License, to extend the patent 507 | license to downstream recipients. "Knowingly relying" means you have 508 | actual knowledge that, but for the patent license, your conveying the 509 | covered work in a country, or your recipient's use of the covered work 510 | in a country, would infringe one or more identifiable patents in that 511 | country that you have reason to believe are valid. 512 | 513 | If, pursuant to or in connection with a single transaction or 514 | arrangement, you convey, or propagate by procuring conveyance of, a 515 | covered work, and grant a patent license to some of the parties 516 | receiving the covered work authorizing them to use, propagate, modify 517 | or convey a specific copy of the covered work, then the patent license 518 | you grant is automatically extended to all recipients of the covered 519 | work and works based on it. 520 | 521 | A patent license is "discriminatory" if it does not include within 522 | the scope of its coverage, prohibits the exercise of, or is 523 | conditioned on the non-exercise of one or more of the rights that are 524 | specifically granted under this License. You may not convey a covered 525 | work if you are a party to an arrangement with a third party that is 526 | in the business of distributing software, under which you make payment 527 | to the third party based on the extent of your activity of conveying 528 | the work, and under which the third party grants, to any of the 529 | parties who would receive the covered work from you, a discriminatory 530 | patent license (a) in connection with copies of the covered work 531 | conveyed by you (or copies made from those copies), or (b) primarily 532 | for and in connection with specific products or compilations that 533 | contain the covered work, unless you entered into that arrangement, 534 | or that patent license was granted, prior to 28 March 2007. 535 | 536 | Nothing in this License shall be construed as excluding or limiting 537 | any implied license or other defenses to infringement that may 538 | otherwise be available to you under applicable patent law. 539 | 540 | 12. No Surrender of Others' Freedom. 541 | 542 | If conditions are imposed on you (whether by court order, agreement or 543 | otherwise) that contradict the conditions of this License, they do not 544 | excuse you from the conditions of this License. If you cannot convey a 545 | covered work so as to satisfy simultaneously your obligations under this 546 | License and any other pertinent obligations, then as a consequence you may 547 | not convey it at all. For example, if you agree to terms that obligate you 548 | to collect a royalty for further conveying from those to whom you convey 549 | the Program, the only way you could satisfy both those terms and this 550 | License would be to refrain entirely from conveying the Program. 551 | 552 | 13. Use with the GNU Affero General Public License. 553 | 554 | Notwithstanding any other provision of this License, you have 555 | permission to link or combine any covered work with a work licensed 556 | under version 3 of the GNU Affero General Public License into a single 557 | combined work, and to convey the resulting work. The terms of this 558 | License will continue to apply to the part which is the covered work, 559 | but the special requirements of the GNU Affero General Public License, 560 | section 13, concerning interaction through a network will apply to the 561 | combination as such. 562 | 563 | 14. Revised Versions of this License. 564 | 565 | The Free Software Foundation may publish revised and/or new versions of 566 | the GNU General Public License from time to time. Such new versions will 567 | be similar in spirit to the present version, but may differ in detail to 568 | address new problems or concerns. 569 | 570 | Each version is given a distinguishing version number. If the 571 | Program specifies that a certain numbered version of the GNU General 572 | Public License "or any later version" applies to it, you have the 573 | option of following the terms and conditions either of that numbered 574 | version or of any later version published by the Free Software 575 | Foundation. If the Program does not specify a version number of the 576 | GNU General Public License, you may choose any version ever published 577 | by the Free Software Foundation. 578 | 579 | If the Program specifies that a proxy can decide which future 580 | versions of the GNU General Public License can be used, that proxy's 581 | public statement of acceptance of a version permanently authorizes you 582 | to choose that version for the Program. 583 | 584 | Later license versions may give you additional or different 585 | permissions. However, no additional obligations are imposed on any 586 | author or copyright holder as a result of your choosing to follow a 587 | later version. 588 | 589 | 15. Disclaimer of Warranty. 590 | 591 | THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY 592 | APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT 593 | HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY 594 | OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, 595 | THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 596 | PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM 597 | IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF 598 | ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 599 | 600 | 16. Limitation of Liability. 601 | 602 | IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 603 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS 604 | THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY 605 | GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE 606 | USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF 607 | DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD 608 | PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), 609 | EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF 610 | SUCH DAMAGES. 611 | 612 | 17. Interpretation of Sections 15 and 16. 613 | 614 | If the disclaimer of warranty and limitation of liability provided 615 | above cannot be given local legal effect according to their terms, 616 | reviewing courts shall apply local law that most closely approximates 617 | an absolute waiver of all civil liability in connection with the 618 | Program, unless a warranty or assumption of liability accompanies a 619 | copy of the Program in return for a fee. 620 | 621 | END OF TERMS AND CONDITIONS 622 | 623 | How to Apply These Terms to Your New Programs 624 | 625 | If you develop a new program, and you want it to be of the greatest 626 | possible use to the public, the best way to achieve this is to make it 627 | free software which everyone can redistribute and change under these terms. 628 | 629 | To do so, attach the following notices to the program. It is safest 630 | to attach them to the start of each source file to most effectively 631 | state the exclusion of warranty; and each file should have at least 632 | the "copyright" line and a pointer to where the full notice is found. 633 | 634 | 635 | Copyright (C) 636 | 637 | This program is free software: you can redistribute it and/or modify 638 | it under the terms of the GNU General Public License as published by 639 | the Free Software Foundation, either version 3 of the License, or 640 | (at your option) any later version. 641 | 642 | This program is distributed in the hope that it will be useful, 643 | but WITHOUT ANY WARRANTY; without even the implied warranty of 644 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 645 | GNU General Public License for more details. 646 | 647 | You should have received a copy of the GNU General Public License 648 | along with this program. If not, see . 649 | 650 | Also add information on how to contact you by electronic and paper mail. 651 | 652 | If the program does terminal interaction, make it output a short 653 | notice like this when it starts in an interactive mode: 654 | 655 | Copyright (C) 656 | This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. 657 | This is free software, and you are welcome to redistribute it 658 | under certain conditions; type `show c' for details. 659 | 660 | The hypothetical commands `show w' and `show c' should show the appropriate 661 | parts of the General Public License. Of course, your program's commands 662 | might be different; for a GUI interface, you would use an "about box". 663 | 664 | You should also get your employer (if you work as a programmer) or school, 665 | if any, to sign a "copyright disclaimer" for the program, if necessary. 666 | For more information on this, and how to apply and follow the GNU GPL, see 667 | . 668 | 669 | The GNU General Public License does not permit incorporating your program 670 | into proprietary programs. If your program is a subroutine library, you 671 | may consider it more useful to permit linking proprietary applications with 672 | the library. If this is what you want to do, use the GNU Lesser General 673 | Public License instead of this License. But first, please read 674 | . 675 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ubitx_v5 2 | Firmware for the version 5 of the ubitx 3 | -------------------------------------------------------------------------------- /ubitx_cat.ino: -------------------------------------------------------------------------------- 1 | /** 2 | * The CAT protocol is used by many radios to provide remote control to comptuers through 3 | * the serial port. 4 | * 5 | * This is very much a work in progress. Parts of this code have been liberally 6 | * borrowed from other GPLicensed works like hamlib. 7 | * 8 | * WARNING : This is an unstable version and it has worked with fldigi, 9 | * it gives time out error with WSJTX 1.8.0 10 | */ 11 | 12 | static unsigned long rxBufferArriveTime = 0; 13 | static byte rxBufferCheckCount = 0; 14 | #define CAT_RECEIVE_TIMEOUT 500 15 | static byte cat[5]; 16 | static byte insideCat = 0; 17 | static byte useOpenRadioControl = 0; 18 | 19 | //for broken protocol 20 | #define CAT_RECEIVE_TIMEOUT 500 21 | 22 | #define CAT_MODE_LSB 0x00 23 | #define CAT_MODE_USB 0x01 24 | #define CAT_MODE_CW 0x02 25 | #define CAT_MODE_CWR 0x03 26 | #define CAT_MODE_AM 0x04 27 | #define CAT_MODE_FM 0x08 28 | #define CAT_MODE_DIG 0x0A 29 | #define CAT_MODE_PKT 0x0C 30 | #define CAT_MODE_FMN 0x88 31 | 32 | #define ACK 0 33 | 34 | unsigned int skipTimeCount = 0; 35 | 36 | byte setHighNibble(byte b,byte v) { 37 | // Clear the high nibble 38 | b &= 0x0f; 39 | // Set the high nibble 40 | return b | ((v & 0x0f) << 4); 41 | } 42 | 43 | byte setLowNibble(byte b,byte v) { 44 | // Clear the low nibble 45 | b &= 0xf0; 46 | // Set the low nibble 47 | return b | (v & 0x0f); 48 | } 49 | 50 | byte getHighNibble(byte b) { 51 | return (b >> 4) & 0x0f; 52 | } 53 | 54 | byte getLowNibble(byte b) { 55 | return b & 0x0f; 56 | } 57 | 58 | // Takes a number and produces the requested number of decimal digits, staring 59 | // from the least significant digit. 60 | // 61 | void getDecimalDigits(unsigned long number,byte* result,int digits) { 62 | for (int i = 0; i < digits; i++) { 63 | // "Mask off" (in a decimal sense) the LSD and return it 64 | result[i] = number % 10; 65 | // "Shift right" (in a decimal sense) 66 | number /= 10; 67 | } 68 | } 69 | 70 | // Takes a frequency and writes it into the CAT command buffer in BCD form. 71 | // 72 | void writeFreq(unsigned long freq,byte* cmd) { 73 | // Convert the frequency to a set of decimal digits. We are taking 9 digits 74 | // so that we can get up to 999 MHz. But the protocol doesn't care about the 75 | // LSD (1's place), so we ignore that digit. 76 | byte digits[9]; 77 | getDecimalDigits(freq,digits,9); 78 | // Start from the LSB and get each nibble 79 | cmd[3] = setLowNibble(cmd[3],digits[1]); 80 | cmd[3] = setHighNibble(cmd[3],digits[2]); 81 | cmd[2] = setLowNibble(cmd[2],digits[3]); 82 | cmd[2] = setHighNibble(cmd[2],digits[4]); 83 | cmd[1] = setLowNibble(cmd[1],digits[5]); 84 | cmd[1] = setHighNibble(cmd[1],digits[6]); 85 | cmd[0] = setLowNibble(cmd[0],digits[7]); 86 | cmd[0] = setHighNibble(cmd[0],digits[8]); 87 | } 88 | 89 | // This function takes a frquency that is encoded using 4 bytes of BCD 90 | // representation and turns it into an long measured in Hz. 91 | // 92 | // [12][34][56][78] = 123.45678? Mhz 93 | // 94 | unsigned long readFreq(byte* cmd) { 95 | // Pull off each of the digits 96 | byte d7 = getHighNibble(cmd[0]); 97 | byte d6 = getLowNibble(cmd[0]); 98 | byte d5 = getHighNibble(cmd[1]); 99 | byte d4 = getLowNibble(cmd[1]); 100 | byte d3 = getHighNibble(cmd[2]); 101 | byte d2 = getLowNibble(cmd[2]); 102 | byte d1 = getHighNibble(cmd[3]); 103 | byte d0 = getLowNibble(cmd[3]); 104 | return 105 | (unsigned long)d7 * 100000000L + 106 | (unsigned long)d6 * 10000000L + 107 | (unsigned long)d5 * 1000000L + 108 | (unsigned long)d4 * 100000L + 109 | (unsigned long)d3 * 10000L + 110 | (unsigned long)d2 * 1000L + 111 | (unsigned long)d1 * 100L + 112 | (unsigned long)d0 * 10L; 113 | } 114 | 115 | //void ReadEEPRom_FT817(byte fromType) 116 | void catReadEEPRom(void) 117 | { 118 | //for remove warnings 119 | byte temp0 = cat[0]; 120 | byte temp1 = cat[1]; 121 | /* 122 | itoa((int) cat[0], b, 16); 123 | strcat(b, ":"); 124 | itoa((int) cat[1], c, 16); 125 | strcat(b, c); 126 | printLine2(b); 127 | */ 128 | 129 | cat[0] = 0; 130 | cat[1] = 0; 131 | //for remove warnings[1] = 0; 132 | 133 | switch (temp1) 134 | { 135 | case 0x45 : // 136 | if (temp0 == 0x03) 137 | { 138 | cat[0] = 0x00; 139 | cat[1] = 0xD0; 140 | } 141 | break; 142 | case 0x47 : // 143 | if (temp0 == 0x03) 144 | { 145 | cat[0] = 0xDC; 146 | cat[1] = 0xE0; 147 | } 148 | break; 149 | case 0x55 : 150 | //0 : VFO A/B 0 = VFO-A, 1 = VFO-B 151 | //1 : MTQMB Select 0 = (Not MTQMB), 1 = MTQMB ("Memory Tune Quick Memory Bank") 152 | //2 : QMB Select 0 = (Not QMB), 1 = QMB ("Quick Memory Bank") 153 | //3 : 154 | //4 : Home Select 0 = (Not HOME), 1 = HOME memory 155 | //5 : Memory/MTUNE select 0 = Memory, 1 = MTUNE 156 | //6 : 157 | //7 : MEM/VFO Select 0 = Memory, 1 = VFO (A or B - see bit 0) 158 | cat[0] = 0x80 + (vfoActive == VFO_B ? 1 : 0); 159 | cat[1] = 0x00; 160 | break; 161 | case 0x57 : // 162 | //0 : 1-0 AGC Mode 00 = Auto, 01 = Fast, 10 = Slow, 11 = Off 163 | //2 DSP On/Off 0 = Off, 1 = On (Display format) 164 | //4 PBT On/Off 0 = Off, 1 = On (Passband Tuning) 165 | //5 NB On/Off 0 = Off, 1 = On (Noise Blanker) 166 | //6 Lock On/Off 0 = Off, 1 = On (Dial Lock) 167 | //7 FST (Fast Tuning) On/Off 0 = Off, 1 = On (Fast tuning) 168 | 169 | cat[0] = 0xC0; 170 | cat[1] = 0x40; 171 | break; 172 | 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) 173 | //http://www.ka7oei.com/ft817_memmap.html 174 | //CAT_BUFF[0] = 0xC2; 175 | //CAT_BUFF[1] = 0x82; 176 | break; 177 | case 0x5C : //Beep Volume (0-100) (#13) 178 | cat[0] = 0xB2; 179 | cat[1] = 0x42; 180 | break; 181 | case 0x5E : 182 | //3-0 : CW Pitch (300-1000 Hz) (#20) From 0 to E (HEX) with 0 = 300 Hz and each step representing 50 Hz 183 | //5-4 : Lock Mode (#32) 00 = Dial, 01 = Freq, 10 = Panel 184 | //7-6 : Op Filter (#38) 00 = Off, 01 = SSB, 10 = CW 185 | //CAT_BUFF[0] = 0x08; 186 | cat[0] = (sideTone - 300)/50; 187 | cat[1] = 0x25; 188 | break; 189 | case 0x61 : //Sidetone (Volume) (#44) 190 | cat[0] = sideTone % 50; 191 | cat[1] = 0x08; 192 | break; 193 | case 0x5F : // 194 | //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 195 | //5 420 ARS (#2) 0 = Off, 1 = On 196 | //6 144 ARS (#1) 0 = Off, 1 = On 197 | //7 Sql/RF-G (#45) 0 = Off, 1 = On 198 | cat[0] = 0x32; 199 | cat[1] = 0x08; 200 | break; 201 | case 0x60 : //CW Delay (10-2500 ms) (#17) From 1 to 250 (decimal) with each step representing 10 ms 202 | cat[0] = cwDelayTime; 203 | cat[1] = 0x32; 204 | break; 205 | case 0x62 : // 206 | //5-0 CW Speed (4-60 WPM) (#21) From 0 to 38 (HEX) with 0 = 4 WPM and 38 = 60 WPM (1 WPM steps) 207 | //7-6 Batt-Chg (6/8/10 Hours (#11) 00 = 6 Hours, 01 = 8 Hours, 10 = 10 Hours 208 | //CAT_BUFF[0] = 0x08; 209 | cat[0] = 1200 / cwSpeed - 4; 210 | cat[1] = 0xB2; 211 | break; 212 | case 0x63 : // 213 | //6-0 VOX Gain (#51) Contains 1-100 (decimal) as displayed 214 | //7 Disable AM/FM Dial (#4) 0 = Enable, 1 = Disable 215 | cat[0] = 0xB2; 216 | cat[1] = 0xA5; 217 | break; 218 | case 0x64 : // 219 | break; 220 | case 0x67 : //6-0 SSB Mic (#46) Contains 0-100 (decimal) as displayed 221 | cat[0] = 0xB2; 222 | cat[1] = 0xB2; 223 | break; case 0x69 : //FM Mic (#29) Contains 0-100 (decimal) as displayed 224 | case 0x78 : 225 | if (isUSB) 226 | cat[0] = CAT_MODE_USB; 227 | else 228 | cat[0] = CAT_MODE_LSB; 229 | 230 | if (cat[0] != 0) cat[0] = 1 << 5; 231 | break; 232 | case 0x79 : // 233 | //1-0 TX Power (All bands) 00 = High, 01 = L3, 10 = L2, 11 = L1 234 | //3 PRI On/Off 0 = Off, 1 = On 235 | //DW On/Off 0 = Off, 1 = On 236 | //SCN (Scan) Mode 00 = No scan, 10 = Scan up, 11 = Scan down 237 | //ART On/Off 0 = Off, 1 = On 238 | cat[0] = 0x00; 239 | cat[1] = 0x00; 240 | break; 241 | case 0x7A : //SPLIT 242 | //7A 0 HF Antenna Select 0 = Front, 1 = Rear 243 | //7A 1 6 M Antenna Select 0 = Front, 1 = Rear 244 | //7A 2 FM BCB Antenna Select 0 = Front, 1 = Rear 245 | //7A 3 Air Antenna Select 0 = Front, 1 = Rear 246 | //7A 4 2 M Antenna Select 0 = Front, 1 = Rear 247 | //7A 5 UHF Antenna Select 0 = Front, 1 = Rear 248 | //7A 6 ? ? 249 | //7A 7 SPL On/Off 0 = Off, 1 = On 250 | 251 | cat[0] = (splitOn ? 0xFF : 0x7F); 252 | break; 253 | case 0xB3 : // 254 | cat[0] = 0x00; 255 | cat[1] = 0x4D; 256 | break; 257 | 258 | } 259 | 260 | // sent the data 261 | Serial.write(cat, 2); 262 | } 263 | 264 | void processCATCommand2(byte* cmd) { 265 | byte response[5]; 266 | unsigned long f; 267 | 268 | switch(cmd[4]){ 269 | /* case 0x00: 270 | response[0]=0; 271 | Serial.write(response, 1); 272 | break; 273 | */ 274 | case 0x01: 275 | //set frequency 276 | f = readFreq(cmd); 277 | setFrequency(f); 278 | updateDisplay(); 279 | response[0]=0; 280 | Serial.write(response, 1); 281 | //sprintf(b, "set:%ld", f); 282 | //printLine2(b); 283 | break; 284 | 285 | case 0x02: 286 | //split on 287 | splitOn = 1; 288 | break; 289 | case 0x82: 290 | //split off 291 | splitOn = 0; 292 | break; 293 | 294 | case 0x03: 295 | writeFreq(frequency,response); // Put the frequency into the buffer 296 | if (isUSB) 297 | response[4] = 0x01; //USB 298 | else 299 | response[4] = 0x00; //LSB 300 | Serial.write(response,5); 301 | //printLine2("cat:getfreq"); 302 | break; 303 | 304 | case 0x07: // set mode 305 | if (cmd[0] == 0x00 || cmd[0] == 0x03) 306 | isUSB = 0; 307 | else 308 | isUSB = 1; 309 | response[0] = 0x00; 310 | Serial.write(response, 1); 311 | setFrequency(frequency); 312 | //printLine2("cat: mode changed"); 313 | //updateDisplay(); 314 | break; 315 | 316 | case 0x08: // PTT On 317 | if (!inTx) { 318 | response[0] = 0; 319 | txCAT = true; 320 | startTx(TX_SSB); 321 | updateDisplay(); 322 | } else { 323 | response[0] = 0xf0; 324 | } 325 | Serial.write(response,1); 326 | updateDisplay(); 327 | break; 328 | 329 | case 0x88 : //PTT OFF 330 | if (inTx) { 331 | stopTx(); 332 | txCAT = false; 333 | } 334 | response[0] = 0; 335 | Serial.write(response,1); 336 | updateDisplay(); 337 | break; 338 | 339 | case 0x81: 340 | //toggle the VFOs 341 | response[0] = 0; 342 | menuVfoToggle(1); // '1' forces it to change the VFO 343 | Serial.write(response,1); 344 | updateDisplay(); 345 | break; 346 | 347 | case 0xBB: //Read FT-817 EEPROM Data (for comfirtable) 348 | catReadEEPRom(); 349 | break; 350 | 351 | case 0xe7 : 352 | // get receiver status, we have hardcoded this as 353 | //as we dont' support ctcss, etc. 354 | response[0] = 0x09; 355 | Serial.write(response,1); 356 | break; 357 | 358 | case 0xf7: 359 | { 360 | boolean isHighSWR = false; 361 | boolean isSplitOn = false; 362 | 363 | /* 364 | Inverted -> *ptt = ((p->tx_status & 0x80) == 0); <-- souce code in ft817.c (hamlib) 365 | */ 366 | response[0] = ((inTx ? 0 : 1) << 7) + 367 | ((isHighSWR ? 1 : 0) << 6) + //hi swr off / on 368 | ((isSplitOn ? 1 : 0) << 5) + //Split on / off 369 | (0 << 4) + //dummy data 370 | 0x08; //P0 meter data 371 | 372 | Serial.write(response, 1); 373 | } 374 | break; 375 | 376 | default: 377 | //somehow, get this to print the four bytes 378 | ultoa(*((unsigned long *)cmd), c, 16); 379 | itoa(cmd[4], b, 16); 380 | strcat(b, ">"); 381 | strcat(b, c); 382 | printLine2(b); 383 | response[0] = 0x00; 384 | Serial.write(response[0]); 385 | } 386 | 387 | insideCat = false; 388 | } 389 | 390 | int catCount = 0; 391 | void checkCAT(){ 392 | byte i; 393 | 394 | //Check Serial Port Buffer 395 | if (Serial.available() == 0) { //Set Buffer Clear status 396 | rxBufferCheckCount = 0; 397 | return; 398 | } 399 | else if (Serial.available() < 5) { //First Arrived 400 | if (rxBufferCheckCount == 0){ 401 | rxBufferCheckCount = Serial.available(); 402 | rxBufferArriveTime = millis() + CAT_RECEIVE_TIMEOUT; //Set time for timeout 403 | } 404 | else if (rxBufferArriveTime < millis()){ //Clear Buffer 405 | for (i = 0; i < Serial.available(); i++) 406 | rxBufferCheckCount = Serial.read(); 407 | rxBufferCheckCount = 0; 408 | } 409 | else if (rxBufferCheckCount < Serial.available()){ // Increase buffer count, slow arrive 410 | rxBufferCheckCount = Serial.available(); 411 | rxBufferArriveTime = millis() + CAT_RECEIVE_TIMEOUT; //Set time for timeout 412 | } 413 | return; 414 | } 415 | 416 | 417 | //Arived CAT DATA 418 | for (i = 0; i < 5; i++) 419 | cat[i] = Serial.read(); 420 | 421 | 422 | //this code is not re-entrant. 423 | if (insideCat == 1) 424 | return; 425 | insideCat = 1; 426 | 427 | /** 428 | * This routine is enabled to debug the cat protocol 429 | **/ 430 | catCount++; 431 | 432 | if (cat[4] != 0xf7 && cat[4] != 0xbb && cat[4] != 0x03){ 433 | sprintf(b, "%d %02x %02x%02x%02x%02x", catCount, cat[4],cat[0], cat[1], cat[2], cat[3]); 434 | printLine2(b); 435 | } 436 | 437 | processCATCommand2(cat); 438 | insideCat = 0; 439 | } 440 | 441 | 442 | -------------------------------------------------------------------------------- /ubitx_factory_alignment.ino: -------------------------------------------------------------------------------- 1 | 2 | /** 3 | * This procedure is only for those who have a signal generator/transceiver tuned to exactly 7.150 and a dummy load 4 | */ 5 | 6 | void btnWaitForClick(){ 7 | while(!btnDown()) 8 | active_delay(50); 9 | while(btnDown()) 10 | active_delay(50); 11 | active_delay(50); 12 | } 13 | 14 | /** 15 | * Take a deep breath, math(ematics) ahead 16 | * The 25 mhz oscillator is multiplied by 35 to run the vco at 875 mhz 17 | * This is divided by a number to generate different frequencies. 18 | * If we divide it by 875, we will get 1 mhz signal 19 | * So, if the vco is shifted up by 875 hz, the generated frequency of 1 mhz is shifted by 1 hz (875/875) 20 | * At 12 Mhz, the carrier will needed to be shifted down by 12 hz for every 875 hz of shift up of the vco 21 | * 22 | */ 23 | 24 | 25 | void factory_alignment(){ 26 | 27 | calibrateClock(); 28 | 29 | if (calibration == 0){ 30 | printLine2("Setup Aborted"); 31 | return; 32 | } 33 | 34 | //move it away to 7.160 for an LSB signal 35 | setFrequency(7170000l); 36 | updateDisplay(); 37 | printLine2("#2 BFO"); 38 | active_delay(1000); 39 | 40 | usbCarrier = 11053000l; 41 | menuSetupCarrier(1); 42 | 43 | if (usbCarrier == 11994999l){ 44 | printLine2("Setup Aborted"); 45 | return; 46 | } 47 | 48 | printLine2("#3:Test 3.5MHz"); 49 | isUSB = false; 50 | setFrequency(3500000l); 51 | updateDisplay(); 52 | 53 | while (!btnDown()){ 54 | checkPTT(); 55 | active_delay(100); 56 | } 57 | 58 | btnWaitForClick(); 59 | printLine2("#4:Test 7MHz"); 60 | 61 | setFrequency(7150000l); 62 | updateDisplay(); 63 | while (!btnDown()){ 64 | checkPTT(); 65 | active_delay(100); 66 | } 67 | 68 | btnWaitForClick(); 69 | printLine2("#5:Test 14MHz"); 70 | 71 | isUSB = true; 72 | setFrequency(14000000l); 73 | updateDisplay(); 74 | while (!btnDown()){ 75 | checkPTT(); 76 | active_delay(100); 77 | } 78 | 79 | btnWaitForClick(); 80 | printLine2("#6:Test 28MHz"); 81 | 82 | setFrequency(28000000l); 83 | updateDisplay(); 84 | while (!btnDown()){ 85 | checkPTT(); 86 | active_delay(100); 87 | } 88 | 89 | printLine2("Alignment done"); 90 | active_delay(1000); 91 | 92 | isUSB = false; 93 | setFrequency(7150000l); 94 | updateDisplay(); 95 | 96 | } 97 | 98 | -------------------------------------------------------------------------------- /ubitx_keyer.ino: -------------------------------------------------------------------------------- 1 | /** 2 | CW Keyer 3 | CW Key logic change with ron's code (ubitx_keyer.cpp) 4 | Ron's logic has been modified to work with the original uBITX by KD8CEC 5 | 6 | Original Comment ---------------------------------------------------------------------------- 7 | * The CW keyer handles either a straight key or an iambic / paddle key. 8 | * They all use just one analog input line. This is how it works. 9 | * The analog line has the internal pull-up resistor enabled. 10 | * When a straight key is connected, it shorts the pull-up resistor, analog input is 0 volts 11 | * When a paddle is connected, the dot and the dash are connected to the analog pin through 12 | * a 10K and a 2.2K resistors. These produce a 4v and a 2v input to the analog pins. 13 | * So, the readings are as follows : 14 | * 0v - straight key 15 | * 1-2.5 v - paddle dot 16 | * 2.5 to 4.5 v - paddle dash 17 | * 2.0 to 0.5 v - dot and dash pressed 18 | * 19 | * The keyer is written to transparently handle all these cases 20 | * 21 | * Generating CW 22 | * The CW is cleanly generated by unbalancing the front-end mixer 23 | * and putting the local oscillator directly at the CW transmit frequency. 24 | * The sidetone, generated by the Arduino is injected into the volume control 25 | */ 26 | 27 | //CW ADC Range 28 | int cwAdcSTFrom = 0; 29 | int cwAdcSTTo = 50; 30 | int cwAdcBothFrom = 51; 31 | int cwAdcBothTo = 300; 32 | int cwAdcDotFrom = 301; 33 | int cwAdcDotTo = 600; 34 | int cwAdcDashFrom = 601; 35 | int cwAdcDashTo = 800; 36 | //byte cwKeyType = 0; //0: straight, 1 : iambica, 2: iambicb 37 | 38 | byte delayBeforeCWStartTime = 50; 39 | 40 | 41 | 42 | 43 | // in milliseconds, this is the parameter that determines how long the tx will hold between cw key downs 44 | //#define CW_TIMEOUT (600l) //Change to CW Delaytime for value save to eeprom 45 | #define PADDLE_DOT 1 46 | #define PADDLE_DASH 2 47 | #define PADDLE_BOTH 3 48 | #define PADDLE_STRAIGHT 4 49 | 50 | //we store the last padde's character 51 | //to alternatively send dots and dashes 52 | //when both are simultaneously pressed 53 | char lastPaddle = 0; 54 | 55 | //reads the analog keyer pin and reports the paddle 56 | byte getPaddle(){ 57 | int paddle = analogRead(ANALOG_KEYER); 58 | 59 | if (paddle > 800) // above 4v is up 60 | return 0; 61 | 62 | if (paddle > 600) // 4-3v is dot 63 | return PADDLE_DASH; 64 | else if (paddle > 300) //1-2v is dash 65 | return PADDLE_DOT; 66 | else if (paddle > 50) 67 | return PADDLE_BOTH; //both are between 1 and 2v 68 | else 69 | return PADDLE_STRAIGHT; //less than 1v is the straight key 70 | } 71 | 72 | /** 73 | * Starts transmitting the carrier with the sidetone 74 | * It assumes that we have called cwTxStart and not called cwTxStop 75 | * each time it is called, the cwTimeOut is pushed further into the future 76 | */ 77 | void cwKeydown(){ 78 | keyDown = 1; //tracks the CW_KEY 79 | tone(CW_TONE, (int)sideTone); 80 | digitalWrite(CW_KEY, 1); 81 | 82 | //Modified by KD8CEC, for CW Delay Time save to eeprom 83 | //cwTimeout = millis() + CW_TIMEOUT; 84 | cwTimeout = millis() + cwDelayTime * 10; 85 | } 86 | 87 | /** 88 | * Stops the cw carrier transmission along with the sidetone 89 | * Pushes the cwTimeout further into the future 90 | */ 91 | void cwKeyUp(){ 92 | keyDown = 0; //tracks the CW_KEY 93 | noTone(CW_TONE); 94 | digitalWrite(CW_KEY, 0); 95 | 96 | //Modified by KD8CEC, for CW Delay Time save to eeprom 97 | //cwTimeout = millis() + CW_TIMEOUT; 98 | cwTimeout = millis() + cwDelayTime * 10; 99 | } 100 | 101 | //Variables for Ron's new logic 102 | #define DIT_L 0x01 // DIT latch 103 | #define DAH_L 0x02 // DAH latch 104 | #define DIT_PROC 0x04 // DIT is being processed 105 | #define PDLSWAP 0x08 // 0 for normal, 1 for swap 106 | #define IAMBICB 0x10 // 0 for Iambic A, 1 for Iambic B 107 | enum KSTYPE {IDLE, CHK_DIT, CHK_DAH, KEYED_PREP, KEYED, INTER_ELEMENT }; 108 | static unsigned long ktimer; 109 | unsigned char keyerState = IDLE; 110 | 111 | //Below is a test to reduce the keying error. do not delete lines 112 | //create by KD8CEC for compatible with new CW Logic 113 | char update_PaddleLatch(byte isUpdateKeyState) { 114 | unsigned char tmpKeyerControl = 0; 115 | 116 | int paddle = analogRead(ANALOG_KEYER); 117 | //diagnostic, VU2ESE 118 | //itoa(paddle, b, 10); 119 | //printLine2(b); 120 | 121 | if (paddle >= cwAdcDashFrom && paddle <= cwAdcDashTo) 122 | tmpKeyerControl |= DAH_L; 123 | else if (paddle >= cwAdcDotFrom && paddle <= cwAdcDotTo) 124 | tmpKeyerControl |= DIT_L; 125 | else if (paddle >= cwAdcBothFrom && paddle <= cwAdcBothTo) 126 | tmpKeyerControl |= (DAH_L | DIT_L) ; 127 | else 128 | { 129 | if (Iambic_Key) 130 | tmpKeyerControl = 0 ; 131 | else if (paddle >= cwAdcSTFrom && paddle <= cwAdcSTTo) 132 | tmpKeyerControl = DIT_L ; 133 | else 134 | tmpKeyerControl = 0 ; 135 | } 136 | 137 | if (isUpdateKeyState == 1) 138 | keyerControl |= tmpKeyerControl; 139 | 140 | return tmpKeyerControl; 141 | } 142 | 143 | /***************************************************************************** 144 | // New logic, by RON 145 | // modified by KD8CEC 146 | ******************************************************************************/ 147 | void cwKeyer(void){ 148 | lastPaddle = 0; 149 | bool continue_loop = true; 150 | unsigned tmpKeyControl = 0; 151 | 152 | if( Iambic_Key ) { 153 | while(continue_loop) { 154 | switch (keyerState) { 155 | case IDLE: 156 | tmpKeyControl = update_PaddleLatch(0); 157 | if ( tmpKeyControl == DAH_L || tmpKeyControl == DIT_L || 158 | tmpKeyControl == (DAH_L | DIT_L) || (keyerControl & 0x03)) { 159 | update_PaddleLatch(1); 160 | keyerState = CHK_DIT; 161 | }else{ 162 | if (0 < cwTimeout && cwTimeout < millis()){ 163 | cwTimeout = 0; 164 | stopTx(); 165 | } 166 | continue_loop = false; 167 | } 168 | break; 169 | 170 | case CHK_DIT: 171 | if (keyerControl & DIT_L) { 172 | keyerControl |= DIT_PROC; 173 | ktimer = cwSpeed; 174 | keyerState = KEYED_PREP; 175 | }else{ 176 | keyerState = CHK_DAH; 177 | } 178 | break; 179 | 180 | case CHK_DAH: 181 | if (keyerControl & DAH_L) { 182 | ktimer = cwSpeed*3; 183 | keyerState = KEYED_PREP; 184 | }else{ 185 | keyerState = IDLE; 186 | } 187 | break; 188 | 189 | case KEYED_PREP: 190 | //modified KD8CEC 191 | if (!inTx){ 192 | //DelayTime Option 193 | active_delay(delayBeforeCWStartTime * 2); 194 | 195 | keyDown = 0; 196 | cwTimeout = millis() + cwDelayTime * 10; //+ CW_TIMEOUT; 197 | startTx(TX_CW); 198 | } 199 | ktimer += millis(); // set ktimer to interval end time 200 | keyerControl &= ~(DIT_L + DAH_L); // clear both paddle latch bits 201 | keyerState = KEYED; // next state 202 | 203 | cwKeydown(); 204 | break; 205 | 206 | case KEYED: 207 | if (millis() > ktimer) { // are we at end of key down ? 208 | cwKeyUp(); 209 | ktimer = millis() + cwSpeed; // inter-element time 210 | keyerState = INTER_ELEMENT; // next state 211 | }else if (keyerControl & IAMBICB) { 212 | update_PaddleLatch(1); // early paddle latch in Iambic B mode 213 | } 214 | break; 215 | 216 | case INTER_ELEMENT: 217 | // Insert time between dits/dahs 218 | update_PaddleLatch(1); // latch paddle state 219 | if (millis() > ktimer) { // are we at end of inter-space ? 220 | if (keyerControl & DIT_PROC) { // was it a dit or dah ? 221 | keyerControl &= ~(DIT_L + DIT_PROC); // clear two bits 222 | keyerState = CHK_DAH; // dit done, check for dah 223 | }else{ 224 | keyerControl &= ~(DAH_L); // clear dah latch 225 | keyerState = IDLE; // go idle 226 | } 227 | } 228 | break; 229 | } 230 | 231 | checkCAT(); 232 | } //end of while 233 | } 234 | else{ 235 | while(1){ 236 | if (update_PaddleLatch(0) == DIT_L) { 237 | // if we are here, it is only because the key is pressed 238 | if (!inTx){ 239 | //DelayTime Option 240 | active_delay(delayBeforeCWStartTime * 2); 241 | 242 | keyDown = 0; 243 | cwTimeout = millis() + cwDelayTime * 10; //+ CW_TIMEOUT; 244 | startTx(TX_CW); 245 | } 246 | cwKeydown(); 247 | 248 | while ( update_PaddleLatch(0) == DIT_L ) 249 | active_delay(1); 250 | 251 | cwKeyUp(); 252 | } 253 | else{ 254 | if (0 < cwTimeout && cwTimeout < millis()){ 255 | cwTimeout = 0; 256 | keyDown = 0; 257 | stopTx(); 258 | } 259 | //if (!cwTimeout) //removed by KD8CEC 260 | // return; 261 | // got back to the beginning of the loop, if no further activity happens on straight key 262 | // we will time out, and return out of this routine 263 | //delay(5); 264 | //delay_background(5, 3); //removed by KD8CEC 265 | //continue; //removed by KD8CEC 266 | return; //Tx stop control by Main Loop 267 | } 268 | 269 | checkCAT(); 270 | } //end of while 271 | } //end of elese 272 | } 273 | 274 | 275 | -------------------------------------------------------------------------------- /ubitx_menu.ino: -------------------------------------------------------------------------------- 1 | /** Menus 2 | * The Radio menus are accessed by tapping on the function button. 3 | * - The main loop() constantly looks for a button press and calls doMenu() when it detects 4 | * a function button press. 5 | * - As the encoder is rotated, at every 10th pulse, the next or the previous menu 6 | * item is displayed. Each menu item is controlled by it's own function. 7 | * - Eache menu function may be called to display itself 8 | * - Each of these menu routines is called with a button parameter. 9 | * - The btn flag denotes if the menu itme was clicked on or not. 10 | * - If the menu item is clicked on, then it is selected, 11 | * - If the menu item is NOT clicked on, then the menu's prompt is to be displayed 12 | */ 13 | 14 | 15 | /** A generic control to read variable values 16 | */ 17 | int getValueByKnob(int minimum, int maximum, int step_size, int initial, char* prefix, char *postfix) 18 | { 19 | int knob = 0; 20 | int knob_value; 21 | 22 | while (btnDown()) 23 | active_delay(100); 24 | 25 | active_delay(200); 26 | knob_value = initial; 27 | 28 | strcpy(b, prefix); 29 | itoa(knob_value, c, 10); 30 | strcat(b, c); 31 | strcat(b, postfix); 32 | printLine2(b); 33 | active_delay(300); 34 | 35 | while(!btnDown() && digitalRead(PTT) == HIGH){ 36 | 37 | knob = enc_read(); 38 | if (knob != 0){ 39 | if (knob_value > minimum && knob < 0) 40 | knob_value -= step_size; 41 | if (knob_value < maximum && knob > 0) 42 | knob_value += step_size; 43 | 44 | printLine2(prefix); 45 | itoa(knob_value, c, 10); 46 | strcpy(b, c); 47 | strcat(b, postfix); 48 | printLine1(b); 49 | } 50 | checkCAT(); 51 | } 52 | 53 | return knob_value; 54 | } 55 | 56 | //# Menu: 1 57 | 58 | int menuBand(int btn){ 59 | int knob = 0; 60 | int band; 61 | unsigned long offset; 62 | 63 | // band = frequency/1000000l; 64 | // offset = frequency % 1000000l; 65 | 66 | if (!btn){ 67 | printLine2("Band Select \x7E"); 68 | return; 69 | } 70 | 71 | printLine2("Band Select:"); 72 | //wait for the button menu select button to be lifted) 73 | while (btnDown()) 74 | active_delay(50); 75 | active_delay(50); 76 | ritDisable(); 77 | 78 | while(!btnDown()){ 79 | 80 | knob = enc_read(); 81 | if (knob != 0){ 82 | /* 83 | if (band > 3 && knob < 0) 84 | band--; 85 | if (band < 30 && knob > 0) 86 | band++; 87 | if (band > 10) 88 | isUSB = true; 89 | else 90 | isUSB = false; 91 | setFrequency(((unsigned long)band * 1000000l) + offset); */ 92 | if (knob < 0 && frequency > 3000000l) 93 | setFrequency(frequency - 200000l); 94 | if (knob > 0 && frequency < 30000000l) 95 | setFrequency(frequency + 200000l); 96 | if (frequency > 10000000l) 97 | isUSB = true; 98 | else 99 | isUSB = false; 100 | updateDisplay(); 101 | } 102 | checkCAT(); 103 | active_delay(20); 104 | } 105 | 106 | while(btnDown()) 107 | active_delay(50); 108 | active_delay(50); 109 | 110 | printLine2(""); 111 | updateDisplay(); 112 | menuOn = 0; 113 | } 114 | 115 | // Menu #2 116 | void menuRitToggle(int btn){ 117 | if (!btn){ 118 | if (ritOn == 1) 119 | printLine2("RIT On \x7E Off"); 120 | else 121 | printLine2("RIT Off \x7E On"); 122 | } 123 | else { 124 | if (ritOn == 0){ 125 | //enable RIT so the current frequency is used at transmit 126 | ritEnable(frequency); 127 | printLine2("RIT is On"); 128 | 129 | } 130 | else{ 131 | ritDisable(); 132 | printLine2("RIT is Off"); 133 | } 134 | menuOn = 0; 135 | active_delay(500); 136 | printLine2(""); 137 | updateDisplay(); 138 | } 139 | } 140 | 141 | 142 | //Menu #3 143 | void menuVfoToggle(int btn){ 144 | 145 | if (!btn){ 146 | if (vfoActive == VFO_A) 147 | printLine2("VFO A \x7E B"); 148 | else 149 | printLine2("VFO B \x7E A"); 150 | } 151 | else { 152 | if (vfoActive == VFO_B){ 153 | vfoB = frequency; 154 | isUsbVfoB = isUSB; 155 | EEPROM.put(VFO_B, frequency); 156 | if (isUsbVfoB) 157 | EEPROM.put(VFO_B_MODE, VFO_MODE_USB); 158 | else 159 | EEPROM.put(VFO_B_MODE, VFO_MODE_LSB); 160 | 161 | vfoActive = VFO_A; 162 | // printLine2("Selected VFO A "); 163 | frequency = vfoA; 164 | isUSB = isUsbVfoA; 165 | } 166 | else { 167 | vfoA = frequency; 168 | isUsbVfoA = isUSB; 169 | EEPROM.put(VFO_A, frequency); 170 | if (isUsbVfoA) 171 | EEPROM.put(VFO_A_MODE, VFO_MODE_USB); 172 | else 173 | EEPROM.put(VFO_A_MODE, VFO_MODE_LSB); 174 | 175 | vfoActive = VFO_B; 176 | // printLine2("Selected VFO B "); 177 | frequency = vfoB; 178 | isUSB = isUsbVfoB; 179 | } 180 | 181 | ritDisable(); 182 | setFrequency(frequency); 183 | updateDisplay(); 184 | printLine2(""); 185 | //exit the menu 186 | menuOn = 0; 187 | } 188 | } 189 | 190 | // Menu #4 191 | void menuSidebandToggle(int btn){ 192 | if (!btn){ 193 | if (isUSB == true) 194 | printLine2("USB \x7E LSB"); 195 | else 196 | printLine2("LSB \x7E USB"); 197 | } 198 | else { 199 | if (isUSB == true){ 200 | isUSB = false; 201 | printLine2("LSB Selected"); 202 | active_delay(500); 203 | printLine2(""); 204 | } 205 | else { 206 | isUSB = true; 207 | printLine2("USB Selected"); 208 | active_delay(500); 209 | printLine2(""); 210 | } 211 | //Added by KD8CEC 212 | if (vfoActive == VFO_B){ 213 | isUsbVfoB = isUSB; 214 | } 215 | else { 216 | isUsbVfoB = isUSB; 217 | } 218 | updateDisplay(); 219 | menuOn = 0; 220 | } 221 | } 222 | 223 | //Split communication using VFOA and VFOB by KD8CEC 224 | //Menu #5 225 | void menuSplitToggle(int btn){ 226 | if (!btn){ 227 | if (splitOn == 0) 228 | printLine2("Split Off \x7E On"); 229 | else 230 | printLine2("Split On \x7E Off"); 231 | } 232 | else { 233 | if (splitOn == 1){ 234 | splitOn = 0; 235 | printLine2("Split ON"); 236 | } 237 | else { 238 | splitOn = 1; 239 | if (ritOn == 1) 240 | ritOn = 0; 241 | printLine2("Split Off"); 242 | } 243 | active_delay(500); 244 | printLine2(""); 245 | updateDisplay(); 246 | menuOn = 0; 247 | } 248 | } 249 | 250 | int menuCWSpeed(int btn){ 251 | int knob = 0; 252 | int wpm; 253 | 254 | wpm = 1200/cwSpeed; 255 | 256 | if (!btn){ 257 | strcpy(b, "CW: "); 258 | itoa(wpm,c, 10); 259 | strcat(b, c); 260 | strcat(b, " WPM \x7E"); 261 | printLine2(b); 262 | return; 263 | } 264 | 265 | /* 266 | printLine1("Press FN to Set"); 267 | strcpy(b, "5:CW>"); 268 | itoa(wpm,c, 10); 269 | strcat(b, c); 270 | strcat(b, " WPM"); 271 | printLine2(b); 272 | active_delay(300); 273 | 274 | while(!btnDown() && digitalRead(PTT) == HIGH){ 275 | 276 | knob = enc_read(); 277 | if (knob != 0){ 278 | if (wpm > 3 && knob < 0) 279 | wpm--; 280 | if (wpm < 50 && knob > 0) 281 | wpm++; 282 | 283 | strcpy(b, "5:CW>"); 284 | itoa(wpm,c, 10); 285 | strcat(b, c); 286 | strcat(b, " WPM"); 287 | printLine2(b); 288 | } 289 | //abort if this button is down 290 | if (btnDown()) 291 | //re-enable the clock1 and clock 2 292 | break; 293 | checkCAT(); 294 | } 295 | */ 296 | wpm = getValueByKnob(1, 100, 1, wpm, "CW: ", " WPM>"); 297 | 298 | printLine2("CW Speed set!"); 299 | cwSpeed = 1200/wpm; 300 | EEPROM.put(CW_SPEED, cwSpeed); 301 | active_delay(500); 302 | 303 | printLine2(""); 304 | updateDisplay(); 305 | menuOn = 0; 306 | } 307 | 308 | void menuExit(int btn){ 309 | 310 | if (!btn){ 311 | printLine2("Exit Menu \x7E"); 312 | } 313 | else{ 314 | printLine2("Exiting..."); 315 | active_delay(500); 316 | printLine2(""); 317 | updateDisplay(); 318 | menuOn = 0; 319 | } 320 | } 321 | 322 | /** 323 | * The calibration routines are not normally shown in the menu as they are rarely used 324 | * They can be enabled by choosing this menu option 325 | */ 326 | int menuSetup(int btn){ 327 | if (!btn){ 328 | if (!modeCalibrate) 329 | printLine2("Settings \x7E"); 330 | else 331 | printLine2("Settings \x7E Off"); 332 | }else { 333 | if (!modeCalibrate){ 334 | modeCalibrate = true; 335 | printLine2("Settings On"); 336 | } 337 | else { 338 | modeCalibrate = false; 339 | printLine2("Settings Off"); 340 | } 341 | 342 | while(btnDown()) 343 | active_delay(100); 344 | active_delay(500); 345 | printLine2(""); 346 | return 10; 347 | } 348 | return 0; 349 | } 350 | 351 | //this is used by the si5351 routines in the ubitx_5351 file 352 | extern int32_t calibration; 353 | extern uint32_t si5351bx_vcoa; 354 | 355 | int calibrateClock(){ 356 | int knob = 0; 357 | int32_t prev_calibration; 358 | 359 | 360 | //keep clear of any previous button press 361 | while (btnDown()) 362 | active_delay(100); 363 | active_delay(100); 364 | 365 | digitalWrite(TX_LPF_A, 0); 366 | digitalWrite(TX_LPF_B, 0); 367 | digitalWrite(TX_LPF_C, 0); 368 | 369 | prev_calibration = calibration; 370 | calibration = 0; 371 | 372 | isUSB = true; 373 | 374 | //turn off the second local oscillator and the bfo 375 | si5351_set_calibration(calibration); 376 | startTx(TX_CW); 377 | si5351bx_setfreq(2, 10000000l); 378 | 379 | strcpy(b, "#1 10 MHz cal:"); 380 | ltoa(calibration/8750, c, 10); 381 | strcat(b, c); 382 | printLine2(b); 383 | 384 | while (!btnDown()) 385 | { 386 | 387 | if (digitalRead(PTT) == LOW && !keyDown) 388 | cwKeydown(); 389 | if (digitalRead(PTT) == HIGH && keyDown) 390 | cwKeyUp(); 391 | 392 | knob = enc_read(); 393 | 394 | if (knob > 0) 395 | calibration += 875; 396 | else if (knob < 0) 397 | calibration -= 875; 398 | else 399 | continue; //don't update the frequency or the display 400 | 401 | si5351_set_calibration(calibration); 402 | si5351bx_setfreq(2, 10000000l); 403 | strcpy(b, "#1 10 MHz cal:"); 404 | ltoa(calibration/8750, c, 10); 405 | strcat(b, c); 406 | printLine2(b); 407 | } 408 | 409 | cwTimeout = 0; 410 | keyDown = 0; 411 | stopTx(); 412 | 413 | printLine2("Calibration set!"); 414 | EEPROM.put(MASTER_CAL, calibration); 415 | initOscillators(); 416 | setFrequency(frequency); 417 | updateDisplay(); 418 | 419 | while(btnDown()) 420 | active_delay(50); 421 | active_delay(100); 422 | } 423 | 424 | int menuSetupCalibration(int btn){ 425 | int knob = 0; 426 | int32_t prev_calibration; 427 | 428 | if (!btn){ 429 | printLine2("Setup:Calibrate\x7E"); 430 | return 0; 431 | } 432 | 433 | printLine1("Press PTT & tune"); 434 | printLine2("to exactly 10 MHz"); 435 | active_delay(2000); 436 | calibrateClock(); 437 | } 438 | 439 | void printCarrierFreq(unsigned long freq){ 440 | 441 | memset(c, 0, sizeof(c)); 442 | memset(b, 0, sizeof(b)); 443 | 444 | ultoa(freq, b, DEC); 445 | 446 | strncat(c, b, 2); 447 | strcat(c, "."); 448 | strncat(c, &b[2], 3); 449 | strcat(c, "."); 450 | strncat(c, &b[5], 1); 451 | printLine2(c); 452 | } 453 | 454 | void menuSetupCarrier(int btn){ 455 | int knob = 0; 456 | unsigned long prevCarrier; 457 | 458 | if (!btn){ 459 | printLine2("Setup:BFO \x7E"); 460 | return; 461 | } 462 | 463 | prevCarrier = usbCarrier; 464 | printLine1("Tune to best Signal"); 465 | printLine2("Press to confirm. "); 466 | active_delay(1000); 467 | 468 | usbCarrier = 11053000l; 469 | si5351bx_setfreq(0, usbCarrier); 470 | printCarrierFreq(usbCarrier); 471 | 472 | //disable all clock 1 and clock 2 473 | while (!btnDown()){ 474 | knob = enc_read(); 475 | 476 | if (knob > 0) 477 | usbCarrier -= 50; 478 | else if (knob < 0) 479 | usbCarrier += 50; 480 | else 481 | continue; //don't update the frequency or the display 482 | 483 | si5351bx_setfreq(0, usbCarrier); 484 | printCarrierFreq(usbCarrier); 485 | 486 | active_delay(100); 487 | } 488 | 489 | printLine2("Carrier set! "); 490 | EEPROM.put(USB_CAL, usbCarrier); 491 | active_delay(1000); 492 | 493 | si5351bx_setfreq(0, usbCarrier); 494 | setFrequency(frequency); 495 | updateDisplay(); 496 | printLine2(""); 497 | menuOn = 0; 498 | } 499 | 500 | void menuSetupCwTone(int btn){ 501 | int knob = 0; 502 | int prev_sideTone; 503 | 504 | if (!btn){ 505 | printLine2("Setup:CW Tone \x7E"); 506 | return; 507 | } 508 | 509 | prev_sideTone = sideTone; 510 | printLine1("Tune CW tone"); 511 | printLine2("PTT to confirm. "); 512 | active_delay(1000); 513 | tone(CW_TONE, sideTone); 514 | 515 | //disable all clock 1 and clock 2 516 | while (digitalRead(PTT) == HIGH && !btnDown()) 517 | { 518 | knob = enc_read(); 519 | 520 | if (knob > 0 && sideTone < 2000) 521 | sideTone += 10; 522 | else if (knob < 0 && sideTone > 100 ) 523 | sideTone -= 10; 524 | else 525 | continue; //don't update the frequency or the display 526 | 527 | tone(CW_TONE, sideTone); 528 | itoa(sideTone, b, 10); 529 | printLine2(b); 530 | 531 | checkCAT(); 532 | active_delay(20); 533 | } 534 | noTone(CW_TONE); 535 | //save the setting 536 | if (digitalRead(PTT) == LOW){ 537 | printLine2("Sidetone set! "); 538 | EEPROM.put(CW_SIDETONE, sideTone); 539 | active_delay(2000); 540 | } 541 | else 542 | sideTone = prev_sideTone; 543 | 544 | printLine2(""); 545 | updateDisplay(); 546 | menuOn = 0; 547 | } 548 | 549 | void menuSetupCwDelay(int btn){ 550 | int knob = 0; 551 | int prev_cw_delay; 552 | 553 | if (!btn){ 554 | printLine2("Setup:CW Delay \x7E"); 555 | return; 556 | } 557 | 558 | active_delay(500); 559 | prev_cw_delay = cwDelayTime; 560 | cwDelayTime = getValueByKnob(10, 1000, 50, cwDelayTime, "CW Delay>", " msec"); 561 | 562 | printLine1("CW Delay Set!"); 563 | printLine2(""); 564 | active_delay(500); 565 | updateDisplay(); 566 | menuOn = 0; 567 | } 568 | 569 | void menuSetupKeyer(int btn){ 570 | int tmp_key, knob; 571 | 572 | if (!btn){ 573 | if (!Iambic_Key) 574 | printLine2("Setup:CW(Hand)\x7E"); 575 | else if (keyerControl & IAMBICB) 576 | printLine2("Setup:CW(IambA)\x7E"); 577 | else 578 | printLine2("Setup:CW(IambB)\x7E"); 579 | return; 580 | } 581 | 582 | active_delay(500); 583 | 584 | if (!Iambic_Key) 585 | tmp_key = 0; //hand key 586 | else if (keyerControl & IAMBICB) 587 | tmp_key = 2; //Iambic B 588 | else 589 | tmp_key = 1; 590 | 591 | while (!btnDown()) 592 | { 593 | knob = enc_read(); 594 | if (knob < 0 && tmp_key > 0) 595 | tmp_key--; 596 | if (knob > 0) 597 | tmp_key++; 598 | 599 | if (tmp_key > 2) 600 | tmp_key = 0; 601 | 602 | if (tmp_key == 0) 603 | printLine1("Hand Key?"); 604 | else if (tmp_key == 1) 605 | printLine1("Iambic A?"); 606 | else if (tmp_key == 2) 607 | printLine1("Iambic B?"); 608 | } 609 | 610 | active_delay(500); 611 | if (tmp_key == 0) 612 | Iambic_Key = false; 613 | else if (tmp_key == 1){ 614 | Iambic_Key = true; 615 | keyerControl &= ~IAMBICB; 616 | } 617 | else if (tmp_key == 2){ 618 | Iambic_Key = true; 619 | keyerControl |= IAMBICB; 620 | } 621 | 622 | EEPROM.put(CW_KEY_TYPE, tmp_key); 623 | 624 | printLine1("Keyer Set!"); 625 | active_delay(600); 626 | printLine1(""); 627 | 628 | //Added KD8CEC 629 | printLine2(""); 630 | updateDisplay(); 631 | menuOn = 0; 632 | } 633 | 634 | void menuReadADC(int btn){ 635 | int adc; 636 | 637 | if (!btn){ 638 | printLine2("6:Setup>Read ADC>"); 639 | return; 640 | } 641 | delay(500); 642 | 643 | while (!btnDown()){ 644 | adc = analogRead(ANALOG_KEYER); 645 | itoa(adc, b, 10); 646 | printLine1(b); 647 | } 648 | 649 | printLine1(""); 650 | updateDisplay(); 651 | } 652 | 653 | void doMenu(){ 654 | int select=0, i,btnState; 655 | 656 | //wait for the button to be raised up 657 | while(btnDown()) 658 | active_delay(50); 659 | active_delay(50); //debounce 660 | 661 | menuOn = 2; 662 | 663 | while (menuOn){ 664 | i = enc_read(); 665 | btnState = btnDown(); 666 | 667 | if (i > 0){ 668 | if (modeCalibrate && select + i < 150) 669 | select += i; 670 | if (!modeCalibrate && select + i < 80) 671 | select += i; 672 | } 673 | if (i < 0 && select - i >= 0) 674 | select += i; //caught ya, i is already -ve here, so you add it 675 | 676 | if (select < 10) 677 | menuBand(btnState); 678 | else if (select < 20) 679 | menuRitToggle(btnState); 680 | else if (select < 30) 681 | menuVfoToggle(btnState); 682 | else if (select < 40) 683 | menuSidebandToggle(btnState); 684 | else if (select < 50) 685 | menuSplitToggle(btnState); 686 | else if (select < 60) 687 | menuCWSpeed(btnState); 688 | else if (select < 70) 689 | select += menuSetup(btnState); 690 | else if (select < 80 && !modeCalibrate) 691 | menuExit(btnState); 692 | else if (select < 90 && modeCalibrate) 693 | menuSetupCalibration(btnState); //crystal 694 | else if (select < 100 && modeCalibrate) 695 | menuSetupCarrier(btnState); //lsb 696 | else if (select < 110 && modeCalibrate) 697 | menuSetupCwTone(btnState); 698 | else if (select < 120 && modeCalibrate) 699 | menuSetupCwDelay(btnState); 700 | else if (select < 130 && modeCalibrate) 701 | menuReadADC(btnState); 702 | else if (select < 140 && modeCalibrate) 703 | menuSetupKeyer(btnState); 704 | else 705 | menuExit(btnState); 706 | } 707 | 708 | //debounce the button 709 | while(btnDown()) 710 | active_delay(50); 711 | active_delay(50); 712 | 713 | checkCAT(); 714 | } 715 | 716 | -------------------------------------------------------------------------------- /ubitx_si5351.ino: -------------------------------------------------------------------------------- 1 | // ************* SI5315 routines - tks Jerry Gaffke, KE7ER *********************** 2 | 3 | // An minimalist standalone set of Si5351 routines. 4 | // VCOA is fixed at 875mhz, VCOB not used. 5 | // The output msynth dividers are used to generate 3 independent clocks 6 | // with 1hz resolution to any frequency between 4khz and 109mhz. 7 | 8 | // Usage: 9 | // Call si5351bx_init() once at startup with no args; 10 | // Call si5351bx_setfreq(clknum, freq) each time one of the 11 | // three output CLK pins is to be updated to a new frequency. 12 | // A freq of 0 serves to shut down that output clock. 13 | 14 | // The global variable si5351bx_vcoa starts out equal to the nominal VCOA 15 | // frequency of 25mhz*35 = 875000000 Hz. To correct for 25mhz crystal errors, 16 | // the user can adjust this value. The vco frequency will not change but 17 | // the number used for the (a+b/c) output msynth calculations is affected. 18 | // Example: We call for a 5mhz signal, but it measures to be 5.001mhz. 19 | // So the actual vcoa frequency is 875mhz*5.001/5.000 = 875175000 Hz, 20 | // To correct for this error: si5351bx_vcoa=875175000; 21 | 22 | // Most users will never need to generate clocks below 500khz. 23 | // But it is possible to do so by loading a value between 0 and 7 into 24 | // the global variable si5351bx_rdiv, be sure to return it to a value of 0 25 | // before setting some other CLK output pin. The affected clock will be 26 | // divided down by a power of two defined by 2**si5351_rdiv 27 | // A value of zero gives a divide factor of 1, a value of 7 divides by 128. 28 | // This lightweight method is a reasonable compromise for a seldom used feature. 29 | 30 | 31 | #define BB0(x) ((uint8_t)x) // Bust int32 into Bytes 32 | #define BB1(x) ((uint8_t)(x>>8)) 33 | #define BB2(x) ((uint8_t)(x>>16)) 34 | 35 | #define SI5351BX_ADDR 0x60 // I2C address of Si5351 (typical) 36 | #define SI5351BX_XTALPF 2 // 1:6pf 2:8pf 3:10pf 37 | 38 | // If using 27mhz crystal, set XTAL=27000000, MSA=33. Then vco=891mhz 39 | #define SI5351BX_XTAL 25000000 // Crystal freq in Hz 40 | #define SI5351BX_MSA 35 // VCOA is at 25mhz*35 = 875mhz 41 | 42 | // User program may have reason to poke new values into these 3 RAM variables 43 | uint32_t si5351bx_vcoa = (SI5351BX_XTAL*SI5351BX_MSA); // 25mhzXtal calibrate 44 | uint8_t si5351bx_rdiv = 0; // 0-7, CLK pin sees fout/(2**rdiv) 45 | uint8_t si5351bx_drive[3] = {3, 3, 3}; // 0=2ma 1=4ma 2=6ma 3=8ma for CLK 0,1,2 46 | uint8_t si5351bx_clken = 0xFF; // Private, all CLK output drivers off 47 | int32_t calibration = 0; 48 | 49 | void i2cWrite(uint8_t reg, uint8_t val) { // write reg via i2c 50 | Wire.beginTransmission(SI5351BX_ADDR); 51 | Wire.write(reg); 52 | Wire.write(val); 53 | Wire.endTransmission(); 54 | } 55 | 56 | void i2cWriten(uint8_t reg, uint8_t *vals, uint8_t vcnt) { // write array 57 | Wire.beginTransmission(SI5351BX_ADDR); 58 | Wire.write(reg); 59 | while (vcnt--) Wire.write(*vals++); 60 | Wire.endTransmission(); 61 | } 62 | 63 | 64 | void si5351bx_init() { // Call once at power-up, start PLLA 65 | uint8_t reg; uint32_t msxp1; 66 | Wire.begin(); 67 | i2cWrite(149, 0); // SpreadSpectrum off 68 | i2cWrite(3, si5351bx_clken); // Disable all CLK output drivers 69 | i2cWrite(183, SI5351BX_XTALPF << 6); // Set 25mhz crystal load capacitance 70 | msxp1 = 128 * SI5351BX_MSA - 512; // and msxp2=0, msxp3=1, not fractional 71 | uint8_t vals[8] = {0, 1, BB2(msxp1), BB1(msxp1), BB0(msxp1), 0, 0, 0}; 72 | i2cWriten(26, vals, 8); // Write to 8 PLLA msynth regs 73 | i2cWrite(177, 0x20); // Reset PLLA (0x80 resets PLLB) 74 | // for (reg=16; reg<=23; reg++) i2cWrite(reg, 0x80); // Powerdown CLK's 75 | // i2cWrite(187, 0); // No fannout of clkin, xtal, ms0, ms4 76 | 77 | //initializing the ppl2 as well 78 | i2cWriten(34, vals, 8); // Write to 8 PLLA msynth regs 79 | i2cWrite(177, 0xa0); // Reset PLLA & PPLB (0x80 resets PLLB) 80 | 81 | } 82 | 83 | void si5351bx_setfreq(uint8_t clknum, uint32_t fout) { // Set a CLK to fout Hz 84 | uint32_t msa, msb, msc, msxp1, msxp2, msxp3p2top; 85 | if ((fout < 500000) || (fout > 109000000)) // If clock freq out of range 86 | si5351bx_clken |= 1 << clknum; // shut down the clock 87 | else { 88 | msa = si5351bx_vcoa / fout; // Integer part of vco/fout 89 | msb = si5351bx_vcoa % fout; // Fractional part of vco/fout 90 | msc = fout; // Divide by 2 till fits in reg 91 | while (msc & 0xfff00000) { 92 | msb = msb >> 1; 93 | msc = msc >> 1; 94 | } 95 | msxp1 = (128 * msa + 128 * msb / msc - 512) | (((uint32_t)si5351bx_rdiv) << 20); 96 | msxp2 = 128 * msb - 128 * msb / msc * msc; // msxp3 == msc; 97 | msxp3p2top = (((msc & 0x0F0000) << 4) | msxp2); // 2 top nibbles 98 | uint8_t vals[8] = { BB1(msc), BB0(msc), BB2(msxp1), BB1(msxp1), 99 | BB0(msxp1), BB2(msxp3p2top), BB1(msxp2), BB0(msxp2) 100 | }; 101 | i2cWriten(42 + (clknum * 8), vals, 8); // Write to 8 msynth regs 102 | // if (clknum == 1) //PLLB | MS src | drive current 103 | // i2cWrite(16 + clknum, 0x20 | 0x0C | si5351bx_drive[clknum]); // use local msynth 104 | // else 105 | i2cWrite(16 + clknum, 0x0C | si5351bx_drive[clknum]); // use local msynth 106 | 107 | si5351bx_clken &= ~(1 << clknum); // Clear bit to enable clock 108 | } 109 | i2cWrite(3, si5351bx_clken); // Enable/disable clock 110 | } 111 | 112 | void si5351_set_calibration(int32_t cal){ 113 | si5351bx_vcoa = (SI5351BX_XTAL * SI5351BX_MSA) + cal; // apply the calibration correction factor 114 | si5351bx_setfreq(0, usbCarrier); 115 | } 116 | 117 | void initOscillators(){ 118 | //initialize the SI5351 119 | si5351bx_init(); 120 | si5351bx_vcoa = (SI5351BX_XTAL * SI5351BX_MSA) + calibration; // apply the calibration correction factor 121 | si5351bx_setfreq(0, usbCarrier); 122 | } 123 | 124 | 125 | 126 | -------------------------------------------------------------------------------- /ubitx_ui.ino: -------------------------------------------------------------------------------- 1 | /** 2 | * The user interface of the ubitx consists of the encoder, the push-button on top of it 3 | * and the 16x2 LCD display. 4 | * The upper line of the display is constantly used to display frequency and status 5 | * of the radio. Occasionally, it is used to provide a two-line information that is 6 | * quickly cleared up. 7 | */ 8 | 9 | //returns true if the button is pressed 10 | int btnDown(){ 11 | if (digitalRead(FBUTTON) == HIGH) 12 | return 0; 13 | else 14 | return 1; 15 | } 16 | 17 | /** 18 | * Meter (not used in this build for anything) 19 | * the meter is drawn using special characters. Each character is composed of 5 x 8 matrix. 20 | * The s_meter array holds the definition of the these characters. 21 | * each line of the array is is one character such that 5 bits of every byte 22 | * makes up one line of pixels of the that character (only 5 bits are used) 23 | * The current reading of the meter is assembled in the string called meter 24 | */ 25 | 26 | 27 | char meter[17]; 28 | 29 | const byte PROGMEM s_meter_bitmap[] = { 30 | B00000,B00000,B00000,B00000,B00000,B00100,B00100,B11011, 31 | B10000,B10000,B10000,B10000,B10100,B10100,B10100,B11011, 32 | B01000,B01000,B01000,B01000,B01100,B01100,B01100,B11011, 33 | B00100,B00100,B00100,B00100,B00100,B00100,B00100,B11011, 34 | B00010,B00010,B00010,B00010,B00110,B00110,B00110,B11011, 35 | B00001,B00001,B00001,B00001,B00101,B00101,B00101,B11011, 36 | B10000,B11000,B11100,B11110,B11100,B11000,B10000,B00000, 37 | B00001,B00011,B00111,B01111,B00111,B00011,B00001,B00000 38 | }; 39 | 40 | 41 | 42 | // initializes the custom characters 43 | // we start from char 1 as char 0 terminates the string! 44 | void initMeter(){ 45 | lcd.createChar(1, s_meter_bitmap); 46 | lcd.createChar(2, s_meter_bitmap + 8); 47 | lcd.createChar(3, s_meter_bitmap + 16); 48 | lcd.createChar(4, s_meter_bitmap + 24); 49 | lcd.createChar(5, s_meter_bitmap + 32); 50 | lcd.createChar(6, s_meter_bitmap + 40); 51 | lcd.createChar(0, s_meter_bitmap + 48); 52 | lcd.createChar(7, s_meter_bitmap + 56); 53 | } 54 | 55 | 56 | /** 57 | * The meter is drawn with special characters. 58 | * character 1 is used to simple draw the blocks of the scale of the meter 59 | * characters 2 to 6 are used to draw the needle in positions 1 to within the block 60 | * This displays a meter from 0 to 100, -1 displays nothing 61 | */ 62 | 63 | void drawMeter(int8_t needle){ 64 | int16_t best, i, s; 65 | 66 | if (needle < 0) 67 | return; 68 | 69 | s = (needle * 4)/10; 70 | for (i = 0; i < 8; i++){ 71 | if (s >= 5) 72 | meter[i] = 1; 73 | else if (s >= 0) 74 | meter[i] = 2 + s; 75 | else 76 | meter[i] = 1; 77 | s = s - 5; 78 | } 79 | if (needle >= 40) 80 | meter[i-1] = 6; 81 | meter[i] = 0; 82 | } 83 | 84 | // The generic routine to display one line on the LCD 85 | void printLine(char linenmbr, char *c) { 86 | if (strcmp(c, printBuff[linenmbr])) { // only refresh the display when there was a change 87 | lcd.setCursor(0, linenmbr); // place the cursor at the beginning of the selected line 88 | lcd.print(c); 89 | strcpy(printBuff[linenmbr], c); 90 | 91 | for (byte i = strlen(c); i < 16; i++) { // add white spaces until the end of the 16 characters line is reached 92 | lcd.print(' '); 93 | } 94 | } 95 | } 96 | 97 | 98 | // short cut to print to the first line 99 | void printLine1(char *c){ 100 | printLine(1,c); 101 | } 102 | // short cut to print to the first line 103 | void printLine2(char *c){ 104 | printLine(0,c); 105 | } 106 | 107 | // this builds up the top line of the display with frequency and mode 108 | void updateDisplay() { 109 | // tks Jack Purdum W8TEE 110 | // replaced fsprint commmands by str commands for code size reduction 111 | 112 | memset(c, 0, sizeof(c)); 113 | memset(b, 0, sizeof(b)); 114 | 115 | ultoa(frequency, b, DEC); 116 | 117 | if (inTx){ 118 | if (cwTimeout > 0) 119 | strcpy(c, " CW:"); 120 | else 121 | strcpy(c, " TX:"); 122 | } 123 | else { 124 | if (ritOn) 125 | strcpy(c, "RIT "); 126 | else { 127 | if (isUSB) 128 | strcpy(c, "USB "); 129 | else 130 | strcpy(c, "LSB "); 131 | } 132 | if (vfoActive == VFO_A) // VFO A is active 133 | strcat(c, "A:"); 134 | else 135 | strcat(c, "B:"); 136 | } 137 | 138 | 139 | 140 | //one mhz digit if less than 10 M, two digits if more 141 | if (frequency < 10000000l){ 142 | c[6] = ' '; 143 | c[7] = b[0]; 144 | strcat(c, "."); 145 | strncat(c, &b[1], 3); 146 | strcat(c, "."); 147 | strncat(c, &b[4], 3); 148 | } 149 | else { 150 | strncat(c, b, 2); 151 | strcat(c, "."); 152 | strncat(c, &b[2], 3); 153 | strcat(c, "."); 154 | strncat(c, &b[5], 3); 155 | } 156 | 157 | if (inTx) 158 | strcat(c, " TX"); 159 | printLine(1, c); 160 | 161 | /* 162 | //now, the second line 163 | memset(c, 0, sizeof(c)); 164 | memset(b, 0, sizeof(b)); 165 | 166 | if (inTx) 167 | strcat(c, "TX "); 168 | else if (ritOn) 169 | strcpy(c, "RIT"); 170 | 171 | strcpy(c, " \xff"); 172 | drawMeter(meter_reading); 173 | strcat(c, meter); 174 | strcat(c, "\xff"); 175 | printLine2(c);*/ 176 | } 177 | 178 | int enc_prev_state = 3; 179 | 180 | /** 181 | * The A7 And A6 are purely analog lines on the Arduino Nano 182 | * These need to be pulled up externally using two 10 K resistors 183 | * 184 | * There are excellent pages on the Internet about how these encoders work 185 | * and how they should be used. We have elected to use the simplest way 186 | * to use these encoders without the complexity of interrupts etc to 187 | * keep it understandable. 188 | * 189 | * The enc_state returns a two-bit number such that each bit reflects the current 190 | * value of each of the two phases of the encoder 191 | * 192 | * The enc_read returns the number of net pulses counted over 50 msecs. 193 | * If the puluses are -ve, they were anti-clockwise, if they are +ve, the 194 | * were in the clockwise directions. Higher the pulses, greater the speed 195 | * at which the enccoder was spun 196 | */ 197 | 198 | byte enc_state (void) { 199 | return (analogRead(ENC_A) > 500 ? 1 : 0) + (analogRead(ENC_B) > 500 ? 2: 0); 200 | } 201 | 202 | int enc_read(void) { 203 | int result = 0; 204 | byte newState; 205 | int enc_speed = 0; 206 | 207 | long stop_by = millis() + 50; 208 | 209 | while (millis() < stop_by) { // check if the previous state was stable 210 | newState = enc_state(); // Get current state 211 | 212 | if (newState != enc_prev_state) 213 | delay (1); 214 | 215 | if (enc_state() != newState || newState == enc_prev_state) 216 | continue; 217 | //these transitions point to the encoder being rotated anti-clockwise 218 | if ((enc_prev_state == 0 && newState == 2) || 219 | (enc_prev_state == 2 && newState == 3) || 220 | (enc_prev_state == 3 && newState == 1) || 221 | (enc_prev_state == 1 && newState == 0)){ 222 | result--; 223 | } 224 | //these transitions point o the enccoder being rotated clockwise 225 | if ((enc_prev_state == 0 && newState == 1) || 226 | (enc_prev_state == 1 && newState == 3) || 227 | (enc_prev_state == 3 && newState == 2) || 228 | (enc_prev_state == 2 && newState == 0)){ 229 | result++; 230 | } 231 | enc_prev_state = newState; // Record state for next pulse interpretation 232 | enc_speed++; 233 | active_delay(1); 234 | } 235 | return(result); 236 | } 237 | 238 | 239 | -------------------------------------------------------------------------------- /ubitx_v5.1_code.ino: -------------------------------------------------------------------------------- 1 | /** 2 | * This source file is under General Public License version 3. 3 | * 4 | * This verision uses a built-in Si5351 library 5 | * Most source code are meant to be understood by the compilers and the computers. 6 | * Code that has to be hackable needs to be well understood and properly documented. 7 | * Donald Knuth coined the term Literate Programming to indicate code that is written be 8 | * easily read and understood. 9 | * 10 | * The Raduino is a small board that includes the Arduin Nano, a 16x2 LCD display and 11 | * an Si5351a frequency synthesizer. This board is manufactured by Paradigm Ecomm Pvt Ltd 12 | * 13 | * To learn more about Arduino you may visit www.arduino.cc. 14 | * 15 | * The Arduino works by starts executing the code in a function called setup() and then it 16 | * repeatedly keeps calling loop() forever. All the initialization code is kept in setup() 17 | * and code to continuously sense the tuning knob, the function button, transmit/receive, 18 | * etc is all in the loop() function. If you wish to study the code top down, then scroll 19 | * to the bottom of this file and read your way up. 20 | * 21 | * Below are the libraries to be included for building the Raduino 22 | * The EEPROM library is used to store settings like the frequency memory, caliberation data, 23 | * callsign etc . 24 | * 25 | * The main chip which generates upto three oscillators of various frequencies in the 26 | * Raduino is the Si5351a. To learn more about Si5351a you can download the datasheet 27 | * from www.silabs.com although, strictly speaking it is not a requirment to understand this code. 28 | * Instead, you can look up the Si5351 library written by xxx, yyy. You can download and 29 | * install it from www.url.com to complile this file. 30 | * The Wire.h library is used to talk to the Si5351 and we also declare an instance of 31 | * Si5351 object to control the clocks. 32 | */ 33 | #include 34 | #include 35 | 36 | /** 37 | The main chip which generates upto three oscillators of various frequencies in the 38 | Raduino is the Si5351a. To learn more about Si5351a you can download the datasheet 39 | from www.silabs.com although, strictly speaking it is not a requirment to understand this code. 40 | 41 | We no longer use the standard SI5351 library because of its huge overhead due to many unused 42 | features consuming a lot of program space. Instead of depending on an external library we now use 43 | Jerry Gaffke's, KE7ER, lightweight standalone mimimalist "si5351bx" routines (see further down the 44 | code). Here are some defines and declarations used by Jerry's routines: 45 | */ 46 | 47 | 48 | /** 49 | * We need to carefully pick assignment of pin for various purposes. 50 | * There are two sets of completely programmable pins on the Raduino. 51 | * First, on the top of the board, in line with the LCD connector is an 8-pin connector 52 | * that is largely meant for analog inputs and front-panel control. It has a regulated 5v output, 53 | * ground and six pins. Each of these six pins can be individually programmed 54 | * either as an analog input, a digital input or a digital output. 55 | * The pins are assigned as follows (left to right, display facing you): 56 | * Pin 1 (Violet), A7, SPARE 57 | * Pin 2 (Blue), A6, KEYER (DATA) 58 | * Pin 3 (Green), +5v 59 | * Pin 4 (Yellow), Gnd 60 | * Pin 5 (Orange), A3, PTT 61 | * Pin 6 (Red), A2, F BUTTON 62 | * Pin 7 (Brown), A1, ENC B 63 | * Pin 8 (Black), A0, ENC A 64 | *Note: A5, A4 are wired to the Si5351 as I2C interface 65 | * * 66 | * Though, this can be assigned anyway, for this application of the Arduino, we will make the following 67 | * assignment 68 | * A2 will connect to the PTT line, which is the usually a part of the mic connector 69 | * A3 is connected to a push button that can momentarily ground this line. This will be used for RIT/Bandswitching, etc. 70 | * A6 is to implement a keyer, it is reserved and not yet implemented 71 | * A7 is connected to a center pin of good quality 100K or 10K linear potentiometer with the two other ends connected to 72 | * ground and +5v lines available on the connector. This implments the tuning mechanism 73 | */ 74 | 75 | #define ENC_A (A0) 76 | #define ENC_B (A1) 77 | #define FBUTTON (A2) 78 | #define PTT (A3) 79 | #define ANALOG_KEYER (A6) 80 | #define ANALOG_SPARE (A7) 81 | 82 | /** 83 | * The Raduino board is the size of a standard 16x2 LCD panel. It has three connectors: 84 | * 85 | * First, is an 8 pin connector that provides +5v, GND and six analog input pins that can also be 86 | * configured to be used as digital input or output pins. These are referred to as A0,A1,A2, 87 | * A3,A6 and A7 pins. The A4 and A5 pins are missing from this connector as they are used to 88 | * talk to the Si5351 over I2C protocol. 89 | * 90 | * Second is a 16 pin LCD connector. This connector is meant specifically for the standard 16x2 91 | * LCD display in 4 bit mode. The 4 bit mode requires 4 data lines and two control lines to work: 92 | * Lines used are : RESET, ENABLE, D4, D5, D6, D7 93 | * We include the library and declare the configuration of the LCD panel too 94 | */ 95 | 96 | #include 97 | LiquidCrystal lcd(8,9,10,11,12,13); 98 | 99 | /** 100 | * The Arduino, unlike C/C++ on a regular computer with gigabytes of RAM, has very little memory. 101 | * We have to be very careful with variables that are declared inside the functions as they are 102 | * created in a memory region called the stack. The stack has just a few bytes of space on the Arduino 103 | * if you declare large strings inside functions, they can easily exceed the capacity of the stack 104 | * and mess up your programs. 105 | * We circumvent this by declaring a few global buffers as kitchen counters where we can 106 | * slice and dice our strings. These strings are mostly used to control the display or handle 107 | * the input and output from the USB port. We must keep a count of the bytes used while reading 108 | * the serial port as we can easily run out of buffer space. This is done in the serial_in_count variable. 109 | */ 110 | char c[30], b[30]; 111 | char printBuff[2][31]; //mirrors what is showing on the two lines of the display 112 | int count = 0; //to generally count ticks, loops, etc 113 | 114 | /** 115 | * 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. 116 | * This assignment is as follows : 117 | * Pin 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 118 | * GND +5V CLK0 GND GND CLK1 GND GND CLK2 GND D2 D3 D4 D5 D6 D7 119 | * These too are flexible with what you may do with them, for the Raduino, we use them to : 120 | * - TX_RX line : Switches between Transmit and Receive after sensing the PTT or the morse keyer 121 | * - CW_KEY line : turns on the carrier for CW 122 | */ 123 | 124 | #define TX_RX (7) 125 | #define CW_TONE (6) 126 | #define TX_LPF_A (5) 127 | #define TX_LPF_B (4) 128 | #define TX_LPF_C (3) 129 | #define CW_KEY (2) 130 | 131 | /** 132 | * These are the indices where these user changable settinngs are stored in the EEPROM 133 | */ 134 | #define MASTER_CAL 0 135 | #define LSB_CAL 4 136 | #define USB_CAL 8 137 | #define SIDE_TONE 12 138 | //these are ids of the vfos as well as their offset into the eeprom storage, don't change these 'magic' values 139 | #define VFO_A 16 140 | #define VFO_B 20 141 | #define CW_SIDETONE 24 142 | #define CW_SPEED 28 143 | 144 | //These are defines for the new features back-ported from KD8CEC's software 145 | //these start from beyond 256 as Ian, KD8CEC has kept the first 256 bytes free for the base version 146 | #define VFO_A_MODE 256 // 2: LSB, 3: USB 147 | #define VFO_B_MODE 257 148 | 149 | //values that are stroed for the VFO modes 150 | #define VFO_MODE_LSB 2 151 | #define VFO_MODE_USB 3 152 | 153 | // handkey, iambic a, iambic b : 0,1,2f 154 | #define CW_KEY_TYPE 358 155 | 156 | /** 157 | * The uBITX is an upconnversion transceiver. The first IF is at 45 MHz. 158 | * The first IF frequency is not exactly at 45 Mhz but about 5 khz lower, 159 | * this shift is due to the loading on the 45 Mhz crystal filter by the matching 160 | * L-network used on it's either sides. 161 | * The first oscillator works between 48 Mhz and 75 MHz. The signal is subtracted 162 | * from the first oscillator to arriive at 45 Mhz IF. Thus, it is inverted : LSB becomes USB 163 | * and USB becomes LSB. 164 | * The second IF of 12 Mhz has a ladder crystal filter. If a second oscillator is used at 165 | * 57 Mhz, the signal is subtracted FROM the oscillator, inverting a second time, and arrives 166 | * at the 12 Mhz ladder filter thus doouble inversion, keeps the sidebands as they originally were. 167 | * If the second oscillator is at 33 Mhz, the oscilaltor is subtracated from the signal, 168 | * thus keeping the signal's sidebands inverted. The USB will become LSB. 169 | * We use this technique to switch sidebands. This is to avoid placing the lsbCarrier close to 170 | * 12 MHz where its fifth harmonic beats with the arduino's 16 Mhz oscillator's fourth harmonic 171 | */ 172 | 173 | 174 | #define INIT_USB_FREQ (11059200l) 175 | // limits the tuning and working range of the ubitx between 3 MHz and 30 MHz 176 | #define LOWEST_FREQ (100000l) 177 | #define HIGHEST_FREQ (30000000l) 178 | 179 | //we directly generate the CW by programmin the Si5351 to the cw tx frequency, hence, both are different modes 180 | //these are the parameter passed to startTx 181 | #define TX_SSB 0 182 | #define TX_CW 1 183 | 184 | char ritOn = 0; 185 | char vfoActive = VFO_A; 186 | int8_t meter_reading = 0; // a -1 on meter makes it invisible 187 | unsigned long vfoA=7150000L, vfoB=14200000L, sideTone=800, usbCarrier; 188 | char isUsbVfoA=0, isUsbVfoB=1; 189 | unsigned long frequency, ritRxFrequency, ritTxFrequency; //frequency is the current frequency on the dial 190 | unsigned long firstIF = 45005000L; 191 | 192 | //these are variables that control the keyer behaviour 193 | int cwSpeed = 100; //this is actuall the dot period in milliseconds 194 | extern int32_t calibration; 195 | byte cwDelayTime = 60; 196 | bool Iambic_Key = true; 197 | #define IAMBICB 0x10 // 0 for Iambic A, 1 for Iambic B 198 | unsigned char keyerControl = IAMBICB; 199 | 200 | 201 | /** 202 | * Raduino needs to keep track of current state of the transceiver. These are a few variables that do it 203 | */ 204 | boolean txCAT = false; //turned on if the transmitting due to a CAT command 205 | char inTx = 0; //it is set to 1 if in transmit mode (whatever the reason : cw, ptt or cat) 206 | char splitOn = 0; //working split, uses VFO B as the transmit frequency, (NOT IMPLEMENTED YET) 207 | char keyDown = 0; //in cw mode, denotes the carrier is being transmitted 208 | char isUSB = 0; //upper sideband was selected, this is reset to the default for the 209 | //frequency when it crosses the frequency border of 10 MHz 210 | byte menuOn = 0; //set to 1 when the menu is being displayed, if a menu item sets it to zero, the menu is exited 211 | unsigned long cwTimeout = 0; //milliseconds to go before the cw transmit line is released and the radio goes back to rx mode 212 | unsigned long dbgCount = 0; //not used now 213 | unsigned char txFilter = 0; //which of the four transmit filters are in use 214 | boolean modeCalibrate = false;//this mode of menus shows extended menus to calibrate the oscillators and choose the proper 215 | //beat frequency 216 | 217 | /** 218 | * Below are the basic functions that control the uBitx. Understanding the functions before 219 | * you start hacking around 220 | */ 221 | 222 | /** 223 | * Our own delay. During any delay, the raduino should still be processing a few times. 224 | */ 225 | 226 | void active_delay(int delay_by){ 227 | unsigned long timeStart = millis(); 228 | 229 | while (millis() - timeStart <= delay_by) { 230 | //Background Work 231 | checkCAT(); 232 | } 233 | } 234 | 235 | /** 236 | * Select the properly tx harmonic filters 237 | * The four harmonic filters use only three relays 238 | * the four LPFs cover 30-21 Mhz, 18 - 14 Mhz, 7-10 MHz and 3.5 to 5 Mhz 239 | * Briefly, it works like this, 240 | * - When KT1 is OFF, the 'off' position routes the PA output through the 30 MHz LPF 241 | * - When KT1 is ON, it routes the PA output to KT2. Which is why you will see that 242 | * the KT1 is on for the three other cases. 243 | * - When the KT1 is ON and KT2 is off, the off position of KT2 routes the PA output 244 | * to 18 MHz LPF (That also works for 14 Mhz) 245 | * - When KT1 is On, KT2 is On, it routes the PA output to KT3 246 | * - KT3, when switched on selects the 7-10 Mhz filter 247 | * - KT3 when switched off selects the 3.5-5 Mhz filter 248 | * See the circuit to understand this 249 | */ 250 | 251 | void setTXFilters(unsigned long freq){ 252 | 253 | if (freq > 21000000L){ // the default filter is with 35 MHz cut-off 254 | digitalWrite(TX_LPF_A, 0); 255 | digitalWrite(TX_LPF_B, 0); 256 | digitalWrite(TX_LPF_C, 0); 257 | } 258 | 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 259 | digitalWrite(TX_LPF_A, 1); 260 | digitalWrite(TX_LPF_B, 0); 261 | digitalWrite(TX_LPF_C, 0); 262 | } 263 | else if (freq > 7000000L){ 264 | digitalWrite(TX_LPF_A, 0); 265 | digitalWrite(TX_LPF_B, 1); 266 | digitalWrite(TX_LPF_C, 0); 267 | } 268 | else { 269 | digitalWrite(TX_LPF_A, 0); 270 | digitalWrite(TX_LPF_B, 0); 271 | digitalWrite(TX_LPF_C, 1); 272 | } 273 | } 274 | 275 | 276 | void setTXFilters_v5(unsigned long freq){ 277 | 278 | if (freq > 21000000L){ // the default filter is with 35 MHz cut-off 279 | digitalWrite(TX_LPF_A, 0); 280 | digitalWrite(TX_LPF_B, 0); 281 | digitalWrite(TX_LPF_C, 0); 282 | } 283 | 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 284 | digitalWrite(TX_LPF_A, 1); 285 | digitalWrite(TX_LPF_B, 0); 286 | digitalWrite(TX_LPF_C, 0); 287 | } 288 | else if (freq > 7000000L){ 289 | digitalWrite(TX_LPF_A, 0); 290 | digitalWrite(TX_LPF_B, 1); 291 | digitalWrite(TX_LPF_C, 0); 292 | } 293 | else { 294 | digitalWrite(TX_LPF_A, 0); 295 | digitalWrite(TX_LPF_B, 0); 296 | digitalWrite(TX_LPF_C, 1); 297 | } 298 | } 299 | 300 | 301 | /** 302 | * This is the most frequently called function that configures the 303 | * radio to a particular frequeny, sideband and sets up the transmit filters 304 | * 305 | * The transmit filter relays are powered up only during the tx so they dont 306 | * draw any current during rx. 307 | * 308 | * The carrier oscillator of the detector/modulator is permanently fixed at 309 | * uppper sideband. The sideband selection is done by placing the second oscillator 310 | * either 12 Mhz below or above the 45 Mhz signal thereby inverting the sidebands 311 | * through mixing of the second local oscillator. 312 | */ 313 | 314 | void setFrequency(unsigned long f){ 315 | uint64_t osc_f, firstOscillator, secondOscillator; 316 | 317 | setTXFilters(f); 318 | 319 | /* 320 | if (isUSB){ 321 | si5351bx_setfreq(2, firstIF + f); 322 | si5351bx_setfreq(1, firstIF + usbCarrier); 323 | } 324 | else{ 325 | si5351bx_setfreq(2, firstIF + f); 326 | si5351bx_setfreq(1, firstIF - usbCarrier); 327 | } 328 | */ 329 | //alternative to reduce the intermod spur 330 | if (isUSB){ 331 | si5351bx_setfreq(2, firstIF + f); 332 | si5351bx_setfreq(1, firstIF + usbCarrier); 333 | } 334 | else{ 335 | si5351bx_setfreq(2, firstIF + f); 336 | si5351bx_setfreq(1, firstIF - usbCarrier); 337 | } 338 | 339 | frequency = f; 340 | } 341 | 342 | /** 343 | * startTx is called by the PTT, cw keyer and CAT protocol to 344 | * put the uBitx in tx mode. It takes care of rit settings, sideband settings 345 | * Note: In cw mode, doesnt key the radio, only puts it in tx mode 346 | * CW offest is calculated as lower than the operating frequency when in LSB mode, and vice versa in USB mode 347 | */ 348 | 349 | void startTx(byte txMode){ 350 | unsigned long tx_freq = 0; 351 | 352 | digitalWrite(TX_RX, 1); 353 | inTx = 1; 354 | 355 | if (ritOn){ 356 | //save the current as the rx frequency 357 | ritRxFrequency = frequency; 358 | setFrequency(ritTxFrequency); 359 | } 360 | else 361 | { 362 | if (splitOn == 1) { 363 | if (vfoActive == VFO_B) { 364 | vfoActive = VFO_A; 365 | isUSB = isUsbVfoA; 366 | frequency = vfoA; 367 | } 368 | else if (vfoActive == VFO_A){ 369 | vfoActive = VFO_B; 370 | frequency = vfoB; 371 | isUSB = isUsbVfoB; 372 | } 373 | } 374 | setFrequency(frequency); 375 | } 376 | 377 | if (txMode == TX_CW){ 378 | //turn off the second local oscillator and the bfo 379 | si5351bx_setfreq(0, 0); 380 | si5351bx_setfreq(1, 0); 381 | 382 | //shif the first oscillator to the tx frequency directly 383 | //the key up and key down will toggle the carrier unbalancing 384 | //the exact cw frequency is the tuned frequency + sidetone 385 | if (isUSB) 386 | si5351bx_setfreq(2, frequency + sideTone); 387 | else 388 | si5351bx_setfreq(2, frequency - sideTone); 389 | } 390 | updateDisplay(); 391 | } 392 | 393 | void stopTx(){ 394 | inTx = 0; 395 | 396 | digitalWrite(TX_RX, 0); //turn off the tx 397 | si5351bx_setfreq(0, usbCarrier); //set back the cardrier oscillator anyway, cw tx switches it off 398 | 399 | if (ritOn) 400 | setFrequency(ritRxFrequency); 401 | else{ 402 | if (splitOn == 1) { 403 | //vfo Change 404 | if (vfoActive == VFO_B){ 405 | vfoActive = VFO_A; 406 | frequency = vfoA; 407 | isUSB = isUsbVfoA; 408 | } 409 | else if (vfoActive == VFO_A){ 410 | vfoActive = VFO_B; 411 | frequency = vfoB; 412 | isUSB = isUsbVfoB; 413 | } 414 | } 415 | setFrequency(frequency); 416 | } 417 | updateDisplay(); 418 | } 419 | 420 | /** 421 | * ritEnable is called with a frequency parameter that determines 422 | * what the tx frequency will be 423 | */ 424 | void ritEnable(unsigned long f){ 425 | ritOn = 1; 426 | //save the non-rit frequency back into the VFO memory 427 | //as RIT is a temporary shift, this is not saved to EEPROM 428 | ritTxFrequency = f; 429 | } 430 | 431 | // this is called by the RIT menu routine 432 | void ritDisable(){ 433 | if (ritOn){ 434 | ritOn = 0; 435 | setFrequency(ritTxFrequency); 436 | updateDisplay(); 437 | } 438 | } 439 | 440 | /** 441 | * Basic User Interface Routines. These check the front panel for any activity 442 | */ 443 | 444 | /** 445 | * The PTT is checked only if we are not already in a cw transmit session 446 | * If the PTT is pressed, we shift to the ritbase if the rit was on 447 | * flip the T/R line to T and update the display to denote transmission 448 | */ 449 | 450 | void checkPTT(){ 451 | //we don't check for ptt when transmitting cw 452 | if (cwTimeout > 0) 453 | return; 454 | 455 | if (digitalRead(PTT) == 0 && inTx == 0){ 456 | startTx(TX_SSB); 457 | active_delay(50); //debounce the PTT 458 | } 459 | 460 | if (digitalRead(PTT) == 1 && inTx == 1) 461 | stopTx(); 462 | } 463 | 464 | void checkButton(){ 465 | int i, t1, t2, knob, new_knob; 466 | 467 | //only if the button is pressed 468 | if (!btnDown()) 469 | return; 470 | active_delay(50); 471 | if (!btnDown()) //debounce 472 | return; 473 | 474 | doMenu(); 475 | //wait for the button to go up again 476 | while(btnDown()) 477 | active_delay(10); 478 | active_delay(50);//debounce 479 | } 480 | 481 | 482 | /** 483 | * The tuning jumps by 50 Hz on each step when you tune slowly 484 | * As you spin the encoder faster, the jump size also increases 485 | * This way, you can quickly move to another band by just spinning the 486 | * tuning knob 487 | */ 488 | 489 | 490 | void doTuning(){ 491 | int s; 492 | unsigned long prev_freq; 493 | 494 | s = enc_read(); 495 | if (s != 0){ 496 | prev_freq = frequency; 497 | 498 | if (s > 4) 499 | frequency += 10000l; 500 | else if (s > 2) 501 | frequency += 500; 502 | else if (s > 0) 503 | frequency += 50l; 504 | else if (s > -2) 505 | frequency -= 50l; 506 | else if (s > -4) 507 | frequency -= 500l; 508 | else 509 | frequency -= 10000l; 510 | 511 | if (prev_freq < 10000000l && frequency > 10000000l) 512 | isUSB = true; 513 | 514 | if (prev_freq > 10000000l && frequency < 10000000l) 515 | isUSB = false; 516 | 517 | setFrequency(frequency); 518 | updateDisplay(); 519 | } 520 | } 521 | 522 | /** 523 | * RIT only steps back and forth by 100 hz at a time 524 | */ 525 | void doRIT(){ 526 | unsigned long newFreq; 527 | 528 | int knob = enc_read(); 529 | unsigned long old_freq = frequency; 530 | 531 | if (knob < 0) 532 | frequency -= 100l; 533 | else if (knob > 0) 534 | frequency += 100; 535 | 536 | if (old_freq != frequency){ 537 | setFrequency(frequency); 538 | updateDisplay(); 539 | } 540 | } 541 | 542 | /** 543 | * The settings are read from EEPROM. The first time around, the values may not be 544 | * present or out of range, in this case, some intelligent defaults are copied into the 545 | * variables. 546 | */ 547 | void initSettings(){ 548 | byte x; 549 | //read the settings from the eeprom and restore them 550 | //if the readings are off, then set defaults 551 | EEPROM.get(MASTER_CAL, calibration); 552 | EEPROM.get(USB_CAL, usbCarrier); 553 | EEPROM.get(VFO_A, vfoA); 554 | EEPROM.get(VFO_B, vfoB); 555 | EEPROM.get(CW_SIDETONE, sideTone); 556 | EEPROM.get(CW_SPEED, cwSpeed); 557 | 558 | 559 | if (usbCarrier > 11060000l || usbCarrier < 11048000l) 560 | usbCarrier = 11052000l; 561 | if (vfoA > 35000000l || 3500000l > vfoA) 562 | vfoA = 7150000l; 563 | if (vfoB > 35000000l || 3500000l > vfoB) 564 | vfoB = 14150000l; 565 | if (sideTone < 100 || 2000 < sideTone) 566 | sideTone = 800; 567 | if (cwSpeed < 10 || 1000 < cwSpeed) 568 | cwSpeed = 100; 569 | 570 | /* 571 | * The VFO modes are read in as either 2 (USB) or 3(LSB), 0, the default 572 | * is taken as 'uninitialized 573 | */ 574 | 575 | EEPROM.get(VFO_A_MODE, x); 576 | 577 | switch(x){ 578 | case VFO_MODE_USB: 579 | isUsbVfoA = 1; 580 | break; 581 | case VFO_MODE_LSB: 582 | isUsbVfoA = 0; 583 | break; 584 | default: 585 | if (vfoA > 10000000l) 586 | isUsbVfoA = 1; 587 | else 588 | isUsbVfoA = 0; 589 | } 590 | 591 | EEPROM.get(VFO_B_MODE, x); 592 | switch(x){ 593 | case VFO_MODE_USB: 594 | isUsbVfoB = 1; 595 | break; 596 | case VFO_MODE_LSB: 597 | isUsbVfoB = 0; 598 | break; 599 | default: 600 | if (vfoA > 10000000l) 601 | isUsbVfoB = 1; 602 | else 603 | isUsbVfoB = 0; 604 | } 605 | 606 | //set the current mode 607 | isUSB = isUsbVfoA; 608 | 609 | /* 610 | * The keyer type splits into two variables 611 | */ 612 | EEPROM.get(CW_KEY_TYPE, x); 613 | 614 | if (x == 0) 615 | Iambic_Key = false; 616 | else if (x == 1){ 617 | Iambic_Key = true; 618 | keyerControl &= ~IAMBICB; 619 | } 620 | else if (x == 2){ 621 | Iambic_Key = true; 622 | keyerControl |= IAMBICB; 623 | } 624 | 625 | } 626 | 627 | void initPorts(){ 628 | 629 | analogReference(DEFAULT); 630 | 631 | //?? 632 | pinMode(ENC_A, INPUT_PULLUP); 633 | pinMode(ENC_B, INPUT_PULLUP); 634 | pinMode(FBUTTON, INPUT_PULLUP); 635 | 636 | //configure the function button to use the external pull-up 637 | // pinMode(FBUTTON, INPUT); 638 | // digitalWrite(FBUTTON, HIGH); 639 | 640 | pinMode(PTT, INPUT_PULLUP); 641 | pinMode(ANALOG_KEYER, INPUT_PULLUP); 642 | 643 | pinMode(CW_TONE, OUTPUT); 644 | digitalWrite(CW_TONE, 0); 645 | 646 | pinMode(TX_RX,OUTPUT); 647 | digitalWrite(TX_RX, 0); 648 | 649 | pinMode(TX_LPF_A, OUTPUT); 650 | pinMode(TX_LPF_B, OUTPUT); 651 | pinMode(TX_LPF_C, OUTPUT); 652 | digitalWrite(TX_LPF_A, 0); 653 | digitalWrite(TX_LPF_B, 0); 654 | digitalWrite(TX_LPF_C, 0); 655 | 656 | pinMode(CW_KEY, OUTPUT); 657 | digitalWrite(CW_KEY, 0); 658 | } 659 | 660 | void setup() 661 | { 662 | Serial.begin(38400); 663 | Serial.flush(); 664 | 665 | lcd.begin(16, 2); 666 | 667 | //we print this line so this shows up even if the raduino 668 | //crashes later in the code 669 | printLine2("uBITX v5.1"); 670 | //active_delay(500); 671 | 672 | // initMeter(); //not used in this build 673 | initSettings(); 674 | initPorts(); 675 | initOscillators(); 676 | 677 | frequency = vfoA; 678 | setFrequency(vfoA); 679 | updateDisplay(); 680 | 681 | if (btnDown()) 682 | factory_alignment(); 683 | } 684 | 685 | 686 | /** 687 | * The loop checks for keydown, ptt, function button and tuning. 688 | */ 689 | 690 | byte flasher = 0; 691 | void loop(){ 692 | 693 | cwKeyer(); 694 | if (!txCAT) 695 | checkPTT(); 696 | checkButton(); 697 | 698 | //tune only when not tranmsitting 699 | if (!inTx){ 700 | if (ritOn) 701 | doRIT(); 702 | else 703 | doTuning(); 704 | } 705 | 706 | //we check CAT after the encoder as it might put the radio into TX 707 | checkCAT(); 708 | } 709 | -------------------------------------------------------------------------------- /ubitx_v5.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/afarhan/ubitx_v5/c519a75934e691f8e42a8a5254f568a5abeac899/ubitx_v5.pdf --------------------------------------------------------------------------------