[P132] Additional support for INA226, INA228, INA230, INA231 and INA260
@@ -0,0 +1,42 @@
|
||||
########################################################################
|
||||
# This section defines which file types are associated with which #
|
||||
# linguist-documentation language types. Initially this was set so that#
|
||||
# the "Arduino" language was chosen, but that is no longer considered #
|
||||
# a language so everything has been reverted to "c++" #
|
||||
# #
|
||||
# Date Author Comments #
|
||||
# ========== ========== ============================================== #
|
||||
# 2018-06-24 SV-Zanshin Changed file #
|
||||
# #
|
||||
########################################################################
|
||||
examples/ linguist-documentation=false
|
||||
*.c linguist-language=c++
|
||||
*.ino linguist-language=c++
|
||||
*.pde linguist-language=c++
|
||||
*.c linguist-language=c++
|
||||
*.h linguist-language=c++
|
||||
*.cpp linguist-language=c++
|
||||
|
||||
########################################################################
|
||||
# Auto detect text files and perform LF normalization #
|
||||
########################################################################
|
||||
* text=auto
|
||||
|
||||
########################################################################
|
||||
# Custom setting for Visual Studio/Atmel Studio files #
|
||||
########################################################################
|
||||
*.cs diff=csharp
|
||||
|
||||
########################################################################
|
||||
# Standard to msysgit #
|
||||
########################################################################
|
||||
*.doc diff=astextplain
|
||||
*.DOC diff=astextplain
|
||||
*.docx diff=astextplain
|
||||
*.DOCX diff=astextplain
|
||||
*.dot diff=astextplain
|
||||
*.DOT diff=astextplain
|
||||
*.pdf diff=astextplain
|
||||
*.PDF diff=astextplain
|
||||
*.rtf diff=astextplain
|
||||
*.RTF diff=astextplain
|
||||
@@ -0,0 +1,109 @@
|
||||
########################################################################
|
||||
# This file defines which file types are to be ignored and skipped by #
|
||||
# git so that they are not transferred and committed. #
|
||||
# #
|
||||
# Date Author Comments #
|
||||
# ========== ========== ============================================== #
|
||||
# 2021-12-11 SV-Zanshin New ignore for Visual Studio 2022 *.vcxitems #
|
||||
# 2020-12-11 SV-Zanshin Ignores for doxygen and clang-format files #
|
||||
# 2019-01-31 SV-Zanshin Ignores for doxygen #
|
||||
# 2018-09-22 SV-Zanshin Ignores for MS VS 2017 #
|
||||
# 2018-06-24 SV-Zanshin Changed file #
|
||||
# #
|
||||
########################################################################
|
||||
|
||||
########################################################################
|
||||
# Windows image file caches #
|
||||
########################################################################
|
||||
Thumbs.db
|
||||
ehthumbs.db
|
||||
|
||||
########################################################################
|
||||
# Windows folder config file #
|
||||
########################################################################
|
||||
Desktop.ini
|
||||
|
||||
########################################################################
|
||||
# Recycle Bin used on file shares #
|
||||
########################################################################
|
||||
$RECYCLE.BIN/
|
||||
|
||||
########################################################################
|
||||
# Windows Installer files #
|
||||
########################################################################
|
||||
*.cab
|
||||
*.msi
|
||||
*.msm
|
||||
*.msp
|
||||
|
||||
########################################################################
|
||||
# Windows shortcuts #
|
||||
########################################################################
|
||||
*.lnk
|
||||
|
||||
########################################################################
|
||||
# Operating System Files - OSX #
|
||||
########################################################################
|
||||
.DS_Store
|
||||
.AppleDouble
|
||||
.LSOverride
|
||||
|
||||
########################################################################
|
||||
# Thumbnails #
|
||||
########################################################################
|
||||
._*
|
||||
|
||||
########################################################################
|
||||
# Files that might appear in the root of a volume #
|
||||
########################################################################
|
||||
.DocumentRevisions-V100
|
||||
.fseventsd
|
||||
.Spotlight-V100
|
||||
.TemporaryItems
|
||||
.Trashes
|
||||
.VolumeIcon.icns
|
||||
|
||||
########################################################################
|
||||
# Directories potentially created on remote AFP share #
|
||||
########################################################################
|
||||
.AppleDB
|
||||
.AppleDesktop
|
||||
Network Trash Folder
|
||||
Temporary Items
|
||||
.apdisk
|
||||
|
||||
########################################################################
|
||||
# Files and directories from the Atmel Studio Arduino IDE #
|
||||
########################################################################
|
||||
.vs
|
||||
__vm
|
||||
Debug
|
||||
Release
|
||||
*.atsln
|
||||
*.componentinfo.xml
|
||||
*.cppproj
|
||||
|
||||
########################################################################
|
||||
# Files and directories from Microsoft Visual Studio #
|
||||
########################################################################
|
||||
*.sln
|
||||
*.vcxproj
|
||||
*.filters
|
||||
*.user
|
||||
*.vcxitems
|
||||
|
||||
########################################################################
|
||||
# Files and directories from Visual Studio Code #
|
||||
########################################################################
|
||||
.vscode
|
||||
|
||||
########################################################################
|
||||
# Files and directories from doxygen #
|
||||
########################################################################
|
||||
html
|
||||
Doxyfile
|
||||
|
||||
########################################################################
|
||||
# Clang-format files #
|
||||
########################################################################
|
||||
.clang-format
|
||||
@@ -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>.
|
||||
@@ -0,0 +1,192 @@
|
||||
/*!
|
||||
*
|
||||
* @file BackgroundRead.ino
|
||||
*
|
||||
* @brief Example program for the INA Library demonstrating background reads
|
||||
*
|
||||
* @section BackgroundRead_section Description
|
||||
*
|
||||
* Program to demonstrate using the interrupt pin of any INA2xx which supports that functionality.
|
||||
* It uses a pin-change interrupt handler and programs any INA2xx found to to read voltage and
|
||||
* current information in the background while allowing the main Arduino code to continue processing
|
||||
* normally until it is ready to consume the readings.\n\n
|
||||
*
|
||||
* The example program uses the Arduino AVR-based interrupt mechanism and will not function on other
|
||||
* platforms\n\n
|
||||
*
|
||||
* Detailed documentation can be found on the GitHub Wiki pages at
|
||||
* https://github.com/Zanduino/INA/wiki \n\n Since the INA library allows multiple devices of
|
||||
* different types and this program demonstrates interrupts and background processing, it will limit
|
||||
* itself to using the first INA226 detected. This is easily changed in the if another device type
|
||||
* or device number to test is required.\n
|
||||
*
|
||||
* This example is for a INA226 set up to measure a 5-Volt load with a 0.1Ohm resistor in place,
|
||||
* this is the same setup that can be found in the Adafruit INA226 breakout board. The complex
|
||||
* calibration options are done at runtime using the 2 parameters specified in the "begin()" call
|
||||
* and the library has gone to great lengths to avoid the use of floating point to conserve space
|
||||
* and minimize runtime. This demo program uses floating point only to convert and display the data
|
||||
* conveniently. The INA226 uses 15 bits of precision, and even though the current and watt
|
||||
* information is returned using 32-bit integers the precision remains the same.\n The INA226 is set
|
||||
* up to measure using the maximum conversion length (and maximum accuracy) and then average those
|
||||
* readings 64 times. This results in readings taking 8.244ms x 64 = 527.616ms or just less than 2
|
||||
* times a second. The pin-change interrupt handler is called when a reading is finished and the
|
||||
* INA226 pulls the pin down to ground, it resets the pin status and adds the readings to the global
|
||||
* variables. The main program will do whatever processing it has to and every 5 seconds it will
|
||||
* display the current averaged readings and reset them.\n
|
||||
*
|
||||
* The datasheet for the INA226 can be found at http://www.ti.com/lit/ds/symlink/INA226.pdf and it
|
||||
* contains the information required in order to hook up the device. Unfortunately it comes as a
|
||||
* VSSOP package but it can be soldered onto a breakout board for breadboard use. The INA226 is
|
||||
* quite similar to the INA219 mentioned above, but it can take bus voltages of up to 36V (which I
|
||||
* needed in order to monitor a 24V battery system which goes above 28V while charging and which is
|
||||
* above the absolute limits of the INA219). It is also significantly more accurate than the INA219,
|
||||
* plus has an alert pin.\n The interrupt is set to pin 8. The tests were done on an Arduino Micro,
|
||||
* and the Atmel 82U4 chip only allows pin change interrupt on selected pins (SS,SCK,MISO,MOSI,8) so
|
||||
* pin 8 was chosen.
|
||||
*
|
||||
* @section BackgroundRead_license GNU General Public License v3.0
|
||||
*
|
||||
* 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(see
|
||||
* https://github.com/Zanduino/INA/blob/master/LICENSE). If not, see
|
||||
* <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* @section BackgroundRead_author Author
|
||||
*
|
||||
* Written by Arnd <Arnd@Zanduino.Com> at https://www.github.com/SV-Zanshin
|
||||
*
|
||||
* @section BackgroundRead_versions Changelog
|
||||
*
|
||||
* Version | Date | Developer | Comments
|
||||
* ------- | ---------- | ----------- | ------------------------------------------------------------
|
||||
* 1.0.5 | 2020-12-01 | SV-Zanshin | Corrected "alertOnConversion()" call
|
||||
* 1.0.4 | 2019-02-16 | SV-Zanshin | ifdef so that sketch won't compile on incompatible platforms
|
||||
* 1.0.3 | 2019-01-09 | SV-Zanshin | Cleaned up doxygen formatting
|
||||
* 1.0.2 | 2018-12-28 | SV-Zanshin | Converted comments to doxygen format
|
||||
* 1.0.0 | 2018-06-23 | SV-Zanshin | Cloned and adapted example from old deprecated INA226
|
||||
* library
|
||||
*
|
||||
*/
|
||||
#if !defined(__AVR__)
|
||||
#error Example program only functions on Atmel AVR-Based platforms
|
||||
#endif
|
||||
|
||||
/**************************************************************************************************
|
||||
** Declare all include files **
|
||||
**************************************************************************************************/
|
||||
#include <INA.h> // Include the INA library
|
||||
|
||||
/**************************************************************************************************
|
||||
** Declare program Constants **
|
||||
**************************************************************************************************/
|
||||
const uint8_t INA_ALERT_PIN = 8; ///< Pin-Change pin used for the INA "ALERT" functionality
|
||||
const uint8_t GREEN_LED_PIN = 13; ///< Arduino standard green LED
|
||||
const uint32_t SERIAL_SPEED = 115200; ///< Use fast serial speed
|
||||
|
||||
/**************************************************************************************************
|
||||
** Declare global variables and instantiate classes **
|
||||
**************************************************************************************************/
|
||||
INA_Class INA; ///< INA class instantiation
|
||||
volatile uint8_t deviceNumber = UINT8_MAX; ///< Device Number to use in example
|
||||
volatile uint64_t sumBusMillVolts = 0; ///< Sum of bus voltage readings
|
||||
volatile int64_t sumBusMicroAmps = 0; ///< Sum of bus amperage readings
|
||||
volatile uint8_t readings = 0; ///< Number of measurements taken
|
||||
|
||||
ISR(PCINT0_vect) {
|
||||
/*!
|
||||
@brief Interrupt service routine for the PCINT0_vect
|
||||
@details Routine is called whenever the INA_ALERT_PIN changes value
|
||||
*/
|
||||
*digitalPinToPCMSK(INA_ALERT_PIN) &= ~bit(digitalPinToPCMSKbit(INA_ALERT_PIN)); // Disable PCMSK
|
||||
PCICR &= ~bit(digitalPinToPCICRbit(INA_ALERT_PIN)); // disable interrupt for the group
|
||||
sei(); // Enable interrupts (for I2C calls)
|
||||
digitalWrite(GREEN_LED_PIN, !digitalRead(GREEN_LED_PIN)); // Toggle LED
|
||||
sumBusMillVolts += INA.getBusMilliVolts(deviceNumber); // Add current value to sum
|
||||
sumBusMicroAmps += INA.getBusMicroAmps(deviceNumber); // Add current value to sum
|
||||
readings++;
|
||||
INA.waitForConversion(deviceNumber); // Wait for conversion & INA int. flag
|
||||
cli(); // Disable interrupts
|
||||
*digitalPinToPCMSK(INA_ALERT_PIN) |=
|
||||
bit(digitalPinToPCMSKbit(INA_ALERT_PIN)); // Enable PCMSK pin
|
||||
PCIFR |= bit(digitalPinToPCICRbit(INA_ALERT_PIN)); // clear any outstanding interrupt
|
||||
PCICR |= bit(digitalPinToPCICRbit(INA_ALERT_PIN)); // enable interrupt for the group
|
||||
} // of ISR handler for INT0 group of pins
|
||||
|
||||
/*!
|
||||
@brief Arduino method called once at startup to initialize the system
|
||||
@details This is an Arduino IDE method which is called first upon boot or restart. It is only
|
||||
called one time and then control goes to the main "loop()" method, from which control
|
||||
never returns
|
||||
@return void
|
||||
*/
|
||||
void setup() {
|
||||
pinMode(GREEN_LED_PIN, OUTPUT); // Make the internal LED an output pin
|
||||
digitalWrite(GREEN_LED_PIN, true); // Turn on the LED
|
||||
pinMode(INA_ALERT_PIN, INPUT_PULLUP); // Declare pin with internal pull-up resistor
|
||||
*digitalPinToPCMSK(INA_ALERT_PIN) |= bit(digitalPinToPCMSKbit(INA_ALERT_PIN)); // Enable PCMSK
|
||||
PCIFR |= bit(digitalPinToPCICRbit(INA_ALERT_PIN)); // clear any outstanding interrupt
|
||||
PCICR |= bit(digitalPinToPCICRbit(INA_ALERT_PIN)); // enable interrupt for the group
|
||||
Serial.begin(SERIAL_SPEED);
|
||||
#ifdef __AVR_ATmega32U4__ // If this is a 32U4 processor, wait 2 seconds for initialization
|
||||
delay(2000);
|
||||
#endif
|
||||
Serial.print(F("\n\nBackground INA Read V1.0.5\n"));
|
||||
uint8_t devicesFound = 0;
|
||||
while (deviceNumber == UINT8_MAX) // Loop until we find the first device
|
||||
{
|
||||
devicesFound = INA.begin(1, 100000); // +/- 1 Amps maximum for 0.1 Ohm resistor
|
||||
for (uint8_t i = 0; i < devicesFound; i++) {
|
||||
/* Change the "INA226" in the following statement to whatever device you have attached
|
||||
and want to measure */
|
||||
if (strcmp(INA.getDeviceName(i), "INA226") == 0) {
|
||||
deviceNumber = i;
|
||||
INA.reset(deviceNumber); // Reset device to default settings
|
||||
break;
|
||||
} // of if-then we have found an INA226
|
||||
} // of for-next loop through all devices found
|
||||
if (deviceNumber == UINT8_MAX) {
|
||||
Serial.print(F("No INA found. Waiting 5s and retrying...\n"));
|
||||
delay(5000);
|
||||
} // of if-then no INA226 found
|
||||
} // of if-then no device found
|
||||
Serial.print(F("Found INA at device number "));
|
||||
Serial.println(deviceNumber);
|
||||
Serial.println();
|
||||
INA.setAveraging(64, deviceNumber); // Average each reading 64 times
|
||||
INA.setBusConversion(8244, deviceNumber); // Maximum conversion time 8.244ms
|
||||
INA.setShuntConversion(8244, deviceNumber); // Maximum conversion time 8.244ms
|
||||
INA.setMode(INA_MODE_CONTINUOUS_BOTH, deviceNumber); // Bus/shunt measured continuously
|
||||
INA.alertOnConversion(true, deviceNumber); // Make alert pin go low on finish
|
||||
} // of method setup()
|
||||
|
||||
void loop() {
|
||||
/*!
|
||||
@brief Arduino method for the main program loop
|
||||
@details This is the main program for the Arduino IDE, it is called in an infinite loop. The
|
||||
INA226 measurements are triggered by the interrupt handler each time a conversion is
|
||||
ready and stored in variables. The main program doesn't call any INA library functions,
|
||||
that is done in the interrupt handler. Each time 10 readings have been collected the
|
||||
program will output the averaged values and measurements resume from that point onwards
|
||||
@return void
|
||||
*/
|
||||
static long lastMillis = millis(); // Store the last time we printed something
|
||||
if (readings >= 10) {
|
||||
Serial.print(F("Averaging readings taken over "));
|
||||
Serial.print((float)(millis() - lastMillis) / 1000, 2);
|
||||
Serial.print(F(" seconds.\nBus voltage: "));
|
||||
Serial.print((float)sumBusMillVolts / readings / 1000.0, 4);
|
||||
Serial.print(F("V\nBus amperage: "));
|
||||
Serial.print((float)sumBusMicroAmps / readings / 1000.0, 4);
|
||||
Serial.print(F("mA\n\n"));
|
||||
lastMillis = millis();
|
||||
cli(); // Disable interrupts to reset values
|
||||
readings = 0;
|
||||
sumBusMillVolts = 0;
|
||||
sumBusMicroAmps = 0;
|
||||
sei(); // Enable interrupts again
|
||||
} // of if-then we've reached the required amount of readings
|
||||
} // of method loop()
|
||||
@@ -0,0 +1,176 @@
|
||||
/*!
|
||||
*
|
||||
* @file BackgroundRead_ESP32.ino
|
||||
*
|
||||
* @brief Example program for the INA Library demonstrating background reads
|
||||
*
|
||||
* @section BackgroundRead_ESP32_section Description
|
||||
*
|
||||
* Program to demonstrate using the interrupt pin of any INA2xx which supports that functionality.
|
||||
* It uses a pin-change interrupt handler and programs any INA2xx found to to read voltage and
|
||||
* current information in the background while allowing the main Arduino code to continue processing
|
||||
* normally until it is ready to consume the readings.\n\n
|
||||
*
|
||||
* This example program is designed for the ESP32/ESP8266 and will not function on other
|
||||
* platforms\n\n
|
||||
*
|
||||
* Detailed documentation can be found on the GitHub Wiki pages at
|
||||
* https://github.com/Zanduino/INA/wiki \n\n Since the INA library allows multiple devices of
|
||||
* different types and this program demonstrates interrupts and background processing, it will limit
|
||||
* itself to using the first INA226 detected. This is easily changed in the if another device type
|
||||
* or device number to test is required.\n
|
||||
*
|
||||
* This example is for a INA226 set up to measure a 5-Volt load with a 0.1Ohm resistor in place,
|
||||
* this is the same setup that can be found in the Adafruit INA226 breakout board. The complex
|
||||
* calibration options are done at runtime using the 2 parameters specified in the "begin()" call
|
||||
* and the library has gone to great lengths to avoid the use of floating point to conserve space
|
||||
* and minimize runtime. This demo program uses floating point only to convert and display the data
|
||||
* conveniently. The INA226 uses 15 bits of precision, and even though the current and watt
|
||||
* information is returned using 32-bit integers the precision remains the same.\n The INA226 is set
|
||||
* up to measure using the maximum conversion length (and maximum accuracy) and then average those
|
||||
* readings 64 times. This results in readings taking 8.244ms x 64 = 527.616ms or just less than 2
|
||||
* times a second. The pin-change interrupt handler is called when a reading is finished and the
|
||||
* INA226 pulls the pin down to ground, it resets the pin status and adds the readings to the global
|
||||
* variables. The main program will do whatever processing it has to and every 5 seconds it will
|
||||
* display the current averaged readings and reset them.\n
|
||||
*
|
||||
* The datasheet for the INA226 can be found at http://www.ti.com/lit/ds/symlink/INA226.pdf and it
|
||||
* contains the information required in order to hook up the device. Unfortunately it comes as a
|
||||
* VSSOP package but it can be soldered onto a breakout board for breadboard use. The INA226 is
|
||||
* quite similar to the INA219 mentioned above, but it can take bus voltages of up to 36V (which I
|
||||
* needed in order to monitor a 24V battery system which goes above 28V while charging and which is
|
||||
* above the absolute limits of the INA219). It is also significantly more accurate than the INA219,
|
||||
* plus has an alert pin.\n
|
||||
*
|
||||
* The interrupt is set to pin 8. The tests were done on an Arduino Micro, and the Atmel 82U4 chip
|
||||
* only allows pin change interrupt on selected pins (SS,SCK,MISO,MOSI,8) so pin 8 was chosen.\n
|
||||
*
|
||||
* @section BackgroundRead_ESP32_license GNU General Public License v3.0
|
||||
*
|
||||
* 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(see
|
||||
* https://github.com/Zanduino/INA/blob/master/LICENSE). If not, see
|
||||
* <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* @section BackgroundRead_ESP32_author Author
|
||||
*
|
||||
* Written by Arnd <Arnd@Zanduino.Com> at https://www.github.com/SV-Zanshin
|
||||
*
|
||||
* @section BackgroundRead_ESP32_versions Changelog
|
||||
*
|
||||
* Version | Date | Developer | Comments
|
||||
* ------- | ---------- | ----------- | --------
|
||||
* 1.0.3 | 2020-12-02 | SV-Zanshin | Corrected call to "AlertOnConversion()"
|
||||
* 1.0.2 | 2020-06-30 | SV-Zanshin | Issue #58 - clang-formatted document
|
||||
* 1.0.1 | 2020-03-24 | SV-Zanshin | Issue #53 - Doxygen documentation
|
||||
* 1.0.0 | 2019-02-17 | SV-Zanshin | Cloned and adapted from "BackgroundRead.ino" program
|
||||
*
|
||||
*/
|
||||
#if !defined(ESP32)
|
||||
#error Example program only functions on the ESP32 / ESP8266 platforms
|
||||
#endif
|
||||
|
||||
/**************************************************************************************************
|
||||
** Declare all include files **
|
||||
**************************************************************************************************/
|
||||
#include <INA.h> // Include the INA library
|
||||
|
||||
/**************************************************************************************************
|
||||
** Declare program Constants, global variables and instantiate classes **
|
||||
**************************************************************************************************/
|
||||
INA_Class INA; ///< INA class instantiation
|
||||
const uint8_t INA_ALERT_PIN = A0; ///< Pin-Change used for INA "ALERT" functionality
|
||||
const uint32_t SERIAL_SPEED = 115200; ///< Use fast serial speed
|
||||
volatile uint8_t deviceNumber = UINT8_MAX; ///< Device Number to use in example
|
||||
volatile uint64_t sumBusMillVolts = 0; ///< Sum of bus voltage readings
|
||||
volatile int64_t sumBusMicroAmps = 0; ///< Sum of bus amperage readings
|
||||
volatile uint8_t readings = 0; ///< Number of measurements taken
|
||||
portMUX_TYPE mux = portMUX_INITIALIZER_UNLOCKED; ///< Synchronization variable
|
||||
|
||||
void IRAM_ATTR InterruptHandler() {
|
||||
/*!
|
||||
@brief Interrupt service routine for the INA pin
|
||||
@details Routine is called whenever the INA_ALERT_PIN changes value
|
||||
*/
|
||||
portENTER_CRITICAL_ISR(&mux);
|
||||
sei(); // Enable interrupts (for I2C calls)
|
||||
sumBusMillVolts += INA.getBusMilliVolts(deviceNumber); // Add current value to sum
|
||||
sumBusMicroAmps += INA.getBusMicroAmps(deviceNumber); // Add current value to sum
|
||||
readings++;
|
||||
INA.waitForConversion(deviceNumber); // Wait for conv and reset interrupt
|
||||
cli(); // Disable interrupts
|
||||
portEXIT_CRITICAL_ISR(&mux);
|
||||
} // of ISR for handling interrupts
|
||||
|
||||
void setup() {
|
||||
/*!
|
||||
@brief Arduino method called once at startup to initialize the system
|
||||
@details This is an Arduino IDE method which is called first upon boot or restart. It is only
|
||||
called one time and then control goes to the main "loop()" method, from which control
|
||||
never returns
|
||||
@return void
|
||||
*/
|
||||
pinMode(INA_ALERT_PIN, INPUT_PULLUP);
|
||||
attachInterrupt(digitalPinToInterrupt(INA_ALERT_PIN), InterruptHandler, FALLING);
|
||||
Serial.begin(SERIAL_SPEED);
|
||||
Serial.print(F("\n\nBackground INA Read V1.0.1\n"));
|
||||
uint8_t devicesFound = 0;
|
||||
while (deviceNumber == UINT8_MAX) // Loop until we find the first device
|
||||
{
|
||||
devicesFound = INA.begin(1, 100000); // +/- 1 Amps maximum for 0.1 Ohm resistor
|
||||
Serial.println(INA.getDeviceName(devicesFound - 1));
|
||||
for (uint8_t i = 0; i < devicesFound; i++) {
|
||||
/* Change the "INA226" in the following statement to whatever device you have attached and
|
||||
want to measure */
|
||||
if (strcmp(INA.getDeviceName(i), "INA219") == 0) {
|
||||
deviceNumber = i;
|
||||
INA.reset(deviceNumber); // Reset device to default settings
|
||||
break;
|
||||
} // of if-then we have found an INA226
|
||||
} // of for-next loop through all devices found
|
||||
if (deviceNumber == UINT8_MAX) {
|
||||
Serial.print(F("No INA found. Waiting 5s and retrying...\n"));
|
||||
delay(5000);
|
||||
} // of if-then no INA226 found
|
||||
} // of if-then no device found
|
||||
Serial.print(F("Found INA at device number "));
|
||||
Serial.println(deviceNumber);
|
||||
Serial.println();
|
||||
INA.setAveraging(64, deviceNumber); // Average each reading 64 times
|
||||
INA.setBusConversion(8244, deviceNumber); // Maximum conversion time 8.244ms
|
||||
INA.setShuntConversion(8244, deviceNumber); // Maximum conversion time 8.244ms
|
||||
INA.setMode(INA_MODE_CONTINUOUS_BOTH, deviceNumber); // Bus/shunt measured continuously
|
||||
INA.alertOnConversion(true, deviceNumber); // Make alert pin go low on finish
|
||||
} // of method setup()
|
||||
|
||||
void loop() {
|
||||
/*!
|
||||
@brief Arduino method for the main program loop
|
||||
@details This is the main program for the Arduino IDE, it is called in an infinite loop. The
|
||||
INA226 measurements are triggered by the interrupt handler each time a conversion is
|
||||
ready and stored in variables. The main program doesn't call any INA library functions,
|
||||
that is done in the interrupt handler. Each time 10 readings have been collected the
|
||||
program will output the averaged values and measurements resume from that point onwards
|
||||
@return void
|
||||
*/
|
||||
static long lastMillis = millis(); // Store the last time we printed something
|
||||
if (readings >= 10) {
|
||||
Serial.print(F("Averaging readings taken over "));
|
||||
Serial.print((float)(millis() - lastMillis) / 1000, 2);
|
||||
Serial.print(F(" seconds.\nBus voltage: "));
|
||||
Serial.print((float)sumBusMillVolts / readings / 1000.0, 4);
|
||||
Serial.print(F("V\nBus amperage: "));
|
||||
Serial.print((float)sumBusMicroAmps / readings / 1000.0, 4);
|
||||
Serial.print(F("mA\n\n"));
|
||||
lastMillis = millis();
|
||||
cli(); // Disable interrupts to reset values
|
||||
readings = 0;
|
||||
sumBusMillVolts = 0;
|
||||
sumBusMicroAmps = 0;
|
||||
sei(); // Enable interrupts again
|
||||
} // of if-then we've reached the required amount of readings
|
||||
} // of method loop()
|
||||
@@ -0,0 +1,430 @@
|
||||
/*
|
||||
Program to demonstrate using the interrupt pin of any INA2xx which supports that functionality in
|
||||
order to trigger readings in the background and using a timer on the Arduino to trigger data
|
||||
averaging and storing or displaying the computed values.
|
||||
|
||||
The INA226 is set up to pull the alert pin down when a measurement is ready. The program has set
|
||||
the bus and shunt to the maximum conversion time of 8.244ms and then averaging to 8, so each
|
||||
measurement will take about 64ms. The interrupt vector "PCINT0_vect" is called and the readings
|
||||
from the INA226 are read and added to the averages.
|
||||
|
||||
A timer interrupt is defined in the setup() method that triggers a call to the vector
|
||||
"TIMER1_COMPA_vect" once every second. The average values collected in the "PCINT0_vect" call are
|
||||
then taken and stored in memory. As the amount of RAM is limited and the absolute readings are 2
|
||||
Bytes long while the delta values to the previous measurement are usually quite small, a variable
|
||||
length Huffmann coding has been implemented at a nibble (4 bit) level to provide a higher-density
|
||||
method of storing data. Each array is declared at 600 Bytes (one array for voltage measurements and
|
||||
one array for shunt voltage) and those 1200 Bytes total can store up to 18 minutes of per-second
|
||||
data, which would otherwise occupy over 4Kb memory. The Huffmann encoding method is described in
|
||||
more detail in the interrupt code below.
|
||||
|
||||
This example works on Atmel-Arduinos since it uses Atmel interrupts which are different on
|
||||
processors such as the ESP32. The value of ARRAY_BYTES is set at 1200, which works on Arduinos with
|
||||
2K or more of RAM, smaller processors would need to reduce this value in order to work correctly.
|
||||
The example is also coded for the INA226, as a chip with an ALERT pin is required for the program
|
||||
to work; additionally the hard-coded LSB values for the bus voltage and shunt voltage have been set
|
||||
to those used in the INA226.
|
||||
|
||||
Detailed documentation can be found on the GitHub Wiki pages at
|
||||
https://github.com/Zanduino/INA/wiki
|
||||
|
||||
This example is for a INA226 set up to measure a 5-Volt load with a 0.1Ω resistor in place, this is
|
||||
the same setup that can be found in the Adafruit INA226 breakout board. The complex calibration
|
||||
options are done at runtime using the 2 parameters specified in the "begin()" call and the library
|
||||
has gone to great lengths to avoid the use of floating point to conserve space and minimize
|
||||
runtime. This demo program uses floating point only to convert and display the data conveniently.
|
||||
The INA226 uses 15 bits of precision, and even though the current and watt information is returned
|
||||
using 32-bit integers the precision remains the same.
|
||||
|
||||
The INA226 is set up to measure using the maximum conversion length (and maximum accuracy) and then
|
||||
average those readings 64 times. This results in readings taking 8.244ms x 64 = 527.616ms or just
|
||||
less than 2 times a second. The pin-change interrupt handler is called when a reading is finished
|
||||
and the INA226 pulls the pin down to ground, it resets the pin status and adds the readings to the
|
||||
global variables. The main program will do whatever processing it has to and every 5 seconds it
|
||||
will display the current averaged readings and reset them.
|
||||
|
||||
The datasheet for the INA226 can be found at http://www.ti.com/lit/ds/symlink/INA226.pdf and it
|
||||
contains the information required in order to hook up the device. Unfortunately it comes as a VSSOP
|
||||
package but it can be soldered onto a breakout board for breadboard use. The INA226 is quite
|
||||
similar to the INA219 mentioned above, but it can take bus voltages of up to 36V (which I needed in
|
||||
order to monitor a 24V battery system which goes above 28V while charging and which is above the
|
||||
absolute limits of the INA219). It is also significantly more accurate than the INA219, plus has an
|
||||
alert pin.
|
||||
|
||||
Interrupts on Arduinos can get a bit confusing, differentiating between external interrupts and pin
|
||||
change interrupts. The external interrupts are limited and which pins are available are different
|
||||
for each processor, see
|
||||
https://www.arduino.cc/reference/en/language/functions/external-interrupts/attachinterrupt/ for
|
||||
additional information. Pin Change interrupts, on the other hand, can be assigned to most pins, but
|
||||
these interrupts are shared in groups of pins (call "ports") and when the interrupts are triggered
|
||||
they call one of 3 possible ISRs. This program makes use of PCINT0_vect and the interrupt is set to
|
||||
pin 8. The tests were done on an Arduino UNO and Arduino Micro using this pin
|
||||
|
||||
Sometimes the INA devices will do a soft/hard reset on voltage spikes (despite using decoupling
|
||||
capacitors) and since the "PCINT0_vect" is called only when the ALERT pin is pulled low and the
|
||||
default mode of the INA226 upon reset is "off, this would result in the program never collecting
|
||||
statistics. For this reason the TIMER1 is used as a watchdog timer, triggering an interrupt every
|
||||
second. If no measurements are detected then the INA226 is manually reset and processing continues.
|
||||
|
||||
GNU General Public License 3
|
||||
============================
|
||||
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 (see
|
||||
https://github.com/Zanduino/INA/blob/master/LICENSE). If not, see
|
||||
<http://www.gnu.org/licenses/>.
|
||||
|
||||
Vers. Date Developer Comments
|
||||
====== ========== ========== ==============================================================
|
||||
1.0.1 2020-06-30 SV-Zanshin Issue #58 - clang-formatted document
|
||||
1.0.0 2018-10-13 SV-Zanshin Ready for publishing
|
||||
1.0.0 2018-10-03 SV-Zanshin Cloned and adapted example
|
||||
*/
|
||||
#include <INA.h> // INA Library
|
||||
|
||||
#include "MB85_FRAM.h" // I2C FRAM Library
|
||||
/**************************************************************************************************
|
||||
** Declare program Constants **
|
||||
**************************************************************************************************/
|
||||
const uint8_t INA_ALERT_PIN = 8; // Pin 8.
|
||||
const uint8_t GREEN_LED_PIN = 13; // Green LED (standard location)
|
||||
const uint32_t SERIAL_SPEED = 115200; // Use fast serial speed
|
||||
const uint16_t ARRAY_BYTES = 1200; // Bytes in data array
|
||||
/**************************************************************************************************
|
||||
** Declare global variables, structures and instantiate classes **
|
||||
**************************************************************************************************/
|
||||
uint8_t deviceNumber = UINT8_MAX; // Device Number to use in example
|
||||
volatile uint64_t sumBusRaw = 0; // Sum of bus raw values
|
||||
volatile int64_t sumShuntRaw = 0; // Sum of shunt raw values
|
||||
volatile uint8_t readings = 0; // Number of measurements taken
|
||||
uint8_t chips_detected = 0; // Number of I2C FRAM chips detected
|
||||
volatile uint32_t framIndex = 0; // Index to the next free position
|
||||
INA_Class INA; // INA class instantiation
|
||||
MB85_FRAM_Class FRAM; // FRAM Memory class instantiation
|
||||
|
||||
void writeNibble(uint8_t dataArray[], const uint16_t nibblePos, const uint8_t nibbleData) {
|
||||
/************************************************************************************************
|
||||
** Method "writeNibble()" will write the LSB 4 bits of "nibbleData" to the "dataArray" nibble **
|
||||
** offset "nibblePos", each index position is 4 bits. **
|
||||
************************************************************************************************/
|
||||
uint8_t writeByte = *(dataArray + (nibblePos / 2)); // Read the correct byte and select
|
||||
if (nibblePos & 1) { // whether the LSB or MSB is to be set
|
||||
writeByte = (writeByte & 0xF0) | (nibbleData & 0xF); // Keep MSB & set the LSB to value
|
||||
} else {
|
||||
writeByte = (nibbleData << 4) | (writeByte & 0xF); // Keep LSB & set the MSB to value
|
||||
} // of if-then-else nibblePos is odd
|
||||
*(dataArray + (nibblePos / 2)) = writeByte; // Write the new value to array
|
||||
} // of method "writeNibble()"
|
||||
uint8_t readNibble(uint8_t dataArray[], const uint16_t nibblePos) {
|
||||
/************************************************************************************************
|
||||
** Method "readNibble()" will read the nibble addressed by "nibblePos" into the write the 4 **
|
||||
** LSB bits of the return value. Each index position of the virtual array is 4 bits. **
|
||||
************************************************************************************************/
|
||||
uint8_t returnVal = *(dataArray + (nibblePos / 2)); // Read the correct byte and select
|
||||
if (nibblePos & 1) { // whether the LSB or MSB is to be returned
|
||||
returnVal = returnVal & 0xF; // Use the 4 LSB bits
|
||||
} else {
|
||||
returnVal = returnVal >> 4; // Use the 4 MSB bits
|
||||
} // of if-then-else nibblePos is odd
|
||||
return returnVal; // Return the computed nibble
|
||||
} // of method "readNibble()"
|
||||
ISR(PCINT0_vect) {
|
||||
/************************************************************************************************
|
||||
** Declare interrupt service routine for the pin-change interrupt on pin 8 which is set in the **
|
||||
** setup() method **
|
||||
************************************************************************************************/
|
||||
static uint16_t tempsumBusRaw; // Declare as static to only init 1
|
||||
static int16_t tempsumShuntRaw; // Declare as static to only init 1
|
||||
*digitalPinToPCMSK(INA_ALERT_PIN) &= ~bit(digitalPinToPCMSKbit(INA_ALERT_PIN)); // Disable PCMSK
|
||||
PCICR &= ~bit(digitalPinToPCICRbit(INA_ALERT_PIN)); // disable interrupt for the group
|
||||
digitalWrite(GREEN_LED_PIN, !digitalRead(GREEN_LED_PIN)); // Toggle LED to show we are working
|
||||
sei(); // Enable interrupts for I2C calls
|
||||
tempsumBusRaw = INA.getBusRaw(deviceNumber); // Read the current value into temp
|
||||
tempsumShuntRaw = INA.getShuntRaw(deviceNumber); // Read the current value into temp
|
||||
INA.waitForConversion(deviceNumber); // Resets interrupt flag and start
|
||||
cli(); // Disable interrupts
|
||||
sumBusRaw += tempsumBusRaw; // copy value while ints disabled
|
||||
sumShuntRaw += tempsumShuntRaw; // copy value while ints disabled
|
||||
readings++; // Increment the number of readings
|
||||
*digitalPinToPCMSK(INA_ALERT_PIN) |= bit(digitalPinToPCMSKbit(INA_ALERT_PIN)); // Enable PCMSK
|
||||
PCIFR |= bit(digitalPinToPCICRbit(INA_ALERT_PIN)); // clear any outstanding interrupt
|
||||
PCICR |= bit(digitalPinToPCICRbit(INA_ALERT_PIN)); // enable interrupt for the group
|
||||
} // of ISR handler for INT0 group of pins
|
||||
void writeDataToArray(uint8_t dataArray[], uint16_t &nibbleIndex, const int16_t deltaData) {
|
||||
/************************************************************************************************
|
||||
** Function "writeDataToArray()" writes 4LSB from "nibbleData" to the "nibbleIndex" nibble in **
|
||||
*"dataArray". A Huffmann-like encoding with variable length is used to write the delta values **
|
||||
** to the appropriate array. Each array "index" element is one nibble (4 bits) and the MSB **
|
||||
** characters of the MSB nibble denote the record type. If the MSB is "B0" then it is a **
|
||||
** 1-nibble long value, if the 2 MSB are "B10" then it is a 2 nibble value, if "B110" then 3 **
|
||||
** nibbles, "B1110" denotes 4 and "B1111" denotes 5 nibbles. See the table below: **
|
||||
** **
|
||||
** 24-Bit representation Data Bits Value Range **
|
||||
** ===================== ============ =============== **
|
||||
** ----------------0xxx 3 bits data -4 to 3 **
|
||||
** ------------10xxxxxx 6 bits data -16 to 15 **
|
||||
** --------110xxxxxxxxx 9 bits data -256 to 255 **
|
||||
** ----1110xxxxxxxxxxxx 12 bits data -2048 to 2047 **
|
||||
** 1111xxxxxxxxxxxxxxxx 16 bits data -32768 to 32767 **
|
||||
************************************************************************************************/
|
||||
|
||||
if (deltaData >= -4 && deltaData <= 3) // 1N, format 0xxx
|
||||
{
|
||||
writeNibble(dataArray, nibbleIndex++, deltaData & B111); // Write 1N to array
|
||||
} else {
|
||||
if (deltaData >= -16 && deltaData <= 15) // 2N, format 10xxxxxx
|
||||
{
|
||||
writeNibble(dataArray, nibbleIndex++, ((deltaData >> 4) & B11) | B1000); // write MSB
|
||||
writeNibble(dataArray, nibbleIndex++, deltaData); // write LSB
|
||||
} else {
|
||||
if (deltaData >= -256 && deltaData <= 255) // 3N, format 110xxxxxxxxx
|
||||
{
|
||||
writeNibble(dataArray, nibbleIndex++, ((deltaData >> 8) & 1) | B1100); // Set 3MSB 9th bit
|
||||
writeNibble(dataArray, nibbleIndex++, deltaData >> 4 & B1111); // write 4 MSB bits byte 1
|
||||
writeNibble(dataArray, nibbleIndex++, deltaData); // write 4 LSB bits byte 1
|
||||
} else {
|
||||
if (deltaData >= -2048 && deltaData <= 2047) // 4N, format 1110xxxxxxxxxxxx
|
||||
{
|
||||
writeNibble(dataArray, nibbleIndex++, B1110); // Header nibble
|
||||
writeNibble(dataArray, nibbleIndex++, deltaData >> 8); // next nibble
|
||||
writeNibble(dataArray, nibbleIndex++, deltaData >> 4); // next nibble
|
||||
writeNibble(dataArray, nibbleIndex++, deltaData); // LSB nibble
|
||||
} else { // 5N, fmt 1111xxxxxxxxxxxxxxxx
|
||||
writeNibble(dataArray, nibbleIndex++, B1111); // Header nibble
|
||||
writeNibble(dataArray, nibbleIndex++, deltaData >> 12); // MSB nibble
|
||||
writeNibble(dataArray, nibbleIndex++, deltaData >> 8); // next nibble
|
||||
writeNibble(dataArray, nibbleIndex++, deltaData >> 4); // next nibble
|
||||
writeNibble(dataArray, nibbleIndex++, deltaData); // LSB nibble
|
||||
} // if-then-else value fits in 4 or 5 nibbles
|
||||
} // if-then-else value fits in 3 nibbles
|
||||
} // if-then-else value fits in 2 nibbles
|
||||
} // if-then-else value fits in 1 nibble
|
||||
} // of method "WriteDataToArray()"
|
||||
int16_t readDataFromArray(uint8_t dataArray[], uint16_t &nibbleIndex) {
|
||||
/************************************************************************************************
|
||||
** Function "readDataToArray()" returns a 2-Byte signed integer from "dataArray" starting at **
|
||||
** "nibbleIndex" and expanding the Array's internal Huffmann-encoding values. See the descrip- **
|
||||
** tion of writeDataToArray() for details **
|
||||
************************************************************************************************/
|
||||
|
||||
int16_t outValue = 0; // Declare return variable
|
||||
uint8_t controlBits = readNibble(dataArray, nibbleIndex++); // Read the header nibble
|
||||
if (controlBits >> 3 == 0) // ----------------0xxx 3 bits data - 4 to 3
|
||||
{
|
||||
outValue = controlBits & B111; // mask High Bit
|
||||
if (outValue >> 2 & B1) { outValue |= 0xFFF8; } // If it is a negative number
|
||||
} else {
|
||||
if (controlBits >> 2 == B10) // ------------10xxxxxx 6 bits data - 16 to 15
|
||||
{
|
||||
outValue = (controlBits & B11) << 4; // mask 2 High Bits
|
||||
outValue |= readNibble(dataArray, nibbleIndex++); // move in 4 LSB
|
||||
if (outValue >> 5 & B1) { outValue |= 0xFFE0; } // If it is a negative number
|
||||
} else {
|
||||
if (controlBits >> 1 == B110) // --------110xxxxxxxxx 9 bits data - 256 to 255
|
||||
{
|
||||
outValue = (controlBits & B1) << 8; // mask 2 High Bits
|
||||
outValue |= readNibble(dataArray, nibbleIndex++) << 4; // move in 4 middle bits
|
||||
outValue |= readNibble(dataArray, nibbleIndex++); // move in 4 LSB
|
||||
if (outValue >> 8 & B1) { outValue |= 0xFE00; } // If it is a negative number
|
||||
} else {
|
||||
if (controlBits == B1110) // ----1110xxxxxxxxxxxx 12 bits data - 2048 to 2047
|
||||
{
|
||||
outValue = readNibble(dataArray, nibbleIndex++) << 8; // move in 4 high bits
|
||||
outValue |= readNibble(dataArray, nibbleIndex++) << 4; // move in 4 middle bits
|
||||
outValue |= readNibble(dataArray, nibbleIndex++); // move in 4 low bits
|
||||
if (outValue >> 11 & B1) { outValue |= 0xF000; } // If it is a negative number
|
||||
} else {
|
||||
if (controlBits == B1111) // 1111xxxxxxxxxxxxxxxx 16 bits data - 16384 to 16383
|
||||
{
|
||||
outValue = readNibble(dataArray, nibbleIndex++) << 12; // move in 4 high bits
|
||||
outValue |= readNibble(dataArray, nibbleIndex++) << 8; // move in 4 middle bits
|
||||
outValue |= readNibble(dataArray, nibbleIndex++) << 4; // move in 4 middle bits
|
||||
outValue |= readNibble(dataArray, nibbleIndex++); // move in 4 low bits
|
||||
} // if-then 5 nibbles
|
||||
} // if-then-else 4 nibbles
|
||||
} // if-then-else 3 nibbles
|
||||
} // if-then-else 2 nibbles
|
||||
} // if-then-else 1 nibble
|
||||
return (outValue);
|
||||
} // of method "readDataFromArray()"
|
||||
|
||||
ISR(TIMER1_COMPA_vect) {
|
||||
/**********************************************************************************************
|
||||
** Declare interrupt service routine for TIMER1, which is set to trigger once every second **
|
||||
**********************************************************************************************/
|
||||
static int16_t deltaBus, deltaShunt; // Difference value from last
|
||||
static uint16_t arrayNibbleIndex = 0; // Array index in Nibbles
|
||||
static int16_t lastBusRaw = 0; // Value from last reading
|
||||
static int16_t lastShuntRaw = 0; // Value from last reading
|
||||
static int16_t baseBusRaw = 0; // Base value for delta readings
|
||||
static int16_t baseShuntRaw = 0; // Base value for delta readings
|
||||
static uint16_t arrayReadings = 0; // Number of readings in array
|
||||
static uint8_t dataArray[ARRAY_BYTES]; // Array for bus and shunt readings
|
||||
if (arrayNibbleIndex == 0 && millis() < 3000) { // Skip first 3 seconds
|
||||
baseBusRaw = (int16_t)(sumBusRaw / readings); // after startup to allow settings
|
||||
lastBusRaw = baseBusRaw; // to settle
|
||||
baseShuntRaw = (int16_t)(sumShuntRaw / readings);
|
||||
lastShuntRaw = baseShuntRaw;
|
||||
readings = 0; // then skip readings to let the
|
||||
sumBusRaw = 0; // sensor settle down
|
||||
sumShuntRaw = 0; // Reset values
|
||||
return;
|
||||
} // of if-then first second after startup
|
||||
deltaBus = ((int16_t)(sumBusRaw / readings) - lastBusRaw); // Compute the delta bus
|
||||
deltaShunt = ((int16_t)(sumShuntRaw / readings) - lastShuntRaw); // Compute the delta shunt
|
||||
writeDataToArray(dataArray, arrayNibbleIndex, deltaBus); // Add bus reading to array
|
||||
writeDataToArray(dataArray, arrayNibbleIndex, deltaShunt); // Add shunt reading to array
|
||||
arrayReadings++; // increment the counter
|
||||
lastBusRaw = sumBusRaw / readings; // Reset values
|
||||
lastShuntRaw = sumShuntRaw / readings; // Reset values
|
||||
readings = 0; // Reset values
|
||||
sumBusRaw = 0; // Reset values
|
||||
sumShuntRaw = 0; // Reset values
|
||||
/*****************************************************************************************************************
|
||||
** Once the array could fill up on the next reading (2x max reading of 5 nibbles) then it is
|
||||
*time to flush the **
|
||||
** the accumulated readings. **
|
||||
*****************************************************************************************************************/
|
||||
if ((arrayNibbleIndex + 10) / 2 >= ARRAY_BYTES) // //
|
||||
{ // //
|
||||
int16_t busValue = 0; // Contains current bus value //
|
||||
int16_t shuntValue = 0; // Contains current shunt value //
|
||||
uint16_t workNibbleIndex = 0; // Index into array for reading //
|
||||
/***************************************************************************************************************
|
||||
** If there is a FRAM memory board attached, then copy the array contents to it **
|
||||
***************************************************************************************************************/
|
||||
if (chips_detected > 0) // Only execute if there is memory //
|
||||
{ // //
|
||||
if ((framIndex + sizeof(dataArray) <
|
||||
FRAM.totalBytes())) // Only write when space available //
|
||||
{ // //
|
||||
cli(); // Enable interrupts temporarily //
|
||||
Serial.print(millis() / 1000 / 60); // //
|
||||
Serial.print(" "); // //
|
||||
Serial.print(F("Writing ")); // //
|
||||
Serial.print(sizeof(dataArray)); // //
|
||||
Serial.print(" Bytes to memory @"); // //
|
||||
Serial.print(framIndex); // //
|
||||
Serial.print(".\n"); // //
|
||||
sei(); // Disable interrupts again //
|
||||
FRAM.write(framIndex, dataArray); // Write the whole array to FRAM //
|
||||
framIndex += sizeof(dataArray); // set index to new location //
|
||||
} // of if-then there is space in the EEPROM // //
|
||||
} // of if-then we have at least one EEPROM attached to the I2C bus // //
|
||||
for (uint16_t readingNo = 1; readingNo <= arrayReadings;
|
||||
readingNo++) // Process every reading in array //
|
||||
{ // //
|
||||
busValue = readDataFromArray(dataArray, workNibbleIndex); // Get next bus value from array //
|
||||
baseBusRaw += busValue; // apply delta value to bus base //
|
||||
shuntValue =
|
||||
readDataFromArray(dataArray, workNibbleIndex); // Get shunt next value from array //
|
||||
baseShuntRaw += shuntValue; // apply delta value to shunt base //
|
||||
/*************************************************************************************************************
|
||||
** Insert code here to save data to static RAM or to a SD-Card or elsewhere **
|
||||
*************************************************************************************************************/
|
||||
|
||||
cli(); // Enable interrupts temporarily //
|
||||
Serial.print(millis() / 1000);
|
||||
Serial.print(" ");
|
||||
Serial.print(readingNo);
|
||||
Serial.print(" ");
|
||||
Serial.print(baseBusRaw * 0.00125, 4);
|
||||
Serial.print("V ");
|
||||
Serial.print(0.0025 * baseShuntRaw);
|
||||
Serial.println("mA");
|
||||
sei(); // Disable interrupts again //
|
||||
|
||||
} // of for-next each array reading // //
|
||||
arrayNibbleIndex = 0; // reset //
|
||||
arrayReadings = 0; // reset //
|
||||
} // of if-then the internal array is full // //
|
||||
} // of ISR "TIMER1_COMPA_vect" // //
|
||||
|
||||
/*******************************************************************************************************************
|
||||
** Method Setup(). This is an Arduino IDE method which is called first upon initial boot or
|
||||
*restart. It is only **
|
||||
** called one time and all of the variables and other initialization calls are done here prior to
|
||||
*entering the **
|
||||
** main loop for data measurement. **
|
||||
*******************************************************************************************************************/
|
||||
void setup() // //
|
||||
{ // //
|
||||
pinMode(GREEN_LED_PIN, OUTPUT); // Define the green LED as an output//
|
||||
digitalWrite(GREEN_LED_PIN, true); // Turn on the LED //
|
||||
pinMode(INA_ALERT_PIN, INPUT_PULLUP); // Declare pin with pull-up resistor//
|
||||
*digitalPinToPCMSK(INA_ALERT_PIN) |=
|
||||
bit(digitalPinToPCMSKbit(INA_ALERT_PIN)); // Enable PCMSK pin //
|
||||
PCIFR |= bit(digitalPinToPCICRbit(INA_ALERT_PIN)); // clear any outstanding interrupt //
|
||||
PCICR |= bit(digitalPinToPCICRbit(INA_ALERT_PIN)); // enable interrupt for the group //
|
||||
Serial.begin(SERIAL_SPEED); // Start serial communications //
|
||||
#ifdef __AVR_ATmega32U4__ // If this is a 32U4 processor, //
|
||||
delay(2000); // wait 3 seconds for serial port //
|
||||
#endif // interface to initialize //
|
||||
Serial.print(
|
||||
F("\n\nINA Data Logging with interrupts V1.0.3\n")); // Display program information //
|
||||
uint8_t devicesFound = 0; // Number of INA2xx found on I2C //
|
||||
while (deviceNumber == UINT8_MAX) // Loop until we find devices //
|
||||
{ // //
|
||||
devicesFound = INA.begin(1, 100000); // ±1Amps maximum for 0.1Ω resistor //
|
||||
for (uint8_t i = 0; i < devicesFound; i++) // the first INA226 device found //
|
||||
{ // Change "INA226" to "INA260" or //
|
||||
// whichever INA2xx to measure //
|
||||
if (strcmp(INA.getDeviceName(i), "INA226") == 0) // Set deviceNumber appropriately //
|
||||
{ // //
|
||||
deviceNumber = i; // //
|
||||
INA.reset(deviceNumber); // Reset device to default settings //
|
||||
break; // //
|
||||
} // of if-then we have found an INA226 // //
|
||||
} // of for-next loop through all devices found // //
|
||||
if (deviceNumber == UINT8_MAX) // Try again if no device found //
|
||||
{ // //
|
||||
Serial.print(F("No INA226 found. Waiting 5s.\n")); // //
|
||||
delay(5000); // //
|
||||
} // of if-then no INA226 found // //
|
||||
} // of if-then no device found // //
|
||||
Serial.print(F("Found INA226 at device number ")); // //
|
||||
Serial.println(deviceNumber); // //
|
||||
Serial.println(); // //
|
||||
INA.setAveraging(64, deviceNumber); // Average each reading 64 times //
|
||||
INA.setAveraging(8, deviceNumber); // Average each reading 4 times //
|
||||
INA.setBusConversion(82440, deviceNumber); // Maximum conversion time 8.244ms //
|
||||
INA.setShuntConversion(82440, deviceNumber); // Maximum conversion time 8.244ms //
|
||||
INA.setMode(INA_MODE_CONTINUOUS_BOTH, deviceNumber); // Bus/shunt measured continuously //
|
||||
INA.AlertOnConversion(true, deviceNumber); // Make alert pin go low on finish //
|
||||
chips_detected = FRAM.begin(); // return number of memories //
|
||||
if (chips_detected > 0) { // //
|
||||
Serial.print(F("Found ")); // //
|
||||
Serial.print(chips_detected); // //
|
||||
Serial.print(F(" FRAM with a total of ")); // //
|
||||
uint32_t totalMemory = 0; // //
|
||||
for (uint8_t i = 0; i < chips_detected; i++) { // //
|
||||
totalMemory += FRAM.memSize(i); // Add memory of chip to total //
|
||||
} // of for-next each memory // //
|
||||
Serial.print(totalMemory / 1024); // //
|
||||
Serial.println(F("KB memory.")); // //
|
||||
} // if-then we have found a FRAM memory // //
|
||||
cli(); // disable interrupts while setting //
|
||||
TCCR1A = 0; // TCCR1A register reset //
|
||||
TCCR1B = 0; // TCCR1B register reset //
|
||||
TCNT1 = 0; // initialize counter //
|
||||
OCR1A = 15624; // ((16*10^6) / (1*1024)) - 1 //
|
||||
TCCR1B |= (1 << WGM12); // Enable CTC mode //
|
||||
TCCR1B |= (1 << CS12) | (1 << CS10); // CS10 & CS12 for 1024 prescaler //
|
||||
TIMSK1 |= (1 << OCIE1A); // Enable timer compare interrupt //
|
||||
sei(); // re-enable interrupts //
|
||||
} // of method setup() // //
|
||||
|
||||
/*******************************************************************************************************************
|
||||
** This is the main program for the Arduino IDE, it is called in an infinite loop. The INA226
|
||||
*measurements are **
|
||||
** triggered by the interrupt handler each time a conversion is ready, and another interrupt is
|
||||
*triggered every **
|
||||
** second to store the collected readings. Thus the main program is free to do other tasks. **
|
||||
*******************************************************************************************************************/
|
||||
void loop() // //
|
||||
{ // //
|
||||
delay(10000);
|
||||
} // of method loop //----------------------------------//
|
||||
@@ -0,0 +1,156 @@
|
||||
/*!
|
||||
@file DisplayReadings.ino
|
||||
|
||||
@brief Example program for the INA Library demonstrating reading an INA device and displaying
|
||||
results
|
||||
|
||||
@section DisplayReadings_section Description
|
||||
|
||||
Program to demonstrate the INA library for the Arduino. When started, the library searches the
|
||||
I2C bus for all INA2xx devices. Then the example program goes into an infinite loop and displays
|
||||
the power measurements (bus voltage and current) for all devices.\n\n
|
||||
|
||||
Detailed documentation can be found on the GitHub Wiki pages at
|
||||
https://github.com/Zanduino/INA/wiki \n\n This example is for a INA set up to measure a 5-Volt
|
||||
load with a 0.1 Ohm resistor in place, this is the same setup that can be found in the Adafruit
|
||||
INA219 breakout board. The complex calibration options are done at runtime using the 2
|
||||
parameters specified in the "begin()" call and the library has gone to great lengths to avoid the
|
||||
use of floating point to conserve space and minimize runtime. This demo program uses floating
|
||||
point only to convert and display the data conveniently. The INA devices have 15 bits of
|
||||
precision, and even though the current and watt information is returned using 32-bit integers the
|
||||
precision remains the same.\n\n
|
||||
|
||||
The library supports multiple INA devices and multiple INA device types. The Atmel's EEPROM is
|
||||
used to store the 96 bytes of static information per device using
|
||||
https://www.arduino.cc/en/Reference/EEPROM function calls. Although up to 16 devices could
|
||||
theoretically be present on the I2C bus the actual limit is determined by the available EEPROM -
|
||||
ATmega328 UNO has 1024k so can support up to 10 devices but the ATmega168 only has 512 bytes
|
||||
which limits it to supporting at most 5 INAs. Support has been added for the ESP32 based
|
||||
Arduinos, these use the EEPROM calls differently and need specific code.
|
||||
|
||||
@section DisplayReadings_license GNU General Public License v3.0
|
||||
|
||||
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(see
|
||||
https://github.com/Zanduino/INA/blob/master/LICENSE). If not, see
|
||||
<http://www.gnu.org/licenses/>.
|
||||
|
||||
@section DisplayReadings_author Author
|
||||
|
||||
Written by Arnd <Arnd@Zanduino.Com> at https://www.github.com/SV-Zanshin
|
||||
|
||||
@section DisplayReadings_versions Changelog
|
||||
|
||||
| Version | Date | Developer | Comments |
|
||||
| ------- | ---------- | -----------| ----------------------------------------------------------- |
|
||||
| 1.0.8 | 2020-12-01 | SV-Zanshin | Issue #72. Allow dynamic RAM allocation instead of EEPROM |
|
||||
| 1.0.7 | 2020-06-30 | SV-Zanshin | Issue #58. Changed formatting to use clang-format |
|
||||
| 1.0.6 | 2020-06-29 | SV-Zanshin | Issue #57. Changed case of functions "Alert..." |
|
||||
| 1.0.5 | 2020-05-03 | SV-Zanshin | Moved setting of maxAmps and shunt to constants |
|
||||
| 1.0.4 | 2019-02-16 | SV-Zanshin | Reformatted and refactored for legibility and clarity |
|
||||
| 1.0.3 | 2019-02-10 | SV-Zanshin | Issue #38. Made pretty-print columns line up |
|
||||
| 1.0.3 | 2019-02-09 | SV-Zanshin | Issue #38. Added device number to display |
|
||||
| 1.0.2 | 2018-12-29 | SV-Zanshin | Converted comments to doxygen format |
|
||||
| 1.0.1 | 2018-09-22 | SV-Zanshin | Comments corrected, add INA wait loop, removed F("") calls |
|
||||
| 1.0.0 | 2018-06-22 | SV-Zanshin | Initial release |
|
||||
| 1.0.0b | 2018-06-17 | SV-Zanshin | INA219 and INA226 completed, including testing |
|
||||
| 1.0.0a | 2018-06-10 | SV-Zanshin | Initial coding |
|
||||
*/
|
||||
|
||||
#if ARDUINO >= 100 // Arduino IDE versions before 100 need to use the older library
|
||||
#include "Arduino.h"
|
||||
#else
|
||||
#include "WProgram.h"
|
||||
#endif
|
||||
#include <INA.h> // Zanshin INA Library
|
||||
|
||||
#if defined(_SAM3XA_) || defined(ARDUINO_ARCH_SAMD)
|
||||
// The SAM3XA architecture needs to include this library, it is already included automatically on
|
||||
// other platforms //
|
||||
#include <avr/dtostrf.h> // Needed for the SAM3XA (Arduino Zero)
|
||||
#endif
|
||||
|
||||
/**************************************************************************************************
|
||||
** Declare program constants, global variables and instantiate INA class **
|
||||
**************************************************************************************************/
|
||||
const uint32_t SERIAL_SPEED{115200}; ///< Use fast serial speed
|
||||
const uint32_t SHUNT_MICRO_OHM{100000}; ///< Shunt resistance in Micro-Ohm, e.g. 100000 is 0.1 Ohm
|
||||
const uint16_t MAXIMUM_AMPS{1}; ///< Max expected amps, clamped from 1A to a max of 1022A
|
||||
uint8_t devicesFound{0}; ///< Number of INAs found
|
||||
INA_Class INA; ///< INA class instantiation to use EEPROM
|
||||
// INA_Class INA(0); ///< INA class instantiation to use EEPROM
|
||||
// INA_Class INA(5); ///< INA class instantiation to use dynamic memory rather
|
||||
// than EEPROM. Allocate storage for up to (n) devices
|
||||
|
||||
void setup() {
|
||||
/*!
|
||||
* @brief Arduino method called once at startup to initialize the system
|
||||
* @details This is an Arduino IDE method which is called first upon boot or restart. It is only
|
||||
* called one time and then control goes to the "loop()" method, from which control
|
||||
* never returns. The serial port is initialized and the INA.begin() method called to
|
||||
* find all INA devices on the I2C bus and then the devices are initialized to given
|
||||
* conversion and averaging rates.
|
||||
* @return void
|
||||
*/
|
||||
Serial.begin(SERIAL_SPEED);
|
||||
#ifdef __AVR_ATmega32U4__ // If a 32U4 processor, then wait 2 seconds to initialize serial port
|
||||
delay(2000);
|
||||
#endif
|
||||
Serial.print("\n\nDisplay INA Readings V1.0.8\n");
|
||||
Serial.print(" - Searching & Initializing INA devices\n");
|
||||
/************************************************************************************************
|
||||
** The INA.begin call initializes the device(s) found with an expected ±1 Amps maximum current **
|
||||
** and for a 0.1Ohm resistor, and since no specific device is given as the 3rd parameter all **
|
||||
** devices are initially set to these values. **
|
||||
************************************************************************************************/
|
||||
devicesFound = INA.begin(MAXIMUM_AMPS, SHUNT_MICRO_OHM); // Expected max Amp & shunt resistance
|
||||
while (devicesFound == 0) {
|
||||
Serial.println(F("No INA device found, retrying in 10 seconds..."));
|
||||
delay(10000); // Wait 10 seconds before retrying
|
||||
devicesFound = INA.begin(MAXIMUM_AMPS, SHUNT_MICRO_OHM); // Expected max Amp & shunt resistance
|
||||
} // while no devices detected
|
||||
Serial.print(F(" - Detected "));
|
||||
Serial.print(devicesFound);
|
||||
Serial.println(F(" INA devices on the I2C bus"));
|
||||
INA.setBusConversion(8500); // Maximum conversion time 8.244ms
|
||||
INA.setShuntConversion(8500); // Maximum conversion time 8.244ms
|
||||
INA.setAveraging(128); // Average each reading n-times
|
||||
INA.setMode(INA_MODE_CONTINUOUS_BOTH); // Bus/shunt measured continuously
|
||||
INA.alertOnBusOverVoltage(true, 5000); // Trigger alert if over 5V on bus
|
||||
} // method setup()
|
||||
|
||||
void loop() {
|
||||
/*!
|
||||
* @brief Arduino method for the main program loop
|
||||
* @details This is the main program for the Arduino IDE, it is an infinite loop and keeps on
|
||||
* repeating. In order to format the output use is made of the "sprintf()" function, but in the
|
||||
* Arduino implementation it has no support for floating point output, so the "dtostrf()" function
|
||||
* is used to convert the floating point numbers into formatted strings.
|
||||
* @return void
|
||||
*/
|
||||
static uint16_t loopCounter = 0; // Count the number of iterations
|
||||
static char sprintfBuffer[100]; // Buffer to format output
|
||||
static char busChar[8], shuntChar[10], busMAChar[10], busMWChar[10]; // Output buffers
|
||||
|
||||
Serial.print(F("Nr Adr Type Bus Shunt Bus Bus\n"));
|
||||
Serial.print(F("== === ====== ======== =========== =========== ===========\n"));
|
||||
for (uint8_t i = 0; i < devicesFound; i++) // Loop through all devices
|
||||
{
|
||||
dtostrf(INA.getBusMilliVolts(i) / 1000.0, 7, 4, busChar); // Convert floating point to char
|
||||
dtostrf(INA.getShuntMicroVolts(i) / 1000.0, 9, 4, shuntChar); // Convert floating point to char
|
||||
dtostrf(INA.getBusMicroAmps(i) / 1000.0, 9, 4, busMAChar); // Convert floating point to char
|
||||
dtostrf(INA.getBusMicroWatts(i) / 1000.0, 9, 4, busMWChar); // Convert floating point to char
|
||||
sprintf(sprintfBuffer, "%2d %3d %s %sV %smV %smA %smW\n", i + 1, INA.getDeviceAddress(i),
|
||||
INA.getDeviceName(i), busChar, shuntChar, busMAChar, busMWChar);
|
||||
Serial.print(sprintfBuffer);
|
||||
} // for-next each INA device loop
|
||||
Serial.println();
|
||||
delay(10000); // Wait 10 seconds before next reading
|
||||
Serial.print(F("Loop iteration "));
|
||||
Serial.print(++loopCounter);
|
||||
Serial.print(F("\n\n"));
|
||||
} // method loop()
|
||||
|
After Width: | Height: | Size: 16 KiB |
|
After Width: | Height: | Size: 12 KiB |
|
After Width: | Height: | Size: 12 KiB |
|
After Width: | Height: | Size: 9.4 KiB |
|
After Width: | Height: | Size: 7.6 KiB |
@@ -0,0 +1,78 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 16.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
width="2272px" height="1424px" viewBox="0 0 2272 1424" enable-background="new 0 0 2272 1424" xml:space="preserve">
|
||||
<g>
|
||||
<g>
|
||||
<g>
|
||||
<rect x="276.251" y="312.91" fill="#42210B" stroke="#000000" stroke-miterlimit="10" width="532.48" height="532.482"/>
|
||||
<path fill="#F2F2F2" stroke="#000000" stroke-miterlimit="10" d="M760.853,779.667c0,9.854-7.99,17.845-17.845,17.845H341.975
|
||||
c-9.856,0-17.845-7.99-17.845-17.845V378.635c0-9.854,7.989-17.845,17.845-17.845h401.033c9.854,0,17.845,7.99,17.845,17.845
|
||||
V779.667z"/>
|
||||
<g>
|
||||
<g>
|
||||
<path stroke="#000000" stroke-width="7" stroke-miterlimit="10" d="M576.165,389.399l-6.323-1.693l-6.505,24.276
|
||||
c2.119,0.517,4.225,1.086,6.322,1.696L576.165,389.399z"/>
|
||||
<path stroke="#000000" stroke-width="7" stroke-miterlimit="10" d="M484.108,387.706l-6.322,1.693l6.504,24.277
|
||||
c2.099-0.608,4.205-1.178,6.322-1.696L484.108,387.706z"/>
|
||||
<path stroke="#000000" stroke-width="7" stroke-miterlimit="10" d="M421.33,450.058l-17.792-17.791l-4.627,4.628l17.791,17.792
|
||||
c0.752-0.787,1.512-1.571,2.284-2.345C419.76,451.568,420.544,450.811,421.33,450.058z"/>
|
||||
<path stroke="#000000" stroke-width="7" stroke-miterlimit="10" d="M356.043,511.143l-1.694,6.323l24.276,6.504
|
||||
c0.516-2.117,1.085-4.224,1.695-6.322L356.043,511.143z"/>
|
||||
<path stroke="#000000" stroke-width="7" stroke-miterlimit="10" d="M354.349,603.2l1.693,6.322l24.277-6.507
|
||||
c-0.61-2.096-1.179-4.202-1.696-6.319L354.349,603.2z"/>
|
||||
</g>
|
||||
<path d="M722.248,681.722c-19.225-19.225-49.098-21.281-70.618-6.202L537.163,561.054c-3.758-3.76-9.852-3.76-13.61,0
|
||||
c-3.758,3.757-3.758,9.85,0,13.608L638.02,689.129c-15.079,21.522-13.022,51.396,6.203,70.619
|
||||
c21.545,21.545,56.478,21.545,78.025,0C743.794,738.2,743.794,703.267,722.248,681.722z M711.361,748.861
|
||||
c-15.533,15.533-40.718,15.533-56.251,0c-15.533-15.536-15.533-40.72,0-56.253c15.534-15.534,40.717-15.533,56.251,0
|
||||
C726.893,708.142,726.895,733.325,711.361,748.861z"/>
|
||||
</g>
|
||||
</g>
|
||||
<g>
|
||||
<path fill="#42210B" stroke="#000000" stroke-miterlimit="10" d="M340.125,1111.09h-42.916V872.959h42.916V1111.09z"/>
|
||||
<path fill="#42210B" stroke="#000000" stroke-miterlimit="10" d="M572.323,1111.09h-41.695l-90.367-152.124
|
||||
c-1.395-2.325-3.663-6.106-6.804-11.34c-1.396-2.324-3.549-5.988-6.455-10.99v174.454h-34.542V872.959h47.452l83.389,140.262
|
||||
c0.698,1.164,5.757,9.712,15.177,25.645V872.959h33.845V1111.09z"/>
|
||||
<path fill="#42210B" stroke="#000000" stroke-miterlimit="10" d="M811.5,1111.09h-45.883l-19.015-56.697h-91.589l-18.666,56.697
|
||||
h-36.636l79.028-238.131H731.6L811.5,1111.09z M735.088,1020.025l-34.716-106.243l-34.717,106.243H735.088z"/>
|
||||
</g>
|
||||
</g>
|
||||
<g>
|
||||
<g>
|
||||
<rect x="971.331" y="312.91" fill="#42210B" stroke="#000000" stroke-miterlimit="10" width="321.421" height="321.423"/>
|
||||
<path fill="#F2F2F2" stroke="#000000" stroke-miterlimit="10" d="M1263.851,594.659c0,5.948-4.823,10.771-10.771,10.771h-242.077
|
||||
c-5.948,0-10.771-4.823-10.771-10.771V352.583c0-5.948,4.823-10.771,10.771-10.771h242.077c5.947,0,10.771,4.823,10.771,10.771
|
||||
V594.659z"/>
|
||||
<g>
|
||||
<g>
|
||||
<path stroke="#000000" stroke-width="7" stroke-miterlimit="10" d="M1152.368,359.083l-3.816-1.023l-3.928,14.654
|
||||
c1.279,0.311,2.551,0.656,3.816,1.023L1152.368,359.083z"/>
|
||||
<path stroke="#000000" stroke-width="7" stroke-miterlimit="10" d="M1096.801,358.06l-3.818,1.023l3.927,14.654
|
||||
c1.267-0.367,2.539-0.712,3.816-1.024L1096.801,358.06z"/>
|
||||
<path stroke="#000000" stroke-width="7" stroke-miterlimit="10" d="M1058.905,395.698l-10.74-10.74l-2.793,2.794l10.738,10.739
|
||||
c0.454-0.475,0.913-0.948,1.379-1.415C1057.957,396.609,1058.43,396.151,1058.905,395.698z"/>
|
||||
<path stroke="#000000" stroke-width="7" stroke-miterlimit="10" d="M1019.496,432.57l-1.022,3.816l14.653,3.926
|
||||
c0.311-1.279,0.655-2.549,1.022-3.816L1019.496,432.57z"/>
|
||||
<path stroke="#000000" stroke-width="7" stroke-miterlimit="10" d="M1018.474,488.138l1.021,3.816l14.654-3.928
|
||||
c-0.367-1.264-0.712-2.536-1.022-3.815L1018.474,488.138z"/>
|
||||
</g>
|
||||
<path d="M1240.548,535.537c-11.604-11.604-29.636-12.847-42.627-3.744l-69.096-69.096c-2.27-2.269-5.946-2.269-8.216,0
|
||||
c-2.269,2.269-2.269,5.946,0,8.215l69.096,69.095c-9.101,12.991-7.86,31.022,3.744,42.627c13.006,13.005,34.092,13.005,47.099,0
|
||||
C1253.554,569.629,1253.554,548.542,1240.548,535.537z M1233.977,576.063c-9.377,9.376-24.579,9.376-33.956,0
|
||||
c-9.376-9.378-9.376-24.579,0-33.956c9.377-9.376,24.578-9.376,33.956,0C1243.352,551.484,1243.354,566.686,1233.977,576.063z"
|
||||
/>
|
||||
</g>
|
||||
</g>
|
||||
<g>
|
||||
<path fill="#42210B" stroke="#000000" stroke-miterlimit="10" d="M1376.68,629.991h-56.36v-312.74h56.36V629.991z"/>
|
||||
<path fill="#42210B" stroke="#000000" stroke-miterlimit="10" d="M1681.633,629.991h-54.76l-118.682-199.786
|
||||
c-1.833-3.053-4.81-8.019-8.936-14.891c-1.831-3.054-4.659-7.865-8.477-14.435v229.112h-45.363v-312.74h62.317l109.515,184.209
|
||||
c0.919,1.529,7.564,12.755,19.936,33.679V317.251h44.449V629.991z"/>
|
||||
<path fill="#42210B" stroke="#000000" stroke-miterlimit="10" d="M1995.749,629.991h-60.26l-24.973-74.459h-120.284
|
||||
l-24.517,74.459h-48.113l103.788-312.74h69.422L1995.749,629.991z M1895.395,510.396l-45.593-139.53l-45.593,139.53H1895.395z"/>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 5.5 KiB |
|
After Width: | Height: | Size: 11 KiB |
|
After Width: | Height: | Size: 74 KiB |
@@ -0,0 +1,45 @@
|
||||
################################
|
||||
# Classes/Datatypes (KEYWORD1) #
|
||||
################################
|
||||
INA_Class KEYWORD1
|
||||
|
||||
####################################
|
||||
# Methods and Functions (KEYWORD2) #
|
||||
####################################
|
||||
begin KEYWORD2
|
||||
getBusMilliVolts KEYWORD2
|
||||
getShuntMicroVolts KEYWORD2
|
||||
getBusMicroAmps KEYWORD2
|
||||
getBusMicroWatts KEYWORD2
|
||||
getBusRaw KEYWORD2
|
||||
getShuntRaw KEYWORD2
|
||||
reset KEYWORD2
|
||||
setMode KEYWORD2
|
||||
setAveraging KEYWORD2
|
||||
setBusConversion KEYWORD2
|
||||
setShuntConversion KEYWORD2
|
||||
AlertOnConversion KEYWORD2
|
||||
waitForConversion KEYWORD2
|
||||
conversionFinished KEYWORD2
|
||||
AlertOnShuntOverVoltage KEYWORD2
|
||||
AlertOnShuntUnderVoltage KEYWORD2
|
||||
AlertOnBusOverVoltage KEYWORD2
|
||||
AlertOnBusUnderVoltage KEYWORD2
|
||||
|
||||
########################
|
||||
# Constants (LITERAL1) #
|
||||
########################
|
||||
INA219 LITERAL1
|
||||
INA226 LITERAL1
|
||||
INA230 LITERAL1
|
||||
INA231 LITERAL1
|
||||
INA260 LITERAL1
|
||||
INA_MODE_SHUTDOWN LITERAL1
|
||||
INA_MODE_TRIGGERED_SHUNT LITERAL1
|
||||
INA_MODE_TRIGGERED_BOTH LITERAL1
|
||||
INA_MODE_POWER_DOWN LITERAL1
|
||||
INA_MODE_CONTINUOUS_SHUNT LITERAL1
|
||||
INA_MODE_CONTINUOUS_BOTH LITERAL1
|
||||
_EEPROM_offset LITERAL1
|
||||
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
name=INA2xx
|
||||
version=1.1.0
|
||||
author=Arnd <Arnd@Zanduino.Com>
|
||||
maintainer=Arnd <Arnd@Zanduino.Com>
|
||||
sentence=Read current, voltage and power data from one or more INA2xx device(s)
|
||||
paragraph=This library allows a number of INA2xx devices (mixed types allowed) to be read and controlled simultaneously.
|
||||
category=Sensors
|
||||
url=https://github.com/Zanduino/INA
|
||||
architectures=*
|
||||
@@ -0,0 +1,351 @@
|
||||
#pragma once
|
||||
|
||||
// clang-format off
|
||||
/*!
|
||||
@file INA.h
|
||||
|
||||
@brief INA Class library header file
|
||||
|
||||
@mainpage Arduino library to support the INAxxx family of current sensors
|
||||
|
||||
@section Library_intro_section Description
|
||||
|
||||
Class definition header for the INA class. This library gives a common interface to various INA
|
||||
power monitor devices, see https://github.com/Zanduino/INA/wiki or the code below for a full
|
||||
list of currently supported devices. The INA devices have a 3-5V power supply and, depending
|
||||
upon the model, can measure voltages up to 26V or 36V. They are devices with High-Side / Low-Side
|
||||
Measurement, Bi-Directional Current and Power Monitor with I2C Compatible Interface. The device
|
||||
documentation can be found at the following location:\n
|
||||
http://www.ti.com/amplifier-circuit/current-sense/power-current-monitors/products.html\n\n
|
||||
Detailed library descriptions are on the INA GitHub Wiki pages at
|
||||
https://github.com/Zanduino/INA/wiki\n\n The INA devices, apart from the INA250 and INA260,
|
||||
require an external shunt of known resistance to be placed across the high-side or low-side
|
||||
supply or ground line and they use the small current generated by the shunt to compute the
|
||||
amperage passing across the circuit. This value, coupled with the voltage measurement, allows
|
||||
the amperage and wattage to be computed by the INA device and these values can be read from the
|
||||
devices using the industry standard I2C protocol.
|
||||
|
||||
@section Style Programming
|
||||
@subsection Coding Coding and comments
|
||||
OK, I admit that I'm "old school" when it comes to programming style. I am used to using a full
|
||||
monitor and keyboard for development and testing, plus I like to heavily document code as it
|
||||
helps me remember what I did when I revisit code after several months (or years). I make use of
|
||||
the full width (which I've limited to 112 characters here) and put my comments at the end of
|
||||
lines. I prefer to use descriptive variable names, which means that they tend to be long.
|
||||
|
||||
@subsection StyleGuide Style Guide
|
||||
Different languages have different coding styles. For the Arduino c++ language I've opted to go
|
||||
with one of the big players in the industry and have adopted the coding and style rules that
|
||||
Google recommends and which are documented at [Google c++ Style
|
||||
Guide](https://google.github.io/styleguide/cppguide.html). I have chosen to put braces on their
|
||||
own lines and include braces for even 1-liners. End braces are always commented so that
|
||||
convoluted code is more easily untangled.
|
||||
|
||||
@subsection Documentation
|
||||
The comments have been formatted for use with [Doxygen](doxygen.nl), one of the leading
|
||||
documentation systems which is not only free but covers just about anything worth documenting.
|
||||
The Doxygen system parses the source files of a package and creates documentation. The default
|
||||
output is a set of HTML pages although it can produce single documents.
|
||||
|
||||
@subsection comments Comment Format
|
||||
This package uses [Markdown](https://en.wikipedia.org/wiki/Markdown) syntax for formatting
|
||||
comments, which makes for easy reading directly in the source code and well-formatted
|
||||
pretty-print in postprocessing.
|
||||
|
||||
@subsection ide IDE
|
||||
I've opted for using Microsoft Visual Studio (version 16.2.5) for development, the community
|
||||
version can be downloaded for free at [Microsoft
|
||||
Downloads](https://visualstudio.microsoft.com/downloads/) and I use the fantastic [Visual
|
||||
Micro](https://www.visualmicro.com/) package which give the Arduino IDE inside Visual Studio. The
|
||||
base version is free or becomes only slightly annoying nagware after 90 days, but the software is
|
||||
inexpensive and the price is well worth it for supporting continued development.
|
||||
|
||||
@section doxygen doxygen configuration
|
||||
|
||||
This library is built with the standard "Doxyfile", which is located at
|
||||
https://github.com/Zanduino/Common/blob/main/Doxygen. As described on that page, there are only 5
|
||||
environment variables used, and these are set in the project's actions file, located at
|
||||
https://github.com/Zanduino/INA/blob/master/.github/workflows/ci-doxygen.yml
|
||||
Edit this file and set the 5 variables - PRETTYNAME, PROJECT_NAME, PROJECT_NUMBER, PROJECT_BRIEF
|
||||
and PROJECT_LOGO so that these values are used in the doxygen documentation.
|
||||
The local copy of the doxyfile should be in the project's root directory in order to do local
|
||||
doxygen testing, but the file is ignored on upload to GitHub.
|
||||
|
||||
@section clang-format
|
||||
Part of the GitHub actions for CI is running every source file through "clang-format" to ensure
|
||||
that coding formatting is done the same for all files. The configuration file ".clang-format" is
|
||||
located at https://github.com/Zanduino/Common/tree/main/clang-format and this is used for CI tests
|
||||
when pushing to GitHub. The local file, if present in the root directory, is ignored when
|
||||
committing and uploading.
|
||||
|
||||
@section license GNU General Public License v3.0
|
||||
|
||||
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/>.
|
||||
|
||||
@section author Author
|
||||
|
||||
Written by Arnd <Arnd@Zanduino.Com> at https://www.github.com/SV-Zanshin
|
||||
|
||||
@section versions Changelog
|
||||
|
||||
| Version | Date | Developer | Comments
|
||||
| ------- | ---------- | ----------- | --------
|
||||
| | 2025-12-24 | tonhuisman | Disable reading/writing EEPROM for ESPEasy via compile flag LIB_INA_EEPROM
|
||||
| 1.1.2 | 2022-01-16 | Oleg-Sob | Issue #87. getBusMicroWatts() only returns positive values
|
||||
| 1.1.1 | 2021-03-12 | x3mEr | Issue #79. Documentation Update
|
||||
| 1.0.14 | 2020-12-01 | SV-Zanshin | Issue #72. Allow INA structures to be in memory rather than EEPROM
|
||||
| 1.0.14 | 2020-11-30 | johntaves | Issue #64. begin() does not set values on subsequent calls
|
||||
| 1.0.14 | 2020-11-29 | SV-Zanshin | Issue #71. Optimize library, cleanup source code
|
||||
| 1.0.14 | 2020-10-25 | gallium70 | Issue #66. Error in INA226/230/231 value for "power_LSB"
|
||||
| 1.0.13 | 2020-07-13 | fg89o | Issue #62. Added "_EEPROM_size" for ESP32 and ESP8266
|
||||
| 1.0.13 | 2020-07-13 | fg89o | Issue #62. Incorrect "_EEPROM_offset" computation
|
||||
| 1.0.12 | 2020-07-13 | SV-Zanshin | Issue #41. Added "_EEPROM_offset" variable
|
||||
| 1.0.12 | 2020-07-03 | sages | Issue #60. Possible Overflow getBus(MicroAmps,MicroWatts)
|
||||
| 1.0.11 | 2020-06-30 | SV-Zanshin | Issue #58, changed formatting to use clang-format
|
||||
| 1.0.11 | 2020-06-29 | SV-Zanshin | Issue #57. "Alert..." functions should be "alert..."
|
||||
| 1.0.11 | 2020-05-05 | oliverb68 | Issue #56. Limit of +/- 2kW on getBusMicroWatts
|
||||
| 1.0.10 | 2020-05-03 | we9v | Issue #54. Limit of 127A maximum current changed to 1022A
|
||||
| 1.0.10 | 2020-05-01 | nathancheek | Issue #53. Extraneous conversion on getShuntMicrovolts
|
||||
| 1.0.10 | 2020-03-24 | nathancheek | Issue #52. Search for all 16 possible devices
|
||||
| 1.0.10 | 2020-03-22 | alphaarea | Issue #50. Wiki fix for "begin()" - MaxBusAmps overflow
|
||||
| 1.0.9 | 2019-12-15 | Steamerzone | Issue #49. Added ifdef for STM32F1 device support
|
||||
| 1.0.9 | 2019-10-27 | SV-Zanshin | Cleaned up Doxygen formatting
|
||||
| 1.0.9 | 2019-10-17 | nathancheek | Issue #47. Added EEPROM support for teensy
|
||||
| 1.0.8 | 2019-09-03 | miky2k | Issue #43. Added new method "conversionFinished()"
|
||||
| 1.0.8 | 2019-05-23 | avaldebe | Issue #42. Restrict I2C scan to possible devices
|
||||
| 1.0.8 | 2019-03-24 | mattlogic | Issue #40. Corrected INA226_CONFIG_SADC_MASK value
|
||||
| 1.0.8 | 2019-03-17 | SV-Zanshin | Issue #19. Corrected 4 value ranges in bus/shunt conversion
|
||||
| 1.0.8 | 2019-02-16 | SV-Zanshin | Corrected and tested ESP32 implementation
|
||||
| 1.0.8 | 2019-02-10 | SV-Zanshin | Issue #39. Allow non-AVR processors without EEPROM to run
|
||||
| 1.0.8 | 2019-02-09 | SV-Zanshin | Cleaned up doxygen comment formatting in .h and .cpp files
|
||||
| 1.0.8 | 2019-02-09 | SV-Zanshin | Issue #38. Add getDeviceAddress() function
|
||||
| 1.0.7 | 2019-01-20 | SV-Zanshin | Issue #36&37. Changed for Travis-CI and automated doxygen
|
||||
| 1.0.7 | 2018-12-27 | SV-Zanshin | Issue #33. Change program documentation to doxygen format
|
||||
| 1.0.6 | 2018-12-13 | delboy711 | Issue #32. Incorrect ESP2866 rather than ESP8266
|
||||
| 1.0.6 | 2018-10-19 | SV-Zanshin | Issue #31. Use full 0-32V Range on INA219 all the time
|
||||
| 1.0.6 | 2018-10-19 | SV-Zanshin | Issue #30. Added TEENSY support & support large EEPROM
|
||||
| 1.0.6 | 2018-10-14 | SV-Zanshin | Added correct wire handling for ESP8266 and ESP32
|
||||
| 1.0.6 | 2018-10-07 | SV-Zanshin | Optimized getBusRaw() and getShuntRaw() functions
|
||||
| 1.0.5 | 2018-10-04 | SV-Zanshin | Added getBusRaw() and getShuntRaw() functions
|
||||
| 1.0.5 | 2018-09-29 | SV-Zanshin | Reformatted comments to different c++ coding style
|
||||
| 1.0.4 | 2018-09-22 | SV-Zanshin | Issue #27. EEPROM Calls don't work with ESP32
|
||||
| 1.0.4 | 2018-09-19 | SV-Zanshin | Issue #28. Ovef error when >31Amps specified in begin()
|
||||
| 1.0.3 | 2018-09-04 | delboy711 | Issue #26. Incorrect INA3221 negative current readings
|
||||
| 1.0.3 | 2018-08-18 | SV-Zanshin | Issue #22. Reduce EEPROM Footprint
|
||||
| 1.0.3 | 2018-08-18 | SV-Zanshin | Issue #21. Rename I2C Constants to avoid redefine STM32F1
|
||||
| 1.0.2 | 2018-07-22 | SV-Zanshin | Issue #11. Reduce EEPROM footprint. Removed "deviceName", 8B. Changed datatype in structure to bit-level length defs, saving additional 3 bytes
|
||||
| 1.0.2 | 2018-07-21 | avaldeve | Issue #12. Incorrect const datatype for I2C Speeds
|
||||
| 1.0.2 | 2018-07-12 | coelner | Issue #9. Esplora doesn't accept "Wire.begin()"
|
||||
| 1.0.2 | 2018-07-08 | SV-Zanshin | Issue #2. Finished testing INA3221 across all functions
|
||||
| 1.0.2 | 2018-07-07 | dnlwgnd | Issue #4. Guard code used incorrect label
|
||||
| 1.0.2 | 2018-06-30 | SV-Zanshin | Issue #3. Adding faster I2C bus support
|
||||
| 1.0.2 | 2018-06-29 | SV-Zanshin | Issue #2. Adding INA3221 support to library
|
||||
| 1.0.2 | 2018-06-29 | SV-Zanshin | Issue #2. Adding INA3221 support to library
|
||||
| 1.0.1 | 2018-06-24 | SV-Zanshin | Removed extraneous elements from ina struct, optimized code
|
||||
| 1.0.1b | 2018-06-23 | SV-Zanshin | Fixed error on multiple devices with ina structure contents
|
||||
| 1.0.1a | 2018-06-23 | SV-Zanshin | Removed debug mode and code
|
||||
| 1.0.0 | 2018-06-22 | SV-Zanshin | Initial release
|
||||
| 1.0.0b | 2018-06-17 | SV-Zanshin | Continued coding, tested on INA219 and INA226
|
||||
| 1.0.0a | 2018-06-10 | SV-Zanshin | Initial coding began
|
||||
*/
|
||||
|
||||
#ifndef LIB_INA_EEPROM
|
||||
#define LIB_INA_EEPROM 0 // disable reading/writing EEPROM (via EEPROM.h functions)
|
||||
#endif // ifndef LIB_INA_EEPROM
|
||||
// #ifndef ARDUINO
|
||||
// /*! Define macro if not defined yet */
|
||||
// #define ARDUINO 0
|
||||
// #endif
|
||||
// #if ARDUINO >= 100 /* Use old library if IDE is prior to V1.0 */
|
||||
#include "Arduino.h"
|
||||
// #else
|
||||
// #include "WProgram.h"
|
||||
// #endif
|
||||
|
||||
#ifndef INA__Class_h
|
||||
/*! Guard code definition to prevent multiple includes */
|
||||
#define INA__Class_h
|
||||
/*! typedef contains a packed bit-level defs of information stored per device */
|
||||
typedef struct {
|
||||
uint8_t type : 4; ///< 0-15 see enumerated "ina_Type" for details
|
||||
uint8_t operatingMode : 4; ///< 0-15 Default to continuous mode
|
||||
uint32_t address : 7; ///< 0-127 I2C Address of device
|
||||
uint32_t maxBusAmps : 10; ///< 0-1023 Store initialization value
|
||||
uint32_t microOhmR : 20; ///< 0-1,048,575 Store initialization value
|
||||
} inaEEPROM; // of structure
|
||||
/*! typedef contains a packed bit-level definition of information stored on a device */
|
||||
typedef struct inaDet : inaEEPROM {
|
||||
uint8_t busVoltageRegister : 3; ///< 0- 7, Bus Voltage Register
|
||||
uint8_t shuntVoltageRegister : 3; ///< 0- 7, Shunt Voltage Register
|
||||
uint8_t currentRegister : 3; ///< 0- 7, Current Register
|
||||
uint16_t shuntVoltage_LSB; ///< Device dependent LSB factor
|
||||
uint16_t busVoltage_LSB; ///< Device dependent LSB factor
|
||||
uint32_t current_LSB; ///< Amperage LSB
|
||||
uint32_t power_LSB; ///< Wattage LSB
|
||||
inaDet(); ///< struct constructor
|
||||
inaDet(inaEEPROM& inaEE); ///< for ina = inaEE; assignment
|
||||
} inaDet; // of structure
|
||||
/*! Enumerated list detailing the names of all supported INA devices. The INA3221 is stored
|
||||
as 3 distinct devices each with their own enumerated type. */
|
||||
enum ina_Type {
|
||||
INA219,
|
||||
INA226,
|
||||
INA228,
|
||||
INA230,
|
||||
INA231,
|
||||
INA260,
|
||||
INA3221_0,
|
||||
INA3221_1,
|
||||
INA3221_2,
|
||||
INA_UNKNOWN
|
||||
};
|
||||
/*! Enumerated list detailing the operating modes of a given device */
|
||||
enum ina_Mode {
|
||||
INA_MODE_SHUTDOWN, ///< Device powered down
|
||||
INA_MODE_TRIGGERED_SHUNT, ///< Triggered shunt, no bus
|
||||
INA_MODE_TRIGGERED_BUS, ///< Triggered bus, no shunt
|
||||
INA_MODE_TRIGGERED_BOTH, ///< Triggered bus and shunt
|
||||
INA_MODE_POWER_DOWN, ///< shutdown or power-down
|
||||
INA_MODE_CONTINUOUS_SHUNT, ///< Continuous shunt, no bus
|
||||
INA_MODE_CONTINUOUS_BUS, ///< Continuous bus, no shunt
|
||||
INA_MODE_CONTINUOUS_BOTH ///< Both continuous, default value
|
||||
}; // of enumerated type
|
||||
/************************************************************************************************
|
||||
** Declare constants used in the class **
|
||||
************************************************************************************************/
|
||||
#ifndef INA_I2C_MODES // I2C related constants
|
||||
#define INA_I2C_MODES ///< Guard code to prevent multiple defs
|
||||
const uint32_t INA_I2C_STANDARD_MODE{100000}; ///< Default normal I2C 100KHz speed
|
||||
const uint32_t INA_I2C_FAST_MODE{400000}; ///< Fast mode
|
||||
const uint32_t INA_I2C_FAST_MODE_PLUS{1000000}; ///< Really fast mode
|
||||
const uint32_t INA_I2C_HIGH_SPEED_MODE{3400000}; ///< Turbo mode
|
||||
#endif
|
||||
const uint8_t INA_CONFIGURATION_REGISTER{0}; ///< Configuration Register address
|
||||
const uint8_t INA_BUS_VOLTAGE_REGISTER{2}; ///< Bus Voltage Register address
|
||||
const uint8_t INA_POWER_REGISTER{3}; ///< Power Register address
|
||||
const uint8_t INA_CALIBRATION_REGISTER{5}; ///< Calibration Register address
|
||||
const uint8_t INA_MASK_ENABLE_REGISTER{6}; ///< Mask enable Register (some devices)
|
||||
const uint8_t INA_ALERT_LIMIT_REGISTER{7}; ///< Alert Limit Register (some devices)
|
||||
const uint8_t INA_MANUFACTURER_ID_REGISTER{0xFE}; ///< Mfgr ID Register (some devices)
|
||||
const uint8_t INA_DIE_ID_REGISTER{0xFF}; ///< Die ID Register (some devices)
|
||||
const uint16_t INA_RESET_DEVICE{0x8000}; ///< Write to config to reset device
|
||||
const uint16_t INA_CONVERSION_READY_MASK{0x0080}; ///< Bit 4
|
||||
const uint16_t INA_CONFIG_MODE_MASK{0x0007}; ///< Bits 0-3
|
||||
const uint16_t INA_ALERT_MASK{0x03FF}; ///< Mask off bits 0-9
|
||||
const uint8_t INA_ALERT_SHUNT_OVER_VOLT_BIT{15}; ///< Register bit
|
||||
const uint8_t INA_ALERT_SHUNT_UNDER_VOLT_BIT{14}; ///< Register bit
|
||||
const uint8_t INA_ALERT_BUS_OVER_VOLT_BIT{13}; ///< Register bit
|
||||
const uint8_t INA_ALERT_BUS_UNDER_VOLT_BIT{12}; ///< Register bit
|
||||
const uint8_t INA_ALERT_POWER_OVER_WATT_BIT{11}; ///< Register bit
|
||||
const uint8_t INA_ALERT_CONVERSION_RDY_BIT{10}; ///< Register bit
|
||||
const uint8_t INA_DEFAULT_OPERATING_MODE{0b111}; ///< Default continuous mode
|
||||
const uint8_t INA219_SHUNT_VOLTAGE_REGISTER{1}; ///< INA219 Shunt Voltage Register
|
||||
const uint8_t INA219_CURRENT_REGISTER{4}; ///< INA219 Current Register
|
||||
const uint16_t INA219_BUS_VOLTAGE_LSB{400}; ///< INA219 LSB in uV *100 4.00mV
|
||||
const uint16_t INA219_SHUNT_VOLTAGE_LSB{100}; ///< INA219 LSB in uV *10 10.0uV
|
||||
const uint16_t INA219_CONFIG_AVG_MASK{0x07F8}; ///< INA219 Bits 3-6, 7-10
|
||||
const uint16_t INA219_CONFIG_PG_MASK{0xE7FF}; ///< INA219 Bits 11-12 masked
|
||||
const uint16_t INA219_CONFIG_BADC_MASK{0x0780}; ///< INA219 Bits 7-10 masked
|
||||
const uint16_t INA219_CONFIG_SADC_MASK{0x0038}; ///< INA219 Bits 3-5
|
||||
const uint8_t INA219_BRNG_BIT{13}; ///< INA219 Bit for BRNG in config reg
|
||||
const uint8_t INA219_PG_FIRST_BIT{11}; ///< INA219 1st bit of Programmable Gain
|
||||
const uint8_t INA226_SHUNT_VOLTAGE_REGISTER{1}; ///< INA226 Shunt Voltage Register
|
||||
const uint8_t INA226_CURRENT_REGISTER{4}; ///< INA226 Current Register
|
||||
const uint16_t INA226_BUS_VOLTAGE_LSB{125}; ///< INA226 LSB in uV *100 1.25mV
|
||||
const uint16_t INA226_SHUNT_VOLTAGE_LSB{25}; ///< INA226 LSB in uV *10 2.5uV
|
||||
const uint16_t INA226_CONFIG_AVG_MASK{0x0E00}; ///< INA226 Bits 9-11
|
||||
const uint16_t INA226_DIE_ID_VALUE{0x2260}; ///< INA226 Hard-coded Die ID for INA226
|
||||
const uint16_t INA226_CONFIG_BADC_MASK{0x01C0}; ///< INA226 Bits 6-8 masked
|
||||
const uint16_t INA226_CONFIG_SADC_MASK{0x0038}; ///< INA226 Bits 3-4
|
||||
|
||||
const uint8_t INA228_DIE_ID_REGISTER{0x3F}; ///< INA228 Device_ID Register
|
||||
const uint16_t INA228_DIE_ID_VALUE{0x2280}; ///< INA228 Hard-coded Die ID for INA228
|
||||
const uint8_t INA228_BUS_VOLTAGE_REGISTER{0x5}; ///< INA228 Bus Voltage Register
|
||||
const uint16_t INA228_BUS_VOLTAGE_LSB{195}; ///< INA228 LSB in uV *100 1953125uV, extra code
|
||||
const uint8_t INA228_SHUNT_VOLTAGE_REGISTER{4}; ///< INA228 Shunt Voltage Register
|
||||
const uint8_t xINA228_CURRENT_REGISTER{4}; ///< INA228 Current Register
|
||||
const uint16_t xINA228_CONFIG_AVG_MASK{0x0E00}; ///< INA228 Bits 9-11
|
||||
const uint16_t xINA228_CONFIG_BADC_MASK{0x01C0}; ///< INA228 Bits 6-8 masked
|
||||
const uint16_t xINA228_CONFIG_SADC_MASK{0x0038}; ///< INA228 Bits 3-4
|
||||
|
||||
const uint8_t INA260_SHUNT_VOLTAGE_REGISTER{0}; ///< INA260 Register doesn't exist
|
||||
const uint8_t INA260_CURRENT_REGISTER{1}; ///< INA260 Current Register
|
||||
const uint16_t INA260_BUS_VOLTAGE_LSB{125}; ///< INA260 LSB in uV *100 1.25mV
|
||||
const uint16_t INA260_CONFIG_BADC_MASK{0x01C0}; ///< INA260 Bits 6-8 masked
|
||||
const uint16_t INA260_CONFIG_SADC_MASK{0x0038}; ///< INA260 Bits 3-5 masked
|
||||
const uint8_t INA3221_SHUNT_VOLTAGE_REGISTER{1}; ///< INA3221 Register number 1
|
||||
const uint16_t INA3221_BUS_VOLTAGE_LSB{800}; ///< INA3221 LSB in uV *100 8mV
|
||||
const uint16_t INA3221_SHUNT_VOLTAGE_LSB{400}; ///< INA3221 LSB in uV *10 40uV
|
||||
const uint16_t INA3221_CONFIG_BADC_MASK{0x01C0}; ///< INA3221 Bits 7-10 masked
|
||||
const uint8_t INA3221_MASK_REGISTER{0xF}; ///< INA32219 Mask register
|
||||
const uint8_t I2C_DELAY{10}; ///< Microsecond delay on I2C writes
|
||||
// clang-format on
|
||||
|
||||
class INA_Class {
|
||||
/*!
|
||||
* @class INA_Class
|
||||
* @brief Forward definitions for the INA_Class
|
||||
*/
|
||||
public:
|
||||
INA_Class(uint8_t expectedDevices = 0);
|
||||
~INA_Class();
|
||||
uint8_t begin(const uint16_t maxBusAmps, const uint32_t microOhmR,
|
||||
const uint8_t deviceNumber = UINT8_MAX);
|
||||
void setI2CSpeed(const uint32_t i2cSpeed = INA_I2C_STANDARD_MODE) const;
|
||||
void setMode(const uint8_t mode, const uint8_t deviceNumber = UINT8_MAX);
|
||||
void setAveraging(const uint16_t averages, const uint8_t deviceNumber = UINT8_MAX);
|
||||
void setBusConversion(const uint32_t convTime, const uint8_t deviceNumber = UINT8_MAX);
|
||||
void setShuntConversion(const uint32_t convTime, const uint8_t deviceNumber = UINT8_MAX);
|
||||
uint16_t getBusMilliVolts(const uint8_t deviceNumber = 0);
|
||||
uint32_t getBusRaw(const uint8_t deviceNumber = 0);
|
||||
int32_t getShuntMicroVolts(const uint8_t deviceNumber = 0);
|
||||
int32_t getShuntRaw(const uint8_t deviceNumber = 0);
|
||||
int32_t getBusMicroAmps(const uint8_t deviceNumber = 0);
|
||||
int64_t getBusMicroWatts(const uint8_t deviceNumber = 0);
|
||||
const char* getDeviceName(const uint8_t deviceNumber = 0);
|
||||
uint8_t getDeviceAddress(const uint8_t deviceNumber = 0);
|
||||
uint8_t getDeviceType(const uint8_t deviceNumber = 0); // Added for ESPEasy
|
||||
void reset(const uint8_t deviceNumber = 0);
|
||||
bool conversionFinished(const uint8_t deviceNumber = 0);
|
||||
void waitForConversion(const uint8_t deviceNumber = UINT8_MAX);
|
||||
bool alertOnConversion(const bool alertState, const uint8_t deviceNumber = UINT8_MAX);
|
||||
bool alertOnShuntOverVoltage(const bool alertState, const int32_t milliVolts,
|
||||
const uint8_t deviceNumber = UINT8_MAX);
|
||||
bool alertOnShuntUnderVoltage(const bool alertState, const int32_t milliVolts,
|
||||
const uint8_t deviceNumber = UINT8_MAX);
|
||||
bool alertOnBusOverVoltage(const bool alertState, const int32_t milliVolts,
|
||||
const uint8_t deviceNumber = UINT8_MAX);
|
||||
bool alertOnBusUnderVoltage(const bool alertState, const int32_t milliVolts,
|
||||
const uint8_t deviceNumber = UINT8_MAX);
|
||||
bool alertOnPowerOverLimit(const bool alertState, const int32_t milliAmps,
|
||||
const uint8_t deviceNumber = UINT8_MAX);
|
||||
uint16_t _EEPROM_offset = 0; ///< Offset to all EEPROM addresses, GitHub issue #41
|
||||
#if defined(ESP32) || defined(ESP8266)
|
||||
uint16_t _EEPROM_size = 512; ///< Default EEPROM reserved space for ESP32 and ESP8266
|
||||
#endif
|
||||
private:
|
||||
int16_t readWord(const uint8_t addr, const uint8_t deviceAddress) const;
|
||||
int32_t read3Bytes(const uint8_t addr, const uint8_t deviceAddress) const;
|
||||
void writeWord(const uint8_t addr, const uint16_t data, const uint8_t deviceAddress) const;
|
||||
void readInafromEEPROM(const uint8_t deviceNumber);
|
||||
void writeInatoEEPROM(const uint8_t deviceNumber);
|
||||
void initDevice(const uint8_t deviceNumber);
|
||||
uint8_t _DeviceCount{0}; ///< Total number of devices detected
|
||||
uint8_t _currentINA{UINT8_MAX}; ///< Stores current INA device number
|
||||
uint8_t _expectedDevices{0}; ///< If 0 use EEPROM, otherwise use RAM for INA structures
|
||||
inaEEPROM* _DeviceArray; ///< Pointer to dynamic array of devices if not using EEPROM
|
||||
inaEEPROM inaEE; ///< INA device structure
|
||||
inaDet ina; ///< INA device structure
|
||||
#if defined(__AVR__) || defined(CORE_TEENSY) || defined(ESP32) || defined(ESP8266) || \
|
||||
defined(__STM32F1__)
|
||||
#else
|
||||
inaEEPROM _EEPROMEmulation[32]; ///< Actual array of up to 32 devices
|
||||
#endif
|
||||
}; // of INA_Class definition
|
||||
#endif
|
||||
@@ -538,6 +538,7 @@ static const char DATA_ESPEASY_DEFAULT_MIN_CSS[] PROGMEM = {
|
||||
|
||||
// #define USES_P131 // NeoPixel Matrix
|
||||
// #define USES_P132 // INA3221
|
||||
// #define P132_EXTENDED 1 // Extend support with INA219, INA226, INA228, INA230, INA231 and INA260
|
||||
// #define USES_P133 // LTR390 UV
|
||||
// #define USES_P134 // A02YYUW
|
||||
// #define USES_P135 // SCD4x
|
||||
|
||||
@@ -1,11 +1,15 @@
|
||||
#include "_Plugin_Helper.h"
|
||||
#ifdef USES_P132
|
||||
|
||||
// #######################################################################################################
|
||||
// ######################### Plugin 132: INA3221 DC Voltage/Current sensor ###############################
|
||||
// ############### Plugin 132: INA3221/INA226/INA228/INA260 DC Voltage/Current sensor ####################
|
||||
// #######################################################################################################
|
||||
|
||||
/**
|
||||
* Changelog:
|
||||
* 2025-12-25 tonhuisman: Add P132_EXTENDED to also implement support for INA219, INA226, INA228, INA230, INA231 and INA260
|
||||
* INA230/INA231 are sometimes recognized as INA226
|
||||
* Without P132_EXTENDED set the original code is used (default for ESP8266).
|
||||
* 2025-01-18 tonhuisman: Implement support for MQTT AutoDiscovery
|
||||
* 2025-01-12 tonhuisman: Add support for MQTT AutoDiscovery (not supported yet for INA3221)
|
||||
* 2022-04-23 tonhuisman: Add separate settings for Conversion rate Voltage and Current
|
||||
@@ -16,18 +20,17 @@
|
||||
|
||||
// Initial development: ## 25 jan 2021 Fred van Duin ####
|
||||
|
||||
#include "_Plugin_Helper.h"
|
||||
# include "./src/PluginStructs/P132_data_struct.h"
|
||||
|
||||
#define PLUGIN_132
|
||||
#define PLUGIN_ID_132 132
|
||||
#define PLUGIN_NAME_132 "Energy (DC) - INA3221"
|
||||
#define PLUGIN_VALUENAME1_132 "Value1"
|
||||
#define PLUGIN_VALUENAME2_132 "Value2"
|
||||
#define PLUGIN_VALUENAME3_132 "Value3"
|
||||
#define PLUGIN_VALUENAME4_132 "Value4"
|
||||
|
||||
#include "./src/PluginStructs/P132_data_struct.h"
|
||||
# define PLUGIN_132
|
||||
# define PLUGIN_ID_132 132
|
||||
# define PLUGIN_VALUENAME1_132 "Value1"
|
||||
# define PLUGIN_VALUENAME2_132 "Value2"
|
||||
# define PLUGIN_VALUENAME3_132 "Value3"
|
||||
# define PLUGIN_VALUENAME4_132 "Value4"
|
||||
|
||||
// See below for original code with just INA3221 support
|
||||
# if P132_EXTENDED
|
||||
boolean Plugin_132(uint8_t function, struct EventStruct *event, String& string)
|
||||
{
|
||||
boolean success = false;
|
||||
@@ -63,13 +66,421 @@ boolean Plugin_132(uint8_t function, struct EventStruct *event, String& string)
|
||||
break;
|
||||
}
|
||||
|
||||
#if FEATURE_MQTT_DISCOVER
|
||||
# if FEATURE_MQTT_DISCOVER
|
||||
case PLUGIN_GET_DISCOVERY_VTYPES:
|
||||
{
|
||||
success = getDiscoveryVType(event, Plugin_132_QueryVType, P132_CONFIG_BASE, event->Par5);;
|
||||
success = getDiscoveryVType(event, Plugin_132_QueryVType, P132_CONFIG_BASE, event->Par5);
|
||||
break;
|
||||
}
|
||||
#endif // if FEATURE_MQTT_DISCOVER
|
||||
# endif // if FEATURE_MQTT_DISCOVER
|
||||
|
||||
case PLUGIN_I2C_HAS_ADDRESS:
|
||||
case PLUGIN_WEBFORM_SHOW_I2C_PARAMS:
|
||||
{
|
||||
const uint8_t i2cAddressValues[] = { 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47,
|
||||
0x48, 0x49, 0x4A, 0x4B, 0x4C, 0x4D, 0x4E, 0x4F };
|
||||
constexpr size_t optionCount = NR_ELEMENTS(i2cAddressValues);
|
||||
|
||||
if (function == PLUGIN_WEBFORM_SHOW_I2C_PARAMS) {
|
||||
const P132_DeviceType deviceType = static_cast<P132_DeviceType>(P132_INA_TYPE);
|
||||
const size_t options = P132_DeviceType::Ina3221 == deviceType ? 4 : optionCount;
|
||||
addFormSelectorI2C(F("i2c_addr"), options, i2cAddressValues, P132_I2C_ADDR);
|
||||
addFormNote(F("Address selection: see docs"));
|
||||
} else {
|
||||
success = intArrayContains(optionCount, i2cAddressValues, event->Par1);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
# if FEATURE_I2C_GET_ADDRESS
|
||||
case PLUGIN_I2C_GET_ADDRESS:
|
||||
{
|
||||
event->Par1 = P132_I2C_ADDR;
|
||||
success = true;
|
||||
break;
|
||||
}
|
||||
# endif // if FEATURE_I2C_GET_ADDRESS
|
||||
|
||||
case PLUGIN_SET_DEFAULTS:
|
||||
{
|
||||
P132_VALUE_1 = 1; // Configure 'randomly'
|
||||
P132_VALUE_2 = 0;
|
||||
P132_VALUE_3 = 3;
|
||||
P132_VALUE_4 = 2;
|
||||
uint32_t lSettings = 0;
|
||||
set3BitToUL(lSettings, P132_FLAG_AVERAGE, 0x00);
|
||||
|
||||
// set3BitToUL(lSettings, P132_FLAG_CONVERSION_B, 0x04); // Voltage
|
||||
// set3BitToUL(lSettings, P132_FLAG_CONVERSION_S, 0x04); // Current
|
||||
set4BitToUL(lSettings, P132_FLAG_V2_CONVERSION_B, 0x04); // Voltage
|
||||
set4BitToUL(lSettings, P132_FLAG_V2_CONVERSION_S, 0x04); // Current
|
||||
set2BitToUL(lSettings, P132_FLAG_CFG_VERSION, P132_CFG_VERSIONS); // V2
|
||||
P132_CONFIG_FLAGS = lSettings;
|
||||
P132_MAX_CURRENT = 10; // Guestimated
|
||||
P132_SHUNT = 1; // Default != 0
|
||||
break;
|
||||
}
|
||||
|
||||
case PLUGIN_WEBFORM_LOAD:
|
||||
{
|
||||
{
|
||||
const __FlashStringHelper*typOptions[] = {
|
||||
toString(P132_DeviceType::Ina3221),
|
||||
toString(P132_DeviceType::Ina219),
|
||||
toString(P132_DeviceType::Ina226),
|
||||
toString(P132_DeviceType::Ina228),
|
||||
toString(P132_DeviceType::Ina230),
|
||||
toString(P132_DeviceType::Ina231),
|
||||
toString(P132_DeviceType::Ina260),
|
||||
};
|
||||
constexpr size_t typOptionsCount = NR_ELEMENTS(typOptions);
|
||||
|
||||
const int typeOptionIndexes[] = {
|
||||
static_cast<int>(P132_DeviceType::Ina3221),
|
||||
static_cast<int>(P132_DeviceType::Ina219),
|
||||
static_cast<int>(P132_DeviceType::Ina226),
|
||||
static_cast<int>(P132_DeviceType::Ina228),
|
||||
static_cast<int>(P132_DeviceType::Ina230),
|
||||
static_cast<int>(P132_DeviceType::Ina231),
|
||||
static_cast<int>(P132_DeviceType::Ina260),
|
||||
};
|
||||
FormSelectorOptions typSelector(typOptionsCount, typOptions, typeOptionIndexes);
|
||||
typSelector.reloadonchange = true;
|
||||
typSelector.addFormSelector(F("INA type"), F("ityp"), P132_INA_TYPE);
|
||||
}
|
||||
|
||||
addFormNumericBox(F("Max. Current"), F("mcur"), P132_MAX_CURRENT, 1, 1022);
|
||||
addUnit(F("1..1022 A"));
|
||||
|
||||
const P132_DeviceType deviceType = static_cast<P132_DeviceType>(P132_INA_TYPE);
|
||||
|
||||
{
|
||||
const __FlashStringHelper *varOptions[9];
|
||||
int varValues[9];
|
||||
|
||||
if (P132_DeviceType::Ina3221 == deviceType)
|
||||
{
|
||||
varOptions[0] = F("Voltage channel 1");
|
||||
varOptions[1] = F("Current channel 1");
|
||||
varOptions[2] = F("Power channel 1");
|
||||
varOptions[3] = F("Voltage channel 2");
|
||||
varOptions[4] = F("Current channel 2");
|
||||
varOptions[5] = F("Power channel 2");
|
||||
varOptions[6] = F("Voltage channel 3");
|
||||
varOptions[7] = F("Current channel 3");
|
||||
varOptions[8] = F("Power channel 3");
|
||||
varValues[0] = 1;
|
||||
varValues[1] = 0;
|
||||
varValues[2] = 6;
|
||||
varValues[3] = 3;
|
||||
varValues[4] = 2;
|
||||
varValues[5] = 7;
|
||||
varValues[6] = 5;
|
||||
varValues[7] = 4;
|
||||
varValues[8] = 8;
|
||||
}
|
||||
else
|
||||
{
|
||||
varOptions[0] = F("Voltage");
|
||||
varOptions[1] = F("Current");
|
||||
varOptions[2] = F("Power");
|
||||
varValues[0] = 1;
|
||||
varValues[1] = 0;
|
||||
varValues[2] = 6;
|
||||
}
|
||||
const size_t optionCount = P132_DeviceTypeToMaxValues(deviceType);
|
||||
|
||||
if (P132_INA_PREVIOUS != P132_INA_TYPE) {
|
||||
P132_VALUE_1 = 1; // Configure randomly
|
||||
P132_VALUE_2 = 0;
|
||||
|
||||
if (P132_DeviceType::Ina3221 == deviceType) {
|
||||
P132_VALUE_3 = 3;
|
||||
P132_VALUE_4 = 2;
|
||||
} else {
|
||||
P132_VALUE_3 = 6;
|
||||
}
|
||||
|
||||
if (P132_DeviceType::Ina260 == deviceType) {
|
||||
P132_SHUNT = 50; // INA260 has 2 mOhm shunt built-in
|
||||
}
|
||||
}
|
||||
|
||||
const FormSelectorOptions selector(optionCount, varOptions, varValues);
|
||||
|
||||
for (uint8_t r = 0; r < min(optionCount, (size_t)VARS_PER_TASK); ++r) {
|
||||
selector.addFormSelector(
|
||||
concat(F("Power value "), r + 1),
|
||||
getPluginCustomArgName(r),
|
||||
PCONFIG(P132_CONFIG_BASE + r));
|
||||
}
|
||||
}
|
||||
|
||||
addFormSubHeader(F("Hardware"));
|
||||
|
||||
{
|
||||
const __FlashStringHelper *varShuntOptions[] = {
|
||||
F("0.1"),
|
||||
F("0.01"),
|
||||
F("0.015"), // INA228 often used
|
||||
F("0.005"),
|
||||
F("0.002"), // INA260 built-in
|
||||
};
|
||||
const int shuntValues[] = { 1, 10, 15, 20, 50 };
|
||||
constexpr size_t optionCount = NR_ELEMENTS(shuntValues);
|
||||
FormSelectorOptions selector(optionCount, varShuntOptions, shuntValues);
|
||||
selector.enabled = P132_DeviceType::Ina260 != deviceType; // Built-in shunt
|
||||
selector.addFormSelector(F("Shunt resistor"), F("shunt"), P132_SHUNT);
|
||||
addUnit(F("Ohm"));
|
||||
addFormNote(F("Select as is installed on the board."));
|
||||
}
|
||||
|
||||
addFormSubHeader(F("Measurement"));
|
||||
|
||||
if (P132_DeviceType::Ina219 != deviceType)
|
||||
{
|
||||
const __FlashStringHelper *averagingSamples[] = {
|
||||
F("1"),
|
||||
F("4"),
|
||||
F("16"),
|
||||
F("64"),
|
||||
F("128"),
|
||||
F("256"),
|
||||
F("512"),
|
||||
F("1024"),
|
||||
};
|
||||
const int averageValue[] = { 0b000, 0b001, 0b010, 0b011, 0b100, 0b101, 0b110, 0b111 };
|
||||
constexpr size_t optionCount = NR_ELEMENTS(averageValue);
|
||||
const FormSelectorOptions selector(optionCount, averagingSamples, averageValue);
|
||||
|
||||
// selector.default_index = 0b000;
|
||||
selector.addFormSelector(F("Averaging samples"), F("average"), P132_GET_AVERAGE);
|
||||
|
||||
// addFormNote(F("Samples > 16 then min. Interval: 64= 4, 128= 7, 256= 14, 512= 26, 1024= 52 seconds!"));
|
||||
}
|
||||
|
||||
{
|
||||
const __FlashStringHelper *conversionRates[11];
|
||||
int conversionValues[11];
|
||||
|
||||
if (P132_DeviceType::Ina219 == deviceType) {
|
||||
conversionRates[0] = F("9 bits / 84 µsec");
|
||||
conversionRates[1] = F("10 bits / 148 µsec");
|
||||
conversionRates[2] = F("11 bits / 276 µsec");
|
||||
conversionRates[3] = F("12 bits / 532 µsec");
|
||||
conversionRates[4] = F("2 samples / 1.106 msec");
|
||||
conversionRates[5] = F("4 samples / 2.13 msec");
|
||||
conversionRates[6] = F("8 samples / 4.26 msec");
|
||||
conversionRates[7] = F("16 samples / 8.51 msec");
|
||||
conversionRates[8] = F("32 samples / 17.02 msec");
|
||||
conversionRates[9] = F("54 samples / 34.05 msec");
|
||||
conversionRates[10] = F("128 samples / 68.10 msec");
|
||||
conversionValues[0] = 0b0000;
|
||||
conversionValues[1] = 0b0001;
|
||||
conversionValues[2] = 0b0010;
|
||||
conversionValues[3] = 0b1000;
|
||||
conversionValues[4] = 0b1001;
|
||||
conversionValues[5] = 0b1010;
|
||||
conversionValues[6] = 0b1011;
|
||||
conversionValues[7] = 0b1100;
|
||||
conversionValues[8] = 0b1101;
|
||||
conversionValues[9] = 0b1110;
|
||||
conversionValues[10] = 0b1111;
|
||||
}
|
||||
else
|
||||
{
|
||||
conversionRates[0] = F("140 µsec");
|
||||
conversionRates[1] = F("204 µsec");
|
||||
conversionRates[2] = F("332 µsec");
|
||||
conversionRates[3] = F("588 µsec");
|
||||
conversionRates[4] = F("1.1 msec");
|
||||
conversionRates[5] = F("2.116 msec");
|
||||
conversionRates[6] = F("4.156 msec");
|
||||
conversionRates[7] = F("8.244 msec");
|
||||
conversionValues[0] = 0b0000;
|
||||
conversionValues[1] = 0b0001;
|
||||
conversionValues[2] = 0b0010;
|
||||
conversionValues[3] = 0b0011;
|
||||
conversionValues[4] = 0b0100;
|
||||
conversionValues[5] = 0b0101;
|
||||
conversionValues[6] = 0b0110;
|
||||
conversionValues[7] = 0b0111;
|
||||
}
|
||||
|
||||
const size_t optionCount = P132_DeviceType::Ina219 == deviceType ? 11 : 8;
|
||||
FormSelectorOptions selector(optionCount, conversionRates, conversionValues);
|
||||
selector.default_index = P132_DeviceType::Ina219 == deviceType ? 0b1000 : 0b0100; // 12 bit / 523 usec or 1.1ms
|
||||
const uint8_t convB = 0 == P132_GET_CFG_VERSION ? P132_GET_CONVERSION_S : P132_GET_V2_CONVERSION_S;
|
||||
const uint8_t convS = 0 == P132_GET_CFG_VERSION ? P132_GET_CONVERSION_S : P132_GET_V2_CONVERSION_S;
|
||||
selector.addFormSelector(F("Conversion rate Voltage"), F("conv_v"), convB);
|
||||
selector.addFormSelector(F("Conversion rate Current"), F("conv_c"), convS);
|
||||
}
|
||||
|
||||
success = true;
|
||||
break;
|
||||
}
|
||||
|
||||
case PLUGIN_WEBFORM_SAVE:
|
||||
{
|
||||
P132_I2C_ADDR = getFormItemInt(F("i2c_addr"));
|
||||
P132_INA_PREVIOUS = P132_INA_TYPE;
|
||||
P132_INA_TYPE = getFormItemInt(F("ityp"));
|
||||
P132_MAX_CURRENT = getFormItemInt(F("mcur"));
|
||||
|
||||
const P132_DeviceType deviceType = static_cast<P132_DeviceType>(P132_INA_TYPE);
|
||||
const size_t optionCount = P132_DeviceTypeToMaxValues(deviceType);
|
||||
|
||||
for (uint8_t r = 0; r < min(optionCount, (size_t)VARS_PER_TASK); ++r) {
|
||||
PCONFIG(P132_CONFIG_BASE + r) = getFormItemIntCustomArgName(r);
|
||||
}
|
||||
P132_SHUNT = getFormItemInt(F("shunt"));
|
||||
|
||||
uint32_t lSettings = 0;
|
||||
|
||||
if (P132_DeviceType::Ina219 != deviceType)
|
||||
{
|
||||
set3BitToUL(lSettings, P132_FLAG_AVERAGE, getFormItemInt(F("average")));
|
||||
}
|
||||
|
||||
// set3BitToUL(lSettings, P132_FLAG_CONVERSION_B, getFormItemInt(F("conv_v")));
|
||||
// set3BitToUL(lSettings, P132_FLAG_CONVERSION_S, getFormItemInt(F("conv_c")));
|
||||
set4BitToUL(lSettings, P132_FLAG_V2_CONVERSION_B, getFormItemInt(F("conv_v")));
|
||||
set4BitToUL(lSettings, P132_FLAG_V2_CONVERSION_S, getFormItemInt(F("conv_c")));
|
||||
|
||||
set2BitToUL(lSettings, P132_FLAG_CFG_VERSION, P132_CFG_VERSION); // Write version update
|
||||
P132_CONFIG_FLAGS = lSettings;
|
||||
|
||||
success = true;
|
||||
break;
|
||||
}
|
||||
|
||||
case PLUGIN_INIT:
|
||||
{
|
||||
initPluginTaskData(event->TaskIndex, new (std::nothrow) P132_data_struct(event));
|
||||
P132_data_struct *P132_data = static_cast<P132_data_struct *>(getPluginTaskData(event->TaskIndex));
|
||||
|
||||
if (nullptr != P132_data) {
|
||||
success = P132_data->isInitialized();
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case PLUGIN_READ:
|
||||
{
|
||||
P132_data_struct *P132_data = static_cast<P132_data_struct *>(getPluginTaskData(event->TaskIndex));
|
||||
|
||||
if (nullptr == P132_data) {
|
||||
return success;
|
||||
}
|
||||
|
||||
const P132_DeviceType deviceType = static_cast<P132_DeviceType>(P132_INA_TYPE);
|
||||
const size_t optionCount = P132_DeviceTypeToMaxValues(deviceType);
|
||||
|
||||
for (uint8_t r = 0; r < min(optionCount, (size_t)VARS_PER_TASK); ++r) {
|
||||
// VALUES 1..4
|
||||
const uint8_t reg = static_cast<uint8_t>(PCONFIG(P132_CONFIG_BASE + r));
|
||||
uint8_t channel = 0; //
|
||||
|
||||
if ((1 == reg) || (3 == reg) || (7 == reg)) {
|
||||
channel = 1;
|
||||
} else if ((2 == reg) || (4 == reg) || (8 == reg)) {
|
||||
channel = 2;
|
||||
}
|
||||
|
||||
switch (reg) {
|
||||
case 0: // Current
|
||||
case 2: // Current
|
||||
case 4: // Current
|
||||
// UserVar.setFloat(event->TaskIndex, r,
|
||||
// (P132_data->getShuntVoltage_mV(channel) / 100.0f) * P132_SHUNT);
|
||||
|
||||
UserVar.setFloat(event->TaskIndex, r,
|
||||
P132_data->getBusCurrent_mA(channel));
|
||||
break;
|
||||
case 1: // Voltage
|
||||
case 3: // Voltage
|
||||
case 5: // Voltage
|
||||
UserVar.setFloat(event->TaskIndex, r,
|
||||
P132_data->getBusVoltage_V(channel)
|
||||
+ (P132_data->getShuntVoltage_mV(channel) / 1000.0f));
|
||||
break;
|
||||
case 6: // Power
|
||||
case 7: // Power
|
||||
case 8: // Power
|
||||
UserVar.setFloat(event->TaskIndex, r,
|
||||
P132_data->getBusPower_mW(channel));
|
||||
break;
|
||||
}
|
||||
|
||||
// }
|
||||
}
|
||||
|
||||
# ifndef BUILD_NO_DEBUG
|
||||
|
||||
if (loglevelActiveFor(LOG_LEVEL_INFO)) {
|
||||
addLog(LOG_LEVEL_INFO, strformat(F("%s: Values: %.2f/%.2f/%.2f/%.2f"),
|
||||
FsP(toString(static_cast<P132_DeviceType>(P132_INA_TYPE))),
|
||||
UserVar.getFloat(event->TaskIndex, 0),
|
||||
UserVar.getFloat(event->TaskIndex, 1),
|
||||
UserVar.getFloat(event->TaskIndex, 2),
|
||||
UserVar.getFloat(event->TaskIndex, 3)));
|
||||
}
|
||||
# endif // ifndef BUILD_NO_DEBUG
|
||||
|
||||
success = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
# endif // if P132_EXTENDED
|
||||
|
||||
# if !P132_EXTENDED
|
||||
boolean Plugin_132(uint8_t function, struct EventStruct *event, String& string)
|
||||
{
|
||||
boolean success = false;
|
||||
|
||||
switch (function)
|
||||
{
|
||||
case PLUGIN_DEVICE_ADD:
|
||||
{
|
||||
auto& dev = Device[++deviceCount];
|
||||
dev.Number = PLUGIN_ID_132;
|
||||
dev.Type = DEVICE_TYPE_I2C;
|
||||
dev.VType = Sensor_VType::SENSOR_TYPE_QUAD;
|
||||
dev.FormulaOption = true;
|
||||
dev.ValueCount = 4;
|
||||
dev.SendDataOption = true;
|
||||
dev.TimerOption = true;
|
||||
dev.PluginStats = true;
|
||||
break;
|
||||
}
|
||||
|
||||
case PLUGIN_GET_DEVICENAME:
|
||||
{
|
||||
string = F(PLUGIN_NAME_132);
|
||||
break;
|
||||
}
|
||||
|
||||
case PLUGIN_GET_DEVICEVALUENAMES:
|
||||
{
|
||||
strcpy_P(ExtraTaskSettings.TaskDeviceValueNames[0], PSTR(PLUGIN_VALUENAME1_132));
|
||||
strcpy_P(ExtraTaskSettings.TaskDeviceValueNames[1], PSTR(PLUGIN_VALUENAME2_132));
|
||||
strcpy_P(ExtraTaskSettings.TaskDeviceValueNames[2], PSTR(PLUGIN_VALUENAME3_132));
|
||||
strcpy_P(ExtraTaskSettings.TaskDeviceValueNames[3], PSTR(PLUGIN_VALUENAME4_132));
|
||||
break;
|
||||
}
|
||||
|
||||
# if FEATURE_MQTT_DISCOVER
|
||||
case PLUGIN_GET_DISCOVERY_VTYPES:
|
||||
{
|
||||
success = getDiscoveryVType(event, Plugin_132_QueryVType, P132_CONFIG_BASE, event->Par5);
|
||||
break;
|
||||
}
|
||||
# endif // if FEATURE_MQTT_DISCOVER
|
||||
|
||||
case PLUGIN_I2C_HAS_ADDRESS:
|
||||
case PLUGIN_WEBFORM_SHOW_I2C_PARAMS:
|
||||
@@ -85,14 +496,14 @@ boolean Plugin_132(uint8_t function, struct EventStruct *event, String& string)
|
||||
break;
|
||||
}
|
||||
|
||||
#if FEATURE_I2C_GET_ADDRESS
|
||||
# if FEATURE_I2C_GET_ADDRESS
|
||||
case PLUGIN_I2C_GET_ADDRESS:
|
||||
{
|
||||
event->Par1 = P132_I2C_ADDR;
|
||||
success = true;
|
||||
break;
|
||||
}
|
||||
#endif // if FEATURE_I2C_GET_ADDRESS
|
||||
# endif // if FEATURE_I2C_GET_ADDRESS
|
||||
|
||||
case PLUGIN_SET_DEFAULTS:
|
||||
{
|
||||
@@ -126,7 +537,7 @@ boolean Plugin_132(uint8_t function, struct EventStruct *event, String& string)
|
||||
for (uint8_t r = 0; r < VARS_PER_TASK; ++r) {
|
||||
selector.addFormSelector(
|
||||
concat(F("Power value "), r + 1),
|
||||
getPluginCustomArgName(r),
|
||||
getPluginCustomArgName(r),
|
||||
PCONFIG(P132_CONFIG_BASE + r));
|
||||
}
|
||||
}
|
||||
@@ -165,7 +576,7 @@ boolean Plugin_132(uint8_t function, struct EventStruct *event, String& string)
|
||||
constexpr size_t optionCount = NR_ELEMENTS(averageValue);
|
||||
FormSelectorOptions selector(optionCount, averagingSamples, averageValue);
|
||||
selector.default_index = 0b000;
|
||||
selector.addFormSelector(F("Averaging samples"),F("average"),P132_GET_AVERAGE);
|
||||
selector.addFormSelector(F("Averaging samples"), F("average"), P132_GET_AVERAGE);
|
||||
addFormNote(F("Samples > 16 then min. Interval: 64= 4, 128= 7, 256= 14, 512= 26, 1024= 52 seconds!"));
|
||||
}
|
||||
|
||||
@@ -247,7 +658,7 @@ boolean Plugin_132(uint8_t function, struct EventStruct *event, String& string)
|
||||
}
|
||||
}
|
||||
|
||||
#ifndef BUILD_NO_DEBUG
|
||||
# ifndef BUILD_NO_DEBUG
|
||||
|
||||
if (loglevelActiveFor(LOG_LEVEL_INFO)) {
|
||||
addLog(LOG_LEVEL_INFO, strformat(F("INA3221: Values: %.2f/%.2f/%.2f/%.2f"),
|
||||
@@ -256,7 +667,7 @@ boolean Plugin_132(uint8_t function, struct EventStruct *event, String& string)
|
||||
UserVar[event->BaseVarIndex + 2],
|
||||
UserVar[event->BaseVarIndex + 3]));
|
||||
}
|
||||
#endif // ifndef BUILD_NO_DEBUG
|
||||
# endif // ifndef BUILD_NO_DEBUG
|
||||
|
||||
success = true;
|
||||
break;
|
||||
@@ -266,4 +677,6 @@ boolean Plugin_132(uint8_t function, struct EventStruct *event, String& string)
|
||||
return success;
|
||||
}
|
||||
|
||||
# endif // if !P132_EXTENDED
|
||||
|
||||
#endif // USES_P132
|
||||
|
||||
@@ -13,29 +13,163 @@ int Plugin_132_QueryVType(uint8_t value_nr) {
|
||||
|
||||
# endif // if FEATURE_MQTT_DISCOVER
|
||||
|
||||
# if P132_EXTENDED
|
||||
const __FlashStringHelper* toString(P132_DeviceType deviceType) {
|
||||
switch (deviceType) {
|
||||
case P132_DeviceType::Ina3221: return F("INA3221");
|
||||
case P132_DeviceType::Ina219: return F("INA219");
|
||||
case P132_DeviceType::Ina226: return F("INA226");
|
||||
case P132_DeviceType::Ina228: return F("INA228");
|
||||
case P132_DeviceType::Ina230: return F("INA230");
|
||||
case P132_DeviceType::Ina231: return F("INA231");
|
||||
case P132_DeviceType::Ina260: return F("INA260");
|
||||
case P132_DeviceType::InaUnknown: return F("");
|
||||
}
|
||||
return F("");
|
||||
}
|
||||
|
||||
const uint8_t P132_DeviceTypeToINAType(P132_DeviceType deviceType) {
|
||||
switch (deviceType) {
|
||||
case P132_DeviceType::Ina3221: return INA3221_0;
|
||||
case P132_DeviceType::Ina219: return INA219;
|
||||
case P132_DeviceType::Ina226: return INA226;
|
||||
case P132_DeviceType::Ina228: return INA228;
|
||||
case P132_DeviceType::Ina230: return INA230;
|
||||
case P132_DeviceType::Ina231: return INA231;
|
||||
case P132_DeviceType::Ina260: return INA260;
|
||||
case P132_DeviceType::InaUnknown: return INA_UNKNOWN;
|
||||
}
|
||||
return INA_UNKNOWN;
|
||||
}
|
||||
|
||||
const P132_DeviceType P132_INATypeToDeviceType(uint8_t inaType) {
|
||||
switch (inaType) {
|
||||
case INA219: return P132_DeviceType::Ina219;
|
||||
case INA226: return P132_DeviceType::Ina226;
|
||||
case INA228: return P132_DeviceType::Ina228;
|
||||
case INA230: return P132_DeviceType::Ina230;
|
||||
case INA231: return P132_DeviceType::Ina231;
|
||||
case INA260: return P132_DeviceType::Ina260;
|
||||
case INA3221_0:
|
||||
case INA3221_1:
|
||||
case INA3221_2: return P132_DeviceType::Ina3221;
|
||||
}
|
||||
return P132_DeviceType::Ina3221;
|
||||
}
|
||||
|
||||
const uint8_t P132_DeviceTypeToMaxValues(P132_DeviceType deviceType) {
|
||||
switch (deviceType) {
|
||||
case P132_DeviceType::Ina3221: return 9; // Voltage 1..3/Current 1..3/Power 1..3
|
||||
case P132_DeviceType::Ina219:
|
||||
case P132_DeviceType::Ina226:
|
||||
case P132_DeviceType::Ina228:
|
||||
case P132_DeviceType::Ina230:
|
||||
case P132_DeviceType::Ina231:
|
||||
case P132_DeviceType::Ina260: return 3; // Voltage/Current/Power
|
||||
case P132_DeviceType::InaUnknown: return 0;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
# endif // if P132_EXTENDED
|
||||
|
||||
// **************************************************************************/
|
||||
// Constructor
|
||||
// **************************************************************************/
|
||||
# if P132_EXTENDED
|
||||
P132_data_struct::P132_data_struct(struct EventStruct *event) {
|
||||
P132_DeviceType altType = P132_DeviceType::InaUnknown;
|
||||
|
||||
_deviceType = static_cast<P132_DeviceType>(P132_INA_TYPE);
|
||||
|
||||
// INA226 and INA231 are interchangeable
|
||||
if (P132_DeviceType::Ina226 == _deviceType) { altType = P132_DeviceType::Ina231; }
|
||||
|
||||
if (P132_DeviceType::Ina231 == _deviceType) { altType = P132_DeviceType::Ina226; }
|
||||
|
||||
_i2c_address = P132_I2C_ADDR;
|
||||
INA = new INA_Class(16); // Max 16 addresses can be used, trade-off: INA3221 uses 3 slots...
|
||||
|
||||
if (nullptr == INA) {
|
||||
addLog(LOG_LEVEL_ERROR, F("INA Class initialization failed"));
|
||||
} else {
|
||||
uint16_t maxCurrent = max((uint16_t)1u, (uint16_t)P132_MAX_CURRENT);
|
||||
const uint8_t count = INA->begin(maxCurrent, (100 / P132_SHUNT) * 1000);
|
||||
uint8_t search = 0;
|
||||
|
||||
while (search < count) {
|
||||
const P132_DeviceType foundType = P132_INATypeToDeviceType(INA->getDeviceType(search));
|
||||
const uint8_t addr = INA->getDeviceAddress(search);
|
||||
|
||||
# ifndef BUILD_NO_DEBUG
|
||||
|
||||
if (loglevelActiveFor(LOG_LEVEL_INFO)) {
|
||||
addLog(LOG_LEVEL_INFO, strformat(F("INA : Detected: %s at 0x%x, device index: %d"),
|
||||
INA->getDeviceName(search), INA->getDeviceAddress(search), search));
|
||||
}
|
||||
# endif// ifndef BUILD_NO_DEBUG
|
||||
|
||||
if (((foundType == _deviceType) || (foundType == altType)) && (addr == _i2c_address)) {
|
||||
_device = search;
|
||||
break; // Found, done searching
|
||||
}
|
||||
|
||||
if (P132_DeviceType::Ina3221 == foundType) {
|
||||
search += 2; // Skip 'sub'-devices for INA3321
|
||||
}
|
||||
++search;
|
||||
}
|
||||
|
||||
if (0xFF == _device) {
|
||||
delete INA;
|
||||
INA = nullptr;
|
||||
addLog(LOG_LEVEL_ERROR, strformat(F("INA : Configured %s at 0x%x not found."),
|
||||
FsP(toString(_deviceType)), _i2c_address));
|
||||
} else
|
||||
if (loglevelActiveFor(LOG_LEVEL_INFO)) {
|
||||
addLog(LOG_LEVEL_INFO, strformat(F("INA : Found: %s at 0x%x, device index: %d"),
|
||||
INA->getDeviceName(_device), INA->getDeviceAddress(_device), _device));
|
||||
}
|
||||
}
|
||||
setCalibration(event);
|
||||
}
|
||||
|
||||
# else // if P132_EXTENDED
|
||||
P132_data_struct::P132_data_struct(struct EventStruct *event) {
|
||||
_i2c_address = P132_I2C_ADDR;
|
||||
setCalibration_INA3221(event);
|
||||
}
|
||||
|
||||
# endif // if P132_EXTENDED
|
||||
|
||||
// **************************************************************************/
|
||||
// Destructor
|
||||
// **************************************************************************/
|
||||
# if P132_EXTENDED
|
||||
P132_data_struct::~P132_data_struct() {
|
||||
delete INA;
|
||||
INA = nullptr;
|
||||
}
|
||||
|
||||
# endif // if P132_EXTENDED
|
||||
|
||||
// **************************************************************************/
|
||||
// Gets the raw bus voltage (7FF8 / 32760) LSB 8mV
|
||||
// **************************************************************************/
|
||||
# if !P132_EXTENDED
|
||||
|
||||
int16_t P132_data_struct::getBusVoltage_raw(byte reg) {
|
||||
uint16_t value = I2C_read16_reg(_i2c_address, reg);
|
||||
|
||||
// Shift to the right 3 to drop CNVR and OVF and multiply by LSB 8 mV
|
||||
# ifndef BUILD_NO_DEBUG
|
||||
# ifndef BUILD_NO_DEBUG
|
||||
|
||||
if (loglevelActiveFor(LOG_LEVEL_DEBUG)) {
|
||||
addLog(LOG_LEVEL_DEBUG,
|
||||
strformat(F("INA3221: get raw bus %d reg - %d"),
|
||||
value, reg));
|
||||
}
|
||||
# endif // ifndef BUILD_NO_DEBUG
|
||||
# endif // ifndef BUILD_NO_DEBUG
|
||||
return (int16_t)((value >> 3) * 8);
|
||||
}
|
||||
|
||||
@@ -45,67 +179,146 @@ int16_t P132_data_struct::getBusVoltage_raw(byte reg) {
|
||||
int16_t P132_data_struct::getShuntVoltage_raw(byte reg) {
|
||||
uint16_t value = I2C_read16_reg(_i2c_address, reg);
|
||||
|
||||
# ifndef BUILD_NO_DEBUG
|
||||
# ifndef BUILD_NO_DEBUG
|
||||
String log = strformat(F("INA3221: get raw shunt voltage %d value2 - "), value);
|
||||
# endif // ifndef BUILD_NO_DEBUG
|
||||
# endif // ifndef BUILD_NO_DEBUG
|
||||
|
||||
// Shift to the right 3 to drop CNVR and OVF and multiply by LSB
|
||||
if (value > 32767) { // check value is negative
|
||||
// value = 0; // no negative measure
|
||||
value = ((value >> 3) | 0xE000); // correct int16_t value
|
||||
# ifndef BUILD_NO_DEBUG
|
||||
# ifndef BUILD_NO_DEBUG
|
||||
log += concat(F(" value_neg - "), value);
|
||||
# endif // ifndef BUILD_NO_DEBUG
|
||||
# endif // ifndef BUILD_NO_DEBUG
|
||||
} else {
|
||||
value = (value >> 3);
|
||||
# ifndef BUILD_NO_DEBUG
|
||||
# ifndef BUILD_NO_DEBUG
|
||||
log += concat(F(" value_pos - "), value);
|
||||
# endif // ifndef BUILD_NO_DEBUG
|
||||
# endif // ifndef BUILD_NO_DEBUG
|
||||
}
|
||||
# ifndef BUILD_NO_DEBUG
|
||||
# ifndef BUILD_NO_DEBUG
|
||||
log += concat(F(" reg - "), reg);
|
||||
addLog(LOG_LEVEL_DEBUG, log);
|
||||
# endif // ifndef BUILD_NO_DEBUG
|
||||
# endif // ifndef BUILD_NO_DEBUG
|
||||
return value;
|
||||
}
|
||||
|
||||
# endif // if !P132_EXTENDED
|
||||
|
||||
// **************************************************************************/
|
||||
// Gets the shunt voltage in mV (32760 so +-163.8 mV) 7ff8 LSB 40uV
|
||||
// Gets the shunt voltage in mV
|
||||
// **************************************************************************/
|
||||
# if P132_EXTENDED
|
||||
float P132_data_struct::getShuntVoltage_mV(uint8_t reg) {
|
||||
if (!isInitialized()) {
|
||||
return 0.0f;
|
||||
}
|
||||
return INA->getShuntMicroVolts(_device + reg) * 0.001f;
|
||||
}
|
||||
|
||||
# else // if P132_EXTENDED
|
||||
float P132_data_struct::getShuntVoltage_mV(byte reg) {
|
||||
int16_t value = getShuntVoltage_raw(reg);
|
||||
|
||||
# ifndef BUILD_NO_DEBUG
|
||||
# ifndef BUILD_NO_DEBUG
|
||||
|
||||
if (loglevelActiveFor(LOG_LEVEL_DEBUG)) {
|
||||
addLog(LOG_LEVEL_DEBUG,
|
||||
strformat(F("INA3221: shunt voltage in mV * 0.04 %d reg - %d"),
|
||||
value, reg));
|
||||
}
|
||||
# endif // ifndef BUILD_NO_DEBUG
|
||||
# endif // ifndef BUILD_NO_DEBUG
|
||||
return value * 0.04f;
|
||||
}
|
||||
|
||||
# endif // if P132_EXTENDED
|
||||
|
||||
// **************************************************************************/
|
||||
// Gets the Bus voltage in volts
|
||||
// **************************************************************************/
|
||||
# if P132_EXTENDED
|
||||
float P132_data_struct::getBusVoltage_V(uint8_t reg) {
|
||||
if (!isInitialized()) {
|
||||
return 0.0f;
|
||||
}
|
||||
return INA->getBusMilliVolts(_device + reg) * 0.001f;
|
||||
}
|
||||
|
||||
# else // if P132_EXTENDED
|
||||
float P132_data_struct::getBusVoltage_V(byte reg) {
|
||||
int16_t value = getBusVoltage_raw(reg);
|
||||
|
||||
# ifndef BUILD_NO_DEBUG
|
||||
# ifndef BUILD_NO_DEBUG
|
||||
|
||||
if (loglevelActiveFor(LOG_LEVEL_DEBUG)) {
|
||||
addLog(LOG_LEVEL_DEBUG,
|
||||
strformat(F("INA3221: get bus voltage %d reg - %d"),
|
||||
value, reg));
|
||||
}
|
||||
# endif // ifndef BUILD_NO_DEBUG
|
||||
# endif // ifndef BUILD_NO_DEBUG
|
||||
return value * 0.001f;
|
||||
}
|
||||
|
||||
# endif // if P132_EXTENDED
|
||||
|
||||
# if P132_EXTENDED
|
||||
|
||||
// **************************************************************************/
|
||||
// Configures to INA3221
|
||||
// Gets the Bus current in milliampere
|
||||
// **************************************************************************/
|
||||
float P132_data_struct::getBusCurrent_mA(uint8_t reg) {
|
||||
if (!isInitialized()) {
|
||||
return 0.0f;
|
||||
}
|
||||
return INA->getBusMicroAmps(_device + reg) * 0.001f;
|
||||
}
|
||||
|
||||
// **************************************************************************/
|
||||
// Gets the Bus power in milliwatt
|
||||
// **************************************************************************/
|
||||
float P132_data_struct::getBusPower_mW(uint8_t reg) {
|
||||
if (!isInitialized()) {
|
||||
return 0.0f;
|
||||
}
|
||||
return INA->getBusMicroWatts(_device + reg) * 0.001f;
|
||||
}
|
||||
|
||||
# endif // if P132_EXTENDED
|
||||
|
||||
// **************************************************************************/
|
||||
// Configures the INA
|
||||
// **************************************************************************/
|
||||
# if P132_EXTENDED
|
||||
void P132_data_struct::setCalibration(struct EventStruct *event) {
|
||||
if (!isInitialized()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (P132_DeviceType::Ina219 != _deviceType) { // Averaging set via conversion
|
||||
INA->setAveraging(getAverageBitsToFactor(P132_GET_AVERAGE, _deviceType), _device);
|
||||
}
|
||||
const uint8_t convB = 0 == P132_GET_CFG_VERSION ? P132_GET_CONVERSION_S : P132_GET_V2_CONVERSION_S;
|
||||
const uint8_t convS = 0 == P132_GET_CFG_VERSION ? P132_GET_CONVERSION_S : P132_GET_V2_CONVERSION_S;
|
||||
INA->setBusConversion(getConversionBitsToFactor(convB, _deviceType), _device);
|
||||
INA->setShuntConversion(getConversionBitsToFactor(convS, _deviceType), _device);
|
||||
|
||||
# ifndef BUILD_NO_DEBUG
|
||||
|
||||
// Config register and manufacurer id
|
||||
const uint32_t config = I2C_read16_reg(_i2c_address, 0x00); // read config
|
||||
const uint16_t mfgid = I2C_read16_reg(_i2c_address, 0xFE); // read manufacturer ID
|
||||
const uint16_t dieid = I2C_read16_reg(_i2c_address, 0xFF); // read Die ID
|
||||
|
||||
|
||||
if (loglevelActiveFor(LOG_LEVEL_INFO)) {
|
||||
addLog(LOG_LEVEL_INFO,
|
||||
strformat(F("INA : init I2C: 0x%02x mfg: 0x%x, did: 0x%x config: 0x%x, 0b%s"),
|
||||
_i2c_address, mfgid, dieid, config, String(config, BIN).c_str()));
|
||||
}
|
||||
# endif // ifndef BUILD_NO_DEBUG
|
||||
}
|
||||
|
||||
# else // if P132_EXTENDED
|
||||
void P132_data_struct::setCalibration_INA3221(struct EventStruct *event) {
|
||||
// Set Config register
|
||||
uint32_t config = I2C_read16_reg(_i2c_address, 0x00); // read, chip default: 0x7127
|
||||
@@ -115,14 +328,14 @@ void P132_data_struct::setCalibration_INA3221(struct EventStruct *event) {
|
||||
set3BitToUL(config, INA3221_CONVERSION_BUS_BIT, P132_GET_CONVERSION_B);
|
||||
set3BitToUL(config, INA3221_CONVERSION_SHUNT_BIT, P132_GET_CONVERSION_S);
|
||||
|
||||
# ifndef BUILD_NO_DEBUG
|
||||
# ifndef BUILD_NO_DEBUG
|
||||
|
||||
if (loglevelActiveFor(LOG_LEVEL_INFO)) {
|
||||
addLog(LOG_LEVEL_INFO,
|
||||
strformat(F("INA3221: init I2C: 0x%02x mfg: 0x%x, config: 0x%x, 0b%s"),
|
||||
_i2c_address, mfgid, config, String(config, BIN).c_str()));
|
||||
}
|
||||
# endif // ifndef BUILD_NO_DEBUG
|
||||
# endif // ifndef BUILD_NO_DEBUG
|
||||
|
||||
if (mfgid != 0x5449) {
|
||||
addLogMove(LOG_LEVEL_ERROR, F("INA3221: Invalid Manufacturer ID! (0x5449)"));
|
||||
@@ -131,4 +344,61 @@ void P132_data_struct::setCalibration_INA3221(struct EventStruct *event) {
|
||||
I2C_write16_reg(_i2c_address, 0x00, static_cast<uint16_t>(config));
|
||||
}
|
||||
|
||||
# endif // if P132_EXTENDED
|
||||
|
||||
# if P132_EXTENDED
|
||||
uint32_t P132_data_struct::getAverageBitsToFactor(uint8_t bits, P132_DeviceType deviceType) {
|
||||
if (P132_DeviceType::Ina219 == deviceType) {
|
||||
if (bits) {
|
||||
return 1 << bits;
|
||||
}
|
||||
return 0u;
|
||||
}
|
||||
|
||||
switch (bits) {
|
||||
case 0b000: return 0u;
|
||||
case 0b001: return 4u;
|
||||
case 0b010: return 16u;
|
||||
case 0b011: return 64u;
|
||||
case 0b100: return 128u;
|
||||
case 0b101: return 256u;
|
||||
case 0b110: return 512u;
|
||||
case 0b111: return 1024u;
|
||||
}
|
||||
return 0u;
|
||||
}
|
||||
|
||||
uint32_t P132_data_struct::getConversionBitsToFactor(uint8_t bits, P132_DeviceType deviceType) {
|
||||
if (P132_DeviceType::Ina219 == deviceType) {
|
||||
switch (bits) {
|
||||
case 0b0000: return 0u;
|
||||
case 0b0001: return 148u;
|
||||
case 0b0010: return 276u;
|
||||
case 0b1000: return 532u;
|
||||
case 0b1001: return 1060u;
|
||||
case 0b1010: return 2130u;
|
||||
case 0b1011: return 4260u;
|
||||
case 0b1100: return 8510u;
|
||||
case 0b1101: return 17020u;
|
||||
case 0b1110: return 34050u;
|
||||
case 0b1111: return 68100u;
|
||||
}
|
||||
return 532u;
|
||||
}
|
||||
|
||||
switch (bits) {
|
||||
case 0b0000: return 0u;
|
||||
case 0b0001: return 204u;
|
||||
case 0b0010: return 332u;
|
||||
case 0b0011: return 588u;
|
||||
case 0b0100: return 1100u;
|
||||
case 0b0101: return 2116u;
|
||||
case 0b0110: return 4156u;
|
||||
case 0b0111: return 8244u;
|
||||
}
|
||||
return 1100u;
|
||||
}
|
||||
|
||||
# endif // if P132_EXTENDED
|
||||
|
||||
#endif // ifdef USES_P132
|
||||
|
||||
@@ -4,24 +4,56 @@
|
||||
#include "../../_Plugin_Helper.h"
|
||||
#ifdef USES_P132
|
||||
|
||||
# ifndef P132_EXTENDED
|
||||
# ifdef ESP8266
|
||||
# define P132_EXTENDED 0
|
||||
# endif // ifdef ESP8266
|
||||
# ifdef ESP32
|
||||
# define P132_EXTENDED 1
|
||||
# endif // ifdef ESP32
|
||||
# endif // ifndef P132_EXTENDED
|
||||
|
||||
// # define P132_DEBUG_LOG // Enable for some (extra) logging
|
||||
|
||||
# if P132_EXTENDED
|
||||
# define PLUGIN_NAME_132 "Energy (DC) - INA3221/INA226/INA228/INA260"
|
||||
# else // if P132_EXTENDED
|
||||
# define PLUGIN_NAME_132 "Energy (DC) - INA3221"
|
||||
# endif // if P132_EXTENDED
|
||||
|
||||
# define P132_CONFIG_BASE 2 // Better not change this...
|
||||
# define P132_INA_TYPE PCONFIG(0)
|
||||
# define P132_INA_PREVIOUS PCONFIG(7)
|
||||
# define P132_I2C_ADDR PCONFIG(1)
|
||||
# define P132_VALUE_1 PCONFIG(P132_CONFIG_BASE)
|
||||
# define P132_VALUE_2 PCONFIG(P132_CONFIG_BASE + 1)
|
||||
# define P132_VALUE_3 PCONFIG(P132_CONFIG_BASE + 2)
|
||||
# define P132_VALUE_4 PCONFIG(P132_CONFIG_BASE + 3)
|
||||
# define P132_SHUNT PCONFIG(6)
|
||||
# define P132_MAX_CURRENT PCONFIG_LONG(1)
|
||||
|
||||
# define P132_CONFIG_FLAGS PCONFIG_LONG(0)
|
||||
# define P132_FLAG_AVERAGE 0
|
||||
# define P132_FLAG_CONVERSION_B 3
|
||||
# define P132_FLAG_CONVERSION_S 6
|
||||
# define P132_CFG_VERSION 0x1 // Config version: 0 = V1, 1 = V2
|
||||
|
||||
# define P132_GET_AVERAGE get3BitFromUL(P132_CONFIG_FLAGS, P132_FLAG_AVERAGE)
|
||||
# define P132_GET_CONVERSION_B get3BitFromUL(P132_CONFIG_FLAGS, P132_FLAG_CONVERSION_B)
|
||||
# define P132_GET_CONVERSION_S get3BitFromUL(P132_CONFIG_FLAGS, P132_FLAG_CONVERSION_S)
|
||||
# define P132_CONFIG_FLAGS PCONFIG_ULONG(0)
|
||||
# define P132_FLAG_AVERAGE 0 // 3 bits
|
||||
# define P132_FLAG_CONVERSION_B 3 // 3 bits, V1 config
|
||||
# define P132_FLAG_CONVERSION_S 6 // 3 bits, V1 config
|
||||
# define P132_FLAG_CFG_VERSION 9 // 2 bits
|
||||
# define P132_FLAG_V2_CONVERSION_B 11 // 4 bits, V2 config
|
||||
# define P132_FLAG_V2_CONVERSION_S 15 // 4 bits, V2 config
|
||||
|
||||
# define P132_SET_AVERAGE(S) set3BitToUL(P132_CONFIG_FLAGS, P132_FLAG_AVERAGE, (S))
|
||||
# define P132_GET_AVERAGE get3BitFromUL(P132_CONFIG_FLAGS, P132_FLAG_AVERAGE)
|
||||
# define P132_SET_CONVERSION_B(S) set3BitToUL(P132_CONFIG_FLAGS, P132_FLAG_CONVERSION_B, (S))
|
||||
# define P132_GET_CONVERSION_B get3BitFromUL(P132_CONFIG_FLAGS, P132_FLAG_CONVERSION_B)
|
||||
# define P132_SET_CONVERSION_S(S) set3BitToUL(P132_CONFIG_FLAGS, P132_FLAG_CONVERSION_S, (S))
|
||||
# define P132_GET_CONVERSION_S get3BitFromUL(P132_CONFIG_FLAGS, P132_FLAG_CONVERSION_S)
|
||||
# define P132_SET_CFG_VERSION(S) set2BitToUL(P132_CONFIG_FLAGS, P132_FLAG_CFG_VERSION, (S))
|
||||
# define P132_GET_CFG_VERSION get2BitFromUL(P132_CONFIG_FLAGS, P132_FLAG_CFG_VERSION)
|
||||
# define P132_SET_V2_CONVERSION_B(S) set3BitToUL(P132_CONFIG_FLAGS, P132_FLAG_V2_CONVERSION_B, (S))
|
||||
# define P132_GET_V2_CONVERSION_B get3BitFromUL(P132_CONFIG_FLAGS, P132_FLAG_V2_CONVERSION_B)
|
||||
# define P132_SET_V2_CONVERSION_S(S) set3BitToUL(P132_CONFIG_FLAGS, P132_FLAG_V2_CONVERSION_S, (S))
|
||||
# define P132_GET_V2_CONVERSION_S get3BitFromUL(P132_CONFIG_FLAGS, P132_FLAG_V2_CONVERSION_S)
|
||||
|
||||
# define INA3221_AVERAGE_BIT 9
|
||||
# define INA3221_CONVERSION_BUS_BIT 6
|
||||
@@ -31,23 +63,69 @@
|
||||
int Plugin_132_QueryVType(uint8_t value_nr);
|
||||
# endif // if FEATURE_MQTT_DISCOVER
|
||||
|
||||
# if P132_EXTENDED
|
||||
# include <INA.h>
|
||||
|
||||
enum class P132_DeviceType : uint8_t {
|
||||
Ina3221 = 0u,
|
||||
Ina219 = 1u,
|
||||
Ina226 = 2u,
|
||||
Ina228 = 3u,
|
||||
Ina230 = 4u,
|
||||
Ina231 = 5u,
|
||||
Ina260 = 6u,
|
||||
InaUnknown = 255u,
|
||||
};
|
||||
|
||||
const __FlashStringHelper* toString(P132_DeviceType deviceType);
|
||||
const uint8_t P132_DeviceTypeToINAType(P132_DeviceType deviceType);
|
||||
const uint8_t P132_DeviceTypeToMaxValues(P132_DeviceType deviceType);
|
||||
# endif // if P132_EXTENDED
|
||||
|
||||
struct P132_data_struct : public PluginTaskData_base {
|
||||
public:
|
||||
|
||||
P132_data_struct(struct EventStruct *event);
|
||||
|
||||
P132_data_struct() = delete;
|
||||
P132_data_struct() = delete;
|
||||
# if P132_EXTENDED
|
||||
virtual ~P132_data_struct();
|
||||
# else // if P132_EXTENDED
|
||||
virtual ~P132_data_struct() = default;
|
||||
# endif // if P132_EXTENDED
|
||||
|
||||
float getShuntVoltage_mV(byte reg);
|
||||
float getBusVoltage_V(byte reg);
|
||||
float getShuntVoltage_mV(uint8_t reg);
|
||||
float getBusVoltage_V(uint8_t reg);
|
||||
# if P132_EXTENDED
|
||||
float getBusCurrent_mA(uint8_t reg);
|
||||
float getBusPower_mW(uint8_t reg);
|
||||
|
||||
void setCalibration_INA3221(struct EventStruct *event);
|
||||
void setCalibration(struct EventStruct *event);
|
||||
|
||||
bool isInitialized() const {
|
||||
return nullptr != INA;
|
||||
}
|
||||
|
||||
# else // if P132_EXTENDED
|
||||
void setCalibration_INA3221(struct EventStruct *event);
|
||||
# endif // if P132_EXTENDED
|
||||
|
||||
private:
|
||||
|
||||
int16_t getBusVoltage_raw(byte reg);
|
||||
int16_t getShuntVoltage_raw(byte reg);
|
||||
# if P132_EXTENDED
|
||||
INA_Class *INA = nullptr;
|
||||
P132_DeviceType _deviceType = P132_DeviceType::Ina3221; // Old default
|
||||
uint8_t _device = 0xFF;
|
||||
|
||||
|
||||
uint32_t getAverageBitsToFactor(uint8_t bits,
|
||||
P132_DeviceType deviceType);
|
||||
uint32_t getConversionBitsToFactor(uint8_t bits,
|
||||
P132_DeviceType deviceType);
|
||||
# else // if P132_EXTENDED
|
||||
int16_t getBusVoltage_raw(byte reg);
|
||||
int16_t getShuntVoltage_raw(byte reg);
|
||||
# endif // if P132_EXTENDED
|
||||
|
||||
int8_t _i2c_address;
|
||||
};
|
||||
|
||||