initial commit. project is working
This commit is contained in:
commit
3d38b0eaea
|
@ -0,0 +1,60 @@
|
|||
arduino_nano
|
||||
============
|
||||
|
||||
Arduino Nano firmware for the remote power meter side of the spark counter.
|
||||
|
||||
the source files are written in C and intended for the Atmel ATmega328P micro-controller.
|
||||
This does not use the Arduino IDE.
|
||||
It requires avrdude and avr-libc.
|
||||
|
||||
Connect the peacefair PZEM-004 power meter to the UART port of the Arduino Nano and the nRF24L01+ radio transceiver to the SPI port (detailed information in `nrf24.h`).
|
||||
The spark counter firmware will periodically read the measurement values from the power meter (voltage, current, power, energy) and send them using the radio transceiver.
|
||||
|
||||
The destination address and channels for the radio communication can be set in EEPROM.
|
||||
The format is the following:
|
||||
- 4 bytes source address
|
||||
- 4 bytes destination address
|
||||
- 1 byte channel number
|
||||
- 1 byte CRC8-ibutton checksum over the previous data
|
||||
The default values are defined in `main.h` but will be overwritten by the raw EEPROM data in `eeprom.bin`.
|
||||
Don't forget to use `make eeprom` to write the configuration data on the micro-controller.
|
||||
|
||||
The source code is interrupt driven when possible (timers, USART, SPI, nRF24 IRQ).
|
||||
|
||||
rpi
|
||||
===
|
||||
|
||||
Raspberry Pi program for the computer side of the spark counter.
|
||||
|
||||
the source files are written in C and require the RF24 library (https://tmrh20.github.io/RF24/RPi.html) for the nRF24L01+ radio transciever and libcurl to save the measurement data in the influxdb time series database.
|
||||
|
||||
on the Raspberry Pi the [https://www.raspberrypi.org/downloads/raspbian/|raspian] OS is installer and the bcm2835 SPI kernel module is enabled (through `rasp-config`).
|
||||
|
||||
connect to nRF24L01+ to the SPI port of the Raspberry Pi and set up an influxdb database.
|
||||
|
||||
note
|
||||
----
|
||||
|
||||
if you see the following error while compiling the RF24 eamples:
|
||||
error: ‘sleep’ was not declared in this scope
|
||||
add the folling line to the corresponding source file:
|
||||
#include <unistd.h>
|
||||
error seen in `gettingstarted.cpp`, `gettingstarted_call_response`, and `transfer.cpp`
|
||||
|
||||
pzem-004_probe.rb
|
||||
=================
|
||||
|
||||
a simple script to query measurement values from the peacefair PZEM-004 power meter.
|
||||
connect the power meter UART port using a USB to UART converter.
|
||||
|
||||
TODO
|
||||
====
|
||||
|
||||
arduino_nano:
|
||||
- use encryption for the RF communication (AES-128)
|
||||
- user a slower timer value (5s) for querying the PZEM-004 data. currently it's set to 1s but the power meter needs already more time for the 4 requested values
|
||||
- enable the watchdog (in case the interrupt handling is putting the device in an unexpected state)
|
||||
|
||||
rpi:
|
||||
- provide influxdb hostname, username, and password as parameter
|
||||
- send all measurement data at once instead of individually
|
|
@ -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,108 @@
|
|||
# Arduino (Nano) with ATmega328P programming script
|
||||
# detailed configuration on http://arduino.cc/en/uploads/Main/boards.txt
|
||||
# required packages: avrdude gcc-avr avr-libc
|
||||
|
||||
# the output firmware name
|
||||
TARGET = firmware
|
||||
|
||||
# the target (arduino nano)
|
||||
DEVICE = atmega328p
|
||||
F_CPU = 16000000UL
|
||||
# the flasher
|
||||
PROGRAMMER = arduino
|
||||
PORT = /dev/ttyUSB0
|
||||
# to flash
|
||||
# use the aduino bootlaoder, with a baudrate of 57600
|
||||
# reset the device to start bootlaoder
|
||||
FLASHER = avrdude -p $(DEVICE) -c $(PROGRAMMER) -P $(PORT) -b 57600 -D
|
||||
|
||||
# compiler executables
|
||||
CC = avr-gcc
|
||||
OBJDUMP = avr-objdump
|
||||
OBJCOPY = avr-objcopy
|
||||
SIZE = avr-size
|
||||
|
||||
# library directories, compiler, and linker flags
|
||||
LIBS = lib
|
||||
CFLAGS = -g -Wall -Werror -O3 -std=c99
|
||||
CFLAGS += -I. $(patsubst %,-I%,$(LIBS))
|
||||
CFLAGS += -mmcu=$(DEVICE) -DF_CPU=$(F_CPU)
|
||||
LDFLAGS = -Wl,-Map=$(TARGET).map,--cref
|
||||
LDFLAGS += -I. $(patsubst %,-I%,$(LIBS))
|
||||
LDFLAGS += -mmcu=$(DEVICE)
|
||||
# floating point printf version (requires -lm below)
|
||||
#LDFLAGS += -Wl,-u,vfprintf -lprintf_flt
|
||||
# math library
|
||||
#LDFLAGS += -lm
|
||||
|
||||
# source files to compile
|
||||
SRC = $(wildcard *.c)
|
||||
SRC += $(foreach LIB,$(LIBS),$(wildcard $(LIB)/*.c))
|
||||
# header files
|
||||
HEADER = $(SRC:.c=.h)
|
||||
# object files
|
||||
OBJ = $(SRC:.c=.o)
|
||||
# listing files.
|
||||
LST = $(SRC:.c=.lst)
|
||||
|
||||
all: compile flash
|
||||
$(info EEPROM has to be programmed separately)
|
||||
|
||||
debug: CFLAGS += -DDEBUG
|
||||
debug: map lst all
|
||||
|
||||
# reset board by setting DTR
|
||||
# the capacitor on DTR with create a pulse on RESET
|
||||
# after reset the bootloader is start
|
||||
# the bootloader can be used to reflash the device
|
||||
reset:
|
||||
stty 57600 raw ignbrk hup < $(PORT)
|
||||
|
||||
# flash the device using the internal bootloader
|
||||
flash: $(TARGET).hex reset
|
||||
$(FLASHER) -U flash:w:$<:i
|
||||
|
||||
# write EEPROM on the device
|
||||
eeprom: $(TARGET)_eeprom.hex reset
|
||||
$(FLASHER) -U eeprom:w:$<:i
|
||||
|
||||
# write bootloader and fuses
|
||||
bootloader:
|
||||
wget https://arduino.googlecode.com/svn/trunk/hardware/arduino/bootloaders/atmega/ATmegaBOOT_168_atmega328.hex
|
||||
avrdude -p $(DEVICE) -c usbtiny -U lfuse:w:0xff:m -U hfuse:w:0xda:m -U efuse:w:0x05:m -U flash:w:ATmegaBOOT_168_atmega328.hex:i
|
||||
rm ATmegaBOOT_168_atmega328.hex
|
||||
|
||||
# create main target firmware
|
||||
compile: $(TARGET).elf
|
||||
$(SIZE) --format=avr --mcu=$(DEVICE) $(TARGET).elf
|
||||
|
||||
# C + ASM file
|
||||
lst: $(TARGET).lst
|
||||
|
||||
# contains global and static variables
|
||||
map: $(TARGET).map
|
||||
|
||||
# compile source files
|
||||
%.o: %.c %.h
|
||||
$(CC) $(CFLAGS) -c -o $@ $<
|
||||
|
||||
# link compiled files
|
||||
%.elf: $(OBJ)
|
||||
$(CC) $(LDFLAGS) -o $@ $^
|
||||
|
||||
$(TARGET).map: $(OBJ)
|
||||
$(CC) $(LDFLAGS) -Wl,-Map=$@,--cref -o /dev/null $^
|
||||
|
||||
# create extended listing for additional information
|
||||
%.lst: %.elf
|
||||
$(OBJDUMP) --section-headers --source $< > $@
|
||||
|
||||
# create flashable ihex from firmware
|
||||
%.hex: %.elf
|
||||
$(OBJCOPY) --only-section .text --only-section .data --output-target ihex $< $@
|
||||
|
||||
%_eeprom.hex: %.elf
|
||||
$(OBJCOPY) --only-section .eeprom --change-section-lma .eeprom=0 --output-target ihex $< $@
|
||||
|
||||
clean:
|
||||
rm -f $(TARGET) $(TARGET).hex $(TARGET)_eeprom.hex $(TARGET).elf $(TARGET).lst $(TARGET).map $(LST) $(OBJ)
|
Binary file not shown.
|
@ -0,0 +1,427 @@
|
|||
/* 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/>.
|
||||
*
|
||||
*/
|
||||
/* Copyright (c) 2015 King Kévin <kingkevin@cuvoodoo.info> */
|
||||
/* This library allows to communicate with a nordic semiconductor nRF24L01 2.4GHz single chip transceiver
|
||||
* this library uses the SPI bus and requires interrupts to handle the IRQ
|
||||
*/
|
||||
#include <stdint.h> // Standard Integer Types
|
||||
#include <stdlib.h> // General utilities
|
||||
#include <stdbool.h> // Boolean
|
||||
#include <string.h> // Memory utilities
|
||||
|
||||
#include <avr/io.h> // AVR device-specific IO definitions
|
||||
#include <avr/interrupt.h> // Interrupts
|
||||
|
||||
#include <spi.h> // SPI functions
|
||||
#include <nrf24.h> // nRF24L01 configuration
|
||||
|
||||
#include <stdio.h> // Standard IO facilities
|
||||
#include <avr/pgmspace.h> // Program Space Utilities
|
||||
|
||||
#if defined(IRQ_DDR) && defined(IRQ_IO)
|
||||
/* if the nRF24L01 is initialized with interrupts, watch for nrf_activity to go true
|
||||
* once true, check if transmission or reception happened using nrf24_activity(), then nrf24_tx_activity() or nrf24_rx_activity()
|
||||
* you can then check is transmission succeeded using nrf24_tx_succeeded()
|
||||
* or get the received data using nrf24_rx_data()
|
||||
* set it to false after checks are done
|
||||
*/
|
||||
volatile bool nrf24_flag = false;
|
||||
#endif
|
||||
/* stay in RX mode to receive data */
|
||||
bool rx = false;
|
||||
|
||||
/* initialize the nRF24L01 */
|
||||
void nrf24_init()
|
||||
{
|
||||
// configure SPI
|
||||
spi_init(); // SPI is used to communicate with the nRF24L01
|
||||
// configure IO
|
||||
CE_DDR |= (1<<CE_IO); // set CE as output
|
||||
CE_PORT &= ~(1<<CE_IO); // disable CE (TR/RX)
|
||||
// initialise variables
|
||||
rx = false;
|
||||
// power up device
|
||||
// use spi_transfer_blocking in case global interrupt has not been enabled
|
||||
uint8_t cmd[2]; // to send commands
|
||||
cmd[0] = 0x00+0x00; // R_REGISTER command + CONFIG register
|
||||
spi_transfer_blocking(cmd,sizeof(cmd)); // read configuration
|
||||
#if defined(IRQ_DDR) && defined(IRQ_IO)
|
||||
cmd[1] &= ~(0x70); // enable interrupt by clearing MASK_RX_DR, MASK_TX_DS, MASK_MAX_RT
|
||||
#else
|
||||
cmd[1] |= (0x70); // disable interrupt by setting MASK_RX_DR, MASK_TX_DS, MASK_MAX_RT
|
||||
#endif
|
||||
cmd[1] |= (1<<1); // set PWR_UP to power up
|
||||
cmd[1] &= ~(1<<2); // set CRC to 1 byte
|
||||
//cmd[1] |= (1<<2); // set CRC to 2 byte
|
||||
cmd[0] = 0x20+0x00; // W_REGISTER command + CONFIG register
|
||||
spi_transfer_blocking(cmd,sizeof(cmd)); // write configuration
|
||||
// flush TX FIFO
|
||||
cmd[0] = 0x00+0x00; // R_REGISTER command + CONFIG register
|
||||
spi_transfer_blocking(cmd,sizeof(cmd)); // read config
|
||||
cmd[0] = 0x20+0x00; // W_REGISTER command + CONFIG register
|
||||
cmd[1] &= ~(1<<0); // set PRIM_RX to 0 (PTX)
|
||||
spi_transfer_blocking(cmd,sizeof(cmd)); // write config to got in PTX mode
|
||||
cmd[0] = 0xe1; // FLUSH_TX
|
||||
spi_transfer_blocking(cmd,1); // flush TX FIFO
|
||||
// flush RX FIFO
|
||||
cmd[0] = 0x00+0x00; // R_REGISTER command + CONFIG register
|
||||
spi_transfer_blocking(cmd,sizeof(cmd)); // read config
|
||||
cmd[0] = 0x20+0x00; // W_REGISTER command + CONFIG register
|
||||
cmd[1] |= (1<<0); // set PRIM_RX to 1 (PRX)
|
||||
spi_transfer_blocking(cmd,sizeof(cmd)); // write config to got in PRX mode
|
||||
cmd[0] = 0xe2; // FLUSH_RX
|
||||
spi_transfer_blocking(cmd,1); // flush RX FIFO
|
||||
// clear interrupts
|
||||
cmd[0] = 0x20+0x07; // W_REGISTER command + STATUS register
|
||||
cmd[1] = 0x70; // clear interrupts
|
||||
spi_transfer_blocking(cmd,sizeof(cmd)); // clear interrupts
|
||||
// enable auto acknowledge
|
||||
cmd[0] = 0x20+0x01; // W_REGISTER command + EN_AA register
|
||||
cmd[1] = 0x3f; // enable auto for all channels
|
||||
spi_transfer_blocking(cmd,sizeof(cmd)); // write auto acknowledge
|
||||
// enable data pipes 0 (to enable acknowledgement) and 1 (to receive)
|
||||
cmd[0] = 0x20+0x02; // W_REGISTER command + EN_RXADDR register
|
||||
cmd[1] = 0x03; // enable RX pipes 0 and 1
|
||||
spi_transfer_blocking(cmd,sizeof(cmd)); // enable pipes
|
||||
// enable dynamic payload
|
||||
cmd[0] = 0x20+0x1d; // W_REGISTER command + FEATURE register
|
||||
cmd[1] = 0x04; // set EN_DPL
|
||||
spi_transfer_blocking(cmd,sizeof(cmd)); // enable dynamic payload
|
||||
cmd[0] = 0x20+0x1c; // W_REGISTER command + DYNPD register
|
||||
cmd[1] = 0x3f; // set DPL_Px
|
||||
spi_transfer_blocking(cmd,sizeof(cmd)); // enable dynamic payload on all pipes
|
||||
// set address width to 5 bytes
|
||||
cmd[0] = 0x20+0x03; // W_REGISTER command + SETUP_AW register
|
||||
cmd[1] = 0x03; // set to 5 bytes
|
||||
spi_transfer_blocking(cmd,sizeof(cmd)); // set address width
|
||||
// set RF: data rate, power
|
||||
cmd[0] = 0x00+0x06; // R_REGISTER command + RF_SETUP register
|
||||
spi_transfer_blocking(cmd,sizeof(cmd)); // read RF setup
|
||||
cmd[0] = 0x20+0x06; // W_REGISTER command + RF_SETUP register
|
||||
cmd[1] &= ~(1<<3); // set RF_DR to 1 Mbps
|
||||
//cmd[1] |= (1<<3); // set RF_DR to 2 Mbps
|
||||
cmd[1] |= (1<<2)|(1<<1); // set RF_PWR to 0 dBm (max)
|
||||
spi_transfer_blocking(cmd,sizeof(cmd)); // set RF: data rate, power
|
||||
// set re-transmit properties
|
||||
cmd[0] = 0x20+0x04; // W_REGISTER command + SETUP_RETR register
|
||||
cmd[1] = 0x1f; // set ARD to 500uS and ARC to 15
|
||||
spi_transfer_blocking(cmd,sizeof(cmd)); // set re-transmit properties
|
||||
// enable interrupt
|
||||
#if defined(IRQ_IO)
|
||||
IRQ_DDR &= ~(1<<IRQ_IO); // set IRQ as input
|
||||
#if defined(IRQ_INT) && (IRQ_INT==1) && (IRQ_IO==PD2) // INT0 interrupt
|
||||
EIMSK |= (1<<INT0); // enable interrupt for IRQ pin
|
||||
EICRA &= ~(1<<ISC00); // interrupt on falling edge
|
||||
EICRA |= (1<<ISC01); // interrupt on falling edge
|
||||
#elif defined(IRQ_INT) && (IRQ_INT==1) && (IRQ_IO==PD3) // INT1 interrupt
|
||||
EIMSK |= (1<<INT1); // enable interrupt for IRQ pin
|
||||
EICRA &= ~(1<<ISC10); // interrupt on falling edge
|
||||
EICRA |= (1<<ISC11); // interrupt on falling edge
|
||||
#else // PCINT interrupt
|
||||
PCICR |= (1<<IRQ_PCIE); // enable interrupt for IRQ port
|
||||
IRQ_PCMSK |= (1<<IRQ_IO); // enable interrupt for IRQ pin
|
||||
#endif
|
||||
nrf24_flag = false;
|
||||
#endif
|
||||
}
|
||||
|
||||
/* register names */
|
||||
const char reg_00[] PROGMEM = "CONFIG";
|
||||
const char reg_01[] PROGMEM = "EN_AA";
|
||||
const char reg_02[] PROGMEM = "EN_RXADDR";
|
||||
const char reg_03[] PROGMEM = "SETUP_AW";
|
||||
const char reg_04[] PROGMEM = "SETUP_RETR";
|
||||
const char reg_05[] PROGMEM = "RF_CH";
|
||||
const char reg_06[] PROGMEM = "RF_SETUP";
|
||||
const char reg_07[] PROGMEM = "STATUS";
|
||||
const char reg_08[] PROGMEM = "OBSERVE_TX";
|
||||
const char reg_09[] PROGMEM = "CD";
|
||||
const char reg_10[] PROGMEM = "RX_ADDR_P0";
|
||||
const char reg_11[] PROGMEM = "RX_ADDR_P1";
|
||||
const char reg_12[] PROGMEM = "RX_ADDR_P2";
|
||||
const char reg_13[] PROGMEM = "RX_ADDR_P3";
|
||||
const char reg_14[] PROGMEM = "RX_ADDR_P4";
|
||||
const char reg_15[] PROGMEM = "RX_ADDR_P5";
|
||||
const char reg_16[] PROGMEM = "TX_ADDR";
|
||||
const char reg_17[] PROGMEM = "RX_PW_P0";
|
||||
const char reg_18[] PROGMEM = "RX_PW_P1";
|
||||
const char reg_19[] PROGMEM = "RX_PW_P2";
|
||||
const char reg_20[] PROGMEM = "RX_PW_P3";
|
||||
const char reg_21[] PROGMEM = "RX_PW_P4";
|
||||
const char reg_22[] PROGMEM = "RX_PW_P5";
|
||||
const char reg_23[] PROGMEM = "FIFO_STATUS";
|
||||
/* register names table */
|
||||
PGM_P const reg_names[] PROGMEM = {
|
||||
reg_00,
|
||||
reg_01,
|
||||
reg_02,
|
||||
reg_03,
|
||||
reg_04,
|
||||
reg_05,
|
||||
reg_06,
|
||||
reg_07,
|
||||
reg_08,
|
||||
reg_09,
|
||||
reg_10,
|
||||
reg_11,
|
||||
reg_12,
|
||||
reg_13,
|
||||
reg_14,
|
||||
reg_15,
|
||||
reg_16,
|
||||
reg_17,
|
||||
reg_18,
|
||||
reg_19,
|
||||
reg_20,
|
||||
reg_21,
|
||||
reg_22,
|
||||
reg_23
|
||||
};
|
||||
/* register size */
|
||||
const uint8_t reg_sizes[] PROGMEM = {1,1,1,1,1,1,1,1,1,1,5,5,1,1,1,1,5,1,1,1,1,1,1,1};
|
||||
|
||||
/* dump config */
|
||||
void nrf24_dump()
|
||||
{
|
||||
uint8_t cmd[6]; // SPI nFR24L01 command
|
||||
uint8_t reg_size; // the register size
|
||||
char* str; // to print strings
|
||||
for (uint8_t i=0; i<sizeof(reg_sizes); i++) { // go through registers
|
||||
cmd[0] = 0x00+i; // R_REGISTER command + register
|
||||
reg_size = pgm_read_byte(&(reg_sizes[i]));
|
||||
spi_transfer_wait(cmd,1+reg_size); // read register
|
||||
str = malloc(strlen_PF((uint_farptr_t)pgm_read_word(&(reg_names[i]))));
|
||||
strcpy_PF(str, (uint_farptr_t)pgm_read_word(&(reg_names[i])));
|
||||
printf(str);
|
||||
free(str);
|
||||
printf(": ");
|
||||
for (uint8_t j=0; j<reg_size; j++) {
|
||||
printf("%02x ",cmd[1+j]);
|
||||
}
|
||||
printf("\n");
|
||||
}
|
||||
}
|
||||
|
||||
/* set the 5 bytes TX address
|
||||
* it will be used as the destination address of the data to transmit
|
||||
* this address will also be used (in RX_ADDR_P0) to receive the ack
|
||||
*/
|
||||
void nrf24_set_tx_addr(uint8_t* addr)
|
||||
{
|
||||
uint8_t cmd[6]; // command
|
||||
cmd[0] = 0x20+0x10; // W_REGISTER command + TX_ADDR register
|
||||
memcpy(&cmd[1],addr,sizeof(cmd)-1); // copy address
|
||||
spi_transfer_wait(cmd,sizeof(cmd)); // write TX_ADDR
|
||||
cmd[0] = 0x20+0x0A; // W_REGISTER command + RX_ADDR_P0 register
|
||||
memcpy(&cmd[1],addr,sizeof(cmd)-1); // copy address (since it has been overwritten)
|
||||
spi_transfer_wait(cmd,sizeof(cmd)); // write RX_ADDR_P0
|
||||
}
|
||||
|
||||
/* set the 5 bytes RX address
|
||||
* it will be used as the address of the receiving device
|
||||
* this address will be used in RX_ADDR_P1
|
||||
*/
|
||||
void nrf24_set_rx_addr(uint8_t* addr)
|
||||
{
|
||||
uint8_t cmd[6]; // command
|
||||
cmd[0] = 0x20+0x0B; // W_REGISTER command + RX_ADDR_P1 register
|
||||
memcpy(&cmd[1],addr,sizeof(cmd)-1); // copy address
|
||||
spi_transfer_wait(cmd,sizeof(cmd)); // write RX_ADDR_P1
|
||||
}
|
||||
|
||||
/* set the RF channel to transmit on */
|
||||
void nrf24_set_rf_channel(uint8_t channel)
|
||||
{
|
||||
if (channel>125) {
|
||||
return;
|
||||
}
|
||||
/* verify there is space in the FIFO */
|
||||
uint8_t cmd[2]; // command
|
||||
cmd[0] = 0x20+0x05; // W_REGISTER command + RF_CH register
|
||||
cmd[1] = channel; // set channel
|
||||
spi_transfer_wait(cmd,sizeof(cmd)); // write RF_CH
|
||||
}
|
||||
|
||||
/* transmit <payload> with of <length> bytes (max 32 bytes)
|
||||
* returns false if input is corrupted or FIFO is already full
|
||||
* returns true after payload is written
|
||||
*/
|
||||
bool nrf24_transmit(uint8_t* payload, uint8_t length)
|
||||
{
|
||||
if (payload==NULL || length==0 || length>32) { // ensure data and size is valid
|
||||
return false;
|
||||
}
|
||||
/* check if TX FIFO is full */
|
||||
uint8_t cmd[2]; // command
|
||||
cmd[0] = 0x00+0x17; // R_REGISTER command + FIFO_STATUS register
|
||||
spi_transfer_wait(cmd,sizeof(cmd)); // read FIFO status
|
||||
if (cmd[1]&(1<<5)) { // TX_FULL
|
||||
return false;
|
||||
}
|
||||
/* go into TX mode */
|
||||
cmd[0] = 0x00+0x00; // R_REGISTER command + CONFIG register
|
||||
spi_transfer_wait(cmd,sizeof(cmd)); // read config
|
||||
cmd[0] = 0x20+0x00; // W_REGISTER command + CONFIG register
|
||||
cmd[1] &= ~(1<<0); // set PRIM_RX to 0 (PTX)
|
||||
spi_transfer_wait(cmd,sizeof(cmd)); // write config to got in PTX mode
|
||||
/* write TX payload */
|
||||
uint8_t* tx = calloc(length+1,sizeof(uint8_t));
|
||||
tx[0] = 0xa0; // W_TX_PAYLOAD command
|
||||
memcpy(&tx[1],payload,length); // copy data
|
||||
spi_transfer_wait(tx,length+1); // write TX payload
|
||||
/* start transmission */
|
||||
CE_PORT |= (1<<CE_IO); // enable CE to start transmission
|
||||
// clean memory (the payload transfer is complete at this point)
|
||||
free(tx);
|
||||
tx = NULL;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/* put device into RX mode or disable it */
|
||||
void nrf24_enable_rx(bool enable)
|
||||
{
|
||||
rx = enable; // remember to stay in RX mode
|
||||
if (rx) {
|
||||
uint8_t cmd[2]; // SPI command
|
||||
cmd[0] = 0x00+0x00; // R_REGISTER command + CONFIG register
|
||||
spi_transfer_wait(cmd,sizeof(cmd)); // read config
|
||||
cmd[0] = 0x20+0x00; // W_REGISTER command + CONFIG register
|
||||
cmd[1] |= (1<<0); // set PRIM_RX to 1 (PRX)
|
||||
spi_transfer_wait(cmd,sizeof(cmd)); // write config to got in PRX mode
|
||||
CE_PORT |= (1<<CE_IO); // enable CE to start receiving
|
||||
} else {
|
||||
uint8_t cmd[2]; // SPI command
|
||||
cmd[0] = 0x00+0x00; // R_REGISTER command + CONFIG register
|
||||
spi_transfer_wait(cmd,sizeof(cmd)); // read config
|
||||
if (cmd[1]&(1<<0)) { // currently in RX mode
|
||||
CE_PORT &= ~(1<<CE_IO); // disable CE to go to sleep
|
||||
} // do not disable if in TX mode
|
||||
}
|
||||
}
|
||||
|
||||
/* handle IRQ interrupt */
|
||||
#if defined(IRQ_IO)
|
||||
#if defined(IRQ_INT) && (IRQ_INT==1) && (IRQ_IO==PD2) // INT0 interrupt
|
||||
ISR(INT0_vect)
|
||||
#elif defined(IRQ_INT) && (IRQ_INT==1) && (IRQ_IO==PD3) // INT1 interrupt
|
||||
ISR(INT1_vect)
|
||||
#else // PCINT interrupt
|
||||
ISR(IRQ_PCINT_vect)
|
||||
#endif
|
||||
{ /* RX or TX succeeded or failed */
|
||||
nrf24_flag = true; // warn user
|
||||
}
|
||||
#endif
|
||||
|
||||
/* check the current status of the device
|
||||
* return nrf24_activity_t bits correspoinding to the activies
|
||||
* call it after an interrupt
|
||||
* puts device back in PRX mode if transmission completed and rx has been enabled
|
||||
* puts device back to sleep if transmission completed and rx has not been enabled
|
||||
*/
|
||||
uint8_t nrf24_activity()
|
||||
{
|
||||
uint8_t to_return = 0; // is there activity
|
||||
uint8_t cmd[2]; // SPI nFR24L01 command
|
||||
// get status
|
||||
cmd[0] = 0xff; // NOP to read STATUS
|
||||
spi_transfer_wait(cmd,1); // read STATUS register
|
||||
// clear receive interrupt
|
||||
to_return = cmd[0]&(0x70); // copy RX_DR, TS_DS, MAX_RT
|
||||
if ((cmd[0]&0x0e)<0x0b) { // payload is available based on RX_P_NO
|
||||
to_return |= RX_AVAILABLE; // warn user
|
||||
}
|
||||
if (cmd[0]&(0x70)) { // activity cause by interrupt
|
||||
cmd[0] = 0x20+0x07; // W_REGISTER command + STATUS register
|
||||
cmd[1] = 0x70; // clear interrupts
|
||||
spi_transfer_wait(cmd,2); // clear interrupt
|
||||
}
|
||||
if (to_return&TX_FAILED) { // transmission failed based on MAX_RT
|
||||
cmd[0] = 0xe1; // FLUSH_TX command
|
||||
spi_transfer_wait(cmd,1); // flush failed TX_FIFO
|
||||
}
|
||||
// verify is there is still data to transmit
|
||||
cmd[0] = 0x00+0x17; // R_REGISTER command + FIFO_STATUS register
|
||||
spi_transfer_wait(cmd,2); // read FIFO_STATUS register
|
||||
if (!(cmd[1]&(1<<4))) { // check TX_EMPTY, if not empty put is TX mode
|
||||
cmd[0] = 0x00+0x00; // R_REGISTER command + CONFIG register
|
||||
spi_transfer_wait(cmd,2); // read config
|
||||
cmd[0] = 0x20+0x00; // W_REGISTER command + CONFIG register
|
||||
cmd[1] &= ~(1<<0); // set PRIM_RX to 0 (PTX)
|
||||
spi_transfer_wait(cmd,2); // write config to got in PTX mode
|
||||
CE_PORT |= (1<<CE_IO); // enable CE to start transmission
|
||||
} else if (rx) { // go in RX mode if enables (and TX FIFO is empty)
|
||||
cmd[0] = 0x00+0x00; // R_REGISTER command + CONFIG register
|
||||
spi_transfer_wait(cmd,2); // read config
|
||||
cmd[0] = 0x20+0x00; // W_REGISTER command + CONFIG register
|
||||
cmd[1] |= (1<<0); // set PRIM_RX to 1 (PRX)
|
||||
spi_transfer_wait(cmd,2); // write config to got in PRX mode
|
||||
CE_PORT |= (1<<CE_IO); // enable CE to start receiving
|
||||
} else { // nothing to send, and RX is not enabled
|
||||
CE_PORT &= ~(1<<CE_IO); // disable CE to go to sleep
|
||||
}
|
||||
|
||||
return to_return;
|
||||
}
|
||||
|
||||
/* verify if data is available in the RX FIFO */
|
||||
bool nrf24_data_available()
|
||||
{
|
||||
uint8_t cmd[2]; // SPI nFR24L01 command
|
||||
cmd[0] = 0x00+0x17; // R_REGISTER command + FIFO_STATUS register
|
||||
spi_transfer_wait(cmd,sizeof(cmd)); // read STATUS register
|
||||
return !(cmd[1]&0x01); // not RX FIFO empty
|
||||
}
|
||||
|
||||
/* writes to received payload in provided <payload> pointer
|
||||
* returns size of received data
|
||||
* returns 0 if no data is available
|
||||
* write maximum <size> in <payload> (max payload size is 32)
|
||||
*/
|
||||
uint8_t nrf24_rx_payload(uint8_t* payload, uint8_t size)
|
||||
{
|
||||
uint8_t to_return = 0; // the size of the data
|
||||
uint8_t cmd[2]; // SPI nFR24L01 command
|
||||
cmd[0] = 0x60; // R_RX_PL_WID command
|
||||
spi_transfer_wait(cmd,2); // get size (and STATUS)
|
||||
if ((cmd[0]&0x0e)==0x0e) { // RX_FIFO empty based on RX_P_NO
|
||||
to_return = 0; // no data is available
|
||||
} else if (cmd[1]>32) { // something is wrong
|
||||
cmd[0] = 0xe2; // FLUSH_RX command
|
||||
spi_transfer_wait(cmd,1); // flush, as suggested by the note
|
||||
to_return = 0;
|
||||
} else { // data is available
|
||||
to_return = cmd[1];
|
||||
if (to_return>size) { // avoid buffer overflow
|
||||
to_return = size;
|
||||
}
|
||||
// go into RX mode
|
||||
cmd[0] = 0x00+0x00; // R_REGISTER command + CONFIG register
|
||||
spi_transfer_wait(cmd,2); // read config
|
||||
cmd[0] = 0x20+0x00; // W_REGISTER command + CONFIG register
|
||||
cmd[1] |= (1<<0); // set PRIM_RX to 1 (PRX)
|
||||
spi_transfer_wait(cmd,2); // write config to got in PRX mode
|
||||
// read RX payload
|
||||
uint8_t* data = calloc(to_return+1,sizeof(uint8_t)); // re-allocated memory to get data
|
||||
data[0] = 0x61; // R_RX_PAYLOAD command
|
||||
spi_transfer_wait(data,to_return+1); // read FIFO (automatically deleted)
|
||||
memcpy(payload,&data[1],to_return); // copy payload
|
||||
free(data); // clean memory
|
||||
data = NULL; // clean memory
|
||||
}
|
||||
|
||||
return to_return;
|
||||
}
|
|
@ -0,0 +1,102 @@
|
|||
/* 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/>.
|
||||
*
|
||||
*/
|
||||
/* Copyright (c) 2015 King Kévin <kingkevin@cuvoodoo.info> */
|
||||
/* This library allows to communicate with a nordic semiconductor nRF24L01 2.4GHz single chip transceiver */
|
||||
|
||||
/* Chip Enable pin to activate TX/RX */
|
||||
#define CE_IO PB1
|
||||
#define CE_PORT PORTB
|
||||
#define CE_DDR DDRB
|
||||
#define CE_PIN PINB
|
||||
/* Interrupt ReQuest pin to activate TX/RX */
|
||||
// comment out IRQ_IO if you don't want to use interrupts
|
||||
#define IRQ_IO PD2
|
||||
#define IRQ_PORT PORTD
|
||||
#define IRQ_DDR DDRD
|
||||
#define IRQ_PIN PIND
|
||||
#define IRQ_PCIE PCIE2
|
||||
#define IRQ_PCMSK PCMSK2
|
||||
#define IRQ_PCINT_vect PCINT2_vect
|
||||
// if you use PD2 or PD3 you can use the INT0 or INT1 interrupt so to not take over a whole PORT PCINT interrupt
|
||||
#if defined(IRQ_IO) && ((IRQ_IO==PD2)||(IRQ_IO==PD3))
|
||||
// comment or set to 0 if you prefer to use PCINT interrupt
|
||||
// define and set to 1 if you prefer to use INT interrupt
|
||||
#define IRQ_INT 1
|
||||
#endif
|
||||
|
||||
#if defined(IRQ_DDR) && defined(IRQ_IO)
|
||||
/* if the nRF24L01 is initialized with interrupts, watch for nrf_activity to go true
|
||||
* once true, check if transmission or reception happened using nrf24_activity(), then nrf24_tx_activity() or nrf24_rx_activity()
|
||||
* you can then check is transmission succeeded using nrf24_tx_succeeded()
|
||||
* or get the received data using nrf24_rx_data()
|
||||
* set it to false after checks are done
|
||||
*/
|
||||
extern volatile bool nrf24_flag;
|
||||
#endif
|
||||
|
||||
/* initialize the nRF24L01 */
|
||||
void nrf24_init();
|
||||
/* set the 5 bytes TX address
|
||||
* it will be used as the destination address of the data to transmit
|
||||
* this address will also be used (in RX_ADDR_P0) to receive the ack
|
||||
*/
|
||||
void nrf24_set_tx_addr(uint8_t* addr);
|
||||
/* set the 5 bytes RX address
|
||||
* it will be used as the address of the receiving device
|
||||
* this address will be used in RX_ADDR_P1
|
||||
*/
|
||||
void nrf24_set_rx_addr(uint8_t* addr);
|
||||
/* set the RF channel to transmit on */
|
||||
void nrf24_set_rf_channel(uint8_t channel);
|
||||
/* transmit <payload> with of <length> bytes (max 32 bytes)
|
||||
* returns false if input is corrupted or FIFO is already full
|
||||
* returns true after payload is written
|
||||
*/
|
||||
bool nrf24_transmit(uint8_t* payload, uint8_t length);
|
||||
/* put device into RX mode or disable it */
|
||||
void nrf24_enable_rx(bool enable);
|
||||
/* handle IRQ interrupt */
|
||||
#if defined(IRQ_IO)
|
||||
#if defined(IRQ_INT) && (IRQ_INT==1) && (IRQ_IO==PD2) // INT0 interrupt
|
||||
ISR(INT0_vect);
|
||||
#elif defined(IRQ_INT) && (IRQ_INT==1) && (IRQ_IO==PD3) // INT1 interrupt
|
||||
ISR(INT1_vect);
|
||||
#else // PCINT interrupt
|
||||
ISR(IRQ_PCINT_vect);
|
||||
#endif
|
||||
#endif
|
||||
/* check the current status of the device
|
||||
* return nrf24_activities bits correspoinding to the activies
|
||||
* call it after an interrupt
|
||||
* puts device back in PRX mode if transmission completed and rx has been enabled
|
||||
* puts device back to sleep if transmission completed and rx has not been enabled
|
||||
*/
|
||||
enum nrf24_activities {
|
||||
TX_SUCCEEDED = (1<<5),
|
||||
TX_FAILED = (1<<4),
|
||||
RX_RECEIVED = (1<<6),
|
||||
RX_AVAILABLE = (1<<3)
|
||||
};
|
||||
uint8_t nrf24_activity();
|
||||
/* writes to received payload in provided <payload> pointer
|
||||
* returns size of received data
|
||||
* returns 0 if no data is available
|
||||
* write maximum <size> in <payload> (max payload size is 32)
|
||||
*/
|
||||
uint8_t nrf24_rx_payload(uint8_t* payload, uint8_t size);
|
||||
/* verify if data is available in the RX FIFO */
|
||||
bool nrf24_data_available();
|
||||
/* dump config */
|
||||
void nrf24_dump();
|
|
@ -0,0 +1,157 @@
|
|||
/* 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/>.
|
||||
*
|
||||
*/
|
||||
/* Copyright (c) 2015 King Kévin <kingkevin@cuvoodoo.info> */
|
||||
/* This library handles the Serial Peripheral Interface (SPI)
|
||||
* it uses the SPI port and can but used with or without interrupt (non-block or blocking)
|
||||
*/
|
||||
#include <stdint.h> // Standard Integer Types
|
||||
#include <stdlib.h> // General utilities
|
||||
#include <stdbool.h> // Boolean
|
||||
|
||||
#include <avr/io.h> // AVR device-specific IO definitions
|
||||
#include <avr/interrupt.h> // Interrupts
|
||||
#include <avr/sleep.h> // Power Management and Sleep Modes
|
||||
|
||||
#include <spi.h> // SPI configuration
|
||||
|
||||
volatile uint8_t* spi_b = NULL; // the byte address to transmit/receive
|
||||
volatile size_t spi_i = NULL; // how many remaining bytes to transmit
|
||||
|
||||
/* receive SPI byte and transmit next on previous transmit completion when transfer is interrupt based (non-blocking) */
|
||||
ISR(SPI_STC_vect)
|
||||
{ /* SPI transfer complete interrupt */
|
||||
*spi_b = SPDR; // save received byte
|
||||
spi_i--; // decrement remaining bytes to transmit/receive
|
||||
if (spi_i==0) { // no remaining bytes to transmit/receive
|
||||
SPI_PORT |= (1<<SS_IO); // de-select slave
|
||||
SPCR &= ~(1<<SPIE); // disable SPI interrupt
|
||||
} else { // bytes to transmit/receive remain
|
||||
spi_b++; // go to next byte
|
||||
SPDR = *spi_b; // transmit next byte
|
||||
}
|
||||
}
|
||||
|
||||
/* initialize SPI
|
||||
* returns true if initialisation succeeded, else false
|
||||
*/
|
||||
void spi_init(void)
|
||||
{
|
||||
/* configure IO */
|
||||
MCUCR &= ~(1<<PUD); // enable global pull-up
|
||||
SPI_DDR |= (1<<SCK_IO)|(1<<MOSI_IO)|(1<<SS_IO); // set SCK, MOSI, and SS as output
|
||||
SPI_DDR &= ~(1<<MISO_IO); // set MISO as input
|
||||
SPI_PORT |= (1<<MISO_IO); // enable pull-up on MISO
|
||||
SPI_PORT |= (1<<SS_IO); // de-select slave
|
||||
/* configure SPI */
|
||||
SPCR &= ~(1<<SPIE); // disable SPI interrupt
|
||||
SPCR &= ~(1<<DORD); // MSB first
|
||||
SPCR &= ~(1<<CPOL); // SCK low on idle
|
||||
SPCR &= ~(1<<CPHA); // sample on leading SCK edge
|
||||
SPCR |= (1<<MSTR); // set as master
|
||||
// use nearset lowest clock divider value
|
||||
if (SCK_DIV>=128) {
|
||||
SPSR &= ~(1<<SPI2X);
|
||||
SPCR |= (1<<SPR1);
|
||||
SPCR |= (1<<SPR0);
|
||||
} else if (SCK_DIV>=64) {
|
||||
SPSR |= (1<<SPI2X);
|
||||
SPCR |= (1<<SPR1);
|
||||
SPCR |= (1<<SPR0);
|
||||
} else if (SCK_DIV>=32) {
|
||||
SPSR |= (1<<SPI2X);
|
||||
SPCR |= (1<<SPR1);
|
||||
SPCR &= ~(1<<SPR0);
|
||||
} else if (SCK_DIV>=16) {
|
||||
SPSR &= ~(1<<SPI2X);
|
||||
SPCR &= ~(1<<SPR1);
|
||||
SPCR |= (1<<SPR0);
|
||||
} else if (SCK_DIV>=8) {
|
||||
SPSR |= (1<<SPI2X);
|
||||
SPCR &= ~(1<<SPR1);
|
||||
SPCR |= (1<<SPR0);
|
||||
} else if (SCK_DIV>=4) {
|
||||
SPSR &= ~(1<<SPI2X);
|
||||
SPCR &= ~(1<<SPR1);
|
||||
SPCR &= ~(1<<SPR0);
|
||||
} else { // use 2 divider
|
||||
SPSR |= (1<<SPI2X);
|
||||
SPCR &= ~(1<<SPR1);
|
||||
SPCR &= ~(1<<SPR0);
|
||||
}
|
||||
SPCR |= (1<<SPE); // enable SPI
|
||||
}
|
||||
|
||||
/* transmit and receive data over SPI
|
||||
* transmits than receives each byte from <data> for <length> bytes
|
||||
* the read bytes are saved back in <data>
|
||||
* this function returns only when communication finished
|
||||
*/
|
||||
void spi_transfer_blocking(uint8_t* data, size_t length)
|
||||
{
|
||||
if ((data==NULL) || (length==0)) { // verify is there is data to transmit
|
||||
return;
|
||||
}
|
||||
SPCR &= ~(1<<SPIE); // disable SPI interrupt
|
||||
SPI_PORT &= ~(1<<SS_IO); // select slave
|
||||
for (size_t i=0; i<length; i++) {
|
||||
SPDR = data[i]; // send byte
|
||||
while(!(SPSR & (1<<SPIF))); // wait until transmission completed
|
||||
data[i] = SPDR; // read byte
|
||||
}
|
||||
SPI_PORT |= (1<<SS_IO); // de-select slave
|
||||
}
|
||||
|
||||
/* transmit and receive data over SPI
|
||||
* transmits than receives each byte from <data> for <length> bytes
|
||||
* the read bytes are saved back in <data>
|
||||
* global interrupts are required to be enabled
|
||||
* the function returns immediatly, while data is transfered
|
||||
* to ensure the transfer is complete, use spi_transfer_wait()
|
||||
* if data is already being transfered it will idle until the last transfer is completed
|
||||
*/
|
||||
void spi_transfer_nonblocking(uint8_t* data, size_t length)
|
||||
{
|
||||
if ((data==NULL) || (length==0)) { // verify is there is data to transfer
|
||||
return;
|
||||
}
|
||||
spi_transfer_wait(NULL, 0); // wait for transfer to complete
|
||||
spi_b = data; // save data pointer
|
||||
spi_i = length; // save length pointer
|
||||
SPCR |= (1<<SPIE); // enable SPI interrupt
|
||||
SPI_PORT &= ~(1<<SS_IO); // select slave
|
||||
SPDR = *data; // send first byte
|
||||
}
|
||||
|
||||
/* transmit and receive data over SPI
|
||||
* transmits than receives each byte from <data> for <length> bytes
|
||||
* the read bytes are saved back in <data>
|
||||
* global interrupts are required to be enabled
|
||||
* the function returns when all the data is transfered
|
||||
* while the data is transfered it goes into idle mode
|
||||
* if <data> is NULL or <length> is 0 it just ensures the previous transfer is complete
|
||||
*/
|
||||
void spi_transfer_wait(uint8_t* data, size_t length)
|
||||
{
|
||||
// wait until the previous transfer is finished
|
||||
while (spi_i>0) {
|
||||
// go to sleep and wait for next interrupt (SPI will interrupt a one point)
|
||||
set_sleep_mode(SLEEP_MODE_IDLE);
|
||||
sleep_mode();
|
||||
}
|
||||
if (data != NULL && length!=0) { // verify is there is data to transfer
|
||||
spi_transfer_nonblocking(data, length); // start new transfer
|
||||
spi_transfer_wait(NULL, 0); // wait for transfer to complete
|
||||
}
|
||||
}
|
|
@ -0,0 +1,57 @@
|
|||
/* 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/>.
|
||||
*
|
||||
*/
|
||||
/* Copyright (c) 2015 King Kévin <kingkevin@cuvoodoo.info> */
|
||||
/* This library handles the Serial Peripheral Interface (SPI) */
|
||||
|
||||
/* SPI pins */
|
||||
#define SPI_PORT PORTB
|
||||
#define SPI_DDR DDRB
|
||||
#define SPI_PIN PINB
|
||||
#define SCK_IO PB5
|
||||
#define MISO_IO PB4
|
||||
#define MOSI_IO PB3
|
||||
#define SS_IO PB2
|
||||
|
||||
/* SCK frequency divider (F_osc/<SCK_DIV>, with <SCK_DIV> within 2-128) */
|
||||
#define SCK_DIV 16
|
||||
|
||||
/* initialize SPI */
|
||||
void spi_init(void);
|
||||
/* receive SPI byte and transmit next on previous transmit completion when transfer is interrupt based (non-blocking) */
|
||||
ISR(SPI_STC_vect);
|
||||
/* transmit and receive data over SPI
|
||||
* transmits than receives each byte from <data> for <length> bytes
|
||||
* the read bytes are saved back in <data>
|
||||
* this function returns only when communication finished
|
||||
*/
|
||||
void spi_transfer_blocking(uint8_t* data, size_t length);
|
||||
/* transmit and receive data over SPI
|
||||
* transmits than receives each byte from <data> for <length> bytes
|
||||
* the read bytes are saved back in <data>
|
||||
* global interrupts are required to be enabled
|
||||
* the function returns immediatly, while data is transfered
|
||||
* to ensure the transfer is complete, use spi_transfer_wait()
|
||||
* if data is already being transfered it will idle until the last transfer is completed
|
||||
*/
|
||||
void spi_transfer_nonblocking(uint8_t* data, size_t length);
|
||||
/* transmit and receive data over SPI
|
||||
* transmits than receives each byte from <data> for <length> bytes
|
||||
* the read bytes are saved back in <data>
|
||||
* global interrupts are required to be enabled
|
||||
* the function returns when all the data is transfered
|
||||
* while the data is transfered it goes into idle mode
|
||||
* if <data> is NULL or <length> is 0 it just ensures the previous transfer is complete
|
||||
*/
|
||||
void spi_transfer_wait(uint8_t* data, size_t length);
|
|
@ -0,0 +1,172 @@
|
|||
/* 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/>.
|
||||
*
|
||||
*/
|
||||
/* Copyright (c) 2015 King Kévin <kingkevin@cuvoodoo.info> */
|
||||
/* this library handles the USART
|
||||
* it uses the TX and RX pins
|
||||
* it can be used with or without interrupts
|
||||
*/
|
||||
|
||||
#include <stdint.h> // Standard Integer Types
|
||||
#include <stdio.h> // Standard IO facilities
|
||||
#include <stdlib.h> // General utilities
|
||||
|
||||
#include <avr/io.h> // AVR device-specific IO definitions
|
||||
#include <avr/interrupt.h> // Interrupts
|
||||
#include <avr/sleep.h> // Power Management and Sleep Modes
|
||||
|
||||
#include "usart.h" // USART header
|
||||
#include <util/setbaud.h> // Helper macros for baud rate calculations
|
||||
|
||||
/* assign input and output streams */
|
||||
FILE usart_input = FDEV_SETUP_STREAM(NULL, (int (*)(struct __file *)) usart_getchar, _FDEV_SETUP_READ);
|
||||
#if defined(USART_INTERRUPT) && (USART_INTERRUPT==1)
|
||||
FILE usart_output = FDEV_SETUP_STREAM((int (*)(char, struct __file *)) usart_putchar_nonblocking, NULL, _FDEV_SETUP_WRITE);
|
||||
FILE usart_io = FDEV_SETUP_STREAM((int (*)(char, struct __file *)) usart_putchar_nonblocking, (int (*)(struct __file *)) usart_getchar, _FDEV_SETUP_RW);
|
||||
#else
|
||||
FILE usart_output = FDEV_SETUP_STREAM((int (*)(char, struct __file *)) usart_putchar_blocking, NULL, _FDEV_SETUP_WRITE);
|
||||
FILE usart_io = FDEV_SETUP_STREAM((int (*)(char, struct __file *)) usart_putchar_blocking, (int (*)(struct __file *)) usart_getchar, _FDEV_SETUP_RW);
|
||||
#endif
|
||||
|
||||
#if defined(USART_INTERRUPT) && (USART_INTERRUPT==1)
|
||||
/* input and output ring buffer, indexes, and available memory */
|
||||
uint8_t rx_buffer[32] = {0};
|
||||
volatile uint8_t rx_i = 0;
|
||||
volatile uint8_t rx_used = 0;
|
||||
uint8_t tx_buffer[32] = {0};
|
||||
volatile uint8_t tx_i = 0;
|
||||
volatile uint8_t tx_used = 0;
|
||||
/* show the user how much received data is ready */
|
||||
volatile uint8_t usart_incoming = 0; // same as rx_used, but since the user can write this variable we don't rely on it
|
||||
#endif
|
||||
|
||||
/* configure USART serial port */
|
||||
void usart_init(void)
|
||||
{
|
||||
UBRR0H = UBRRH_VALUE; // set baurate (calculated from BAUD)
|
||||
UBRR0L = UBRRL_VALUE; // set baurate (calculated from BAUD)
|
||||
#if USE_2X
|
||||
UCSR0A |= (1<<U2X0); // use double speed (set depending on BAUD)
|
||||
#else
|
||||
UCSR0A &= ~(1<<U2X0); // do not use double speed (set depending on BAUD)
|
||||
#endif
|
||||
UCSR0C = (0<<UMSEL00)|(0<<UMSEL01)|(0<<UCSZ02)|(1<<UCSZ01)|(1<<UCSZ00)|(0<<UPM01)|(0<<UPM00)|(0<<USBS0); // async 8N1 bit data
|
||||
UCSR0B = (1<<RXEN0)|(1<<TXEN0); // enable RX and TX
|
||||
#if defined(USART_INTERRUPT) && (USART_INTERRUPT==1)
|
||||
UCSR0B |= (1<<RXCIE0)|(1<<UDRIE0); // enable RX complete and TX data register empty interrupt
|
||||
// reset buffer states
|
||||
rx_i = 0;
|
||||
rx_used = 0;
|
||||
tx_i = 0;
|
||||
tx_used = 0;
|
||||
#else
|
||||
UCSR0B &= ~((1<<RXCIE0)|(1<<UDRIE0)); // disable RX complete and TX data register empty interrupt
|
||||
#endif
|
||||
}
|
||||
|
||||
/* put character on USART stream (blocking) */
|
||||
void usart_putchar_blocking(char c, FILE *stream)
|
||||
{
|
||||
if (c == '\n') { // add carrier return before line feed. this is recommended for most UART terminals
|
||||
usart_putchar_blocking('\r', stream);
|
||||
}
|
||||
#if defined(USART_INTERRUPT) && (USART_INTERRUPT==1)
|
||||
// idle until buffer is empty
|
||||
while (tx_used) {
|
||||
set_sleep_mode(SLEEP_MODE_IDLE);
|
||||
sleep_mode();
|
||||
}
|
||||
#endif
|
||||
while (!(UCSR0A&(1<<UDRE0))); // wait for transmit register to empty
|
||||
UDR0 = c; // put data in transmit register
|
||||
}
|
||||
|
||||
/* ensure all data has been transmitted */
|
||||
void usart_flush()
|
||||
{
|
||||
#if defined(USART_INTERRUPT) && (USART_INTERRUPT==1)
|
||||
while (tx_used) { // idle until buffer is empty
|
||||
set_sleep_mode(SLEEP_MODE_IDLE);
|
||||
sleep_mode();
|
||||
}
|
||||
#endif
|
||||
while (!(UCSR0A&(1<<TXC0))); // wait for transmission to complete
|
||||
UCSR0A |= (1<<TXC0); // clear transmit flag
|
||||
}
|
||||
|
||||
/* get character from USART stream (blocking) */
|
||||
char usart_getchar(FILE *stream)
|
||||
{
|
||||
#if defined(USART_INTERRUPT) && (USART_INTERRUPT==1)
|
||||
// idle until data is available
|
||||
while (!rx_used) {
|
||||
set_sleep_mode(SLEEP_MODE_IDLE);
|
||||
sleep_mode();
|
||||
}
|
||||
char to_return = rx_buffer[rx_i];
|
||||
rx_i = (rx_i+1)%sizeof(rx_buffer); // update used buffer
|
||||
rx_used--; // update used buffer
|
||||
usart_incoming = rx_used; // update available data
|
||||
return to_return;
|
||||
#else
|
||||
while (!(UCSR0A&(1<<RXC0))); // wait until data exists
|
||||
return UDR0;
|
||||
#endif
|
||||
}
|
||||
|
||||
#if defined(USART_INTERRUPT) && (USART_INTERRUPT==1)
|
||||
ISR(USART_RX_vect) { // USART receive interrupt
|
||||
// only save data if there is space in the buffer
|
||||
if (rx_used>=sizeof(rx_buffer)) {
|
||||
return;
|
||||
}
|
||||
cli();
|
||||
rx_buffer[(rx_i+rx_used)%sizeof(rx_buffer)] = UDR0; // put character in buffer
|
||||
rx_used++; // update used buffer
|
||||
usart_incoming = rx_used; // update available data
|
||||
sei();
|
||||
}
|
||||
|
||||
/* put character on USART stream (non-blocking using a buffer) */
|
||||
void usart_putchar_nonblocking(char c, FILE *stream)
|
||||
{
|
||||
if (c == '\n') { // add carrier return before line feed. this is recommended for most UART terminals
|
||||
usart_putchar_nonblocking('\r', stream);
|
||||
}
|
||||
if (UCSR0A&(1<<UDRE0)) { // if transmit register is empty immediatly send the data
|
||||
UDR0 = c; // put data in transmit register
|
||||
} else { // save into buffer
|
||||
// idle until buffer has some space
|
||||
while (tx_used>=sizeof(tx_buffer)) {
|
||||
set_sleep_mode(SLEEP_MODE_IDLE);
|
||||
sleep_mode();
|
||||
}
|
||||
cli();
|
||||
tx_buffer[(tx_i+tx_used)%sizeof(tx_buffer)] = c; // put character in buffer
|
||||
tx_used++; // update used buffer
|
||||
sei();
|
||||
}
|
||||
}
|
||||
|
||||
ISR(USART_UDRE_vect) { // USART transmit register interrupt
|
||||
if (!tx_used) { // no data in the buffer to transmit
|
||||
return;
|
||||
}
|
||||
UDR0 = tx_buffer[tx_i]; // put data in transmit register
|
||||
tx_i = (tx_i+1)%sizeof(rx_buffer); // update location on buffer
|
||||
tx_used--; // update used size
|
||||
}
|
||||
#endif
|
||||
|
||||
|
|
@ -0,0 +1,44 @@
|
|||
/* 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/>.
|
||||
*
|
||||
*/
|
||||
/* Copyright (c) 2015 King Kévin <kingkevin@cuvoodoo.info> */
|
||||
/* This library handles the USART configuration */
|
||||
|
||||
/* use interrupts */
|
||||
#define USART_INTERRUPT 1
|
||||
/* set tolerance to 3% to allow 115200 baudrate with 16 MHz clock, else use 9600 for default <2% */
|
||||
#define BAUD_TOL 3
|
||||
/* serial baudrate, in bits per second (with 8N1 8 bits, no parity bit, 1 stop bit settings) */
|
||||
#define BAUD 9600
|
||||
|
||||
/* input & output streams */
|
||||
FILE usart_output;
|
||||
FILE usart_input;
|
||||
FILE usart_io;
|
||||
|
||||
/* show the user how much received data is ready */
|
||||
volatile uint8_t usart_incoming;
|
||||
|
||||
/* configure serial port */
|
||||
void usart_init(void);
|
||||
/* put character on USART stream (blocking) */
|
||||
void usart_putchar_blocking(char c, FILE *stream);
|
||||
/* ensure all data has been transmitted */
|
||||
void usart_flush();
|
||||
/* get character from USART stream (blocking) */
|
||||
char usart_getchar(FILE *stream);
|
||||
#if defined(USART_INTERRUPT) && (USART_INTERRUPT==1)
|
||||
/* put character on USART stream (non-blocking using a buffer) */
|
||||
void usart_putchar_nonblocking(char c, FILE *stream);
|
||||
#endif
|
|
@ -0,0 +1,244 @@
|
|||
/* 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/>.
|
||||
*
|
||||
*/
|
||||
/* Copyright (c) 2015 King Kévin <kingkevin@cuvoodoo.info> */
|
||||
/* the spark counter transmits electricity measurements over radio
|
||||
* the electricity measurements (voltage, current, power, energy) are query everry second from a peacefair PZEM-004 power meter
|
||||
* they are then transmitted using an nRF2L01+ transceiver
|
||||
*/
|
||||
|
||||
#include <stdint.h> // Standard Integer Types
|
||||
#include <stdio.h> // Standard IO facilities
|
||||
#include <stdlib.h> // General utilities
|
||||
#include <stdbool.h> // Boolean
|
||||
#include <string.h> // Strings
|
||||
|
||||
#include <avr/io.h> // AVR device-specific IO definitions
|
||||
#include <util/delay.h> // Convenience functions for busy-wait delay loops
|
||||
#include <avr/interrupt.h> // Interrupts
|
||||
#include <avr/wdt.h> // Watchdog timer handling
|
||||
#include <avr/pgmspace.h> // Program Space Utilities
|
||||
#include <avr/sleep.h> // Power Management and Sleep Modes
|
||||
#include <avr/eeprom.h> // EEPROM handling
|
||||
#include <util/crc16.h> // CRC Computations
|
||||
|
||||
#include "main.h" // main definitions
|
||||
#include "usart.h" // basic USART functions
|
||||
#include "nrf24.h" // nRF24L01 functions
|
||||
|
||||
/* variables */
|
||||
volatile bool query_pzem004 = false; // flag to query the PZEM-004 power meter
|
||||
|
||||
/* disable watchdog when booting */
|
||||
void wdt_init(void) __attribute__((naked)) __attribute__((section(".init3")));
|
||||
void wdt_init(void)
|
||||
{
|
||||
MCUSR = 0;
|
||||
wdt_disable();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
/* initialize GPIO */
|
||||
void io_init(void)
|
||||
{
|
||||
/* use UART as terminal */
|
||||
usart_init();
|
||||
stdout = &usart_output;
|
||||
stdin = &usart_input;
|
||||
/* use nRF24L01 */
|
||||
nrf24_init();
|
||||
/* set up timer 1 to regularly wake up and request data from the power meter */
|
||||
TCCR1B |= (0<<WGM13)|(1<<WGM12); // use timer 1 in CTC mode
|
||||
TCCR1A |= (0<<WGM11)|(0<<WGM10); // use timer 1 in CTC mode
|
||||
TCNT1 = 0; // reset timer 1 counter
|
||||
OCR1A = (F_CPU/256UL)*1.0; // the time to wait before triggering. with a prescale of 256 the max is 1.05s. here I choose 1s.
|
||||
TIMSK1 |= (1<<OCIE1A); // enable interrupt
|
||||
|
||||
sei(); // enable interrupts
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
io_init(); // initialize IOs
|
||||
|
||||
//printf(PSTR("welcome to the spar counter power meter monitor\n"));
|
||||
|
||||
/* fallback nRF24 node configuration */
|
||||
struct configuration conf = {{1,'h','o','m','e'},{0,'h','o','m','e'},42};
|
||||
// read configuration data from EEPROM + CRC-8-iButton value
|
||||
uint8_t eeprom_conf[sizeof(struct configuration)+1];
|
||||
uint8_t crc = 0; // CRC8 iButton/1Wire/Dallas/Maxim calculation
|
||||
for (uint16_t i=0; i<sizeof(eeprom_conf); i++) {
|
||||
eeprom_conf[i] = eeprom_read_byte((const uint8_t*)i); // read data
|
||||
crc = _crc_ibutton_update(crc, eeprom_conf[i]); // calculate CRC
|
||||
}
|
||||
if (crc==0) { // CRC succeeded
|
||||
memcpy(&conf,eeprom_conf,sizeof(struct configuration));
|
||||
}
|
||||
nrf24_set_rx_addr(conf.rx_addr);
|
||||
nrf24_set_tx_addr(conf.tx_addr);
|
||||
nrf24_set_rf_channel(conf.channel);
|
||||
|
||||
/* set the PZEM-004 IP address (it won't reply to other requests before) */
|
||||
uint8_t cmd[7] = {0xB4,0xC0,0xA8,0x01,0x01,0x00,0x1E}; // set the address 192.168.1.1 (keep this address for the other commands)
|
||||
for (uint8_t i=0; i<sizeof(cmd); i++) {
|
||||
usart_putchar_nonblocking(cmd[i],NULL);
|
||||
}
|
||||
|
||||
/* start timer to periodically query the power meter */
|
||||
TCCR1B |= (1<<CS12)|(0<<CS11)|(0<<CS10); // set prescale to 256 (starting the timer)
|
||||
query_pzem004 = false; // immediately start querying the power meter
|
||||
bool queried_pzem004 = true; // all values have been queried from the power meter
|
||||
|
||||
/* PZEM-004 power meter values */
|
||||
float voltage = 0, current = 0, power = 0, energy = 0; // the read values
|
||||
bool send_values = false; // set to true to send out the values
|
||||
|
||||
bool action = false; // to know if we performed any king of action during which some other activity could have happend
|
||||
while (true) { // endless loop for micro-controller
|
||||
action = false; // new cycle of actions
|
||||
if (query_pzem004) {
|
||||
action = true; // an action is performed
|
||||
query_pzem004 = false; // clear flag
|
||||
if (queried_pzem004) { // wait until previous queries finished
|
||||
queried_pzem004 = false;
|
||||
// query all values (voltage, current, power, energy)
|
||||
// start with voltage, the other will be queried after the power meter answered
|
||||
cmd[0] = 0xb0; // query voltage
|
||||
cmd[6] = 0x1a; // update checksum
|
||||
for (uint8_t i=0; i<sizeof(cmd); i++) {
|
||||
usart_putchar_nonblocking(cmd[i],NULL);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (usart_incoming>=7) { // wait for an answer to be available
|
||||
action = true; // an action is performed
|
||||
//printf(PSTR("got value\n"));
|
||||
// store answer
|
||||
uint8_t answer[7];
|
||||
for (uint8_t i=0; i<sizeof(answer); i++) {
|
||||
answer[i] = usart_getchar(NULL); // save input (this updates usart_incoming)
|
||||
}
|
||||
// verify checksum
|
||||
uint8_t checksum = 0;
|
||||
for (uint8_t i=0; i<sizeof(answer)-1; i++) {
|
||||
checksum += answer[i];
|
||||
}
|
||||
if (checksum==answer[sizeof(answer)-1]) { // checksum is correct
|
||||
char str[5+1+3+1]; // store the value as string
|
||||
switch (answer[0]) {
|
||||
case 0xa0: // voltage
|
||||
snprintf(str,sizeof(str),"%u.%u",(answer[1]<<8)+answer[2],answer[3]);
|
||||
voltage = atof(str);
|
||||
cmd[0] = 0xb1; // query current
|
||||
cmd[6] = 0x1b; // update checksum
|
||||
for (uint8_t i=0; i<sizeof(cmd); i++) {
|
||||
usart_putchar_nonblocking(cmd[i],NULL);
|
||||
}
|
||||
break;
|
||||
case 0xa1: // current
|
||||
snprintf(str,sizeof(str),"%u.%u",(answer[1]<<8)+answer[2],answer[3]);
|
||||
current = atof(str);
|
||||
cmd[0] = 0xb2; // query power
|
||||
cmd[6] = 0x1c; // update checksum
|
||||
for (uint8_t i=0; i<sizeof(cmd); i++) {
|
||||
usart_putchar_nonblocking(cmd[i],NULL);
|
||||
}
|
||||
break;
|
||||
case 0xa2: // power
|
||||
snprintf(str,sizeof(str),"%u.%u",(answer[1]<<8)+answer[2],answer[3]);
|
||||
power = atof(str);
|
||||
cmd[0] = 0xb3; // query energy
|
||||
cmd[6] = 0x1d; // update checksum
|
||||
for (uint8_t i=0; i<sizeof(cmd); i++) {
|
||||
usart_putchar_nonblocking(cmd[i],NULL);
|
||||
}
|
||||
break;
|
||||
case 0xa3: // energy
|
||||
energy = (((uint32_t)answer[1])<<16)+(answer[2]<<8)+answer[3];
|
||||
queried_pzem004 = true; // all have been queried
|
||||
send_values = true; // this should be the last of the 4 values we requested. now send them
|
||||
break;
|
||||
case 0xa4: // address
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (send_values) { // send the stored values from the power meter using the nRF24L01
|
||||
action = true; // an action is performed
|
||||
send_values = false; // clear flag
|
||||
uint8_t message[3+6+6+6+6]; // create TLV based message
|
||||
uint8_t i = 0;
|
||||
message[i++] = 0; // type: message source
|
||||
message[i++] = 1; // length
|
||||
message[i++] = 1; // value: home 1
|
||||
message[i++] = 1; // type: voltage
|
||||
message[i++] = 4; // length
|
||||
memcpy(&message[i],&voltage,4); // value
|
||||
i += 4; // value
|
||||
message[i++] = 2; // type: current
|
||||
message[i++] = 4; // length
|
||||
memcpy(&message[i],¤t,4); // value
|
||||
i += 4; // value
|
||||
message[i++] = 3; // type: power
|
||||
message[i++] = 4; // length
|
||||
memcpy(&message[i],&power,4); // value
|
||||
i += 4; // value
|
||||
message[i++] = 4; // type: energy
|
||||
message[i++] = 4; // length
|
||||
memcpy(&message[i],&energy,4); // value
|
||||
i += 4; // value
|
||||
nrf24_transmit(message,i);
|
||||
}
|
||||
if (nrf24_flag) {
|
||||
action = true; // an action was performed
|
||||
nrf24_flag = false; // reset flag
|
||||
uint8_t activity = nrf24_activity();
|
||||
if (activity&TX_SUCCEEDED) {
|
||||
//printf(PSTR("transmission succeeded\n"));
|
||||
}
|
||||
if (activity&TX_FAILED) {
|
||||
//printf(PSTR("transmission failed\n"));
|
||||
}
|
||||
if (activity&RX_RECEIVED) {
|
||||
//printf(PSTR("received data\n"));
|
||||
}
|
||||
if (activity&RX_AVAILABLE) {
|
||||
//printf(PSTR("data available\n"));
|
||||
// read all RX FIFO to clear them
|
||||
while (nrf24_data_available()) {
|
||||
uint8_t data[32];
|
||||
nrf24_rx_payload(data,sizeof(data));
|
||||
//uint8_t size = nrf24_rx_payload(data,sizeof(data));
|
||||
//printf("got %d bytes\n",size);
|
||||
}
|
||||
}
|
||||
}
|
||||
/* go to sleep and wait for next interrupt */
|
||||
if (!action) { // only go to sleep if no action had to be performed
|
||||
set_sleep_mode(SLEEP_MODE_IDLE);
|
||||
sleep_mode();
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* timer 1 triggered
|
||||
* start querying the power meter
|
||||
*/
|
||||
ISR(TIMER1_COMPA_vect)
|
||||
{
|
||||
query_pzem004 = true; // warm main programm to start querying
|
||||
}
|
|
@ -0,0 +1,46 @@
|
|||
/* 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/>.
|
||||
*
|
||||
*/
|
||||
/* Copyright (c) 2015 King Kévin <kingkevin@cuvoodoo.info> */
|
||||
/* the spark counter transmits electricity measurements over radio
|
||||
* the electricity measurements (voltage, current, power, energy) are query everry second from a peacefair PZEM-004 power meter
|
||||
* they are then transmitted using an nRF2L01+ transceiver
|
||||
*/
|
||||
|
||||
/* peripherals */
|
||||
/* LED to indicate scale reading
|
||||
* pin: PB5, LED L
|
||||
*/
|
||||
/* not used because PB5 is used for SCK
|
||||
#define LED_IO PB5
|
||||
#define LED_PORT PORTB
|
||||
#define LED_DDR DDRB
|
||||
#define LED_PIN PINB
|
||||
*/
|
||||
|
||||
|
||||
/* spark counter configuration */
|
||||
struct configuration {
|
||||
uint8_t rx_addr[5]; // nRF24 receiving address
|
||||
uint8_t tx_addr[5]; // nRF24 transmit address
|
||||
uint8_t channel; // nRF24 channel
|
||||
};
|
||||
|
||||
/* spark counter default configuration stored in EEPROM
|
||||
* it's corresponds to the configuration structure
|
||||
* there is an additionnal CRC8 iButton/1Wire/Dallas/Maxim checksum
|
||||
* the variable order is reversed (avr-gcc behaviour?)
|
||||
*/
|
||||
uint8_t EEMEM eeprom_crc = 0xeb; // CRC8-ibutton checksum
|
||||
struct configuration EEMEM eeprom_default_conf = {{1,'h','o','m','e'},{0,'h','o','m','e'},42};
|
|
@ -0,0 +1,52 @@
|
|||
#!/usr/bin/env ruby
|
||||
# encoding: utf-8
|
||||
# ruby: 1.9.3
|
||||
=begin
|
||||
script to queries the measurements from a peacefair PZEM-004 power meter
|
||||
the command and data payload are copied from the manual
|
||||
here the device address is set to 192.168.1.2 in all messages
|
||||
=end
|
||||
require 'serialport'
|
||||
|
||||
@serial = SerialPort.open("/dev/ttyUSB0",{ baud: 9600, databits: 8, parity: SerialPort::NONE, stop_bit: 1, flow_control: SerialPort::NONE})
|
||||
@serial.baud = 9600
|
||||
|
||||
# set address
|
||||
cmd = [0xB4,0xC0,0xA8,0x01,0x02,0x00,0x1F]
|
||||
@serial.write(cmd.pack("C*"))
|
||||
puts "< "+cmd.collect { |b| sprintf("%02X ",b) }.join
|
||||
data = @serial.read(7).unpack("C*")
|
||||
puts "> "+data.collect { |b| sprintf("%02X ",b) }.join
|
||||
puts "address set (192.168.0.1)"
|
||||
|
||||
# get voltage
|
||||
cmd = [0xB0,0xC0,0xA8,0x01,0x02,0x00,0x1B]
|
||||
@serial.write(cmd.pack("C*"))
|
||||
puts "< "+cmd.collect { |b| sprintf("%02X ",b) }.join
|
||||
data = @serial.read(7).unpack("C*")
|
||||
puts "> "+data.collect { |b| sprintf("%02X ",b) }.join
|
||||
puts "voltage: #{(data[1]<<8)+data[2]}.#{data[3]} V"
|
||||
|
||||
# get current
|
||||
cmd = [0xB1,0xC0,0xA8,0x01,0x02,0x00,0x1C]
|
||||
@serial.write(cmd.pack("C*"))
|
||||
puts "< "+cmd.collect { |b| sprintf("%02X ",b) }.join
|
||||
data = @serial.read(7).unpack("C*")
|
||||
puts "> "+data.collect { |b| sprintf("%02X ",b) }.join
|
||||
puts "current: #{(data[1]<<8)+data[2]}.#{data[3]} A"
|
||||
|
||||
# get power
|
||||
cmd = [0xB2,0xC0,0xA8,0x01,0x02,0x00,0x1D]
|
||||
@serial.write(cmd.pack("C*"))
|
||||
puts "< "+cmd.collect { |b| sprintf("%02X ",b) }.join
|
||||
data = @serial.read(7).unpack("C*")
|
||||
puts "> "+data.collect { |b| sprintf("%02X ",b) }.join
|
||||
puts "power: #{(data[1]<<8)+data[2]}.#{data[3]} W"
|
||||
|
||||
# get energy
|
||||
cmd = [0xB3,0xC0,0xA8,0x01,0x02,0x00,0x1E]
|
||||
@serial.write(cmd.pack("C*"))
|
||||
puts "< "+cmd.collect { |b| sprintf("%02X ",b) }.join
|
||||
data = @serial.read(7).unpack("C*")
|
||||
puts "> "+data.collect { |b| sprintf("%02X ",b) }.join
|
||||
puts "energy: #{(data[1]<<16)+(data[2]<<8)+data[3]} Wh"
|
|
@ -0,0 +1,30 @@
|
|||
prefix := /usr/local
|
||||
|
||||
# Detect the Raspberry Pi by the existence of the bcm_host.h file
|
||||
BCMLOC=/opt/vc/include/bcm_host.h
|
||||
|
||||
ifneq ("$(wildcard $(BCMLOC))","")
|
||||
# The recommended compiler flags for the Raspberry Pi
|
||||
CCFLAGS=-Ofast -mfpu=vfp -mfloat-abi=hard -march=armv6zk -mtune=arm1176jzf-s
|
||||
endif
|
||||
|
||||
# define all programs
|
||||
PROGRAMS = spark_counter_receiver
|
||||
SOURCES = ${PROGRAMS:=.c}
|
||||
|
||||
all: ${PROGRAMS}
|
||||
|
||||
${PROGRAMS}: ${SOURCES}
|
||||
gcc ${CCFLAGS} -Wall -I../ -lrf24-bcm -lcurl $@.c -o $@
|
||||
|
||||
clean:
|
||||
rm -rf $(PROGRAMS)
|
||||
|
||||
install: all
|
||||
test -d $(prefix) || mkdir $(prefix)
|
||||
test -d $(prefix)/bin || mkdir $(prefix)/bin
|
||||
for prog in $(PROGRAMS); do \
|
||||
install -m 0755 $$prog $(prefix)/bin; \
|
||||
done
|
||||
|
||||
.PHONY: install
|
|
@ -0,0 +1,172 @@
|
|||
/* 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/>.
|
||||
*
|
||||
*/
|
||||
/* Copyright (c) 2015 King Kévin <kingkevin@cuvoodoo.info> */
|
||||
/* the spark counter receives electricity measurements over radio
|
||||
* they are then received using an nRF2L01+ transceiver
|
||||
* to communicate with the transceiver the RF24 library is requiered
|
||||
* they are then stored in an InfluxDB time series database using the HTTP API
|
||||
* to send values to the database the curl library is required
|
||||
*/
|
||||
|
||||
#include <unistd.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <time.h>
|
||||
|
||||
#include <RF24/RF24.h> // http://tmrh20.github.io/RF24 library to communicate to the nRF24L01+
|
||||
#include <curl/curl.h> // curl library to send the measurement data to the influxDB.
|
||||
|
||||
// Setup for RPi B1 GPIO 22 CE and CE0 CSN with SPI Speed @ 8Mhz
|
||||
RF24 radio(RPI_V2_GPIO_P1_22, BCM2835_SPI_CS0, BCM2835_SPI_SPEED_8MHZ);
|
||||
|
||||
// nRF24L01+ addresses
|
||||
const uint8_t tx_addr[] = {1,'h','o','m','e'};
|
||||
const uint8_t rx_addr[] = {0,'h','o','m','e'};
|
||||
|
||||
CURL *curl; // curl handle to post data to influxbd using the HTTP API
|
||||
|
||||
int main(int argc, char** argv){
|
||||
|
||||
// configure influxdb connection
|
||||
curl = curl_easy_init();
|
||||
if(curl) {
|
||||
curl_easy_setopt(curl, CURLOPT_URL, "http://localhost:8086/write?db=electricity");
|
||||
}
|
||||
|
||||
// configure nRF24L01+ radio
|
||||
radio.begin();
|
||||
radio.setChannel(42);
|
||||
radio.setPALevel(RF24_PA_MAX);
|
||||
radio.setDataRate(RF24_1MBPS);
|
||||
radio.setAutoAck(true);
|
||||
radio.enableDynamicPayloads();
|
||||
radio.setRetries(2,15);
|
||||
radio.setCRCLength(RF24_CRC_8);
|
||||
radio.openWritingPipe(tx_addr);
|
||||
radio.openReadingPipe(1,rx_addr);
|
||||
//radio.printDetails();
|
||||
|
||||
printf("wait for packet to arrive\n");
|
||||
radio.startListening();
|
||||
while (1) { // forever loop
|
||||
while (radio.available()) {
|
||||
time_t ltime; // calendar time
|
||||
ltime=time(NULL); // get current cal time
|
||||
char stime[64]; // do display the time
|
||||
strftime(stime, sizeof(stime), "%F %T %z %Z", localtime(<ime));
|
||||
printf("%s: payload received\n",stime);
|
||||
uint8_t payload[32]; // buffer to save the payload
|
||||
uint8_t size = radio.getDynamicPayloadSize();
|
||||
radio.read(&payload,size);
|
||||
/*
|
||||
printf("got %d bytes:",size);
|
||||
for (uint8_t i=0; i<size; i++) {
|
||||
printf(" %02x",payload[i]);
|
||||
}
|
||||
printf("\n");
|
||||
*/
|
||||
// got through payload
|
||||
uint8_t id = 0; // the meter id (0 is myself, used for unknown source)
|
||||
CURLcode res; // curl response
|
||||
char post[128] = {0}; // string to submit data to DB using POST request
|
||||
uint8_t i = 0; // index within the data
|
||||
while (i+2<size) {
|
||||
uint8_t type = payload[i]; // type of IE
|
||||
uint8_t length = payload[i+1]; // length of IE
|
||||
if (i+1+length<size) { // value is within data
|
||||
switch (type) { // read type
|
||||
case 0: // id
|
||||
if (length==1) {
|
||||
id = payload[i+2];
|
||||
printf("meter: %d\n",id);
|
||||
}
|
||||
break;
|
||||
case 1: // voltage
|
||||
if (length==4) {
|
||||
float value = 0;
|
||||
memcpy(&value,&payload[i+2],length);
|
||||
printf("voltage: %f V\n",value);
|
||||
if (curl) {
|
||||
snprintf(post, sizeof(post), "voltage,meter=%d value=%f", id, value);
|
||||
curl_easy_setopt(curl, CURLOPT_POSTFIELDS, post);
|
||||
res = curl_easy_perform(curl);
|
||||
if(res!= CURLE_OK) {
|
||||
fprintf(stderr, "curl_easy_perform() failed: %s\n", curl_easy_strerror(res));
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 2: // current
|
||||
if (length==4) {
|
||||
float value = 0;
|
||||
memcpy(&value,&payload[i+2],length);
|
||||
printf("current: %f A\n",value);
|
||||
if (curl) {
|
||||
snprintf(post, sizeof(post), "current,meter=%d value=%f", id, value);
|
||||
curl_easy_setopt(curl, CURLOPT_POSTFIELDS, post);
|
||||
res = curl_easy_perform(curl);
|
||||
if(res!= CURLE_OK) {
|
||||
fprintf(stderr, "curl_easy_perform() failed: %s\n", curl_easy_strerror(res));
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 3: // power
|
||||
if (length==4) {
|
||||
float value = 0;
|
||||
memcpy(&value,&payload[i+2],length);
|
||||
printf("power: %f W\n",value);
|
||||
if (curl) {
|
||||
snprintf(post, sizeof(post), "power,meter=%d value=%f", id, value);
|
||||
curl_easy_setopt(curl, CURLOPT_POSTFIELDS, post);
|
||||
res = curl_easy_perform(curl);
|
||||
if(res!= CURLE_OK) {
|
||||
fprintf(stderr, "curl_easy_perform() failed: %s\n", curl_easy_strerror(res));
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 4: // energy
|
||||
if (length==4) {
|
||||
float value = 0;
|
||||
memcpy(&value,&payload[i+2],length);
|
||||
printf("energy: %f Wh\n",value);
|
||||
if (curl) {
|
||||
snprintf(post, sizeof(post), "energy,meter=%d value=%f", id, value);
|
||||
curl_easy_setopt(curl, CURLOPT_POSTFIELDS, post);
|
||||
res = curl_easy_perform(curl);
|
||||
if(res!= CURLE_OK) {
|
||||
fprintf(stderr, "curl_easy_perform() failed: %s\n", curl_easy_strerror(res));
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
printf("unknown type: %d\n",payload[i]);
|
||||
}
|
||||
i += 2+length; // got to next IE
|
||||
} else { // value isn't within data
|
||||
i = size; // end the loop
|
||||
}
|
||||
}
|
||||
}
|
||||
usleep(10000); // wait 10ms before request if data is available again (the IRQ signal is not used)
|
||||
}
|
||||
|
||||
if (curl) {
|
||||
curl_easy_cleanup(curl);
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue