diff --git a/BUILDS.md b/BUILDS.md
index 9cf437df2..9e4e847d0 100644
--- a/BUILDS.md
+++ b/BUILDS.md
@@ -210,6 +210,7 @@
| USE_DISPLAY_SSD1351 | - | - | - | - | - | - | x |
| USE_DISPLAY_RA8876 | - | - | - | - | - | - | x |
| USE_DISPLAY_ST7789 | - | - | - | - | - | - | x |
+| USE_DISPLAY_TM1637 | - | - | - | - | - | - | x |
| | | | | | | | |
| USE_FT5206 | - | - | - | - | - | - | - |
| USE_FTC532 | - | - | - | - | - | - | - |
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 06044e3e7..5f05cb8d3 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -13,8 +13,10 @@ All notable changes to this project will be documented in this file.
- Support for Frysk language translations by Christiaan Heerze
- ESP8266 Fallback to ``*.bin.gz`` binary when OTA upload of ``*.bin`` binary fails
- Berry language improved Tasmota integration
+- Berry file system support
- Filesystem commands ``Ufs``, ``UfsType``, ``UfsSize``, ``UfsFree``, ``UfsDelete``, ``UfsRename`` and ``UfsRun``
-- Basic support for filesystem ``autoexec.bat``
+- Support for filesystem ``autoexec.bat`` to execute sequential commands like backlog
+- Support for TM1637 seven segment display by Ajith Vasudevan (#10889)
### Changed
- IRremoteESP8266 library from v2.7.14 to v2.7.15
diff --git a/RELEASENOTES.md b/RELEASENOTES.md
index 70ca1ce08..8c48f748e 100644
--- a/RELEASENOTES.md
+++ b/RELEASENOTES.md
@@ -96,7 +96,7 @@ The attached binaries can also be downloaded from http://ota.tasmota.com/tasmota
- Commands ``ZbNoAutoBind``, ``ZbReceivedTopic`` and ``ZbOmitDevice`` as synonyms for ``SetOption116, 118`` and ``119``
- Commands ``BuzzerActive`` and ``BuzzerPwm`` as synonyms for ``SetOption67`` and ``111``
- Filesystem commands ``Ufs``, ``UfsType``, ``UfsSize``, ``UfsFree``, ``UfsDelete``, ``UfsRename`` and ``UfsRun``
-- Basic support for filesystem ``autoexec.bat``
+- Support for filesystem ``autoexec.bat`` to execute sequential commands like backlog
- Milliseconds to console output [#10152](https://github.com/arendst/Tasmota/issues/10152)
- Gpio ``Option_a1`` enabling PWM2 high impedance if powered off as used by Wyze bulbs [#10196](https://github.com/arendst/Tasmota/issues/10196)
- Rotary No Pullup GPIO selection ``Rotary A/B_n`` [#10407](https://github.com/arendst/Tasmota/issues/10407)
@@ -112,6 +112,7 @@ The attached binaries can also be downloaded from http://ota.tasmota.com/tasmota
- Support for IR inverted leds using ``#define IR_SEND_INVERTED true`` [#10301](https://github.com/arendst/Tasmota/issues/10301)
- Support for disabling 38kHz IR modulation using ``#define IR_SEND_USE_MODULATION false`` [#10301](https://github.com/arendst/Tasmota/issues/10301)
- Support for SPI display driver for ST7789 TFT by Gerhard Mutz [#9037](https://github.com/arendst/Tasmota/issues/9037)
+- Support for TM1637 seven segment display by Ajith Vasudevan [#10889](https://github.com/arendst/Tasmota/issues/10889)
- Support for time proportioned (``#define USE_TIMEPROP``) and optional PID (``#define USE_PID``) relay control [#10412](https://github.com/arendst/Tasmota/issues/10412)
- Support for 24/26/32/34 bit RFID Wiegand interface (D0/D1) by Sigurd Leuther [#3647](https://github.com/arendst/Tasmota/issues/3647)
- Support for SM2135 current selection using GPIO ``SM2135 DAT`` index [#10634](https://github.com/arendst/Tasmota/issues/10634)
@@ -121,6 +122,7 @@ The attached binaries can also be downloaded from http://ota.tasmota.com/tasmota
- Support for ESP32 ``Module 3`` Odroid Go 16MB binary tasmota32-odroidgo.bin [#8630](https://github.com/arendst/Tasmota/issues/8630)
- Support for ESP32 ``Module 5`` Wireless Tag Eth01 [#9496](https://github.com/arendst/Tasmota/issues/9496)
- Support for ESP32 ``Module 7`` M5stack core2 16MB binary tasmota32-core2.bin [#10635](https://github.com/arendst/Tasmota/issues/10635)
+- Support for Berry language on ESP32
- Support rotary encoder on Shelly Dimmer [#10407](https://github.com/arendst/Tasmota/issues/10407#issuecomment-756240920)
- Support character `#` to be replaced by `space`-character in command ``Publish`` topic [#10258](https://github.com/arendst/Tasmota/issues/10258)
- Support trailing silence in buzzer tune [#10694](https://github.com/arendst/Tasmota/issues/10694)
diff --git a/lib/lib_display/Display_Renderer-gemu-1.0/src/renderer.cpp b/lib/lib_display/Display_Renderer-gemu-1.0/src/renderer.cpp
index 76d78e077..641645ef1 100644
--- a/lib/lib_display/Display_Renderer-gemu-1.0/src/renderer.cpp
+++ b/lib/lib_display/Display_Renderer-gemu-1.0/src/renderer.cpp
@@ -540,14 +540,10 @@ void Renderer::drawPixel(int16_t x, int16_t y, uint16_t color) {
}
-extern uint16_t index_colors[MAX_INDEXCOLORS];
// this is called for every driver
void Renderer::setDrawMode(uint8_t mode) {
drawmode=mode;
- for (uint32_t count = 0; count < MAX_INDEXCOLORS; count++) {
- index_colors[count] = GetColorFromIndex(count);
- }
}
void Renderer::invertDisplay(boolean i) {
diff --git a/lib/lib_display/arduino-tm1637/.gitignore b/lib/lib_display/arduino-tm1637/.gitignore
new file mode 100755
index 000000000..f46c84016
--- /dev/null
+++ b/lib/lib_display/arduino-tm1637/.gitignore
@@ -0,0 +1,6 @@
+build
+libraries
+sketches
+/.project
+*.h.gch
+.vscode
\ No newline at end of file
diff --git a/lib/lib_display/arduino-tm1637/.travis.yml b/lib/lib_display/arduino-tm1637/.travis.yml
new file mode 100755
index 000000000..5a6aaca4c
--- /dev/null
+++ b/lib/lib_display/arduino-tm1637/.travis.yml
@@ -0,0 +1,17 @@
+language: c
+before_install:
+ - "/sbin/start-stop-daemon --start --quiet --pidfile /tmp/custom_xvfb_1.pid --make-pidfile --background --exec /usr/bin/Xvfb -- :1 -ac -screen 0 1280x1024x16"
+ - sleep 3
+ - export DISPLAY=:1.0
+ - wget https://downloads.arduino.cc/arduino-1.8.12-linux64.tar.xz
+ - tar xf arduino-1.8.12-linux64.tar.xz
+ - sudo mv arduino-1.8.12 /usr/local/share/arduino
+ - sudo ln -s /usr/local/share/arduino/arduino /usr/local/bin/arduino
+install:
+ - ln -s $PWD /usr/local/share/arduino/libraries/SevenSegmentTM1637
+script:
+ - arduino --verify --board arduino:avr:uno --pref sketchbook.path="$PWD" $PWD/examples/Basic/Basic.ino
+notifications:
+ email:
+ on_success: change
+ on_failure: change
diff --git a/lib/lib_display/arduino-tm1637/LICENSE b/lib/lib_display/arduino-tm1637/LICENSE
new file mode 100644
index 000000000..8cdb8451d
--- /dev/null
+++ b/lib/lib_display/arduino-tm1637/LICENSE
@@ -0,0 +1,340 @@
+ GNU GENERAL PUBLIC LICENSE
+ Version 2, June 1991
+
+ Copyright (C) 1989, 1991 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.
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users. This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it. (Some other Free Software Foundation software is covered by
+the GNU Lesser General Public License instead.) You can apply it to
+your programs, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+ To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have. You must make sure that they, too, receive or can get the
+source code. And you must show them these terms so they know their
+rights.
+
+ We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+ Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software. If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+ Finally, any free program is threatened constantly by software
+patents. We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary. To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ GNU GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License. The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language. (Hereinafter, translation is included without limitation in
+the term "modification".) Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+ 1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+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 Program or any portion
+of it, thus forming a work based on the Program, 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) You must cause the modified files to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ b) You must cause any work that you distribute or publish, that in
+ whole or in part contains or is derived from the Program or any
+ part thereof, to be licensed as a whole at no charge to all third
+ parties under the terms of this License.
+
+ c) If the modified program normally reads commands interactively
+ when run, you must cause it, when started running for such
+ interactive use in the most ordinary way, to print or display an
+ announcement including an appropriate copyright notice and a
+ notice that there is no warranty (or else, saying that you provide
+ a warranty) and that users may redistribute the program under
+ these conditions, and telling the user how to view a copy of this
+ License. (Exception: if the Program itself is interactive but
+ does not normally print such an announcement, your work based on
+ the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Program,
+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 Program, 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 Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+ a) 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; or,
+
+ b) Accompany it with a written offer, valid for at least three
+ years, to give any third party, for a charge no more than your
+ cost of physically performing source distribution, a complete
+ machine-readable copy of the corresponding source code, to be
+ distributed under the terms of Sections 1 and 2 above on a medium
+ customarily used for software interchange; or,
+
+ c) Accompany it with the information you received as to the offer
+ to distribute corresponding source code. (This alternative is
+ allowed only for noncommercial distribution and only if you
+ received the program in object code or executable form with such
+ an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it. For an executable work, 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 executable. However, as a
+special exception, the source code 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.
+
+If distribution of executable or 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 counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+ 4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License. Any attempt
+otherwise to copy, modify, sublicense or distribute the Program 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.
+
+ 5. 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 Program or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+ 6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program 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 to
+this License.
+
+ 7. 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 Program at all. For example, if a patent
+license would not permit royalty-free redistribution of the Program 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 Program.
+
+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.
+
+ 8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program 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.
+
+ 9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Program
+specifies 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 Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+ 10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, 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
+
+ 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+ 12. 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 PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+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 program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along
+ with this program; if not, 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.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+ Gnomovision version 69, Copyright (C) year name of author
+ Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+ This is free software, and you are welcome to redistribute it
+ under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License. Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary. Here is a sample; alter the names:
+
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+ `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+ {signature of Ty Coon}, 1 April 1989
+ Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs. If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library. If this is what you want to do, use the GNU Lesser General
+Public License instead of this License.
+
diff --git a/lib/lib_display/arduino-tm1637/README.md b/lib/lib_display/arduino-tm1637/README.md
new file mode 100644
index 000000000..1edd60d1d
--- /dev/null
+++ b/lib/lib_display/arduino-tm1637/README.md
@@ -0,0 +1,150 @@
+# SevenSegmentTM1637 Arduino Library
+
+* master [](https://travis-ci.org/bremme/arduino-tm1637)
+* develop [](https://travis-ci.org/bremme/arduino-tm1637)
+
+
+Arduino library for controlling a TM163x based 7-segment display module. These modules are sold under various names by various suppliers. For example:
+
+* [Seed Studio: Grove 4 digit display](http://www.seeedstudio.com/depot/grove-4digit-display-p-1198.html)
+* [Ebay: 4 Bits Digital Tube LED TM1637](http://www.ebay.com/sch/i.html?_odkw=4+Bits+Digital+Tube+LED&_osacat=0&_from=R40&_trksid=p2045573.m570.l1313.TR0.TRC0.H0.X4+Bits+Digital+Tube+LED+TM1637.TRS0&_nkw=4+Bits+Digital+Tube+LED+TM1637&_sacat=0)
+* [DealExtreme: LED 4-Digit Display Module](http://www.dx.com/s/TM1637)
+
+They come in different sizes, colors and there is a clock (with a colon) and decimal (with four decimal dots) display variant. But the most common one is the red 0.36" clock version, this is also the cheapest one (you can get those for about $1.50). I've written this library using the above module, if I get my hands on any of the other variants I might add some specific code, for example to print floats on the decimal version.
+
+
+
+# Hardware setup
+
+| TM1637 PIN | Arduino PIN | Description |
+|------------|------------------|---------------------|
+| CLK | Any digital pin | Clock |
+| DIO | Any digital pin | Digital output |
+| VCC | 5V | Supply voltage |
+| GND | GND | Ground |
+
+
+# Installation
+
+Like any other Arduino library this library is installed by copying the files into a directory on the Arduino IDE search path. Most common is to put all files in a director in `your sketch folder/libraries/SevenSegmentTM1637/`. See [installing additional Arduino libraries](https://www.arduino.cc/en/Guide/Libraries) for more information.
+
+# Usage
+
+This library uses the [LCD API v1.0](http://playground.arduino.cc/Code/LCDAPI) so you can use the same functions/methods using this library as any lcd library which conforms to the LCD API. Furthermore this library (like described in the LCD API) inherent the Print class (See [Serial.print()](https://www.arduino.cc/en/Serial/Print) and for more details [Print.h](https://github.com/arduino/Arduino/blob/master/hardware/arduino/avr/cores/arduino/Print.h)). This means that you can use all Print class functions/methods like you're used to when you're doing `Serial.print("Something")` or `Serial.print(128, BIN)` for example.
+
+## Basic methods
+
+* `SevenSegmentTM1637(clkPin, dioPin)`Creates a display object
+* `init()` Initializes the display
+* `print(value)` Prints anything to the display (e.g. a string, number..)
+* `clear()` Clears the display (and resets cursor)
+* `home()` Resets cursor
+* `setCursor(row, col)` Set the cursor to specified position
+* `setBacklight(value)` Sets the brightness of the display
+* `on()` Turn the display on (set brightness to default)
+* `off()` Turns the display off (set brightness to zero)
+* `blink()` Blinks what ever is on the display
+* `setColonOn(bool)` Sets the colon in the middle of the display
+* `setPrintDelay(time)` Sets the delay when printing more than 4 characters
+
+## Advanced methods
+
+* `encode()` Encodes a single digit, single char or char array (C string)
+* `printRaw()` Prints raw byte(s) to the display
+* `command()` Sends one or more raw byte commands to the display
+
+## Low level methods
+
+* `comStart()` Serial communication start command
+* `comWriteByte(bytes)` Serial communication send byte command
+* `comAck()` Serial communication get acknowledged command
+* `comStop()` Serial communication stop command
+
+If you still want or need more functionality, I've included two super classes:
+
+* `SevenSegmentExtended()` Extends the base class with more usefull functions.
+* `SevenSegmentFun()` Even more extensions for fun.
+
+If you use any of these super classes, you will also get all the basic, advanced and low level methods as well. If you use the fun class extension you will get the extended class methods as well.
+
+## Extended class extra methods
+
+* `SevenSegmentExtended(clkPin, dioPin)` Creates a display object
+* `printTime(hour, min, [blink], [blinkDelay])` Prints the time to the display
+* `printTime(time, [blink], [blinkDelay])` Prints the time to the display
+* `printDualCounter(leftValue, rightValue)` Prints two digits to the display
+
+## Fun class extra methods
+
+* `SevenSegmentFun(clkPin, dioPin)` Creates a display object
+* `printLevelVertical(level, [leftToRight])` Print 0-100% (2 steps per digit) vertical level e.g. volume, battery
+* `printLevelVertical(level, leftToRight, symbol)` Print 0-100% (1 step per digit) vertical level using custom symbol
+* `printLevelHorizontal(levels[])` Prints 4 horizontal levels e.g. equalizer
+* `scrollingText()` Prints text and (keeps) scrolling
+* `snake()` Classic snake demo
+* `nightrider()` Nightrider Kit demo
+* `bombTimer()` Count down a (bomb) timer
+* `bouncingBall()` Bouncing ball demo
+
+For more extended information on what arguments all above functions accept and return see the header files of the classes (SevenSegmentTM1637.h, SevenSegmentExtended.h and SevenSegmentFun.h).
+
+# Trouble shooting
+
+* Some boards might not have enough power on their 5V pin. In that case try to use an external 5V power supply. People reported that the ESP8266 might be one of those boards.
+
+# Todo
+
+* Refactor library to make it more modular and support more chips
+ + Add support for all TM16xx chips (most should already work)
+ + Add support for MAX7219 displays
+ + Add support for TM74HC595 displays
+
+# Changelog
+
++ 09-07-2020 version 1.1.1
+ + Bug fixes
+ + fixed a typo in SevenSegmentFun.h file
++ 04-07-2020 version 1.1.0
+ + Improved `printLevelVertical()` using [pablo-lp](https://github.com/pablo-lp) suggestions
+ + The default `printLevelVertical()` can diplay twice the number of levels now (e.g. 9 levels for a defaulf 4 digit display)
+ + Add configurable blink delay when using the `printTime` methods (thanks to [simoneluconi](https://github.com/simoneluconi))
+ + Added a new `printNumber()` method to make it easier to print right aligned numbers
+ + Thanks [dan2600](https://github.com/dan2600), [Bilick88](https://github.com/Bilick88), [jasonacox](https://github.com/jasonacox) for your suggestions.
+ + Bug fixes
+ + Merge PR from [berendkleinhaneveld](https://github.com/berendkleinhaneveld) to fix some compiler warnings
+ + Merge PR from [facelessloser](https://github.com/facelessloser) Remove some `Serial.prinln()` calls
+ + Merge PR from [RAnders00](https://github.com/RAnders00) Fix incorrect repsonse from `comReadByte()`
+ + Merge PR from [per1234](https://github.com/per1234) Use correct separator in `keywords.txt`
++ 04-11-2016 version 1.0.4
+ + Fixed bombTimer not counting down to zero (thanks to [foons](https://github.com/fooons) for opening an issue)
+* 22-05-2016 version 1.0.3
+ + add support for all AVR MCU's (thanks to [per1234](https://github.com/per1234))
+* 08-05-2016 version 1.0.2
+ + fixed digitalHigh() macro for non AVR boards (thanks to [per1234](https://github.com/per1234))
+* 28-09-2015 version 1.0.1
+ + fixed folder structure to comply with Arduino library 1.5 rev 2 specifications
+* 28-09-2015 version 1.0.0
+ + First release
+
+
+# Note
+
+I spend quite a bit of time to build this library. I hope it will be useful to others. I decided to publish it, although there still might be small bugs. If you find one, just let me know and I will try to fix it. If you have any other suggestion, don't hesitate to contact me.
+
+# Sources
+
+I've looked at many sources while constructing this library, bassicly all alternative Arduino TM1637 libraries and some AVR TM1637 libraries. While doing so I've found some really nice improvements compared to other implementations. For example, most libraries use a really low clock speed (~50us), I've tested with clock speeds as low as 1us and the chip still seems to respond well. Furthermore, from the (Chinese) datasheet it seems that you always have to perform three operation when communicating with the IC; set some configuration, set the address and send 1-4 digit data and set the display brightness. I've found out that this is not the case, you can do all of those separately if you want.
+
+Still without all these fine examples it would have taken me a lot more time to figure out the inner workings of this IC!
+
+Sources:
+* http://blog.3d-logic.com/2015/01/21/arduino-and-the-tm1637-4-digit-seven-segment-display/
+* http://www.arduino.md/hardware/lcd-and-leds/0-36-led-display-4-digit-red/
+* https://brainy-bits.com/tutorials/4-bits-7-segment-led-display-with-arduino/
+
+
+#### Keywords
+
+People will probably Google for keywords when looking for a driver, so here are some:
+
+```TM1637 TM1636 library Arduino Led Driver 4 Digit Bits Digital LED Tube module LCD API Print.h Print class API display LCD AVR Atmega```
diff --git a/lib/lib_display/arduino-tm1637/SevenSegmentAsciiMap.cpp b/lib/lib_display/arduino-tm1637/SevenSegmentAsciiMap.cpp
new file mode 100644
index 000000000..e166f6903
--- /dev/null
+++ b/lib/lib_display/arduino-tm1637/SevenSegmentAsciiMap.cpp
@@ -0,0 +1,100 @@
+#include "SevenSegmentAsciiMap.h"
+
+// store an ASCII Map in PROGMEM (Flash memory)
+const uint8_t AsciiMap::map[] PROGMEM = {
+ TM1637_CHAR_SPACE,
+ TM1637_CHAR_EXC,
+ TM1637_CHAR_D_QUOTE,
+ TM1637_CHAR_POUND,
+ TM1637_CHAR_DOLLAR,
+ TM1637_CHAR_PERC,
+ TM1637_CHAR_AMP,
+ TM1637_CHAR_S_QUOTE,
+ TM1637_CHAR_L_BRACKET,
+ TM1637_CHAR_R_BRACKET,
+ TM1637_CHAR_STAR,
+ TM1637_CHAR_PLUS,
+ TM1637_CHAR_COMMA,
+ TM1637_CHAR_MIN,
+ TM1637_CHAR_DOT,
+ TM1637_CHAR_F_SLASH,
+ TM1637_CHAR_0, // 48 (ASCII)
+ TM1637_CHAR_1,
+ TM1637_CHAR_2,
+ TM1637_CHAR_3,
+ TM1637_CHAR_4,
+ TM1637_CHAR_5,
+ TM1637_CHAR_6,
+ TM1637_CHAR_7,
+ TM1637_CHAR_8,
+ TM1637_CHAR_9,
+ TM1637_CHAR_COLON,
+ TM1637_CHAR_S_COLON,
+ TM1637_CHAR_LESS,
+ TM1637_CHAR_EQUAL,
+ TM1637_CHAR_GREAT,
+ TM1637_CHAR_QUEST,
+ TM1637_CHAR_AT,
+ TM1637_CHAR_A, // 65 (ASCII)
+ TM1637_CHAR_B,
+ TM1637_CHAR_C,
+ TM1637_CHAR_D,
+ TM1637_CHAR_E,
+ TM1637_CHAR_F,
+ TM1637_CHAR_G,
+ TM1637_CHAR_H,
+ TM1637_CHAR_I,
+ TM1637_CHAR_J,
+ TM1637_CHAR_K,
+ TM1637_CHAR_L,
+ TM1637_CHAR_M,
+ TM1637_CHAR_N,
+ TM1637_CHAR_O,
+ TM1637_CHAR_P,
+ TM1637_CHAR_Q,
+ TM1637_CHAR_R,
+ TM1637_CHAR_S,
+ TM1637_CHAR_T,
+ TM1637_CHAR_U,
+ TM1637_CHAR_V,
+ TM1637_CHAR_W,
+ TM1637_CHAR_X,
+ TM1637_CHAR_Y,
+ TM1637_CHAR_Z,
+ TM1637_CHAR_L_S_BRACKET, // 91 (ASCII)
+ TM1637_CHAR_B_SLASH,
+ TM1637_CHAR_R_S_BRACKET,
+ TM1637_CHAR_A_CIRCUM,
+ TM1637_CHAR_UNDERSCORE,
+ TM1637_CHAR_A_GRAVE, // 96 (ASCII)
+ TM1637_CHAR_a,
+ TM1637_CHAR_b,
+ TM1637_CHAR_c,
+ TM1637_CHAR_d,
+ TM1637_CHAR_e,
+ TM1637_CHAR_f,
+ TM1637_CHAR_g,
+ TM1637_CHAR_h,
+ TM1637_CHAR_i,
+ TM1637_CHAR_j,
+ TM1637_CHAR_k,
+ TM1637_CHAR_l,
+ TM1637_CHAR_m,
+ TM1637_CHAR_n,
+ TM1637_CHAR_o,
+ TM1637_CHAR_p,
+ TM1637_CHAR_q,
+ TM1637_CHAR_r,
+ TM1637_CHAR_s,
+ TM1637_CHAR_t,
+ TM1637_CHAR_u,
+ TM1637_CHAR_v,
+ TM1637_CHAR_w,
+ TM1637_CHAR_x,
+ TM1637_CHAR_y,
+ TM1637_CHAR_z,
+ TM1637_CHAR_L_ACCON, // 123 (ASCII)
+ TM1637_CHAR_BAR,
+ TM1637_CHAR_R_ACCON,
+ TM1637_CHAR_TILDE // 126 (ASCII)
+};
\ No newline at end of file
diff --git a/lib/lib_display/arduino-tm1637/SevenSegmentAsciiMap.h b/lib/lib_display/arduino-tm1637/SevenSegmentAsciiMap.h
new file mode 100644
index 000000000..e00ca2d34
--- /dev/null
+++ b/lib/lib_display/arduino-tm1637/SevenSegmentAsciiMap.h
@@ -0,0 +1,109 @@
+#ifndef SevenSegmentAsciiMap_H
+#define SevenSegmentAsciiMap_H
+
+#include
+
+// ASCII MAPPINGS
+#define TM1637_CHAR_SPACE B00000000 // 32 (ASCII)
+#define TM1637_CHAR_EXC B00000110
+#define TM1637_CHAR_D_QUOTE B00100010
+#define TM1637_CHAR_POUND B01110110
+#define TM1637_CHAR_DOLLAR B01101101
+#define TM1637_CHAR_PERC B00100100
+#define TM1637_CHAR_AMP B01111111
+#define TM1637_CHAR_S_QUOTE B00100000
+#define TM1637_CHAR_L_BRACKET B00111001
+#define TM1637_CHAR_R_BRACKET B00001111
+#define TM1637_CHAR_STAR B01011100
+#define TM1637_CHAR_PLUS B01010000
+#define TM1637_CHAR_COMMA B00010000
+#define TM1637_CHAR_MIN B01000000
+#define TM1637_CHAR_DOT B00001000
+#define TM1637_CHAR_F_SLASH B00000110
+#define TM1637_CHAR_0 B00111111 // 48
+#define TM1637_CHAR_1 B00000110
+#define TM1637_CHAR_2 B01011011
+#define TM1637_CHAR_3 B01001111
+#define TM1637_CHAR_4 B01100110
+#define TM1637_CHAR_5 B01101101
+#define TM1637_CHAR_6 B01111101
+#define TM1637_CHAR_7 B00000111
+#define TM1637_CHAR_8 B01111111
+#define TM1637_CHAR_9 B01101111
+#define TM1637_CHAR_COLON B00110000
+#define TM1637_CHAR_S_COLON B00110000
+#define TM1637_CHAR_LESS B01011000
+#define TM1637_CHAR_EQUAL B01001000
+#define TM1637_CHAR_GREAT B01001100
+#define TM1637_CHAR_QUEST B01010011
+#define TM1637_CHAR_AT B01011111
+#define TM1637_CHAR_A B01110111 // 65 (ASCII)
+#define TM1637_CHAR_B B01111111
+#define TM1637_CHAR_C B00111001
+#define TM1637_CHAR_D TM1637_CHAR_d
+#define TM1637_CHAR_E B01111001
+#define TM1637_CHAR_F B01110001
+#define TM1637_CHAR_G B00111101
+#define TM1637_CHAR_H B01110110
+#define TM1637_CHAR_I B00000110
+#define TM1637_CHAR_J B00001110
+#define TM1637_CHAR_K B01110101
+#define TM1637_CHAR_L B00111000
+#define TM1637_CHAR_M B00010101
+#define TM1637_CHAR_N B00110111
+#define TM1637_CHAR_O B00111111
+#define TM1637_CHAR_P B01110011
+#define TM1637_CHAR_Q B01100111
+#define TM1637_CHAR_R B00110011
+#define TM1637_CHAR_S B01101101
+#define TM1637_CHAR_T TM1637_CHAR_t
+#define TM1637_CHAR_U B00111110
+#define TM1637_CHAR_V B00011100
+#define TM1637_CHAR_W B00101010
+#define TM1637_CHAR_X TM1637_CHAR_H
+#define TM1637_CHAR_Y B01101110
+#define TM1637_CHAR_Z B01011011
+#define TM1637_CHAR_L_S_BRACKET B00111001 // 91 (ASCII)
+#define TM1637_CHAR_B_SLASH B00110000
+#define TM1637_CHAR_R_S_BRACKET B00001111
+#define TM1637_CHAR_A_CIRCUM B00010011
+#define TM1637_CHAR_UNDERSCORE B00001000
+#define TM1637_CHAR_A_GRAVE B00010000
+#define TM1637_CHAR_a B01011111 // 97 (ASCII)
+#define TM1637_CHAR_b B01111100
+#define TM1637_CHAR_c B01011000
+#define TM1637_CHAR_d B01011110
+#define TM1637_CHAR_e B01111011
+#define TM1637_CHAR_f TM1637_CHAR_F
+#define TM1637_CHAR_g B01101111
+#define TM1637_CHAR_h B01110100
+#define TM1637_CHAR_i B00000100
+#define TM1637_CHAR_j B00001100
+#define TM1637_CHAR_k TM1637_CHAR_K
+#define TM1637_CHAR_l B00110000
+#define TM1637_CHAR_m TM1637_CHAR_M
+#define TM1637_CHAR_n B01010100
+#define TM1637_CHAR_o B01011100
+#define TM1637_CHAR_p TM1637_CHAR_P
+#define TM1637_CHAR_q TM1637_CHAR_Q
+#define TM1637_CHAR_r B01010000
+#define TM1637_CHAR_s TM1637_CHAR_S
+#define TM1637_CHAR_t B01111000
+#define TM1637_CHAR_u B00011100
+#define TM1637_CHAR_v B00011100
+#define TM1637_CHAR_w TM1637_CHAR_W
+#define TM1637_CHAR_x TM1637_CHAR_X
+#define TM1637_CHAR_y B01100110
+#define TM1637_CHAR_z TM1637_CHAR_Z
+#define TM1637_CHAR_L_ACCON B01111001 // 123 (ASCII)
+#define TM1637_CHAR_BAR B00000110
+#define TM1637_CHAR_R_ACCON B01001111
+#define TM1637_CHAR_TILDE B01000000 // 126 (ASCII)
+
+class AsciiMap {
+public:
+ const static uint8_t map[96];
+};
+// static const uint8_t asciiMap[96];
+
+#endif
\ No newline at end of file
diff --git a/lib/lib_display/arduino-tm1637/SevenSegmentExtended.cpp b/lib/lib_display/arduino-tm1637/SevenSegmentExtended.cpp
new file mode 100644
index 000000000..ad76120e0
--- /dev/null
+++ b/lib/lib_display/arduino-tm1637/SevenSegmentExtended.cpp
@@ -0,0 +1,140 @@
+#include "SevenSegmentExtended.h"
+
+
+SevenSegmentExtended::SevenSegmentExtended(uint8_t pinClk, uint8_t pinDIO) :
+ SevenSegmentTM1637(pinClk, pinDIO)
+{ };
+
+void SevenSegmentExtended::printTime(uint16_t t, bool blink, uint16_t blinkDelay) {
+ uint16_t max = 2359;
+ t = ( t > max)?max:t;
+
+ uint8_t hour = t / 100;
+ t -= (hour * 100);
+ uint8_t min = t;
+ printTime(hour, min, blink);
+};
+
+void SevenSegmentExtended::printTime(uint8_t hour, uint8_t min, bool blink, uint16_t blinkDelay) {
+
+ bool colonWasOn = getColonOn();
+ setColonOn(true);
+
+ uint8_t buffer[4];
+
+ buffer[0] = encode(int16_t(hour / 10));
+ buffer[1] = encode(int16_t(hour % 10));
+ buffer[2] = encode(int16_t(min / 10));
+ buffer[3] = encode(int16_t(min % 10));
+
+ printRaw(buffer, 4, 0);
+
+ // turn colon off and on again
+ if (blink) {
+ delay(blinkDelay);
+ setColonOn(false);
+ printRaw(buffer[1],1);
+ delay(blinkDelay);
+ setColonOn(true);
+ printRaw(buffer[1],1);
+ }
+
+ setColonOn(colonWasOn);
+
+};
+
+void SevenSegmentExtended::printNumber(int16_t number, bool zeroPadding, bool rollOver, bool alignRight) {
+ const int16_t maxNumber = 9999;
+ const int16_t minNumber = -999;
+ bool positive = true;
+
+ // get and store sign
+ if (number < 0) {
+ positive = false;
+ };
+
+ // roll over if rollOver is set to true
+ if (rollOver == true) {
+ if (positive == true) {
+ number = number % int16_t(10000);
+ } else {
+ number = -1 * ((-1 * number) % 1000);
+ }
+ // limit number by default
+ } else {
+ number = number > maxNumber?maxNumber:number;
+ number = number < minNumber?minNumber:number;
+ }
+
+ // clear the display first
+ clear();
+
+ // align left is the default behavior, just forward to print
+ if (alignRight == false) {
+ print(number);
+ return;
+ }
+
+ if (positive == false) {
+ number *= -1;
+ print("-");
+ }
+
+ if (number > 999) {
+ setCursor(0, 0);
+ } else if (number > 99) {
+ if (zeroPadding == true) {print('0');}
+ setCursor(0, 1);
+ } else if (number > 9) {
+ if (zeroPadding == true) {print(F("00"));}
+ setCursor(0, 2);
+ } else {
+ if (zeroPadding == true) {print(F("000"));}
+ setCursor(0, 3);
+ };
+
+ print(number);
+};
+
+// positive counter 0..99, negative counter 0..-9
+void SevenSegmentExtended::printDualCounter(int8_t leftCounter, int8_t rightCounter, bool zeroPadding) {
+
+ int8_t max = 99;
+ int8_t min = -9;
+ uint8_t zeroByte = encode('0');
+
+ leftCounter = (leftCounter > max)?max:leftCounter;
+ leftCounter = (leftCounter < min)?min:leftCounter;
+ rightCounter = (rightCounter > max)?max:rightCounter;
+ rightCounter = (rightCounter < min)?min:rightCounter;
+
+ bool colonWasOn = getColonOn(); // get current state
+ setColonOn(true); // turn on the colon
+ home(); // set cursor to zero
+
+ if ( leftCounter < 10 && leftCounter >= 0) {
+ if ( zeroPadding ) {
+ printRaw(zeroByte,1);
+ setCursor(0,1);
+ } else {
+ // print colon
+ printRaw(TM1637_COLON_BIT,1);
+ };
+ };
+ print(leftCounter);
+
+ if ( rightCounter < 10 && rightCounter >= 0) {
+ if ( zeroPadding ) {
+ printRaw(zeroByte,2);
+ }
+ uint8_t pos = 2;
+ printRaw(0,pos);
+ setCursor(0,3);
+ } else {
+ setCursor(0,2);
+ }
+ print(rightCounter);
+
+ // set to previous state
+ setColonOn(colonWasOn);
+};
diff --git a/lib/lib_display/arduino-tm1637/SevenSegmentExtended.h b/lib/lib_display/arduino-tm1637/SevenSegmentExtended.h
new file mode 100644
index 000000000..43fccca1c
--- /dev/null
+++ b/lib/lib_display/arduino-tm1637/SevenSegmentExtended.h
@@ -0,0 +1,44 @@
+#ifndef SevenSegmentExtended_H
+#define SevenSegmentExtended_H
+
+#if ARDUINO >= 100
+ #include
+#else
+ #include
+#endif
+
+#include "SevenSegmentTM1637.h"
+
+class SevenSegmentExtended : public SevenSegmentTM1637 {
+
+public:
+SevenSegmentExtended(uint8_t pinClk, uint8_t pinDIO);
+/* Prints given time to the display
+@param [in] hour hours or minutes
+@param [in] min minutes or seconds
+*/
+void printTime(uint8_t hour, uint8_t min, bool blink = false, uint16_t blinkDelay = TM1637_DEFAULT_CLOCK_BLINK_DELAY);
+/* Prints given time to the display
+@param [in] t time given as an int, e.g. 1643 prints 16:43
+*/
+void printTime(uint16_t t, bool blink = false, uint16_t blinkDelay = TM1637_DEFAULT_CLOCK_BLINK_DELAY);
+/* Prints a number and aligns it (right by default)
+
+ Can print both positive and negative numbers. When the the number is larger then 9999
+ it will roll over to 0, the same happens for a negative number smaller then -999.
+
+@param [in] number the number to print to the display
+@param [in] alignRight (optional) set to true to alightright (default)
+@param [in] rollOver (optional) set to true make the number rollover*/
+void printNumber(int16_t number, bool zeroPadding = false, bool rollOver = false, bool alignRight = true);
+/* Print two one or two digit numbers to the display
+* Prints a number to the left and right of the display
+*
+@param [in] leftCounter the number on the left side of the display
+@param [in] rightcounter the numnber on the right side of the display
+@param [in] zeroPadding optional: pad counters with zero
+*/
+void printDualCounter(int8_t leftCounter, int8_t rightCounter, bool zeroPadding = false);
+
+};
+#endif
diff --git a/lib/lib_display/arduino-tm1637/SevenSegmentFun.cpp b/lib/lib_display/arduino-tm1637/SevenSegmentFun.cpp
new file mode 100644
index 000000000..c5a26405a
--- /dev/null
+++ b/lib/lib_display/arduino-tm1637/SevenSegmentFun.cpp
@@ -0,0 +1,254 @@
+#include "SevenSegmentFun.h"
+
+const PROGMEM uint8_t levelVerticalMap[4] = {
+ TM1637_CHAR_HOR_LEVEL_0,
+ TM1637_CHAR_HOR_LEVEL_1,
+ TM1637_CHAR_HOR_LEVEL_2,
+ TM1637_CHAR_HOR_LEVEL_3
+};
+
+SevenSegmentFun::SevenSegmentFun(uint8_t pinClk, uint8_t pinDIO) :
+ SevenSegmentExtended(pinClk, pinDIO)
+{
+ randomSeed(analogRead(0));
+};
+
+// 9 levels ( 0, 12.5, 25, 37.4, 50, 62.5, 75.0, 87.5, 100)
+void SevenSegmentFun::printLevelVertical(uint8_t level, bool leftToRight) {
+
+ level = (level > 100?100:level);
+ float levelScale = (100 / (TM1637_MAX_COLOM * 2.0));
+
+ uint8_t barsOn = float(level) / levelScale;
+
+ uint8_t d = leftToRight == true?0:TM1637_MAX_COLOM-1;
+
+ for (uint8_t i=0; i < TM1637_MAX_COLOM; i++) {
+ if (barsOn - (2 * (i + 1)) >= 0) {
+ _rawBuffer[d] = TM1637_CHAR_VERT_LEVEL_II;
+ } else if (barsOn - (2 * (i + 1)) >= 1) {
+ _rawBuffer[d] = (leftToRight == true)?TM1637_CHAR_VERT_LEVEL_I0:TM1637_CHAR_VERT_LEVEL_0I;
+ } else {
+ _rawBuffer[d] = 0;
+ }
+ if (leftToRight == true) {d++;}
+ else {d--;};
+ };
+ printRaw(_rawBuffer, TM1637_MAX_COLOM, 0);
+};
+
+
+void SevenSegmentFun::printLevelVertical(uint8_t level, bool leftToRight, uint8_t symbol) {
+ level /= (100 / TM1637_MAX_COLOM);
+ level = (level > TM1637_MAX_COLOM)?TM1637_MAX_COLOM:level;
+
+ for( uint8_t i=0; i < TM1637_MAX_COLOM;i++) {
+ if ( ( i < level && leftToRight ) || ( i >= ( TM1637_MAX_COLOM - level ) && !leftToRight) ) {
+ _rawBuffer[i] = symbol;
+ } else {
+ _rawBuffer[i] = 0;
+ }
+ };
+
+ printRaw(_rawBuffer, TM1637_MAX_COLOM, 0);
+
+};
+
+void SevenSegmentFun::nightrider(uint8_t repeats, uint16_t d, uint8_t symbol) {
+
+ uint8_t buffer[((TM1637_MAX_COLOM * 2) - 1)] = {0, };
+
+ for ( uint8_t i= 0; i < ((TM1637_MAX_COLOM * 2) - 1); i++) {
+ if ( i == (TM1637_MAX_COLOM - 1) ) {
+ buffer[i] = symbol;
+ } else {
+ buffer[i] = 0;
+ }
+ };
+
+ for( int8_t r=0; r < repeats; r++) {
+ for (int8_t i = (TM1637_MAX_COLOM - 1); i > 0; i--) {
+ printRaw( &buffer[i],4,0);
+ delay(d);
+ };
+ for ( int8_t i = 0; i < (TM1637_MAX_COLOM - 1); i++) {
+ printRaw( &buffer[i], TM1637_MAX_COLOM, 0);
+ delay(d);
+ };
+ }
+}
+// 5 levels ( 0, 33, 66, 100)
+void SevenSegmentFun::printLevelHorizontal( uint8_t levels[4] ) {
+ for (uint8_t i=0; i < TM1637_MAX_COLOM;i++) {
+ levels[i] /= (100 / 3);
+ levels[i] = ( levels[i] > 3)?3:levels[i];
+ _rawBuffer[i] = pgm_read_byte_near(levelVerticalMap + levels[i]);
+ };
+ printRaw(_rawBuffer, TM1637_MAX_COLOM, 0);
+}
+
+void SevenSegmentFun::scrollingText(const char* str, uint8_t repeats) {
+
+ uint8_t buffer[TM1637_MAX_CHARS];
+
+ size_t length = encode(&buffer[3], str, TM1637_MAX_CHARS);
+
+ uint8_t paddingBegin = TM1637_MAX_COLOM - 1;
+ uint8_t totalPadding = (2 * paddingBegin) + 1;
+
+ // padd with three spaces in front
+ for(uint8_t i=0; i < paddingBegin; i++) {
+ buffer[i] = 0;
+ };
+ // padd with four spaces at the end (empty scree)
+ for(uint8_t i = length + paddingBegin; i < length + totalPadding ; i++) {
+ buffer[i] = 0;
+ };
+
+ for( ; repeats != 0; repeats--) {
+ printRaw(buffer, length + totalPadding);
+ if ( repeats > 250 ) { // more than 250 -> forever
+ repeats++;
+ }
+ };
+}
+
+void SevenSegmentFun::snake(uint8_t repeats, uint16_t d) {
+
+ uint8_t outerEdges = (4 * TM1637_MAX_LINES) + (TM1637_MAX_COLOM * 2);
+ uint8_t widthEdged = (TM1637_MAX_COLOM * 2);
+
+ // number of repeats/rounds
+ for ( uint8_t r=0; r < repeats; r++) {
+ // 12 steps for all outer edges
+ for (uint8_t i=0; i < outerEdges; i++) {
+ for (uint8_t j=0; j < TM1637_MAX_COLOM; j++) {
+
+ // set segments
+ // top edges
+ if ( i == j) {
+ _rawBuffer[j] =TM1637_CHAR_SNAKE_0;
+ }
+ // top right edge
+ else if ( i == TM1637_MAX_COLOM && j == 3) {
+ _rawBuffer[j] =TM1637_CHAR_SNAKE_1;
+ }
+ // bottom left edge
+ else if ( i == (TM1637_MAX_COLOM + 1) && j == 3) {
+ _rawBuffer[j] =TM1637_CHAR_SNAKE_2;
+ }
+ // bottom edges
+ else if ( i + j == (widthEdged + 1) ) {
+ _rawBuffer[j] =TM1637_CHAR_SNAKE_3;
+ }
+ // bottom left edge
+ else if ( i == (widthEdged + 2) && j == 0) {
+ _rawBuffer[j] =TM1637_CHAR_SNAKE_4;
+ }
+ // top left edge
+ else if ( i == (widthEdged + 3) && j == 0) {
+ _rawBuffer[j] =TM1637_CHAR_SNAKE_5;
+ } else {
+ _rawBuffer[j] = 0;
+ };
+ };
+ printRaw(_rawBuffer, 4, 0);
+ delay(d);
+ }
+
+ };
+}
+
+void SevenSegmentFun::bombTimer(uint8_t hours, uint8_t min, uint16_t speed) {
+
+ // maximum speed is 60000 -> 1min per ms
+ speed = (speed > 60000)?60000:speed;
+ uint16_t d = 60000 / speed;
+
+ // copy start minute
+ int8_t m = min;
+ int8_t h;
+ for ( h=hours; h >= 0; h--) {
+ for ( ; m >= 0; m--) {
+ printTime((uint8_t)h, (uint8_t)m);
+ delay(d);
+ };
+ m = 59; // reset minute timer
+ };
+ blink();
+};
+
+void SevenSegmentFun::bombTimer(uint8_t hours, uint8_t min, uint16_t speed, char* str) {
+ bombTimer(hours, min, speed);
+ print(str);
+ blink();
+};
+
+void SevenSegmentFun::bouncingBall(uint16_t moves, uint16_t d, bool runForever) {
+
+ int8_t wallRight = TM1637_MAX_COLOM - 1;
+ int8_t wallBottom = TM1637_MAX_LINES;
+
+ // start at top left
+ int8_t x = 0; int8_t y = 0;
+ int8_t newX; int8_t newY;
+ // default speed is 1 x right and 2 y down
+ int8_t vx = 1; int8_t vy = 1;
+ bool flipVx = false; bool flipVy = false;
+
+ // print initial state
+ printBall(x, y);
+
+ for ( ; moves != 0; moves--) {
+
+ newX = x + vx;
+ newY = y + vy;
+
+ // check if ball has hit the wall
+ if (newX > wallRight || newX < 0 ) {
+ vx = vx * -1;
+ newX = x + vx;
+ // random flip other direction as well
+ flipVy = (random(3) == 1);
+ };
+
+ if ( newY > wallBottom || newY < 0) {
+ vy = vy * -1;
+ newY = y + vy;
+ // random flip other direction as well
+ flipVx = (random(3) == 1);
+ };
+
+ if ( flipVy ) {
+ newY = y;
+ } else if ( flipVx ) {
+ newX = x;
+ };
+ // set new coordinates
+ x = newX; y = newY;
+
+ printBall(x, y);
+ delay(d);
+
+ if (runForever) {
+ moves++;
+ };
+ };
+}
+
+void SevenSegmentFun::printBall( const int8_t x, const int8_t y ) {
+ // uint8_t buffer[4] = {0,0,0,0};
+ // buffer[x] = (y > 0)?TM1637_CHAR_BALL_LOW:TM1637_CHAR_BALL_HIGH;
+ // printRaw(buffer);
+
+ uint8_t symbol = (y > 0)?TM1637_CHAR_BALL_LOW:TM1637_CHAR_BALL_HIGH;
+
+ print4Bit(x, y, symbol);
+
+};
+
+void SevenSegmentFun::print4Bit( const uint8_t x, const uint8_t y, uint8_t symbol) {
+ uint8_t buffer[4] = {0,0,0,0};
+ buffer[x] = symbol;
+ printRaw(buffer);
+}
diff --git a/lib/lib_display/arduino-tm1637/SevenSegmentFun.h b/lib/lib_display/arduino-tm1637/SevenSegmentFun.h
new file mode 100644
index 000000000..40ebc1469
--- /dev/null
+++ b/lib/lib_display/arduino-tm1637/SevenSegmentFun.h
@@ -0,0 +1,65 @@
+#ifndef SevenSegmentFun_H
+#define SevenSegmentFun_H
+
+#if ARDUINO >= 100
+ #include
+#else
+ #include
+#endif
+
+#include "SevenSegmentTM1637.h"
+#include "SevenSegmentExtended.h"
+
+// COMPILE TIME USER CONFIG ////////////////////////////////////////////////////
+#define TM1637_SNAKE_DEFAULT_DELAY 50 // Snake step delay ms
+#define TM1637_BOUNCING_BALL_DEFAULT_DELAY 100 // Bouncing ball delay ms
+#define TM1637_NIGHTRIDER_DEFAULT_DELAY 200 // Nightrider delay ms
+//
+// A
+// ---
+// * F | | B
+// X -G-
+// * E | | C
+// ---
+// D
+// X defines the colon (dots) and only applies to byte 1 (second from left)
+// BXGFEDCBA
+
+#define TM1637_CHAR_VERT_LEVEL_I0 B00110000 // | one bar left
+#define TM1637_CHAR_VERT_LEVEL_0I B00000110 // | one bar right
+#define TM1637_CHAR_VERT_LEVEL_II B00110110 // || two bars
+
+#define TM1637_CHAR_HOR_LEVEL_0 B00000000
+#define TM1637_CHAR_HOR_LEVEL_1 B00001000
+#define TM1637_CHAR_HOR_LEVEL_2 B01001000
+#define TM1637_CHAR_HOR_LEVEL_3 B01001001
+
+#define TM1637_CHAR_SNAKE_0 B00000001
+#define TM1637_CHAR_SNAKE_1 B00000010
+#define TM1637_CHAR_SNAKE_2 B00000100
+#define TM1637_CHAR_SNAKE_3 B00001000
+#define TM1637_CHAR_SNAKE_4 B00010000
+#define TM1637_CHAR_SNAKE_5 B00100000
+
+#define TM1637_CHAR_BALL_HIGH B01100011
+#define TM1637_CHAR_BALL_LOW B01011100
+
+class SevenSegmentFun : public SevenSegmentExtended {
+ public:
+ SevenSegmentFun(uint8_t pinClk, uint8_t pinDIO);
+ void printLevelVertical(uint8_t level, bool leftToRight = true);
+ void printLevelVertical(uint8_t level, bool leftToRight, uint8_t symbol);
+ void printLevelHorizontal(uint8_t levels[4]);
+ void scrollingText(const char* str, uint8_t repeats);
+ void snake(uint8_t repeats = 1, uint16_t d = TM1637_SNAKE_DEFAULT_DELAY);
+ void nightrider(uint8_t repeats = 10, uint16_t d = TM1637_NIGHTRIDER_DEFAULT_DELAY, uint8_t symbol = TM1637_CHAR_VERT_LEVEL_II);
+ void bombTimer(uint8_t hours, uint8_t min, uint16_t speed = 60);
+ void bombTimer(uint8_t hours, uint8_t min, uint16_t speed, char* str);
+ void bouncingBall(uint16_t moves, uint16_t d, bool runForever = false);
+ void printBall(const int8_t x, const int8_t y);
+ void print4Bit(const uint8_t x, const uint8_t y, uint8_t symbol);
+
+ private:
+};
+
+#endif
diff --git a/lib/lib_display/arduino-tm1637/SevenSegmentTM1637.cpp b/lib/lib_display/arduino-tm1637/SevenSegmentTM1637.cpp
new file mode 100644
index 000000000..b12c52e77
--- /dev/null
+++ b/lib/lib_display/arduino-tm1637/SevenSegmentTM1637.cpp
@@ -0,0 +1,434 @@
+
+#include "SevenSegmentTM1637.h"
+
+
+SevenSegmentTM1637::SevenSegmentTM1637(uint8_t pinClk, uint8_t pinDIO) :
+ _pinClk(pinClk),
+ _pinDIO(pinDIO)
+{
+ // setup pins
+ pinAsOutput(_pinClk);
+ pinAsOutput(_pinDIO);
+ digitalHigh(_pinClk);
+ digitalHigh(_pinDIO);
+
+ // setup defaults
+ setCursor(0, TM1637_DEFAULT_CURSOR_POS);
+ setPrintDelay(TM1637_DEFAULT_PRINT_DELAY);
+ setColonOn(TM1637_DEFAULT_COLON_ON);
+ setBacklight(TM1637_DEFAULT_BACKLIGHT);
+
+ // write command SET_DATA (Command1) Defaults
+ command(
+ TM1637_COM_SET_DATA |
+ TM1637_SET_DATA_WRITE |
+ TM1637_SET_DATA_A_ADDR |
+ TM1637_SET_DATA_M_NORM
+ );
+
+
+
+};
+
+// Print API ///////////////////////////////////////////////////////////////////
+// single byte
+size_t SevenSegmentTM1637::write(uint8_t byte) {
+ TM1637_DEBUG_PRINT(F("write byte:\t")); TM1637_DEBUG_PRINTLN((char)byte);
+
+ size_t n = 0;
+ if ( _cursorPos == _numCols ) {
+ shiftLeft(_rawBuffer, _numCols);
+ _rawBuffer[_cursorPos] = encode( (char)byte );
+ // buffer, length, position
+ printRaw( _rawBuffer, _numCols, 0);
+ ++n;
+ };
+
+ if (_cursorPos < _numCols) {
+ _rawBuffer[_cursorPos] = encode( (char)byte );
+ // buffer, length, position
+ printRaw( _rawBuffer, _cursorPos+1, 0);
+ setCursor(1, _cursorPos + 1);
+ ++n;
+ }
+ return n;
+}
+
+// null terminated char array
+size_t SevenSegmentTM1637::write(const char* str) {
+ TM1637_DEBUG_PRINT(F("write char*:\t")); TM1637_DEBUG_PRINTLN(str);
+ uint8_t encodedBytes[4];
+
+ encode(encodedBytes, str, 4);
+ uint8_t i =4;
+ while( str[i] != '\0' ) {
+ printRaw(encodedBytes);
+ shiftLeft(encodedBytes, 4);
+ encodedBytes[3] = encode( str[i] );
+ i++;
+ if ( i == TM1637_MAX_CHARS) {
+ break;
+ }
+ }
+ return i;
+};
+
+// byte array with length
+size_t SevenSegmentTM1637::write(const uint8_t* buffer, size_t size) {
+ TM1637_DEBUG_PRINT(F("write uint8_t*:\t("));
+ for(size_t i=0; i < size; i++) {
+ TM1637_DEBUG_PRINT((char)buffer[i]);
+ TM1637_DEBUG_PRINT(i == size -1?F(""):F(", "));
+ }
+ TM1637_DEBUG_PRINT(F(") "));
+ TM1637_DEBUG_PRINT(size);
+
+ uint8_t encodedBytes[TM1637_MAX_CHARS];
+
+ if ( size > TM1637_MAX_CHARS) {
+ size = TM1637_MAX_CHARS;
+ }
+ size_t length = encode(encodedBytes, buffer, size);
+ TM1637_DEBUG_PRINT(F(" (")); TM1637_DEBUG_PRINT(length); TM1637_DEBUG_PRINT(F(", "));
+ TM1637_DEBUG_PRINT(_cursorPos); TM1637_DEBUG_PRINTLN(F(")"));
+ printRaw(encodedBytes, length, _cursorPos);
+ return length;
+};
+
+// Liquid cristal API
+void SevenSegmentTM1637::begin(uint8_t cols, uint8_t rows) {
+ _numCols = cols;
+ _numRows = rows;
+ clear();
+};
+
+void SevenSegmentTM1637::init(uint8_t cols, uint8_t rows) {
+ begin(cols, rows);
+}
+
+void SevenSegmentTM1637::clear(void) {
+ uint8_t rawBytes[4] = {0,0,0,0};
+ printRaw(rawBytes);
+ home();
+};
+
+void SevenSegmentTM1637::home(void) {
+ setCursor(0, 0);
+};
+
+void SevenSegmentTM1637::setCursor(uint8_t row, uint8_t col) {
+ col = (col > TM1637_MAX_COLOM -1)?TM1637_MAX_COLOM-1:col;
+ _cursorPos = col;
+
+};
+
+void SevenSegmentTM1637::setBacklight(uint8_t value) {
+ value = (value > 100 )?100:value; // 0..100 brightness
+ // Store the backlight value
+ _backLightValue = value;
+ // scale backlight value to 0..8
+ value /= 10; // 0..10
+ value = (value > 8 )? 8:value; // only 8 levels and off
+ uint8_t cmd = TM1637_COM_SET_DISPLAY;;
+ switch ( value ) {
+ case 0:
+ cmd |= TM1637_SET_DISPLAY_OFF;
+ break;
+ case 1:
+ cmd |= TM1637_SET_DISPLAY_ON | TM1637_SET_DISPLAY_1;
+ break;
+ case 2:
+ cmd |= TM1637_SET_DISPLAY_ON | TM1637_SET_DISPLAY_2;
+ break;
+ case 3:
+ cmd |= TM1637_SET_DISPLAY_ON | TM1637_SET_DISPLAY_4;
+ break;
+ case 4:
+ cmd |= TM1637_SET_DISPLAY_ON | TM1637_SET_DISPLAY_10;
+ break;
+ case 5:
+ cmd |= TM1637_SET_DISPLAY_ON | TM1637_SET_DISPLAY_11;
+ break;
+ case 6:
+ cmd |= TM1637_SET_DISPLAY_ON | TM1637_SET_DISPLAY_12;
+ break;
+ case 7:
+ cmd |= TM1637_SET_DISPLAY_ON | TM1637_SET_DISPLAY_13;
+ break;
+ case 8:
+ cmd |= TM1637_SET_DISPLAY_ON | TM1637_SET_DISPLAY_14;
+ break;
+ };
+#if TM1637_DEBUG
+ bool ack = command(cmd);
+ TM1637_DEBUG_PRINT(F("SET_DISPLAY:\t")); TM1637_DEBUG_PRINTLN((
+ cmd
+ ), BIN);
+ TM1637_DEBUG_PRINT(F("Acknowledged:\t")); TM1637_DEBUG_PRINTLN(ack);
+#else
+ command(cmd);
+#endif
+};
+
+void SevenSegmentTM1637::setContrast(uint8_t value) {
+ setBacklight(value);
+}
+
+void SevenSegmentTM1637::on(void) {
+ setBacklight(TM1637_DEFAULT_BACKLIGHT);
+};
+
+void SevenSegmentTM1637::off(void) {
+ setBacklight(0);
+ clear();
+};
+
+// SevenSegmentTM1637 public methods
+void SevenSegmentTM1637::blink(uint8_t blinkDelay, uint8_t repeats, uint8_t maxBacklight, uint8_t minBacklight) {
+ for (uint8_t i=0; i < repeats; i++) {
+ setBacklight(minBacklight); // turn backlight off
+ delay(blinkDelay);
+ setBacklight(maxBacklight); // turn backlight on
+ delay(blinkDelay);
+ }
+ // restore backlight
+ setBacklight(_backLightValue);
+}
+
+void SevenSegmentTM1637::setPrintDelay(uint16_t printDelay) {
+ _printDelay = printDelay;
+};
+
+bool SevenSegmentTM1637::getColonOn(void) {
+ return (_colonOn);
+};
+
+void SevenSegmentTM1637::setColonOn(bool setToOn) {
+ _colonOn = setToOn;
+}
+void SevenSegmentTM1637::printRaw(uint8_t rawByte, uint8_t position) {
+ uint8_t cmd[2];
+ cmd[0] = TM1637_COM_SET_ADR | position;
+ cmd[1] = rawByte;
+ if (position == 1) { cmd[1]|=(_colonOn)?TM1637_COLON_BIT:0; };
+ command(cmd, 2);
+};
+
+void SevenSegmentTM1637::printRaw(const uint8_t* rawBytes, size_t length, uint8_t position) {
+ // if fits on display
+ if ( (length + position) <= _numCols) {
+ uint8_t cmd[5] = {0, };
+ cmd[0] = TM1637_COM_SET_ADR | (position & B111); // sets address
+ memcpy(&cmd[1], rawBytes, length); // copy bytes
+
+ // do we have to print a colon?
+ if ( position < 2 ) { // printing after position 2 has never a colon
+ if ( position == 0 && length >= 2) {
+ // second index is the colon
+ cmd[2] |= (_colonOn)?TM1637_COLON_BIT:0;
+ } else {
+ // first index is the colon
+ cmd[1] |= (_colonOn)?TM1637_COLON_BIT:0;
+ }
+ }
+ // TM1637_DEBUG_PRINT(F("ADDR :\t")); TM1637_DEBUG_PRINTLN(cmd[0],BIN);
+ // TM1637_DEBUG_PRINT(F("DATA0:\t")); TM1637_DEBUG_PRINTLN(cmd[1],BIN);
+ command(cmd, length+1); // send to display
+ }
+ // does not fit on display, need to print with delay
+ else {
+ // First print 1-4 characters
+ uint8_t numtoPrint = _numCols - position;
+ printRaw(rawBytes, numtoPrint, position);
+ delay(_printDelay);
+
+ // keep printing 4 characters till done
+ uint8_t remaining = length - numtoPrint + 3;
+ uint8_t i = 1;
+ while( remaining >= _numCols) {
+ printRaw(&rawBytes[i], _numCols, 0);
+ delay(_printDelay);
+ remaining--;
+ i++;
+ };
+ }
+
+};
+
+// Helpers
+uint8_t SevenSegmentTM1637::encode(char c) {
+ if ( c < ' ') { // 32 (ASCII)
+ return 0;
+ }
+ return pgm_read_byte_near(AsciiMap::map + c - ' ');
+};
+
+uint8_t SevenSegmentTM1637::encode(int16_t d) {
+ // can only encode single digit
+ if ( d > 9 || d < 0) {
+ return 0;
+ };
+ return pgm_read_byte_near(AsciiMap::map + d + '0' - ' ');
+};
+
+size_t SevenSegmentTM1637::encode(uint8_t* buffer, const char* str, size_t bufferSize) {
+ size_t i;
+
+ for (i=0; i < bufferSize; i++) {
+ if (str[i] == '\0' ) {
+ return i;
+ }
+ buffer[i] = encode( str[i] );
+ };
+ return i;
+}
+
+size_t SevenSegmentTM1637::encode(uint8_t* buffer, const uint8_t* byteArr, size_t bufferSize) {
+ size_t i;
+
+ for (i=0; i < bufferSize; i++) {
+ buffer[i] = encode( (char)byteArr[i] );
+ };
+ return i;
+}
+
+void SevenSegmentTM1637::shiftLeft(uint8_t* buffer, size_t length) {
+ for (uint8_t i=0; i < length ; i++) {
+ buffer[i] = buffer[i+1];
+ }
+}
+
+// SevenSegmentTM1637 LOW LEVEL
+bool SevenSegmentTM1637::command(uint8_t cmd) const{
+ return command(_pinClk, _pinDIO, cmd);
+};
+
+bool SevenSegmentTM1637::command(uint8_t pinClk, uint8_t pinDIO, uint8_t cmd) {
+ comStart(pinClk, pinDIO);
+ comWriteByte(pinClk, pinDIO,cmd);
+ bool acknowledged = comAck(pinClk, pinDIO);
+ comStop(pinClk, pinDIO);
+ return acknowledged;
+}
+
+bool SevenSegmentTM1637::command(const uint8_t* commands, uint8_t length) const {
+ return command(_pinClk, _pinDIO, commands, length);
+};
+
+bool SevenSegmentTM1637::command(uint8_t pinClk, uint8_t pinDIO, const uint8_t* commands, uint8_t length) {
+ bool acknowledged = true;
+ comStart(pinClk, pinDIO);
+ for (uint8_t i=0; i < length;i++) {
+ comWriteByte(pinClk, pinDIO, commands[i]);
+ acknowledged &= comAck(pinClk, pinDIO);
+ };
+ comStop(pinClk, pinDIO);
+ return acknowledged;
+}
+
+uint8_t SevenSegmentTM1637::comReadByte(void) const {
+ uint8_t readKey = 0;
+
+ comStart();
+ comWriteByte(TM1637_COM_SET_DATA | TM1637_SET_DATA_READ);
+ comAck();
+
+ pinAsInput(_pinDIO);
+ digitalHigh(_pinDIO);
+ delayMicroseconds(5);
+
+ for ( uint8_t i=0; i < 8; i++) {
+
+ readKey >>= 1;
+ digitalLow(_pinClk);
+ delayMicroseconds(30);
+
+ digitalHigh(_pinClk);
+
+ if ( isHigh(_pinDIO) ) {
+ readKey = readKey | B10000000;
+ };
+
+ delayMicroseconds(30);
+
+
+ };
+ pinAsOutput(_pinDIO);
+ comAck();
+ comStop();
+ return readKey;
+};
+
+void SevenSegmentTM1637::comWriteByte(uint8_t command) const{
+ comWriteByte(_pinClk, _pinDIO, command);
+};
+
+void SevenSegmentTM1637::comWriteByte(uint8_t pinClk, uint8_t pinDIO, uint8_t command) {
+ // CLK in bits
+ for ( uint8_t i=0; i < 8; i++) {
+ digitalLow(pinClk); // CLK LOW
+
+ if ( command & B1) {
+ digitalHigh(pinDIO);// DIO HIGH
+ } else {
+ digitalLow(pinDIO); // DIO LOW
+ }
+ delayMicroseconds(TM1637_CLK_DELAY_US);
+
+ command >>= 1;
+
+ digitalHigh(pinClk); // CLK HIGH
+ delayMicroseconds(TM1637_CLK_DELAY_US);
+ };
+}
+
+void SevenSegmentTM1637::comStart(void) const {
+ comStart(_pinClk, _pinDIO);
+};
+
+void SevenSegmentTM1637::comStart(uint8_t pinClk, uint8_t pinDIO) {
+ digitalHigh(pinDIO); // DIO HIGH
+ digitalHigh(pinClk); // CLK HIGH
+ delayMicroseconds(TM1637_CLK_DELAY_US);
+
+ digitalLow(pinDIO); // DIO LOW
+}
+
+void SevenSegmentTM1637::comStop(void) const {
+ comStop(_pinClk, _pinDIO);
+};
+
+void SevenSegmentTM1637::comStop(uint8_t pinClk, uint8_t pinDIO) {
+ digitalLow(pinClk); // CLK LOW
+ delayMicroseconds(TM1637_CLK_DELAY_US);
+
+ digitalLow(pinDIO); // DIO LOW
+ delayMicroseconds(TM1637_CLK_DELAY_US);
+
+ digitalHigh(pinClk); // CLK HIGH
+ delayMicroseconds(TM1637_CLK_DELAY_US);
+
+ digitalHigh(pinDIO); // DIO HIGH
+}
+
+bool SevenSegmentTM1637::comAck(void) const {
+ return comAck(_pinClk, _pinDIO);
+};
+
+bool SevenSegmentTM1637::comAck(uint8_t pinClk, uint8_t pinDIO) {
+ bool acknowledged = false;
+
+ digitalLow(pinClk); // CLK LOW
+ pinAsInputPullUp(pinDIO); // DIO INPUT PULLUP (state==HIGH)
+ delayMicroseconds(TM1637_CLK_DELAY_US);
+
+ acknowledged = isLow(pinDIO);// Ack should pull the pin low again
+
+ digitalHigh(pinClk); // CLK HIGH
+ delayMicroseconds(TM1637_CLK_DELAY_US);
+
+ digitalLow(pinClk); // CLK LOW
+ pinAsOutput(pinDIO);
+
+ return acknowledged;
+}
diff --git a/lib/lib_display/arduino-tm1637/SevenSegmentTM1637.h b/lib/lib_display/arduino-tm1637/SevenSegmentTM1637.h
new file mode 100644
index 000000000..95a06b113
--- /dev/null
+++ b/lib/lib_display/arduino-tm1637/SevenSegmentTM1637.h
@@ -0,0 +1,347 @@
+/*
+ SevenSegmentTM1637 - class to control a 4 digit seven segment display with a TM1636 or TM1637 driver IC
+ Created by Bram Harmsen, September 25, 2015
+ Released into the public domain.
+ Licence: GNU GENERAL PUBLIC LICENSE V2.0
+
+ # Changelog
+
+ v1.0 25-10-2015
+ v1.1 04-07-2020
+
+*/
+
+#ifndef SevenSegmentTM1637_H
+#define SevenSegmentTM1637_H
+
+#include
+#include "SevenSegmentAsciiMap.h"
+
+// COMPILE TIME USER CONFIG ////////////////////////////////////////////////////
+#define TM1637_DEBUG false // true for serial debugging
+#define TM1637_BEGIN_DELAY 500 // ms
+#define TM1637_PRINT_BUFFER_SIZE 128 // lower if you don't need it
+
+// Default values //////////////////////////////////////////////////////////////
+#define TM1637_DEFAULT_PRINT_DELAY 300 // 300 ms delay between characters
+#define TM1637_DEFAULT_BLINK_DELAY 50 // ms
+#define TM1637_DEFAULT_CLOCK_BLINK_DELAY 500 // the default delay for when using printTime
+#define TM1637_DEFAULT_BLINK_REPEAT 10
+#define TM1637_DEFAULT_CURSOR_POS 0 // 0-MAX-1 (e.g 3)
+#define TM1637_DEFAULT_COLON_ON false //
+#define TM1637_DEFAULT_BACKLIGHT 100 // 0..100
+
+#define TM1637_MAX_LINES 1 // number of display lines
+#define TM1637_MAX_COLOM 4 // number of coloms (digits)
+#define TM1637_MAX_CHARS 128
+
+// PROGRAM CONFIG (ONLY CHANGE WHEN YOU KNOW WHAT YOU RE DOING:)////////////////
+#define TM1637_CLK_DELAY_US 5 // clock delay for communication
+// mine works with 1us, perhaps increase if display does not function ( tested upto 1ms)
+
+
+// COMMANDS ////////////////////////////////////////////////////////////////////
+#define TM1637_COM_SET_DATA B01000000 // 0x40 (1) Data set
+#define TM1637_COM_SET_ADR B11000000 // 0xC0 (2) Address command set
+#define TM1637_COM_SET_DISPLAY B10000000 // 0x80 (3) Display control command set
+
+// Data set (1) (use logical OR to contruct complete command)
+#define TM1637_SET_DATA_WRITE B00000000 // Write data to the display register
+#define TM1637_SET_DATA_READ B00000010 // Read the key scan data
+#define TM1637_SET_DATA_A_ADDR B00000000 // Automatic address increment
+#define TM1637_SET_DATA_F_ADDR B00000100 // Fixed address
+#define TM1637_SET_DATA_M_NORM B00000000 // Normal mode
+#define TM1637_SET_DATA_M_TEST B00100000 // Test mode
+
+// Address settings (2) (use logical OR to contruct complete command)
+#define TM1637_SET_ADR_00H B0000000 // addr 00
+#define TM1637_SET_ADR_01H B0000001 // addr 01
+#define TM1637_SET_ADR_02H B0000010 // addr 02
+#define TM1637_SET_ADR_03H B0000011 // addr 03
+#define TM1637_SET_ADR_04H B0000100 // addr 04 (only TM1637)
+#define TM1637_SET_ADR_05H B0000101 // addr 05 (only TM1637)
+// The command is used to set the display register address; if the address is set to 0C4H or higher, the data is ignored, until the effective address is set; when the power is on, the default is set to 00H address.
+
+// Display control command set (use logical OR to consruct complete command)
+#define TM1637_SET_DISPLAY_1 B0000000 // Pulse width 1/16 (0.0625) (0)
+#define TM1637_SET_DISPLAY_2 B0000001 // Pulse width 2/16 (0.0625) (1)
+#define TM1637_SET_DISPLAY_4 B0000010 // Pulse width 4/16 (0.0625) (2)
+#define TM1637_SET_DISPLAY_10 B0000011 // Pulse width 10/16 (0.0625) (3)
+#define TM1637_SET_DISPLAY_11 B0000100 // Pulse width 11/16 (0.0625) (4)
+#define TM1637_SET_DISPLAY_12 B0000101 // Pulse width 12/16 (0.0625) (5)
+#define TM1637_SET_DISPLAY_13 B0000110 // Pulse width 13/16 (0.0625) (6)
+#define TM1637_SET_DISPLAY_14 B0000111 // Pulse width 14/16 (0.0625) (7)
+#define TM1637_SET_DISPLAY_OFF B0000000 // OFF
+#define TM1637_SET_DISPLAY_ON B0001000 // ON
+// there are a total of 8 brighness values, plus off
+
+// PROTOCOL SPECIFICATION
+/*
+* Structure
+* START COMMAND ACK STOP set config or display
+* START ADR_CMD DATA ACK STOP sets single digit
+* START ADR_CMD DATA0 .. DATAN ACK STOP sets multiple digits when in auto mode
+*
+* There are basicly three things you can do:
+* 1. Set some configuration values
+* - read/write mode, auto/manual address, normal/test mode
+* 2. Set a (starting) address followed by 1 or N data bytes
+* 3. Set the display brightness (pwm) 0-7 and on or off
+*
+* From the datasheet it might seem that you always have to perform all three commands; setting configuration, setting address and data bytes and display. I'v tested this and this is not true. You can just set only one of these three. But ofcourse you have to make sure that your configuration is set properly. For example if you haven't set the configuration to automatic addresses, you can't just send out 4 data bytes, it won't work. Simlilair, if your display is off and you write some data to it, it won't display. On the other hand most default setting are what you want most of the time.
+
+*/
+
+class SevenSegmentTM1637 : public Print {
+
+public:
+ // LIQUID CRISTAL API ///////////////////////////////////////////////////////
+ // See http://playground.arduino.cc/Code/LCDAPI for more details.
+
+ /* Constructor
+ @param [in] pinClk clock pin (any digital pin)
+ @param [in] pinDIO digital output pin (any digital pin)
+ */
+ SevenSegmentTM1637(uint8_t pinClk, uint8_t pinDIO);
+ /* Initializes the display
+ * Initializes the display, sets some text and blinks the display
+
+ @param [in] cols optional: number of coloms (digits)
+ @param [in] rows optional: number of rows
+ */
+ void init(uint8_t cols = TM1637_MAX_COLOM, uint8_t rows = TM1637_MAX_LINES);
+ /* Implemented for compatibility, see begin() above */
+ void begin(uint8_t cols = TM1637_MAX_COLOM, uint8_t rows = TM1637_MAX_LINES);
+ // Print class inheritance ///////////////////////////////////////////////////
+ // See https://github.com/arduino/Arduino/blob/master/hardware/arduino/avr/cores/arduino/Print.h for more details
+ /* This library inherent the Print class, this means that all regular print function can be used. For example:
+ * printing a number: print(78)
+ * printint a number in BIN, OCT, HEX..: print(78, BIN)
+ * printing a float: print(2.85)
+ * printing a PROGMEM c string: print(F("Arduino"))
+ *
+ * Also the more low level write() function can be used. (Actually all print function eventually call one of these write methods, every class that wants to inherent from the Print class needs to implement these)
+ */
+ size_t write(uint8_t byte);
+ size_t write(const char* str);
+ size_t write(const uint8_t* buffer, size_t size);
+
+ /* Clears the display
+ * Writes zero to all digits and segments, display off.
+ */
+ void clear(void);
+ /* Sets the cursor position to zero
+ */
+ void home(void);
+ /* Sets the cursor position to a specfic position
+ *
+ @param [in] col colomn (position)
+ */
+ void setCursor(uint8_t row, uint8_t col);
+ // Liquid cristal optional //////////////////////////////////////////////////
+ /* Sets the display backlight
+ * The display has 8 PWM modes and an off mode. The function accepts a value from 0 to 100, where 80-100 are the same; full brighness.
+
+ @param [in] value brightness value (0..80(100))
+ */
+ void setBacklight(uint8_t value);
+ /* Sets the display contrast (identical to brightness)
+ * This function is mainly for compatibility with the LCD API
+ */
+ void setContrast(uint8_t value);
+
+ /* Turns the display ON
+ * Identical to setting the brightness to the default value.
+ */
+ void on(void);
+ /* Turns the display ON
+ * Identical to setting the brightness to zero and clearing the display.
+ */
+ void off(void);
+
+ // SevenSegmentTM1637 METHODS ///////////////////////////////////////////////
+ /* Blink the last printed text
+ *
+ @param [in] blinkDelay optional: blink delay in ms
+ @param [in] repeats optional: number of blink repeats
+ */
+ void blink(uint8_t blinkDelay = TM1637_DEFAULT_BLINK_DELAY, uint8_t repeats = TM1637_DEFAULT_BLINK_REPEAT, uint8_t maxBacklight=100, uint8_t minBacklight=0);
+
+ // getters and setters ///////////////////////////////////////////////////////
+ /* Turn the colon on or off
+ * When turing the colon on, the next displayed text/numbers will have a colon
+ @param [in] setToOn sets the colon to on or off
+ */
+ void setColonOn(bool setToOn);
+ /* Get the currrent colon setting
+ */
+ bool getColonOn(void);
+ /* Sets the delay for scrolling text
+ * When printing more than four characters/ the display will scroll, this setting determines the scrolling speed in ms
+ @param [in] printDelay the print delay in ms
+ */
+ void setPrintDelay(uint16_t printDelay);
+
+ // helpers //////////////////////////////////////////////////////////////////
+ /* Encodes a character to sevensegment binairy
+ *
+ @param [in] c a character to encode
+ */
+ uint8_t encode(char c);
+ /* Encodes a single digit to sevensegment binairy
+ *
+ @param [in] d a digit to encode
+ */
+ uint8_t encode(int16_t d);
+ /* Encodes a null terminated c string (char array) to sevensegment binairy
+ *
+ @param [out] buffer holds the encodes char array
+ @param [in] str the c string to encode
+ @param [in] bufferSize the size/length of the buffer
+ */
+ size_t encode(uint8_t* buffer, const char* str, size_t bufferSize);
+ /* Encodes a byte array to sevensegment binairy
+ *
+ @param [out] buffer holds the encodes char array
+ @param [in] byteArr the byte array to encode
+ @param [in] bufferSize the size/length of the buffer
+ */
+ size_t encode(uint8_t* buffer, const uint8_t* byteArr, size_t arrSize);
+ /* Shift an array one position to the left
+ @param [out] buffer the buffer to be shifted
+ @param [in] length the length to the buffer
+ */
+ void shiftLeft(uint8_t* buffer, size_t length);
+
+ // SevenSegmentTM1637 low level methods (use when you know what you're doing)
+ /* Prints raw (encoded) bytes to the display
+ * A
+ * ___
+ * * F | | B
+ * X -G-
+ * * E | | C
+ * ___
+ * D
+ * Bit: 76543210
+ * Segment: XGFEDCBA
+ *
+ * For example to print an H, you would set bits BCEFG, this gives B01110110 in binary or 118 in decimal or 0x76 in HEX.
+ * Bit 7 (X) only applies to the second digit and sets the colon
+ */
+ /* Print raw (binary encodes) bytes to the display
+ @param [in] rawBytes Array of raw bytes
+ @param [in] length optional: length to print to display
+ @param [in] position optional: Start position
+ */
+ void printRaw(const uint8_t* rawBytes, size_t length = 4, uint8_t position = 0);
+ /* Print raw (binary encodes) bytes to the display
+ @param [in] rawByte Raw byte
+ @param [in] position optional: Start position
+ */
+ void printRaw(uint8_t rawByte, uint8_t position);
+ /* Write command to IC TM1637
+ @param [in] cmd command to send
+ @return acknowledged? command was (successful) acknowledged
+ */
+ bool command(uint8_t cmd) const;
+ bool command(const uint8_t* command, uint8_t length) const;
+ /* Read bytes from IC TM1637
+ * The IC also can read the state of a keypad? TODO untested
+ */
+ uint8_t comReadByte(void) const;
+ /* Write a single command to the display
+ @param [in] cmd command to send
+ */
+ void comWriteByte(uint8_t command) const;
+ /* Send start signal
+ * Send the start signal for serial communication
+ */
+ void comStart(void) const;
+ /* Send stop signal
+ * Send the stop signal for serial communication
+ */
+ void comStop(void) const;
+ /* Get command acknowledged
+ * Get acknowledge signal (command was succesful received)
+ */
+ bool comAck(void) const;
+
+ /* Static version of low level function
+ * If using more than one display, this saves some space since these methods will be shared among all instances/objects of the class
+ */
+ static bool command(uint8_t pinClk, uint8_t pinDIO, uint8_t cmd);
+ static bool command(uint8_t pinClk, uint8_t pinDIO, const uint8_t* command, uint8_t length);
+ static void comStart(uint8_t pinClk, uint8_t pinDIO);
+ static void comWriteByte(uint8_t pinClk, uint8_t pinDIO, uint8_t command);
+ static bool comAck(uint8_t pinClk, uint8_t pinDIO);
+ static void comStop(uint8_t pinClk, uint8_t pinDIO);
+protected:
+ const uint8_t _pinClk; // clock pin
+ const uint8_t _pinDIO; // digital out pin
+ uint8_t _numCols; // number of columns
+ uint8_t _numRows; // number of rows
+
+ uint8_t _backLightValue; // brightness of the display (0..100)
+ uint8_t _cursorPos; // current cursor position
+ uint16_t _printDelay; // print delay in ms (multiple chars)
+ uint8_t _colonOn; // colon bit if set
+ uint8_t _rawBuffer[TM1637_MAX_COLOM];// hold the last chars printed to display
+};
+
+
+#define TM1637_COLON_BIT B10000000
+
+// debug macros for debugging
+#if TM1637_DEBUG
+ #define TM1637_DEBUG_BEGIN(x) Serial.begin(x)
+ #define TM1637_DEBUG_PRINT(...) Serial.print(__VA_ARGS__)
+ #define TM1637_DEBUG_PRINTLN(...) Serial.println(__VA_ARGS__)
+ #define TM1637_DEBUG_WRITE(x) Serial.write(x)
+ #define TM1637_DEBUG_MESSAGE(...) \
+ Serial.print(millis()); \
+ Serial.print(F("\t")); \
+ Serial.print(__VA_ARGS__);
+ #define TM1637_DEBUG_MESSAGELN(...) \
+ TM1637_DEBUG_MESSAGE(__VA_ARGS__) \
+ Serial.println();
+#else
+ #define TM1637_DEBUG_BEGIN(x)
+ #define TM1637_DEBUG_PRINT(...)
+ #define TM1637_DEBUG_PRINTLN(...)
+ #define TM1637_DEBUG_WRITE(x)
+ #define TM1637_DEBUG_MESSAGE(x)
+ #define TM1637_DEBUG_MESSAGELN(x)
+#endif
+
+// arduino:standard variant direct port access macros for more speed ( communication is ~us)
+#if defined(__AVR_ATmega8__) || defined(__AVR_ATmega8A__) || defined(__AVR_ATmega168__) || defined(__AVR_ATmega168A__) || defined(__AVR_ATmega168P__) || defined(__AVR_ATmega168PA__) || defined(__AVR_ATmega328__) || defined(__AVR_ATmega328P__)
+ #define portOfPin(P) \
+ ( ((P) >= 0 && (P) < 8)? &PORTD:( ((P) > 7 && (P) < 14) ? &PORTB: &PORTC ) )
+ #define ddrOfPin(P) \
+ ( ((P) >= 0 && (P) < 8)? &DDRD: ( ((P) > 7 && (P) < 14) ? &DDRB: &DDRC ) )
+ #define pinOfPin(P) \
+ ( ((P) >= 0 && (P) < 8)? &PIND: ( ((P) > 7 && (P) < 14) ? &PINB: &PINC ) )
+
+ #define pinIndex(P) ( (uint8_t)( P > 13 ? P-14: P&7 ) )
+ #define pinMask(P) ( (uint8_t)( 1 << pinIndex(P) ) )
+
+ #define pinAsInput(P) *(ddrOfPin(P) ) &= ~pinMask(P)
+ #define pinAsInputPullUp(P) *(ddrOfPin(P) ) &= ~pinMask(P);digitalHigh(P)
+ #define pinAsOutput(P) *(ddrOfPin(P) ) |= pinMask(P)
+ #define digitalLow(P) *(portOfPin(P)) &= ~pinMask(P)
+ #define digitalHigh(P) *(portOfPin(P)) |= pinMask(P)
+ #define isHigh(P) ( ( *( pinOfPin(P) ) & pinMask(P) ) > 0 )
+ #define isLow(P) ( ( *( pinOfPin(P) ) & pinMask(P) ) == 0 )
+ #define digitalState(P) ((uint8_t)isHigh(P))
+#else
+ #define pinAsOutput(P) pinMode(P, OUTPUT)
+ #define pinAsInput(P) pinMode(P, INPUT)
+ #define pinAsInputPullUp(P) pinMode(P, INPUT_PULLUP)
+ #define digitalLow(P) digitalWrite(P, LOW)
+ #define digitalHigh(P) digitalWrite(P, HIGH)
+ #define isHigh(P) (digitalRead(P) == 1)
+ #define isLow(P) (digitalRead(P) == 0)
+ #define digitalState(P) digitalRead(P)
+#endif
+
+#endif
diff --git a/lib/lib_display/arduino-tm1637/examples/Basic/Basic.ino b/lib/lib_display/arduino-tm1637/examples/Basic/Basic.ino
new file mode 100755
index 000000000..28526df47
--- /dev/null
+++ b/lib/lib_display/arduino-tm1637/examples/Basic/Basic.ino
@@ -0,0 +1,57 @@
+/*
+ Basic usage example
+
+ Demonstrated some of the basic functionality of the library. Initialize the display,
+ set the backlight brightness, print some text, count from 0 to 100 and
+ print and blink some text.
+
+ Note: make sure to set your serial monitor to line end: NEW LINE!
+
+ The circuit:
+ * connect TM1637 pin CLK to Arduino pin D4
+ * connect TM1637 pin DIO to Arduino pin D5
+ * connect TM1637 pin Vcc to Arduino pin 5V
+ * connect TM1637 pin GND to Arduino pin GND
+
+ Created 25 September 2015
+ By Bram Harmsen
+
+ https://github.com/bremme/arduino-tm1637
+
+*/
+
+// include the SevenSegmentTM1637 library
+#include "SevenSegmentTM1637.h"
+
+/* initialize global TM1637 Display object
+* The constructor takes two arguments, the number of the clock pin and the digital output pin:
+* SevenSegmentTM1637(byte pinCLK, byte pinDIO);
+*/
+const byte PIN_CLK = 4; // define CLK pin (any digital pin)
+const byte PIN_DIO = 5; // define DIO pin (any digital pin)
+SevenSegmentTM1637 display(PIN_CLK, PIN_DIO);
+
+// run setup code
+void setup() {
+ Serial.begin(9600); // initializes the Serial connection @ 9600 baud
+ display.begin(); // initializes the display
+ display.setBacklight(100); // set the brightness to 100 %
+ display.print("INIT"); // display INIT on the display
+ delay(1000); // wait 1000 ms
+};
+
+// run loop (forever)
+void loop() {
+ display.print("LOOP"); // display LOOP on the display
+ delay(1000); // wait 1000 ms
+ display.print("COUNTING SOME DIGITS");// print COUNTING SOME DIGITS
+ display.clear(); // clear the display
+ for (uint8_t i=0; i < 100; i++) { // loop from 0 to 100
+ display.print(i); // display loop counter
+ delay(100); // wait 100 ms
+ };
+ display.clear(); // clear the display
+ display.print("SUCC"); // print SUCC for success
+ display.blink(); // blink SUCC
+ delay(1000); // wait 1000 ms
+};
diff --git a/lib/lib_display/arduino-tm1637/examples/ExtClock/ExtClock.ino b/lib/lib_display/arduino-tm1637/examples/ExtClock/ExtClock.ino
new file mode 100755
index 000000000..9018cefbe
--- /dev/null
+++ b/lib/lib_display/arduino-tm1637/examples/ExtClock/ExtClock.ino
@@ -0,0 +1,53 @@
+/*
+ Clock example example (Extended class example)
+
+ Display a clock on the display. For this demo you can add a speed multiplier to make the clock run faster. For a real clock you want to use a delay of 1 min or even use a Real Time Clock module (RTC)
+
+ The circuit:
+ * connect TM1637 pin CLK to Arduino pin D4
+ * connect TM1637 pin DIO to Arduino pin D5
+ * connect TM1637 pin Vcc to Arduino pin 5V
+ * connect TM1637 pin GND to Arduino pin GND
+
+ Created 25 September 2015
+ By Bram Harmsen
+
+ https://github.com/bremme/arduino-tm1637
+
+*/
+// include the SevenSegmentTM1637 library
+#include "SevenSegmentTM1637.h"
+#include "SevenSegmentExtended.h"
+
+/* initialize global TM1637 Display object
+* The constructor takes two arguments, the number of the clock pin and the digital output pin:
+* SevenSegmentTM1637(byte pinCLK, byte pinDIO);
+*/
+const byte PIN_CLK = 4; // define CLK pin (any digital pin)
+const byte PIN_DIO = 5; // define DIO pin (any digital pin)
+SevenSegmentExtended display(PIN_CLK, PIN_DIO);
+
+const unsigned int clockSpeed = 10000; // speed up clock for demo
+
+// run setup code
+void setup() {
+ Serial.begin(9600); // initializes the Serial connection @ 9600 baud
+ display.begin(); // initializes the display
+ display.setBacklight(100); // set the brightness to 100 %
+ delay(1000); // wait 1000 ms
+};
+
+// run loop (forever)
+void loop() {
+
+ byte hours = 14; // initialize hours
+ byte minutes = 39; // initialize minutes
+
+ for ( ; hours < 24; hours++) { // count hours up to 24
+ for ( ; minutes < 60; minutes++) { // count minutes up to 59
+ display.printTime(hours, minutes, true); // display time
+ delay(60000 / clockSpeed); // clock delay ms
+ };
+ minutes = 0; // reset minutes
+ };
+};
diff --git a/lib/lib_display/arduino-tm1637/examples/ExtScoreboard/ExtScoreboard.ino b/lib/lib_display/arduino-tm1637/examples/ExtScoreboard/ExtScoreboard.ino
new file mode 100755
index 000000000..0b1570d23
--- /dev/null
+++ b/lib/lib_display/arduino-tm1637/examples/ExtScoreboard/ExtScoreboard.ino
@@ -0,0 +1,114 @@
+/*
+ Dual counter example (Extended class example)
+
+ You could do many things with a dual counter. This example demoonstrates a scoreboard. Two players rol a dice every round, the player with the highest score wins the round. The number of rounds won are displayed on the scoreboard. Once one of the players reaches a maximum score the game is over. At the end of the game a player won message or draw will be displayed.
+
+ The circuit:
+ * connect TM1637 pin CLK to Arduino pin D4
+ * connect TM1637 pin DIO to Arduino pin D5
+ * connect TM1637 pin Vcc to Arduino pin 5V
+ * connect TM1637 pin GND to Arduino pin GND
+
+ Created 25 September 2015
+ By Bram Harmsen
+
+ https://github.com/bremme/arduino-tm1637
+
+*/
+
+// include the SevenSegmentTM1637 library
+#include "SevenSegmentTM1637.h"
+#include "SevenSegmentExtended.h"
+
+/* initialize global TM1637 Display object
+* The constructor takes two arguments, the number of the clock pin and the digital output pin:
+* SevenSegmentTM1637(byte pinCLK, byte pinDIO);
+*/
+const byte PIN_CLK = 4; // define CLK pin (any digital pin)
+const byte PIN_DIO = 5; // define DIO pin (any digital pin)
+SevenSegmentExtended display(PIN_CLK, PIN_DIO);
+
+// sets the maximum score for the game
+const byte maxScore = 10;
+
+// run setup code
+void setup() {
+ Serial.begin(9600); // initializes the Serial connection @ 9600 baud
+ display.begin(); // initializes the display
+ display.setBacklight(100); // set the brightness to 100 %
+ delay(1000); // wait 1000 ms
+ randomSeed(analogRead(0)); // get a random seed for throwing dices
+};
+
+// run loop (forever)
+void loop() {
+
+ byte playerOneScore = 0; // initialize player one score
+ byte playerTwoScore = 0; // initialize player two score
+ bool gameEnd = false; // initialize gameEnd?
+
+ display.print("START NEW GAME "); // Start a new round
+ delay(500); // delay 1000 ms
+ display.clear(); // clear the display
+
+ while( !gameEnd ) { // play till the end of the game
+
+ byte playerOneTurn = rollDice(); // player one rolls a dice
+ byte playerTwoTurn = rollDice(); // player two rolls a dice
+
+ if ( playerOneTurn > playerTwoTurn ) { // player one had a higher dice
+ playerOneScore++; // increase player one's score
+ } else if ( playerOneTurn < playerTwoTurn ){
+ playerTwoScore++; // increase player two's score
+ } else { // player one and two had same score
+ display.clear();
+ display.print("SAME");
+ display.blink();
+ display.clear();
+ }
+ // update scoreboard
+ display.printDualCounter(playerOneScore, playerTwoScore);
+ delay(500);
+
+ // check if game has ended (player one or two's score >= maxScore)
+ if ( (playerOneScore >= maxScore || playerTwoScore >= maxScore) ) {
+ gameEnd = true; // game has ended
+ printGameEnd(); // print end game message
+ break; // break from while loop (jump out)
+ };
+
+ };
+
+ // check who won
+ if ( playerOneScore > playerTwoScore ) {
+ printPlayerWon(1); // player one won
+ } else if ( playerOneScore < playerTwoScore ){
+ printPlayerWon(2); // player two won
+ } else {
+ display.clear(); display.print("DRAW"); // draw
+ display.blink(); display.clear();
+ };
+};
+
+// roll a dice: random rumber fom 0 to 5 + 1 = 0 to 6
+byte rollDice() {
+ return (random(6) + 1);
+}
+
+// print winning player message
+void printPlayerWon(byte player) {
+ display.clear();
+ char winText[] = "PLAYER x WON ";
+ winText[7] = (char)player + '0';
+ display.print(winText);
+ delay(500);
+ display.clear();
+};
+
+// print end game message
+void printGameEnd(void) {
+ display.clear();
+ display.print("GAME END ");
+ display.blink();
+ delay(500);
+};
diff --git a/lib/lib_display/arduino-tm1637/examples/FunPrintAll/FunPrintAll.ino b/lib/lib_display/arduino-tm1637/examples/FunPrintAll/FunPrintAll.ino
new file mode 100755
index 000000000..87ca6b896
--- /dev/null
+++ b/lib/lib_display/arduino-tm1637/examples/FunPrintAll/FunPrintAll.ino
@@ -0,0 +1,139 @@
+/*
+ Fun Print all example
+
+ This is a demonstration off a lot of the possibilities of the Fun superclass
+
+ The circuit:
+ * connect TM1637 pin CLK to Arduino pin D4
+ * connect TM1637 pin DIO to Arduino pin D5
+ * connect TM1637 pin Vcc to Arduino pin 5V
+ * connect TM1637 pin GND to Arduino pin GND
+
+ Created 25 September 2015
+ By Bram Harmsen
+
+ https://github.com/bremme/arduino-tm1637
+
+*/
+
+// include the SevenSegmentTM1637 library
+#include "SevenSegmentTM1637.h"
+#include "SevenSegmentExtended.h"
+#include "SevenSegmentFun.h"
+
+/* initialize global TM1637 Display object
+* The constructor takes two arguments, the number of the clock pin and the digital output pin:
+* SevenSegmentTM1637(byte pinCLK, byte pinDIO);
+*/
+const byte PIN_CLK = 4; // define CLK pin (any digital pin)
+const byte PIN_DIO = 5; // define DIO pin (any digital pin)
+SevenSegmentFun display(PIN_CLK, PIN_DIO);
+
+
+// run setup code
+void setup() {
+ Serial.begin(9600); // initializes the Serial connection @ 9600 baud
+ display.begin(); // initializes the display
+ display.setBacklight(100); // set the brightness to 100 %
+ delay(1000); // wait 1000 ms
+};
+
+// run loop (forever)
+void loop() {
+
+ // vertical level (e.g. audio volume)
+ introDuceNextDemo("AUDIO VOLUME DEMO");
+ audioVolume();
+
+ // bouncing ball
+ introDuceNextDemo("BOUNCING BALL DEMO");
+ unsigned int numMoves = 100; unsigned int timeDelay = 100;
+ display.bouncingBall(numMoves, timeDelay);
+
+ // scrolling text
+ introDuceNextDemo("SCROLLING TEXT DEMO");
+ byte repeats = 2;
+ display.scrollingText("ARDUINO TM1637 FUN", repeats);
+
+ // nightrider
+ introDuceNextDemo("REMEMBER KIT? NIGHTRIDER DEMO");
+ repeats = 4;
+ display.nightrider(repeats);
+
+ // snake
+ introDuceNextDemo("SNAKE DEMO");
+ display.snake(repeats);
+
+ // horizontal level (e.g equalizer)
+ introDuceNextDemo("EQUALIZER DEMO");
+ equalizer();
+
+ // bomb timer
+ introDuceNextDemo("GET READY FOR THE BOMB");
+ byte hours = 5; byte min = 16; unsigned int speed = 10000;
+ display.bombTimer(hours, min, speed, " RUN ");
+ delay(1000);;
+};
+
+// Demo for displaying a vertical level, for example audio volume, battery charge etc.
+void audioVolume() {
+
+ for (byte repeats=0; repeats < 2; repeats++) {
+ for (byte level=0; level < 125; level+=25) {
+ display.printLevelVertical(level);
+ delay(200);
+ };
+ for (byte level=100; level != 0; level-=25) {
+ display.printLevelVertical(level);
+ delay(200);
+ };
+ }
+
+ // maybe try another symbol instead of default | |
+ byte symbol = display.encode((byte)0);
+ bool leftToRight = false; // print the other direction
+
+
+ for (byte repeats=0; repeats < 2; repeats++) {
+ for (byte level=0; level < 125; level+=25) {
+ display.printLevelVertical(level, leftToRight, symbol);
+ delay(200);
+ if ( level == 100 ) {
+ display.blink();
+ }
+ };
+ for (byte level=100; level != 0; level-=25) {
+ display.printLevelVertical(level, leftToRight, symbol);
+ delay(200);
+ };
+ }
+ // 0, 25, 50, 75, 100
+
+}
+
+void equalizer() {
+ // initialize horizontal level counters
+ char i,j,k,l;
+ byte levels[4];
+
+ // repeat 5 times
+ for ( byte r=0; r < 4; r++) {
+ // increae i and k, decrease j and l
+ for (i=0,j=100,k=0,l=100; i <= 100; i+=33, j-=33, k+=33, l-=33) {
+ levels[0] = i; levels[1] = j; levels[2] = k; levels[3] = l;
+ display.printLevelHorizontal(levels);
+ delay(100);
+ }
+ // increae j and l, decrease i and k
+ for (i=100,j=0,k=100,l=0; i >= 0; i-=33, j+=33, k-=33, l+=33) {
+ levels[0] = i; levels[1] = j; levels[2] = k; levels[3] = l;
+ display.printLevelHorizontal(levels);
+ delay(100);
+ }
+ }
+}
+
+void introDuceNextDemo(char* str) {
+ display.print(str);
+ delay(500);
+}
diff --git a/lib/lib_display/arduino-tm1637/examples/FunPrintLevel/FunPrintLevel.ino b/lib/lib_display/arduino-tm1637/examples/FunPrintLevel/FunPrintLevel.ino
new file mode 100755
index 000000000..e9b73ebc0
--- /dev/null
+++ b/lib/lib_display/arduino-tm1637/examples/FunPrintLevel/FunPrintLevel.ino
@@ -0,0 +1,52 @@
+#include "SevenSegmentTM1637.h"
+#include "SevenSegmentExtended.h"
+#include "SevenSegmentFun.h"
+
+// define clock and digital input pins
+#define PIN_CLK 4
+#define PIN_DIO 5
+
+// initialize TM1637 Display objects
+SevenSegmentFun display(PIN_CLK, PIN_DIO);
+
+
+void setup() {
+ // initialize the display
+ display.begin();
+}
+
+void loop() {
+
+ // increase level from 0 to 100
+ for (uint8_t i=0; i <= 100; i+=25) {
+ display.printLevelVertical(i);
+ delay(100);
+ };
+ // decrease level from 100 to 0
+ for (int8_t i=100; i >=0; i-=25) {
+ display.printLevelVertical(i);
+ delay(100);
+ }
+
+ // initialize horizontal level counters
+ int8_t i,j,k,l;
+ uint8_t levels[4];
+
+
+ // repeat 5 times
+ for ( uint8_t r=0; r < 4; r++) {
+ // increae i and k, decrease j and l
+ for (i=0,j=100,k=0,l=100; i <= 100; i+=33, j-=33, k+=33, l-=33) {
+ levels[0] = i; levels[1] = j; levels[2] = k; levels[3] = l;
+ display.printLevelHorizontal(levels);
+ delay(100);
+ }
+ // increae j and l, decrease i and k
+ for (i=100,j=0,k=100,l=0; i >= 0; i-=33, j+=33, k-=33, l+=33) {
+ levels[0] = i; levels[1] = j; levels[2] = k; levels[3] = l;
+ display.printLevelHorizontal(levels);
+ delay(100);
+ }
+ }
+
+}
diff --git a/lib/lib_display/arduino-tm1637/examples/LowLevel/LowLevel.ino b/lib/lib_display/arduino-tm1637/examples/LowLevel/LowLevel.ino
new file mode 100755
index 000000000..e309950b7
--- /dev/null
+++ b/lib/lib_display/arduino-tm1637/examples/LowLevel/LowLevel.ino
@@ -0,0 +1,111 @@
+/*
+ Low level (advanced) example
+
+ In this example the display is controlled by the low level command function.
+ See SevenSegmentTM1637.h for more details on how the protocol actually works and which commands it accepts.
+
+ The circuit:
+ * connect TM1637 pin CLK to Arduino pin D4
+ * connect TM1637 pin DIO to Arduino pin D5
+ * connect TM1637 pin Vcc to Arduino pin 5V
+ * connect TM1637 pin GND to Arduino pin GND
+
+ Created 25 September 2015
+ By Bram Harmsen
+
+ https://github.com/bremme/arduino-tm1637
+
+*/
+
+// include the SevenSegmentTM1637 library
+#include "SevenSegmentTM1637.h"
+
+/* initialize global TM1637 Display object
+* The constructor takes two arguments, the number of the clock pin and the digital output pin:
+* SevenSegmentTM1637(byte pinCLK, byte pinDIO);
+*/
+const byte PIN_CLK = 4; // define CLK pin (any digital pin)
+const byte PIN_DIO = 5; // define DIO pin (any digital pin)
+SevenSegmentTM1637 display(PIN_CLK, PIN_DIO);
+
+// run setup code
+void setup() {
+ Serial.begin(9600); // initializes the Serial connection @ 9600 baud
+};
+
+void loop() {
+ // set brightness
+ uint8_t setDisplayCmd = B10000000; // bit 6,7
+ uint8_t brightnessBits = B111; // bit 0,1,2 (7 = max)
+ uint8_t displayOnBit = B1000; // bit 3
+ // construct complete command
+ uint8_t command = setDisplayCmd | brightnessBits | displayOnBit;
+
+ // turn display on and set brightness to max (7)
+ bool ack = display.command(command);
+ // print acknowledged?
+ Serial.println(ack);
+
+ // write init to display using automatic address
+ uint8_t setDataCmd = B01000000; // bit 6,7
+ ack = display.command(setDataCmd);
+ Serial.println(ack);
+
+ uint8_t setAddrCmd = B11000000; // bit 6,7 set addr to 0
+ uint8_t dataI = B00000110; // I
+ uint8_t dataN = B00110111; // N
+ uint8_t dataT = B01111000; // T
+
+ uint8_t commands[5];
+ commands[0] = setAddrCmd;
+ commands[1] = dataI;
+ commands[2] = dataN;
+ commands[3] = dataI;
+ commands[4] = dataT;
+
+ ack = display.command(commands, 5);
+ Serial.println(ack);
+ delay(1000);
+
+ // dim display
+ brightnessBits = B001; // bit 0,1,2 level=1;
+ command = setDisplayCmd | brightnessBits | displayOnBit;
+ display.command(command);
+ delay(1000);
+
+ // set back to full brightness
+ brightnessBits = B111; // bit 0,1,2 (7 = max)
+ command = setDisplayCmd | brightnessBits | displayOnBit;
+ display.command(command);
+ delay(1000);
+
+ // write characters to display in non auto mode
+ setDataCmd |= 1 << 2; // bit 2 control auto/manual address
+ ack = display.command(setDataCmd);
+ Serial.println(ack);
+
+ commands[1] = B01110111;
+ commands[2] = B01111111;
+ commands[3] = B00111001;
+ commands[4] = B01011110;
+
+ for (uint8_t i = 1; i < 5; i++) {
+ commands[1] = commands[i]; // set character
+ display.command(commands, 2); // write character to display
+ commands[0]++; // increase address
+ }
+ delay(1000);
+
+ // set back to auto address mode
+ setDataCmd &= ~(1 << 2); // bit 2 control auto/manual address
+ ack = display.command(setDataCmd);
+ Serial.println(ack);
+
+ // clear display
+ commands[0] = setAddrCmd;
+ commands[1] = 0; commands[2] = 0;commands[3] = 0; commands[4] = 0;
+
+ // clear display
+ display.command(commands, 5);
+ delay(1000);
+}
diff --git a/lib/lib_display/arduino-tm1637/examples/PrintNumber/PrintNumber.ino b/lib/lib_display/arduino-tm1637/examples/PrintNumber/PrintNumber.ino
new file mode 100755
index 000000000..0280d81aa
--- /dev/null
+++ b/lib/lib_display/arduino-tm1637/examples/PrintNumber/PrintNumber.ino
@@ -0,0 +1,76 @@
+/*
+ Printing numbers example
+
+ Demonstrate a couple different ways nubers can be printed: positive, negative,
+ with padding and with rollover.
+
+
+ The circuit:
+ * connect TM1637 pin CLK to Arduino pin D4
+ * connect TM1637 pin DIO to Arduino pin D5
+ * connect TM1637 pin Vcc to Arduino pin 5V
+ * connect TM1637 pin GND to Arduino pin GND
+
+ Created 22 June 2020
+ By Bram Harmsen
+
+ https://github.com/bremme/arduino-tm1637
+
+*/
+
+// include the SevenSegmentTM1637 library
+#include "SevenSegmentTM1637.h"
+#include "SevenSegmentExtended.h"
+
+/* initialize global TM1637 Display object
+* The constructor takes two arguments, the number of the clock pin and the digital output pin:
+* SevenSegmentTM1637(byte pinCLK, byte pinDIO);
+*/
+const byte PIN_CLK = 4; // define CLK pin (any digital pin)
+const byte PIN_DIO = 5; // define DIO pin (any digital pin)
+SevenSegmentExtended display(PIN_CLK, PIN_DIO);
+
+// run setup code
+void setup() {
+ Serial.begin(9600); // initializes the Serial connection @ 9600 baud
+ display.begin(); // initializes the display
+ display.setBacklight(100); // set the brightness to 100 %
+ display.print("INIT"); // display INIT on the display
+ delay(1000); // wait 1000 ms
+};
+
+// run loop (forever)
+void loop() {
+
+ // print positive numbers
+ display.print("PRINT POSITIVE NUMBERS");
+ delay(1000); // wait 1000 ms
+ for (int16_t number=0; number < 2000; number++) {
+ display.printNumber(number);
+ delay(1);
+ };
+
+ // print negative numbers
+ display.print("PRINT NEGATIVE NUMBERS");
+ delay(1000); // wait 1000 ms
+ for (int16_t number=0; number > -999; number--) {
+ display.printNumber(number);
+ delay(2);
+ };
+
+ // print with positive with zero padding
+ display.print("PRINT POSITIVE NUMBERS WITH PADDING");
+ delay(1000); // wait 1000 ms
+ for (int16_t number=0; number < 2000; number++) {
+ display.printNumber(number, true);
+ delay(1);
+ };
+
+ // print with rollover (e.g. 10000 -> 0, 10001 -> 1)
+ display.print("PRINT POSITIVE NUMBERS WITH ROLLOVER");
+ delay(1000); // wait 1000 ms
+ for (int16_t number=0; number < 20000; number+=250) {
+ display.printNumber(number, false, true);
+ delay(50);
+ };
+};
diff --git a/lib/lib_display/arduino-tm1637/examples/SerialPrint/SerialPrint.ino b/lib/lib_display/arduino-tm1637/examples/SerialPrint/SerialPrint.ino
new file mode 100755
index 000000000..5951a3276
--- /dev/null
+++ b/lib/lib_display/arduino-tm1637/examples/SerialPrint/SerialPrint.ino
@@ -0,0 +1,79 @@
+/*
+ Serial Print example
+
+ Prints text typed to the Serial Monitor on the display. Connect an Arduino to a TM1637 4 digit 7-segment display. Connect the wires as descibed below. Run the sketch and open the Serial Monitor. Set the the speed to 9600 baud, set the line ending to Newline. Now type some text and press enter, the text will be displayed on the 7-segment display.
+
+ Note: make sure to set your serial monitor to line end: NEW LINE!
+
+ The circuit:
+ * connect TM1637 pin CLK to Arduino pin D4
+ * connect TM1637 pin DIO to Arduino pin D5
+ * connect TM1637 pin Vcc to Arduino pin 5V
+ * connect TM1637 pin GND to Arduino pin GND
+
+ Created 25 September 2015
+ By Bram Harmsen
+
+ http://url/of/online/tutorial.cc
+
+*/
+
+// include the SevenSegmentTM1637 library
+#include "SevenSegmentTM1637.h"
+
+/* initialize global TM1637 Display object
+* The constructor takes two arguments, the number of the clock pin and the digital output pin:
+* SevenSegmentTM1637(byte pinCLK, byte pinDIO);
+*/
+const byte PIN_CLK = 4; // define CLK pin (any digital pin)
+const byte PIN_DIO = 5; // define DIO pin (any digital pin)
+SevenSegmentTM1637 display(PIN_CLK, PIN_DIO);
+
+// define a fixed buffer size for receiving characters via Serial
+const byte BUFFER_SIZE = 128;
+char serialBuffer[BUFFER_SIZE]; // initialize global serial buffer
+
+// run setup code
+void setup() {
+ display.begin(); // initializes the display
+ Serial.begin(9600); // initializes the Serial connection @ 9600 baud
+ Serial.println(F("Enter some text followed by ENTER"));
+ Serial.println(F("also make sure to set the line ending to Newline (\\n)"));
+};
+
+// run loop (forever)
+void loop() {
+ // if received new serial data, print to display
+ if ( receivedSerialString() ) {
+ display.print(serialBuffer); // Print received serial data to display
+ Serial.print(F("Echo:\t")); // Echo serial data back to Serial Monitor
+ Serial.println(serialBuffer);
+ };
+};
+
+// Read in bytes from Serial Monitor and return true is ether the serialBuffer is full or a Newline character is received
+bool receivedSerialString() {
+ static unsigned int i=0; // init static counter to keep track of count
+
+ while( Serial.available() ) { // check if new data arrived
+
+ if ( i == BUFFER_SIZE-1) { // if buffer is full RETURN true
+ serialBuffer[i] = '\0'; // add termination char
+ i = 0;
+ Serial.println(F("Buffer full!"));
+ return true;
+ };
+
+ char c = Serial.read(); // read new char from serial port
+
+ if ( c == '\n') { // if new line RETURN true
+ serialBuffer[i] = '\0'; // add termination char
+ i = 0;
+ return true;
+ } else {
+ serialBuffer[i] = c; // add received character to buffer
+ }
+ i++; // increase counter
+ };
+ return false; // default RETURN false
+};
diff --git a/lib/lib_display/arduino-tm1637/keywords.txt b/lib/lib_display/arduino-tm1637/keywords.txt
new file mode 100644
index 000000000..84f7303ec
--- /dev/null
+++ b/lib/lib_display/arduino-tm1637/keywords.txt
@@ -0,0 +1,56 @@
+#######################################
+# Syntax Coloring Map For SevenSegmentTM1637
+#######################################
+
+#######################################
+# Datatypes (KEYWORD1)
+#######################################
+
+SevenSegmentTM1637 KEYWORD1
+SevenSegmentExtended KEYWORD1
+SevenSegmentFun KEYWORD1
+
+#######################################
+# Methods and Functions (KEYWORD2)
+#######################################
+
+init KEYWORD2
+begin KEYWORD2
+write KEYWORD2
+clear KEYWORD2
+home KEYWORD2
+setCursor KEYWORD2
+setBacklight KEYWORD2
+setContrast KEYWORD2
+on KEYWORD2
+off KEYWORD2
+blink KEYWORD2
+setColonOn KEYWORD2
+getColonOn KEYWORD2
+setPrintDelay KEYWORD2
+encode KEYWORD2
+shiftLeft KEYWORD2
+printRaw KEYWORD2
+command KEYWORD2
+comReadByte KEYWORD2
+comWriteByte KEYWORD2
+comStart KEYWORD2
+comStop KEYWORD2
+comAck KEYWORD2
+printTime KEYWORD2
+printNumber KEYWORD2
+printDualCounter KEYWORD2
+printLevelVertical KEYWORD2
+printLevelHorizontal KEYWORD2
+scrollingText KEYWORD2
+snake KEYWORD2
+nightrider KEYWORD2
+bombTimer KEYWORD2
+bouchingBall KEYWORD2
+printBall KEYWORD2
+print4Bit KEYWORD2
+
+
+#######################################
+# Constants (LITERAL1)
+#######################################
diff --git a/lib/lib_display/arduino-tm1637/library.properties b/lib/lib_display/arduino-tm1637/library.properties
new file mode 100644
index 000000000..f8431c31b
--- /dev/null
+++ b/lib/lib_display/arduino-tm1637/library.properties
@@ -0,0 +1,9 @@
+name=SevenSegmentTM1637
+version=1.1.0
+author=Bram Harmsen
+maintainer=Bram Harmsen
+sentence=Library for using a 4 digit seven segment display with TM1636 or TM1637 driver IC
+paragraph=Extensive library for controlling a 4 digit seven segment display. This library inherent the Print class and uses the LCDAPI 1.0. For example you can use all normal Print methods like: print() and println(). From the LCDAPI among others begin(), clear(), home(), setCursor() and setBacklight() are implementend. On top of these regular functionality a segerate fun class which adds more features can be used. For example a bombtimer(), nightrider() and bouncingBall() method can be used when using the fun class.
+category=Display
+url=https://github.com/bremme/arduino-tm1637
+architectures=*
diff --git a/lib/libesp32/Berry-0.1.10/src/port/be_port.cpp b/lib/libesp32/Berry-0.1.10/src/port/be_port.cpp
index fd9ac48d6..cf67833c5 100644
--- a/lib/libesp32/Berry-0.1.10/src/port/be_port.cpp
+++ b/lib/libesp32/Berry-0.1.10/src/port/be_port.cpp
@@ -8,10 +8,14 @@
#include "berry.h"
#include "be_mem.h"
#include "be_sys.h"
-#include
+// #include
#include
#include
+// Local pointer for file managment
+#include
+extern FS *dfsp;
+
/* this file contains configuration for the file system. */
/* standard input and output */
@@ -47,9 +51,36 @@ extern "C" {
}
}
+// We need to create a local buffer, since we might mess up mqtt_data
+#ifndef BERRY_LOGSZ
+#define BERRY_LOGSZ 128
+#endif
+static char log_berry_buffer[BERRY_LOGSZ] = { 0, };
+extern void berry_log(const char * berry_buf);
+
BERRY_API void be_writebuffer(const char *buffer, size_t length)
{
- Serial.write(buffer, length);
+ if (buffer == nullptr || length == 0) { return; }
+ uint32_t idx = 0;
+ while (idx < length) {
+ int32_t cr_pos = -1;
+ // find next occurence of '\n' or '\r'
+ for (uint32_t i = idx; i < length; i++) {
+ if ((pgm_read_byte(&buffer[i]) == '\n') || (pgm_read_byte(&buffer[i]) == '\r')) {
+ cr_pos = i;
+ break;
+ }
+ }
+ uint32_t chars_to_append = (cr_pos >= 0) ? cr_pos - idx : length - idx; // note cr_pos < length
+ snprintf(log_berry_buffer, sizeof(log_berry_buffer), "%s%.*s", log_berry_buffer, chars_to_append, &buffer[idx]); // append at most `length` chars
+ if (cr_pos >= 0) {
+ // flush
+ berry_log(log_berry_buffer);
+ log_berry_buffer[0] = 0; // clear string
+ }
+ idx += chars_to_append + 1; // skip '\n'
+ }
+ // Serial.write(buffer, length);
// be_fwrite(stdout, buffer, length);
}
@@ -63,54 +94,116 @@ BERRY_API char* be_readstring(char *buffer, size_t size)
void* be_fopen(const char *filename, const char *modes)
{
+ if (dfsp != nullptr && filename != nullptr && modes != nullptr) {
+ // Serial.printf("be_fopen filename=%s, modes=%s\n", filename, modes);
+ File f = dfsp->open(filename, modes); // returns an object, not a pointer
+ if (f) {
+ File * f_ptr = new File(f); // copy to dynamic object
+ *f_ptr = f; // TODO is this necessary?
+ return f_ptr;
+ }
+ }
return nullptr;
// return fopen(filename, modes);
}
int be_fclose(void *hfile)
{
- return 0;
+ // Serial.printf("be_fclose\n");
+ if (dfsp != nullptr && hfile != nullptr) {
+ File * f_ptr = (File*) hfile;
+ f_ptr->close();
+ delete f_ptr;
+ return 0;
+ }
+ return -1;
// return fclose(hfile);
}
size_t be_fwrite(void *hfile, const void *buffer, size_t length)
{
+ // Serial.printf("be_fwrite %d\n", length);
+ if (dfsp != nullptr && hfile != nullptr && buffer != nullptr) {
+ File * f_ptr = (File*) hfile;
+ return f_ptr->write((const uint8_t*) buffer, length);
+ }
return 0;
// return fwrite(buffer, 1, length, hfile);
}
size_t be_fread(void *hfile, void *buffer, size_t length)
{
+ // Serial.printf("be_fread %d\n", length);
+ if (dfsp != nullptr && hfile != nullptr && buffer != nullptr) {
+ File * f_ptr = (File*) hfile;
+ int32_t ret = f_ptr->read((uint8_t*) buffer, length);
+ if (ret >= 0) {
+ // Serial.printf("be_fread ret = %d\n", ret);
+ return ret;
+ }
+ }
return 0;
// return fread(buffer, 1, length, hfile);
}
char* be_fgets(void *hfile, void *buffer, int size)
{
+ // Serial.printf("be_fgets %d\n", size);
+ uint8_t * buf = (uint8_t*) buffer;
+ if (dfsp != nullptr && hfile != nullptr && buffer != nullptr && size > 0) {
+ File * f_ptr = (File*) hfile;
+ int ret = f_ptr->readBytesUntil('\n', buf, size - 1);
+ if (ret >= 0) {
+ buf[ret] = 0; // add string terminator
+ return (char*) buffer;
+ }
+ }
return nullptr;
// return fgets(buffer, size, hfile);
}
int be_fseek(void *hfile, long offset)
{
- return 0;
+ // Serial.printf("be_fseek %d\n", offset);
+ if (dfsp != nullptr && hfile != nullptr) {
+ File * f_ptr = (File*) hfile;
+ if (f_ptr->seek(offset)) {
+ return 0; // success
+ }
+ }
+ return -1;
// return fseek(hfile, offset, SEEK_SET);
}
long int be_ftell(void *hfile)
{
+ // Serial.printf("be_ftell\n");
+ if (dfsp != nullptr && hfile != nullptr) {
+ File * f_ptr = (File*) hfile;
+ return f_ptr->position();
+ }
return 0;
// return ftell(hfile);
}
long int be_fflush(void *hfile)
{
+ // Serial.printf("be_fflush\n");
+ if (dfsp != nullptr && hfile != nullptr) {
+ File * f_ptr = (File*) hfile;
+ f_ptr->flush();
+ }
return 0;
// return fflush(hfile);
}
size_t be_fsize(void *hfile)
{
+ // Serial.printf("be_fsize\n");
+ if (dfsp != nullptr && hfile != nullptr) {
+ File * f_ptr = (File*) hfile;
+ return f_ptr->size();
+ }
// long int size, offset = be_ftell(hfile);
// fseek(hfile, 0L, SEEK_END);
// size = ftell(hfile);
@@ -121,233 +214,233 @@ size_t be_fsize(void *hfile)
-// #if BE_USE_FILE_SYSTEM
-// #if defined(USE_FATFS) /* FatFs */
+#if BE_USE_FILE_SYSTEM
+#if defined(USE_FATFS) /* FatFs */
-// int be_isdir(const char *path)
-// {
-// FILINFO fno;
-// FRESULT fr = f_stat(path, &fno);
-// return fr == FR_OK && fno.fattrib & AM_DIR;
-// }
+int be_isdir(const char *path)
+{
+ FILINFO fno;
+ FRESULT fr = f_stat(path, &fno);
+ return fr == FR_OK && fno.fattrib & AM_DIR;
+}
-// int be_isfile(const char *path)
-// {
-// FILINFO fno;
-// FRESULT fr = f_stat(path, &fno);
-// return fr == FR_OK && !(fno.fattrib & AM_DIR);
-// }
+int be_isfile(const char *path)
+{
+ FILINFO fno;
+ FRESULT fr = f_stat(path, &fno);
+ return fr == FR_OK && !(fno.fattrib & AM_DIR);
+}
-// int be_isexist(const char *path)
-// {
-// FILINFO fno;
-// return f_stat(path, &fno) == FR_OK;
-// }
+int be_isexist(const char *path)
+{
+ FILINFO fno;
+ return f_stat(path, &fno) == FR_OK;
+}
-// char* be_getcwd(char *buf, size_t size)
-// {
-// FRESULT fr = f_getcwd(buf, (UINT)size);
-// return fr == FR_OK ? buf : NULL;
-// }
+char* be_getcwd(char *buf, size_t size)
+{
+ FRESULT fr = f_getcwd(buf, (UINT)size);
+ return fr == FR_OK ? buf : NULL;
+}
-// int be_chdir(const char *path)
-// {
-// return f_chdir(path);
-// }
+int be_chdir(const char *path)
+{
+ return f_chdir(path);
+}
-// int be_mkdir(const char *path)
-// {
-// return f_mkdir(path);
-// }
+int be_mkdir(const char *path)
+{
+ return f_mkdir(path);
+}
-// int be_unlink(const char *filename)
-// {
-// return f_unlink(filename);
-// }
+int be_unlink(const char *filename)
+{
+ return f_unlink(filename);
+}
-// int be_dirfirst(bdirinfo *info, const char *path)
-// {
-// info->dir = be_os_malloc(sizeof(DIR));
-// info->file = be_os_malloc(sizeof(FILINFO));
-// if (info->dir && info->file) {
-// FRESULT fr = f_opendir(info->dir, path);
-// return fr == FR_OK ? be_dirnext(info) : 1;
-// }
-// be_os_free(info->dir);
-// be_os_free(info->file);
-// info->dir = NULL;
-// info->file = NULL;
-// return 1;
-// }
+int be_dirfirst(bdirinfo *info, const char *path)
+{
+ info->dir = be_os_malloc(sizeof(DIR));
+ info->file = be_os_malloc(sizeof(FILINFO));
+ if (info->dir && info->file) {
+ FRESULT fr = f_opendir(info->dir, path);
+ return fr == FR_OK ? be_dirnext(info) : 1;
+ }
+ be_os_free(info->dir);
+ be_os_free(info->file);
+ info->dir = NULL;
+ info->file = NULL;
+ return 1;
+}
-// int be_dirnext(bdirinfo *info)
-// {
-// FRESULT fr = f_readdir(info->dir, info->file);
-// info->name = ((FILINFO *)info->file)->fname;
-// return fr != FR_OK || *info->name == '\0';
-// }
+int be_dirnext(bdirinfo *info)
+{
+ FRESULT fr = f_readdir(info->dir, info->file);
+ info->name = ((FILINFO *)info->file)->fname;
+ return fr != FR_OK || *info->name == '\0';
+}
-// int be_dirclose(bdirinfo *info)
-// {
-// if (info->dir) {
-// int res = f_closedir(info->dir) != FR_OK;
-// be_os_free(info->dir);
-// be_os_free(info->file);
-// return res;
-// }
-// return 1;
-// }
+int be_dirclose(bdirinfo *info)
+{
+ if (info->dir) {
+ int res = f_closedir(info->dir) != FR_OK;
+ be_os_free(info->dir);
+ be_os_free(info->file);
+ return res;
+ }
+ return 1;
+}
-// #elif defined(_MSC_VER) /* MSVC*/
+#elif defined(_MSC_VER) /* MSVC*/
-// #include
-// #include
-// #include
+#include
+#include
+#include
-// int be_isdir(const char *path)
-// {
-// DWORD type = GetFileAttributes(path);
-// return type != INVALID_FILE_ATTRIBUTES
-// && (type & FILE_ATTRIBUTE_DIRECTORY) != 0;
-// }
+int be_isdir(const char *path)
+{
+ DWORD type = GetFileAttributes(path);
+ return type != INVALID_FILE_ATTRIBUTES
+ && (type & FILE_ATTRIBUTE_DIRECTORY) != 0;
+}
-// int be_isfile(const char *path)
-// {
-// DWORD type = GetFileAttributes(path);
-// return type != INVALID_FILE_ATTRIBUTES
-// && (type & FILE_ATTRIBUTE_DIRECTORY) == 0;
-// }
+int be_isfile(const char *path)
+{
+ DWORD type = GetFileAttributes(path);
+ return type != INVALID_FILE_ATTRIBUTES
+ && (type & FILE_ATTRIBUTE_DIRECTORY) == 0;
+}
-// int be_isexist(const char *path)
-// {
-// return GetFileAttributes(path) != INVALID_FILE_ATTRIBUTES;
-// }
+int be_isexist(const char *path)
+{
+ return GetFileAttributes(path) != INVALID_FILE_ATTRIBUTES;
+}
-// char* be_getcwd(char *buf, size_t size)
-// {
-// return _getcwd(buf, (int)size);
-// }
+char* be_getcwd(char *buf, size_t size)
+{
+ return _getcwd(buf, (int)size);
+}
-// int be_chdir(const char *path)
-// {
-// return _chdir(path);
-// }
+int be_chdir(const char *path)
+{
+ return _chdir(path);
+}
-// int be_mkdir(const char *path)
-// {
-// return _mkdir(path);
-// }
+int be_mkdir(const char *path)
+{
+ return _mkdir(path);
+}
-// int be_unlink(const char *filename)
-// {
-// return remove(filename);
-// }
+int be_unlink(const char *filename)
+{
+ return remove(filename);
+}
-// int be_dirfirst(bdirinfo *info, const char *path)
-// {
-// char *buf = be_os_malloc(strlen(path) + 3);
-// info->file = be_os_malloc(sizeof(struct _finddata_t));
-// info->dir = NULL;
-// if (buf && info->file) {
-// struct _finddata_t *cfile = info->file;
-// strcat(strcpy(buf, path), "/*");
-// info->dir = (void *)_findfirst(buf, cfile);
-// info->name = cfile->name;
-// be_os_free(buf);
-// return (intptr_t)info->dir == -1;
-// }
-// be_os_free(buf);
-// return 1;
-// }
+int be_dirfirst(bdirinfo *info, const char *path)
+{
+ char *buf = be_os_malloc(strlen(path) + 3);
+ info->file = be_os_malloc(sizeof(struct _finddata_t));
+ info->dir = NULL;
+ if (buf && info->file) {
+ struct _finddata_t *cfile = info->file;
+ strcat(strcpy(buf, path), "/*");
+ info->dir = (void *)_findfirst(buf, cfile);
+ info->name = cfile->name;
+ be_os_free(buf);
+ return (intptr_t)info->dir == -1;
+ }
+ be_os_free(buf);
+ return 1;
+}
-// int be_dirnext(bdirinfo *info)
-// {
-// struct _finddata_t *cfile = info->file;
-// int res = _findnext((intptr_t)info->dir, cfile) != 0;
-// info->name = cfile->name;
-// return res;
-// }
+int be_dirnext(bdirinfo *info)
+{
+ struct _finddata_t *cfile = info->file;
+ int res = _findnext((intptr_t)info->dir, cfile) != 0;
+ info->name = cfile->name;
+ return res;
+}
-// int be_dirclose(bdirinfo *info)
-// {
-// be_os_free(info->file);
-// return _findclose((intptr_t)info->dir) != 0;
-// }
+int be_dirclose(bdirinfo *info)
+{
+ be_os_free(info->file);
+ return _findclose((intptr_t)info->dir) != 0;
+}
-// #else /* must be POSIX */
+#else /* must be POSIX */
-// #include
-// #include
-// #include
+#include
+#include
+#include
-// int be_isdir(const char *path)
-// {
-// struct stat path_stat;
-// int res = stat(path, &path_stat);
-// return res == 0 && S_ISDIR(path_stat.st_mode);
-// }
+int be_isdir(const char *path)
+{
+ struct stat path_stat;
+ int res = stat(path, &path_stat);
+ return res == 0 && S_ISDIR(path_stat.st_mode);
+}
-// int be_isfile(const char *path)
-// {
-// struct stat path_stat;
-// int res = stat(path, &path_stat);
-// return res == 0 && !S_ISDIR(path_stat.st_mode);
-// }
+int be_isfile(const char *path)
+{
+ struct stat path_stat;
+ int res = stat(path, &path_stat);
+ return res == 0 && !S_ISDIR(path_stat.st_mode);
+}
-// int be_isexist(const char *path)
-// {
-// struct stat path_stat;
-// return stat(path, &path_stat) == 0;
-// }
+int be_isexist(const char *path)
+{
+ struct stat path_stat;
+ return stat(path, &path_stat) == 0;
+}
-// char* be_getcwd(char *buf, size_t size)
-// {
-// return getcwd(buf, size);
-// }
+char* be_getcwd(char *buf, size_t size)
+{
+ return getcwd(buf, size);
+}
-// int be_chdir(const char *path)
-// {
-// return chdir(path);
-// }
+int be_chdir(const char *path)
+{
+ return chdir(path);
+}
-// int be_mkdir(const char *path)
-// {
-// #ifdef _WIN32
-// return mkdir(path);
-// #else
-// return mkdir(path, S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH);
-// #endif
-// }
+int be_mkdir(const char *path)
+{
+#ifdef _WIN32
+ return mkdir(path);
+#else
+ return mkdir(path, S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH);
+#endif
+}
-// int be_unlink(const char *filename)
-// {
-// return remove(filename);
-// }
+int be_unlink(const char *filename)
+{
+ return remove(filename);
+}
-// int be_dirfirst(bdirinfo *info, const char *path)
-// {
-// info->dir = opendir(path);
-// if (info->dir) {
-// return be_dirnext(info);
-// }
-// return 1;
-// }
+int be_dirfirst(bdirinfo *info, const char *path)
+{
+ info->dir = opendir(path);
+ if (info->dir) {
+ return be_dirnext(info);
+ }
+ return 1;
+}
-// int be_dirnext(bdirinfo *info)
-// {
-// struct dirent *file;
-// info->file = file = readdir(info->dir);
-// if (file) {
-// info->name = file->d_name;
-// return 0;
-// }
-// return 1;
-// }
+int be_dirnext(bdirinfo *info)
+{
+ struct dirent *file;
+ info->file = file = readdir(info->dir);
+ if (file) {
+ info->name = file->d_name;
+ return 0;
+ }
+ return 1;
+}
-// int be_dirclose(bdirinfo *info)
-// {
-// return closedir(info->dir) != 0;
-// }
+int be_dirclose(bdirinfo *info)
+{
+ return closedir(info->dir) != 0;
+}
-// #endif /* POSIX */
-// #endif /* BE_USE_OS_MODULE || BE_USE_FILE_SYSTEM */
+#endif /* POSIX */
+#endif /* BE_USE_OS_MODULE || BE_USE_FILE_SYSTEM */
diff --git a/lib/libesp32/Berry-0.1.10/src/port/berry_conf.h b/lib/libesp32/Berry-0.1.10/src/port/berry_conf.h
index 5228f567f..820207003 100644
--- a/lib/libesp32/Berry-0.1.10/src/port/berry_conf.h
+++ b/lib/libesp32/Berry-0.1.10/src/port/berry_conf.h
@@ -38,7 +38,7 @@ extern "C" {
* type when the value is 2.
* Default: 2
*/
-#define BE_INTGER_TYPE 1
+#define BE_INTGER_TYPE 1 // use long int = uint32_t
/* Macro: BE_USE_SINGLE_FLOAT
* Select floating point precision.
@@ -47,7 +47,7 @@ extern "C" {
* numbers.
* Default: 0
**/
-#define BE_USE_SINGLE_FLOAT 1
+#define BE_USE_SINGLE_FLOAT 1 // use `float` not `double`
/* Macro: BE_USE_PRECOMPILED_OBJECT
* Use precompiled objects to avoid creating these objects at
@@ -118,14 +118,14 @@ extern "C" {
* otherwise disable the feature.
* Default: 1
**/
-#define BE_USE_BYTECODE_SAVER 0
+#define BE_USE_BYTECODE_SAVER 1
/* Macro: BE_USE_BYTECODE_LOADER
* Enable load bytecode from file when BE_USE_BYTECODE_LOADER is not 0,
* otherwise disable the feature.
* Default: 1
**/
-#define BE_USE_BYTECODE_LOADER 0
+#define BE_USE_BYTECODE_LOADER 1
/* Macro: BE_USE_SHARED_LIB
* Enable shared library when BE_USE_SHARED_LIB is not 0,
@@ -160,7 +160,7 @@ extern "C" {
#define BE_USE_TIME_MODULE 0
#define BE_USE_OS_MODULE 0
#define BE_USE_SYS_MODULE 0
-#define BE_USE_DEBUG_MODULE 0
+#define BE_USE_DEBUG_MODULE 1
#define BE_USE_GC_MODULE 1
/* Macro: BE_EXPLICIT_XXX
diff --git a/lib/libesp32/NimBLE-Arduino/CHANGELOG.md b/lib/libesp32/NimBLE-Arduino/CHANGELOG.md
index e763a3c6d..a76ef59a6 100644
--- a/lib/libesp32/NimBLE-Arduino/CHANGELOG.md
+++ b/lib/libesp32/NimBLE-Arduino/CHANGELOG.md
@@ -2,6 +2,70 @@
All notable changes to this project will be documented in this file.
+## [1.2.0] - 2021-02-08
+
+### Added
+- `NimBLECharacteristic::getDescriptorByHandle`: Return the BLE Descriptor for the given handle.
+
+- `NimBLEDescriptor::getStringValue`: Get the value of this descriptor as a string.
+
+- `NimBLEServer::getServiceByHandle`: Get a service by its handle.
+
+- `NimBLEService::getCharacteristicByHandle`: Get a pointer to the characteristic object with the specified handle.
+
+- `NimBLEService::getCharacteristics`: Get the vector containing pointers to each characteristic associated with this service.
+Overloads to get a vector containing pointers to all the characteristics in a service with the UUID. (supports multiple same UUID's in a service)
+ - `NimBLEService::getCharacteristics(const char *uuid)`
+ - `NimBLEService::getCharacteristics(const NimBLEUUID &uuid)`
+
+- `NimBLEAdvertisementData` New methods:
+ - `NimBLEAdvertisementData::addTxPower`: Adds transmission power to the advertisement.
+ - `NimBLEAdvertisementData::setPreferredParams`: Adds connection parameters to the advertisement.
+ - `NimBLEAdvertisementData::setURI`: Adds URI data to the advertisement.
+
+- `NimBLEAdvertising` New methods:
+ - `NimBLEAdvertising::setName`: Set the name advertised.
+ - `NimBLEAdvertising::setManufacturerData`: Adds manufacturer data to the advertisement.
+ - `NimBLEAdvertising::setURI`: Adds URI data to the advertisement.
+ - `NimBLEAdvertising::setServiceData`: Adds service data to the advertisement.
+ - `NimBLEAdvertising::addTxPower`: Adds transmission power to the advertisement.
+ - `NimBLEAdvertising::reset`: Stops the current advertising and resets the advertising data to the default values.
+
+- `NimBLEDevice::setScanFilterMode`: Set the controller duplicate filter mode for filtering scanned devices.
+
+- `NimBLEDevice::setScanDuplicateCacheSize`: Sets the number of advertisements filtered before the cache is reset.
+
+- `NimBLEScan::setMaxResults`: This allows for setting a maximum number of advertised devices stored in the results vector.
+
+- `NimBLEAdvertisedDevice` New data retrieval methods added:
+ - `haveAdvInterval/getAdvInterval`: checks if the interval is advertised / gets the advertisement interval value.
+
+ - `haveConnParams/getMinInterval/getMaxInterval`: checks if the parameters are advertised / get min value / get max value.
+
+ - `haveURI/getURI`: checks if a URI is advertised / gets the URI data.
+
+ - `haveTargetAddress/getTargetAddressCount/getTargetAddress(index)`: checks if a target address is present / gets a count of the addresses targeted / gets the address of the target at index.
+
+### Changed
+- `nimconfig.h` (Arduino) is now easier to use.
+
+- `NimBLEServer::getServiceByUUID` Now takes an extra parameter of instanceID to support multiple services with the same UUID.
+
+- `NimBLEService::getCharacteristic` Now takes an extra parameter of instanceID to support multiple characteristics with the same UUID.
+
+- `NimBLEAdvertising` Transmission power is no longer advertised by default and can be added to the advertisement by calling `NimBLEAdvertising::addTxPower`
+
+- `NimBLEAdvertising` Custom scan response data can now be used without custom advertisment.
+
+- `NimBLEScan` Now uses the controller duplicate filter.
+
+- `NimBLEAdvertisedDevice` Has been refactored to store the complete advertisement payload and no longer parses the data from each advertisement.
+Instead the data will be parsed on-demand when the user application asks for specific data.
+
+### Fixed
+- `NimBLEHIDDevice` Characteristics now use encryption, this resolves an issue with communicating with devices requiring encryption for HID devices.
+
+
## [1.1.0] - 2021-01-20
### Added
@@ -9,7 +73,7 @@ All notable changes to this project will be documented in this file.
- New examples for securing and authenticating client/server connections, by mblasee.
-- `NimBLEAdvertiseing::SetMinPreferred` and `NimBLEAdvertiseing::SetMinPreferred` re-added.
+- `NimBLEAdvertising::SetMinPreferred` and `NimBLEAdvertising::SetMinPreferred` re-added.
- Conditional checks added for command line config options in `nimconfig.h` to support custom configuration in platformio.
@@ -71,6 +135,7 @@ advertised them as 16/32bit but resolved them to 128bits. Both are now checked.
- (Arduino) Ensure controller mode is set to BLE Only.
+
## [1.0.2] - 2020-09-13
### Changed
@@ -84,6 +149,7 @@ Any changes to the controller max connection settings in `sdkconfig.h` will now
- (Arduino) Revert the previous change to fix the advertising start delay. Instead a replacement fix that routes all BLE controller commands from
a task running on core 0 (same as the controller) has been implemented. This improves response times and reliability for all BLE functions.
+
## [1.0.1] - 2020-09-02
### Added
diff --git a/lib/libesp32/NimBLE-Arduino/docs/Command_line_config.md b/lib/libesp32/NimBLE-Arduino/docs/Command_line_config.md
index 813156f76..c22565f4f 100644
--- a/lib/libesp32/NimBLE-Arduino/docs/Command_line_config.md
+++ b/lib/libesp32/NimBLE-Arduino/docs/Command_line_config.md
@@ -1,93 +1,117 @@
# Arduino command line and platformio config options
-`CONFIG_BT_NIMBLE_ROLE_CENTRAL_DISABLED`
-
- If defined, NimBLE Client functions will not be included.
-- Reduces flash size by approx. 7kB.
-
+`CONFIG_BT_NIMBLE_MAX_CONNECTIONS`
-`CONFIG_BT_NIMBLE_ROLE_OBSERVER_DISABLED`
+Sets the number of simultaneous connections (esp controller max is 9)
+- Default value is 3
+
-If defined, NimBLE Scan functions will not be included.
-- Reduces flash size by approx. 26kB.
-
+`CONFIG_BT_NIMBLE_ATT_PREFERRED_MTU`
-`CONFIG_BT_NIMBLE_ROLE_PERIPHERAL_DISABLED`
+Sets the default MTU size.
+- Default value is 255
+
-If defined NimBLE Server functions will not be included.
-- Reduces flash size by approx. 16kB.
-
+`CONFIG_BT_NIMBLE_SVC_GAP_DEVICE_NAME`
-`CONFIG_BT_NIMBLE_ROLE_BROADCASTER_DISABLED`
-
-If defined, NimBLE Advertising functions will not be included.
-- Reduces flash size by approx. 5kB.
-
+Set the default device name
+- Default value is "nimble"
+
`CONFIG_BT_NIMBLE_DEBUG`
If defined, enables debug log messages from the NimBLE host
- Uses approx. 32kB of flash memory.
-
+
`CONFIG_NIMBLE_CPP_ENABLE_RETURN_CODE_TEXT`
If defined, NimBLE host return codes will be printed as text in debug log messages.
- Uses approx. 7kB of flash memory.
-
+
`CONFIG_NIMBLE_CPP_ENABLE_GAP_EVENT_CODE_TEXT`
If defined, GAP event codes will be printed as text in debug log messages.
- Uses approx. 1kB of flash memory.
-
+
`CONFIG_NIMBLE_CPP_ENABLE_ADVERTISMENT_TYPE_TEXT`
If defined, advertisment types will be printed as text while scanning in debug log messages.
- Uses approx. 250 bytes of flash memory.
-
+
+
+`CONFIG_BT_NIMBLE_SVC_GAP_APPEARANCE`
+
+Set the default appearance.
+- Default value is 0x00
+
+
+`CONFIG_BT_NIMBLE_ROLE_CENTRAL_DISABLED`
+
+If defined, NimBLE Client functions will not be included.
+- Reduces flash size by approx. 7kB.
+
+
+`CONFIG_BT_NIMBLE_ROLE_OBSERVER_DISABLED`
+
+If defined, NimBLE Scan functions will not be included.
+- Reduces flash size by approx. 26kB.
+
+
+`CONFIG_BT_NIMBLE_ROLE_PERIPHERAL_DISABLED`
+
+If defined NimBLE Server functions will not be included.
+- Reduces flash size by approx. 16kB.
+
+
+`CONFIG_BT_NIMBLE_ROLE_BROADCASTER_DISABLED`
+
+If defined, NimBLE Advertising functions will not be included.
+- Reduces flash size by approx. 5kB.
+
+
+`CONFIG_BT_NIMBLE_MAX_BONDS`
+
+Sets the number of devices allowed to store/bond with
+- Default value is 3
+
+
+`CONFIG_BT_NIMBLE_MAX_CCCDS`
+
+Sets the maximum number of CCCD subscriptions to store
+- Default value is 8
+
+
+`CONFIG_BT_NIMBLE_RPA_TIMEOUT`
+
+Sets the random address refresh time in seconds.
+- Default value is 900
+
+
+`CONFIG_BT_NIMBLE_MSYS1_BLOCK_COUNT`
+
+Set the number of msys blocks For prepare write & prepare responses. This may need to be increased if
+you are sending large blocks of data with a low MTU. E.g: 512 bytes with 23 MTU will fail.
+- Default value is 12
+
+
+`CONFIG_BT_NIMBLE_MEM_ALLOC_MODE_EXTERNAL`
+
+Sets the NimBLE stack to use external PSRAM will be loaded
+- Must be defined with a value of 1; Default is CONFIG_BT_NIMBLE_MEM_ALLOC_MODE_INTERNAL 1
+
`CONFIG_BT_NIMBLE_PINNED_TO_CORE`
Sets the core the NimBLE host stack will run on
- Options: 0 or 1
-
+
`CONFIG_BT_NIMBLE_TASK_STACK_SIZE`
-Set the task stack size for the NimBLE core.
+Set the task stack size for the NimBLE core.
- Default is 4096
-
-
-
-`CONFIG_BT_NIMBLE_MEM_ALLOC_MODE_EXTERNAL`
-
-Sets the NimBLE stack to use external PSRAM will be loaded
-- Must be defined with a value of 1; Default is CONFIG_BT_NIMBLE_MEM_ALLOC_MODE_INTERNAL 1
-
-
-`CONFIG_BT_NIMBLE_MAX_CONNECTIONS`
-
-Sets the number of simultaneous connections (esp controller max is 9)
-- Default value is 3
-
-
-`CONFIG_BT_NIMBLE_MAX_BONDS`
-
-Sets the number of devices allowed to store/bond with
-- Default value is 3
-
-
-`CONFIG_BT_NIMBLE_MAX_CCCDS`
-
-Sets the maximum number of CCCD subscriptions to store
-- Default value is 8
-
-
-`CONFIG_BT_NIMBLE_SVC_GAP_DEVICE_NAME`
-
-Set the default device name
-- Default value is "nimble"
-
+
diff --git a/lib/libesp32/NimBLE-Arduino/docs/Migration_guide.md b/lib/libesp32/NimBLE-Arduino/docs/Migration_guide.md
index 69671824a..59854e5fd 100644
--- a/lib/libesp32/NimBLE-Arduino/docs/Migration_guide.md
+++ b/lib/libesp32/NimBLE-Arduino/docs/Migration_guide.md
@@ -69,6 +69,8 @@ For example `BLEServer::createService(SERVICE_UUID)` will work just as it did be
### Characteristics
+The constructor for `(Nim)BLECharacteristic` is now private, so if you currently subclass it to add logic you should switch to use `NimBLEService::createCharacteristic` instead. Any custom processing logic previously in a `BLECharacteristic` subclass should be moved to a `NimBLECharacteristicCallbacks` subclass instead, and passed into `NimBLECharacteristic::setCallbacks`.
+
`BLEService::createCharacteristic` (`NimBLEService::createCharacteristic`) is used the same way as originally except the properties parameter has changed.
When creating a characteristic the properties are now set with `NIMBLE_PROPERTY::XXXX` instead of `BLECharacteristic::XXXX`.
@@ -218,10 +220,11 @@ If you wish to advertise these parameters you can still do so manually via `BLEA
Calling `NimBLEAdvertising::setAdvertisementData` will entirely replace any data set with `NimBLEAdvertising::addServiceUUID`, or
-`NimBLEAdvertising::setAppearance`. You should set all the data you wish to advertise within the `NimBLEAdvertisementData` instead.
+`NimBLEAdvertising::setAppearance` or similar methods. You should set all the data you wish to advertise within the `NimBLEAdvertisementData` instead.
-Calling `NimBLEAdvertising::setScanResponseData` without also calling `NimBLEAdvertising::setAdvertisementData` will have no effect.
-When using custom scan response data you must also use custom advertisement data.
+~~Calling `NimBLEAdvertising::setScanResponseData` without also calling `NimBLEAdvertising::setAdvertisementData` will have no effect.
+When using custom scan response data you must also use custom advertisement data.~~
+No longer true as of release 1.2.0 and above, custom scan response is now supported without custom advertisement data.
> BLEAdvertising::start (NimBLEAdvertising::start)
diff --git a/lib/libesp32/NimBLE-Arduino/docs/Usage_tips.md b/lib/libesp32/NimBLE-Arduino/docs/Usage_tips.md
new file mode 100644
index 000000000..b8edde2de
--- /dev/null
+++ b/lib/libesp32/NimBLE-Arduino/docs/Usage_tips.md
@@ -0,0 +1,41 @@
+# Usage Tips
+
+## Put BLE functions in a task running on the NimBLE stack core
+
+When commands are sent to the stack from a differnt core they can experience delays in execution.
+This library detects this and invokes the esp32 IPC to reroute these commands through the correct core but this also increases overhead.
+Therefore it is highly recommended to create tasks for BLE to run on the same core, the macro `CONFIG_BT_NIMBLE_PINNED_TO_CORE` can be used to set the core.
+
+
+## Do not delete client instances unless necessary or unused
+
+When a client instance has been created and has connected to a peer device and it has retrieved service/characteristic information it will store that data for the life of the client instance.
+If you are periodically connecting to the same devices and you have deleted the client instance or the services when connecting again it will cause a retrieval of that information from the peer again.
+This results in significant energy drain on the battery of the devices, fragments heap, and reduces connection performance.
+
+Client instances in this library use approximately 20% of the original bluedroid library, deleteing them will provide much less gain than it did before.
+
+It is recommended to retain the client instance in cases where the time between connecting to the same device is less than 5 minutes.
+
+
+## Only retrieve the services and characteriscs needed
+
+As a client the use of `NimBLEClient::getServices` or `NimBLERemoteService::getCharacteristics` and using `true` for the parameter should be limited to devices that are not known.
+Instead `NimBLEClient::getService(NimBLEUUID)` or `NimBLERemoteService::getCharacteristic(NimBLEUUID)` should be used to access certain attributes that are useful to the application.
+This reduces energy consumed, heap allocated, connection time and improves overall efficiency.
+
+
+## Check return values
+
+Many user issues can be avoided by checking if a function returned successfully, by either testing for true/false such as when calling `NimBLEClient::connect`,
+or nullptr such as when calling `NimBLEClient::getService`. The latter being a must, as calling a method on a nullptr will surely result in a crash.
+Most of the functions in this library return something that should be checked before proceeding.
+
+
+## There will be bugs - please report them
+
+No code is bug free and unit testing will not find them all on it's own. If you encounter a bug, please report it along with any logs and decoded backtrace if applicable.
+Best efforts will be made to correct any errors ASAP.
+
+Bug reports can be made at https://github.com/h2zero/NimBLE-Arduino/issues or https://github.com/h2zero/esp-nimble-cpp/issues.
+Questions and suggestions will be happily accepted there as well.
diff --git a/lib/libesp32/NimBLE-Arduino/examples/NimBLE_Scan_Continuous/NimBLE_Scan_Continuous.ino b/lib/libesp32/NimBLE-Arduino/examples/NimBLE_Scan_Continuous/NimBLE_Scan_Continuous.ino
new file mode 100644
index 000000000..b8d24d264
--- /dev/null
+++ b/lib/libesp32/NimBLE-Arduino/examples/NimBLE_Scan_Continuous/NimBLE_Scan_Continuous.ino
@@ -0,0 +1,71 @@
+/** Example of continuous scanning for BLE advertisements.
+ * This example will scan forever while consuming as few resources as possible
+ * and report all advertisments on the serial monitor.
+ *
+ * Created: on January 31 2021
+ * Author: H2zero
+ *
+ */
+
+#include "NimBLEDevice.h"
+
+NimBLEScan* pBLEScan;
+
+class MyAdvertisedDeviceCallbacks: public NimBLEAdvertisedDeviceCallbacks {
+ void onResult(NimBLEAdvertisedDevice* advertisedDevice) {
+ Serial.printf("Advertised Device: %s \n", advertisedDevice->toString().c_str());
+ }
+};
+
+void setup() {
+ Serial.begin(115200);
+ Serial.println("Scanning...");
+
+/** *Optional* Sets the filtering mode used by the scanner in the BLE controller.
+ *
+ * Can be one of:
+ * CONFIG_BTDM_SCAN_DUPL_TYPE_DEVICE (0) (default)
+ * Filter by device address only, advertisements from the same address will be reported only once.
+ *
+ * CONFIG_BTDM_SCAN_DUPL_TYPE_DATA (1)
+ * Filter by data only, advertisements with the same data will only be reported once,
+ * even from different addresses.
+ *
+ * CONFIG_BTDM_SCAN_DUPL_TYPE_DATA_DEVICE (2)
+ * Filter by address and data, advertisements from the same address will be reported only once,
+ * except if the data in the advertisement has changed, then it will be reported again.
+ *
+ * Can only be used BEFORE calling NimBLEDevice::init.
+*/
+ NimBLEDevice::setScanFilterMode(CONFIG_BTDM_SCAN_DUPL_TYPE_DEVICE);
+
+/** *Optional* Sets the scan filter cache size in the BLE controller.
+ * When the number of duplicate advertisements seen by the controller
+ * reaches this value it will clear the cache and start reporting previously
+ * seen devices. The larger this number, the longer time between repeated
+ * device reports. Range 10 - 1000. (default 20)
+ *
+ * Can only be used BEFORE calling NimBLEDevice::init.
+ */
+ NimBLEDevice::setScanDuplicateCacheSize(200);
+
+ NimBLEDevice::init("");
+
+ pBLEScan = NimBLEDevice::getScan(); //create new scan
+ // Set the callback for when devices are discovered, no duplicates.
+ pBLEScan->setAdvertisedDeviceCallbacks(new MyAdvertisedDeviceCallbacks(), false);
+ pBLEScan->setActiveScan(true); // Set active scanning, this will get more data from the advertiser.
+ pBLEScan->setInterval(97); // How often the scan occurs / switches channels; in milliseconds,
+ pBLEScan->setWindow(37); // How long to scan during the interval; in milliseconds.
+ pBLEScan->setMaxResults(0); // do not store the scan results, use callback only.
+}
+
+void loop() {
+ // If an error occurs that stops the scan, it will be restarted here.
+ if(pBLEScan->isScanning() == false) {
+ // Start scan with: duration = 0 seconds(forever), no scan end callback, not a continuation of a previous scan.
+ pBLEScan->start(0, nullptr, false);
+ }
+
+ delay(2000);
+}
\ No newline at end of file
diff --git a/lib/libesp32/NimBLE-Arduino/examples/NimBLE_Service_Data_Advertiser/NimBLE_Service_Data_Advertiser.ino b/lib/libesp32/NimBLE-Arduino/examples/NimBLE_Service_Data_Advertiser/NimBLE_Service_Data_Advertiser.ino
new file mode 100644
index 000000000..dd588fd22
--- /dev/null
+++ b/lib/libesp32/NimBLE-Arduino/examples/NimBLE_Service_Data_Advertiser/NimBLE_Service_Data_Advertiser.ino
@@ -0,0 +1,33 @@
+/** NimBLE_Service_Data_Advertiser Demo:
+ *
+ * Simple demo of advertising service data that changes every 5 seconds
+ *
+ * Created: on February 7 2021
+ * Author: H2zero
+ *
+*/
+
+#include
+
+#define SERVICE_UUID "4fafc201-1fb5-459e-8fcc-c5c9c331914b"
+
+static NimBLEUUID dataUuid(SERVICE_UUID);
+static NimBLEAdvertising *pAdvertising = NimBLEDevice::getAdvertising();
+static uint32_t count = 0;
+
+void setup() {
+ Serial.begin(115200);
+ Serial.println("Starting BLE work!");
+
+ NimBLEDevice::init("svc data");
+}
+
+void loop() {
+ pAdvertising->stop();
+ pAdvertising->setServiceData(dataUuid, std::string((char*)&count, sizeof(count)));
+ pAdvertising->start();
+
+ Serial.printf("Advertising count = %d\n", count);
+ count++;
+ delay(5000);
+}
diff --git a/lib/libesp32/NimBLE-Arduino/library.json b/lib/libesp32/NimBLE-Arduino/library.json
index a64024df6..a56985a06 100644
--- a/lib/libesp32/NimBLE-Arduino/library.json
+++ b/lib/libesp32/NimBLE-Arduino/library.json
@@ -2,7 +2,7 @@
"name": "NimBLE-Arduino",
"keywords": "esp32, bluetooth",
"description": "Bluetooth low energy (BLE) library for arduino-esp32 based on NimBLE",
- "version": "1.1.0",
+ "version": "1.2.0",
"frameworks": "arduino",
"platforms": "espressif32"
}
diff --git a/lib/libesp32/NimBLE-Arduino/library.properties b/lib/libesp32/NimBLE-Arduino/library.properties
index 7f2508333..4435958d1 100644
--- a/lib/libesp32/NimBLE-Arduino/library.properties
+++ b/lib/libesp32/NimBLE-Arduino/library.properties
@@ -1,5 +1,5 @@
name=NimBLE-Arduino
-version=1.1.0
+version=1.2.0
author=h2zero
maintainer=h2zero
sentence=Bluetooth low energy (BLE) library for arduino-esp32 based on NimBLE.
diff --git a/lib/libesp32/NimBLE-Arduino/src/NimBLEAdvertisedDevice.cpp b/lib/libesp32/NimBLE-Arduino/src/NimBLEAdvertisedDevice.cpp
index 992b1f377..c697fe752 100644
--- a/lib/libesp32/NimBLE-Arduino/src/NimBLEAdvertisedDevice.cpp
+++ b/lib/libesp32/NimBLE-Arduino/src/NimBLEAdvertisedDevice.cpp
@@ -28,25 +28,14 @@ static const char* LOG_TAG = "NimBLEAdvertisedDevice";
/**
* @brief Constructor
*/
-NimBLEAdvertisedDevice::NimBLEAdvertisedDevice() {
+NimBLEAdvertisedDevice::NimBLEAdvertisedDevice() :
+ m_payload(62,0)
+{
m_advType = 0;
- m_appearance = 0;
- m_manufacturerData = "";
- m_name = "";
m_rssi = -9999;
- m_txPower = 0;
- m_payloadLength = 0;
- m_payload = nullptr;
-
- m_haveAppearance = false;
- m_haveManufacturerData = false;
- m_haveName = false;
- m_haveRSSI = false;
- m_haveServiceData = false;
- m_haveServiceUUID = false;
- m_haveTXPower = false;
- m_callbackSent = false;
-
+ m_callbackSent = false;
+ m_timestamp = 0;
+ m_advLength = 0;
} // NimBLEAdvertisedDevice
@@ -82,25 +71,126 @@ uint8_t NimBLEAdvertisedDevice::getAdvType() {
* @return The appearance of the advertised device.
*/
uint16_t NimBLEAdvertisedDevice::getAppearance() {
- return m_appearance;
+ uint8_t data_loc = 0;
+
+ if(findAdvField(BLE_HS_ADV_TYPE_APPEARANCE, 0, &data_loc) > 0) {
+ ble_hs_adv_field *field = (ble_hs_adv_field *)&m_payload[data_loc];
+ if(field->length == BLE_HS_ADV_APPEARANCE_LEN + 1) {
+ return *field->value | *(field->value + 1) << 8;
+ }
+ }
+
+ return 0;
} // getAppearance
+/**
+ * @brief Get the advertisement interval.
+ * @return The advertisement interval in 0.625ms units.
+ */
+uint16_t NimBLEAdvertisedDevice::getAdvInterval() {
+ uint8_t data_loc = 0;
+
+ if(findAdvField(BLE_HS_ADV_TYPE_ADV_ITVL, 0, &data_loc) > 0) {
+ ble_hs_adv_field *field = (ble_hs_adv_field *)&m_payload[data_loc];
+ if(field->length == BLE_HS_ADV_ADV_ITVL_LEN + 1) {
+ return *field->value | *(field->value + 1) << 8;
+ }
+ }
+
+ return 0;
+} // getAdvInterval
+
+
+/**
+ * @brief Get the preferred min connection interval.
+ * @return The preferred min connection interval in 1.25ms units.
+ */
+uint16_t NimBLEAdvertisedDevice::getMinInterval() {
+ uint8_t data_loc = 0;
+
+ if(findAdvField(BLE_HS_ADV_TYPE_SLAVE_ITVL_RANGE, 0, &data_loc) > 0) {
+ ble_hs_adv_field *field = (ble_hs_adv_field *)&m_payload[data_loc];
+ if(field->length == BLE_HS_ADV_SLAVE_ITVL_RANGE_LEN + 1) {
+ return *field->value | *(field->value + 1) << 8;
+ }
+ }
+
+ return 0;
+} // getMinInterval
+
+
+/**
+ * @brief Get the preferred max connection interval.
+ * @return The preferred max connection interval in 1.25ms units.
+ */
+uint16_t NimBLEAdvertisedDevice::getMaxInterval() {
+ uint8_t data_loc = 0;
+
+ if(findAdvField(BLE_HS_ADV_TYPE_SLAVE_ITVL_RANGE, 0, &data_loc) > 0) {
+ ble_hs_adv_field *field = (ble_hs_adv_field *)&m_payload[data_loc];
+ if(field->length == BLE_HS_ADV_SLAVE_ITVL_RANGE_LEN + 1) {
+ return *(field->value + 2) | *(field->value + 3) << 8;
+ }
+ }
+
+ return 0;
+} // getMaxInterval
+
+
/**
* @brief Get the manufacturer data.
* @return The manufacturer data of the advertised device.
*/
std::string NimBLEAdvertisedDevice::getManufacturerData() {
- return m_manufacturerData;
+ uint8_t data_loc = 0;
+
+ if(findAdvField(BLE_HS_ADV_TYPE_MFG_DATA, 0, &data_loc) > 0) {
+ ble_hs_adv_field *field = (ble_hs_adv_field *)&m_payload[data_loc];
+ if(field->length > 1) {
+ return std::string((char*)field->value, field->length - 1);
+ }
+ }
+
+ return "";
} // getManufacturerData
+/**
+ * @brief Get the URI from the advertisement.
+ * @return The URI data.
+ */
+std::string NimBLEAdvertisedDevice::getURI() {
+ uint8_t data_loc = 0;
+
+ if(findAdvField(BLE_HS_ADV_TYPE_URI, 0, &data_loc) > 0) {
+ ble_hs_adv_field *field = (ble_hs_adv_field *)&m_payload[data_loc];
+ if(field->length > 1) {
+ return std::string((char*)field->value, field->length - 1);
+ }
+ }
+
+ return "";
+} // getURI
+
+
/**
* @brief Get the advertised name.
* @return The name of the advertised device.
*/
std::string NimBLEAdvertisedDevice::getName() {
- return m_name;
+ uint8_t data_loc = 0;
+
+ if(findAdvField(BLE_HS_ADV_TYPE_COMP_NAME, 0, &data_loc) > 0 ||
+ findAdvField(BLE_HS_ADV_TYPE_INCOMP_NAME, 0, &data_loc) > 0)
+ {
+ ble_hs_adv_field *field = (ble_hs_adv_field *)&m_payload[data_loc];
+ if(field->length > 1) {
+ return std::string((char*)field->value, field->length - 1);
+ }
+ }
+
+ return "";
} // getName
@@ -122,17 +212,70 @@ NimBLEScan* NimBLEAdvertisedDevice::getScan() {
} // getScan
+/**
+ * @brief Get the number of target addresses.
+ * @return The number of addresses.
+ */
+size_t NimBLEAdvertisedDevice::getTargetAddressCount() {
+ uint8_t count = 0;
+
+ count = findAdvField(BLE_HS_ADV_TYPE_PUBLIC_TGT_ADDR);
+ count += findAdvField(BLE_HS_ADV_TYPE_RANDOM_TGT_ADDR);
+
+ return count;
+}
+
+
+/**
+ * @brief Get the target address at the index.
+ * @param [in] index The index of the target address.
+ * @return The target address.
+ */
+NimBLEAddress NimBLEAdvertisedDevice::getTargetAddress(uint8_t index) {
+ ble_hs_adv_field *field = nullptr;
+ uint8_t count = 0;
+ uint8_t data_loc = 0xFF;
+
+ index++;
+ count = findAdvField(BLE_HS_ADV_TYPE_PUBLIC_TGT_ADDR, index, &data_loc);
+
+ if (count < index) {
+ index -= count;
+ count = findAdvField(BLE_HS_ADV_TYPE_RANDOM_TGT_ADDR, index, &data_loc);
+ }
+
+ if(count > 0 && data_loc != 0xFF) {
+ field = (ble_hs_adv_field *)&m_payload[data_loc];
+ if(field->length < index * BLE_HS_ADV_PUBLIC_TGT_ADDR_ENTRY_LEN) {
+ index -= count - field->length / BLE_HS_ADV_PUBLIC_TGT_ADDR_ENTRY_LEN;
+ }
+ if(field->length > index * BLE_HS_ADV_PUBLIC_TGT_ADDR_ENTRY_LEN) {
+ return NimBLEAddress(field->value + (index - 1) * BLE_HS_ADV_PUBLIC_TGT_ADDR_ENTRY_LEN);
+ }
+ }
+
+ return NimBLEAddress("");
+}
+
+
/**
* @brief Get the service data.
- * @param [in] index The vector index of the service data requested.
+ * @param [in] index The index of the service data requested.
* @return The advertised service data or empty string if no data.
*/
std::string NimBLEAdvertisedDevice::getServiceData(uint8_t index) {
- if(index > m_serviceDataVec.size()) {
- NIMBLE_LOGW(LOG_TAG, "getServiceData: index out of range");
- return "";
+ ble_hs_adv_field *field = nullptr;
+ uint8_t bytes;
+ uint8_t data_loc = findServiceData(index, &bytes);
+
+ if(data_loc != 0xFF) {
+ field = (ble_hs_adv_field *)&m_payload[data_loc];
+ if(field->length > bytes) {
+ return std::string((char*)(field->value + bytes), field->length - bytes - 1);
+ }
}
- return m_serviceDataVec[index].second;
+
+ return "";
} //getServiceData
@@ -141,51 +284,148 @@ std::string NimBLEAdvertisedDevice::getServiceData(uint8_t index) {
* @param [in] uuid The uuid of the service data requested.
* @return The advertised service data or empty string if no data.
*/
-std::string NimBLEAdvertisedDevice::getServiceData(const NimBLEUUID &uuid) const {
- for(auto &it : m_serviceDataVec) {
- if(it.first == uuid) {
- return it.second;
+std::string NimBLEAdvertisedDevice::getServiceData(const NimBLEUUID &uuid) {
+ ble_hs_adv_field *field = nullptr;
+ uint8_t bytes;
+ uint8_t index = 0;
+ uint8_t data_loc = findServiceData(index, &bytes);
+ uint8_t uuidBytes = uuid.bitSize() / 8;
+ uint8_t plSize = m_payload.size() - 2;
+
+ while(data_loc < plSize) {
+ field = (ble_hs_adv_field *)&m_payload[data_loc];
+ if(bytes == uuidBytes && NimBLEUUID(field->value, bytes, false) == uuid) {
+ return std::string((char*)(field->value + bytes), field->length - bytes - 1);
}
+
+ index++;
+ data_loc = findServiceData(index, &bytes);
}
- NIMBLE_LOGW(LOG_TAG, "getServiceData: uuid not found");
+
+ NIMBLE_LOGI(LOG_TAG, "No service data found");
return "";
} //getServiceData
/**
- * @brief Get the advertised service UUID.
- * @param [in] index The vector index of the service data UUID requested.
- * @return The advertised service UUID or an empty UUID if not found.
+ * @brief Get the UUID of the serice data at the index.
+ * @param [in] index The index of the service data UUID requested.
+ * @return The advertised service data UUID or an empty UUID if not found.
*/
NimBLEUUID NimBLEAdvertisedDevice::getServiceDataUUID(uint8_t index) {
- if(!haveServiceData() || index > m_serviceDataVec.size()) {
- NIMBLE_LOGW(LOG_TAG, "getServiceDataUUID: index out of range");
- return NimBLEUUID("");
+ ble_hs_adv_field *field = nullptr;
+ uint8_t bytes;
+ uint8_t data_loc = findServiceData(index, &bytes);
+
+ if(data_loc != 0xFF) {
+ field = (ble_hs_adv_field *)&m_payload[data_loc];
+ if(field->length >= bytes) {
+ return NimBLEUUID(field->value, bytes, false);
+ }
}
- return m_serviceDataVec[index].first;
+
+ return NimBLEUUID("");
} // getServiceDataUUID
+/**
+ * @brief Find the service data at the index.
+ * @param [in] index The index of the service data to find.
+ * @param [in] bytes A pointer to storage for the number of the bytes in the UUID.
+ * @return The index in the vector where the data is located, 0xFF if not found.
+ */
+uint8_t NimBLEAdvertisedDevice::findServiceData(uint8_t index, uint8_t *bytes) {
+ uint8_t data_loc = 0;
+ uint8_t found = 0;
+
+ *bytes = 0;
+ index++;
+ found = findAdvField(BLE_HS_ADV_TYPE_SVC_DATA_UUID16, index, &data_loc);
+ if(found == index) {
+ *bytes = 2;
+ return data_loc;
+ }
+
+ index -= found;
+ found = findAdvField(BLE_HS_ADV_TYPE_SVC_DATA_UUID32, index, &data_loc);
+ if(found == index) {
+ *bytes = 4;
+ return data_loc;
+ }
+
+ index -= found;
+ found = findAdvField(BLE_HS_ADV_TYPE_SVC_DATA_UUID128, index, &data_loc);
+ if(found == index) {
+ *bytes = 16;
+ return data_loc;
+ }
+
+ return 0xFF;
+}
+
+
/**
* @brief Get the count of advertised service data UUIDS
* @return The number of service data UUIDS in the vector.
*/
size_t NimBLEAdvertisedDevice::getServiceDataCount() {
- return m_serviceDataVec.size();
+ uint8_t count = 0;
+
+ count += findAdvField(BLE_HS_ADV_TYPE_SVC_DATA_UUID16);
+ count += findAdvField(BLE_HS_ADV_TYPE_SVC_DATA_UUID32);
+ count += findAdvField(BLE_HS_ADV_TYPE_SVC_DATA_UUID128);
+
+ return count;
} // getServiceDataCount
/**
* @brief Get the Service UUID.
- * @param [in] index The vector index of the service UUID requested.
+ * @param [in] index The index of the service UUID requested.
* @return The Service UUID of the advertised service, or an empty UUID if not found.
*/
NimBLEUUID NimBLEAdvertisedDevice::getServiceUUID(uint8_t index) {
- if(!haveServiceUUID() || index > m_serviceUUIDs.size()) {
- NIMBLE_LOGW(LOG_TAG, "getServiceUUID: index out of range");
- return NimBLEUUID("");
+ uint8_t count = 0;
+ uint8_t data_loc = 0;
+ uint8_t uuidBytes = 0;
+ uint8_t type = BLE_HS_ADV_TYPE_INCOMP_UUIDS16;
+ ble_hs_adv_field *field = nullptr;
+
+ index++;
+
+ do {
+ count = findAdvField(type, index, &data_loc);
+ if(count >= index) {
+ if(type < BLE_HS_ADV_TYPE_INCOMP_UUIDS32) {
+ uuidBytes = 2;
+ } else if(type < BLE_HS_ADV_TYPE_INCOMP_UUIDS128) {
+ uuidBytes = 4;
+ } else {
+ uuidBytes = 16;
+ }
+ break;
+
+ } else {
+ type++;
+ index -= count;
+ }
+
+ } while(type <= BLE_HS_ADV_TYPE_COMP_UUIDS128);
+
+ if(uuidBytes > 0) {
+ field = (ble_hs_adv_field *)&m_payload[data_loc];
+ // In the case of more than one field of service uuid's we need to adjust
+ // the index to account for the uuids of the previous fields.
+ if(field->length < index * uuidBytes) {
+ index -= count - field->length / uuidBytes;
+ }
+
+ if(field->length > uuidBytes * index) {
+ return NimBLEUUID(field->value + uuidBytes * (index - 1), uuidBytes, false);
+ }
}
- return m_serviceUUIDs[index];
+
+ return NimBLEUUID("");
} // getServiceUUID
@@ -194,18 +434,32 @@ NimBLEUUID NimBLEAdvertisedDevice::getServiceUUID(uint8_t index) {
* @return The count of services in the advertising packet.
*/
size_t NimBLEAdvertisedDevice::getServiceUUIDCount() {
- return m_serviceUUIDs.size();
+ uint8_t count = 0;
+
+ count += findAdvField(BLE_HS_ADV_TYPE_INCOMP_UUIDS16);
+ count += findAdvField(BLE_HS_ADV_TYPE_COMP_UUIDS16);
+ count += findAdvField(BLE_HS_ADV_TYPE_INCOMP_UUIDS32);
+ count += findAdvField(BLE_HS_ADV_TYPE_COMP_UUIDS32);
+ count += findAdvField(BLE_HS_ADV_TYPE_INCOMP_UUIDS128);
+ count += findAdvField(BLE_HS_ADV_TYPE_COMP_UUIDS128);
+
+ return count;
} // getServiceUUIDCount
/**
* @brief Check advertised services for existance of the required UUID
+ * @param [in] uuid The service uuid to look for in the advertisement.
* @return Return true if service is advertised
*/
-bool NimBLEAdvertisedDevice::isAdvertisingService(const NimBLEUUID &uuid) const {
- for (int i = 0; i < m_serviceUUIDs.size(); i++) {
- if (m_serviceUUIDs[i].equals(uuid)) return true;
+bool NimBLEAdvertisedDevice::isAdvertisingService(const NimBLEUUID &uuid) {
+ size_t count = getServiceUUIDCount();
+ for(size_t i = 0; i < count; i++) {
+ if(uuid == getServiceUUID(i)) {
+ return true;
+ }
}
+
return false;
} // isAdvertisingService
@@ -215,16 +469,43 @@ bool NimBLEAdvertisedDevice::isAdvertisingService(const NimBLEUUID &uuid) const
* @return The TX Power of the advertised device.
*/
int8_t NimBLEAdvertisedDevice::getTXPower() {
- return m_txPower;
+ uint8_t data_loc = 0;
+
+ if(findAdvField(BLE_HS_ADV_TYPE_TX_PWR_LVL, 0, &data_loc) > 0) {
+ ble_hs_adv_field *field = (ble_hs_adv_field *)&m_payload[data_loc];
+ if(field->length == BLE_HS_ADV_TX_PWR_LVL_LEN + 1) {
+ return *(int8_t*)field->value;
+ }
+ }
+
+ return -99;
} // getTXPower
+/**
+ * @brief Does this advertisement have preferred connection parameters?
+ * @return True if connection parameters are present.
+ */
+bool NimBLEAdvertisedDevice::haveConnParams() {
+ return findAdvField(BLE_HS_ADV_TYPE_SLAVE_ITVL_RANGE) > 0;
+} // haveConnParams
+
+
+/**
+ * @brief Does this advertisement have have the advertising interval?
+ * @return True if the advertisement interval is present.
+ */
+bool NimBLEAdvertisedDevice::haveAdvInterval() {
+ return findAdvField(BLE_HS_ADV_TYPE_ADV_ITVL) > 0;
+} // haveAdvInterval
+
+
/**
* @brief Does this advertisement have an appearance value?
* @return True if there is an appearance value present.
*/
bool NimBLEAdvertisedDevice::haveAppearance() {
- return m_haveAppearance;
+ return findAdvField(BLE_HS_ADV_TYPE_APPEARANCE) > 0;
} // haveAppearance
@@ -233,16 +514,36 @@ bool NimBLEAdvertisedDevice::haveAppearance() {
* @return True if there is manufacturer data present.
*/
bool NimBLEAdvertisedDevice::haveManufacturerData() {
- return m_haveManufacturerData;
+ return findAdvField(BLE_HS_ADV_TYPE_MFG_DATA) > 0;
} // haveManufacturerData
+/**
+ * @brief Does this advertisement have a URI?
+ * @return True if there is a URI present.
+ */
+bool NimBLEAdvertisedDevice::haveURI() {
+ return findAdvField(BLE_HS_ADV_TYPE_URI) > 0;
+} // haveURI
+
+
+/**
+ * @brief Does the advertisement contain a target address?
+ * @return True if an address is present.
+ */
+bool NimBLEAdvertisedDevice::haveTargetAddress() {
+ return findAdvField(BLE_HS_ADV_TYPE_PUBLIC_TGT_ADDR) > 0 ||
+ findAdvField(BLE_HS_ADV_TYPE_RANDOM_TGT_ADDR) > 0;
+}
+
+
/**
* @brief Does this advertisement have a name value?
* @return True if there is a name value present.
*/
bool NimBLEAdvertisedDevice::haveName() {
- return m_haveName;
+ return findAdvField(BLE_HS_ADV_TYPE_COMP_NAME) > 0 ||
+ findAdvField(BLE_HS_ADV_TYPE_INCOMP_NAME) > 0;
} // haveName
@@ -251,7 +552,7 @@ bool NimBLEAdvertisedDevice::haveName() {
* @return True if there is a signal strength value present.
*/
bool NimBLEAdvertisedDevice::haveRSSI() {
- return m_haveRSSI;
+ return m_rssi != -9999;
} // haveRSSI
@@ -260,7 +561,7 @@ bool NimBLEAdvertisedDevice::haveRSSI() {
* @return True if there is a service data value present.
*/
bool NimBLEAdvertisedDevice::haveServiceData() {
- return m_haveServiceData;
+ return getServiceDataCount() > 0;
} // haveServiceData
@@ -269,7 +570,7 @@ bool NimBLEAdvertisedDevice::haveServiceData() {
* @return True if there is a service UUID value present.
*/
bool NimBLEAdvertisedDevice::haveServiceUUID() {
- return m_haveServiceUUID;
+ return getServiceUUIDCount() > 0;
} // haveServiceUUID
@@ -278,143 +579,71 @@ bool NimBLEAdvertisedDevice::haveServiceUUID() {
* @return True if there is a transmission power value present.
*/
bool NimBLEAdvertisedDevice::haveTXPower() {
- return m_haveTXPower;
+ return findAdvField(BLE_HS_ADV_TYPE_TX_PWR_LVL) > 0;
} // haveTXPower
-/**
- * @brief Parse the advertising pay load.
- *
- * The pay load is a buffer of bytes that is either 31 bytes long or terminated by
- * a 0 length value. Each entry in the buffer has the format:
- * [length][type][data...]
- *
- * The length does not include itself but does include everything after it until the next record. A record
- * with a length value of 0 indicates a terminator.
- *
- * https://www.bluetooth.com/specifications/assigned-numbers/generic-access-profile
- */
- void NimBLEAdvertisedDevice::parseAdvertisement(uint8_t* payload, uint8_t length) {
- struct ble_hs_adv_fields fields;
- int rc = ble_hs_adv_parse_fields(&fields, payload, length);
- if (rc != 0) {
- NIMBLE_LOGE(LOG_TAG, "Gap Event Parse ERROR.");
- return;
+uint8_t NimBLEAdvertisedDevice::findAdvField(uint8_t type, uint8_t index, uint8_t *data_loc) {
+ ble_hs_adv_field *field = nullptr;
+ uint8_t data = 0;
+ uint8_t length = m_payload.size();
+ uint8_t count = 0;
+
+ if(length < 2) {
+ return count;
}
- m_payload = payload;
- m_payloadLength = length;
+ while (length > 1) {
+ field = (ble_hs_adv_field*)&m_payload[data];
-#if CONFIG_LOG_DEFAULT_LEVEL > 3 || (ARDUINO_ARCH_ESP32 && CORE_DEBUG_LEVEL >= 4)
- char* pHex = NimBLEUtils::buildHexData(nullptr, m_payload, m_payloadLength);
- NIMBLE_LOGD(LOG_TAG,"payload: %s", pHex);
- free(pHex);
-#endif
-
- if (fields.uuids16 != NULL) {
- for (int i = 0; i < fields.num_uuids16; i++) {
- setServiceUUID(NimBLEUUID(fields.uuids16[i].value));
+ if (field->length >= length) {
+ return count;
}
- }
- if (fields.uuids32 != NULL) {
- for (int i = 0; i < fields.num_uuids32; i++) {
- setServiceUUID(NimBLEUUID(fields.uuids32[i].value));
- }
- }
+ if (field->type == type) {
+ switch(type) {
+ case BLE_HS_ADV_TYPE_INCOMP_UUIDS16:
+ case BLE_HS_ADV_TYPE_COMP_UUIDS16:
+ count += field->length / 2;
+ break;
- if (fields.uuids128 != NULL) {
- for (int i = 0; i < fields.num_uuids128; i++) {
- setServiceUUID(NimBLEUUID(&fields.uuids128[i]));
- }
- }
+ case BLE_HS_ADV_TYPE_INCOMP_UUIDS32:
+ case BLE_HS_ADV_TYPE_COMP_UUIDS32:
+ count += field->length / 4;
+ break;
- if (fields.name != NULL) {
- setName(std::string(reinterpret_cast(fields.name), fields.name_len));
- }
+ case BLE_HS_ADV_TYPE_INCOMP_UUIDS128:
+ case BLE_HS_ADV_TYPE_COMP_UUIDS128:
+ count += field->length / 16;
+ break;
- if (fields.tx_pwr_lvl_is_present) {
- setTXPower(fields.tx_pwr_lvl);
- }
+ case BLE_HS_ADV_TYPE_PUBLIC_TGT_ADDR:
+ case BLE_HS_ADV_TYPE_RANDOM_TGT_ADDR:
+ count += field->length / 6;
+ break;
- if (fields.svc_data_uuid16 != NULL ||
- fields.svc_data_uuid32 != NULL ||
- fields.svc_data_uuid128 != NULL)
- {
- ble_hs_adv_field *field;
- uint8_t *data = payload;
- while(length > 1) {
- field = (ble_hs_adv_field*)data;
-
- if(field->length > length) {
- break;
+ default:
+ count++;
+ break;
}
- if(field->type == BLE_HS_ADV_TYPE_SVC_DATA_UUID16) {
- if(field->length > 2) {
- uint16_t uuid;
- memcpy(&uuid, field->value, 2);
- setServiceData(NimBLEUUID(uuid), std::string(reinterpret_cast(field->value + 2), field->length - 3));
+ if(data_loc != nullptr) {
+ if(index == 0 || count >= index) {
+ break;
}
}
-
- if(field->type == BLE_HS_ADV_TYPE_SVC_DATA_UUID32) {
- if(field->length > 4) {
- uint32_t uuid;
- memcpy(&uuid, field->value, 4);
- setServiceData(NimBLEUUID(uuid), std::string(reinterpret_cast(field->value + 4), field->length - 5));
- }
- }
-
- if(field->type == BLE_HS_ADV_TYPE_SVC_DATA_UUID128) {
- if(field->length > 16) {
- NimBLEUUID uuid(field->value, (size_t)16, false);
- setServiceData(uuid, std::string(reinterpret_cast(field->value + 16), field->length - 17));
- }
- }
-
- length -= 1 + field->length;
- data += 1 + field->length;
}
+
+ length -= 1 + field->length;
+ data += 1 + field->length;
}
- if (fields.appearance_is_present) {
- setAppearance(fields.appearance);
+ if(data_loc != nullptr && field != nullptr) {
+ *data_loc = data;
}
- if (fields.mfg_data != NULL) {
- setManufacturerData(std::string(reinterpret_cast(fields.mfg_data), fields.mfg_data_len));
- }
-
-/* TODO: create storage and fucntions for these parameters
- if (fields.public_tgt_addr != NULL) {
- NIMBLE_LOGD(LOG_TAG, " public_tgt_addr=");
- u8p = fields.public_tgt_addr;
- for (i = 0; i < fields.num_public_tgt_addrs; i++) {
- NIMBLE_LOGD(LOG_TAG, "public_tgt_addr=%s ", addr_str(u8p));
- u8p += BLE_HS_ADV_PUBLIC_TGT_ADDR_ENTRY_LEN;
- }
- NIMBLE_LOGD(LOG_TAG, "\n");
- }
-
- if (fields.slave_itvl_range != NULL) {
- NIMBLE_LOGD(LOG_TAG, " slave_itvl_range=");
- print_bytes(fields.slave_itvl_range, BLE_HS_ADV_SLAVE_ITVL_RANGE_LEN);
- NIMBLE_LOGD(LOG_TAG, "\n");
- }
-
- if (fields.adv_itvl_is_present) {
- NIMBLE_LOGD(LOG_TAG, " adv_itvl=0x%04x\n", fields.adv_itvl);
- }
-
- if (fields.uri != NULL) {
- NIMBLE_LOGD(LOG_TAG, " uri=");
- print_bytes(fields.uri, fields.uri_len);
- NIMBLE_LOGD(LOG_TAG, "\n");
- }
-*/
-
- } //parseAdvertisement
+ return count;
+}
/**
@@ -428,106 +657,22 @@ void NimBLEAdvertisedDevice::setAddress(NimBLEAddress address) {
/**
* @brief Set the adFlag for this device.
- * @param [in] The discovered adFlag.
+ * @param [in] advType The advertisement flag data from the advertisement.
*/
void NimBLEAdvertisedDevice::setAdvType(uint8_t advType) {
m_advType = advType;
} // setAdvType
-/**
- * @brief Set the appearance for this device.
- * @param [in] The discovered appearance.
- */
-void NimBLEAdvertisedDevice::setAppearance(uint16_t appearance) {
- m_appearance = appearance;
- m_haveAppearance = true;
-} // setAppearance
-
-
-/**
- * @brief Set the manufacturer data for this device.
- * @param [in] The discovered manufacturer data.
- */
-void NimBLEAdvertisedDevice::setManufacturerData(std::string manufacturerData) {
- m_manufacturerData = manufacturerData;
- m_haveManufacturerData = true;
-} // setManufacturerData
-
-
-/**
- * @brief Set the name for this device.
- * @param [in] name The discovered name.
- */
-void NimBLEAdvertisedDevice::setName(std::string name) {
- m_name = name;
- m_haveName = true;
-} // setName
-
-
/**
* @brief Set the RSSI for this device.
- * @param [in] rssi The discovered RSSI.
+ * @param [in] rssi The RSSI of the discovered device.
*/
void NimBLEAdvertisedDevice::setRSSI(int rssi) {
- m_rssi = rssi;
- m_haveRSSI = true;
+ m_rssi = rssi;
} // setRSSI
-/**
- * @brief Set the Service UUID for this device.
- * @param [in] serviceUUID The discovered serviceUUID
- */
-
-void NimBLEAdvertisedDevice::setServiceUUID(const char* serviceUUID) {
- return setServiceUUID(NimBLEUUID(serviceUUID));
-} // setServiceUUID
-
-
-/**
- * @brief Set the Service UUID for this device.
- * @param [in] serviceUUID The discovered serviceUUID
- */
-void NimBLEAdvertisedDevice::setServiceUUID(NimBLEUUID serviceUUID) {
- // Don't add duplicates
- for (int i = 0; i < m_serviceUUIDs.size(); i++) {
- if (m_serviceUUIDs[i] == serviceUUID) {
- return;
- }
- }
- m_serviceUUIDs.push_back(serviceUUID);
- m_haveServiceUUID = true;
-} // setServiceUUID
-
-
-/**
- * @brief Set the ServiceData value.
- * @param [in] uuid The UUID that the service data belongs to.
- * @param [in] data The service data.
- */
-void NimBLEAdvertisedDevice::setServiceData(NimBLEUUID uuid, std::string data) {
- m_haveServiceData = true;
- for(auto &it : m_serviceDataVec) {
- if(it.first == uuid) {
- it.second = data;
- return;
- }
- }
- m_serviceDataVec.push_back({uuid, data});
-} //setServiceData
-
-
-/**
- * @brief Set the power level for this device.
- * @param [in] txPower The discovered power level.
- */
-void NimBLEAdvertisedDevice::setTXPower(int8_t txPower) {
- m_txPower = txPower;
- m_haveTXPower = true;
-} // setTXPower
-
-
/**
* @brief Create a string representation of this device.
* @return A string representation of this device.
@@ -579,10 +724,35 @@ std::string NimBLEAdvertisedDevice::toString() {
* @return The advertisement payload.
*/
uint8_t* NimBLEAdvertisedDevice::getPayload() {
- return m_payload;
+ return &m_payload[0];
} // getPayload
+/**
+ * @brief Stores the payload of the advertised device in a vector.
+ * @param [in] payload The advertisement payload.
+ * @param [in] length The length of the payload in bytes.
+ * @param [in] append Indicates if the the data should be appended (scan response).
+ */
+void NimBLEAdvertisedDevice::setPayload(uint8_t *payload, uint8_t length, bool append) {
+ if(!append) {
+ m_advLength = length;
+ m_payload.assign(payload, payload + length);
+ } else {
+ m_payload.insert(m_payload.end(), payload, payload + length);
+ }
+}
+
+
+/**
+ * @brief Get the length of the advertisement data in the payload.
+ * @return The number of bytes in the payload that is from the advertisment.
+ */
+uint8_t NimBLEAdvertisedDevice::getAdvLength() {
+ return m_advLength;
+}
+
+
/**
* @brief Get the advertised device address type.
* @return The device address type:
@@ -610,7 +780,7 @@ time_t NimBLEAdvertisedDevice::getTimestamp() {
* @return The size of the payload in bytes.
*/
size_t NimBLEAdvertisedDevice::getPayloadLength() {
- return m_payloadLength;
+ return m_payload.size();
} // getPayloadLength
diff --git a/lib/libesp32/NimBLE-Arduino/src/NimBLEAdvertisedDevice.h b/lib/libesp32/NimBLE-Arduino/src/NimBLEAdvertisedDevice.h
index ebdb85c9e..ef354bc41 100644
--- a/lib/libesp32/NimBLE-Arduino/src/NimBLEAdvertisedDevice.h
+++ b/lib/libesp32/NimBLE-Arduino/src/NimBLEAdvertisedDevice.h
@@ -44,7 +44,11 @@ public:
NimBLEAddress getAddress();
uint8_t getAdvType();
uint16_t getAppearance();
+ uint16_t getAdvInterval();
+ uint16_t getMinInterval();
+ uint16_t getMaxInterval();
std::string getManufacturerData();
+ std::string getURI();
/**
* @brief A template to convert the service data to .
@@ -67,7 +71,7 @@ public:
NimBLEScan* getScan();
size_t getServiceDataCount();
std::string getServiceData(uint8_t index = 0);
- std::string getServiceData(const NimBLEUUID &uuid) const;
+ std::string getServiceData(const NimBLEUUID &uuid);
/**
* @brief A template to convert the service data to .
@@ -106,12 +110,15 @@ public:
NimBLEUUID getServiceDataUUID(uint8_t index = 0);
NimBLEUUID getServiceUUID(uint8_t index = 0);
size_t getServiceUUIDCount();
+ NimBLEAddress getTargetAddress(uint8_t index = 0);
+ size_t getTargetAddressCount();
int8_t getTXPower();
uint8_t* getPayload();
+ uint8_t getAdvLength();
size_t getPayloadLength();
uint8_t getAddressType();
time_t getTimestamp();
- bool isAdvertisingService(const NimBLEUUID &uuid) const;
+ bool isAdvertisingService(const NimBLEUUID &uuid);
bool haveAppearance();
bool haveManufacturerData();
bool haveName();
@@ -119,46 +126,30 @@ public:
bool haveServiceData();
bool haveServiceUUID();
bool haveTXPower();
+ bool haveConnParams();
+ bool haveAdvInterval();
+ bool haveTargetAddress();
+ bool haveURI();
std::string toString();
private:
friend class NimBLEScan;
- void parseAdvertisement(uint8_t* payload, uint8_t length);
- void setAddress(NimBLEAddress address);
- void setAdvType(uint8_t advType);
- void setAppearance(uint16_t appearance);
- void setManufacturerData(std::string manufacturerData);
- void setName(std::string name);
- void setRSSI(int rssi);
- void setServiceData(NimBLEUUID serviceUUID, std::string data);
- void setServiceUUID(const char* serviceUUID);
- void setServiceUUID(NimBLEUUID serviceUUID);
- void setTXPower(int8_t txPower);
-
- bool m_haveAppearance;
- bool m_haveManufacturerData;
- bool m_haveName;
- bool m_haveRSSI;
- bool m_haveServiceData;
- bool m_haveServiceUUID;
- bool m_haveTXPower;
-
+ void setAddress(NimBLEAddress address);
+ void setAdvType(uint8_t advType);
+ void setPayload(uint8_t *payload, uint8_t length, bool append);
+ void setRSSI(int rssi);
+ uint8_t findAdvField(uint8_t type, uint8_t index = 0, uint8_t *data_loc = nullptr);
+ uint8_t findServiceData(uint8_t index, uint8_t* bytes);
NimBLEAddress m_address = NimBLEAddress("");
uint8_t m_advType;
- uint16_t m_appearance;
- std::string m_manufacturerData;
- std::string m_name;
int m_rssi;
- int8_t m_txPower;
- uint8_t* m_payload;
- size_t m_payloadLength;
time_t m_timestamp;
bool m_callbackSent;
+ uint8_t m_advLength;
- std::vector m_serviceUUIDs;
- std::vector>m_serviceDataVec;
+ std::vector m_payload;
};
/**
diff --git a/lib/libesp32/NimBLE-Arduino/src/NimBLEAdvertising.cpp b/lib/libesp32/NimBLE-Arduino/src/NimBLEAdvertising.cpp
index a10480410..3e9ae4a64 100644
--- a/lib/libesp32/NimBLE-Arduino/src/NimBLEAdvertising.cpp
+++ b/lib/libesp32/NimBLE-Arduino/src/NimBLEAdvertising.cpp
@@ -32,23 +32,29 @@ static const char* LOG_TAG = "NimBLEAdvertising";
/**
* @brief Construct a default advertising object.
*/
-NimBLEAdvertising::NimBLEAdvertising() : m_slaveItvl() {
+NimBLEAdvertising::NimBLEAdvertising() {
+ reset();
+} // NimBLEAdvertising
+
+
+/**
+ * @brief Stops the current advertising and resets the advertising data to the default values.
+ */
+void NimBLEAdvertising::reset() {
+ if(NimBLEDevice::getInitialized() && isAdvertising()) {
+ stop();
+ }
memset(&m_advData, 0, sizeof m_advData);
memset(&m_scanData, 0, sizeof m_scanData);
memset(&m_advParams, 0, sizeof m_advParams);
+ memset(&m_slaveItvl, 0, sizeof m_slaveItvl);
const char *name = ble_svc_gap_device_name();
m_advData.name = (uint8_t *)name;
m_advData.name_len = strlen(name);
m_advData.name_is_complete = 1;
- m_advData.tx_pwr_lvl_is_present = 1;
m_advData.tx_pwr_lvl = NimBLEDevice::getPower();
m_advData.flags = (BLE_HS_ADV_F_DISC_GEN | BLE_HS_ADV_F_BREDR_UNSUP);
- m_advData.appearance = 0;
- m_advData.appearance_is_present = 0;
- m_advData.mfg_data_len = 0;
- m_advData.mfg_data = nullptr;
- m_advData.slave_itvl_range = nullptr;
#if !defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL)
m_advParams.conn_mode = BLE_GAP_CONN_MODE_NON;
@@ -56,17 +62,13 @@ NimBLEAdvertising::NimBLEAdvertising() : m_slaveItvl() {
m_advParams.conn_mode = BLE_GAP_CONN_MODE_UND;
#endif
m_advParams.disc_mode = BLE_GAP_DISC_MODE_GEN;
- m_advParams.itvl_min = 0;
- m_advParams.itvl_max = 0;
-
m_customAdvData = false;
m_customScanResponseData = false;
m_scanResp = true;
m_advDataSet = false;
// Set this to non-zero to prevent auto start if host reset before started by app.
m_duration = BLE_HS_FOREVER;
-
-} // NimBLEAdvertising
+} // reset
/**
@@ -85,6 +87,7 @@ void NimBLEAdvertising::addServiceUUID(const NimBLEUUID &serviceUUID) {
*/
void NimBLEAdvertising::addServiceUUID(const char* serviceUUID) {
addServiceUUID(NimBLEUUID(serviceUUID));
+ m_advDataSet = false;
} // addServiceUUID
@@ -112,9 +115,95 @@ void NimBLEAdvertising::removeServiceUUID(const NimBLEUUID &serviceUUID) {
void NimBLEAdvertising::setAppearance(uint16_t appearance) {
m_advData.appearance = appearance;
m_advData.appearance_is_present = 1;
+ m_advDataSet = false;
} // setAppearance
+/**
+ * @brief Add the transmission power level to the advertisement packet.
+ */
+void NimBLEAdvertising::addTxPower() {
+ m_advData.tx_pwr_lvl_is_present = 1;
+ m_advDataSet = false;
+} // addTxPower
+
+
+/**
+ * @brief Set the advertised name of the device.
+ * @param [in] name The name to advertise.
+ */
+void NimBLEAdvertising::setName(const std::string &name) {
+ m_name.assign(name.begin(), name.end());
+ m_advData.name = &m_name[0];
+ m_advData.name_len = m_name.size();
+ m_advDataSet = false;
+} // setName
+
+
+/**
+ * @brief Set the advertised manufacturer data.
+ * @param [in] data The data to advertise.
+ */
+void NimBLEAdvertising::setManufacturerData(const std::string &data) {
+ m_mfgData.assign(data.begin(), data.end());
+ m_advData.mfg_data = &m_mfgData[0];
+ m_advData.mfg_data_len = m_mfgData.size();
+ m_advDataSet = false;
+} // setManufacturerData
+
+
+/**
+ * @brief Set the advertised URI.
+ * @param [in] uri The URI to advertise.
+ */
+void NimBLEAdvertising::setURI(const std::string &uri) {
+ m_uri.assign(uri.begin(), uri.end());
+ m_advData.uri = &m_uri[0];
+ m_advData.uri_len = m_uri.size();
+ m_advDataSet = false;
+} // setURI
+
+
+/**
+ * @brief Set the service data advertised for the UUID.
+ * @param [in] uuid The UUID the service data belongs to.
+ * @param [in] data The data to advertise.
+ * @note If data length is 0 the service data will not be advertised.
+ */
+void NimBLEAdvertising::setServiceData(const NimBLEUUID &uuid, const std::string &data) {
+ switch (uuid.bitSize()) {
+ case 16: {
+ m_svcData16.assign((uint8_t*)&uuid.getNative()->u16.value, (uint8_t*)&uuid.getNative()->u16.value + 2);
+ m_svcData16.insert(m_svcData16.end(), data.begin(), data.end());
+ m_advData.svc_data_uuid16 = (uint8_t*)&m_svcData16[0];
+ m_advData.svc_data_uuid16_len = (data.length() > 0) ? m_svcData16.size() : 0;
+ break;
+ }
+
+ case 32: {
+ m_svcData32.assign((uint8_t*)&uuid.getNative()->u32.value, (uint8_t*)&uuid.getNative()->u32.value + 4);
+ m_svcData32.insert(m_svcData32.end(), data.begin(), data.end());
+ m_advData.svc_data_uuid32 = (uint8_t*)&m_svcData32[0];
+ m_advData.svc_data_uuid32_len = (data.length() > 0) ? m_svcData32.size() : 0;
+ break;
+ }
+
+ case 128: {
+ m_svcData128.assign(uuid.getNative()->u128.value, uuid.getNative()->u128.value + 16);
+ m_svcData128.insert(m_svcData128.end(), data.begin(), data.end());
+ m_advData.svc_data_uuid128 = (uint8_t*)&m_svcData128[0];
+ m_advData.svc_data_uuid128_len = (data.length() > 0) ? m_svcData128.size() : 0;
+ break;
+ }
+
+ default:
+ return;
+ }
+
+ m_advDataSet = false;
+} // setServiceData
+
+
/**
* @brief Set the type of advertisment to use.
* @param [in] adv_type:
@@ -172,6 +261,8 @@ void NimBLEAdvertising::setMinPreferred(uint16_t mininterval) {
m_slaveItvl[2] = m_slaveItvl[0];
m_slaveItvl[3] = m_slaveItvl[1];
}
+
+ m_advDataSet = false;
} // setMinPreferred
@@ -200,6 +291,8 @@ void NimBLEAdvertising::setMaxPreferred(uint16_t maxinterval) {
m_slaveItvl[0] = m_slaveItvl[2];
m_slaveItvl[1] = m_slaveItvl[3];
}
+
+ m_advDataSet = false;
} // setMaxPreferred
@@ -209,6 +302,7 @@ void NimBLEAdvertising::setMaxPreferred(uint16_t maxinterval) {
*/
void NimBLEAdvertising::setScanResponse(bool set) {
m_scanResp = set;
+ m_advDataSet = false;
} // setScanResponse
@@ -344,12 +438,29 @@ bool NimBLEAdvertising::start(uint32_t duration, void (*advCompleteCB)(NimBLEAdv
if (!m_customAdvData && !m_advDataSet) {
//start with 3 bytes for the flags data
uint8_t payloadLen = (2 + 1);
+ if(m_advData.mfg_data_len > 0)
+ payloadLen += (2 + m_advData.mfg_data_len);
+
+ if(m_advData.svc_data_uuid16_len > 0)
+ payloadLen += (2 + m_advData.svc_data_uuid16_len);
+
+ if(m_advData.svc_data_uuid32_len > 0)
+ payloadLen += (2 + m_advData.svc_data_uuid32_len);
+
+ if(m_advData.svc_data_uuid128_len > 0)
+ payloadLen += (2 + m_advData.svc_data_uuid128_len);
+
+ if(m_advData.uri_len > 0)
+ payloadLen += (2 + m_advData.uri_len);
+
if(m_advData.appearance_is_present)
payloadLen += (2 + BLE_HS_ADV_APPEARANCE_LEN);
+
if(m_advData.tx_pwr_lvl_is_present)
- payloadLen += (2 + 1);
+ payloadLen += (2 + BLE_HS_ADV_TX_PWR_LVL_LEN);
+
if(m_advData.slave_itvl_range != nullptr)
- payloadLen += (2 + 4);
+ payloadLen += (2 + BLE_HS_ADV_SLAVE_ITVL_RANGE_LEN);
for(auto &it : m_serviceUUIDs) {
if(it.getNative()->u.type == BLE_UUID_TYPE_16) {
@@ -419,7 +530,7 @@ bool NimBLEAdvertising::start(uint32_t duration, void (*advCompleteCB)(NimBLEAdv
// check if there is room for the name, if not put it in scan data
if((payloadLen + (2 + m_advData.name_len)) > BLE_HS_ADV_MAX_SZ) {
- if(m_scanResp){
+ if(m_scanResp && !m_customScanResponseData){
m_scanData.name = m_advData.name;
m_scanData.name_len = m_advData.name_len;
if(m_scanData.name_len > BLE_HS_ADV_MAX_SZ - 2) {
@@ -433,7 +544,6 @@ bool NimBLEAdvertising::start(uint32_t duration, void (*advCompleteCB)(NimBLEAdv
m_advData.name_is_complete = 0;
} else {
if(m_advData.tx_pwr_lvl_is_present) {
- m_advData.tx_pwr_lvl = 0;
m_advData.tx_pwr_lvl_is_present = 0;
payloadLen -= (2 + 1);
}
@@ -446,7 +556,7 @@ bool NimBLEAdvertising::start(uint32_t duration, void (*advCompleteCB)(NimBLEAdv
}
}
- if(m_scanResp) {
+ if(m_scanResp && !m_customScanResponseData) {
rc = ble_gap_adv_rsp_set_fields(&m_scanData);
switch(rc) {
case 0:
@@ -581,7 +691,7 @@ void NimBLEAdvertising::advCompleteCB() {
if(m_advCompCB != nullptr) {
m_advCompCB(this);
}
-}
+} // advCompleteCB
/**
@@ -590,7 +700,7 @@ void NimBLEAdvertising::advCompleteCB() {
*/
bool NimBLEAdvertising::isAdvertising() {
return ble_gap_adv_active();
-}
+} // isAdvertising
/*
@@ -608,7 +718,7 @@ void NimBLEAdvertising::onHostSync() {
// Otherwise we should tell the app that advertising stopped.
advCompleteCB();
}
-}
+} // onHostSync
/**
@@ -646,6 +756,7 @@ int NimBLEAdvertising::handleGapEvent(struct ble_gap_event *event, void *arg) {
*/
void NimBLEAdvertisementData::addData(const std::string &data) {
if ((m_payload.length() + data.length()) > BLE_HS_ADV_MAX_SZ) {
+ NIMBLE_LOGE(LOG_TAG, "Advertisement data length exceded");
return;
}
m_payload.append(data);
@@ -657,11 +768,8 @@ void NimBLEAdvertisementData::addData(const std::string &data) {
* @param [in] data The data to be added to the payload.
* @param [in] length The size of data to be added to the payload.
*/
-void NimBLEAdvertisementData::addData(char * data, size_t length){
- if ((m_payload.length() + length) > BLE_HS_ADV_MAX_SZ) {
- return;
- }
- m_payload.append(data,length);
+void NimBLEAdvertisementData::addData(char * data, size_t length) {
+ addData(std::string(data, length));
} // addData
@@ -680,43 +788,6 @@ void NimBLEAdvertisementData::setAppearance(uint16_t appearance) {
} // setAppearance
-/**
- * @brief Set the complete services to advertise.
- * @param [in] uuid The UUID of the service.
- */
-void NimBLEAdvertisementData::setCompleteServices(const NimBLEUUID &uuid) {
- char cdata[2];
- switch (uuid.bitSize()) {
- case 16: {
- // [Len] [0x02] [LL] [HH]
- cdata[0] = 3;
- cdata[1] = BLE_HS_ADV_TYPE_COMP_UUIDS16; // 0x03
- addData(std::string(cdata, 2) + std::string((char*) &uuid.getNative()->u16.value, 2));
- break;
- }
-
- case 32: {
- // [Len] [0x04] [LL] [LL] [HH] [HH]
- cdata[0] = 5;
- cdata[1] = BLE_HS_ADV_TYPE_COMP_UUIDS32; // 0x05
- addData(std::string(cdata, 2) + std::string((char*) &uuid.getNative()->u32.value, 4));
- break;
- }
-
- case 128: {
- // [Len] [0x04] [0] [1] ... [15]
- cdata[0] = 17;
- cdata[1] = BLE_HS_ADV_TYPE_COMP_UUIDS128; // 0x07
- addData(std::string(cdata, 2) + std::string((char*) uuid.getNative()->u128.value, 16));
- break;
- }
-
- default:
- return;
- }
-} // setCompleteServices
-
-
/**
* @brief Set the advertisement flags.
* @param [in] flag The flags to be set in the advertisement.
@@ -738,64 +809,141 @@ void NimBLEAdvertisementData::setFlags(uint8_t flag) {
* @param [in] data The manufacturer data to advertise.
*/
void NimBLEAdvertisementData::setManufacturerData(const std::string &data) {
- NIMBLE_LOGD("NimBLEAdvertisementData", ">> setManufacturerData");
char cdata[2];
cdata[0] = data.length() + 1;
cdata[1] = BLE_HS_ADV_TYPE_MFG_DATA ; // 0xff
addData(std::string(cdata, 2) + data);
- NIMBLE_LOGD("NimBLEAdvertisementData", "<< setManufacturerData");
} // setManufacturerData
+/**
+ * @brief Set the URI to advertise.
+ * @param [in] uri The uri to advertise.
+ */
+void NimBLEAdvertisementData::setURI(const std::string &uri) {
+ char cdata[2];
+ cdata[0] = uri.length() + 1;
+ cdata[1] = BLE_HS_ADV_TYPE_URI;
+ addData(std::string(cdata, 2) + uri);
+} // setURI
+
+
/**
* @brief Set the complete name of this device.
* @param [in] name The name to advertise.
*/
void NimBLEAdvertisementData::setName(const std::string &name) {
- NIMBLE_LOGD("NimBLEAdvertisementData", ">> setName: %s", name.c_str());
char cdata[2];
cdata[0] = name.length() + 1;
cdata[1] = BLE_HS_ADV_TYPE_COMP_NAME; // 0x09
addData(std::string(cdata, 2) + name);
- NIMBLE_LOGD("NimBLEAdvertisementData", "<< setName");
} // setName
/**
- * @brief Set the partial services to advertise.
- * @param [in] uuid The single service to advertise.
+ * @brief Set a single service to advertise as a complete list of services.
+ * @param [in] uuid The service to advertise.
+ */
+void NimBLEAdvertisementData::setCompleteServices(const NimBLEUUID &uuid) {
+ setServices(true, uuid.bitSize(), {uuid});
+} // setCompleteServices
+
+
+/**
+ * @brief Set the complete list of 16 bit services to advertise.
+ * @param [in] v_uuid A vector of 16 bit UUID's to advertise.
+ */
+void NimBLEAdvertisementData::setCompleteServices16(const std::vector& v_uuid) {
+ setServices(true, 16, v_uuid);
+} // setCompleteServices16
+
+
+/**
+ * @brief Set the complete list of 32 bit services to advertise.
+ * @param [in] v_uuid A vector of 32 bit UUID's to advertise.
+ */
+void NimBLEAdvertisementData::setCompleteServices32(const std::vector& v_uuid) {
+ setServices(true, 32, v_uuid);
+} // setCompleteServices32
+
+
+/**
+ * @brief Set a single service to advertise as a partial list of services.
+ * @param [in] uuid The service to advertise.
*/
void NimBLEAdvertisementData::setPartialServices(const NimBLEUUID &uuid) {
+ setServices(false, uuid.bitSize(), {uuid});
+} // setPartialServices
+
+
+/**
+ * @brief Set the partial list of services to advertise.
+ * @param [in] v_uuid A vector of 16 bit UUID's to advertise.
+ */
+void NimBLEAdvertisementData::setPartialServices16(const std::vector& v_uuid) {
+ setServices(false, 16, v_uuid);
+} // setPartialServices16
+
+
+/**
+ * @brief Set the partial list of services to advertise.
+ * @param [in] v_uuid A vector of 32 bit UUID's to advertise.
+ */
+void NimBLEAdvertisementData::setPartialServices32(const std::vector& v_uuid) {
+ setServices(false, 32, v_uuid);
+} // setPartialServices32
+
+
+/**
+ * @brief Utility function to create the list of service UUID's from a vector.
+ * @param [in] complete If true the vector is the complete set of services.
+ * @param [in] size The bit size of the UUID's in the vector. (16, 32, or 128).
+ * @param [in] v_uuid The vector of service UUID's to advertise.
+ */
+void NimBLEAdvertisementData::setServices(const bool complete, const uint8_t size,
+ const std::vector &v_uuid)
+{
char cdata[2];
- switch (uuid.bitSize()) {
- case 16: {
- // [Len] [0x02] [LL] [HH]
- cdata[0] = 3;
- cdata[1] = BLE_HS_ADV_TYPE_INCOMP_UUIDS16; // 0x02
- addData(std::string(cdata, 2) + std::string((char *) &uuid.getNative()->u16.value, 2));
+ cdata[0] = (size / 8) * v_uuid.size() + 1;
+ switch(size) {
+ case 16:
+ cdata[1] = complete ? BLE_HS_ADV_TYPE_COMP_UUIDS16 : BLE_HS_ADV_TYPE_INCOMP_UUIDS16;
break;
- }
-
- case 32: {
- // [Len] [0x04] [LL] [LL] [HH] [HH]
- cdata[0] = 5;
- cdata[1] = BLE_HS_ADV_TYPE_INCOMP_UUIDS32; // 0x04
- addData(std::string(cdata, 2) + std::string((char *) &uuid.getNative()->u32.value, 4));
+ case 32:
+ cdata[1] = complete ? BLE_HS_ADV_TYPE_COMP_UUIDS32 : BLE_HS_ADV_TYPE_INCOMP_UUIDS32;
break;
- }
-
- case 128: {
- // [Len] [0x04] [0] [1] ... [15]
- cdata[0] = 17;
- cdata[1] = BLE_HS_ADV_TYPE_INCOMP_UUIDS128; // 0x06
- addData(std::string(cdata, 2) + std::string((char *) &uuid.getNative()->u128.value, 16));
+ case 128:
+ cdata[1] = complete ? BLE_HS_ADV_TYPE_COMP_UUIDS128 : BLE_HS_ADV_TYPE_INCOMP_UUIDS128;
break;
- }
-
default:
return;
}
-} // setPartialServices
+
+ std::string uuids;
+
+ for(auto &it : v_uuid){
+ if(it.bitSize() != size) {
+ NIMBLE_LOGE(LOG_TAG, "Service UUID(%d) invalid", size);
+ return;
+ } else {
+ switch(size) {
+ case 16:
+ uuids += std::string((char*)&it.getNative()->u16.value, 2);
+ break;
+ case 32:
+ uuids += std::string((char*)&it.getNative()->u32.value, 4);
+ break;
+ case 128:
+ uuids += std::string((char*)&it.getNative()->u128.value, 16);
+ break;
+ default:
+ return;
+ }
+ }
+ }
+
+ addData(std::string(cdata, 2) + uuids);
+} // setServices
/**
@@ -841,15 +989,42 @@ void NimBLEAdvertisementData::setServiceData(const NimBLEUUID &uuid, const std::
* @param [in] name The short name of the device.
*/
void NimBLEAdvertisementData::setShortName(const std::string &name) {
- NIMBLE_LOGD("NimBLEAdvertisementData", ">> setShortName: %s", name.c_str());
char cdata[2];
cdata[0] = name.length() + 1;
cdata[1] = BLE_HS_ADV_TYPE_INCOMP_NAME; // 0x08
addData(std::string(cdata, 2) + name);
- NIMBLE_LOGD("NimBLEAdvertisementData", "<< setShortName");
} // setShortName
+/**
+ * @brief Adds Tx power level to the advertisement data.
+ */
+void NimBLEAdvertisementData::addTxPower() {
+ char cdata[3];
+ cdata[0] = BLE_HS_ADV_TX_PWR_LVL_LEN + 1;
+ cdata[1] = BLE_HS_ADV_TYPE_TX_PWR_LVL;
+ cdata[2] = NimBLEDevice::getPower();
+ addData(cdata, 3);
+} // addTxPower
+
+
+/**
+ * @brief Set the preferred connection interval parameters.
+ * @param [in] min The minimum interval desired.
+ * @param [in] max The maximum interval desired.
+ */
+void NimBLEAdvertisementData::setPreferredParams(uint16_t min, uint16_t max) {
+ char cdata[6];
+ cdata[0] = BLE_HS_ADV_SLAVE_ITVL_RANGE_LEN + 1;
+ cdata[1] = BLE_HS_ADV_TYPE_SLAVE_ITVL_RANGE;
+ cdata[2] = min;
+ cdata[3] = min >> 8;
+ cdata[4] = max;
+ cdata[5] = max >> 8;
+ addData(cdata, 6);
+} // setPreferredParams
+
+
/**
* @brief Retrieve the payload that is to be advertised.
* @return The payload that is to be advertised.
diff --git a/lib/libesp32/NimBLE-Arduino/src/NimBLEAdvertising.h b/lib/libesp32/NimBLE-Arduino/src/NimBLEAdvertising.h
index 0d97ecbbc..17a866561 100644
--- a/lib/libesp32/NimBLE-Arduino/src/NimBLEAdvertising.h
+++ b/lib/libesp32/NimBLE-Arduino/src/NimBLEAdvertising.h
@@ -50,18 +50,27 @@ class NimBLEAdvertisementData {
public:
void setAppearance(uint16_t appearance);
void setCompleteServices(const NimBLEUUID &uuid);
+ void setCompleteServices16(const std::vector &v_uuid);
+ void setCompleteServices32(const std::vector &v_uuid);
void setFlags(uint8_t);
void setManufacturerData(const std::string &data);
+ void setURI(const std::string &uri);
void setName(const std::string &name);
void setPartialServices(const NimBLEUUID &uuid);
+ void setPartialServices16(const std::vector &v_uuid);
+ void setPartialServices32(const std::vector &v_uuid);
void setServiceData(const NimBLEUUID &uuid, const std::string &data);
void setShortName(const std::string &name);
void addData(const std::string &data); // Add data to the payload.
void addData(char * data, size_t length);
+ void addTxPower();
+ void setPreferredParams(uint16_t min, uint16_t max);
std::string getPayload(); // Retrieve the current advert payload.
private:
friend class NimBLEAdvertising;
+ void setServices(const bool complete, const uint8_t size,
+ const std::vector &v_uuid);
std::string m_payload; // The payload of the advertisement.
}; // NimBLEAdvertisementData
@@ -80,6 +89,10 @@ public:
bool start(uint32_t duration = 0, void (*advCompleteCB)(NimBLEAdvertising *pAdv) = nullptr);
void stop();
void setAppearance(uint16_t appearance);
+ void setName(const std::string &name);
+ void setManufacturerData(const std::string &data);
+ void setURI(const std::string &uri);
+ void setServiceData(const NimBLEUUID &uuid, const std::string &data);
void setAdvertisementType(uint8_t adv_type);
void setMaxInterval(uint16_t maxinterval);
void setMinInterval(uint16_t mininterval);
@@ -89,6 +102,8 @@ public:
void setScanResponse(bool);
void setMinPreferred(uint16_t);
void setMaxPreferred(uint16_t);
+ void addTxPower();
+ void reset();
void advCompleteCB();
bool isAdvertising();
@@ -109,6 +124,12 @@ private:
void (*m_advCompCB)(NimBLEAdvertising *pAdv);
uint8_t m_slaveItvl[4];
uint32_t m_duration;
+ std::vector m_svcData16;
+ std::vector m_svcData32;
+ std::vector m_svcData128;
+ std::vector m_name;
+ std::vector m_mfgData;
+ std::vector m_uri;
};
#endif // #if defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL)
diff --git a/lib/libesp32/NimBLE-Arduino/src/NimBLECharacteristic.cpp b/lib/libesp32/NimBLE-Arduino/src/NimBLECharacteristic.cpp
index e9a5a49c9..0ffa9fb03 100644
--- a/lib/libesp32/NimBLE-Arduino/src/NimBLECharacteristic.cpp
+++ b/lib/libesp32/NimBLE-Arduino/src/NimBLECharacteristic.cpp
@@ -101,9 +101,9 @@ NimBLEDescriptor* NimBLECharacteristic::createDescriptor(const NimBLEUUID &uuid,
/**
- * @brief Return the BLE Descriptor for the given UUID if associated with this characteristic.
- * @param [in] uuid The UUID of the descriptor that we wish to retrieve.
- * @return pointer to the NimBLEDescriptor. If no such descriptor is associated with the characteristic, nullptr is returned.
+ * @brief Return the BLE Descriptor for the given UUID.
+ * @param [in] uuid The UUID of the descriptor.
+ * @return A pointer to the descriptor object or nullptr if not found.
*/
NimBLEDescriptor* NimBLECharacteristic::getDescriptorByUUID(const char* uuid) {
return getDescriptorByUUID(NimBLEUUID(uuid));
@@ -111,9 +111,9 @@ NimBLEDescriptor* NimBLECharacteristic::getDescriptorByUUID(const char* uuid) {
/**
- * @brief Return the BLE Descriptor for the given UUID if associated with this characteristic.
- * @param [in] uuid The UUID of the descriptor that we wish to retrieve.
- * @return pointer to the NimBLEDescriptor. If no such descriptor is associated with the characteristic, nullptr is returned.
+ * @brief Return the BLE Descriptor for the given UUID.
+ * @param [in] uuid The UUID of the descriptor.
+ * @return A pointer to the descriptor object or nullptr if not found.
*/
NimBLEDescriptor* NimBLECharacteristic::getDescriptorByUUID(const NimBLEUUID &uuid) {
for (auto &it : m_dscVec) {
@@ -124,6 +124,20 @@ NimBLEDescriptor* NimBLECharacteristic::getDescriptorByUUID(const NimBLEUUID &uu
return nullptr;
} // getDescriptorByUUID
+/**
+ * @brief Return the BLE Descriptor for the given handle.
+ * @param [in] handle The handle of the descriptor.
+ * @return A pointer to the descriptor object or nullptr if not found.
+ */
+NimBLEDescriptor *NimBLECharacteristic::getDescriptorByHandle(uint16_t handle) {
+ for (auto &it : m_dscVec) {
+ if (it->getHandle() == handle) {
+ return it;
+ }
+ }
+ return nullptr;
+}
+
/**
* @brief Get the handle of the characteristic.
diff --git a/lib/libesp32/NimBLE-Arduino/src/NimBLECharacteristic.h b/lib/libesp32/NimBLE-Arduino/src/NimBLECharacteristic.h
index 1c7418aef..9bb9d079b 100644
--- a/lib/libesp32/NimBLE-Arduino/src/NimBLECharacteristic.h
+++ b/lib/libesp32/NimBLE-Arduino/src/NimBLECharacteristic.h
@@ -59,6 +59,17 @@ class NimBLECharacteristicCallbacks;
*/
class NimBLECharacteristic {
public:
+
+ uint16_t getHandle();
+ NimBLEUUID getUUID();
+ std::string toString();
+
+ void setCallbacks(NimBLECharacteristicCallbacks* pCallbacks);
+
+ void indicate();
+ void notify(bool is_notification = true);
+ size_t getSubscribedCount();
+
NimBLEDescriptor* createDescriptor(const char* uuid,
uint32_t properties =
NIMBLE_PROPERTY::READ |
@@ -72,9 +83,10 @@ public:
NimBLEDescriptor* getDescriptorByUUID(const char* uuid);
NimBLEDescriptor* getDescriptorByUUID(const NimBLEUUID &uuid);
- NimBLEUUID getUUID();
- std::string getValue(time_t *timestamp = nullptr);
+ NimBLEDescriptor* getDescriptorByHandle(uint16_t handle);
+ std::string getValue(time_t *timestamp = nullptr);
+ size_t getDataLength();
/**
* @brief A template to convert the characteristic data to .
* @tparam T The type to convert the data to.
@@ -92,25 +104,20 @@ public:
return *((T *)pData);
}
- size_t getDataLength();
- void indicate();
- void notify(bool is_notification = true);
- void setCallbacks(NimBLECharacteristicCallbacks* pCallbacks);
void setValue(const uint8_t* data, size_t size);
void setValue(const std::string &value);
-
/**
* @brief Convenience template to set the characteristic value to val.
* @param [in] s The value to set.
*/
template
- void setValue(const T &s) {
+ void setValue(const T &s) {
setValue((uint8_t*)&s, sizeof(T));
}
- std::string toString();
- uint16_t getHandle();
- size_t getSubscribedCount();
+
+
+
private:
diff --git a/lib/libesp32/NimBLE-Arduino/src/NimBLEDescriptor.cpp b/lib/libesp32/NimBLE-Arduino/src/NimBLEDescriptor.cpp
index e8f7f9f8b..95c3291f3 100644
--- a/lib/libesp32/NimBLE-Arduino/src/NimBLEDescriptor.cpp
+++ b/lib/libesp32/NimBLE-Arduino/src/NimBLEDescriptor.cpp
@@ -122,11 +122,17 @@ uint8_t* NimBLEDescriptor::getValue() {
return m_value.attr_value;
} // getValue
+/**
+ * @brief Get the value of this descriptor as a string.
+ * @return A std::string instance containing a copy of the descriptor's value.
+ */
+std::string NimBLEDescriptor::getStringValue() {
+ return std::string((char *) m_value.attr_value, m_value.attr_len);
+}
int NimBLEDescriptor::handleGapEvent(uint16_t conn_handle, uint16_t attr_handle,
struct ble_gatt_access_ctxt *ctxt,
- void *arg)
-{
+ void *arg) {
const ble_uuid_t *uuid;
int rc;
NimBLEDescriptor* pDescriptor = (NimBLEDescriptor*)arg;
diff --git a/lib/libesp32/NimBLE-Arduino/src/NimBLEDescriptor.h b/lib/libesp32/NimBLE-Arduino/src/NimBLEDescriptor.h
index 16b6edceb..76318b729 100644
--- a/lib/libesp32/NimBLE-Arduino/src/NimBLEDescriptor.h
+++ b/lib/libesp32/NimBLE-Arduino/src/NimBLEDescriptor.h
@@ -44,20 +44,23 @@ class NimBLEDescriptorCallbacks;
class NimBLEDescriptor {
public:
uint16_t getHandle();
- size_t getLength();
NimBLEUUID getUUID();
- uint8_t* getValue();
- void setCallbacks(NimBLEDescriptorCallbacks* pCallbacks);
- void setValue(const uint8_t* data, size_t size);
- void setValue(const std::string &value);
std::string toString();
+ void setCallbacks(NimBLEDescriptorCallbacks* pCallbacks);
+
+ size_t getLength();
+ uint8_t* getValue();
+ std::string getStringValue();
+
+ void setValue(const uint8_t* data, size_t size);
+ void setValue(const std::string &value);
/**
* @brief Convenience template to set the descriptor value to val.
* @param [in] s The value to set.
*/
template
- void setValue(const T &s) {
+ void setValue(const T &s) {
setValue((uint8_t*)&s, sizeof(T));
}
diff --git a/lib/libesp32/NimBLE-Arduino/src/NimBLEDevice.cpp b/lib/libesp32/NimBLE-Arduino/src/NimBLEDevice.cpp
index f34a522f7..dc9caf3d5 100644
--- a/lib/libesp32/NimBLE-Arduino/src/NimBLEDevice.cpp
+++ b/lib/libesp32/NimBLE-Arduino/src/NimBLEDevice.cpp
@@ -62,6 +62,8 @@ std::list NimBLEDevice::m_cList;
std::list NimBLEDevice::m_ignoreList;
NimBLESecurityCallbacks* NimBLEDevice::m_securityCallbacks = nullptr;
uint8_t NimBLEDevice::m_own_addr_type = BLE_OWN_ADDR_PUBLIC;
+uint16_t NimBLEDevice::m_scanDuplicateSize = CONFIG_BTDM_SCAN_DUPL_CACHE_SIZE;
+uint8_t NimBLEDevice::m_scanFilterMode = CONFIG_BTDM_SCAN_DUPL_TYPE;
/**
@@ -303,7 +305,7 @@ void NimBLEDevice::stopAdvertising() {
/**
- * @brief Set the transmission power.
+ * @brief Get the transmission power.
* @param [in] powerType The power level to set, can be one of:
* * ESP_BLE_PWR_TYPE_CONN_HDL0 = 0, For connection handle 0
* * ESP_BLE_PWR_TYPE_CONN_HDL1 = 1, For connection handle 1
@@ -342,7 +344,7 @@ void NimBLEDevice::stopAdvertising() {
default:
return BLE_HS_ADV_TX_PWR_LVL_AUTO;
}
-} // setPower
+} // getPower
/**
@@ -400,6 +402,53 @@ void NimBLEDevice::stopAdvertising() {
}
+/**
+ * @brief Set the duplicate filter cache size for filtering scanned devices.
+ * @param [in] cacheSize The number of advertisements filtered before the cache is reset.\n
+ * Range is 10-1000, a larger value will reduce how often the same devices are reported.
+ * @details Must only be called before calling NimBLEDevice::init.
+ */
+/*STATIC*/
+void NimBLEDevice::setScanDuplicateCacheSize(uint16_t cacheSize) {
+ if(initialized) {
+ NIMBLE_LOGE(LOG_TAG, "Cannot change scan cache size while initialized");
+ return;
+ } else if(cacheSize > 1000 || cacheSize <10) {
+ NIMBLE_LOGE(LOG_TAG, "Invalid scan cache size; min=10 max=1000");
+ return;
+ }
+
+ m_scanDuplicateSize = cacheSize;
+}
+
+
+/**
+ * @brief Set the duplicate filter mode for filtering scanned devices.
+ * @param [in] mode One of three possible options:
+ * * CONFIG_BTDM_SCAN_DUPL_TYPE_DEVICE (0) (default)\n
+ Filter by device address only, advertisements from the same address will be reported only once.
+ * * CONFIG_BTDM_SCAN_DUPL_TYPE_DATA (1)\n
+ Filter by data only, advertisements with the same data will only be reported once,\n
+ even from different addresses.
+ * * CONFIG_BTDM_SCAN_DUPL_TYPE_DATA_DEVICE (2)\n
+ Filter by address and data, advertisements from the same address will be reported only once,\n
+ except if the data in the advertisement has changed, then it will be reported again.
+ * @details Must only be called before calling NimBLEDevice::init.
+ */
+/*STATIC*/
+void NimBLEDevice::setScanFilterMode(uint8_t mode) {
+ if(initialized) {
+ NIMBLE_LOGE(LOG_TAG, "Cannot change scan duplicate type while initialized");
+ return;
+ } else if(mode > 2) {
+ NIMBLE_LOGE(LOG_TAG, "Invalid scan duplicate type");
+ return;
+ }
+
+ m_scanFilterMode = mode;
+}
+
+
/**
* @brief Host reset, we pass the message so we don't make calls until resynced.
* @param [in] reason The reason code for the reset.
@@ -499,8 +548,17 @@ void NimBLEDevice::stopAdvertising() {
ESP_ERROR_CHECK(errRc);
- ESP_ERROR_CHECK(esp_nimble_hci_and_controller_init());
+ esp_bt_controller_mem_release(ESP_BT_MODE_CLASSIC_BT);
+ esp_bt_controller_config_t bt_cfg = BT_CONTROLLER_INIT_CONFIG_DEFAULT();
+ bt_cfg.mode = ESP_BT_MODE_BLE;
+ bt_cfg.ble_max_conn = CONFIG_BT_NIMBLE_MAX_CONNECTIONS;
+ bt_cfg.normal_adv_size = m_scanDuplicateSize;
+ bt_cfg.scan_duplicate_type = m_scanFilterMode;
+
+ ESP_ERROR_CHECK(esp_bt_controller_init(&bt_cfg));
+ ESP_ERROR_CHECK(esp_bt_controller_enable(ESP_BT_MODE_BLE));
+ ESP_ERROR_CHECK(esp_nimble_hci_init());
nimble_port_init();
// Setup callbacks for host events
diff --git a/lib/libesp32/NimBLE-Arduino/src/NimBLEDevice.h b/lib/libesp32/NimBLE-Arduino/src/NimBLEDevice.h
index 2d586bb42..27549ed60 100644
--- a/lib/libesp32/NimBLE-Arduino/src/NimBLEDevice.h
+++ b/lib/libesp32/NimBLE-Arduino/src/NimBLEDevice.h
@@ -123,6 +123,8 @@ public:
static bool isIgnored(const NimBLEAddress &address);
static void addIgnored(const NimBLEAddress &address);
static void removeIgnored(const NimBLEAddress &address);
+ static void setScanDuplicateCacheSize(uint16_t cacheSize);
+ static void setScanFilterMode(uint8_t type);
#if defined(CONFIG_BT_NIMBLE_ROLE_BROADCASTER)
static NimBLEAdvertising* getAdvertising();
@@ -184,6 +186,8 @@ private:
static ble_gap_event_listener m_listener;
static gap_event_handler m_customGapHandler;
static uint8_t m_own_addr_type;
+ static uint16_t m_scanDuplicateSize;
+ static uint8_t m_scanFilterMode;
};
diff --git a/lib/libesp32/NimBLE-Arduino/src/NimBLEHIDDevice.cpp b/lib/libesp32/NimBLE-Arduino/src/NimBLEHIDDevice.cpp
index a06b75360..37d0f52f5 100644
--- a/lib/libesp32/NimBLE-Arduino/src/NimBLEHIDDevice.cpp
+++ b/lib/libesp32/NimBLE-Arduino/src/NimBLEHIDDevice.cpp
@@ -103,7 +103,7 @@ void NimBLEHIDDevice::manufacturer(std::string name) {
/**
* @brief Sets the Plug n Play characterisc value.
* @param [in] sig The vendor ID source number.
- * @param [in[ vid The vendor ID number.
+ * @param [in] vid The vendor ID number.
* @param [in] pid The product ID number.
* @param [in] version The produce version number.
*/
@@ -128,8 +128,8 @@ void NimBLEHIDDevice::hidInfo(uint8_t country, uint8_t flags) {
* @return pointer to new input report characteristic
*/
NimBLECharacteristic* NimBLEHIDDevice::inputReport(uint8_t reportID) {
- NimBLECharacteristic* inputReportCharacteristic = m_hidService->createCharacteristic((uint16_t) 0x2a4d, NIMBLE_PROPERTY::READ | NIMBLE_PROPERTY::NOTIFY);
- NimBLEDescriptor* inputReportDescriptor = inputReportCharacteristic->createDescriptor((uint16_t) 0x2908);
+ NimBLECharacteristic* inputReportCharacteristic = m_hidService->createCharacteristic((uint16_t) 0x2a4d, NIMBLE_PROPERTY::READ | NIMBLE_PROPERTY::NOTIFY | NIMBLE_PROPERTY::READ_ENC);
+ NimBLEDescriptor* inputReportDescriptor = inputReportCharacteristic->createDescriptor((uint16_t) 0x2908, NIMBLE_PROPERTY::READ | NIMBLE_PROPERTY::READ_ENC);
uint8_t desc1_val[] = { reportID, 0x01 };
inputReportDescriptor->setValue((uint8_t*) desc1_val, 2);
@@ -144,7 +144,7 @@ NimBLECharacteristic* NimBLEHIDDevice::inputReport(uint8_t reportID) {
*/
NimBLECharacteristic* NimBLEHIDDevice::outputReport(uint8_t reportID) {
NimBLECharacteristic* outputReportCharacteristic = m_hidService->createCharacteristic((uint16_t) 0x2a4d, NIMBLE_PROPERTY::READ | NIMBLE_PROPERTY::WRITE | NIMBLE_PROPERTY::WRITE_NR | NIMBLE_PROPERTY::READ_ENC | NIMBLE_PROPERTY::WRITE_ENC);
- NimBLEDescriptor* outputReportDescriptor = outputReportCharacteristic->createDescriptor((uint16_t) 0x2908);
+ NimBLEDescriptor* outputReportDescriptor = outputReportCharacteristic->createDescriptor((uint16_t) 0x2908, NIMBLE_PROPERTY::READ | NIMBLE_PROPERTY::WRITE | NIMBLE_PROPERTY::READ_ENC | NIMBLE_PROPERTY::WRITE_ENC);
uint8_t desc1_val[] = { reportID, 0x02 };
outputReportDescriptor->setValue((uint8_t*) desc1_val, 2);
@@ -159,7 +159,7 @@ NimBLECharacteristic* NimBLEHIDDevice::outputReport(uint8_t reportID) {
*/
NimBLECharacteristic* NimBLEHIDDevice::featureReport(uint8_t reportID) {
NimBLECharacteristic* featureReportCharacteristic = m_hidService->createCharacteristic((uint16_t) 0x2a4d, NIMBLE_PROPERTY::READ | NIMBLE_PROPERTY::WRITE | NIMBLE_PROPERTY::READ_ENC | NIMBLE_PROPERTY::WRITE_ENC);
- NimBLEDescriptor* featureReportDescriptor = featureReportCharacteristic->createDescriptor((uint16_t) 0x2908);
+ NimBLEDescriptor* featureReportDescriptor = featureReportCharacteristic->createDescriptor((uint16_t) 0x2908, NIMBLE_PROPERTY::READ | NIMBLE_PROPERTY::WRITE | NIMBLE_PROPERTY::READ_ENC | NIMBLE_PROPERTY::WRITE_ENC);
uint8_t desc1_val[] = { reportID, 0x03 };
featureReportDescriptor->setValue((uint8_t*) desc1_val, 2);
diff --git a/lib/libesp32/NimBLE-Arduino/src/NimBLEScan.cpp b/lib/libesp32/NimBLE-Arduino/src/NimBLEScan.cpp
index 122ff3332..00aeab03c 100644
--- a/lib/libesp32/NimBLE-Arduino/src/NimBLEScan.cpp
+++ b/lib/libesp32/NimBLE-Arduino/src/NimBLEScan.cpp
@@ -38,9 +38,9 @@ NimBLEScan::NimBLEScan() {
m_scan_params.filter_duplicates = 0; // If set, the controller ignores all but the first advertisement from each device.
m_pAdvertisedDeviceCallbacks = nullptr;
m_ignoreResults = false;
- m_wantDuplicates = false;
m_pTaskData = nullptr;
m_duration = BLE_HS_FOREVER; // make sure this is non-zero in the event of a host reset
+ m_maxResults = 0xFF;
}
@@ -64,7 +64,7 @@ NimBLEScan::~NimBLEScan() {
case BLE_GAP_EVENT_DISC: {
if(pScan->m_ignoreResults) {
- NIMBLE_LOGE(LOG_TAG, "Scan op in progress - ignoring results");
+ NIMBLE_LOGI(LOG_TAG, "Scan op in progress - ignoring results");
return 0;
}
@@ -88,34 +88,49 @@ NimBLEScan::~NimBLEScan() {
// If we haven't seen this device before; create a new instance and insert it in the vector.
// Otherwise just update the relevant parameters of the already known device.
- if(advertisedDevice == nullptr){
+ if(advertisedDevice == nullptr && event->disc.event_type != BLE_HCI_ADV_RPT_EVTYPE_SCAN_RSP){
+ // Check if we have reach the scan results limit, ignore this one if so.
+ // We still need to store each device when maxResults is 0 to be able to append the scan results
+ if(pScan->m_maxResults > 0 && pScan->m_maxResults < 0xFF &&
+ (pScan->m_scanResults.m_advertisedDevicesVector.size() >= pScan->m_maxResults))
+ {
+ return 0;
+ }
advertisedDevice = new NimBLEAdvertisedDevice();
advertisedDevice->setAddress(advertisedAddress);
advertisedDevice->setAdvType(event->disc.event_type);
pScan->m_scanResults.m_advertisedDevicesVector.push_back(advertisedDevice);
- NIMBLE_LOGI(LOG_TAG, "NEW DEVICE FOUND: %s", advertisedAddress.toString().c_str());
- }
- else{
- NIMBLE_LOGI(LOG_TAG, "UPDATING PREVIOUSLY FOUND DEVICE: %s", advertisedAddress.toString().c_str());
- }
- advertisedDevice->setRSSI(event->disc.rssi);
- if(event->disc.length_data > 0) {
- advertisedDevice->parseAdvertisement(event->disc.data, event->disc.length_data);
+ NIMBLE_LOGI(LOG_TAG, "New advertiser: %s", advertisedAddress.toString().c_str());
+ } else if(advertisedDevice != nullptr) {
+ NIMBLE_LOGI(LOG_TAG, "Updated advertiser: %s", advertisedAddress.toString().c_str());
+ } else {
+ // Scan response from unknown device
+ return 0;
}
+
advertisedDevice->m_timestamp = time(nullptr);
+ advertisedDevice->setRSSI(event->disc.rssi);
+ advertisedDevice->setPayload(event->disc.data, event->disc.length_data,
+ event->disc.event_type == BLE_HCI_ADV_RPT_EVTYPE_SCAN_RSP);
if (pScan->m_pAdvertisedDeviceCallbacks) {
- if(pScan->m_wantDuplicates || !advertisedDevice->m_callbackSent) {
- // If not active scanning report the result to the listener.
- if(pScan->m_scan_params.passive || event->disc.event_type == BLE_HCI_ADV_TYPE_ADV_NONCONN_IND) {
- advertisedDevice->m_callbackSent = true;
- pScan->m_pAdvertisedDeviceCallbacks->onResult(advertisedDevice);
+ // If not active scanning or scan response is not available
+ // report the result to the callback now.
+ if(pScan->m_scan_params.passive ||
+ (advertisedDevice->getAdvType() != BLE_HCI_ADV_TYPE_ADV_IND &&
+ advertisedDevice->getAdvType() != BLE_HCI_ADV_TYPE_ADV_SCAN_IND))
+ {
+ advertisedDevice->m_callbackSent = true;
+ pScan->m_pAdvertisedDeviceCallbacks->onResult(advertisedDevice);
- // Otherwise wait for the scan response so we can report all of the data at once.
- } else if (event->disc.event_type == BLE_HCI_ADV_RPT_EVTYPE_SCAN_RSP) {
- advertisedDevice->m_callbackSent = true;
- pScan->m_pAdvertisedDeviceCallbacks->onResult(advertisedDevice);
- }
+ // Otherwise, wait for the scan response so we can report the complete data.
+ } else if (event->disc.event_type == BLE_HCI_ADV_RPT_EVTYPE_SCAN_RSP) {
+ advertisedDevice->m_callbackSent = true;
+ pScan->m_pAdvertisedDeviceCallbacks->onResult(advertisedDevice);
+ }
+ // If not storing results and we have invoked the callback, delete the device.
+ if(pScan->m_maxResults == 0 && advertisedDevice->m_callbackSent) {
+ pScan->erase(advertisedAddress);
}
}
@@ -123,7 +138,21 @@ NimBLEScan::~NimBLEScan() {
}
case BLE_GAP_EVENT_DISC_COMPLETE: {
NIMBLE_LOGD(LOG_TAG, "discovery complete; reason=%d",
- event->disc_complete.reason);
+ event->disc_complete.reason);
+
+ // If a device advertised with scan reponse available and it was not received
+ // the callback would not have been invoked, so do it here.
+ if(pScan->m_pAdvertisedDeviceCallbacks) {
+ for(auto &it : pScan->m_scanResults.m_advertisedDevicesVector) {
+ if(!it->m_callbackSent) {
+ pScan->m_pAdvertisedDeviceCallbacks->onResult(it);
+ }
+ }
+ }
+
+ if(pScan->m_maxResults == 0) {
+ pScan->clearResults();
+ }
if (pScan->m_scanCompleteCB != nullptr) {
pScan->m_scanCompleteCB(pScan->m_scanResults);
@@ -145,15 +174,11 @@ NimBLEScan::~NimBLEScan() {
/**
* @brief Should we perform an active or passive scan?
- * The default is a passive scan. An active scan means that we will wish a scan response.
+ * The default is a passive scan. An active scan means that we will request a scan response.
* @param [in] active If true, we perform an active scan otherwise a passive scan.
*/
void NimBLEScan::setActiveScan(bool active) {
- if (active) {
- m_scan_params.passive = 0;
- } else {
- m_scan_params.passive = 1;
- }
+ m_scan_params.passive = !active;
} // setActiveScan
@@ -202,6 +227,16 @@ void NimBLEScan::setFilterPolicy(uint8_t filter) {
} // setFilterPolicy
+/**
+ * @brief Sets the max number of results to store.
+ * @param [in] maxResults The number of results to limit storage to\n
+ * 0 == none (callbacks only) 0xFF == unlimited, any other value is the limit.
+ */
+void NimBLEScan::setMaxResults(uint8_t maxResults) {
+ m_maxResults = maxResults;
+}
+
+
/**
* @brief Set the call backs to be invoked.
* @param [in] pAdvertisedDeviceCallbacks Call backs to be invoked.
@@ -209,7 +244,7 @@ void NimBLEScan::setFilterPolicy(uint8_t filter) {
*/
void NimBLEScan::setAdvertisedDeviceCallbacks(NimBLEAdvertisedDeviceCallbacks* pAdvertisedDeviceCallbacks,
bool wantDuplicates) {
- m_wantDuplicates = wantDuplicates;
+ setDuplicateFilter(!wantDuplicates);
m_pAdvertisedDeviceCallbacks = pAdvertisedDeviceCallbacks;
} // setAdvertisedDeviceCallbacks
@@ -342,10 +377,14 @@ bool NimBLEScan::stop() {
int rc = ble_gap_disc_cancel();
if (rc != 0 && rc != BLE_HS_EALREADY) {
- NIMBLE_LOGE(LOG_TAG, "Failed to cancel scan; rc=%d\n", rc);
+ NIMBLE_LOGE(LOG_TAG, "Failed to cancel scan; rc=%d", rc);
return false;
}
+ if(m_maxResults == 0) {
+ clearResults();
+ }
+
if (rc != BLE_HS_EALREADY && m_scanCompleteCB != nullptr) {
m_scanCompleteCB(m_scanResults);
}
@@ -365,7 +404,7 @@ bool NimBLEScan::stop() {
* @details After disconnecting, it may be required in the case we were connected to a device without a public address.
*/
void NimBLEScan::erase(const NimBLEAddress &address) {
- NIMBLE_LOGI(LOG_TAG, "erase device: %s", address.toString().c_str());
+ NIMBLE_LOGD(LOG_TAG, "erase device: %s", address.toString().c_str());
for(auto it = m_scanResults.m_advertisedDevicesVector.begin(); it != m_scanResults.m_advertisedDevicesVector.end(); ++it) {
if((*it)->getAddress() == address) {
diff --git a/lib/libesp32/NimBLE-Arduino/src/NimBLEScan.h b/lib/libesp32/NimBLE-Arduino/src/NimBLEScan.h
index 9007a7dd8..57e743009 100644
--- a/lib/libesp32/NimBLE-Arduino/src/NimBLEScan.h
+++ b/lib/libesp32/NimBLE-Arduino/src/NimBLEScan.h
@@ -73,6 +73,7 @@ public:
bool stop();
void clearResults();
NimBLEScanResults getResults();
+ void setMaxResults(uint8_t maxResults);
void erase(const NimBLEAddress &address);
@@ -89,10 +90,10 @@ private:
void (*m_scanCompleteCB)(NimBLEScanResults scanResults);
ble_gap_disc_params m_scan_params;
bool m_ignoreResults;
- bool m_wantDuplicates;
NimBLEScanResults m_scanResults;
uint32_t m_duration;
ble_task_data_t *m_pTaskData;
+ uint8_t m_maxResults;
};
#endif // #if defined(CONFIG_BT_NIMBLE_ROLE_OBSERVER)
diff --git a/lib/libesp32/NimBLE-Arduino/src/NimBLEServer.cpp b/lib/libesp32/NimBLE-Arduino/src/NimBLEServer.cpp
index fd5d8266b..423e87967 100644
--- a/lib/libesp32/NimBLE-Arduino/src/NimBLEServer.cpp
+++ b/lib/libesp32/NimBLE-Arduino/src/NimBLEServer.cpp
@@ -104,28 +104,47 @@ NimBLEService* NimBLEServer::createService(const NimBLEUUID &uuid, uint32_t numH
/**
* @brief Get a %BLE Service by its UUID
- * @param [in] uuid The UUID of the new service.
- * @return A reference to the service object.
+ * @param [in] uuid The UUID of the service.
+ * @param instanceId The index of the service to return (used when multiple services have the same UUID).
+ * @return A pointer to the service object or nullptr if not found.
*/
-NimBLEService* NimBLEServer::getServiceByUUID(const char* uuid) {
- return getServiceByUUID(NimBLEUUID(uuid));
+NimBLEService* NimBLEServer::getServiceByUUID(const char* uuid, uint16_t instanceId) {
+ return getServiceByUUID(NimBLEUUID(uuid), instanceId);
} // getServiceByUUID
/**
* @brief Get a %BLE Service by its UUID
- * @param [in] uuid The UUID of the new service.
- * @return A reference to the service object.
+ * @param [in] uuid The UUID of the service.
+ * @param instanceId The index of the service to return (used when multiple services have the same UUID).
+ * @return A pointer to the service object or nullptr if not found.
*/
-NimBLEService* NimBLEServer::getServiceByUUID(const NimBLEUUID &uuid) {
+NimBLEService* NimBLEServer::getServiceByUUID(const NimBLEUUID &uuid, uint16_t instanceId) {
+ uint16_t position = 0;
for (auto &it : m_svcVec) {
if (it->getUUID() == uuid) {
- return it;
+ if (position == instanceId){
+ return it;
+ }
+ position++;
}
}
return nullptr;
} // getServiceByUUID
+/**
+ * @brief Get a %BLE Service by its handle
+ * @param handle The handle of the service.
+ * @return A pointer to the service object or nullptr if not found.
+ */
+NimBLEService *NimBLEServer::getServiceByHandle(uint16_t handle) {
+ for (auto &it : m_svcVec) {
+ if (it->getHandle() == handle) {
+ return it;
+ }
+ }
+ return nullptr;
+}
/**
* @brief Retrieve the advertising object that can be used to advertise the existence of the server.
@@ -646,7 +665,7 @@ void NimBLEServer::updateConnParams(uint16_t conn_handle,
if(rc != 0) {
NIMBLE_LOGE(LOG_TAG, "Update params error: %d, %s", rc, NimBLEUtils::returnCodeToString(rc));
}
-} // updateConnParams
+}// updateConnParams
/** Default callback handlers */
diff --git a/lib/libesp32/NimBLE-Arduino/src/NimBLEServer.h b/lib/libesp32/NimBLE-Arduino/src/NimBLEServer.h
index bedf9cf58..aaaf287d2 100644
--- a/lib/libesp32/NimBLE-Arduino/src/NimBLEServer.h
+++ b/lib/libesp32/NimBLE-Arduino/src/NimBLEServer.h
@@ -49,8 +49,9 @@ public:
void startAdvertising();
void stopAdvertising();
void start();
- NimBLEService* getServiceByUUID(const char* uuid);
- NimBLEService* getServiceByUUID(const NimBLEUUID &uuid);
+ NimBLEService* getServiceByUUID(const char* uuid, uint16_t instanceId = 0);
+ NimBLEService* getServiceByUUID(const NimBLEUUID &uuid, uint16_t instanceId = 0);
+ NimBLEService* getServiceByHandle(uint16_t handle);
int disconnect(uint16_t connID,
uint8_t reason = BLE_ERR_REM_USER_CONN_TERM);
void updateConnParams(uint16_t conn_handle,
diff --git a/lib/libesp32/NimBLE-Arduino/src/NimBLEService.cpp b/lib/libesp32/NimBLE-Arduino/src/NimBLEService.cpp
index 3420da2c7..a830eec95 100644
--- a/lib/libesp32/NimBLE-Arduino/src/NimBLEService.cpp
+++ b/lib/libesp32/NimBLE-Arduino/src/NimBLEService.cpp
@@ -233,9 +233,9 @@ NimBLECharacteristic* NimBLEService::createCharacteristic(const char* uuid, uint
*/
NimBLECharacteristic* NimBLEService::createCharacteristic(const NimBLEUUID &uuid, uint32_t properties) {
NimBLECharacteristic* pCharacteristic = new NimBLECharacteristic(uuid, properties, this);
- // Check that we don't add the same characteristic twice.
+
if (getCharacteristic(uuid) != nullptr) {
- NIMBLE_LOGW(LOG_TAG, "<< Adding a duplicate characteristic with UUID: %s",
+ NIMBLE_LOGD(LOG_TAG, "<< Adding a duplicate characteristic with UUID: %s",
std::string(uuid).c_str());
}
@@ -249,28 +249,72 @@ NimBLECharacteristic* NimBLEService::createCharacteristic(const NimBLEUUID &uuid
/**
* @brief Get a pointer to the characteristic object with the specified UUID.
* @param [in] uuid The UUID of the characteristic.
+ * @param instanceId The index of the characteristic to return (used when multiple characteristics have the same UUID).
* @return A pointer to the characteristic object or nullptr if not found.
*/
-NimBLECharacteristic* NimBLEService::getCharacteristic(const char* uuid) {
- return getCharacteristic(NimBLEUUID(uuid));
+NimBLECharacteristic* NimBLEService::getCharacteristic(const char* uuid, uint16_t instanceId) {
+ return getCharacteristic(NimBLEUUID(uuid), instanceId);
}
-
/**
* @brief Get a pointer to the characteristic object with the specified UUID.
* @param [in] uuid The UUID of the characteristic.
+ * @param instanceId The index of the characteristic to return (used when multiple characteristics have the same UUID).
* @return A pointer to the characteristic object or nullptr if not found.
*/
-NimBLECharacteristic* NimBLEService::getCharacteristic(const NimBLEUUID &uuid) {
+NimBLECharacteristic* NimBLEService::getCharacteristic(const NimBLEUUID &uuid, uint16_t instanceId) {
+ uint16_t position = 0;
for (auto &it : m_chrVec) {
if (it->getUUID() == uuid) {
- return it;
+ if (position == instanceId) {
+ return it;
+ }
+ position++;
}
}
-
return nullptr;
}
+/**
+ * @brief Get a pointer to the characteristic object with the specified handle.
+ * @param handle The handle of the characteristic.
+ * @return A pointer to the characteristic object or nullptr if not found.
+ */
+NimBLECharacteristic *NimBLEService::getCharacteristicByHandle(uint16_t handle) {
+ for (auto &it : m_chrVec) {
+ if (it->getHandle() == handle) {
+ return it;
+ }
+ }
+ return nullptr;
+}
+
+/**
+ * @return A vector containing pointers to each characteristic associated with this service.
+ */
+std::vector NimBLEService::getCharacteristics() {
+ return m_chrVec;
+}
+
+/**
+ * @return A vector containing pointers to each characteristic with the provided UUID associated with this service.
+ */
+std::vector NimBLEService::getCharacteristics(const char *uuid) {
+ return getCharacteristics(NimBLEUUID(uuid));
+}
+
+/**
+ * @return A vector containing pointers to each characteristic with the provided UUID associated with this service.
+ */
+std::vector NimBLEService::getCharacteristics(const NimBLEUUID &uuid) {
+ std::vector result;
+ for (auto &it : m_chrVec) {
+ if (it->getUUID() == uuid) {
+ result.push_back(it);
+ }
+ }
+ return result;
+}
/**
* @brief Return a string representation of this service.
@@ -295,7 +339,7 @@ std::string NimBLEService::toString() {
*/
NimBLEServer* NimBLEService::getServer() {
return m_pServer;
-} // getServer
+}// getServer
#endif // #if defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL)
#endif // CONFIG_BT_ENABLED
diff --git a/lib/libesp32/NimBLE-Arduino/src/NimBLEService.h b/lib/libesp32/NimBLE-Arduino/src/NimBLEService.h
index 191d97fa5..1203d3e3b 100644
--- a/lib/libesp32/NimBLE-Arduino/src/NimBLEService.h
+++ b/lib/libesp32/NimBLE-Arduino/src/NimBLEService.h
@@ -35,6 +35,16 @@ class NimBLECharacteristic;
*/
class NimBLEService {
public:
+
+ NimBLEServer* getServer();
+
+ NimBLEUUID getUUID();
+ uint16_t getHandle();
+ std::string toString();
+ void dump();
+
+ bool start();
+
NimBLECharacteristic* createCharacteristic(const char* uuid,
uint32_t properties =
NIMBLE_PROPERTY::READ |
@@ -45,14 +55,14 @@ public:
NIMBLE_PROPERTY::READ |
NIMBLE_PROPERTY::WRITE);
- void dump();
- NimBLECharacteristic* getCharacteristic(const char* uuid);
- NimBLECharacteristic* getCharacteristic(const NimBLEUUID &uuid);
- NimBLEUUID getUUID();
- NimBLEServer* getServer();
- bool start();
- std::string toString();
- uint16_t getHandle();
+ NimBLECharacteristic* getCharacteristic(const char* uuid, uint16_t instanceId = 0);
+ NimBLECharacteristic* getCharacteristic(const NimBLEUUID &uuid, uint16_t instanceId = 0);
+ NimBLECharacteristic* getCharacteristicByHandle(uint16_t handle);
+
+ std::vector getCharacteristics();
+ std::vector getCharacteristics(const char* uuid);
+ std::vector getCharacteristics(const NimBLEUUID &uuid);
+
private:
NimBLEService(const char* uuid, uint16_t numHandles, NimBLEServer* pServer);
diff --git a/lib/libesp32/NimBLE-Arduino/src/NimBLEUUID.cpp b/lib/libesp32/NimBLE-Arduino/src/NimBLEUUID.cpp
index 21ff27047..ea58ae7b2 100644
--- a/lib/libesp32/NimBLE-Arduino/src/NimBLEUUID.cpp
+++ b/lib/libesp32/NimBLE-Arduino/src/NimBLEUUID.cpp
@@ -65,29 +65,42 @@ static const char* LOG_TAG = "NimBLEUUID";
*this = NimBLEUUID(first, second, third, (uint64_t(fourth) << 48) + fifth);
}
else {
- NIMBLE_LOGE(LOG_TAG,"ERROR: UUID value not 2, 4, 16 or 36 bytes");
m_valueSet = false;
}
} // NimBLEUUID(std::string)
/**
- * @brief Create a UUID from 16 bytes of memory.
+ * @brief Create a UUID from 2, 4, 16 bytes of memory.
* @param [in] pData The pointer to the start of the UUID.
* @param [in] size The size of the data.
* @param [in] msbFirst Is the MSB first in pData memory?
*/
NimBLEUUID::NimBLEUUID(const uint8_t* pData, size_t size, bool msbFirst) {
- if (size != 16) {
- NIMBLE_LOGE(LOG_TAG,"ERROR: UUID length not 16 bytes");
- return;
- }
- m_uuid.u.type = BLE_UUID_TYPE_128;
+ uint8_t *uuidValue = nullptr;
+ switch(size) {
+ case 2:
+ uuidValue = (uint8_t*)&m_uuid.u16.value;
+ m_uuid.u.type = BLE_UUID_TYPE_16;
+ break;
+ case 4:
+ uuidValue = (uint8_t*)&m_uuid.u32.value;
+ m_uuid.u.type = BLE_UUID_TYPE_32;
+ break;
+ case 16:
+ uuidValue = m_uuid.u128.value;
+ m_uuid.u.type = BLE_UUID_TYPE_128;
+ break;
+ default:
+ m_valueSet = false;
+ NIMBLE_LOGE(LOG_TAG, "Invalid UUID size");
+ return;
+ }
if (msbFirst) {
- std::reverse_copy(pData, pData + 16, m_uuid.u128.value);
+ std::reverse_copy(pData, pData + size, uuidValue);
} else {
- memcpy(m_uuid.u128.value, pData, 16);
+ memcpy(uuidValue, pData, size);
}
m_valueSet = true;
} // NimBLEUUID
diff --git a/lib/libesp32/NimBLE-Arduino/src/esp-hci/src/esp_nimble_hci.c b/lib/libesp32/NimBLE-Arduino/src/esp-hci/src/esp_nimble_hci.c
index 98c133fa5..9af13c355 100644
--- a/lib/libesp32/NimBLE-Arduino/src/esp-hci/src/esp_nimble_hci.c
+++ b/lib/libesp32/NimBLE-Arduino/src/esp-hci/src/esp_nimble_hci.c
@@ -523,10 +523,6 @@ esp_err_t esp_nimble_hci_and_controller_init(void)
esp_bt_controller_mem_release(ESP_BT_MODE_CLASSIC_BT);
esp_bt_controller_config_t bt_cfg = BT_CONTROLLER_INIT_CONFIG_DEFAULT();
- /* Added to ensure BLE only mode */
- bt_cfg.mode = ESP_BT_MODE_BLE;
- /* Added to set max connections from nimconfig */
- bt_cfg.ble_max_conn = CONFIG_BT_NIMBLE_MAX_CONNECTIONS;
if ((ret = esp_bt_controller_init(&bt_cfg)) != ESP_OK) {
return ret;
diff --git a/lib/libesp32/NimBLE-Arduino/src/nimconfig.h b/lib/libesp32/NimBLE-Arduino/src/nimconfig.h
index d90921fa1..5ad95ddb9 100644
--- a/lib/libesp32/NimBLE-Arduino/src/nimconfig.h
+++ b/lib/libesp32/NimBLE-Arduino/src/nimconfig.h
@@ -1,145 +1,156 @@
-/** @file
- *
- * IGNORE THIS FILE IF USING ESP-IDF, USE MENUCONFIG TO SET NIMBLE OPTIONS.
- *
- * The config options here are for Arduino use only.
- */
-
-#pragma once
+ #pragma once
#include "sdkconfig.h"
-
-/*
- * For ESP-IDF compatibility
- * Some versions of ESP-IDF used the config name format "CONFIG_NIMBLE_".
- * This converts them to "CONFIG_BT_NIMBLE_" format used in the latest IDF.
- */
-
-/* Detect if using ESP-IDF or Arduino (Arduino won't have these defines in sdkconfig)
- *
- * Note: We do not use #ifdef CONFIG_BT_NIMBLE_ENABLED since we cannot enable NimBLE when using
- * Arduino as a component and the esp-nimble-compnent, so we check if other config options are defined.
- * We also need to use a config parameter that must be present and not likely defined in the command line.
- */
-#if defined(CONFIG_BT_NIMBLE_GAP_DEVICE_NAME_MAX_LEN) || defined(CONFIG_NIMBLE_GAP_DEVICE_NAME_MAX_LEN)
-
-#if defined(CONFIG_NIMBLE_ENABLED) && !defined(CONFIG_BT_NIMBLE_ENABLED)
-#define CONFIG_BT_NIMBLE_ENABLED
-#endif
-
-#if defined(CONFIG_NIMBLE_ROLE_OBSERVER) && !defined(CONFIG_BT_NIMBLE_ROLE_OBSERVER)
-#define CONFIG_BT_NIMBLE_ROLE_OBSERVER
-#endif
-
-#if defined(CONFIG_NIMBLE_ROLE_BROADCASTER) && !defined(CONFIG_BT_NIMBLE_ROLE_BROADCASTER)
-#define CONFIG_BT_NIMBLE_ROLE_BROADCASTER
-#endif
-
-#if defined(CONFIG_NIMBLE_ROLE_CENTRAL) && !defined(CONFIG_BT_NIMBLE_ROLE_CENTRAL)
-#define CONFIG_BT_NIMBLE_ROLE_CENTRAL
-#endif
-
-#if defined(CONFIG_NIMBLE_ROLE_PERIPHERAL) && !defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL)
-#define CONFIG_BT_NIMBLE_ROLE_PERIPHERAL
-#endif
-
-#if defined(CONFIG_NIMBLE_DEBUG) && !defined(CONFIG_BT_NIMBLE_DEBUG)
-#define CONFIG_BT_NIMBLE_DEBUG
-#endif
-
-#else // Using Arduino
+#include "nimconfig_rename.h"
/***********************************************
- * Arduino config options start here
+ * Arduino user-config options start here
**********************************************/
-/** @brief Comment out if not using NimBLE Client functions \n
- * Reduces flash size by approx. 7kB.
- */
-#ifndef CONFIG_BT_NIMBLE_ROLE_CENTRAL_DISABLED
-#define CONFIG_BT_NIMBLE_ROLE_CENTRAL
-#endif
+/** @brief Un-comment to change the number of simultaneous connections (esp controller max is 9) */
+// #define CONFIG_BT_NIMBLE_MAX_CONNECTIONS 3
-/** @brief Comment out if not using NimBLE Scan functions \n
- * Reduces flash size by approx. 26kB.
- */
-#ifndef CONFIG_BT_NIMBLE_ROLE_OBSERVER_DISABLED
-#define CONFIG_BT_NIMBLE_ROLE_OBSERVER
-#endif
+/** @brief Un-comment to change the default MTU size */
+// #define CONFIG_BT_NIMBLE_ATT_PREFERRED_MTU 255
-/** @brief Comment out if not using NimBLE Server functions \n
- * Reduces flash size by approx. 16kB.
- */
-#ifndef CONFIG_BT_NIMBLE_ROLE_PERIPHERAL_DISABLED
-#define CONFIG_BT_NIMBLE_ROLE_PERIPHERAL
-#endif
+/** @brief Un-comment to change default device name */
+// #define CONFIG_BT_NIMBLE_SVC_GAP_DEVICE_NAME "nimble"
-/** @brief Comment out if not using NimBLE Advertising functions \n
- * Reduces flash size by approx. 5kB.
- */
-#ifndef CONFIG_BT_NIMBLE_ROLE_BROADCASTER_DISABLED
-#define CONFIG_BT_NIMBLE_ROLE_BROADCASTER
-#endif
-
-/* Uncomment to see debug log messages from the NimBLE host
+/** @brief Un-comment to see debug log messages from the NimBLE host
* Uses approx. 32kB of flash memory.
*/
// #define CONFIG_BT_NIMBLE_DEBUG
-/* Uncomment to see NimBLE host return codes as text debug log messages.
+/** @brief Un-comment to see NimBLE host return codes as text debug log messages.
* Uses approx. 7kB of flash memory.
*/
// #define CONFIG_NIMBLE_CPP_ENABLE_RETURN_CODE_TEXT
-/* Uncomment to see GAP event codes as text in debug log messages.
+/** @brief Un-comment to see GAP event codes as text in debug log messages.
* Uses approx. 1kB of flash memory.
*/
// #define CONFIG_NIMBLE_CPP_ENABLE_GAP_EVENT_CODE_TEXT
-/* Uncomment to see advertisment types as text while scanning in debug log messages.
+/** @brief Un-comment to see advertisment types as text while scanning in debug log messages.
* Uses approx. 250 bytes of flash memory.
*/
// #define CONFIG_NIMBLE_CPP_ENABLE_ADVERTISMENT_TYPE_TEXT
-/** @brief Sets the core NimBLE host runs on */
+/** @brief Un-comment to change the default GAP appearance */
+// #define CONFIG_BT_NIMBLE_SVC_GAP_APPEARANCE 0x0
+
+ /** @brief Un-comment if not using NimBLE Client functions \n
+ * Reduces flash size by approx. 7kB.
+ */
+// #define CONFIG_BT_NIMBLE_ROLE_CENTRAL_DISABLED
+
+/** @brief Un-comment if not using NimBLE Scan functions \n
+ * Reduces flash size by approx. 26kB.
+ */
+// #define CONFIG_BT_NIMBLE_ROLE_OBSERVER_DISABLED
+
+/** @brief Un-comment if not using NimBLE Server functions \n
+ * Reduces flash size by approx. 16kB.
+ */
+// #define CONFIG_BT_NIMBLE_ROLE_PERIPHERAL_DISABLED
+
+/** @brief Un-comment if not using NimBLE Advertising functions \n
+ * Reduces flash size by approx. 5kB.
+ */
+// #define CONFIG_BT_NIMBLE_ROLE_BROADCASTER_DISABLED
+
+/** @brief Un-comment to change the number of devices allowed to store/bond with */
+// #define CONFIG_BT_NIMBLE_MAX_BONDS 3
+
+/** @brief Un-comment to change the maximum number of CCCD subscriptions to store */
+// #define CONFIG_BT_NIMBLE_MAX_CCCDS 8
+
+/** @brief Un-comment to change the random address refresh time (in seconds) */
+// #define CONFIG_BT_NIMBLE_RPA_TIMEOUT 900
+
+/**
+ * @brief Un-comment to change the number of MSYS buffers available.
+ * @details MSYS is a system level mbuf registry. For prepare write & prepare \n
+ * responses MBUFs are allocated out of msys_1 pool. This may need to be increased if\n
+ * you are sending large blocks of data with a low MTU. E.g: 512 bytes with 23 MTU will fail.
+ */
+// #define CONFIG_BT_NIMBLE_MSYS1_BLOCK_COUNT 12
+
+/** @brief Un-comment to use external PSRAM for the NimBLE host */
+// #define CONFIG_BT_NIMBLE_MEM_ALLOC_MODE_EXTERNAL 1
+
+/** @brief Un-comment to change the core NimBLE host runs on */
+// #define CONFIG_BT_NIMBLE_PINNED_TO_CORE 0
+
+/** @brief Un-comment to change the stack size for the NimBLE host task */
+// #define CONFIG_BT_NIMBLE_TASK_STACK_SIZE 4096
+
+/**********************************
+ End Arduino user-config
+**********************************/
+
+/* This section should not be altered */
+#ifndef CONFIG_BT_NIMBLE_ROLE_CENTRAL_DISABLED
+#define CONFIG_BT_NIMBLE_ROLE_CENTRAL
+#endif
+
+#ifndef CONFIG_BT_NIMBLE_ROLE_OBSERVER_DISABLED
+#define CONFIG_BT_NIMBLE_ROLE_OBSERVER
+#endif
+
+#ifndef CONFIG_BT_NIMBLE_ROLE_PERIPHERAL_DISABLED
+#define CONFIG_BT_NIMBLE_ROLE_PERIPHERAL
+#endif
+
+#ifndef CONFIG_BT_NIMBLE_ROLE_BROADCASTER_DISABLED
+#define CONFIG_BT_NIMBLE_ROLE_BROADCASTER
+#endif
+
#ifndef CONFIG_BT_NIMBLE_PINNED_TO_CORE
#define CONFIG_BT_NIMBLE_PINNED_TO_CORE 0
#endif
-/** @brief Sets the stack size for the NimBLE host task */
#ifndef CONFIG_BT_NIMBLE_TASK_STACK_SIZE
#define CONFIG_BT_NIMBLE_TASK_STACK_SIZE 4096
#endif
-/**
- * @brief Sets the memory pool where NimBLE will be loaded
- * @details By default NimBLE is loaded in internal ram.\n
- * To use external PSRAM you must change this to `#define CONFIG_BT_NIMBLE_MEM_ALLOC_MODE_EXTERNAL 1`
- */
#ifndef CONFIG_BT_NIMBLE_MEM_ALLOC_MODE_EXTERNAL
#define CONFIG_BT_NIMBLE_MEM_ALLOC_MODE_INTERNAL 1
#endif
-/** @brief Sets the number of simultaneous connections (esp controller max is 9) */
#ifndef CONFIG_BT_NIMBLE_MAX_CONNECTIONS
#define CONFIG_BT_NIMBLE_MAX_CONNECTIONS 3
#endif
-/** @brief Sets the number of devices allowed to store/bond with */
#ifndef CONFIG_BT_NIMBLE_MAX_BONDS
#define CONFIG_BT_NIMBLE_MAX_BONDS 3
#endif
-/** @brief Sets the maximum number of CCCD subscriptions to store */
#ifndef CONFIG_BT_NIMBLE_MAX_CCCDS
#define CONFIG_BT_NIMBLE_MAX_CCCDS 8
#endif
-/** @brief Default device name */
#ifndef CONFIG_BT_NIMBLE_SVC_GAP_DEVICE_NAME
#define CONFIG_BT_NIMBLE_SVC_GAP_DEVICE_NAME "nimble"
#endif
+#ifndef CONFIG_BT_NIMBLE_ATT_PREFERRED_MTU
+#define CONFIG_BT_NIMBLE_ATT_PREFERRED_MTU 255
+#endif
+
+#ifndef CONFIG_BT_NIMBLE_SVC_GAP_APPEARANCE
+#define CONFIG_BT_NIMBLE_SVC_GAP_APPEARANCE 0x0
+#endif
+
+#ifndef CONFIG_BT_NIMBLE_MSYS1_BLOCK_COUNT
+#define CONFIG_BT_NIMBLE_MSYS1_BLOCK_COUNT 12
+#endif
+
+#ifndef CONFIG_BT_NIMBLE_RPA_TIMEOUT
+#define CONFIG_BT_NIMBLE_RPA_TIMEOUT 900
+#endif
+
+
/** @brief Set if CCCD's and bond data should be stored in NVS */
#define CONFIG_BT_NIMBLE_NVS_PERSIST 1
@@ -152,12 +163,6 @@
/** @brief Max device name length (bytes) */
#define CONFIG_BT_NIMBLE_GAP_DEVICE_NAME_MAX_LEN 31
-/** @brief Default MTU size */
-#define CONFIG_BT_NIMBLE_ATT_PREFERRED_MTU 256
-
-/** @brief Default GAP appearance */
-#define CONFIG_BT_NIMBLE_SVC_GAP_APPEARANCE 0x0
-
/** @brief ACL Buffer count */
#define CONFIG_BT_NIMBLE_ACL_BUF_COUNT 12
@@ -173,21 +178,9 @@
/** @brief Number of low priority HCI event buffers */
#define CONFIG_BT_NIMBLE_HCI_EVT_LO_BUF_COUNT 8
-/**
- * @brief Sets the number of MSYS buffers available.
- * @details MSYS is a system level mbuf registry. For prepare write & prepare \n
- * responses MBUFs are allocated out of msys_1 pool. This may need to be increased if\n
- * you are sending large blocks of data with a low MTU. E.g: 512 bytes with 23 MTU will fail.
- */
-#define CONFIG_BT_NIMBLE_MSYS1_BLOCK_COUNT 12
-
-/** @brief Random address refresh time in seconds */
-#define CONFIG_BT_NIMBLE_RPA_TIMEOUT 900
-
/** @brief Maximum number of connection oriented channels */
#define CONFIG_BT_NIMBLE_L2CAP_COC_MAX_NUM 0
-/* These should not be altered */
#define CONFIG_BT_NIMBLE_HS_FLOW_CTRL 1
#define CONFIG_BT_NIMBLE_HS_FLOW_CTRL_ITVL 1000
#define CONFIG_BT_NIMBLE_HS_FLOW_CTRL_THRESH 2
@@ -197,14 +190,9 @@
#define CONFIG_BT_ENABLED
#endif
+#ifndef CONFIG_BTDM_CONTROLLER_MODE_BLE_ONLY
#define CONFIG_BTDM_CONTROLLER_MODE_BLE_ONLY
-
-#endif // #if defined(CONFIG_BT_NIMBLE_TASK_STACK_SIZE) || defined(CONFIG_NIMBLE_TASK_STACK_SIZE)
-
-/**********************************
- End Arduino config
-**********************************/
-
+#endif
/* Cannot use client without scan */
#if defined(CONFIG_BT_NIMBLE_ROLE_CENTRAL) && !defined(CONFIG_BT_NIMBLE_ROLE_OBSERVER)
@@ -216,26 +204,3 @@
#define CONFIG_BT_NIMBLE_ROLE_BROADCASTER
#endif
-
-
-#ifdef _DOXYGEN_
-/** @brief Uncomment to see debug log messages from the NimBLE host \n
- * Uses approx. 32kB of flash memory.
- */
-#define CONFIG_BT_NIMBLE_DEBUG
-
-/** @brief Uncomment to see NimBLE host return codes as text debug log messages. \n
- * Uses approx. 7kB of flash memory.
- */
-#define CONFIG_NIMBLE_CPP_ENABLE_RETURN_CODE_TEXT
-
-/** @brief Uncomment to see GAP event codes as text in debug log messages. \n
- * Uses approx. 1kB of flash memory.
- */
-#define CONFIG_NIMBLE_CPP_ENABLE_GAP_EVENT_CODE_TEXT
-
-/** @brief Uncomment to see advertisment types as text while scanning in debug log messages. \n
- * Uses approx. 250 bytes of flash memory.
- */
-#define CONFIG_NIMBLE_CPP_ENABLE_ADVERTISMENT_TYPE_TEXT
-#endif // _DOXYGEN_
diff --git a/lib/libesp32/NimBLE-Arduino/src/nimconfig_rename.h b/lib/libesp32/NimBLE-Arduino/src/nimconfig_rename.h
new file mode 100644
index 000000000..3b96c1fec
--- /dev/null
+++ b/lib/libesp32/NimBLE-Arduino/src/nimconfig_rename.h
@@ -0,0 +1,53 @@
+/*
+ * For ESP-IDF compatibility
+ * Some versions of ESP-IDF used the config name format "CONFIG_NIMBLE_".
+ * This converts them to "CONFIG_BT_NIMBLE_" format used in the latest IDF.
+ */
+
+#if defined(CONFIG_NIMBLE_ENABLED) && !defined(CONFIG_BT_NIMBLE_ENABLED)
+#define CONFIG_BT_NIMBLE_ENABLED
+#endif
+
+#if defined(CONFIG_NIMBLE_ROLE_OBSERVER) && !defined(CONFIG_BT_NIMBLE_ROLE_OBSERVER)
+#define CONFIG_BT_NIMBLE_ROLE_OBSERVER
+#endif
+
+#if defined(CONFIG_NIMBLE_ROLE_BROADCASTER) && !defined(CONFIG_BT_NIMBLE_ROLE_BROADCASTER)
+#define CONFIG_BT_NIMBLE_ROLE_BROADCASTER
+#endif
+
+#if defined(CONFIG_NIMBLE_ROLE_CENTRAL) && !defined(CONFIG_BT_NIMBLE_ROLE_CENTRAL)
+#define CONFIG_BT_NIMBLE_ROLE_CENTRAL
+#endif
+
+#if defined(CONFIG_NIMBLE_ROLE_PERIPHERAL) && !defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL)
+#define CONFIG_BT_NIMBLE_ROLE_PERIPHERAL
+#endif
+
+#if defined(CONFIG_NIMBLE_DEBUG) && !defined(CONFIG_BT_NIMBLE_DEBUG)
+#define CONFIG_BT_NIMBLE_DEBUG
+#endif
+
+#if defined(CONFIG_SCAN_DUPLICATE_BY_DEVICE_ADDR) && !defined(CONFIG_BTDM_SCAN_DUPL_TYPE_DEVICE)
+#define CONFIG_BTDM_SCAN_DUPL_TYPE_DEVICE CONFIG_SCAN_DUPLICATE_BY_DEVICE_ADDR
+#endif
+
+#if defined(CONFIG_SCAN_DUPLICATE_BY_ADV_DATA ) && !defined(CONFIG_BTDM_SCAN_DUPL_TYPE_DATA)
+#define CONFIG_BTDM_SCAN_DUPL_TYPE_DATA CONFIG_SCAN_DUPLICATE_BY_ADV_DATA
+#endif
+
+#if defined(CONFIG_SCAN_DUPLICATE_BY_ADV_DATA_AND_DEVICE_ADDR) && !defined(CONFIG_BTDM_SCAN_DUPL_TYPE_DATA_DEVICE)
+#define CONFIG_BTDM_SCAN_DUPL_TYPE_DATA_DEVICE CONFIG_SCAN_DUPLICATE_BY_ADV_DATA_AND_DEVICE_ADDR
+#endif
+
+#if defined(CONFIG_SCAN_DUPLICATE_TYPE) && !defined(CONFIG_BTDM_SCAN_DUPL_TYPE)
+#define CONFIG_BTDM_SCAN_DUPL_TYPE CONFIG_SCAN_DUPLICATE_TYPE
+#endif
+
+#if defined(CONFIG_DUPLICATE_SCAN_CACHE_SIZE) && !defined(CONFIG_BTDM_SCAN_DUPL_CACHE_SIZE)
+#define CONFIG_BTDM_SCAN_DUPL_CACHE_SIZE CONFIG_DUPLICATE_SCAN_CACHE_SIZE
+#endif
+
+#if defined(CONFIG_NIMBLE_MAX_CONNECTIONS ) && !defined(CONFIG_BT_NIMBLE_MAX_CONNECTIONS)
+#define CONFIG_BT_NIMBLE_MAX_CONNECTIONS CONFIG_NIMBLE_MAX_CONNECTIONS
+#endif
diff --git a/platformio_override_sample.ini b/platformio_override_sample.ini
index 60c1ee7cd..0209e7091 100644
--- a/platformio_override_sample.ini
+++ b/platformio_override_sample.ini
@@ -124,12 +124,12 @@ lib_extra_dirs = ${library.lib_extra_dirs}
[core32]
; Activate Stage Core32 by removing ";" in next 3 lines, if you want to override the standard core32
-;platform_packages = ${core32_stage.platform_packages}
-;build_unflags = ${core32_stage.build_unflags}
-;build_flags = ${core32_stage.build_flags}
+platform_packages = ${core32_stage.platform_packages}
+build_unflags = ${core32_stage.build_unflags}
+build_flags = ${core32_stage.build_flags}
[core32_stage]
-platform_packages = framework-arduinoespressif32 @ https://github.com/Jason2866/arduino-esp32/releases/download/1.0.5-rc6/esp32-1.0.5-rc6.zip
+platform_packages = framework-arduinoespressif32 @ https://github.com/Jason2866/arduino-esp32/releases/download/1.0.5-rc7/esp32-1.0.5-rc7.zip
platformio/tool-mklittlefs @ ~1.203.200522
build_unflags = ${esp32_defaults.build_unflags}
build_flags = ${esp32_defaults.build_flags}
diff --git a/tasmota/berry/tasmota.be b/tasmota/berry/tasmota.be
index c5629a5e6..8129b0a6e 100644
--- a/tasmota/berry/tasmota.be
+++ b/tasmota/berry/tasmota.be
@@ -1,5 +1,16 @@
import json import string
tasmota = module("tasmota")
+def log(m) print(m) end
+def save() end
+
+#######
+import string
+import json
+import gc
+import tasmota
+#// import alias
+import tasmota as t
+
def charsinstring(s,c)
for i:0..size(s)-1
for j:0..size(c)-1
@@ -32,17 +43,17 @@ tasmota._operators="=<>!|"
# split the item when there is an operator, returns a list of (left,op,right)
# ex: "Dimmer>50" -> ["Dimmer",tasmota_gt,"50"]
tasmota.find_op = def (item)
- pos = charsinstring(item, tasmota._operators)
+ var pos = charsinstring(item, tasmota._operators)
if pos>=0
- op_split = string.split(item,pos)
+ var op_split = string.split(item,pos)
#print(op_split)
- op_left = op_split[0]
- op_rest = op_split[1]
+ var op_left = op_split[0]
+ var op_rest = op_split[1]
# iterate through operators
for op:tasmota._op
if string.find(op_rest,op[0]) == 0
- op_func = op[1]
- op_right = string.split(op_rest,size(op[0]))[1]
+ var op_func = op[1]
+ var op_right = string.split(op_rest,size(op[0]))[1]
return [op_left,op_func,op_right]
end
end
@@ -52,7 +63,7 @@ end
def findkeyi(m,keyi)
- keyu=string.toupper(keyi)
+ var keyu = string.toupper(keyi)
if classof(m) == map
for k:m.keys()
if string.toupper(k)==keyu || keyi=='?'
@@ -64,9 +75,9 @@ end
tasmota.try_rule = def (ev, rule, f)
- rl_list = tasmota.find_op(rule)
- e=ev
- rl=string.split(rl_list[0],'#')
+ var rl_list = tasmota.find_op(rule)
+ var e=ev
+ var rl=string.split(rl_list[0],'#')
for it:rl
found=findkeyi(e,it)
if found == nil
@@ -89,11 +100,11 @@ tasmota_rules={}
tasmota.rule = def(pat,f) tasmota_rules[pat] = f end
tasmota.exec_rules = def (ev_json)
- ev = json.load(ev_json)
+ var ev = json.load(ev_json)
+ var ret = false
if ev == nil
- log("ERROR, bad json: "+ev_json, 3)
+ log('BRY: ERROR, bad json: '+ev_json, 3)
end
- ret = false
for r:tasmota_rules.keys()
ret = tasmota.try_rule(ev,r,tasmota_rules[r]) || ret
end
@@ -107,6 +118,15 @@ tasmota.delay = def(ms)
end
end
+def load(f)
+ try
+ if f[0] != '/' f = '/' + f end
+ compile(f,'file')()
+ except .. as e
+ log(string.format("BRY: could not load file '%s' - %s",f,e))
+ end
+end
+
#- Test
#################################################################
diff --git a/tasmota/my_user_config.h b/tasmota/my_user_config.h
index 4fd5cc6b4..730ffd923 100644
--- a/tasmota/my_user_config.h
+++ b/tasmota/my_user_config.h
@@ -641,6 +641,9 @@
#endif // USE_SPI
+//#define USE_DISPLAY // Add Display support
+// #define USE_DISPLAY_TM1637 // [DisplayModel 15] Enable TM1637 module
+
// -- Serial sensors ------------------------------
//#define USE_MHZ19 // Add support for MH-Z19 CO2 sensor (+2k code)
//#define USE_SENSEAIR // Add support for SenseAir K30, K70 and S8 CO2 sensor (+2k3 code)
diff --git a/tasmota/support_command.ino b/tasmota/support_command.ino
index 065fabbcc..f75a5cd89 100644
--- a/tasmota/support_command.ino
+++ b/tasmota/support_command.ino
@@ -108,6 +108,10 @@ void ResponseCmndError(void) {
ResponseCmndChar_P(PSTR(D_JSON_ERROR));
}
+void ResponseCmndFailed(void) {
+ ResponseCmndChar_P(PSTR(D_JSON_FAILED));
+}
+
void ResponseCmndIdxChar(const char* value) {
Response_P(S_JSON_COMMAND_INDEX_SVALUE, XdrvMailbox.command, XdrvMailbox.index, EscapeJSONString(value).c_str());
}
diff --git a/tasmota/support_device_groups.ino b/tasmota/support_device_groups.ino
index 1bc6ba956..44e0c5106 100644
--- a/tasmota/support_device_groups.ino
+++ b/tasmota/support_device_groups.ino
@@ -480,7 +480,7 @@ cleanup:
}
}
-bool _SendDeviceGroupMessage(uint32_t device, DevGroupMessageType message_type, ...)
+bool _SendDeviceGroupMessage(int32_t device, DevGroupMessageType message_type, ...)
{
// If device groups is not up, ignore this request.
if (!device_groups_up) return 1;
diff --git a/tasmota/support_features.ino b/tasmota/support_features.ino
index 8e1766439..45ec475fc 100644
--- a/tasmota/support_features.ino
+++ b/tasmota/support_features.ino
@@ -715,7 +715,9 @@ void ResponseAppendFeatures(void)
#if defined(USE_ENERGY_SENSOR) && defined(USE_SDM72)
feature7 |= 0x20000000; // xnrg_18_sdm72.ino
#endif
-// feature7 |= 0x40000000;
+#if defined(USE_DISPLAY) && defined(USE_DISPLAY_TM1637)
+ feature7 |= 0x40000000;
+#endif
// feature7 |= 0x80000000;
}
diff --git a/tasmota/support_tasmota.ino b/tasmota/support_tasmota.ino
index 1d6cab43b..af9aca1e2 100644
--- a/tasmota/support_tasmota.ino
+++ b/tasmota/support_tasmota.ino
@@ -964,7 +964,7 @@ void Every100mSeconds(void)
bool CommandsReady(void) {
bool ready = BACKLOG_EMPTY ;
#ifdef USE_UFILESYS
- ready |= FileRunReady();
+ ready |= UfsExecuteCommandFileReady();
#endif // USE_UFILESYS
return ready;
}
diff --git a/tasmota/tasmota.ino b/tasmota/tasmota.ino
index 4ebe0b647..c0b7b4669 100644
--- a/tasmota/tasmota.ino
+++ b/tasmota/tasmota.ino
@@ -426,9 +426,6 @@ void Scheduler(void) {
DeviceGroupsLoop();
#endif // USE_DEVICE_GROUPS
BacklogLoop();
-#ifdef USE_UFILESYS
- FileRunLoop();
-#endif // USE_UFILESYS
static uint32_t state_50msecond = 0; // State 50msecond timer
if (TimeReached(state_50msecond)) {
diff --git a/tasmota/tasmota_configurations.h b/tasmota/tasmota_configurations.h
index d65106a92..e0bf0100e 100644
--- a/tasmota/tasmota_configurations.h
+++ b/tasmota/tasmota_configurations.h
@@ -301,25 +301,26 @@
#undef USE_BLE_ESP32 // (ESP32 only) Disable support for native BLE on ESP32 - use new driver
#undef USE_MI_ESP32 // (ESP32 only) Disable support for ESP32 as a BLE-bridge (+9k2 mem, +292k flash)
+#define USE_DISPLAY // Add Display Support (+2k code)
+ #define USE_DISPLAY_TM1637 // [DisplayModel 15] Enable TM1637 module
#define USE_I2C // I2C using library wire (+10k code, 0k2 mem, 124 iram)
- #define USE_DISPLAY // Add I2C Display Support (+2k code)
- #define USE_DISPLAY_MODES1TO5 // Enable display mode 1 to 5 in addition to mode 0
- #define USE_DISPLAY_LCD // [DisplayModel 1] Enable Lcd display (I2C addresses 0x27 and 0x3F) (+6k code)
- #define USE_DISPLAY_SSD1306 // [DisplayModel 2] Enable SSD1306 Oled 128x64 display (I2C addresses 0x3C and 0x3D) (+16k code)
- #define USE_DISPLAY_MATRIX // [DisplayModel 3] Enable 8x8 Matrix display (I2C adresseses see below) (+11k code)
- #define USE_DISPLAY_SEVENSEG // [DisplayModel 11] [I2cDriver47] Enable sevenseg display (I2C addresses 0x70 - 0x77) (<+11k code)
- #define USE_DISPLAY_SH1106 // [DisplayModel 7] Enable SH1106 Oled 128x64 display (I2C addresses 0x3C and 0x3D)
+ #define USE_DISPLAY_MODES1TO5 // Enable display mode 1 to 5 in addition to mode 0
+ #define USE_DISPLAY_LCD // [DisplayModel 1] Enable Lcd display (I2C addresses 0x27 and 0x3F) (+6k code)
+ #define USE_DISPLAY_SSD1306 // [DisplayModel 2] Enable SSD1306 Oled 128x64 display (I2C addresses 0x3C and 0x3D) (+16k code)
+ #define USE_DISPLAY_MATRIX // [DisplayModel 3] Enable 8x8 Matrix display (I2C adresseses see below) (+11k code)
+ #define USE_DISPLAY_SEVENSEG // [DisplayModel 11] [I2cDriver47] Enable sevenseg display (I2C addresses 0x70 - 0x77) (<+11k code)
+ #define USE_DISPLAY_SH1106 // [DisplayModel 7] Enable SH1106 Oled 128x64 display (I2C addresses 0x3C and 0x3D)
#define USE_SPI // Hardware SPI using GPIO12(MISO), GPIO13(MOSI) and GPIO14(CLK) in addition to two user selectable GPIOs(CS and DC)
- #define USE_DISPLAY_ILI9341 // [DisplayModel 4] Enable ILI9341 Tft 480x320 display (+19k code)
- #define USE_DISPLAY_EPAPER_29 // [DisplayModel 5] Enable e-paper 2.9 inch display (+19k code)
- #define USE_DISPLAY_EPAPER_42 // [DisplayModel 6] Enable e-paper 4.2 inch display
- #define USE_DISPLAY_ILI9488 // [DisplayModel 8]
- #define USE_DISPLAY_SSD1351 // [DisplayModel 9]
- #define USE_DISPLAY_RA8876 // [DisplayModel 10]
- #define USE_DISPLAY_ST7789 // [DisplayModel 12] Enable ST7789 module
- #define USE_DISPLAY_SSD1331 // [DisplayModel 14] Enable SSD1331 module
+ #define USE_DISPLAY_ILI9341 // [DisplayModel 4] Enable ILI9341 Tft 480x320 display (+19k code)
+ #define USE_DISPLAY_EPAPER_29 // [DisplayModel 5] Enable e-paper 2.9 inch display (+19k code)
+ #define USE_DISPLAY_EPAPER_42 // [DisplayModel 6] Enable e-paper 4.2 inch display
+ #define USE_DISPLAY_ILI9488 // [DisplayModel 8]
+ #define USE_DISPLAY_SSD1351 // [DisplayModel 9]
+ #define USE_DISPLAY_RA8876 // [DisplayModel 10]
+ #define USE_DISPLAY_ST7789 // [DisplayModel 12] Enable ST7789 module
+ #define USE_DISPLAY_SSD1331 // [DisplayModel 14] Enable SSD1331 module
#undef DEBUG_THEO // Disable debug code
#undef USE_DEBUG_DRIVER // Disable debug code
diff --git a/tasmota/xdrv_13_display.ino b/tasmota/xdrv_13_display.ino
index 2acefcdf8..c3ecd407b 100755
--- a/tasmota/xdrv_13_display.ino
+++ b/tasmota/xdrv_13_display.ino
@@ -65,6 +65,25 @@ const uint8_t DISPLAY_LOG_ROWS = 32; // Number of lines in display log
#define D_CMND_DISP_HEIGHT "Height"
#define D_CMND_DISP_BLINKRATE "Blinkrate"
#define D_CMND_DISP_BATCH "Batch"
+#define D_CMND_DISP_CLEAR "Clear"
+#define D_CMND_DISP_NUMBER "Number"
+#define D_CMND_DISP_FLOAT "Float"
+#define D_CMND_DISP_NUMBERNC "NumberNC" // NC - "No Clear"
+#define D_CMND_DISP_FLOATNC "FloatNC" // NC - "No Clear"
+#define D_CMND_DISP_BRIGHTNESS "Brightness"
+#define D_CMND_DISP_RAW "Raw"
+#define D_CMND_DISP_LEVEL "Level"
+#define D_CMND_DISP_SEVENSEG_TEXT "SevensegText"
+#define D_CMND_DISP_SEVENSEG_TEXTNC "SevensegTextNC" // NC - "No Clear"
+#define D_CMND_DISP_SCROLLDELAY "ScrollDelay"
+#define D_CMND_DISP_CLOCK "Clock"
+#define D_CMND_DISP_TEXTNC "TextNC" // NC - "No Clear"
+#define D_CMND_DISP_SETLEDS "SetLEDs"
+#define D_CMND_DISP_SETLED "SetLED"
+#define D_CMND_DISP_BUTTONS "Buttons"
+#define D_CMND_DISP_SCROLLTEXT "ScrollText"
+
+
enum XdspFunctions { FUNC_DISPLAY_INIT_DRIVER, FUNC_DISPLAY_INIT, FUNC_DISPLAY_EVERY_50_MSECOND, FUNC_DISPLAY_EVERY_SECOND,
FUNC_DISPLAY_MODEL, FUNC_DISPLAY_MODE, FUNC_DISPLAY_POWER,
@@ -77,6 +96,10 @@ enum XdspFunctions { FUNC_DISPLAY_INIT_DRIVER, FUNC_DISPLAY_INIT, FUNC_DISPLAY_E
#ifdef USE_UFILESYS
,FUNC_DISPLAY_BATCH
#endif
+ , FUNC_DISPLAY_NUMBER, FUNC_DISPLAY_FLOAT, FUNC_DISPLAY_NUMBERNC, FUNC_DISPLAY_FLOATNC,
+ FUNC_DISPLAY_BRIGHTNESS, FUNC_DISPLAY_RAW, FUNC_DISPLAY_LEVEL, FUNC_DISPLAY_SEVENSEG_TEXT, FUNC_DISPLAY_SEVENSEG_TEXTNC,
+ FUNC_DISPLAY_SCROLLDELAY, FUNC_DISPLAY_CLOCK, FUNC_DISPLAY_SETLEDS, FUNC_DISPLAY_SETLED,
+ FUNC_DISPLAY_BUTTONS, FUNC_DISPLAY_SCROLLTEXT
};
enum DisplayInitModes { DISPLAY_INIT_MODE, DISPLAY_INIT_PARTIAL, DISPLAY_INIT_FULL };
@@ -88,6 +111,10 @@ const char kDisplayCommands[] PROGMEM = D_PRFX_DISPLAY "|" // Prefix
#ifdef USE_UFILESYS
"|" D_CMND_DISP_BATCH
#endif
+ "|" D_CMND_DISP_CLEAR "|" D_CMND_DISP_NUMBER "|" D_CMND_DISP_FLOAT "|" D_CMND_DISP_NUMBERNC "|" D_CMND_DISP_FLOATNC "|"
+ D_CMND_DISP_BRIGHTNESS "|" D_CMND_DISP_RAW "|" D_CMND_DISP_LEVEL "|" D_CMND_DISP_SEVENSEG_TEXT "|" D_CMND_DISP_SEVENSEG_TEXTNC "|"
+ D_CMND_DISP_SCROLLDELAY "|" D_CMND_DISP_CLOCK "|" D_CMND_DISP_TEXTNC "|" D_CMND_DISP_SETLEDS "|" D_CMND_DISP_SETLED "|"
+ D_CMND_DISP_BUTTONS "|" D_CMND_DISP_SCROLLTEXT
;
void (* const DisplayCommand[])(void) PROGMEM = {
@@ -97,6 +124,10 @@ void (* const DisplayCommand[])(void) PROGMEM = {
#ifdef USE_UFILESYS
,&CmndDisplayBatch
#endif
+ , &CmndDisplayClear, &CmndDisplayNumber, &CmndDisplayFloat, &CmndDisplayNumberNC, &CmndDisplayFloatNC,
+ &CmndDisplayBrightness, &CmndDisplayRaw, &CmndDisplayLevel, &CmndDisplaySevensegText, &CmndDisplaySevensegTextNC,
+ &CmndDisplayScrollDelay, &CmndDisplayClock, &CmndDisplayTextNC, &CmndDisplaySetLEDs, &CmndDisplaySetLED,
+ &CmndDisplayButtons, &CmndDisplayScrollText
};
char *dsp_str;
@@ -120,7 +151,8 @@ uint8_t dsp_font;
uint8_t dsp_flag;
uint8_t dsp_on;
-uint16_t index_colors[MAX_INDEXCOLORS];
+#define PREDEF_INDEXCOLORS 19
+uint16_t index_colors[MAX_INDEXCOLORS - PREDEF_INDEXCOLORS];
#ifdef USE_DISPLAY_MODES1TO5
@@ -291,7 +323,12 @@ uint32_t decode_te(char *line) {
uint16_t GetColorFromIndex(uint32_t index) {
if (index >= MAX_INDEXCOLORS) index = 0;
- return index_colors[index];
+
+ if (index < PREDEF_INDEXCOLORS) {
+ return renderer->GetColorFromIndex(index);
+ } else {
+ return index_colors[index - PREDEF_INDEXCOLORS];
+ }
}
void DisplayText(void)
@@ -579,49 +616,53 @@ void DisplayText(void)
cp++;
var = fatoiv(cp, &ftemp);
cp += var;
- if (temp >= MAX_INDEXCOLORS) temp = 0;
- index_colors[temp] = ftemp;
+ if (temp >= MAX_INDEXCOLORS) temp = PREDEF_INDEXCOLORS;
+ if (temp < PREDEF_INDEXCOLORS) temp = PREDEF_INDEXCOLORS;
+ index_colors[temp - PREDEF_INDEXCOLORS] = ftemp;
break;
}
#ifdef USE_DT_VARS
- if (*cp == 'v') {
+ if (*cp == 'v') {
+ cp++;
+ { int16_t num, gxp, gyp, textbcol, textfcol, font, textsize, txlen, dp, time;
+ var=atoiv(cp,&num);
+ cp+=var;
cp++;
- { int16_t num, gxp, gyp, textbcol, textfcol, font, textsize, txlen, dp;
- var=atoiv(cp,&num);
- cp+=var;
- cp++;
- var=atoiv(cp,&gxp);
- cp+=var;
- cp++;
- var=atoiv(cp,&gyp);
- cp+=var;
- cp++;
- var=atoiv(cp,&textbcol);
- cp+=var;
- cp++;
- var=atoiv(cp,&textfcol);
- cp+=var;
- cp++;
- var=atoiv(cp,&font);
- cp+=var;
- cp++;
- var=atoiv(cp,&textsize);
- cp+=var;
- cp++;
- var=atoiv(cp,&txlen);
- cp+=var;
- cp++;
- var=atoiv(cp,&dp);
- cp+=var;
- cp++;
- // text itself
- char bbuff[32];
- cp = get_string(bbuff, sizeof(bbuff), cp);
- char unit[4];
- cp = get_string(unit, sizeof(unit), cp);
- define_dt_var(num, gxp, gyp, textbcol, textfcol, font, textsize, txlen, dp, bbuff, unit);
- }
+ var=atoiv(cp,&gxp);
+ cp+=var;
+ cp++;
+ var=atoiv(cp,&gyp);
+ cp+=var;
+ cp++;
+ var=atoiv(cp,&textbcol);
+ cp+=var;
+ cp++;
+ var=atoiv(cp,&textfcol);
+ cp+=var;
+ cp++;
+ var=atoiv(cp,&font);
+ cp+=var;
+ cp++;
+ var=atoiv(cp,&textsize);
+ cp+=var;
+ cp++;
+ var=atoiv(cp,&txlen);
+ cp+=var;
+ cp++;
+ var=atoiv(cp,&dp);
+ cp+=var;
+ cp++;
+ var=atoiv(cp,&time);
+ cp+=var;
+ cp++;
+ // text itself
+ char bbuff[32];
+ cp = get_string(bbuff, sizeof(bbuff), cp);
+ char unit[4];
+ cp = get_string(unit, sizeof(unit), cp);
+ define_dt_var(num, gxp, gyp, textbcol, textfcol, font, textsize, txlen, time, dp, bbuff, unit);
}
+ }
#endif // USE_DT_VARS
// force draw grafics buffer
if (renderer) renderer->Updateframe();
@@ -964,6 +1005,8 @@ typedef struct {
int8_t txtlen;
int8_t dp;
int8_t font;
+ int8_t time;
+ int8_t timer;
char unit[6];
char *jstrbuf;
char rstr[32];
@@ -971,7 +1014,7 @@ typedef struct {
DT_VARS *dt_vars[MAX_DT_VARS];
-void define_dt_var(uint32_t num, uint32_t xp, uint32_t yp, uint32_t txtbcol, uint32_t txtfcol, int32_t font, int32_t txtsiz, int32_t txtlen, int32_t dp, char *jstr, char *unit) {
+void define_dt_var(uint32_t num, uint32_t xp, uint32_t yp, uint32_t txtbcol, uint32_t txtfcol, int32_t font, int32_t txtsiz, int32_t txtlen, int32_t time, int32_t dp, char *jstr, char *unit) {
if (num >= MAX_DT_VARS) return;
if (dt_vars[num]) {
@@ -991,56 +1034,86 @@ void define_dt_var(uint32_t num, uint32_t xp, uint32_t yp, uint32_t txtbcol, u
dtp->txtfcol = txtfcol;
dtp->font = font;
dtp->txtsiz = txtsiz;
+ dtp->time = time;
if (txtlen > MAX_DVTSIZE) {txtlen = MAX_DVTSIZE;}
dtp->txtlen = txtlen;
dtp->dp = dp;
- dtp->jstrbuf = (char*)malloc(strlen(jstr + 1));
+ uint8_t jlen = strlen(jstr);
+ dtp->jstrbuf = (char*)calloc(jlen + 2,1);
if (!dtp->jstrbuf) {
free (dtp);
return;
}
+ dtp->rstr[0] = 0;
+ strcpy(dtp->unit, unit);
strcpy(dtp->jstrbuf, jstr);
- strcpy(dtp->unit,unit);
+ if (!time) time = 1;
+ dtp->timer = time;
}
void draw_dt_vars(void) {
if (!renderer) return;
for (uint32_t cnt = 0; cnt < MAX_DT_VARS; cnt++) {
- if (dt_vars[cnt]) {
- if (dt_vars[cnt]->jstrbuf) {
+ DT_VARS *dtp = dt_vars[cnt];
+ if (dtp) {
+ if (dtp->jstrbuf) {
// draw
- char vstr[MAX_DVTSIZE + 7];
- memset(vstr, ' ', sizeof(vstr));
- strcpy(vstr, dt_vars[cnt]->rstr);
- strcat(vstr, " ");
- strcat(vstr, dt_vars[cnt]->unit);
- uint16_t slen = strlen(vstr);
- vstr[slen] = ' ';
+ dtp->timer--;
+ if (!dtp->timer) {
+ dtp->timer = dtp->time;
+ char vstr[MAX_DVTSIZE + 7];
+ memset(vstr, ' ', sizeof(vstr));
+ strcpy(vstr, dtp->rstr);
+ strcat(vstr, " ");
+ strcat(vstr, dtp->unit);
+ uint16_t slen = strlen(vstr);
+ vstr[slen] = ' ';
- if (!dt_vars[cnt]->txtlen) {
+ if (!dtp->txtlen) {
vstr[slen] = 0;
- } else {
- vstr[abs(int(dt_vars[cnt]->txtlen))] = 0;
- }
- if (dt_vars[cnt]->txtlen < 0) {
- // right align
- alignright(vstr);
- }
+ } else {
+ vstr[abs(int(dtp->txtlen))] = 0;
+ }
+ if (dtp->txtlen < 0) {
+ // right align
+ alignright(vstr);
+ }
- if (dt_vars[cnt]->txtsiz > 0) {
- renderer->setDrawMode(0);
- } else {
- renderer->setDrawMode(2);
- }
- renderer->setTextColor(GetColorFromIndex(dt_vars[cnt]->txtfcol),GetColorFromIndex(dt_vars[cnt]->txtbcol));
- renderer->setTextFont(dt_vars[cnt]->font);
- renderer->setTextSize(abs(dt_vars[cnt]->txtsiz));
- renderer->DrawStringAt(dt_vars[cnt]->xp, dt_vars[cnt]->yp, vstr, GetColorFromIndex(dt_vars[cnt]->txtfcol), 0);
+ if (dtp->txtsiz > 0) {
+ renderer->setDrawMode(0);
+ } else {
+ renderer->setDrawMode(2);
+ }
+ renderer->setTextColor(GetColorFromIndex(dtp->txtfcol),GetColorFromIndex(dtp->txtbcol));
+ renderer->setTextFont(dtp->font);
+ renderer->setTextSize(abs(dtp->txtsiz));
- // reset display vars
- renderer->setTextColor(fg_color, bg_color);
- renderer->setDrawMode(auto_draw);
+ if (dtp->jstrbuf[0]=='[') {
+ uint16_t s_disp_xpos = disp_xpos;
+ uint16_t s_disp_ypos = disp_ypos;
+ uint16_t s_bg_color = bg_color;
+ uint16_t s_fg_color = fg_color;
+ disp_xpos = dtp->xp;
+ disp_ypos = dtp->yp;
+ bg_color = GetColorFromIndex(dtp->txtbcol);
+ fg_color = GetColorFromIndex(dtp->txtfcol);
+ char *savmbd = XdrvMailbox.data;
+ XdrvMailbox.data = dtp->jstrbuf;
+ DisplayText();
+ XdrvMailbox.data = savmbd;
+ disp_xpos = s_disp_xpos;
+ disp_ypos = s_disp_ypos;
+ bg_color = s_bg_color;
+ fg_color = s_fg_color;
+ } else {
+ renderer->DrawStringAt(dtp->xp, dtp->yp, vstr, GetColorFromIndex(dtp->txtfcol), 0);
+ }
+
+ // restore display vars
+ renderer->setTextColor(fg_color, bg_color);
+ renderer->setDrawMode(auto_draw);
+ }
}
}
}
@@ -1049,11 +1122,17 @@ void draw_dt_vars(void) {
#define DTV_JSON_SIZE 1024
void DTVarsTeleperiod(void) {
- char *json = (char*)malloc(DTV_JSON_SIZE);
- if (json) {
- strlcpy(json, TasmotaGlobal.mqtt_data, DTV_JSON_SIZE);
- get_dt_vars(json);
- free(json);
+ if (TasmotaGlobal.mqtt_data && TasmotaGlobal.mqtt_data[0]) {
+ uint32_t jlen = strlen(TasmotaGlobal.mqtt_data);
+
+ if (jlen < DTV_JSON_SIZE) {
+ char *json = (char*)malloc(jlen + 2);
+ if (json) {
+ strlcpy(json, TasmotaGlobal.mqtt_data, jlen + 1);
+ get_dt_vars(json);
+ free(json);
+ }
+ }
}
}
@@ -1077,7 +1156,7 @@ void get_dt_vars(char *json) {
for (uint32_t cnt = 0; cnt < MAX_DT_VARS; cnt++) {
if (dt_vars[cnt]) {
- if (dt_vars[cnt]->jstrbuf) {
+ if (dt_vars[cnt]->jstrbuf && dt_vars[cnt]->jstrbuf[0]!='[') {
char sbuf[32];
uint32_t res = JsonParsePath(&obj, dt_vars[cnt]->jstrbuf, '#', NULL, sbuf, sizeof(sbuf));
if (res) {
@@ -1505,15 +1584,21 @@ void DisplayInitDriver(void)
renderer->setTextSize(Settings.display_size);
// force opaque mode
renderer->setDrawMode(0);
+
+ for (uint32_t cnt = 0; cnt < (MAX_INDEXCOLORS - PREDEF_INDEXCOLORS); cnt++) {
+ index_colors[cnt] = 0;
+ }
}
+#ifdef USE_DT_VARS
+ free_dt_vars();
+#endif
+
#ifdef USE_UFILESYS
Display_Text_From_File("/display.ini");
#endif
-#ifdef USE_DT_VARS
- free_dt_vars();
-#endif
+
// AddLog(LOG_LEVEL_DEBUG, PSTR(D_LOG_DEBUG "Display model %d"), Settings.display_model);
@@ -1658,6 +1743,7 @@ void CmndDisplayBlinkrate(void)
ResponseCmndNumber(XdrvMailbox.payload);
}
+
#ifdef USE_UFILESYS
void CmndDisplayBatch(void) {
if (XdrvMailbox.data_len > 0) {
@@ -1669,9 +1755,160 @@ void CmndDisplayBatch(void) {
}
#endif
+
+void CmndDisplayClear(void)
+{
+ if (!renderer)
+ XdspCall(FUNC_DISPLAY_CLEAR);
+ ResponseCmndChar(XdrvMailbox.data);
+}
+
+void CmndDisplayNumber(void)
+{
+ if (!renderer) {
+ XdspCall(FUNC_DISPLAY_NUMBER);
+ }
+ ResponseCmndChar(XdrvMailbox.data);
+}
+
+void CmndDisplayFloat(void)
+{
+ if (!renderer) {
+ XdspCall(FUNC_DISPLAY_FLOAT);
+ }
+ ResponseCmndChar(XdrvMailbox.data);
+}
+
+void CmndDisplayNumberNC(void)
+{
+ if (!renderer) {
+ XdspCall(FUNC_DISPLAY_NUMBERNC);
+ }
+ ResponseCmndChar(XdrvMailbox.data);
+}
+
+void CmndDisplayFloatNC(void)
+{
+ if (!renderer) {
+ XdspCall(FUNC_DISPLAY_FLOATNC);
+ }
+ ResponseCmndChar(XdrvMailbox.data);
+}
+
+void CmndDisplayBrightness(void)
+{
+ bool result = false;
+ if (!renderer) {
+ result = XdspCall(FUNC_DISPLAY_BRIGHTNESS);
+ }
+ if(result) ResponseCmndNumber(XdrvMailbox.payload);
+ else ResponseCmndChar(XdrvMailbox.data);
+}
+
+void CmndDisplayRaw(void)
+{
+ if (!renderer) {
+ XdspCall(FUNC_DISPLAY_RAW);
+ }
+ ResponseCmndChar(XdrvMailbox.data);
+}
+
+void CmndDisplayLevel(void)
+{
+ bool result = false;
+ if (!renderer) {
+ result = XdspCall(FUNC_DISPLAY_LEVEL);
+ }
+ if(result) ResponseCmndNumber(XdrvMailbox.payload);
+ else ResponseCmndChar(XdrvMailbox.data);
+}
+
+void CmndDisplaySevensegText(void)
+{
+ if (!renderer) {
+ XdspCall(FUNC_DISPLAY_SEVENSEG_TEXT);
+ }
+ ResponseCmndChar(XdrvMailbox.data);
+}
+
+void CmndDisplayTextNC(void)
+{
+ if (!renderer) {
+ XdspCall(FUNC_DISPLAY_SEVENSEG_TEXTNC);
+ }
+ ResponseCmndChar(XdrvMailbox.data);
+}
+
+void CmndDisplaySevensegTextNC(void)
+{
+ if (!renderer) {
+ XdspCall(FUNC_DISPLAY_SEVENSEG_TEXTNC);
+ }
+ ResponseCmndChar(XdrvMailbox.data);
+}
+
+void CmndDisplayScrollDelay(void)
+{
+ if (!renderer) {
+ XdspCall(FUNC_DISPLAY_SCROLLDELAY);
+ }
+ ResponseCmndNumber(XdrvMailbox.payload);
+}
+
+void CmndDisplayClock(void)
+{
+ if (!renderer) {
+ XdspCall(FUNC_DISPLAY_CLOCK);
+ }
+ ResponseCmndNumber(XdrvMailbox.payload);
+}
+
+void CmndDisplaySetLEDs(void)
+{
+ bool result = false;
+ if (!renderer) {
+ result = XdspCall(FUNC_DISPLAY_SETLEDS);
+ }
+ if(result) ResponseCmndNumber(XdrvMailbox.payload);
+ else ResponseCmndChar(XdrvMailbox.data);
+}
+
+
+void CmndDisplaySetLED(void)
+{
+ if (!renderer) {
+ XdspCall(FUNC_DISPLAY_SETLED);
+ }
+ ResponseCmndChar(XdrvMailbox.data);
+}
+
+void CmndDisplayButtons(void)
+{
+ if (!renderer) {
+ XdspCall(FUNC_DISPLAY_BUTTONS);
+ }
+ ResponseCmndNumber(XdrvMailbox.payload);
+}
+
+
+void CmndDisplayScrollText(void)
+{
+ bool result = false;
+ if (!renderer) {
+ result = XdspCall(FUNC_DISPLAY_SCROLLTEXT);
+ }
+ if(result) ResponseCmndNumber(XdrvMailbox.payload);
+ else ResponseCmndChar(XdrvMailbox.data);
+}
+
+
void CmndDisplaySize(void)
{
+#ifdef USE_DISPLAY_TM1637
+ if ((XdrvMailbox.payload > 0) && (XdrvMailbox.payload <= 6)) {
+#else
if ((XdrvMailbox.payload > 0) && (XdrvMailbox.payload <= 4)) {
+#endif
Settings.display_size = XdrvMailbox.payload;
if (renderer) renderer->setTextSize(Settings.display_size);
//else DisplaySetSize(Settings.display_size);
@@ -1720,7 +1957,9 @@ void CmndDisplayText(void)
#ifndef USE_DISPLAY_MODES1TO5
DisplayText();
#else
- if (!Settings.display_mode) {
+ if(Settings.display_model == 15) {
+ XdspCall(FUNC_DISPLAY_SEVENSEG_TEXT);
+ } else if (!Settings.display_mode) {
DisplayText();
} else {
DisplayLogBufferAdd(XdrvMailbox.data);
diff --git a/tasmota/xdrv_23_zigbee_7_0_statemachine.ino b/tasmota/xdrv_23_zigbee_7_0_statemachine.ino
index fcacc81c2..b413e1092 100644
--- a/tasmota/xdrv_23_zigbee_7_0_statemachine.ino
+++ b/tasmota/xdrv_23_zigbee_7_0_statemachine.ino
@@ -153,82 +153,81 @@ ZBM(ZBS_ZNPHC, Z_SREQ | Z_SYS, SYS_OSAL_NV_READ, ZNP_HAS_CONFIGURED & 0xFF, ZNP_
ZBM(ZBR_ZNPHC, Z_SRSP | Z_SYS, SYS_OSAL_NV_READ, Z_SUCCESS, 0x01 /* len */, 0x55) // 6108000155
// If not set, the response is 61-08-02-00 = Z_SRSP | Z_SYS, SYS_OSAL_NV_READ, Z_INVALIDPARAMETER, 0x00 /* len */
-ZBM(ZBS_PAN, Z_SREQ | Z_SAPI, SAPI_READ_CONFIGURATION, CONF_PANID ) // 260483
-ZBR(ZBR_PAN, Z_SRSP | Z_SAPI, SAPI_READ_CONFIGURATION, Z_SUCCESS, CONF_PANID, 0x02 /* len */,
- 0x00, 0x00 /* pan_id */ ) // 6604008302xxxx
+ZBM(ZBS_PAN, Z_SREQ | Z_SYS, SYS_OSAL_NV_READ, CONF_PANID,0x00, 0x00 ) // 2108830000
+ZBR(ZBR_PAN, Z_SRSP | Z_SYS, SYS_OSAL_NV_READ, Z_SUCCESS, 0x02 /* len */,
+ 0x00, 0x00 /* pan_id */ ) // 61080002xxxx
-ZBM(ZBS_EXTPAN, Z_SREQ | Z_SAPI, SAPI_READ_CONFIGURATION, CONF_EXTENDED_PAN_ID ) // 26042D
-ZBR(ZBR_EXTPAN, Z_SRSP | Z_SAPI, SAPI_READ_CONFIGURATION, Z_SUCCESS, CONF_EXTENDED_PAN_ID,
+ZBM(ZBS_EXTPAN, Z_SREQ | Z_SYS, SYS_OSAL_NV_READ, CONF_EXTENDED_PAN_ID,0x00, 0x00 ) // 21082D0000
+ZBR(ZBR_EXTPAN, Z_SRSP | Z_SYS, SYS_OSAL_NV_READ, Z_SUCCESS,
0x08 /* len */,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* ext_pan_id */
- ) // 6604002D08xxxxxxxxxxxxxxxx
+ ) // 61080008xxxxxxxxxxxxxxxx
-ZBM(ZBS_CHANN, Z_SREQ | Z_SAPI, SAPI_READ_CONFIGURATION, CONF_CHANLIST ) // 260484
-ZBR(ZBR_CHANN, Z_SRSP | Z_SAPI, SAPI_READ_CONFIGURATION, Z_SUCCESS, CONF_CHANLIST,
+ZBM(ZBS_CHANN, Z_SREQ | Z_SYS, SYS_OSAL_NV_READ, CONF_CHANLIST,0x00, 0x00 ) // 2108840000
+ZBR(ZBR_CHANN, Z_SRSP | Z_SYS, SYS_OSAL_NV_READ, Z_SUCCESS,
0x04 /* len */,
Z_B0(USE_ZIGBEE_CHANNEL_MASK), Z_B1(USE_ZIGBEE_CHANNEL_MASK), Z_B2(USE_ZIGBEE_CHANNEL_MASK), Z_B3(USE_ZIGBEE_CHANNEL_MASK),
- ) // 6604008404xxxxxxxx
+ ) // 61080004xxxxxxxx
-ZBM(ZBS_PFGK, Z_SREQ | Z_SAPI, SAPI_READ_CONFIGURATION, CONF_PRECFGKEY ) // 260462
-ZBR(ZBR_PFGK, Z_SRSP | Z_SAPI, SAPI_READ_CONFIGURATION, Z_SUCCESS, CONF_PRECFGKEY,
+ZBM(ZBS_PFGK, Z_SREQ | Z_SYS, SYS_OSAL_NV_READ, CONF_PRECFGKEY,0x00, 0x00 ) // 2108620000
+ZBR(ZBR_PFGK, Z_SRSP | Z_SYS, SYS_OSAL_NV_READ, Z_SUCCESS,
0x10 /* len */,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* key_l */
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* key_h */
/*0x01, 0x03, 0x05, 0x07, 0x09, 0x0B, 0x0D, 0x0F,
- 0x00, 0x02, 0x04, 0x06, 0x08, 0x0A, 0x0C, 0x0D*/ ) // 660400621001030507090B0D0F00020406080A0C0D
+ 0x00, 0x02, 0x04, 0x06, 0x08, 0x0A, 0x0C, 0x0D*/ ) // 6108001001030507090B0D0F00020406080A0C0D
-ZBM(ZBS_PFGKEN, Z_SREQ | Z_SAPI, SAPI_READ_CONFIGURATION, CONF_PRECFGKEYS_ENABLE ) // 260463
-ZBM(ZBR_PFGKEN, Z_SRSP | Z_SAPI, SAPI_READ_CONFIGURATION, Z_SUCCESS, CONF_PRECFGKEYS_ENABLE,
- 0x01 /* len */, 0x00 ) // 660400630100
+ZBM(ZBS_PFGKEN, Z_SREQ | Z_SYS, SYS_OSAL_NV_READ, CONF_PRECFGKEYS_ENABLE,0x00, 0x00 ) // 2108630000
+ZBM(ZBR_PFGKEN, Z_SRSP | Z_SYS, SYS_OSAL_NV_READ, Z_SUCCESS,
+ 0x01 /* len */, 0x00 ) // 6108000100
-ZBM(ZBS_LOGTYPE, Z_SREQ | Z_SAPI, SAPI_READ_CONFIGURATION, CONF_LOGICAL_TYPE ) // 260487
-ZBM(ZBS_LOGTYPE_COORD, Z_SRSP | Z_SAPI, SAPI_READ_CONFIGURATION, Z_SUCCESS, CONF_LOGICAL_TYPE,
- 0x01 /* len */, 0x00 ) // 660400870100 - coordinator
-ZBM(ZBS_LOGTYPE_ROUTER, Z_SRSP | Z_SAPI, SAPI_READ_CONFIGURATION, Z_SUCCESS, CONF_LOGICAL_TYPE,
- 0x01 /* len */, 0x01 ) // 660400870101 - router
-ZBM(ZBS_LOGTYPE_DEVICE, Z_SRSP | Z_SAPI, SAPI_READ_CONFIGURATION, Z_SUCCESS, CONF_LOGICAL_TYPE,
- 0x01 /* len */, 0x02 ) // 660400870102 - device
+ZBM(ZBS_LOGTYPE, Z_SREQ | Z_SYS, SYS_OSAL_NV_READ, CONF_LOGICAL_TYPE,0x00, 0x00 ) // 2108870000
+ZBM(ZBS_LOGTYPE_COORD, Z_SRSP | Z_SYS, SYS_OSAL_NV_READ, Z_SUCCESS,
+ 0x01 /* len */, 0x00 ) // 6108000100 - coordinator
+ZBM(ZBS_LOGTYPE_ROUTER, Z_SRSP | Z_SYS, SYS_OSAL_NV_READ, Z_SUCCESS,
+ 0x01 /* len */, 0x01 ) // 6108000101 - router
+ZBM(ZBS_LOGTYPE_DEVICE, Z_SRSP | Z_SYS, SYS_OSAL_NV_READ, Z_SUCCESS,
+ 0x01 /* len */, 0x02 ) // 6108000102 - device
// commands to "format" the device
// Write configuration - write success
-ZBM(ZBR_W_OK, Z_SRSP | Z_SAPI, SAPI_WRITE_CONFIGURATION, Z_SUCCESS ) // 660500 - Write Configuration
ZBM(ZBR_WNV_OK, Z_SRSP | Z_SYS, SYS_OSAL_NV_WRITE, Z_SUCCESS ) // 610900 - NV Write
ZBM(ZBR_WNV_OKE, Z_SRSP | Z_SYS, SYS_OSAL_NV_WRITE ) // 6109xx - NV Write, OK or error
// Factory reset
-ZBM(ZBS_FACTRES, Z_SREQ | Z_SAPI, SAPI_WRITE_CONFIGURATION, CONF_STARTUP_OPTION, 0x01 /* len */, 0x03 ) // 2605030103
+ZBM(ZBS_FACTRES, Z_SREQ | Z_SYS, SYS_OSAL_NV_WRITE, CONF_STARTUP_OPTION,0x00, 0x00, 0x01 /* len */, 0x03 ) // 21090300000103
// Write PAN ID
-ZBR(ZBS_W_PAN, Z_SREQ | Z_SAPI, SAPI_WRITE_CONFIGURATION, CONF_PANID, 0x02 /* len */, 0x00, 0x00 /* pan_id */ ) // 26058302xxxx
+ZBR(ZBS_W_PAN, Z_SREQ | Z_SYS, SYS_OSAL_NV_WRITE, CONF_PANID,0x00, 0x00, 0x02 /* len */, 0x00, 0x00 /* pan_id */ ) // 210983000002xxxx
// Write Universal PAN ID
-ZBR(ZBS_W_ALL_PAN, Z_SREQ | Z_SAPI, SAPI_WRITE_CONFIGURATION, CONF_PANID, 0x02 /* len */, Z_B0(0xFFFF), Z_B1(0xFFFF) ) // 26058302FFFF
+ZBR(ZBS_W_ALL_PAN, Z_SREQ | Z_SYS, SYS_OSAL_NV_WRITE, CONF_PANID,0x00, 0x00, 0x02 /* len */, Z_B0(0xFFFF), Z_B1(0xFFFF) ) // 210983000002FFFF
// Write EXT PAN ID
-ZBR(ZBS_W_EXTPAN, Z_SREQ | Z_SAPI, SAPI_WRITE_CONFIGURATION, CONF_EXTENDED_PAN_ID, 0x08 /* len */,
+ZBR(ZBS_W_EXTPAN, Z_SREQ | Z_SYS, SYS_OSAL_NV_WRITE, CONF_EXTENDED_PAN_ID,0x00, 0x00, 0x08 /* len */,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* ext_pan_id */
- ) // 26052D086263151D004B1200
+ ) // 21092D0000086263151D004B1200
// Write Channel ID
-ZBR(ZBS_W_CHANN, Z_SREQ | Z_SAPI, SAPI_WRITE_CONFIGURATION, CONF_CHANLIST, 0x04 /* len */,
+ZBR(ZBS_W_CHANN, Z_SREQ | Z_SYS, SYS_OSAL_NV_WRITE, CONF_CHANLIST,0x00, 0x00, 0x04 /* len */,
Z_B0(USE_ZIGBEE_CHANNEL_MASK), Z_B1(USE_ZIGBEE_CHANNEL_MASK), Z_B2(USE_ZIGBEE_CHANNEL_MASK), Z_B3(USE_ZIGBEE_CHANNEL_MASK),
- /*0x00, 0x08, 0x00, 0x00*/ ) // 26058404xxxxxxxx
+ /*0x00, 0x08, 0x00, 0x00*/ ) // 210984000004xxxxxxxx
// Write All Channels
const uint32_t ZB_ALL_CHANNELS = 0x07FFF800;
-ZBR(ZBS_W_ALL_CHANN, Z_SREQ | Z_SAPI, SAPI_WRITE_CONFIGURATION, CONF_CHANLIST, 0x04 /* len */,
+ZBR(ZBS_W_ALL_CHANN, Z_SREQ | Z_SYS, SYS_OSAL_NV_WRITE, CONF_CHANLIST,0x00, 0x00, 0x04 /* len */,
Z_B0(ZB_ALL_CHANNELS), Z_B1(ZB_ALL_CHANNELS), Z_B2(ZB_ALL_CHANNELS), Z_B3(ZB_ALL_CHANNELS),
- /*0x00, 0x08, 0x00, 0x00*/ ) // 2605840400F8FF7F
+ /*0x00, 0x08, 0x00, 0x00*/ ) // 21098400000400F8FF7F
// Write Logical Type = 00 = coordinator
-ZBM(ZBS_W_LOGTYP_COORD, Z_SREQ | Z_SAPI, SAPI_WRITE_CONFIGURATION, CONF_LOGICAL_TYPE, 0x01 /* len */, 0x00 ) // 2605870100
+ZBM(ZBS_W_LOGTYP_COORD, Z_SREQ | Z_SYS, SYS_OSAL_NV_WRITE, CONF_LOGICAL_TYPE,0x00, 0x00, 0x01 /* len */, 0x00 ) // 21098700000100
// Write Logical Type = 01 = router
-ZBM(ZBS_W_LOGTYP_ROUTER, Z_SREQ | Z_SAPI, SAPI_WRITE_CONFIGURATION, CONF_LOGICAL_TYPE, 0x01 /* len */, 0x01 ) // 2605870101
+ZBM(ZBS_W_LOGTYP_ROUTER, Z_SREQ | Z_SYS, SYS_OSAL_NV_WRITE, CONF_LOGICAL_TYPE,0x00, 0x00, 0x01 /* len */, 0x01 ) // 21098700000101
// Write Logical Type = 02 = device
-ZBM(ZBS_W_LOGTYP_DEVICE, Z_SREQ | Z_SAPI, SAPI_WRITE_CONFIGURATION, CONF_LOGICAL_TYPE, 0x01 /* len */, 0x02 ) // 2605870102
+ZBM(ZBS_W_LOGTYP_DEVICE, Z_SREQ | Z_SYS, SYS_OSAL_NV_WRITE, CONF_LOGICAL_TYPE,0x00, 0x00, 0x01 /* len */, 0x02 ) // 21098700000102
// Write precfgkey
-ZBR(ZBS_W_PFGK, Z_SREQ | Z_SAPI, SAPI_WRITE_CONFIGURATION, CONF_PRECFGKEY,
+ZBR(ZBS_W_PFGK, Z_SREQ | Z_SYS, SYS_OSAL_NV_WRITE, CONF_PRECFGKEY,0x00, 0x00,
0x10 /* len */,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* key_l */
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* key_h */ /*0x01, 0x03, 0x05, 0x07, 0x09, 0x0B, 0x0D, 0x0F,
- 0x00, 0x02, 0x04, 0x06, 0x08, 0x0A, 0x0C, 0x0D*/ ) // 2605621001030507090B0D0F00020406080A0C0D
+ 0x00, 0x02, 0x04, 0x06, 0x08, 0x0A, 0x0C, 0x0D*/ ) // 21096200001001030507090B0D0F00020406080A0C0D
// Write precfgkey enable
-ZBM(ZBS_W_PFGKEN, Z_SREQ | Z_SAPI, SAPI_WRITE_CONFIGURATION, CONF_PRECFGKEYS_ENABLE, 0x01 /* len */, 0x00 ) // 2605630100
+ZBM(ZBS_W_PFGKEN, Z_SREQ | Z_SYS, SYS_OSAL_NV_WRITE, CONF_PRECFGKEYS_ENABLE,0x00, 0x00, 0x01 /* len */, 0x00 ) // 21096300000100
// Write Security Mode
ZBM(ZBS_WNV_SECMODE, Z_SREQ | Z_SYS, SYS_OSAL_NV_WRITE, Z_B0(CONF_TCLK_TABLE_START), Z_B1(CONF_TCLK_TABLE_START),
0x00 /* offset */, 0x20 /* len */,
@@ -237,7 +236,7 @@ ZBM(ZBS_WNV_SECMODE, Z_SREQ | Z_SYS, SYS_OSAL_NV_WRITE, Z_B0(CONF_TCLK_TABLE_STA
0x6c, 0x69, 0x61, 0x6e, 0x63, 0x65, 0x30, 0x39,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00) // 2109010100200FFFFFFFFFFFFFFFF5A6967426565416C6C69616E636530390000000000000000
// Write Z_ZDO Direct CB
-ZBM(ZBS_W_ZDODCB, Z_SREQ | Z_SAPI, SAPI_WRITE_CONFIGURATION, CONF_ZDO_DIRECT_CB, 0x01 /* len */, 0x01 ) // 26058F0101
+ZBM(ZBS_W_ZDODCB, Z_SREQ | Z_SYS, SYS_OSAL_NV_WRITE, CONF_ZDO_DIRECT_CB,0x00, 0x00, 0x01 /* len */, 0x01 ) // 21098F00000101
// NV Init ZNP Has Configured
ZBM(ZBS_WNV_INITZNPHC, Z_SREQ | Z_SYS, SYS_OSAL_NV_ITEM_INIT, ZNP_HAS_CONFIGURED & 0xFF, ZNP_HAS_CONFIGURED >> 8,
0x01, 0x00 /* InitLen 16 bits */, 0x01 /* len */, 0x00 ) // 2107000F01000100 - 610709
@@ -321,47 +320,47 @@ ZBM(ZBR_PERMITJOIN_AREQ_RSP, Z_AREQ | Z_ZDO, ZDO_MGMT_PERMIT_JOIN_RSP, 0x00, 0x
void ZNP_UpdateConfig(uint8_t zb_channel, uint16_t zb_pan_id, uint64_t zb_ext_panid, uint64_t zb_precfgkey_l, uint64_t zb_precfgkey_h) {
uint32_t zb_channel_mask = (1 << zb_channel);
- ZBW(ZBR_PAN, Z_SRSP | Z_SAPI, SAPI_READ_CONFIGURATION, Z_SUCCESS, CONF_PANID, 0x02 /* len */,
- Z_B0(zb_pan_id), Z_B1(zb_pan_id) ) // 6604008302xxxx
+ ZBW(ZBR_PAN, Z_SRSP | Z_SYS, SYS_OSAL_NV_READ, Z_SUCCESS, 0x02 /* len */,
+ Z_B0(zb_pan_id), Z_B1(zb_pan_id) ) // 61080002xxxx
- ZBW(ZBR_EXTPAN, Z_SRSP | Z_SAPI, SAPI_READ_CONFIGURATION, Z_SUCCESS, CONF_EXTENDED_PAN_ID,
+ ZBW(ZBR_EXTPAN, Z_SRSP | Z_SYS, SYS_OSAL_NV_READ, Z_SUCCESS,
0x08 /* len */,
Z_B0(zb_ext_panid), Z_B1(zb_ext_panid), Z_B2(zb_ext_panid), Z_B3(zb_ext_panid),
Z_B4(zb_ext_panid), Z_B5(zb_ext_panid), Z_B6(zb_ext_panid), Z_B7(zb_ext_panid),
- ) // 6604002D08xxxxxxxxxxxxxxxx
+ ) // 61080008xxxxxxxxxxxxxxxx
- ZBW(ZBR_CHANN, Z_SRSP | Z_SAPI, SAPI_READ_CONFIGURATION, Z_SUCCESS, CONF_CHANLIST,
+ ZBW(ZBR_CHANN, Z_SRSP | Z_SYS, SYS_OSAL_NV_READ, Z_SUCCESS,
0x04 /* len */,
Z_B0(zb_channel_mask), Z_B1(zb_channel_mask), Z_B2(zb_channel_mask), Z_B3(zb_channel_mask),
- ) // 6604008404xxxxxxxx
+ ) // 61080004xxxxxxxx
- ZBW(ZBR_PFGK, Z_SRSP | Z_SAPI, SAPI_READ_CONFIGURATION, Z_SUCCESS, CONF_PRECFGKEY,
+ ZBW(ZBR_PFGK, Z_SRSP | Z_SYS, SYS_OSAL_NV_READ, Z_SUCCESS,
0x10 /* len */,
Z_B0(zb_precfgkey_l), Z_B1(zb_precfgkey_l), Z_B2(zb_precfgkey_l), Z_B3(zb_precfgkey_l),
Z_B4(zb_precfgkey_l), Z_B5(zb_precfgkey_l), Z_B6(zb_precfgkey_l), Z_B7(zb_precfgkey_l),
Z_B0(zb_precfgkey_h), Z_B1(zb_precfgkey_h), Z_B2(zb_precfgkey_h), Z_B3(zb_precfgkey_h),
Z_B4(zb_precfgkey_h), Z_B5(zb_precfgkey_h), Z_B6(zb_precfgkey_h), Z_B7(zb_precfgkey_h),
/*0x01, 0x03, 0x05, 0x07, 0x09, 0x0B, 0x0D, 0x0F,
- 0x00, 0x02, 0x04, 0x06, 0x08, 0x0A, 0x0C, 0x0D*/ ) // 660400621001030507090B0D0F00020406080A0C0D
+ 0x00, 0x02, 0x04, 0x06, 0x08, 0x0A, 0x0C, 0x0D*/ ) // 6108001001030507090B0D0F00020406080A0C0D
- ZBW(ZBS_W_PAN, Z_SREQ | Z_SAPI, SAPI_WRITE_CONFIGURATION, CONF_PANID, 0x02 /* len */, Z_B0(zb_pan_id), Z_B1(zb_pan_id) ) // 26058302xxxx
+ ZBW(ZBS_W_PAN, Z_SREQ | Z_SYS, SYS_OSAL_NV_WRITE, CONF_PANID,0x00, 0x00, 0x02 /* len */, Z_B0(zb_pan_id), Z_B1(zb_pan_id) ) // 210983000002xxxx
// Write EXT PAN ID
- ZBW(ZBS_W_EXTPAN, Z_SREQ | Z_SAPI, SAPI_WRITE_CONFIGURATION, CONF_EXTENDED_PAN_ID, 0x08 /* len */,
+ ZBW(ZBS_W_EXTPAN, Z_SREQ | Z_SYS, SYS_OSAL_NV_WRITE, CONF_EXTENDED_PAN_ID,0x00, 0x00, 0x08 /* len */,
Z_B0(zb_ext_panid), Z_B1(zb_ext_panid), Z_B2(zb_ext_panid), Z_B3(zb_ext_panid),
Z_B4(zb_ext_panid), Z_B5(zb_ext_panid), Z_B6(zb_ext_panid), Z_B7(zb_ext_panid)
- ) // 26052D086263151D004B1200
+ ) // 21092D0000086263151D004B1200
// Write Channel ID
- ZBW(ZBS_W_CHANN, Z_SREQ | Z_SAPI, SAPI_WRITE_CONFIGURATION, CONF_CHANLIST, 0x04 /* len */,
+ ZBW(ZBS_W_CHANN, Z_SREQ | Z_SYS, SYS_OSAL_NV_WRITE, CONF_CHANLIST,0x00, 0x00, 0x04 /* len */,
Z_B0(zb_channel_mask), Z_B1(zb_channel_mask), Z_B2(zb_channel_mask), Z_B3(zb_channel_mask),
- /*0x00, 0x08, 0x00, 0x00*/ ) // 26058404xxxxxxxx
+ /*0x00, 0x08, 0x00, 0x00*/ ) // 210984000004xxxxxxxx
// Write precfgkey
- ZBW(ZBS_W_PFGK, Z_SREQ | Z_SAPI, SAPI_WRITE_CONFIGURATION, CONF_PRECFGKEY,
+ ZBW(ZBS_W_PFGK, Z_SREQ | Z_SYS, SYS_OSAL_NV_WRITE, CONF_PRECFGKEY,0x00, 0x00,
0x10 /* len */,
Z_B0(zb_precfgkey_l), Z_B1(zb_precfgkey_l), Z_B2(zb_precfgkey_l), Z_B3(zb_precfgkey_l),
Z_B4(zb_precfgkey_l), Z_B5(zb_precfgkey_l), Z_B6(zb_precfgkey_l), Z_B7(zb_precfgkey_l),
Z_B0(zb_precfgkey_h), Z_B1(zb_precfgkey_h), Z_B2(zb_precfgkey_h), Z_B3(zb_precfgkey_h),
Z_B4(zb_precfgkey_h), Z_B5(zb_precfgkey_h), Z_B6(zb_precfgkey_h), Z_B7(zb_precfgkey_h),
- ) // 2605621001030507090B0D0F00020406080A0C0D
+ ) // 21096200001001030507090B0D0F00020406080A0C0D
}
static const Zigbee_Instruction zb_prog[] PROGMEM = {
@@ -457,25 +456,25 @@ static const Zigbee_Instruction zb_prog[] PROGMEM = {
//ZI_LOG(LOG_LEVEL_INFO, D_LOG_ZIGBEE "zigbee bad configuration of device, doing a factory reset")
ZI_ON_ERROR_GOTO(ZIGBEE_LABEL_ABORT)
ZI_SEND(ZBS_FACTRES) // factory reset
- ZI_WAIT_RECV(1000, ZBR_W_OK)
+ ZI_WAIT_RECV(1000, ZBR_WNV_OK)
ZI_SEND(ZBS_RESET) // reset device
ZI_WAIT_RECV(5000, ZBR_RESET)
ZI_SEND(ZBS_W_PAN) // write PAN ID
- ZI_WAIT_RECV(1000, ZBR_W_OK)
+ ZI_WAIT_RECV(1000, ZBR_WNV_OK)
ZI_SEND(ZBS_W_EXTPAN) // write EXT PAN ID
- ZI_WAIT_RECV(1000, ZBR_W_OK)
+ ZI_WAIT_RECV(1000, ZBR_WNV_OK)
ZI_SEND(ZBS_W_CHANN) // write CHANNEL
- ZI_WAIT_RECV(1000, ZBR_W_OK)
+ ZI_WAIT_RECV(1000, ZBR_WNV_OK)
ZI_SEND(ZBS_W_LOGTYP_COORD) // write Logical Type = coordinator
- ZI_WAIT_RECV(1000, ZBR_W_OK)
+ ZI_WAIT_RECV(1000, ZBR_WNV_OK)
ZI_SEND(ZBS_W_PFGK) // write PRECFGKEY
- ZI_WAIT_RECV(1000, ZBR_W_OK)
+ ZI_WAIT_RECV(1000, ZBR_WNV_OK)
ZI_SEND(ZBS_W_PFGKEN) // write PRECFGKEY Enable
- ZI_WAIT_RECV(1000, ZBR_W_OK)
+ ZI_WAIT_RECV(1000, ZBR_WNV_OK)
ZI_SEND(ZBS_WNV_SECMODE) // write Security Mode
ZI_WAIT_RECV(1000, ZBR_WNV_OKE) // Tolerate error for ZNP 3.x
ZI_SEND(ZBS_W_ZDODCB) // write Z_ZDO Direct CB
- ZI_WAIT_RECV(1000, ZBR_W_OK)
+ ZI_WAIT_RECV(1000, ZBR_WNV_OK)
// Now mark the device as ready, writing 0x55 in memory slot 0x0F00
ZI_SEND(ZBS_WNV_INITZNPHC) // Init NV ZNP Has Configured
ZI_WAIT_RECV_FUNC(1000, ZBR_WNV_INIT_OK, &ZNP_CheckNVWrite)
@@ -513,16 +512,16 @@ static const Zigbee_Instruction zb_prog[] PROGMEM = {
ZI_MQTT_STATE(ZIGBEE_STATUS_RESET_CONF, kResetting)
ZI_ON_ERROR_GOTO(ZIGBEE_LABEL_ABORT)
ZI_SEND(ZBS_FACTRES) // factory reset
- ZI_WAIT_RECV(1000, ZBR_W_OK)
+ ZI_WAIT_RECV(1000, ZBR_WNV_OK)
ZI_SEND(ZBS_RESET) // reset device
ZI_WAIT_RECV(5000, ZBR_RESET)
ZI_SEND(ZBS_W_LOGTYP_ROUTER) // write Logical Type = router
- ZI_WAIT_RECV(1000, ZBR_W_OK)
+ ZI_WAIT_RECV(1000, ZBR_WNV_OK)
ZI_LABEL(ZIGBEE_LABEL_FACT_RESET_ROUTER_DEVICE_POST)
ZI_SEND(ZBS_W_ALL_PAN) // write universal PAN ID = 0xFFFF
- ZI_WAIT_RECV(1000, ZBR_W_OK)
+ ZI_WAIT_RECV(1000, ZBR_WNV_OK)
ZI_SEND(ZBS_W_ALL_CHANN) // write Allows all CHANNELS = 0x07FFF800, 11-26
- ZI_WAIT_RECV(1000, ZBR_W_OK)
+ ZI_WAIT_RECV(1000, ZBR_WNV_OK)
// Now mark the device as ready, writing 0x55 in memory slot 0x0F00
ZI_SEND(ZBS_WNV_INITZNPHC) // Init NV ZNP Has Configured
@@ -550,11 +549,11 @@ static const Zigbee_Instruction zb_prog[] PROGMEM = {
ZI_MQTT_STATE(ZIGBEE_STATUS_RESET_CONF, kResetting)
ZI_ON_ERROR_GOTO(ZIGBEE_LABEL_ABORT)
ZI_SEND(ZBS_FACTRES) // factory reset
- ZI_WAIT_RECV(1000, ZBR_W_OK)
+ ZI_WAIT_RECV(1000, ZBR_WNV_OK)
ZI_SEND(ZBS_RESET) // reset device
ZI_WAIT_RECV(5000, ZBR_RESET)
ZI_SEND(ZBS_W_LOGTYP_DEVICE) // write Logical Type = router
- ZI_WAIT_RECV(1000, ZBR_W_OK)
+ ZI_WAIT_RECV(1000, ZBR_WNV_OK)
ZI_GOTO(ZIGBEE_LABEL_FACT_RESET_ROUTER_DEVICE_POST)
// Error: version of Z-Stack is not supported
diff --git a/tasmota/xdrv_34_wemos_motor_v1.ino b/tasmota/xdrv_34_wemos_motor_v1.ino
index cfd9de2d2..b38a863e1 100644
--- a/tasmota/xdrv_34_wemos_motor_v1.ino
+++ b/tasmota/xdrv_34_wemos_motor_v1.ino
@@ -1,7 +1,7 @@
/*
xdrv_34_wemos_motor_v1.ino - Support for I2C WEMOS motor shield (6612FNG)
- Copyright (C) 2021 Denis Sborets and Theo Arends
+ Copyright (C) 2021 Denis Sborets, Theo Arends, Peter Franck
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -21,6 +21,7 @@
#ifdef USE_WEMOS_MOTOR_V1
/*********************************************************************************************\
* WEMOS_MOTOR_V1 - DC motor driver shield (6612FNG) v 1.0.0
+ * WEMOS_MOTOR_V2 - DC motor driver shield (6612FNG) v 2.0.0 (#define WEMOS_MOTOR_V2)
*
* I2C Address: 0x30
*
@@ -28,10 +29,11 @@
* driver44 ,,{,}
* command:
* RESET - reset a motor shield
- * SETMOTOR - seter motor state
+ * SETMOTOR - set motor state
* motor:
* 0 - motor A
* 1 - motor B
+ * 2 - both motors (V2 only)
* direction:
* 0 - short break
* 1 - CCW
@@ -61,6 +63,38 @@
#define STOP 3
#define STANDBY 4
+#ifdef WEMOS_MOTOR_V2 // Support latest Lolin board
+// #define DEBUG_WEMOS_MOTOR // be more verbose
+#define WEMOS_MOTOR_PID_V2 0x02 // Product ID of V2.0.0
+#define WMTR_V2_BUFFSZ 5 // I2C command buffer size
+enum WEMOS_MOTOR_V2_CMD
+{
+ WV2_GET_SLAVE_STATUS = 0x01,
+ WV2_RESET_SLAVE,
+ WV2_CHANGE_I2C_ADDRESS,
+ WV2_CHANGE_STATUS,
+ WV2_CHANGE_FREQ,
+ WV2_CHANGE_DUTY
+};
+enum WEMOS_MOTOR_V2_STATUS
+{
+ WV2_MOTOR_STATUS_STOP = 0x00,
+ WV2_MOTOR_STATUS_CCW,
+ WV2_MOTOR_STATUS_CW,
+ WV2_MOTOR_STATUS_SHORT_BRAKE,
+ WV2_MOTOR_STATUS_STANDBY
+};
+enum WEMOS_MOTOR_V2_CHANNEL
+{
+ WV2_MOTOR_CH_A=0x00,
+ WV2_MOTOR_CH_B,
+ WV2_MOTOR_CH_BOTH
+};
+const char WemosMotorDriver[] = "WEMOS_MOTOR_V2";
+#else // WEMOS_MOTOR_V2
+const char WemosMotorDriver[] = "WEMOS_MOTOR_V1";
+#endif // WEMOS_MOTOR_V2
+
struct WMOTORV1 {
bool detected = false;
uint8_t motor;
@@ -69,31 +103,75 @@ struct WMOTORV1 {
void WMotorV1Detect(void)
{
if (I2cSetDevice(WEMOS_MOTOR_V1_ADDR)) {
+#ifdef WEMOS_MOTOR_V2
+ uint8_t i2c_data[WMTR_V2_BUFFSZ];
+ // Check product ID & version
+ i2c_data[0] = WV2_GET_SLAVE_STATUS;
+ WMotorV2command(i2c_data, 1);
+ if (i2c_data[0] == WEMOS_MOTOR_PID_V2) {
+ #ifdef DEBUG_WEMOS_MOTOR
+ AddLog(LOG_LEVEL_INFO, PSTR("WEM: %s Rev. %u found"), WemosMotorDriver, i2c_data[1]);
+ #endif // DEBUG_WEMOS_MOTOR
+ } else { return; }
+#endif // WEMOS_MOTOR_V2
WMotorV1.detected = true;
- I2cSetActiveFound(WEMOS_MOTOR_V1_ADDR, "WEMOS_MOTOR_V1");
+ I2cSetActiveFound(WEMOS_MOTOR_V1_ADDR, WemosMotorDriver);
WMotorV1Reset();
}
}
void WMotorV1Reset(void)
{
-// Wire.begin();
WMotorV1SetFrequency(WEMOS_MOTOR_V1_FREQ);
}
void WMotorV1SetFrequency(uint32_t freq)
{
+#ifdef WEMOS_MOTOR_V2
+ uint8_t i2c_data[WMTR_V2_BUFFSZ];
+ i2c_data[0] = WV2_CHANGE_FREQ;
+ i2c_data[1] = WV2_MOTOR_CH_BOTH;
+ i2c_data[2] = (uint8_t)(freq & 0xff);
+ i2c_data[3] = (uint8_t)((freq >> 8) & 0xff);
+ i2c_data[4] = (uint8_t)((freq >> 16) & 0xff);
+ WMotorV2command(i2c_data, 5);
+#else // WEMOS_MOTOR_V2
Wire.beginTransmission(WEMOS_MOTOR_V1_ADDR);
Wire.write(((byte)(freq >> 16)) & (byte)0x0f);
Wire.write((byte)(freq >> 16));
Wire.write((byte)(freq >> 8));
Wire.write((byte)freq);
- Wire.endTransmission(); // stop transmitting
-// delay(100);
+ Wire.endTransmission();
+#endif // WEMOS_MOTOR_V2
}
void WMotorV1SetMotor(uint8_t motor, uint8_t dir, float pwm_val)
{
+#ifdef WEMOS_MOTOR_V2
+ uint8_t i2c_data[WMTR_V2_BUFFSZ];
+ // send command
+ uint8_t cmd = dir;
+ if (cmd == 0 || cmd == 3) {
+ cmd ^= 3; // short brake and stop swapped
+ }
+ i2c_data[0] = WV2_CHANGE_STATUS;
+ i2c_data[1] = (uint8_t)motor;
+ i2c_data[2] = (uint8_t)cmd;
+ WMotorV2command(i2c_data, 3);
+ #ifdef DEBUG_WEMOS_MOTOR
+ AddLog(LOG_LEVEL_INFO, PSTR("WEM: Ch: %u Cmd: %u"), motor, cmd);
+ #endif // DEBUG_WEMOS_MOTOR
+ // set duty cycle
+ uint16_t duty = (pwm_val > 100.0) ? 10000 : (uint16_t)(pwm_val * 100);
+ #ifdef DEBUG_WEMOS_MOTOR
+ AddLog(LOG_LEVEL_INFO, PSTR("WEM: Duty: %u"), duty);
+ #endif // DEBUG_WEMOS_MOTOR
+ i2c_data[0] = WV2_CHANGE_DUTY;
+ i2c_data[1] = (uint8_t)motor;
+ i2c_data[2] = (uint8_t)(duty & 0xff);
+ i2c_data[3] = (uint8_t)((duty >> 8) & 0xff);
+ WMotorV2command(i2c_data,4);
+#else // WEMOS_MOTOR_V2
Wire.beginTransmission(WEMOS_MOTOR_V1_ADDR);
Wire.write(motor | (byte)0x10);
Wire.write(dir);
@@ -106,7 +184,7 @@ void WMotorV1SetMotor(uint8_t motor, uint8_t dir, float pwm_val)
Wire.write((byte)(_pwm_val >> 8));
Wire.write((byte)_pwm_val);
Wire.endTransmission();
-// delay(100);
+#endif // WEMOS_MOTOR_V2
}
bool WMotorV1Command(void)
@@ -132,8 +210,13 @@ bool WMotorV1Command(void)
char *command = strtok(XdrvMailbox.data, ",");
if (strcmp(command, "RESET") == 0) {
+#ifdef WEMOS_MOTOR_V2 // do a 'real' reset
+ uint8_t i2c_data[WMTR_V2_BUFFSZ];
+ i2c_data[0] = WV2_RESET_SLAVE;
+ WMotorV2command(i2c_data, 1);
+#endif // WEMOS_MOTOR_V2
WMotorV1Reset();
- Response_P(PSTR("{\"WEMOS_MOTOR_V1\":{\"RESET\":\"OK\"}}"));
+ Response_P(PSTR("{\"%s\":{\"RESET\":\"OK\"}}"), WemosMotorDriver);
return true;
}
@@ -148,13 +231,39 @@ bool WMotorV1Command(void)
}
WMotorV1SetMotor(motor, dir, duty);
- Response_P(PSTR("{\"WEMOS_MOTOR_V1\":{\"SETMOTOR\":\"OK\"}}"));
+ Response_P(PSTR("{\"%s\":{\"SETMOTOR\":\"OK\"}}"), WemosMotorDriver);
return true;
}
}
return false;
}
+#ifdef WEMOS_MOTOR_V2
+void WMotorV2command(uint8_t *data, uint8_t len) // process V2 request
+{
+ int i;
+ Wire.beginTransmission(WEMOS_MOTOR_V1_ADDR);
+ for (i = 0; i < len; i++) {
+ Wire.write(data[i]);
+ }
+ Wire.endTransmission();
+
+ if (data[0] == WV2_GET_SLAVE_STATUS) {
+ Wire.requestFrom(WEMOS_MOTOR_V1_ADDR, 2);
+ } else {
+ Wire.requestFrom(WEMOS_MOTOR_V1_ADDR, 1);
+ }
+ i = 0;
+ bzero(data, WMTR_V2_BUFFSZ);
+ while (Wire.available())
+ {
+ data[i] = Wire.read();
+ i++;
+ }
+}
+#endif // WEMOS_MOTOR_V2
+
+
/*********************************************************************************************\
* Interface
\*********************************************************************************************/
diff --git a/tasmota/xdrv_35_pwm_dimmer.ino b/tasmota/xdrv_35_pwm_dimmer.ino
index 892f05da9..d86082f19 100644
--- a/tasmota/xdrv_35_pwm_dimmer.ino
+++ b/tasmota/xdrv_35_pwm_dimmer.ino
@@ -491,7 +491,7 @@ void PWMDimmerHandleButton(uint32_t button_index, bool pressed)
}
// If we need to adjust the brightness, do it.
- uint32_t negated_device_group_index = -power_button_index;
+ int32_t negated_device_group_index = -power_button_index;
if (bri_offset) {
int32_t bri;
#ifdef USE_PWM_DIMMER_REMOTE
@@ -510,7 +510,7 @@ void PWMDimmerHandleButton(uint32_t button_index, bool pressed)
}
if (new_bri != bri) {
#ifdef USE_DEVICE_GROUPS
- SendDeviceGroupMessage(negated_device_group_index, (dgr_more_to_come ? DGR_MSGTYP_UPDATE_MORE_TO_COME : DGR_MSGTYP_UPDATE_DIRECT), DGR_ITEM_LIGHT_BRI, new_bri);
+ SendDeviceGroupMessage(negated_device_group_index, (dgr_more_to_come ? DGR_MSGTYP_UPDATE_MORE_TO_COME : DGR_MSGTYP_UPDATE_DIRECT), DGR_ITEM_LIGHT_BRI, new_bri, DGR_ITEM_BRI_POWER_ON, new_bri);
#endif // USE_DEVICE_GROUPS
#ifdef USE_PWM_DIMMER_REMOTE
if (active_remote_pwm_dimmer) {
diff --git a/tasmota/xdrv_40_telegram.ino b/tasmota/xdrv_40_telegram.ino
index 3e4cc6f0a..d73f39341 100644
--- a/tasmota/xdrv_40_telegram.ino
+++ b/tasmota/xdrv_40_telegram.ino
@@ -434,14 +434,14 @@ void CmndTmChatId(void) {
void CmndTmSend(void) {
if (!Telegram.send_enable || !strlen(SettingsText(SET_TELEGRAM_CHATID))) {
- ResponseCmndChar(PSTR(D_JSON_FAILED));
+ ResponseCmndFailed();
return;
}
if (XdrvMailbox.data_len > 0) {
String message = XdrvMailbox.data;
String chat_id = SettingsText(SET_TELEGRAM_CHATID);
if (!TelegramSendMessage(chat_id.toInt(), message)) {
- ResponseCmndChar(PSTR(D_JSON_FAILED));
+ ResponseCmndFailed();
return;
}
}
diff --git a/tasmota/xdrv_50_filesystem.ino b/tasmota/xdrv_50_filesystem.ino
index 3651291ac..2ed092618 100644
--- a/tasmota/xdrv_50_filesystem.ino
+++ b/tasmota/xdrv_50_filesystem.ino
@@ -279,7 +279,7 @@ bool TfsFileExists(const char *fname){
bool yes = ffsp->exists(fname);
if (!yes) {
- AddLog(LOG_LEVEL_INFO, PSTR("TFS: File not found"));
+ AddLog(LOG_LEVEL_DEBUG, PSTR("TFS: File not found"));
}
return yes;
}
@@ -353,14 +353,14 @@ bool TfsRenameFile(const char *fname1, const char *fname2) {
* File command execute support
\*********************************************************************************************/
-bool FileRunReady(void) {
+bool UfsExecuteCommandFileReady(void) {
return (UfsData.run_file_pos < 0); // Check file ready to disable concurrency
}
-void FileRunLoop(void) {
- if (FileRunReady() || !ffs_type) { return; }
+void UfsExecuteCommandFileLoop(void) {
+ if (UfsExecuteCommandFileReady() || !ffs_type) { return; }
- if (strlen(UfsData.run_file) && !UfsData.run_file_mutex) {
+ if (TimeReached(TasmotaGlobal.backlog_timer) && strlen(UfsData.run_file) && !UfsData.run_file_mutex) {
File file = ffsp->open(UfsData.run_file, "r");
if (!file || !file.seek(UfsData.run_file_pos)) {
UfsData.run_file_pos = -1; // Signal file ready
@@ -401,7 +401,11 @@ void FileRunLoop(void) {
UfsData.run_file_pos = (file.available()) ? file.position() : -1;
file.close();
if (strlen(cmd_line)) {
+ bool nodelay = (!(!strncasecmp_P(cmd_line, PSTR(D_CMND_DELAY), strlen(D_CMND_DELAY))));
ExecuteCommand(cmd_line, SRC_FILE);
+ if (nodelay) {
+ TasmotaGlobal.backlog_timer = millis(); // Reset backlog_timer which has been set by ExecuteCommand (CommandHandler)
+ }
}
UfsData.run_file_mutex = false;
@@ -410,7 +414,7 @@ void FileRunLoop(void) {
bool UfsExecuteCommandFile(const char *fname) {
// Check for non-concurrency and file existance
- if (FileRunReady() && TfsFileExists(fname)) {
+ if (UfsExecuteCommandFileReady() && TfsFileExists(fname)) {
snprintf(UfsData.run_file, sizeof(UfsData.run_file), fname);
UfsData.run_file_pos = 0; // Signal start of file
return true;
@@ -471,7 +475,7 @@ void UFSDelete(void) {
result = (ufs_type && ufsp->remove(XdrvMailbox.data));
}
if (!result) {
- ResponseCmndChar(PSTR(D_JSON_FAILED));
+ ResponseCmndFailed();
} else {
ResponseCmndDone();
}
@@ -493,7 +497,7 @@ void UFSRename(void) {
}
}
if (!result) {
- ResponseCmndChar(PSTR(D_JSON_FAILED));
+ ResponseCmndFailed();
} else {
ResponseCmndDone();
}
@@ -505,8 +509,12 @@ void UFSRun(void) {
if (UfsExecuteCommandFile(XdrvMailbox.data)) {
ResponseClear();
} else {
- ResponseCmndChar(PSTR(D_JSON_FAILED));
+ ResponseCmndFailed();
}
+ } else {
+ bool not_active = UfsExecuteCommandFileReady();
+ UfsData.run_file_pos = -1;
+ ResponseCmndChar(not_active ? PSTR(D_JSON_DONE) : PSTR(D_JSON_ABORTED));
}
}
@@ -864,6 +872,9 @@ bool Xdrv50(uint8_t function) {
bool result = false;
switch (function) {
+ case FUNC_LOOP:
+ UfsExecuteCommandFileLoop();
+ break;
#ifdef USE_SDCARD
case FUNC_PRE_INIT:
UfsCheckSDCardInit();
diff --git a/tasmota/xdrv_52_berry.ino b/tasmota/xdrv_52_berry.ino
index a37fd78ad..d6e4fa015 100644
--- a/tasmota/xdrv_52_berry.ino
+++ b/tasmota/xdrv_52_berry.ino
@@ -184,8 +184,28 @@ extern "C" {
optimistic_yield(10);
be_return(vm);
}
+
+ // Berry: `save(file:string, f:closure) -> bool`
+ int32_t l_save(struct bvm *vm);
+ int32_t l_save(struct bvm *vm) {
+ int32_t top = be_top(vm); // Get the number of arguments
+ if (top ==2 && be_isstring(vm, 1) && be_isclosure(vm, 2)) { // only 1 argument of type string accepted
+ const char *fname = be_tostring(vm, 1);
+ int32_t ret = be_savecode(vm, fname);
+ be_pushint(vm, ret);
+ be_return(vm); // Return
+ }
+ be_return_nil(vm); // Return nil when something goes wrong
+ }
+
+
}
+// called as a replacement to Berry `print()`
+void berry_log(const char * berry_buf);
+void berry_log(const char * berry_buf) {
+ AddLog(LOG_LEVEL_INFO, PSTR("%s"), berry_buf);
+}
/*********************************************************************************************\
* Handlers for Berry calls and async
@@ -302,7 +322,7 @@ const char berry_prog[] PROGMEM =
// find a key in map, case insensitive, return actual key or nil if not found
"def findkeyi(m,keyi) "
- "keyu=string.toupper(keyi) "
+ "var keyu = string.toupper(keyi) "
"if classof(m) == map "
"for k:m.keys() "
"if string.toupper(k)==keyu || keyi=='?' "
@@ -341,17 +361,17 @@ const char berry_prog[] PROGMEM =
// # split the item when there is an operator, returns a list of (left,op,right)
// # ex: "Dimmer>50" -> ["Dimmer",tasmota_gt,"50"]
"tasmota.find_op = def (item) "
- "pos = charsinstring(item, tasmota._operators) "
+ "var pos = charsinstring(item, tasmota._operators) "
"if pos>=0 "
- "op_split = string.split(item,pos) "
+ "var op_split = string.split(item,pos) "
// #print(op_split)
- "op_left = op_split[0] "
- "op_rest = op_split[1] "
+ "var op_left = op_split[0] "
+ "var op_rest = op_split[1] "
// # iterate through operators
"for op:tasmota._op "
"if string.find(op_rest,op[0]) == 0 "
- "op_func = op[1] "
- "op_right = string.split(op_rest,size(op[0]))[1] "
+ "var op_func = op[1] "
+ "var op_right = string.split(op_rest,size(op[0]))[1] "
"return [op_left,op_func,op_right] "
"end "
"end "
@@ -362,9 +382,9 @@ const char berry_prog[] PROGMEM =
// Rules trigger if match. return true if match, false if not
// Note: condition is not yet managed
"tasmota.try_rule = def (ev, rule, f) "
- "rl_list = tasmota.find_op(rule) "
- "e=ev "
- "rl=string.split(rl_list[0],'#') "
+ "var rl_list = tasmota.find_op(rule) "
+ "var e=ev "
+ "var rl=string.split(rl_list[0],'#') "
"for it:rl "
"found=findkeyi(e,it) "
"if found == nil "
@@ -386,8 +406,8 @@ const char berry_prog[] PROGMEM =
// Run rules, i.e. check each individual rule
// Returns true if at least one rule matched, false if none
"tasmota.exec_rules = def (ev_json) "
- "ev = json.load(ev_json) "
- "ret = false "
+ "var ev = json.load(ev_json) "
+ "var ret = false "
"if ev == nil "
"log('BRY: ERROR, bad json: '+ev_json, 3) "
"end "
@@ -404,7 +424,7 @@ const char berry_prog[] PROGMEM =
"tasmota.timer = def (delay,f) tasmota_timers.push([tasmota.millis(delay),f]) end "
"def _run_deferred() "
- "i=0 "
+ "var i=0 "
"while i 'f'){
+ return 0;
+ }
+ }
+ if (isalpha(t[1])){
+ if (t[1] < 'a' || t[1] > 'f'){
+ return 0;
+ }
+ }
int byte = strtol(t, NULL, 16);
*dest++ = byte;
diff --git a/tasmota/xdsp_15_tm1637.ino b/tasmota/xdsp_15_tm1637.ino
new file mode 100644
index 000000000..79bf3ab93
--- /dev/null
+++ b/tasmota/xdsp_15_tm1637.ino
@@ -0,0 +1,773 @@
+/*
+ xdsp_15_tm1637.ino - Support for TM1637 seven-segment display (upto 6 digits) for Tasmota
+
+ Copyright (C) 2021 Ajith Vasudevan
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see .
+*/
+
+#ifdef USE_DISPLAY
+#ifdef USE_DISPLAY_TM1637
+/*********************************************************************************************\
+ This driver enables the display of numbers (both integers and floats) and basic text
+ on the inexpensive TM1637-based seven-segment modules (tested on both 4- and 6-digit variants).
+ Raw segments can also be displayed.
+ In addition, it is also possible to set brightness (8 levels), clear the display, scroll text, display
+ a rudimentary bar graph, and a Clock (12 hr and 24 hr).
+
+ To use, compile Tasmota with USE_DISPLAY and USE_DISPLAY_TM1637, or build the tasmota-display env.
+
+ The pins to use are "SSPI MOSI" and "SSPI SCLK".
+
+ Connect the TM1637 display module's DIO and CLK pins to any free GPIOs of the ESP8266 module
+ and assign the pins as follows from Tasmota's GUI:
+
+ DIO hardware pin --> "SSPI MOSI"
+ CLK hardware pin --> "SSPI SCLK"
+
+ Once the device restarts the following "Display" commands become available:
+
+
+
+ DisplaySize size {1-6}
+
+ Sets the number of digits to use. This is typically set to the actual number of digits available
+ in the display module. command e.g., "DisplaySize 6"
+
+
+ DisplayClear
+
+ Clears the display, command: "DisplayClear"
+
+
+ DisplayNumber num [,position {0-(NUM_DIGITS-1))} [,leading_zeros {0|1} [,length {1 to NUM_DIGITS}]]]
+
+ Clears and then displays number without decimal. command e.g., "DisplayNumber 1234"
+ Control 'leading zeros', 'length' and 'position' with "DisplayNumber 1234, , , "
+ 'leading zeros' can be 1 or 0 (default), 'length' can be 1 to NUM_DIGITS, 'position' can be 0 (left-most) to NUM_DIGITS (right-most).
+ See function description below for more details.
+
+ DisplayNumberNC num [,position {0-(NUM_DIGITS-1))} [,leading_zeros {0|1} [,length {1 to NUM_DIGITS}]]]
+
+ Display integer number as above, but without clearing first. e.g., "DisplayNumberNC 1234". Usage is same as above.
+
+
+
+ DisplayFloat num [,position {0-(NUM_DIGITS-1)} [,precision {0-NUM_DIGITS} [,length {1 to NUM_DIGITS}]]]
+
+ Clears and then displays float (with decimal point) command e.g., "DisplayFloat 12.34"
+ See function description below for more details.
+
+
+
+ DisplayFloatNC num [,position {0-(NUM_DIGITS-1)} [,precision {0-NUM_DIGITS} [,length {1 to NUM_DIGITS}]]]
+
+ Displays float (with decimal point) as above, but without clearing first. command e.g., "DisplayFloatNC 12.34"
+ See function description below for more details.
+
+
+
+ DisplayBrightness num {0-8}
+
+ Set brightness (0 (off) to 8) command e.g., "DisplayBrightness 2"
+
+
+
+ DisplayRaw position {0-(NUM_DIGITS-1)},length {1 to NUM_DIGITS}, num1 [, num2[, num3[, num4[, ...upto NUM_DIGITS numbers]]]]]
+
+ Takes upto NUM_DIGITS comma-separated integers (0-255) and displays raw segments. Each number represents a
+ 7-segment digit. Each 8-bit number represents individual segments of a digit.
+ For example, the command "DisplayRaw 0, 4, 255, 255, 255, 255" would display "[8.8.8.8.]"
+
+
+
+ DisplayText text [, position {0-(NUM_DIGITS-1)} [,length {1 to NUM_DIGITS}]]
+
+ Clears and then displays basic text. command e.g., "DisplayText ajith vasudevan"
+ Control 'length' and 'position' with "DisplayText , , "
+ 'length' can be 1 to NUM_DIGITS, 'position' can be 0 (left-most) to NUM_DIGITS-1 (right-most)
+ A caret(^) symbol in the text input is dispayed as the degrees(°) symbol. This is useful for displaying Temperature!
+ For example, the command "DisplayText 22.5^" will display "22.5°".
+
+
+ DisplayTextNC text [, position {0-NUM_DIGITS-1} [,length {1 to NUM_DIGITS}]]
+
+ Clears first, then displays text. Usage is same as above.
+
+
+
+ DisplayScrollText text
+
+ Displays scrolling text.
+
+
+
+ DisplayScrollDelay delay {0-15} // default = 4
+
+ Sets the speed of text scroll. Smaller delay = faster scrolling.
+
+
+
+ DisplayLevel num {0-100}
+
+ Display a horizontal bar graph (0-100) command e.g., "DisplayLevel 50" will display [|||| ]
+
+
+
+ DisplayClock 1|2|0
+
+ Displays a clock.
+ Commands "DisplayClock 1" // 12 hr format
+ "DisplayClock 2" // 24 hr format
+ "DisplayClock 0" // turn off clock
+
+\*********************************************************************************************/
+
+#define XDSP_15 15
+#include "SevenSegmentTM1637.h"
+
+SevenSegmentTM1637 *display;
+bool showClock = false;
+bool clock24 = false;
+char tm[5];
+char msg[60];
+uint32_t NUM_DIGITS = 4;
+uint32_t prev_num_digits = 4;
+bool scroll = false;
+uint32_t scrolldelay = 4;
+uint32_t scrollindex = 0;
+uint32_t iteration = 0;
+uint32_t brightness = 5;
+
+
+#define BRIGHTNESS_MIN 0 // Display OFF
+#define BRIGHTNESS_MAX 8
+#define CMD_MAX_LEN 55
+#define LEVEL_MIN 0
+#define LEVEL_MAX 100
+#define SCROLL_MAX_LEN 50
+
+char scrolltext[CMD_MAX_LEN];
+
+/*********************************************************************************************\
+* Init function
+\*********************************************************************************************/
+bool TM1637Init(void) {
+ display = new SevenSegmentTM1637(Pin(GPIO_SSPI_SCLK), Pin(GPIO_SSPI_MOSI) );
+ NUM_DIGITS = Settings.display_size > 3 ? Settings.display_size : 4;
+ Settings.display_size = NUM_DIGITS;
+ display->begin(NUM_DIGITS, 1);
+ display->setBacklight(brightness * 10);
+ clearDisplay();
+ Settings.display_model = XDSP_15;
+ AddLog(LOG_LEVEL_INFO, PSTR("DSP: TM1637 display driver initialized"));
+ return true;
+}
+
+
+/*********************************************************************************************\
+* Displays number without decimal, with/without leading zeros, specifying start-position
+* and length, optionally skipping clearing display before displaying the number.
+* commands: DisplayNumber num [,position {0-(NUM_DIGITS-1)} [,leading_zeros {0|1} [,length {1 to NUM_DIGITS}]]]
+* DisplayNumberNC num [,position {0-(NUM_DIGITS-1)} [,leading_zeros {0|1} [,length {1 to NUM_DIGITS}]]] // "NC" --> "No Clear"
+\*********************************************************************************************/
+bool CmndTM1637Number(bool clear) {
+ char sNum[CMD_MAX_LEN];
+ char sLeadingzeros[CMD_MAX_LEN];
+ char sPosition[CMD_MAX_LEN];
+ char sLength[CMD_MAX_LEN];
+ uint8_t length = 0;
+ bool leadingzeros = false;
+ uint8_t position = 0;
+
+ uint32_t num = 0;
+
+ switch (ArgC())
+ {
+ case 4 :
+ subStr(sLength, XdrvMailbox.data, ",", 4);
+ length = atoi(sLength);
+ case 3 :
+ subStr(sLeadingzeros, XdrvMailbox.data, ",", 3);
+ leadingzeros = atoi(sLeadingzeros);
+ case 2 :
+ subStr(sPosition, XdrvMailbox.data, ",", 2);
+ position = atoi(sPosition);
+ case 1 :
+ subStr(sNum, XdrvMailbox.data, ",", 1);
+ num = atof(sNum);
+ }
+
+
+ if((position < 0) || (position > (NUM_DIGITS-1))) position = 0;
+
+ AddLog(LOG_LEVEL_DEBUG, PSTR("LOG: TM1637: num=%d"), num);
+ AddLog(LOG_LEVEL_DEBUG, PSTR("LOG: TM1637: position=%d"), position);
+ AddLog(LOG_LEVEL_DEBUG, PSTR("LOG: TM1637: leadingzeros=%d"), leadingzeros);
+ AddLog(LOG_LEVEL_DEBUG, PSTR("LOG: TM1637: length=%d"), length);
+
+ if(clear) clearDisplay();
+
+ char txt[30];
+ snprintf_P(txt, sizeof(txt), PSTR("%d"), num);
+ if(!length) length = strlen(txt);
+ if((length < 0) || (length > NUM_DIGITS)) length = NUM_DIGITS;
+
+ char pad = (leadingzeros ? '0': ' ');
+ uint32_t i = position;
+ uint8_t rawBytes[1];
+ rawBytes[0] = display->encode(pad);
+ for(; iNUM_DIGITS) break;
+ display->printRaw(rawBytes, 1, i);
+ }
+
+ for(uint32_t j = 0; i< position + length; i++, j++) {
+ if(txt[j] == 0) break;
+ rawBytes[0] = display->encode(txt[j]);
+ if(i>NUM_DIGITS) break;
+ display->printRaw(rawBytes, 1, i);
+ }
+
+ return true;
+}
+
+
+
+/*********************************************************************************************\
+* Displays number with decimal, specifying position, precision and length,
+* optionally skipping clearing display before displaying the number.
+* commands: DisplayFloat num [,position {0-(NUM_DIGITS-1)} [,precision {0-NUM_DIGITS} [,length {1 to NUM_DIGITS}]]]
+* DisplayFloatNC num [,position {0-(NUM_DIGITS-1)} [,precision {0-NUM_DIGITS} [,length {1 to NUM_DIGITS}]]] // "NC" --> "No Clear"
+\*********************************************************************************************/
+bool CmndTM1637Float(bool clear) {
+
+ char sNum[CMD_MAX_LEN];
+ char sPrecision[CMD_MAX_LEN];
+ char sPosition[CMD_MAX_LEN];
+ char sLength[CMD_MAX_LEN];
+ uint8_t length = 0;
+ uint8_t precision = NUM_DIGITS;
+ uint8_t position = 0;
+
+ float fnum = 0.0f;
+
+ switch (ArgC())
+ {
+ case 4 :
+ subStr(sLength, XdrvMailbox.data, ",", 4);
+ length = atoi(sLength);
+ case 3 :
+ subStr(sPrecision, XdrvMailbox.data, ",", 3);
+ precision = atoi(sPrecision);
+ case 2 :
+ subStr(sPosition, XdrvMailbox.data, ",", 2);
+ position = atoi(sPosition);
+ case 1 :
+ subStr(sNum, XdrvMailbox.data, ",", 1);
+ fnum = atof(sNum);
+ }
+
+
+ if((position < 0) || (position > (NUM_DIGITS-1))) position = 0;
+ if((precision < 0) || (precision > NUM_DIGITS)) precision = NUM_DIGITS;
+
+ if(clear) clearDisplay();
+
+ char txt[30];
+ ext_snprintf_P(txt, sizeof(txt), PSTR("%*_f"), precision, &fnum);
+
+ if(!length) length = strlen(txt);
+ if((length <= 0) || (length > NUM_DIGITS)) length = NUM_DIGITS;
+
+ char s[30];
+ ext_snprintf_P(s, sizeof(s), PSTR("LOG: TM1637: num=%*_f"), 4, &fnum);
+ AddLog(LOG_LEVEL_DEBUG, PSTR("%s"), s);
+ AddLog(LOG_LEVEL_DEBUG, PSTR("LOG: TM1637: precision=%d"), precision);
+ AddLog(LOG_LEVEL_DEBUG, PSTR("LOG: TM1637: length=%d"), length);
+ uint8_t rawBytes[1];
+ for(uint32_t i=0, j=0; iencode(txt[i]);
+ if(txt[i+1] == '.') {
+ rawBytes[0] = rawBytes[0] | 128;
+ i++;
+ length++;
+ }
+ if((j+position) > NUM_DIGITS) break;
+ display->printRaw(rawBytes, 1, j+position);
+ }
+
+ return true;
+}
+
+
+// /*********************************************************************************************\
+// * Clears the display
+// * Command: DisplayClear
+// \*********************************************************************************************/
+bool CmndTM1637Clear(void) {
+ clearDisplay();
+ sprintf(msg, PSTR("Cleared"));
+ XdrvMailbox.data = msg;
+ return true;
+}
+
+
+void clearDisplay (void) {
+ unsigned char arr[] = {0};
+ AddLog(LOG_LEVEL_DEBUG, PSTR("Clearing digit %d"), NUM_DIGITS);
+ for(int i=0; iprintRaw(arr, 1, i);
+}
+
+
+/*********************************************************************************************\
+* Display scrolling text
+* Command: DisplayScrollText text
+\*********************************************************************************************/
+bool CmndTM1637ScrollText(void) {
+
+ AddLog(LOG_LEVEL_DEBUG, PSTR("LOG: TM1637: text=%s"), XdrvMailbox.data);
+
+ if(XdrvMailbox.data_len > SCROLL_MAX_LEN) {
+ snprintf(msg, sizeof(msg), PSTR("Text too long. Length should be less than %d"), SCROLL_MAX_LEN);
+ XdrvMailbox.data = msg;
+ return false;
+ } else {
+ snprintf(scrolltext, sizeof(scrolltext), PSTR("%s"), XdrvMailbox.data);
+ scrolltext[XdrvMailbox.data_len] = 0;
+ scrollindex = 0;
+ scroll = true;
+ return true;
+ }
+
+}
+
+
+
+/*********************************************************************************************\
+* Sets the scroll delay for scrolling text.
+* Command: DisplayScrollDelay delay {0-15} // default = 4
+\*********************************************************************************************/
+bool CmndTM1637ScrollDelay(void) {
+ if(scrolldelay<0) scrolldelay=0;
+ scrolldelay = XdrvMailbox.payload;
+ return true;
+}
+
+
+
+/*********************************************************************************************\
+* Scrolls a given string. Called every 50ms
+\*********************************************************************************************/
+void scrollText(void) {
+ if(scroll) {
+ iteration++;
+ if(scrolldelay) iteration = iteration % scrolldelay;
+ else iteration = 0;
+ if(iteration) return;
+
+ if(scrollindex > strlen(scrolltext)) {
+ scroll = false;
+ scrollindex = 0;
+ return;
+ }
+ bool clr = false;
+ uint8_t rawBytes[1];
+ for(uint32_t i=0, j=scrollindex; i< strlen(scrolltext); i++, j++) {
+ if(i > (NUM_DIGITS-1)) break;
+ if(scrolltext[j] == 0) {clr = true;};
+ char charToDisp = (clr ? ' ' : scrolltext[j]);
+ rawBytes[0] = display->encode(charToDisp);
+ display->printRaw(rawBytes, 1, i);
+ }
+ scrollindex++;
+ }
+}
+
+
+
+
+
+
+/*********************************************************************************************\
+* Displays a horizontal bar graph. Takes a percentage number (0-100) as input
+* Command: DisplayLevel level {0-100}
+\*********************************************************************************************/
+bool CmndTM1637Level(void) {
+ uint16_t val = XdrvMailbox.payload;
+ if((val < LEVEL_MIN) || (val > LEVEL_MAX)) {
+ sprintf(msg, PSTR("Level should be a number in the range [%d, %d]"), LEVEL_MIN, LEVEL_MAX);
+ XdrvMailbox.data = msg;
+ return false;
+ }
+
+ uint8_t totalBars = 2*NUM_DIGITS;
+ AddLog(LOG_LEVEL_DEBUG, PSTR("LOG: TM1637: CmndTM1637Level totalBars=%d"), totalBars);
+ float barsToDisplay = totalBars * val / 100.0f;
+ char txt[5];
+ ext_snprintf_P(txt, sizeof(txt), PSTR("%*_f"), 1, &barsToDisplay);
+ AddLog(LOG_LEVEL_DEBUG, PSTR("LOG: TM1637: CmndTM1637Level barsToDisplay=%s"), txt);
+ char s[4];
+ ext_snprintf_P(s, sizeof(s), PSTR("%*_f"), 0, &barsToDisplay);
+ uint8_t numBars = atoi(s);
+ AddLog(LOG_LEVEL_DEBUG, PSTR("LOG: TM1637: CmndTM1637Level numBars=%d"), numBars);
+
+ clearDisplay();
+ uint8_t rawBytes[1];
+ for(int i=1; i<=numBars; i++) {
+ uint8_t digit = (i-1) / 2;
+ AddLog(LOG_LEVEL_DEBUG, PSTR("LOG: TM1637: CmndTM1637Level digit=%d"), digit);
+ uint8_t value = (((i%2) == 0) ? 54 : 48);
+ AddLog(LOG_LEVEL_DEBUG, PSTR("LOG: TM1637: CmndTM1637Level value=%d"), value);
+ rawBytes[0] = value;
+ display->printRaw(rawBytes, 1, digit);
+ }
+ return true;
+}
+
+
+/*********************************************************************************************\
+* Display arbitrary data on the display module
+* Command: DisplayRaw position {0-(NUM_DIGITS-1)},length {1 to NUM_DIGITS}, a [, b[, c[, d[...upto NUM_DIGITS]]]]
+* where a,b,c,d... are upto NUM_DIGITS numbers in the range 0-255, each number (byte)
+* corresponding to a single 7-segment digit. Within each byte, bit 0 is segment A,
+* bit 1 is segment B etc. The function may either set the entire display
+* or any desired part using the length and position parameters.
+\*********************************************************************************************/
+bool CmndTM1637Raw(void) {
+ uint8_t DATA[6] = { 0, 0, 0, 0, 0, 0 };
+
+ char as[CMD_MAX_LEN];
+ char bs[CMD_MAX_LEN];
+ char cs[CMD_MAX_LEN];
+ char ds[CMD_MAX_LEN];
+ char es[CMD_MAX_LEN];
+ char fs[CMD_MAX_LEN];
+
+ char sLength[CMD_MAX_LEN];
+ char sPos[CMD_MAX_LEN];
+
+
+ uint32_t position = 0;
+ uint32_t length = 0;
+
+ switch (ArgC())
+ {
+ case 8 :
+ subStr(fs, XdrvMailbox.data, ",", 8);
+ DATA[5] = atoi(fs);
+ case 7 :
+ subStr(es, XdrvMailbox.data, ",", 7);
+ DATA[4] = atoi(es);
+ case 6 :
+ subStr(ds, XdrvMailbox.data, ",", 6);
+ DATA[3] = atoi(ds);
+ case 5 :
+ subStr(cs, XdrvMailbox.data, ",", 5);
+ DATA[2] = atoi(cs);
+ case 4 :
+ subStr(bs, XdrvMailbox.data, ",", 4);
+ DATA[1] = atoi(bs);
+ case 3 :
+ subStr(as, XdrvMailbox.data, ",", 3);
+ DATA[0] = atoi(as);
+ case 2 :
+ subStr(sLength, XdrvMailbox.data, ",", 2);
+ length = atoi(sLength);
+ case 1 :
+ subStr(sPos, XdrvMailbox.data, ",", 1);
+ position = atoi(sPos);
+ }
+
+ if(!length) length = ArgC() - 2;
+ if(length < 0 || length > NUM_DIGITS) length = NUM_DIGITS;
+ if(position < 0 || position > (NUM_DIGITS-1)) position = 0;
+
+
+ AddLog(LOG_LEVEL_DEBUG, PSTR("LOG: TM1637: a=%d"), DATA[0]);
+ AddLog(LOG_LEVEL_DEBUG, PSTR("LOG: TM1637: b=%d"), DATA[1]);
+ AddLog(LOG_LEVEL_DEBUG, PSTR("LOG: TM1637: c=%d"), DATA[2]);
+ AddLog(LOG_LEVEL_DEBUG, PSTR("LOG: TM1637: d=%d"), DATA[3]);
+ AddLog(LOG_LEVEL_DEBUG, PSTR("LOG: TM1637: e=%d"), DATA[4]);
+ AddLog(LOG_LEVEL_DEBUG, PSTR("LOG: TM1637: f=%d"), DATA[5]);
+
+ AddLog(LOG_LEVEL_DEBUG, PSTR("LOG: TM1637: length=%d"), length);
+ AddLog(LOG_LEVEL_DEBUG, PSTR("LOG: TM1637: position=%d"), position);
+
+ uint8_t rawBytes[1];
+ for(uint32_t i=position; i(NUM_DIGITS-1)) break;
+ rawBytes[0] = DATA[i-position];
+ display->printRaw(rawBytes, 1, i);
+ }
+
+ return true;
+}
+
+
+
+/*********************************************************************************************\
+* Display a given string.
+* Text can be placed at arbitrary location on the display using the length and
+* position parameters without affecting the rest of the display.
+* Command: DisplayText text [, position {0-(NUM_DIGITS-1)} [,length {1 to NUM_DIGITS}]]
+\*********************************************************************************************/
+bool CmndTM1637Text(bool clear) {
+ char sString[CMD_MAX_LEN + 1];
+ char sPosition[CMD_MAX_LEN];
+ char sLength[CMD_MAX_LEN];
+ uint8_t length = 0;
+ uint8_t position = 0;
+
+ switch (ArgC())
+ {
+ case 3 :
+ subStr(sLength, XdrvMailbox.data, ",", 3);
+ length = atoi(sLength);
+ case 2 :
+ subStr(sPosition, XdrvMailbox.data, ",", 2);
+ position = atoi(sPosition);
+ case 1 :
+ subStr(sString, XdrvMailbox.data, ",", 1);
+ }
+
+
+ if((position < 0) || (position > (NUM_DIGITS-1))) position = 0;
+
+ AddLog(LOG_LEVEL_DEBUG, PSTR("LOG: TM1637: sString=%s"), sString);
+ AddLog(LOG_LEVEL_DEBUG, PSTR("LOG: TM1637: position=%d"), position);
+ AddLog(LOG_LEVEL_DEBUG, PSTR("LOG: TM1637: length=%d"), length);
+
+ if(clear) clearDisplay();
+
+ if(!length) length = strlen(sString);
+ if((length < 0) || (length > NUM_DIGITS)) length = NUM_DIGITS;
+
+ uint32_t i = position;
+ uint8_t rawBytes[1];
+ for(uint32_t j = 0; i< position + length; i++, j++) {
+ if(i > (NUM_DIGITS-1)) break;
+ if(sString[j] == 0) break;
+ rawBytes[0] = display->encode(sString[j]);
+ if(sString[j+1] == '.') {
+ rawBytes[0] = rawBytes[0] | 128;
+ j++;
+ } else if(sString[j] == '^') {
+ rawBytes[0] = 1 | 2 | 32 | 64;
+ }
+ display->printRaw(rawBytes, 1, i);
+ }
+
+ return true;
+}
+
+
+/*********************************************************************************************\
+* Sets brightness of the display.
+* Command: DisplayBrightness {0-8} // 0 => off
+\*********************************************************************************************/
+bool CmndTM1637Brightness(void) {
+
+ uint16_t val = XdrvMailbox.payload;
+ if(ArgC() == 0) {
+ XdrvMailbox.payload = brightness;
+ return true;
+ }
+
+ if((val < BRIGHTNESS_MIN) || (val > BRIGHTNESS_MAX)) {
+ sprintf(msg, PSTR("Brightness should be a number in the range [%d, %d]"), BRIGHTNESS_MIN, BRIGHTNESS_MAX);
+ XdrvMailbox.data = msg;
+ return false;
+ }
+ brightness = val;
+
+ display->setBacklight(brightness*10);
+ return true;
+}
+
+
+
+/*********************************************************************************************\
+* Displays a clock.
+* Command: DisplayClock 1 // 12-hour format
+* DisplayClock 2 // 24-hour format
+* DisplayClock 0 // turn off clock and clear
+\*********************************************************************************************/
+bool CmndTM1637Clock(void) {
+
+ showClock = XdrvMailbox.payload;
+
+ if(ArgC() == 0) XdrvMailbox.payload = 1;
+ if(XdrvMailbox.payload > 1) clock24 = true;
+ else if(XdrvMailbox.payload == 1) clock24 = false;
+
+ AddLog(LOG_LEVEL_DEBUG, PSTR("LOG: TM1637: showClock=%d"), showClock);
+ AddLog(LOG_LEVEL_DEBUG, PSTR("LOG: TM1637: clock24=%d"), clock24);
+
+ if(!showClock) {
+ clearDisplay();
+ }
+ return true;
+}
+
+
+/*********************************************************************************************\
+* refreshes the time if clock is displayed
+\*********************************************************************************************/
+void showTime() {
+ uint8_t hr = RtcTime.hour;
+ uint8_t mn = RtcTime.minute;
+ // uint8_t hr = 1;
+ // uint8_t mn = 0;
+ char z = ' ';
+ if(clock24) {
+ z = '0';
+ } else {
+ if(hr > 12) hr -= 12;
+ if(hr == 0) hr = 12;
+ }
+
+ if(hr < 10) {
+ if(mn < 10) snprintf(tm, sizeof(tm), PSTR("%c%d0%d"), z, hr, mn);
+ else snprintf(tm, sizeof(tm), PSTR("%c%d%d"), z, hr, mn);
+ } else {
+ if(mn < 10) snprintf(tm, sizeof(tm), PSTR("%d0%d"), hr, mn);
+ else snprintf(tm, sizeof(tm), PSTR("%d%d"), hr, mn);
+ }
+ uint8_t rawBytes[1];
+ for(uint32_t i = 0; i< 4; i++) {
+ rawBytes[0] = display->encode(tm[i]);
+ if((millis() % 1000) > 500 && (i == 1)) rawBytes[0] = rawBytes[0] | 128;
+ display->printRaw(rawBytes, 1, i);
+ }
+}
+
+/*********************************************************************************************\
+* This function is called for all TM1637 Display functions.
+\*********************************************************************************************/
+bool TM1637Cmd(uint8_t fn) {
+ bool result = false;
+ NUM_DIGITS = Settings.display_size;
+ if(prev_num_digits != NUM_DIGITS) { // Cleck for change of display size, and re-init the library
+ AddLog(LOG_LEVEL_DEBUG, PSTR("LOG: TM1637: Size changed. Re-initializing library..."));
+ display = new SevenSegmentTM1637(Pin(GPIO_SSPI_SCLK), Pin(GPIO_SSPI_MOSI) );
+ display->begin(NUM_DIGITS, 1);
+ display->setBacklight(40);
+ clearDisplay();
+ prev_num_digits = NUM_DIGITS;
+ AddLog(LOG_LEVEL_DEBUG, PSTR("LOG: TM1637: Re-initialized library"));
+ }
+
+ if(XdrvMailbox.data_len > CMD_MAX_LEN) {
+ sprintf(msg, PSTR("Command text too long. Please limit it to %d characters"), CMD_MAX_LEN);
+ XdrvMailbox.data = msg;
+ return result;
+ }
+
+ switch (fn) {
+ case FUNC_DISPLAY_CLEAR:
+ result = CmndTM1637Clear();
+ break;
+ case FUNC_DISPLAY_NUMBER :
+ result = CmndTM1637Number(true);
+ break;
+ case FUNC_DISPLAY_NUMBERNC :
+ result = CmndTM1637Number(false);
+ break;
+ case FUNC_DISPLAY_FLOAT :
+ result = CmndTM1637Float(true);
+ break;
+ case FUNC_DISPLAY_FLOATNC :
+ result = CmndTM1637Float(false);
+ break;
+ case FUNC_DISPLAY_BRIGHTNESS:
+ result = CmndTM1637Brightness();
+ break;
+ case FUNC_DISPLAY_RAW:
+ result = CmndTM1637Raw();
+ break;
+ case FUNC_DISPLAY_SEVENSEG_TEXT:
+ result = CmndTM1637Text(true);
+ break;
+ case FUNC_DISPLAY_SEVENSEG_TEXTNC:
+ result = CmndTM1637Text(false);
+ break;
+ case FUNC_DISPLAY_LEVEL:
+ result = CmndTM1637Level();
+ break;
+ case FUNC_DISPLAY_SCROLLTEXT:
+ result = CmndTM1637ScrollText();
+ break;
+ case FUNC_DISPLAY_SCROLLDELAY:
+ result = CmndTM1637ScrollDelay();
+ break;
+ case FUNC_DISPLAY_CLOCK:
+ result = CmndTM1637Clock();
+ break;
+ }
+
+ return result;
+}
+
+
+/*********************************************************************************************\
+ * Interface
+\*********************************************************************************************/
+
+bool Xdsp15(uint8_t function)
+{
+ bool result = false;
+
+ if (FUNC_DISPLAY_INIT_DRIVER == function) {
+ result = TM1637Init(); // init
+ }
+ else if (XDSP_15 == Settings.display_model) {
+ switch (function) {
+ case FUNC_DISPLAY_MODEL:
+ AddLog(LOG_LEVEL_DEBUG, PSTR("LOG: TM1637: FUNC_DISPLAY_MODEL"));
+ result = true;
+ break;
+ case FUNC_DISPLAY_INIT:
+ CmndTM1637Clear();
+ AddLog(LOG_LEVEL_DEBUG, PSTR("LOG: TM1637: FUNC_DISPLAY_INIT"));
+ break;
+ case FUNC_DISPLAY_SEVENSEG_TEXT:
+ case FUNC_DISPLAY_CLEAR:
+ case FUNC_DISPLAY_NUMBER:
+ case FUNC_DISPLAY_FLOAT:
+ case FUNC_DISPLAY_NUMBERNC:
+ case FUNC_DISPLAY_FLOATNC:
+ case FUNC_DISPLAY_BRIGHTNESS:
+ case FUNC_DISPLAY_RAW:
+ case FUNC_DISPLAY_LEVEL:
+ case FUNC_DISPLAY_SEVENSEG_TEXTNC:
+ case FUNC_DISPLAY_SCROLLTEXT:
+ case FUNC_DISPLAY_SCROLLDELAY:
+ case FUNC_DISPLAY_CLOCK:
+ result = TM1637Cmd(function);
+ break;
+ case FUNC_DISPLAY_EVERY_50_MSECOND:
+ scrollText();
+ if(showClock) {
+ showTime();
+ }
+ break;
+ }
+ }
+ return result;
+}
+
+#endif // USE_DISPLAY_TM1637
+#endif // USE_DISPLAY
diff --git a/tasmota/xsns_62_esp32_mi_ble.ino b/tasmota/xsns_62_esp32_mi_ble.ino
index cfa81b5ca..44a475942 100644
--- a/tasmota/xsns_62_esp32_mi_ble.ino
+++ b/tasmota/xsns_62_esp32_mi_ble.ino
@@ -5,6 +5,7 @@
Copyright (C) 2020 Christian Baars and Theo Arends
+ Also Simon Hailes and Robert Klauco
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -23,6 +24,9 @@
--------------------------------------------------------------------------------------------
Version yyyymmdd Action Description
--------------------------------------------------------------------------------------------
+ 0.9.2.1 20210217 changed - make features alos depend on received data - i.e. 'unknown' devices will show what they send.
+ Add MI32Option6 1 to switch to tele/tasmota_ble/ style MQTT independent of HASS discovery.
+ -------
0.9.2.0 20210127 changed - Officially includes as the mi driver when using USE_BLE_ESP32.
-------
0.9.1.9 20201226 changed - All change now.
@@ -124,6 +128,7 @@ struct {
uint32_t ignoreBogusBattery:1;
uint32_t minimalSummary:1; // DEPRECATED!!
uint32_t onlyAliased:1; // only include sensors that are aliased
+ uint32_t MQTTType:1;
} option;
} MI32;
@@ -279,6 +284,8 @@ struct mi_sensor_t{
uint32_t NMT:1;
uint32_t PIR:1;
uint32_t Btn:1;
+ uint32_t events:1;
+ uint32_t pairing:1;
};
uint32_t raw;
} feature;
@@ -369,8 +376,9 @@ void (*const MI32_Commands[])(void) PROGMEM = {
#define MI_MHOC401 11
#define MI_MHOC303 12
#define MI_ATC 13
+#define MI_DOOR 14
-#define MI_MI32_TYPES 13 //count this manually
+#define MI_MI32_TYPES 14 //count this manually
const uint16_t kMI32DeviceID[MI_MI32_TYPES]={
0x0000, // Unkown
@@ -385,7 +393,8 @@ const uint16_t kMI32DeviceID[MI_MI32_TYPES]={
0x0153, // yee-rc
0x0387, // MHO-C401
0x06d3, // MHO-C303
- 0x0a1c // ATC -> this is a fake ID
+ 0x0a1c, // ATC -> this is a fake ID
+ 0x098b // door/window sensor
};
const char kMI32DeviceType0[] PROGMEM = "Unknown";
@@ -401,7 +410,8 @@ const char kMI32DeviceType9[] PROGMEM = "YEERC";
const char kMI32DeviceType10[] PROGMEM ="MHOC401";
const char kMI32DeviceType11[] PROGMEM ="MHOC303";
const char kMI32DeviceType12[] PROGMEM ="ATC";
-const char * kMI32DeviceType[] PROGMEM = {kMI32DeviceType0,kMI32DeviceType1,kMI32DeviceType2,kMI32DeviceType3,kMI32DeviceType4,kMI32DeviceType5,kMI32DeviceType6,kMI32DeviceType7,kMI32DeviceType8,kMI32DeviceType9,kMI32DeviceType10,kMI32DeviceType11,kMI32DeviceType12};
+const char kMI32DeviceType13[] PROGMEM ="DOOR";
+const char * kMI32DeviceType[] PROGMEM = {kMI32DeviceType0,kMI32DeviceType1,kMI32DeviceType2,kMI32DeviceType3,kMI32DeviceType4,kMI32DeviceType5,kMI32DeviceType6,kMI32DeviceType7,kMI32DeviceType8,kMI32DeviceType9,kMI32DeviceType10,kMI32DeviceType11,kMI32DeviceType12,kMI32DeviceType13};
typedef int BATREAD_FUNCTION(int slot);
typedef int UNITWRITE_FUNCTION(int slot, int unit);
@@ -1406,6 +1416,7 @@ uint32_t MIBLEgetSensorSlot(const uint8_t *mac, uint16_t _type, uint8_t counter)
_newSensor.events=0x00;
_newSensor.feature.PIR=1;
_newSensor.feature.NMT=1;
+ _newSensor.feature.events=1;
break;
case MI_MJYD2S:
_newSensor.NMT=0;
@@ -1414,8 +1425,10 @@ uint32_t MIBLEgetSensorSlot(const uint8_t *mac, uint16_t _type, uint8_t counter)
_newSensor.feature.NMT=1;
_newSensor.feature.lux=1;
_newSensor.feature.bat=1;
+ _newSensor.feature.events=1;
break;
case MI_YEERC:
+ case MI_DOOR:
_newSensor.feature.Btn=1;
break;
default:
@@ -1633,15 +1646,25 @@ int MI32parseMiPayload(int _slot, struct mi_beacon_data_t *parsed){
char tmp[20];
BLE_ESP32::dump(tmp, 20, (uint8_t*)&(parsed->payload), parsed->payload.size+3);
if (BLE_ESP32::BLEDebugMode > 0) AddLog(LOG_LEVEL_DEBUG_MORE,PSTR("M32: MI%d payload %s"), _slot, tmp);
+
+ // clear this for every payload
+ MIBLEsensors[_slot].pairing = 0;
+ MIBLEsensors[_slot].eventType.PairBtn = 0;
switch(parsed->payload.type){
case 0x01: // button press
MIBLEsensors[_slot].Btn = pld->Btn.num + (pld->Btn.longPress/2)*6;
+ MIBLEsensors[_slot].feature.Btn = 1;
MIBLEsensors[_slot].eventType.Btn = 1;
MI32.mode.shallTriggerTele = 1;
+ MIBLEsensors[_slot].shallSendMQTT = 1;
break;
- case 0x02:
- res = 0;
+ case 0x02: // related to pair button?
+ MIBLEsensors[_slot].pairing = 1;
+ MIBLEsensors[_slot].eventType.PairBtn = 1;
+ MIBLEsensors[_slot].feature.pairing = 1;
+ MI32.mode.shallTriggerTele = 1;
+ MIBLEsensors[_slot].shallSendMQTT = 1;
break;
case 0x03: {// motion? 1 byte
uint8_t motion = parsed->payload.data[0];
@@ -1651,6 +1674,7 @@ int MI32parseMiPayload(int _slot, struct mi_beacon_data_t *parsed){
float _tempFloat=(float)(pld->temp)/10.0f;
if(_tempFloat<60){
MIBLEsensors[_slot].temp=_tempFloat;
+ MIBLEsensors[_slot].feature.temp = 1;
MIBLEsensors[_slot].eventType.temp = 1;
if (BLE_ESP32::BLEDebugMode > 0) AddLog(LOG_LEVEL_DEBUG_MORE,PSTR("M32: Mode 4: temp updated"));
} else {
@@ -1662,6 +1686,7 @@ int MI32parseMiPayload(int _slot, struct mi_beacon_data_t *parsed){
float _tempFloat=(float)(pld->hum)/10.0f;
if(_tempFloat<101){
MIBLEsensors[_slot].hum=_tempFloat;
+ MIBLEsensors[_slot].feature.hum = 1;
MIBLEsensors[_slot].eventType.hum = 1;
if (BLE_ESP32::BLEDebugMode > 0) AddLog(LOG_LEVEL_DEBUG_MORE,PSTR("M32: Mode 6: hum updated"));
} else {
@@ -1675,17 +1700,20 @@ int MI32parseMiPayload(int _slot, struct mi_beacon_data_t *parsed){
MIBLEsensors[_slot].eventType.noMotion = 1;
}
MIBLEsensors[_slot].eventType.lux = 1;
+ MIBLEsensors[_slot].feature.lux = 1;
// AddLog(LOG_LEVEL_DEBUG,PSTR("M32: Mode 7: U24: %u Lux"), _beacon.lux & 0x00ffffff);
break;
case 0x08:
MIBLEsensors[_slot].moisture=pld->moist;
MIBLEsensors[_slot].eventType.moist = 1;
+ MIBLEsensors[_slot].feature.moist = 1;
if (BLE_ESP32::BLEDebugMode > 0) AddLog(LOG_LEVEL_DEBUG_MORE,PSTR("M32: Mode 8: moisture updated"));
// AddLog(LOG_LEVEL_DEBUG,PSTR("M32: Mode 8: U8: %u Moisture"), _beacon.moist);
break;
case 0x09: // 'conductivity'
MIBLEsensors[_slot].fertility=pld->fert;
MIBLEsensors[_slot].eventType.fert = 1;
+ MIBLEsensors[_slot].feature.fert = 1;
if (BLE_ESP32::BLEDebugMode > 0) AddLog(LOG_LEVEL_DEBUG_MORE,PSTR("M32: Mode 9: fertility updated"));
// AddLog(LOG_LEVEL_DEBUG,PSTR("M32: Mode 9: U16: %u Fertility"), _beacon.fert);
break;
@@ -1696,6 +1724,7 @@ int MI32parseMiPayload(int _slot, struct mi_beacon_data_t *parsed){
break;
}
}
+ MIBLEsensors[_slot].feature.bat = 1;
if(pld->bat<101){
MIBLEsensors[_slot].bat = pld->bat;
MIBLEsensors[_slot].eventType.bat = 1;
@@ -1708,6 +1737,7 @@ int MI32parseMiPayload(int _slot, struct mi_beacon_data_t *parsed){
// AddLog(LOG_LEVEL_DEBUG,PSTR("M32: Mode a: U8: %u %%"), _beacon.bat);
break;
case 0x0d:{
+ MIBLEsensors[_slot].feature.tempHum = 1;
float _tempFloat=(float)(pld->HT.temp)/10.0f;
if(_tempFloat < 60){
MIBLEsensors[_slot].temp = _tempFloat;
@@ -1734,6 +1764,11 @@ int MI32parseMiPayload(int _slot, struct mi_beacon_data_t *parsed){
MIBLEsensors[_slot].eventType.lux = 1;
MIBLEsensors[_slot].NMT = 0;
MI32.mode.shallTriggerTele = 1;
+ MIBLEsensors[_slot].shallSendMQTT = 1;
+ MIBLEsensors[_slot].feature.lux = 1;
+ MIBLEsensors[_slot].feature.NMT = 1;
+ MIBLEsensors[_slot].feature.events=1;
+
// AddLog(LOG_LEVEL_DEBUG,PSTR("M32: PIR: primary"),MIBLEsensors[_slot].lux );
break;
case 0x10:{ // 'formaldehide'
@@ -1758,9 +1793,20 @@ int MI32parseMiPayload(int _slot, struct mi_beacon_data_t *parsed){
MIBLEsensors[_slot].NMT = pld->NMT;
MIBLEsensors[_slot].eventType.NMT = 1;
MI32.mode.shallTriggerTele = 1;
+ MIBLEsensors[_slot].shallSendMQTT = 1;
+ MIBLEsensors[_slot].feature.NMT = 1;
+
// AddLog(LOG_LEVEL_DEBUG,PSTR("M32: Mode 17: NMT: %u seconds"), _beacon.NMT);
} break;
+ case 0x19:{
+ MIBLEsensors[_slot].Btn = uint8_t(parsed->payload.data[0]); // just an 8 bit value in a union.
+ MIBLEsensors[_slot].eventType.Btn = 1;
+ MI32.mode.shallTriggerTele = 1;
+ MIBLEsensors[_slot].shallSendMQTT = 1;
+ MIBLEsensors[_slot].feature.Btn = 1;
+ } break;
+
default: {
AddLog(LOG_LEVEL_DEBUG,PSTR("M32: Unknown MI pld"));
res = 0;
@@ -1894,10 +1940,12 @@ void MI32EverySecond(bool restart){
// AddLog(LOG_LEVEL_DEBUG_MORE, PSTR("M32: onesec"));
MI32TimeoutSensors();
- MI32ShowSomeSensors();
-
- MI32DiscoveryOneMISensor();
- MI32ShowOneMISensor();
+ if (MI32.option.MQTTType == 0){
+ MI32ShowSomeSensors();
+ } else {
+ MI32DiscoveryOneMISensor();
+ MI32ShowOneMISensor();
+ }
// read a battery if
// MI32.batteryreader.slot < filled and !MI32.batteryreader.active
@@ -2122,16 +2170,24 @@ void CmndMi32Option(void){
bool onOff = atoi(XdrvMailbox.data);
switch(XdrvMailbox.index) {
case 0:
- MI32.option.allwaysAggregate = onOff;
+ MI32.option.allwaysAggregate = onOff;
+ ResponseCmndIdxNumber(onOff);
+ return;
break;
case 1:
MI32.option.noSummary = onOff;
+ ResponseCmndIdxNumber(onOff);
+ return;
break;
case 2:
MI32.option.directBridgeMode = onOff;
+ ResponseCmndIdxNumber(onOff);
+ return;
break;
case 4:{
MI32.option.ignoreBogusBattery = onOff;
+ ResponseCmndIdxNumber(onOff);
+ return;
} break;
case 5:{
MI32.option.onlyAliased = onOff;
@@ -2139,9 +2195,17 @@ void CmndMi32Option(void){
// discard all sensors for a restart
MIBLEsensors.clear();
}
+ ResponseCmndIdxNumber(onOff);
+ return;
} break;
+ case 6:{
+ MI32.option.MQTTType = onOff;
+ ResponseCmndIdxNumber(onOff);
+ return;
+ } break;
+
}
- ResponseCmndDone();
+ ResponseCmndIdxError();
}
void MI32KeyListResp(){
@@ -2239,7 +2303,7 @@ void CmndMi32Keys(void){
* Presentation
\*********************************************************************************************/
-const char HTTP_MI32[] PROGMEM = "{s}MI ESP32 v0920{m}%u%s / %u{e}";
+const char HTTP_MI32[] PROGMEM = "{s}MI ESP32 v0921{m}%u%s / %u{e}";
const char HTTP_MI32_ALIAS[] PROGMEM = "{s}%s Alias {m}%s{e}";
const char HTTP_MI32_MAC[] PROGMEM = "{s}%s %s{m}%s{e}";
const char HTTP_RSSI[] PROGMEM = "{s}%s " D_RSSI "{m}%d dBm{e}";
@@ -2517,9 +2581,13 @@ void MI32ShowSomeSensors(){
ResponseTime_P(PSTR(""));
int cnt = 0;
- for (; (MI32.mqttCurrentSlot < numsensors) && (cnt < 4); MI32.mqttCurrentSlot++, cnt++) {
+ int maxcnt = 4;
+ mi_sensor_t *p;
+ for (; (MI32.mqttCurrentSlot < numsensors) && (cnt < maxcnt); MI32.mqttCurrentSlot++, cnt++) {
ResponseAppend_P(PSTR(","));
- MI32GetOneSensorJson(MI32.mqttCurrentSlot, 0);
+ p = &MIBLEsensors[MI32.mqttCurrentSlot];
+
+ MI32GetOneSensorJson(MI32.mqttCurrentSlot, (maxcnt == 1));
int mlen = strlen(TasmotaGlobal.mqtt_data);
// if we ran out of room, leave here.
@@ -2527,6 +2595,7 @@ void MI32ShowSomeSensors(){
MI32.mqttCurrentSlot++;
break;
}
+ cnt++;
}
ResponseAppend_P(PSTR("}"));
MqttPublishPrefixTopic_P(TELE, PSTR(D_RSLT_SENSOR), Settings.flag.mqtt_sensor_retain);
@@ -2557,8 +2626,13 @@ void MI32ShowOneMISensor(){
return;
}
+ if(
#ifdef USE_HOME_ASSISTANT
- if(Settings.flag.hass_discovery){
+ Settings.flag.hass_discovery
+ ||
+#endif //USE_HOME_ASSISTANT
+ MI32.option.MQTTType == 1
+ ){
ResponseTime_P(PSTR(","));
MI32GetOneSensorJson(MI32.mqttCurrentSingleSlot, 1);
@@ -2584,7 +2658,6 @@ void MI32ShowOneMISensor(){
MqttPublish(SensorTopic);
//AddLog_P(LOG_LEVEL_DEBUG,PSTR("M32: %s: show some %d %s"),D_CMND_MI32, MI32.mqttCurrentSlot, TasmotaGlobal.mqtt_data);
}
-#endif //USE_HOME_ASSISTANT
MI32.mqttCurrentSingleSlot++;
}
@@ -2739,18 +2812,31 @@ void MI32ShowTriggeredSensors(){
int sensor = 0;
+ int maxcnt = 4;
+ if(
+#ifdef USE_HOME_ASSISTANT
+ Settings.flag.hass_discovery
+ ||
+#endif //USE_HOME_ASSISTANT
+ MI32.option.MQTTType == 1
+ ){
+ maxcnt = 1;
+ }
+
+
do {
ResponseTime_P(PSTR(""));
int cnt = 0;
- for (; (sensor < numsensors) && (cnt < 4); sensor++) {
- mi_sensor_t *p;
+ mi_sensor_t *p;
+ for (; (sensor < numsensors) && (cnt < maxcnt); sensor++) {
p = &MIBLEsensors[sensor];
if(p->eventType.raw == 0) continue;
if(p->shallSendMQTT==0) continue;
cnt++;
ResponseAppend_P(PSTR(","));
- MI32GetOneSensorJson(sensor, 0);
+ // hide sensor name if HASS or option6
+ MI32GetOneSensorJson(sensor, (maxcnt == 1));
int mlen = strlen(TasmotaGlobal.mqtt_data);
// if we ran out of room, leave here.
@@ -2761,7 +2847,30 @@ void MI32ShowTriggeredSensors(){
}
if (cnt){ // if we got one, then publish
ResponseAppend_P(PSTR("}"));
- MqttPublishPrefixTopic_P(STAT, PSTR(D_RSLT_SENSOR), Settings.flag.mqtt_sensor_retain);
+ if(
+ #ifdef USE_HOME_ASSISTANT
+ Settings.flag.hass_discovery
+ ||
+ #endif //USE_HOME_ASSISTANT
+ MI32.option.MQTTType == 1
+ ){
+ char SensorTopic[60];
+ char idstr[32];
+ const char *alias = BLE_ESP32::getAlias(p->MAC);
+ const char *id = idstr;
+ if (alias && *alias){
+ id = alias;
+ } else {
+ sprintf(idstr, PSTR("%s%02x%02x%02x"),
+ kMI32DeviceType[p->type-1],
+ p->MAC[3], p->MAC[4], p->MAC[5]);
+ }
+ sprintf(SensorTopic, "tele/tasmota_ble/%s",
+ id);
+ MqttPublish(SensorTopic);
+ } else {
+ MqttPublishPrefixTopic_P(STAT, PSTR(D_RSLT_SENSOR), Settings.flag.mqtt_sensor_retain);
+ }
AddLog(LOG_LEVEL_DEBUG,PSTR("M32: %s: triggered %d %s"),D_CMND_MI32, sensor, TasmotaGlobal.mqtt_data);
#ifdef USE_RULES
@@ -2877,21 +2986,25 @@ void MI32Show(bool json)
WSContentSend_PD(HTTP_NEEDKEY, typeName, _MAC, Webserver->client().localIP().toString().c_str(), tmp );
}
- if (p->type==MI_NLIGHT || p->type==MI_MJYD2S) {
-#else
- if (p->type==MI_NLIGHT) {
#endif //USE_MI_DECRYPTION
+
+ if (p->feature.events){
WSContentSend_PD(HTTP_EVENTS, typeName, p->events);
+ }
+ if (p->feature.NMT){
+ // no motion time
if(p->NMT>0) WSContentSend_PD(HTTP_NMT, typeName, p->NMT);
}
- if (p->lux!=0x00ffffff) { // this is the error code -> no valid value
- WSContentSend_PD(HTTP_SNS_ILLUMINANCE, typeName, p->lux);
+ if (p->feature.lux){
+ if (p->lux!=0x00ffffff) { // this is the error code -> no valid value
+ WSContentSend_PD(HTTP_SNS_ILLUMINANCE, typeName, p->lux);
+ }
}
if(p->bat!=0x00){
WSContentSend_PD(HTTP_BATTERY, typeName, p->bat);
}
- if (p->type==MI_YEERC){
+ if (p->feature.Btn){
WSContentSend_PD(HTTP_LASTBUTTON, typeName, p->Btn);
}
if (p->pairing){
diff --git a/tools/decode-status.py b/tools/decode-status.py
index 995d0e111..9ab99763f 100755
--- a/tools/decode-status.py
+++ b/tools/decode-status.py
@@ -245,7 +245,7 @@ a_features = [[
"USE_DISPLAY_ILI9488","USE_DISPLAY_SSD1351","USE_DISPLAY_RA8876","USE_DISPLAY_ST7789",
"USE_DISPLAY_SSD1331","USE_UFILESYS","USE_TIMEPROP","USE_PID",
"USE_BS814A2","USE_SEESAW_SOIL","USE_WIEGAND","USE_NEOPOOL",
- "USE_TOF10120","USE_SDM72","",""
+ "USE_TOF10120","USE_SDM72","USE_DISPLAY_TM1637",""
],[
"","","","",
"","","","",
@@ -282,7 +282,7 @@ else:
obj = json.load(fp)
def StartDecode():
- print ("\n*** decode-status.py v20210122 by Theo Arends and Jacek Ziolkowski ***")
+ print ("\n*** decode-status.py v20210217 by Theo Arends and Jacek Ziolkowski ***")
# print("Decoding\n{}".format(obj))