From 29e502bbf994ca78fc61869dffb315b817293eef Mon Sep 17 00:00:00 2001 From: "Hansi, dl9rdz" Date: Tue, 8 Oct 2019 07:59:30 +0200 Subject: [PATCH] - Enhanced autoconfig for various board types - Reorganised display support, preparation for other larger displays (ILI9225) - T-Beam 1.0 PMU support - Data in web: show only active frequencies - added TCP/IP servicer for APRSdroid - Easier-to-modify display layout --- .travis.yml | 23 +- Licenses/README | 2 + Licenses/gpl-3.0.txt | 674 +++++++++++++ RX_FSK/RX_FSK.ino | 289 ++++-- RX_FSK/data/config.txt | 12 +- RX_FSK/data/screens.txt | 143 +++ RX_FSK/version.h | 4 +- Setup.md | 1 + libraries/SX1278FSK/SX1278FSK.cpp | 7 +- libraries/SondeLib/DefaultFonts.c | 367 +++++++ libraries/SondeLib/Display.cpp | 547 ++++++++-- libraries/SondeLib/Display.h | 60 ++ libraries/SondeLib/RS41.cpp | 7 +- libraries/SondeLib/Scanner.cpp | 5 +- libraries/SondeLib/Sonde.cpp | 53 +- libraries/SondeLib/Sonde.h | 10 +- libraries/SondeLib/TFT22_ILI9225.cpp | 1379 ++++++++++++++++++++++++++ libraries/SondeLib/TFT22_ILI9225.h | 466 +++++++++ libraries/SondeLib/aprs.cpp | 23 + libraries/SondeLib/aprs.h | 8 + libraries/SondeLib/autodetect-infos | 27 +- libraries/SondeLib/geteph.cpp | 18 +- libraries/SondeLib/gfxfont.h | 26 + libraries/SondeLib/rs92gps.cpp | 1 - libraries/fonts/FreeSans12pt7b.h | 270 +++++ libraries/fonts/FreeSans9pt7b.h | 201 ++++ 26 files changed, 4441 insertions(+), 182 deletions(-) create mode 100644 Licenses/gpl-3.0.txt create mode 100644 RX_FSK/data/screens.txt create mode 100644 libraries/SondeLib/DefaultFonts.c create mode 100644 libraries/SondeLib/TFT22_ILI9225.cpp create mode 100644 libraries/SondeLib/TFT22_ILI9225.h create mode 100644 libraries/SondeLib/gfxfont.h create mode 100644 libraries/fonts/FreeSans12pt7b.h create mode 100644 libraries/fonts/FreeSans9pt7b.h diff --git a/.travis.yml b/.travis.yml index aae5b02..d7ce010 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,15 +1,15 @@ language: c env: global: - - ESP32TOOLS=/home/travis/.arduino15/packages/esp32/hardware/esp32/1.0.2/tools + - ESP32TOOLS=/home/travis/.arduino15/packages/esp32/hardware/esp32/1.0.4/tools - MKSPIFFS=/home/travis/.arduino15/packages/esp32/tools/mkspiffs/0.2.3/mkspiffs before_install: - "/sbin/start-stop-daemon --start --quiet --pidfile /tmp/custom_xvfb_1.pid --make-pidfile --background --exec /usr/bin/Xvfb -- :1 -ac -screen 0 1280x1024x16" - sleep 3 - export DISPLAY=:1.0 - - wget https://downloads.arduino.cc/arduino-1.8.9-linux64.tar.xz - - tar xf arduino-1.8.9-linux64.tar.xz - - sudo mv arduino-1.8.9 /usr/local/share/arduino + - wget https://downloads.arduino.cc/arduino-1.8.10-linux64.tar.xz + - tar xf arduino-1.8.10-linux64.tar.xz + - sudo mv arduino-1.8.10 /usr/local/share/arduino - sudo ln -s /usr/local/share/arduino/arduino /usr/local/bin/arduino - wget https://github.com/me-no-dev/ESPAsyncWebServer/archive/master.zip - unzip master.zip @@ -19,6 +19,18 @@ before_install: - unzip master.zip - sudo mv AsyncTCP-master /usr/local/share/arduino/libraries/AsyncTCP - wget https://github.com/me-no-dev/arduino-esp32fs-plugin/releases/download/1.0/ESP32FS-1.0.zip + - wget https://github.com/lewisxhe/AXP202X_Library/archive/v1.0.zip + - unzip v1.0.zip + - sudo mv AXP202X_Library-1.0 /usr/local/share/arduino/libraries/ + # Trying to get rid of mDNS warnings (1000s of them...) + # as suggested by https://forum.arduino.cc/index.php?topic=469428.0 + # Arduino IDE adds a lot of noise caused by network traffic, trying to firewall it off + - sudo iptables -P INPUT DROP + - sudo iptables -P FORWARD DROP + - sudo iptables -P OUTPUT ACCEPT + - sudo iptables -A INPUT -i lo -j ACCEPT + - sudo iptables -A OUTPUT -o lo -j ACCEPT + - sudo iptables -A INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT install: - arduino --pref "boardsmanager.additional.urls=https://dl.espressif.com/dl/package_esp32_index.json" --save-prefs @@ -32,8 +44,9 @@ install: - arduino --install-library "U8g2" - arduino --install-library "MicroNMEA" script: - - arduino --board esp32:esp32:ttgo-lora32-v1 --verify $PWD/RX_FSK/RX_FSK.ino + - arduino --board esp32:esp32:t-beam --verify $PWD/RX_FSK/RX_FSK.ino - find build + - find /home/travis/.arduino15/packages/esp32/hardware/esp32/ - $MKSPIFFS -c $PWD/RX_FSK/data -b 4096 -p 256 -s 1503232 $PWD/spiffs.bin - $PWD/scripts/makeimage.py $ESP32TOOLS $PWD/build/RX_FSK.ino.bin $PWD/spiffs.bin $PWD/out.bin after_success: diff --git a/Licenses/README b/Licenses/README index a6f97fd..ac81c9c 100644 --- a/Licenses/README +++ b/Licenses/README @@ -28,3 +28,5 @@ Full name SPDX Identifier OSI Approved File name URI GNU General Public License v2.0 only GPL-2.0 Y gpl-2.0.txt https://www.gnu.org/licenses/gpl-2.0.txt GNU General Public License v2.0 or later GPL-2.0+ Y gpl-2.0.txt https://www.gnu.org/licenses/gpl-2.0.txt GNU Lesser General Public License v2.1 LGPL-2.1+ Y lgpl-2.txt https://www.gnu.org/licenses/lgpl-2.1.txt +GNU General Public License v3.0 only GPL-3.0 Y gpl-3.0.txt https://www.gnu.org/licenses/gpl-3.0.txt + diff --git a/Licenses/gpl-3.0.txt b/Licenses/gpl-3.0.txt new file mode 100644 index 0000000..f288702 --- /dev/null +++ b/Licenses/gpl-3.0.txt @@ -0,0 +1,674 @@ + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + Copyright (C) + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +. diff --git a/RX_FSK/RX_FSK.ino b/RX_FSK/RX_FSK.ino index fe0157b..7570b4a 100644 --- a/RX_FSK/RX_FSK.ino +++ b/RX_FSK/RX_FSK.ino @@ -1,9 +1,11 @@ +#include + #include #include #include #include -#include -#include +//#include +//#include #include #include #include @@ -19,12 +21,6 @@ #include "geteph.h" #include "rs92gps.h" -// UNCOMMENT one of the constructor lines below -U8X8_SSD1306_128X64_NONAME_SW_I2C *u8x8 = NULL; // initialize later after reading config file -//U8X8_SSD1306_128X64_NONAME_SW_I2C u8x8(/* clock=*/ OLED_SCL, /* data=*/ OLED_SDA, /* reset=*/ OLED_RST); // Unbuffered, basic graphics, software I2C -//U8G2_SSD1306_128X64_NONAME_1_SW_I2C Display(U8G2_R0, /* clock=*/ OLED_SCL, /* data=*/ OLED_SDA, /* reset=*/ OLED_RST); // Page buffer, SW I2C -//U8G2_SSD1306_128X64_NONAME_F_SW_I2C Display(U8G2_R0, /* clock=*/ OLED_SCL, /* data=*/ OLED_SDA, /* reset=*/ OLED_RST); // Full framebuffer, SW I2C - int LORA_LED = 9; // default POUT for LORA LED used as serial monitor int e; @@ -32,6 +28,9 @@ enum MainState { ST_DECODER, ST_SPECTRUM, ST_WIFISCAN, ST_UPDATE }; static MainState mainState = ST_WIFISCAN; // ST_WIFISCAN; AsyncWebServer server(80); +AXP20X_Class axp; +#define PMU_IRQ 35 + String updateHost = "rdzsonde.mooo.com"; int updatePort = 80; @@ -45,6 +44,9 @@ boolean connected = false; WiFiUDP udp; WiFiClient client; +// KISS over TCP für communicating with APRSdroid +WiFiServer tncserver(14580); +WiFiClient tncclient; enum KeyPress { KP_NONE = 0, KP_SHORT, KP_DOUBLE, KP_MID, KP_LONG }; @@ -347,7 +349,10 @@ const char *createStatusForm() { strcpy(ptr, ""); for (int i = 0; i < sonde.nSonde; i++) { - addSondeStatus(ptr, (i + sonde.currentSonde) % sonde.nSonde); + int snum = (i + sonde.currentSonde) % sonde.nSonde; + if (sonde.sondeList[snum].active) { + addSondeStatus(ptr, snum); + } } strcat(ptr, ""); return message; @@ -397,6 +402,9 @@ struct st_configitems config_list[] = { {"call", "Call", 8, sonde.config.call}, {"passcode", "Passcode", 8, sonde.config.passcode}, {"---", "---", -1, NULL}, + /* KISS tnc settings */ + {"kisstnc", "KISS TNC (port 14590) (needs reboot)", 0, &sonde.config.kisstnc.active}, + {"kisstnc.idformat", "DFM ID Format", -2, &sonde.config.kisstnc.idformat}, /* AXUDP settings */ {"axudp.active", "AXUDP active", -3, &sonde.config.udpfeed.active}, {"axudp.host", "AXUDP Host", 63, sonde.config.udpfeed.host}, @@ -420,9 +428,12 @@ struct st_configitems config_list[] = { {"dfm.rxbw", "DFM6/9 RX bandwidth", 0, &sonde.config.dfm.rxbw}, {"---", "---", -1, NULL}, /* Hardware dependeing settings */ - {"oled_sda", "OLED SDA (needs reboot)", 0, &sonde.config.oled_sda}, - {"oled_scl", "OLED SCL (needs reboot)", 0, &sonde.config.oled_scl}, - {"oled_rst", "OLED RST (needs reboot)", 0, &sonde.config.oled_rst}, + {"disptype", "Display type (0=OLED/SSD1306, 1=TFT/ILI9225, 2=OLED/SH1106)", 0, &sonde.config.disptype}, + {"oled_sda", "OLED/TFT SDA (needs reboot)", 0, &sonde.config.oled_sda}, + {"oled_scl", "OLED SCL/TFT CLK (needs reboot)", 0, &sonde.config.oled_scl}, + {"oled_rst", "OLED/TFT RST (needs reboot)", 0, &sonde.config.oled_rst}, + {"tft_rs", "TFT RS (needs reboot)", 0, &sonde.config.tft_rs}, + {"tft_cs", "TFT CS (needs reboot)", 0, &sonde.config.tft_cs}, {"button_pin", "Button input port (needs reboot)", -4, &sonde.config.button_pin}, {"button2_pin", "Button 2 input port (needs reboot)", -4, &sonde.config.button2_pin}, {"touch_thresh", "Touch button threshold (needs reboot)", 0, &sonde.config.touch_thresh}, @@ -631,6 +642,10 @@ const char *handleEditPost(AsyncWebServerRequest *request) { } file.print(content); file.close(); + if (strcmp(filename.c_str(), "screens.txt") == 0) { + // screens update => reload + disp.initFromFile(); + } return ""; } @@ -847,11 +862,11 @@ void initTouch() { ticker.attach_ms(300, checkTouchStatus); if ( IS_TOUCH(sonde.config.button_pin) ) { - touchAttachInterrupt(sonde.config.button_pin & 0x7f, touchISR, 20); + touchAttachInterrupt(sonde.config.button_pin & 0x7f, touchISR, 60); Serial.printf("Initializing touch 1 on pin %d\n", sonde.config.button_pin & 0x7f); } if ( IS_TOUCH(sonde.config.button2_pin) ) { - touchAttachInterrupt(sonde.config.button2_pin & 0x7f, touchISR2, 20); + touchAttachInterrupt(sonde.config.button2_pin & 0x7f, touchISR2, 60); Serial.printf("Initializing touch 2 on pin %d\n", sonde.config.button2_pin & 0x7f); } } @@ -871,7 +886,7 @@ void gpsTask(void *parameter) { bool b = nmea.getAltitude(alt); bool valid = nmea.isValid(); uint8_t hdop = nmea.getHDOP(); - //Serial.printf("\nDecode: valid: %d N %ld E %ld alt %ld (%d) dop:%d", valid?1:0, lat, lon, alt, b, hdop); + Serial.printf("\nDecode: valid: %d N %ld E %ld alt %ld (%d) dop:%d", valid ? 1 : 0, lat, lon, alt, b, hdop); } } delay(50); @@ -949,6 +964,7 @@ void IRAM_ATTR touchISR2() { void checkTouchButton(Button & button) { if (button.isTouched) { int tmp = touchRead(button.pin & 0x7f); + Serial.printf("touch read %d: value is %d\n", button.pin,tmp); if (tmp > sonde.config.touch_thresh) { button.isTouched = false; unsigned long elapsed = my_millis() - button.keydownTime; @@ -973,18 +989,21 @@ void checkTouchStatus() { checkTouchButton(button2); } - +unsigned long bdd1, bdd2; void IRAM_ATTR buttonISR() { - unsigned long now = my_millis(); if (digitalRead(button1.pin) == 0) { // Button down + unsigned long now = my_millis(); if (now - button1.keydownTime < 500) { // Double press button1.doublepress = 1; + bdd1 = now; bdd2 = button1.keydownTime; } else { button1.doublepress = 0; } + button1.numberKeyPresses += 1; button1.keydownTime = now; } else { //Button up + unsigned long now = my_millis(); if (button1.doublepress == -1) return; // key was never pressed before, ignore button up unsigned int elapsed = now - button1.keydownTime; if (elapsed > 1500) { @@ -1007,7 +1026,9 @@ int getKeyPress() { KeyPress p = button1.pressed; button1.pressed = KP_NONE; int x = digitalRead(button1.pin); - Serial.printf("button1 press (now:%d): %d at %ld (%d)\n", x, p, button1.keydownTime, button1.numberKeyPresses); + Serial.printf("Debug: bdd1=%ld, bdd2=%ld\b", bdd1, bdd2); + + Serial.printf("button1 press (dbl:%d) (now:%d): %d at %ld (%d)\n", button1.doublepress, x, p, button1.keydownTime, button1.numberKeyPresses); return p; } @@ -1031,7 +1052,52 @@ int getKeyPressEvent() { return p; /* map KP_x to EVT_KEY1_x / EVT_KEY2_x*/ } +#define SSD1306_ADDRESS 0x3c +#define AXP192_SLAVE_ADDRESS 0x34 +bool ssd1306_found = false; +bool axp192_found = false; + +void scanI2Cdevice(void) +{ + byte err, addr; + int nDevices = 0; + for (addr = 1; addr < 127; addr++) { + Wire.beginTransmission(addr); + err = Wire.endTransmission(); + if (err == 0) { + Serial.print("I2C device found at address 0x"); + if (addr < 16) + Serial.print("0"); + Serial.print(addr, HEX); + Serial.println(" !"); + nDevices++; + + if (addr == SSD1306_ADDRESS) { + ssd1306_found = true; + Serial.println("ssd1306 display found"); + } + if (addr == AXP192_SLAVE_ADDRESS) { + axp192_found = true; + Serial.println("axp192 PMU found"); + } + } else if (err == 4) { + Serial.print("Unknow error at address 0x"); + if (addr < 16) + Serial.print("0"); + Serial.println(addr, HEX); + } + } + if (nDevices == 0) + Serial.println("No I2C devices found\n"); + else + Serial.println("done\n"); +} + extern int initlevels[40]; +extern DispInfo *layouts; +bool pmu_irq = false; + + void setup() { char buf[12]; @@ -1042,23 +1108,62 @@ void setup() Serial.printf("%d:%d ", i, v); } Serial.println(""); + delay(2000); for (int i = 0; i < 39; i++) { Serial.printf("%d:%d ", i, initlevels[i]); } Serial.println(" (before setup)"); - pinMode(LORA_LED, OUTPUT); aprs_gencrctab(); + Serial.println("Initializing SPIFFS"); // Initialize SPIFFS if (!SPIFFS.begin(true)) { Serial.println("An Error has occurred while mounting SPIFFS"); return; } + Serial.println("Reading initial configuration"); setupConfigData(); // configuration must be read first due to OLED ports!!! + + // FOr T-Beam 1.0 + Wire.begin(21, 22); + // Make sure the whole thing powers up!?!?!?!?!? + U8X8 *u8x8 = new U8X8_SSD1306_128X64_NONAME_HW_I2C(0, 22, 21); + u8x8->initDisplay(); + delay(500); + + scanI2Cdevice(); + + if (!axp.begin(Wire, AXP192_SLAVE_ADDRESS)) { + Serial.println("AXP192 Begin PASS"); + } else { + Serial.println("AXP192 Begin FAIL"); + } + axp.setPowerOutPut(AXP192_LDO2, AXP202_ON); + axp.setPowerOutPut(AXP192_LDO3, AXP202_ON); + axp.setPowerOutPut(AXP192_DCDC2, AXP202_ON); + axp.setPowerOutPut(AXP192_EXTEN, AXP202_ON); + axp.setPowerOutPut(AXP192_DCDC1, AXP202_ON); + axp.setDCDC1Voltage(3300); + + pinMode(PMU_IRQ, INPUT_PULLUP); + attachInterrupt(PMU_IRQ, [] { + pmu_irq = true; + }, FALLING); + + axp.adc1Enable(AXP202_BATT_CUR_ADC1, 1); + axp.enableIRQ(AXP202_VBUS_REMOVED_IRQ | AXP202_VBUS_CONNECT_IRQ | AXP202_BATT_REMOVED_IRQ | AXP202_BATT_CONNECT_IRQ, 1); + axp.clearIRQ(); + + delay(500); + scanI2Cdevice(); + + LORA_LED = sonde.config.led_pout; + pinMode(LORA_LED, OUTPUT); + button1.pin = sonde.config.button_pin; button2.pin = sonde.config.button2_pin; if (button1.pin != 0xff) @@ -1073,58 +1178,55 @@ void setup() } initTouch(); - u8x8 = new U8X8_SSD1306_128X64_NONAME_SW_I2C(/* clock=*/ sonde.config.oled_scl, /* data=*/ sonde.config.oled_sda, /* reset=*/ sonde.config.oled_rst); // Unbuffered, basic graphics, software I2C - u8x8->begin(); + disp.init(); delay(100); - - u8x8->clear(); - - u8x8->setFont(u8x8_font_7x14_1x2_r); - u8x8->drawString(8 - strlen(version_name) / 2, 1, version_name); - u8x8->drawString(8 - strlen(version_id) / 2, 3, version_id); - u8x8->setFont(u8x8_font_chroma48medium8_r); - u8x8->drawString(0, 5, "by Hansi, DL9RDZ"); - u8x8->drawString(1, 6, "Mods by DL2MF"); + Serial.println("Showing welcome display"); + disp.rdis->welcome(); delay(3000); - + Serial.println("Clearing display"); sonde.clearDisplay(); setupWifiList(); + Serial.printf("before disp.initFromFile... layouts is %p", layouts); + + disp.initFromFile(); + Serial.printf("disp.initFromFile... layouts is %p", layouts); + // == show initial values from config.txt ========================= // if (sonde.config.debug == 1) { - u8x8->setFont(u8x8_font_chroma48medium8_r); - u8x8->drawString(0, 0, "Config:"); + disp.rdis->setFont(FONT_SMALL); + disp.rdis->drawString(0, 0, "Config:"); delay(500); itoa(sonde.config.oled_sda, buf, 10); - u8x8->drawString(0, 1, " SDA:"); - u8x8->drawString(6, 1, buf); + disp.rdis->drawString(0, 1, " SDA:"); + disp.rdis->drawString(6, 1, buf); delay(500); itoa(sonde.config.oled_scl, buf, 10); - u8x8->drawString(0, 2, " SCL:"); - u8x8->drawString(6, 2, buf); + disp.rdis->drawString(0, 2, " SCL:"); + disp.rdis->drawString(6, 2, buf); delay(500); itoa(sonde.config.oled_rst, buf, 10); - u8x8->drawString(0, 3, " RST:"); - u8x8->drawString(6, 3, buf); + disp.rdis->drawString(0, 3, " RST:"); + disp.rdis->drawString(6, 3, buf); delay(1000); itoa(sonde.config.led_pout, buf, 10); - u8x8->drawString(0, 4, " LED:"); - u8x8->drawString(6, 4, buf); + disp.rdis->drawString(0, 4, " LED:"); + disp.rdis->drawString(6, 4, buf); delay(500); itoa(sonde.config.spectrum, buf, 10); - u8x8->drawString(0, 5, " SPEC:"); - u8x8->drawString(6, 5, buf); + disp.rdis->drawString(0, 5, " SPEC:"); + disp.rdis->drawString(6, 5, buf); delay(500); itoa(sonde.config.maxsonde, buf, 10); - u8x8->drawString(0, 6, " MAX:"); - u8x8->drawString(6, 6, buf); + disp.rdis->drawString(0, 6, " MAX:"); + disp.rdis->drawString(6, 6, buf); delay(5000); sonde.clearDisplay(); @@ -1186,6 +1288,9 @@ void setup() sonde.setup(); initGPS(); + if (sonde.config.kisstnc.active) { + tncserver.begin(); + } WiFi.onEvent(WiFiEvent); getKeyPress(); // clear key buffer } @@ -1204,7 +1309,7 @@ void enterMode(int mode) { if (mainState == ST_SPECTRUM) { Serial.println("Entering ST_SPECTRUM mode"); sonde.clearDisplay(); - u8x8->setFont(u8x8_font_chroma48medium8_r); + disp.rdis->setFont(FONT_SMALL); specTimer = millis(); //scanner.init(); } else if (mainState == ST_WIFISCAN) { @@ -1260,24 +1365,49 @@ void loopDecoder() { Serial.printf("current main is %d, current rxtask is %d\n", sonde.currentSonde, rxtask.currentSonde); } - - if ((res & 0xff) == 0 && connected) { + if (!tncclient.connected()) { + Serial.println("TNC client not connected"); + tncclient = tncserver.available(); + if (tncclient.connected()) { + Serial.println("new TCP KISS connection"); + } + } + if (tncclient.available()) { + Serial.print("TCP KISS socket: recevied "); + while (tncclient.available()) { + Serial.print(tncclient.read()); // Check if we receive anything from from APRSdroid + } + Serial.println(""); + } + // wifi (axudp) or bluetooth (bttnc) active => send packet + if ((res & 0xff) == 0 && (connected || tncclient.connected() )) { //Send a packet with position information // first check if ID and position lat+lonis ok SondeInfo *s = &sonde.sondeList[rxtask.receiveSonde]; if (s->validID && ((s->validPos & 0x03) == 0x03)) { - Serial.println("Sending position via UDP"); - char raw[201]; const char *str = aprs_senddata(s->lat, s->lon, s->alt, s->hs, s->dir, s->vs, sondeTypeStr[s->type], s->id, "TE0ST", sonde.config.udpfeed.symbol); - int rawlen = aprsstr_mon2raw(str, raw, APRS_MAXLEN); - Serial.print("Sending: "); Serial.println(raw); - udp.beginPacket(sonde.config.udpfeed.host, sonde.config.udpfeed.port); - udp.write((const uint8_t *)raw, rawlen); - udp.endPacket(); + if (connected) { + char raw[201]; + int rawlen = aprsstr_mon2raw(str, raw, APRS_MAXLEN); + Serial.println("Sending position via UDP"); + Serial.print("Sending: "); Serial.println(raw); + udp.beginPacket(sonde.config.udpfeed.host, sonde.config.udpfeed.port); + udp.write((const uint8_t *)raw, rawlen); + udp.endPacket(); + } + if (tncclient.connected()) { + Serial.println("Sending position via TCP"); + char raw[201]; + int rawlen = aprsstr_mon2kiss(str, raw, APRS_MAXLEN); + Serial.print("sending: "); Serial.println(raw); + tncclient.write(raw, rawlen); + } } } + Serial.println("updateDisplay started"); sonde.updateDisplay(); + Serial.println("updateDisplay done"); } @@ -1306,10 +1436,10 @@ void loopSpectrum() { scanner.plotResult(); if (sonde.config.marker != 0) { itoa((sonde.config.startfreq), buf, 10); - u8x8->drawString(0, 1, buf); - u8x8->drawString(7, 1, "MHz"); + disp.rdis->drawString(0, 1, buf); + disp.rdis->drawString(7, 1, "MHz"); itoa((sonde.config.startfreq + 6), buf, 10); - u8x8->drawString(13, 1, buf); + disp.rdis->drawString(13, 1, buf); } if (sonde.config.timer) { int remaining = sonde.config.spectrum - (millis() - specTimer) / 1000; @@ -1318,8 +1448,8 @@ void loopSpectrum() { if (sonde.config.marker != 0) { marker = 1; } - u8x8->drawString(0, 1 + marker, buf); - u8x8->drawString(2, 1 + marker, "Sec."); + disp.rdis->drawString(0, 1 + marker, buf); + disp.rdis->drawString(2, 1 + marker, "Sec."); if (remaining <= 0) { currentDisplay = 0; enterMode(ST_DECODER); @@ -1329,8 +1459,8 @@ void loopSpectrum() { void startSpectrumDisplay() { sonde.clearDisplay(); - u8x8->setFont(u8x8_font_chroma48medium8_r); - u8x8->drawString(0, 0, "Spectrum Scan..."); + disp.rdis->setFont(FONT_SMALL); + disp.rdis->drawString(0, 0, "Spectrum Scan..."); delay(500); enterMode(ST_SPECTRUM); } @@ -1360,6 +1490,7 @@ void enableNetwork(bool enable) { SetupAsyncServer(); udp.begin(WiFi.localIP(), LOCALUDPPORT); MDNS.addService("http", "tcp", 80); + tncserver.begin(); connected = true; } else { MDNS.end(); @@ -1600,8 +1731,8 @@ void loopWifiScan() { return; } // wifi==3 => original mode with non-async wifi setup - u8x8->setFont(u8x8_font_chroma48medium8_r); - u8x8->drawString(0, 0, "WiFi Scan..."); + disp.rdis->setFont(FONT_SMALL); + disp.rdis->drawString(0, 0, "WiFi Scan..."); int line = 0; int cnt = 0; @@ -1614,7 +1745,7 @@ void loopWifiScan() { Serial.print("Network name: "); String ssid = WiFi.SSID(i); Serial.println(ssid); - u8x8->drawString(0, 1 + line, ssid.c_str()); + disp.rdis->drawString(0, 1 + line, ssid.c_str()); line = (line + 1) % 5; Serial.print("Signal strength: "); Serial.println(WiFi.RSSI(i)); @@ -1634,8 +1765,8 @@ void loopWifiScan() { Serial.print("Connecting to: "); Serial.print(fetchWifiSSID(index)); Serial.print(" with password "); Serial.println(fetchWifiPw(index)); - u8x8->drawString(0, 6, "Conn:"); - u8x8->drawString(6, 6, fetchWifiSSID(index)); + disp.rdis->drawString(0, 6, "Conn:"); + disp.rdis->drawString(6, 6, fetchWifiSSID(index)); WiFi.begin(fetchWifiSSID(index), fetchWifiPw(index)); while (WiFi.status() != WL_CONNECTED && cnt < MAXWIFIDELAY) { delay(500); @@ -1649,7 +1780,7 @@ void loopWifiScan() { Serial.print(" with password "); Serial.println(fetchWifiPw(index)); delay(500); } - u8x8->drawString(15, 7, _scan[cnt & 1]); + disp.rdis->drawString(15, 7, _scan[cnt & 1]); cnt++; } } @@ -1660,8 +1791,8 @@ void loopWifiScan() { IPAddress myIP = WiFi.softAPIP(); Serial.print("AP IP address: "); Serial.println(myIP); - u8x8->drawString(0, 6, "AP: "); - u8x8->drawString(6, 6, networks[0].id.c_str()); + disp.rdis->drawString(0, 6, "AP: "); + disp.rdis->drawString(6, 6, networks[0].id.c_str()); delay(3000); } else { Serial.println(""); @@ -1707,17 +1838,17 @@ void execOTA() { int contentLength = 0; bool isValidContentType = false; sonde.clearDisplay(); - u8x8->setFont(u8x8_font_chroma48medium8_r); - u8x8->drawString(0, 0, "C:"); + disp.rdis->setFont(FONT_SMALL); + disp.rdis->drawString(0, 0, "C:"); String dispHost = updateHost.substring(0, 14); - u8x8->drawString(2, 0, dispHost.c_str()); + disp.rdis->drawString(2, 0, dispHost.c_str()); Serial.println("Connecting to: " + updateHost); // Connect to Update host if (client.connect(updateHost.c_str(), updatePort)) { // Connection succeeded, fecthing the bin Serial.println("Fetching bin: " + String(*updateBin)); - u8x8->drawString(0, 1, "Fetching update"); + disp.rdis->drawString(0, 1, "Fetching update"); // Get the contents of the bin file client.print(String("GET ") + *updateBin + " HTTP/1.1\r\n" + @@ -1810,22 +1941,22 @@ void execOTA() { // Check what is the contentLength and if content type is `application/octet-stream` Serial.println("contentLength : " + String(contentLength) + ", isValidContentType : " + String(isValidContentType)); - u8x8->drawString(0, 2, "Len: "); + disp.rdis->drawString(0, 2, "Len: "); String cls = String(contentLength); - u8x8->drawString(5, 2, cls.c_str()); + disp.rdis->drawString(5, 2, cls.c_str()); // check contentLength and content type if (contentLength && isValidContentType) { // Check if there is enough to OTA Update bool canBegin = Update.begin(contentLength); - u8x8->drawString(0, 4, "Starting update"); + disp.rdis->drawString(0, 4, "Starting update"); // If yes, begin if (canBegin) { Serial.println("Begin OTA. This may take 2 - 5 mins to complete. Things might be quite for a while.. Patience!"); // No activity would appear on the Serial monitor // So be patient. This may take 2 - 5mins to complete - u8x8->drawString(0, 5, "Please wait!"); + disp.rdis->drawString(0, 5, "Please wait!"); size_t written = Update.writeStream(client); if (written == contentLength) { @@ -1840,7 +1971,7 @@ void execOTA() { Serial.println("OTA done!"); if (Update.isFinished()) { Serial.println("Update successfully completed. Rebooting."); - u8x8->drawString(0, 7, "Rebooting...."); + disp.rdis->drawString(0, 7, "Rebooting...."); delay(1000); ESP.restart(); } else { diff --git a/RX_FSK/data/config.txt b/RX_FSK/data/config.txt index bbd1e4a..a6cd674 100644 --- a/RX_FSK/data/config.txt +++ b/RX_FSK/data/config.txt @@ -12,10 +12,17 @@ led_pout=9 # OLED Setup is depending on hardware of LoRa board # TTGO v1: SDA=4 SCL=15, RST=16 # TTGO v2: SDA=21 SCL=22, RST=16 +# T-BEAM, OLED: SDA=21, SCL=22, RST=16 +# T-BEAM, ILI9225: SDA=4, CLK=21, RS=2, RST=22, CS=0 # No specification in config file: try autodetection (gpio4 pin level at startup) +# +# disptype: 0=OLED, 1=ILI9225 +#disptype=0 #oled_sda=21 #oled_scl=22 -oled_rst=16 +#oled_rst=16 +#tft_rs=2 +#tft_cs=0 #gps_rxd=-1 gps_txd=-1 #-------------------------------# @@ -25,6 +32,9 @@ maxsonde=20 debug=0 # wifi mode: 1=client in background; 2=AP in background; 3=client on startup, ap if failure wifi=3 +# TCP/IP KISS TNC in port 14590 for APRSdroid (0=disabled, 1=enabled) +kisstnc.active = 1 + # display mode: 1=standard 2=fieldmode 3=field w/sondetype display=1 #-------------------------------# diff --git a/RX_FSK/data/screens.txt b/RX_FSK/data/screens.txt new file mode 100644 index 0000000..ffbca23 --- /dev/null +++ b/RX_FSK/data/screens.txt @@ -0,0 +1,143 @@ +# Definition of display content and action behaviour +# +# Timer: (view timer, rx timer, norx timer) +# - value -1: timer is disabled; value>=0: timer fires after (value) seconds +# - view timer: time since current view (display mode and sonde) was started +# - rx timer: time since when sonde data has been received continuously (trigger immediatly after RX) +# - norx timer: time since when no sonde data has been received continuously +# (rx and norx timer is started after tuning a new frequency and receiving a signal or not receiving +# anything for a 1s period) +# +# Actions: +# - W: activate WiFi scan +# - F: activate frequency spectrum display +# - 0: activate "Scan:" display (this is basically just display mode 0) +# - x: (1..N): activate display mode x +# - D: activate default receiver display (display mode specified in config) +# - +: advance to next active sonde from QRG config +# - #: no action +# +# Display content (lower/upper case: small/large font) +# line,column=content +# +# XText : Text +# F(suffix): frequency (with suffix, e.g., " MHz") +# L latitade +# O lOngitute +# A altitude +# H hor. speed +# V vert. speef +# Ix sonde ID (dfm format by x: d=>dxlaprs, a=>autorx, s=>real serial number) +# Q signal quality statistics bar +# T type string (RS41/DFM9/DFM6/RS92) +# C afC value +# N ip address (only tiny font) +# S launch site +# Mx telemetry value x (t temp p preassure h hyg) +# Gx value relativ to GPS reference point (x: D dist, I direction) GV. GPS valid symbol; GL, GO, GA: ref lat long alt +# R RSSI +########### +# +# Default configuration for "Scanner" display: +# - view timer disabled; rx timer=0; norx timer = 0 +# => after 1 second immediately an action is triggered +# (norx: go to next sonde; rx: go to default receiver display) +# - key1 actions: D,0,F,W +# => Button press activates default receiver view, double press does nothing +# Mid press activates Spectrum display, long press activates Wifi scan +# - key2 has no function +@Scanner:5 +timer=-1,0,0 +key1action=D,#,F,W +key2action=#,#,#,# +timeaction=#,D,+ +0,0=XScan: +0,8=T +3,0=F MHz +5,0=S +7,5=n + +############ +# Default configuration for "Legacy" display: +# - view timer=-1, rx timer=-1 (disabled); norx timer=20000 (or -1 for "old" behaviour) +# => norx timer fires after not receiving a singla for 20 seconds +# - key1 actions: +,0,F,W +# => Button1 press: next sonde; double press => @Scanner display +# => Mid press activates Spectrum display, long press activates Wifi scan +# - key2 actions: 2,#,#,# +# => BUtton2 activates display 2 (@Field) +# - timer actions: #,#,0 +# (norx timer: if no signal for >20 seconds: go back to scanner mode) +# +@Legacy:11 +timer=-1,-1,20000 +key1action=+,0,F,W +key2action=2,#,#,# +timeaction=#,#,0 +0,5=f MHz +1,8=c +0,0=t +1,0=is +2,0=L +4,0=O +2,10=a +3,10=h +4,9=v +6,0=R +6,7=Q + +############ +# Configuratoon for "Field" display (display 2) +# similar to @Legacy, but no norx timer, and Key2 goes to display 4 +@Field:7 +timer=-1,-1,-1 +key1action=+,0,F,W +key2action=3,#,#,# +timeaction=#,#,# +2,0=L +4,0=O +3,10=h +4,9=v +0,0=Is +6,0=A +6,7=Q + +############ +# Configuration for "Field2" display (display 3) +# similar to @Field +@Field2:9 +timer=-1,-1,-1 +key1action=+,0,F,W +key2action=4,#,#,# +timeaction=#,#,# +2,0=L +4,0=O +1,12=t +0,9=f +3,10=h +4,9=v +0,0=Is +6,0=A +6,7=Q + +############# +# Configuration for "GPS" display +# not yet the final version, just for testing +@GPSDIST:14 +timer=-1,-1,-1 +key1action=+,0,F,W +key2action=1,#,#,# +timeaction=#,#,# +0,0=Is +0,9=f +1,12=t +2,0=L +4,0=O +2,10=a +3,10=h +4,9=v +6,7=Q +7,0=gV +7,2=xd= +7,4=gD +7,12=gI° diff --git a/RX_FSK/version.h b/RX_FSK/version.h index 98b3b87..5b414a2 100644 --- a/RX_FSK/version.h +++ b/RX_FSK/version.h @@ -1,2 +1,2 @@ -const char *version_name = "RDZ_TTGO_SONDE"; -const char *version_id = "master v0.7.0"; +const char *version_name = "rdzTTGOsonde"; +const char *version_id = "master_v0.7.1"; diff --git a/Setup.md b/Setup.md index dab887b..84d234a 100644 --- a/Setup.md +++ b/Setup.md @@ -34,6 +34,7 @@ Select Tools -> Library Manager Install "U8g2" Install "MicroNMEA" +Install "TFT_22_ILI9225" ## Additional libraries, part 2 diff --git a/libraries/SX1278FSK/SX1278FSK.cpp b/libraries/SX1278FSK/SX1278FSK.cpp index a9f2bfd..7bff0a0 100644 --- a/libraries/SX1278FSK/SX1278FSK.cpp +++ b/libraries/SX1278FSK/SX1278FSK.cpp @@ -12,6 +12,7 @@ #include "SX1278FSK.h" #include "SPI.h" #include +#include SX1278FSK::SX1278FSK() { @@ -19,7 +20,7 @@ SX1278FSK::SX1278FSK() }; - +static SPISettings spiset = SPISettings(40000000L, MSBFIRST, SPI_MODE0); /* Function: Turns the module ON. @@ -97,6 +98,7 @@ byte SX1278FSK::readRegister(byte address) { byte value = 0x00; + SPI.beginTransaction(spiset); digitalWrite(SX1278_SS,LOW); //delay(1); @@ -104,6 +106,7 @@ byte SX1278FSK::readRegister(byte address) SPI.transfer(address); value = SPI.transfer(0x00); digitalWrite(SX1278_SS,HIGH); + SPI.endTransaction(); #if (SX1278FSK_debug_mode > 1) if(address!=0x3F) { @@ -128,6 +131,7 @@ Parameters: */ void SX1278FSK::writeRegister(byte address, byte data) { + SPI.beginTransaction(spiset); digitalWrite(SX1278_SS,LOW); //delay(1); @@ -135,6 +139,7 @@ void SX1278FSK::writeRegister(byte address, byte data) SPI.transfer(address); SPI.transfer(data); digitalWrite(SX1278_SS,HIGH); + SPI.endTransaction(); #if (SX1278FSK_debug_mode > 1) Serial.print(F("## Writing: ##\t")); diff --git a/libraries/SondeLib/DefaultFonts.c b/libraries/SondeLib/DefaultFonts.c new file mode 100644 index 0000000..665c6ac --- /dev/null +++ b/libraries/SondeLib/DefaultFonts.c @@ -0,0 +1,367 @@ +// SPDX-License-Identifier: GPL-3.0 +// original source: https://github.com/Nkawu/TFT_22_ILI9225 + + +#ifdef __AVR__ + #include +#elif defined(ESP8266) || defined(ESP32) + #include +#endif + +#if defined(ARDUINO_ARCH_SAM) || defined(ARDUINO_ARCH_SAMD) || defined(ARDUINO_ARCH_STM32) || defined(ARDUINO_ARCH_STM32F1) || defined(STM32F1) || defined(ESP32) + #define PROGMEM + #define fontdatatype const char +#else + #define fontdatatype const uint8_t +#endif + +//Font Generated by MikroElektronika GLCD Font Creator 1.2.0.0 +//MikroElektronika 2011 +//http://www.mikroe.com + +//GLCD FontName : Terminal6x8 +//GLCD FontSize : 6 x 8 + +fontdatatype Terminal6x8[] PROGMEM = { + 0x06, 0x08, 0x20, 0x60, + 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char + 0x05, 0x00, 0x00, 0x06, 0x5F, 0x06, 0x00, // Code for char ! + 0x06, 0x00, 0x07, 0x03, 0x00, 0x07, 0x03, // Code for char " + 0x06, 0x00, 0x24, 0x7E, 0x24, 0x7E, 0x24, // Code for char # + 0x05, 0x00, 0x24, 0x2B, 0x6A, 0x12, 0x00, // Code for char $ + 0x06, 0x00, 0x63, 0x13, 0x08, 0x64, 0x63, // Code for char % + 0x06, 0x00, 0x36, 0x49, 0x56, 0x20, 0x50, // Code for char & + 0x04, 0x00, 0x00, 0x07, 0x03, 0x00, 0x00, // Code for char ' + 0x04, 0x00, 0x00, 0x3E, 0x41, 0x00, 0x00, // Code for char ( + 0x04, 0x00, 0x00, 0x41, 0x3E, 0x00, 0x00, // Code for char ) + 0x06, 0x00, 0x08, 0x3E, 0x1C, 0x3E, 0x08, // Code for char * + 0x06, 0x00, 0x08, 0x08, 0x3E, 0x08, 0x08, // Code for char + + 0x04, 0x00, 0x00, 0xE0, 0x60, 0x00, 0x00, // Code for char , + 0x06, 0x00, 0x08, 0x08, 0x08, 0x08, 0x08, // Code for char - + 0x04, 0x00, 0x00, 0x60, 0x60, 0x00, 0x00, // Code for char . + 0x06, 0x00, 0x20, 0x10, 0x08, 0x04, 0x02, // Code for char / + 0x06, 0x00, 0x3E, 0x51, 0x49, 0x45, 0x3E, // Code for char 0 + 0x05, 0x00, 0x00, 0x42, 0x7F, 0x40, 0x00, // Code for char 1 + 0x06, 0x00, 0x62, 0x51, 0x49, 0x49, 0x46, // Code for char 2 + 0x06, 0x00, 0x22, 0x49, 0x49, 0x49, 0x36, // Code for char 3 + 0x06, 0x00, 0x18, 0x14, 0x12, 0x7F, 0x10, // Code for char 4 + 0x06, 0x00, 0x2F, 0x49, 0x49, 0x49, 0x31, // Code for char 5 + 0x06, 0x00, 0x3C, 0x4A, 0x49, 0x49, 0x30, // Code for char 6 + 0x06, 0x00, 0x01, 0x71, 0x09, 0x05, 0x03, // Code for char 7 + 0x06, 0x00, 0x36, 0x49, 0x49, 0x49, 0x36, // Code for char 8 + 0x06, 0x00, 0x06, 0x49, 0x49, 0x29, 0x1E, // Code for char 9 + 0x04, 0x00, 0x00, 0x6C, 0x6C, 0x00, 0x00, // Code for char : + 0x04, 0x00, 0x00, 0xEC, 0x6C, 0x00, 0x00, // Code for char ; + 0x05, 0x00, 0x08, 0x14, 0x22, 0x41, 0x00, // Code for char < + 0x06, 0x00, 0x24, 0x24, 0x24, 0x24, 0x24, // Code for char = + 0x06, 0x00, 0x00, 0x41, 0x22, 0x14, 0x08, // Code for char > + 0x06, 0x00, 0x02, 0x01, 0x59, 0x09, 0x06, // Code for char ? + 0x06, 0x00, 0x3E, 0x41, 0x5D, 0x55, 0x1E, // Code for char @ + 0x06, 0x00, 0x7E, 0x11, 0x11, 0x11, 0x7E, // Code for char A + 0x06, 0x00, 0x7F, 0x49, 0x49, 0x49, 0x36, // Code for char B + 0x06, 0x00, 0x3E, 0x41, 0x41, 0x41, 0x22, // Code for char C + 0x06, 0x00, 0x7F, 0x41, 0x41, 0x41, 0x3E, // Code for char D + 0x06, 0x00, 0x7F, 0x49, 0x49, 0x49, 0x41, // Code for char E + 0x06, 0x00, 0x7F, 0x09, 0x09, 0x09, 0x01, // Code for char F + 0x06, 0x00, 0x3E, 0x41, 0x49, 0x49, 0x7A, // Code for char G + 0x06, 0x00, 0x7F, 0x08, 0x08, 0x08, 0x7F, // Code for char H + 0x05, 0x00, 0x00, 0x41, 0x7F, 0x41, 0x00, // Code for char I + 0x06, 0x00, 0x30, 0x40, 0x40, 0x40, 0x3F, // Code for char J + 0x06, 0x00, 0x7F, 0x08, 0x14, 0x22, 0x41, // Code for char K + 0x06, 0x00, 0x7F, 0x40, 0x40, 0x40, 0x40, // Code for char L + 0x06, 0x00, 0x7F, 0x02, 0x04, 0x02, 0x7F, // Code for char M + 0x06, 0x00, 0x7F, 0x02, 0x04, 0x08, 0x7F, // Code for char N + 0x06, 0x00, 0x3E, 0x41, 0x41, 0x41, 0x3E, // Code for char O + 0x06, 0x00, 0x7F, 0x09, 0x09, 0x09, 0x06, // Code for char P + 0x06, 0x00, 0x3E, 0x41, 0x51, 0x21, 0x5E, // Code for char Q + 0x06, 0x00, 0x7F, 0x09, 0x09, 0x19, 0x66, // Code for char R + 0x06, 0x00, 0x26, 0x49, 0x49, 0x49, 0x32, // Code for char S + 0x06, 0x00, 0x01, 0x01, 0x7F, 0x01, 0x01, // Code for char T + 0x06, 0x00, 0x3F, 0x40, 0x40, 0x40, 0x3F, // Code for char U + 0x06, 0x00, 0x1F, 0x20, 0x40, 0x20, 0x1F, // Code for char V + 0x06, 0x00, 0x3F, 0x40, 0x3C, 0x40, 0x3F, // Code for char W + 0x06, 0x00, 0x63, 0x14, 0x08, 0x14, 0x63, // Code for char X + 0x06, 0x00, 0x07, 0x08, 0x70, 0x08, 0x07, // Code for char Y + 0x05, 0x00, 0x71, 0x49, 0x45, 0x43, 0x00, // Code for char Z + 0x05, 0x00, 0x00, 0x7F, 0x41, 0x41, 0x00, // Code for char [ + 0x06, 0x00, 0x02, 0x04, 0x08, 0x10, 0x20, // Code for char BackSlash + 0x05, 0x00, 0x00, 0x41, 0x41, 0x7F, 0x00, // Code for char ] + 0x06, 0x00, 0x04, 0x02, 0x01, 0x02, 0x04, // Code for char ^ + 0x06, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, // Code for char _ + 0x04, 0x00, 0x00, 0x03, 0x07, 0x00, 0x00, // Code for char ` + 0x06, 0x00, 0x20, 0x54, 0x54, 0x54, 0x78, // Code for char a + 0x06, 0x00, 0x7F, 0x44, 0x44, 0x44, 0x38, // Code for char b + 0x06, 0x00, 0x38, 0x44, 0x44, 0x44, 0x28, // Code for char c + 0x06, 0x00, 0x38, 0x44, 0x44, 0x44, 0x7F, // Code for char d + 0x06, 0x00, 0x38, 0x54, 0x54, 0x54, 0x08, // Code for char e + 0x05, 0x00, 0x08, 0x7E, 0x09, 0x09, 0x00, // Code for char f + 0x06, 0x00, 0x18, 0xA4, 0xA4, 0xA4, 0x7C, // Code for char g + 0x05, 0x00, 0x7F, 0x04, 0x04, 0x78, 0x00, // Code for char h + 0x05, 0x00, 0x00, 0x00, 0x7D, 0x40, 0x00, // Code for char i + 0x05, 0x00, 0x40, 0x80, 0x84, 0x7D, 0x00, // Code for char j + 0x05, 0x00, 0x7F, 0x10, 0x28, 0x44, 0x00, // Code for char k + 0x05, 0x00, 0x00, 0x00, 0x7F, 0x40, 0x00, // Code for char l + 0x06, 0x00, 0x7C, 0x04, 0x18, 0x04, 0x78, // Code for char m + 0x05, 0x00, 0x7C, 0x04, 0x04, 0x78, 0x00, // Code for char n + 0x06, 0x00, 0x38, 0x44, 0x44, 0x44, 0x38, // Code for char o + 0x06, 0x00, 0xFC, 0x44, 0x44, 0x44, 0x38, // Code for char p + 0x06, 0x00, 0x38, 0x44, 0x44, 0x44, 0xFC, // Code for char q + 0x06, 0x00, 0x44, 0x78, 0x44, 0x04, 0x08, // Code for char r + 0x06, 0x00, 0x08, 0x54, 0x54, 0x54, 0x20, // Code for char s + 0x05, 0x00, 0x04, 0x3E, 0x44, 0x24, 0x00, // Code for char t + 0x05, 0x00, 0x3C, 0x40, 0x20, 0x7C, 0x00, // Code for char u + 0x06, 0x00, 0x1C, 0x20, 0x40, 0x20, 0x1C, // Code for char v + 0x06, 0x00, 0x3C, 0x60, 0x30, 0x60, 0x3C, // Code for char w + 0x05, 0x00, 0x6C, 0x10, 0x10, 0x6C, 0x00, // Code for char x + 0x05, 0x00, 0x9C, 0xA0, 0x60, 0x3C, 0x00, // Code for char y + 0x05, 0x00, 0x64, 0x54, 0x54, 0x4C, 0x00, // Code for char z + 0x05, 0x00, 0x08, 0x3E, 0x41, 0x41, 0x00, // Code for char { + 0x04, 0x00, 0x00, 0x00, 0x77, 0x00, 0x00, // Code for char | + 0x06, 0x00, 0x00, 0x41, 0x41, 0x3E, 0x08, // Code for char } + 0x05, 0x00, 0x02, 0x01, 0x02, 0x01, 0x00, // Code for char ~ + 0x06, 0x00, 0x3C, 0x26, 0x23, 0x26, 0x3C // Code for char  + }; + + +//Font Generated by MikroElektronika GLCD Font Creator 1.2.0.0 +//MikroElektronika 2011 +//http://www.mikroe.com + +//GLCD FontName : Terminal11x16 +//GLCD FontSize : 11 x 16 + +fontdatatype Terminal11x16[] PROGMEM = { + 0x0B, 0x10, 0x20, 0x60, + 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char + 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7C, 0x00, 0xFF, 0x33, 0xFF, 0x33, 0x7C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char ! + 0x08, 0x00, 0x00, 0x00, 0x00, 0x3C, 0x00, 0x3C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3C, 0x00, 0x3C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char " + 0x0B, 0x00, 0x02, 0x10, 0x1E, 0x90, 0x1F, 0xF0, 0x03, 0x7E, 0x02, 0x1E, 0x1E, 0x90, 0x1F, 0xF0, 0x03, 0x7E, 0x02, 0x1E, 0x00, 0x10, 0x00, // Code for char # + 0x09, 0x00, 0x00, 0x78, 0x04, 0xFC, 0x0C, 0xCC, 0x0C, 0xFF, 0x3F, 0xFF, 0x3F, 0xCC, 0x0C, 0xCC, 0x0F, 0x88, 0x07, 0x00, 0x00, 0x00, 0x00, // Code for char $ + 0x0B, 0x00, 0x30, 0x38, 0x38, 0x38, 0x1C, 0x38, 0x0E, 0x00, 0x07, 0x80, 0x03, 0xC0, 0x01, 0xE0, 0x38, 0x70, 0x38, 0x38, 0x38, 0x1C, 0x00, // Code for char % + 0x0A, 0x00, 0x00, 0x00, 0x1F, 0xB8, 0x3F, 0xFC, 0x31, 0xC6, 0x21, 0xE2, 0x37, 0x3E, 0x1E, 0x1C, 0x1C, 0x00, 0x36, 0x00, 0x22, 0x00, 0x00, // Code for char & + 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x27, 0x00, 0x3F, 0x00, 0x1F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char ' + 0x08, 0x00, 0x00, 0x00, 0x00, 0xF0, 0x03, 0xFC, 0x0F, 0xFE, 0x1F, 0x07, 0x38, 0x01, 0x20, 0x01, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char ( + 0x08, 0x00, 0x00, 0x00, 0x00, 0x01, 0x20, 0x01, 0x20, 0x07, 0x38, 0xFE, 0x1F, 0xFC, 0x0F, 0xF0, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char ) + 0x09, 0x00, 0x00, 0x98, 0x0C, 0xB8, 0x0E, 0xE0, 0x03, 0xF8, 0x0F, 0xF8, 0x0F, 0xE0, 0x03, 0xB8, 0x0E, 0x98, 0x0C, 0x00, 0x00, 0x00, 0x00, // Code for char * + 0x09, 0x00, 0x00, 0x80, 0x01, 0x80, 0x01, 0x80, 0x01, 0xF0, 0x0F, 0xF0, 0x0F, 0x80, 0x01, 0x80, 0x01, 0x80, 0x01, 0x00, 0x00, 0x00, 0x00, // Code for char + + 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xB8, 0x00, 0xF8, 0x00, 0x78, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char , + 0x09, 0x00, 0x00, 0x80, 0x01, 0x80, 0x01, 0x80, 0x01, 0x80, 0x01, 0x80, 0x01, 0x80, 0x01, 0x80, 0x01, 0x80, 0x01, 0x00, 0x00, 0x00, 0x00, // Code for char - + 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0x00, 0x38, 0x00, 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char . + 0x0B, 0x00, 0x18, 0x00, 0x1C, 0x00, 0x0E, 0x00, 0x07, 0x80, 0x03, 0xC0, 0x01, 0xE0, 0x00, 0x70, 0x00, 0x38, 0x00, 0x1C, 0x00, 0x0E, 0x00, // Code for char / + 0x0B, 0xF8, 0x07, 0xFE, 0x1F, 0x06, 0x1E, 0x03, 0x33, 0x83, 0x31, 0xC3, 0x30, 0x63, 0x30, 0x33, 0x30, 0x1E, 0x18, 0xFE, 0x1F, 0xF8, 0x07, // Code for char 0 + 0x0A, 0x00, 0x00, 0x00, 0x00, 0x0C, 0x30, 0x0C, 0x30, 0x0E, 0x30, 0xFF, 0x3F, 0xFF, 0x3F, 0x00, 0x30, 0x00, 0x30, 0x00, 0x30, 0x00, 0x00, // Code for char 1 + 0x0B, 0x1C, 0x30, 0x1E, 0x38, 0x07, 0x3C, 0x03, 0x3E, 0x03, 0x37, 0x83, 0x33, 0xC3, 0x31, 0xE3, 0x30, 0x77, 0x30, 0x3E, 0x30, 0x1C, 0x30, // Code for char 2 + 0x0B, 0x0C, 0x0C, 0x0E, 0x1C, 0x07, 0x38, 0xC3, 0x30, 0xC3, 0x30, 0xC3, 0x30, 0xC3, 0x30, 0xC3, 0x30, 0xE7, 0x39, 0x7E, 0x1F, 0x3C, 0x0E, // Code for char 3 + 0x0B, 0xC0, 0x03, 0xE0, 0x03, 0x70, 0x03, 0x38, 0x03, 0x1C, 0x03, 0x0E, 0x03, 0x07, 0x03, 0xFF, 0x3F, 0xFF, 0x3F, 0x00, 0x03, 0x00, 0x03, // Code for char 4 + 0x0B, 0x3F, 0x0C, 0x7F, 0x1C, 0x63, 0x38, 0x63, 0x30, 0x63, 0x30, 0x63, 0x30, 0x63, 0x30, 0x63, 0x30, 0xE3, 0x38, 0xC3, 0x1F, 0x83, 0x0F, // Code for char 5 + 0x0B, 0xC0, 0x0F, 0xF0, 0x1F, 0xF8, 0x39, 0xDC, 0x30, 0xCE, 0x30, 0xC7, 0x30, 0xC3, 0x30, 0xC3, 0x30, 0xC3, 0x39, 0x80, 0x1F, 0x00, 0x0F, // Code for char 6 + 0x0B, 0x03, 0x00, 0x03, 0x00, 0x03, 0x00, 0x03, 0x30, 0x03, 0x3C, 0x03, 0x0F, 0xC3, 0x03, 0xF3, 0x00, 0x3F, 0x00, 0x0F, 0x00, 0x03, 0x00, // Code for char 7 + 0x0B, 0x00, 0x0F, 0xBC, 0x1F, 0xFE, 0x39, 0xE7, 0x30, 0xC3, 0x30, 0xC3, 0x30, 0xC3, 0x30, 0xE7, 0x30, 0xFE, 0x39, 0xBC, 0x1F, 0x00, 0x0F, // Code for char 8 + 0x0B, 0x3C, 0x00, 0x7E, 0x00, 0xE7, 0x30, 0xC3, 0x30, 0xC3, 0x30, 0xC3, 0x38, 0xC3, 0x1C, 0xC3, 0x0E, 0xE7, 0x07, 0xFE, 0x03, 0xFC, 0x00, // Code for char 9 + 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x70, 0x1C, 0x70, 0x1C, 0x70, 0x1C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char : + 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x70, 0x9C, 0x70, 0xFC, 0x70, 0x7C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char ; + 0x09, 0x00, 0x00, 0xC0, 0x00, 0xE0, 0x01, 0xF0, 0x03, 0x38, 0x07, 0x1C, 0x0E, 0x0E, 0x1C, 0x07, 0x38, 0x03, 0x30, 0x00, 0x00, 0x00, 0x00, // Code for char < + 0x0A, 0x00, 0x00, 0x60, 0x06, 0x60, 0x06, 0x60, 0x06, 0x60, 0x06, 0x60, 0x06, 0x60, 0x06, 0x60, 0x06, 0x60, 0x06, 0x60, 0x06, 0x00, 0x00, // Code for char = + 0x09, 0x00, 0x00, 0x03, 0x30, 0x07, 0x38, 0x0E, 0x1C, 0x1C, 0x0E, 0x38, 0x07, 0xF0, 0x03, 0xE0, 0x01, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char > + 0x0A, 0x1C, 0x00, 0x1E, 0x00, 0x07, 0x00, 0x03, 0x00, 0x83, 0x37, 0xC3, 0x37, 0xE3, 0x00, 0x77, 0x00, 0x3E, 0x00, 0x1C, 0x00, 0x00, 0x00, // Code for char ? + 0x0B, 0xF8, 0x0F, 0xFE, 0x1F, 0x07, 0x18, 0xF3, 0x33, 0xFB, 0x37, 0x1B, 0x36, 0xFB, 0x37, 0xFB, 0x37, 0x07, 0x36, 0xFE, 0x03, 0xF8, 0x01, // Code for char @ + 0x0A, 0x00, 0x38, 0x00, 0x3F, 0xE0, 0x07, 0xFC, 0x06, 0x1F, 0x06, 0x1F, 0x06, 0xFC, 0x06, 0xE0, 0x07, 0x00, 0x3F, 0x00, 0x38, 0x00, 0x00, // Code for char A + 0x0A, 0xFF, 0x3F, 0xFF, 0x3F, 0xC3, 0x30, 0xC3, 0x30, 0xC3, 0x30, 0xC3, 0x30, 0xE7, 0x30, 0xFE, 0x39, 0xBC, 0x1F, 0x00, 0x0F, 0x00, 0x00, // Code for char B + 0x0A, 0xF0, 0x03, 0xFC, 0x0F, 0x0E, 0x1C, 0x07, 0x38, 0x03, 0x30, 0x03, 0x30, 0x03, 0x30, 0x07, 0x38, 0x0E, 0x1C, 0x0C, 0x0C, 0x00, 0x00, // Code for char C + 0x0A, 0xFF, 0x3F, 0xFF, 0x3F, 0x03, 0x30, 0x03, 0x30, 0x03, 0x30, 0x03, 0x30, 0x07, 0x38, 0x0E, 0x1C, 0xFC, 0x0F, 0xF0, 0x03, 0x00, 0x00, // Code for char D + 0x0A, 0xFF, 0x3F, 0xFF, 0x3F, 0xC3, 0x30, 0xC3, 0x30, 0xC3, 0x30, 0xC3, 0x30, 0xC3, 0x30, 0xC3, 0x30, 0x03, 0x30, 0x03, 0x30, 0x00, 0x00, // Code for char E + 0x0A, 0xFF, 0x3F, 0xFF, 0x3F, 0xC3, 0x00, 0xC3, 0x00, 0xC3, 0x00, 0xC3, 0x00, 0xC3, 0x00, 0xC3, 0x00, 0x03, 0x00, 0x03, 0x00, 0x00, 0x00, // Code for char F + 0x0A, 0xF0, 0x03, 0xFC, 0x0F, 0x0E, 0x1C, 0x07, 0x38, 0x03, 0x30, 0xC3, 0x30, 0xC3, 0x30, 0xC3, 0x30, 0xC7, 0x3F, 0xC6, 0x3F, 0x00, 0x00, // Code for char G + 0x0A, 0xFF, 0x3F, 0xFF, 0x3F, 0xC0, 0x00, 0xC0, 0x00, 0xC0, 0x00, 0xC0, 0x00, 0xC0, 0x00, 0xC0, 0x00, 0xFF, 0x3F, 0xFF, 0x3F, 0x00, 0x00, // Code for char H + 0x08, 0x00, 0x00, 0x00, 0x00, 0x03, 0x30, 0x03, 0x30, 0xFF, 0x3F, 0xFF, 0x3F, 0x03, 0x30, 0x03, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char I + 0x0A, 0x00, 0x0E, 0x00, 0x1E, 0x00, 0x38, 0x00, 0x30, 0x00, 0x30, 0x00, 0x30, 0x00, 0x30, 0x00, 0x38, 0xFF, 0x1F, 0xFF, 0x07, 0x00, 0x00, // Code for char J + 0x0A, 0xFF, 0x3F, 0xFF, 0x3F, 0xC0, 0x00, 0xE0, 0x01, 0xF0, 0x03, 0x38, 0x07, 0x1C, 0x0E, 0x0E, 0x1C, 0x07, 0x38, 0x03, 0x30, 0x00, 0x00, // Code for char K + 0x0A, 0xFF, 0x3F, 0xFF, 0x3F, 0x00, 0x30, 0x00, 0x30, 0x00, 0x30, 0x00, 0x30, 0x00, 0x30, 0x00, 0x30, 0x00, 0x30, 0x00, 0x30, 0x00, 0x00, // Code for char L + 0x0A, 0xFF, 0x3F, 0xFF, 0x3F, 0x1E, 0x00, 0x78, 0x00, 0xE0, 0x01, 0xE0, 0x01, 0x78, 0x00, 0x1E, 0x00, 0xFF, 0x3F, 0xFF, 0x3F, 0x00, 0x00, // Code for char M + 0x0A, 0xFF, 0x3F, 0xFF, 0x3F, 0x0E, 0x00, 0x38, 0x00, 0xF0, 0x00, 0xC0, 0x03, 0x00, 0x07, 0x00, 0x1C, 0xFF, 0x3F, 0xFF, 0x3F, 0x00, 0x00, // Code for char N + 0x0A, 0xF0, 0x03, 0xFC, 0x0F, 0x0E, 0x1C, 0x07, 0x38, 0x03, 0x30, 0x03, 0x30, 0x07, 0x38, 0x0E, 0x1C, 0xFC, 0x0F, 0xF0, 0x03, 0x00, 0x00, // Code for char O + 0x0A, 0xFF, 0x3F, 0xFF, 0x3F, 0x83, 0x01, 0x83, 0x01, 0x83, 0x01, 0x83, 0x01, 0x83, 0x01, 0xC7, 0x01, 0xFE, 0x00, 0x7C, 0x00, 0x00, 0x00, // Code for char P + 0x0A, 0xF0, 0x03, 0xFC, 0x0F, 0x0E, 0x1C, 0x07, 0x38, 0x03, 0x30, 0x03, 0x36, 0x07, 0x3E, 0x0E, 0x1C, 0xFC, 0x3F, 0xF0, 0x33, 0x00, 0x00, // Code for char Q + 0x0A, 0xFF, 0x3F, 0xFF, 0x3F, 0x83, 0x01, 0x83, 0x01, 0x83, 0x03, 0x83, 0x07, 0x83, 0x0F, 0xC7, 0x1D, 0xFE, 0x38, 0x7C, 0x30, 0x00, 0x00, // Code for char R + 0x0A, 0x3C, 0x0C, 0x7E, 0x1C, 0xE7, 0x38, 0xC3, 0x30, 0xC3, 0x30, 0xC3, 0x30, 0xC3, 0x30, 0xC7, 0x39, 0x8E, 0x1F, 0x0C, 0x0F, 0x00, 0x00, // Code for char S + 0x09, 0x00, 0x00, 0x03, 0x00, 0x03, 0x00, 0x03, 0x00, 0xFF, 0x3F, 0xFF, 0x3F, 0x03, 0x00, 0x03, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char T + 0x0A, 0xFF, 0x07, 0xFF, 0x1F, 0x00, 0x38, 0x00, 0x30, 0x00, 0x30, 0x00, 0x30, 0x00, 0x30, 0x00, 0x38, 0xFF, 0x1F, 0xFF, 0x07, 0x00, 0x00, // Code for char U + 0x0A, 0x07, 0x00, 0x3F, 0x00, 0xF8, 0x01, 0xC0, 0x0F, 0x00, 0x3E, 0x00, 0x3E, 0xC0, 0x0F, 0xF8, 0x01, 0x3F, 0x00, 0x07, 0x00, 0x00, 0x00, // Code for char V + 0x0A, 0xFF, 0x3F, 0xFF, 0x3F, 0x00, 0x1C, 0x00, 0x06, 0x80, 0x03, 0x80, 0x03, 0x00, 0x06, 0x00, 0x1C, 0xFF, 0x3F, 0xFF, 0x3F, 0x00, 0x00, // Code for char W + 0x0A, 0x03, 0x30, 0x0F, 0x3C, 0x1C, 0x0E, 0x30, 0x03, 0xE0, 0x01, 0xE0, 0x01, 0x30, 0x03, 0x1C, 0x0E, 0x0F, 0x3C, 0x03, 0x30, 0x00, 0x00, // Code for char X + 0x0A, 0x03, 0x00, 0x0F, 0x00, 0x3C, 0x00, 0xF0, 0x00, 0xC0, 0x3F, 0xC0, 0x3F, 0xF0, 0x00, 0x3C, 0x00, 0x0F, 0x00, 0x03, 0x00, 0x00, 0x00, // Code for char Y + 0x0A, 0x03, 0x30, 0x03, 0x3C, 0x03, 0x3E, 0x03, 0x33, 0xC3, 0x31, 0xE3, 0x30, 0x33, 0x30, 0x1F, 0x30, 0x0F, 0x30, 0x03, 0x30, 0x00, 0x00, // Code for char Z + 0x08, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x3F, 0xFF, 0x3F, 0x03, 0x30, 0x03, 0x30, 0x03, 0x30, 0x03, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char [ + 0x0B, 0x0E, 0x00, 0x1C, 0x00, 0x38, 0x00, 0x70, 0x00, 0xE0, 0x00, 0xC0, 0x01, 0x80, 0x03, 0x00, 0x07, 0x00, 0x0E, 0x00, 0x1C, 0x00, 0x18, // Code for char BackSlash + 0x08, 0x00, 0x00, 0x00, 0x00, 0x03, 0x30, 0x03, 0x30, 0x03, 0x30, 0x03, 0x30, 0xFF, 0x3F, 0xFF, 0x3F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char ] + 0x0B, 0x60, 0x00, 0x70, 0x00, 0x38, 0x00, 0x1C, 0x00, 0x0E, 0x00, 0x07, 0x00, 0x0E, 0x00, 0x1C, 0x00, 0x38, 0x00, 0x70, 0x00, 0x60, 0x00, // Code for char ^ + 0x0B, 0x00, 0xC0, 0x00, 0xC0, 0x00, 0xC0, 0x00, 0xC0, 0x00, 0xC0, 0x00, 0xC0, 0x00, 0xC0, 0x00, 0xC0, 0x00, 0xC0, 0x00, 0xC0, 0x00, 0xC0, // Code for char _ + 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x7E, 0x00, 0x4E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char ` + 0x0A, 0x00, 0x1C, 0x40, 0x3E, 0x60, 0x33, 0x60, 0x33, 0x60, 0x33, 0x60, 0x33, 0x60, 0x33, 0x60, 0x33, 0xE0, 0x3F, 0xC0, 0x3F, 0x00, 0x00, // Code for char a + 0x0A, 0xFF, 0x3F, 0xFF, 0x3F, 0xC0, 0x30, 0x60, 0x30, 0x60, 0x30, 0x60, 0x30, 0x60, 0x30, 0xE0, 0x38, 0xC0, 0x1F, 0x80, 0x0F, 0x00, 0x00, // Code for char b + 0x0A, 0x80, 0x0F, 0xC0, 0x1F, 0xE0, 0x38, 0x60, 0x30, 0x60, 0x30, 0x60, 0x30, 0x60, 0x30, 0x60, 0x30, 0xC0, 0x18, 0x80, 0x08, 0x00, 0x00, // Code for char c + 0x0A, 0x80, 0x0F, 0xC0, 0x1F, 0xE0, 0x38, 0x60, 0x30, 0x60, 0x30, 0x60, 0x30, 0xE0, 0x30, 0xC0, 0x30, 0xFF, 0x3F, 0xFF, 0x3F, 0x00, 0x00, // Code for char d + 0x0A, 0x80, 0x0F, 0xC0, 0x1F, 0xE0, 0x3B, 0x60, 0x33, 0x60, 0x33, 0x60, 0x33, 0x60, 0x33, 0x60, 0x33, 0xC0, 0x13, 0x80, 0x01, 0x00, 0x00, // Code for char e + 0x08, 0xC0, 0x00, 0xC0, 0x00, 0xFC, 0x3F, 0xFE, 0x3F, 0xC7, 0x00, 0xC3, 0x00, 0xC3, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char f + 0x0A, 0x80, 0x03, 0xC0, 0xC7, 0xE0, 0xCE, 0x60, 0xCC, 0x60, 0xCC, 0x60, 0xCC, 0x60, 0xCC, 0x60, 0xE6, 0xE0, 0x7F, 0xE0, 0x3F, 0x00, 0x00, // Code for char g + 0x09, 0xFF, 0x3F, 0xFF, 0x3F, 0xC0, 0x00, 0x60, 0x00, 0x60, 0x00, 0x60, 0x00, 0xE0, 0x00, 0xC0, 0x3F, 0x80, 0x3F, 0x00, 0x00, 0x00, 0x00, // Code for char h + 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x60, 0x30, 0xEC, 0x3F, 0xEC, 0x3F, 0x00, 0x30, 0x00, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char i + 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x60, 0x00, 0xE0, 0x00, 0xC0, 0x60, 0xC0, 0xEC, 0xFF, 0xEC, 0x7F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char j + 0x09, 0x00, 0x00, 0xFF, 0x3F, 0xFF, 0x3F, 0x00, 0x03, 0x80, 0x07, 0xC0, 0x0F, 0xE0, 0x1C, 0x60, 0x38, 0x00, 0x30, 0x00, 0x00, 0x00, 0x00, // Code for char k + 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x03, 0x30, 0xFF, 0x3F, 0xFF, 0x3F, 0x00, 0x30, 0x00, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char l + 0x0A, 0xE0, 0x3F, 0xC0, 0x3F, 0xE0, 0x00, 0xE0, 0x00, 0xC0, 0x3F, 0xC0, 0x3F, 0xE0, 0x00, 0xE0, 0x00, 0xC0, 0x3F, 0x80, 0x3F, 0x00, 0x00, // Code for char m + 0x0A, 0x00, 0x00, 0xE0, 0x3F, 0xE0, 0x3F, 0x60, 0x00, 0x60, 0x00, 0x60, 0x00, 0x60, 0x00, 0xE0, 0x00, 0xC0, 0x3F, 0x80, 0x3F, 0x00, 0x00, // Code for char n + 0x0A, 0x80, 0x0F, 0xC0, 0x1F, 0xE0, 0x38, 0x60, 0x30, 0x60, 0x30, 0x60, 0x30, 0x60, 0x30, 0xE0, 0x38, 0xC0, 0x1F, 0x80, 0x0F, 0x00, 0x00, // Code for char o + 0x0A, 0xE0, 0xFF, 0xE0, 0xFF, 0x60, 0x0C, 0x60, 0x18, 0x60, 0x18, 0x60, 0x18, 0x60, 0x18, 0xE0, 0x1C, 0xC0, 0x0F, 0x80, 0x07, 0x00, 0x00, // Code for char p + 0x0A, 0x80, 0x07, 0xC0, 0x0F, 0xE0, 0x1C, 0x60, 0x18, 0x60, 0x18, 0x60, 0x18, 0x60, 0x18, 0x60, 0x0C, 0xE0, 0xFF, 0xE0, 0xFF, 0x00, 0x00, // Code for char q + 0x0A, 0x00, 0x00, 0xE0, 0x3F, 0xE0, 0x3F, 0xC0, 0x00, 0x60, 0x00, 0x60, 0x00, 0x60, 0x00, 0x60, 0x00, 0xE0, 0x00, 0xC0, 0x00, 0x00, 0x00, // Code for char r + 0x08, 0xC0, 0x11, 0xE0, 0x33, 0x60, 0x33, 0x60, 0x33, 0x60, 0x33, 0x60, 0x33, 0x60, 0x3F, 0x40, 0x1E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char s + 0x08, 0x60, 0x00, 0x60, 0x00, 0xFE, 0x1F, 0xFE, 0x3F, 0x60, 0x30, 0x60, 0x30, 0x60, 0x30, 0x00, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char t + 0x0A, 0xE0, 0x0F, 0xE0, 0x1F, 0x00, 0x38, 0x00, 0x30, 0x00, 0x30, 0x00, 0x30, 0x00, 0x30, 0x00, 0x18, 0xE0, 0x3F, 0xE0, 0x3F, 0x00, 0x00, // Code for char u + 0x0A, 0x60, 0x00, 0xE0, 0x01, 0x80, 0x07, 0x00, 0x1E, 0x00, 0x38, 0x00, 0x38, 0x00, 0x1E, 0x80, 0x07, 0xE0, 0x01, 0x60, 0x00, 0x00, 0x00, // Code for char v + 0x0A, 0xE0, 0x07, 0xE0, 0x1F, 0x00, 0x38, 0x00, 0x1C, 0xE0, 0x0F, 0xE0, 0x0F, 0x00, 0x1C, 0x00, 0x38, 0xE0, 0x1F, 0xE0, 0x07, 0x00, 0x00, // Code for char w + 0x09, 0x60, 0x30, 0xE0, 0x38, 0xC0, 0x1D, 0x80, 0x0F, 0x00, 0x07, 0x80, 0x0F, 0xC0, 0x1D, 0xE0, 0x38, 0x60, 0x30, 0x00, 0x00, 0x00, 0x00, // Code for char x + 0x09, 0x00, 0x00, 0x60, 0x00, 0xE0, 0x81, 0x80, 0xE7, 0x00, 0x7E, 0x00, 0x1E, 0x80, 0x07, 0xE0, 0x01, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char y + 0x09, 0x60, 0x30, 0x60, 0x38, 0x60, 0x3C, 0x60, 0x36, 0x60, 0x33, 0xE0, 0x31, 0xE0, 0x30, 0x60, 0x30, 0x20, 0x30, 0x00, 0x00, 0x00, 0x00, // Code for char z + 0x09, 0x00, 0x00, 0x80, 0x00, 0xC0, 0x01, 0xFC, 0x1F, 0x7E, 0x3F, 0x07, 0x70, 0x03, 0x60, 0x03, 0x60, 0x03, 0x60, 0x00, 0x00, 0x00, 0x00, // Code for char { + 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x3F, 0xFF, 0x3F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char | + 0x09, 0x00, 0x00, 0x03, 0x60, 0x03, 0x60, 0x03, 0x60, 0x07, 0x70, 0x7E, 0x3F, 0xFC, 0x1F, 0xC0, 0x01, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char } + 0x0A, 0x10, 0x00, 0x18, 0x00, 0x0C, 0x00, 0x04, 0x00, 0x0C, 0x00, 0x18, 0x00, 0x10, 0x00, 0x18, 0x00, 0x0C, 0x00, 0x04, 0x00, 0x00, 0x00, // Code for char ~ + 0x0A, 0x00, 0x0F, 0x80, 0x0F, 0xC0, 0x0C, 0x60, 0x0C, 0x30, 0x0C, 0x30, 0x0C, 0x60, 0x0C, 0xC0, 0x0C, 0x80, 0x0F, 0x00, 0x0F, 0x00, 0x00 // Code for char  + }; + + +//Font Generated by MikroElektronika GLCD Font Creator 1.2.0.0 +//MikroElektronika 2011 +//http://www.mikroe.com + +//GLCD FontName : Terminal12x16 +//GLCD FontSize : 12 x 16 + +fontdatatype Terminal12x16[] PROGMEM = { + 0x0C, 0x10, 0x20, 0x60, + 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char + 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7C, 0x00, 0xFF, 0x33, 0xFF, 0x33, 0xFF, 0x33, 0x7C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char ! + 0x09, 0x00, 0x00, 0x00, 0x00, 0x3C, 0x00, 0x3C, 0x00, 0x3C, 0x00, 0x00, 0x00, 0x3C, 0x00, 0x3C, 0x00, 0x3C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char " + 0x0C, 0x00, 0x02, 0x10, 0x1E, 0x90, 0x1F, 0xF0, 0x1F, 0xFE, 0x03, 0x7E, 0x1E, 0x9E, 0x1F, 0xF0, 0x1F, 0xFE, 0x03, 0x7E, 0x02, 0x1E, 0x00, 0x10, 0x00, // Code for char # + 0x0A, 0x00, 0x00, 0x78, 0x04, 0xFC, 0x0C, 0xFC, 0x0C, 0xFF, 0x3F, 0xFF, 0x3F, 0xFF, 0x3F, 0xCC, 0x0F, 0xCC, 0x0F, 0x88, 0x07, 0x00, 0x00, 0x00, 0x00, // Code for char $ + 0x0C, 0x00, 0x30, 0x38, 0x38, 0x38, 0x3C, 0x38, 0x1E, 0x38, 0x0F, 0x80, 0x07, 0xC0, 0x03, 0xE0, 0x39, 0xF0, 0x38, 0x78, 0x38, 0x3C, 0x38, 0x1C, 0x00, // Code for char % + 0x0B, 0x00, 0x00, 0x00, 0x1F, 0xB8, 0x3F, 0xFC, 0x3F, 0xFE, 0x31, 0xE6, 0x37, 0xFE, 0x3F, 0x3E, 0x1E, 0x1C, 0x3E, 0x00, 0x36, 0x00, 0x22, 0x00, 0x00, // Code for char & + 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x27, 0x00, 0x3F, 0x00, 0x3F, 0x00, 0x1F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char ' + 0x09, 0x00, 0x00, 0x00, 0x00, 0xF0, 0x03, 0xFC, 0x0F, 0xFE, 0x1F, 0xFF, 0x3F, 0x07, 0x38, 0x01, 0x20, 0x01, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char ( + 0x09, 0x00, 0x00, 0x00, 0x00, 0x01, 0x20, 0x01, 0x20, 0x07, 0x38, 0xFF, 0x3F, 0xFE, 0x1F, 0xFC, 0x0F, 0xF0, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char ) + 0x0A, 0x00, 0x00, 0x98, 0x0C, 0xB8, 0x0E, 0xF8, 0x0F, 0xF8, 0x0F, 0xF8, 0x0F, 0xF8, 0x0F, 0xF8, 0x0F, 0xB8, 0x0E, 0x98, 0x0C, 0x00, 0x00, 0x00, 0x00, // Code for char * + 0x0A, 0x00, 0x00, 0x80, 0x01, 0x80, 0x01, 0x80, 0x01, 0xF0, 0x0F, 0xF0, 0x0F, 0xF0, 0x0F, 0x80, 0x01, 0x80, 0x01, 0x80, 0x01, 0x00, 0x00, 0x00, 0x00, // Code for char + + 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xB8, 0x00, 0xF8, 0x00, 0xF8, 0x00, 0x78, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char , + 0x0A, 0x00, 0x00, 0x80, 0x01, 0x80, 0x01, 0x80, 0x01, 0x80, 0x01, 0x80, 0x01, 0x80, 0x01, 0x80, 0x01, 0x80, 0x01, 0x80, 0x01, 0x00, 0x00, 0x00, 0x00, // Code for char - + 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0x00, 0x38, 0x00, 0x38, 0x00, 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char . + 0x0C, 0x00, 0x18, 0x00, 0x1C, 0x00, 0x1E, 0x00, 0x0F, 0x80, 0x07, 0xC0, 0x03, 0xE0, 0x01, 0xF0, 0x00, 0x78, 0x00, 0x3C, 0x00, 0x1E, 0x00, 0x0E, 0x00, // Code for char / + 0x0C, 0xF8, 0x07, 0xFE, 0x1F, 0xFE, 0x1F, 0x07, 0x3F, 0x83, 0x33, 0xC3, 0x31, 0xE3, 0x30, 0x73, 0x30, 0x3F, 0x38, 0xFE, 0x1F, 0xFE, 0x1F, 0xF8, 0x07, // Code for char 0 + 0x0B, 0x00, 0x00, 0x00, 0x00, 0x0C, 0x30, 0x0C, 0x30, 0x0E, 0x30, 0xFF, 0x3F, 0xFF, 0x3F, 0xFF, 0x3F, 0x00, 0x30, 0x00, 0x30, 0x00, 0x30, 0x00, 0x00, // Code for char 1 + 0x0C, 0x1C, 0x30, 0x1E, 0x38, 0x1F, 0x3C, 0x07, 0x3E, 0x03, 0x3F, 0x83, 0x37, 0xC3, 0x33, 0xE3, 0x31, 0xF7, 0x30, 0x7F, 0x30, 0x3E, 0x30, 0x1C, 0x30, // Code for char 2 + 0x0C, 0x0C, 0x0C, 0x0E, 0x1C, 0x0F, 0x3C, 0xC7, 0x38, 0xC3, 0x30, 0xC3, 0x30, 0xC3, 0x30, 0xC3, 0x30, 0xE7, 0x39, 0xFF, 0x3F, 0x7E, 0x1F, 0x3C, 0x0E, // Code for char 3 + 0x0C, 0xC0, 0x03, 0xE0, 0x03, 0xF0, 0x03, 0x78, 0x03, 0x3C, 0x03, 0x1E, 0x03, 0x0F, 0x03, 0xFF, 0x3F, 0xFF, 0x3F, 0xFF, 0x3F, 0x00, 0x03, 0x00, 0x03, // Code for char 4 + 0x0C, 0x3F, 0x0C, 0x7F, 0x1C, 0x7F, 0x3C, 0x63, 0x38, 0x63, 0x30, 0x63, 0x30, 0x63, 0x30, 0x63, 0x30, 0xE3, 0x38, 0xE3, 0x3F, 0xC3, 0x1F, 0x83, 0x0F, // Code for char 5 + 0x0C, 0xC0, 0x0F, 0xF0, 0x1F, 0xF8, 0x3F, 0xFC, 0x39, 0xDE, 0x30, 0xCF, 0x30, 0xC7, 0x30, 0xC3, 0x30, 0xC3, 0x39, 0xC3, 0x3F, 0x80, 0x1F, 0x00, 0x0F, // Code for char 6 + 0x0C, 0x03, 0x00, 0x03, 0x00, 0x03, 0x00, 0x03, 0x30, 0x03, 0x3C, 0x03, 0x3F, 0xC3, 0x0F, 0xF3, 0x03, 0xFF, 0x00, 0x3F, 0x00, 0x0F, 0x00, 0x03, 0x00, // Code for char 7 + 0x0C, 0x00, 0x0F, 0xBC, 0x1F, 0xFE, 0x3F, 0xFF, 0x39, 0xE7, 0x30, 0xC3, 0x30, 0xC3, 0x30, 0xE7, 0x30, 0xFF, 0x39, 0xFE, 0x3F, 0xBC, 0x1F, 0x00, 0x0F, // Code for char 8 + 0x0C, 0x3C, 0x00, 0x7E, 0x00, 0xFF, 0x30, 0xE7, 0x30, 0xC3, 0x30, 0xC3, 0x38, 0xC3, 0x3C, 0xC3, 0x1E, 0xE7, 0x0F, 0xFF, 0x07, 0xFE, 0x03, 0xFC, 0x00, // Code for char 9 + 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x70, 0x1C, 0x70, 0x1C, 0x70, 0x1C, 0x70, 0x1C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char : + 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x70, 0x9C, 0x70, 0xFC, 0x70, 0xFC, 0x70, 0x7C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char ; + 0x0A, 0x00, 0x00, 0xC0, 0x00, 0xE0, 0x01, 0xF0, 0x03, 0xF8, 0x07, 0x3C, 0x0F, 0x1E, 0x1E, 0x0F, 0x3C, 0x07, 0x38, 0x03, 0x30, 0x00, 0x00, 0x00, 0x00, // Code for char < + 0x0B, 0x00, 0x00, 0x60, 0x06, 0x60, 0x06, 0x60, 0x06, 0x60, 0x06, 0x60, 0x06, 0x60, 0x06, 0x60, 0x06, 0x60, 0x06, 0x60, 0x06, 0x60, 0x06, 0x00, 0x00, // Code for char = + 0x0A, 0x00, 0x00, 0x03, 0x30, 0x07, 0x38, 0x0F, 0x3C, 0x1E, 0x1E, 0x3C, 0x0F, 0xF8, 0x07, 0xF0, 0x03, 0xE0, 0x01, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char > + 0x0B, 0x1C, 0x00, 0x1E, 0x00, 0x1F, 0x00, 0x07, 0x00, 0x83, 0x37, 0xC3, 0x37, 0xE3, 0x37, 0xF7, 0x00, 0x7F, 0x00, 0x3E, 0x00, 0x1C, 0x00, 0x00, 0x00, // Code for char ? + 0x0C, 0xF8, 0x0F, 0xFE, 0x1F, 0xFF, 0x1F, 0xF7, 0x3B, 0xFB, 0x37, 0xFB, 0x37, 0xFB, 0x37, 0xFB, 0x37, 0xFF, 0x37, 0xFF, 0x37, 0xFE, 0x03, 0xF8, 0x01, // Code for char @ + 0x0B, 0x00, 0x38, 0x00, 0x3F, 0xE0, 0x3F, 0xFC, 0x07, 0xFF, 0x06, 0x1F, 0x06, 0xFF, 0x06, 0xFC, 0x07, 0xE0, 0x3F, 0x00, 0x3F, 0x00, 0x38, 0x00, 0x00, // Code for char A + 0x0B, 0xFF, 0x3F, 0xFF, 0x3F, 0xFF, 0x3F, 0xC3, 0x30, 0xC3, 0x30, 0xC3, 0x30, 0xE7, 0x30, 0xFF, 0x39, 0xFE, 0x3F, 0xBC, 0x1F, 0x00, 0x0F, 0x00, 0x00, // Code for char B + 0x0B, 0xF0, 0x03, 0xFC, 0x0F, 0xFE, 0x1F, 0x0F, 0x3C, 0x07, 0x38, 0x03, 0x30, 0x03, 0x30, 0x07, 0x38, 0x0F, 0x3C, 0x0E, 0x1C, 0x0C, 0x0C, 0x00, 0x00, // Code for char C + 0x0B, 0xFF, 0x3F, 0xFF, 0x3F, 0xFF, 0x3F, 0x03, 0x30, 0x03, 0x30, 0x03, 0x30, 0x07, 0x38, 0x0F, 0x3C, 0xFE, 0x1F, 0xFC, 0x0F, 0xF0, 0x03, 0x00, 0x00, // Code for char D + 0x0B, 0xFF, 0x3F, 0xFF, 0x3F, 0xFF, 0x3F, 0xC3, 0x30, 0xC3, 0x30, 0xC3, 0x30, 0xC3, 0x30, 0xC3, 0x30, 0xC3, 0x30, 0x03, 0x30, 0x03, 0x30, 0x00, 0x00, // Code for char E + 0x0B, 0xFF, 0x3F, 0xFF, 0x3F, 0xFF, 0x3F, 0xC3, 0x00, 0xC3, 0x00, 0xC3, 0x00, 0xC3, 0x00, 0xC3, 0x00, 0xC3, 0x00, 0x03, 0x00, 0x03, 0x00, 0x00, 0x00, // Code for char F + 0x0B, 0xF0, 0x03, 0xFC, 0x0F, 0xFE, 0x1F, 0x0F, 0x3C, 0x07, 0x38, 0xC3, 0x30, 0xC3, 0x30, 0xC3, 0x30, 0xC7, 0x3F, 0xC7, 0x3F, 0xC6, 0x3F, 0x00, 0x00, // Code for char G + 0x0B, 0xFF, 0x3F, 0xFF, 0x3F, 0xFF, 0x3F, 0xC0, 0x00, 0xC0, 0x00, 0xC0, 0x00, 0xC0, 0x00, 0xC0, 0x00, 0xFF, 0x3F, 0xFF, 0x3F, 0xFF, 0x3F, 0x00, 0x00, // Code for char H + 0x09, 0x00, 0x00, 0x00, 0x00, 0x03, 0x30, 0x03, 0x30, 0xFF, 0x3F, 0xFF, 0x3F, 0xFF, 0x3F, 0x03, 0x30, 0x03, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char I + 0x0B, 0x00, 0x0E, 0x00, 0x1E, 0x00, 0x3E, 0x00, 0x38, 0x00, 0x30, 0x00, 0x30, 0x00, 0x30, 0x00, 0x38, 0xFF, 0x3F, 0xFF, 0x1F, 0xFF, 0x07, 0x00, 0x00, // Code for char J + 0x0B, 0xFF, 0x3F, 0xFF, 0x3F, 0xFF, 0x3F, 0xE0, 0x01, 0xF0, 0x03, 0xF8, 0x07, 0x3C, 0x0F, 0x1E, 0x1E, 0x0F, 0x3C, 0x07, 0x38, 0x03, 0x30, 0x00, 0x00, // Code for char K + 0x0B, 0xFF, 0x3F, 0xFF, 0x3F, 0xFF, 0x3F, 0x00, 0x30, 0x00, 0x30, 0x00, 0x30, 0x00, 0x30, 0x00, 0x30, 0x00, 0x30, 0x00, 0x30, 0x00, 0x30, 0x00, 0x00, // Code for char L + 0x0B, 0xFF, 0x3F, 0xFF, 0x3F, 0xFF, 0x3F, 0x7E, 0x00, 0xF8, 0x01, 0xE0, 0x01, 0xF8, 0x01, 0x7E, 0x00, 0xFF, 0x3F, 0xFF, 0x3F, 0xFF, 0x3F, 0x00, 0x00, // Code for char M + 0x0B, 0xFF, 0x3F, 0xFF, 0x3F, 0xFF, 0x3F, 0x3E, 0x00, 0xF8, 0x00, 0xF0, 0x03, 0xC0, 0x07, 0x00, 0x1F, 0xFF, 0x3F, 0xFF, 0x3F, 0xFF, 0x3F, 0x00, 0x00, // Code for char N + 0x0B, 0xF0, 0x03, 0xFC, 0x0F, 0xFE, 0x1F, 0x0F, 0x3C, 0x07, 0x38, 0x03, 0x30, 0x07, 0x38, 0x0F, 0x3C, 0xFE, 0x1F, 0xFC, 0x0F, 0xF0, 0x03, 0x00, 0x00, // Code for char O + 0x0B, 0xFF, 0x3F, 0xFF, 0x3F, 0xFF, 0x3F, 0x83, 0x01, 0x83, 0x01, 0x83, 0x01, 0x83, 0x01, 0xC7, 0x01, 0xFF, 0x01, 0xFE, 0x00, 0x7C, 0x00, 0x00, 0x00, // Code for char P + 0x0B, 0xF0, 0x03, 0xFC, 0x0F, 0xFE, 0x1F, 0x0F, 0x3C, 0x07, 0x38, 0x03, 0x36, 0x07, 0x3E, 0x0F, 0x3E, 0xFE, 0x3F, 0xFC, 0x3F, 0xF0, 0x33, 0x00, 0x00, // Code for char Q + 0x0B, 0xFF, 0x3F, 0xFF, 0x3F, 0xFF, 0x3F, 0x83, 0x01, 0x83, 0x03, 0x83, 0x07, 0x83, 0x0F, 0xC7, 0x1F, 0xFF, 0x3D, 0xFE, 0x38, 0x7C, 0x30, 0x00, 0x00, // Code for char R + 0x0B, 0x3C, 0x0C, 0x7E, 0x1C, 0xFF, 0x3C, 0xE7, 0x38, 0xC3, 0x30, 0xC3, 0x30, 0xC3, 0x30, 0xC7, 0x39, 0xCF, 0x3F, 0x8E, 0x1F, 0x0C, 0x0F, 0x00, 0x00, // Code for char S + 0x0A, 0x00, 0x00, 0x03, 0x00, 0x03, 0x00, 0x03, 0x00, 0xFF, 0x3F, 0xFF, 0x3F, 0xFF, 0x3F, 0x03, 0x00, 0x03, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char T + 0x0B, 0xFF, 0x07, 0xFF, 0x1F, 0xFF, 0x3F, 0x00, 0x38, 0x00, 0x30, 0x00, 0x30, 0x00, 0x30, 0x00, 0x38, 0xFF, 0x3F, 0xFF, 0x1F, 0xFF, 0x07, 0x00, 0x00, // Code for char U + 0x0B, 0x07, 0x00, 0x3F, 0x00, 0xFF, 0x01, 0xF8, 0x0F, 0xC0, 0x3F, 0x00, 0x3E, 0xC0, 0x3F, 0xF8, 0x0F, 0xFF, 0x01, 0x3F, 0x00, 0x07, 0x00, 0x00, 0x00, // Code for char V + 0x0B, 0xFF, 0x3F, 0xFF, 0x3F, 0xFF, 0x3F, 0x00, 0x1E, 0x80, 0x07, 0x80, 0x03, 0x80, 0x07, 0x00, 0x1E, 0xFF, 0x3F, 0xFF, 0x3F, 0xFF, 0x3F, 0x00, 0x00, // Code for char W + 0x0B, 0x03, 0x30, 0x0F, 0x3C, 0x1F, 0x3E, 0x3C, 0x0F, 0xF0, 0x03, 0xE0, 0x01, 0xF0, 0x03, 0x3C, 0x0F, 0x1F, 0x3E, 0x0F, 0x3C, 0x03, 0x30, 0x00, 0x00, // Code for char X + 0x0B, 0x03, 0x00, 0x0F, 0x00, 0x3F, 0x00, 0xFC, 0x00, 0xF0, 0x3F, 0xC0, 0x3F, 0xF0, 0x3F, 0xFC, 0x00, 0x3F, 0x00, 0x0F, 0x00, 0x03, 0x00, 0x00, 0x00, // Code for char Y + 0x0B, 0x03, 0x30, 0x03, 0x3C, 0x03, 0x3E, 0x03, 0x3F, 0xC3, 0x33, 0xE3, 0x31, 0xF3, 0x30, 0x3F, 0x30, 0x1F, 0x30, 0x0F, 0x30, 0x03, 0x30, 0x00, 0x00, // Code for char Z + 0x09, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x3F, 0xFF, 0x3F, 0xFF, 0x3F, 0x03, 0x30, 0x03, 0x30, 0x03, 0x30, 0x03, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char [ + 0x0C, 0x0E, 0x00, 0x1E, 0x00, 0x3C, 0x00, 0x78, 0x00, 0xF0, 0x00, 0xE0, 0x01, 0xC0, 0x03, 0x80, 0x07, 0x00, 0x0F, 0x00, 0x1E, 0x00, 0x1C, 0x00, 0x18, // Code for char BackSlash + 0x09, 0x00, 0x00, 0x00, 0x00, 0x03, 0x30, 0x03, 0x30, 0x03, 0x30, 0x03, 0x30, 0xFF, 0x3F, 0xFF, 0x3F, 0xFF, 0x3F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char ] + 0x0C, 0x60, 0x00, 0x70, 0x00, 0x78, 0x00, 0x3C, 0x00, 0x1E, 0x00, 0x0F, 0x00, 0x0F, 0x00, 0x1E, 0x00, 0x3C, 0x00, 0x78, 0x00, 0x70, 0x00, 0x60, 0x00, // Code for char ^ + 0x0C, 0x00, 0xC0, 0x00, 0xC0, 0x00, 0xC0, 0x00, 0xC0, 0x00, 0xC0, 0x00, 0xC0, 0x00, 0xC0, 0x00, 0xC0, 0x00, 0xC0, 0x00, 0xC0, 0x00, 0xC0, 0x00, 0xC0, // Code for char _ + 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x7E, 0x00, 0x7E, 0x00, 0x4E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char ` + 0x0B, 0x00, 0x1C, 0x40, 0x3E, 0x60, 0x3F, 0x60, 0x33, 0x60, 0x33, 0x60, 0x33, 0x60, 0x33, 0x60, 0x33, 0xE0, 0x3F, 0xE0, 0x3F, 0xC0, 0x3F, 0x00, 0x00, // Code for char a + 0x0B, 0xFF, 0x3F, 0xFF, 0x3F, 0xFF, 0x3F, 0xE0, 0x30, 0x60, 0x30, 0x60, 0x30, 0x60, 0x30, 0xE0, 0x38, 0xE0, 0x3F, 0xC0, 0x1F, 0x80, 0x0F, 0x00, 0x00, // Code for char b + 0x0B, 0x80, 0x0F, 0xC0, 0x1F, 0xE0, 0x3F, 0xE0, 0x38, 0x60, 0x30, 0x60, 0x30, 0x60, 0x30, 0x60, 0x30, 0xE0, 0x38, 0xC0, 0x18, 0x80, 0x08, 0x00, 0x00, // Code for char c + 0x0B, 0x80, 0x0F, 0xC0, 0x1F, 0xE0, 0x3F, 0xE0, 0x38, 0x60, 0x30, 0x60, 0x30, 0xE0, 0x30, 0xE0, 0x30, 0xFF, 0x3F, 0xFF, 0x3F, 0xFF, 0x3F, 0x00, 0x00, // Code for char d + 0x0B, 0x80, 0x0F, 0xC0, 0x1F, 0xE0, 0x3F, 0xE0, 0x3B, 0x60, 0x33, 0x60, 0x33, 0x60, 0x33, 0x60, 0x33, 0xE0, 0x33, 0xC0, 0x13, 0x80, 0x01, 0x00, 0x00, // Code for char e + 0x09, 0xC0, 0x00, 0xC0, 0x00, 0xFC, 0x3F, 0xFE, 0x3F, 0xFF, 0x3F, 0xC7, 0x00, 0xC3, 0x00, 0xC3, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char f + 0x0B, 0x80, 0x03, 0xC0, 0xC7, 0xE0, 0xCF, 0xE0, 0xCE, 0x60, 0xCC, 0x60, 0xCC, 0x60, 0xCC, 0x60, 0xEE, 0xE0, 0xFF, 0xE0, 0x7F, 0xE0, 0x3F, 0x00, 0x00, // Code for char g + 0x0A, 0xFF, 0x3F, 0xFF, 0x3F, 0xFF, 0x3F, 0xE0, 0x00, 0x60, 0x00, 0x60, 0x00, 0xE0, 0x00, 0xE0, 0x3F, 0xC0, 0x3F, 0x80, 0x3F, 0x00, 0x00, 0x00, 0x00, // Code for char h + 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x60, 0x30, 0xEC, 0x3F, 0xEC, 0x3F, 0xEC, 0x3F, 0x00, 0x30, 0x00, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char i + 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x60, 0x00, 0xE0, 0x00, 0xE0, 0x60, 0xC0, 0xEC, 0xFF, 0xEC, 0xFF, 0xEC, 0x7F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char j + 0x0A, 0x00, 0x00, 0xFF, 0x3F, 0xFF, 0x3F, 0xFF, 0x3F, 0x80, 0x07, 0xC0, 0x0F, 0xE0, 0x1F, 0xE0, 0x3C, 0x60, 0x38, 0x00, 0x30, 0x00, 0x00, 0x00, 0x00, // Code for char k + 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x03, 0x30, 0xFF, 0x3F, 0xFF, 0x3F, 0xFF, 0x3F, 0x00, 0x30, 0x00, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char l + 0x0B, 0xE0, 0x3F, 0xE0, 0x3F, 0xE0, 0x3F, 0xE0, 0x00, 0xE0, 0x3F, 0xC0, 0x3F, 0xE0, 0x3F, 0xE0, 0x00, 0xE0, 0x3F, 0xC0, 0x3F, 0x80, 0x3F, 0x00, 0x00, // Code for char m + 0x0B, 0x00, 0x00, 0xE0, 0x3F, 0xE0, 0x3F, 0xE0, 0x3F, 0x60, 0x00, 0x60, 0x00, 0x60, 0x00, 0xE0, 0x00, 0xE0, 0x3F, 0xC0, 0x3F, 0x80, 0x3F, 0x00, 0x00, // Code for char n + 0x0B, 0x80, 0x0F, 0xC0, 0x1F, 0xE0, 0x3F, 0xE0, 0x38, 0x60, 0x30, 0x60, 0x30, 0x60, 0x30, 0xE0, 0x38, 0xE0, 0x3F, 0xC0, 0x1F, 0x80, 0x0F, 0x00, 0x00, // Code for char o + 0x0B, 0xE0, 0xFF, 0xE0, 0xFF, 0xE0, 0xFF, 0x60, 0x1C, 0x60, 0x18, 0x60, 0x18, 0x60, 0x18, 0xE0, 0x1C, 0xE0, 0x1F, 0xC0, 0x0F, 0x80, 0x07, 0x00, 0x00, // Code for char p + 0x0B, 0x80, 0x07, 0xC0, 0x0F, 0xE0, 0x1F, 0xE0, 0x1C, 0x60, 0x18, 0x60, 0x18, 0x60, 0x18, 0x60, 0x1C, 0xE0, 0xFF, 0xE0, 0xFF, 0xE0, 0xFF, 0x00, 0x00, // Code for char q + 0x0B, 0x00, 0x00, 0xE0, 0x3F, 0xE0, 0x3F, 0xE0, 0x3F, 0xE0, 0x00, 0x60, 0x00, 0x60, 0x00, 0x60, 0x00, 0xE0, 0x00, 0xE0, 0x00, 0xC0, 0x00, 0x00, 0x00, // Code for char r + 0x09, 0xC0, 0x11, 0xE0, 0x33, 0xE0, 0x33, 0x60, 0x33, 0x60, 0x33, 0x60, 0x33, 0x60, 0x3F, 0x60, 0x3F, 0x40, 0x1E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char s + 0x09, 0x60, 0x00, 0x60, 0x00, 0xFE, 0x1F, 0xFE, 0x3F, 0xFE, 0x3F, 0x60, 0x30, 0x60, 0x30, 0x60, 0x30, 0x00, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char t + 0x0B, 0xE0, 0x0F, 0xE0, 0x1F, 0xE0, 0x3F, 0x00, 0x38, 0x00, 0x30, 0x00, 0x30, 0x00, 0x30, 0x00, 0x38, 0xE0, 0x3F, 0xE0, 0x3F, 0xE0, 0x3F, 0x00, 0x00, // Code for char u + 0x0B, 0x60, 0x00, 0xE0, 0x01, 0xE0, 0x07, 0x80, 0x1F, 0x00, 0x3E, 0x00, 0x38, 0x00, 0x3E, 0x80, 0x1F, 0xE0, 0x07, 0xE0, 0x01, 0x60, 0x00, 0x00, 0x00, // Code for char v + 0x0B, 0xE0, 0x07, 0xE0, 0x1F, 0xE0, 0x3F, 0x00, 0x3C, 0xE0, 0x1F, 0xE0, 0x0F, 0xE0, 0x1F, 0x00, 0x3C, 0xE0, 0x3F, 0xE0, 0x1F, 0xE0, 0x07, 0x00, 0x00, // Code for char w + 0x0A, 0x60, 0x30, 0xE0, 0x38, 0xE0, 0x3D, 0xC0, 0x1F, 0x80, 0x0F, 0x80, 0x0F, 0xC0, 0x1F, 0xE0, 0x3D, 0xE0, 0x38, 0x60, 0x30, 0x00, 0x00, 0x00, 0x00, // Code for char x + 0x0A, 0x00, 0x00, 0x60, 0x00, 0xE0, 0x81, 0xE0, 0xE7, 0x80, 0xFF, 0x00, 0x7E, 0x80, 0x1F, 0xE0, 0x07, 0xE0, 0x01, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char y + 0x0A, 0x60, 0x30, 0x60, 0x38, 0x60, 0x3C, 0x60, 0x3E, 0x60, 0x37, 0xE0, 0x33, 0xE0, 0x31, 0xE0, 0x30, 0x60, 0x30, 0x20, 0x30, 0x00, 0x00, 0x00, 0x00, // Code for char z + 0x0A, 0x00, 0x00, 0x80, 0x00, 0xC0, 0x01, 0xFC, 0x1F, 0xFE, 0x3F, 0x7F, 0x7F, 0x07, 0x70, 0x03, 0x60, 0x03, 0x60, 0x03, 0x60, 0x00, 0x00, 0x00, 0x00, // Code for char { + 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x3F, 0xFF, 0x3F, 0xFF, 0x3F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char | + 0x0A, 0x00, 0x00, 0x03, 0x60, 0x03, 0x60, 0x03, 0x60, 0x07, 0x70, 0x7F, 0x7F, 0xFE, 0x3F, 0xFC, 0x1F, 0xC0, 0x01, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char } + 0x0B, 0x10, 0x00, 0x18, 0x00, 0x1C, 0x00, 0x0C, 0x00, 0x0C, 0x00, 0x1C, 0x00, 0x18, 0x00, 0x18, 0x00, 0x1C, 0x00, 0x0C, 0x00, 0x04, 0x00, 0x00, 0x00, // Code for char ~ + 0x0B, 0x00, 0x0F, 0x80, 0x0F, 0xC0, 0x0F, 0xE0, 0x0C, 0x70, 0x0C, 0x30, 0x0C, 0x70, 0x0C, 0xE0, 0x0C, 0xC0, 0x0F, 0x80, 0x0F, 0x00, 0x0F, 0x00, 0x00 // Code for char  + }; + + +//Font Generated by MikroElektronika GLCD Font Creator 1.2.0.0 +//MikroElektronika 2011 +//http://www.mikroe.com + +//GLCD FontName : Trebuchet_MS16x21 +//GLCD FontSize : 16 x 21 + +fontdatatype Trebuchet_MS16x21[] PROGMEM = { + 0x10, 0x15, 0x2E, 0x0D, + 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x1E, 0x00, 0x00, 0x1E, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char . + 0x0C, 0x00, 0x00, 0x10, 0x00, 0x00, 0x1E, 0x00, 0xC0, 0x1F, 0x00, 0xF0, 0x1F, 0x00, 0xFE, 0x0F, 0x80, 0xFF, 0x03, 0xF0, 0x7F, 0x00, 0xFE, 0x0F, 0x00, 0xFF, 0x03, 0x00, 0x7F, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char / + 0x10, 0x00, 0x00, 0x00, 0xC0, 0x7F, 0x00, 0xF0, 0xFF, 0x03, 0xFC, 0xFF, 0x07, 0xFE, 0xFF, 0x0F, 0x3E, 0x80, 0x0F, 0x0F, 0x00, 0x1E, 0x07, 0x00, 0x1C, 0x07, 0x00, 0x1C, 0x07, 0x00, 0x1C, 0x0F, 0x00, 0x1E, 0x1F, 0x80, 0x0F, 0xFE, 0xFF, 0x0F, 0xFC, 0xFF, 0x07, 0xF8, 0xFF, 0x01, 0xC0, 0x7F, 0x00, // Code for char 0 + 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF0, 0x00, 0x00, 0x78, 0x00, 0x00, 0x38, 0x00, 0x00, 0x3C, 0x00, 0x00, 0xFE, 0xFF, 0x1F, 0xFE, 0xFF, 0x1F, 0xFF, 0xFF, 0x1F, 0xFF, 0xFF, 0x1F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char 1 + 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x18, 0x1E, 0x00, 0x1C, 0x0E, 0x00, 0x1F, 0x0F, 0x80, 0x1F, 0x07, 0xC0, 0x1F, 0x07, 0xF0, 0x1F, 0x07, 0xF8, 0x1D, 0x07, 0xFE, 0x1C, 0x0F, 0x3F, 0x1C, 0xFE, 0x1F, 0x1C, 0xFE, 0x0F, 0x1C, 0xFC, 0x03, 0x1C, 0xF8, 0x00, 0x1C, 0x00, 0x00, 0x1C, // Code for char 2 + 0x0F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x04, 0x00, 0x0E, 0x0E, 0x00, 0x0F, 0x0F, 0x00, 0x1E, 0x07, 0x00, 0x1C, 0x07, 0x07, 0x1C, 0x07, 0x07, 0x1C, 0x07, 0x07, 0x1C, 0x8F, 0x0F, 0x1E, 0xFF, 0x1F, 0x1E, 0xFE, 0xFD, 0x0F, 0xFC, 0xFD, 0x0F, 0x78, 0xF8, 0x07, 0x00, 0xF0, 0x01, 0x00, 0x00, 0x00, // Code for char 3 + 0x10, 0x00, 0x00, 0x00, 0x00, 0xE0, 0x00, 0x00, 0xF0, 0x00, 0x00, 0xF8, 0x00, 0x00, 0xFE, 0x00, 0x00, 0xEF, 0x00, 0x80, 0xE7, 0x00, 0xC0, 0xE3, 0x00, 0xF0, 0xE0, 0x00, 0x78, 0xE0, 0x00, 0xFC, 0xFF, 0x1F, 0xFE, 0xFF, 0x1F, 0xFF, 0xFF, 0x1F, 0xFF, 0xFF, 0x1F, 0x00, 0xE0, 0x00, 0x00, 0xE0, 0x00, // Code for char 4 + 0x0F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0xFF, 0x03, 0x0F, 0xFF, 0x07, 0x0E, 0xFF, 0x03, 0x1C, 0xFF, 0x01, 0x1C, 0xC7, 0x01, 0x1C, 0xC7, 0x01, 0x1C, 0xC7, 0x01, 0x1E, 0xC7, 0x03, 0x1F, 0xC7, 0xFF, 0x0F, 0x87, 0xFF, 0x0F, 0x07, 0xFF, 0x07, 0x00, 0xFC, 0x01, 0x00, 0x00, 0x00, // Code for char 5 + 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFE, 0x00, 0x80, 0xFF, 0x03, 0xE0, 0xFF, 0x07, 0xF0, 0xFF, 0x0F, 0xF8, 0x0F, 0x1E, 0x7C, 0x07, 0x1C, 0x3E, 0x07, 0x1C, 0x1E, 0x07, 0x1C, 0x0F, 0x07, 0x1C, 0x07, 0x0F, 0x1E, 0x02, 0xFE, 0x0F, 0x00, 0xFE, 0x0F, 0x00, 0xFC, 0x07, 0x00, 0xF0, 0x01, // Code for char 6 + 0x10, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x07, 0x00, 0x00, 0x07, 0x00, 0x18, 0x07, 0x00, 0x1E, 0x07, 0xC0, 0x1F, 0x07, 0xF0, 0x1F, 0x07, 0xFC, 0x0F, 0x07, 0xFF, 0x01, 0xC7, 0x7F, 0x00, 0xF7, 0x0F, 0x00, 0xFF, 0x03, 0x00, 0xFF, 0x00, 0x00, 0x3F, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x03, 0x00, 0x00, // Code for char 7 + 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE0, 0x03, 0x78, 0xF8, 0x07, 0xFC, 0xFD, 0x0F, 0xFE, 0xFF, 0x0F, 0xFF, 0x1F, 0x1E, 0x8F, 0x0F, 0x1C, 0x07, 0x07, 0x1C, 0x07, 0x0F, 0x1C, 0x8F, 0x0F, 0x1C, 0xFF, 0x3F, 0x1E, 0xFE, 0xFD, 0x0F, 0xFE, 0xFD, 0x0F, 0x78, 0xF0, 0x07, 0x00, 0xE0, 0x03, // Code for char 8 + 0x0F, 0x00, 0x00, 0x00, 0xF0, 0x01, 0x00, 0xFC, 0x07, 0x00, 0xFE, 0x0F, 0x00, 0xFE, 0x0F, 0x08, 0x0F, 0x1E, 0x1C, 0x07, 0x1C, 0x1E, 0x07, 0x1C, 0x0F, 0x07, 0x9C, 0x0F, 0x07, 0xDC, 0x07, 0x0F, 0xFE, 0x03, 0xFE, 0xFF, 0x01, 0xFC, 0xFF, 0x00, 0xF8, 0x3F, 0x00, 0xE0, 0x0F, 0x00, 0x00, 0x00, 0x00, // Code for char 9 + 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x01, 0x0C, 0xC0, 0x03, 0x1E, 0xC0, 0x03, 0x1E, 0x80, 0x01, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 // Code for char : + }; + + + diff --git a/libraries/SondeLib/Display.cpp b/libraries/SondeLib/Display.cpp index 03a0b66..84215c0 100644 --- a/libraries/SondeLib/Display.cpp +++ b/libraries/SondeLib/Display.cpp @@ -1,14 +1,22 @@ #include #include +#include #include #include "Display.h" #include "Sonde.h" +extern const char *version_name; +extern const char *version_id; + +#include <../fonts/FreeSans9pt7b.h> +#include <../fonts/FreeSans12pt7b.h> + extern Sonde sonde; extern MicroNMEA nmea; -extern U8X8_SSD1306_128X64_NONAME_SW_I2C *u8x8; + +SPIClass spiDisp(HSPI); const char *sondeTypeStr[5] = { "DFM6", "DFM9", "RS41", "RS92" }; @@ -32,7 +40,7 @@ static unsigned char kmh_tiles[] U8X8_PROGMEM = { 0x1F, 0x04, 0x0A, 0x11, 0x00, 0x1F, 0x02, 0x04, 0x42, 0x3F, 0x10, 0x08, 0xFC, 0x22, 0x20, 0xF8 }; static unsigned char ms_tiles[] U8X8_PROGMEM = { - 0x1F, 0x02, 0x04, 0x02, 0x1F, 0x40, 0x20, 0x10, 0x08, 0x04, 0x12, 0xA4, 0xA4, 0xA4, 0x40, 0x00 + 0x1F, 0x02, 0x04, 0x02, 0x1F, 0x40, 0x20, 0x10, 0x08, 0x04, 0x12, 0xA8, 0xA8, 0xA4, 0x40, 0x00 }; static unsigned char stattiles[5][4] = { 0x00, 0x1F, 0x00, 0x00 , // | == ok @@ -56,7 +64,8 @@ static uint8_t empty_tile2[8]={0x00, 0x11, 0x02, 0x02, 0x02, 0x01, 0x00, 0x00}; static uint8_t gps_tile[8]={0x00, 0x0E, 0x1F, 0x3B, 0x71, 0x3B, 0x1F, 0x0E}; static uint8_t nogps_tile[8]={0x41, 0x22, 0x14, 0x08, 0x14, 0x22, 0x41, 0x00}; -#define SETFONT(large) u8x8->setFont((large)?u8x8_font_7x14_1x2_r:u8x8_font_chroma48medium8_r); +static uint8_t deg_tile[8]={0x00, 0x06,0x09, 0x09, 0x06, 0x00, 0x00, 0x00}; + /* Description of display layouts. * for each display, the content is described by a DispEntry structure @@ -150,17 +159,401 @@ uint8_t gpsActions[] = { ACT_DISPLAY(1), ACT_NONE, ACT_NONE, ACT_NONE, ACT_NONE, ACT_NONE, ACT_NONE}; -DispInfo layouts[5] = { +DispInfo staticLayouts[5] = { { searchLayout, searchActions, searchTimeouts }, { legacyLayout, legacyActions, legacyTimeouts }, { fieldLayout, fieldActions, fieldTimeouts }, { field2Layout, field2Actions, fieldTimeouts }, { gpsLayout, gpsActions, fieldTimeouts } }; +DispInfo *layouts = staticLayouts; + +/////////////// Wrapper code for various display + +void U8x8Display::begin() { + Serial.printf("Init SSD1306 display %d %d\n", sonde.config.oled_scl, sonde.config.oled_sda); + //u8x8 = new U8X8_SSD1306_128X64_NONAME_SW_I2C(/* clock=*/ sonde.config.oled_scl, /* data=*/ sonde.config.oled_sda, /* reset=*/ sonde.config.oled_rst); // Unbuffered, basic graphics, software I2C + if (_type==2) { + u8x8 = new U8X8_SH1106_128X64_NONAME_HW_I2C(/* reset=*/ sonde.config.oled_rst, /* clock=*/ sonde.config.oled_scl, /* data=*/ sonde.config.oled_sda); // Unbuffered, basic graphics, software I2C + } else { //__type==0 or anything else + u8x8 = new U8X8_SSD1306_128X64_NONAME_HW_I2C(/* reset=*/ sonde.config.oled_rst, /* clock=*/ sonde.config.oled_scl, /* data=*/ sonde.config.oled_sda); // Unbuffered, basic graphics, software I2C + } + u8x8->begin(); + +} + +void U8x8Display::clear() { + u8x8->clear(); +} + +// For u8x8 oled display: 0=small font, 1=large font 7x14 +void U8x8Display::setFont(int large) { + u8x8->setFont((large)?u8x8_font_7x14_1x2_r:u8x8_font_chroma48medium8_r); +} + +void U8x8Display::drawString(uint8_t x, uint8_t y, const char *s) { + u8x8->drawString(x, y, s); +} + +void U8x8Display::drawTile(uint8_t x, uint8_t y, uint8_t cnt, uint8_t *tile_ptr) { + u8x8->drawTile(x, y, cnt, tile_ptr); +} +void U8x8Display::welcome() { + u8x8->clear(); + setFont(FONT_LARGE); + drawString(8 - strlen(version_name) / 2, 0, version_name); + drawString(8 - strlen(version_id) / 2, 2, version_id); + setFont(FONT_SMALL); + drawString(0, 4, "RS41,RS92,DFM6/9"); + drawString(0, 6, "by Hansi, DL9RDZ"); +} + + +#define TFT_LED 0 // 0 if wired to +5V directly +#define TFT_BRIGHTNESS 100 // Initial brightness of TFT backlight (optional) + +void ILI9225Display::begin() { + tft = new MY_ILI9225(sonde.config.oled_rst, sonde.config.tft_rs, sonde.config.tft_cs, + sonde.config.oled_sda, sonde.config.oled_scl, TFT_LED, TFT_BRIGHTNESS); + tft->begin(spiDisp); + tft->setOrientation(1); +} + +void ILI9225Display::clear() { + tft->clear(); +} + +// for now, 0=small=FreeSans9pt7b, 1=large=FreeSans18pt7b +void ILI9225Display::setFont(int large) { + tft->setGFXFont(large ? &FreeSans12pt7b : &FreeSans9pt7b); + //tft->setFont(large?Terminal12x16: Terminal6x8); + fsize = large; + yofs = 0; +} + +/* Notes on Fonts: + * FreeSans9pt: höhe: baseline -13..+5; breite: max 18, i.d.r. <=15 +*/ +// normal size: avg. 14x22 // large wvg. 17x29 +#define XSKIP 14 +#define YSKIP 22 +void ILI9225Display::drawString(uint8_t x, uint8_t y, const char *s) { + int16_t w,h; +#if 1 + tft->getGFXTextExtent(s, x*XSKIP, y*YSKIP, &w, &h); + int len = strlen(s); + if(fsize) { + tft->fillRectangle(x*XSKIP, y*YSKIP+3, x*XSKIP + len*17, y*YSKIP +29, COLOR_BLACK); + } else { + tft->fillRectangle(x*XSKIP, y*YSKIP+3, x*XSKIP + len*14, y*YSKIP +22, COLOR_BLACK); + } + tft->drawGFXText(x*XSKIP, (1+y)*YSKIP, s, COLOR_WHITE); +#endif +} + +void ILI9225Display::drawTile(uint8_t x, uint8_t y, uint8_t cnt, uint8_t *tile_ptr) { + tft->drawTile(x, 2*y, cnt, tile_ptr); +#if 0 + int i,j; + tft->startWrite(); + for(int i=0; idrawPixel(8*x+i, 8*y+j, (v&0x01) ? COLOR_GREEN:COLOR_BLUE); + v >>= 1; + } + } + tft->endWrite(); + //tft->drawBitmap(x*8, y*8, tile_ptr, cnt*8, 8, COLOR_RED, COLOR_BLUE); + //???u8x8->drawTile(x, y, cnt, tile_ptr); +#endif +} + +void ILI9225Display::welcome() { + tft->clear(); + setFont(FONT_LARGE); + drawString(0, 0, version_name); + setFont(FONT_SMALL); + drawString(0, 1, "RS41,RS92,DFM6/9"); + drawString(0, 3, version_id); + drawString(0, 5, "by Hansi, DL9RDZ"); + +} +#include +#define pgm_read_pointer(addr) ((void *)pgm_read_dword(addr)) + +void MY_ILI9225::drawTile(uint8_t x, uint8_t y, uint8_t cnt, uint8_t *tile_ptr) { + int i,j; + startWrite(); + for(int i=0; i>= 1; + } + } + endWrite(); +} + + +uint16_t MY_ILI9225::drawGFXChar(int16_t x, int16_t y, unsigned char c, uint16_t color) { + + c -= (uint8_t)pgm_read_byte(&gfxFont->first); + GFXglyph *glyph = &(((GFXglyph *)pgm_read_pointer(&gfxFont->glyph))[c]); + uint8_t *bitmap = (uint8_t *)pgm_read_pointer(&gfxFont->bitmap); + + uint16_t bo = pgm_read_word(&glyph->bitmapOffset); + uint8_t w = pgm_read_byte(&glyph->width), + h = pgm_read_byte(&glyph->height), + xa = pgm_read_byte(&glyph->xAdvance); + int8_t xo = pgm_read_byte(&glyph->xOffset), + yo = pgm_read_byte(&glyph->yOffset); + uint8_t xx, yy, bits = 0, bit = 0; + + // Add character clipping here one day + + startWrite(); + for(yy=0; yybegin(); + delay(100); + Serial.println("Display initialized"); + rdis->clear(); +} + + Display::Display() { - setLayout(1); + setLayout(0); +} + +#define MAXSCREENS 10 +#define DISP_ACTIONS_N 12 +#define DISP_TIMEOUTS_N 3 + +void Display::freeLayouts() { + if(layouts==staticLayouts) return; + DispInfo *old = layouts; + layouts=staticLayouts; + setLayout(0); + for(int i=0; ide = (DispEntry *)mem; + mem += (entries+1) * sizeof(DispEntry); + + d->actions = (uint8_t *)mem; + mem += DISP_ACTIONS_N * sizeof(uint8_t); + d->actions[0] = ACT_NONE; + + d->timeouts = (int16_t *)mem; + Serial.printf("allocated %d bytes (%d entries) for %p (addr=%p)\n", totalsize, entries, d, d->de); + return 0; +} + +void Display::parseDispElement(char *text, DispEntry *de) +{ + char type = *text; + if(type>='A'&&type<='Z') { + type += 32; // lc + de->fmt = FONT_LARGE; + } else { + de->fmt = FONT_SMALL; + } + switch(type) { + case 'l': + de->func = disp.drawLat; break; + case 'o': + de->func = disp.drawLon; break; + case 'a': + de->func = disp.drawAlt; break; + case 'h': + de->func = disp.drawHS; break; + case 'v': + de->func = disp.drawVS; break; + case 'i': + de->func = disp.drawID; + de->extra = strdup(text+1); + break; + case 'q': + de->func = disp.drawQS; break; + case 't': + de->func = disp.drawType; break; + case 'c': + de->func = disp.drawAFC; break; + case 'f': + de->func = disp.drawFreq; + de->extra = strdup(text+1); + Serial.printf("parsing 'f' entry: extra is '%s'\n", de->extra); + break; + case 'n': + de->func = disp.drawIP; break; + case 's': + de->func = disp.drawSite; break; + case 'g': + de->func = disp.drawGPS; + de->extra = strdup(text+1); + Serial.printf("parsing 'g' entry: extra is '%s'\n", de->extra); + break; + case 'r': + de->func = disp.drawRSSI; break; + case 'x': + de->func = disp.drawText; + de->extra = strdup(text+1); + break; + default: + Serial.printf("Unknown element: %c\n", type); + break; + } +} + +static uint8_t ACTION(char c) { + switch(c) { + case 'D': + return ACT_DISPLAY_DEFAULT; + case 'F': + return ACT_DISPLAY_SPECTRUM; + case 'W': + return ACT_DISPLAY_WIFI; + case '+': + return ACT_NEXTSONDE; + case '#': + return ACT_NONE; + default: + if(c>='0'&&c<='9') + return ACT_DISPLAY(c-'0'); + } + return ACT_NONE; +} + +void Display::initFromFile() { + File d = SPIFFS.open("/screens.txt", "r"); + if(!d) return; + + freeLayouts(); + layouts = (DispInfo *)malloc(MAXSCREENS * sizeof(DispInfo)); + if(!layouts) { + layouts = staticLayouts; + return; + } + memset(layouts, 0, MAXSCREENS * sizeof(DispInfo)); + + int idx = -1; + int what = -1; + int entrysize; + Serial.printf("Reading from /screens.txt. available=%d\n",d.available()); + while(d.available()) { + String line = d.readStringUntil('\n'); + line.trim(); + const char *s = line.c_str(); + Serial.printf("Line: '%s'\n", s); + if(*s == '#') continue; // ignore comments + switch(what) { + case -1: // wait for start of screen (@) + { + if(*s != '@') { + Serial.printf("Illegal start of screen: %s\n", s); + continue; + } + char *num = strchr(s, ':'); + if(!num) { + Serial.println("Line missing size length indication"); + continue; + } + entrysize = atoi(num+1); + Serial.printf("Reading entry with %d elements\n", entrysize); + idx++; + int res = allocDispInfo(entrysize, &layouts[idx]); + if(res<0) { + Serial.println("Error allocating memory for disp info"); + continue; + } + what = 0; + } + break; + default: // parse content... (additional data or line `what`) + if(strncmp(s,"timer=",6)==0) { // timer values + sscanf(s+6, "%hd,%hd,%hd", layouts[idx].timeouts, layouts[idx].timeouts+1, layouts[idx].timeouts+2); + Serial.printf("timer values: %d, %d, %d\n", layouts[idx].timeouts[0], layouts[idx].timeouts[1], layouts[idx].timeouts[2]); + } else if(strncmp(s, "key1action=",11)==0) { // key 1 actions + char c1,c2,c3,c4; + sscanf(s+11, "%c,%c,%c,%c", &c1, &c2, &c3, &c4); + layouts[idx].actions[1] = ACTION(c1); + layouts[idx].actions[2] = ACTION(c2); + layouts[idx].actions[3] = ACTION(c3); + layouts[idx].actions[4] = ACTION(c4); + } else if(strncmp(s, "key2action=",11)==0) { // key 2 actions + char c1,c2,c3,c4; + sscanf(s+11, "%c,%c,%c,%c", &c1, &c2, &c3, &c4); + layouts[idx].actions[5] = ACTION(c1); + layouts[idx].actions[6] = ACTION(c2); + layouts[idx].actions[7] = ACTION(c3); + layouts[idx].actions[8] = ACTION(c4); + Serial.printf("parsing key2action: %c %c %c %c\n", c1, c2, c3, c4); + } else if(strncmp(s, "timeaction=",11)==0) { // timer actions + char c1,c2,c3; + sscanf(s+11, "%c,%c,%c", &c1, &c2, &c3); + layouts[idx].actions[9] = ACTION(c1); + layouts[idx].actions[10] = ACTION(c2); + layouts[idx].actions[11] = ACTION(c3); + } else if(strchr(s, '=')) { // one line with some data... + int x,y; + char text[30]; + sscanf(s, "%d,%d=%30[^\r\n]", &y, &x, text); + layouts[idx].de[what].x = x; + layouts[idx].de[what].y = y; + parseDispElement(text, layouts[idx].de+what); + what++; + layouts[idx].de[what].func = NULL; + } else { + for(int i=0; i<12; i++) { + Serial.printf("action %d: %d\n", i, (int)layouts[idx].actions[i]); + } + what=-1; + } + break; + } + } } void Display::setLayout(int layoutIdx) { @@ -168,70 +561,85 @@ void Display::setLayout(int layoutIdx) { } void Display::drawLat(DispEntry *de) { - SETFONT(de->fmt); + rdis->setFont(de->fmt); if(!sonde.si()->validPos) { - u8x8->drawString(de->x,de->y," "); + rdis->drawString(de->x,de->y," "); return; } snprintf(buf, 16, "%2.5f", sonde.si()->lat); - u8x8->drawString(de->x,de->y,buf); + rdis->drawString(de->x,de->y,buf); } void Display::drawLon(DispEntry *de) { - SETFONT(de->fmt); + rdis->setFont(de->fmt); if(!sonde.si()->validPos) { - u8x8->drawString(de->x,de->y," "); + rdis->drawString(de->x,de->y," "); return; } snprintf(buf, 16, "%2.5f", sonde.si()->lon); - u8x8->drawString(de->x,de->y,buf); + rdis->drawString(de->x,de->y,buf); } void Display::drawAlt(DispEntry *de) { - SETFONT(de->fmt); + rdis->setFont(de->fmt); if(!sonde.si()->validPos) { - u8x8->drawString(de->x,de->y," "); + rdis->drawString(de->x,de->y," "); return; } snprintf(buf, 16, sonde.si()->alt>=1000?" %5.0fm":" %3.1fm", sonde.si()->alt); - u8x8->drawString(de->x,de->y,buf+strlen(buf)-6); + rdis->drawString(de->x,de->y,buf+strlen(buf)-6); } void Display::drawHS(DispEntry *de) { - SETFONT(de->fmt); + rdis->setFont(de->fmt); if(!sonde.si()->validPos) { - u8x8->drawString(de->x,de->y," "); + rdis->drawString(de->x,de->y," "); return; } snprintf(buf, 16, sonde.si()->hs>99?" %3.0f":" %2.1f", sonde.si()->hs); - u8x8->drawString(de->x,de->y,buf+strlen(buf)-4); - u8x8->drawTile(de->x+4,de->y,2,kmh_tiles); + rdis->drawString(de->x,de->y,buf+strlen(buf)-4); + rdis->drawTile(de->x+4,de->y,2,kmh_tiles); } void Display::drawVS(DispEntry *de) { - SETFONT(de->fmt); + rdis->setFont(de->fmt); if(!sonde.si()->validPos) { - u8x8->drawString(de->x,de->y," "); + rdis->drawString(de->x,de->y," "); return; } snprintf(buf, 16, " %+2.1f", sonde.si()->vs); - u8x8->drawString(de->x, de->y, buf+strlen(buf)-5); - u8x8->drawTile(de->x+5,de->y,2,ms_tiles); - + rdis->drawString(de->x, de->y, buf+strlen(buf)-5); + rdis->drawTile(de->x+5,de->y,2,ms_tiles); } void Display::drawID(DispEntry *de) { - SETFONT((de->fmt&0x01)); + rdis->setFont(de->fmt); if(!sonde.si()->validID) { - u8x8->drawString(de->x, de->y, "nnnnnnnn "); + rdis->drawString(de->x, de->y, "nnnnnnnn "); return; } - u8x8->drawString(de->x, de->y, sonde.si()->id); + // TODO: handle DFM6 IDs + + if(!de->extra || de->extra[0]=='s') { + // real serial number, as printed on sonde + rdis->drawString(de->x, de->y, sonde.si()->id); + } else if (de->extra[0]=='a') { + // autorx sonde number ("DF9" and last 6 digits of real serial number + strcpy(buf, sonde.si()->id); + memcpy(buf, "DF9", 3); + rdis->drawString(de->x, de->y, buf); + } else { + // dxlAPRS sonde number (DF6 (why??) and 5 last digits of serial number as hex number + uint32_t id = atoi(sonde.si()->id); + id = id&0xfffff; + snprintf(buf, 16, "DF6%05X", id); + rdis->drawString(de->x, de->y, buf); + } } void Display::drawRSSI(DispEntry *de) { - SETFONT(de->fmt); + rdis->setFont(de->fmt); snprintf(buf, 16, "-%d ", sonde.si()->rssi/2); int len=strlen(buf)-3; Serial.printf("drawRSSI: %d %d %d (%d)[%d]\n", de->y, de->x, sonde.si()->rssi/2, sonde.currentSonde, len); buf[5]=0; - u8x8->drawString(de->x,de->y,buf); - u8x8->drawTile(de->x+len, de->y, 1, (sonde.si()->rssi&1)?halfdb_tile1:empty_tile1); - u8x8->drawTile(de->x+len, de->y+1, 1, (sonde.si()->rssi&1)?halfdb_tile2:empty_tile2); + rdis->drawString(de->x,de->y,buf); + rdis->drawTile(de->x+len, de->y, 1, (sonde.si()->rssi&1)?halfdb_tile1:empty_tile1); + rdis->drawTile(de->x+len, de->y+1, 1, (sonde.si()->rssi&1)?halfdb_tile2:empty_tile2); } void Display::drawQS(DispEntry *de) { uint8_t *stat = sonde.si()->rxStat; @@ -239,32 +647,32 @@ void Display::drawQS(DispEntry *de) { uint8_t tile[8]; *(uint32_t *)(&tile[0]) = *(uint32_t *)(&(stattiles[stat[i]])); *(uint32_t *)(&tile[4]) = *(uint32_t *)(&(stattiles[stat[i+1]])); - u8x8->drawTile(de->x+i/2, de->y, 1, tile); + rdis->drawTile(de->x+i/2, de->y, 1, tile); } } void Display::drawType(DispEntry *de) { - SETFONT(de->fmt); - u8x8->drawString(de->x, de->y, sondeTypeStr[sonde.si()->type]); + rdis->setFont(de->fmt); + rdis->drawString(de->x, de->y, sondeTypeStr[sonde.si()->type]); } void Display::drawFreq(DispEntry *de) { - SETFONT(de->fmt); + rdis->setFont(de->fmt); snprintf(buf, 16, "%3.3f%s", sonde.si()->freq, de->extra?de->extra:""); - u8x8->drawString(de->x, de->y, buf); + rdis->drawString(de->x, de->y, buf); } void Display::drawAFC(DispEntry *de) { if(!sonde.config.showafc) return; - SETFONT(de->fmt); + rdis->setFont(de->fmt); if(sonde.si()->afc==0) { strcpy(buf, " "); } else { snprintf(buf, 15, " %+3.2fk", sonde.si()->afc*0.001); } - u8x8->drawString(de->x, de->y, buf+strlen(buf)-8); + rdis->drawString(de->x, de->y, buf+strlen(buf)-8); } void Display::drawIP(DispEntry *de) { - u8x8->drawTile(de->x, de->y, 11, myIP_tiles); + rdis->drawTile(de->x, de->y, 11, myIP_tiles); } void Display::drawSite(DispEntry *de) { - SETFONT(de->fmt); - u8x8->drawString(de->x, de->y, sonde.si()->launchsite); + rdis->setFont(de->fmt); + rdis->drawString(de->x, de->y, sonde.si()->launchsite); } void Display::drawTelemetry(DispEntry *de) { } @@ -277,13 +685,13 @@ void Display::drawTelemetry(DispEntry *de) { void Display::drawGPS(DispEntry *de) { if(sonde.config.gps_rxd<0) return; - SETFONT(de->fmt); + rdis->setFont(de->fmt); switch(de->extra[0]) { case 'V': { // show if GPS location is valid uint8_t *tile = nmea.isValid()?gps_tile:nogps_tile; - u8x8->drawTile(de->x, de->y, 1, tile); + rdis->drawTile(de->x, de->y, 1, tile); } break; case 'O': @@ -292,7 +700,7 @@ void Display::drawGPS(DispEntry *de) { float lon = nmea.getLongitude()*0.000001; Serial.print("lon: "); Serial.println(lon); snprintf(buf, 16, "%2.5f", lon); - u8x8->drawString(de->x,de->y,buf); + rdis->drawString(de->x,de->y,buf); } break; case 'A': @@ -301,7 +709,7 @@ void Display::drawGPS(DispEntry *de) { float lat = nmea.getLatitude()*0.000001; Serial.print("lat: "); Serial.println(lat); snprintf(buf, 16, "%2.5f", lat); - u8x8->drawString(de->x,de->y,buf); + rdis->drawString(de->x,de->y,buf); } break; case 'H': @@ -310,25 +718,45 @@ void Display::drawGPS(DispEntry *de) { long alt = -1; nmea.getAltitude(alt); snprintf(buf, 16, "%5fm", alt*0.00001); - u8x8->drawString(de->x,de->y,buf); + rdis->drawString(de->x,de->y,buf); } break; case 'D': { // distance // equirectangular approximation is good enough - float lat1 = nmea.getLatitude()*0.000001; - float lat2 = sonde.si()->lat; - float x = radians(nmea.getLongitude()*0.000001-sonde.si()->lon) * cos( radians((lat1+lat2)/2) ); - float y = radians(lat2-lat1); - float d = sqrt(x*x+y*y)*EARTH_RADIUS; - snprintf(buf, 16, "d=%.0fm ", d); - buf[7]=0; - u8x8->drawString(de->x, de->y, buf); + if( (sonde.si()->validPos&0x03)!=0x03 ) { + snprintf(buf, 16, "no pos "); + if(de->extra && *de->extra=='5') buf[5]=0; + } else if(!nmea.isValid()) { + snprintf(buf, 16, "no gps "); + if(de->extra && *de->extra=='5') buf[5]=0; + } else { + float lat1 = nmea.getLatitude()*0.000001; + float lat2 = sonde.si()->lat; + float x = radians(nmea.getLongitude()*0.000001-sonde.si()->lon) * cos( radians((lat1+lat2)/2) ); + float y = radians(lat2-lat1); + float d = sqrt(x*x+y*y)*EARTH_RADIUS; + if(de->extra && *de->extra=='5') { // 5-character version: ****m / ***km / **e6m + if(d>999999) snprintf(buf, 16, "%de6m ", (int)(d/1000000)); + if(d>9999) snprintf(buf, 16, "%dkm ", (int)(d/1000)); + else snprintf(buf, 16, "%dm ", (int)d); + buf[5]=0; + } else { // 6-character version: *****m / ****km) + if(d>99999) snprintf(buf, 16, "%dkm ", (int)(d/1000)); + else snprintf(buf, 16, "%dm ", (int)d); + buf[6]=0; + } + } + rdis->drawString(de->x, de->y, buf); } break; case 'I': // dIrection + if( (!nmea.isValid()) || ((sonde.si()->validPos&0x03)!=0x03 ) ) { + rdis->drawString(de->x, de->y, "---"); + break; + } { float lat1 = radians(nmea.getLatitude()*0.000001); float lat2 = radians(sonde.si()->lat); @@ -337,10 +765,13 @@ void Display::drawGPS(DispEntry *de) { float y = sin(lon2-lon1)*cos(lat2); float x = cos(lat1)*sin(lat2) - sin(lat1)*cos(lat2)*cos(lon2-lon1); float dir = atan2(y, x)/PI*180; + if(dir<0) dir+=360; Serial.printf("direction is %.2f\n", dir); - snprintf(buf, 16, "dir=%d ", (int)dir); - buf[8]=0; - u8x8->drawString(de->x, de->y, buf); + snprintf(buf, 16, "%3d", (int)dir); + buf[3]=0; + rdis->drawString(de->x, de->y, buf); + if(de->extra[1]==(char)176) + rdis->drawTile(de->x+3, de->y, 1, deg_tile); } break; case 'E': @@ -349,8 +780,8 @@ void Display::drawGPS(DispEntry *de) { } } void Display::drawText(DispEntry *de) { - SETFONT(de->fmt); - u8x8->drawString(de->x, de->y, de->extra); + rdis->setFont(de->fmt); + rdis->drawString(de->x, de->y, de->extra); } diff --git a/libraries/SondeLib/Display.h b/libraries/SondeLib/Display.h index f0a7f90..d5dbc13 100644 --- a/libraries/SondeLib/Display.h +++ b/libraries/SondeLib/Display.h @@ -5,6 +5,10 @@ #define FONT_LARGE 1 #define FONT_SMALL 0 +#include +#include +#include + struct DispEntry { int8_t y; @@ -20,14 +24,70 @@ struct DispInfo { int16_t *timeouts; }; +// Now starting towards supporting different Display types / libraries +class RawDisplay { +public: + virtual void begin() = 0; + virtual void clear() = 0; + virtual void setFont(int nr) = 0; + virtual void drawString(uint8_t x, uint8_t y, const char *s) = 0; + virtual void drawTile(uint8_t x, uint8_t y, uint8_t cnt, uint8_t *tile_ptr) = 0; + virtual void welcome() = 0; +}; + +class U8x8Display : public RawDisplay { +private: + U8X8 *u8x8 = NULL; // initialize later after reading config file + int _type; + +public: + U8x8Display(int type = 0) { _type = type; } + void begin(); + void clear(); + void setFont(int nr); + void drawString(uint8_t x, uint8_t y, const char *s); + void drawTile(uint8_t x, uint8_t y, uint8_t cnt, uint8_t *tile_ptr); + void welcome(); +}; + +class MY_ILI9225 : public TFT22_ILI9225 { + using TFT22_ILI9225::TFT22_ILI9225; +public: + uint16_t drawGFXChar(int16_t x, int16_t y, unsigned char c, uint16_t color); + void drawTile(uint8_t x, uint8_t y, uint8_t cnt, uint8_t *tile_ptr); +}; + +class ILI9225Display : public RawDisplay { +private: + MY_ILI9225 *tft = NULL; // initialize later after reading config file + uint8_t yofs=0; + uint8_t fsize=0; + + +public: + void begin(); + void clear(); + void setFont(int nr); + void drawString(uint8_t x, uint8_t y, const char *s); + void drawTile(uint8_t x, uint8_t y, uint8_t cnt, uint8_t *tile_ptr); + void welcome(); +}; class Display { private: + void freeLayouts(); + int allocDispInfo(int entries, DispInfo *d); + void parseDispElement(char *text, DispEntry *de); + public: + void initFromFile(); + void setLayout(DispInfo *layout); DispInfo *layout; + static RawDisplay *rdis; Display(); + void init(); static char buf[17]; static void drawLat(DispEntry *de); static void drawLon(DispEntry *de); diff --git a/libraries/SondeLib/RS41.cpp b/libraries/SondeLib/RS41.cpp index 2fb6c5d..31af27a 100644 --- a/libraries/SondeLib/RS41.cpp +++ b/libraries/SondeLib/RS41.cpp @@ -89,7 +89,7 @@ int RS41::setup(float frequency) return 1; } if(sx1278.setFSK()!=0) { - RS41_DBG(Serial.println("Setting FSM mode FAILED")); + RS41_DBG(Serial.println("Setting FSK mode FAILED")); return 1; } if(sx1278.setBitrate(4800)!=0) { @@ -363,7 +363,10 @@ static void posrs41(const byte b[], uint32_t b_len, uint32_t p) Serial.print(getcard16(b, b_len, p+18UL)&255UL); Serial.print("Sats"); sonde.si()->alt = heig; - sonde.si()->validPos = true; + if( 0==(int)(lat*10000) && 0==(int)(long0*10000) ) + sonde.si()->validPos = 0; + else + sonde.si()->validPos = 0x3f; } /* end posrs41() */ diff --git a/libraries/SondeLib/Scanner.cpp b/libraries/SondeLib/Scanner.cpp index 4239910..ec0b87e 100644 --- a/libraries/SondeLib/Scanner.cpp +++ b/libraries/SondeLib/Scanner.cpp @@ -3,8 +3,7 @@ #include #include "Sonde.h" - -extern U8X8_SSD1306_128X64_NONAME_SW_I2C *u8x8; +#include "Display.h" #define CHANBW 10 #define PIXSAMPL (50/CHANBW) @@ -56,7 +55,7 @@ void Scanner::plotResult() // don't overwrite MHz marker text if(i<3*8 || (i>=7*8&&i<10*8) || i>=13*8) continue; } - u8x8->drawTile(i/8, y, 1, row+8*y); + disp.rdis->drawTile(i/8, y, 1, row+8*y); } } } diff --git a/libraries/SondeLib/Sonde.cpp b/libraries/SondeLib/Sonde.cpp index 42a1c55..4265136 100644 --- a/libraries/SondeLib/Sonde.cpp +++ b/libraries/SondeLib/Sonde.cpp @@ -8,7 +8,6 @@ #include "SX1278FSK.h" #include "Display.h" -extern U8X8_SSD1306_128X64_NONAME_SW_I2C *u8x8; extern SX1278FSK sx1278; RXTask rxtask = { -1, -1, -1, 0xFFFF, 0 }; @@ -51,6 +50,8 @@ Sonde::Sonde() { // Seems like on startup, GPIO4 is 1 on v1 boards, 0 on v2.1 boards? config.gps_rxd = -1; config.gps_txd = -1; + config.oled_rst = 16; + config.disptype = 0; if(initlevels[16]==0) { config.oled_sda = 4; config.oled_scl = 15; @@ -60,16 +61,38 @@ Sonde::Sonde() { config.oled_sda = 21; config.oled_scl = 22; if(initlevels[17]==0) { // T-Beam - config.button_pin = 39; - config.button2_pin = T4 + 128; // T4 == GPIO13 - config.gps_rxd = 12; + if(initlevels[12]==0) { // T-Beam v1.0 + config.button_pin = 38; + config.button2_pin = -1; //T4 + 128; // T4 = GPIO13 + config.gps_rxd = 34; + // for now, lets assume TFT display / SPI + // CS=0, RST=14, RS=2, SDA=4, CLK=13 + config.disptype = 1; + config.oled_sda = 4; + config.oled_scl = 13; + config.oled_rst = 14; + config.tft_rs = 2; + config.tft_cs = 0; + } else { + config.button_pin = 39; + config.button2_pin = T4 + 128; // T4 == GPIO13 + config.gps_rxd = 12; + // Check if we possibly have a large display + if(initlevels[21]==0) { + config.disptype = 1; + config.oled_sda = 4; + config.oled_scl = 21; + config.oled_rst = 22; + config.tft_rs = 2; + config.tft_cs = 0; + } + } } else { config.button_pin = 2 + 128; // GPIO2 / T2 config.button2_pin = 14 + 128; // GPIO14 / T6 } } // - config.oled_rst = 16; config.noisefloor = -125; strcpy(config.call,"NOCALL"); strcpy(config.passcode, "---"); @@ -130,13 +153,19 @@ void Sonde::setConfig(const char *cfg) { } else if(strcmp(cfg,"touch_thresh")==0) { config.touch_thresh = atoi(val); } else if(strcmp(cfg,"led_pout")==0) { - config.led_pout = atoi(val); + config.led_pout = atoi(val); + } else if(strcmp(cfg,"disptype")==0) { + config.disptype = atoi(val); } else if(strcmp(cfg,"oled_sda")==0) { config.oled_sda = atoi(val); } else if(strcmp(cfg,"oled_scl")==0) { config.oled_scl = atoi(val); } else if(strcmp(cfg,"oled_rst")==0) { config.oled_rst = atoi(val); + } else if(strcmp(cfg,"tft_rs")==0) { + config.tft_rs = atoi(val); + } else if(strcmp(cfg,"tft_cs")==0) { + config.tft_cs = atoi(val); } else if(strcmp(cfg,"gps_rxd")==0) { config.gps_rxd = atoi(val); } else if(strcmp(cfg,"gps_txd")==0) { @@ -329,6 +358,7 @@ void Sonde::receive() { int event = getKeyPressEvent(); if (!event) event = timeoutEvent(si); int action = (event==EVT_NONE) ? ACT_NONE : disp.layout->actions[event]; + Serial.printf("event %x: action is %x\n", event, action); // If action is to move to a different sonde index, we do update things here, set activate // to force the sx1278 task to call sonde.setup(), and pass information about sonde to // main loop (display update...) @@ -369,8 +399,7 @@ rxloop: /// TODO: THis has caused an exception when swithcing back to spectrumm... Serial.printf("waitRXcomplete returning %04x (%s)\n", res, (res&0xff)<4?RXstr[res&0xff]:""); // currently used only by RS92 - // TODO: rxtask.currentSonde might not be the right thing (after sonde channel change) - switch(sondeList[/*rxtask.*/currentSonde].type) { + switch(sondeList[rxtask.receiveSonde].type) { case STYPE_RS41: rs41.waitRXcomplete(); break; @@ -430,12 +459,8 @@ uint8_t Sonde::updateState(uint8_t event) { n = config.display; } if(n>=0&&n<5) { + Serial.printf("Setting display mode %d\n", n); disp.setLayout(n); - // TODO: This is kind of a hack... - // ACT_NEXTSONDE will cause loopDecoder to call enterMode(ST_DECODER) - //return ACT_NEXTSONDE; - - // TODO::: we probably should clear the display?? -- YES sonde.clearDisplay(); return 0xFF; } @@ -500,7 +525,7 @@ void Sonde::updateDisplay() } void Sonde::clearDisplay() { - u8x8->clearDisplay(); + disp.rdis->clear(); } Sonde sonde = Sonde(); diff --git a/libraries/SondeLib/Sonde.h b/libraries/SondeLib/Sonde.h index aa5f056..a0ad31a 100644 --- a/libraries/SondeLib/Sonde.h +++ b/libraries/SondeLib/Sonde.h @@ -85,9 +85,12 @@ typedef struct st_rdzconfig { int button2_pin; // PIN port number menu button (+128 for touch mode) int touch_thresh; // Threshold value (0..100) for touch input button int led_pout; // POUT port number of LED (used as serial monitor) - int oled_sda; // OLED data pin - int oled_scl; // OLED clock pin - int oled_rst; // OLED reset pin + int disptype; // 0=OLED; 1=ILI9225 + int oled_sda; // OLED/TFT data pin + int oled_scl; // OLED/TFT clock pin + int oled_rst; // OLED/TFT reset pin + int tft_rs; // TFT RS pin + int tft_cs; // TFT CS pin int gps_rxd; // GPS module RXD pin. We expect 9600 baud NMEA data. int gps_txd; // GPS module TXD pin int debug; // show port and config options after reboot @@ -112,6 +115,7 @@ typedef struct st_rdzconfig { // for now, one feed for each type is enough, but might get extended to more? struct st_feedinfo udpfeed; // target for AXUDP messages struct st_feedinfo tcpfeed; // target for APRS-IS TCP connections + struct st_kisstnc kisstnc; // target for KISS TNC (via TCP, mainly for APRSdroid) } RDZConfig; typedef struct st_sondeinfo { diff --git a/libraries/SondeLib/TFT22_ILI9225.cpp b/libraries/SondeLib/TFT22_ILI9225.cpp new file mode 100644 index 0000000..0fb7105 --- /dev/null +++ b/libraries/SondeLib/TFT22_ILI9225.cpp @@ -0,0 +1,1379 @@ +// SPDX-License-Identifier: GPL-3.0 +// original source: https://github.com/Nkawu/TFT22_ILI9225 + + +#include "TFT22_ILI9225.h" + +//#define DEBUG +#ifdef DEBUG + //#define DB_PRINT( x, ... ) { char dbgbuf[60]; sprintf_P( dbgbuf, (const char*) F( x ), __VA_ARGS__ ) ; Serial.print( dbgbuf ); } + #define DB_PRINT( ... ) { char dbgbuf[60]; sprintf( dbgbuf, __VA_ARGS__ ) ; Serial.println( dbgbuf ); } + +#else + #define DB_PRINT( ... ) ; +#endif + + + +#ifndef ARDUINO_STM32_FEATHER + #include "pins_arduino.h" + #ifndef RASPI + #include "wiring_private.h" + #endif +#endif +#include +#ifdef __AVR__ + #include +#elif defined(ESP8266) || defined(ESP32) + #include +#endif + +// Many (but maybe not all) non-AVR board installs define macros +// for compatibility with existing PROGMEM-reading AVR code. +// Do our own checks and defines here for good measure... + +#ifndef pgm_read_byte + #define pgm_read_byte(addr) (*(const unsigned char *)(addr)) +#endif +#ifndef pgm_read_word + #define pgm_read_word(addr) (*(const unsigned short *)(addr)) +#endif +#ifndef pgm_read_dword + #define pgm_read_dword(addr) (*(const unsigned long *)(addr)) +#endif + +// Pointers are a peculiar case...typically 16-bit on AVR boards, +// 32 bits elsewhere. Try to accommodate both... + +#if !defined(__INT_MAX__) || (__INT_MAX__ > 0xFFFF) + #define pgm_read_pointer(addr) ((void *)pgm_read_dword(addr)) +#else + #define pgm_read_pointer(addr) ((void *)pgm_read_word(addr)) +#endif + +// Control pins + +#ifdef USE_FAST_PINIO + #define SPI_DC_HIGH() *dcport |= dcpinmask + #define SPI_DC_LOW() *dcport &= ~dcpinmask + #define SPI_CS_HIGH() *csport |= cspinmask + #define SPI_CS_LOW() *csport &= ~cspinmask +#else + #define SPI_DC_HIGH() digitalWrite(_rs, HIGH) + #define SPI_DC_LOW() digitalWrite(_rs, LOW) + #define SPI_CS_HIGH() digitalWrite(_cs, HIGH) + #define SPI_CS_LOW() digitalWrite(_cs, LOW) +#endif + +// Software SPI Macros + +#ifdef USE_FAST_PINIO + #define SSPI_MOSI_HIGH() *mosiport |= mosipinmask + #define SSPI_MOSI_LOW() *mosiport &= ~mosipinmask + #define SSPI_SCK_HIGH() *clkport |= clkpinmask + #define SSPI_SCK_LOW() *clkport &= ~clkpinmask +#else + #define SSPI_MOSI_HIGH() digitalWrite(_sdi, HIGH) + #define SSPI_MOSI_LOW() digitalWrite(_sdi, LOW) + #define SSPI_SCK_HIGH() digitalWrite(_clk, HIGH) + #define SSPI_SCK_LOW() digitalWrite(_clk, LOW) +#endif + +#define SSPI_BEGIN_TRANSACTION() +#define SSPI_END_TRANSACTION() +#define SSPI_WRITE(v) _spiWrite(v) +#define SSPI_WRITE16(s) SSPI_WRITE((s) >> 8); SSPI_WRITE(s) +#define SSPI_WRITE32(l) SSPI_WRITE((l) >> 24); SSPI_WRITE((l) >> 16); SSPI_WRITE((l) >> 8); SSPI_WRITE(l) +#define SSPI_WRITE_PIXELS(c,l) for(uint32_t i=0; i<(l); i+=2){ SSPI_WRITE(((uint8_t*)(c))[i+1]); SSPI_WRITE(((uint8_t*)(c))[i]); } + +// Hardware SPI Macros +#ifndef ESP32 + #ifdef SPI_CHANNEL + extern SPIClass SPI_CHANNEL; + #define SPI_OBJECT SPI_CHANNEL + #else + #define SPI_OBJECT SPI + #endif +#else + #define SPI_OBJECT _spi +#endif + +#if defined (__AVR__) || defined(TEENSYDUINO) || defined(ARDUINO_ARCH_STM32F1) + #define HSPI_SET_CLOCK() SPI_OBJECT.setClockDivider(SPI_CLOCK_DIV2); +#elif defined (__arm__) + #define HSPI_SET_CLOCK() SPI_OBJECT.setClockDivider(11); +#elif defined(ESP8266) || defined(ESP32) + #define HSPI_SET_CLOCK() SPI_OBJECT.setFrequency(SPI_DEFAULT_FREQ); +#elif defined(RASPI) + #define HSPI_SET_CLOCK() SPI_OBJECT.setClock(SPI_DEFAULT_FREQ); +#elif defined(ARDUINO_ARCH_STM32F1) + #define HSPI_SET_CLOCK() SPI_OBJECT.setClock(SPI_DEFAULT_FREQ); +#else + #define HSPI_SET_CLOCK() +#endif + +#ifdef SPI_HAS_TRANSACTION + #define HSPI_BEGIN_TRANSACTION() SPI_OBJECT.beginTransaction(SPISettings(SPI_DEFAULT_FREQ, MSBFIRST, SPI_MODE0)) + #define HSPI_END_TRANSACTION() SPI_OBJECT.endTransaction() +#else + #define HSPI_BEGIN_TRANSACTION() HSPI_SET_CLOCK(); SPI_OBJECT.setBitOrder(MSBFIRST); SPI_OBJECT.setDataMode(SPI_MODE0) + #define HSPI_END_TRANSACTION() +#endif + +#ifdef ESP32 + #define SPI_HAS_WRITE_PIXELS +#endif +#if defined(ESP8266) || defined(ESP32) + // Optimized SPI (ESP8266 and ESP32) + #define HSPI_READ() SPI_OBJECT.transfer(0) + #define HSPI_WRITE(b) SPI_OBJECT.write(b) + #define HSPI_WRITE16(s) SPI_OBJECT.write16(s) + #define HSPI_WRITE32(l) SPI_OBJECT.write32(l) + #ifdef SPI_HAS_WRITE_PIXELS + #define SPI_MAX_PIXELS_AT_ONCE 32 + #define HSPI_WRITE_PIXELS(c,l) SPI_OBJECT.writePixels(c,l) + #else + #define HSPI_WRITE_PIXELS(c,l) for(uint32_t i=0; i<((l)/2); i++){ SPI_WRITE16(((uint16_t*)(c))[i]); } + #endif +#elif defined ( __STM32F1__ ) + #define HSPI_WRITE(b) SPI_OBJECT.write(b) + #define HSPI_WRITE16(s) SPI_OBJECT.write16(s) + +#else + // Standard Byte-by-Byte SPI + + #if defined (__AVR__) || defined(TEENSYDUINO) + static inline uint8_t _avr_spi_read(void) __attribute__((always_inline)); + static inline uint8_t _avr_spi_read(void) { + uint8_t r = 0; + SPDR = r; + while(!(SPSR & _BV(SPIF))); + r = SPDR; + return r; + } + #define HSPI_WRITE(b) {SPDR = (b); while(!(SPSR & _BV(SPIF)));} + // #define HSPI_READ() _avr_spi_read() + #else + #define HSPI_WRITE(b) SPI_OBJECT.transfer((uint8_t)(b)) + // #define HSPI_READ() HSPI_WRITE(0) + #endif + // #define HSPI_WRITE16(s) HSPI_WRITE((s) >> 8); HSPI_WRITE(s) + // #define HSPI_WRITE32(l) HSPI_WRITE((l) >> 24); HSPI_WRITE((l) >> 16); HSPI_WRITE((l) >> 8); HSPI_WRITE(l) + // #define HSPI_WRITE_PIXELS(c,l) for(uint32_t i=0; i<(l); i+=2){ HSPI_WRITE(((uint8_t*)(c))[i+1]); HSPI_WRITE(((uint8_t*)(c))[i]); } +#endif + +// Final SPI Macros + +#if defined (ARDUINO_ARCH_ARC32) + #define SPI_DEFAULT_FREQ 16000000 +#elif defined (__AVR__) || defined(TEENSYDUINO) + #define SPI_DEFAULT_FREQ 8000000 +#elif defined(ESP8266) || defined(ESP32) + #define SPI_DEFAULT_FREQ 40000000 +#elif defined(RASPI) + #define SPI_DEFAULT_FREQ 80000000 +#elif defined(ARDUINO_ARCH_STM32F1) + #define SPI_DEFAULT_FREQ 18000000 + //#define SPI_DEFAULT_FREQ 36000000 +#else + #define SPI_DEFAULT_FREQ 24000000 +#endif + +#define SPI_BEGIN() if(_clk < 0){SPI_OBJECT.begin();} +#define SPI_BEGIN_TRANSACTION() if(_clk < 0){HSPI_BEGIN_TRANSACTION();} +#define SPI_END_TRANSACTION() if(_clk < 0){HSPI_END_TRANSACTION();} +// #define SPI_WRITE16(s) if(_clk < 0){HSPI_WRITE16(s);}else{SSPI_WRITE16(s);} +// #define SPI_WRITE32(l) if(_clk < 0){HSPI_WRITE32(l);}else{SSPI_WRITE32(l);} +// #define SPI_WRITE_PIXELS(c,l) if(_clk < 0){HSPI_WRITE_PIXELS(c,l);}else{SSPI_WRITE_PIXELS(c,l);} + +// Constructor when using software SPI. All output pins are configurable. +TFT22_ILI9225::TFT22_ILI9225(int8_t rst, int8_t rs, int8_t cs, int8_t sdi, int8_t clk, int8_t led) { + _rst = rst; + _rs = rs; + _cs = cs; + _sdi = sdi; + _clk = clk; + _led = led; + _brightness = 255; // Set to maximum brightness + hwSPI = false; + writeFunctionLevel = 0; + gfxFont = NULL; +} + +// Constructor when using software SPI. All output pins are configurable. Adds backlight brightness 0-255 +TFT22_ILI9225::TFT22_ILI9225(int8_t rst, int8_t rs, int8_t cs, int8_t sdi, int8_t clk, int8_t led, uint8_t brightness) { + _rst = rst; + _rs = rs; + _cs = cs; + _sdi = sdi; + _clk = clk; + _led = led; + _brightness = brightness; + hwSPI = false; + writeFunctionLevel = 0; + gfxFont = NULL; +} + +// Constructor when using hardware SPI. Faster, but must use SPI pins +// specific to each board type (e.g. 11,13 for Uno, 51,52 for Mega, etc.) +TFT22_ILI9225::TFT22_ILI9225(int8_t rst, int8_t rs, int8_t cs, int8_t led) { + _rst = rst; + _rs = rs; + _cs = cs; + _sdi = _clk = -1; + _led = led; + _brightness = 255; // Set to maximum brightness + hwSPI = true; + writeFunctionLevel = 0; + gfxFont = NULL; +} + +// Constructor when using hardware SPI. Faster, but must use SPI pins +// specific to each board type (e.g. 11,13 for Uno, 51,52 for Mega, etc.) +// Adds backlight brightness 0-255 +TFT22_ILI9225::TFT22_ILI9225(int8_t rst, int8_t rs, int8_t cs, int8_t led, uint8_t brightness) { + _rst = rst; + _rs = rs; + _cs = cs; + _sdi = _clk = -1; + _led = led; + _brightness = brightness; + hwSPI = true; + writeFunctionLevel = 0; + gfxFont = NULL; +} + + +#ifdef ESP32 +void TFT22_ILI9225::begin(SPIClass &spi) +#else +void TFT22_ILI9225::begin() +#endif +{ +#ifdef ESP32 + _spi = spi; +#endif + // Set up reset pin + if (_rst > 0) { + pinMode(_rst, OUTPUT); + digitalWrite(_rst, LOW); + } + // Set up backlight pin, turn off initially + if (_led > 0) { + pinMode(_led, OUTPUT); + setBacklight(false); + } + + // Control pins + pinMode(_rs, OUTPUT); + digitalWrite(_rs, LOW); + pinMode(_cs, OUTPUT); + digitalWrite(_cs, HIGH); + + #ifdef USE_FAST_PINIO + csport = portOutputRegister(digitalPinToPort(_cs)); + cspinmask = digitalPinToBitMask(_cs); + dcport = portOutputRegister(digitalPinToPort(_rs)); + dcpinmask = digitalPinToBitMask(_rs); +#endif + + // Software SPI + if(_clk >= 0){ + pinMode(_sdi, OUTPUT); + digitalWrite(_sdi, LOW); + pinMode(_clk, OUTPUT); + digitalWrite(_clk, HIGH); +#ifdef USE_FAST_PINIO + clkport = portOutputRegister(digitalPinToPort(_clk)); + clkpinmask = digitalPinToBitMask(_clk); + mosiport = portOutputRegister(digitalPinToPort(_sdi)); + mosipinmask = digitalPinToBitMask(_sdi); + SSPI_SCK_LOW(); + SSPI_MOSI_LOW(); + } else { + clkport = 0; + clkpinmask = 0; + mosiport = 0; + mosipinmask = 0; +#endif + } + + // Hardware SPI + if(_clk < 0){ + SPI_BEGIN(); + } else { + SPI_OBJECT.begin(_clk, -1, _sdi, _cs); + _clk = -1; // force use of hardware SPI + } + + // Initialization Code + if (_rst > 0) { + digitalWrite(_rst, HIGH); // Pull the reset pin high to release the ILI9225C from the reset status + delay(1); + digitalWrite(_rst, LOW); // Pull the reset pin low to reset ILI9225 + delay(10); + digitalWrite(_rst, HIGH); // Pull the reset pin high to release the ILI9225C from the reset status + delay(50); + } + + /* Start Initial Sequence */ + + /* Set SS bit and direction output from S528 to S1 */ + startWrite(); + _writeRegister(ILI9225_POWER_CTRL1, 0x0000); // Set SAP,DSTB,STB + _writeRegister(ILI9225_POWER_CTRL2, 0x0000); // Set APON,PON,AON,VCI1EN,VC + _writeRegister(ILI9225_POWER_CTRL3, 0x0000); // Set BT,DC1,DC2,DC3 + _writeRegister(ILI9225_POWER_CTRL4, 0x0000); // Set GVDD + _writeRegister(ILI9225_POWER_CTRL5, 0x0000); // Set VCOMH/VCOML voltage + endWrite(); + delay(40); + + // Power-on sequence + startWrite(); + _writeRegister(ILI9225_POWER_CTRL2, 0x0018); // Set APON,PON,AON,VCI1EN,VC + _writeRegister(ILI9225_POWER_CTRL3, 0x6121); // Set BT,DC1,DC2,DC3 + _writeRegister(ILI9225_POWER_CTRL4, 0x006F); // Set GVDD /*007F 0088 */ + _writeRegister(ILI9225_POWER_CTRL5, 0x495F); // Set VCOMH/VCOML voltage + _writeRegister(ILI9225_POWER_CTRL1, 0x0800); // Set SAP,DSTB,STB + endWrite(); + delay(10); + startWrite(); + _writeRegister(ILI9225_POWER_CTRL2, 0x103B); // Set APON,PON,AON,VCI1EN,VC + endWrite(); + delay(50); + + startWrite(); + _writeRegister(ILI9225_DRIVER_OUTPUT_CTRL, 0x011C); // set the display line number and display direction + _writeRegister(ILI9225_LCD_AC_DRIVING_CTRL, 0x0100); // set 1 line inversion + _writeRegister(ILI9225_ENTRY_MODE, 0x1038); // set GRAM write direction and BGR=1. + _writeRegister(ILI9225_DISP_CTRL1, 0x0000); // Display off + _writeRegister(ILI9225_BLANK_PERIOD_CTRL1, 0x0808); // set the back porch and front porch + _writeRegister(ILI9225_FRAME_CYCLE_CTRL, 0x1100); // set the clocks number per line + _writeRegister(ILI9225_INTERFACE_CTRL, 0x0000); // CPU interface + _writeRegister(ILI9225_OSC_CTRL, 0x0D01); // Set Osc /*0e01*/ + _writeRegister(ILI9225_VCI_RECYCLING, 0x0020); // Set VCI recycling + _writeRegister(ILI9225_RAM_ADDR_SET1, 0x0000); // RAM Address + _writeRegister(ILI9225_RAM_ADDR_SET2, 0x0000); // RAM Address + + /* Set GRAM area */ + _writeRegister(ILI9225_GATE_SCAN_CTRL, 0x0000); + _writeRegister(ILI9225_VERTICAL_SCROLL_CTRL1, 0x00DB); + _writeRegister(ILI9225_VERTICAL_SCROLL_CTRL2, 0x0000); + _writeRegister(ILI9225_VERTICAL_SCROLL_CTRL3, 0x0000); + _writeRegister(ILI9225_PARTIAL_DRIVING_POS1, 0x00DB); + _writeRegister(ILI9225_PARTIAL_DRIVING_POS2, 0x0000); + _writeRegister(ILI9225_HORIZONTAL_WINDOW_ADDR1, 0x00AF); + _writeRegister(ILI9225_HORIZONTAL_WINDOW_ADDR2, 0x0000); + _writeRegister(ILI9225_VERTICAL_WINDOW_ADDR1, 0x00DB); + _writeRegister(ILI9225_VERTICAL_WINDOW_ADDR2, 0x0000); + + /* Set GAMMA curve */ + _writeRegister(ILI9225_GAMMA_CTRL1, 0x0000); + _writeRegister(ILI9225_GAMMA_CTRL2, 0x0808); + _writeRegister(ILI9225_GAMMA_CTRL3, 0x080A); + _writeRegister(ILI9225_GAMMA_CTRL4, 0x000A); + _writeRegister(ILI9225_GAMMA_CTRL5, 0x0A08); + _writeRegister(ILI9225_GAMMA_CTRL6, 0x0808); + _writeRegister(ILI9225_GAMMA_CTRL7, 0x0000); + _writeRegister(ILI9225_GAMMA_CTRL8, 0x0A00); + _writeRegister(ILI9225_GAMMA_CTRL9, 0x0710); + _writeRegister(ILI9225_GAMMA_CTRL10, 0x0710); + + _writeRegister(ILI9225_DISP_CTRL1, 0x0012); + endWrite(); + delay(50); + startWrite(); + _writeRegister(ILI9225_DISP_CTRL1, 0x1017); + endWrite(); + + // Turn on backlight + setBacklight(true); + setOrientation(0); + + // Initialize variables + setBackgroundColor( COLOR_BLACK ); + + clear(); +} + + +void TFT22_ILI9225::_spiWrite(uint8_t b) { + if(_clk < 0){ + HSPI_WRITE(b); + return; + } + // Fast SPI bitbang swiped from LPD8806 library + for(uint8_t bit = 0x80; bit; bit >>= 1){ + if((b) & bit){ + SSPI_MOSI_HIGH(); + } else { + SSPI_MOSI_LOW(); + } + SSPI_SCK_HIGH(); + SSPI_SCK_LOW(); + } +} + +void TFT22_ILI9225::_spiWrite16(uint16_t s) +{ + // Attempt to use HSPI_WRITE16 if available + #ifdef HSPI_WRITE16 + if(_clk < 0){ + HSPI_WRITE16(s); + return; + } + #endif + // Fallback to SSPI_WRITE16 if HSPI_WRITE16 not available + SSPI_WRITE16(s); +} + +void TFT22_ILI9225::_spiWriteCommand(uint8_t c) { + SPI_DC_LOW(); + SPI_CS_LOW(); + _spiWrite(c); + SPI_CS_HIGH(); +} + + +void TFT22_ILI9225::_spiWriteData(uint8_t c) { + SPI_DC_HIGH(); + SPI_CS_LOW(); + _spiWrite(c); + SPI_CS_HIGH(); +} + +void TFT22_ILI9225::_orientCoordinates(uint16_t &x1, uint16_t &y1) { + + switch (_orientation) { + case 0: // ok + break; + case 1: // ok + y1 = _maxY - y1 - 1; + _swap(x1, y1); + break; + case 2: // ok + x1 = _maxX - x1 - 1; + y1 = _maxY - y1 - 1; + break; + case 3: // ok + x1 = _maxX - x1 - 1; + _swap(x1, y1); + break; + } +} + +void TFT22_ILI9225::_setWindow(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1) { + _setWindow( x0, y0, x1, y1, TopDown_L2R ); // default for drawing characters +} + +void TFT22_ILI9225::_setWindow(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1, autoIncMode_t mode) { + DB_PRINT( "setWindows( x0=%d, y0=%d, x1=%d, y1=%d, mode=%d", x0,y0,x1,y1,mode ); + // clip to TFT-Dimensions + x0 = min( x0, (uint16_t) (_maxX-1) ); + x1 = min( x1, (uint16_t) (_maxX-1) ); + y0 = min( y0, (uint16_t) (_maxY-1) ); + y1 = min( y1, (uint16_t) (_maxY-1) ); + _orientCoordinates(x0, y0); + _orientCoordinates(x1, y1); + + if (x1 0 ) mode = modeTab[_orientation-1][mode]; + _writeRegister(ILI9225_ENTRY_MODE, 0x1000 | ( mode<<3) ); + _writeRegister(ILI9225_HORIZONTAL_WINDOW_ADDR1,x1); + _writeRegister(ILI9225_HORIZONTAL_WINDOW_ADDR2,x0); + + _writeRegister(ILI9225_VERTICAL_WINDOW_ADDR1,y1); + _writeRegister(ILI9225_VERTICAL_WINDOW_ADDR2,y0); + DB_PRINT( "gedreht: x0=%d, y0=%d, x1=%d, y1=%d, mode=%d", x0,y0,x1,y1,mode ); + // starting position within window and increment/decrement direction + switch ( mode>>1 ) { + case 0: + _writeRegister(ILI9225_RAM_ADDR_SET1,x1); + _writeRegister(ILI9225_RAM_ADDR_SET2,y1); + break; + case 1: + _writeRegister(ILI9225_RAM_ADDR_SET1,x0); + _writeRegister(ILI9225_RAM_ADDR_SET2,y1); + break; + case 2: + _writeRegister(ILI9225_RAM_ADDR_SET1,x1); + _writeRegister(ILI9225_RAM_ADDR_SET2,y0); + break; + case 3: + _writeRegister(ILI9225_RAM_ADDR_SET1,x0); + _writeRegister(ILI9225_RAM_ADDR_SET2,y0); + break; + } + _writeCommand16( ILI9225_GRAM_DATA_REG ); + + //_writeRegister(ILI9225_RAM_ADDR_SET1,x0); + //_writeRegister(ILI9225_RAM_ADDR_SET2,y0); + + //_writeCommand(0x00, 0x22); + + endWrite(); +} + + +void TFT22_ILI9225::_resetWindow() { + _writeRegister(ILI9225_HORIZONTAL_WINDOW_ADDR1, 0x00AF); + _writeRegister(ILI9225_HORIZONTAL_WINDOW_ADDR2, 0x0000); + _writeRegister(ILI9225_VERTICAL_WINDOW_ADDR1, 0x00DB); + _writeRegister(ILI9225_VERTICAL_WINDOW_ADDR2, 0x0000); + +} + +void TFT22_ILI9225::clear() { + uint8_t old = _orientation; + setOrientation(0); + fillRectangle(0, 0, _maxX - 1, _maxY - 1, COLOR_BLACK); + setOrientation(old); + delay(10); +} + + +void TFT22_ILI9225::invert(boolean flag) { + startWrite(); + _writeCommand16(flag ? ILI9225C_INVON : ILI9225C_INVOFF); + //_writeCommand(0x00, flag ? ILI9225C_INVON : ILI9225C_INVOFF); + endWrite(); +} + + +void TFT22_ILI9225::setBacklight(boolean flag) { + blState = flag; +#ifndef ESP32 + if (_led) analogWrite(_led, blState ? _brightness : 0); +#endif +} + + +void TFT22_ILI9225::setBacklightBrightness(uint8_t brightness) { + _brightness = brightness; + setBacklight(blState); +} + + +void TFT22_ILI9225::setDisplay(boolean flag) { + if (flag) { + startWrite(); + _writeRegister(0x00ff, 0x0000); + _writeRegister(ILI9225_POWER_CTRL1, 0x0000); + endWrite(); + delay(50); + startWrite(); + _writeRegister(ILI9225_DISP_CTRL1, 0x1017); + endWrite(); + delay(200); + } else { + startWrite(); + _writeRegister(0x00ff, 0x0000); + _writeRegister(ILI9225_DISP_CTRL1, 0x0000); + endWrite(); + delay(50); + startWrite(); + _writeRegister(ILI9225_POWER_CTRL1, 0x0003); + endWrite(); + delay(200); + } +} + + +void TFT22_ILI9225::setOrientation(uint8_t orientation) { + + _orientation = orientation % 4; + + switch (_orientation) { + case 0: + _maxX = ILI9225_LCD_WIDTH; + _maxY = ILI9225_LCD_HEIGHT; + + break; + case 1: + _maxX = ILI9225_LCD_HEIGHT; + _maxY = ILI9225_LCD_WIDTH; + break; + case 2: + _maxX = ILI9225_LCD_WIDTH; + _maxY = ILI9225_LCD_HEIGHT; + break; + case 3: + _maxX = ILI9225_LCD_HEIGHT; + _maxY = ILI9225_LCD_WIDTH; + break; + } +} + + +uint8_t TFT22_ILI9225::getOrientation() { + return _orientation; +} + + +void TFT22_ILI9225::drawRectangle(uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2, uint16_t color) { + startWrite(); + drawLine(x1, y1, x1, y2, color); + drawLine(x1, y1, x2, y1, color); + drawLine(x1, y2, x2, y2, color); + drawLine(x2, y1, x2, y2, color); + endWrite(); +} + + +void TFT22_ILI9225::fillRectangle(uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2, uint16_t color) { + + _setWindow(x1, y1, x2, y2); + + startWrite(); + for(uint16_t t=(y2 - y1 + 1) * (x2 - x1 + 1); t > 0; t--) + _writeData16(color); + endWrite(); + _resetWindow(); +} + + +void TFT22_ILI9225::drawCircle(uint16_t x0, uint16_t y0, uint16_t r, uint16_t color) { + + int16_t f = 1 - r; + int16_t ddF_x = 1; + int16_t ddF_y = -2 * r; + int16_t x = 0; + int16_t y = r; + + startWrite(); + + drawPixel(x0, y0 + r, color); + drawPixel(x0, y0- r, color); + drawPixel(x0 + r, y0, color); + drawPixel(x0 - r, y0, color); + + while (x= 0) { + y--; + ddF_y += 2; + f += ddF_y; + } + x++; + ddF_x += 2; + f += ddF_x; + + drawPixel(x0 + x, y0 + y, color); + drawPixel(x0 - x, y0 + y, color); + drawPixel(x0 + x, y0 - y, color); + drawPixel(x0 - x, y0 - y, color); + drawPixel(x0 + y, y0 + x, color); + drawPixel(x0 - y, y0 + x, color); + drawPixel(x0 + y, y0 - x, color); + drawPixel(x0 - y, y0 - x, color); + } + endWrite(); +} + + +void TFT22_ILI9225::fillCircle(uint8_t x0, uint8_t y0, uint8_t radius, uint16_t color) { + + int16_t f = 1 - radius; + int16_t ddF_x = 1; + int16_t ddF_y = -2 * radius; + int16_t x = 0; + int16_t y = radius; + + startWrite(); + while (x= 0) { + y--; + ddF_y += 2; + f += ddF_y; + } + x++; + ddF_x += 2; + f += ddF_x; + + drawLine(x0 + x, y0 + y, x0 - x, y0 + y, color); // bottom + drawLine(x0 + x, y0 - y, x0 - x, y0 - y, color); // top + drawLine(x0 + y, y0 - x, x0 + y, y0 + x, color); // right + drawLine(x0 - y, y0 - x, x0 - y, y0 + x, color); // left + } + endWrite(); + fillRectangle(x0-x, y0-y, x0+x, y0+y, color); +} + + +void TFT22_ILI9225::drawLine(uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2, uint16_t color) { + + // Classic Bresenham algorithm + int16_t steep = abs((int16_t)(y2 - y1)) > abs((int16_t)(x2 - x1)); + + int16_t dx, dy; + + if (steep) { + _swap(x1, y1); + _swap(x2, y2); + } + + if (x1 > x2) { + _swap(x1, x2); + _swap(y1, y2); + } + + dx = x2 - x1; + dy = abs((int16_t)(y2 - y1)); + + int16_t err = dx / 2; + int16_t ystep; + + if (y1 < y2) ystep = 1; + else ystep = -1; + + startWrite(); + for (; x1<=x2; x1++) { + if (steep) drawPixel(y1, x1, color); + else drawPixel(x1, y1, color); + + err -= dy; + if (err < 0) { + y1 += ystep; + err += dx; + } + } + endWrite(); +} + + +void TFT22_ILI9225::drawPixel(uint16_t x1, uint16_t y1, uint16_t color) { + + if((x1 >= _maxX) || (y1 >= _maxY)) return; +/* + _setWindow(x1, y1, x1+1, y1+1); + _orientCoordinates(x1, y1); + startWrite(); + //_writeData(color >> 8, color); + _writeData16(color); + endWrite(); +*/ + _orientCoordinates(x1, y1); + startWrite(); + _writeRegister(ILI9225_RAM_ADDR_SET1,x1); + _writeRegister(ILI9225_RAM_ADDR_SET2,y1); + _writeRegister(ILI9225_GRAM_DATA_REG,color); + + endWrite(); + + + +} + + +uint16_t TFT22_ILI9225::maxX() { + return _maxX; +} + + +uint16_t TFT22_ILI9225::maxY() { + return _maxY; +} + + +uint16_t TFT22_ILI9225::setColor(uint8_t red8, uint8_t green8, uint8_t blue8) { + // rgb16 = red5 green6 blue5 + return (red8 >> 3) << 11 | (green8 >> 2) << 5 | (blue8 >> 3); +} + + +void TFT22_ILI9225::splitColor(uint16_t rgb, uint8_t &red, uint8_t &green, uint8_t &blue) { + // rgb16 = red5 green6 blue5 + red = (rgb & 0b1111100000000000) >> 11 << 3; + green = (rgb & 0b0000011111100000) >> 5 << 2; + blue = (rgb & 0b0000000000011111) << 3; +} + + +void TFT22_ILI9225::_swap(uint16_t &a, uint16_t &b) { + uint16_t w = a; + a = b; + b = w; +} + +// Utilities +/*void TFT22_ILI9225::_writeCommand16(uint16_t command) { +# ifdef HSPI_WRITE16 + SPI_DC_LOW(); + SPI_CS_LOW(); + HSPI_WRITE16(command); + SPI_CS_HIGH(); +#else + _spiWriteCommand(command >> 8); + _spiWriteCommand(0x00ff & command); +#endif +} + +void TFT22_ILI9225::_writeData16(uint16_t data) { +# ifdef HSPI_WRITE16 + SPI_DC_HIGH(); + SPI_CS_LOW(); + HSPI_WRITE16(data); + SPI_CS_HIGH(); +#else + _spiWriteData(data >> 8); + _spiWriteData(0x00ff & data); +#endif +} +*/ +void TFT22_ILI9225::_writeCommand16(uint16_t command) { + SPI_DC_LOW(); + SPI_CS_LOW(); + if ( _clk < 0 ) { + # ifdef HSPI_WRITE16 + HSPI_WRITE16(command); + #else + HSPI_WRITE(command >> 8); + HSPI_WRITE(0x00ff & command); + #endif + } else { + // Fast SPI bitbang swiped from LPD8806 library + for(uint16_t bit = 0x8000; bit; bit >>= 1){ + if((command) & bit){ + SSPI_MOSI_HIGH(); + } else { + SSPI_MOSI_LOW(); + } + SSPI_SCK_HIGH(); + SSPI_SCK_LOW(); + } + } + SPI_CS_HIGH(); +} + +void TFT22_ILI9225::_writeData16(uint16_t data) { + SPI_DC_HIGH(); + SPI_CS_LOW(); + if ( _clk < 0 ) { + # ifdef HSPI_WRITE16 + HSPI_WRITE16(data); + #else + HSPI_WRITE(data >> 8); + HSPI_WRITE(0x00ff & data); + #endif + } else { + // Fast SPI bitbang swiped from LPD8806 library + for(uint16_t bit = 0x8000; bit; bit >>= 1){ + if((data) & bit){ + SSPI_MOSI_HIGH(); + } else { + SSPI_MOSI_LOW(); + } + SSPI_SCK_HIGH(); + SSPI_SCK_LOW(); + } + } + SPI_CS_HIGH(); +} +/*void TFT22_ILI9225::_writeData(uint8_t HI, uint8_t LO) { + _spiWriteData(HI); + _spiWriteData(LO); +} + +void TFT22_ILI9225::_writeCommand(uint8_t HI, uint8_t LO) { + _spiWriteCommand(HI); + _spiWriteCommand(LO); +}*/ + + +void TFT22_ILI9225::_writeRegister(uint16_t reg, uint16_t data) { + _writeCommand16(reg); + _writeData16(data); +} + + +void TFT22_ILI9225::drawTriangle(uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2, uint16_t x3, uint16_t y3, uint16_t color) { + startWrite(); + drawLine(x1, y1, x2, y2, color); + drawLine(x2, y2, x3, y3, color); + drawLine(x3, y3, x1, y1, color); + endWrite(); +} + + +void TFT22_ILI9225::fillTriangle(uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2, uint16_t x3, uint16_t y3, uint16_t color) { + + uint16_t a, b, y, last; + + // Sort coordinates by Y order (y3 >= y2 >= y1) + if (y1 > y2) { + _swap(y1, y2); _swap(x1, x2); + } + if (y2 > y3) { + _swap(y3, y2); _swap(x3, x2); + } + if (y1 > y2) { + _swap(y1, y2); _swap(x1, x2); + } + + startWrite(); + if (y1 == y3) { // Handle awkward all-on-same-line case as its own thing + a = b = x1; + if (x2 < a) a = x2; + else if (x2 > b) b = x2; + if (x3 < a) a = x3; + else if (x3 > b) b = x3; + drawLine(a, y1, b, y1, color); + return; + } + + int16_t dx11 = x2 - x1, + dy11 = y2 - y1, + dx12 = x3 - x1, + dy12 = y3 - y1, + dx22 = x3 - x2, + dy22 = y3 - y2; + int32_t sa = 0, + sb = 0; + + // For upper part of triangle, find scanline crossings for segments + // 0-1 and 0-2. If y2=y3 (flat-bottomed triangle), the scanline y2 + // is included here (and second loop will be skipped, avoiding a /0 + // error there), otherwise scanline y2 is skipped here and handled + // in the second loop...which also avoids a /0 error here if y1=y2 + // (flat-topped triangle). + if (y2 == y3) last = y2; // Include y2 scanline + else last = y2 - 1; // Skip it + + for (y = y1; y <= last; y++) { + a = x1 + sa / dy11; + b = x1 + sb / dy12; + sa += dx11; + sb += dx12; + /* longhand: + a = x1 + (x2 - x1) * (y - y1) / (y2 - y1); + b = x1 + (x3 - x1) * (y - y1) / (y3 - y1); + */ + if (a > b) _swap(a,b); + drawLine(a, y, b, y, color); + } + + // For lower part of triangle, find scanline crossings for segments + // 0-2 and 1-2. This loop is skipped if y2=y3. + sa = dx22 * (y - y2); + sb = dx12 * (y - y1); + for (; y<=y3; y++) { + a = x2 + sa / dy22; + b = x1 + sb / dy12; + sa += dx22; + sb += dx12; + /* longhand: + a = x2 + (x3 - x2) * (y - y2) / (y3 - y2); + b = x1 + (x3 - x1) * (y - y1) / (y3 - y1); + */ + if (a > b) _swap(a,b); + drawLine(a, y, b, y, color); + } + endWrite(); +} + + +void TFT22_ILI9225::setBackgroundColor(uint16_t color) { + _bgColor = color; +} + +void TFT22_ILI9225::setFont(uint8_t* font, bool monoSp) { + + cfont.font = font; + cfont.width = readFontByte(0); + cfont.height = readFontByte(1); + cfont.offset = readFontByte(2); + cfont.numchars = readFontByte(3); + cfont.nbrows = cfont.height / 8; + cfont.monoSp = monoSp; + + if (cfont.height % 8) cfont.nbrows++; // Set number of bytes used by height of font in multiples of 8 +} + +_currentFont TFT22_ILI9225::getFont() { + return cfont; +} + +uint16_t TFT22_ILI9225::drawText(uint16_t x, uint16_t y, STRING s, uint16_t color) { + + uint16_t currx = x; + + // Print every character in string + #ifdef USE_STRING_CLASS + for (uint8_t k = 0; k < s.length(); k++) { + currx += drawChar(currx, y, s.charAt(k), color) + 1; + } + #else + for (uint8_t k = 0; k < strlen(s); k++) { + currx += drawChar(currx, y, s[k], color) + 1; + } + #endif + return currx; +} + +uint16_t TFT22_ILI9225::getTextWidth( STRING s ) { + + uint16_t width = 0; + // Count every character in string ( +1 for spacing ) + #ifdef USE_STRING_CLASS + for (uint8_t k = 0; k < s.length(); k++) { + width += getCharWidth(s.charAt(k) ) + 1; + } + #else + for (uint8_t k = 0; k < strlen(s); k++) { + width += getCharWidth(s[k]) + 1; + } + #endif + return width; +} + +uint16_t TFT22_ILI9225::drawChar(uint16_t x, uint16_t y, uint16_t ch, uint16_t color) { + + uint8_t charData, charWidth; + uint8_t h, i, j; + uint16_t charOffset; + bool fastMode; + charOffset = (cfont.width * cfont.nbrows) + 1; // bytes used by each character + charOffset = (charOffset * (ch - cfont.offset)) + FONT_HEADER_SIZE; // char offset (add 4 for font header) + if ( cfont.monoSp ) charWidth = cfont.width; // monospaced: get char width from font + else charWidth = readFontByte(charOffset); // get chracter width from 1st byte + charOffset++; // increment pointer to first character data byte + + startWrite(); + + // use autoincrement/decrement feature, if character fits completely on screen + fastMode = ( (x+charWidth+1) < _maxX && (y+cfont.height-1) < _maxY ) ; + + if ( fastMode )_setWindow( x,y,x+charWidth+1, y+cfont.height-1 ); // set character Window + + for (i = 0; i <= charWidth; i++) { // each font "column" (+1 blank column for spacing) + h = 0; // keep track of char height + for (j = 0; j < cfont.nbrows; j++) { // each column byte + if (i == charWidth) charData = (uint8_t)0x0; // Insert blank column + else charData = readFontByte(charOffset); + charOffset++; + + // Process every row in font character + for (uint8_t k = 0; k < 8; k++) { + if (h >= cfont.height ) break; // No need to process excess bits + if (fastMode ) _writeData16( bitRead(charData, k)?color:_bgColor ); + else drawPixel( x + i, y + (j * 8) + k, bitRead(charData, k)?color:_bgColor ); + h++; + } + } + } + endWrite(); + _resetWindow(); + return charWidth; +} + +uint16_t TFT22_ILI9225::getCharWidth(uint16_t ch) { + uint16_t charOffset; + charOffset = (cfont.width * cfont.nbrows) + 1; // bytes used by each character + charOffset = (charOffset * (ch - cfont.offset)) + FONT_HEADER_SIZE; // char offset (add 4 for font header) + + return readFontByte(charOffset); // get font width from 1st byte +} + +// Draw a 1-bit image (bitmap) at the specified (x,y) position from the +// provided bitmap buffer (must be PROGMEM memory) using the specified +// foreground color (unset bits are transparent). +void TFT22_ILI9225::drawBitmap(int16_t x, int16_t y, +const uint8_t *bitmap, int16_t w, int16_t h, uint16_t color) { + _drawBitmap( x, y, bitmap, w, h, color, 0, true, true, false ); +} + +// Draw a 1-bit image (bitmap) at the specified (x,y) position from the +// provided bitmap buffer (must be PROGMEM memory) using the specified +// foreground (for set bits) and background (for clear bits) colors. +void TFT22_ILI9225::drawBitmap(int16_t x, int16_t y, +const uint8_t *bitmap, int16_t w, int16_t h, uint16_t color, uint16_t bg) { + _drawBitmap( x, y, bitmap, w, h, color, bg, false, true, false ); +} + +// drawBitmap() variant for RAM-resident (not PROGMEM) bitmaps. +void TFT22_ILI9225::drawBitmap(int16_t x, int16_t y, +uint8_t *bitmap, int16_t w, int16_t h, uint16_t color) { + _drawBitmap( x, y, bitmap, w, h, color, 0, true, false, false ); +} + +// drawBitmap() variant w/background for RAM-resident (not PROGMEM) bitmaps. +void TFT22_ILI9225::drawBitmap(int16_t x, int16_t y, uint8_t *bitmap, int16_t w, int16_t h, uint16_t color, uint16_t bg) { + _drawBitmap( x, y, bitmap, w, h, color, bg, false, false, false ); +} + +//Draw XBitMap Files (*.xbm), exported from GIMP, +//Usage: Export from GIMP to *.xbm, rename *.xbm to *.c and open in editor. +//C Array can be directly used with this function +void TFT22_ILI9225::drawXBitmap(int16_t x, int16_t y, const uint8_t *bitmap, int16_t w, int16_t h, uint16_t color) { + _drawBitmap( x, y, bitmap, w, h, color, 0, true, true, true ); +} + +void TFT22_ILI9225::drawXBitmap(int16_t x, int16_t y, const uint8_t *bitmap, int16_t w, int16_t h, uint16_t color, uint16_t bg) { + _drawBitmap( x, y, bitmap, w, h, color, bg, false, true, true ); +} + + +// internal function for drawing bitmaps with/without transparent bg, or from ram or progmem +void TFT22_ILI9225::_drawBitmap(int16_t x, int16_t y, +const uint8_t *bitmap, int16_t w, int16_t h, uint16_t color, uint16_t bg, bool transparent, bool progmem,bool Xbit) { + bool noAutoInc = false; // Flag set when transparent pixel was 'written' + int16_t i, j, byteWidth = (w + 7) / 8; + int16_t wx0,wy0,wx1,wy1,wh;//,ww; // Window-position and size + uint8_t byte=0, maskBit; + maskBit = Xbit? 0x01:0x80; + // adjust window hight/width to displaydimensions + DB_PRINT( "DrawBitmap.. maxX=%d, maxY=%d", _maxX,_maxY ); + wx0 = x<0?0:x; + wy0 = y<0?0:y; + wx1 = (x+w>_maxX?_maxX:x+w)-1; + wy1 = (y+h>_maxY?_maxY:y+h)-1; + wh = wy1-wy0 +1; + //ww = wx1-wx0 +1; + _setWindow( wx0,wy0,wx1,wy1,L2R_TopDown); + startWrite(); + for (j = y>=0?0:-y; j < (y>=0?0:-y)+wh; j++) { + for (i = 0; i < w; i++ ) { + if (i & 7) { if ( Xbit ) byte >>=1; else byte <<= 1; } + else { if ( progmem ) byte = pgm_read_byte(bitmap + j * byteWidth + i / 8); + else byte = bitmap[j * byteWidth + i / 8]; + } + if ( x+i >= wx0 && x+i <= wx1 ) { + // write only if pixel is within window + if (byte & maskBit) { + if (noAutoInc) { + //there was a transparent area, set pixelkoordinates again + drawPixel(x + i, y + j, color); + noAutoInc = false; + } + else { + _writeData16(color); + } + } + else { + if (transparent) noAutoInc = true; // no autoincrement in transparent area! + else _writeData16( bg); + } + } + } + } + endWrite(); +} + + + +//Draw XBitMap Files (*.xbm), exported from GIMP, +//Usage: Export from GIMP to *.xbm, rename *.xbm to *.c and open in editor. +//C Array can be directly used with this function +/*void TFT22_ILI9225::drawXBitmap(int16_t x, int16_t y, const uint8_t *bitmap, int16_t w, int16_t h, uint16_t color) { + + int16_t i, j, byteWidth = (w + 7) / 8; + uint8_t byte; + + startWrite(); + for (j = 0; j < h; j++) { + for (i = 0; i < w; i++ ) { + if (i & 7) byte >>= 1; + else byte = pgm_read_byte(bitmap + j * byteWidth + i / 8); + if (byte & 0x01) drawPixel(x + i, y + j, color); + } + } + endWrite(); +} +*/ + +//High speed color bitmap +void TFT22_ILI9225::drawBitmap(uint16_t x1, uint16_t y1, +const uint16_t** bitmap, int16_t w, int16_t h) { + _setWindow(x1, y1, x1+w-1, y1+h-1,L2R_TopDown); + startWrite(); + SPI_DC_HIGH(); + SPI_CS_LOW(); + for (uint16_t y = 0; y < h; y++) { + #ifdef HSPI_WRITE_PIXELS + if (_clk < 0) { + HSPI_WRITE_PIXELS(bitmap[y], w * sizeof(uint16_t)); + continue; + } + #endif + for (uint16_t x = 0; x < w; x++) { + _spiWrite16(bitmap[y][x]); + } + } + SPI_CS_HIGH(); + endWrite(); +} + +//High speed color bitmap +void TFT22_ILI9225::drawBitmap(uint16_t x1, uint16_t y1, +uint16_t** bitmap, int16_t w, int16_t h) { + _setWindow(x1, y1, x1+w-1, y1+h-1,L2R_TopDown); + startWrite(); + SPI_DC_HIGH(); + SPI_CS_LOW(); + for (uint16_t y = 0; y < h; y++) { + #ifdef HSPI_WRITE_PIXELS + if (_clk < 0) { + HSPI_WRITE_PIXELS(bitmap[y], w * sizeof(uint16_t)); + continue; + } + #endif + for (uint16_t x = 0; x < w; x++) { + _spiWrite16(bitmap[y][x]); + } + } + SPI_CS_HIGH(); + endWrite(); +} + +//1-D array High speed color bitmap +void TFT22_ILI9225::drawBitmap(uint16_t x1, uint16_t y1, +const uint16_t* bitmap, int16_t w, int16_t h) { + _setWindow(x1, y1, x1+w-1, y1+h-1,L2R_TopDown); + startWrite(); + SPI_DC_HIGH(); + SPI_CS_LOW(); + #ifdef HSPI_WRITE_PIXELS + if (_clk < 0) { + HSPI_WRITE_PIXELS(bitmap, w * h * sizeof(uint16_t)); + } else + #endif + for (uint16_t i = 0; i < h * w; ++i) { + _spiWrite16(bitmap[i]); + } + SPI_CS_HIGH(); + endWrite(); +} + +//1-D array High speed color bitmap +void TFT22_ILI9225::drawBitmap(uint16_t x1, uint16_t y1, +uint16_t* bitmap, int16_t w, int16_t h) { + _setWindow(x1, y1, x1+w-1, y1+h-1,L2R_TopDown); + startWrite(); + SPI_DC_HIGH(); + SPI_CS_LOW(); + #ifdef HSPI_WRITE_PIXELS + if (_clk < 0) { + HSPI_WRITE_PIXELS(bitmap, w * h * sizeof(uint16_t)); + } else + #endif + for (uint16_t i = 0; i < h * w; ++i) { + _spiWrite16(bitmap[i]); + } + SPI_CS_HIGH(); + endWrite(); +} + +void TFT22_ILI9225::startWrite(void){ + if (writeFunctionLevel++ == 0) { + SPI_BEGIN_TRANSACTION(); + SPI_CS_LOW(); + } +} + + +void TFT22_ILI9225::endWrite(void){ + if (--writeFunctionLevel == 0) { + SPI_CS_HIGH(); + SPI_END_TRANSACTION(); + } +} + + +// TEXT- AND CHARACTER-HANDLING FUNCTIONS ---------------------------------- + +void TFT22_ILI9225::setGFXFont(const GFXfont *f) { + gfxFont = (GFXfont *)f; +} + + +// Draw a string +void TFT22_ILI9225::drawGFXText(int16_t x, int16_t y, STRING s, uint16_t color) { + + int16_t currx = x; + + if(gfxFont) { + // Print every character in string + #ifdef USE_STRING_CLASS + for (uint8_t k = 0; k < s.length(); k++) { + currx += drawGFXChar(currx, y, s.charAt(k), color) + 1; + } + #else + for (uint8_t k = 0; k < strlen(s); k++) { + currx += drawGFXChar(currx, y, s[k], color) + 1; + } + #endif + } +} + + +// Draw a character +uint16_t TFT22_ILI9225::drawGFXChar(int16_t x, int16_t y, unsigned char c, uint16_t color) { + + c -= (uint8_t)pgm_read_byte(&gfxFont->first); + GFXglyph *glyph = &(((GFXglyph *)pgm_read_pointer(&gfxFont->glyph))[c]); + uint8_t *bitmap = (uint8_t *)pgm_read_pointer(&gfxFont->bitmap); + + uint16_t bo = pgm_read_word(&glyph->bitmapOffset); + uint8_t w = pgm_read_byte(&glyph->width), + h = pgm_read_byte(&glyph->height), + xa = pgm_read_byte(&glyph->xAdvance); + int8_t xo = pgm_read_byte(&glyph->xOffset), + yo = pgm_read_byte(&glyph->yOffset); + uint8_t xx, yy, bits = 0, bit = 0; + + // Add character clipping here one day + + startWrite(); + for(yy=0; yyfirst), + last = pgm_read_byte(&gfxFont->last); + // Char present in this font? + if((c >= first) && (c <= last)) { + GFXglyph *glyph = &(((GFXglyph *)pgm_read_pointer(&gfxFont->glyph))[c - first]); + *gw = pgm_read_byte(&glyph->width); + *gh = pgm_read_byte(&glyph->height); + *xa = pgm_read_byte(&glyph->xAdvance); + // int8_t xo = pgm_read_byte(&glyph->xOffset), + // yo = pgm_read_byte(&glyph->yOffset); + } +} + + +void TFT22_ILI9225::getGFXTextExtent(STRING str, int16_t x, int16_t y, int16_t *w, int16_t *h) { + *w = *h = 0; + #ifdef USE_STRING_CLASS + for (uint8_t k = 0; k < str.length(); k++) { + uint8_t c = str.charAt(k); + #else + for (uint8_t k = 0; k < strlen(str); k++) { + uint8_t c = str[k]; + #endif + int16_t gw, gh, xa; + getGFXCharExtent(c, &gw, &gh, &xa); + if(gh > *h) { + *h = gh; + } + *w += xa; + } +} + diff --git a/libraries/SondeLib/TFT22_ILI9225.h b/libraries/SondeLib/TFT22_ILI9225.h new file mode 100644 index 0000000..b67c964 --- /dev/null +++ b/libraries/SondeLib/TFT22_ILI9225.h @@ -0,0 +1,466 @@ +// SPDX-License-Identifier: GPL-3.0 +// original source: https://github.com/Nkawu/TFT22_ILI9225 + +#ifndef TFT22_ILI9225_h +#define TFT22_ILI9225_h + +#ifdef __STM32F1__ +#define ARDUINO_STM32_FEATHER +#define PROGMEM +// if 'SPI_CHANNEL' is not defined, 'SPI' is used, only valid for STM32F1 +//#define SPI_CHANNEL SPI_2 +#endif + +#define USE_STRING_CLASS + +#ifdef USE_STRING_CLASS + #define STRING String +#else + #define STRING const char * +#endif + +#if ARDUINO >= 100 + #include "Arduino.h" +#else + #include "WProgram.h" +#endif +#include +#include "gfxfont.h" + +#if defined(ARDUINO_STM32_FEATHER) || defined(ARDUINO_ARCH_STM32) || defined(ARDUINO_ARCH_STM32F1) || defined(STM32F1) +typedef volatile uint32 RwReg; +#endif +#if defined(ARDUINO_FEATHER52) +typedef volatile uint32_t RwReg; +#endif + +/* ILI9225 screen size */ +#define ILI9225_LCD_WIDTH 176 +#define ILI9225_LCD_HEIGHT 220 + +/* ILI9225 LCD Registers */ +#define ILI9225_DRIVER_OUTPUT_CTRL (0x01u) // Driver Output Control +#define ILI9225_LCD_AC_DRIVING_CTRL (0x02u) // LCD AC Driving Control +#define ILI9225_ENTRY_MODE (0x03u) // Entry Mode +#define ILI9225_DISP_CTRL1 (0x07u) // Display Control 1 +#define ILI9225_BLANK_PERIOD_CTRL1 (0x08u) // Blank Period Control +#define ILI9225_FRAME_CYCLE_CTRL (0x0Bu) // Frame Cycle Control +#define ILI9225_INTERFACE_CTRL (0x0Cu) // Interface Control +#define ILI9225_OSC_CTRL (0x0Fu) // Osc Control +#define ILI9225_POWER_CTRL1 (0x10u) // Power Control 1 +#define ILI9225_POWER_CTRL2 (0x11u) // Power Control 2 +#define ILI9225_POWER_CTRL3 (0x12u) // Power Control 3 +#define ILI9225_POWER_CTRL4 (0x13u) // Power Control 4 +#define ILI9225_POWER_CTRL5 (0x14u) // Power Control 5 +#define ILI9225_VCI_RECYCLING (0x15u) // VCI Recycling +#define ILI9225_RAM_ADDR_SET1 (0x20u) // Horizontal GRAM Address Set +#define ILI9225_RAM_ADDR_SET2 (0x21u) // Vertical GRAM Address Set +#define ILI9225_GRAM_DATA_REG (0x22u) // GRAM Data Register +#define ILI9225_GATE_SCAN_CTRL (0x30u) // Gate Scan Control Register +#define ILI9225_VERTICAL_SCROLL_CTRL1 (0x31u) // Vertical Scroll Control 1 Register +#define ILI9225_VERTICAL_SCROLL_CTRL2 (0x32u) // Vertical Scroll Control 2 Register +#define ILI9225_VERTICAL_SCROLL_CTRL3 (0x33u) // Vertical Scroll Control 3 Register +#define ILI9225_PARTIAL_DRIVING_POS1 (0x34u) // Partial Driving Position 1 Register +#define ILI9225_PARTIAL_DRIVING_POS2 (0x35u) // Partial Driving Position 2 Register +#define ILI9225_HORIZONTAL_WINDOW_ADDR1 (0x36u) // Horizontal Address Start Position +#define ILI9225_HORIZONTAL_WINDOW_ADDR2 (0x37u) // Horizontal Address End Position +#define ILI9225_VERTICAL_WINDOW_ADDR1 (0x38u) // Vertical Address Start Position +#define ILI9225_VERTICAL_WINDOW_ADDR2 (0x39u) // Vertical Address End Position +#define ILI9225_GAMMA_CTRL1 (0x50u) // Gamma Control 1 +#define ILI9225_GAMMA_CTRL2 (0x51u) // Gamma Control 2 +#define ILI9225_GAMMA_CTRL3 (0x52u) // Gamma Control 3 +#define ILI9225_GAMMA_CTRL4 (0x53u) // Gamma Control 4 +#define ILI9225_GAMMA_CTRL5 (0x54u) // Gamma Control 5 +#define ILI9225_GAMMA_CTRL6 (0x55u) // Gamma Control 6 +#define ILI9225_GAMMA_CTRL7 (0x56u) // Gamma Control 7 +#define ILI9225_GAMMA_CTRL8 (0x57u) // Gamma Control 8 +#define ILI9225_GAMMA_CTRL9 (0x58u) // Gamma Control 9 +#define ILI9225_GAMMA_CTRL10 (0x59u) // Gamma Control 10 + +#define ILI9225C_INVOFF 0x20 +#define ILI9225C_INVON 0x21 + +// autoincrement modes (register ILI9225_ENTRY_MODE, bit 5..3 ) +enum autoIncMode_t { R2L_BottomUp, BottomUp_R2L, L2R_BottomUp, BottomUp_L2R, R2L_TopDown, TopDown_R2L, L2R_TopDown, TopDown_L2R }; + +/* RGB 16-bit color table definition (RG565) */ +#define COLOR_BLACK 0x0000 /* 0, 0, 0 */ +#define COLOR_WHITE 0xFFFF /* 255, 255, 255 */ +#define COLOR_BLUE 0x001F /* 0, 0, 255 */ +#define COLOR_GREEN 0x07E0 /* 0, 255, 0 */ +#define COLOR_RED 0xF800 /* 255, 0, 0 */ +#define COLOR_NAVY 0x000F /* 0, 0, 128 */ +#define COLOR_DARKBLUE 0x0011 /* 0, 0, 139 */ +#define COLOR_DARKGREEN 0x03E0 /* 0, 128, 0 */ +#define COLOR_DARKCYAN 0x03EF /* 0, 128, 128 */ +#define COLOR_CYAN 0x07FF /* 0, 255, 255 */ +#define COLOR_TURQUOISE 0x471A /* 64, 224, 208 */ +#define COLOR_INDIGO 0x4810 /* 75, 0, 130 */ +#define COLOR_DARKRED 0x8000 /* 128, 0, 0 */ +#define COLOR_OLIVE 0x7BE0 /* 128, 128, 0 */ +#define COLOR_GRAY 0x8410 /* 128, 128, 128 */ +#define COLOR_GREY 0x8410 /* 128, 128, 128 */ +#define COLOR_SKYBLUE 0x867D /* 135, 206, 235 */ +#define COLOR_BLUEVIOLET 0x895C /* 138, 43, 226 */ +#define COLOR_LIGHTGREEN 0x9772 /* 144, 238, 144 */ +#define COLOR_DARKVIOLET 0x901A /* 148, 0, 211 */ +#define COLOR_YELLOWGREEN 0x9E66 /* 154, 205, 50 */ +#define COLOR_BROWN 0xA145 /* 165, 42, 42 */ +#define COLOR_DARKGRAY 0x7BEF /* 128, 128, 128 */ +#define COLOR_DARKGREY 0x7BEF /* 128, 128, 128 */ +#define COLOR_SIENNA 0xA285 /* 160, 82, 45 */ +#define COLOR_LIGHTBLUE 0xAEDC /* 172, 216, 230 */ +#define COLOR_GREENYELLOW 0xAFE5 /* 173, 255, 47 */ +#define COLOR_SILVER 0xC618 /* 192, 192, 192 */ +#define COLOR_LIGHTGRAY 0xC618 /* 192, 192, 192 */ +#define COLOR_LIGHTGREY 0xC618 /* 192, 192, 192 */ +#define COLOR_LIGHTCYAN 0xE7FF /* 224, 255, 255 */ +#define COLOR_VIOLET 0xEC1D /* 238, 130, 238 */ +#define COLOR_AZUR 0xF7FF /* 240, 255, 255 */ +#define COLOR_BEIGE 0xF7BB /* 245, 245, 220 */ +#define COLOR_MAGENTA 0xF81F /* 255, 0, 255 */ +#define COLOR_TOMATO 0xFB08 /* 255, 99, 71 */ +#define COLOR_GOLD 0xFEA0 /* 255, 215, 0 */ +#define COLOR_ORANGE 0xFD20 /* 255, 165, 0 */ +#define COLOR_SNOW 0xFFDF /* 255, 250, 250 */ +#define COLOR_YELLOW 0xFFE0 /* 255, 255, 0 */ + + +/* Font defines */ +#define FONT_HEADER_SIZE 4 // 1: pixel width of 1 font character, 2: pixel height, +#define readFontByte(x) pgm_read_byte(&cfont.font[x]) + +extern uint8_t Terminal6x8[]; +extern uint8_t Terminal11x16[]; +extern uint8_t Terminal12x16[]; +extern uint8_t Trebuchet_MS16x21[]; + +struct _currentFont +{ + uint8_t* font; + uint8_t width; + uint8_t height; + uint8_t offset; + uint8_t numchars; + uint8_t nbrows; + bool monoSp; +}; +#define MONOSPACE 1 + +#if defined (ARDUINO_STM32_FEATHER) + #undef USE_FAST_PINIO +#elif defined (__AVR__) || defined(TEENSYDUINO) || defined(ESP8266) || defined(__arm__) + #define USE_FAST_PINIO +#endif + +/// Main and core class +class TFT22_ILI9225 { + + public: + + TFT22_ILI9225(int8_t RST, int8_t RS, int8_t CS, int8_t SDI, int8_t CLK, int8_t LED); + TFT22_ILI9225(int8_t RST, int8_t RS, int8_t CS, int8_t LED); + TFT22_ILI9225(int8_t RST, int8_t RS, int8_t CS, int8_t SDI, int8_t CLK, int8_t LED, uint8_t brightness); + TFT22_ILI9225(int8_t RST, int8_t RS, int8_t CS, int8_t LED, uint8_t brightness); + + /// Initialization +#ifndef ESP32 + void begin(void); +#else + void begin(SPIClass &spi=SPI); +#endif + + /// Clear the screen + void clear(void); + + /// Invert screen + /// @param flag true to invert, false for normal screen + void invert(boolean flag); + + /// Switch backlight on or off + /// @param flag true=on, false=off + void setBacklight(boolean flag); + + /// Set backlight brightness + /// @param brightness sets backlight brightness 0-255 + void setBacklightBrightness(uint8_t brightness); + + /// Switch display on or off + /// @param flag true=on, false=off + void setDisplay(boolean flag); + + /// Set orientation + /// @param orientation orientation, 0=portrait, 1=right rotated landscape, 2=reverse portrait, 3=left rotated landscape + void setOrientation(uint8_t orientation); + + /// Get orientation + /// @return orientation orientation, 0=portrait, 1=right rotated landscape, 2=reverse portrait, 3=left rotated landscape + uint8_t getOrientation(void); + + /// Font size, x-axis + /// @return horizontal size of current font, in pixels + // uint8_t fontX(void); + + /// Font size, y-axis + /// @return vertical size of current font, in pixels + // uint8_t fontY(void); + + /// Screen size, x-axis + /// @return horizontal size of the screen, in pixels + /// @note 240 means 240 pixels and thus 0..239 coordinates (decimal) + uint16_t maxX(void); + + /// Screen size, y-axis + /// @return vertical size of the screen, in pixels + /// @note 220 means 220 pixels and thus 0..219 coordinates (decimal) + uint16_t maxY(void); + + /// Draw circle + /// @param x0 center, point coordinate, x-axis + /// @param y0 center, point coordinate, y-axis + /// @param radius radius + /// @param color 16-bit color + void drawCircle(uint16_t x0, uint16_t y0, uint16_t radius, uint16_t color); + + /// Draw solid circle + /// @param x0 center, point coordinate, x-axis + /// @param y0 center, point coordinate, y-axis + /// @param radius radius + /// @param color 16-bit color + void fillCircle(uint8_t x0, uint8_t y0, uint8_t radius, uint16_t color); + + /// Set background color + /// @param color background color, default=black + void setBackgroundColor(uint16_t color = COLOR_BLACK); + + /// Draw line, rectangle coordinates + /// @param x1 start point coordinate, x-axis + /// @param y1 start point coordinate, y-axis + /// @param x2 end point coordinate, x-axis + /// @param y2 end point coordinate, y-axis + /// @param color 16-bit color + void drawLine(uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2, uint16_t color); + + /// Draw rectangle, rectangle coordinates + /// @param x1 top left coordinate, x-axis + /// @param y1 top left coordinate, y-axis + /// @param x2 bottom right coordinate, x-axis + /// @param y2 bottom right coordinate, y-axis + /// @param color 16-bit color + void drawRectangle(uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2, uint16_t color); + + /// Draw solid rectangle, rectangle coordinates + /// @param x1 top left coordinate, x-axis + /// @param y1 top left coordinate, y-axis + /// @param x2 bottom right coordinate, x-axis + /// @param y2 bottom right coordinate, y-axis + /// @param color 16-bit color + void fillRectangle(uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2, uint16_t color); + + /// Draw pixel + /// @param x1 point coordinate, x-axis + /// @param y1 point coordinate, y-axis + /// @param color 16-bit color + void drawPixel(uint16_t x1, uint16_t y1, uint16_t color); + + /// Draw ASCII Text (pixel coordinates) + /// @param x point coordinate, x-axis + /// @param y point coordinate, y-axis + /// @param s text string + /// @param color 16-bit color, default=white + /// @return x-position behind text + uint16_t drawText(uint16_t x, uint16_t y, STRING s, uint16_t color = COLOR_WHITE); + + /// width of an ASCII Text (pixel ) + /// @param s text string + uint16_t getTextWidth( STRING s ) ; + + /// Calculate 16-bit color from 8-bit Red-Green-Blue components + /// @param red red component, 0x00..0xff + /// @param green green component, 0x00..0xff + /// @param blue blue component, 0x00..0xff + /// @return 16-bit color + uint16_t setColor(uint8_t red, uint8_t green, uint8_t blue); + + /// Calculate 8-bit Red-Green-Blue components from 16-bit color + /// @param rgb 16-bit color + /// @param red red component, 0x00..0xff + /// @param green green component, 0x00..0xff + /// @param blue blue component, 0x00..0xff + void splitColor(uint16_t rgb, uint8_t &red, uint8_t &green, uint8_t &blue); + + /// Draw triangle, triangle coordinates + /// @param x1 corner 1 coordinate, x-axis + /// @param y1 corner 1 coordinate, y-axis + /// @param x2 corner 2 coordinate, x-axis + /// @param y2 corner 2 coordinate, y-axis + /// @param x3 corner 3 coordinate, x-axis + /// @param y3 corner 3 coordinate, y-axis + /// @param color 16-bit color + void drawTriangle(uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2, uint16_t x3, uint16_t y3, uint16_t color); + + /// Draw solid triangle, triangle coordinates + /// @param x1 corner 1 coordinate, x-axis + /// @param y1 corner 1 coordinate, y-axis + /// @param x2 corner 2 coordinate, x-axis + /// @param y2 corner 2 coordinate, y-axis + /// @param x3 corner 3 coordinate, x-axis + /// @param y3 corner 3 coordinate, y-axis + /// @param color 16-bit color + void fillTriangle(uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2, uint16_t x3, uint16_t y3, uint16_t color); + + /// Set current font + /// @param font Font name + void setFont(uint8_t* font, bool monoSp=false ); // default = proportional + + /// Get current font + _currentFont getFont(); + + /// Draw single character (pixel coordinates) + /// @param x point coordinate, x-axis + /// @param y point coordinate, y-axis + /// @param ch ASCII character + /// @param color 16-bit color, default=white + /// @return width of character in display pixels + uint16_t drawChar(uint16_t x, uint16_t y, uint16_t ch, uint16_t color = COLOR_WHITE); + + /// width of an ASCII character (pixel ) + /// @param ch ASCII character + uint16_t getCharWidth( uint16_t ch ) ; + + /// Draw bitmap + /// @param x point coordinate, x-axis + /// @param y point coordinate, y-axis + /// @param bitmap + /// @param w width + /// @param h height + /// @param color 16-bit color, default=white + /// @param bg 16-bit color, background + void drawBitmap(int16_t x, int16_t y, const uint8_t *bitmap, int16_t w, int16_t h, uint16_t color); + void drawBitmap(int16_t x, int16_t y, const uint8_t *bitmap, int16_t w, int16_t h, uint16_t color, uint16_t bg); + void drawBitmap(int16_t x, int16_t y, uint8_t *bitmap, int16_t w, int16_t h, uint16_t color); + void drawBitmap(int16_t x, int16_t y, uint8_t *bitmap, int16_t w, int16_t h, uint16_t color, uint16_t bg); + + void drawXBitmap(int16_t x, int16_t y, const uint8_t *bitmap, int16_t w, int16_t h, uint16_t color); + void drawXBitmap(int16_t x, int16_t y, const uint8_t *bitmap, int16_t w, int16_t h, uint16_t color, uint16_t bg); + + /// Draw bitmap + /// @param x point coordinate, x-axis + /// @param y point coordinate, y-axis + /// @param bitmap, 2D 16bit color bitmap + /// @param w width + /// @param h height + void drawBitmap(uint16_t x, uint16_t y, const uint16_t** bitmap, int16_t w, int16_t h); + void drawBitmap(uint16_t x, uint16_t y, uint16_t** bitmap, int16_t w, int16_t h); + + /// Draw bitmap + /// @param x point coordinate, x-axis + /// @param y point coordinate, y-axis + /// @param bitmap, 1D 16bit color bitmap + /// @param w width + /// @param h height + void drawBitmap(uint16_t x, uint16_t y, const uint16_t* bitmap, int16_t w, int16_t h); + void drawBitmap(uint16_t x, uint16_t y, uint16_t* bitmap, int16_t w, int16_t h); + + /// Set current GFX font + /// @param f GFX font name defined in include file + void setGFXFont(const GFXfont *f = NULL); + + /// Draw a string with the current GFX font + /// @param x point coordinate, x-axis + /// @param y point coordinate, y-axis + /// @param s string to print + /// @param color 16-bit color + void drawGFXText(int16_t x, int16_t y, STRING s, uint16_t color); + + /// Get the width & height of a text string with the current GFX font + /// @param str string to analyze + /// @param x point coordinate, x-axis + /// @param y point coordinate, y-axis + /// @param w width in pixels of string + /// @param h height in pixels of string + void getGFXTextExtent(STRING str, int16_t x, int16_t y, int16_t *w, int16_t *h); + + /// Draw a single character with the current GFX font + /// @param x point coordinate, x-axis + /// @param y point coordinate, y-axis + /// @param c character to draw + /// @param color 16-bit color + /// @return width of character in display pixels + uint16_t drawGFXChar(int16_t x, int16_t y, unsigned char c, uint16_t color); + + + private: + + void _spiWrite(uint8_t v); + void _spiWrite16(uint16_t v); + void _spiWriteCommand(uint8_t c); + void _spiWriteData(uint8_t d); + + void _swap(uint16_t &a, uint16_t &b); + void _setWindow(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1); + void _setWindow(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1, autoIncMode_t mode); + void _resetWindow(); + void _drawBitmap(int16_t x, int16_t y, const uint8_t *bitmap, int16_t w, int16_t h, + uint16_t color, uint16_t bg, bool transparent, bool progmem, bool Xbit ); + void _orientCoordinates(uint16_t &x1, uint16_t &y1); + void _writeRegister(uint16_t reg, uint16_t data); + void _writeData(uint8_t HI, uint8_t LO); + void _writeData16(uint16_t HILO); + void _writeCommand(uint8_t HI, uint8_t LO); + void _writeCommand16(uint16_t HILO); + uint16_t _maxX, _maxY, _bgColor; + +#if defined (__AVR__) || defined(TEENSYDUINO) + int8_t _rst, _rs, _cs, _sdi, _clk, _led; + #ifdef USE_FAST_PINIO + volatile uint8_t *mosiport, *clkport, *dcport, *rsport, *csport; + uint8_t mosipinmask, clkpinmask, cspinmask, dcpinmask; + #endif +#elif defined (__arm__) + int32_t _rst, _rs, _cs, _sdi, _clk, _led; + #ifdef USE_FAST_PINIO + volatile RwReg *mosiport, *clkport, *dcport, *rsport, *csport; + uint32_t mosipinmask, clkpinmask, cspinmask, dcpinmask; + #endif +#elif defined (ESP8266) || defined (ESP32) + int8_t _rst, _rs, _cs, _sdi, _clk, _led; + #ifdef USE_FAST_PINIO + volatile uint32_t *mosiport, *clkport, *dcport, *rsport, *csport; + uint32_t mosipinmask, clkpinmask, cspinmask, dcpinmask; + #endif +#else + int8_t _rst, _rs, _cs, _sdi, _clk, _led; +#endif + + uint8_t _orientation, _brightness; + + // correspondig modes if orientation changed: + const autoIncMode_t modeTab [3][8] = { + // { R2L_BottomUp, BottomUp_R2L, L2R_BottomUp, BottomUp_L2R, R2L_TopDown, TopDown_R2L, L2R_TopDown, TopDown_L2R }// + /* 90° */ { BottomUp_L2R, L2R_BottomUp, TopDown_L2R, L2R_TopDown, BottomUp_R2L, R2L_BottomUp, TopDown_R2L, R2L_TopDown }, + /*180° */ { L2R_TopDown , TopDown_L2R, R2L_TopDown, TopDown_R2L, L2R_BottomUp, BottomUp_L2R, R2L_BottomUp, BottomUp_R2L}, + /*270° */ { TopDown_R2L , R2L_TopDown, BottomUp_R2L, R2L_BottomUp, TopDown_L2R, L2R_TopDown, BottomUp_L2R, L2R_BottomUp} + }; + + + bool hwSPI, blState; + + _currentFont cfont; + +#ifdef ESP32 + SPIClass _spi; +#endif + + protected: + + uint32_t writeFunctionLevel; + void startWrite(void); + void endWrite(void); + + void getGFXCharExtent(uint8_t c, int16_t *gw, int16_t *gh, int16_t *xa); + + GFXfont *gfxFont; +}; + +#endif diff --git a/libraries/SondeLib/aprs.cpp b/libraries/SondeLib/aprs.cpp index 447a437..bfa73fc 100644 --- a/libraries/SondeLib/aprs.cpp +++ b/libraries/SondeLib/aprs.cpp @@ -137,6 +137,7 @@ static int mkaprscall(int32_t * p, char raw[], } /* end call() */ + // returns raw len, 0 in case of error extern int aprsstr_mon2raw(const char *mon, char raw[], int raw_len) { @@ -207,6 +208,28 @@ extern int aprsstr_mon2raw(const char *mon, char raw[], int raw_len) return p+2; } /* end mon2raw() */ +extern int aprsstr_mon2kiss(const char *mon, char raw[], int raw_len) +{ + char tmp[201]; + int len = aprsstr_mon2raw(mon, tmp, 201); + if(len==0) return 0; + int idx=0; + raw[idx++] = '\xC0'; + for(int i=0; i=raw_len) + return 0; + } + return idx; +} #define FEET (1.0/0.3048) #define KNOTS (1.851984) diff --git a/libraries/SondeLib/aprs.h b/libraries/SondeLib/aprs.h index 2c14db7..5f993fd 100644 --- a/libraries/SondeLib/aprs.h +++ b/libraries/SondeLib/aprs.h @@ -16,10 +16,18 @@ struct st_feedinfo { int idformat; // 0: dxl 1: real 2: auto }; +// maybe extend for external Bluetooth interface? +// internal bluetooth consumes too much memory +struct st_kisstnc { + bool active; + int idformat; +}; + #define APRS_MAXLEN 201 void aprs_gencrctab(void); int aprsstr_mon2raw(const char *mon, char raw[], int raw_len); +int aprsstr_mon2kiss(const char *mon, char raw[], int raw_len); char * aprs_senddata(float lat, float lon, float alt, float speed, float dir, float climb, const char *type, const char *objname, const char *usercall, const char *sym); diff --git a/libraries/SondeLib/autodetect-infos b/libraries/SondeLib/autodetect-infos index f3067ef..3541d7a 100644 --- a/libraries/SondeLib/autodetect-infos +++ b/libraries/SondeLib/autodetect-infos @@ -17,6 +17,16 @@ TTGO T-Beam 0:1 1:0 2:0 3:1 4:0 5:1 6:0 7:1 8:0 9:1 10:1 11:1 12:1 13:0 14:1 15:1 16:1 17:0 18:0 19:0 20:0 21:1 22:1 23:1 24:0 25:0 26:0 27:0 28:0 29:0 30:0 31:0 32:0 33:0 34:0 35:0 36:0 37:0 38:0 0:4 1:4 2:0 3:4 4:0 5:4 6:0 7:4 8:0 9:4 10:4 11:4 12:4 13:0 14:4 15:4 16:4 17:0 18:0 19:0 20:0 21:4 22:4 23:4 24:0 25:0 26:0 27:0 28:0 29:0 30:0 31:0 32:0 33:0 34:0 35:0 36:0 37:0 38:0 (before setup) +TTGO T-Beam 1.0 with OLED display +0:1 1:0 2:0 3:1 4:0 5:1 6:0 7:1 8:0 9:1 10:1 11:1 12:0 13:0 14:1 15:1 16:1 17:0 18:0 19:0 20:0 21:1 22:1 23:1 24:0 25:0 26:0 27:0 28:0 29:0 30:0 31:0 32:0 33:0 34:0 35:0 36:0 37:0 38:0 +0:4 1:4 2:0 3:4 4:0 5:4 6:0 7:4 8:0 9:4 10:4 11:4 12:0 13:0 14:4 15:4 16:4 17:0 18:0 19:0 20:0 21:4 22:4 23:4 24:0 25:0 26:0 27:0 28:0 29:0 30:0 31:0 32:0 33:0 34:0 35:0 36:0 37:0 38:0 (before setup) + + + +TTGO T-Beam with extern 2" ILI9225 Display +0:1 1:0 2:0 3:1 4:0 5:1 6:0 7:1 8:0 9:1 10:1 11:1 12:1 13:0 14:1 15:1 16:1 17:0 18:0 19:0 20:0 21:0 22:0 23:1 24:0 25:0 26:0 27:0 28:0 29:0 30:0 31:0 32:0 33:0 34:0 35:0 36:0 37:0 38:0 +0:4 1:4 2:0 3:4 4:0 5:4 6:0 7:4 8:0 9:4 10:4 11:4 12:4 13:0 14:4 15:4 16:4 17:0 18:0 19:0 20:0 21:0 22:0 23:4 24:0 25:0 26:0 27:0 28:0 29:0 30:0 31:0 32:0 33:0 34:0 35:0 36:0 37:0 38:0 (before setup) +1 Current autodetect strategy: RST always set to 16 @@ -30,13 +40,22 @@ GPIO16=0 (GPio22,23 would also work): otherwise ==LORA32 v2.1 or T-Beam== - SDA,SCL set to (21,22) GPIO17=0: == T-BEAM = - GPS RX set to 12 - Button 1 set to GPIO39 - Button 2 set to Touch GPIO13 (141) + GPIO12==0: v1 (or check PMU via I2C?) + GPS RX set to 34 + Button 1 set to GPIO28 + BUtton 2 set to Touch GPIO13 + else: + GPS RX set to 12 + Button 1 set to GPIO39 + Button 2 set to Touch GPIO13 (141) + GPIO21=0: + large display connected (use ILI9225 contig: SDA4 CLK21 RS2 RST22 CS0) + else: + small display connected, set SDA,SCL to (21,22) otherweise: + SDA,SCL set to (21,22) GPS disabled Button 1 set to Touch GPIO2 (130) Button 2 set to Touch GPIO14 (142) diff --git a/libraries/SondeLib/geteph.cpp b/libraries/SondeLib/geteph.cpp index 944d499..ba78522 100644 --- a/libraries/SondeLib/geteph.cpp +++ b/libraries/SondeLib/geteph.cpp @@ -5,9 +5,9 @@ #include #include #include -#include +#include "Display.h" + -extern U8X8_SSD1306_128X64_NONAME_SW_I2C *u8x8; extern WiFiClient client; static const char *ftpserver = "www.ngs.noaa.gov"; @@ -72,9 +72,9 @@ void geteph() { Serial.printf("now: %s, existing: %s => updating\n", nowstr, tsstr); } status.close(); - u8x8->clear(); - u8x8->setFont(u8x8_font_chroma48medium8_r); - u8x8->drawString(0, 0, "FTP ngs.noaa.gov"); + disp.rdis->clear(); + disp.rdis->setFont(FONT_SMALL); + disp.rdis->drawString(0, 0, "FTP ngs.noaa.gov"); // fetch rinex from server File fh = SPIFFS.open("/brdc.gz","w"); if(!fh) { @@ -84,7 +84,7 @@ void geteph() { char buf[252]; snprintf(buf, 128, "/cors/rinex/%04d/%03d/brdc%03d0.%02dn.gz", year, day, day, year-2000); Serial.println("running geteph\n"); - u8x8->drawString(0, 1, buf+21); + disp.rdis->drawString(0, 1, buf+21); if(!client.connect(ftpserver, 21)) { Serial.println("FTP connection to www.ngs.noaa.gov failed"); @@ -145,9 +145,9 @@ void geteph() { fh.close(); snprintf(buf, 16, "Fetched %d B ",len); buf[16]=0; - u8x8->drawString(0,2,buf); + disp.rdis->drawString(0,2,buf); - u8x8->drawString(0,4,"Decompressing..."); + disp.rdis->drawString(0,4,"Decompressing..."); // decompression tinfl_decompressor *decomp = (tinfl_decompressor *)malloc(sizeof(tinfl_decompressor)); tinfl_init(decomp); @@ -215,7 +215,7 @@ void geteph() { status.close(); snprintf(buf, 16, "Done: %d B ",total); buf[16]=0; - u8x8->drawString(0,5,buf); + disp.rdis->drawString(0,5,buf); delay(1000); free(obuf); diff --git a/libraries/SondeLib/gfxfont.h b/libraries/SondeLib/gfxfont.h new file mode 100644 index 0000000..f99a757 --- /dev/null +++ b/libraries/SondeLib/gfxfont.h @@ -0,0 +1,26 @@ +// SPDX-License-Identifier: GPL-3.0 +// original source: https://github.com/Nkawu/TFT_22_ILI9225 + +// Font structures like Adafruit_GFX (1.1 and later). +// Example fonts are included in 'fonts' directory. +// To use a font in your Arduino sketch, #include the corresponding .h +// file and pass address of GFXfont struct to setFont(). + +#ifndef _GFFFONT_H_ +#define _GFFFONT_H_ + +typedef struct { // Data stored PER GLYPH + uint16_t bitmapOffset; // Pointer into GFXfont->bitmap + uint8_t width, height; // Bitmap dimensions in pixels + uint8_t xAdvance; // Distance to advance cursor (x axis) + int8_t xOffset, yOffset; // Dist from cursor pos to UL corner +} GFXglyph; + +typedef struct { // Data stored for FONT AS A WHOLE: + uint8_t *bitmap; // Glyph bitmaps, concatenated + GFXglyph *glyph; // Glyph array + uint8_t first, last; // ASCII extents + uint8_t yAdvance; // Newline distance (y axis) +} GFXfont; + +#endif // _GFFFONT_H_ diff --git a/libraries/SondeLib/rs92gps.cpp b/libraries/SondeLib/rs92gps.cpp index 7365329..0d6aea6 100644 --- a/libraries/SondeLib/rs92gps.cpp +++ b/libraries/SondeLib/rs92gps.cpp @@ -73,7 +73,6 @@ int option_verbose = 0, // ausfuehrliche Anzeige rawin = 0; double dop_limit = 9.9; double d_err = 10000; -//double fixalt2d = 480; // bei mir zu Hause :-) TODO: make it configurable int rollover = 0, err_gps = 0; diff --git a/libraries/fonts/FreeSans12pt7b.h b/libraries/fonts/FreeSans12pt7b.h new file mode 100644 index 0000000..9ecbb8f --- /dev/null +++ b/libraries/fonts/FreeSans12pt7b.h @@ -0,0 +1,270 @@ +const uint8_t FreeSans12pt7bBitmaps[] PROGMEM = { + 0xFF, 0xFF, 0xFF, 0xF0, 0xF0, 0xCF, 0x3C, 0xF3, 0x8A, 0x20, 0x06, 0x30, + 0x31, 0x03, 0x18, 0x18, 0xC7, 0xFF, 0xBF, 0xFC, 0x31, 0x03, 0x18, 0x18, + 0xC7, 0xFF, 0xBF, 0xFC, 0x31, 0x01, 0x18, 0x18, 0xC0, 0xC6, 0x06, 0x30, + 0x04, 0x03, 0xE1, 0xFF, 0x72, 0x6C, 0x47, 0x88, 0xF1, 0x07, 0x20, 0x7E, + 0x03, 0xF0, 0x17, 0x02, 0x3C, 0x47, 0x88, 0xF1, 0x1B, 0x26, 0x7F, 0xC3, + 0xE0, 0x10, 0x02, 0x00, 0x00, 0x06, 0x03, 0xC0, 0x40, 0x7E, 0x0C, 0x0E, + 0x70, 0x80, 0xC3, 0x18, 0x0C, 0x31, 0x00, 0xE7, 0x30, 0x07, 0xE6, 0x00, + 0x3C, 0x40, 0x00, 0x0C, 0x7C, 0x00, 0x8F, 0xE0, 0x19, 0xC7, 0x01, 0x18, + 0x30, 0x31, 0x83, 0x02, 0x1C, 0x70, 0x40, 0xFE, 0x04, 0x07, 0xC0, 0x0F, + 0x00, 0x7E, 0x03, 0x9C, 0x0C, 0x30, 0x30, 0xC0, 0xE7, 0x01, 0xF8, 0x03, + 0x80, 0x3E, 0x01, 0xCC, 0x6E, 0x19, 0xB0, 0x7C, 0xC0, 0xF3, 0x03, 0xCE, + 0x1F, 0x9F, 0xE6, 0x1E, 0x1C, 0xFF, 0xA0, 0x08, 0x8C, 0x66, 0x31, 0x98, + 0xC6, 0x31, 0x8C, 0x63, 0x08, 0x63, 0x08, 0x61, 0x0C, 0x20, 0x82, 0x18, + 0xC3, 0x18, 0xC3, 0x18, 0xC6, 0x31, 0x8C, 0x62, 0x31, 0x88, 0xC4, 0x62, + 0x00, 0x10, 0x23, 0x5B, 0xE3, 0x8D, 0x91, 0x00, 0x0C, 0x03, 0x00, 0xC0, + 0x30, 0xFF, 0xFF, 0xF0, 0xC0, 0x30, 0x0C, 0x03, 0x00, 0xC0, 0xF5, 0x60, + 0xFF, 0xF0, 0xF0, 0x02, 0x0C, 0x10, 0x20, 0xC1, 0x02, 0x0C, 0x10, 0x20, + 0xC1, 0x02, 0x0C, 0x10, 0x20, 0xC1, 0x00, 0x1F, 0x07, 0xF1, 0xC7, 0x30, + 0x6E, 0x0F, 0x80, 0xF0, 0x1E, 0x03, 0xC0, 0x78, 0x0F, 0x01, 0xE0, 0x3C, + 0x0E, 0xC1, 0x9C, 0x71, 0xFC, 0x1F, 0x00, 0x08, 0xCF, 0xFF, 0x8C, 0x63, + 0x18, 0xC6, 0x31, 0x8C, 0x63, 0x18, 0x1F, 0x0F, 0xF9, 0x87, 0x60, 0x7C, + 0x06, 0x00, 0xC0, 0x18, 0x07, 0x01, 0xC0, 0xF0, 0x78, 0x1C, 0x06, 0x00, + 0x80, 0x30, 0x07, 0xFF, 0xFF, 0xE0, 0x3F, 0x0F, 0xF3, 0x87, 0x60, 0x6C, + 0x0C, 0x01, 0x80, 0x70, 0x7C, 0x0F, 0x80, 0x18, 0x01, 0x80, 0x3C, 0x07, + 0x80, 0xD8, 0x73, 0xFC, 0x1F, 0x00, 0x01, 0x80, 0x70, 0x0E, 0x03, 0xC0, + 0xD8, 0x1B, 0x06, 0x61, 0x8C, 0x21, 0x8C, 0x33, 0x06, 0x7F, 0xFF, 0xFE, + 0x03, 0x00, 0x60, 0x0C, 0x01, 0x80, 0x3F, 0xCF, 0xF9, 0x80, 0x30, 0x06, + 0x00, 0xDE, 0x1F, 0xE7, 0x0E, 0x00, 0xE0, 0x0C, 0x01, 0x80, 0x30, 0x07, + 0x81, 0xF8, 0x73, 0xFC, 0x1F, 0x00, 0x0F, 0x07, 0xF9, 0xC3, 0x30, 0x74, + 0x01, 0x80, 0x33, 0xC7, 0xFE, 0xF0, 0xDC, 0x1F, 0x01, 0xE0, 0x3C, 0x06, + 0xC1, 0xDC, 0x71, 0xFC, 0x1F, 0x00, 0xFF, 0xFF, 0xFC, 0x01, 0x00, 0x60, + 0x18, 0x02, 0x00, 0xC0, 0x30, 0x06, 0x01, 0x80, 0x30, 0x04, 0x01, 0x80, + 0x30, 0x06, 0x01, 0x80, 0x30, 0x00, 0x1F, 0x07, 0xF1, 0xC7, 0x30, 0x66, + 0x0C, 0xC1, 0x8C, 0x61, 0xFC, 0x3F, 0x8E, 0x3B, 0x01, 0xE0, 0x3C, 0x07, + 0x80, 0xD8, 0x31, 0xFC, 0x1F, 0x00, 0x1F, 0x07, 0xF1, 0xC7, 0x70, 0x6C, + 0x07, 0x80, 0xF0, 0x1E, 0x07, 0x61, 0xEF, 0xFC, 0x79, 0x80, 0x30, 0x05, + 0x81, 0x98, 0x73, 0xFC, 0x1E, 0x00, 0xF0, 0x00, 0x03, 0xC0, 0xF0, 0x00, + 0x0F, 0x56, 0x00, 0x00, 0x07, 0x01, 0xE0, 0xF8, 0x3C, 0x0F, 0x00, 0xE0, + 0x07, 0xC0, 0x0F, 0x00, 0x3C, 0x00, 0xF0, 0x01, 0xFF, 0xFF, 0xFF, 0x00, + 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0x00, 0x0E, 0x00, 0x78, 0x01, 0xF0, 0x07, + 0xC0, 0x0F, 0x00, 0x70, 0x1E, 0x0F, 0x03, 0xC0, 0xF0, 0x08, 0x00, 0x1F, + 0x1F, 0xEE, 0x1B, 0x03, 0xC0, 0xC0, 0x30, 0x0C, 0x06, 0x03, 0x81, 0xC0, + 0xE0, 0x30, 0x0C, 0x03, 0x00, 0x00, 0x00, 0x0C, 0x03, 0x00, 0x00, 0xFE, + 0x00, 0x0F, 0xFE, 0x00, 0xF0, 0x3E, 0x07, 0x00, 0x3C, 0x38, 0x00, 0x30, + 0xC1, 0xE0, 0x66, 0x0F, 0xD9, 0xD8, 0x61, 0xC3, 0xC3, 0x07, 0x0F, 0x1C, + 0x1C, 0x3C, 0x60, 0x60, 0xF1, 0x81, 0x83, 0xC6, 0x06, 0x1B, 0x18, 0x38, + 0xEE, 0x71, 0xE7, 0x18, 0xFD, 0xF8, 0x71, 0xE7, 0xC0, 0xE0, 0x00, 0x01, + 0xE0, 0x00, 0x01, 0xFF, 0xC0, 0x01, 0xFC, 0x00, 0x03, 0xC0, 0x03, 0xC0, + 0x03, 0xC0, 0x07, 0xE0, 0x06, 0x60, 0x06, 0x60, 0x0E, 0x70, 0x0C, 0x30, + 0x0C, 0x30, 0x1C, 0x38, 0x18, 0x18, 0x1F, 0xF8, 0x3F, 0xFC, 0x30, 0x1C, + 0x30, 0x0C, 0x70, 0x0E, 0x60, 0x06, 0x60, 0x06, 0xFF, 0xC7, 0xFF, 0x30, + 0x19, 0x80, 0x6C, 0x03, 0x60, 0x1B, 0x00, 0xD8, 0x0C, 0xFF, 0xC7, 0xFF, + 0x30, 0x0D, 0x80, 0x3C, 0x01, 0xE0, 0x0F, 0x00, 0x78, 0x06, 0xFF, 0xF7, + 0xFE, 0x00, 0x07, 0xE0, 0x3F, 0xF0, 0xE0, 0x73, 0x80, 0x66, 0x00, 0x6C, + 0x00, 0x30, 0x00, 0x60, 0x00, 0xC0, 0x01, 0x80, 0x03, 0x00, 0x06, 0x00, + 0x06, 0x00, 0x6C, 0x00, 0xDC, 0x03, 0x1E, 0x0E, 0x1F, 0xF8, 0x0F, 0xC0, + 0xFF, 0x83, 0xFF, 0x8C, 0x07, 0x30, 0x0E, 0xC0, 0x1B, 0x00, 0x7C, 0x00, + 0xF0, 0x03, 0xC0, 0x0F, 0x00, 0x3C, 0x00, 0xF0, 0x03, 0xC0, 0x1F, 0x00, + 0x6C, 0x03, 0xB0, 0x1C, 0xFF, 0xE3, 0xFF, 0x00, 0xFF, 0xFF, 0xFF, 0xC0, + 0x0C, 0x00, 0xC0, 0x0C, 0x00, 0xC0, 0x0C, 0x00, 0xFF, 0xEF, 0xFE, 0xC0, + 0x0C, 0x00, 0xC0, 0x0C, 0x00, 0xC0, 0x0C, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0x00, 0x60, 0x0C, 0x01, 0x80, 0x30, 0x06, 0x00, 0xFF, 0xDF, + 0xFB, 0x00, 0x60, 0x0C, 0x01, 0x80, 0x30, 0x06, 0x00, 0xC0, 0x18, 0x00, + 0x07, 0xF0, 0x1F, 0xFC, 0x3C, 0x1E, 0x70, 0x06, 0x60, 0x03, 0xE0, 0x00, + 0xC0, 0x00, 0xC0, 0x00, 0xC0, 0x7F, 0xC0, 0x7F, 0xC0, 0x03, 0xC0, 0x03, + 0x60, 0x03, 0x60, 0x07, 0x30, 0x0F, 0x3C, 0x1F, 0x1F, 0xFB, 0x07, 0xE1, + 0xC0, 0x1E, 0x00, 0xF0, 0x07, 0x80, 0x3C, 0x01, 0xE0, 0x0F, 0x00, 0x78, + 0x03, 0xFF, 0xFF, 0xFF, 0xF0, 0x07, 0x80, 0x3C, 0x01, 0xE0, 0x0F, 0x00, + 0x78, 0x03, 0xC0, 0x1E, 0x00, 0xC0, 0xFF, 0xFF, 0xFF, 0xFF, 0xF0, 0x01, + 0x80, 0xC0, 0x60, 0x30, 0x18, 0x0C, 0x06, 0x03, 0x01, 0x80, 0xC0, 0x60, + 0x3C, 0x1E, 0x0F, 0x07, 0xC7, 0x7F, 0x1F, 0x00, 0xC0, 0x3B, 0x01, 0xCC, + 0x0E, 0x30, 0x70, 0xC3, 0x83, 0x1C, 0x0C, 0xE0, 0x33, 0x80, 0xDE, 0x03, + 0xDC, 0x0E, 0x38, 0x30, 0x60, 0xC1, 0xC3, 0x03, 0x8C, 0x06, 0x30, 0x1C, + 0xC0, 0x3B, 0x00, 0x60, 0xC0, 0x30, 0x0C, 0x03, 0x00, 0xC0, 0x30, 0x0C, + 0x03, 0x00, 0xC0, 0x30, 0x0C, 0x03, 0x00, 0xC0, 0x30, 0x0C, 0x03, 0x00, + 0xFF, 0xFF, 0xF0, 0xE0, 0x07, 0xE0, 0x07, 0xF0, 0x0F, 0xF0, 0x0F, 0xD0, + 0x0F, 0xD8, 0x1B, 0xD8, 0x1B, 0xD8, 0x1B, 0xCC, 0x33, 0xCC, 0x33, 0xCC, + 0x33, 0xC6, 0x63, 0xC6, 0x63, 0xC6, 0x63, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, + 0xC3, 0xC1, 0x83, 0xE0, 0x1F, 0x00, 0xFC, 0x07, 0xE0, 0x3D, 0x81, 0xEE, + 0x0F, 0x30, 0x79, 0xC3, 0xC6, 0x1E, 0x18, 0xF0, 0xE7, 0x83, 0x3C, 0x1D, + 0xE0, 0x6F, 0x01, 0xF8, 0x0F, 0xC0, 0x3E, 0x01, 0xC0, 0x03, 0xE0, 0x0F, + 0xFC, 0x0F, 0x07, 0x86, 0x00, 0xC6, 0x00, 0x33, 0x00, 0x1B, 0x00, 0x07, + 0x80, 0x03, 0xC0, 0x01, 0xE0, 0x00, 0xF0, 0x00, 0x78, 0x00, 0x36, 0x00, + 0x33, 0x00, 0x18, 0xC0, 0x18, 0x78, 0x3C, 0x1F, 0xFC, 0x03, 0xF8, 0x00, + 0xFF, 0x8F, 0xFE, 0xC0, 0x6C, 0x03, 0xC0, 0x3C, 0x03, 0xC0, 0x3C, 0x07, + 0xFF, 0xEF, 0xFC, 0xC0, 0x0C, 0x00, 0xC0, 0x0C, 0x00, 0xC0, 0x0C, 0x00, + 0xC0, 0x0C, 0x00, 0x03, 0xE0, 0x0F, 0xFC, 0x0F, 0x07, 0x86, 0x00, 0xC6, + 0x00, 0x33, 0x00, 0x1B, 0x00, 0x07, 0x80, 0x03, 0xC0, 0x01, 0xE0, 0x00, + 0xF0, 0x00, 0x78, 0x00, 0x36, 0x00, 0x33, 0x01, 0x98, 0xC0, 0xFC, 0x78, + 0x3C, 0x1F, 0xFF, 0x03, 0xF9, 0x80, 0x00, 0x40, 0xFF, 0xC3, 0xFF, 0xCC, + 0x03, 0xB0, 0x06, 0xC0, 0x1B, 0x00, 0x6C, 0x01, 0xB0, 0x0C, 0xFF, 0xE3, + 0xFF, 0xCC, 0x03, 0xB0, 0x06, 0xC0, 0x1B, 0x00, 0x6C, 0x01, 0xB0, 0x06, + 0xC0, 0x1B, 0x00, 0x70, 0x0F, 0xE0, 0x7F, 0xC3, 0x83, 0x9C, 0x07, 0x60, + 0x0D, 0x80, 0x06, 0x00, 0x1E, 0x00, 0x3F, 0x80, 0x3F, 0xC0, 0x0F, 0x80, + 0x07, 0xC0, 0x0F, 0x00, 0x3E, 0x00, 0xDE, 0x0E, 0x3F, 0xF0, 0x3F, 0x80, + 0xFF, 0xFF, 0xFF, 0x06, 0x00, 0x60, 0x06, 0x00, 0x60, 0x06, 0x00, 0x60, + 0x06, 0x00, 0x60, 0x06, 0x00, 0x60, 0x06, 0x00, 0x60, 0x06, 0x00, 0x60, + 0x06, 0x00, 0x60, 0xC0, 0x1E, 0x00, 0xF0, 0x07, 0x80, 0x3C, 0x01, 0xE0, + 0x0F, 0x00, 0x78, 0x03, 0xC0, 0x1E, 0x00, 0xF0, 0x07, 0x80, 0x3C, 0x01, + 0xE0, 0x0F, 0x80, 0xEE, 0x0E, 0x3F, 0xE0, 0x7C, 0x00, 0x60, 0x06, 0xC0, + 0x1D, 0xC0, 0x31, 0x80, 0x63, 0x01, 0xC7, 0x03, 0x06, 0x06, 0x0C, 0x1C, + 0x1C, 0x30, 0x18, 0x60, 0x31, 0xC0, 0x73, 0x00, 0x66, 0x00, 0xDC, 0x01, + 0xF0, 0x01, 0xE0, 0x03, 0xC0, 0x07, 0x00, 0xE0, 0x30, 0x1D, 0x80, 0xE0, + 0x76, 0x07, 0x81, 0xD8, 0x1E, 0x06, 0x70, 0x7C, 0x18, 0xC1, 0xB0, 0xE3, + 0x0C, 0xC3, 0x8C, 0x33, 0x0C, 0x38, 0xC6, 0x30, 0x67, 0x18, 0xC1, 0x98, + 0x67, 0x06, 0x61, 0xD8, 0x1D, 0x83, 0x60, 0x3C, 0x0D, 0x80, 0xF0, 0x3E, + 0x03, 0xC0, 0x70, 0x0F, 0x01, 0xC0, 0x18, 0x07, 0x00, 0x70, 0x0E, 0x60, + 0x38, 0xE0, 0x60, 0xE1, 0xC0, 0xC3, 0x01, 0xCC, 0x01, 0xF8, 0x01, 0xE0, + 0x03, 0x80, 0x07, 0x80, 0x1F, 0x00, 0x33, 0x00, 0xE7, 0x03, 0x86, 0x06, + 0x0E, 0x1C, 0x0E, 0x70, 0x0C, 0xC0, 0x1C, 0x60, 0x06, 0x70, 0x0E, 0x30, + 0x1C, 0x38, 0x18, 0x1C, 0x38, 0x0C, 0x30, 0x0E, 0x70, 0x06, 0x60, 0x03, + 0xC0, 0x03, 0xC0, 0x01, 0x80, 0x01, 0x80, 0x01, 0x80, 0x01, 0x80, 0x01, + 0x80, 0x01, 0x80, 0x01, 0x80, 0x01, 0x80, 0xFF, 0xFF, 0xFF, 0xC0, 0x0E, + 0x00, 0xE0, 0x0E, 0x00, 0x60, 0x07, 0x00, 0x70, 0x07, 0x00, 0x30, 0x03, + 0x80, 0x38, 0x03, 0x80, 0x18, 0x01, 0xC0, 0x1C, 0x00, 0xFF, 0xFF, 0xFF, + 0xC0, 0xFF, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCF, + 0xF0, 0x81, 0x81, 0x02, 0x06, 0x04, 0x08, 0x18, 0x10, 0x20, 0x60, 0x40, + 0x81, 0x81, 0x02, 0x06, 0x04, 0xFF, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, + 0x33, 0x33, 0x33, 0x3F, 0xF0, 0x0C, 0x0E, 0x05, 0x86, 0xC3, 0x21, 0x19, + 0x8C, 0x83, 0xC1, 0x80, 0xFF, 0xFE, 0xE3, 0x8C, 0x30, 0x3F, 0x07, 0xF8, + 0xE1, 0xCC, 0x0C, 0x00, 0xC0, 0x1C, 0x3F, 0xCF, 0x8C, 0xC0, 0xCC, 0x0C, + 0xE3, 0xC7, 0xEF, 0x3C, 0x70, 0xC0, 0x0C, 0x00, 0xC0, 0x0C, 0x00, 0xC0, + 0x0C, 0xF8, 0xDF, 0xCF, 0x0E, 0xE0, 0x7C, 0x03, 0xC0, 0x3C, 0x03, 0xC0, + 0x3C, 0x03, 0xE0, 0x6F, 0x0E, 0xDF, 0xCC, 0xF8, 0x1F, 0x0F, 0xE7, 0x1B, + 0x83, 0xC0, 0x30, 0x0C, 0x03, 0x00, 0xC0, 0x38, 0x37, 0x1C, 0xFE, 0x1F, + 0x00, 0x00, 0x60, 0x0C, 0x01, 0x80, 0x30, 0x06, 0x3C, 0xCF, 0xFB, 0x8F, + 0xE0, 0xF8, 0x0F, 0x01, 0xE0, 0x3C, 0x07, 0x80, 0xF8, 0x3B, 0x8F, 0x3F, + 0x63, 0xCC, 0x1F, 0x07, 0xF1, 0xC7, 0x70, 0x3C, 0x07, 0xFF, 0xFF, 0xFE, + 0x00, 0xC0, 0x1C, 0x0D, 0xC3, 0x1F, 0xE1, 0xF0, 0x3B, 0xD8, 0xC6, 0x7F, + 0xEC, 0x63, 0x18, 0xC6, 0x31, 0x8C, 0x63, 0x00, 0x1E, 0x67, 0xFD, 0xC7, + 0xF0, 0x7C, 0x07, 0x80, 0xF0, 0x1E, 0x03, 0xC0, 0x7C, 0x1D, 0xC7, 0x9F, + 0xB1, 0xE6, 0x00, 0xC0, 0x3E, 0x0E, 0x7F, 0xC7, 0xE0, 0xC0, 0x30, 0x0C, + 0x03, 0x00, 0xC0, 0x33, 0xCD, 0xFB, 0xC7, 0xE0, 0xF0, 0x3C, 0x0F, 0x03, + 0xC0, 0xF0, 0x3C, 0x0F, 0x03, 0xC0, 0xF0, 0x30, 0xF0, 0x3F, 0xFF, 0xFF, + 0xF0, 0x33, 0x00, 0x03, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x3F, + 0xE0, 0xC0, 0x18, 0x03, 0x00, 0x60, 0x0C, 0x01, 0x83, 0x30, 0xC6, 0x30, + 0xCC, 0x1B, 0x83, 0xF0, 0x77, 0x0C, 0x61, 0x8E, 0x30, 0xE6, 0x0C, 0xC1, + 0xD8, 0x18, 0xFF, 0xFF, 0xFF, 0xFF, 0xF0, 0xCF, 0x1F, 0x6F, 0xDF, 0xFC, + 0x78, 0xFC, 0x18, 0x3C, 0x0C, 0x1E, 0x06, 0x0F, 0x03, 0x07, 0x81, 0x83, + 0xC0, 0xC1, 0xE0, 0x60, 0xF0, 0x30, 0x78, 0x18, 0x3C, 0x0C, 0x18, 0xCF, + 0x37, 0xEF, 0x1F, 0x83, 0xC0, 0xF0, 0x3C, 0x0F, 0x03, 0xC0, 0xF0, 0x3C, + 0x0F, 0x03, 0xC0, 0xC0, 0x1F, 0x07, 0xF1, 0xC7, 0x70, 0x7C, 0x07, 0x80, + 0xF0, 0x1E, 0x03, 0xC0, 0x7C, 0x1D, 0xC7, 0x1F, 0xC1, 0xF0, 0xCF, 0x8D, + 0xFC, 0xF0, 0xEE, 0x06, 0xC0, 0x3C, 0x03, 0xC0, 0x3C, 0x03, 0xC0, 0x3E, + 0x07, 0xF0, 0xEF, 0xFC, 0xCF, 0x8C, 0x00, 0xC0, 0x0C, 0x00, 0xC0, 0x00, + 0x1E, 0x67, 0xFD, 0xC7, 0xF0, 0x7C, 0x07, 0x80, 0xF0, 0x1E, 0x03, 0xC0, + 0x7C, 0x1D, 0xC7, 0x9F, 0xF1, 0xE6, 0x00, 0xC0, 0x18, 0x03, 0x00, 0x60, + 0xCF, 0x7F, 0x38, 0xC3, 0x0C, 0x30, 0xC3, 0x0C, 0x30, 0xC0, 0x3E, 0x1F, + 0xEE, 0x1B, 0x00, 0xC0, 0x3C, 0x07, 0xF0, 0x3E, 0x01, 0xF0, 0x3E, 0x1D, + 0xFE, 0x3E, 0x00, 0x63, 0x19, 0xFF, 0xB1, 0x8C, 0x63, 0x18, 0xC6, 0x31, + 0xE7, 0xC0, 0xF0, 0x3C, 0x0F, 0x03, 0xC0, 0xF0, 0x3C, 0x0F, 0x03, 0xC0, + 0xF0, 0x7E, 0x3D, 0xFB, 0x3C, 0xC0, 0xE0, 0x66, 0x06, 0x60, 0x67, 0x0C, + 0x30, 0xC3, 0x0C, 0x39, 0x81, 0x98, 0x19, 0x81, 0xF0, 0x0F, 0x00, 0xE0, + 0x0E, 0x00, 0xC1, 0xC1, 0xB0, 0xE1, 0xD8, 0x70, 0xCC, 0x2C, 0x66, 0x36, + 0x31, 0x9B, 0x18, 0xCD, 0x98, 0x64, 0x6C, 0x16, 0x36, 0x0F, 0x1A, 0x07, + 0x8F, 0x03, 0x83, 0x80, 0xC1, 0xC0, 0x60, 0xEE, 0x18, 0xC6, 0x0C, 0xC1, + 0xF0, 0x1C, 0x01, 0x80, 0x78, 0x1B, 0x03, 0x30, 0xC7, 0x30, 0x66, 0x06, + 0xE0, 0x6C, 0x0D, 0x83, 0x38, 0x63, 0x0C, 0x63, 0x0E, 0x60, 0xCC, 0x1B, + 0x03, 0x60, 0x3C, 0x07, 0x00, 0xE0, 0x18, 0x03, 0x00, 0xE0, 0x78, 0x0E, + 0x00, 0xFF, 0xFF, 0xF0, 0x18, 0x0C, 0x07, 0x03, 0x81, 0xC0, 0x60, 0x30, + 0x18, 0x0E, 0x03, 0xFF, 0xFF, 0xC0, 0x19, 0xCC, 0x63, 0x18, 0xC6, 0x31, + 0x99, 0x86, 0x18, 0xC6, 0x31, 0x8C, 0x63, 0x1C, 0x60, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFC, 0xC7, 0x18, 0xC6, 0x31, 0x8C, 0x63, 0x0C, 0x33, 0x31, + 0x8C, 0x63, 0x18, 0xC6, 0x73, 0x00, 0x70, 0x3E, 0x09, 0xE4, 0x1F, 0x03, + 0x80 }; + +const GFXglyph FreeSans12pt7bGlyphs[] PROGMEM = { + { 0, 0, 0, 6, 0, 1 }, // 0x20 ' ' + { 0, 2, 18, 8, 3, -17 }, // 0x21 '!' + { 5, 6, 6, 8, 1, -16 }, // 0x22 '"' + { 10, 13, 16, 13, 0, -15 }, // 0x23 '#' + { 36, 11, 20, 13, 1, -17 }, // 0x24 '$' + { 64, 20, 17, 21, 1, -16 }, // 0x25 '%' + { 107, 14, 17, 16, 1, -16 }, // 0x26 '&' + { 137, 2, 6, 5, 1, -16 }, // 0x27 ''' + { 139, 5, 23, 8, 2, -17 }, // 0x28 '(' + { 154, 5, 23, 8, 1, -17 }, // 0x29 ')' + { 169, 7, 7, 9, 1, -17 }, // 0x2A '*' + { 176, 10, 11, 14, 2, -10 }, // 0x2B '+' + { 190, 2, 6, 7, 2, -1 }, // 0x2C ',' + { 192, 6, 2, 8, 1, -7 }, // 0x2D '-' + { 194, 2, 2, 6, 2, -1 }, // 0x2E '.' + { 195, 7, 18, 7, 0, -17 }, // 0x2F '/' + { 211, 11, 17, 13, 1, -16 }, // 0x30 '0' + { 235, 5, 17, 13, 3, -16 }, // 0x31 '1' + { 246, 11, 17, 13, 1, -16 }, // 0x32 '2' + { 270, 11, 17, 13, 1, -16 }, // 0x33 '3' + { 294, 11, 17, 13, 1, -16 }, // 0x34 '4' + { 318, 11, 17, 13, 1, -16 }, // 0x35 '5' + { 342, 11, 17, 13, 1, -16 }, // 0x36 '6' + { 366, 11, 17, 13, 1, -16 }, // 0x37 '7' + { 390, 11, 17, 13, 1, -16 }, // 0x38 '8' + { 414, 11, 17, 13, 1, -16 }, // 0x39 '9' + { 438, 2, 13, 6, 2, -12 }, // 0x3A ':' + { 442, 2, 16, 6, 2, -11 }, // 0x3B ';' + { 446, 12, 12, 14, 1, -11 }, // 0x3C '<' + { 464, 12, 6, 14, 1, -8 }, // 0x3D '=' + { 473, 12, 12, 14, 1, -11 }, // 0x3E '>' + { 491, 10, 18, 13, 2, -17 }, // 0x3F '?' + { 514, 22, 21, 24, 1, -17 }, // 0x40 '@' + { 572, 16, 18, 16, 0, -17 }, // 0x41 'A' + { 608, 13, 18, 16, 2, -17 }, // 0x42 'B' + { 638, 15, 18, 17, 1, -17 }, // 0x43 'C' + { 672, 14, 18, 17, 2, -17 }, // 0x44 'D' + { 704, 12, 18, 15, 2, -17 }, // 0x45 'E' + { 731, 11, 18, 14, 2, -17 }, // 0x46 'F' + { 756, 16, 18, 18, 1, -17 }, // 0x47 'G' + { 792, 13, 18, 17, 2, -17 }, // 0x48 'H' + { 822, 2, 18, 7, 2, -17 }, // 0x49 'I' + { 827, 9, 18, 13, 1, -17 }, // 0x4A 'J' + { 848, 14, 18, 16, 2, -17 }, // 0x4B 'K' + { 880, 10, 18, 14, 2, -17 }, // 0x4C 'L' + { 903, 16, 18, 20, 2, -17 }, // 0x4D 'M' + { 939, 13, 18, 18, 2, -17 }, // 0x4E 'N' + { 969, 17, 18, 19, 1, -17 }, // 0x4F 'O' + { 1008, 12, 18, 16, 2, -17 }, // 0x50 'P' + { 1035, 17, 19, 19, 1, -17 }, // 0x51 'Q' + { 1076, 14, 18, 17, 2, -17 }, // 0x52 'R' + { 1108, 14, 18, 16, 1, -17 }, // 0x53 'S' + { 1140, 12, 18, 15, 1, -17 }, // 0x54 'T' + { 1167, 13, 18, 17, 2, -17 }, // 0x55 'U' + { 1197, 15, 18, 15, 0, -17 }, // 0x56 'V' + { 1231, 22, 18, 22, 0, -17 }, // 0x57 'W' + { 1281, 15, 18, 16, 0, -17 }, // 0x58 'X' + { 1315, 16, 18, 16, 0, -17 }, // 0x59 'Y' + { 1351, 13, 18, 15, 1, -17 }, // 0x5A 'Z' + { 1381, 4, 23, 7, 2, -17 }, // 0x5B '[' + { 1393, 7, 18, 7, 0, -17 }, // 0x5C '\' + { 1409, 4, 23, 7, 1, -17 }, // 0x5D ']' + { 1421, 9, 9, 11, 1, -16 }, // 0x5E '^' + { 1432, 15, 1, 13, -1, 4 }, // 0x5F '_' + { 1434, 5, 4, 6, 1, -17 }, // 0x60 '`' + { 1437, 12, 13, 13, 1, -12 }, // 0x61 'a' + { 1457, 12, 18, 13, 1, -17 }, // 0x62 'b' + { 1484, 10, 13, 12, 1, -12 }, // 0x63 'c' + { 1501, 11, 18, 13, 1, -17 }, // 0x64 'd' + { 1526, 11, 13, 13, 1, -12 }, // 0x65 'e' + { 1544, 5, 18, 7, 1, -17 }, // 0x66 'f' + { 1556, 11, 18, 13, 1, -12 }, // 0x67 'g' + { 1581, 10, 18, 13, 1, -17 }, // 0x68 'h' + { 1604, 2, 18, 5, 2, -17 }, // 0x69 'i' + { 1609, 4, 23, 6, 0, -17 }, // 0x6A 'j' + { 1621, 11, 18, 12, 1, -17 }, // 0x6B 'k' + { 1646, 2, 18, 5, 1, -17 }, // 0x6C 'l' + { 1651, 17, 13, 19, 1, -12 }, // 0x6D 'm' + { 1679, 10, 13, 13, 1, -12 }, // 0x6E 'n' + { 1696, 11, 13, 13, 1, -12 }, // 0x6F 'o' + { 1714, 12, 17, 13, 1, -12 }, // 0x70 'p' + { 1740, 11, 17, 13, 1, -12 }, // 0x71 'q' + { 1764, 6, 13, 8, 1, -12 }, // 0x72 'r' + { 1774, 10, 13, 12, 1, -12 }, // 0x73 's' + { 1791, 5, 16, 7, 1, -15 }, // 0x74 't' + { 1801, 10, 13, 13, 1, -12 }, // 0x75 'u' + { 1818, 12, 13, 12, 0, -12 }, // 0x76 'v' + { 1838, 17, 13, 17, 0, -12 }, // 0x77 'w' + { 1866, 11, 13, 11, 0, -12 }, // 0x78 'x' + { 1884, 11, 18, 11, 0, -12 }, // 0x79 'y' + { 1909, 10, 13, 12, 1, -12 }, // 0x7A 'z' + { 1926, 5, 23, 8, 1, -17 }, // 0x7B '{' + { 1941, 2, 23, 6, 2, -17 }, // 0x7C '|' + { 1947, 5, 23, 8, 2, -17 }, // 0x7D '}' + { 1962, 10, 5, 12, 1, -10 } }; // 0x7E '~' + +const GFXfont FreeSans12pt7b PROGMEM = { + (uint8_t *)FreeSans12pt7bBitmaps, + (GFXglyph *)FreeSans12pt7bGlyphs, + 0x20, 0x7E, 29 }; + +// Approx. 2641 bytes diff --git a/libraries/fonts/FreeSans9pt7b.h b/libraries/fonts/FreeSans9pt7b.h new file mode 100644 index 0000000..1f006a1 --- /dev/null +++ b/libraries/fonts/FreeSans9pt7b.h @@ -0,0 +1,201 @@ +const uint8_t FreeSans9pt7bBitmaps[] PROGMEM = { + 0xFF, 0xFF, 0xF8, 0xC0, 0xDE, 0xF7, 0x20, 0x09, 0x86, 0x41, 0x91, 0xFF, + 0x13, 0x04, 0xC3, 0x20, 0xC8, 0xFF, 0x89, 0x82, 0x61, 0x90, 0x10, 0x1F, + 0x14, 0xDA, 0x3D, 0x1E, 0x83, 0x40, 0x78, 0x17, 0x08, 0xF4, 0x7A, 0x35, + 0x33, 0xF0, 0x40, 0x20, 0x38, 0x10, 0xEC, 0x20, 0xC6, 0x20, 0xC6, 0x40, + 0xC6, 0x40, 0x6C, 0x80, 0x39, 0x00, 0x01, 0x3C, 0x02, 0x77, 0x02, 0x63, + 0x04, 0x63, 0x04, 0x77, 0x08, 0x3C, 0x0E, 0x06, 0x60, 0xCC, 0x19, 0x81, + 0xE0, 0x18, 0x0F, 0x03, 0x36, 0xC2, 0xD8, 0x73, 0x06, 0x31, 0xE3, 0xC4, + 0xFE, 0x13, 0x26, 0x6C, 0xCC, 0xCC, 0xC4, 0x66, 0x23, 0x10, 0x8C, 0x46, + 0x63, 0x33, 0x33, 0x32, 0x66, 0x4C, 0x80, 0x25, 0x7E, 0xA5, 0x00, 0x30, + 0xC3, 0x3F, 0x30, 0xC3, 0x0C, 0xD6, 0xF0, 0xC0, 0x08, 0x44, 0x21, 0x10, + 0x84, 0x42, 0x11, 0x08, 0x00, 0x3C, 0x66, 0x42, 0xC3, 0xC3, 0xC3, 0xC3, + 0xC3, 0xC3, 0xC3, 0x42, 0x66, 0x3C, 0x11, 0x3F, 0x33, 0x33, 0x33, 0x33, + 0x30, 0x3E, 0x31, 0xB0, 0x78, 0x30, 0x18, 0x1C, 0x1C, 0x1C, 0x18, 0x18, + 0x10, 0x08, 0x07, 0xF8, 0x3C, 0x66, 0xC3, 0xC3, 0x03, 0x06, 0x1C, 0x07, + 0x03, 0xC3, 0xC3, 0x66, 0x3C, 0x0C, 0x18, 0x71, 0x62, 0xC9, 0xA3, 0x46, + 0xFE, 0x18, 0x30, 0x60, 0xC0, 0x7F, 0x20, 0x10, 0x08, 0x08, 0x07, 0xF3, + 0x8C, 0x03, 0x01, 0x80, 0xF0, 0x6C, 0x63, 0xE0, 0x1E, 0x31, 0x98, 0x78, + 0x0C, 0x06, 0xF3, 0x8D, 0x83, 0xC1, 0xE0, 0xD0, 0x6C, 0x63, 0xE0, 0xFF, + 0x03, 0x02, 0x06, 0x04, 0x0C, 0x08, 0x18, 0x18, 0x18, 0x10, 0x30, 0x30, + 0x3E, 0x31, 0xB0, 0x78, 0x3C, 0x1B, 0x18, 0xF8, 0xC6, 0xC1, 0xE0, 0xF0, + 0x6C, 0x63, 0xE0, 0x3C, 0x66, 0xC2, 0xC3, 0xC3, 0xC3, 0x67, 0x3B, 0x03, + 0x03, 0xC2, 0x66, 0x3C, 0xC0, 0x00, 0x30, 0xC0, 0x00, 0x00, 0x64, 0xA0, + 0x00, 0x81, 0xC7, 0x8E, 0x0C, 0x07, 0x80, 0x70, 0x0E, 0x01, 0x80, 0xFF, + 0x80, 0x00, 0x1F, 0xF0, 0x00, 0x70, 0x0E, 0x01, 0xC0, 0x18, 0x38, 0x71, + 0xC0, 0x80, 0x00, 0x3E, 0x31, 0xB0, 0x78, 0x30, 0x18, 0x18, 0x38, 0x18, + 0x18, 0x0C, 0x00, 0x00, 0x01, 0x80, 0x03, 0xF0, 0x06, 0x0E, 0x06, 0x01, + 0x86, 0x00, 0x66, 0x1D, 0xBB, 0x31, 0xCF, 0x18, 0xC7, 0x98, 0x63, 0xCC, + 0x31, 0xE6, 0x11, 0xB3, 0x99, 0xCC, 0xF7, 0x86, 0x00, 0x01, 0x80, 0x00, + 0x70, 0x40, 0x0F, 0xE0, 0x06, 0x00, 0xF0, 0x0F, 0x00, 0x90, 0x19, 0x81, + 0x98, 0x10, 0x83, 0x0C, 0x3F, 0xC2, 0x04, 0x60, 0x66, 0x06, 0xC0, 0x30, + 0xFF, 0x18, 0x33, 0x03, 0x60, 0x6C, 0x0D, 0x83, 0x3F, 0xC6, 0x06, 0xC0, + 0x78, 0x0F, 0x01, 0xE0, 0x6F, 0xF8, 0x1F, 0x86, 0x19, 0x81, 0xA0, 0x3C, + 0x01, 0x80, 0x30, 0x06, 0x00, 0xC0, 0x68, 0x0D, 0x83, 0x18, 0x61, 0xF0, + 0xFF, 0x18, 0x33, 0x03, 0x60, 0x3C, 0x07, 0x80, 0xF0, 0x1E, 0x03, 0xC0, + 0x78, 0x0F, 0x03, 0x60, 0xCF, 0xF0, 0xFF, 0xE0, 0x30, 0x18, 0x0C, 0x06, + 0x03, 0xFD, 0x80, 0xC0, 0x60, 0x30, 0x18, 0x0F, 0xF8, 0xFF, 0xC0, 0xC0, + 0xC0, 0xC0, 0xC0, 0xFE, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0x0F, 0x83, + 0x0E, 0x60, 0x66, 0x03, 0xC0, 0x0C, 0x00, 0xC1, 0xFC, 0x03, 0xC0, 0x36, + 0x03, 0x60, 0x73, 0x0F, 0x0F, 0x10, 0xC0, 0x78, 0x0F, 0x01, 0xE0, 0x3C, + 0x07, 0x80, 0xFF, 0xFE, 0x03, 0xC0, 0x78, 0x0F, 0x01, 0xE0, 0x3C, 0x06, + 0xFF, 0xFF, 0xFF, 0xC0, 0x06, 0x0C, 0x18, 0x30, 0x60, 0xC1, 0x83, 0x07, + 0x8F, 0x1E, 0x27, 0x80, 0xC0, 0xD8, 0x33, 0x0C, 0x63, 0x0C, 0xC1, 0xB8, + 0x3F, 0x07, 0x30, 0xC3, 0x18, 0x63, 0x06, 0x60, 0x6C, 0x0C, 0xC0, 0xC0, + 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xFF, 0xE0, + 0x3F, 0x01, 0xFC, 0x1F, 0xE0, 0xFD, 0x05, 0xEC, 0x6F, 0x63, 0x79, 0x13, + 0xCD, 0x9E, 0x6C, 0xF1, 0x47, 0x8E, 0x3C, 0x71, 0x80, 0xE0, 0x7C, 0x0F, + 0xC1, 0xE8, 0x3D, 0x87, 0x98, 0xF1, 0x1E, 0x33, 0xC3, 0x78, 0x6F, 0x07, + 0xE0, 0x7C, 0x0E, 0x0F, 0x81, 0x83, 0x18, 0x0C, 0xC0, 0x6C, 0x01, 0xE0, + 0x0F, 0x00, 0x78, 0x03, 0xC0, 0x1B, 0x01, 0x98, 0x0C, 0x60, 0xC0, 0xF8, + 0x00, 0xFF, 0x30, 0x6C, 0x0F, 0x03, 0xC0, 0xF0, 0x6F, 0xF3, 0x00, 0xC0, + 0x30, 0x0C, 0x03, 0x00, 0xC0, 0x00, 0x0F, 0x81, 0x83, 0x18, 0x0C, 0xC0, + 0x6C, 0x01, 0xE0, 0x0F, 0x00, 0x78, 0x03, 0xC0, 0x1B, 0x01, 0x98, 0x6C, + 0x60, 0xC0, 0xFB, 0x00, 0x08, 0xFF, 0x8C, 0x0E, 0xC0, 0x6C, 0x06, 0xC0, + 0x6C, 0x0C, 0xFF, 0x8C, 0x0E, 0xC0, 0x6C, 0x06, 0xC0, 0x6C, 0x06, 0xC0, + 0x70, 0x3F, 0x18, 0x6C, 0x0F, 0x03, 0xC0, 0x1E, 0x01, 0xF0, 0x0E, 0x00, + 0xF0, 0x3C, 0x0D, 0x86, 0x3F, 0x00, 0xFF, 0x86, 0x03, 0x01, 0x80, 0xC0, + 0x60, 0x30, 0x18, 0x0C, 0x06, 0x03, 0x01, 0x80, 0xC0, 0xC0, 0x78, 0x0F, + 0x01, 0xE0, 0x3C, 0x07, 0x80, 0xF0, 0x1E, 0x03, 0xC0, 0x78, 0x0F, 0x01, + 0xB0, 0x61, 0xF0, 0xC0, 0x6C, 0x0D, 0x81, 0x10, 0x63, 0x0C, 0x61, 0x04, + 0x60, 0xCC, 0x19, 0x01, 0x60, 0x3C, 0x07, 0x00, 0x60, 0xC1, 0x81, 0x30, + 0xE1, 0x98, 0x70, 0xCC, 0x28, 0x66, 0x26, 0x21, 0x13, 0x30, 0xC8, 0x98, + 0x6C, 0x4C, 0x14, 0x34, 0x0A, 0x1A, 0x07, 0x07, 0x03, 0x03, 0x80, 0x81, + 0x80, 0x60, 0x63, 0x0C, 0x30, 0xC1, 0x98, 0x0F, 0x00, 0xE0, 0x06, 0x00, + 0xF0, 0x19, 0x01, 0x98, 0x30, 0xC6, 0x0E, 0x60, 0x60, 0xC0, 0x36, 0x06, + 0x30, 0xC3, 0x0C, 0x19, 0x81, 0xD8, 0x0F, 0x00, 0x60, 0x06, 0x00, 0x60, + 0x06, 0x00, 0x60, 0x06, 0x00, 0xFF, 0xC0, 0x60, 0x30, 0x0C, 0x06, 0x03, + 0x01, 0xC0, 0x60, 0x30, 0x18, 0x06, 0x03, 0x00, 0xFF, 0xC0, 0xFB, 0x6D, + 0xB6, 0xDB, 0x6D, 0xB6, 0xE0, 0x84, 0x10, 0x84, 0x10, 0x84, 0x10, 0x84, + 0x10, 0x80, 0xED, 0xB6, 0xDB, 0x6D, 0xB6, 0xDB, 0xE0, 0x30, 0x60, 0xA2, + 0x44, 0xD8, 0xA1, 0x80, 0xFF, 0xC0, 0xC6, 0x30, 0x7E, 0x71, 0xB0, 0xC0, + 0x60, 0xF3, 0xDB, 0x0D, 0x86, 0xC7, 0x3D, 0xC0, 0xC0, 0x60, 0x30, 0x1B, + 0xCE, 0x36, 0x0F, 0x07, 0x83, 0xC1, 0xE0, 0xF0, 0x7C, 0x6D, 0xE0, 0x3C, + 0x66, 0xC3, 0xC0, 0xC0, 0xC0, 0xC0, 0xC3, 0x66, 0x3C, 0x03, 0x03, 0x03, + 0x3B, 0x67, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0x67, 0x3B, 0x3C, 0x66, + 0xC3, 0xC3, 0xFF, 0xC0, 0xC0, 0xC3, 0x66, 0x3C, 0x36, 0x6F, 0x66, 0x66, + 0x66, 0x66, 0x60, 0x3B, 0x67, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0x67, + 0x3B, 0x03, 0x03, 0xC6, 0x7C, 0xC0, 0xC0, 0xC0, 0xDE, 0xE3, 0xC3, 0xC3, + 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xFF, 0xFF, 0xC0, 0x30, 0x03, + 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0xE0, 0xC0, 0x60, 0x30, 0x18, 0x4C, + 0x46, 0x63, 0x61, 0xF0, 0xEC, 0x62, 0x31, 0x98, 0x6C, 0x30, 0xFF, 0xFF, + 0xFF, 0xC0, 0xDE, 0xF7, 0x1C, 0xF0, 0xC7, 0x86, 0x3C, 0x31, 0xE1, 0x8F, + 0x0C, 0x78, 0x63, 0xC3, 0x1E, 0x18, 0xC0, 0xDE, 0xE3, 0xC3, 0xC3, 0xC3, + 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0x3C, 0x66, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, + 0xC3, 0x66, 0x3C, 0xDE, 0x71, 0xB0, 0x78, 0x3C, 0x1E, 0x0F, 0x07, 0x83, + 0xE3, 0x6F, 0x30, 0x18, 0x0C, 0x00, 0x3B, 0x67, 0xC3, 0xC3, 0xC3, 0xC3, + 0xC3, 0xC3, 0x67, 0x3B, 0x03, 0x03, 0x03, 0xDF, 0x31, 0x8C, 0x63, 0x18, + 0xC6, 0x00, 0x3E, 0xE3, 0xC0, 0xC0, 0xE0, 0x3C, 0x07, 0xC3, 0xE3, 0x7E, + 0x66, 0xF6, 0x66, 0x66, 0x66, 0x67, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, + 0xC3, 0xC3, 0xC7, 0x7B, 0xC1, 0xA0, 0x98, 0xCC, 0x42, 0x21, 0xB0, 0xD0, + 0x28, 0x1C, 0x0C, 0x00, 0xC6, 0x1E, 0x38, 0x91, 0xC4, 0xCA, 0x66, 0xD3, + 0x16, 0xD0, 0xA6, 0x87, 0x1C, 0x38, 0xC0, 0xC6, 0x00, 0x43, 0x62, 0x36, + 0x1C, 0x18, 0x1C, 0x3C, 0x26, 0x62, 0x43, 0xC1, 0x21, 0x98, 0xCC, 0x42, + 0x61, 0xB0, 0xD0, 0x38, 0x1C, 0x0C, 0x06, 0x03, 0x01, 0x03, 0x00, 0xFE, + 0x0C, 0x30, 0xC1, 0x86, 0x18, 0x20, 0xC1, 0xFC, 0x36, 0x66, 0x66, 0x6E, + 0xCE, 0x66, 0x66, 0x66, 0x30, 0xFF, 0xFF, 0xFF, 0xFF, 0xC0, 0xC6, 0x66, + 0x66, 0x67, 0x37, 0x66, 0x66, 0x66, 0xC0, 0x61, 0x24, 0x38 }; + +const GFXglyph FreeSans9pt7bGlyphs[] PROGMEM = { + { 0, 0, 0, 5, 0, 1 }, // 0x20 ' ' + { 0, 2, 13, 6, 2, -12 }, // 0x21 '!' + { 4, 5, 4, 6, 1, -12 }, // 0x22 '"' + { 7, 10, 12, 10, 0, -11 }, // 0x23 '#' + { 22, 9, 16, 10, 1, -13 }, // 0x24 '$' + { 40, 16, 13, 16, 1, -12 }, // 0x25 '%' + { 66, 11, 13, 12, 1, -12 }, // 0x26 '&' + { 84, 2, 4, 4, 1, -12 }, // 0x27 ''' + { 85, 4, 17, 6, 1, -12 }, // 0x28 '(' + { 94, 4, 17, 6, 1, -12 }, // 0x29 ')' + { 103, 5, 5, 7, 1, -12 }, // 0x2A '*' + { 107, 6, 8, 11, 3, -7 }, // 0x2B '+' + { 113, 2, 4, 5, 2, 0 }, // 0x2C ',' + { 114, 4, 1, 6, 1, -4 }, // 0x2D '-' + { 115, 2, 1, 5, 1, 0 }, // 0x2E '.' + { 116, 5, 13, 5, 0, -12 }, // 0x2F '/' + { 125, 8, 13, 10, 1, -12 }, // 0x30 '0' + { 138, 4, 13, 10, 3, -12 }, // 0x31 '1' + { 145, 9, 13, 10, 1, -12 }, // 0x32 '2' + { 160, 8, 13, 10, 1, -12 }, // 0x33 '3' + { 173, 7, 13, 10, 2, -12 }, // 0x34 '4' + { 185, 9, 13, 10, 1, -12 }, // 0x35 '5' + { 200, 9, 13, 10, 1, -12 }, // 0x36 '6' + { 215, 8, 13, 10, 0, -12 }, // 0x37 '7' + { 228, 9, 13, 10, 1, -12 }, // 0x38 '8' + { 243, 8, 13, 10, 1, -12 }, // 0x39 '9' + { 256, 2, 10, 5, 1, -9 }, // 0x3A ':' + { 259, 3, 12, 5, 1, -8 }, // 0x3B ';' + { 264, 9, 9, 11, 1, -8 }, // 0x3C '<' + { 275, 9, 4, 11, 1, -5 }, // 0x3D '=' + { 280, 9, 9, 11, 1, -8 }, // 0x3E '>' + { 291, 9, 13, 10, 1, -12 }, // 0x3F '?' + { 306, 17, 16, 18, 1, -12 }, // 0x40 '@' + { 340, 12, 13, 12, 0, -12 }, // 0x41 'A' + { 360, 11, 13, 12, 1, -12 }, // 0x42 'B' + { 378, 11, 13, 13, 1, -12 }, // 0x43 'C' + { 396, 11, 13, 13, 1, -12 }, // 0x44 'D' + { 414, 9, 13, 11, 1, -12 }, // 0x45 'E' + { 429, 8, 13, 11, 1, -12 }, // 0x46 'F' + { 442, 12, 13, 14, 1, -12 }, // 0x47 'G' + { 462, 11, 13, 13, 1, -12 }, // 0x48 'H' + { 480, 2, 13, 5, 2, -12 }, // 0x49 'I' + { 484, 7, 13, 10, 1, -12 }, // 0x4A 'J' + { 496, 11, 13, 12, 1, -12 }, // 0x4B 'K' + { 514, 8, 13, 10, 1, -12 }, // 0x4C 'L' + { 527, 13, 13, 15, 1, -12 }, // 0x4D 'M' + { 549, 11, 13, 13, 1, -12 }, // 0x4E 'N' + { 567, 13, 13, 14, 1, -12 }, // 0x4F 'O' + { 589, 10, 13, 12, 1, -12 }, // 0x50 'P' + { 606, 13, 14, 14, 1, -12 }, // 0x51 'Q' + { 629, 12, 13, 13, 1, -12 }, // 0x52 'R' + { 649, 10, 13, 12, 1, -12 }, // 0x53 'S' + { 666, 9, 13, 11, 1, -12 }, // 0x54 'T' + { 681, 11, 13, 13, 1, -12 }, // 0x55 'U' + { 699, 11, 13, 12, 0, -12 }, // 0x56 'V' + { 717, 17, 13, 17, 0, -12 }, // 0x57 'W' + { 745, 12, 13, 12, 0, -12 }, // 0x58 'X' + { 765, 12, 13, 12, 0, -12 }, // 0x59 'Y' + { 785, 10, 13, 11, 1, -12 }, // 0x5A 'Z' + { 802, 3, 17, 5, 1, -12 }, // 0x5B '[' + { 809, 5, 13, 5, 0, -12 }, // 0x5C '\' + { 818, 3, 17, 5, 0, -12 }, // 0x5D ']' + { 825, 7, 7, 8, 1, -12 }, // 0x5E '^' + { 832, 10, 1, 10, 0, 3 }, // 0x5F '_' + { 834, 4, 3, 5, 0, -12 }, // 0x60 '`' + { 836, 9, 10, 10, 1, -9 }, // 0x61 'a' + { 848, 9, 13, 10, 1, -12 }, // 0x62 'b' + { 863, 8, 10, 9, 1, -9 }, // 0x63 'c' + { 873, 8, 13, 10, 1, -12 }, // 0x64 'd' + { 886, 8, 10, 10, 1, -9 }, // 0x65 'e' + { 896, 4, 13, 5, 1, -12 }, // 0x66 'f' + { 903, 8, 14, 10, 1, -9 }, // 0x67 'g' + { 917, 8, 13, 10, 1, -12 }, // 0x68 'h' + { 930, 2, 13, 4, 1, -12 }, // 0x69 'i' + { 934, 4, 17, 4, 0, -12 }, // 0x6A 'j' + { 943, 9, 13, 9, 1, -12 }, // 0x6B 'k' + { 958, 2, 13, 4, 1, -12 }, // 0x6C 'l' + { 962, 13, 10, 15, 1, -9 }, // 0x6D 'm' + { 979, 8, 10, 10, 1, -9 }, // 0x6E 'n' + { 989, 8, 10, 10, 1, -9 }, // 0x6F 'o' + { 999, 9, 13, 10, 1, -9 }, // 0x70 'p' + { 1014, 8, 13, 10, 1, -9 }, // 0x71 'q' + { 1027, 5, 10, 6, 1, -9 }, // 0x72 'r' + { 1034, 8, 10, 9, 1, -9 }, // 0x73 's' + { 1044, 4, 12, 5, 1, -11 }, // 0x74 't' + { 1050, 8, 10, 10, 1, -9 }, // 0x75 'u' + { 1060, 9, 10, 9, 0, -9 }, // 0x76 'v' + { 1072, 13, 10, 13, 0, -9 }, // 0x77 'w' + { 1089, 8, 10, 9, 0, -9 }, // 0x78 'x' + { 1099, 9, 14, 9, 0, -9 }, // 0x79 'y' + { 1115, 7, 10, 9, 1, -9 }, // 0x7A 'z' + { 1124, 4, 17, 6, 1, -12 }, // 0x7B '{' + { 1133, 2, 17, 4, 2, -12 }, // 0x7C '|' + { 1138, 4, 17, 6, 1, -12 }, // 0x7D '}' + { 1147, 7, 3, 9, 1, -7 } }; // 0x7E '~' + +const GFXfont FreeSans9pt7b PROGMEM = { + (uint8_t *)FreeSans9pt7bBitmaps, + (GFXglyph *)FreeSans9pt7bGlyphs, + 0x20, 0x7E, 22 }; + +// Approx. 1822 bytes