diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..f288702
--- /dev/null
+++ b/LICENSE
@@ -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/README.md b/README.md
new file mode 100644
index 0000000..e69de29
diff --git a/index.html b/index.html
new file mode 100644
index 0000000..2f28982
--- /dev/null
+++ b/index.html
@@ -0,0 +1,35 @@
+
+
+
+
+ Meshtastic Monitor
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/meshmon.css b/meshmon.css
new file mode 100644
index 0000000..1098f3b
--- /dev/null
+++ b/meshmon.css
@@ -0,0 +1,62 @@
+body {
+ background-color: #232327;
+ color: #b1b1b3;
+}
+thead, tbody {
+ font-family: monospace;
+}
+
+table, th, td {
+ white-space: nowrap;
+ padding-left: 8px;
+ padding-right: 8px;
+}
+thead tr {
+ text-align: center;
+}
+thead {
+ position: sticky;
+ top: 0;
+}
+tbody tr {
+ text-align: right;
+}
+.decoded td {
+ text-align: left;
+ font-size: 80%;
+}
+
+.main-table {
+ border-collapse: collapse;
+ border: none;
+}
+.main-table td {
+ border: 1px solid #444444;
+}
+.main-table tr:first-child td {
+ border-top: none;
+}
+.main-table tr:last-child td {
+ border-bottom: none;
+}
+.main-table tr td:first-child {
+ border-left: none;
+}
+.main-table tr td:last-child {
+ border-right: none;
+}
+
+.main-table thead tr {
+ background: #000000;
+}
+
+.main-table tbody tr:nth-child(odd) {
+ background: #333333;
+}
+
+.status-disconnected {
+ background-color: #ea7a6c;
+}
+.status-connected {
+ background-color: #2ea043;
+}
diff --git a/meshmon.js b/meshmon.js
new file mode 100644
index 0000000..08fcbd0
--- /dev/null
+++ b/meshmon.js
@@ -0,0 +1,291 @@
+'use strict';
+
+const defaultMqttUrl = 'wss://mqtt.eclipseprojects.io/mqtt';
+const defaultMqttTopic = 'marsupial';
+const defaultKey = CryptoJS.enc.Base64.parse('1PG7OiApB1nwvP+rz05pAQ==');
+
+var mqttUrlInput = null;
+var mqttTopicInput = null;
+var mqttConnected = false;
+var mqttClient = null;
+
+var tbody = null;
+var statusRow = null;
+var connectButton = null;
+
+function formatTime(v) {
+ const t = new Date(v * 1000);
+ return t.toISOString();
+}
+
+function formatNodeId(v) {
+ return '!' + v.toString(16).padStart(8, '0');
+}
+
+function formatFloat(v) {
+ const n = new Number(v);
+ return n.toFixed(2);
+}
+
+function formatCoord(v) {
+ return v / 10000000;
+}
+
+const formatters = new Map([
+ ['nodeId', formatNodeId],
+ ['lastSentById', formatNodeId],
+ ['time', formatTime],
+ ['temperature', formatFloat],
+ ['voltage', formatFloat],
+ ['barometricPressure', formatFloat],
+ ['relativeHumidity', formatFloat],
+ ['channelUtilization', formatFloat],
+ ['airUtilTx', formatFloat],
+ ['latitudeI', formatCoord],
+ ['longitudeI', formatCoord],
+]);
+
+function decodeDefault(proto, name, payload) {
+ const info = proto.decode(payload);
+ const replacer = (k, v) => {
+ const formatter = formatters.get(k);
+ if (formatter) {
+ return formatter(v);
+ } else {
+ return v;
+ }
+ };
+ return `${name}\n${JSON.stringify(info, replacer, 2)}`;
+};
+
+function decodeText(_proto, name, payload) {
+ return `${name}\n${payload.decode('utf-8')}`;
+}
+
+function buildMeshtasticProtos() {
+ var protobuf_root = new protobuf.Root();
+ protobuf_root.resolvePath = function(_origin, target) {
+ return 'protobufs/' + target;
+ };
+
+ const modules = ['meshtastic/mqtt.proto', 'meshtastic/storeforward.proto'];
+ var meshtastic = {};
+
+ protobuf_root.load(modules, function(err, root) {
+ if (err)
+ throw err;
+
+ meshtastic.ServiceEnvelope = root.lookupType('meshtastic.ServiceEnvelope');
+ meshtastic.Data = root.lookupType('meshtastic.Data');
+ meshtastic.PortNum = root.lookupEnum('meshtastic.PortNum');
+
+ const ports = meshtastic.PortNum.values;
+ const protos = [
+ { port: ports.TEXT_MESSAGE_APP, name: 'Text', decode: decodeText },
+ { port: ports.POSITION_APP, name: 'Position', decode: decodeDefault },
+ { port: ports.NODEINFO_APP, name: 'User', decode: decodeDefault },
+ { port: ports.ROUTING_APP, name: 'Routing', decode: decodeDefault },
+ { port: ports.STORE_FORWARD_APP, name: 'StoreAndForward', decode: decodeDefault },
+ { port: ports.TELEMETRY_APP, name: 'Telemetry', decode: decodeDefault },
+ { port: ports.TRACEROUTE_APP, name: 'RouteDiscovery', decode: decodeDefault },
+ { port: ports.NEIGHBORINFO_APP, name: 'NeighborInfo', decode: decodeDefault },
+ ];
+
+ meshtastic.protos = {};
+ protos.forEach((p) => {
+ var proto = null;
+ try {
+ proto = root.lookupType('meshtastic.' + p.name);
+ } catch (_error) {
+ }
+ meshtastic.protos[p.port] = {
+ decode: (v) => { return p.decode(proto, p.name, v); },
+ };
+ });
+ });
+
+ return meshtastic;
+}
+
+const meshtastic = buildMeshtasticProtos();
+
+function swap32(val) {
+ return ((val & 0xff000000) >>> 24)
+ | ((val & 0x00ff0000) >>> 8)
+ | ((val & 0x0000ff00) << 8)
+ | ((val & 0x000000ff) << 24);
+}
+
+function wordsToByteArray(wordArray) {
+ var byteArray = new Uint8Array(wordArray.sigBytes);
+ for (var i = 0; i < wordArray.sigBytes; ++i) {
+ byteArray[i] = (wordArray.words[i >>> 2] >>> (24 - ((i & 3) << 3))) & 0xff;
+ }
+ return byteArray;
+}
+
+function arrayToString(arr) {
+ return arr.map((v) => v.toString(16).padStart(2, '0')).join('');
+}
+
+function decodeEncrypted(packet, key) {
+ const iv = CryptoJS.lib.WordArray.create([
+ swap32(packet.id), 0,
+ swap32(packet.from), 0,
+ ]);
+
+ const encrypted = CryptoJS.lib.WordArray.create(packet.encrypted);
+ const decrypted = CryptoJS.AES.decrypt(
+ CryptoJS.lib.CipherParams.create({
+ ciphertext: encrypted,
+ }),
+ key,
+ {
+ mode: CryptoJS.mode.CTR,
+ iv: iv,
+ padding: CryptoJS.pad.NoPadding,
+ }
+ );
+
+ try {
+ packet.decoded = meshtastic.Data.decode(wordsToByteArray(decrypted));
+ } catch (error) {
+ console.log('failed to decode encrypted packet: ' + error);
+ }
+}
+
+function mqttOnConnect() {
+ mqttConnected = true;
+ connectButton.textContent = 'Disconnect';
+ connectButton.disabled = false;
+ statusRow.className = 'status-connected';
+ mqttClient.subscribe(`${mqttTopicInput.value}/2/e/#`);
+}
+
+function mqttOnDisconnect() {
+ mqttConnected = false;
+ mqttClient = null;
+ connectButton.textContent = 'Connect';
+ connectButton.disabled = false;
+ mqttUrlInput.disabled = false;
+ mqttTopicInput.disabled = false;
+ statusRow.className = 'status-disconnected';
+}
+
+function onClick() {
+ connectButton.disabled = true;
+ if (mqttConnected) {
+ mqttClient.end(false, null, mqttOnDisconnect);
+ } else {
+ mqttUrlInput.disabled = true;
+ mqttTopicInput.disabled = true;
+ mqttClient = mqtt.connect(mqttUrlInput.value);
+ mqttClient.on('connect', mqttOnConnect);
+ mqttClient.on('message', mqttOnMessage);
+ mqttClient.on('error', mqttOnDisconnect);
+ }
+}
+
+const fields = [
+ 'rxTime', 'gatewayId', 'channelId', 'id', 'hopStart', 'hopLimit',
+ 'wantAck', 'viaMqtt', 'rxRssi', 'rxSnr', 'from', 'to', 'portnum',
+];
+
+function mqttOnMessage(topic, message) {
+ var se = null;
+ try {
+ se = meshtastic.ServiceEnvelope.decode(message);
+ } catch (error) {
+ console.error(`Failed to decode ServiceEnvelope: ${error}`);
+ console.error(`Topic: ${topic}`);
+ console.error(`Message: x${arrayToString(message)}`);
+ return;
+ }
+
+ var row = tbody.insertRow();
+ var cells = {};
+ fields.forEach((field) => {
+ cells[field] = row.insertCell();
+ });
+
+ cells['rxTime'].innerHTML = formatTime(se.packet.rxTime);
+ cells['gatewayId'].innerHTML = se.gatewayId;
+ cells['channelId'].innerHTML = se.channelId;
+ cells['id'].innerHTML = se.packet.id.toString(16).padStart(8, '0');
+ cells['hopStart'].innerHTML = se.packet.hopStart;
+ cells['hopLimit'].innerHTML = se.packet.hopLimit;
+ cells['wantAck'].innerHTML = se.packet.wantAck ? '1' : '0';
+ cells['viaMqtt'].innerHTML = se.packet.viaMqtt ? '1' : '0';
+ cells['rxRssi'].innerHTML = se.packet.rxRssi;
+ cells['rxSnr'].innerHTML = se.packet.rxSnr;
+ cells['from'].innerHTML = formatNodeId(se.packet.from);
+ cells['to'].innerHTML = formatNodeId(se.packet.to);
+
+ if (se.packet.payloadVariant == 'encrypted' && se.channelId == 'LongFast') {
+ decodeEncrypted(se.packet, defaultKey);
+ }
+
+ var row = tbody.insertRow();
+ row.className = 'decoded';
+ var cell = row.insertCell();
+ cell.colSpan = fields.length;
+ var decoded = document.createElement('pre');
+ cell.appendChild(decoded);
+
+ var decodedText = "";
+ if (se.packet.encrypted) {
+ decodedText += `x${arrayToString(se.packet.encrypted)} Encrypted\n`;
+ }
+
+ if (se.packet.decoded) {
+ decodedText += `x${arrayToString(se.packet.decoded.payload)} `
+
+ const port = se.packet.decoded.portnum;
+ cells['portnum'].innerHTML = port;
+
+ var decode = null;
+ if (port in meshtastic.protos) {
+ decode = meshtastic.protos[port].decode;
+ }
+
+ if (decode) {
+ try {
+ decodedText += decode(se.packet.decoded.payload);
+ } catch (error) {
+ decodedText += `Error ${error}`;
+ }
+ } else {
+ decodedText += `NYI ${port}`;
+ }
+ } else {
+ cells['portnum'].innerHTML = '?';
+ }
+ decoded.textContent = decodedText;
+
+ window.scrollTo(0, document.body.scrollHeight);
+}
+
+window.onload = function() {
+ var theadRow = document.getElementById('thead-row');
+ fields.forEach((field) => {
+ var th = theadRow.insertCell();
+ th.innerHTML = field;
+ });
+
+ tbody = document.getElementById('tbody');
+
+ statusRow = document.getElementById('status-row');
+ document.getElementById('status-cell').colSpan = fields.length;
+ document.getElementById('input-cell').colSpan = fields.length;
+
+ mqttUrlInput = document.getElementById('mqtt-url');
+ mqttUrlInput.placeholder = defaultMqttUrl;
+ mqttUrlInput.defaultValue = defaultMqttUrl;
+
+ mqttTopicInput = document.getElementById('mqtt-topic');
+ mqttTopicInput.placeholder = defaultMqttTopic;
+ mqttTopicInput.defaultValue = defaultMqttTopic;
+
+ connectButton = document.getElementById('mqtt-connect');
+ connectButton.addEventListener('click', onClick);
+};
diff --git a/protobufs/meshtastic/admin.proto b/protobufs/meshtastic/admin.proto
new file mode 100644
index 0000000..3b227d8
--- /dev/null
+++ b/protobufs/meshtastic/admin.proto
@@ -0,0 +1,383 @@
+syntax = "proto3";
+
+package meshtastic;
+
+import "meshtastic/channel.proto";
+import "meshtastic/config.proto";
+import "meshtastic/connection_status.proto";
+import "meshtastic/mesh.proto";
+import "meshtastic/module_config.proto";
+
+option csharp_namespace = "Meshtastic.Protobufs";
+option go_package = "github.com/meshtastic/go/generated";
+option java_outer_classname = "AdminProtos";
+option java_package = "com.geeksville.mesh";
+option swift_prefix = "";
+
+/*
+ * This message is handled by the Admin module and is responsible for all settings/channel read/write operations.
+ * This message is used to do settings operations to both remote AND local nodes.
+ * (Prior to 1.2 these operations were done via special ToRadio operations)
+ */
+message AdminMessage {
+ /*
+ * TODO: REPLACE
+ */
+ enum ConfigType {
+ /*
+ * TODO: REPLACE
+ */
+ DEVICE_CONFIG = 0;
+
+ /*
+ * TODO: REPLACE
+ */
+ POSITION_CONFIG = 1;
+
+ /*
+ * TODO: REPLACE
+ */
+ POWER_CONFIG = 2;
+
+ /*
+ * TODO: REPLACE
+ */
+ NETWORK_CONFIG = 3;
+
+ /*
+ * TODO: REPLACE
+ */
+ DISPLAY_CONFIG = 4;
+
+ /*
+ * TODO: REPLACE
+ */
+ LORA_CONFIG = 5;
+
+ /*
+ * TODO: REPLACE
+ */
+ BLUETOOTH_CONFIG = 6;
+ }
+
+ /*
+ * TODO: REPLACE
+ */
+ enum ModuleConfigType {
+ /*
+ * TODO: REPLACE
+ */
+ MQTT_CONFIG = 0;
+
+ /*
+ * TODO: REPLACE
+ */
+ SERIAL_CONFIG = 1;
+
+ /*
+ * TODO: REPLACE
+ */
+ EXTNOTIF_CONFIG = 2;
+
+ /*
+ * TODO: REPLACE
+ */
+ STOREFORWARD_CONFIG = 3;
+
+ /*
+ * TODO: REPLACE
+ */
+ RANGETEST_CONFIG = 4;
+
+ /*
+ * TODO: REPLACE
+ */
+ TELEMETRY_CONFIG = 5;
+
+ /*
+ * TODO: REPLACE
+ */
+ CANNEDMSG_CONFIG = 6;
+
+ /*
+ * TODO: REPLACE
+ */
+ AUDIO_CONFIG = 7;
+
+ /*
+ * TODO: REPLACE
+ */
+ REMOTEHARDWARE_CONFIG = 8;
+
+ /*
+ * TODO: REPLACE
+ */
+ NEIGHBORINFO_CONFIG = 9;
+
+ /*
+ * TODO: REPLACE
+ */
+ AMBIENTLIGHTING_CONFIG = 10;
+
+ /*
+ * TODO: REPLACE
+ */
+ DETECTIONSENSOR_CONFIG = 11;
+
+ /*
+ * TODO: REPLACE
+ */
+ PAXCOUNTER_CONFIG = 12;
+ }
+
+ /*
+ * TODO: REPLACE
+ */
+ oneof payload_variant {
+ /*
+ * Send the specified channel in the response to this message
+ * NOTE: This field is sent with the channel index + 1 (to ensure we never try to send 'zero' - which protobufs treats as not present)
+ */
+ uint32 get_channel_request = 1;
+
+ /*
+ * TODO: REPLACE
+ */
+ Channel get_channel_response = 2;
+
+ /*
+ * Send the current owner data in the response to this message.
+ */
+ bool get_owner_request = 3;
+
+ /*
+ * TODO: REPLACE
+ */
+ User get_owner_response = 4;
+
+ /*
+ * Ask for the following config data to be sent
+ */
+ ConfigType get_config_request = 5;
+
+ /*
+ * Send the current Config in the response to this message.
+ */
+ Config get_config_response = 6;
+
+ /*
+ * Ask for the following config data to be sent
+ */
+ ModuleConfigType get_module_config_request = 7;
+
+ /*
+ * Send the current Config in the response to this message.
+ */
+ ModuleConfig get_module_config_response = 8;
+
+ /*
+ * Get the Canned Message Module messages in the response to this message.
+ */
+ bool get_canned_message_module_messages_request = 10;
+
+ /*
+ * Get the Canned Message Module messages in the response to this message.
+ */
+ string get_canned_message_module_messages_response = 11;
+
+ /*
+ * Request the node to send device metadata (firmware, protobuf version, etc)
+ */
+ bool get_device_metadata_request = 12;
+
+ /*
+ * Device metadata response
+ */
+ DeviceMetadata get_device_metadata_response = 13;
+
+ /*
+ * Get the Ringtone in the response to this message.
+ */
+ bool get_ringtone_request = 14;
+
+ /*
+ * Get the Ringtone in the response to this message.
+ */
+ string get_ringtone_response = 15;
+
+ /*
+ * Request the node to send it's connection status
+ */
+ bool get_device_connection_status_request = 16;
+
+ /*
+ * Device connection status response
+ */
+ DeviceConnectionStatus get_device_connection_status_response = 17;
+
+ /*
+ * Setup a node for licensed amateur (ham) radio operation
+ */
+ HamParameters set_ham_mode = 18;
+
+ /*
+ * Get the mesh's nodes with their available gpio pins for RemoteHardware module use
+ */
+ bool get_node_remote_hardware_pins_request = 19;
+
+ /*
+ * Respond with the mesh's nodes with their available gpio pins for RemoteHardware module use
+ */
+ NodeRemoteHardwarePinsResponse get_node_remote_hardware_pins_response = 20;
+
+ /*
+ * Enter (UF2) DFU mode
+ * Only implemented on NRF52 currently
+ */
+ bool enter_dfu_mode_request = 21;
+
+ /*
+ * Delete the file by the specified path from the device
+ */
+ string delete_file_request = 22;
+
+ /*
+ * Set the owner for this node
+ */
+ User set_owner = 32;
+
+ /*
+ * Set channels (using the new API).
+ * A special channel is the "primary channel".
+ * The other records are secondary channels.
+ * Note: only one channel can be marked as primary.
+ * If the client sets a particular channel to be primary, the previous channel will be set to SECONDARY automatically.
+ */
+ Channel set_channel = 33;
+
+ /*
+ * Set the current Config
+ */
+ Config set_config = 34;
+
+ /*
+ * Set the current Config
+ */
+ ModuleConfig set_module_config = 35;
+
+ /*
+ * Set the Canned Message Module messages text.
+ */
+ string set_canned_message_module_messages = 36;
+
+ /*
+ * Set the ringtone for ExternalNotification.
+ */
+ string set_ringtone_message = 37;
+
+ /*
+ * Remove the node by the specified node-num from the NodeDB on the device
+ */
+ uint32 remove_by_nodenum = 38;
+
+ /*
+ * Set specified node-num to be favorited on the NodeDB on the device
+ */
+ uint32 set_favorite_node = 39;
+
+ /*
+ * Set specified node-num to be un-favorited on the NodeDB on the device
+ */
+ uint32 remove_favorite_node = 40;
+
+ /*
+ * Set fixed position data on the node and then set the position.fixed_position = true
+ */
+ Position set_fixed_position = 41;
+
+ /*
+ * Clear fixed position coordinates and then set position.fixed_position = false
+ */
+ bool remove_fixed_position = 42;
+
+ /*
+ * Begins an edit transaction for config, module config, owner, and channel settings changes
+ * This will delay the standard *implicit* save to the file system and subsequent reboot behavior until committed (commit_edit_settings)
+ */
+ bool begin_edit_settings = 64;
+
+ /*
+ * Commits an open transaction for any edits made to config, module config, owner, and channel settings
+ */
+ bool commit_edit_settings = 65;
+
+ /*
+ * Tell the node to reboot into the OTA Firmware in this many seconds (or <0 to cancel reboot)
+ * Only Implemented for ESP32 Devices. This needs to be issued to send a new main firmware via bluetooth.
+ */
+ int32 reboot_ota_seconds = 95;
+
+ /*
+ * This message is only supported for the simulator Portduino build.
+ * If received the simulator will exit successfully.
+ */
+ bool exit_simulator = 96;
+
+ /*
+ * Tell the node to reboot in this many seconds (or <0 to cancel reboot)
+ */
+ int32 reboot_seconds = 97;
+
+ /*
+ * Tell the node to shutdown in this many seconds (or <0 to cancel shutdown)
+ */
+ int32 shutdown_seconds = 98;
+
+ /*
+ * Tell the node to factory reset, all device settings will be returned to factory defaults.
+ */
+ int32 factory_reset = 99;
+
+ /*
+ * Tell the node to reset the nodedb.
+ */
+ int32 nodedb_reset = 100;
+ }
+}
+
+/*
+ * Parameters for setting up Meshtastic for ameteur radio usage
+ */
+message HamParameters {
+ /*
+ * Amateur radio call sign, eg. KD2ABC
+ */
+ string call_sign = 1;
+
+ /*
+ * Transmit power in dBm at the LoRA transceiver, not including any amplification
+ */
+ int32 tx_power = 2;
+
+ /*
+ * The selected frequency of LoRA operation
+ * Please respect your local laws, regulations, and band plans.
+ * Ensure your radio is capable of operating of the selected frequency before setting this.
+ */
+ float frequency = 3;
+
+ /*
+ * Optional short name of user
+ */
+ string short_name = 4;
+}
+
+/*
+ * Response envelope for node_remote_hardware_pins
+ */
+message NodeRemoteHardwarePinsResponse {
+ /*
+ * Nodes and their respective remote hardware GPIO pins
+ */
+ repeated NodeRemoteHardwarePin node_remote_hardware_pins = 1;
+}
diff --git a/protobufs/meshtastic/apponly.proto b/protobufs/meshtastic/apponly.proto
new file mode 100644
index 0000000..100833f
--- /dev/null
+++ b/protobufs/meshtastic/apponly.proto
@@ -0,0 +1,31 @@
+syntax = "proto3";
+
+package meshtastic;
+
+import "meshtastic/channel.proto";
+import "meshtastic/config.proto";
+
+option csharp_namespace = "Meshtastic.Protobufs";
+option go_package = "github.com/meshtastic/go/generated";
+option java_outer_classname = "AppOnlyProtos";
+option java_package = "com.geeksville.mesh";
+option swift_prefix = "";
+
+/*
+ * This is the most compact possible representation for a set of channels.
+ * It includes only one PRIMARY channel (which must be first) and
+ * any SECONDARY channels.
+ * No DISABLED channels are included.
+ * This abstraction is used only on the the 'app side' of the world (ie python, javascript and android etc) to show a group of Channels as a (long) URL
+ */
+message ChannelSet {
+ /*
+ * Channel list with settings
+ */
+ repeated ChannelSettings settings = 1;
+
+ /*
+ * LoRa config
+ */
+ Config.LoRaConfig lora_config = 2;
+}
diff --git a/protobufs/meshtastic/atak.proto b/protobufs/meshtastic/atak.proto
new file mode 100644
index 0000000..199f06b
--- /dev/null
+++ b/protobufs/meshtastic/atak.proto
@@ -0,0 +1,252 @@
+syntax = "proto3";
+
+package meshtastic;
+
+option csharp_namespace = "Meshtastic.Protobufs";
+option go_package = "github.com/meshtastic/go/generated";
+option java_outer_classname = "ATAKProtos";
+option java_package = "com.geeksville.mesh";
+option swift_prefix = "";
+
+/*
+ * Packets for the official ATAK Plugin
+ */
+message TAKPacket {
+ /*
+ * Are the payloads strings compressed for LoRA transport?
+ */
+ bool is_compressed = 1;
+ /*
+ * The contact / callsign for ATAK user
+ */
+ Contact contact = 2;
+ /*
+ * The group for ATAK user
+ */
+ Group group = 3;
+ /*
+ * The status of the ATAK EUD
+ */
+ Status status = 4;
+ /*
+ * The payload of the packet
+ */
+ oneof payload_variant {
+ /*
+ * TAK position report
+ */
+ PLI pli = 5;
+ /*
+ * ATAK GeoChat message
+ */
+ GeoChat chat = 6;
+ }
+}
+
+/*
+ * ATAK GeoChat message
+ */
+message GeoChat {
+ /*
+ * The text message
+ */
+ string message = 1;
+
+ /*
+ * Uid recipient of the message
+ */
+ optional string to = 2;
+}
+
+/*
+ * ATAK Group
+ * <__group role='Team Member' name='Cyan'/>
+ */
+message Group {
+ /*
+ * Role of the group member
+ */
+ MemberRole role = 1;
+ /*
+ * Team (color)
+ * Default Cyan
+ */
+ Team team = 2;
+}
+
+enum Team {
+ /*
+ * Unspecifed
+ */
+ Unspecifed_Color = 0;
+ /*
+ * White
+ */
+ White = 1;
+ /*
+ * Yellow
+ */
+ Yellow = 2;
+ /*
+ * Orange
+ */
+ Orange = 3;
+ /*
+ * Magenta
+ */
+ Magenta = 4;
+ /*
+ * Red
+ */
+ Red = 5;
+ /*
+ * Maroon
+ */
+ Maroon = 6;
+ /*
+ * Purple
+ */
+ Purple = 7;
+ /*
+ * Dark Blue
+ */
+ Dark_Blue = 8;
+ /*
+ * Blue
+ */
+ Blue = 9;
+ /*
+ * Cyan
+ */
+ Cyan = 10;
+ /*
+ * Teal
+ */
+ Teal = 11;
+ /*
+ * Green
+ */
+ Green = 12;
+ /*
+ * Dark Green
+ */
+ Dark_Green = 13;
+ /*
+ * Brown
+ */
+ Brown = 14;
+}
+
+/*
+ * Role of the group member
+ */
+enum MemberRole {
+ /*
+ * Unspecifed
+ */
+ Unspecifed = 0;
+ /*
+ * Team Member
+ */
+ TeamMember = 1;
+ /*
+ * Team Lead
+ */
+ TeamLead = 2;
+ /*
+ * Headquarters
+ */
+ HQ = 3;
+ /*
+ * Airsoft enthusiast
+ */
+ Sniper = 4;
+ /*
+ * Medic
+ */
+ Medic = 5;
+ /*
+ * ForwardObserver
+ */
+ ForwardObserver = 6;
+ /*
+ * Radio Telephone Operator
+ */
+ RTO = 7;
+ /*
+ * Doggo
+ */
+ K9 = 8;
+}
+
+/*
+ * ATAK EUD Status
+ *
+ */
+message Status {
+ /*
+ * Battery level
+ */
+ uint32 battery = 1;
+}
+
+/*
+ * ATAK Contact
+ *
+ */
+message Contact {
+ /*
+ * Callsign
+ */
+ string callsign = 1;
+
+ /*
+ * Device callsign
+ */
+ string device_callsign = 2;
+ /*
+ * IP address of endpoint in integer form (0.0.0.0 default)
+ */
+ // fixed32 enpoint_address = 3;
+ /*
+ * Port of endpoint (4242 default)
+ */
+ // uint32 endpoint_port = 4;
+ /*
+ * Phone represented as integer
+ * Terrible practice, but we really need the wire savings
+ */
+ // uint32 phone = 4;
+}
+
+/*
+ * Position Location Information from ATAK
+ */
+message PLI {
+ /*
+ * The new preferred location encoding, multiply by 1e-7 to get degrees
+ * in floating point
+ */
+ sfixed32 latitude_i = 1;
+
+ /*
+ * The new preferred location encoding, multiply by 1e-7 to get degrees
+ * in floating point
+ */
+ sfixed32 longitude_i = 2;
+
+ /*
+ * Altitude (ATAK prefers HAE)
+ */
+ int32 altitude = 3;
+
+ /*
+ * Speed
+ */
+ uint32 speed = 4;
+
+ /*
+ * Course in degrees
+ */
+ uint32 course = 5;
+}
diff --git a/protobufs/meshtastic/cannedmessages.proto b/protobufs/meshtastic/cannedmessages.proto
new file mode 100644
index 0000000..baa5134
--- /dev/null
+++ b/protobufs/meshtastic/cannedmessages.proto
@@ -0,0 +1,19 @@
+syntax = "proto3";
+
+package meshtastic;
+
+option csharp_namespace = "Meshtastic.Protobufs";
+option go_package = "github.com/meshtastic/go/generated";
+option java_outer_classname = "CannedMessageConfigProtos";
+option java_package = "com.geeksville.mesh";
+option swift_prefix = "";
+
+/*
+ * Canned message module configuration.
+ */
+message CannedMessageModuleConfig {
+ /*
+ * Predefined messages for canned message module separated by '|' characters.
+ */
+ string messages = 1;
+}
diff --git a/protobufs/meshtastic/channel.proto b/protobufs/meshtastic/channel.proto
new file mode 100644
index 0000000..d2ec81a
--- /dev/null
+++ b/protobufs/meshtastic/channel.proto
@@ -0,0 +1,150 @@
+syntax = "proto3";
+
+package meshtastic;
+
+option csharp_namespace = "Meshtastic.Protobufs";
+option go_package = "github.com/meshtastic/go/generated";
+option java_outer_classname = "ChannelProtos";
+option java_package = "com.geeksville.mesh";
+option swift_prefix = "";
+
+/*
+ * This information can be encoded as a QRcode/url so that other users can configure
+ * their radio to join the same channel.
+ * A note about how channel names are shown to users: channelname-X
+ * poundsymbol is a prefix used to indicate this is a channel name (idea from @professr).
+ * Where X is a letter from A-Z (base 26) representing a hash of the PSK for this
+ * channel - so that if the user changes anything about the channel (which does
+ * force a new PSK) this letter will also change. Thus preventing user confusion if
+ * two friends try to type in a channel name of "BobsChan" and then can't talk
+ * because their PSKs will be different.
+ * The PSK is hashed into this letter by "0x41 + [xor all bytes of the psk ] modulo 26"
+ * This also allows the option of someday if people have the PSK off (zero), the
+ * users COULD type in a channel name and be able to talk.
+ * FIXME: Add description of multi-channel support and how primary vs secondary channels are used.
+ * FIXME: explain how apps use channels for security.
+ * explain how remote settings and remote gpio are managed as an example
+ */
+message ChannelSettings {
+ /*
+ * Deprecated in favor of LoraConfig.channel_num
+ */
+ uint32 channel_num = 1 [deprecated = true];
+
+ /*
+ * A simple pre-shared key for now for crypto.
+ * Must be either 0 bytes (no crypto), 16 bytes (AES128), or 32 bytes (AES256).
+ * A special shorthand is used for 1 byte long psks.
+ * These psks should be treated as only minimally secure,
+ * because they are listed in this source code.
+ * Those bytes are mapped using the following scheme:
+ * `0` = No crypto
+ * `1` = The special "default" channel key: {0xd4, 0xf1, 0xbb, 0x3a, 0x20, 0x29, 0x07, 0x59, 0xf0, 0xbc, 0xff, 0xab, 0xcf, 0x4e, 0x69, 0x01}
+ * `2` through 10 = The default channel key, except with 1 through 9 added to the last byte.
+ * Shown to user as simple1 through 10
+ */
+ bytes psk = 2;
+
+ /*
+ * A SHORT name that will be packed into the URL.
+ * Less than 12 bytes.
+ * Something for end users to call the channel
+ * If this is the empty string it is assumed that this channel
+ * is the special (minimally secure) "Default"channel.
+ * In user interfaces it should be rendered as a local language translation of "X".
+ * For channel_num hashing empty string will be treated as "X".
+ * Where "X" is selected based on the English words listed above for ModemPreset
+ */
+ string name = 3;
+
+ /*
+ * Used to construct a globally unique channel ID.
+ * The full globally unique ID will be: "name.id" where ID is shown as base36.
+ * Assuming that the number of meshtastic users is below 20K (true for a long time)
+ * the chance of this 64 bit random number colliding with anyone else is super low.
+ * And the penalty for collision is low as well, it just means that anyone trying to decrypt channel messages might need to
+ * try multiple candidate channels.
+ * Any time a non wire compatible change is made to a channel, this field should be regenerated.
+ * There are a small number of 'special' globally known (and fairly) insecure standard channels.
+ * Those channels do not have a numeric id included in the settings, but instead it is pulled from
+ * a table of well known IDs.
+ * (see Well Known Channels FIXME)
+ */
+ fixed32 id = 4;
+
+ /*
+ * If true, messages on the mesh will be sent to the *public* internet by any gateway ndoe
+ */
+ bool uplink_enabled = 5;
+
+ /*
+ * If true, messages seen on the internet will be forwarded to the local mesh.
+ */
+ bool downlink_enabled = 6;
+
+ /*
+ * Per-channel module settings.
+ */
+ ModuleSettings module_settings = 7;
+}
+
+/*
+ * This message is specifically for modules to store per-channel configuration data.
+ */
+message ModuleSettings {
+ /*
+ * Bits of precision for the location sent in position packets.
+ */
+ uint32 position_precision = 1;
+}
+
+/*
+ * A pair of a channel number, mode and the (sharable) settings for that channel
+ */
+message Channel {
+ /*
+ * How this channel is being used (or not).
+ * Note: this field is an enum to give us options for the future.
+ * In particular, someday we might make a 'SCANNING' option.
+ * SCANNING channels could have different frequencies and the radio would
+ * occasionally check that freq to see if anything is being transmitted.
+ * For devices that have multiple physical radios attached, we could keep multiple PRIMARY/SCANNING channels active at once to allow
+ * cross band routing as needed.
+ * If a device has only a single radio (the common case) only one channel can be PRIMARY at a time
+ * (but any number of SECONDARY channels can't be sent received on that common frequency)
+ */
+ enum Role {
+ /*
+ * This channel is not in use right now
+ */
+ DISABLED = 0;
+
+ /*
+ * This channel is used to set the frequency for the radio - all other enabled channels must be SECONDARY
+ */
+ PRIMARY = 1;
+
+ /*
+ * Secondary channels are only used for encryption/decryption/authentication purposes.
+ * Their radio settings (freq etc) are ignored, only psk is used.
+ */
+ SECONDARY = 2;
+ }
+
+ /*
+ * The index of this channel in the channel table (from 0 to MAX_NUM_CHANNELS-1)
+ * (Someday - not currently implemented) An index of -1 could be used to mean "set by name",
+ * in which case the target node will find and set the channel by settings.name.
+ */
+ int32 index = 1;
+
+ /*
+ * The new settings, or NULL to disable that channel
+ */
+ ChannelSettings settings = 2;
+
+ /*
+ * TODO: REPLACE
+ */
+ Role role = 3;
+}
diff --git a/protobufs/meshtastic/clientonly.proto b/protobufs/meshtastic/clientonly.proto
new file mode 100644
index 0000000..b1a27b1
--- /dev/null
+++ b/protobufs/meshtastic/clientonly.proto
@@ -0,0 +1,42 @@
+syntax = "proto3";
+
+package meshtastic;
+
+import "meshtastic/localonly.proto";
+
+option csharp_namespace = "Meshtastic.Protobufs";
+option go_package = "github.com/meshtastic/go/generated";
+option java_outer_classname = "ClientOnlyProtos";
+option java_package = "com.geeksville.mesh";
+option swift_prefix = "";
+
+/*
+ * This abstraction is used to contain any configuration for provisioning a node on any client.
+ * It is useful for importing and exporting configurations.
+ */
+message DeviceProfile {
+ /*
+ * Long name for the node
+ */
+ optional string long_name = 1;
+
+ /*
+ * Short name of the node
+ */
+ optional string short_name = 2;
+
+ /*
+ * The url of the channels from our node
+ */
+ optional string channel_url = 3;
+
+ /*
+ * The Config of the node
+ */
+ optional LocalConfig config = 4;
+
+ /*
+ * The ModuleConfig of the node
+ */
+ optional LocalModuleConfig module_config = 5;
+}
diff --git a/protobufs/meshtastic/config.proto b/protobufs/meshtastic/config.proto
new file mode 100644
index 0000000..5b93649
--- /dev/null
+++ b/protobufs/meshtastic/config.proto
@@ -0,0 +1,955 @@
+syntax = "proto3";
+
+package meshtastic;
+
+option csharp_namespace = "Meshtastic.Protobufs";
+option go_package = "github.com/meshtastic/go/generated";
+option java_outer_classname = "ConfigProtos";
+option java_package = "com.geeksville.mesh";
+option swift_prefix = "";
+
+message Config {
+ /*
+ * Configuration
+ */
+ message DeviceConfig {
+ /*
+ * Defines the device's role on the Mesh network
+ */
+ enum Role {
+ /*
+ * Description: App connected or stand alone messaging device.
+ * Technical Details: Default Role
+ */
+ CLIENT = 0;
+ /*
+ * Description: Device that does not forward packets from other devices.
+ */
+ CLIENT_MUTE = 1;
+
+ /*
+ * Description: Infrastructure node for extending network coverage by relaying messages. Visible in Nodes list.
+ * Technical Details: Mesh packets will prefer to be routed over this node. This node will not be used by client apps.
+ * The wifi radio and the oled screen will be put to sleep.
+ * This mode may still potentially have higher power usage due to it's preference in message rebroadcasting on the mesh.
+ */
+ ROUTER = 2;
+
+ /*
+ * Description: Combination of both ROUTER and CLIENT. Not for mobile devices.
+ */
+ ROUTER_CLIENT = 3;
+
+ /*
+ * Description: Infrastructure node for extending network coverage by relaying messages with minimal overhead. Not visible in Nodes list.
+ * Technical Details: Mesh packets will simply be rebroadcasted over this node. Nodes configured with this role will not originate NodeInfo, Position, Telemetry
+ * or any other packet type. They will simply rebroadcast any mesh packets on the same frequency, channel num, spread factor, and coding rate.
+ */
+ REPEATER = 4;
+
+ /*
+ * Description: Broadcasts GPS position packets as priority.
+ * Technical Details: Position Mesh packets will be prioritized higher and sent more frequently by default.
+ * When used in conjunction with power.is_power_saving = true, nodes will wake up,
+ * send position, and then sleep for position.position_broadcast_secs seconds.
+ */
+ TRACKER = 5;
+
+ /*
+ * Description: Broadcasts telemetry packets as priority.
+ * Technical Details: Telemetry Mesh packets will be prioritized higher and sent more frequently by default.
+ * When used in conjunction with power.is_power_saving = true, nodes will wake up,
+ * send environment telemetry, and then sleep for telemetry.environment_update_interval seconds.
+ */
+ SENSOR = 6;
+
+ /*
+ * Description: Optimized for ATAK system communication and reduces routine broadcasts.
+ * Technical Details: Used for nodes dedicated for connection to an ATAK EUD.
+ * Turns off many of the routine broadcasts to favor CoT packet stream
+ * from the Meshtastic ATAK plugin -> IMeshService -> Node
+ */
+ TAK = 7;
+
+ /*
+ * Description: Device that only broadcasts as needed for stealth or power savings.
+ * Technical Details: Used for nodes that "only speak when spoken to"
+ * Turns all of the routine broadcasts but allows for ad-hoc communication
+ * Still rebroadcasts, but with local only rebroadcast mode (known meshes only)
+ * Can be used for clandestine operation or to dramatically reduce airtime / power consumption
+ */
+ CLIENT_HIDDEN = 8;
+
+ /*
+ * Description: Broadcasts location as message to default channel regularly for to assist with device recovery.
+ * Technical Details: Used to automatically send a text message to the mesh
+ * with the current position of the device on a frequent interval:
+ * "I'm lost! Position: lat / long"
+ */
+ LOST_AND_FOUND = 9;
+
+ /*
+ * Description: Enables automatic TAK PLI broadcasts and reduces routine broadcasts.
+ * Technical Details: Turns off many of the routine broadcasts to favor ATAK CoT packet stream
+ * and automatic TAK PLI (position location information) broadcasts.
+ * Uses position module configuration to determine TAK PLI broadcast interval.
+ */
+ TAK_TRACKER = 10;
+ }
+
+ /*
+ * Defines the device's behavior for how messages are rebroadcast
+ */
+ enum RebroadcastMode {
+ /*
+ * Default behavior.
+ * Rebroadcast any observed message, if it was on our private channel or from another mesh with the same lora params.
+ */
+ ALL = 0;
+
+ /*
+ * Same as behavior as ALL but skips packet decoding and simply rebroadcasts them.
+ * Only available in Repeater role. Setting this on any other roles will result in ALL behavior.
+ */
+ ALL_SKIP_DECODING = 1;
+
+ /*
+ * Ignores observed messages from foreign meshes that are open or those which it cannot decrypt.
+ * Only rebroadcasts message on the nodes local primary / secondary channels.
+ */
+ LOCAL_ONLY = 2;
+
+ /*
+ * Ignores observed messages from foreign meshes like LOCAL_ONLY,
+ * but takes it step further by also ignoring messages from nodenums not in the node's known list (NodeDB)
+ */
+ KNOWN_ONLY = 3;
+ }
+
+ /*
+ * Sets the role of node
+ */
+ Role role = 1;
+
+ /*
+ * Disabling this will disable the SerialConsole by not initilizing the StreamAPI
+ */
+ bool serial_enabled = 2;
+
+ /*
+ * By default we turn off logging as soon as an API client connects (to keep shared serial link quiet).
+ * Set this to true to leave the debug log outputting even when API is active.
+ */
+ bool debug_log_enabled = 3;
+
+ /*
+ * For boards without a hard wired button, this is the pin number that will be used
+ * Boards that have more than one button can swap the function with this one. defaults to BUTTON_PIN if defined.
+ */
+ uint32 button_gpio = 4;
+
+ /*
+ * For boards without a PWM buzzer, this is the pin number that will be used
+ * Defaults to PIN_BUZZER if defined.
+ */
+ uint32 buzzer_gpio = 5;
+
+ /*
+ * Sets the role of node
+ */
+ RebroadcastMode rebroadcast_mode = 6;
+
+ /*
+ * Send our nodeinfo this often
+ * Defaults to 900 Seconds (15 minutes)
+ */
+ uint32 node_info_broadcast_secs = 7;
+
+ /*
+ * Treat double tap interrupt on supported accelerometers as a button press if set to true
+ */
+ bool double_tap_as_button_press = 8;
+
+ /*
+ * If true, device is considered to be "managed" by a mesh administrator
+ * Clients should then limit available configuration and administrative options inside the user interface
+ */
+ bool is_managed = 9;
+
+ /*
+ * Disables the triple-press of user button to enable or disable GPS
+ */
+ bool disable_triple_click = 10;
+ }
+
+ /*
+ * Position Config
+ */
+ message PositionConfig {
+ /*
+ * Bit field of boolean configuration options, indicating which optional
+ * fields to include when assembling POSITION messages.
+ * Longitude, latitude, altitude, speed, heading, and DOP
+ * are always included (also time if GPS-synced)
+ * NOTE: the more fields are included, the larger the message will be -
+ * leading to longer airtime and a higher risk of packet loss
+ */
+ enum PositionFlags {
+ /*
+ * Required for compilation
+ */
+ UNSET = 0x0000;
+
+ /*
+ * Include an altitude value (if available)
+ */
+ ALTITUDE = 0x0001;
+
+ /*
+ * Altitude value is MSL
+ */
+ ALTITUDE_MSL = 0x0002;
+
+ /*
+ * Include geoidal separation
+ */
+ GEOIDAL_SEPARATION = 0x0004;
+
+ /*
+ * Include the DOP value ; PDOP used by default, see below
+ */
+ DOP = 0x0008;
+
+ /*
+ * If POS_DOP set, send separate HDOP / VDOP values instead of PDOP
+ */
+ HVDOP = 0x0010;
+
+ /*
+ * Include number of "satellites in view"
+ */
+ SATINVIEW = 0x0020;
+
+ /*
+ * Include a sequence number incremented per packet
+ */
+ SEQ_NO = 0x0040;
+
+ /*
+ * Include positional timestamp (from GPS solution)
+ */
+ TIMESTAMP = 0x0080;
+
+ /*
+ * Include positional heading
+ * Intended for use with vehicle not walking speeds
+ * walking speeds are likely to be error prone like the compass
+ */
+ HEADING = 0x0100;
+
+ /*
+ * Include positional speed
+ * Intended for use with vehicle not walking speeds
+ * walking speeds are likely to be error prone like the compass
+ */
+ SPEED = 0x0200;
+ }
+
+ enum GpsMode {
+ /*
+ * GPS is present but disabled
+ */
+ DISABLED = 0;
+
+ /*
+ * GPS is present and enabled
+ */
+ ENABLED = 1;
+
+ /*
+ * GPS is not present on the device
+ */
+ NOT_PRESENT = 2;
+ }
+
+ /*
+ * We should send our position this often (but only if it has changed significantly)
+ * Defaults to 15 minutes
+ */
+ uint32 position_broadcast_secs = 1;
+
+ /*
+ * Adaptive position braoadcast, which is now the default.
+ */
+ bool position_broadcast_smart_enabled = 2;
+
+ /*
+ * If set, this node is at a fixed position.
+ * We will generate GPS position updates at the regular interval, but use whatever the last lat/lon/alt we have for the node.
+ * The lat/lon/alt can be set by an internal GPS or with the help of the app.
+ */
+ bool fixed_position = 3;
+
+ /*
+ * Is GPS enabled for this node?
+ */
+ bool gps_enabled = 4 [deprecated = true];
+
+ /*
+ * How often should we try to get GPS position (in seconds)
+ * or zero for the default of once every 30 seconds
+ * or a very large value (maxint) to update only once at boot.
+ */
+ uint32 gps_update_interval = 5;
+
+ /*
+ * Deprecated in favor of using smart / regular broadcast intervals as implicit attempt time
+ */
+ uint32 gps_attempt_time = 6 [deprecated = true];
+
+ /*
+ * Bit field of boolean configuration options for POSITION messages
+ * (bitwise OR of PositionFlags)
+ */
+ uint32 position_flags = 7;
+
+ /*
+ * (Re)define GPS_RX_PIN for your board.
+ */
+ uint32 rx_gpio = 8;
+
+ /*
+ * (Re)define GPS_TX_PIN for your board.
+ */
+ uint32 tx_gpio = 9;
+
+ /*
+ * The minimum distance in meters traveled (since the last send) before we can send a position to the mesh if position_broadcast_smart_enabled
+ */
+ uint32 broadcast_smart_minimum_distance = 10;
+
+ /*
+ * The minimum number of seconds (since the last send) before we can send a position to the mesh if position_broadcast_smart_enabled
+ */
+ uint32 broadcast_smart_minimum_interval_secs = 11;
+
+ /*
+ * (Re)define PIN_GPS_EN for your board.
+ */
+ uint32 gps_en_gpio = 12;
+
+ /*
+ * Set where GPS is enabled, disabled, or not present
+ */
+ GpsMode gps_mode = 13;
+ }
+
+ /*
+ * Power Config\
+ * See [Power Config](/docs/settings/config/power) for additional power config details.
+ */
+ message PowerConfig {
+ /*
+ * If set, we are powered from a low-current source (i.e. solar), so even if it looks like we have power flowing in
+ * we should try to minimize power consumption as much as possible.
+ * YOU DO NOT NEED TO SET THIS IF YOU'VE set is_router (it is implied in that case).
+ * Advanced Option
+ */
+ bool is_power_saving = 1;
+
+ /*
+ * If non-zero, the device will fully power off this many seconds after external power is removed.
+ */
+ uint32 on_battery_shutdown_after_secs = 2;
+
+ /*
+ * Ratio of voltage divider for battery pin eg. 3.20 (R1=100k, R2=220k)
+ * Overrides the ADC_MULTIPLIER defined in variant for battery voltage calculation.
+ * Should be set to floating point value between 2 and 4
+ * Fixes issues on Heltec v2
+ */
+ float adc_multiplier_override = 3;
+
+ /*
+ * Wait Bluetooth Seconds
+ * The number of seconds for to wait before turning off BLE in No Bluetooth states
+ * 0 for default of 1 minute
+ */
+ uint32 wait_bluetooth_secs = 4;
+
+ /*
+ * Super Deep Sleep Seconds
+ * While in Light Sleep if mesh_sds_timeout_secs is exceeded we will lower into super deep sleep
+ * for this value (default 1 year) or a button press
+ * 0 for default of one year
+ */
+ uint32 sds_secs = 6;
+
+ /*
+ * Light Sleep Seconds
+ * In light sleep the CPU is suspended, LoRa radio is on, BLE is off an GPS is on
+ * ESP32 Only
+ * 0 for default of 300
+ */
+ uint32 ls_secs = 7;
+
+ /*
+ * Minimum Wake Seconds
+ * While in light sleep when we receive packets on the LoRa radio we will wake and handle them and stay awake in no BLE mode for this value
+ * 0 for default of 10 seconds
+ */
+ uint32 min_wake_secs = 8;
+
+ /*
+ * I2C address of INA_2XX to use for reading device battery voltage
+ */
+ uint32 device_battery_ina_address = 9;
+ }
+
+ /*
+ * Network Config
+ */
+ message NetworkConfig {
+ enum AddressMode {
+ /*
+ * obtain ip address via DHCP
+ */
+ DHCP = 0;
+
+ /*
+ * use static ip address
+ */
+ STATIC = 1;
+ }
+
+ message IpV4Config {
+ /*
+ * Static IP address
+ */
+ fixed32 ip = 1;
+
+ /*
+ * Static gateway address
+ */
+ fixed32 gateway = 2;
+
+ /*
+ * Static subnet mask
+ */
+ fixed32 subnet = 3;
+
+ /*
+ * Static DNS server address
+ */
+ fixed32 dns = 4;
+ }
+
+ /*
+ * Enable WiFi (disables Bluetooth)
+ */
+ bool wifi_enabled = 1;
+
+ /*
+ * If set, this node will try to join the specified wifi network and
+ * acquire an address via DHCP
+ */
+ string wifi_ssid = 3;
+
+ /*
+ * If set, will be use to authenticate to the named wifi
+ */
+ string wifi_psk = 4;
+
+ /*
+ * NTP server to use if WiFi is conneced, defaults to `0.pool.ntp.org`
+ */
+ string ntp_server = 5;
+
+ /*
+ * Enable Ethernet
+ */
+ bool eth_enabled = 6;
+
+ /*
+ * acquire an address via DHCP or assign static
+ */
+ AddressMode address_mode = 7;
+
+ /*
+ * struct to keep static address
+ */
+ IpV4Config ipv4_config = 8;
+
+ /*
+ * rsyslog Server and Port
+ */
+ string rsyslog_server = 9;
+ }
+
+ /*
+ * Display Config
+ */
+ message DisplayConfig {
+ /*
+ * How the GPS coordinates are displayed on the OLED screen.
+ */
+ enum GpsCoordinateFormat {
+ /*
+ * GPS coordinates are displayed in the normal decimal degrees format:
+ * DD.DDDDDD DDD.DDDDDD
+ */
+ DEC = 0;
+
+ /*
+ * GPS coordinates are displayed in the degrees minutes seconds format:
+ * DD°MM'SS"C DDD°MM'SS"C, where C is the compass point representing the locations quadrant
+ */
+ DMS = 1;
+
+ /*
+ * Universal Transverse Mercator format:
+ * ZZB EEEEEE NNNNNNN, where Z is zone, B is band, E is easting, N is northing
+ */
+ UTM = 2;
+
+ /*
+ * Military Grid Reference System format:
+ * ZZB CD EEEEE NNNNN, where Z is zone, B is band, C is the east 100k square, D is the north 100k square,
+ * E is easting, N is northing
+ */
+ MGRS = 3;
+
+ /*
+ * Open Location Code (aka Plus Codes).
+ */
+ OLC = 4;
+
+ /*
+ * Ordnance Survey Grid Reference (the National Grid System of the UK).
+ * Format: AB EEEEE NNNNN, where A is the east 100k square, B is the north 100k square,
+ * E is the easting, N is the northing
+ */
+ OSGR = 5;
+ }
+
+ /*
+ * Unit display preference
+ */
+ enum DisplayUnits {
+ /*
+ * Metric (Default)
+ */
+ METRIC = 0;
+
+ /*
+ * Imperial
+ */
+ IMPERIAL = 1;
+ }
+
+ /*
+ * Override OLED outo detect with this if it fails.
+ */
+ enum OledType {
+ /*
+ * Default / Auto
+ */
+ OLED_AUTO = 0;
+
+ /*
+ * Default / Auto
+ */
+ OLED_SSD1306 = 1;
+
+ /*
+ * Default / Auto
+ */
+ OLED_SH1106 = 2;
+
+ /*
+ * Can not be auto detected but set by proto. Used for 128x128 screens
+ */
+ OLED_SH1107 = 3;
+ }
+
+ /*
+ * Number of seconds the screen stays on after pressing the user button or receiving a message
+ * 0 for default of one minute MAXUINT for always on
+ */
+ uint32 screen_on_secs = 1;
+
+ /*
+ * How the GPS coordinates are formatted on the OLED screen.
+ */
+ GpsCoordinateFormat gps_format = 2;
+
+ /*
+ * Automatically toggles to the next page on the screen like a carousel, based the specified interval in seconds.
+ * Potentially useful for devices without user buttons.
+ */
+ uint32 auto_screen_carousel_secs = 3;
+
+ /*
+ * If this is set, the displayed compass will always point north. if unset, the old behaviour
+ * (top of display is heading direction) is used.
+ */
+ bool compass_north_top = 4;
+
+ /*
+ * Flip screen vertically, for cases that mount the screen upside down
+ */
+ bool flip_screen = 5;
+
+ /*
+ * Perferred display units
+ */
+ DisplayUnits units = 6;
+
+ /*
+ * Override auto-detect in screen
+ */
+ OledType oled = 7;
+
+ enum DisplayMode {
+ /*
+ * Default. The old style for the 128x64 OLED screen
+ */
+ DEFAULT = 0;
+
+ /*
+ * Rearrange display elements to cater for bicolor OLED displays
+ */
+ TWOCOLOR = 1;
+
+ /*
+ * Same as TwoColor, but with inverted top bar. Not so good for Epaper displays
+ */
+ INVERTED = 2;
+
+ /*
+ * TFT Full Color Displays (not implemented yet)
+ */
+ COLOR = 3;
+ }
+ /*
+ * Display Mode
+ */
+ DisplayMode displaymode = 8;
+
+ /*
+ * Print first line in pseudo-bold? FALSE is original style, TRUE is bold
+ */
+ bool heading_bold = 9;
+
+ /*
+ * Should we wake the screen up on accelerometer detected motion or tap
+ */
+ bool wake_on_tap_or_motion = 10;
+ }
+
+ /*
+ * Lora Config
+ */
+ message LoRaConfig {
+ enum RegionCode {
+ /*
+ * Region is not set
+ */
+ UNSET = 0;
+
+ /*
+ * United States
+ */
+ US = 1;
+
+ /*
+ * European Union 433mhz
+ */
+ EU_433 = 2;
+
+ /*
+ * European Union 868mhz
+ */
+ EU_868 = 3;
+
+ /*
+ * China
+ */
+ CN = 4;
+
+ /*
+ * Japan
+ */
+ JP = 5;
+
+ /*
+ * Australia / New Zealand
+ */
+ ANZ = 6;
+
+ /*
+ * Korea
+ */
+ KR = 7;
+
+ /*
+ * Taiwan
+ */
+ TW = 8;
+
+ /*
+ * Russia
+ */
+ RU = 9;
+
+ /*
+ * India
+ */
+ IN = 10;
+
+ /*
+ * New Zealand 865mhz
+ */
+ NZ_865 = 11;
+
+ /*
+ * Thailand
+ */
+ TH = 12;
+
+ /*
+ * WLAN Band
+ */
+ LORA_24 = 13;
+
+ /*
+ * Ukraine 433mhz
+ */
+ UA_433 = 14;
+
+ /*
+ * Ukraine 868mhz
+ */
+ UA_868 = 15;
+
+ /*
+ * Malaysia 433mhz
+ */
+ MY_433 = 16;
+
+ /*
+ * Malaysia 919mhz
+ */
+ MY_919 = 17;
+
+ /*
+ * Singapore 923mhz
+ */
+ SG_923 = 18;
+ }
+
+ /*
+ * Standard predefined channel settings
+ * Note: these mappings must match ModemPreset Choice in the device code.
+ */
+ enum ModemPreset {
+ /*
+ * Long Range - Fast
+ */
+ LONG_FAST = 0;
+
+ /*
+ * Long Range - Slow
+ */
+ LONG_SLOW = 1;
+
+ /*
+ * Very Long Range - Slow
+ */
+ VERY_LONG_SLOW = 2;
+
+ /*
+ * Medium Range - Slow
+ */
+ MEDIUM_SLOW = 3;
+
+ /*
+ * Medium Range - Fast
+ */
+ MEDIUM_FAST = 4;
+
+ /*
+ * Short Range - Slow
+ */
+ SHORT_SLOW = 5;
+
+ /*
+ * Short Range - Fast
+ */
+ SHORT_FAST = 6;
+
+ /*
+ * Long Range - Moderately Fast
+ */
+ LONG_MODERATE = 7;
+ }
+
+ /*
+ * When enabled, the `modem_preset` fields will be adhered to, else the `bandwidth`/`spread_factor`/`coding_rate`
+ * will be taked from their respective manually defined fields
+ */
+ bool use_preset = 1;
+
+ /*
+ * Either modem_config or bandwidth/spreading/coding will be specified - NOT BOTH.
+ * As a heuristic: If bandwidth is specified, do not use modem_config.
+ * Because protobufs take ZERO space when the value is zero this works out nicely.
+ * This value is replaced by bandwidth/spread_factor/coding_rate.
+ * If you'd like to experiment with other options add them to MeshRadio.cpp in the device code.
+ */
+ ModemPreset modem_preset = 2;
+
+ /*
+ * Bandwidth in MHz
+ * Certain bandwidth numbers are 'special' and will be converted to the
+ * appropriate floating point value: 31 -> 31.25MHz
+ */
+ uint32 bandwidth = 3;
+
+ /*
+ * A number from 7 to 12.
+ * Indicates number of chirps per symbol as 1< 7 results in the default
+ */
+ uint32 hop_limit = 8;
+
+ /*
+ * Disable TX from the LoRa radio. Useful for hot-swapping antennas and other tests.
+ * Defaults to false
+ */
+ bool tx_enabled = 9;
+
+ /*
+ * If zero, then use default max legal continuous power (ie. something that won't
+ * burn out the radio hardware)
+ * In most cases you should use zero here.
+ * Units are in dBm.
+ */
+ int32 tx_power = 10;
+
+ /*
+ * This controls the actual hardware frequency the radio transmits on.
+ * Most users should never need to be exposed to this field/concept.
+ * A channel number between 1 and NUM_CHANNELS (whatever the max is in the current region).
+ * If ZERO then the rule is "use the old channel name hash based
+ * algorithm to derive the channel number")
+ * If using the hash algorithm the channel number will be: hash(channel_name) %
+ * NUM_CHANNELS (Where num channels depends on the regulatory region).
+ */
+ uint32 channel_num = 11;
+
+ /*
+ * If true, duty cycle limits will be exceeded and thus you're possibly not following
+ * the local regulations if you're not a HAM.
+ * Has no effect if the duty cycle of the used region is 100%.
+ */
+ bool override_duty_cycle = 12;
+
+ /*
+ * If true, sets RX boosted gain mode on SX126X based radios
+ */
+ bool sx126x_rx_boosted_gain = 13;
+
+ /*
+ * This parameter is for advanced users and licensed HAM radio operators.
+ * Ignore Channel Calculation and use this frequency instead. The frequency_offset
+ * will still be applied. This will allow you to use out-of-band frequencies.
+ * Please respect your local laws and regulations. If you are a HAM, make sure you
+ * enable HAM mode and turn off encryption.
+ */
+ float override_frequency = 14;
+
+ /*
+ * For testing it is useful sometimes to force a node to never listen to
+ * particular other nodes (simulating radio out of range). All nodenums listed
+ * in ignore_incoming will have packets they send dropped on receive (by router.cpp)
+ */
+ repeated uint32 ignore_incoming = 103;
+
+ /*
+ * If true, the device will not process any packets received via LoRa that passed via MQTT anywhere on the path towards it.
+ */
+ bool ignore_mqtt = 104;
+ }
+
+ message BluetoothConfig {
+ enum PairingMode {
+ /*
+ * Device generates a random PIN that will be shown on the screen of the device for pairing
+ */
+ RANDOM_PIN = 0;
+
+ /*
+ * Device requires a specified fixed PIN for pairing
+ */
+ FIXED_PIN = 1;
+
+ /*
+ * Device requires no PIN for pairing
+ */
+ NO_PIN = 2;
+ }
+
+ /*
+ * Enable Bluetooth on the device
+ */
+ bool enabled = 1;
+
+ /*
+ * Determines the pairing strategy for the device
+ */
+ PairingMode mode = 2;
+
+ /*
+ * Specified PIN for PairingMode.FixedPin
+ */
+ uint32 fixed_pin = 3;
+ }
+
+ /*
+ * Payload Variant
+ */
+ oneof payload_variant {
+ DeviceConfig device = 1;
+ PositionConfig position = 2;
+ PowerConfig power = 3;
+ NetworkConfig network = 4;
+ DisplayConfig display = 5;
+ LoRaConfig lora = 6;
+ BluetoothConfig bluetooth = 7;
+ }
+}
diff --git a/protobufs/meshtastic/connection_status.proto b/protobufs/meshtastic/connection_status.proto
new file mode 100644
index 0000000..7551596
--- /dev/null
+++ b/protobufs/meshtastic/connection_status.proto
@@ -0,0 +1,120 @@
+syntax = "proto3";
+
+package meshtastic;
+
+option csharp_namespace = "Meshtastic.Protobufs";
+option go_package = "github.com/meshtastic/go/generated";
+option java_outer_classname = "ConnStatusProtos";
+option java_package = "com.geeksville.mesh";
+option swift_prefix = "";
+
+message DeviceConnectionStatus {
+ /*
+ * WiFi Status
+ */
+ optional WifiConnectionStatus wifi = 1;
+ /*
+ * WiFi Status
+ */
+ optional EthernetConnectionStatus ethernet = 2;
+
+ /*
+ * Bluetooth Status
+ */
+ optional BluetoothConnectionStatus bluetooth = 3;
+
+ /*
+ * Serial Status
+ */
+ optional SerialConnectionStatus serial = 4;
+}
+
+/*
+ * WiFi connection status
+ */
+message WifiConnectionStatus {
+ /*
+ * Connection status
+ */
+ NetworkConnectionStatus status = 1;
+
+ /*
+ * WiFi access point SSID
+ */
+ string ssid = 2;
+
+ /*
+ * RSSI of wireless connection
+ */
+ int32 rssi = 3;
+}
+
+/*
+ * Ethernet connection status
+ */
+message EthernetConnectionStatus {
+ /*
+ * Connection status
+ */
+ NetworkConnectionStatus status = 1;
+}
+
+/*
+ * Ethernet or WiFi connection status
+ */
+message NetworkConnectionStatus {
+ /*
+ * IP address of device
+ */
+ fixed32 ip_address = 1;
+
+ /*
+ * Whether the device has an active connection or not
+ */
+ bool is_connected = 2;
+
+ /*
+ * Whether the device has an active connection to an MQTT broker or not
+ */
+ bool is_mqtt_connected = 3;
+
+ /*
+ * Whether the device is actively remote syslogging or not
+ */
+ bool is_syslog_connected = 4;
+}
+
+/*
+ * Bluetooth connection status
+ */
+message BluetoothConnectionStatus {
+ /*
+ * The pairing PIN for bluetooth
+ */
+ uint32 pin = 1;
+
+ /*
+ * RSSI of bluetooth connection
+ */
+ int32 rssi = 2;
+
+ /*
+ * Whether the device has an active connection or not
+ */
+ bool is_connected = 3;
+}
+
+/*
+ * Serial connection status
+ */
+message SerialConnectionStatus {
+ /*
+ * Serial baud rate
+ */
+ uint32 baud = 1;
+
+ /*
+ * Whether the device has an active connection or not
+ */
+ bool is_connected = 2;
+}
diff --git a/protobufs/meshtastic/deviceonly.proto b/protobufs/meshtastic/deviceonly.proto
new file mode 100644
index 0000000..2929559
--- /dev/null
+++ b/protobufs/meshtastic/deviceonly.proto
@@ -0,0 +1,256 @@
+syntax = "proto3";
+
+package meshtastic;
+
+import "meshtastic/channel.proto";
+import "meshtastic/localonly.proto";
+import "meshtastic/mesh.proto";
+import "meshtastic/module_config.proto";
+import "meshtastic/telemetry.proto";
+import "nanopb.proto";
+
+option csharp_namespace = "Meshtastic.Protobufs";
+option go_package = "github.com/meshtastic/go/generated";
+option java_outer_classname = "DeviceOnly";
+option java_package = "com.geeksville.mesh";
+option swift_prefix = "";
+option (nanopb_fileopt).include = "";
+
+/*
+ * This message is never sent over the wire, but it is used for serializing DB
+ * state to flash in the device code
+ * FIXME, since we write this each time we enter deep sleep (and have infinite
+ * flash) it would be better to use some sort of append only data structure for
+ * the receive queue and use the preferences store for the other stuff
+ */
+message DeviceState {
+ /*
+ * Read only settings/info about this node
+ */
+ MyNodeInfo my_node = 2;
+
+ /*
+ * My owner info
+ */
+ User owner = 3;
+
+ /*
+ * Received packets saved for delivery to the phone
+ */
+ repeated MeshPacket receive_queue = 5;
+
+ /*
+ * A version integer used to invalidate old save files when we make
+ * incompatible changes This integer is set at build time and is private to
+ * NodeDB.cpp in the device code.
+ */
+ uint32 version = 8;
+
+ /*
+ * We keep the last received text message (only) stored in the device flash,
+ * so we can show it on the screen.
+ * Might be null
+ */
+ MeshPacket rx_text_message = 7;
+
+ /*
+ * Used only during development.
+ * Indicates developer is testing and changes should never be saved to flash.
+ * Deprecated in 2.3.1
+ */
+ bool no_save = 9 [deprecated = true];
+
+ /*
+ * Some GPS receivers seem to have bogus settings from the factory, so we always do one factory reset.
+ */
+ bool did_gps_reset = 11;
+
+ /*
+ * We keep the last received waypoint stored in the device flash,
+ * so we can show it on the screen.
+ * Might be null
+ */
+ MeshPacket rx_waypoint = 12;
+
+ /*
+ * The mesh's nodes with their available gpio pins for RemoteHardware module
+ */
+ repeated NodeRemoteHardwarePin node_remote_hardware_pins = 13;
+
+ /*
+ * New lite version of NodeDB to decrease memory footprint
+ */
+ repeated NodeInfoLite node_db_lite = 14 [(nanopb).callback_datatype = "std::vector"];
+}
+
+message NodeInfoLite {
+ /*
+ * The node number
+ */
+ uint32 num = 1;
+
+ /*
+ * The user info for this node
+ */
+ User user = 2;
+
+ /*
+ * This position data. Note: before 1.2.14 we would also store the last time we've heard from this node in position.time, that is no longer true.
+ * Position.time now indicates the last time we received a POSITION from that node.
+ */
+ PositionLite position = 3;
+
+ /*
+ * Returns the Signal-to-noise ratio (SNR) of the last received message,
+ * as measured by the receiver. Return SNR of the last received message in dB
+ */
+ float snr = 4;
+
+ /*
+ * Set to indicate the last time we received a packet from this node
+ */
+ fixed32 last_heard = 5;
+ /*
+ * The latest device metrics for the node.
+ */
+ DeviceMetrics device_metrics = 6;
+
+ /*
+ * local channel index we heard that node on. Only populated if its not the default channel.
+ */
+ uint32 channel = 7;
+
+ /*
+ * True if we witnessed the node over MQTT instead of LoRA transport
+ */
+ bool via_mqtt = 8;
+
+ /*
+ * Number of hops away from us this node is (0 if adjacent)
+ */
+ uint32 hops_away = 9;
+
+ /*
+ * True if node is in our favorites list
+ * Persists between NodeDB internal clean ups
+ */
+ bool is_favorite = 10;
+}
+
+/*
+ * Position with static location information only for NodeDBLite
+ */
+message PositionLite {
+ /*
+ * The new preferred location encoding, multiply by 1e-7 to get degrees
+ * in floating point
+ */
+ sfixed32 latitude_i = 1;
+
+ /*
+ * TODO: REPLACE
+ */
+ sfixed32 longitude_i = 2;
+
+ /*
+ * In meters above MSL (but see issue #359)
+ */
+ int32 altitude = 3;
+
+ /*
+ * This is usually not sent over the mesh (to save space), but it is sent
+ * from the phone so that the local device can set its RTC If it is sent over
+ * the mesh (because there are devices on the mesh without GPS), it will only
+ * be sent by devices which has a hardware GPS clock.
+ * seconds since 1970
+ */
+ fixed32 time = 4;
+
+ /*
+ * TODO: REPLACE
+ */
+ Position.LocSource location_source = 5;
+}
+
+/*
+ * The on-disk saved channels
+ */
+message ChannelFile {
+ /*
+ * The channels our node knows about
+ */
+ repeated Channel channels = 1;
+
+ /*
+ * A version integer used to invalidate old save files when we make
+ * incompatible changes This integer is set at build time and is private to
+ * NodeDB.cpp in the device code.
+ */
+ uint32 version = 2;
+}
+
+/*
+ * TODO: REPLACE
+ */
+enum ScreenFonts {
+ /*
+ * TODO: REPLACE
+ */
+ FONT_SMALL = 0;
+
+ /*
+ * TODO: REPLACE
+ */
+ FONT_MEDIUM = 1;
+
+ /*
+ * TODO: REPLACE
+ */
+ FONT_LARGE = 2;
+}
+
+/*
+ * This can be used for customizing the firmware distribution. If populated,
+ * show a secondary bootup screen with custom logo and text for 2.5 seconds.
+ */
+message OEMStore {
+ /*
+ * The Logo width in Px
+ */
+ uint32 oem_icon_width = 1;
+
+ /*
+ * The Logo height in Px
+ */
+ uint32 oem_icon_height = 2;
+
+ /*
+ * The Logo in XBM bytechar format
+ */
+ bytes oem_icon_bits = 3;
+
+ /*
+ * Use this font for the OEM text.
+ */
+ ScreenFonts oem_font = 4;
+
+ /*
+ * Use this font for the OEM text.
+ */
+ string oem_text = 5;
+
+ /*
+ * The default device encryption key, 16 or 32 byte
+ */
+ bytes oem_aes_key = 6;
+
+ /*
+ * A Preset LocalConfig to apply during factory reset
+ */
+ LocalConfig oem_local_config = 7;
+
+ /*
+ * A Preset LocalModuleConfig to apply during factory reset
+ */
+ LocalModuleConfig oem_local_module_config = 8;
+}
diff --git a/protobufs/meshtastic/localonly.proto b/protobufs/meshtastic/localonly.proto
new file mode 100644
index 0000000..9694d7b
--- /dev/null
+++ b/protobufs/meshtastic/localonly.proto
@@ -0,0 +1,135 @@
+syntax = "proto3";
+
+package meshtastic;
+
+import "meshtastic/config.proto";
+import "meshtastic/module_config.proto";
+
+option csharp_namespace = "Meshtastic.Protobufs";
+option go_package = "github.com/meshtastic/go/generated";
+option java_outer_classname = "LocalOnlyProtos";
+option java_package = "com.geeksville.mesh";
+option swift_prefix = "";
+
+/*
+ * Protobuf structures common to apponly.proto and deviceonly.proto
+ * This is never sent over the wire, only for local use
+ */
+
+message LocalConfig {
+ /*
+ * The part of the config that is specific to the Device
+ */
+ Config.DeviceConfig device = 1;
+
+ /*
+ * The part of the config that is specific to the GPS Position
+ */
+ Config.PositionConfig position = 2;
+
+ /*
+ * The part of the config that is specific to the Power settings
+ */
+ Config.PowerConfig power = 3;
+
+ /*
+ * The part of the config that is specific to the Wifi Settings
+ */
+ Config.NetworkConfig network = 4;
+
+ /*
+ * The part of the config that is specific to the Display
+ */
+ Config.DisplayConfig display = 5;
+
+ /*
+ * The part of the config that is specific to the Lora Radio
+ */
+ Config.LoRaConfig lora = 6;
+
+ /*
+ * The part of the config that is specific to the Bluetooth settings
+ */
+ Config.BluetoothConfig bluetooth = 7;
+
+ /*
+ * A version integer used to invalidate old save files when we make
+ * incompatible changes This integer is set at build time and is private to
+ * NodeDB.cpp in the device code.
+ */
+ uint32 version = 8;
+}
+
+message LocalModuleConfig {
+ /*
+ * The part of the config that is specific to the MQTT module
+ */
+ ModuleConfig.MQTTConfig mqtt = 1;
+
+ /*
+ * The part of the config that is specific to the Serial module
+ */
+ ModuleConfig.SerialConfig serial = 2;
+
+ /*
+ * The part of the config that is specific to the ExternalNotification module
+ */
+ ModuleConfig.ExternalNotificationConfig external_notification = 3;
+
+ /*
+ * The part of the config that is specific to the Store & Forward module
+ */
+ ModuleConfig.StoreForwardConfig store_forward = 4;
+
+ /*
+ * The part of the config that is specific to the RangeTest module
+ */
+ ModuleConfig.RangeTestConfig range_test = 5;
+
+ /*
+ * The part of the config that is specific to the Telemetry module
+ */
+ ModuleConfig.TelemetryConfig telemetry = 6;
+
+ /*
+ * The part of the config that is specific to the Canned Message module
+ */
+ ModuleConfig.CannedMessageConfig canned_message = 7;
+
+ /*
+ * The part of the config that is specific to the Audio module
+ */
+ ModuleConfig.AudioConfig audio = 9;
+
+ /*
+ * The part of the config that is specific to the Remote Hardware module
+ */
+ ModuleConfig.RemoteHardwareConfig remote_hardware = 10;
+
+ /*
+ * The part of the config that is specific to the Neighbor Info module
+ */
+ ModuleConfig.NeighborInfoConfig neighbor_info = 11;
+
+ /*
+ * The part of the config that is specific to the Ambient Lighting module
+ */
+ ModuleConfig.AmbientLightingConfig ambient_lighting = 12;
+
+ /*
+ * The part of the config that is specific to the Detection Sensor module
+ */
+ ModuleConfig.DetectionSensorConfig detection_sensor = 13;
+
+ /*
+ * Paxcounter Config
+ */
+ ModuleConfig.PaxcounterConfig paxcounter = 14;
+
+ /*
+ * A version integer used to invalidate old save files when we make
+ * incompatible changes This integer is set at build time and is private to
+ * NodeDB.cpp in the device code.
+ */
+ uint32 version = 8;
+}
diff --git a/protobufs/meshtastic/mesh.proto b/protobufs/meshtastic/mesh.proto
new file mode 100644
index 0000000..6d049fa
--- /dev/null
+++ b/protobufs/meshtastic/mesh.proto
@@ -0,0 +1,1596 @@
+syntax = "proto3";
+
+package meshtastic;
+
+import "meshtastic/channel.proto";
+import "meshtastic/config.proto";
+import "meshtastic/module_config.proto";
+import "meshtastic/portnums.proto";
+import "meshtastic/telemetry.proto";
+import "meshtastic/xmodem.proto";
+
+option csharp_namespace = "Meshtastic.Protobufs";
+option go_package = "github.com/meshtastic/go/generated";
+option java_outer_classname = "MeshProtos";
+option java_package = "com.geeksville.mesh";
+option swift_prefix = "";
+
+/*
+ * a gps position
+ */
+message Position {
+ /*
+ * The new preferred location encoding, multiply by 1e-7 to get degrees
+ * in floating point
+ */
+ sfixed32 latitude_i = 1;
+
+ /*
+ * TODO: REPLACE
+ */
+ sfixed32 longitude_i = 2;
+
+ /*
+ * In meters above MSL (but see issue #359)
+ */
+ int32 altitude = 3;
+
+ /*
+ * This is usually not sent over the mesh (to save space), but it is sent
+ * from the phone so that the local device can set its time if it is sent over
+ * the mesh (because there are devices on the mesh without GPS or RTC).
+ * seconds since 1970
+ */
+ fixed32 time = 4;
+
+ /*
+ * How the location was acquired: manual, onboard GPS, external (EUD) GPS
+ */
+ enum LocSource {
+ /*
+ * TODO: REPLACE
+ */
+ LOC_UNSET = 0;
+
+ /*
+ * TODO: REPLACE
+ */
+ LOC_MANUAL = 1;
+
+ /*
+ * TODO: REPLACE
+ */
+ LOC_INTERNAL = 2;
+
+ /*
+ * TODO: REPLACE
+ */
+ LOC_EXTERNAL = 3;
+ }
+
+ /*
+ * TODO: REPLACE
+ */
+ LocSource location_source = 5;
+
+ /*
+ * How the altitude was acquired: manual, GPS int/ext, etc
+ * Default: same as location_source if present
+ */
+ enum AltSource {
+ /*
+ * TODO: REPLACE
+ */
+ ALT_UNSET = 0;
+
+ /*
+ * TODO: REPLACE
+ */
+ ALT_MANUAL = 1;
+
+ /*
+ * TODO: REPLACE
+ */
+ ALT_INTERNAL = 2;
+
+ /*
+ * TODO: REPLACE
+ */
+ ALT_EXTERNAL = 3;
+
+ /*
+ * TODO: REPLACE
+ */
+ ALT_BAROMETRIC = 4;
+ }
+
+ /*
+ * TODO: REPLACE
+ */
+ AltSource altitude_source = 6;
+
+ /*
+ * Positional timestamp (actual timestamp of GPS solution) in integer epoch seconds
+ */
+ fixed32 timestamp = 7;
+
+ /*
+ * Pos. timestamp milliseconds adjustment (rarely available or required)
+ */
+ int32 timestamp_millis_adjust = 8;
+
+ /*
+ * HAE altitude in meters - can be used instead of MSL altitude
+ */
+ sint32 altitude_hae = 9;
+
+ /*
+ * Geoidal separation in meters
+ */
+ sint32 altitude_geoidal_separation = 10;
+
+ /*
+ * Horizontal, Vertical and Position Dilution of Precision, in 1/100 units
+ * - PDOP is sufficient for most cases
+ * - for higher precision scenarios, HDOP and VDOP can be used instead,
+ * in which case PDOP becomes redundant (PDOP=sqrt(HDOP^2 + VDOP^2))
+ * TODO: REMOVE/INTEGRATE
+ */
+ uint32 PDOP = 11;
+
+ /*
+ * TODO: REPLACE
+ */
+ uint32 HDOP = 12;
+
+ /*
+ * TODO: REPLACE
+ */
+ uint32 VDOP = 13;
+
+ /*
+ * GPS accuracy (a hardware specific constant) in mm
+ * multiplied with DOP to calculate positional accuracy
+ * Default: "'bout three meters-ish" :)
+ */
+ uint32 gps_accuracy = 14;
+
+ /*
+ * Ground speed in m/s and True North TRACK in 1/100 degrees
+ * Clarification of terms:
+ * - "track" is the direction of motion (measured in horizontal plane)
+ * - "heading" is where the fuselage points (measured in horizontal plane)
+ * - "yaw" indicates a relative rotation about the vertical axis
+ * TODO: REMOVE/INTEGRATE
+ */
+ uint32 ground_speed = 15;
+
+ /*
+ * TODO: REPLACE
+ */
+ uint32 ground_track = 16;
+
+ /*
+ * GPS fix quality (from NMEA GxGGA statement or similar)
+ */
+ uint32 fix_quality = 17;
+
+ /*
+ * GPS fix type 2D/3D (from NMEA GxGSA statement)
+ */
+ uint32 fix_type = 18;
+
+ /*
+ * GPS "Satellites in View" number
+ */
+ uint32 sats_in_view = 19;
+
+ /*
+ * Sensor ID - in case multiple positioning sensors are being used
+ */
+ uint32 sensor_id = 20;
+
+ /*
+ * Estimated/expected time (in seconds) until next update:
+ * - if we update at fixed intervals of X seconds, use X
+ * - if we update at dynamic intervals (based on relative movement etc),
+ * but "AT LEAST every Y seconds", use Y
+ */
+ uint32 next_update = 21;
+
+ /*
+ * A sequence number, incremented with each Position message to help
+ * detect lost updates if needed
+ */
+ uint32 seq_number = 22;
+
+ /*
+ * Indicates the bits of precision set by the sending node
+ */
+ uint32 precision_bits = 23;
+}
+
+/*
+ * Note: these enum names must EXACTLY match the string used in the device
+ * bin/build-all.sh script.
+ * Because they will be used to find firmware filenames in the android app for OTA updates.
+ * To match the old style filenames, _ is converted to -, p is converted to .
+ */
+enum HardwareModel {
+ /*
+ * TODO: REPLACE
+ */
+ UNSET = 0;
+
+ /*
+ * TODO: REPLACE
+ */
+ TLORA_V2 = 1;
+
+ /*
+ * TODO: REPLACE
+ */
+ TLORA_V1 = 2;
+
+ /*
+ * TODO: REPLACE
+ */
+ TLORA_V2_1_1P6 = 3;
+
+ /*
+ * TODO: REPLACE
+ */
+ TBEAM = 4;
+
+ /*
+ * The original heltec WiFi_Lora_32_V2, which had battery voltage sensing hooked to GPIO 13
+ * (see HELTEC_V2 for the new version).
+ */
+ HELTEC_V2_0 = 5;
+
+ /*
+ * TODO: REPLACE
+ */
+ TBEAM_V0P7 = 6;
+
+ /*
+ * TODO: REPLACE
+ */
+ T_ECHO = 7;
+
+ /*
+ * TODO: REPLACE
+ */
+ TLORA_V1_1P3 = 8;
+
+ /*
+ * TODO: REPLACE
+ */
+ RAK4631 = 9;
+
+ /*
+ * The new version of the heltec WiFi_Lora_32_V2 board that has battery sensing hooked to GPIO 37.
+ * Sadly they did not update anything on the silkscreen to identify this board
+ */
+ HELTEC_V2_1 = 10;
+
+ /*
+ * Ancient heltec WiFi_Lora_32 board
+ */
+ HELTEC_V1 = 11;
+
+ /*
+ * New T-BEAM with ESP32-S3 CPU
+ */
+ LILYGO_TBEAM_S3_CORE = 12;
+
+ /*
+ * RAK WisBlock ESP32 core: https://docs.rakwireless.com/Product-Categories/WisBlock/RAK11200/Overview/
+ */
+ RAK11200 = 13;
+
+ /*
+ * B&Q Consulting Nano Edition G1: https://uniteng.com/wiki/doku.php?id=meshtastic:nano
+ */
+ NANO_G1 = 14;
+
+ /*
+ * TODO: REPLACE
+ */
+ TLORA_V2_1_1P8 = 15;
+
+ /*
+ * TODO: REPLACE
+ */
+ TLORA_T3_S3 = 16;
+
+ /*
+ * B&Q Consulting Nano G1 Explorer: https://wiki.uniteng.com/en/meshtastic/nano-g1-explorer
+ */
+ NANO_G1_EXPLORER = 17;
+
+ /*
+ * B&Q Consulting Nano G2 Ultra: https://wiki.uniteng.com/en/meshtastic/nano-g2-ultra
+ */
+ NANO_G2_ULTRA = 18;
+
+ /*
+ * LoRAType device: https://loratype.org/
+ */
+ LORA_TYPE = 19;
+
+ /*
+ * B&Q Consulting Station Edition G1: https://uniteng.com/wiki/doku.php?id=meshtastic:station
+ */
+ STATION_G1 = 25;
+
+ /*
+ * RAK11310 (RP2040 + SX1262)
+ */
+ RAK11310 = 26;
+
+ /*
+ * Makerfabs SenseLoRA Receiver (RP2040 + RFM96)
+ */
+ SENSELORA_RP2040 = 27;
+
+ /*
+ * Makerfabs SenseLoRA Industrial Monitor (ESP32-S3 + RFM96)
+ */
+ SENSELORA_S3 = 28;
+
+ /*
+ * Canary Radio Company - CanaryOne: https://canaryradio.io/products/canaryone
+ */
+ CANARYONE = 29;
+
+ /*
+ * Waveshare RP2040 LoRa - https://www.waveshare.com/rp2040-lora.htm
+ */
+ RP2040_LORA = 30;
+
+ /*
+ * B&Q Consulting Station G2: https://wiki.uniteng.com/en/meshtastic/station-g2
+ */
+ STATION_G2 = 31;
+
+ /*
+ * ---------------------------------------------------------------------------
+ * Less common/prototype boards listed here (needs one more byte over the air)
+ * ---------------------------------------------------------------------------
+ */
+ LORA_RELAY_V1 = 32;
+
+ /*
+ * TODO: REPLACE
+ */
+ NRF52840DK = 33;
+
+ /*
+ * TODO: REPLACE
+ */
+ PPR = 34;
+
+ /*
+ * TODO: REPLACE
+ */
+ GENIEBLOCKS = 35;
+
+ /*
+ * TODO: REPLACE
+ */
+ NRF52_UNKNOWN = 36;
+
+ /*
+ * TODO: REPLACE
+ */
+ PORTDUINO = 37;
+
+ /*
+ * The simulator built into the android app
+ */
+ ANDROID_SIM = 38;
+
+ /*
+ * Custom DIY device based on @NanoVHF schematics: https://github.com/NanoVHF/Meshtastic-DIY/tree/main/Schematics
+ */
+ DIY_V1 = 39;
+
+ /*
+ * nRF52840 Dongle : https://www.nordicsemi.com/Products/Development-hardware/nrf52840-dongle/
+ */
+ NRF52840_PCA10059 = 40;
+
+ /*
+ * Custom Disaster Radio esp32 v3 device https://github.com/sudomesh/disaster-radio/tree/master/hardware/board_esp32_v3
+ */
+ DR_DEV = 41;
+
+ /*
+ * M5 esp32 based MCU modules with enclosure, TFT and LORA Shields. All Variants (Basic, Core, Fire, Core2, Paper) https://m5stack.com/
+ */
+ M5STACK = 42;
+
+ /*
+ * New Heltec LoRA32 with ESP32-S3 CPU
+ */
+ HELTEC_V3 = 43;
+
+ /*
+ * New Heltec Wireless Stick Lite with ESP32-S3 CPU
+ */
+ HELTEC_WSL_V3 = 44;
+
+ /*
+ * New BETAFPV ELRS Micro TX Module 2.4G with ESP32 CPU
+ */
+ BETAFPV_2400_TX = 45;
+
+ /*
+ * BetaFPV ExpressLRS "Nano" TX Module 900MHz with ESP32 CPU
+ */
+ BETAFPV_900_NANO_TX = 46;
+
+ /*
+ * Raspberry Pi Pico (W) with Waveshare SX1262 LoRa Node Module
+ */
+ RPI_PICO = 47;
+
+ /*
+ * Heltec Wireless Tracker with ESP32-S3 CPU, built-in GPS, and TFT
+ * Newer V1.1, version is written on the PCB near the display.
+ */
+ HELTEC_WIRELESS_TRACKER = 48;
+
+ /*
+ * Heltec Wireless Paper with ESP32-S3 CPU and E-Ink display
+ */
+ HELTEC_WIRELESS_PAPER = 49;
+
+ /*
+ * LilyGo T-Deck with ESP32-S3 CPU, Keyboard and IPS display
+ */
+ T_DECK = 50;
+
+ /*
+ * LilyGo T-Watch S3 with ESP32-S3 CPU and IPS display
+ */
+ T_WATCH_S3 = 51;
+
+ /*
+ * Bobricius Picomputer with ESP32-S3 CPU, Keyboard and IPS display
+ */
+ PICOMPUTER_S3 = 52;
+
+ /*
+ * Heltec HT-CT62 with ESP32-C3 CPU and SX1262 LoRa
+ */
+ HELTEC_HT62 = 53;
+
+ /*
+ * EBYTE SPI LoRa module and ESP32-S3
+ */
+ EBYTE_ESP32_S3 = 54;
+
+ /*
+ * Waveshare ESP32-S3-PICO with PICO LoRa HAT and 2.9inch e-Ink
+ */
+ ESP32_S3_PICO = 55;
+
+ /*
+ * CircuitMess Chatter 2 LLCC68 Lora Module and ESP32 Wroom
+ * Lora module can be swapped out for a Heltec RA-62 which is "almost" pin compatible
+ * with one cut and one jumper Meshtastic works
+ */
+ CHATTER_2 = 56;
+
+ /*
+ * Heltec Wireless Paper, With ESP32-S3 CPU and E-Ink display
+ * Older "V1.0" Variant, has no "version sticker"
+ * E-Ink model is DEPG0213BNS800
+ * Tab on the screen protector is RED
+ * Flex connector marking is FPC-7528B
+ */
+ HELTEC_WIRELESS_PAPER_V1_0 = 57;
+
+ /*
+ * Heltec Wireless Tracker with ESP32-S3 CPU, built-in GPS, and TFT
+ * Older "V1.0" Variant
+ */
+ HELTEC_WIRELESS_TRACKER_V1_0 = 58;
+
+ /*
+ * ------------------------------------------------------------------------------------------------------------------------------------------
+ * Reserved ID For developing private Ports. These will show up in live traffic sparsely, so we can use a high number. Keep it within 8 bits.
+ * ------------------------------------------------------------------------------------------------------------------------------------------
+ */
+ PRIVATE_HW = 255;
+}
+
+/*
+ * Broadcast when a newly powered mesh node wants to find a node num it can use
+ * Sent from the phone over bluetooth to set the user id for the owner of this node.
+ * Also sent from nodes to each other when a new node signs on (so all clients can have this info)
+ * The algorithm is as follows:
+ * when a node starts up, it broadcasts their user and the normal flow is for all
+ * other nodes to reply with their User as well (so the new node can build its nodedb)
+ * If a node ever receives a User (not just the first broadcast) message where
+ * the sender node number equals our node number, that indicates a collision has
+ * occurred and the following steps should happen:
+ * If the receiving node (that was already in the mesh)'s macaddr is LOWER than the
+ * new User who just tried to sign in: it gets to keep its nodenum.
+ * We send a broadcast message of OUR User (we use a broadcast so that the other node can
+ * receive our message, considering we have the same id - it also serves to let
+ * observers correct their nodedb) - this case is rare so it should be okay.
+ * If any node receives a User where the macaddr is GTE than their local macaddr,
+ * they have been vetoed and should pick a new random nodenum (filtering against
+ * whatever it knows about the nodedb) and rebroadcast their User.
+ * A few nodenums are reserved and will never be requested:
+ * 0xff - broadcast
+ * 0 through 3 - for future use
+ */
+message User {
+ /*
+ * A globally unique ID string for this user.
+ * In the case of Signal that would mean +16504442323, for the default macaddr derived id it would be !<8 hexidecimal bytes>.
+ * Note: app developers are encouraged to also use the following standard
+ * node IDs "^all" (for broadcast), "^local" (for the locally connected node)
+ */
+ string id = 1;
+
+ /*
+ * A full name for this user, i.e. "Kevin Hester"
+ */
+ string long_name = 2;
+
+ /*
+ * A VERY short name, ideally two characters.
+ * Suitable for a tiny OLED screen
+ */
+ string short_name = 3;
+
+ /*
+ * Deprecated in Meshtastic 2.1.x
+ * This is the addr of the radio.
+ * Not populated by the phone, but added by the esp32 when broadcasting
+ */
+ bytes macaddr = 4 [deprecated = true];
+
+ /*
+ * TBEAM, HELTEC, etc...
+ * Starting in 1.2.11 moved to hw_model enum in the NodeInfo object.
+ * Apps will still need the string here for older builds
+ * (so OTA update can find the right image), but if the enum is available it will be used instead.
+ */
+ HardwareModel hw_model = 5;
+
+ /*
+ * In some regions Ham radio operators have different bandwidth limitations than others.
+ * If this user is a licensed operator, set this flag.
+ * Also, "long_name" should be their licence number.
+ */
+ bool is_licensed = 6;
+
+ /*
+ * Indicates that the user's role in the mesh
+ */
+ Config.DeviceConfig.Role role = 7;
+}
+
+/*
+ * A message used in our Dynamic Source Routing protocol (RFC 4728 based)
+ */
+message RouteDiscovery {
+ /*
+ * The list of nodenums this packet has visited so far
+ */
+ repeated fixed32 route = 1;
+}
+
+/*
+ * A Routing control Data packet handled by the routing module
+ */
+message Routing {
+ /*
+ * A failure in delivering a message (usually used for routing control messages, but might be provided in addition to ack.fail_id to provide
+ * details on the type of failure).
+ */
+ enum Error {
+ /*
+ * This message is not a failure
+ */
+ NONE = 0;
+
+ /*
+ * Our node doesn't have a route to the requested destination anymore.
+ */
+ NO_ROUTE = 1;
+
+ /*
+ * We received a nak while trying to forward on your behalf
+ */
+ GOT_NAK = 2;
+
+ /*
+ * TODO: REPLACE
+ */
+ TIMEOUT = 3;
+
+ /*
+ * No suitable interface could be found for delivering this packet
+ */
+ NO_INTERFACE = 4;
+
+ /*
+ * We reached the max retransmission count (typically for naive flood routing)
+ */
+ MAX_RETRANSMIT = 5;
+
+ /*
+ * No suitable channel was found for sending this packet (i.e. was requested channel index disabled?)
+ */
+ NO_CHANNEL = 6;
+
+ /*
+ * The packet was too big for sending (exceeds interface MTU after encoding)
+ */
+ TOO_LARGE = 7;
+
+ /*
+ * The request had want_response set, the request reached the destination node, but no service on that node wants to send a response
+ * (possibly due to bad channel permissions)
+ */
+ NO_RESPONSE = 8;
+
+ /*
+ * Cannot send currently because duty cycle regulations will be violated.
+ */
+ DUTY_CYCLE_LIMIT = 9;
+
+ /*
+ * The application layer service on the remote node received your request, but considered your request somehow invalid
+ */
+ BAD_REQUEST = 32;
+
+ /*
+ * The application layer service on the remote node received your request, but considered your request not authorized
+ * (i.e you did not send the request on the required bound channel)
+ */
+ NOT_AUTHORIZED = 33;
+ }
+
+ oneof variant {
+ /*
+ * A route request going from the requester
+ */
+ RouteDiscovery route_request = 1;
+
+ /*
+ * A route reply
+ */
+ RouteDiscovery route_reply = 2;
+
+ /*
+ * A failure in delivering a message (usually used for routing control messages, but might be provided
+ * in addition to ack.fail_id to provide details on the type of failure).
+ */
+ Error error_reason = 3;
+ }
+}
+
+/*
+ * (Formerly called SubPacket)
+ * The payload portion fo a packet, this is the actual bytes that are sent
+ * inside a radio packet (because from/to are broken out by the comms library)
+ */
+message Data {
+ /*
+ * Formerly named typ and of type Type
+ */
+ PortNum portnum = 1;
+
+ /*
+ * TODO: REPLACE
+ */
+ bytes payload = 2;
+
+ /*
+ * Not normally used, but for testing a sender can request that recipient
+ * responds in kind (i.e. if it received a position, it should unicast back it's position).
+ * Note: that if you set this on a broadcast you will receive many replies.
+ */
+ bool want_response = 3;
+
+ /*
+ * The address of the destination node.
+ * This field is is filled in by the mesh radio device software, application
+ * layer software should never need it.
+ * RouteDiscovery messages _must_ populate this.
+ * Other message types might need to if they are doing multihop routing.
+ */
+ fixed32 dest = 4;
+
+ /*
+ * The address of the original sender for this message.
+ * This field should _only_ be populated for reliable multihop packets (to keep
+ * packets small).
+ */
+ fixed32 source = 5;
+
+ /*
+ * Only used in routing or response messages.
+ * Indicates the original message ID that this message is reporting failure on. (formerly called original_id)
+ */
+ fixed32 request_id = 6;
+
+ /*
+ * If set, this message is intened to be a reply to a previously sent message with the defined id.
+ */
+ fixed32 reply_id = 7;
+
+ /*
+ * Defaults to false. If true, then what is in the payload should be treated as an emoji like giving
+ * a message a heart or poop emoji.
+ */
+ fixed32 emoji = 8;
+}
+
+/*
+ * Waypoint message, used to share arbitrary locations across the mesh
+ */
+message Waypoint {
+ /*
+ * Id of the waypoint
+ */
+ uint32 id = 1;
+
+ /*
+ * latitude_i
+ */
+ sfixed32 latitude_i = 2;
+
+ /*
+ * longitude_i
+ */
+ sfixed32 longitude_i = 3;
+
+ /*
+ * Time the waypoint is to expire (epoch)
+ */
+ uint32 expire = 4;
+
+ /*
+ * If greater than zero, treat the value as a nodenum only allowing them to update the waypoint.
+ * If zero, the waypoint is open to be edited by any member of the mesh.
+ */
+ uint32 locked_to = 5;
+
+ /*
+ * Name of the waypoint - max 30 chars
+ */
+ string name = 6;
+
+ /*
+ * Description of the waypoint - max 100 chars
+ */
+ string description = 7;
+
+ /*
+ * Designator icon for the waypoint in the form of a unicode emoji
+ */
+ fixed32 icon = 8;
+}
+
+/*
+ * This message will be proxied over the PhoneAPI for the client to deliver to the MQTT server
+ */
+message MqttClientProxyMessage {
+ /*
+ * The MQTT topic this message will be sent /received on
+ */
+ string topic = 1;
+
+ /*
+ * The actual service envelope payload or text for mqtt pub / sub
+ */
+ oneof payload_variant {
+ /*
+ * Bytes
+ */
+ bytes data = 2;
+
+ /*
+ * Text
+ */
+ string text = 3;
+ }
+
+ /*
+ * Whether the message should be retained (or not)
+ */
+ bool retained = 4;
+}
+
+/*
+ * A packet envelope sent/received over the mesh
+ * only payload_variant is sent in the payload portion of the LORA packet.
+ * The other fields are either not sent at all, or sent in the special 16 byte LORA header.
+ */
+message MeshPacket {
+ /*
+ * The priority of this message for sending.
+ * Higher priorities are sent first (when managing the transmit queue).
+ * This field is never sent over the air, it is only used internally inside of a local device node.
+ * API clients (either on the local node or connected directly to the node)
+ * can set this parameter if necessary.
+ * (values must be <= 127 to keep protobuf field to one byte in size.
+ * Detailed background on this field:
+ * I noticed a funny side effect of lora being so slow: Usually when making
+ * a protocol there isn’t much need to use message priority to change the order
+ * of transmission (because interfaces are fairly fast).
+ * But for lora where packets can take a few seconds each, it is very important
+ * to make sure that critical packets are sent ASAP.
+ * In the case of meshtastic that means we want to send protocol acks as soon as possible
+ * (to prevent unneeded retransmissions), we want routing messages to be sent next,
+ * then messages marked as reliable and finally 'background' packets like periodic position updates.
+ * So I bit the bullet and implemented a new (internal - not sent over the air)
+ * field in MeshPacket called 'priority'.
+ * And the transmission queue in the router object is now a priority queue.
+ */
+ enum Priority {
+ /*
+ * Treated as Priority.DEFAULT
+ */
+ UNSET = 0;
+
+ /*
+ * TODO: REPLACE
+ */
+ MIN = 1;
+
+ /*
+ * Background position updates are sent with very low priority -
+ * if the link is super congested they might not go out at all
+ */
+ BACKGROUND = 10;
+
+ /*
+ * This priority is used for most messages that don't have a priority set
+ */
+ DEFAULT = 64;
+
+ /*
+ * If priority is unset but the message is marked as want_ack,
+ * assume it is important and use a slightly higher priority
+ */
+ RELIABLE = 70;
+
+ /*
+ * Ack/naks are sent with very high priority to ensure that retransmission
+ * stops as soon as possible
+ */
+ ACK = 120;
+
+ /*
+ * TODO: REPLACE
+ */
+ MAX = 127;
+ }
+
+ /*
+ * Identify if this is a delayed packet
+ */
+ enum Delayed {
+ /*
+ * If unset, the message is being sent in real time.
+ */
+ NO_DELAY = 0;
+
+ /*
+ * The message is delayed and was originally a broadcast
+ */
+ DELAYED_BROADCAST = 1;
+
+ /*
+ * The message is delayed and was originally a direct message
+ */
+ DELAYED_DIRECT = 2;
+ }
+
+ /*
+ * The sending node number.
+ * Note: Our crypto implementation uses this field as well.
+ * See [crypto](/docs/overview/encryption) for details.
+ */
+ fixed32 from = 1;
+
+ /*
+ * The (immediate) destination for this packet
+ */
+ fixed32 to = 2;
+
+ /*
+ * (Usually) If set, this indicates the index in the secondary_channels table that this packet was sent/received on.
+ * If unset, packet was on the primary channel.
+ * A particular node might know only a subset of channels in use on the mesh.
+ * Therefore channel_index is inherently a local concept and meaningless to send between nodes.
+ * Very briefly, while sending and receiving deep inside the device Router code, this field instead
+ * contains the 'channel hash' instead of the index.
+ * This 'trick' is only used while the payload_variant is an 'encrypted'.
+ */
+ uint32 channel = 3;
+
+ /*
+ * Internally to the mesh radios we will route SubPackets encrypted per [this](docs/developers/firmware/encryption).
+ * However, when a particular node has the correct
+ * key to decode a particular packet, it will decode the payload into a SubPacket protobuf structure.
+ * Software outside of the device nodes will never encounter a packet where
+ * "decoded" is not populated (i.e. any encryption/decryption happens before reaching the applications)
+ * The numeric IDs for these fields were selected to keep backwards compatibility with old applications.
+ */
+
+ oneof payload_variant {
+ /*
+ * TODO: REPLACE
+ */
+ Data decoded = 4;
+
+ /*
+ * TODO: REPLACE
+ */
+ bytes encrypted = 5;
+ }
+
+ /*
+ * A unique ID for this packet.
+ * Always 0 for no-ack packets or non broadcast packets (and therefore take zero bytes of space).
+ * Otherwise a unique ID for this packet, useful for flooding algorithms.
+ * ID only needs to be unique on a _per sender_ basis, and it only
+ * needs to be unique for a few minutes (long enough to last for the length of
+ * any ACK or the completion of a mesh broadcast flood).
+ * Note: Our crypto implementation uses this id as well.
+ * See [crypto](/docs/overview/encryption) for details.
+ */
+ fixed32 id = 6;
+
+ /*
+ * The time this message was received by the esp32 (secs since 1970).
+ * Note: this field is _never_ sent on the radio link itself (to save space) Times
+ * are typically not sent over the mesh, but they will be added to any Packet
+ * (chain of SubPacket) sent to the phone (so the phone can know exact time of reception)
+ */
+ fixed32 rx_time = 7;
+
+ /*
+ * *Never* sent over the radio links.
+ * Set during reception to indicate the SNR of this packet.
+ * Used to collect statistics on current link quality.
+ */
+ float rx_snr = 8;
+
+ /*
+ * If unset treated as zero (no forwarding, send to adjacent nodes only)
+ * if 1, allow hopping through one node, etc...
+ * For our usecase real world topologies probably have a max of about 3.
+ * This field is normally placed into a few of bits in the header.
+ */
+ uint32 hop_limit = 9;
+
+ /*
+ * This packet is being sent as a reliable message, we would prefer it to arrive at the destination.
+ * We would like to receive a ack packet in response.
+ * Broadcasts messages treat this flag specially: Since acks for broadcasts would
+ * rapidly flood the channel, the normal ack behavior is suppressed.
+ * Instead, the original sender listens to see if at least one node is rebroadcasting this packet (because naive flooding algorithm).
+ * If it hears that the odds (given typical LoRa topologies) the odds are very high that every node should eventually receive the message.
+ * So FloodingRouter.cpp generates an implicit ack which is delivered to the original sender.
+ * If after some time we don't hear anyone rebroadcast our packet, we will timeout and retransmit, using the regular resend logic.
+ * Note: This flag is normally sent in a flag bit in the header when sent over the wire
+ */
+ bool want_ack = 10;
+
+ /*
+ * The priority of this message for sending.
+ * See MeshPacket.Priority description for more details.
+ */
+ Priority priority = 11;
+
+ /*
+ * rssi of received packet. Only sent to phone for dispay purposes.
+ */
+ int32 rx_rssi = 12;
+
+ /*
+ * Describe if this message is delayed
+ */
+ Delayed delayed = 13 [deprecated = true];
+
+ /*
+ * Describes whether this packet passed via MQTT somewhere along the path it currently took.
+ */
+ bool via_mqtt = 14;
+
+ /*
+ * Hop limit with which the original packet started. Sent via LoRa using three bits in the unencrypted header.
+ * When receiving a packet, the difference between hop_start and hop_limit gives how many hops it traveled.
+ */
+ uint32 hop_start = 15;
+}
+
+/*
+ * Shared constants between device and phone
+ */
+enum Constants {
+ /*
+ * First enum must be zero, and we are just using this enum to
+ * pass int constants between two very different environments
+ */
+ ZERO = 0;
+
+ /*
+ * From mesh.options
+ * note: this payload length is ONLY the bytes that are sent inside of the Data protobuf (excluding protobuf overhead). The 16 byte header is
+ * outside of this envelope
+ */
+ DATA_PAYLOAD_LEN = 237;
+}
+
+/*
+ * The bluetooth to device link:
+ * Old BTLE protocol docs from TODO, merge in above and make real docs...
+ * use protocol buffers, and NanoPB
+ * messages from device to phone:
+ * POSITION_UPDATE (..., time)
+ * TEXT_RECEIVED(from, text, time)
+ * OPAQUE_RECEIVED(from, payload, time) (for signal messages or other applications)
+ * messages from phone to device:
+ * SET_MYID(id, human readable long, human readable short) (send down the unique ID
+ * string used for this node, a human readable string shown for that id, and a very
+ * short human readable string suitable for oled screen) SEND_OPAQUE(dest, payload)
+ * (for signal messages or other applications) SEND_TEXT(dest, text) Get all
+ * nodes() (returns list of nodes, with full info, last time seen, loc, battery
+ * level etc) SET_CONFIG (switches device to a new set of radio params and
+ * preshared key, drops all existing nodes, force our node to rejoin this new group)
+ * Full information about a node on the mesh
+ */
+message NodeInfo {
+ /*
+ * The node number
+ */
+ uint32 num = 1;
+
+ /*
+ * The user info for this node
+ */
+ User user = 2;
+
+ /*
+ * This position data. Note: before 1.2.14 we would also store the last time we've heard from this node in position.time, that is no longer true.
+ * Position.time now indicates the last time we received a POSITION from that node.
+ */
+ Position position = 3;
+
+ /*
+ * Returns the Signal-to-noise ratio (SNR) of the last received message,
+ * as measured by the receiver. Return SNR of the last received message in dB
+ */
+ float snr = 4;
+
+ /*
+ * TODO: REMOVE/INTEGRATE
+ * Returns the last measured frequency error.
+ * The LoRa receiver estimates the frequency offset between the receiver
+ * center frequency and that of the received LoRa signal. This function
+ * returns the estimates offset (in Hz) of the last received message.
+ * Caution: this measurement is not absolute, but is measured relative to the
+ * local receiver's oscillator. Apparent errors may be due to the
+ * transmitter, the receiver or both. \return The estimated center frequency
+ * offset in Hz of the last received message.
+ * int32 frequency_error = 6;
+ * enum RouteState {
+ * Invalid = 0;
+ * Discovering = 1;
+ * Valid = 2;
+ * }
+ * Not needed?
+ * RouteState route = 4;
+ */
+
+ /*
+ * TODO: REMOVE/INTEGRATE
+ * Not currently used (till full DSR deployment?) Our current preferred node node for routing - might be the same as num if
+ * we are adjacent Or zero if we don't yet know a route to this node.
+ * fixed32 next_hop = 5;
+ */
+
+ /*
+ * Set to indicate the last time we received a packet from this node
+ */
+ fixed32 last_heard = 5;
+ /*
+ * The latest device metrics for the node.
+ */
+ DeviceMetrics device_metrics = 6;
+
+ /*
+ * local channel index we heard that node on. Only populated if its not the default channel.
+ */
+ uint32 channel = 7;
+
+ /*
+ * True if we witnessed the node over MQTT instead of LoRA transport
+ */
+ bool via_mqtt = 8;
+
+ /*
+ * Number of hops away from us this node is (0 if adjacent)
+ */
+ uint32 hops_away = 9;
+
+ /*
+ * True if node is in our favorites list
+ * Persists between NodeDB internal clean ups
+ */
+ bool is_favorite = 10;
+}
+
+/*
+ * Error codes for critical errors
+ * The device might report these fault codes on the screen.
+ * If you encounter a fault code, please post on the meshtastic.discourse.group
+ * and we'll try to help.
+ */
+enum CriticalErrorCode {
+ /*
+ * TODO: REPLACE
+ */
+ NONE = 0;
+
+ /*
+ * A software bug was detected while trying to send lora
+ */
+ TX_WATCHDOG = 1;
+
+ /*
+ * A software bug was detected on entry to sleep
+ */
+ SLEEP_ENTER_WAIT = 2;
+
+ /*
+ * No Lora radio hardware could be found
+ */
+ NO_RADIO = 3;
+
+ /*
+ * Not normally used
+ */
+ UNSPECIFIED = 4;
+
+ /*
+ * We failed while configuring a UBlox GPS
+ */
+ UBLOX_UNIT_FAILED = 5;
+
+ /*
+ * This board was expected to have a power management chip and it is missing or broken
+ */
+ NO_AXP192 = 6;
+
+ /*
+ * The channel tried to set a radio setting which is not supported by this chipset,
+ * radio comms settings are now undefined.
+ */
+ INVALID_RADIO_SETTING = 7;
+
+ /*
+ * Radio transmit hardware failure. We sent data to the radio chip, but it didn't
+ * reply with an interrupt.
+ */
+ TRANSMIT_FAILED = 8;
+
+ /*
+ * We detected that the main CPU voltage dropped below the minimum acceptable value
+ */
+ BROWNOUT = 9;
+
+ /* Selftest of SX1262 radio chip failed */
+ SX1262_FAILURE = 10;
+
+ /*
+ * A (likely software but possibly hardware) failure was detected while trying to send packets.
+ * If this occurs on your board, please post in the forum so that we can ask you to collect some information to allow fixing this bug
+ */
+ RADIO_SPI_BUG = 11;
+}
+
+/*
+ * Unique local debugging info for this node
+ * Note: we don't include position or the user info, because that will come in the
+ * Sent to the phone in response to WantNodes.
+ */
+message MyNodeInfo {
+ /*
+ * Tells the phone what our node number is, default starting value is
+ * lowbyte of macaddr, but it will be fixed if that is already in use
+ */
+ uint32 my_node_num = 1;
+
+ /*
+ * The total number of reboots this node has ever encountered
+ * (well - since the last time we discarded preferences)
+ */
+ uint32 reboot_count = 8;
+
+ /*
+ * The minimum app version that can talk to this device.
+ * Phone/PC apps should compare this to their build number and if too low tell the user they must update their app
+ */
+ uint32 min_app_version = 11;
+}
+
+/*
+ * Debug output from the device.
+ * To minimize the size of records inside the device code, if a time/source/level is not set
+ * on the message it is assumed to be a continuation of the previously sent message.
+ * This allows the device code to use fixed maxlen 64 byte strings for messages,
+ * and then extend as needed by emitting multiple records.
+ */
+message LogRecord {
+ /*
+ * Log levels, chosen to match python logging conventions.
+ */
+ enum Level {
+ /*
+ * Log levels, chosen to match python logging conventions.
+ */
+ UNSET = 0;
+
+ /*
+ * Log levels, chosen to match python logging conventions.
+ */
+ CRITICAL = 50;
+
+ /*
+ * Log levels, chosen to match python logging conventions.
+ */
+ ERROR = 40;
+
+ /*
+ * Log levels, chosen to match python logging conventions.
+ */
+ WARNING = 30;
+
+ /*
+ * Log levels, chosen to match python logging conventions.
+ */
+ INFO = 20;
+
+ /*
+ * Log levels, chosen to match python logging conventions.
+ */
+ DEBUG = 10;
+
+ /*
+ * Log levels, chosen to match python logging conventions.
+ */
+ TRACE = 5;
+ }
+
+ /*
+ * Log levels, chosen to match python logging conventions.
+ */
+ string message = 1;
+
+ /*
+ * Seconds since 1970 - or 0 for unknown/unset
+ */
+ fixed32 time = 2;
+
+ /*
+ * Usually based on thread name - if known
+ */
+ string source = 3;
+
+ /*
+ * Not yet set
+ */
+ Level level = 4;
+}
+
+message QueueStatus {
+ /* Last attempt to queue status, ErrorCode */
+ int32 res = 1;
+
+ /* Free entries in the outgoing queue */
+ uint32 free = 2;
+
+ /* Maximum entries in the outgoing queue */
+ uint32 maxlen = 3;
+
+ /* What was mesh packet id that generated this response? */
+ uint32 mesh_packet_id = 4;
+}
+
+/*
+ * Packets from the radio to the phone will appear on the fromRadio characteristic.
+ * It will support READ and NOTIFY. When a new packet arrives the device will BLE notify?
+ * It will sit in that descriptor until consumed by the phone,
+ * at which point the next item in the FIFO will be populated.
+ */
+message FromRadio {
+ /*
+ * The packet id, used to allow the phone to request missing read packets from the FIFO,
+ * see our bluetooth docs
+ */
+ uint32 id = 1;
+
+ /*
+ * Log levels, chosen to match python logging conventions.
+ */
+ oneof payload_variant {
+ /*
+ * Log levels, chosen to match python logging conventions.
+ */
+ MeshPacket packet = 2;
+
+ /*
+ * Tells the phone what our node number is, can be -1 if we've not yet joined a mesh.
+ * NOTE: This ID must not change - to keep (minimal) compatibility with <1.2 version of android apps.
+ */
+ MyNodeInfo my_info = 3;
+
+ /*
+ * One packet is sent for each node in the on radio DB
+ * starts over with the first node in our DB
+ */
+ NodeInfo node_info = 4;
+
+ /*
+ * Include a part of the config (was: RadioConfig radio)
+ */
+ Config config = 5;
+
+ /*
+ * Set to send debug console output over our protobuf stream
+ */
+ LogRecord log_record = 6;
+
+ /*
+ * Sent as true once the device has finished sending all of the responses to want_config
+ * recipient should check if this ID matches our original request nonce, if
+ * not, it means your config responses haven't started yet.
+ * NOTE: This ID must not change - to keep (minimal) compatibility with <1.2 version of android apps.
+ */
+ uint32 config_complete_id = 7;
+
+ /*
+ * Sent to tell clients the radio has just rebooted.
+ * Set to true if present.
+ * Not used on all transports, currently just used for the serial console.
+ * NOTE: This ID must not change - to keep (minimal) compatibility with <1.2 version of android apps.
+ */
+ bool rebooted = 8;
+
+ /*
+ * Include module config
+ */
+ ModuleConfig moduleConfig = 9;
+
+ /*
+ * One packet is sent for each channel
+ */
+ Channel channel = 10;
+
+ /*
+ * Queue status info
+ */
+ QueueStatus queueStatus = 11;
+
+ /*
+ * File Transfer Chunk
+ */
+ XModem xmodemPacket = 12;
+
+ /*
+ * Device metadata message
+ */
+ DeviceMetadata metadata = 13;
+
+ /*
+ * MQTT Client Proxy Message (device sending to client / phone for publishing to MQTT)
+ */
+ MqttClientProxyMessage mqttClientProxyMessage = 14;
+ }
+}
+
+/*
+ * Packets/commands to the radio will be written (reliably) to the toRadio characteristic.
+ * Once the write completes the phone can assume it is handled.
+ */
+message ToRadio {
+ /*
+ * Log levels, chosen to match python logging conventions.
+ */
+ oneof payload_variant {
+ /*
+ * Send this packet on the mesh
+ */
+ MeshPacket packet = 1;
+
+ /*
+ * Phone wants radio to send full node db to the phone, This is
+ * typically the first packet sent to the radio when the phone gets a
+ * bluetooth connection. The radio will respond by sending back a
+ * MyNodeInfo, a owner, a radio config and a series of
+ * FromRadio.node_infos, and config_complete
+ * the integer you write into this field will be reported back in the
+ * config_complete_id response this allows clients to never be confused by
+ * a stale old partially sent config.
+ */
+ uint32 want_config_id = 3;
+
+ /*
+ * Tell API server we are disconnecting now.
+ * This is useful for serial links where there is no hardware/protocol based notification that the client has dropped the link.
+ * (Sending this message is optional for clients)
+ */
+ bool disconnect = 4;
+
+ /*
+ * File Transfer Chunk
+ */
+
+ XModem xmodemPacket = 5;
+
+ /*
+ * MQTT Client Proxy Message (for client / phone subscribed to MQTT sending to device)
+ */
+ MqttClientProxyMessage mqttClientProxyMessage = 6;
+
+ /*
+ * Heartbeat message (used to keep the device connection awake on serial)
+ */
+ Heartbeat heartbeat = 7;
+ }
+}
+
+/*
+ * Compressed message payload
+ */
+message Compressed {
+ /*
+ * PortNum to determine the how to handle the compressed payload.
+ */
+ PortNum portnum = 1;
+
+ /*
+ * Compressed data.
+ */
+ bytes data = 2;
+}
+
+/*
+ * Full info on edges for a single node
+ */
+message NeighborInfo {
+ /*
+ * The node ID of the node sending info on its neighbors
+ */
+ uint32 node_id = 1;
+ /*
+ * Field to pass neighbor info for the next sending cycle
+ */
+ uint32 last_sent_by_id = 2;
+
+ /*
+ * Broadcast interval of the represented node (in seconds)
+ */
+ uint32 node_broadcast_interval_secs = 3;
+ /*
+ * The list of out edges from this node
+ */
+ repeated Neighbor neighbors = 4;
+}
+
+/*
+ * A single edge in the mesh
+ */
+message Neighbor {
+ /*
+ * Node ID of neighbor
+ */
+ uint32 node_id = 1;
+
+ /*
+ * SNR of last heard message
+ */
+ float snr = 2;
+
+ /*
+ * Reception time (in secs since 1970) of last message that was last sent by this ID.
+ * Note: this is for local storage only and will not be sent out over the mesh.
+ */
+ fixed32 last_rx_time = 3;
+
+ /*
+ * Broadcast interval of this neighbor (in seconds).
+ * Note: this is for local storage only and will not be sent out over the mesh.
+ */
+ uint32 node_broadcast_interval_secs = 4;
+}
+
+/*
+ * Device metadata response
+ */
+message DeviceMetadata {
+ /*
+ * Device firmware version string
+ */
+ string firmware_version = 1;
+
+ /*
+ * Device state version
+ */
+ uint32 device_state_version = 2;
+
+ /*
+ * Indicates whether the device can shutdown CPU natively or via power management chip
+ */
+ bool canShutdown = 3;
+
+ /*
+ * Indicates that the device has native wifi capability
+ */
+ bool hasWifi = 4;
+
+ /*
+ * Indicates that the device has native bluetooth capability
+ */
+ bool hasBluetooth = 5;
+
+ /*
+ * Indicates that the device has an ethernet peripheral
+ */
+ bool hasEthernet = 6;
+
+ /*
+ * Indicates that the device's role in the mesh
+ */
+ Config.DeviceConfig.Role role = 7;
+
+ /*
+ * Indicates the device's current enabled position flags
+ */
+ uint32 position_flags = 8;
+
+ /*
+ * Device hardware model
+ */
+ HardwareModel hw_model = 9;
+
+ /*
+ * Has Remote Hardware enabled
+ */
+ bool hasRemoteHardware = 10;
+}
+
+/*
+ * A heartbeat message is sent to the node from the client to keep the connection alive.
+ * This is currently only needed to keep serial connections alive, but can be used by any PhoneAPI.
+ */
+message Heartbeat {}
+
+/*
+ * RemoteHardwarePins associated with a node
+ */
+message NodeRemoteHardwarePin {
+ /*
+ * The node_num exposing the available gpio pin
+ */
+ uint32 node_num = 1;
+
+ /*
+ * The the available gpio pin for usage with RemoteHardware module
+ */
+ RemoteHardwarePin pin = 2;
+}
diff --git a/protobufs/meshtastic/module_config.proto b/protobufs/meshtastic/module_config.proto
new file mode 100644
index 0000000..36a2b4b
--- /dev/null
+++ b/protobufs/meshtastic/module_config.proto
@@ -0,0 +1,791 @@
+syntax = "proto3";
+
+package meshtastic;
+
+option csharp_namespace = "Meshtastic.Protobufs";
+option go_package = "github.com/meshtastic/go/generated";
+option java_outer_classname = "ModuleConfigProtos";
+option java_package = "com.geeksville.mesh";
+option swift_prefix = "";
+
+/*
+ * Module Config
+ */
+message ModuleConfig {
+ /*
+ * MQTT Client Config
+ */
+ message MQTTConfig {
+ /*
+ * If a meshtastic node is able to reach the internet it will normally attempt to gateway any channels that are marked as
+ * is_uplink_enabled or is_downlink_enabled.
+ */
+ bool enabled = 1;
+
+ /*
+ * The server to use for our MQTT global message gateway feature.
+ * If not set, the default server will be used
+ */
+ string address = 2;
+
+ /*
+ * MQTT username to use (most useful for a custom MQTT server).
+ * If using a custom server, this will be honoured even if empty.
+ * If using the default server, this will only be honoured if set, otherwise the device will use the default username
+ */
+ string username = 3;
+
+ /*
+ * MQTT password to use (most useful for a custom MQTT server).
+ * If using a custom server, this will be honoured even if empty.
+ * If using the default server, this will only be honoured if set, otherwise the device will use the default password
+ */
+ string password = 4;
+
+ /*
+ * Whether to send encrypted or decrypted packets to MQTT.
+ * This parameter is only honoured if you also set server
+ * (the default official mqtt.meshtastic.org server can handle encrypted packets)
+ * Decrypted packets may be useful for external systems that want to consume meshtastic packets
+ */
+ bool encryption_enabled = 5;
+
+ /*
+ * Whether to send / consume json packets on MQTT
+ */
+ bool json_enabled = 6;
+
+ /*
+ * If true, we attempt to establish a secure connection using TLS
+ */
+ bool tls_enabled = 7;
+
+ /*
+ * The root topic to use for MQTT messages. Default is "msh".
+ * This is useful if you want to use a single MQTT server for multiple meshtastic networks and separate them via ACLs
+ */
+ string root = 8;
+
+ /*
+ * If true, we can use the connected phone / client to proxy messages to MQTT instead of a direct connection
+ */
+ bool proxy_to_client_enabled = 9;
+
+ /*
+ * If true, we will periodically report unencrypted information about our node to a map via MQTT
+ */
+ bool map_reporting_enabled = 10;
+
+ /*
+ * Settings for reporting information about our node to a map via MQTT
+ */
+ MapReportSettings map_report_settings = 11;
+ }
+
+ /*
+ * Settings for reporting unencrypted information about our node to a map via MQTT
+ */
+ message MapReportSettings {
+ /*
+ * How often we should report our info to the map (in seconds)
+ */
+ uint32 publish_interval_secs = 1;
+
+ /*
+ * Bits of precision for the location sent (default of 32 is full precision).
+ */
+ uint32 position_precision = 2;
+ }
+
+ /*
+ * RemoteHardwareModule Config
+ */
+ message RemoteHardwareConfig {
+ /*
+ * Whether the Module is enabled
+ */
+ bool enabled = 1;
+
+ /*
+ * Whether the Module allows consumers to read / write to pins not defined in available_pins
+ */
+ bool allow_undefined_pin_access = 2;
+
+ /*
+ * Exposes the available pins to the mesh for reading and writing
+ */
+ repeated RemoteHardwarePin available_pins = 3;
+ }
+
+ /*
+ * NeighborInfoModule Config
+ */
+ message NeighborInfoConfig {
+ /*
+ * Whether the Module is enabled
+ */
+ bool enabled = 1;
+
+ /*
+ * Interval in seconds of how often we should try to send our
+ * Neighbor Info to the mesh
+ */
+ uint32 update_interval = 2;
+ }
+
+ /*
+ * Detection Sensor Module Config
+ */
+ message DetectionSensorConfig {
+ /*
+ * Whether the Module is enabled
+ */
+ bool enabled = 1;
+
+ /*
+ * Interval in seconds of how often we can send a message to the mesh when a state change is detected
+ */
+ uint32 minimum_broadcast_secs = 2;
+
+ /*
+ * Interval in seconds of how often we should send a message to the mesh with the current state regardless of changes
+ * When set to 0, only state changes will be broadcasted
+ * Works as a sort of status heartbeat for peace of mind
+ */
+ uint32 state_broadcast_secs = 3;
+ /*
+ * Send ASCII bell with alert message
+ * Useful for triggering ext. notification on bell
+ */
+ bool send_bell = 4;
+
+ /*
+ * Friendly name used to format message sent to mesh
+ * Example: A name "Motion" would result in a message "Motion detected"
+ * Maximum length of 20 characters
+ */
+ string name = 5;
+
+ /*
+ * GPIO pin to monitor for state changes
+ */
+ uint32 monitor_pin = 6;
+
+ /*
+ * Whether or not the GPIO pin state detection is triggered on HIGH (1)
+ * Otherwise LOW (0)
+ */
+ bool detection_triggered_high = 7;
+
+ /*
+ * Whether or not use INPUT_PULLUP mode for GPIO pin
+ * Only applicable if the board uses pull-up resistors on the pin
+ */
+ bool use_pullup = 8;
+ }
+
+ /*
+ * Audio Config for codec2 voice
+ */
+ message AudioConfig {
+ /*
+ * Baudrate for codec2 voice
+ */
+ enum Audio_Baud {
+ CODEC2_DEFAULT = 0;
+ CODEC2_3200 = 1;
+ CODEC2_2400 = 2;
+ CODEC2_1600 = 3;
+ CODEC2_1400 = 4;
+ CODEC2_1300 = 5;
+ CODEC2_1200 = 6;
+ CODEC2_700 = 7;
+ CODEC2_700B = 8;
+ }
+
+ /*
+ * Whether Audio is enabled
+ */
+ bool codec2_enabled = 1;
+
+ /*
+ * PTT Pin
+ */
+ uint32 ptt_pin = 2;
+
+ /*
+ * The audio sample rate to use for codec2
+ */
+ Audio_Baud bitrate = 3;
+
+ /*
+ * I2S Word Select
+ */
+ uint32 i2s_ws = 4;
+
+ /*
+ * I2S Data IN
+ */
+ uint32 i2s_sd = 5;
+
+ /*
+ * I2S Data OUT
+ */
+ uint32 i2s_din = 6;
+
+ /*
+ * I2S Clock
+ */
+ uint32 i2s_sck = 7;
+ }
+
+ /*
+ * Config for the Paxcounter Module
+ */
+ message PaxcounterConfig {
+ /*
+ * Enable the Paxcounter Module
+ */
+ bool enabled = 1;
+
+ /*
+ * Interval in seconds of how often we should try to send our
+ * metrics to the mesh
+ */
+
+ uint32 paxcounter_update_interval = 2;
+ }
+
+ /*
+ * Serial Config
+ */
+ message SerialConfig {
+ /*
+ * TODO: REPLACE
+ */
+ enum Serial_Baud {
+ BAUD_DEFAULT = 0;
+ BAUD_110 = 1;
+ BAUD_300 = 2;
+ BAUD_600 = 3;
+ BAUD_1200 = 4;
+ BAUD_2400 = 5;
+ BAUD_4800 = 6;
+ BAUD_9600 = 7;
+ BAUD_19200 = 8;
+ BAUD_38400 = 9;
+ BAUD_57600 = 10;
+ BAUD_115200 = 11;
+ BAUD_230400 = 12;
+ BAUD_460800 = 13;
+ BAUD_576000 = 14;
+ BAUD_921600 = 15;
+ }
+
+ /*
+ * TODO: REPLACE
+ */
+ enum Serial_Mode {
+ DEFAULT = 0;
+ SIMPLE = 1;
+ PROTO = 2;
+ TEXTMSG = 3;
+ NMEA = 4;
+ // NMEA messages specifically tailored for CalTopo
+ CALTOPO = 5;
+ }
+
+ /*
+ * Preferences for the SerialModule
+ */
+ bool enabled = 1;
+
+ /*
+ * TODO: REPLACE
+ */
+ bool echo = 2;
+
+ /*
+ * RX pin (should match Arduino gpio pin number)
+ */
+ uint32 rxd = 3;
+
+ /*
+ * TX pin (should match Arduino gpio pin number)
+ */
+ uint32 txd = 4;
+
+ /*
+ * Serial baud rate
+ */
+ Serial_Baud baud = 5;
+
+ /*
+ * TODO: REPLACE
+ */
+ uint32 timeout = 6;
+
+ /*
+ * Mode for serial module operation
+ */
+ Serial_Mode mode = 7;
+
+ /*
+ * Overrides the platform's defacto Serial port instance to use with Serial module config settings
+ * This is currently only usable in output modes like NMEA / CalTopo and may behave strangely or not work at all in other modes
+ * Existing logging over the Serial Console will still be present
+ */
+ bool override_console_serial_port = 8;
+ }
+
+ /*
+ * External Notifications Config
+ */
+ message ExternalNotificationConfig {
+ /*
+ * Enable the ExternalNotificationModule
+ */
+ bool enabled = 1;
+
+ /*
+ * When using in On/Off mode, keep the output on for this many
+ * milliseconds. Default 1000ms (1 second).
+ */
+ uint32 output_ms = 2;
+
+ /*
+ * Define the output pin GPIO setting Defaults to
+ * EXT_NOTIFY_OUT if set for the board.
+ * In standalone devices this pin should drive the LED to match the UI.
+ */
+ uint32 output = 3;
+
+ /*
+ * Optional: Define a secondary output pin for a vibra motor
+ * This is used in standalone devices to match the UI.
+ */
+ uint32 output_vibra = 8;
+
+ /*
+ * Optional: Define a tertiary output pin for an active buzzer
+ * This is used in standalone devices to to match the UI.
+ */
+ uint32 output_buzzer = 9;
+
+ /*
+ * IF this is true, the 'output' Pin will be pulled active high, false
+ * means active low.
+ */
+ bool active = 4;
+
+ /*
+ * True: Alert when a text message arrives (output)
+ */
+ bool alert_message = 5;
+
+ /*
+ * True: Alert when a text message arrives (output_vibra)
+ */
+ bool alert_message_vibra = 10;
+
+ /*
+ * True: Alert when a text message arrives (output_buzzer)
+ */
+ bool alert_message_buzzer = 11;
+
+ /*
+ * True: Alert when the bell character is received (output)
+ */
+ bool alert_bell = 6;
+
+ /*
+ * True: Alert when the bell character is received (output_vibra)
+ */
+ bool alert_bell_vibra = 12;
+
+ /*
+ * True: Alert when the bell character is received (output_buzzer)
+ */
+ bool alert_bell_buzzer = 13;
+
+ /*
+ * use a PWM output instead of a simple on/off output. This will ignore
+ * the 'output', 'output_ms' and 'active' settings and use the
+ * device.buzzer_gpio instead.
+ */
+ bool use_pwm = 7;
+
+ /*
+ * The notification will toggle with 'output_ms' for this time of seconds.
+ * Default is 0 which means don't repeat at all. 60 would mean blink
+ * and/or beep for 60 seconds
+ */
+ uint32 nag_timeout = 14;
+
+ /*
+ * When true, enables devices with native I2S audio output to use the RTTTL over speaker like a buzzer
+ * T-Watch S3 and T-Deck for example have this capability
+ */
+ bool use_i2s_as_buzzer = 15;
+ }
+
+ /*
+ * Store and Forward Module Config
+ */
+ message StoreForwardConfig {
+ /*
+ * Enable the Store and Forward Module
+ */
+ bool enabled = 1;
+
+ /*
+ * TODO: REPLACE
+ */
+ bool heartbeat = 2;
+
+ /*
+ * TODO: REPLACE
+ */
+ uint32 records = 3;
+
+ /*
+ * TODO: REPLACE
+ */
+ uint32 history_return_max = 4;
+
+ /*
+ * TODO: REPLACE
+ */
+ uint32 history_return_window = 5;
+ }
+
+ /*
+ * Preferences for the RangeTestModule
+ */
+ message RangeTestConfig {
+ /*
+ * Enable the Range Test Module
+ */
+ bool enabled = 1;
+
+ /*
+ * Send out range test messages from this node
+ */
+ uint32 sender = 2;
+
+ /*
+ * Bool value indicating that this node should save a RangeTest.csv file.
+ * ESP32 Only
+ */
+ bool save = 3;
+ }
+
+ /*
+ * Configuration for both device and environment metrics
+ */
+ message TelemetryConfig {
+ /*
+ * Interval in seconds of how often we should try to send our
+ * device metrics to the mesh
+ */
+ uint32 device_update_interval = 1;
+
+ /*
+ * Interval in seconds of how often we should try to send our
+ * environment measurements to the mesh
+ */
+
+ uint32 environment_update_interval = 2;
+
+ /*
+ * Preferences for the Telemetry Module (Environment)
+ * Enable/Disable the telemetry measurement module measurement collection
+ */
+ bool environment_measurement_enabled = 3;
+
+ /*
+ * Enable/Disable the telemetry measurement module on-device display
+ */
+ bool environment_screen_enabled = 4;
+
+ /*
+ * We'll always read the sensor in Celsius, but sometimes we might want to
+ * display the results in Fahrenheit as a "user preference".
+ */
+ bool environment_display_fahrenheit = 5;
+
+ /*
+ * Enable/Disable the air quality metrics
+ */
+ bool air_quality_enabled = 6;
+
+ /*
+ * Interval in seconds of how often we should try to send our
+ * air quality metrics to the mesh
+ */
+ uint32 air_quality_interval = 7;
+
+ /*
+ * Interval in seconds of how often we should try to send our
+ * air quality metrics to the mesh
+ */
+ bool power_measurement_enabled = 8;
+
+ /*
+ * Interval in seconds of how often we should try to send our
+ * air quality metrics to the mesh
+ */
+ uint32 power_update_interval = 9;
+
+ /*
+ * Interval in seconds of how often we should try to send our
+ * air quality metrics to the mesh
+ */
+ bool power_screen_enabled = 10;
+ }
+
+ /*
+ * TODO: REPLACE
+ */
+ message CannedMessageConfig {
+ /*
+ * TODO: REPLACE
+ */
+ enum InputEventChar {
+ /*
+ * TODO: REPLACE
+ */
+ NONE = 0;
+
+ /*
+ * TODO: REPLACE
+ */
+ UP = 17;
+
+ /*
+ * TODO: REPLACE
+ */
+ DOWN = 18;
+
+ /*
+ * TODO: REPLACE
+ */
+ LEFT = 19;
+
+ /*
+ * TODO: REPLACE
+ */
+ RIGHT = 20;
+
+ /*
+ * '\n'
+ */
+ SELECT = 10;
+
+ /*
+ * TODO: REPLACE
+ */
+ BACK = 27;
+
+ /*
+ * TODO: REPLACE
+ */
+ CANCEL = 24;
+ }
+
+ /*
+ * Enable the rotary encoder #1. This is a 'dumb' encoder sending pulses on both A and B pins while rotating.
+ */
+ bool rotary1_enabled = 1;
+
+ /*
+ * GPIO pin for rotary encoder A port.
+ */
+ uint32 inputbroker_pin_a = 2;
+
+ /*
+ * GPIO pin for rotary encoder B port.
+ */
+ uint32 inputbroker_pin_b = 3;
+
+ /*
+ * GPIO pin for rotary encoder Press port.
+ */
+ uint32 inputbroker_pin_press = 4;
+
+ /*
+ * Generate input event on CW of this kind.
+ */
+ InputEventChar inputbroker_event_cw = 5;
+
+ /*
+ * Generate input event on CCW of this kind.
+ */
+ InputEventChar inputbroker_event_ccw = 6;
+
+ /*
+ * Generate input event on Press of this kind.
+ */
+ InputEventChar inputbroker_event_press = 7;
+
+ /*
+ * Enable the Up/Down/Select input device. Can be RAK rotary encoder or 3 buttons. Uses the a/b/press definitions from inputbroker.
+ */
+ bool updown1_enabled = 8;
+
+ /*
+ * Enable/disable CannedMessageModule.
+ */
+ bool enabled = 9;
+
+ /*
+ * Input event origin accepted by the canned message module.
+ * Can be e.g. "rotEnc1", "upDownEnc1" or keyword "_any"
+ */
+ string allow_input_source = 10;
+
+ /*
+ * CannedMessageModule also sends a bell character with the messages.
+ * ExternalNotificationModule can benefit from this feature.
+ */
+ bool send_bell = 11;
+ }
+
+ /*
+ Ambient Lighting Module - Settings for control of onboard LEDs to allow users to adjust the brightness levels and respective color levels.
+ Initially created for the RAK14001 RGB LED module.
+ */
+ message AmbientLightingConfig {
+ /*
+ * Sets LED to on or off.
+ */
+ bool led_state = 1;
+
+ /*
+ * Sets the current for the LED output. Default is 10.
+ */
+ uint32 current = 2;
+
+ /*
+ * Sets the red LED level. Values are 0-255.
+ */
+ uint32 red = 3;
+
+ /*
+ * Sets the green LED level. Values are 0-255.
+ */
+ uint32 green = 4;
+
+ /*
+ * Sets the blue LED level. Values are 0-255.
+ */
+ uint32 blue = 5;
+ }
+
+ /*
+ * TODO: REPLACE
+ */
+ oneof payload_variant {
+ /*
+ * TODO: REPLACE
+ */
+ MQTTConfig mqtt = 1;
+
+ /*
+ * TODO: REPLACE
+ */
+ SerialConfig serial = 2;
+
+ /*
+ * TODO: REPLACE
+ */
+ ExternalNotificationConfig external_notification = 3;
+
+ /*
+ * TODO: REPLACE
+ */
+ StoreForwardConfig store_forward = 4;
+
+ /*
+ * TODO: REPLACE
+ */
+ RangeTestConfig range_test = 5;
+
+ /*
+ * TODO: REPLACE
+ */
+ TelemetryConfig telemetry = 6;
+
+ /*
+ * TODO: REPLACE
+ */
+ CannedMessageConfig canned_message = 7;
+
+ /*
+ * TODO: REPLACE
+ */
+ AudioConfig audio = 8;
+
+ /*
+ * TODO: REPLACE
+ */
+ RemoteHardwareConfig remote_hardware = 9;
+
+ /*
+ * TODO: REPLACE
+ */
+ NeighborInfoConfig neighbor_info = 10;
+
+ /*
+ * TODO: REPLACE
+ */
+ AmbientLightingConfig ambient_lighting = 11;
+
+ /*
+ * TODO: REPLACE
+ */
+ DetectionSensorConfig detection_sensor = 12;
+
+ /*
+ * TODO: REPLACE
+ */
+ PaxcounterConfig paxcounter = 13;
+ }
+}
+
+/*
+ * A GPIO pin definition for remote hardware module
+ */
+message RemoteHardwarePin {
+ /*
+ * GPIO Pin number (must match Arduino)
+ */
+ uint32 gpio_pin = 1;
+
+ /*
+ * Name for the GPIO pin (i.e. Front gate, mailbox, etc)
+ */
+ string name = 2;
+
+ /*
+ * Type of GPIO access available to consumers on the mesh
+ */
+ RemoteHardwarePinType type = 3;
+}
+
+enum RemoteHardwarePinType {
+ /*
+ * Unset/unused
+ */
+ UNKNOWN = 0;
+
+ /*
+ * GPIO pin can be read (if it is high / low)
+ */
+ DIGITAL_READ = 1;
+
+ /*
+ * GPIO pin can be written to (high / low)
+ */
+ DIGITAL_WRITE = 2;
+}
diff --git a/protobufs/meshtastic/mqtt.proto b/protobufs/meshtastic/mqtt.proto
new file mode 100644
index 0000000..2dbc820
--- /dev/null
+++ b/protobufs/meshtastic/mqtt.proto
@@ -0,0 +1,106 @@
+syntax = "proto3";
+
+package meshtastic;
+
+import "meshtastic/config.proto";
+import "meshtastic/mesh.proto";
+
+option csharp_namespace = "Meshtastic.Protobufs";
+option go_package = "github.com/meshtastic/go/generated";
+option java_outer_classname = "MQTTProtos";
+option java_package = "com.geeksville.mesh";
+option swift_prefix = "";
+
+/*
+ * This message wraps a MeshPacket with extra metadata about the sender and how it arrived.
+ */
+message ServiceEnvelope {
+ /*
+ * The (probably encrypted) packet
+ */
+ MeshPacket packet = 1;
+
+ /*
+ * The global channel ID it was sent on
+ */
+ string channel_id = 2;
+
+ /*
+ * The sending gateway node ID. Can we use this to authenticate/prevent fake
+ * nodeid impersonation for senders? - i.e. use gateway/mesh id (which is authenticated) + local node id as
+ * the globally trusted nodenum
+ */
+ string gateway_id = 3;
+}
+
+/*
+ * Information about a node intended to be reported unencrypted to a map using MQTT.
+ */
+message MapReport {
+ /*
+ * A full name for this user, i.e. "Kevin Hester"
+ */
+ string long_name = 1;
+
+ /*
+ * A VERY short name, ideally two characters.
+ * Suitable for a tiny OLED screen
+ */
+ string short_name = 2;
+
+ /*
+ * Role of the node that applies specific settings for a particular use-case
+ */
+ Config.DeviceConfig.Role role = 3;
+
+ /*
+ * Hardware model of the node, i.e. T-Beam, Heltec V3, etc...
+ */
+ HardwareModel hw_model = 4;
+
+ /*
+ * Device firmware version string
+ */
+ string firmware_version = 5;
+
+ /*
+ * The region code for the radio (US, CN, EU433, etc...)
+ */
+ Config.LoRaConfig.RegionCode region = 6;
+
+ /*
+ * Modem preset used by the radio (LongFast, MediumSlow, etc...)
+ */
+ Config.LoRaConfig.ModemPreset modem_preset = 7;
+
+ /*
+ * Whether the node has a channel with default PSK and name (LongFast, MediumSlow, etc...)
+ * and it uses the default frequency slot given the region and modem preset.
+ */
+ bool has_default_channel = 8;
+
+ /*
+ * Latitude: multiply by 1e-7 to get degrees in floating point
+ */
+ sfixed32 latitude_i = 9;
+
+ /*
+ * Longitude: multiply by 1e-7 to get degrees in floating point
+ */
+ sfixed32 longitude_i = 10;
+
+ /*
+ * Altitude in meters above MSL
+ */
+ int32 altitude = 11;
+
+ /*
+ * Indicates the bits of precision for latitude and longitude set by the sending node
+ */
+ uint32 position_precision = 12;
+
+ /*
+ * Number of online nodes (heard in the last 2 hours) this node has in its list that were received locally (not via MQTT)
+ */
+ uint32 num_online_local_nodes = 13;
+}
diff --git a/protobufs/meshtastic/paxcount.proto b/protobufs/meshtastic/paxcount.proto
new file mode 100644
index 0000000..47b2639
--- /dev/null
+++ b/protobufs/meshtastic/paxcount.proto
@@ -0,0 +1,29 @@
+syntax = "proto3";
+
+package meshtastic;
+
+option csharp_namespace = "Meshtastic.Protobufs";
+option go_package = "github.com/meshtastic/go/generated";
+option java_outer_classname = "PaxcountProtos";
+option java_package = "com.geeksville.mesh";
+option swift_prefix = "";
+
+/*
+ * TODO: REPLACE
+ */
+message Paxcount {
+ /*
+ * seen Wifi devices
+ */
+ uint32 wifi = 1;
+
+ /*
+ * Seen BLE devices
+ */
+ uint32 ble = 2;
+
+ /*
+ * Uptime in seconds
+ */
+ uint32 uptime = 3;
+}
diff --git a/protobufs/meshtastic/portnums.proto b/protobufs/meshtastic/portnums.proto
new file mode 100644
index 0000000..5808eb7
--- /dev/null
+++ b/protobufs/meshtastic/portnums.proto
@@ -0,0 +1,216 @@
+syntax = "proto3";
+
+package meshtastic;
+
+option csharp_namespace = "Meshtastic.Protobufs";
+option go_package = "github.com/meshtastic/go/generated";
+option java_outer_classname = "Portnums";
+option java_package = "com.geeksville.mesh";
+option swift_prefix = "";
+
+/*
+ * For any new 'apps' that run on the device or via sister apps on phones/PCs they should pick and use a
+ * unique 'portnum' for their application.
+ * If you are making a new app using meshtastic, please send in a pull request to add your 'portnum' to this
+ * master table.
+ * PortNums should be assigned in the following range:
+ * 0-63 Core Meshtastic use, do not use for third party apps
+ * 64-127 Registered 3rd party apps, send in a pull request that adds a new entry to portnums.proto to register your application
+ * 256-511 Use one of these portnums for your private applications that you don't want to register publically
+ * All other values are reserved.
+ * Note: This was formerly a Type enum named 'typ' with the same id #
+ * We have change to this 'portnum' based scheme for specifying app handlers for particular payloads.
+ * This change is backwards compatible by treating the legacy OPAQUE/CLEAR_TEXT values identically.
+ */
+enum PortNum {
+ /*
+ * Deprecated: do not use in new code (formerly called OPAQUE)
+ * A message sent from a device outside of the mesh, in a form the mesh does not understand
+ * NOTE: This must be 0, because it is documented in IMeshService.aidl to be so
+ * ENCODING: binary undefined
+ */
+ UNKNOWN_APP = 0;
+
+ /*
+ * A simple UTF-8 text message, which even the little micros in the mesh
+ * can understand and show on their screen eventually in some circumstances
+ * even signal might send messages in this form (see below)
+ * ENCODING: UTF-8 Plaintext (?)
+ */
+ TEXT_MESSAGE_APP = 1;
+
+ /*
+ * Reserved for built-in GPIO/example app.
+ * See remote_hardware.proto/HardwareMessage for details on the message sent/received to this port number
+ * ENCODING: Protobuf
+ */
+ REMOTE_HARDWARE_APP = 2;
+
+ /*
+ * The built-in position messaging app.
+ * Payload is a Position message.
+ * ENCODING: Protobuf
+ */
+ POSITION_APP = 3;
+
+ /*
+ * The built-in user info app.
+ * Payload is a User message.
+ * ENCODING: Protobuf
+ */
+ NODEINFO_APP = 4;
+
+ /*
+ * Protocol control packets for mesh protocol use.
+ * Payload is a Routing message.
+ * ENCODING: Protobuf
+ */
+ ROUTING_APP = 5;
+
+ /*
+ * Admin control packets.
+ * Payload is a AdminMessage message.
+ * ENCODING: Protobuf
+ */
+ ADMIN_APP = 6;
+
+ /*
+ * Compressed TEXT_MESSAGE payloads.
+ * ENCODING: UTF-8 Plaintext (?) with Unishox2 Compression
+ * NOTE: The Device Firmware converts a TEXT_MESSAGE_APP to TEXT_MESSAGE_COMPRESSED_APP if the compressed
+ * payload is shorter. There's no need for app developers to do this themselves. Also the firmware will decompress
+ * any incoming TEXT_MESSAGE_COMPRESSED_APP payload and convert to TEXT_MESSAGE_APP.
+ */
+ TEXT_MESSAGE_COMPRESSED_APP = 7;
+
+ /*
+ * Waypoint payloads.
+ * Payload is a Waypoint message.
+ * ENCODING: Protobuf
+ */
+ WAYPOINT_APP = 8;
+
+ /*
+ * Audio Payloads.
+ * Encapsulated codec2 packets. On 2.4 GHZ Bandwidths only for now
+ * ENCODING: codec2 audio frames
+ * NOTE: audio frames contain a 3 byte header (0xc0 0xde 0xc2) and a one byte marker for the decompressed bitrate.
+ * This marker comes from the 'moduleConfig.audio.bitrate' enum minus one.
+ */
+ AUDIO_APP = 9;
+
+ /*
+ * Same as Text Message but originating from Detection Sensor Module.
+ * NOTE: This portnum traffic is not sent to the public MQTT starting at firmware version 2.2.9
+ */
+ DETECTION_SENSOR_APP = 10;
+
+ /*
+ * Provides a 'ping' service that replies to any packet it receives.
+ * Also serves as a small example module.
+ * ENCODING: ASCII Plaintext
+ */
+ REPLY_APP = 32;
+
+ /*
+ * Used for the python IP tunnel feature
+ * ENCODING: IP Packet. Handled by the python API, firmware ignores this one and pases on.
+ */
+ IP_TUNNEL_APP = 33;
+
+ /*
+ * Paxcounter lib included in the firmware
+ * ENCODING: protobuf
+ */
+ PAXCOUNTER_APP = 34;
+
+ /*
+ * Provides a hardware serial interface to send and receive from the Meshtastic network.
+ * Connect to the RX/TX pins of a device with 38400 8N1. Packets received from the Meshtastic
+ * network is forwarded to the RX pin while sending a packet to TX will go out to the Mesh network.
+ * Maximum packet size of 240 bytes.
+ * Module is disabled by default can be turned on by setting SERIAL_MODULE_ENABLED = 1 in SerialPlugh.cpp.
+ * ENCODING: binary undefined
+ */
+ SERIAL_APP = 64;
+
+ /*
+ * STORE_FORWARD_APP (Work in Progress)
+ * Maintained by Jm Casler (MC Hamster) : jm@casler.org
+ * ENCODING: Protobuf
+ */
+ STORE_FORWARD_APP = 65;
+
+ /*
+ * Optional port for messages for the range test module.
+ * ENCODING: ASCII Plaintext
+ * NOTE: This portnum traffic is not sent to the public MQTT starting at firmware version 2.2.9
+ */
+ RANGE_TEST_APP = 66;
+
+ /*
+ * Provides a format to send and receive telemetry data from the Meshtastic network.
+ * Maintained by Charles Crossan (crossan007) : crossan007@gmail.com
+ * ENCODING: Protobuf
+ */
+ TELEMETRY_APP = 67;
+
+ /*
+ * Experimental tools for estimating node position without a GPS
+ * Maintained by Github user a-f-G-U-C (a Meshtastic contributor)
+ * Project files at https://github.com/a-f-G-U-C/Meshtastic-ZPS
+ * ENCODING: arrays of int64 fields
+ */
+ ZPS_APP = 68;
+
+ /*
+ * Used to let multiple instances of Linux native applications communicate
+ * as if they did using their LoRa chip.
+ * Maintained by GitHub user GUVWAF.
+ * Project files at https://github.com/GUVWAF/Meshtasticator
+ * ENCODING: Protobuf (?)
+ */
+ SIMULATOR_APP = 69;
+
+ /*
+ * Provides a traceroute functionality to show the route a packet towards
+ * a certain destination would take on the mesh.
+ * ENCODING: Protobuf
+ */
+ TRACEROUTE_APP = 70;
+
+ /*
+ * Aggregates edge info for the network by sending out a list of each node's neighbors
+ * ENCODING: Protobuf
+ */
+ NEIGHBORINFO_APP = 71;
+
+ /*
+ * ATAK Plugin
+ * Portnum for payloads from the official Meshtastic ATAK plugin
+ */
+ ATAK_PLUGIN = 72;
+
+ /*
+ * Provides unencrypted information about a node for consumption by a map via MQTT
+ */
+ MAP_REPORT_APP = 73;
+
+ /*
+ * Private applications should use portnums >= 256.
+ * To simplify initial development and testing you can use "PRIVATE_APP"
+ * in your code without needing to rebuild protobuf files (via [regen-protos.sh](https://github.com/meshtastic/firmware/blob/master/bin/regen-protos.sh))
+ */
+ PRIVATE_APP = 256;
+
+ /*
+ * ATAK Forwarder Module https://github.com/paulmandal/atak-forwarder
+ * ENCODING: libcotshrink
+ */
+ ATAK_FORWARDER = 257;
+
+ /*
+ * Currently we limit port nums to no higher than this value
+ */
+ MAX = 511;
+}
diff --git a/protobufs/meshtastic/remote_hardware.proto b/protobufs/meshtastic/remote_hardware.proto
new file mode 100644
index 0000000..ba4a693
--- /dev/null
+++ b/protobufs/meshtastic/remote_hardware.proto
@@ -0,0 +1,75 @@
+syntax = "proto3";
+
+package meshtastic;
+
+option csharp_namespace = "Meshtastic.Protobufs";
+option go_package = "github.com/meshtastic/go/generated";
+option java_outer_classname = "RemoteHardware";
+option java_package = "com.geeksville.mesh";
+option swift_prefix = "";
+
+/*
+ * An example app to show off the module system. This message is used for
+ * REMOTE_HARDWARE_APP PortNums.
+ * Also provides easy remote access to any GPIO.
+ * In the future other remote hardware operations can be added based on user interest
+ * (i.e. serial output, spi/i2c input/output).
+ * FIXME - currently this feature is turned on by default which is dangerous
+ * because no security yet (beyond the channel mechanism).
+ * It should be off by default and then protected based on some TBD mechanism
+ * (a special channel once multichannel support is included?)
+ */
+message HardwareMessage {
+ /*
+ * TODO: REPLACE
+ */
+ enum Type {
+ /*
+ * Unset/unused
+ */
+ UNSET = 0;
+
+ /*
+ * Set gpio gpios based on gpio_mask/gpio_value
+ */
+ WRITE_GPIOS = 1;
+
+ /*
+ * We are now interested in watching the gpio_mask gpios.
+ * If the selected gpios change, please broadcast GPIOS_CHANGED.
+ * Will implicitly change the gpios requested to be INPUT gpios.
+ */
+ WATCH_GPIOS = 2;
+
+ /*
+ * The gpios listed in gpio_mask have changed, the new values are listed in gpio_value
+ */
+ GPIOS_CHANGED = 3;
+
+ /*
+ * Read the gpios specified in gpio_mask, send back a READ_GPIOS_REPLY reply with gpio_value populated
+ */
+ READ_GPIOS = 4;
+
+ /*
+ * A reply to READ_GPIOS. gpio_mask and gpio_value will be populated
+ */
+ READ_GPIOS_REPLY = 5;
+ }
+
+ /*
+ * What type of HardwareMessage is this?
+ */
+ Type type = 1;
+
+ /*
+ * What gpios are we changing. Not used for all MessageTypes, see MessageType for details
+ */
+ uint64 gpio_mask = 2;
+
+ /*
+ * For gpios that were listed in gpio_mask as valid, what are the signal levels for those gpios.
+ * Not used for all MessageTypes, see MessageType for details
+ */
+ uint64 gpio_value = 3;
+}
diff --git a/protobufs/meshtastic/rtttl.proto b/protobufs/meshtastic/rtttl.proto
new file mode 100644
index 0000000..11c8b92
--- /dev/null
+++ b/protobufs/meshtastic/rtttl.proto
@@ -0,0 +1,19 @@
+syntax = "proto3";
+
+package meshtastic;
+
+option csharp_namespace = "Meshtastic.Protobufs";
+option go_package = "github.com/meshtastic/go/generated";
+option java_outer_classname = "RTTTLConfigProtos";
+option java_package = "com.geeksville.mesh";
+option swift_prefix = "";
+
+/*
+ * Canned message module configuration.
+ */
+message RTTTLConfig {
+ /*
+ * Ringtone for PWM Buzzer in RTTTL Format.
+ */
+ string ringtone = 1;
+}
diff --git a/protobufs/meshtastic/storeforward.proto b/protobufs/meshtastic/storeforward.proto
new file mode 100644
index 0000000..651eae5
--- /dev/null
+++ b/protobufs/meshtastic/storeforward.proto
@@ -0,0 +1,218 @@
+syntax = "proto3";
+
+package meshtastic;
+
+option csharp_namespace = "Meshtastic.Protobufs";
+option go_package = "github.com/meshtastic/go/generated";
+option java_outer_classname = "StoreAndForwardProtos";
+option java_package = "com.geeksville.mesh";
+option swift_prefix = "";
+
+/*
+ * TODO: REPLACE
+ */
+message StoreAndForward {
+ /*
+ * 001 - 063 = From Router
+ * 064 - 127 = From Client
+ */
+ enum RequestResponse {
+ /*
+ * Unset/unused
+ */
+ UNSET = 0;
+
+ /*
+ * Router is an in error state.
+ */
+ ROUTER_ERROR = 1;
+
+ /*
+ * Router heartbeat
+ */
+ ROUTER_HEARTBEAT = 2;
+
+ /*
+ * Router has requested the client respond. This can work as a
+ * "are you there" message.
+ */
+ ROUTER_PING = 3;
+
+ /*
+ * The response to a "Ping"
+ */
+ ROUTER_PONG = 4;
+
+ /*
+ * Router is currently busy. Please try again later.
+ */
+ ROUTER_BUSY = 5;
+
+ /*
+ * Router is responding to a request for history.
+ */
+ ROUTER_HISTORY = 6;
+
+ /*
+ * Router is responding to a request for stats.
+ */
+ ROUTER_STATS = 7;
+
+ /*
+ * Router sends a text message from its history that was a direct message.
+ */
+ ROUTER_TEXT_DIRECT = 8;
+
+ /*
+ * Router sends a text message from its history that was a broadcast.
+ */
+ ROUTER_TEXT_BROADCAST = 9;
+
+ /*
+ * Client is an in error state.
+ */
+ CLIENT_ERROR = 64;
+
+ /*
+ * Client has requested a replay from the router.
+ */
+ CLIENT_HISTORY = 65;
+
+ /*
+ * Client has requested stats from the router.
+ */
+ CLIENT_STATS = 66;
+
+ /*
+ * Client has requested the router respond. This can work as a
+ * "are you there" message.
+ */
+ CLIENT_PING = 67;
+
+ /*
+ * The response to a "Ping"
+ */
+ CLIENT_PONG = 68;
+
+ /*
+ * Client has requested that the router abort processing the client's request
+ */
+ CLIENT_ABORT = 106;
+ }
+
+ /*
+ * TODO: REPLACE
+ */
+ message Statistics {
+ /*
+ * Number of messages we have ever seen
+ */
+ uint32 messages_total = 1;
+
+ /*
+ * Number of messages we have currently saved our history.
+ */
+ uint32 messages_saved = 2;
+
+ /*
+ * Maximum number of messages we will save
+ */
+ uint32 messages_max = 3;
+
+ /*
+ * Router uptime in seconds
+ */
+ uint32 up_time = 4;
+
+ /*
+ * Number of times any client sent a request to the S&F.
+ */
+ uint32 requests = 5;
+
+ /*
+ * Number of times the history was requested.
+ */
+ uint32 requests_history = 6;
+
+ /*
+ * Is the heartbeat enabled on the server?
+ */
+ bool heartbeat = 7;
+
+ /*
+ * Maximum number of messages the server will return.
+ */
+ uint32 return_max = 8;
+
+ /*
+ * Maximum history window in minutes the server will return messages from.
+ */
+ uint32 return_window = 9;
+ }
+
+ /*
+ * TODO: REPLACE
+ */
+ message History {
+ /*
+ * Number of that will be sent to the client
+ */
+ uint32 history_messages = 1;
+
+ /*
+ * The window of messages that was used to filter the history client requested
+ */
+ uint32 window = 2;
+
+ /*
+ * Index in the packet history of the last message sent in a previous request to the server.
+ * Will be sent to the client before sending the history and can be set in a subsequent request to avoid getting packets the server already sent to the client.
+ */
+ uint32 last_request = 3;
+ }
+
+ /*
+ * TODO: REPLACE
+ */
+ message Heartbeat {
+ /*
+ * Period in seconds that the heartbeat is sent out that will be sent to the client
+ */
+ uint32 period = 1;
+
+ /*
+ * If set, this is not the primary Store & Forward router on the mesh
+ */
+ uint32 secondary = 2;
+ }
+
+ /*
+ * TODO: REPLACE
+ */
+ RequestResponse rr = 1;
+
+ /*
+ * TODO: REPLACE
+ */
+ oneof variant {
+ /*
+ * TODO: REPLACE
+ */
+ Statistics stats = 2;
+
+ /*
+ * TODO: REPLACE
+ */
+ History history = 3;
+
+ /*
+ * TODO: REPLACE
+ */
+ Heartbeat heartbeat = 4;
+
+ /*
+ * Text from history message.
+ */
+ bytes text = 5;
+ }
+}
diff --git a/protobufs/meshtastic/telemetry.proto b/protobufs/meshtastic/telemetry.proto
new file mode 100644
index 0000000..a822c5d
--- /dev/null
+++ b/protobufs/meshtastic/telemetry.proto
@@ -0,0 +1,286 @@
+syntax = "proto3";
+
+package meshtastic;
+
+option csharp_namespace = "Meshtastic.Protobufs";
+option go_package = "github.com/meshtastic/go/generated";
+option java_outer_classname = "TelemetryProtos";
+option java_package = "com.geeksville.mesh";
+option swift_prefix = "";
+
+/*
+ * Key native device metrics such as battery level
+ */
+message DeviceMetrics {
+ /*
+ * 0-100 (>100 means powered)
+ */
+ uint32 battery_level = 1;
+
+ /*
+ * Voltage measured
+ */
+ float voltage = 2;
+
+ /*
+ * Utilization for the current channel, including well formed TX, RX and malformed RX (aka noise).
+ */
+ float channel_utilization = 3;
+
+ /*
+ * Percent of airtime for transmission used within the last hour.
+ */
+ float air_util_tx = 4;
+}
+
+/*
+ * Weather station or other environmental metrics
+ */
+message EnvironmentMetrics {
+ /*
+ * Temperature measured
+ */
+ float temperature = 1;
+
+ /*
+ * Relative humidity percent measured
+ */
+ float relative_humidity = 2;
+
+ /*
+ * Barometric pressure in hPA measured
+ */
+ float barometric_pressure = 3;
+
+ /*
+ * Gas resistance in MOhm measured
+ */
+ float gas_resistance = 4;
+
+ /*
+ * Voltage measured (To be depreciated in favor of PowerMetrics in Meshtastic 3.x)
+ */
+ float voltage = 5;
+
+ /*
+ * Current measured (To be depreciated in favor of PowerMetrics in Meshtastic 3.x)
+ */
+ float current = 6;
+}
+
+/*
+ * Power Metrics (voltage / current / etc)
+ */
+message PowerMetrics {
+ /*
+ * Voltage (Ch1)
+ */
+ float ch1_voltage = 1;
+
+ /*
+ * Current (Ch1)
+ */
+ float ch1_current = 2;
+
+ /*
+ * Voltage (Ch2)
+ */
+ float ch2_voltage = 3;
+
+ /*
+ * Current (Ch2)
+ */
+ float ch2_current = 4;
+
+ /*
+ * Voltage (Ch3)
+ */
+ float ch3_voltage = 5;
+
+ /*
+ * Current (Ch3)
+ */
+ float ch3_current = 6;
+}
+
+/*
+ * Air quality metrics
+ */
+message AirQualityMetrics {
+ /*
+ * Concentration Units Standard PM1.0
+ */
+ uint32 pm10_standard = 1;
+
+ /*
+ * Concentration Units Standard PM2.5
+ */
+ uint32 pm25_standard = 2;
+
+ /*
+ * Concentration Units Standard PM10.0
+ */
+ uint32 pm100_standard = 3;
+
+ /*
+ * Concentration Units Environmental PM1.0
+ */
+ uint32 pm10_environmental = 4;
+
+ /*
+ * Concentration Units Environmental PM2.5
+ */
+ uint32 pm25_environmental = 5;
+
+ /*
+ * Concentration Units Environmental PM10.0
+ */
+ uint32 pm100_environmental = 6;
+
+ /*
+ * 0.3um Particle Count
+ */
+ uint32 particles_03um = 7;
+
+ /*
+ * 0.5um Particle Count
+ */
+ uint32 particles_05um = 8;
+
+ /*
+ * 1.0um Particle Count
+ */
+ uint32 particles_10um = 9;
+
+ /*
+ * 2.5um Particle Count
+ */
+ uint32 particles_25um = 10;
+
+ /*
+ * 5.0um Particle Count
+ */
+ uint32 particles_50um = 11;
+
+ /*
+ * 10.0um Particle Count
+ */
+ uint32 particles_100um = 12;
+}
+
+/*
+ * Types of Measurements the telemetry module is equipped to handle
+ */
+message Telemetry {
+ /*
+ * Seconds since 1970 - or 0 for unknown/unset
+ */
+ fixed32 time = 1;
+
+ oneof variant {
+ /*
+ * Key native device metrics such as battery level
+ */
+ DeviceMetrics device_metrics = 2;
+
+ /*
+ * Weather station or other environmental metrics
+ */
+ EnvironmentMetrics environment_metrics = 3;
+
+ /*
+ * Air quality metrics
+ */
+ AirQualityMetrics air_quality_metrics = 4;
+
+ /*
+ * Power Metrics
+ */
+ PowerMetrics power_metrics = 5;
+ }
+}
+
+/*
+ * Supported I2C Sensors for telemetry in Meshtastic
+ */
+enum TelemetrySensorType {
+ /*
+ * No external telemetry sensor explicitly set
+ */
+ SENSOR_UNSET = 0;
+
+ /*
+ * High accuracy temperature, pressure, humidity
+ */
+ BME280 = 1;
+
+ /*
+ * High accuracy temperature, pressure, humidity, and air resistance
+ */
+ BME680 = 2;
+
+ /*
+ * Very high accuracy temperature
+ */
+ MCP9808 = 3;
+
+ /*
+ * Moderate accuracy current and voltage
+ */
+ INA260 = 4;
+
+ /*
+ * Moderate accuracy current and voltage
+ */
+ INA219 = 5;
+
+ /*
+ * High accuracy temperature and pressure
+ */
+ BMP280 = 6;
+
+ /*
+ * High accuracy temperature and humidity
+ */
+ SHTC3 = 7;
+
+ /*
+ * High accuracy pressure
+ */
+ LPS22 = 8;
+
+ /*
+ * 3-Axis magnetic sensor
+ */
+ QMC6310 = 9;
+
+ /*
+ * 6-Axis inertial measurement sensor
+ */
+ QMI8658 = 10;
+
+ /*
+ * 3-Axis magnetic sensor
+ */
+ QMC5883L = 11;
+
+ /*
+ * High accuracy temperature and humidity
+ */
+ SHT31 = 12;
+
+ /*
+ * PM2.5 air quality sensor
+ */
+ PMSA003I = 13;
+
+ /*
+ * INA3221 3 Channel Voltage / Current Sensor
+ */
+ INA3221 = 14;
+
+ /*
+ * BMP085/BMP180 High accuracy temperature and pressure (older Version of BMP280)
+ */
+ BMP085 = 15;
+}
diff --git a/protobufs/meshtastic/xmodem.proto b/protobufs/meshtastic/xmodem.proto
new file mode 100644
index 0000000..732780a
--- /dev/null
+++ b/protobufs/meshtastic/xmodem.proto
@@ -0,0 +1,27 @@
+syntax = "proto3";
+
+package meshtastic;
+
+option csharp_namespace = "Meshtastic.Protobufs";
+option go_package = "github.com/meshtastic/go/generated";
+option java_outer_classname = "XmodemProtos";
+option java_package = "com.geeksville.mesh";
+option swift_prefix = "";
+
+message XModem {
+ enum Control {
+ NUL = 0;
+ SOH = 1;
+ STX = 2;
+ EOT = 4;
+ ACK = 6;
+ NAK = 21;
+ CAN = 24;
+ CTRLZ = 26;
+ }
+
+ Control control = 1;
+ uint32 seq = 2;
+ uint32 crc16 = 3;
+ bytes buffer = 4;
+}