From 21d657e5184e3773429c88ab7b8976649169e6c1 Mon Sep 17 00:00:00 2001 From: Sandeep Mistry Date: Tue, 13 Nov 2018 13:01:50 -0500 Subject: [PATCH] Initial version --- LICENSE | 504 ++++++++++ README.md | 27 + .../BatteryMonitor/BatteryMonitor.ino | 101 ++ examples/Peripheral/ButtonLED/ButtonLED.ino | 88 ++ .../Peripheral/CallbackLED/CallbackLED.ino | 90 ++ examples/Peripheral/LED/LED.ino | 84 ++ keywords.txt | 95 ++ library.properties | 10 + src/ArduinoBLE.h | 27 + src/BLEAttribute.cpp | 64 ++ src/BLEAttribute.h | 53 + src/BLECharacteristic.cpp | 166 ++++ src/BLECharacteristic.h | 86 ++ src/BLEDescriptor.cpp | 86 ++ src/BLEDescriptor.h | 51 + src/BLEDevice.cpp | 103 ++ src/BLEDevice.h | 65 ++ src/BLEProperty.h | 32 + src/BLEService.cpp | 68 ++ src/BLEService.h | 52 + src/BLETypedCharacteristic.h | 102 ++ src/BLETypedCharacteristics.cpp | 92 ++ src/BLETypedCharacteristics.h | 95 ++ src/BLEUuid.cpp | 67 ++ src/BLEUuid.h | 42 + src/local/BLELocalCharacteristic.cpp | 241 +++++ src/local/BLELocalCharacteristic.h | 93 ++ src/local/BLELocalDescriptor.cpp | 69 ++ src/local/BLELocalDescriptor.h | 52 + src/local/BLELocalDevice.cpp | 248 +++++ src/local/BLELocalDevice.h | 75 ++ src/local/BLELocalService.cpp | 89 ++ src/local/BLELocalService.h | 59 ++ src/utility/ATT.cpp | 923 ++++++++++++++++++ src/utility/ATT.h | 86 ++ src/utility/BLELinkedList.h | 114 +++ src/utility/GAP.cpp | 173 ++++ src/utility/GAP.h | 61 ++ src/utility/GATT.cpp | 166 ++++ src/utility/GATT.h | 72 ++ src/utility/HCI.cpp | 546 +++++++++++ src/utility/HCI.h | 90 ++ src/utility/HCITransport.h | 36 + src/utility/HCIUartTransport.cpp | 93 ++ src/utility/HCIUartTransport.h | 41 + src/utility/L2CAPSignaling.cpp | 149 +++ src/utility/L2CAPSignaling.h | 54 + 47 files changed, 5780 insertions(+) create mode 100644 LICENSE create mode 100644 README.md create mode 100644 examples/Peripheral/BatteryMonitor/BatteryMonitor.ino create mode 100644 examples/Peripheral/ButtonLED/ButtonLED.ino create mode 100644 examples/Peripheral/CallbackLED/CallbackLED.ino create mode 100644 examples/Peripheral/LED/LED.ino create mode 100644 keywords.txt create mode 100644 library.properties create mode 100644 src/ArduinoBLE.h create mode 100644 src/BLEAttribute.cpp create mode 100644 src/BLEAttribute.h create mode 100644 src/BLECharacteristic.cpp create mode 100644 src/BLECharacteristic.h create mode 100644 src/BLEDescriptor.cpp create mode 100644 src/BLEDescriptor.h create mode 100644 src/BLEDevice.cpp create mode 100644 src/BLEDevice.h create mode 100644 src/BLEProperty.h create mode 100644 src/BLEService.cpp create mode 100644 src/BLEService.h create mode 100644 src/BLETypedCharacteristic.h create mode 100644 src/BLETypedCharacteristics.cpp create mode 100644 src/BLETypedCharacteristics.h create mode 100644 src/BLEUuid.cpp create mode 100644 src/BLEUuid.h create mode 100644 src/local/BLELocalCharacteristic.cpp create mode 100644 src/local/BLELocalCharacteristic.h create mode 100644 src/local/BLELocalDescriptor.cpp create mode 100644 src/local/BLELocalDescriptor.h create mode 100644 src/local/BLELocalDevice.cpp create mode 100644 src/local/BLELocalDevice.h create mode 100644 src/local/BLELocalService.cpp create mode 100644 src/local/BLELocalService.h create mode 100644 src/utility/ATT.cpp create mode 100644 src/utility/ATT.h create mode 100644 src/utility/BLELinkedList.h create mode 100644 src/utility/GAP.cpp create mode 100644 src/utility/GAP.h create mode 100644 src/utility/GATT.cpp create mode 100644 src/utility/GATT.h create mode 100644 src/utility/HCI.cpp create mode 100644 src/utility/HCI.h create mode 100644 src/utility/HCITransport.h create mode 100644 src/utility/HCIUartTransport.cpp create mode 100644 src/utility/HCIUartTransport.h create mode 100644 src/utility/L2CAPSignaling.cpp create mode 100644 src/utility/L2CAPSignaling.h diff --git a/LICENSE b/LICENSE new file mode 100644 index 00000000..19e30718 --- /dev/null +++ b/LICENSE @@ -0,0 +1,504 @@ + GNU LESSER GENERAL PUBLIC LICENSE + Version 2.1, February 1999 + + Copyright (C) 1991, 1999 Free Software Foundation, Inc. + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + +(This is the first released version of the Lesser GPL. It also counts + as the successor of the GNU Library Public License, version 2, hence + the version number 2.1.) + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +Licenses are intended to guarantee your freedom to share and change +free software--to make sure the software is free for all its users. + + This license, the Lesser General Public License, applies to some +specially designated software packages--typically libraries--of the +Free Software Foundation and other authors who decide to use it. You +can use it too, but we suggest you first think carefully about whether +this license or the ordinary General Public License is the better +strategy to use in any particular case, based on the explanations below. + + When we speak of free software, we are referring to freedom of use, +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 this service if you wish); that you receive source code or can get +it if you want it; that you can change the software and use pieces of +it in new free programs; and that you are informed that you can do +these things. + + To protect your rights, we need to make restrictions that forbid +distributors to deny you these rights or to ask you to surrender these +rights. These restrictions translate to certain responsibilities for +you if you distribute copies of the library or if you modify it. + + For example, if you distribute copies of the library, whether gratis +or for a fee, you must give the recipients all the rights that we gave +you. You must make sure that they, too, receive or can get the source +code. If you link other code with the library, you must provide +complete object files to the recipients, so that they can relink them +with the library after making changes to the library and recompiling +it. And you must show them these terms so they know their rights. + + We protect your rights with a two-step method: (1) we copyright the +library, and (2) we offer you this license, which gives you legal +permission to copy, distribute and/or modify the library. + + To protect each distributor, we want to make it very clear that +there is no warranty for the free library. Also, if the library is +modified by someone else and passed on, the recipients should know +that what they have is not the original version, so that the original +author's reputation will not be affected by problems that might be +introduced by others. + + Finally, software patents pose a constant threat to the existence of +any free program. We wish to make sure that a company cannot +effectively restrict the users of a free program by obtaining a +restrictive license from a patent holder. Therefore, we insist that +any patent license obtained for a version of the library must be +consistent with the full freedom of use specified in this license. + + Most GNU software, including some libraries, is covered by the +ordinary GNU General Public License. This license, the GNU Lesser +General Public License, applies to certain designated libraries, and +is quite different from the ordinary General Public License. We use +this license for certain libraries in order to permit linking those +libraries into non-free programs. + + When a program is linked with a library, whether statically or using +a shared library, the combination of the two is legally speaking a +combined work, a derivative of the original library. The ordinary +General Public License therefore permits such linking only if the +entire combination fits its criteria of freedom. The Lesser General +Public License permits more lax criteria for linking other code with +the library. + + We call this license the "Lesser" General Public License because it +does Less to protect the user's freedom than the ordinary General +Public License. It also provides other free software developers Less +of an advantage over competing non-free programs. These disadvantages +are the reason we use the ordinary General Public License for many +libraries. However, the Lesser license provides advantages in certain +special circumstances. + + For example, on rare occasions, there may be a special need to +encourage the widest possible use of a certain library, so that it becomes +a de-facto standard. To achieve this, non-free programs must be +allowed to use the library. A more frequent case is that a free +library does the same job as widely used non-free libraries. In this +case, there is little to gain by limiting the free library to free +software only, so we use the Lesser General Public License. + + In other cases, permission to use a particular library in non-free +programs enables a greater number of people to use a large body of +free software. For example, permission to use the GNU C Library in +non-free programs enables many more people to use the whole GNU +operating system, as well as its variant, the GNU/Linux operating +system. + + Although the Lesser General Public License is Less protective of the +users' freedom, it does ensure that the user of a program that is +linked with the Library has the freedom and the wherewithal to run +that program using a modified version of the Library. + + The precise terms and conditions for copying, distribution and +modification follow. Pay close attention to the difference between a +"work based on the library" and a "work that uses the library". The +former contains code derived from the library, whereas the latter must +be combined with the library in order to run. + + GNU LESSER GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License Agreement applies to any software library or other +program which contains a notice placed by the copyright holder or +other authorized party saying it may be distributed under the terms of +this Lesser General Public License (also called "this License"). +Each licensee is addressed as "you". + + A "library" means a collection of software functions and/or data +prepared so as to be conveniently linked with application programs +(which use some of those functions and data) to form executables. + + The "Library", below, refers to any such software library or work +which has been distributed under these terms. A "work based on the +Library" means either the Library or any derivative work under +copyright law: that is to say, a work containing the Library or a +portion of it, either verbatim or with modifications and/or translated +straightforwardly into another language. (Hereinafter, translation is +included without limitation in the term "modification".) + + "Source code" for a work means the preferred form of the work for +making modifications to it. For a library, complete source code means +all the source code for all modules it contains, plus any associated +interface definition files, plus the scripts used to control compilation +and installation of the library. + + Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running a program using the Library is not restricted, and output from +such a program is covered only if its contents constitute a work based +on the Library (independent of the use of the Library in a tool for +writing it). Whether that is true depends on what the Library does +and what the program that uses the Library does. + + 1. You may copy and distribute verbatim copies of the Library's +complete source code as you receive it, in any medium, provided that +you conspicuously and appropriately publish on each copy an +appropriate copyright notice and disclaimer of warranty; keep intact +all the notices that refer to this License and to the absence of any +warranty; and distribute a copy of this License along with the +Library. + + You may charge a fee for the physical act of transferring a copy, +and you may at your option offer warranty protection in exchange for a +fee. + + 2. You may modify your copy or copies of the Library or any portion +of it, thus forming a work based on the Library, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) The modified work must itself be a software library. + + b) You must cause the files modified to carry prominent notices + stating that you changed the files and the date of any change. + + c) You must cause the whole of the work to be licensed at no + charge to all third parties under the terms of this License. + + d) If a facility in the modified Library refers to a function or a + table of data to be supplied by an application program that uses + the facility, other than as an argument passed when the facility + is invoked, then you must make a good faith effort to ensure that, + in the event an application does not supply such function or + table, the facility still operates, and performs whatever part of + its purpose remains meaningful. + + (For example, a function in a library to compute square roots has + a purpose that is entirely well-defined independent of the + application. Therefore, Subsection 2d requires that any + application-supplied function or table used by this function must + be optional: if the application does not supply it, the square + root function must still compute square roots.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Library, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Library, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote +it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Library. + +In addition, mere aggregation of another work not based on the Library +with the Library (or with a work based on the Library) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may opt to apply the terms of the ordinary GNU General Public +License instead of this License to a given copy of the Library. To do +this, you must alter all the notices that refer to this License, so +that they refer to the ordinary GNU General Public License, version 2, +instead of to this License. (If a newer version than version 2 of the +ordinary GNU General Public License has appeared, then you can specify +that version instead if you wish.) Do not make any other change in +these notices. + + Once this change is made in a given copy, it is irreversible for +that copy, so the ordinary GNU General Public License applies to all +subsequent copies and derivative works made from that copy. + + This option is useful when you wish to copy part of the code of +the Library into a program that is not a library. + + 4. You may copy and distribute the Library (or a portion or +derivative of it, under Section 2) in object code or executable form +under the terms of Sections 1 and 2 above provided that you accompany +it with the complete corresponding machine-readable source code, which +must be distributed under the terms of Sections 1 and 2 above on a +medium customarily used for software interchange. + + If distribution of object code is made by offering access to copy +from a designated place, then offering equivalent access to copy the +source code from the same place satisfies the requirement to +distribute the source code, even though third parties are not +compelled to copy the source along with the object code. + + 5. A program that contains no derivative of any portion of the +Library, but is designed to work with the Library by being compiled or +linked with it, is called a "work that uses the Library". Such a +work, in isolation, is not a derivative work of the Library, and +therefore falls outside the scope of this License. + + However, linking a "work that uses the Library" with the Library +creates an executable that is a derivative of the Library (because it +contains portions of the Library), rather than a "work that uses the +library". The executable is therefore covered by this License. +Section 6 states terms for distribution of such executables. + + When a "work that uses the Library" uses material from a header file +that is part of the Library, the object code for the work may be a +derivative work of the Library even though the source code is not. +Whether this is true is especially significant if the work can be +linked without the Library, or if the work is itself a library. The +threshold for this to be true is not precisely defined by law. + + If such an object file uses only numerical parameters, data +structure layouts and accessors, and small macros and small inline +functions (ten lines or less in length), then the use of the object +file is unrestricted, regardless of whether it is legally a derivative +work. (Executables containing this object code plus portions of the +Library will still fall under Section 6.) + + Otherwise, if the work is a derivative of the Library, you may +distribute the object code for the work under the terms of Section 6. +Any executables containing that work also fall under Section 6, +whether or not they are linked directly with the Library itself. + + 6. As an exception to the Sections above, you may also combine or +link a "work that uses the Library" with the Library to produce a +work containing portions of the Library, and distribute that work +under terms of your choice, provided that the terms permit +modification of the work for the customer's own use and reverse +engineering for debugging such modifications. + + You must give prominent notice with each copy of the work that the +Library is used in it and that the Library and its use are covered by +this License. You must supply a copy of this License. If the work +during execution displays copyright notices, you must include the +copyright notice for the Library among them, as well as a reference +directing the user to the copy of this License. Also, you must do one +of these things: + + a) Accompany the work with the complete corresponding + machine-readable source code for the Library including whatever + changes were used in the work (which must be distributed under + Sections 1 and 2 above); and, if the work is an executable linked + with the Library, with the complete machine-readable "work that + uses the Library", as object code and/or source code, so that the + user can modify the Library and then relink to produce a modified + executable containing the modified Library. (It is understood + that the user who changes the contents of definitions files in the + Library will not necessarily be able to recompile the application + to use the modified definitions.) + + b) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (1) uses at run time a + copy of the library already present on the user's computer system, + rather than copying library functions into the executable, and (2) + will operate properly with a modified version of the library, if + the user installs one, as long as the modified version is + interface-compatible with the version that the work was made with. + + c) Accompany the work with a written offer, valid for at + least three years, to give the same user the materials + specified in Subsection 6a, above, for a charge no more + than the cost of performing this distribution. + + d) If distribution of the work is made by offering access to copy + from a designated place, offer equivalent access to copy the above + specified materials from the same place. + + e) Verify that the user has already received a copy of these + materials or that you have already sent this user a copy. + + For an executable, the required form of the "work that uses the +Library" must include any data and utility programs needed for +reproducing the executable from it. However, as a special exception, +the materials to be distributed need not include anything that is +normally distributed (in either source or binary form) with the major +components (compiler, kernel, and so on) of the operating system on +which the executable runs, unless that component itself accompanies +the executable. + + It may happen that this requirement contradicts the license +restrictions of other proprietary libraries that do not normally +accompany the operating system. Such a contradiction means you cannot +use both them and the Library together in an executable that you +distribute. + + 7. You may place library facilities that are a work based on the +Library side-by-side in a single library together with other library +facilities not covered by this License, and distribute such a combined +library, provided that the separate distribution of the work based on +the Library and of the other library facilities is otherwise +permitted, and provided that you do these two things: + + a) Accompany the combined library with a copy of the same work + based on the Library, uncombined with any other library + facilities. This must be distributed under the terms of the + Sections above. + + b) Give prominent notice with the combined library of the fact + that part of it is a work based on the Library, and explaining + where to find the accompanying uncombined form of the same work. + + 8. You may not copy, modify, sublicense, link with, or distribute +the Library except as expressly provided under this License. Any +attempt otherwise to copy, modify, sublicense, link with, or +distribute the Library is void, and will automatically terminate your +rights under this License. However, parties who have received copies, +or rights, from you under this License will not have their licenses +terminated so long as such parties remain in full compliance. + + 9. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Library or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Library (or any work based on the +Library), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Library or works based on it. + + 10. Each time you redistribute the Library (or any work based on the +Library), the recipient automatically receives a license from the +original licensor to copy, distribute, link with or modify the Library +subject to these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties with +this License. + + 11. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +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 +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Library at all. For example, if a patent +license would not permit royalty-free redistribution of the Library by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Library. + +If any portion of this section is held invalid or unenforceable under any +particular circumstance, the balance of the section is intended to apply, +and the section as a whole is intended to apply in other circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 12. If the distribution and/or use of the Library is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Library under this License may add +an explicit geographical distribution limitation excluding those countries, +so that distribution is permitted only in or among countries not thus +excluded. In such case, this License incorporates the limitation as if +written in the body of this License. + + 13. The Free Software Foundation may publish revised and/or new +versions of the Lesser 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 Library +specifies a version number of this License which applies to it and +"any later version", you have the option of following the terms and +conditions either of that version or of any later version published by +the Free Software Foundation. If the Library does not specify a +license version number, you may choose any version ever published by +the Free Software Foundation. + + 14. If you wish to incorporate parts of the Library into other free +programs whose distribution conditions are incompatible with these, +write to the author to ask for permission. For software which is +copyrighted by the Free Software Foundation, write to the Free +Software Foundation; we sometimes make exceptions for this. Our +decision will be guided by the two goals of preserving the free status +of all derivatives of our free software and of promoting the sharing +and reuse of software generally. + + NO WARRANTY + + 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO +WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. +EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR +OTHER PARTIES PROVIDE THE LIBRARY "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 +LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME +THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN +WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY +AND/OR REDISTRIBUTE THE LIBRARY 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 +LIBRARY (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 LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF +SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Libraries + + If you develop a new library, and you want it to be of the greatest +possible use to the public, we recommend making it free software that +everyone can redistribute and change. You can do so by permitting +redistribution under these terms (or, alternatively, under the terms of the +ordinary General Public License). + + To apply these terms, attach the following notices to the library. It is +safest to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least the +"copyright" line and a pointer to where the full notice is found. + + {description} + Copyright (C) {year} {fullname} + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 + USA + +Also add information on how to contact you by electronic and paper mail. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the library, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the + library `Frob' (a library for tweaking knobs) written by James Random + Hacker. + + {signature of Ty Coon}, 1 April 1990 + Ty Coon, President of Vice + +That's all there is to it! diff --git a/README.md b/README.md new file mode 100644 index 00000000..9c53cae4 --- /dev/null +++ b/README.md @@ -0,0 +1,27 @@ +# ArduinoBLE + +Enables BLE connectivity on the Arduino MKR WiFi 1010 and Arduino UNO WiFi Rev.2. + +This library currently supports creating a BLE peripheral. + +Requires the NINA module to be running [Arduino NINA-W102 firmware](https://github.com/arduino/nina-fw) v1.2.0 or later. + +## License + +``` +Copyright (c) 2018 Arduino SA. All rights reserved. + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library 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 +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +``` diff --git a/examples/Peripheral/BatteryMonitor/BatteryMonitor.ino b/examples/Peripheral/BatteryMonitor/BatteryMonitor.ino new file mode 100644 index 00000000..90193152 --- /dev/null +++ b/examples/Peripheral/BatteryMonitor/BatteryMonitor.ino @@ -0,0 +1,101 @@ +/* + Battery Monitor + + This example creates a BLE peripheral with the standard battery service and + level characteristic. The A0 pin is used to calculate the battery level. + + The circuit: + - Arduino MKR WiFi 1010 or Arduino Uno WiFi Rev2 board + + This example code is in the public domain. +*/ + +#include + + // BLE Battery Service +BLEService batteryService("180F"); + +// BLE Battery Level Characteristic +BLEUnsignedCharCharacteristic batteryLevelChar("2A19", // standard 16-bit characteristic UUID + BLERead | BLENotify); // remote clients will be able to get notifications if this characteristic changes + +int oldBatteryLevel = 0; // last battery level reading from analog input +long previousMillis = 0; // last time the battery level was checked, in ms + +void setup() { + Serial.begin(9600); // initialize serial communication + while (!Serial); + + pinMode(LED_BUILTIN, OUTPUT); // initialize the built-in LED pin to indicate when a central is connected + + // begin initialization + if (!BLE.begin()) { + Serial.println("starting BLE failed!"); + + while (1); + } + + /* Set a local name for the BLE device + This name will appear in advertising packets + and can be used by remote devices to identify this BLE device + The name can be changed but maybe be truncated based on space left in advertisement packet + */ + BLE.setLocalName("BatteryMonitor"); + BLE.setAdvertisedService(batteryService); // add the service UUID + batteryService.addCharacteristic(batteryLevelChar); // add the battery level characteristic + BLE.addService(batteryService); // Add the battery service + batteryLevelChar.writeValue(oldBatteryLevel); // set initial value for this characteristic + + /* Start advertising BLE. It will start continuously transmitting BLE + advertising packets and will be visible to remote BLE central devices + until it receives a new connection */ + + // start advertising + BLE.advertise(); + + Serial.println("Bluetooth device active, waiting for connections..."); +} + +void loop() { + // wait for a BLE central + BLEDevice central = BLE.central(); + + // if a central is connected to the peripheral: + if (central) { + Serial.print("Connected to central: "); + // print the central's BT address: + Serial.println(central.address()); + // turn on the LED to indicate the connection: + digitalWrite(LED_BUILTIN, HIGH); + + // check the battery level every 200ms + // while the central is connected: + while (central.connected()) { + long currentMillis = millis(); + // if 200ms have passed, check the battery level: + if (currentMillis - previousMillis >= 200) { + previousMillis = currentMillis; + updateBatteryLevel(); + } + } + // when the central disconnects, turn off the LED: + digitalWrite(LED_BUILTIN, LOW); + Serial.print("Disconnected from central: "); + Serial.println(central.address()); + } +} + +void updateBatteryLevel() { + /* Read the current voltage level on the A0 analog input pin. + This is used here to simulate the charge level of a battery. + */ + int battery = analogRead(A0); + int batteryLevel = map(battery, 0, 1023, 0, 100); + + if (batteryLevel != oldBatteryLevel) { // if the battery level has changed + Serial.print("Battery Level % is now: "); // print it + Serial.println(batteryLevel); + batteryLevelChar.writeValue(batteryLevel); // and update the battery level characteristic + oldBatteryLevel = batteryLevel; // save the level for next comparison + } +} diff --git a/examples/Peripheral/ButtonLED/ButtonLED.ino b/examples/Peripheral/ButtonLED/ButtonLED.ino new file mode 100644 index 00000000..c89e58db --- /dev/null +++ b/examples/Peripheral/ButtonLED/ButtonLED.ino @@ -0,0 +1,88 @@ +/* + Button LED + + This example creates a BLE peripheral with service that contains a + characteristic to control an LED and another characteristic that + represents the state of the button. + + The circuit: + - Arduino MKR WiFi 1010 or Arduino Uno WiFi Rev2 board + - Button connected to pin 4 + + This example code is in the public domain. +*/ + +#include + +const int ledPin = LED_BUILTIN; // set ledPin to on-board LED +const int buttonPin = 4; // set buttonPin to digital pin 4 + +BLEService ledService("19B10010-E8F2-537E-4F6C-D104768A1214"); // create service + +// create switch characteristic and allow remote device to read and write +BLEByteCharacteristic ledCharacteristic("19B10011-E8F2-537E-4F6C-D104768A1214", BLERead | BLEWrite); +// create button characteristic and allow remote device to get notifications +BLEByteCharacteristic buttonCharacteristic("19B10012-E8F2-537E-4F6C-D104768A1214", BLERead | BLENotify); + +void setup() { + Serial.begin(9600); + while (!Serial); + + pinMode(ledPin, OUTPUT); // use the LED as an output + pinMode(buttonPin, INPUT); // use button pin as an input + + // begin initialization + if (!BLE.begin()) { + Serial.println("starting BLE failed!"); + + while (1); + } + + // set the local name peripheral advertises + BLE.setLocalName("ButtonLED"); + // set the UUID for the service this peripheral advertises: + BLE.setAdvertisedService(ledService); + + // add the characteristics to the service + ledService.addCharacteristic(ledCharacteristic); + ledService.addCharacteristic(buttonCharacteristic); + + // add the service + BLE.addService(ledService); + + ledCharacteristic.writeValue(0); + buttonCharacteristic.writeValue(0); + + // start advertising + BLE.advertise(); + + Serial.println("Bluetooth device active, waiting for connections..."); +} + +void loop() { + // poll for BLE events + BLE.poll(); + + // read the current button pin state + char buttonValue = digitalRead(buttonPin); + + // has the value changed since the last read + boolean buttonChanged = (buttonCharacteristic.value() != buttonValue); + + if (buttonChanged) { + // button state changed, update characteristics + ledCharacteristic.writeValue(buttonValue); + buttonCharacteristic.writeValue(buttonValue); + } + + if (ledCharacteristic.written() || buttonChanged) { + // update LED, either central has written to characteristic or button state has changed + if (ledCharacteristic.value()) { + Serial.println("LED on"); + digitalWrite(ledPin, HIGH); + } else { + Serial.println("LED off"); + digitalWrite(ledPin, LOW); + } + } +} diff --git a/examples/Peripheral/CallbackLED/CallbackLED.ino b/examples/Peripheral/CallbackLED/CallbackLED.ino new file mode 100644 index 00000000..c92cc60b --- /dev/null +++ b/examples/Peripheral/CallbackLED/CallbackLED.ino @@ -0,0 +1,90 @@ +/* + Callback LED + + This example creates a BLE peripheral with service that contains a + characteristic to control an LED. The callback features of the + library are used. + + The circuit: + - Arduino MKR WiFi 1010 or Arduino Uno WiFi Rev2 board + + This example code is in the public domain. +*/ + +#include + +BLEService ledService("19B10000-E8F2-537E-4F6C-D104768A1214"); // create service + +// create switch characteristic and allow remote device to read and write +BLEByteCharacteristic switchCharacteristic("19B10001-E8F2-537E-4F6C-D104768A1214", BLERead | BLEWrite); + +const int ledPin = LED_BUILTIN; // pin to use for the LED + +void setup() { + Serial.begin(9600); + while (!Serial); + + pinMode(ledPin, OUTPUT); // use the LED pin as an output + + // begin initialization + if (!BLE.begin()) { + Serial.println("starting BLE failed!"); + + while (1); + } + + // set the local name peripheral advertises + BLE.setLocalName("LEDCallback"); + // set the UUID for the service this peripheral advertises + BLE.setAdvertisedService(ledService); + + // add the characteristic to the service + ledService.addCharacteristic(switchCharacteristic); + + // add service + BLE.addService(ledService); + + // assign event handlers for connected, disconnected to peripheral + BLE.setEventHandler(BLEConnected, blePeripheralConnectHandler); + BLE.setEventHandler(BLEDisconnected, blePeripheralDisconnectHandler); + + // assign event handlers for characteristic + switchCharacteristic.setEventHandler(BLEWritten, switchCharacteristicWritten); + // set an initial value for the characteristic + switchCharacteristic.setValue(0); + + // start advertising + BLE.advertise(); + + Serial.println(("Bluetooth device active, waiting for connections...")); +} + +void loop() { + // poll for BLE events + BLE.poll(); +} + +void blePeripheralConnectHandler(BLEDevice central) { + // central connected event handler + Serial.print("Connected event, central: "); + Serial.println(central.address()); +} + +void blePeripheralDisconnectHandler(BLEDevice central) { + // central disconnected event handler + Serial.print("Disconnected event, central: "); + Serial.println(central.address()); +} + +void switchCharacteristicWritten(BLEDevice central, BLECharacteristic characteristic) { + // central wrote new value to characteristic, update LED + Serial.print("Characteristic event, written: "); + + if (switchCharacteristic.value()) { + Serial.println("LED on"); + digitalWrite(ledPin, HIGH); + } else { + Serial.println("LED off"); + digitalWrite(ledPin, LOW); + } +} diff --git a/examples/Peripheral/LED/LED.ino b/examples/Peripheral/LED/LED.ino new file mode 100644 index 00000000..c9f2be88 --- /dev/null +++ b/examples/Peripheral/LED/LED.ino @@ -0,0 +1,84 @@ +/* + LED + + This example creates a BLE peripheral with service that contains a + characteristic to control an LED. + + The circuit: + - Arduino MKR WiFi 1010 or Arduino Uno WiFi Rev2 board + + This example code is in the public domain. +*/ + +#include + +BLEService ledService("19B10000-E8F2-537E-4F6C-D104768A1214"); // BLE LED Service + +// BLE LED Switch Characteristic - custom 128-bit UUID, read and writable by central +BLEByteCharacteristic switchCharacteristic("19B10001-E8F2-537E-4F6C-D104768A1214", BLERead | BLEWrite); + +const int ledPin = LED_BUILTIN; // pin to use for the LED + +void setup() { + Serial.begin(9600); + while (!Serial); + + // set LED pin to output mode + pinMode(ledPin, OUTPUT); + + // begin initialization + if (!BLE.begin()) { + Serial.println("starting BLE failed!"); + + while (1); + } + + // set advertised local name and service UUID: + BLE.setLocalName("LED"); + BLE.setAdvertisedService(ledService); + + // add the characteristic to the service + ledService.addCharacteristic(switchCharacteristic); + + // add service + BLE.addService(ledService); + + // set the initial value for the characeristic: + switchCharacteristic.writeValue(0); + + // start advertising + BLE.advertise(); + + Serial.println("BLE LED Peripheral"); +} + +void loop() { + // listen for BLE peripherals to connect: + BLEDevice central = BLE.central(); + + // if a central is connected to peripheral: + if (central) { + Serial.print("Connected to central: "); + // print the central's MAC address: + Serial.println(central.address()); + + // while the central is still connected to peripheral: + while (central.connected()) { + // if the remote device wrote to the characteristic, + // use the value to control the LED: + if (switchCharacteristic.written()) { + if (switchCharacteristic.value()) { // any value other than 0 + Serial.println("LED on"); + digitalWrite(ledPin, HIGH); // will turn the LED on + } else { // a 0 value + Serial.println(F("LED off")); + digitalWrite(ledPin, LOW); // will turn the LED off + } + } + } + + // when the central disconnects, print it out: + Serial.print(F("Disconnected from central: ")); + Serial.println(central.address()); + } +} diff --git a/keywords.txt b/keywords.txt new file mode 100644 index 00000000..4519babc --- /dev/null +++ b/keywords.txt @@ -0,0 +1,95 @@ +####################################### +# Syntax Coloring Map For ArduinoBLE +####################################### + +####################################### +# Datatypes (KEYWORD1) +####################################### + +ArduinoBLE KEYWORD1 +BLE KEYWORD1 + +BLE KEYWORD1 +BLECharacteristic KEYWORD1 +BLEDescriptor KEYWORD1 +BLEService KEYWORD1 + +BLEBoolCharacteristic KEYWORD1 +BLEBooleanCharacteristic KEYWORD1 +BLECharCharacteristic KEYWORD1 +BLEUnsignedCharCharacteristic KEYWORD1 +BLEByteCharacteristic KEYWORD1 +BLEShortCharacteristic KEYWORD1 +BLEUnsignedShortCharacteristic KEYWORD1 +BLEWordCharacteristic KEYWORD1 +BLEIntCharacteristic KEYWORD1 +BLEUnsignedIntCharacteristic KEYWORD1 +BLELongCharacteristic KEYWORD1 +BLEUnsignedLongCharacteristic KEYWORD1 +BLEFloatCharacteristic KEYWORD1 +BLEDoubleCharacteristic KEYWORD1 + +####################################### +# Methods and Functions (KEYWORD2) +####################################### + +begin KEYWORD2 +poll KEYWORD2 +end KEYWORD2 + +connected KEYWORD2 +disconnect KEYWORD +address KEYWORD2 +rssi KEYWORD + +setAdvertisedServiceUuid KEYWORD +setManufacturerData KEYWORD +setLocalName KEYWORD +setDeviceName KEYWORD +setAppearance KEYWORD +addService KEYWORD +advertise KEYWORD +stopAdvertise KEYWORD +central KEYWORD +setEventHandler KEYWORD +setAdvertisingInterval KEYWORD +setConnectionInterval KEYWORD +setConnectable KEYWORD +debug KEYWORD +noDebug KEYWORD + +properties KEYWORD +valueSize KEYWORD +value KEYWORD +valueLength KEYWORD +writeValue KEYWORD +setValue KEYWORD +broadcast KEYWORD +written KEYWORD +subscribed KEYWORD +addDescriptor KEYWORD +writeValueLE KEYWORD +setValueLE KEYWORD +valueLE KEYWORD +writeValueBE KEYWORD +setValueBE KEYWORD +valueBE KEYWORD + +uuid KEYWORD +addCharacteristic KEYWORD + +####################################### +# Constants (LITERAL1) +####################################### + +BLEBroadcast LITERAL1 +BLERead LITERAL1 +BLEWriteWithoutResponse LITERAL1 +BLEWrite LITERAL1 +BLENotify LITERAL1 +BLEIndicate LITERAL1 + +BLESubscribed LITERAL1 +BLEUnsubscribed LITERAL1 +BLEWritten LITERAL1 + diff --git a/library.properties b/library.properties new file mode 100644 index 00000000..84b7eb70 --- /dev/null +++ b/library.properties @@ -0,0 +1,10 @@ +name=ArduinoBLE +version=0.0.0 +author=Arduino +maintainer=Arduino +sentence=[BETA] Enables BLE connectivity on the Arduino MKR WiFi 1010 and Arduino UNO WiFi Rev.2. +paragraph=This library currently supports creating a BLE peripheral. +category=Communication +url=https://github.com/arduino-libraries/ArduinoBLE +architectures=samd,megaavr +includes=ArduinoBLE.h diff --git a/src/ArduinoBLE.h b/src/ArduinoBLE.h new file mode 100644 index 00000000..f6b8dfb4 --- /dev/null +++ b/src/ArduinoBLE.h @@ -0,0 +1,27 @@ +/* + This file is part of the ArduinoBLE library. + Copyright (c) 2018 Arduino SA. All rights reserved. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#ifndef _ARDUINO_BLE_H_ +#define _ARDUINO_BLE_H_ + +#include "local/BLELocalDevice.h" +#include "BLEProperty.h" +#include "BLETypedCharacteristics.h" + +#endif diff --git a/src/BLEAttribute.cpp b/src/BLEAttribute.cpp new file mode 100644 index 00000000..efe4d49b --- /dev/null +++ b/src/BLEAttribute.cpp @@ -0,0 +1,64 @@ +/* + This file is part of the ArduinoBLE library. + Copyright (c) 2018 Arduino SA. All rights reserved. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include "BLEAttribute.h" + +BLEAttribute::BLEAttribute(const char* uuid) : + _uuid(uuid), + _refCount(0) +{ +} + +BLEAttribute::~BLEAttribute() +{ +} + +const char* BLEAttribute::uuid() const +{ + return _uuid.str(); +} + +const uint8_t* BLEAttribute::uuidData() const +{ + return _uuid.data(); +} + +uint8_t BLEAttribute::uuidLength() const +{ + return _uuid.length(); +} + +enum BLEAttributeType BLEAttribute::type() const +{ + return BLETypeUnknown; +} + +int BLEAttribute::retain() +{ + _refCount++; + + return _refCount; +} + +int BLEAttribute::release() +{ + _refCount--; + + return _refCount; +} diff --git a/src/BLEAttribute.h b/src/BLEAttribute.h new file mode 100644 index 00000000..670c3a06 --- /dev/null +++ b/src/BLEAttribute.h @@ -0,0 +1,53 @@ +/* + This file is part of the ArduinoBLE library. + Copyright (c) 2018 Arduino SA. All rights reserved. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#ifndef _BLE_ATTRIBUTE_H_ +#define _BLE_ATTRIBUTE_H_ + +#include "BLEUuid.h" + +enum BLEAttributeType { + BLETypeUnknown = 0x0000, + + BLETypeService = 0x2800, + BLETypeCharacteristic = 0x2803, + BLETypeDescriptor = 0x2900 +}; + +class BLEAttribute +{ +public: + BLEAttribute(const char* uuid); + virtual ~BLEAttribute(); + + const char* uuid() const; + const uint8_t* uuidData() const; + uint8_t uuidLength() const; + + virtual enum BLEAttributeType type() const; + + int retain(); + int release(); + +private: + BLEUuid _uuid; + int _refCount; +}; + +#endif diff --git a/src/BLECharacteristic.cpp b/src/BLECharacteristic.cpp new file mode 100644 index 00000000..7ff92430 --- /dev/null +++ b/src/BLECharacteristic.cpp @@ -0,0 +1,166 @@ +/* + This file is part of the ArduinoBLE library. + Copyright (c) 2018 Arduino SA. All rights reserved. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include "local/BLELocalCharacteristic.h" + +#include "BLECharacteristic.h" + +BLECharacteristic::BLECharacteristic() : + BLECharacteristic(NULL) +{ +} + +BLECharacteristic::BLECharacteristic(BLELocalCharacteristic* local) : + _local(local) +{ + if (_local) { + _local->retain(); + } +} + +BLECharacteristic::BLECharacteristic(const char* uuid, uint8_t properties, int valueSize, bool fixedLength) : + BLECharacteristic(new BLELocalCharacteristic(uuid, properties, valueSize, fixedLength)) +{ +} + +BLECharacteristic::BLECharacteristic(const char* uuid, uint8_t properties, const char* value) : + BLECharacteristic(new BLELocalCharacteristic(uuid, properties, value)) +{ +} + +BLECharacteristic::~BLECharacteristic() +{ + if (_local && _local->release() <= 0) { + delete _local; + } +} + +uint8_t BLECharacteristic::properties() const +{ + if (_local) { + return _local->properties(); + } + + return 0; +} + +int BLECharacteristic::valueSize() const +{ + if (_local) { + return _local->valueSize(); + } + + return 0; +} + +const uint8_t* BLECharacteristic::value() const +{ + if (_local) { + return _local->value(); + } + + return NULL; +} + +int BLECharacteristic::valueLength() const +{ + if (_local) { + return _local->valueLength(); + } + + return 0; +} + +uint8_t BLECharacteristic::operator[] (int offset) const +{ + if (_local) { + return (*_local)[offset]; + } + + return 0; +} + +int BLECharacteristic::writeValue(const uint8_t value[], int length) +{ + if (_local) { + return _local->writeValue(value, length); + } + + return 0; +} + +int BLECharacteristic::writeValue(const char* value) +{ + if (_local) { + return _local->writeValue(value); + } + + return 0; +} + +int BLECharacteristic::broadcast() +{ + if (_local) { + return _local->broadcast(); + } + + return 0; +} + +bool BLECharacteristic::written() +{ + if (_local) { + return _local->written(); + } + + return false; +} + +bool BLECharacteristic::subscribed() +{ + if (_local) { + return _local->subscribed(); + } + + return false; +} + +void BLECharacteristic::addDescriptor(BLEDescriptor& descriptor) +{ + if (_local) { + return _local->addDescriptor(descriptor); + } +} + +BLECharacteristic::operator bool() const +{ + return (_local != NULL); +} + +BLELocalCharacteristic* BLECharacteristic::local() +{ + return _local; +} + +void BLECharacteristic::setEventHandler(int event, BLECharacteristicEventHandler eventHandler) +{ + if (_local) { + _local->setEventHandler((BLECharacteristicEvent)event, eventHandler); + } +} diff --git a/src/BLECharacteristic.h b/src/BLECharacteristic.h new file mode 100644 index 00000000..9513c1d0 --- /dev/null +++ b/src/BLECharacteristic.h @@ -0,0 +1,86 @@ +/* + This file is part of the ArduinoBLE library. + Copyright (c) 2018 Arduino SA. All rights reserved. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#ifndef _BLE_CHARACTERISTIC_H_ +#define _BLE_CHARACTERISTIC_H_ + +#include + +#include "BLEDescriptor.h" + +enum BLECharacteristicEvent { + BLESubscribed = 0, + BLEUnsubscribed = 1, +//BLERead = 2, // defined in BLEProperties.h + BLEWritten = 3, + + BLECharacteristicEventLast +}; + +class BLECharacteristic; +class BLEDevice; + +typedef void (*BLECharacteristicEventHandler)(BLEDevice device, BLECharacteristic characteristic); + +class BLELocalCharacteristic; + +class BLECharacteristic { +public: + BLECharacteristic(); + BLECharacteristic(const char* uuid, uint8_t properties, int valueSize, bool fixedLength = false); + BLECharacteristic(const char* uuid, uint8_t properties, const char* value); + virtual ~BLECharacteristic(); + + uint8_t properties() const; + + int valueSize() const; + const uint8_t* value() const; + int valueLength() const; + uint8_t operator[] (int offset) const; + + int writeValue(const uint8_t value[], int length); + int writeValue(const char* value); + + int setValue(const uint8_t value[], int length) { return writeValue(value, length); } + int setValue(const char* value) { return writeValue(value); } + + int broadcast(); + + bool written(); + bool subscribed(); + + void addDescriptor(BLEDescriptor& descriptor); + + operator bool() const; + + void setEventHandler(int event, BLECharacteristicEventHandler eventHandler); + +protected: + friend class BLELocalCharacteristic; + friend class BLELocalService; + + BLECharacteristic(BLELocalCharacteristic* local); + + BLELocalCharacteristic* local(); + +private: + BLELocalCharacteristic* _local; +}; + +#endif diff --git a/src/BLEDescriptor.cpp b/src/BLEDescriptor.cpp new file mode 100644 index 00000000..3f12fd0f --- /dev/null +++ b/src/BLEDescriptor.cpp @@ -0,0 +1,86 @@ +/* + This file is part of the ArduinoBLE library. + Copyright (c) 2018 Arduino SA. All rights reserved. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include + +#include "local/BLELocalDescriptor.h" + +#include "BLEDescriptor.h" + +BLEDescriptor::BLEDescriptor() : + BLEDescriptor(NULL) +{ +} + +BLEDescriptor::BLEDescriptor(BLELocalDescriptor* local) : + _local(local) +{ + if (_local) { + _local->retain(); + } +} + +BLEDescriptor::BLEDescriptor(const char* uuid, const uint8_t value[], int valueSize) : + BLEDescriptor(new BLELocalDescriptor(uuid, value, valueSize)) +{ +} + +BLEDescriptor::BLEDescriptor(const char* uuid, const char* value) : + BLEDescriptor(new BLELocalDescriptor(uuid, value)) +{ +} + +BLEDescriptor::~BLEDescriptor() +{ + if (_local && _local->release() <= 0) { + delete _local; + } +} + +int BLEDescriptor::valueSize() const +{ + if (_local) { + return _local->valueSize(); + } + + return 0; +} + +const uint8_t* BLEDescriptor::value() const +{ + if (_local) { + return _local->value(); + } + + return NULL; +} + +uint8_t BLEDescriptor::operator[] (int offset) const +{ + if (_local) { + return (*_local)[offset]; + } + + return 0; +} + +BLELocalDescriptor* BLEDescriptor::local() +{ + return _local; +} diff --git a/src/BLEDescriptor.h b/src/BLEDescriptor.h new file mode 100644 index 00000000..01ae55b6 --- /dev/null +++ b/src/BLEDescriptor.h @@ -0,0 +1,51 @@ +/* + This file is part of the ArduinoBLE library. + Copyright (c) 2018 Arduino SA. All rights reserved. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#ifndef _BLE_DESCRIPTOR_H_ +#define _BLE_DESCRIPTOR_H_ + +#include + +class BLELocalDescriptor; + +class BLEDescriptor { +public: + BLEDescriptor(); + BLEDescriptor(const char* uuid, const uint8_t value[], int valueSize); + BLEDescriptor(const char* uuid, const char* value); + virtual ~BLEDescriptor(); + + int valueSize() const; + const uint8_t* value() const; + uint8_t operator[] (int offset) const; + + operator bool() const; + +protected: + friend class BLELocalCharacteristic; + + BLEDescriptor(BLELocalDescriptor* local); + + BLELocalDescriptor* local(); + +private: + BLELocalDescriptor* _local; +}; + +#endif diff --git a/src/BLEDevice.cpp b/src/BLEDevice.cpp new file mode 100644 index 00000000..01118060 --- /dev/null +++ b/src/BLEDevice.cpp @@ -0,0 +1,103 @@ +/* + This file is part of the ArduinoBLE library. + Copyright (c) 2018 Arduino SA. All rights reserved. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include "utility/ATT.h" +#include "utility/HCI.h" + +#include "BLEDevice.h" + +BLEDevice::BLEDevice() : + _handle(0xffff) +{ + memset(_address, 0x00, sizeof(_address)); +} + +BLEDevice::BLEDevice(uint16_t handle, uint8_t address[6]) : + _handle(handle) +{ + memcpy(_address, address, sizeof(_address)); +} + +BLEDevice::~BLEDevice() +{ +} + +void BLEDevice::poll() +{ + HCI.poll(); +} + +void BLEDevice::poll(unsigned long timeout) +{ + HCI.poll(timeout); +} + +bool BLEDevice::connected() const +{ + HCI.poll(); + + if (!(*this)) { + return false; + } + + return ATT.connected(_handle, _address); +} + +bool BLEDevice::disconnect() +{ + if (_handle != 0xffff) { + return HCI.disconnect(_handle); + } + + return false; +} + +String BLEDevice::address() const +{ + char result[18]; + sprintf(result, "%02x:%02x:%02x:%02x:%02x:%02x", _address[5], _address[4], _address[3], _address[2], _address[1], _address[0]); + + return result; +} + +int BLEDevice::rssi() +{ + if (_handle != 0xffff) { + return HCI.readRssi(_handle); + } + + return 127; +} + +BLEDevice::operator bool() const +{ + uint8_t zeros[6] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,}; + + return ((_handle != 0xffff) && memcmp(_address, zeros, sizeof(zeros)) != 0); +} + +bool BLEDevice::operator==(const BLEDevice& rhs) const +{ + return ((_handle == rhs._handle) && memcmp(_address, rhs._address, sizeof(_address)) == 0); +} + +bool BLEDevice::operator!=(const BLEDevice& rhs) const +{ + return ((_handle != rhs._handle) || memcmp(_address, rhs._address, sizeof(_address)) != 0); +} diff --git a/src/BLEDevice.h b/src/BLEDevice.h new file mode 100644 index 00000000..7675b195 --- /dev/null +++ b/src/BLEDevice.h @@ -0,0 +1,65 @@ +/* + This file is part of the ArduinoBLE library. + Copyright (c) 2018 Arduino SA. All rights reserved. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#ifndef _BLE_DEVICE_H_ +#define _BLE_DEVICE_H_ + +#include + +enum BLEDeviceEvent { + BLEConnected = 0, + BLEDisconnected = 1, + + BLEDeviceLastEvent +}; + +class BLEDevice; + +typedef void (*BLEDeviceEventHandler)(BLEDevice device); + +class BLEDevice { +public: + BLEDevice(); + virtual ~BLEDevice(); + + virtual void poll(); + virtual void poll(unsigned long timeout); + + virtual bool connected() const; + virtual bool disconnect(); + + virtual String address() const; + + virtual int rssi(); + + virtual operator bool() const; + virtual bool operator==(const BLEDevice& rhs) const; + virtual bool operator!=(const BLEDevice& rhs) const; + +protected: + friend class ATTClass; + + BLEDevice(uint16_t handle, uint8_t address[6]); + +private: + uint16_t _handle; + uint8_t _address[6]; +}; + +#endif diff --git a/src/BLEProperty.h b/src/BLEProperty.h new file mode 100644 index 00000000..6cdd888f --- /dev/null +++ b/src/BLEProperty.h @@ -0,0 +1,32 @@ +/* + This file is part of the ArduinoBLE library. + Copyright (c) 2018 Arduino SA. All rights reserved. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#ifndef _BLE_PROPERTY_H_ +#define _BLE_PROPERTY_H_ + +enum BLEProperty { + BLEBroadcast = 0x01, + BLERead = 0x02, + BLEWriteWithoutResponse = 0x04, + BLEWrite = 0x08, + BLENotify = 0x10, + BLEIndicate = 0x20 +}; + +#endif diff --git a/src/BLEService.cpp b/src/BLEService.cpp new file mode 100644 index 00000000..67d0d1af --- /dev/null +++ b/src/BLEService.cpp @@ -0,0 +1,68 @@ +/* + This file is part of the ArduinoBLE library. + Copyright (c) 2018 Arduino SA. All rights reserved. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include "local/BLELocalService.h" + +#include "BLEService.h" + +BLEService::BLEService() : + BLEService((BLELocalService*)NULL) +{ +} + +BLEService::BLEService(BLELocalService* local) : + _local(local) +{ + if (_local) { + _local->retain(); + } +} + +BLEService::BLEService(const char* uuid) : + BLEService(new BLELocalService(uuid)) +{ +} + +BLEService::~BLEService() +{ + if (_local && _local->release() <= 0) { + delete _local; + } +} + +const char* BLEService::uuid() const +{ + if (_local) { + return _local->uuid(); + } + + return ""; +} + +void BLEService::addCharacteristic(BLECharacteristic& characteristic) +{ + if (_local) { + _local->addCharacteristic(characteristic); + } +} + +BLELocalService* BLEService::local() +{ + return _local; +} diff --git a/src/BLEService.h b/src/BLEService.h new file mode 100644 index 00000000..ef8e4234 --- /dev/null +++ b/src/BLEService.h @@ -0,0 +1,52 @@ +/* + This file is part of the ArduinoBLE library. + Copyright (c) 2018 Arduino SA. All rights reserved. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#ifndef _BLE_SERVICE_H_ +#define _BLE_SERVICE_H_ + +#include "BLECharacteristic.h" + +class BLELocalService; + +class BLEService { +public: + BLEService(); + BLEService(const char* uuid); + virtual ~BLEService(); + + const char* uuid() const; + + void addCharacteristic(BLECharacteristic& characteristic); + + operator bool() const; + +protected: + friend class GATTClass; + + BLEService(BLELocalService* local); + + BLELocalService* local(); + + void addCharacteristic(BLELocalCharacteristic* characteristic); + +private: + BLELocalService* _local; +}; + +#endif diff --git a/src/BLETypedCharacteristic.h b/src/BLETypedCharacteristic.h new file mode 100644 index 00000000..d6e6e4a1 --- /dev/null +++ b/src/BLETypedCharacteristic.h @@ -0,0 +1,102 @@ +/* + This file is part of the ArduinoBLE library. + Copyright (c) 2018 Arduino SA. All rights reserved. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#ifndef _BLE_TYPED_CHARACTERISTIC_H_ +#define _BLE_TYPED_CHARACTERISTIC_H_ + +#include "BLECharacteristic.h" + +template class BLETypedCharacteristic : public BLECharacteristic +{ +public: + BLETypedCharacteristic(const char* uuid, unsigned char properties); + + int writeValue(T value); + int setValue(T value) { return writeValue(value); } + T value(void); + + int writeValueLE(T value); + int setValueLE(T value) { return writeValueLE(value); } + T valueLE(void); + + int writeValueBE(T value); + int setValueBE(T value) { return writeValueBE(value); } + T valueBE(void); + +private: + T byteSwap(T value); +}; + +template BLETypedCharacteristic::BLETypedCharacteristic(const char* uuid, unsigned char properties) : + BLECharacteristic(uuid, properties, sizeof(T), true) +{ + T value; + memset(&value, 0x00, sizeof(value)); + + writeValue(value); +} + +template int BLETypedCharacteristic::writeValue(T value) +{ + return BLECharacteristic::writeValue((uint8_t*)&value, sizeof(T)); +} + +template T BLETypedCharacteristic::value() +{ + T value; + + memcpy(&value, (unsigned char*)BLECharacteristic::value(), BLECharacteristic::valueSize()); + + return value; +} + +template int BLETypedCharacteristic::writeValueLE(T value) +{ + return writeValue(value); +} + +template T BLETypedCharacteristic::valueLE() +{ + return value(); +} + +template int BLETypedCharacteristic::writeValueBE(T value) +{ + return writeValue(byteSwap(value)); +} + +template T BLETypedCharacteristic::valueBE() +{ + return byteSwap(value()); +} + +template T BLETypedCharacteristic::byteSwap(T value) +{ + T result; + unsigned char* src = (unsigned char*)&value; + unsigned char* dst = (unsigned char*)&result; + + for (int i = 0; i < sizeof(T); i++) { + dst[i] = src[sizeof(T) - i - 1]; + } + + return result; +} + +#endif diff --git a/src/BLETypedCharacteristics.cpp b/src/BLETypedCharacteristics.cpp new file mode 100644 index 00000000..976f6159 --- /dev/null +++ b/src/BLETypedCharacteristics.cpp @@ -0,0 +1,92 @@ +/* + This file is part of the ArduinoBLE library. + Copyright (c) 2018 Arduino SA. All rights reserved. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include + +#include "BLETypedCharacteristics.h" + +BLEBoolCharacteristic::BLEBoolCharacteristic(const char* uuid, unsigned char properties) : + BLETypedCharacteristic(uuid, properties) +{ +} + +BLEBooleanCharacteristic::BLEBooleanCharacteristic(const char* uuid, unsigned char properties) : + BLETypedCharacteristic(uuid, properties) +{ +} + +BLECharCharacteristic::BLECharCharacteristic(const char* uuid, unsigned char properties) : + BLETypedCharacteristic(uuid, properties) +{ +} + +BLEUnsignedCharCharacteristic::BLEUnsignedCharCharacteristic(const char* uuid, unsigned char properties) : + BLETypedCharacteristic(uuid, properties) +{ +} + +BLEByteCharacteristic::BLEByteCharacteristic(const char* uuid, unsigned char properties) : + BLETypedCharacteristic(uuid, properties) +{ +} + +BLEShortCharacteristic::BLEShortCharacteristic(const char* uuid, unsigned char properties) : + BLETypedCharacteristic(uuid, properties) +{ +} + +BLEUnsignedShortCharacteristic::BLEUnsignedShortCharacteristic(const char* uuid, unsigned char properties) : + BLETypedCharacteristic(uuid, properties) +{ +} + +BLEWordCharacteristic::BLEWordCharacteristic(const char* uuid, unsigned char properties) : + BLETypedCharacteristic(uuid, properties) +{ +} + +BLEIntCharacteristic::BLEIntCharacteristic(const char* uuid, unsigned char properties) : + BLETypedCharacteristic(uuid, properties) +{ +} + +BLEUnsignedIntCharacteristic::BLEUnsignedIntCharacteristic(const char* uuid, unsigned char properties) : + BLETypedCharacteristic(uuid, properties) +{ +} + +BLELongCharacteristic::BLELongCharacteristic(const char* uuid, unsigned char properties) : + BLETypedCharacteristic(uuid, properties) +{ +} + +BLEUnsignedLongCharacteristic::BLEUnsignedLongCharacteristic(const char* uuid, unsigned char properties) : + BLETypedCharacteristic(uuid, properties) +{ +} + +BLEFloatCharacteristic::BLEFloatCharacteristic(const char* uuid, unsigned char properties) : + BLETypedCharacteristic(uuid, properties) +{ +} + +BLEDoubleCharacteristic::BLEDoubleCharacteristic(const char* uuid, unsigned char properties) : + BLETypedCharacteristic(uuid, properties) +{ +} diff --git a/src/BLETypedCharacteristics.h b/src/BLETypedCharacteristics.h new file mode 100644 index 00000000..465fc046 --- /dev/null +++ b/src/BLETypedCharacteristics.h @@ -0,0 +1,95 @@ +/* + This file is part of the ArduinoBLE library. + Copyright (c) 2018 Arduino SA. All rights reserved. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#ifndef _BLE_TYPED_CHARACTERISTICS_H_ +#define _BLE_TYPED_CHARACTERISTICS_H_ + +#include "BLETypedCharacteristic.h" + +class BLEBoolCharacteristic : public BLETypedCharacteristic { +public: + BLEBoolCharacteristic(const char* uuid, unsigned char properties); +}; + +class BLEBooleanCharacteristic : public BLETypedCharacteristic { +public: + BLEBooleanCharacteristic(const char* uuid, unsigned char properties); +}; + +class BLECharCharacteristic : public BLETypedCharacteristic { +public: + BLECharCharacteristic(const char* uuid, unsigned char properties); +}; + +class BLEUnsignedCharCharacteristic : public BLETypedCharacteristic { +public: + BLEUnsignedCharCharacteristic(const char* uuid, unsigned char properties); +}; + +class BLEByteCharacteristic : public BLETypedCharacteristic { +public: + BLEByteCharacteristic(const char* uuid, unsigned char properties); +}; + +class BLEShortCharacteristic : public BLETypedCharacteristic { +public: + BLEShortCharacteristic(const char* uuid, unsigned char properties); +}; + +class BLEUnsignedShortCharacteristic : public BLETypedCharacteristic { +public: + BLEUnsignedShortCharacteristic(const char* uuid, unsigned char properties); +}; + +class BLEWordCharacteristic : public BLETypedCharacteristic { +public: + BLEWordCharacteristic(const char* uuid, unsigned char properties); +}; + +class BLEIntCharacteristic : public BLETypedCharacteristic { +public: + BLEIntCharacteristic(const char* uuid, unsigned char properties); +}; + +class BLEUnsignedIntCharacteristic : public BLETypedCharacteristic { +public: + BLEUnsignedIntCharacteristic(const char* uuid, unsigned char properties); +}; + +class BLELongCharacteristic : public BLETypedCharacteristic { +public: + BLELongCharacteristic(const char* uuid, unsigned char properties); +}; + +class BLEUnsignedLongCharacteristic : public BLETypedCharacteristic { +public: + BLEUnsignedLongCharacteristic(const char* uuid, unsigned char properties); +}; + +class BLEFloatCharacteristic : public BLETypedCharacteristic { +public: + BLEFloatCharacteristic(const char* uuid, unsigned char properties); +}; + +class BLEDoubleCharacteristic : public BLETypedCharacteristic { +public: + BLEDoubleCharacteristic(const char* uuid, unsigned char properties); +}; + +#endif diff --git a/src/BLEUuid.cpp b/src/BLEUuid.cpp new file mode 100644 index 00000000..d1bcfd62 --- /dev/null +++ b/src/BLEUuid.cpp @@ -0,0 +1,67 @@ +/* + This file is part of the ArduinoBLE library. + Copyright (c) 2018 Arduino SA. All rights reserved. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include +#include + +#include "BLEUuid.h" + +BLEUuid::BLEUuid(const char * str) : + _str(str) +{ + char temp[] = {0, 0, 0}; + + memset(_data, 0x00, sizeof(_data)); + + _length = 0; + for (int i = strlen(str) - 1; i >= 0 && _length < BLE_UUID_MAX_LENGTH; i -= 2) { + if (str[i] == '-') { + i++; + continue; + } + + temp[0] = str[i - 1]; + temp[1] = str[i]; + + _data[_length] = strtoul(temp, NULL, 16); + + _length++; + } + + if (_length <= 2) { + _length = 2; + } else { + _length = 16; + } +} + +const char* BLEUuid::str() const +{ + return _str; +} + +const uint8_t* BLEUuid::data() const +{ + return _data; +} + +uint8_t BLEUuid::length() const +{ + return _length; +} diff --git a/src/BLEUuid.h b/src/BLEUuid.h new file mode 100644 index 00000000..d8315711 --- /dev/null +++ b/src/BLEUuid.h @@ -0,0 +1,42 @@ +/* + This file is part of the ArduinoBLE library. + Copyright (c) 2018 Arduino SA. All rights reserved. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#ifndef _BLE_UUID_H_ +#define _BLE_UUID_H_ + +#include + +#define BLE_UUID_MAX_LENGTH 16 + +class BLEUuid +{ +public: + BLEUuid(const char * str); + + const char* str() const; + const uint8_t * data() const; + uint8_t length() const; + +private: + const char* _str; + uint8_t _data[BLE_UUID_MAX_LENGTH]; + uint8_t _length; +}; + +#endif diff --git a/src/local/BLELocalCharacteristic.cpp b/src/local/BLELocalCharacteristic.cpp new file mode 100644 index 00000000..18430b03 --- /dev/null +++ b/src/local/BLELocalCharacteristic.cpp @@ -0,0 +1,241 @@ +/* + This file is part of the ArduinoBLE library. + Copyright (c) 2018 Arduino SA. All rights reserved. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include + +#include "utility/ATT.h" +#include "utility/GAP.h" +#include "utility/GATT.h" + +#include "BLELocalDescriptor.h" +#include "BLEProperty.h" + +#include "BLELocalCharacteristic.h" + +BLELocalCharacteristic::BLELocalCharacteristic(const char* uuid, uint8_t properties, int valueSize, bool fixedLength) : + BLEAttribute(uuid), + _properties(properties), + _valueSize(min(valueSize, 512)), + _valueLength(0), + _fixedLength(fixedLength), + _handle(0x0000), + _broadcast(false), + _written(false), + _cccdValue(0x0000) +{ + memset(_eventHandlers, 0x00, sizeof(_eventHandlers)); + + if (properties & (BLENotify | BLEIndicate)) { + BLELocalDescriptor* cccd = new BLELocalDescriptor("2902", (uint8_t*)&_cccdValue, sizeof(_cccdValue)); + + _descriptors.add(cccd); + } + + _value = (uint8_t*)malloc(valueSize); +} + +BLELocalCharacteristic::BLELocalCharacteristic(const char* uuid, uint8_t properties, const char* value) : + BLELocalCharacteristic(uuid, properties, strlen(value)) +{ + writeValue(value); +} + +BLELocalCharacteristic::~BLELocalCharacteristic() +{ + for (unsigned int i = 0; i < descriptorCount(); i++) { + BLELocalDescriptor* d = descriptor(i); + + if (d->release() <= 0) { + delete d; + } + } + + _descriptors.clear(); + + if (_value) { + free(_value); + } +} + +enum BLEAttributeType BLELocalCharacteristic::type() const +{ + return BLETypeCharacteristic; +} + +uint8_t BLELocalCharacteristic::properties() const +{ + return _properties; +} + +int BLELocalCharacteristic::valueSize() const +{ + return _valueSize; +} + +const uint8_t* BLELocalCharacteristic::value() const +{ + return _value; +} + +int BLELocalCharacteristic::valueLength() const +{ + return _valueLength; +} + +uint8_t BLELocalCharacteristic::operator[] (int offset) const +{ + return _value[offset]; +} + +int BLELocalCharacteristic::writeValue(const uint8_t value[], int length) +{ + _valueLength = min(length, _valueSize); + memcpy(_value, value, _valueLength); + + if (_fixedLength) { + _valueLength = _valueSize; + } + + if ((_properties & BLEIndicate) && (_cccdValue & 0x0002)) { + return ATT.handleInd(valueHandle(), _value, _valueLength); + } else if ((_properties & BLENotify) && (_cccdValue & 0x0001)) { + return ATT.handleNotify(valueHandle(), _value, _valueLength); + } + + if (_broadcast) { + uint16_t serviceUuid = GATT.serviceUuidForCharacteristic(this); + + GAP.setAdvertisedServiceData(serviceUuid, value, length); + + if (!ATT.connected() && GAP.advertising()) { + GAP.advertise(); + } + } + + return 1; +} + +int BLELocalCharacteristic::writeValue(const char* value) +{ + return writeValue((uint8_t*)value, strlen(value)); +} + +int BLELocalCharacteristic::broadcast() +{ + if (_properties & BLEBroadcast) { + _broadcast = true; + + return 1; + } + + return 0; +} + +bool BLELocalCharacteristic::written() +{ + bool written = _written; + + _written = false; + + return written; +} + + +bool BLELocalCharacteristic::subscribed() +{ + return (_cccdValue != 0x0000); +} + +void BLELocalCharacteristic::addDescriptor(BLEDescriptor& descriptor) +{ + BLELocalDescriptor* localDescriptor = descriptor.local(); + + if (localDescriptor) { + localDescriptor->retain(); + + _descriptors.add(localDescriptor); + } +} + +void BLELocalCharacteristic::setEventHandler(BLECharacteristicEvent event, BLECharacteristicEventHandler eventHandler) +{ + if (event < (sizeof(_eventHandlers) / sizeof(_eventHandlers[0]))) { + _eventHandlers[event] = eventHandler; + } +} + +void BLELocalCharacteristic::setHandle(uint16_t handle) +{ + _handle = handle; +} + +uint16_t BLELocalCharacteristic::handle() const +{ + return _handle; +} + +uint16_t BLELocalCharacteristic::valueHandle() const +{ + return (_handle + 1); +} + +unsigned int BLELocalCharacteristic::descriptorCount() const +{ + return _descriptors.size(); +} + +BLELocalDescriptor* BLELocalCharacteristic::descriptor(unsigned int index) const +{ + return _descriptors.get(index); +} + +void BLELocalCharacteristic::readValue(BLEDevice device, uint16_t offset, uint8_t value[], int length) +{ + if (_eventHandlers[BLERead]) { + _eventHandlers[BLERead](device, BLECharacteristic(this)); + } + + memcpy(value, _value + offset, length); +} + +void BLELocalCharacteristic::writeValue(BLEDevice device, const uint8_t value[], int length) +{ + _written = true; + + writeValue(value, length); + + if (_eventHandlers[BLEWritten]) { + _eventHandlers[BLEWritten](device, BLECharacteristic(this)); + } +} + +void BLELocalCharacteristic::writeCccdValue(BLEDevice device, uint16_t value) +{ + value &= 0x0003; + + if (_cccdValue != value) { + _cccdValue = value; + + BLECharacteristicEvent event = (_cccdValue) ? BLESubscribed : BLEUnsubscribed; + + if (_eventHandlers[event]) { + _eventHandlers[event](device, BLECharacteristic(this)); + } + } +} diff --git a/src/local/BLELocalCharacteristic.h b/src/local/BLELocalCharacteristic.h new file mode 100644 index 00000000..418081e5 --- /dev/null +++ b/src/local/BLELocalCharacteristic.h @@ -0,0 +1,93 @@ +/* + This file is part of the ArduinoBLE library. + Copyright (c) 2018 Arduino SA. All rights reserved. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#ifndef _BLE_LOCAL_CHARACTERISTIC_H_ +#define _BLE_LOCAL_CHARACTERISTIC_H_ + +#include + +#include "BLEAttribute.h" +#include "BLECharacteristic.h" +#include "BLEDescriptor.h" + +#include "utility/BLELinkedList.h" + +class BLELocalDescriptor; + +class BLELocalCharacteristic : public BLEAttribute { +public: + BLELocalCharacteristic(const char* uuid, uint8_t properties, int valueSize, bool fixedLength = false); + BLELocalCharacteristic(const char* uuid, uint8_t properties, const char* value); + virtual ~BLELocalCharacteristic(); + + virtual enum BLEAttributeType type() const; + + uint8_t properties() const; + + int valueSize() const; + const uint8_t* value() const; + int valueLength() const; + uint8_t operator[] (int offset) const; + + int writeValue(const uint8_t value[], int length); + int writeValue(const char* value); + + int broadcast(); + + bool written(); + bool subscribed(); + + void addDescriptor(BLEDescriptor& descriptor); + + void setEventHandler(BLECharacteristicEvent event, BLECharacteristicEventHandler eventHandler); + +protected: + friend class ATTClass; + friend class GATTClass; + + void setHandle(uint16_t handle); + uint16_t handle() const; + uint16_t valueHandle() const; + + unsigned int descriptorCount() const; + BLELocalDescriptor* descriptor(unsigned int index) const; + + void readValue(BLEDevice device, uint16_t offset, uint8_t value[], int length); + void writeValue(BLEDevice device, const uint8_t value[], int length); + void writeCccdValue(BLEDevice device, uint16_t value); + +private: + uint8_t _properties; + int _valueSize; + uint8_t* _value; + uint8_t _valueLength; + bool _fixedLength; + + uint16_t _handle; + + bool _broadcast; + bool _written; + + uint16_t _cccdValue; + BLELinkedList _descriptors; + + BLECharacteristicEventHandler _eventHandlers[BLECharacteristicEventLast]; +}; + +#endif diff --git a/src/local/BLELocalDescriptor.cpp b/src/local/BLELocalDescriptor.cpp new file mode 100644 index 00000000..601a8afe --- /dev/null +++ b/src/local/BLELocalDescriptor.cpp @@ -0,0 +1,69 @@ +/* + This file is part of the ArduinoBLE library. + Copyright (c) 2018 Arduino SA. All rights reserved. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include + +#include "BLELocalDescriptor.h" + +BLELocalDescriptor::BLELocalDescriptor(const char* uuid, const uint8_t value[], int valueSize) : + BLEAttribute(uuid), + _value(value), + _valueSize(min(valueSize, 512)), + _handle(0x0000) +{ +} + +BLELocalDescriptor::BLELocalDescriptor(const char* uuid, const char* value) : + BLELocalDescriptor(uuid, (const uint8_t*)value, strlen(value)) +{ +} + +BLELocalDescriptor::~BLELocalDescriptor() +{ +} + +enum BLEAttributeType BLELocalDescriptor::type() const +{ + return BLETypeDescriptor; +} + +int BLELocalDescriptor::valueSize() const +{ + return _valueSize; +} + +const uint8_t* BLELocalDescriptor::value() const +{ + return _value; +} + +uint8_t BLELocalDescriptor::operator[] (int offset) const +{ + return _value[offset]; +} + +void BLELocalDescriptor::setHandle(uint16_t handle) +{ + _handle = handle; +} + +uint16_t BLELocalDescriptor::handle() const +{ + return _handle; +} diff --git a/src/local/BLELocalDescriptor.h b/src/local/BLELocalDescriptor.h new file mode 100644 index 00000000..915a1cd9 --- /dev/null +++ b/src/local/BLELocalDescriptor.h @@ -0,0 +1,52 @@ +/* + This file is part of the ArduinoBLE library. + Copyright (c) 2018 Arduino SA. All rights reserved. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#ifndef _BLE_LOCAL_DESCRIPTOR_H_ +#define _BLE_LOCAL_DESCRIPTOR_H_ + +#include + +#include "BLEAttribute.h" + +class BLELocalDescriptor : public BLEAttribute { +public: + BLELocalDescriptor(const char* uuid, const uint8_t value[], int valueSize); + BLELocalDescriptor(const char* uuid, const char* value); + virtual ~BLELocalDescriptor(); + + virtual enum BLEAttributeType type() const; + + int valueSize() const; + const uint8_t* value() const; + uint8_t operator[] (int offset) const; + +protected: + friend class GATTClass; + + void setHandle(uint16_t handle); + uint16_t handle() const; + +private: + const uint8_t* _value; + int _valueSize; + + uint16_t _handle; +}; + +#endif diff --git a/src/local/BLELocalDevice.cpp b/src/local/BLELocalDevice.cpp new file mode 100644 index 00000000..0545db08 --- /dev/null +++ b/src/local/BLELocalDevice.cpp @@ -0,0 +1,248 @@ +/* + This file is part of the ArduinoBLE library. + Copyright (c) 2018 Arduino SA. All rights reserved. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include "utility/ATT.h" +#include "utility/HCI.h" +#include "utility/GAP.h" +#include "utility/GATT.h" +#include "utility/L2CAPSignaling.h" + +#include "BLELocalDevice.h" + +BLELocalDevice::BLELocalDevice() +{ +} + +BLELocalDevice::~BLELocalDevice() +{ +} + +int BLELocalDevice::begin() +{ +#if defined(ARDUINO_SAMD_MKRWIFI1010) || defined(ARDUINO_AVR_UNO_WIFI_REV2) + // reset the NINA in BLE mode + pinMode(SPIWIFI_SS, OUTPUT); + pinMode(NINA_RESETN, OUTPUT); + + digitalWrite(SPIWIFI_SS, LOW); + + digitalWrite(NINA_RESETN, HIGH); + delay(100); + digitalWrite(NINA_RESETN, LOW); + delay(750); +#endif + +#ifdef ARDUINO_AVR_UNO_WIFI_REV2 + // set SS HIGH + digitalWrite(SPIWIFI_SS, HIGH); + + // set RTS HIGH + pinMode(NINA_RTS, OUTPUT); + digitalWrite(NINA_RTS, HIGH); + + // set CTS as input + pinMode(NINA_CTS, INPUT); +#endif + + if (!HCI.begin()) { + end(); + return 0; + } + + delay(100); + + if (HCI.reset() != 0) { + end(); + + return 0; + } + + uint8_t hciVer; + uint16_t hciRev; + uint8_t lmpVer; + uint16_t manufacturer; + uint16_t lmpSubVer; + + if (HCI.readLocalVersion(hciVer, hciRev, lmpVer, manufacturer, lmpSubVer) != 0) { + end(); + return 0; + } + + if (HCI.setEventMask(0x3FFFFFFFFFFFFFFF) != 0) { + end(); + return 0; + } + + uint16_t pktLen; + uint8_t maxPkt; + + if (HCI.readLeBufferSize(pktLen, maxPkt) != 0) { + end(); + return 0; + } + + GATT.begin(); + + return 1; +} + +void BLELocalDevice::end() +{ + GATT.end(); + + HCI.end(); + +#if defined(ARDUINO_SAMD_MKRWIFI1010) || defined(ARDUINO_AVR_UNO_WIFI_REV2) + // disable the NINA + digitalWrite(NINA_RESETN, HIGH); +#endif +} + +bool BLELocalDevice::connected() const +{ + HCI.poll(); + + return ATT.connected(); +} + +bool BLELocalDevice::disconnect() +{ + return ATT.disconnect(); +} + +String BLELocalDevice::address() const +{ + uint8_t addr[6] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; + HCI.readBdAddr(addr); + + char result[18]; + sprintf(result, "%02x:%02x:%02x:%02x:%02x:%02x", addr[5], addr[4], addr[3], addr[2], addr[1], addr[0]); + + return result; +} + +int BLELocalDevice::rssi() +{ + BLEDevice central = ATT.central(); + + if (central) { + return central.rssi(); + } + + return 127; +} + +void BLELocalDevice::setAdvertisedServiceUuid(const char* advertisedServiceUuid) +{ + GAP.setAdvertisedServiceUuid(advertisedServiceUuid); +} + +void BLELocalDevice::setAdvertisedService(const BLEService& service) +{ + setAdvertisedServiceUuid(service.uuid()); +} + +void BLELocalDevice::setManufacturerData(const uint8_t manufacturerData[], int manufacturerDataLength) +{ + GAP.setManufacturerData(manufacturerData, manufacturerDataLength); +} + +void BLELocalDevice::setLocalName(const char *localName) +{ + GAP.setLocalName(localName); +} + +void BLELocalDevice::setDeviceName(const char* deviceName) +{ + GATT.setDeviceName(deviceName); +} + +void BLELocalDevice::setAppearance(uint16_t appearance) +{ + GATT.setAppearance(appearance); +} + +void BLELocalDevice::addService(BLEService& service) +{ + GATT.addService(service); +} + +int BLELocalDevice::advertise() +{ + return GAP.advertise(); +} + +void BLELocalDevice::stopAdvertise() +{ + GAP.stopAdvertise(); +} + +BLEDevice BLELocalDevice::central() +{ + HCI.poll(); + + return ATT.central(); +} + +void BLELocalDevice::setEventHandler(BLEDeviceEvent event, BLEDeviceEventHandler eventHandler) +{ + ATT.setEventHandler(event, eventHandler); +} + +void BLELocalDevice::setAdvertisingInterval(uint16_t advertisingInterval) +{ + GAP.setAdvertisingInterval(advertisingInterval); +} + +void BLELocalDevice::setConnectionInterval(uint16_t minimumConnectionInterval, uint16_t maximumConnectionInterval) +{ + L2CAPSignaling.setConnectionInterval(minimumConnectionInterval, maximumConnectionInterval); +} + +void BLELocalDevice::setConnectable(bool connectable) +{ + GAP.setConnectable(connectable); +} + +BLELocalDevice::operator bool() const +{ + return true; +} + +bool BLELocalDevice::operator==(const BLEDevice& rhs) const +{ + return (this == &rhs); +} + +bool BLELocalDevice::operator!=(const BLEDevice& rhs) const +{ + return (this != &rhs); +} + +void BLELocalDevice::debug(Stream& stream) +{ + HCI.debug(stream); +} + +void BLELocalDevice::noDebug() +{ + HCI.noDebug(); +} + +BLELocalDevice BLE; diff --git a/src/local/BLELocalDevice.h b/src/local/BLELocalDevice.h new file mode 100644 index 00000000..f7d8684e --- /dev/null +++ b/src/local/BLELocalDevice.h @@ -0,0 +1,75 @@ +/* + This file is part of the ArduinoBLE library. + Copyright (c) 2018 Arduino SA. All rights reserved. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#ifndef _BLE_LOCAL_DEVICE_H_ +#define _BLE_LOCAL_DEVICE_H_ + +#include "BLEDevice.h" +#include "BLEService.h" + +class BLELocalDevice : public BLEDevice { +public: + BLELocalDevice(); + virtual ~BLELocalDevice(); + + int begin(); + void end(); + + virtual bool connected() const; + virtual bool disconnect(); + + virtual String address() const; + + virtual int rssi(); + + void setAdvertisedServiceUuid(const char* advertisedServiceUuid); + void setAdvertisedService(const BLEService& service); + void setManufacturerData(const uint8_t manufacturerData[], int manufacturerDataLength); + void setLocalName(const char *localName); + + void setDeviceName(const char* deviceName); + void setAppearance(uint16_t appearance); + + void addService(BLEService& service); + + int advertise(); + void stopAdvertise(); + + BLEDevice central(); + + void setEventHandler(BLEDeviceEvent event, BLEDeviceEventHandler eventHandler); + + void setAdvertisingInterval(uint16_t advertisingInterval); + void setConnectionInterval(uint16_t minimumConnectionInterval, uint16_t maximumConnectionInterval); + void setConnectable(bool connectable); + + virtual operator bool() const; + virtual bool operator==(const BLEDevice& rhs) const; + virtual bool operator!=(const BLEDevice& rhs) const; + + void debug(Stream& stream); + void noDebug(); + +private: + +}; + +extern BLELocalDevice BLE; + +#endif diff --git a/src/local/BLELocalService.cpp b/src/local/BLELocalService.cpp new file mode 100644 index 00000000..a856c73d --- /dev/null +++ b/src/local/BLELocalService.cpp @@ -0,0 +1,89 @@ +/* + This file is part of the ArduinoBLE library. + Copyright (c) 2018 Arduino SA. All rights reserved. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include "BLELocalCharacteristic.h" + +#include "BLELocalService.h" + +BLELocalService::BLELocalService(const char* uuid) : + BLEAttribute(uuid), + _startHandle(0x0000), + _endHandle(0x0000) +{ +} + +BLELocalService::~BLELocalService() +{ + for (unsigned int i = 0; i < characteristicCount(); i++) { + BLELocalCharacteristic* c = characteristic(i); + + if (c->release() <= 0) { + delete c; + } + } + + _characteristics.clear(); +} + +enum BLEAttributeType BLELocalService::type() const +{ + return BLETypeService; +} + +void BLELocalService::addCharacteristic(BLECharacteristic& characteristic) +{ + BLELocalCharacteristic* localCharacteristic = characteristic.local(); + + if (localCharacteristic) { + addCharacteristic(localCharacteristic); + } +} + +void BLELocalService::setHandles(uint16_t start, uint16_t end) +{ + _startHandle = start; + _endHandle = end; +} + +uint16_t BLELocalService::startHandle() const +{ + return _startHandle; +} + +uint16_t BLELocalService::endHandle() const +{ + return _endHandle; +} + +unsigned int BLELocalService::characteristicCount() const +{ + return _characteristics.size(); +} + +BLELocalCharacteristic* BLELocalService::characteristic(unsigned int index) const +{ + return _characteristics.get(index); +} + +void BLELocalService::addCharacteristic(BLELocalCharacteristic* characteristic) +{ + characteristic->retain(); + + _characteristics.add(characteristic); +} diff --git a/src/local/BLELocalService.h b/src/local/BLELocalService.h new file mode 100644 index 00000000..184a1e01 --- /dev/null +++ b/src/local/BLELocalService.h @@ -0,0 +1,59 @@ +/* + This file is part of the ArduinoBLE library. + Copyright (c) 2018 Arduino SA. All rights reserved. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#ifndef _BLE_LOCAL_SERVICE_H_ +#define _BLE_LOCAL_SERVICE_H_ + +#include "BLEAttribute.h" +#include "BLECharacteristic.h" + +#include "utility/BLELinkedList.h" + +class BLELocalCharacteristic; + +class BLELocalService : public BLEAttribute { +public: + BLELocalService(const char* uuid); + virtual ~BLELocalService(); + + virtual enum BLEAttributeType type() const; + + void addCharacteristic(BLECharacteristic& characteristic); + +protected: + friend class ATTClass; + friend class GATTClass; + + void setHandles(uint16_t start, uint16_t end); + uint16_t startHandle() const; + uint16_t endHandle() const; + + unsigned int characteristicCount() const; + BLELocalCharacteristic* characteristic(unsigned int index) const; + + void addCharacteristic(BLELocalCharacteristic* characteristic); + +private: + uint16_t _startHandle; + uint16_t _endHandle; + + BLELinkedList _characteristics; +}; + +#endif diff --git a/src/utility/ATT.cpp b/src/utility/ATT.cpp new file mode 100644 index 00000000..bddd9c69 --- /dev/null +++ b/src/utility/ATT.cpp @@ -0,0 +1,923 @@ +/* + This file is part of the ArduinoBLE library. + Copyright (c) 2018 Arduino SA. All rights reserved. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include + +#include "HCI.h" +#include "GATT.h" + +#include "local/BLELocalCharacteristic.h" +#include "local/BLELocalDescriptor.h" +#include "local/BLELocalService.h" + +#include "BLEProperty.h" + +#include "ATT.h" + +#define ATT_OP_ERROR 0x01 +#define ATT_OP_MTU_REQ 0x02 +#define ATT_OP_MTU_RESP 0x03 +#define ATT_OP_FIND_INFO_REQ 0x04 +#define ATT_OP_FIND_INFO_RESP 0x05 +#define ATT_OP_FIND_BY_TYPE_REQ 0x06 +#define ATT_OP_FIND_BY_TYPE_RESP 0x07 +#define ATT_OP_READ_BY_TYPE_REQ 0x08 +#define ATT_OP_READ_BY_TYPE_RESP 0x09 +#define ATT_OP_READ_REQ 0x0a +#define ATT_OP_READ_RESP 0x0b +#define ATT_OP_READ_BLOB_REQ 0x0c +#define ATT_OP_READ_BLOB_RESP 0x0d +#define ATT_OP_READ_MULTI_REQ 0x0e +#define ATT_OP_READ_MULTI_RESP 0x0f +#define ATT_OP_READ_BY_GROUP_REQ 0x10 +#define ATT_OP_READ_BY_GROUP_RESP 0x11 +#define ATT_OP_WRITE_REQ 0x12 +#define ATT_OP_WRITE_RESP 0x13 +#define ATT_OP_WRITE_CMD 0x52 +#define ATT_OP_PREP_WRITE_REQ 0x16 +#define ATT_OP_PREP_WRITE_RESP 0x17 +#define ATT_OP_EXEC_WRITE_REQ 0x18 +#define ATT_OP_EXEC_WRITE_RESP 0x19 +#define ATT_OP_HANDLE_NOTIFY 0x1b +#define ATT_OP_HANDLE_IND 0x1d +#define ATT_OP_HANDLE_CNF 0x1e +#define ATT_OP_SIGNED_WRITE_CMD 0xd2 + +#define ATT_ECODE_INVALID_HANDLE 0x01 +#define ATT_ECODE_READ_NOT_PERM 0x02 +#define ATT_ECODE_WRITE_NOT_PERM 0x03 +#define ATT_ECODE_INVALID_PDU 0x04 +#define ATT_ECODE_AUTHENTICATION 0x05 +#define ATT_ECODE_REQ_NOT_SUPP 0x06 +#define ATT_ECODE_INVALID_OFFSET 0x07 +#define ATT_ECODE_AUTHORIZATION 0x08 +#define ATT_ECODE_PREP_QUEUE_FULL 0x09 +#define ATT_ECODE_ATTR_NOT_FOUND 0x0a +#define ATT_ECODE_ATTR_NOT_LONG 0x0b +#define ATT_ECODE_INSUFF_ENCR_KEY_SIZE 0x0c +#define ATT_ECODE_INVAL_ATTR_VALUE_LEN 0x0d +#define ATT_ECODE_UNLIKELY 0x0e +#define ATT_ECODE_INSUFF_ENC 0x0f +#define ATT_ECODE_UNSUPP_GRP_TYPE 0x10 +#define ATT_ECODE_INSUFF_RESOURCES 0x11 + +ATTClass::ATTClass() : + _maxMtu(23), + _connectionHandle(0xffff), + _mtu(23), + _longWriteHandle(0x0000), + _longWriteValue(NULL), + _longWriteValueLength(0) +{ + memset(_peerAddress, 0x00, sizeof(_peerAddress)); + memset(_eventHandlers, 0x00, sizeof(_eventHandlers)); +} + +ATTClass::~ATTClass() +{ + if (_longWriteValue) { + free(_longWriteValue); + } +} + +void ATTClass::setMaxMtu(uint16_t maxMtu) +{ + _maxMtu = maxMtu; +} + +void ATTClass::addConnection(uint16_t handle, uint8_t role, uint8_t /*peerBdaddrType*/, + uint8_t peerBdaddr[6], uint16_t /*interval*/, + uint16_t /*latency*/, uint16_t /*supervisionTimeout*/, + uint8_t /*masterClockAccuracy*/) +{ + if (role == 1) { + _connectionHandle = handle; + _mtu = 23; + memcpy(_peerAddress, peerBdaddr, sizeof(_peerAddress)); + + if (_eventHandlers[BLEConnected]) { + _eventHandlers[BLEConnected](BLEDevice(_connectionHandle, _peerAddress)); + } + } +} + +void ATTClass::handleData(uint16_t connectionHandle, uint8_t dlen, uint8_t data[]) +{ + uint8_t opcode = data[0]; + + dlen--; + data++; + + switch (opcode) { + case ATT_OP_MTU_REQ: + mtuReq(connectionHandle, dlen, data); + break; + + case ATT_OP_FIND_INFO_REQ: + findInfoReq(connectionHandle, dlen, data); + break; + + case ATT_OP_FIND_BY_TYPE_REQ: + findByTypeReq(connectionHandle, dlen, data); + break; + + case ATT_OP_READ_BY_TYPE_REQ: + readByTypeReq(connectionHandle, dlen, data); + break; + + case ATT_OP_READ_BY_GROUP_REQ: + readByGroupReq(connectionHandle, dlen, data); + break; + + case ATT_OP_READ_REQ: + case ATT_OP_READ_BLOB_REQ: + readOrReadBlobReq(connectionHandle, opcode, dlen, data); + break; + + case ATT_OP_WRITE_REQ: + case ATT_OP_WRITE_CMD: + writeReqOrCmd(connectionHandle, opcode, dlen, data); + break; + + case ATT_OP_PREP_WRITE_REQ: + prepWriteReq(connectionHandle, dlen, data); + break; + + case ATT_OP_EXEC_WRITE_REQ: + execWriteReq(connectionHandle, dlen, data); + break; + + case ATT_OP_HANDLE_CNF: + handleCnf(connectionHandle, dlen, data); + break; + + case ATT_OP_READ_MULTI_REQ: + case ATT_OP_SIGNED_WRITE_CMD: + default: + sendError(connectionHandle, opcode, 0x00, ATT_ECODE_REQ_NOT_SUPP); + break; + } +} + +void ATTClass::removeConnection(uint8_t handle, uint16_t /*reason*/) +{ + if (_connectionHandle == handle) { + BLEDevice bleDevice(_connectionHandle, _peerAddress); + + // clear CCCD values on disconnect + for (uint16_t i = 0; i < GATT.attributeCount(); i++) { + BLEAttribute* attribute = GATT.attribute(i); + + if (attribute->type() == BLETypeCharacteristic) { + BLELocalCharacteristic* characteristic = (BLELocalCharacteristic*)attribute; + + characteristic->writeCccdValue(bleDevice, 0x0000); + } + } + + if (_eventHandlers[BLEDisconnected]) { + _eventHandlers[BLEDisconnected](bleDevice); + } + + _connectionHandle = 0xffff; + memset(_peerAddress, 0x00, sizeof(_peerAddress)); + _longWriteHandle = 0x0000; + _longWriteValueLength = 0; + } +} + +bool ATTClass::connected() const +{ + return (_connectionHandle != 0xffff); +} + +bool ATTClass::connected(uint16_t handle, const uint8_t address[6]) const +{ + return ((_connectionHandle == handle) && memcmp(_peerAddress, address, 6) == 0); +} + +bool ATTClass::disconnect() +{ + if (_connectionHandle != 0xffff) { + if (HCI.disconnect(_connectionHandle) != 0) { + return false; + } + + _connectionHandle = 0xffff; + memset(_peerAddress, 0x00, sizeof(_peerAddress)); + return true; + } + + return false; +} + +BLEDevice ATTClass::central() +{ + if (connected()) { + return BLEDevice(_connectionHandle, _peerAddress); + } + + return BLEDevice(); +} + +bool ATTClass::handleNotify(uint16_t handle, const uint8_t* value, int length) +{ + if (_connectionHandle != 0xffff) { + uint8_t notication[_mtu]; + uint16_t noticationLength = 0; + + notication[0] = ATT_OP_HANDLE_NOTIFY; + noticationLength++; + + memcpy(¬ication[1], &handle, sizeof(handle)); + noticationLength += sizeof(handle); + + length = min((uint16_t)(_mtu - noticationLength), (uint16_t)length); + memcpy(¬ication[noticationLength], value, length); + noticationLength += length; + + HCI.sendAclPkt(_connectionHandle, ATT_CID, noticationLength, notication); + + return true; + } + + return false; +} + +bool ATTClass::handleInd(uint16_t handle, const uint8_t* value, int length) +{ + if (_connectionHandle != 0xffff) { + uint8_t indication[_mtu]; + uint16_t indicationLength = 0; + + indication[0] = ATT_OP_HANDLE_IND; + indicationLength++; + + memcpy(&indication[1], &handle, sizeof(handle)); + indicationLength += sizeof(handle); + + length = min((uint16_t)(_mtu - indicationLength), (uint16_t)length); + memcpy(&indication[indicationLength], value, length); + indicationLength += length; + + _cnf = false; + + HCI.sendAclPkt(_connectionHandle, ATT_CID, indicationLength, indication); + + while (!_cnf) { + HCI.poll(); + + if (!connected()) { + return false; + } + } + + return true; + } + + return false; +} + +void ATTClass::mtuReq(uint16_t connectionHandle, uint8_t dlen, uint8_t data[]) +{ + uint16_t mtu = *(uint16_t*)data; + + if (dlen != 2) { + sendError(connectionHandle, ATT_OP_MTU_REQ, 0x0000, ATT_ECODE_INVALID_PDU); + return; + } + + if (mtu > _maxMtu) { + mtu = _maxMtu; + } + + _mtu = mtu; + + struct __attribute__ ((packed)) { + uint8_t op; + uint16_t mtu; + } mtuResp = { ATT_OP_MTU_RESP, mtu }; + + HCI.sendAclPkt(connectionHandle, ATT_CID, sizeof(mtuResp), &mtuResp); +} + +void ATTClass::findInfoReq(uint16_t connectionHandle, uint8_t dlen, uint8_t data[]) +{ + struct __attribute__ ((packed)) FindInfoReq { + uint16_t startHandle; + uint16_t endHandle; + } *findInfoReq = (FindInfoReq*)data; + + if (dlen != sizeof(FindInfoReq)) { + sendError(connectionHandle, ATT_OP_FIND_INFO_REQ, findInfoReq->startHandle, ATT_ECODE_INVALID_PDU); + return; + } + + uint8_t response[_mtu]; + uint16_t responseLength; + + response[0] = ATT_OP_FIND_INFO_RESP; + response[1] = 0x00; + responseLength = 2; + + for (uint16_t i = (findInfoReq->startHandle - 1); i < GATT.attributeCount() && i <= (findInfoReq->endHandle - 1); i++) { + BLEAttribute* attribute = GATT.attribute(i); + uint16_t handle = (i + 1); + bool isValueHandle = (attribute->type() == BLETypeCharacteristic) && (((BLELocalCharacteristic*)attribute)->valueHandle() == handle); + int uuidLen = isValueHandle ? 2 : attribute->uuidLength(); + int infoType = (uuidLen == 2) ? 0x01 : 0x02; + + if (response[1] == 0) { + response[1] = infoType; + } + + if (response[1] != infoType) { + // different type + break; + } + + // add the handle + memcpy(&response[responseLength], &handle, sizeof(handle)); + responseLength += sizeof(handle); + + if (isValueHandle || attribute->type() == BLETypeDescriptor) { + // add the UUID + memcpy(&response[responseLength], attribute->uuidData(), uuidLen); + responseLength += uuidLen; + } else { + // add the type + uint16_t type = attribute->type(); + + memcpy(&response[responseLength], &type, sizeof(type)); + responseLength += sizeof(type); + } + + if ((responseLength + (2 + uuidLen)) > _mtu) { + break; + } + } + + if (responseLength == 2) { + sendError(connectionHandle, ATT_OP_FIND_INFO_REQ, findInfoReq->startHandle, ATT_ECODE_ATTR_NOT_FOUND); + } else { + HCI.sendAclPkt(connectionHandle, ATT_CID, responseLength, response); + } +} + +void ATTClass::findByTypeReq(uint16_t connectionHandle, uint8_t dlen, uint8_t data[]) +{ + struct __attribute__ ((packed)) FindByTypeReq { + uint16_t startHandle; + uint16_t endHandle; + uint16_t type; + } *findByTypeReq = (FindByTypeReq*)data; + + if (dlen < sizeof(FindByTypeReq)) { + sendError(connectionHandle, ATT_OP_FIND_BY_TYPE_RESP, findByTypeReq->startHandle, ATT_ECODE_INVALID_PDU); + return; + } + + uint16_t valueLength = dlen - sizeof(*findByTypeReq); + uint8_t* value = &data[sizeof(*findByTypeReq)]; + + uint8_t response[_mtu]; + uint16_t responseLength; + + response[0] = ATT_OP_FIND_BY_TYPE_RESP; + responseLength = 1; + + if (findByTypeReq->type == BLETypeService) { + for (uint16_t i = (findByTypeReq->startHandle - 1); i < GATT.attributeCount() && i <= (findByTypeReq->endHandle - 1); i++) { + BLEAttribute* attribute = GATT.attribute(i); + + if ((attribute->type() == findByTypeReq->type) && (attribute->uuidLength() == valueLength) && memcmp(attribute->uuidData(), value, valueLength) == 0) { + BLELocalService* service = (BLELocalService*)attribute; + + // add the start handle + uint16_t startHandle = service->startHandle(); + memcpy(&response[responseLength], &startHandle, sizeof(startHandle)); + responseLength += sizeof(startHandle); + + // add the end handle + uint16_t endHandle = service->endHandle(); + memcpy(&response[responseLength], &endHandle, sizeof(endHandle)); + responseLength += sizeof(endHandle); + } + + if ((responseLength + 4) > _mtu) { + break; + } + } + } + + if (responseLength == 1) { + sendError(connectionHandle, ATT_OP_FIND_BY_TYPE_RESP, findByTypeReq->startHandle, ATT_ECODE_ATTR_NOT_FOUND); + } else { + HCI.sendAclPkt(connectionHandle, ATT_CID, responseLength, response); + } +} + +void ATTClass::readByGroupReq(uint16_t connectionHandle, uint8_t dlen, uint8_t data[]) +{ + struct __attribute__ ((packed)) ReadByGroupReq { + uint16_t startHandle; + uint16_t endHandle; + uint16_t uuid; + } *readByGroupReq = (ReadByGroupReq*)data; + + if (dlen != sizeof(ReadByGroupReq) || (readByGroupReq->uuid != BLETypeService && readByGroupReq->uuid != 0x2801)) { + sendError(connectionHandle, ATT_OP_READ_BY_GROUP_REQ, readByGroupReq->startHandle, ATT_ECODE_UNSUPP_GRP_TYPE); + return; + } + + uint8_t response[_mtu]; + uint16_t responseLength; + + response[0] = ATT_OP_READ_BY_GROUP_RESP; + response[1] = 0x00; + responseLength = 2; + + for (uint16_t i = (readByGroupReq->startHandle - 1); i < GATT.attributeCount() && i <= (readByGroupReq->endHandle - 1); i++) { + BLEAttribute* attribute = GATT.attribute(i); + + if (readByGroupReq->uuid != attribute->type()) { + // not the type + continue; + } + + int uuidLen = attribute->uuidLength(); + int infoSize = (uuidLen == 2) ? 6 : 20; + + if (response[1] == 0) { + response[1] = infoSize; + } + + if (response[1] != infoSize) { + // different size + break; + } + + BLELocalService* service = (BLELocalService*)attribute; + + // add the start handle + uint16_t startHandle = service->startHandle(); + memcpy(&response[responseLength], &startHandle, sizeof(startHandle)); + responseLength += sizeof(startHandle); + + // add the end handle + uint16_t endHandle = service->endHandle(); + memcpy(&response[responseLength], &endHandle, sizeof(endHandle)); + responseLength += sizeof(endHandle); + + // add the UUID + memcpy(&response[responseLength], service->uuidData(), uuidLen); + responseLength += uuidLen; + + if ((responseLength + infoSize) > _mtu) { + break; + } + } + + if (responseLength == 2) { + sendError(connectionHandle, ATT_OP_READ_BY_GROUP_REQ, readByGroupReq->startHandle, ATT_ECODE_ATTR_NOT_FOUND); + } else { + HCI.sendAclPkt(connectionHandle, ATT_CID, responseLength, response); + } +} + +void ATTClass::readOrReadBlobReq(uint16_t connectionHandle, uint8_t opcode, uint8_t dlen, uint8_t data[]) +{ + if (opcode == ATT_OP_READ_REQ) { + if (dlen != sizeof(uint16_t)) { + sendError(connectionHandle, ATT_OP_READ_REQ, 0x0000, ATT_ECODE_INVALID_PDU); + return; + } + } else { + if (dlen != (sizeof(uint16_t) + sizeof(uint16_t))) { + sendError(connectionHandle, ATT_OP_READ_BLOB_REQ, 0x0000, ATT_ECODE_INVALID_PDU); + return; + } + } + + uint16_t handle = *(uint16_t*)data; + uint16_t offset = (opcode == ATT_OP_READ_REQ) ? 0 : *(uint16_t*)&data[sizeof(handle)]; + + if ((uint16_t)(handle - 1) > GATT.attributeCount()) { + sendError(connectionHandle, opcode, handle, ATT_ECODE_ATTR_NOT_FOUND); + return; + } + + uint8_t response[_mtu]; + uint16_t responseLength; + + response[0] = (opcode == ATT_OP_READ_REQ) ? ATT_OP_READ_RESP : ATT_OP_READ_BLOB_RESP; + responseLength = 1; + + BLEAttribute* attribute = GATT.attribute(handle - 1); + enum BLEAttributeType attributeType = attribute->type(); + + if (attributeType == BLETypeService) { + if (offset) { + sendError(connectionHandle, ATT_ECODE_ATTR_NOT_LONG, handle, ATT_ECODE_INVALID_PDU); + return; + } + + BLELocalService* service = (BLELocalService*)attribute; + + // add the UUID + uint8_t uuidLen = service->uuidLength(); + memcpy(&response[responseLength], service->uuidData(), uuidLen); + responseLength += uuidLen; + } else if (attributeType == BLETypeCharacteristic) { + BLELocalCharacteristic* characteristic = (BLELocalCharacteristic*)attribute; + + if (characteristic->handle() == handle) { + if (offset) { + sendError(connectionHandle, opcode, handle, ATT_ECODE_ATTR_NOT_LONG); + return; + } + + // add the properties + response[responseLength++] = characteristic->properties(); + + // add the value handle + uint16_t valueHandle = characteristic->valueHandle(); + memcpy(&response[responseLength], &valueHandle, sizeof(valueHandle)); + responseLength += sizeof(valueHandle); + + // add the UUID + uint8_t uuidLen = characteristic->uuidLength(); + memcpy(&response[responseLength], characteristic->uuidData(), uuidLen); + responseLength += uuidLen; + } else { + if ((characteristic->properties() & BLERead) == 0) { + sendError(connectionHandle, opcode, handle, ATT_ECODE_READ_NOT_PERM); + return; + } + + uint16_t valueLength = characteristic->valueLength(); + + if (offset >= valueLength) { + sendError(connectionHandle, opcode, handle, ATT_ECODE_INVALID_OFFSET); + return; + } + + valueLength = min(_mtu - responseLength, valueLength - offset); + + characteristic->readValue(BLEDevice(connectionHandle, _peerAddress), offset, &response[responseLength], valueLength); + responseLength += valueLength; + } + } else if (attributeType == BLETypeDescriptor) { + BLELocalDescriptor* descriptor = (BLELocalDescriptor*)attribute; + + uint16_t valueLength = descriptor->valueSize(); + + if (offset >= valueLength) { + sendError(connectionHandle, opcode, handle, ATT_ECODE_INVALID_OFFSET); + return; + } + + valueLength = min(_mtu - responseLength, valueLength - offset); + + memcpy(&response[responseLength], descriptor->value() + offset, valueLength); + responseLength += valueLength; + } + + HCI.sendAclPkt(connectionHandle, ATT_CID, responseLength, response); +} + +void ATTClass::readByTypeReq(uint16_t connectionHandle, uint8_t dlen, uint8_t data[]) +{ + struct __attribute__ ((packed)) ReadByTypeReq { + uint16_t startHandle; + uint16_t endHandle; + uint16_t uuid; + } *readByTypeReq = (ReadByTypeReq*)data; + + if (dlen != sizeof(ReadByTypeReq)) { + sendError(connectionHandle, ATT_OP_READ_BY_TYPE_REQ, readByTypeReq->startHandle, ATT_ECODE_INVALID_PDU); + return; + } + + uint8_t response[_mtu]; + uint16_t responseLength; + + response[0] = ATT_OP_READ_BY_TYPE_RESP; + response[1] = 0x00; + responseLength = 2; + + for (uint16_t i = (readByTypeReq->startHandle - 1); i < GATT.attributeCount() && i <= (readByTypeReq->endHandle - 1); i++) { + BLEAttribute* attribute = GATT.attribute(i); + uint16_t handle = (i + 1); + + if (attribute->type() == readByTypeReq->uuid) { + if (attribute->type() == BLETypeCharacteristic) { + BLELocalCharacteristic* characteristic = (BLELocalCharacteristic*)attribute; + + if (characteristic->valueHandle() == handle) { + // value handle, skip + continue; + } + + int uuidLen = attribute->uuidLength(); + int typeSize = (uuidLen == 2) ? 7 : 21; + + if (response[1] == 0) { + response[1] = typeSize; + } + + if (response[1] != typeSize) { + // all done, wrong size + break; + } + + // add the handle + memcpy(&response[responseLength], &handle, sizeof(handle)); + responseLength += sizeof(handle); + + // add the properties + response[responseLength++] = characteristic->properties(); + + // add the value handle + uint16_t valueHandle = (handle + 1); + memcpy(&response[responseLength], &valueHandle, sizeof(valueHandle)); + responseLength += sizeof(valueHandle); + + // add the UUID + memcpy(&response[responseLength], characteristic->uuidData(), uuidLen); + responseLength += uuidLen; + + // skip the next handle, it's a value handle + i++; + + if ((responseLength + typeSize) > _mtu) { + break; + } + } else if (attribute->type() == 0x2902) { + BLELocalDescriptor* descriptor = (BLELocalDescriptor*)attribute; + + // add the handle + memcpy(&response[responseLength], &handle, sizeof(handle)); + responseLength += sizeof(handle); + + // add the value + int valueSize = min((uint16_t)(_mtu - responseLength), (uint16_t)descriptor->valueSize()); + memcpy(&response[responseLength], descriptor->value(), valueSize); + responseLength += valueSize; + + response[1] = 2 + valueSize; + + break; // all done + } + } else if (attribute->type() == BLETypeCharacteristic && attribute->uuidLength() == 2 && memcmp(&readByTypeReq->uuid, attribute->uuidData(), 2) == 0) { + BLELocalCharacteristic* characteristic = (BLELocalCharacteristic*)attribute; + + // add the handle + memcpy(&response[responseLength], &handle, sizeof(handle)); + responseLength += sizeof(handle); + + // add the value + int valueLength = min((uint16_t)(_mtu - responseLength), (uint16_t)characteristic->valueLength()); + memcpy(&response[responseLength], characteristic->value(), valueLength); + responseLength += valueLength; + + response[1] = 2 + valueLength; + + break; // all done + } + } + + if (responseLength == 2) { + sendError(connectionHandle, ATT_OP_READ_BY_TYPE_REQ, readByTypeReq->startHandle, ATT_ECODE_ATTR_NOT_FOUND); + } else { + HCI.sendAclPkt(connectionHandle, ATT_CID, responseLength, response); + } +} + +void ATTClass::writeReqOrCmd(uint16_t connectionHandle, uint8_t op, uint8_t dlen, uint8_t data[]) +{ + boolean withResponse = (op == ATT_OP_WRITE_REQ); + + if (dlen < sizeof(uint16_t)) { + if (withResponse) { + sendError(connectionHandle, ATT_OP_WRITE_REQ, 0x0000, ATT_ECODE_INVALID_PDU); + } + return; + } + + uint16_t handle = *(uint16_t*)data; + + if ((uint16_t)(handle - 1) > GATT.attributeCount()) { + if (withResponse) { + sendError(connectionHandle, ATT_OP_WRITE_REQ, handle, ATT_ECODE_ATTR_NOT_FOUND); + } + return; + } + + uint8_t valueLength = dlen - sizeof(handle); + uint8_t* value = &data[sizeof(handle)]; + + BLEAttribute* attribute = GATT.attribute(handle - 1); + + if (attribute->type() == BLETypeCharacteristic) { + BLELocalCharacteristic* characteristic = (BLELocalCharacteristic*)attribute; + + if (handle != characteristic->valueHandle() || + withResponse ? ((characteristic->properties() & BLEWrite) == 0) : + ((characteristic->properties() & BLEWriteWithoutResponse) == 0)) { + if (withResponse) { + sendError(connectionHandle, ATT_OP_WRITE_REQ, handle, ATT_ECODE_WRITE_NOT_PERM); + } + return; + } + + if (connectionHandle == _connectionHandle) { + characteristic->writeValue(BLEDevice(connectionHandle, _peerAddress), value, valueLength); + } + } else if (attribute->type() == BLETypeDescriptor) { + BLELocalDescriptor* descriptor = (BLELocalDescriptor*)attribute; + + // only CCCD's are writable + if (descriptor->uuidLength() != 2 || *((uint16_t*)(descriptor->uuidData())) != 0x2902) { + if (withResponse) { + sendError(connectionHandle, ATT_OP_WRITE_REQ, handle, ATT_ECODE_WRITE_NOT_PERM); + } + return; + } + + // get the previous handle, should be the characteristic for the CCCD + attribute = GATT.attribute(handle - 2); + + if (attribute->type() != BLETypeCharacteristic) { + if (withResponse) { + sendError(connectionHandle, ATT_OP_WRITE_REQ, handle, ATT_ECODE_WRITE_NOT_PERM); + } + return; + } + + BLELocalCharacteristic* characteristic = (BLELocalCharacteristic*)attribute; + + if (connectionHandle == _connectionHandle) { + characteristic->writeCccdValue(BLEDevice(connectionHandle, _peerAddress), *((uint16_t*)value)); + } + } else { + if (withResponse) { + sendError(connectionHandle, ATT_OP_WRITE_REQ, handle, ATT_ECODE_WRITE_NOT_PERM); + } + return; + } + + if (withResponse) { + uint8_t response[_mtu]; + uint16_t responseLength; + + response[0] = ATT_OP_WRITE_RESP; + responseLength = 1; + + HCI.sendAclPkt(connectionHandle, ATT_CID, responseLength, response); + } +} + +void ATTClass::prepWriteReq(uint16_t connectionHandle, uint8_t dlen, uint8_t data[]) +{ + struct __attribute__ ((packed)) PrepWriteReq { + uint16_t handle; + uint16_t offset; + } *prepWriteReq = (PrepWriteReq*)data; + + if (dlen < sizeof(PrepWriteReq)) { + sendError(connectionHandle, ATT_OP_PREP_WRITE_REQ, 0x0000, ATT_ECODE_INVALID_PDU); + return; + } + + uint16_t handle = prepWriteReq->handle; + uint16_t offset = prepWriteReq->offset; + + if ((uint16_t)(handle - 1) > GATT.attributeCount()) { + sendError(connectionHandle, ATT_OP_PREP_WRITE_REQ, handle, ATT_ECODE_ATTR_NOT_FOUND); + return; + } + + BLEAttribute* attribute = GATT.attribute(handle - 1); + + if (attribute->type() != BLETypeCharacteristic) { + sendError(connectionHandle, ATT_OP_PREP_WRITE_REQ, handle, ATT_ECODE_ATTR_NOT_LONG); + return; + } + + BLELocalCharacteristic* characteristic = (BLELocalCharacteristic*)attribute; + + if (handle != characteristic->valueHandle()) { + sendError(connectionHandle, ATT_OP_PREP_WRITE_REQ, handle, ATT_ECODE_ATTR_NOT_LONG); + return; + } + + if ((characteristic->properties() & BLEWrite) == 0) { + sendError(connectionHandle, ATT_OP_PREP_WRITE_REQ, handle, ATT_ECODE_WRITE_NOT_PERM); + return; + } + + if (_longWriteHandle == 0) { + int valueSize = characteristic->valueSize(); + + _longWriteValue = (uint8_t*)realloc(_longWriteValue, valueSize); + _longWriteValueLength = 0; + _longWriteHandle = handle; + + memset(_longWriteValue, 0x00, valueSize); + } else if (_longWriteHandle != handle) { + sendError(connectionHandle, ATT_OP_PREP_WRITE_REQ, handle, ATT_ECODE_UNLIKELY); + return; + } + + uint8_t valueLength = dlen - sizeof(PrepWriteReq); + uint8_t* value = &data[sizeof(PrepWriteReq)]; + + if ((offset != _longWriteValueLength) || ((offset + valueLength) > (uint16_t)characteristic->valueSize())) { + sendError(connectionHandle, ATT_OP_PREP_WRITE_REQ, handle, ATT_ECODE_INVALID_OFFSET); + return; + } + + memcpy(_longWriteValue + offset, value, valueLength); + _longWriteValueLength += valueLength; + + uint8_t response[_mtu]; + uint16_t responseLength; + + response[0] = ATT_OP_PREP_WRITE_RESP; + memcpy(&response[1], data, dlen); + responseLength = dlen + 1; + + HCI.sendAclPkt(connectionHandle, ATT_CID, responseLength, response); +} + +void ATTClass::execWriteReq(uint16_t connectionHandle, uint8_t dlen, uint8_t data[]) +{ + if (dlen != sizeof(uint8_t)) { + sendError(connectionHandle, ATT_OP_EXEC_WRITE_REQ, 0x0000, ATT_ECODE_INVALID_PDU); + return; + } + + uint8_t flag = data[0]; + + if (_longWriteHandle && (flag & 0x01)) { + BLELocalCharacteristic* characteristic = (BLELocalCharacteristic*)GATT.attribute(_longWriteHandle - 1); + + if (connectionHandle == _connectionHandle) { + characteristic->writeValue(BLEDevice(connectionHandle, _peerAddress), _longWriteValue, _longWriteValueLength); + } + } + + _longWriteHandle = 0x0000; + _longWriteValueLength = 0; + + uint8_t response[_mtu]; + uint16_t responseLength; + + response[0] = ATT_OP_EXEC_WRITE_RESP; + responseLength = 1; + + HCI.sendAclPkt(connectionHandle, ATT_CID, responseLength, response); +} + +void ATTClass::handleCnf(uint16_t /*connectionHandle*/, uint8_t /*dlen*/, uint8_t /*data*/[]) +{ + _cnf = true; +} + +void ATTClass::sendError(uint16_t connectionHandle, uint8_t opcode, uint16_t handle, uint8_t code) +{ + struct __attribute__ ((packed)) { + uint8_t op; + uint8_t opcode; + uint16_t handle; + uint8_t code; + } attError = { ATT_OP_ERROR, opcode, handle, code }; + + HCI.sendAclPkt(connectionHandle, ATT_CID, sizeof(attError), &attError); +} + +void ATTClass::setEventHandler(BLEDeviceEvent event, BLEDeviceEventHandler eventHandler) +{ + if (event < (sizeof(_eventHandlers) / (sizeof(_eventHandlers[0])))) { + _eventHandlers[event] = eventHandler; + } +} + +ATTClass ATT; diff --git a/src/utility/ATT.h b/src/utility/ATT.h new file mode 100644 index 00000000..7f6e1c57 --- /dev/null +++ b/src/utility/ATT.h @@ -0,0 +1,86 @@ +/* + This file is part of the ArduinoBLE library. + Copyright (c) 2018 Arduino SA. All rights reserved. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#ifndef _ATT_H_ +#define _ATT_H_ + +#include + +#include "BLEDevice.h" + +#define ATT_CID 0x0004 + +class ATTClass { +public: + ATTClass(); + virtual ~ATTClass(); + + void setMaxMtu(uint16_t maxMtu); + + void addConnection(uint16_t handle, uint8_t role, uint8_t peerBdaddrType, + uint8_t peerBdaddr[6], uint16_t interval, + uint16_t latency, uint16_t supervisionTimeout, + uint8_t masterClockAccuracy); + + void handleData(uint16_t connectionHandle, uint8_t dlen, uint8_t data[]); + + void removeConnection(uint8_t handle, uint16_t reason); + + bool connected() const; + bool connected(uint16_t handle, const uint8_t address[6]) const; + + bool disconnect(); + + BLEDevice central(); + + bool handleNotify(uint16_t handle, const uint8_t* value, int length); + bool handleInd(uint16_t handle, const uint8_t* value, int length); + + void setEventHandler(BLEDeviceEvent event, BLEDeviceEventHandler eventHandler); + +private: + void mtuReq(uint16_t connectionHandle, uint8_t dlen, uint8_t data[]); + void findInfoReq(uint16_t connectionHandle, uint8_t dlen, uint8_t data[]); + void findByTypeReq(uint16_t connectionHandle, uint8_t dlen, uint8_t data[]); + void readByTypeReq(uint16_t connectionHandle, uint8_t dlen, uint8_t data[]); + void readOrReadBlobReq(uint16_t connectionHandle, uint8_t opcode, uint8_t dlen, uint8_t data[]); + void readByGroupReq(uint16_t connectionHandle, uint8_t dlen, uint8_t data[]); + void writeReqOrCmd(uint16_t connectionHandle, uint8_t op, uint8_t dlen, uint8_t data[]); + void prepWriteReq(uint16_t connectionHandle, uint8_t dlen, uint8_t data[]); + void execWriteReq(uint16_t connectionHandle, uint8_t dlen, uint8_t data[]); + void handleCnf(uint16_t connectionHandle, uint8_t dlen, uint8_t data[]); + void sendError(uint16_t connectionHandle, uint8_t opcode, uint16_t handle, uint8_t code); + +private: + uint16_t _maxMtu; + uint16_t _connectionHandle; + uint8_t _peerAddress[6]; + uint16_t _mtu; + volatile bool _cnf; + + uint16_t _longWriteHandle; + uint8_t* _longWriteValue; + uint16_t _longWriteValueLength; + + BLEDeviceEventHandler _eventHandlers[BLEDeviceLastEvent]; +}; + +extern ATTClass ATT; + +#endif diff --git a/src/utility/BLELinkedList.h b/src/utility/BLELinkedList.h new file mode 100644 index 00000000..fce2765d --- /dev/null +++ b/src/utility/BLELinkedList.h @@ -0,0 +1,114 @@ +/* + This file is part of the ArduinoBLE library. + Copyright (c) 2018 Arduino SA. All rights reserved. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#ifndef _BLE_LINKED_LIST_ +#define _BLE_LINKED_LIST_ + +#include + +template struct BLELinkedListNode +{ + T data; + BLELinkedListNode* next; +}; + +template class BLELinkedList { +public: + BLELinkedList(); + ~BLELinkedList(); + + void add(T); + T get(unsigned int index) const; + void clear(); + + unsigned int size() const; + +private: + unsigned int _size; + BLELinkedListNode* _root; + BLELinkedListNode* _last; +}; + +template BLELinkedList::BLELinkedList() : + _size(0), + _root(NULL), + _last(NULL) +{ +} + +template BLELinkedList::~BLELinkedList() +{ + clear(); +} + +template void BLELinkedList::add(T item) +{ + BLELinkedListNode* itemNode = new BLELinkedListNode(); + + itemNode->data = item; + itemNode->next = NULL; + + if (_root == NULL) { + _root = itemNode; + } else { + _last->next = itemNode; + } + _last = itemNode; + + _size++; +} + +template T BLELinkedList::get(unsigned int index) const +{ + if (index >= _size) { + return T(); + } + + BLELinkedListNode* itemNode = _root; + + for (unsigned int i = 0; i < index; i++) { + itemNode = itemNode->next; + } + + return itemNode->data; +} + +template void BLELinkedList::clear() +{ + BLELinkedListNode* itemNode = _root; + + for (unsigned int i = 0; i < _size; i++) { + BLELinkedListNode* n = itemNode; + + itemNode = itemNode->next; + + delete n; + } + + _size = 0; + _root = NULL; + _last = NULL; +} + +template unsigned int BLELinkedList::size() const +{ + return _size; +} + +#endif diff --git a/src/utility/GAP.cpp b/src/utility/GAP.cpp new file mode 100644 index 00000000..07b2d436 --- /dev/null +++ b/src/utility/GAP.cpp @@ -0,0 +1,173 @@ +/* + This file is part of the ArduinoBLE library. + Copyright (c) 2018 Arduino SA. All rights reserved. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include "BLEUuid.h" +#include "HCI.h" + +#include "GAP.h" + + +GAPClass::GAPClass() : + _advertising(false), + _advertisedServiceUuid(NULL), + _manufacturerData(NULL), + _manufacturerDataLength(0), + _localName(NULL), + _advertisingInterval(160), + _connectable(true), + _serviceData(NULL), + _serviceDataLength(0) +{ +} + +GAPClass::~GAPClass() +{ +} + +void GAPClass::setAdvertisedServiceUuid(const char* advertisedServiceUuid) +{ + _advertisedServiceUuid = advertisedServiceUuid; +} + +void GAPClass::setManufacturerData(const uint8_t manufacturerData[], int manufacturerDataLength) +{ + _manufacturerData = manufacturerData; + _manufacturerDataLength = manufacturerDataLength; +} + +void GAPClass::setLocalName(const char *localName) +{ + _localName = localName; +} + +bool GAPClass::advertising() +{ + return _advertising; +} + +int GAPClass::advertise() +{ + uint8_t directBdaddr[6] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; + + uint8_t type = (_connectable) ? 0x00 : (_localName ? 0x02 : 0x03); + + _advertising = false; + + if (HCI.leSetAdvertisingParameters(_advertisingInterval, _advertisingInterval, type, 0x00, 0x00, directBdaddr, 0x07, 0) != 0) { + return 0; + } + + uint8_t advertisingData[31]; + uint8_t advertisingDataLen = 0; + + advertisingData[0] = 0x02; + advertisingData[1] = 0x01; + advertisingData[2] = 0x06; + advertisingDataLen += 3; + + if (_advertisedServiceUuid) { + BLEUuid uuid(_advertisedServiceUuid); + int uuidLen = uuid.length(); + + advertisingData[3] = 1 + uuidLen; + advertisingData[4] = (uuidLen > 2) ? 0x06 : 0x02; + memcpy(&advertisingData[5], uuid.data(), uuidLen); + + advertisingDataLen += (2 + uuidLen); + } else if (_manufacturerData && _manufacturerDataLength) { + advertisingData[3] = 1 + _manufacturerDataLength; + advertisingData[4] = 0xff; + memcpy(&advertisingData[5], _manufacturerData, _manufacturerDataLength); + + advertisingDataLen += (2 + _manufacturerDataLength); + } + + if (_serviceData && _serviceDataLength > 0 && advertisingDataLen >= (_serviceDataLength + 4)) { + advertisingData[advertisingDataLen++] = _serviceDataLength + 3; + advertisingData[advertisingDataLen++] = 0x16; + + memcpy(&advertisingData[advertisingDataLen], &_serviceDataUuid, sizeof(_serviceDataUuid)); + advertisingDataLen += sizeof(_serviceDataUuid); + + memcpy(&advertisingData[advertisingDataLen],_serviceData, _serviceDataLength); + advertisingDataLen += _serviceDataLength; + } + + if (HCI.leSetAdvertisingData(advertisingDataLen, advertisingData) != 0) { + return 0; + } + + uint8_t scanResponseData[31]; + uint8_t scanResponseDataLen = 0; + + if (_localName) { + int localNameLen = strlen(_localName); + + if (localNameLen > 29) { + localNameLen = 29; + scanResponseData[1] = 0x08; + } else { + scanResponseData[1] = 0x09; + } + + scanResponseData[0] = 1 + localNameLen; + + memcpy(&scanResponseData[2], _localName, localNameLen); + + scanResponseDataLen += (2 + localNameLen); + } + + if (HCI.leSetScanResponseData(scanResponseDataLen, scanResponseData) != 0) { + return 0; + } + + if (HCI.leSetAdvertiseEnable(0x01) != 0) { + return 0; + } + + _advertising = false; + + return 1; +} + +void GAPClass::stopAdvertise() +{ + _advertising = false; + + HCI.leSetAdvertiseEnable(0x00); +} + +void GAPClass::setAdvertisingInterval(uint16_t advertisingInterval) +{ + _advertisingInterval = advertisingInterval; +} + +void GAPClass::setConnectable(bool connectable) +{ + _connectable = connectable; +} + +void GAPClass::setAdvertisedServiceData(uint16_t uuid, const uint8_t data[], int length) +{ + _serviceDataUuid = uuid; + _serviceData = data; + _serviceDataLength = length; +} + +GAPClass GAP; diff --git a/src/utility/GAP.h b/src/utility/GAP.h new file mode 100644 index 00000000..dd8a73fb --- /dev/null +++ b/src/utility/GAP.h @@ -0,0 +1,61 @@ +/* + This file is part of the ArduinoBLE library. + Copyright (c) 2018 Arduino SA. All rights reserved. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#ifndef _GAP_H_ +#define _GAP_H_ + +class GAPClass { +public: + GAPClass(); + virtual ~GAPClass(); + + void setAdvertisedServiceUuid(const char* advertisedServiceUuid); + void setManufacturerData(const uint8_t manufacturerData[], int manufacturerDataLength); + void setLocalName(const char *localName); + + bool advertising(); + int advertise(); + void stopAdvertise(); + + void setAdvertisingInterval(uint16_t advertisingInterval); + void setConnectable(bool connectable); + +protected: + friend class BLELocalCharacteristic; + + void setAdvertisedServiceData(uint16_t uuid, const uint8_t data[], int length); + +private: + bool _advertising; + + const char* _advertisedServiceUuid; + const uint8_t* _manufacturerData; + int _manufacturerDataLength; + const char* _localName; + uint16_t _advertisingInterval; + bool _connectable; + + uint16_t _serviceDataUuid; + const uint8_t* _serviceData; + int _serviceDataLength; +}; + +extern GAPClass GAP; + +#endif diff --git a/src/utility/GATT.cpp b/src/utility/GATT.cpp new file mode 100644 index 00000000..906f4f2c --- /dev/null +++ b/src/utility/GATT.cpp @@ -0,0 +1,166 @@ +/* + This file is part of the ArduinoBLE library. + Copyright (c) 2018 Arduino SA. All rights reserved. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include + +#include "local/BLELocalCharacteristic.h" +#include "local/BLELocalDescriptor.h" +#include "local/BLELocalService.h" + +#include "BLEProperty.h" + +#include "GATT.h" + +GATTClass::GATTClass() : + _genericAccessService("1800"), + _deviceNameCharacteristic("2a00", BLERead, 20), + _appearanceCharacteristic("2a01", BLERead, 2), + _genericAttributeService("1801"), + _servicesChangedCharacteristic("2a05", BLEIndicate, 4) +{ + _genericAccessService.retain(); + _genericAttributeService.retain(); + + _genericAccessService.addCharacteristic(&_deviceNameCharacteristic); + _genericAccessService.addCharacteristic(&_appearanceCharacteristic); + + _genericAttributeService.addCharacteristic(&_servicesChangedCharacteristic); +} + +GATTClass::~GATTClass() +{ + clearAttributes(); +} + +void GATTClass::begin() +{ + setDeviceName("Arduino"); + setAppearance(0x000); + + clearAttributes(); + + addService(&_genericAccessService); + addService(&_genericAttributeService); +} + +void GATTClass::end() +{ + _attributes.clear(); +} + +void GATTClass::setDeviceName(const char* deviceName) +{ + _deviceNameCharacteristic.writeValue(deviceName); +} + +void GATTClass::setAppearance(uint16_t appearance) +{ + _appearanceCharacteristic.writeValue((uint8_t*)&appearance, sizeof(appearance)); +} + +void GATTClass::addService(BLEService& service) +{ + BLELocalService* localService = service.local(); + + if (localService) { + addService(localService); + } +} + +unsigned int GATTClass::attributeCount() const +{ + return _attributes.size(); +} + +BLEAttribute* GATTClass::attribute(unsigned int index) const +{ + return _attributes.get(index); +} + +uint16_t GATTClass::serviceUuidForCharacteristic(BLELocalCharacteristic* characteristic) const +{ + uint16_t serviceUuid = 0x0000; + + BLELocalService* lastService = NULL; + + for (unsigned int i = 0; i < attributeCount(); i++) { + BLEAttribute* a = attribute(i); + uint16_t attributeType = a->type(); + + if (attributeType == BLETypeService) { + lastService = (BLELocalService*)a; + } else if (a == characteristic) { + break; + } + } + + if (lastService) { + if (lastService->uuidLength() == 2) { + serviceUuid = *(uint16_t*)(lastService->uuidData()); + } else { + serviceUuid = *(uint16_t*)(lastService->uuidData() + 10); + } + } + + return serviceUuid; +} + +void GATTClass::addService(BLELocalService* service) +{ + service->retain(); + _attributes.add(service); + + uint16_t startHandle = attributeCount(); + + for (unsigned int i = 0; i < service->characteristicCount(); i++) { + BLELocalCharacteristic* characteristic = service->characteristic(i); + + characteristic->retain(); + _attributes.add(characteristic); + characteristic->setHandle(attributeCount()); + + // add the characteristic again to make space of the characteristic value handle + _attributes.add(characteristic); + + for (unsigned int j = 0; j < characteristic->descriptorCount(); j++) { + BLELocalDescriptor* descriptor = characteristic->descriptor(j); + + descriptor->retain(); + _attributes.add(descriptor); + descriptor->setHandle(attributeCount()); + } + } + + service->setHandles(startHandle, attributeCount()); +} + +void GATTClass::clearAttributes() +{ + for (unsigned int i = 0; i < attributeCount(); i++) { + BLEAttribute* a = attribute(i); + + if (a->release() <= 0) { + delete a; + } + } + + _attributes.clear(); +} + +GATTClass GATT; diff --git a/src/utility/GATT.h b/src/utility/GATT.h new file mode 100644 index 00000000..ae53f62b --- /dev/null +++ b/src/utility/GATT.h @@ -0,0 +1,72 @@ +/* + This file is part of the ArduinoBLE library. + Copyright (c) 2018 Arduino SA. All rights reserved. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#ifndef _GATT_H_ +#define _GATT_H_ + +#include "utility/BLELinkedList.h" + +#include "local/BLELocalService.h" +#include "local/BLELocalCharacteristic.h" + +#include "BLEAttribute.h" +#include "BLEService.h" + +class GATTClass { +public: + GATTClass(); + virtual ~GATTClass(); + + void begin(); + void end(); + + void setDeviceName(const char* deviceName); + void setAppearance(uint16_t appearance); + + void addService(BLEService& service); + +protected: + friend class ATTClass; + + unsigned int attributeCount() const; + BLEAttribute* attribute(unsigned int index) const; + +protected: + friend class BLELocalCharacteristic; + + uint16_t serviceUuidForCharacteristic(BLELocalCharacteristic* characteristic) const; + +private: + void addService(BLELocalService* service); + + void clearAttributes(); + +private: + BLELinkedList _attributes; + + BLELocalService _genericAccessService; + BLELocalCharacteristic _deviceNameCharacteristic; + BLELocalCharacteristic _appearanceCharacteristic; + BLELocalService _genericAttributeService; + BLELocalCharacteristic _servicesChangedCharacteristic; +}; + +extern GATTClass GATT; + +#endif diff --git a/src/utility/HCI.cpp b/src/utility/HCI.cpp new file mode 100644 index 00000000..a188bcab --- /dev/null +++ b/src/utility/HCI.cpp @@ -0,0 +1,546 @@ +/* + This file is part of the ArduinoBLE library. + Copyright (c) 2018 Arduino SA. All rights reserved. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include "ATT.h" +#include "HCIUartTransport.h" +#include "L2CAPSignaling.h" + +#include "HCI.h" + +#define HCI_COMMAND_PKT 0x01 +#define HCI_ACLDATA_PKT 0x02 +#define HCI_EVENT_PKT 0x04 + +#define EVT_CMD_COMPLETE 0xe +#define EVT_DISCONN_COMPLETE 0x05 +#define EVT_NUM_COMP_PKTS 0x13 +#define EVT_LE_META_EVENT 0x3e + +#define EVT_LE_CONN_COMPLETE 0x01 + +#define OGF_LINK_CTL 0x01 +#define OGF_HOST_CTL 0x03 +#define OGF_INFO_PARAM 0x04 +#define OGF_STATUS_PARAM 0x05 +#define OGF_LE_CTL 0x08 + +// OGF_LINK_CTL +#define OCF_DISCONNECT 0x0006 + +// OGF_HOST_CTL +#define OCF_SET_EVENT_MASK 0x0001 +#define OCF_RESET 0x0003 + +// OGF_INFO_PARAM +#define OCF_READ_LOCAL_VERSION 0x0001 +#define OCF_READ_BD_ADDR 0x0009 + +// OGF_STATUS_PARAM +#define OCF_READ_RSSI 0x0005 + +// OGF_LE_CTL +#define OCF_LE_READ_BUFFER_SIZE 0x0002 +#define OCF_LE_SET_RANDOM_ADDRESS 0x0005 +#define OCF_LE_SET_ADVERTISING_PARAMETERS 0x0006 +#define OCF_LE_SET_ADVERTISING_DATA 0x0008 +#define OCF_LE_SET_SCAN_RESPONSE_DATA 0x0009 +#define OCF_LE_SET_ADVERTISE_ENABLE 0x000a +#define OCF_LE_CONN_UPDATE 0x0013 + +#define HCI_OE_USER_ENDED_CONNECTION 0x13 + +HCIClass::HCIClass() : + _debug(NULL), + _recvIndex(0), + _pendingPkt(0) +{ +} + +HCIClass::~HCIClass() +{ +} + +int HCIClass::begin() +{ + _recvIndex = 0; + + return HCITransport.begin(); +} + +void HCIClass::end() +{ + HCITransport.end(); +} + +void HCIClass::poll() +{ + poll(0); +} + +void HCIClass::poll(unsigned long timeout) +{ +#ifdef ARDUINO_AVR_UNO_WIFI_REV2 + digitalWrite(NINA_RTS, LOW); +#endif + + if (timeout) { + HCITransport.wait(timeout); + } + + while (HCITransport.available()) { + byte b = HCITransport.read(); + + _recvBuffer[_recvIndex++] = b; + + if (_recvBuffer[0] == HCI_ACLDATA_PKT) { + if (_recvIndex > 5 && _recvIndex >= (5 + (_recvBuffer[3] + (_recvBuffer[4] << 8)))) { + if (_debug) { + dumpPkt("HCI ACLDATA RX <- ", _recvIndex, _recvBuffer); + } +#ifdef ARDUINO_AVR_UNO_WIFI_REV2 + digitalWrite(NINA_RTS, HIGH); +#endif + int pktLen = _recvIndex - 1; + _recvIndex = 0; + + handleAclDataPkt(pktLen, &_recvBuffer[1]); + +#ifdef ARDUINO_AVR_UNO_WIFI_REV2 + digitalWrite(NINA_RTS, LOW); +#endif + } + } else if (_recvBuffer[0] == HCI_EVENT_PKT) { + if (_recvIndex > 3 && _recvIndex >= (3 + _recvBuffer[2])) { + if (_debug) { + dumpPkt("HCI EVENT RX <- ", _recvIndex, _recvBuffer); + } +#ifdef ARDUINO_AVR_UNO_WIFI_REV2 + digitalWrite(NINA_RTS, HIGH); +#endif + // received full event + int pktLen = _recvIndex - 1; + _recvIndex = 0; + + handleEventPkt(pktLen, &_recvBuffer[1]); + +#ifdef ARDUINO_AVR_UNO_WIFI_REV2 + digitalWrite(NINA_RTS, LOW); +#endif + } + } else { + _recvIndex = 0; + + if (_debug) { + _debug->println(b, HEX); + } + } + } + +#ifdef ARDUINO_AVR_UNO_WIFI_REV2 + digitalWrite(NINA_RTS, HIGH); +#endif +} + +int HCIClass::reset() +{ + return sendCommand(OGF_HOST_CTL << 10 | OCF_RESET); +} + +int HCIClass::readLocalVersion(uint8_t& hciVer, uint16_t& hciRev, uint8_t& lmpVer, uint16_t& manufacturer, uint16_t& lmpSubVer) +{ + int result = sendCommand(OGF_INFO_PARAM << 10 | OCF_READ_LOCAL_VERSION); + + if (result == 0) { + struct __attribute__ ((packed)) HCILocalVersion { + uint8_t hciVer; + uint16_t hciRev; + uint8_t lmpVer; + uint16_t manufacturer; + uint16_t lmpSubVer; + } *localVersion = (HCILocalVersion*)_cmdResponse; + + hciVer = localVersion->hciVer; + hciRev = localVersion->hciRev; + lmpVer = localVersion->lmpVer; + manufacturer = localVersion->manufacturer; + lmpSubVer = localVersion->lmpSubVer; + } + + return result; +} + +int HCIClass::readBdAddr(uint8_t addr[6]) +{ + int result = sendCommand(OGF_INFO_PARAM << 10 | OCF_READ_BD_ADDR); + + if (result == 0) { + memcpy(addr, _cmdResponse, 6); + } + + return result; +} + +int HCIClass::readRssi(uint16_t handle) +{ + int result = sendCommand(OGF_STATUS_PARAM << 10 | OCF_READ_RSSI, sizeof(handle), &handle); + int rssi = 127; + + if (result == 0) { + struct __attribute__ ((packed)) HCIReadRssi { + uint16_t handle; + int8_t rssi; + } *readRssi = (HCIReadRssi*)_cmdResponse; + + if (readRssi->handle == handle) { + rssi = readRssi->rssi; + } + } + + return rssi; +} + +int HCIClass::setEventMask(uint64_t eventMask) +{ + return sendCommand(OGF_HOST_CTL << 10 | OCF_SET_EVENT_MASK, sizeof(eventMask), &eventMask); +} + +int HCIClass::readLeBufferSize(uint16_t& pktLen, uint8_t& maxPkt) +{ + int result = sendCommand(OGF_LE_CTL << 10 | OCF_LE_READ_BUFFER_SIZE); + + if (result == 0) { + struct __attribute__ ((packed)) HCILeBufferSize { + uint16_t pktLen; + uint8_t maxPkt; + } *leBufferSize = (HCILeBufferSize*)_cmdResponse; + + pktLen = leBufferSize->pktLen; + _maxPkt = maxPkt = leBufferSize->maxPkt; + +#ifndef __AVR__ + ATT.setMaxMtu(pktLen - 9); // max pkt len - ACL header size +#endif + } + + return result; +} + +int HCIClass::leSetRandomAddress(uint8_t addr[6]) +{ + return sendCommand(OGF_LE_CTL << 10 | OCF_LE_SET_RANDOM_ADDRESS, 6, addr); +} + +int HCIClass::leSetAdvertisingParameters(uint16_t minInterval, uint16_t maxInterval, + uint8_t advType, uint8_t ownBdaddrType, + uint8_t directBdaddrType, uint8_t directBdaddr[6], + uint8_t chanMap, + uint8_t filter) +{ + struct __attribute__ ((packed)) HCILeAdvertisingParameters { + uint16_t minInterval; + uint16_t maxInterval; + uint8_t advType; + uint8_t ownBdaddrType; + uint8_t directBdaddrType; + uint8_t directBdaddr[6]; + uint8_t chanMap; + uint8_t filter; + } leAdvertisingParamters; + + leAdvertisingParamters.minInterval = minInterval; + leAdvertisingParamters.maxInterval = maxInterval; + leAdvertisingParamters.advType = advType; + leAdvertisingParamters.ownBdaddrType = ownBdaddrType; + leAdvertisingParamters.directBdaddrType = directBdaddrType; + memcpy(leAdvertisingParamters.directBdaddr, directBdaddr, 6); + leAdvertisingParamters.chanMap = chanMap; + leAdvertisingParamters.filter = filter; + + return sendCommand(OGF_LE_CTL << 10 | OCF_LE_SET_ADVERTISING_PARAMETERS, sizeof(leAdvertisingParamters), &leAdvertisingParamters); +} + +int HCIClass::leSetAdvertisingData(uint8_t length, uint8_t data[]) +{ + struct __attribute__ ((packed)) HCILeAdvertisingData { + uint8_t length; + uint8_t data[31]; + } leAdvertisingData; + + memset(&leAdvertisingData, 0, sizeof(leAdvertisingData)); + leAdvertisingData.length = length; + memcpy(leAdvertisingData.data, data, length); + + return sendCommand(OGF_LE_CTL << 10 | OCF_LE_SET_ADVERTISING_DATA, sizeof(leAdvertisingData), &leAdvertisingData); +} + +int HCIClass::leSetScanResponseData(uint8_t length, uint8_t data[]) +{ + struct __attribute__ ((packed)) HCILeScanResponseData { + uint8_t length; + uint8_t data[31]; + } leScanResponseData; + + memset(&leScanResponseData, 0, sizeof(leScanResponseData)); + leScanResponseData.length = length; + memcpy(leScanResponseData.data, data, length); + + return sendCommand(OGF_LE_CTL << 10 | OCF_LE_SET_SCAN_RESPONSE_DATA, sizeof(leScanResponseData), &leScanResponseData); +} + +int HCIClass::leSetAdvertiseEnable(uint8_t enable) +{ + return sendCommand(OGF_LE_CTL << 10 | OCF_LE_SET_ADVERTISE_ENABLE, sizeof(enable), &enable); +} + +int HCIClass::leConnUpdate(uint16_t handle, uint16_t minInterval, uint16_t maxInterval, + uint16_t latency, uint16_t supervisionTimeout) +{ + struct __attribute__ ((packed)) HCILeConnUpdateData { + uint16_t handle; + uint16_t minInterval; + uint16_t maxInterval; + uint16_t latency; + uint16_t supervisionTimeout; + uint16_t minCeLength; + uint16_t maxCeLength; + } leConnUpdateData; + + leConnUpdateData.handle = handle; + leConnUpdateData.minInterval = minInterval; + leConnUpdateData.maxInterval = maxInterval; + leConnUpdateData.latency = latency; + leConnUpdateData.supervisionTimeout = supervisionTimeout; + leConnUpdateData.minCeLength = 0x0004; + leConnUpdateData.maxCeLength = 0x0006; + + return sendCommand(OGF_LE_CTL << 10 | OCF_LE_CONN_UPDATE, sizeof(leConnUpdateData), &leConnUpdateData); +} + +int HCIClass::sendAclPkt(uint16_t handle, uint8_t cid, uint8_t plen, void* data) +{ + while (_pendingPkt >= _maxPkt) { + poll(); + } + + struct __attribute__ ((packed)) HCIACLHdr { + uint8_t pktType; + uint16_t handle; + uint16_t dlen; + uint16_t plen; + uint16_t cid; + } aclHdr = { HCI_ACLDATA_PKT, handle, uint8_t(plen + 4), plen, cid }; + + uint8_t txBuffer[sizeof(aclHdr) + plen]; + memcpy(txBuffer, &aclHdr, sizeof(aclHdr)); + memcpy(&txBuffer[sizeof(aclHdr)], data, plen); + + if (_debug) { + dumpPkt("HCI ACLDATA TX -> ", sizeof(aclHdr) + plen, txBuffer); + } + + _pendingPkt++; + HCITransport.write(txBuffer, sizeof(aclHdr) + plen); + + return 0; +} + +int HCIClass::disconnect(uint16_t handle) +{ + struct __attribute__ ((packed)) HCIDisconnectData { + uint16_t handle; + uint8_t reason; + } disconnectData = { handle, HCI_OE_USER_ENDED_CONNECTION }; + + return sendCommand(OGF_LINK_CTL << 10 | OCF_DISCONNECT, sizeof(disconnectData), &disconnectData); +} + +void HCIClass::debug(Stream& stream) +{ + _debug = &stream; +} + +void HCIClass::noDebug() +{ + _debug = NULL; +} + +int HCIClass::sendCommand(uint16_t opcode, uint8_t plen, void* parameters) +{ + struct __attribute__ ((packed)) { + uint8_t pktType; + uint16_t opcode; + uint8_t plen; + } pktHdr = {HCI_COMMAND_PKT, opcode, plen}; + + uint8_t txBuffer[sizeof(pktHdr) + plen]; + memcpy(txBuffer, &pktHdr, sizeof(pktHdr)); + memcpy(&txBuffer[sizeof(pktHdr)], parameters, plen); + + if (_debug) { + dumpPkt("HCI COMMAND TX -> ", sizeof(pktHdr) + plen, txBuffer); + } + + HCITransport.write(txBuffer, sizeof(pktHdr) + plen); + + _cmdCompleteOpcode = 0xffff; + _cmdCompleteStatus = -1; + + for (unsigned long start = millis(); _cmdCompleteOpcode != opcode && millis() < (start + 1000);) { + poll(); + } + + return _cmdCompleteStatus; +} + +void HCIClass::handleAclDataPkt(uint8_t /*plen*/, uint8_t pdata[]) +{ + struct __attribute__ ((packed)) HCIACLHdr { + uint16_t handle; + uint16_t dlen; + uint16_t len; + uint16_t cid; + } *aclHdr = (HCIACLHdr*)pdata; + + if (aclHdr->cid == ATT_CID) { + ATT.handleData(aclHdr->handle & 0x0fff, aclHdr->len, &_recvBuffer[1 + sizeof(HCIACLHdr)]); + } else if (aclHdr->cid == SIGNALING_CID) { + L2CAPSignaling.handleData(aclHdr->handle & 0x0fff, aclHdr->len, &_recvBuffer[1 + sizeof(HCIACLHdr)]); + } else { + struct __attribute__ ((packed)) { + uint8_t op; + uint8_t id; + uint16_t length; + uint16_t reason; + uint16_t localCid; + uint16_t remoteCid; + } l2capRejectCid= { 0x01, 0x00, 0x006, 0x0002, aclHdr->cid, 0x0000 }; + + sendAclPkt(aclHdr->handle & 0x0fff, 0x0005, sizeof(l2capRejectCid), &l2capRejectCid); + } +} + +void HCIClass::handleNumCompPkts(uint16_t /*handle*/, uint16_t numPkts) +{ + if (numPkts && _pendingPkt > numPkts) { + _pendingPkt -= numPkts; + } else { + _pendingPkt = 0; + } +} + +void HCIClass::handleEventPkt(uint8_t /*plen*/, uint8_t pdata[]) +{ + struct __attribute__ ((packed)) HCIEventHdr { + uint8_t evt; + uint8_t plen; + } *eventHdr = (HCIEventHdr*)pdata; + + if (eventHdr->evt == EVT_DISCONN_COMPLETE) { + struct __attribute__ ((packed)) DisconnComplete { + uint8_t status; + uint16_t handle; + uint8_t reason; + } *disconnComplete = (DisconnComplete*)&pdata[sizeof(HCIEventHdr)]; + + ATT.removeConnection(disconnComplete->handle, disconnComplete->reason); + L2CAPSignaling.removeConnection(disconnComplete->handle, disconnComplete->reason); + + HCI.leSetAdvertiseEnable(0x01); + } else if (eventHdr->evt == EVT_CMD_COMPLETE) { + struct __attribute__ ((packed)) CmdComplete { + uint8_t ncmd; + uint16_t opcode; + uint8_t status; + } *cmdCompleteHeader = (CmdComplete*)&pdata[sizeof(HCIEventHdr)]; + + _cmdCompleteOpcode = cmdCompleteHeader->opcode; + _cmdCompleteStatus = cmdCompleteHeader->status; + _cmdResponseLen = pdata[1] - sizeof(CmdComplete); + _cmdResponse = &pdata[sizeof(HCIEventHdr) + sizeof(CmdComplete)]; + } else if (eventHdr->evt == EVT_NUM_COMP_PKTS) { + uint8_t numHandles = pdata[sizeof(HCIEventHdr)]; + uint16_t* data = (uint16_t*)&pdata[sizeof(HCIEventHdr) + sizeof(numHandles)]; + + for (uint8_t i = 0; i < numHandles; i++) { + handleNumCompPkts(data[0], data[1]); + + data += 2; + } + } else if (eventHdr->evt == EVT_LE_META_EVENT) { + struct __attribute__ ((packed)) LeMetaEventHeader { + uint8_t subevent; + } *leMetaHeader = (LeMetaEventHeader*)&pdata[sizeof(HCIEventHdr)]; + + if (leMetaHeader->subevent == EVT_LE_CONN_COMPLETE) { + struct __attribute__ ((packed)) EvtLeConnectionComplete { + uint8_t status; + uint16_t handle; + uint8_t role; + uint8_t peerBdaddrType; + uint8_t peerBdaddr[6]; + uint16_t interval; + uint16_t latency; + uint16_t supervisionTimeout; + uint8_t masterClockAccuracy; + } *leConnectionComplete = (EvtLeConnectionComplete*)&pdata[sizeof(HCIEventHdr) + sizeof(LeMetaEventHeader)]; + + if (leConnectionComplete->status == 0x00) { + ATT.addConnection(leConnectionComplete->handle, + leConnectionComplete->role, + leConnectionComplete->peerBdaddrType, + leConnectionComplete->peerBdaddr, + leConnectionComplete->interval, + leConnectionComplete->latency, + leConnectionComplete->supervisionTimeout, + leConnectionComplete->masterClockAccuracy); + + L2CAPSignaling.addConnection(leConnectionComplete->handle, + leConnectionComplete->role, + leConnectionComplete->peerBdaddrType, + leConnectionComplete->peerBdaddr, + leConnectionComplete->interval, + leConnectionComplete->latency, + leConnectionComplete->supervisionTimeout, + leConnectionComplete->masterClockAccuracy); + } + } + } +} + +void HCIClass::dumpPkt(const char* prefix, uint8_t plen, uint8_t pdata[]) +{ + if (_debug) { + _debug->print(prefix); + + for (uint8_t i = 0; i < plen; i++) { + byte b = pdata[i]; + + if (b < 16) { + _debug->print("0"); + } + + _debug->print(b, HEX); + } + + _debug->println(); + _debug->flush(); + } +} + +HCIClass HCI; diff --git a/src/utility/HCI.h b/src/utility/HCI.h new file mode 100644 index 00000000..8235c1a6 --- /dev/null +++ b/src/utility/HCI.h @@ -0,0 +1,90 @@ +/* + This file is part of the ArduinoBLE library. + Copyright (c) 2018 Arduino SA. All rights reserved. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#ifndef _HCI_H_ +#define _HCI_H_ + +#include + +class HCIClass { +public: + HCIClass(); + virtual ~HCIClass(); + + int begin(); + void end(); + + void poll(); + void poll(unsigned long timeout); + + int reset(); + int readLocalVersion(uint8_t& hciVer, uint16_t& hciRev, uint8_t& lmpVer, + uint16_t& manufacturer, uint16_t& lmpSubVer); + int readBdAddr(uint8_t addr[6]); + + int readRssi(uint16_t handle); + + int setEventMask(uint64_t eventMask); + + int readLeBufferSize(uint16_t& pktLen, uint8_t& maxPkt); + int leSetRandomAddress(uint8_t addr[6]); + int leSetAdvertisingParameters(uint16_t minInterval, uint16_t maxInterval, + uint8_t advType, uint8_t ownBdaddrType, + uint8_t directBdaddrType, uint8_t directBdaddr[6], + uint8_t chanMap, + uint8_t filter); + int leSetAdvertisingData(uint8_t length, uint8_t data[]); + int leSetScanResponseData(uint8_t length, uint8_t data[]); + int leSetAdvertiseEnable(uint8_t enable); + int leConnUpdate(uint16_t handle, uint16_t minInterval, uint16_t maxInterval, + uint16_t latency, uint16_t supervisionTimeout); + + int sendAclPkt(uint16_t handle, uint8_t cid, uint8_t plen, void* data); + + int disconnect(uint16_t handle); + + void debug(Stream& stream); + void noDebug(); + +private: + int sendCommand(uint16_t opcode, uint8_t plen = 0, void* parameters = NULL); + + void handleAclDataPkt(uint8_t plen, uint8_t pdata[]); + void handleNumCompPkts(uint16_t handle, uint16_t numPkts); + void handleEventPkt(uint8_t plen, uint8_t pdata[]); + + void dumpPkt(const char* prefix, uint8_t plen, uint8_t pdata[]); + + Stream* _debug; + + int _recvIndex; + uint8_t _recvBuffer[3 + 255]; + + uint16_t _cmdCompleteOpcode; + int _cmdCompleteStatus; + uint8_t _cmdResponseLen; + uint8_t* _cmdResponse; + + uint8_t _maxPkt; + uint8_t _pendingPkt; +}; + +extern HCIClass HCI; + +#endif diff --git a/src/utility/HCITransport.h b/src/utility/HCITransport.h new file mode 100644 index 00000000..9279977e --- /dev/null +++ b/src/utility/HCITransport.h @@ -0,0 +1,36 @@ +/* + This file is part of the ArduinoBLE library. + Copyright (c) 2018 Arduino SA. All rights reserved. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include + +class HCITransportInterface { +public: + virtual int begin() = 0; + virtual void end() = 0; + + virtual void wait(unsigned long timeout) = 0; + + virtual int available() = 0; + virtual int peek() = 0; + virtual int read() = 0; + + virtual size_t write(const uint8_t* data, size_t length) = 0; +}; + +extern HCITransportInterface& HCITransport; diff --git a/src/utility/HCIUartTransport.cpp b/src/utility/HCIUartTransport.cpp new file mode 100644 index 00000000..eabc00f1 --- /dev/null +++ b/src/utility/HCIUartTransport.cpp @@ -0,0 +1,93 @@ +/* + This file is part of the ArduinoBLE library. + Copyright (c) 2018 Arduino SA. All rights reserved. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include "HCIUartTransport.h" + +#if defined(ARDUINO_SAMD_MKRWIFI1010) || defined(ARDUINO_AVR_UNO_WIFI_REV2) +#define SerialHCI Serial2 +#else +#error "Unsupported board selected!" +#endif + +HCIUartTransportClass::HCIUartTransportClass(HardwareSerial& uart, unsigned long baudrate) : + _uart(&uart), + _baudrate(baudrate) +{ +} + +HCIUartTransportClass::~HCIUartTransportClass() +{ +} + +int HCIUartTransportClass::begin() +{ + _uart->begin(_baudrate); + + return 1; +} + +void HCIUartTransportClass::end() +{ + _uart->end(); +} + +void HCIUartTransportClass::wait(unsigned long timeout) +{ + for (unsigned long start = millis(); (millis() - start) < timeout;) { + if (available()) { + break; + } + } +} + +int HCIUartTransportClass::available() +{ + return _uart->available(); +} + +int HCIUartTransportClass::peek() +{ + return _uart->peek(); +} + +int HCIUartTransportClass::read() +{ + return _uart->read(); +} + +size_t HCIUartTransportClass::write(const uint8_t* data, size_t length) +{ +#ifdef ARDUINO_AVR_UNO_WIFI_REV2 + // wait while the CTS pin is low + while (digitalRead(NINA_CTS) == HIGH); +#endif + + size_t result = _uart->write(data, length); + + _uart->flush(); + + return result; +} + +#ifdef ARDUINO_AVR_UNO_WIFI_REV2 +HCIUartTransportClass HCIUartTransport(SerialHCI, 119600); +#else +HCIUartTransportClass HCIUartTransport(SerialHCI, 912600); +#endif +HCITransportInterface& HCITransport = HCIUartTransport; diff --git a/src/utility/HCIUartTransport.h b/src/utility/HCIUartTransport.h new file mode 100644 index 00000000..619acc28 --- /dev/null +++ b/src/utility/HCIUartTransport.h @@ -0,0 +1,41 @@ +/* + This file is part of the ArduinoBLE library. + Copyright (c) 2018 Arduino SA. All rights reserved. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include "HCITransport.h" + +class HCIUartTransportClass : public HCITransportInterface { +public: + HCIUartTransportClass(HardwareSerial& uart, unsigned long baudrate); + virtual ~HCIUartTransportClass(); + + virtual int begin(); + virtual void end(); + + virtual void wait(unsigned long timeout); + + virtual int available(); + virtual int peek(); + virtual int read(); + + virtual size_t write(const uint8_t* data, size_t length); + +private: + HardwareSerial* _uart; + unsigned long _baudrate; +}; diff --git a/src/utility/L2CAPSignaling.cpp b/src/utility/L2CAPSignaling.cpp new file mode 100644 index 00000000..f650cab2 --- /dev/null +++ b/src/utility/L2CAPSignaling.cpp @@ -0,0 +1,149 @@ +/* + This file is part of the ArduinoBLE library. + Copyright (c) 2018 Arduino SA. All rights reserved. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include "HCI.h" + +#include "L2CAPSignaling.h" + +#define CONNECTION_PARAMETER_UPDATE_REQUEST 0x12 +#define CONNECTION_PARAMETER_UPDATE_RESPONSE 0x13 + +L2CAPSignalingClass::L2CAPSignalingClass() : + _minInterval(0), + _maxInterval(0) +{ +} + +L2CAPSignalingClass::~L2CAPSignalingClass() +{ +} + +void L2CAPSignalingClass::addConnection(uint16_t handle, uint8_t role, uint8_t /*peerBdaddrType*/, + uint8_t /*peerBdaddr*/[6], uint16_t interval, + uint16_t /*latency*/, uint16_t /*supervisionTimeout*/, + uint8_t /*masterClockAccuracy*/) +{ + if (role != 1) { + // ignore + return; + } + + if (!_minInterval || !_maxInterval) { + // no connection intervale to request + return; + } + + if (interval >= _minInterval && interval <= _maxInterval) { + // all good, within interval range + return; + } + + struct __attribute__ ((packed)) L2CAPConnectionParameterUpdateRequest { + uint8_t code; + uint8_t identifier; + uint16_t length; + uint16_t minInterval; + uint16_t maxInterval; + uint16_t latency; + uint16_t supervisionTimeout; + } request = { CONNECTION_PARAMETER_UPDATE_REQUEST, 0x01, 8, + _minInterval, _maxInterval, 0x0000, 0x00c8 }; + + HCI.sendAclPkt(handle, SIGNALING_CID, sizeof(request), &request); +} + +void L2CAPSignalingClass::handleData(uint16_t connectionHandle, uint8_t dlen, uint8_t data[]) +{ + struct __attribute__ ((packed)) L2CAPSignalingHdr { + uint8_t code; + uint8_t identifier; + uint16_t length; + } *l2capSignalingHdr = (L2CAPSignalingHdr*)data; + + if (dlen < sizeof(L2CAPSignalingHdr)) { + // too short, ignore + return; + } + + if (dlen != (sizeof(L2CAPSignalingHdr) + l2capSignalingHdr->length)) { + // invalid length, ignore + return; + } + + uint8_t code = l2capSignalingHdr->code; + uint8_t identifier = l2capSignalingHdr->identifier; + uint16_t length = l2capSignalingHdr->length; + data = &data[sizeof(L2CAPSignalingHdr)]; + + if (code == CONNECTION_PARAMETER_UPDATE_REQUEST) { + connectionParameterUpdateRequest(connectionHandle, identifier, length, data); + } else if (code == CONNECTION_PARAMETER_UPDATE_RESPONSE) { + connectionParameterUpdateResponse(connectionHandle, identifier, length, data); + } +} + +void L2CAPSignalingClass::removeConnection(uint8_t /*handle*/, uint16_t /*reason*/) +{ +} + +void L2CAPSignalingClass::setConnectionInterval(uint16_t minInterval, uint16_t maxInterval) +{ + _minInterval = minInterval; + _maxInterval = maxInterval; +} + +void L2CAPSignalingClass::connectionParameterUpdateRequest(uint16_t handle, uint8_t identifier, uint8_t dlen, uint8_t data[]) +{ + struct __attribute__ ((packed)) L2CAPConnectionParameterUpdateRequest { + uint16_t minInterval; + uint16_t maxInterval; + uint16_t latency; + uint16_t supervisionTimeout; + } *request = (L2CAPConnectionParameterUpdateRequest*)data; + + if (dlen < sizeof(L2CAPConnectionParameterUpdateRequest)) { + // too short, ignore + return; + } + + struct __attribute__ ((packed)) L2CAPConnectionParameterUpdateResponse { + uint8_t code; + uint8_t identifier; + uint16_t length; + uint16_t value; + } response = { CONNECTION_PARAMETER_UPDATE_RESPONSE, identifier, 2, 0x0000 }; + + if (_minInterval && _maxInterval) { + if (request->minInterval < _minInterval || request->maxInterval > _maxInterval) { + response.value = 0x0001; // reject + } + } + + HCI.sendAclPkt(handle, SIGNALING_CID, sizeof(response), &response); + + if (response.value == 0x0000) { + HCI.leConnUpdate(handle, request->minInterval, request->maxInterval, request->latency, request->supervisionTimeout); + } +} + +void L2CAPSignalingClass::connectionParameterUpdateResponse(uint16_t /*handle*/, uint8_t /*identifier*/, uint8_t /*dlen*/, uint8_t /*data*/[]) +{ +} + +L2CAPSignalingClass L2CAPSignaling; diff --git a/src/utility/L2CAPSignaling.h b/src/utility/L2CAPSignaling.h new file mode 100644 index 00000000..349ffb85 --- /dev/null +++ b/src/utility/L2CAPSignaling.h @@ -0,0 +1,54 @@ +/* + This file is part of the ArduinoBLE library. + Copyright (c) 2018 Arduino SA. All rights reserved. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#ifndef _L2CAP_SIGNALING_H_ +#define _L2CAP_SIGNALING_H_ + +#include + +#define SIGNALING_CID 0x0005 + +class L2CAPSignalingClass { +public: + L2CAPSignalingClass(); + virtual ~L2CAPSignalingClass(); + + void addConnection(uint16_t handle, uint8_t role, uint8_t peerBdaddrType, + uint8_t peerBdaddr[6], uint16_t interval, + uint16_t latency, uint16_t supervisionTimeout, + uint8_t masterClockAccuracy); + + void handleData(uint16_t connectionHandle, uint8_t dlen, uint8_t data[]); + + void removeConnection(uint8_t handle, uint16_t reason); + + void setConnectionInterval(uint16_t minInterval, uint16_t maxInterval); + +private: + void connectionParameterUpdateRequest(uint16_t handle, uint8_t identifier, uint8_t dlen, uint8_t data[]); + void connectionParameterUpdateResponse(uint16_t handle, uint8_t identifier, uint8_t dlen, uint8_t data[]); + +private: + uint16_t _minInterval; + uint16_t _maxInterval; +}; + +extern L2CAPSignalingClass L2CAPSignaling; + +#endif