From 47b5f8d1ea55c2ac10b6e4a1d26a7ab1afd7d18b Mon Sep 17 00:00:00 2001
From: Frederik Lindenaar <frederik@lindenaar.nl>
Date: Sat, 6 Apr 2019 15:20:52 +0200
Subject: [PATCH] Initial Check-in of CLI Library

---
 LICENSE.txt                           | 674 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 README.md                             | 499 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 examples/Blink/Blink.ino              | 226 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 examples/DS1307RTC/DS1307RTC.ino      |  94 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 examples/DS1307RTC/DS1307_Command.cpp | 114 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 examples/DS1307RTC/DS1307_Command.h   |  29 +++++++++++++++++++++++++++++
 examples/Debug/Debug.ino              |  71 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 keywords.txt                          |  59 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 library.json                          |  21 +++++++++++++++++++++
 library.properties                    |  12 ++++++++++++
 src/CLI.cpp                           | 178 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 src/CLI.h                             | 182 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 src/CLI_Utils.cpp                     | 110 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 src/Commands.cpp                      |  98 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 src/I2C_Commands.cpp                  | 110 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 15 files changed, 2477 insertions(+), 0 deletions(-)
 create mode 100644 LICENSE.txt
 create mode 100644 README.md
 create mode 100644 examples/Blink/Blink.ino
 create mode 100644 examples/DS1307RTC/DS1307RTC.ino
 create mode 100644 examples/DS1307RTC/DS1307_Command.cpp
 create mode 100644 examples/DS1307RTC/DS1307_Command.h
 create mode 100644 examples/Debug/Debug.ino
 create mode 100644 keywords.txt
 create mode 100644 library.json
 create mode 100644 library.properties
 create mode 100644 src/CLI.cpp
 create mode 100644 src/CLI.h
 create mode 100644 src/CLI_Utils.cpp
 create mode 100644 src/Commands.cpp
 create mode 100644 src/I2C_Commands.cpp

diff --git a/LICENSE.txt b/LICENSE.txt
new file mode 100644
index 0000000..94a9ed0
--- /dev/null
+++ b/LICENSE.txt
@@ -0,0 +1,674 @@
+                    GNU GENERAL PUBLIC LICENSE
+                       Version 3, 29 June 2007
+
+ Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
+ 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.
+
+    <one line to give the program's name and a brief idea of what it does.>
+    Copyright (C) <year>  <name of author>
+
+    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 <http://www.gnu.org/licenses/>.
+
+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:
+
+    <program>  Copyright (C) <year>  <name of author>
+    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
+<http://www.gnu.org/licenses/>.
+
+  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
+<http://www.gnu.org/philosophy/why-not-lgpl.html>.
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..5612a52
--- /dev/null
+++ b/README.md
@@ -0,0 +1,499 @@
+CLI Library
+===========
+
+Library to build a real-time serial (or network) command-line interface (CLI) to
+configure or control your Arduino or compatible microcontroller.
+
+This is **Version 1.0**, the latest version, documentation and bugtracker are
+available on my [GitLab instance](https://gitlab.lindenaar.net/arduino/CLI)
+
+Copyright (c) 2019 Frederik Lindenaar. free for distribution under the
+GNU License, see [below](#license)
+
+
+Introduction
+------------
+Frequently need to interact with a microcontroller to, for example:
+  * see what's stored in the embedded or an I2C EEPROM
+  * check that all I2C devices are detected and responding
+  * check or set a connected real-time clock
+  * check or set configuration options (I don't hard-code config)
+
+Since I don't like reprogramming the microcontroller every time this is needed,
+I wrote this library to provide a Command Line Interface over a Serial / USB
+line. The library includes a number of commands that can be included where
+applicable (which I expect will grow over time as I build more) so that it can
+be used as a toolbox to start your development without having to worry about
+stuff that could be standard. At this moment it includes the following commands:
+  * `eeprom_dump` to display the contents of the built-in EEPROM
+  * `i2c_scan` to scan the I2C bus for slave devices
+  * `i2c_dump` to display the contents of I2C attached EEPROM or Memory
+  * `reset` to restart the microcontroller (software reset)
+  * `help` to display available commands and provided help on how to use them
+
+See below how to use the Library, to get an idea on how to use the library,
+have a look at the examples included:
+  * _Blink_: control the Blink example (using the built-in LED) using a
+    Serial/USB console to change its blink rate or turn it on or off
+  * _Debug_: CLI with the built-in debug commands listed above
+  * _DS1307RTC_: CLI to read/set an DS1307 Real-Time Clock module (includes
+    the built-in commands to access a module's NVRAM and EEPROM)
+
+
+Download / Installation
+-----------------------
+At this moment this library is not yet available directly from the Arduino IDE
+but has to be installed manually. For this, download the latest distribution
+.zip file and install it using the following links:
+  * From my GitLab instance, download to your computer the
+    [Latest .zip archive](https://gitlab.lindenaar.net/arduino/CLI/repository/archive.zip)
+  * Follow the documentation for the Arduino IDE on
+    [importing a .zip Library](https://www.arduino.cc/en/Guide/Libraries#toc4).
+    Alternatively, you can also extract the downloaded .zip and follow the steps
+    for [manual Installation](https://www.arduino.cc/en/Guide/Libraries#toc5)
+
+You can also use `git` to checkout the latest version from my repository with
+
+```
+  git clone https://gitlab.lindenaar.net/arduino/CLI.git
+```
+
+so that it is easy to upgrade in the future. To find where to checkout check the
+guide for [manual Installation](https://www.arduino.cc/en/Guide/Libraries#toc5).
+
+
+Using the library
+-----------------
+Before adding this library to your code, it is important to realize that by
+adding the CLI you are builing a so-called real-time system. The microcontroller
+should be able to perform it's main task (normally not responding to Serial/USB
+input) and in the background listen to commands given through the CLI. As most
+microcontrollers have only a single CPU and no OS that can multitask, all logic
+is handled from the main `loop()` function. As a consequence, one should avoid
+writing code that waits (e.g. using the `delay()` function) but instead create
+a loop that does not wait/block but determines whether to do something and if
+not moves on to the next task.
+
+### Writing a real-time (i.e. non-blocking) loop
+Looking at the Arduino IDE's standard Blink example (probably the starting point
+for most when starting with the platform), you see the following code in `loop`:
+
+~~~
+// the loop function runs over and over again forever
+void loop() {
+  digitalWrite(LED_BUILTIN, HIGH);   // turn LED on (HIGH is the voltage level)
+  delay(1000);                       // wait for a second
+  digitalWrite(LED_BUILTIN, LOW);    // turn LED off by making the voltage LOW
+  delay(1000);                       // wait for a second
+}
+~~~
+
+While this is perfectly fine code when the microcontroller has nothing else to
+do, for a background CLI this would cause 1 second delays between each check for
+input (ignoring more complex interrupt-driven approaches, which have their own
+challenges). The key for making this loop non-blocking is to change it so that
+it no longer waits but each time checks if it needs to change the LED status
+instead, like in the example below:
+
+~~~
+// variable to be preserved
+unsigned long last_blink = 0;
+
+// the loop function runs over and over again forever
+void loop() {
+  // millis() will wrap every 49 days, below code is wrap-proof
+  if (millis() - last_blink > 1000) {  // last_blink was longer ago than delay?
+    last_blink = millis();             // led state will change, store when
+    if(digitalRead(LED_BUILTIN)) {     // check if the LED is on or off
+      digitalWrite(LED_BUILTIN, LOW);  // turn LED off by making the pin LOW
+    } else {
+      digitalWrite(LED_BUILTIN, HIGH); // turn LED on by making the pin HIGH
+    }
+  }
+}
+~~~
+
+This code uses the Arduino platform function `millis()` to obtain how long the
+code is running (in milliseconds) and keeps track of when the LED state changed
+last in variable `last_blink` (stored outside the loop so it is preserved).
+The loop now simply checks every time whether the last blink was more than 1000
+milliseconds ago and if so changes the LED state, otherwise it does nothing.
+Please note that the above code was kept as much in line with the original Blink
+example though could also be written as:
+
+~~~
+// variable to be preserved
+unsigned long last_blink = 0;
+
+// the loop function runs over and over again forever
+void loop() {
+  // millis() will wrap every 49 days, below code is wrap-proof
+  if (millis() - last_blink > 1000) {  // last_blink was longer ago than delay?
+    last_blink = millis();             // led state will change, store when
+    digitalWrite(LED_BUILTIN, !digitalRead(LED_BUILTIN));   // invert LED PIN
+  }
+}
+~~~
+
+Once the loop of your code is non-blocking, the CLI can be added to your Sketch.
+
+### Adding the CLI to a Sketch
+Adding the CLI to your sketch is pretty straightforward, first include `CLI.h`
+at the top of your sketch like this:
+
+~~~
+#include <CLI.h>
+~~~
+
+Next instantiate the CLI object by adding the following above (and outside) your
+`setup()` and `loop()` functions:
+
+~~~
+// Initialize the Command Line Interface
+CLI CLI(Serial);           // Initialize the CLI, telling it to attach to Serial
+~~~
+
+Directly under the instantation of the `CLI` object commands can be instantiated
+(added) like is shown below for the built-in Help command:
+
+~~~
+Help_Command Help(CLI);    // Initialize/Register (built-in) help command
+~~~
+
+The constructor of each command requires a `CLI` to register with to make the
+implemented command available in the CLI.
+Next in your `loop()`, place the following logic at the top:
+
+~~~
+// handle CLI, if this returns true a command is running so skip code block
+if (!CLI.process()) {
+  // Code to run when no command is executing, make sure it is non-blocking...
+}
+// Code to execute every loop goes here, make sure it is non-blocking...
+~~~
+
+The `CLI.process()` method handles the Command Line Interpreter; it responds to
+user input, parses the command and executes the command code. To ensure that
+this is also non-blocking, each of these steps is executed in smaller chunks so
+that your main logic can be intertwined with the execution of the CLI logic. The
+`CLI.process()` method will return `true` if a command is still executing so by
+placing logic inside the `if() { ... }` block, you ensure it only runs when the
+CLI is not doing anything while if you put it outside the block it will always
+be executed. This can also be used to add an LED indicator to display whether
+the CLI is executing as is shown in the below full sketch example (which shows
+how a basic full implementation would look like):
+
+~~~
+#include <CLI.h>
+
+// Initialize the Command Line Interface
+CLI CLI(Serial);           // Initialize the CLI, telling it to attach to Serial
+Help_Command Help(CLI);    // Initialize/Register (built-in) help command
+
+
+// the setup function runs once when you reset or power the board
+void setup() {
+  // Initialize digital pin LED_BUILTIN as an output.
+  pinMode(LED_BUILTIN, OUTPUT);
+
+  // Initialize the Serial port for the CLI
+  while (!Serial); // For Leonardo: wait for serial USB to connect
+  Serial.begin(9600);
+}
+
+
+// the loop function runs over and over again forever
+void loop() {
+  // handle CLI, if this returns true a command is running so skip code block
+  if (CLI.process()) {
+    digitalWrite(LED_BUILTIN, HIGH); // turn LED on when processing CLI command
+  } else {
+    digitalWrite(LED_BUILTIN, LOW); // turn LED off when CLI command not active
+    // Code to run when no command is executing, make sure it is non-blocking...
+  }
+  // Code to execute every loop goes here, make sure it is non-blocking...
+}
+~~~
+
+You can run the above code by creating a new sketch and replacing its contents
+with the full example above. Please also have a look at the examples provided as
+they give a better view on what can be done and how to include a CLI in your
+sketch.
+
+#### CLI on the Serial port
+As you can see in the above example, the CLI object is initiated and on `Serial`
+and used to instantiate the `Help_Command` while `Serial.begin()` is only called
+from setup() (i.e. afterwards). Due to the way the initialization of the Arduino
+platform works, it is not possible to use `Serial.begin()` before `setup()` is
+called as things like interrupts and other boot-strap initialization (including
+that of the Serial port) has not taken place yet. For this reason the CLI object
+cannot initialize a `Serial` port but you have to do this from your `setup()`
+routine (which doesn't really matter and also gives you full control over it)
+but should not be forgotten (as the CLI won't work then).
+Please note that due to this it is also not possible to use `Serial.print()` in
+a constructor (this has nothing do to with this library but is a quirk of the
+Arduino platform).
+
+#### CLI object parameters
+The CLI object supports printing a banner upon startup and allows to configure
+the defaults prompt ( `> `). To reduce the memory usage, the passed string for
+either must be stored in `PROGMEM` (program memory, i.e. with the program in
+Flash). Both parameters can be passed to the constructor when initializing the
+`CLI` object like this:
+
+~~~
+const char CLI_banner[] PROGMEM = "My Microcontroller v1.0 CLI";
+const char CLI_prompt[] PROGMEM = "mm> ";
+CLI CLI(Serial, CLI_banner, CLI_prompt);
+~~~
+
+Currently both must hence be hardcoded static strings and there is no way to
+make them dynamic or change them. This is a conscious design choice to reduce
+the size of the code and the memory it requires. In case you have a good use
+case to reconsider this decision, let's discuss and for that please do
+(raise an issue here)[https://gitlab.lindenaar.net/arduino/CLI/issues].
+
+#### CLI is a `Stream`
+The CLI Object implements a Stream so it can be used as well to interact with
+the user, both from a command as well as from your main loop. Besides the CLI
+implementation described in this document and the `print()` family of functions,
+it also provides:
+  * `print_P (const char *str PROGMEM)` to print a string stored in `PROGMEM`
+  * `print2digits(uint8_t num, char filler = '0', uint8_t base=10)`
+    to print a number with at least 2 digits (useful for `HEX` bytes)
+  * `print_mem(uint16_t addr, const uint8_t buff[], uint8_t len, uint8_t width=16)`
+    to dump a memory block in HEX and ASCII (`addr` is the startaddress to print)
+
+
+### Implementing a Command
+CLI commands are implemented as separate classes that register themselves with
+the CLI upon instantiation. The actions to implement for a command are:
+  1. instantiate - the command's class is instantiated and registers with a CLI
+  2. set parameters - the command parses the user's parameters for the execution
+  3. execute - the command performs it's tasks using the parameters provided.
+
+To implement a new CLI command, one should inherit from `CLI_Command`
+
+~~~
+class CLI_Command {
+  public:
+    CLI_Command(CLI &cli, const char *command PROGMEM,
+                          const char *description PROGMEM,
+                          const char *help PROGMEM = NULL);
+    virtual bool setparams(const char *params);
+    virtual bool execute(CLI &cli) = 0;
+};
+~~~
+
+and implement it's public methods. At least a constructor (calling the one from
+`CLI_Command`) and the `execute()` method must be implemented. The class has a
+default implementation for the `setparams()` method that accepts no parameters
+that can be used for commands that do need parameters. The details of the
+implementation steps are covered in the next sections to demonstrate how to
+implement a "hello" command accepting a parameter and printing that.
+
+#### Implementation (Class Definition)
+The first step for the implementation of our "hello" command is to define a
+class `Hello_Command` inheriting from `CLI_Command`. In this case we implement
+all three methods as we want to accept parameter(s) and need the private
+variable `_params` to store it in `setparams()` to be used by `execute()`. The
+definition of this basic class looks like:
+
+~~~
+class Hello_Command : CLI_Command {
+  const char *_params;
+  public:
+    Hello_Command(CLI &cli);
+    bool setparams(const char *params);
+    bool execute(CLI &cli);
+};
+~~~
+
+Although it is possible to define the method in the definition, in this example
+they will be defined separately first. See the full code at the end of this
+section that implements the methods inline.
+
+#### Instantiate (Class constructor)
+The implementation of the constructor can be very simple; call the constructor
+of `CLI_Command` with the following parameters:
+  1. instance of `CLI` class to register with (should be passed as parameter)
+  2. (static) string in `PROGMEM` with the name of the command
+  3. (static) string in `PROGMEM` with a short (1-line) command description
+  4. (static) string in `PROGMEM` with additional usage information
+
+below the implementation of the example "hello" command with empty constructor
+(as no functional initiation is required) only calling the parent `CLI_Command`
+with the above parameters:
+
+~~~
+Hello_Command::Hello_Command(CLI &cli) : CLI_Command(&cli,
+        PSTR("hello"),
+        PSTR("Print an \"Hello\" greeting message"),
+        PSTR("Usage:\thello <name>\n"
+             "Where:\t<name>\ta string to include in the greeting")) { };
+~~~
+
+The above uses the `PSTR()` macro to inline define the static strings for the
+command name, description and help text. These normally are static and hence
+hardcoded. The base implementation of the `Command` class takes care of storing
+the references and making them available. It will also register the command with
+the `CLI` instance provided. As already mentioned, the library assumes these are
+in `PROGMEM` so please make sure your sketch stores them there.
+
+#### Set parameters (`setparams(const char *params)` method)
+When the user invokes a command, the `setparams()` method is called with the
+parameters the user provided after the command. This allows the command to
+parse the parameters provided and do whatever is necessary for the command to
+use this information (i.e. store it in an efficient way for `execute()`).
+
+The `setparams()` method is always called once when the user enters a command so
+that it can ensure that everything is ready for the command's `execute()` method
+to be invoked. It should return `true` in case the parameters are valid. In case
+`setparams()` returns `false`, the execution of the command is aborted and its
+`execute()` is never called but a standard error message is given instead.
+
+The `params` provided is a pointer to the start of the parameters with trailing
+spaces removed and terminated with a `char(0)`. In case no (or only whitespace)
+parameters were provided, this method is called with `NULL` so an Implementation
+does not have to check for empty strings. As the CLI should not block the main
+flow, make sure the parsing is efficient and keep it simple so that this method
+(which is only called once for each command) does not take long.
+
+A very simple implementation for `setparams()` is provided below for our `hello`
+command. This simply stores the pointer of the parameter string provided an will
+result in a `true` result in case a parameter is provided or a `false` result
+when the user did not provide any parameters.
+
+~~~
+bool Hello_Command::setparams(const char *params) {
+  _params = params;
+  return (params);
+}
+~~~
+
+Often the parse will more complex as it is needs to parse the string provided.
+The design choice to have this implemented in the command is that this provides
+the greatest flexibility and does not assume anything w.r.t. how or which
+parameters are passed. The library does include a number of support functions
+that can be used to parse parameters and encode them in flags.
+
+Please note that any attribute / variable used to store parameters are global
+(part of the object memory) so will eat up ram. Use them wisely so you don't
+run out of RAM on the smaller Arduino platforms that have only 2Kb or RAM.
+
+#### Execute logic (`execute(CLI &cli)` method)
+The `execute()` method contains the actual implementation of the command. It is
+called with a reference to the `CLI` so that there is no need to store that in
+the object. To support a real-time implementation even if the command needs a
+longer time (or is waiting), the implementation can return `true` to pause the
+current invocation of the `execute` method and will be called in the next main
+`loop()` cycle again. This allows for returning to the main loop in between of
+waiting or execution of the command so that the main loop can continue as well.
+
+Before `execute()` is the first time, `setparams()` is called once to process
+parameters from the user and perform initialization or setup needed. As long
+as `execute()` returns `true` it will continue to be invoked again until it
+returns `false` to signal that the command's execution is complete. The Command
+Line Interface will only process user input again when the command is completed
+(so a command can also prompt the user for additional input)
+
+Below the implementation of the `hello` command, which is pretty simple as it
+only prints "Hello" (stored in `PROGMEM` followed by the string the user
+provided. It then returns `false` to signal to the `CLI` that it is done.
+
+~~~
+bool Hello_Command::execute(CLI &cli) {
+  cli.print_P(PSTR("Hello "));
+  cli.println(_params);
+  return false;
+}
+~~~
+
+Commands can be as complex as needed. For more extensive examples, please have a
+look at the provided examples and built-in commands in `Commands.cpp`.
+
+#### Full Example Sketch for the Hello command
+Below the full example sketch built up in the previous sections with the methods
+implemented inline:
+
+~~~
+#include <CLI.h>
+
+class Hello_Command : CLI_Command {
+    const char *_params;
+  public:
+    Hello_Command(CLI &cli) :
+      CLI_Command(cli,
+                  PSTR("hello"),
+                  PSTR("Print an \"Hello\" greeting message"),
+                  PSTR("Usage:\thello <name>\n"
+                       "Where:\t<name>\tstring to include in greeting")) { };
+    bool setparams(const char *params) {
+      _params = params;
+      return (params);
+    }
+    bool execute(CLI &cli) {
+      cli.print_P(PSTR("Hello "));
+      cli.println(_params);
+      return false;
+    }
+};
+
+// Initialize the Command Line Interface
+const char CLI_banner[] PROGMEM = "Hello CLI v1.0";
+CLI CLI(Serial, CLI_banner); // Initialize CLI, telling it to attach to Serial
+Hello_Command Hello(CLI);    // Initialize/Register above defined hello command
+Help_Command Help(CLI);      // Initialize/Register (built-in) help command
+
+
+// the setup function runs once when you reset or power the board
+void setup() {
+  // Initialize digital pin LED_BUILTIN as an output.
+  pinMode(LED_BUILTIN, OUTPUT);
+
+  // Initialize the Serial port for the CLI
+  while (!Serial); // For Leonardo: wait for serial USB to connect
+  Serial.begin(9600);
+}
+
+
+// the loop function runs over and over again forever
+void loop() {
+  // handle CLI, if this returns true a command is running so skip code block
+  if (CLI.process()) {
+    digitalWrite(LED_BUILTIN, HIGH); // turn LED on when processing CLI command
+  } else {
+    digitalWrite(LED_BUILTIN, LOW); // turn LED off when CLI command not active
+    // Code to run when no command is executing, make sure it is non-blocking...
+  }
+  // Code to execute every loop goes here, make sure it is non-blocking...
+}
+~~~
+
+I hope this clarifies how this library can and should be used. In case you find
+any issues with the documentation, code or examples, please do raise an issue
+[here](https://gitlab.lindenaar.net/arduino/CLI/issues).
+
+
+### Parser Support Functions
+The library contains a number of support functions to ease with building a
+parser for command parameters. These still need to be documented but can be found
+already in `CLI_Utils.cpp` within the library and are used by the built-in
+commands found in `Commands.cpp` and exampled included.
+
+
+<a name="license">License</a>
+=============================
+This library, documentation and examples are free software: you can redistribute
+them and/or modify them 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 script, documentation and configuration examples are 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, download it from <http://www.gnu.org/licenses/>.
+
diff --git a/examples/Blink/Blink.ino b/examples/Blink/Blink.ino
new file mode 100644
index 0000000..b434e3f
--- /dev/null
+++ b/examples/Blink/Blink.ino
@@ -0,0 +1,226 @@
+/*
+   Blink.ino - CLI library sample implementing a CLI for the Blink sample code
+
+   Version 1.0, latest version, documentation and bugtracker available at:
+                https://gitlab.lindenaar.net/arduino/CLI
+
+   Copyright (c) 2019 Frederik Lindenaar
+
+   This example shows how to build a simple command line to control the default
+   Blink example of the Arduino IDE. This requires additional code to control
+   the parameters (maintained within the C++ objects for the commands) as well
+   as a different main loop logic to become a real-time process with a Command
+   Line in te backgrouns while blinking and no longer using delay() for timing.
+
+   The implementation provides the following commands available through the
+   Serial Monitor (available from the Tools menu of the Arduino IDE):
+     - mode     display and/or set the LED mode (either on, off or blink)
+     - delay    display and/or set the blinking delay for the led
+     - faster   double the blinking rate (half the delay)
+     - slower   half the blinking rate (double the delay)
+     - help     show the available commands and how to use (builtin command)
+
+   For each of the commands an CLI_Command derived class is implemented with :
+     - Private variables to hold the internal state of the command
+     - Object constructor to set default values
+     - a setparams() method to handle parameters (when applicable)
+     - a execute() method for the implementation of the command
+     - Getter (and Setter) methods to access object state (where applicable)
+
+   This sketch is free software: you can redistribute it and/or modify it under
+   the terms of version 3 of the GNU General Public License as published by the
+   Free Software Foundation, or (at your option) a later version of the license.
+
+   This code 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, visit <http://www.gnu.org/licenses/> to download it.
+*/
+
+#include <CLI.h>
+
+enum LED_MODE { OFF, ON, BLINKING };    // LED modes implemented/supported
+
+#define BLINK_MIN_DELAY 25              // min blink delay is 25ms
+#define BLINK_MAX_DELAY 10000           // max blink delay is 10s
+
+// Convert macro to string (https://gcc.gnu.org/onlinedocs/cpp/Stringizing.html)
+#define TO_STRING(val) #val
+#define VAL_TO_STRING(val)  TO_STRING(val)
+
+
+// Store mode command parameters as static PROGMEM strings (needed below)
+const char CMD_MODE_PARAM_ON[]    PROGMEM = "on";
+const char CMD_MODE_PARAM_OFF[]   PROGMEM = "off";
+const char CMD_MODE_PARAM_BLINK[] PROGMEM = "blink";
+
+// struct stored in PROGMEM to map the parameters for mode command to a value
+const struct CLI_Command_Param Mode_Command_Parameters[] PROGMEM = {
+  { CMD_MODE_PARAM_ON,    ON,       },
+  { CMD_MODE_PARAM_OFF,   OFF,      },
+  { CMD_MODE_PARAM_BLINK, BLINKING, },
+  { NULL }
+};
+
+
+class Mode_Command : CLI_Command {                          // Implementation of the mode command
+    LED_MODE _mode;                                         // Private variable to store current mode
+  public:
+    Mode_Command(CLI &cli, LED_MODE mode = OFF) :           // Constructor with default initial mode
+      CLI_Command(cli,                                      // CLI to register with
+                  PSTR("mode"),                             // Command name
+                  PSTR("Set and/or show LED mode"),         // Description
+                  PSTR("Usage:\tmode [<led_mode>]\n"        // Usage Help
+                       "where <led_mode> is one of:\n"
+                       "\ton\tturn LED on\n"
+                       "\toff\tturn LED off\n"
+                       "\tblink\tblink the LED\n")), _mode(mode) { };
+    bool setparams(const char *params) {        // Called once before execute to process parameters
+      if (params) {                             // Check if we have parameters
+        // Use the CLI_STR_parseParam_P utility function to parse the parameter
+        const struct CLI_Command_Param *p = CLI_STR_parseParam_P(params, Mode_Command_Parameters);
+        if (!p) return false;                   // return false in case of an invalid parameter
+        _mode = pgm_read_word(&p->uint16);      // set led_mode to the one of the parameter
+        if (_mode != BLINKING)                  // ensure LED state == led_mode if not blinking
+          digitalWrite(LED_BUILTIN, _mode == ON);
+      }
+      return true;                              // Return true in case of no or a valid parameter
+    }
+    bool execute(CLI &cli) {                    // Implementation, called as long as it returns true
+      cli.print_P(PSTR("LED mode is "));        // Simply show the LED mode (need to do that always)
+      cli.print_P((_mode == ON) ? PSTR("ON") :
+                  (_mode == OFF) ? PSTR("OFF") :
+                  (_mode == BLINKING) ? PSTR("blink") : PSTR("unknown"));
+      return false;                             // return false as we're done
+    }
+    inline LED_MODE get() {                     // Getter for the mode
+      return _mode;
+    }
+    inline void set(LED_MODE mode) {            // Setter for the mode
+      _mode = mode;
+    }
+};
+
+
+class Delay_Command : CLI_Command {                         // Implementation of the delay command
+    unsigned int _delay;                                    // Private variable to store current delay
+  public:
+    Delay_Command(CLI &cli) :                               // Constructor
+      CLI_Command(cli,                                      // CLI to register with
+                  PSTR("delay"),                            // Command name
+                  PSTR("set and/or show LED blink delay"),  // Description
+                  PSTR("Usage:\tdelay [<blink_delay>]\n"    // Usage Help
+                       "\twhere <blink_delay> is the delay in milliseconds (integer between "
+                       VAL_TO_STRING(BLINK_MIN_DELAY) " and " VAL_TO_STRING(BLINK_MAX_DELAY) ")\n")) {
+      _delay = 1000;
+    };
+    bool setparams(const char *params) {        // Called once before execute to process parameters
+      if (params) {                             // Check if we have parameters
+        int d;
+        params = CLI_STR_parseInt(params, d);   // parse params using CLI_STR_parseInt utility function
+        if (params && !*params) {               // check if an int was found and reached end of params
+          return set(d);                        // return true if all OK or false when not within range
+        }
+        return false;                           // return false if no int or more param input was found
+      }
+      return true;                              // return true in case of no or a valid parameter
+    }
+    bool execute(CLI &cli) {                    // Implementation, called as long as it returns true
+      cli.print_P(PSTR("blink delay "));        // Simply show the blink delay (need to do that always)
+      cli.print(_delay);
+      cli.print_P(PSTR(" milliseconds\n"));
+      return false;                             // return false as we're done
+    }
+    inline unsigned int get() {                 // Getter for the mode
+      return _delay;
+    }
+    inline bool set(unsigned int d) {           // Setter for the mode, checking it is within range
+      if (d < BLINK_MIN_DELAY || d > BLINK_MAX_DELAY)
+        return false;                           // Return false if new value is out of bounds
+      _delay = d;                               // store new value
+      return true;                              // return true as new value is OK
+    }
+};
+
+
+class Faster_Command : CLI_Command {                        // Implementation of the faster command
+    Delay_Command &_delay;                                  // Private variable to store delay command
+  public:
+    Faster_Command(CLI &cli, Delay_Command &delay) :        // Constructor, requires delay command reference
+      CLI_Command(cli,                                      // CLI to register with
+                  PSTR("faster"),                           // Command name
+                  PSTR("Blink faster")),                    // Description, no Usage Help provided
+      _delay(delay) { };                                    // Store reference to delay command, empty constructor
+    // Please note: no parameters will be accepted (using default setparams() implementation)
+    bool execute(CLI &cli) {                    // Implementation, called as long as it returns true
+      if (_delay.set(_delay.get() / 2)) {       // Half the blink time, returns false if outside range
+        _delay.setparams(0);                    // clear _delay's parameters as we call it's execute() next
+        _delay.execute(cli);                    // call _delay's execute() method to print the delay
+      } else {
+        cli.print_P(PSTR("Can't blink faster"));// print an error message if half blink delay is out of bounds
+      }
+      return false;                             // return false as we're done
+    }
+};
+
+
+class Slower_Command : CLI_Command {                        // Implementation of the slower command
+    Delay_Command &_delay;                                  // Private variable to store delay command
+  public:
+    Slower_Command(CLI &cli, Delay_Command &delay) :        // Constructor, requires delay command reference
+      CLI_Command(cli,                                      // CLI to register with
+                  PSTR("slower"),                           // Command name
+                  PSTR("Blink slower")),                    // Description, no Usage Help provided
+      _delay(delay) { };                                    // Store reference to delay command, empty constructor
+    // Please note: no parameters will be accepted (using default setparams() implementation)
+    bool execute(CLI &cli) {                    // Implementation, called as long as it returns true
+      if (_delay.set(_delay.get() * 2)) {       // Half the blink time, returns false if outside range
+        _delay.setparams(0);                    // clear _delay's parameters as we call it's execute() next
+        _delay.execute(cli);                    // call _delay's execute() method to print the delay
+      } else {
+        cli.print_P(PSTR("Can't blink slower"));// print an error message if half blink delay is out of bounds
+      }
+      return false;                             // return false as we're done
+    }
+};
+
+
+// Initialize the Blink Command Line Interface
+const char banner[] PROGMEM = "Blink Sample CLI"; // Banner to show upon startup of the CLI
+CLI CLI(Serial, banner);                          // Initialize the CLI, telling it to attach to Serial
+Mode_Command Mode(CLI, BLINKING);                 // Initialize/Register mode command, BLINKING initially
+Delay_Command Delay(CLI);                         // Initialize/Register delay command
+Faster_Command Faster(CLI, Delay);                // Initialize/Register faster command
+Slower_Command Slower(CLI, Delay);                // Initialize/Register slower command
+Help_Command Help(CLI);                           // Initialize/Register (built-in) help command
+
+
+// the setup function runs once when you reset or power the board
+void setup() {
+  // initialize digital pin LED_BUILTIN as an output.
+  pinMode(LED_BUILTIN, OUTPUT);
+
+  // Initialize the Serial port for the CLI
+  while (!Serial); // For Leonardo: wait for serial USB to connect
+  Serial.begin(9600);
+}
+
+// variable to be preserved
+unsigned long last_blink = 0;
+
+// the loop function runs over and over again forever
+void loop() {
+  // First handle CLI, if this returns true, a command is running and the code
+  // block with blink logic is skipped till the command has finished executing
+  if (!CLI.process()) {
+    // check if mode is blinking and last_blink was longer ago than delay
+    // millis() will wrap every 49 days, below approach is wrap-proof
+    if (Mode.get() == BLINKING && millis() - last_blink > Delay.get()) {
+      last_blink = millis();             // led state will change, store when
+      digitalWrite(LED_BUILTIN, !digitalRead(LED_BUILTIN));   // invert LED PIN
+    }
+  }
+}
+
diff --git a/examples/DS1307RTC/DS1307RTC.ino b/examples/DS1307RTC/DS1307RTC.ino
new file mode 100644
index 0000000..34e2f97
--- /dev/null
+++ b/examples/DS1307RTC/DS1307RTC.ino
@@ -0,0 +1,94 @@
+/*
+   DS1307RTC.ino - CLI library sample implementing a CLI to control a DS1307 RTC
+
+   Version 1.0, latest version, documentation and bugtracker available at:
+                https://gitlab.lindenaar.net/arduino/CLI
+
+   Copyright (c) 2019 Frederik Lindenaar
+
+   This example shows how to add commands to read and set an DS1307 RTC (Real
+   Time Clock) module to the CLI. It extends the Debug example so that the CLI
+   library debugging commands are also included to check the I2C bus wiring and
+   access the module's NVRAM and EEPROM. The implementation uses Paul
+   Stoffregen's DS1307RTC library (https://github.com/PaulStoffregen/DS1307RTC)
+   so please ensure you have added that (and it's dependency TimeLib) through
+   the Aruino IDE's built-in library manager.
+
+   The following commands are available through the Serial Monitor (available
+   from the Tools menu of the Arduino IDE):
+     - ds1307           Set and/or show DS1307 RTC date and time
+     - eeprom_dump	dump the contents of the built-in EEPROM
+     - i2c_scan         scan the I2C bus for slave devices
+     - i2c_dump         dump the contents of I2C attached EEPROM or Memory
+     - reset            restart the microcontroller (software reset)
+     - help             show the available commands and how to use them
+
+   To read the the DS1307 use:
+     ds1307                             show the current time of the DS1307
+     i2c_dump 0x68 64 8 single          show NVRAM (assuming device ID is 0x68)
+                                        (dump 56-byte NVRAM, start at offset 8)
+
+   To set the DS1307 clock use:
+     ds1307 2019-01-01 12:34:56         to set both the date and time
+     ds1307 2019-01-31                  to set only the date
+     ds1307 12:34                       to set only the time (seconds set to 0)
+
+   When your module also has an EEPROM (assuming device ID is 0x50) use:
+     i2c_dump 0x50 4096                 dump 24LC32 EEPROM contents (4k)
+
+   Please note that the ds1307 command is implemented in separate files (that
+   opens in a separate tab in the Arduino IDE)
+
+   This sketch is free software: you can redistribute it and/or modify it under
+   the terms of version 3 of the GNU General Public License as published by the
+   Free Software Foundation, or (at your option) a later version of the license.
+
+   This code 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, visit <http://www.gnu.org/licenses/> to download it.
+*/
+
+#include <CLI.h>
+#include <Wire.h>
+
+#include "DS1307_Command.h"
+
+// Initialize the Debug Command Line Interface
+const char banner[] PROGMEM = "DS1307RTC Sample CLI"; // Banner to show upon startup of the CLI
+CLI CLI(Serial, banner);                          // Initialize the CLI, telling it to attach to Serial
+DS1307_Command DS1307RTC(CLI);                    // Initialize/Register ds1307 command
+EEPROM_Dump_Command EEPROM_Dump(CLI);             // Initialize/Register (built-in) eeprom_dump command
+I2C_Scan_Command I2C_Scan(CLI);                   // Initialize/Register (built-in) i2c_scan command
+I2C_Dump_Command I2C_Dump(CLI);                   // Initialize/Register (built-in) i2c_dump command
+Reset_Command Reset(CLI);                         // Initialize/Register (built-in) reset command
+Help_Command Help(CLI);                           // Initialize/Register (built-in) help command
+
+
+// the setup function runs once when you reset or power the board
+void setup() {
+  // initialize digital pin LED_BUILTIN as an output.
+  pinMode(LED_BUILTIN, OUTPUT);
+
+  // Initialize the Serial port for the CLI
+  while (!Serial); // For Leonardo: wait for serial USB to connect
+  Serial.begin(9600);
+
+  // Initialize the Wire Interface
+  Wire.begin();
+
+}
+
+
+// the loop function runs over and over again forever
+void loop() {
+  // handle CLI, if this returns true a command is running. Set Builtin LED accordingly
+  if (CLI.process()) {
+    digitalWrite(LED_BUILTIN, HIGH); // turn LED on when processing CLI command
+  } else {
+    digitalWrite(LED_BUILTIN, LOW); // turn LED off when not processing CLI command
+  }
+}
+
diff --git a/examples/DS1307RTC/DS1307_Command.cpp b/examples/DS1307RTC/DS1307_Command.cpp
new file mode 100644
index 0000000..8bbc889
--- /dev/null
+++ b/examples/DS1307RTC/DS1307_Command.cpp
@@ -0,0 +1,114 @@
+/*
+   DS1307_Command.cpp - CLI library for Arduino/ESP8266 - DS1307 Command implementation
+
+   Version 1.0, latest version, documentation and bugtracker available at:
+                https://gitlab.lindenaar.net/arduino/CLI
+
+   Copyright (c) 2019 Frederik Lindenaar
+
+   This library is free software: you can redistribute it and/or modify it under
+   the terms of version 3 of the GNU General Public License as published by the
+   Free Software Foundation, or (at your option) a later version of the license.
+
+   This code 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, visit <http://www.gnu.org/licenses/> to download it.
+*/
+
+#include <TimeLib.h>
+#include <DS1307RTC.h>
+
+#include "DS1307_Command.h"
+
+DS1307_Command::DS1307_Command(CLI &cli) :                              // Constructor for ds1307 command
+  CLI_Command(cli,                                                      // CLI to register with
+              PSTR("ds1307"),                                           // Command name
+              PSTR("Set and/or show DS1307 RTC date and time"),         // Description
+              PSTR("Usage:\tds1307 [YYYY-MM-DD] [HH:MM[:SS]]\n"         // Usage Help
+                   "where YYYY-MM-DD is the date in ISO format\n"
+                   "  and HH:MM[:SS] the new time (seconds are optional)\n")) { };
+
+// macro to determine whether the given year is a leap year
+#define LEAP_YEAR(y)    (!((y) % 4) && (((y) % 100 ) || !((y) % 400) ))
+
+inline uint8_t monthDays(int y, uint8_t m) {    // get number of days for month
+  return (m == 2) ? 28 + LEAP_YEAR(y) : 30 + ((m & 1) == (m <= 7));
+}
+
+
+// Function below parses the string passed in params (if provided) and sets the
+// time accordingly. returns false in case an invalid date/time was found (and
+// an invalid parameter message will be given) and true in case of no params or
+// when a string of the format: [YYYY-MM-DD] [HH:MM[:SS]] is found. In case a
+// partial date/time is provided, the remaining part will be substituted with
+// the DS1307's current value. Additional whitespaces are ignored and values are
+// checked for validity.
+bool DS1307_Command::setparams(const char *params) {
+  if (!params) return true;                     // return true when no parameter
+  int value;
+  tmElements_t tm;
+  if (const char *sep = CLI_STR_parseInt(params, value)) { // parse first integer value
+    RTC.read(tm);                               // Got a valid start get current time
+    // check if character following the integer is a '-' and we have a valid year
+    if (*sep == '-' && value >= tmYearToCalendar(0) && value <= tmYearToCalendar(255)) {
+      // Got a valid year and date separator, parse the rest of the date
+      tm.Year = CalendarYrToTm(value);
+      if ((sep = CLI_STR_parseInt(sep + 1, value)) && value >= 1 && value <= 12 && *sep == '-') {
+        tm.Month = value;
+        if ((sep = CLI_STR_parseInt(sep + 1, value)) && value >= 1 &&
+                        value <= monthDays(tmYearToCalendar(tm.Year), value)) {
+          tm.Day = value;
+          sep = CLI_STR_skipSep(sep);
+          if (*sep) sep = CLI_STR_parseInt(sep, value); // parse the hour integer
+        } else return false;    // exit if parsing a date and day is invald
+      } else return false;      // exit if parsing a date and month is invald
+    }
+    // check if character following the integer is a ':' and we have a valid hour
+    if (*sep == ':' && value >= 0 && value < 24) {
+      // Got a valid hour and time separator, parse the rest of the time
+      tm.Hour = value;
+      if ((sep = CLI_STR_parseInt(sep + 1, value)) && value >= 0 && value <= 59) {
+        tm.Minute = value;
+        if (*sep == ':') {      // if next char is a separator, parse seconds
+          if ((sep = CLI_STR_parseInt(sep + 1, value)) && value >= 0 && value <= 59) {
+            tm.Second = value;
+          } else return false;
+        } else tm.Second = 0;   // otherwise set seconds to 0
+      } else return false; // exit if minutes is invalid
+    }
+    if (*CLI_STR_skipSep(sep)) return false;     // if not the end of the string, return false
+    RTC.write(tm);      // if we get here, we have a valid time in tm, set it
+    return true;        // return true as we got valid input
+  }
+  return false;         // no valid parameter, return false
+}
+
+bool DS1307_Command::execute(CLI &cli) {    // execute  prints the current time
+  tmElements_t tm;
+  if (RTC.read(tm)) {                           // get current time from DS1307
+    cli.print(dayStr(tm.Wday));                 // print day of week
+    cli.print(' ');
+    cli.print(tm.Day);                          // print day of month
+    cli.print(' ');
+    cli.print(monthStr(tm.Month));              // print month name
+    cli.print(' ');
+    cli.print(tmYearToCalendar(tm.Year));       // print year
+    cli.print(' ');
+    cli.print2digits(tm.Hour);                  // print hour
+    cli.print(':');
+    cli.print2digits(tm.Minute);                // print minute
+    cli.print(':');
+    cli.print2digits(tm.Second);                // print seconds
+  } else {                              // if get time fails, print an error
+    if (RTC.chipPresent()) {            // if a chip was found, it is not set
+      cli.print_P(PSTR("Error: cannot read RTC clock, please set time\n"));
+    } else {                            // otherwise tell user no DS1307 was found
+      cli.print_P(PSTR("Error: RTC clock not found, check wiring\n"));
+    }
+  }
+  return false;                 // We're done and should not be called again
+}
+
diff --git a/examples/DS1307RTC/DS1307_Command.h b/examples/DS1307RTC/DS1307_Command.h
new file mode 100644
index 0000000..a04b66d
--- /dev/null
+++ b/examples/DS1307RTC/DS1307_Command.h
@@ -0,0 +1,29 @@
+/*
+   DS1307_Command.cpp - CLI library for Arduino/ESP8266 - DS1307 Command definitions
+
+   Version 1.0, latest version, documentation and bugtracker available at:
+                https://gitlab.lindenaar.net/arduino/CLI
+
+   Copyright (c) 2019 Frederik Lindenaar
+
+   This library is free software: you can redistribute it and/or modify it under
+   the terms of version 3 of the GNU General Public License as published by the
+   Free Software Foundation, or (at your option) a later version of the license.
+
+   This code 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, visit <http://www.gnu.org/licenses/> to download it.
+*/
+
+#include <CLI.h>
+
+class DS1307_Command : public CLI_Command {     // Definition of ds1307 command
+  public:
+    DS1307_Command(CLI &cli);                   // Constructor, requires CLI
+    bool setparams(const char *params);         // Parameter parser
+    bool execute(CLI &cli);                     // Implementation of logic
+};
+
diff --git a/examples/Debug/Debug.ino b/examples/Debug/Debug.ino
new file mode 100644
index 0000000..b78671d
--- /dev/null
+++ b/examples/Debug/Debug.ino
@@ -0,0 +1,71 @@
+/*
+   Debug.ino - CLI library sample implementing a CLI for the Blink sample code
+
+   Version 1.0, latest version, documentation and bugtracker available at:
+                https://gitlab.lindenaar.net/arduino/CLI
+
+   Copyright (c) 2019 Frederik Lindenaar
+
+   This sketch demonstrates the debugging commands available as part of the CLI
+   library. These commands are intended to eliminate the need to write code to
+   check things in or connected to your microcontroller. Over time, additional
+   commands will be added, when needed (feel free to submit your favorites for
+   inclusion!). At this moment it makes the following commands available through
+   the Serial Monitor (available from the Tools menu of the Arduino IDE):
+     - eeprom_dump	dump the contents of the built-in EEPROM
+     - i2c_scan         scan the I2C bus for slave devices
+     - i2c_dump         dump the contents of I2C attached EEPROM or Memory
+     - reset            restart the microcontroller (software reset)
+     - help             show the available commands and how to use them
+
+   For each of these commands the implementation is included in the CLI library.
+
+   This sketch is free software: you can redistribute it and/or modify it under
+   the terms of version 3 of the GNU General Public License as published by the
+   Free Software Foundation, or (at your option) a later version of the license.
+
+   This code 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, visit <http://www.gnu.org/licenses/> to download it.
+*/
+
+#include <CLI.h>
+#include <Wire.h>
+
+// Initialize the Debug Command Line Interface
+const char banner[] PROGMEM = "Debug Sample CLI"; // Banner to show upon startup of the CLI
+CLI CLI(Serial, banner);                          // Initialize the CLI, telling it to attach to Serial
+EEPROM_Dump_Command EEPROM_Dump(CLI);             // Initialize/Register (built-in) eeprom_dump command
+I2C_Scan_Command I2C_Scan(CLI);                   // Initialize/Register (built-in) i2c_scan command
+I2C_Dump_Command I2C_Dump(CLI);                   // Initialize/Register (built-in) i2c_dump command
+Reset_Command Reset(CLI);                         // Initialize/Register (built-in) reset command
+Help_Command Help(CLI);                           // Initialize/Register (built-in) help command
+
+
+// the setup function runs once when you reset or power the board
+void setup() {
+  // initialize digital pin LED_BUILTIN as an output.
+  pinMode(LED_BUILTIN, OUTPUT);
+
+  // Initialize the Serial port for the CLI
+  while (!Serial); // For Leonardo: wait for serial USB to connect
+  Serial.begin(9600);
+
+  // Initialize the Wire Interface
+  Wire.begin();
+}
+
+
+// the loop function runs over and over again forever
+void loop() {
+  // handle CLI, if this returns true a command is running. Set Builtin LED accordingly
+  if (CLI.process()) {
+    digitalWrite(LED_BUILTIN, HIGH); // turn LED on when processing CLI command
+  } else {
+    digitalWrite(LED_BUILTIN, LOW); // turn LED off when not processing CLI command
+  }
+}
+
diff --git a/keywords.txt b/keywords.txt
new file mode 100644
index 0000000..e99c010
--- /dev/null
+++ b/keywords.txt
@@ -0,0 +1,59 @@
+#######################################
+# Syntax Coloring Map for CLI v1.0
+#######################################
+
+#######################################
+# Datatypes (KEYWORD1)
+#######################################
+CLI			KEYWORD1
+CLI_Command		KEYWORD1
+
+CLI_State		KEYWORD1
+CLI_Command_Param	KEYWORD1
+CLI_Command_Flags	KEYWORD1
+
+Help_Command		KEYWORD1
+Reset_Command		KEYWORD1
+EEPROM_Dump_Command	KEYWORD1
+I2C_Scan_Command	KEYWORD1
+I2C_Dump_Command	KEYWORD1
+
+#######################################
+# Methods and Functions (KEYWORD2)
+#######################################
+
+# functions
+CLI_STR_skipSep		KEYWORD2
+CLI_STR_findSep		KEYWORD2
+CLI_STR_parseInt	KEYWORD2
+CLI_STR_parseParam_P	KEYWORD2
+CLI_STR_parseFlags_P	KEYWORD2
+
+# CLI
+process			KEYWORD2
+add_command		KEYWORD2
+get_command		KEYWORD2
+find_command		KEYWORD2
+command_count		KEYWORD2
+print_P			KEYWORD2
+print2digits		KEYWORD2
+print_mem		KEYWORD2
+printtab		KEYWORD2
+
+# CLI_Command
+matches			KEYWORD2
+setparams		KEYWORD2
+execute			KEYWORD2
+
+#######################################
+# Constants (LITERAL1)
+#######################################
+CLI_MAX_CMDS		LITERAL1
+CLI_MAX_LINE		LITERAL1
+
+STATE_INIT		LITERAL1
+STATE_READY		LITERAL1
+STATE_INPUT		LITERAL1
+STATE_PARSE		LITERAL1
+STATE_EXEC		LITERAL1
+
diff --git a/library.json b/library.json
new file mode 100644
index 0000000..ee8dc3d
--- /dev/null
+++ b/library.json
@@ -0,0 +1,21 @@
+{
+  "name": "CLI",
+  "description": "Library to buid a real-time serial (or network) command-line interface (CLI) to configure or control your microcontroller.",
+  "keywords": "CLI, Commandline",
+  "homepage": "https://gitlab.lindenaar.net/arduino/CLI",
+  "authors": {
+          "name": "Frederik Lindenaar",
+          "url": "https://frederik.lindenaar.nl",
+          "maintainer": true
+  },
+  "repository": {
+    "type": "git",
+    "url": "https://gitlab.lindenaar.net/arduino/CLI.git"
+  },
+  "version": "1.0",
+  "license": "GPL",
+  "frameworks": [ "arduino" ],
+  "platforms": "*",
+  "examples": "examples/*/*.ino"
+}
+
diff --git a/library.properties b/library.properties
new file mode 100644
index 0000000..914532f
--- /dev/null
+++ b/library.properties
@@ -0,0 +1,12 @@
+name=Command Line Interface
+version=1.0
+author=Frederik Lindenaar
+maintainer=Frederik Lindenaar
+sentence=Library to build a Command Line Interface (CLI)
+paragraph=Library to bui;d a real-time serial (or network) command-line interface (CLI) to configure or control your microcontroller.
+category=Device Control
+url=https://gitlab.lindenaar.net/arduino/CLI
+architectures=*
+license=GPL
+includes=CLI.h
+
diff --git a/src/CLI.cpp b/src/CLI.cpp
new file mode 100644
index 0000000..9288a3e
--- /dev/null
+++ b/src/CLI.cpp
@@ -0,0 +1,178 @@
+/*
+ * CLI.cpp - CLI library for Arduino/ESP8266 and others implementation
+ *
+ * Version 1.0, latest version, documentation and bugtracker available at:
+ *              https://gitlab.lindenaar.net/arduino/CLI
+ *
+ * Copyright (c) 2019 Frederik Lindenaar
+ *
+ * This library is free software: you can redistribute it and/or modify it under
+ * the terms of version 3 of the GNU General Public License as published by the
+ * Free Software Foundation, or (at your option) a later version of the license.
+ *
+ * This code 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, visit <http://www.gnu.org/licenses/> to download it.
+ */
+
+#include <CLI.h>
+
+#define CHAR_TAB  '\t'
+#define CHAR_BS   '\b'
+#define CHAR_DELETE char(127)
+#define CHAR_CR   '\r'
+#define CHAR_LF   '\n'
+
+const char CLI_DEFAULT_PROMPT[]         PROGMEM = "\n> ";
+const char CLI_PREFIX_PARAMETER_ERROR[] PROGMEM = "Invalid parameters for ";
+const char CLI_PREFIX_UNKNOWN_COMMAND[] PROGMEM = "Unknown command ";
+
+size_t CLI::print_P (const char *str PROGMEM) {
+  size_t len = 0;
+  if (str) {
+    while (char c = pgm_read_byte(str++)) {
+      write(c);
+      len++;
+    }
+  }
+  return len;
+}
+
+void CLI::print2digits(uint8_t num, char filler, uint8_t base) {
+  if (num < base) print(filler);
+  print(num, base);
+}
+
+void CLI::print_mem(uint16_t addr, const uint8_t buff[], uint8_t len, uint8_t width) {
+  if (addr < 0x1000) print(' ');
+  if (addr < 0x100) print(' ');
+  if (addr < 0x10) print(' ');
+  print(addr, HEX);
+  print(':');
+
+  for (uint8_t i = 0; i < len; i++) {
+    print(' ');
+    if (buff[i] < 0x10) print(0);
+    print(buff[i], HEX);
+  }
+  while (width > len++) print_P(PSTR("   "));
+
+  print('\t');
+  for (uint8_t i = 0; i < len; i++)
+    write(isprint(buff[i]) ? buff[i] : '.');
+
+  println();
+}
+
+void CLI::printtab() {
+  print(CHAR_TAB);
+};
+
+CLI::CLI(Stream &stream, const char *banner PROGMEM, const char *prompt PROGMEM) : _stream(&stream), _banner(banner) {
+  _state = STATE_INIT;
+  _command_count = 0;
+  _prompt = (prompt) ? prompt : CLI_DEFAULT_PROMPT;
+};
+
+void CLI::add_command(CLI_Command *cmd) {
+  if (_command_count < CLI_MAX_CMDS)
+    _commands[_command_count++] = cmd;
+};
+
+CLI_Command *CLI::get_command(int index) {
+  return (index < _command_count) ? _commands[index] : NULL;
+}
+
+CLI_Command *CLI::get_command(const char *name, int namelen) {
+  for (int i = 0; i < _command_count; i++) {
+    if (_commands[i]->matches(name, namelen)) return _commands[i];
+  }
+  return NULL;
+}
+
+#if CLI_MAX_CMDS < 255
+uint8_t CLI::find_command(const char *name, int namelen) {
+  uint8_t idx = _command_count;
+#else
+uint16_t CLI::find_command(const char *name, int namelen) {
+  uint16_t idx = _command_count;
+#endif
+  while (idx-- && !_commands[idx]->matches(name, namelen));
+  return idx;
+}
+
+bool CLI::process() {                       // Workhorse of the CLI
+  if (_state == STATE_INIT) {                   // INIT: print banner (if set)
+    if (_banner) {
+      print_P(_banner);
+      println();
+    }
+    _state = STATE_READY;
+  } else if (_state == STATE_READY) {           // READY: print prompt & init
+    print_P(_prompt);
+    memset(_cmdbuffer, 0, _line_len);
+    _line_len = 0;
+    _current_cmd = NULL;
+    _state = STATE_INPUT;
+  } else if (_state == STATE_INPUT) {           // INPUT: process user input
+    while (available()) {                           // loop while stream input
+      int c = read();                               // get next char
+      if (c == CHAR_DELETE) {                   // Process BACKSPACE key
+        print_P(PSTR("\b \b"));
+        if (_line_len) _cmdbuffer[_line_len--] = 0;
+      } else if (c == CHAR_CR) {                // Process ENTER key
+        println();
+        _state = STATE_PARSE;                       // Change state to PARSE
+        return false;                               // and stop processing input
+      } else if (c != CHAR_LF && isprint(c)) {  // Process other valid input
+        if (_line_len < CLI_MAX_LINE - 1) {         // add to line if space left
+          _cmdbuffer[_line_len++] = c;
+          write(c);                                 // print char to echo input
+        } else {                                    // if not, beep & ignore
+          write(char(7));
+        }
+      }
+    }
+  } else if (_state == STATE_PARSE) {           // PARSE: parse user command
+    const char *cmd = CLI_STR_skipSep(_cmdbuffer);  // skip initial whitespace
+    if (*cmd && cmd - _cmdbuffer < _line_len) {     // do we have a command?
+      for (char *p = _cmdbuffer + _line_len - 1; *p == ' '; p--, _line_len--)
+        *p = 0;                                     // clear trailing whitespace
+      const char *sep = CLI_STR_findSep(cmd);       // find end of command
+      if (_current_cmd = get_command(cmd, sep - cmd)) { // known command?
+        sep = CLI_STR_skipSep(sep);                     // get start of params
+        if (_current_cmd->setparams((*sep) ? sep : 0)) {// parse params
+          _state = STATE_EXEC;                          // all OK, change state
+          return true;                                  // and stop processing
+        } else print_P(CLI_PREFIX_PARAMETER_ERROR); // print invalid parameters
+      } else print_P(CLI_PREFIX_UNKNOWN_COMMAND) ;  // print unknown command
+      _stream->write(cmd, sep - cmd);               // print command name
+      println();                                    // and add newline
+    }
+    _state = STATE_READY;                           // if we get here: READY
+  } else if (_state == STATE_EXEC) {            // EXEC: execute the command
+    if (_current_cmd->execute(*this)) {             // Execute, false when done
+      return true;                                  // not yet done, return true
+    } else {
+      _state = STATE_READY;                         // done, READY
+    }
+  }
+  return false;                                 // default result false (done)
+}
+
+
+CLI_Command::CLI_Command(CLI &cli, const char *command, const char *description, const char *usagehelp) : command(command), description(description), usagehelp(usagehelp) {
+  cli.add_command(this);
+};
+
+bool CLI_Command::matches(const char *cmd, uint8_t cmdlen) {
+  return pgm_read_byte(&command[cmdlen]) == 0 && strncmp_P(cmd, command, cmdlen) == 0;
+};
+
+bool CLI_Command::setparams(const char *params) {
+  return !params;
+}
+
diff --git a/src/CLI.h b/src/CLI.h
new file mode 100644
index 0000000..26b8178
--- /dev/null
+++ b/src/CLI.h
@@ -0,0 +1,182 @@
+/*
+ * CLI.h - CLI library for Arduino/ESP8266 and others definitions
+ *
+ * Version 1.0, latest version, documentation and bugtracker available at:
+ *              https://gitlab.lindenaar.net/arduino/CLI
+ *
+ * Copyright (c) 2019 Frederik Lindenaar
+ *
+ * This library is free software: you can redistribute it and/or modify it under
+ * the terms of version 3 of the GNU General Public License as published by the
+ * Free Software Foundation, or (at your option) a later version of the license.
+ *
+ * This code 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, visit <http://www.gnu.org/licenses/> to download it.
+ */
+
+#include <Stream.h>
+
+#ifndef CLI_H
+#define CLI_H
+
+#ifndef CLI_MAX_CMDS
+  #define CLI_MAX_CMDS  16
+#endif // CLI_MAX_CMDS
+
+#ifndef CLI_MAX_LINE
+  #define CLI_MAX_LINE  80
+#endif // CLI_MAX_LINE
+
+class CLI;
+
+struct CLI_Command_Param {
+  const char *param PROGMEM;
+  union {
+    int int16;
+    uint16_t uint16;
+    int8_t int8;
+    uint8_t uint8;
+    int32_t int32;
+    uint32_t uint32;
+    char *str PROGMEM;
+  };
+};
+
+struct CLI_Command_Flags {
+  const char *command;
+  uint8_t cmdlen, flags, intparam;
+};
+
+const char *CLI_STR_skipSep(const char *str);
+const char *CLI_STR_findSep(const char *str);
+const char *CLI_STR_parseInt(const char *, int &, int = -32768, int = 32767);
+const char *CLI_STR_parse_HEX_byte(const char *, uint8_t &);
+const struct CLI_Command_Param *CLI_STR_parseParam_P (const char *, const struct CLI_Command_Param params[]);
+const char *CLI_STR_parseFlags_P (const char *, const struct CLI_Command_Flags params[], uint8_t, uint8_t, uint8_t *);
+
+class CLI_Command {
+  friend class Help_Command;
+  friend class CLI;
+  protected:
+    const char *command, *description, *usagehelp;
+    bool matches(const char *cmd, uint8_t cmdlen);
+  public:
+    CLI_Command(CLI &cli , const char *command PROGMEM, const char *description PROGMEM, const char *help PROGMEM = NULL);
+    virtual bool setparams(const char *params);
+    virtual bool execute(CLI &cli) = 0;
+};
+
+
+class Help_Command : public CLI_Command {
+  CLI &_cli;
+#if CLI_MAX_CMDS < 255
+    uint8_t _cmd_idx;
+#else
+    uint16_t _cmd_idx;
+#endif
+    bool _cmd_list;
+  public:
+    Help_Command(CLI &cli);
+    bool setparams(const char *);
+    bool execute(CLI &cli);
+};
+
+
+class EEPROM_Dump_Command : public CLI_Command {
+    int _offset;
+  public:
+    EEPROM_Dump_Command(CLI &cli);
+    bool setparams(const char *);
+    bool execute(CLI &cli);
+};
+
+class Reset_Command : public CLI_Command {
+    int _resetting;
+  public:
+    Reset_Command(CLI &cli);
+    bool execute(CLI &cli);
+};
+
+class I2C_Scan_Command : public CLI_Command {
+    uint8_t _address, _found;
+  public:
+    I2C_Scan_Command(CLI &cli);
+    bool setparams(const char *);
+    bool execute(CLI &cli);
+};
+
+class I2C_Dump_Command : public CLI_Command {
+    uint8_t _address;
+    bool _large;
+    uint16_t _length, _offset;
+  public:
+    I2C_Dump_Command(CLI &cli);
+    bool setparams(const char *);
+    bool execute(CLI &cli);
+};
+
+
+class CLI : public Stream {
+    enum CLI_State { STATE_INIT, STATE_READY, STATE_INPUT, STATE_PARSE, STATE_EXEC } _state;
+#if CLI_MAX_CMDS < 255
+    uint8_t _command_count;
+#else
+    uint16_t _command_count;
+#endif
+    CLI_Command *_commands[CLI_MAX_CMDS];
+#if CLI_MAX_LINE < 255
+    uint8_t _line_len;
+#else
+    uint16_t _line_len;
+#endif
+    char _cmdbuffer[CLI_MAX_LINE];
+    //    const char *_params;
+    CLI_Command *_current_cmd;
+  protected:
+    Stream *_stream;
+    const char *_banner, *_prompt;
+  public:
+    CLI(Stream &stream, const char *banner PROGMEM = NULL, const char *prompt PROGMEM = NULL);
+    void add_command(CLI_Command *cmd);
+    CLI_Command *get_command(int);
+    CLI_Command *get_command(const char *, int);
+#if CLI_MAX_CMDS < 256
+    uint8_t find_command(const char *, int);
+    inline uint8_t command_count() {
+#else
+    uint16_t find_command(const char *, int);
+    inline uint16_t command_count() {
+#endif
+      return _command_count;
+    };
+
+    inline int available() {
+      return _stream->available();
+    };
+
+    inline int peek() {
+      return _stream->peek();
+    };
+
+    inline int read() {
+      return _stream->read();
+    };
+
+    inline size_t write(uint8_t c) {
+      return _stream->write(c);
+    };
+
+    size_t print_P (const char *str PROGMEM);
+    void print2digits(uint8_t num, char filler = '0', uint8_t base=10);
+    void print_mem(uint16_t addr, const uint8_t buff[], uint8_t len, uint8_t width=16);
+    void printtab();
+
+    bool process();
+};
+
+#endif // CLI_H
+
diff --git a/src/CLI_Utils.cpp b/src/CLI_Utils.cpp
new file mode 100644
index 0000000..be8fe80
--- /dev/null
+++ b/src/CLI_Utils.cpp
@@ -0,0 +1,110 @@
+/*
+ * CLI_Utils.cpp - CLI library for Arduino/ESP8266 and others utility functions
+ *
+ * Version 1.0, latest version, documentation and bugtracker available at:
+ *              https://gitlab.lindenaar.net/arduino/CLI
+ *
+ * Copyright (c) 2019 Frederik Lindenaar
+ *
+ * This library is free software: you can redistribute it and/or modify it under
+ * the terms of version 3 of the GNU General Public License as published by the
+ * Free Software Foundation, or (at your option) a later version of the license.
+ *
+ * This code 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, visit <http://www.gnu.org/licenses/> to download it.
+ */
+
+#include <CLI.h>
+
+const char *CLI_STR_skipSep(const char *str) {
+  while (*str == ' ') str++;
+  return str;
+};
+
+const char *CLI_STR_findSep(const char *str) {
+  while (*str && *str != ' ') str++;
+  return str;
+};
+
+const char *CLI_STR_parseInt(const char *str, int &value, int minvalue, int maxvalue) {
+  int v = 0;
+  const char *p = str;
+  bool negative = *p == '-';
+  if (negative || *p == '+') p++;
+  while (*p >= '0' && *p <= '9') {
+    v *= 10;
+    v += *(p++) - '0';
+  }
+  if (str != p && v >= minvalue && v <= maxvalue) {
+    value = (negative) ? -v : v;
+    return p;
+  }
+  return 0;
+}
+
+uint8_t parseHexNibble(char c) {
+  if(c >= 'a' && c <= 'f') {
+    return c - 'a' + 10;
+  } else if(c >= 'A' && c <= 'F') {
+    return c - 'A' + 10;
+  } else if(c >= '0' && c <= '9') {
+    return c - '0';
+  } else return 0xff;
+}
+
+const char *CLI_STR_parse_HEX_byte(const char *str, uint8_t &value) {
+  if (str) {
+    uint8_t v;
+    if((v = parseHexNibble(*(str++))) <= 0xf) {
+      value = v << 4;
+      if((v = parseHexNibble(*(str++))) <= 0xf) {
+        value |= v;
+        return str;
+      }
+    }
+  }
+  return NULL;
+}
+
+const struct CLI_Command_Param *CLI_STR_parseParam_P (const char *param, const struct CLI_Command_Param params[]) {
+  for (const struct CLI_Command_Param *p = params; pgm_read_ptr(&p->param); p++)
+    if (!strcmp_P(param, (char *)pgm_read_ptr(&p->param))) return p;
+  return NULL;
+}
+
+const char *CLI_STR_parseFlags_P (const char *str, const struct CLI_Command_Flags params[], uint8_t param_count, uint8_t mask, uint8_t *flags) {
+  uint8_t f = 0;
+  while (*str) {
+    const struct CLI_Command_Flags *p = params;
+    while (p <= &params[param_count] && strncmp_P(str, pgm_read_word(&p->command), pgm_read_byte(&p->cmdlen)) ) p++;
+    uint8_t flags = pgm_read_byte(&p->flags);
+    if (p > &params[param_count] || (f != 0 && ((f & mask) != (flags & mask)))) return 0;
+    f |= flags;
+    str = CLI_STR_skipSep(str + pgm_read_byte(&p->cmdlen));
+
+    if (uint8_t intparam = pgm_read_byte(&p->intparam)) {
+      int i;
+      if (str = CLI_STR_parseInt(str, i)) {
+        if (pgm_read_byte(&p->cmdlen) == 0)
+          if (int divider = pgm_read_word(&p->command)) {
+            i /= divider & 0xff;
+            i += divider >> 8;
+          }
+        if (i <= (intparam & 0x1f)) {
+          f |= i << (intparam >> 5);
+          str = CLI_STR_skipSep(str);
+          continue;
+        }
+      }
+      return 0;
+    }
+  }
+  if (*str) return 0;
+  *flags = f;
+  return str;
+}
+
diff --git a/src/Commands.cpp b/src/Commands.cpp
new file mode 100644
index 0000000..c0da1bd
--- /dev/null
+++ b/src/Commands.cpp
@@ -0,0 +1,98 @@
+/*
+ * CLI_Commands.cpp - CLI library for Arduino/ESP8266 and others general commands
+ *
+ * Version 1.0, latest version, documentation and bugtracker available at:
+ *              https://gitlab.lindenaar.net/arduino/CLI
+ *
+ * Copyright (c) 2019 Frederik Lindenaar
+ *
+ * This library is free software: you can redistribute it and/or modify it under
+ * the terms of version 3 of the GNU General Public License as published by the
+ * Free Software Foundation, or (at your option) a later version of the license.
+ *
+ * This code 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, visit <http://www.gnu.org/licenses/> to download it.
+ */
+
+#include <CLI.h>
+#include <avr/eeprom.h>
+
+Help_Command::Help_Command(CLI & cli) :
+  CLI_Command(cli, PSTR("help"), PSTR("Display command help"),
+      PSTR("Usage: help [<command>]\n"
+           "\tdisplays usage help for <command> or lists available commands\n"
+           "\twhen called without parameters\n")), _cli(cli) { };
+
+bool Help_Command::setparams(const char *params) {
+  if (_cmd_list = !params) {
+    _cmd_idx = 0;
+    return true;
+  } else {
+    _cmd_idx = _cli.find_command(params, strlen(params));
+    return _cmd_idx < _cli.command_count();
+  }
+}
+
+bool Help_Command::execute(CLI &cli) {
+  if(CLI_Command *cmd = cli.get_command(_cmd_idx)) {
+    if (_cmd_list) {
+      if (_cmd_idx == 0) cli.print_P(PSTR("Known Commands:\n"));
+      _cmd_idx++;
+      cli.printtab();
+      if (cli.print_P(cmd->command) < 8) cli.printtab();
+      cli.printtab();
+      cli.print_P(cmd->description);
+      cli.println();
+      return true;
+    } else {
+        cli.print_P(cmd->description);
+        cli.println();
+        if (cmd->usagehelp) cli.print_P(cmd->usagehelp);
+    }
+  } else if (_cmd_list) {
+    cli.print_P((_cmd_idx)
+                ? PSTR("\nfor more information on a command use \"help <command>\"")
+                : PSTR("No Commands"));
+  }
+  cli.println();
+  return false;
+}
+
+
+EEPROM_Dump_Command::EEPROM_Dump_Command(CLI & cli) : CLI_Command(cli,
+      PSTR("eeprom_dump"), PSTR("Show EEPROM contents in HEX and ASCII")) { };
+
+bool EEPROM_Dump_Command::setparams(const char *params) {
+  _offset = 0;
+  return !params && E2END > 0;
+}
+
+bool EEPROM_Dump_Command::execute(CLI &cli) {
+  if (_offset == 0) {
+    cli.print_P(PSTR("EEPROM Size: "));
+    cli.print(E2END + 1);
+    cli.print_P(PSTR(" Bytes\n\n"));
+  }
+  uint8_t buffer[16];
+  eeprom_read_block(&buffer, (void *)_offset, sizeof(buffer));
+  cli.print_mem(_offset, buffer, sizeof(buffer));
+  _offset += sizeof(buffer);
+  return _offset <= E2END;
+}
+
+
+Reset_Command::Reset_Command(CLI & cli) : CLI_Command(cli,
+      PSTR("reset"), PSTR("Restart microcontroller")) { };
+
+void(* resetFunc) (void) = 0; //declare reset function @ address 0
+bool Reset_Command::execute(CLI &cli) {
+  if (_resetting == 2000) resetFunc();
+  if (!_resetting) cli.print(PSTR("resetting...\n\n"));
+  _resetting++;
+  return true;
+}
+
diff --git a/src/I2C_Commands.cpp b/src/I2C_Commands.cpp
new file mode 100644
index 0000000..62c6106
--- /dev/null
+++ b/src/I2C_Commands.cpp
@@ -0,0 +1,110 @@
+/*
+ * CLI_I2C_Commands.cpp - CLI library for Arduino/ESP8266 - I2C Commands implementation
+ *
+ * Version 1.0, latest version, documentation and bugtracker available at:
+ *              https://gitlab.lindenaar.net/arduino/CLI
+ *
+ * Copyright (c) 2019 Frederik Lindenaar
+ *
+ * This library is free software: you can redistribute it and/or modify it under
+ * the terms of version 3 of the GNU General Public License as published by the
+ * Free Software Foundation, or (at your option) a later version of the license.
+ *
+ * This code 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, visit <http://www.gnu.org/licenses/> to download it.
+ */
+
+#include <CLI.h>
+#include <Wire.h>
+
+I2C_Scan_Command::I2C_Scan_Command(CLI & cli) : CLI_Command(cli,
+      PSTR("i2c_scan"), PSTR("Scan I2C bus for slave devices")) { };
+
+bool I2C_Scan_Command::setparams(const char *params) {
+  _address = _found = 0;
+  return !params;
+}
+
+bool I2C_Scan_Command::execute(CLI &cli) {
+  if (_address < 127) {
+    if (_address == 0) cli.println(F("Scanning I2C..."));
+    Wire.beginTransmission(_address);
+    uint8_t error = Wire.endTransmission();
+
+    if (error == 0) {
+      cli.print(F("I2C device found at address 0x"));
+      if (_address < 0x10) cli.print("0");
+      cli.println(_address, HEX);
+      _found++;
+    } else if (error == 4) {
+      cli.print(F("Unknown error at address 0x"));
+      if (_address < 0x10) cli.print("0");
+      cli.println(_address, HEX);
+    }
+    _address++;
+    return true;
+  } else {
+    if (_found == 0)
+      cli.print_P(PSTR("No I2C devices found"));
+    return false;
+  }
+}
+
+
+I2C_Dump_Command::I2C_Dump_Command(CLI & cli) : CLI_Command(cli,
+      PSTR("i2c_dump"), PSTR("Display I2C EEPROM/Memory in HEX and ASCII"),
+      PSTR("Usage: i2c_dump 0x<id> <size> [<skip>] [single]\n"
+           "where:\t<id>\tHEX I2C device ID\n"
+                 "\t<size>\tsize of memory\n"
+                 "\t<skip>\t(optional) start offset\n"
+                 "\tsingle\tto enforce 1-byte addressing")) { };
+
+bool I2C_Dump_Command::setparams(const char *params) {
+  if (params && *(params++) == '0' && *(params++) == 'x') {
+    if ((params = CLI_STR_parse_HEX_byte(params, _address)) && *params == ' ') {
+      if(params = CLI_STR_parseInt(CLI_STR_skipSep(params), (int &)_length)) {
+        _offset = 0;
+        _large = _length > 0xff;
+        if (*params == ' ') {
+          params = CLI_STR_skipSep(params);
+          if(const char *p = CLI_STR_parseInt(params, (int &)_offset))
+            params = CLI_STR_skipSep(p);
+          if (strcmp_P(params, PSTR("single")) == 0) {
+            if (_large) return false;
+            params += 6;
+          }
+        }
+        return *params == 0;
+      }
+    }
+  }
+  return false;
+}
+
+bool I2C_Dump_Command::execute(CLI &cli) {
+  uint8_t buffer[16], len = 0;
+
+  Wire.beginTransmission(_address);
+  if (_large) Wire.write((uint8_t)(_offset >> 8));   // MSB
+  Wire.write((uint8_t)(_offset & 0xff));  // LSB
+  if (Wire.endTransmission() != 0) {
+    cli.print(F("No I2C device at 0x"));
+    cli.print(_address, HEX);
+    cli.println();
+    return false;
+  }
+
+  if (_offset == 0) cli.println(F("I2C EEPROM/Memory: "));
+
+  Wire.requestFrom(_address, (uint8_t) (_length - _offset < sizeof(buffer)) ? _length - _offset : sizeof(buffer));
+  while (Wire.available() && len < sizeof(buffer)) buffer[len++] = Wire.read();
+
+  cli.print_mem(_offset, buffer, len);
+  _offset += len;
+  return _offset < _length;
+}
+
--
libgit2 0.22.2