Merge pull request #3 from arendst/development

Sync fork
This commit is contained in:
crispy78 2021-01-31 11:42:01 +01:00 committed by GitHub
commit 4de7185321
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
321 changed files with 17843 additions and 4394 deletions

View File

@ -7,7 +7,7 @@
- [ ] Only relevant files were touched
- [ ] Only one feature/fix was added per PR and the code change compiles without warnings
- [ ] The code change is tested and works on Tasmota core ESP8266 V.2.7.4.9
- [ ] The code change is tested and works on Tasmota core ESP32 V.1.0.5-rc4
- [ ] The code change is tested and works on Tasmota core ESP32 V.1.0.5-rc6
- [ ] I accept the [CLA](https://github.com/arendst/Tasmota/blob/development/CONTRIBUTING.md#contributor-license-agreement-cla).
_NOTE: The code change must pass CI tests. **Your PR cannot be merged unless tests pass**_

View File

@ -64,6 +64,26 @@ jobs:
name: firmware
path: ./build_output/firmware
tasmota32-core2:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v1
- name: Set up Python
uses: actions/setup-python@v1
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install -U platformio
platformio upgrade --dev
platformio update
- name: Run PlatformIO
run: |
platformio run -e tasmota32-core2
- uses: actions/upload-artifact@v2
with:
name: firmware
path: ./build_output/firmware
tasmota32-minimal:
runs-on: ubuntu-latest
steps:

View File

@ -898,6 +898,29 @@ jobs:
path: ./build_output/firmware
tasmota32-core2:
needs: tasmota_pull
runs-on: ubuntu-latest
continue-on-error: true
steps:
- uses: actions/checkout@v1
- name: Set up Python
uses: actions/setup-python@v1
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install -U platformio
platformio upgrade --dev
platformio update
- name: Run PlatformIO
run: |
platformio run -e tasmota32-core2
- uses: actions/upload-artifact@v2
with:
name: firmware
path: ./build_output/firmware
tasmota32-knx:
needs: tasmota_pull
runs-on: ubuntu-latest
@ -1600,11 +1623,10 @@ jobs:
[ ! -f ./mv_firmware/tasmota32-display.* ] || mv ./mv_firmware/tasmota32-display.* ./firmware/tasmota32/
[ ! -f ./mv_firmware/tasmota32-web*.* ] || mv ./mv_firmware/tasmota32-web*.* ./firmware/tasmota32/
[ ! -f ./mv_firmware/tasmota32-odroidgo.* ] || mv ./mv_firmware/tasmota32-odroidgo.* ./firmware/tasmota32/
[ ! -f ./mv_firmware/tasmota32-core2.* ] || mv ./mv_firmware/tasmota32-core2.* ./firmware/tasmota32/
[ ! -f ./mv_firmware/tasmota32-knx.* ] || mv ./mv_firmware/tasmota32-knx.* ./firmware/tasmota32/
[ ! -f ./mv_firmware/tasmota32* ] || mv ./mv_firmware/tasmota32* ./firmware/tasmota32/languages/
[ ! -f ./mv_firmware/* ] || mv ./mv_firmware/* ./firmware/tasmota/languages/
rm ./firmware/tasmota32/*.gz
rm ./firmware/tasmota32/languages/*.gz
[ ! -f ./tools/Esptool/ESP32/*.* ] || mv ./tools/Esptool/ESP32/*.* ./firmware/tasmota32/ESP32_needed_files/
[ ! -f ./tools/Esptool/Odroid_go/*.* ] || mv ./tools/Esptool/Odroid_go/*.* ./firmware/tasmota32/Odroid_go_needed_files/
[ ! -f ./FIRMWARE.md ] || mv -f ./FIRMWARE.md ./README.md

View File

@ -898,6 +898,29 @@ jobs:
path: ./build_output/firmware
tasmota32-core2:
needs: tasmota_pull
runs-on: ubuntu-latest
continue-on-error: true
steps:
- uses: actions/checkout@v1
- name: Set up Python
uses: actions/setup-python@v1
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install -U platformio
platformio upgrade --dev
platformio update
- name: Run PlatformIO
run: |
platformio run -e tasmota32-core2
- uses: actions/upload-artifact@v2
with:
name: firmware
path: ./build_output/firmware
tasmota32-knx:
needs: tasmota_pull
runs-on: ubuntu-latest
@ -1600,11 +1623,10 @@ jobs:
[ ! -f ./mv_firmware/tasmota32-display.* ] || mv ./mv_firmware/tasmota32-display.* ./firmware/tasmota32/
[ ! -f ./mv_firmware/tasmota32-web*.* ] || mv ./mv_firmware/tasmota32-web*.* ./firmware/tasmota32/
[ ! -f ./mv_firmware/tasmota32-odroidgo.* ] || mv ./mv_firmware/tasmota32-odroidgo.* ./firmware/tasmota32/
[ ! -f ./mv_firmware/tasmota32-core2.* ] || mv ./mv_firmware/tasmota32-core2.* ./firmware/tasmota32/
[ ! -f ./mv_firmware/tasmota32-knx.* ] || mv ./mv_firmware/tasmota32-knx.* ./firmware/tasmota32/
[ ! -f ./mv_firmware/tasmota32* ] || mv ./mv_firmware/tasmota32* ./firmware/tasmota32/languages/
[ ! -f ./mv_firmware/* ] || mv ./mv_firmware/* ./firmware/tasmota/languages/
rm ./firmware/tasmota32/*.gz
rm ./firmware/tasmota32/languages/*.gz
[ ! -f ./tools/Esptool/ESP32/*.* ] || mv ./tools/Esptool/ESP32/*.* ./firmware/tasmota32/ESP32_needed_files/
[ ! -f ./tools/Esptool/Odroid_go/*.* ] || mv ./tools/Esptool/Odroid_go/*.* ./firmware/tasmota32/Odroid_go_needed_files/
[ ! -f ./FIRMWARE.md ] || mv -f ./RELEASENOTES.md ./README.md

View File

@ -2,13 +2,13 @@ name: "Mark or close stale issues and PRs"
on:
schedule:
- cron: "15 05 * * *"
- cron: "30 * * * *"
jobs:
stale:
runs-on: ubuntu-latest
steps:
- uses: actions/stale@v3.0.14
- uses: actions/stale@v3.0.15
with:
repo-token: ${{ secrets.GITHUB_TOKEN }}
days-before-stale: 25

View File

@ -149,6 +149,7 @@
| USE_EZORGB | - | - | - | - | - | - | - |
| USE_EZORTD | - | - | - | - | - | - | - |
| USE_SEESAW_SOIL | - | - | - | - | - | - | - |
| USE_TOF10120 | - | - | - | - | - | - | - |
| | | | | | | | |
| Feature or Sensor | minimal | lite | tasmota | knx | sensors | ir | display | Remarks
| USE_SPI | - | - | - | - | - | - | x |
@ -193,6 +194,7 @@
| USE_RF_SENSOR | - | - | - | - | x | - | - | AlectoV2 only
| USE_HRE | - | - | - | - | x | - | - |
| USE_A4988_STEPPER | - | - | - | - | - | - | - |
| USE_NEOPOOL | - | - | - | - | - | - | - |
| | | | | | | | |
| Feature or Sensor | minimal | lite | tasmota | knx | sensors | ir | display | Remarks
| USE_DISPLAY | - | - | - | - | - | - | x |

View File

@ -3,7 +3,23 @@ All notable changes to this project will be documented in this file.
## [Unreleased] - Development
## [9.2.0.3]
## [9.2.0.4]
### Added
- Function ``AddLog`` to provide logging for up to 128 (LOGSZ) characters to save stack space
- Commands ``ChannelRemap``, ``MultiPWM``, ``AlexaCTRange``, ``PowerOnFade``, ``PWMCT``, ``WhiteBlend`` and ``VirtualCT`` as synonyms for ``SetOption37, 68, 82, 91, 92, 105`` and ``106``
- Commands ``ZbNameKey``, ``ZbDeviceTopic``, ``ZbNoPrefix``, ``ZbEndpointSuffix``, ``ZbNoAutoBind`` and ``ZbNameTopic`` as synonyms for ``SetOption83, 89, 100, 101, 110`` and ``112``
- Commands ``ZbNoAutoBind``, ``ZbReceivedTopic`` and ``ZbOmitDevice`` as synonyms for ``SetOption116, 118`` and ``119``
- Commands ``BuzzerActive`` and ``BuzzerPwm`` as synonyms for ``SetOption67`` and ``111``
- Support for ESP32 ``Module 5`` Wireless Tag Eth01 (#9496)
- Support trailing silence in buzzer tune (#10694)
- Command ``L1MusicSync <0|Off>|<1|On>|<2|Toggle>, 1..10, 1..100>`` to control Sonoff L1 Music Sync mode sensitivity and speed (#10722)
- Command ``Speed2`` to control a once off fade (#10741)
- Zigbee command ``SetOption120 1`` or ``ZbEndpointTopic 1`` to add the endpoint as suffix in topic when using ``SetOption89 1``
### Changed
- Maximum chars in ``AddLog_P`` logging restored from 128 to 700 (MAX_LOGSZ) to solve broken error messages
## [9.2.0.3] 20210122
### Added
- Support for time proportioned (``#define USE_TIMEPROP``) and optional PID (``#define USE_PID``) relay control (#10412)
- Support rotary encoder on Shelly Dimmer (#10407)
@ -12,6 +28,14 @@ All notable changes to this project will be documented in this file.
- Support for up to 4 I2C SEESAW_SOIL Capacitance & Temperature sensors by Peter Franck (#10481)
- ESP8266 Support for 2MB and up linker files with 1MB and up LittleFS
- ESP32 support for TLS MQTT using BearSSL (same as ESP8266)
- Support for 24/26/32/34 bit RFID Wiegand interface (D0/D1) by Sigurd Leuther (#3647)
- Compile time option ``USE_MQTT_TLS_DROP_OLD_FINGERPRINT`` to drop old (less secure) TLS fingerprint
- Command ``SetOption40 0..250`` to disable button functionality if activated for over 0.1 second re-introduced
- Support for SM2135 current selection using GPIO ``SM2135 DAT`` index (#10634)
- Support for ESP32 ``Module 7`` M5stack core2 16MB binary tasmota32-core2.bin (#10635)
- Support for Sugar Valley NeoPool Controller by Norbert Richter (#10637)
- Rule trigger string comparisons for EndsWith ``$>``, StartsWith ``$<`` and Contains ``$|`` (#10538)
- Support for TOF10120 time of flight sensor by Cyril Pawelko (#10190)
### Breaking Changed
- ESP32 switch from default SPIFFS to default LittleFS file system loosing current (zigbee) files
@ -20,10 +44,12 @@ All notable changes to this project will be documented in this file.
### Changed
- Force initial default state ``SetOption57 1`` to scan wifi network every 44 minutes for strongest signal (#10395)
- Command ``Sleep 0`` removes any sleep from wifi modem except when ESP32 BLE is active
- PubSubClient MQTT_SOCKET_TIMEOUT from 15 to 4 seconds
- Domoticz fixed 2 decimals resolution by user selectable ``TempRes``, ``HumRes`` and ``PressRes`` resolutions
## [9.2.0.2] 20210105
### Added
- Basic support for ESP32 Odroid Go 16MB binary tasmota32-odroidgo.bin (#8630)
- Support for ESP32 ``Module 3`` Odroid Go 16MB binary tasmota32-odroidgo.bin (#8630)
- Command ``CTRange`` to specify the visible CT range the bulb is capable of (#10311)
- Command ``VirtualCT`` to simulate or fine tune CT bulbs with 3,4,5 channels (#10311)
- Command ``SetOption118 1`` to move ZbReceived from JSON message and into the subtopic replacing "SENSOR" default (#10353)
@ -45,7 +71,7 @@ All notable changes to this project will be documented in this file.
- Replaced RA8876 GPIO selection from ``SPI CS`` by ``RA8876 CS``
### Changed
- Maximum chars in AddLog_P logging reduced from 700 to 128 (LOGSZ) to enhance stability
- Maximum chars in ``AddLog_P`` logging reduced from 700 to 128 (LOGSZ) to enhance stability
- Disabled ``USE_LIGHT`` light support for ZBBridge saving 17.6kB (#10374)
## [9.2.0.1] 20201229

View File

@ -90,3 +90,4 @@ Index | Define | Driver | Device | Address(es) | Description
55 | USE_EZORGB | xsns_78 | EZORGB | 0x61 - 0x70 | Color sensor
55 | USE_EZOPMP | xsns_78 | EZOPMP | 0x61 - 0x70 | Peristaltic Pump
56 | USE_SEESAW_SOIL | xsns_81 | SEESOIL | 0x36 - 0x39 | Adafruit seesaw soil moisture sensor
57 | USE_TOF10120 | xsns_84 | TOF10120 | 0x52 | Time-of-flight (ToF) distance sensor

View File

@ -56,14 +56,22 @@ The attached binaries can also be downloaded from http://ota.tasmota.com/tasmota
[Complete list](BUILDS.md) of available feature and sensors.
## Changelog v9.2.0.3
## Changelog v9.2.0.4
### Added
- Command ``CTRange`` to specify the visible CT range the bulb is capable of [#10311](https://github.com/arendst/Tasmota/issues/10311)
- Command ``L1MusicSync <0|Off>|<1|On>|<2|Toggle>, 1..10, 1..100>`` to control Sonoff L1 Music Sync mode sensitivity and speed [#10722](https://github.com/arendst/Tasmota/issues/10722)
- Command ``RuleTimer0`` to access all RuleTimers at once [#10352](https://github.com/arendst/Tasmota/issues/10352)
- Command ``Speed2`` to control a once off fade [#10741](https://github.com/arendst/Tasmota/issues/10741)
- Command ``VirtualCT`` to simulate or fine tune CT bulbs with 3,4,5 channels [#10311](https://github.com/arendst/Tasmota/issues/10311)
- Command ``SetOption40 0..250`` to disable button functionality if activated for over 0.1 second re-introduced
- Command ``SetOption43 1..255`` to control Rotary step (#10407)
- Command ``SetOption118 1`` to move ZbReceived from JSON message and into the subtopic replacing "SENSOR" default [#10353](https://github.com/arendst/Tasmota/issues/10353)
- Command ``SetOption119 1`` to remove the device addr from json payload, can be used with zb_topic_fname where the addr is already known from the topic [#10355](https://github.com/arendst/Tasmota/issues/10355)
- Zigbee command ``SetOption120 1`` or ``ZbEndpointTopic 1`` to add the zigbee endpoint as suffix in topic when using ``SetOption89 1``
- Commands ``ChannelRemap``, ``MultiPWM``, ``AlexaCTRange``, ``PowerOnFade``, ``PWMCT``, ``WhiteBlend`` and ``VirtualCT`` as synonyms for ``SetOption37, 68, 82, 91, 92, 105`` and ``106``
- Commands ``ZbNameKey``, ``ZbDeviceTopic``, ``ZbNoPrefix``, ``ZbEndpointSuffix``, ``ZbNoAutoBind`` and ``ZbNameTopic`` as synonyms for ``SetOption83, 89, 100, 101, 110`` and ``112``
- Commands ``ZbNoAutoBind``, ``ZbReceivedTopic`` and ``ZbOmitDevice`` as synonyms for ``SetOption116, 118`` and ``119``
- Commands ``BuzzerActive`` and ``BuzzerPwm`` as synonyms for ``SetOption67`` and ``111``
- 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)
@ -73,15 +81,24 @@ The attached binaries can also be downloaded from http://ota.tasmota.com/tasmota
- Support for FTC532 8-button touch controller by Peter Franck [#10222](https://github.com/arendst/Tasmota/issues/10222)
- Support for BS814A-2 8-button touch buttons by Peter Franck [#10447](https://github.com/arendst/Tasmota/issues/10447)
- Support for up to 4 I2C SEESAW_SOIL Capacitance & Temperature sensors by Peter Franck [#10481](https://github.com/arendst/Tasmota/issues/10481)
- Support for TOF10120 time of flight sensor by Cyril Pawelko [#10190](https://github.com/arendst/Tasmota/issues/10190)
- Support for Afrikaans language translations by Christiaan Heerze
- 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 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)
- Support for Sugar Valley NeoPool Controller by Norbert Richter [#10637](https://github.com/arendst/Tasmota/issues/10637)
- 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 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)
- Basic support for ESP32 Odroid Go 16MB binary tasmota32-odroidgo.bin [#8630](https://github.com/arendst/Tasmota/issues/8630)
- Support trailing silence in buzzer tune [#10694](https://github.com/arendst/Tasmota/issues/10694)
- Rule trigger string comparisons for EndsWith ``$>``, StartsWith ``$<`` and Contains ``$|`` [#10538](https://github.com/arendst/Tasmota/issues/10538)
- SPI display driver SSD1331 Color oled by Jeroen Vermeulen [#10376](https://github.com/arendst/Tasmota/issues/10376)
- Compile time option ``USE_MQTT_TLS_DROP_OLD_FINGERPRINT`` to drop old (less secure) TLS fingerprint
### Breaking Changed
- ESP32 switch from default SPIFFS to default LittleFS file system loosing current (zigbee) files
@ -100,6 +117,8 @@ The attached binaries can also be downloaded from http://ota.tasmota.com/tasmota
- Logging from heap to stack freeing 700 bytes RAM
- Disabled ``USE_LIGHT`` light support for ZBBridge saving 17.6kB [#10374](https://github.com/arendst/Tasmota/issues/10374)
- Force initial default state ``SetOption57 1`` to scan wifi network every 44 minutes for strongest signal [#10395](https://github.com/arendst/Tasmota/issues/10395)
- PubSubClient MQTT_SOCKET_TIMEOUT from 15 to 4 seconds
- Domoticz fixed 2 decimals resolution by user selectable ``TempRes``, ``HumRes`` and ``PressRes`` resolutions
### Fixed
- Redesign syslog and mqttlog using log buffer [#10164](https://github.com/arendst/Tasmota/issues/10164)

View File

@ -0,0 +1,7 @@
name=Ext-printf
version=1.0
author=Stephan Hadinger
maintainer=Stephan <stephan.hadinger@gmail.com>
sentence=Extension of snprintf() and vsnprintf()
paragraph=This library provides extended types support for snprintf (float, uint64_t)
architectures=esp8266, esp32

View File

@ -237,6 +237,21 @@ public:
return buf2;
}
// nullptr accepted
static bool equalsSBuffer(const class SBuffer * buf1, const class SBuffer * buf2) {
if (buf1 == buf2) { return true; }
if (!buf1 && (buf2->len() == 0)) { return true; }
if (!buf2 && (buf1->len() == 0)) { return true; }
if (!buf1 || !buf2) { return false; } // at least one buf is not empty
// we know that both buf1 and buf2 are non-null
if (buf1->len() != buf2->len()) { return false; }
size_t len = buf1->len();
for (uint32_t i=0; i<len; i++) {
if (buf1->get8(i) != buf2->get8(i)) { return false; }
}
return true;
}
protected:
static uint8_t asc2byte(char chr) {
@ -269,18 +284,3 @@ public:
_buf = nullptr;
}
} PreAllocatedSBuffer;
// nullptr accepted
bool equalsSBuffer(const class SBuffer * buf1, const class SBuffer * buf2) {
if (buf1 == buf2) { return true; }
if (!buf1 && (buf2->len() == 0)) { return true; }
if (!buf2 && (buf1->len() == 0)) { return true; }
if (!buf1 || !buf2) { return false; } // at least one buf is not empty
// we know that both buf1 and buf2 are non-null
if (buf1->len() != buf2->len()) { return false; }
size_t len = buf1->len();
for (uint32_t i=0; i<len; i++) {
if (buf1->get8(i) != buf2->get8(i)) { return false; }
}
return true;
}

View File

@ -0,0 +1,364 @@
/*
ext_printf.ino - Extended printf for Arduino objects
Copyright (C) 2021 Stephan Hadinger
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "ext_printf.h"
#include <Arduino.h>
#include <IPAddress.h>
#include <SBuffer.hpp>
/*********************************************************************************************\
* va_list extended support
*
* va_list allows to get the next argument but not to get the address of this argument in the stack.
*
* We add `va_cur_ptr(va, TYPE)` to get a pointer to the current argument.
* This will allow to modify it in place and call back printf with altered arguments
\*********************************************************************************************/
// This code is heavily inspired by the gcc implementation of va_list
// https://github.com/gcc-mirror/gcc/blob/master/gcc/config/xtensa/xtensa.c
// Here is the va_list structure:
// struct va_list {
// void * __va_stk; // offset 0 - pointer to arguments on the stack
// void * __va_reg; // offset 4 - pointer to arguments from registers
// uint32_t __va_ndx; // offset 8 - index in bytes of the argument (overshoot by sizeof(T))
// }
//
// When `va_start()` is called, the first 6 arguments are passed through registers r2-r7 and
// are saved on the stack like local variables
// The algorightm used by `va_arg()` is the following:
// /* Implement `va_arg'.  */
// /* First align __va_ndx if necessary for this arg:
//     orig_ndx = (AP).__va_ndx;
//     if (__alignof__ (TYPE) > 4 )
//       orig_ndx = ((orig_ndx + __alignof__ (TYPE) - 1)
// & -__alignof__ (TYPE)); */
// /* Increment __va_ndx to point past the argument:
//     (AP).__va_ndx = orig_ndx + __va_size (TYPE); */
// /* Check if the argument is in registers:
//     if ((AP).__va_ndx <= __MAX_ARGS_IN_REGISTERS * 4
//         && !must_pass_in_stack (type))
//       __array = (AP).__va_reg; */
// /* ...otherwise, the argument is on the stack (never split between
//     registers and the stack -- change __va_ndx if necessary):
//     else
//       {
// if (orig_ndx <= __MAX_ARGS_IN_REGISTERS * 4)
//     (AP).__va_ndx = 32 + __va_size (TYPE);
// __array = (AP).__va_stk;
//       } */
// /* Given the base array pointer (__array) and index to the subsequent
//     argument (__va_ndx), find the address:
//     __array + (AP).__va_ndx - (BYTES_BIG_ENDIAN && sizeof (TYPE) < 4
// ? sizeof (TYPE)
// : __va_size (TYPE))
//     The results are endian-dependent because values smaller than one word
//     are aligned differently.  */
// So we can simply get the argument address
#define MAX_ARGS_IN_REGISTERS 6 // ESP8266 passes 6 arguments by register, then on stack
// #define va_cur_ptr(va,T) ( (T*) __va_cur_ptr(va,sizeof(T)) ) // we only support 4 bytes aligned arguments, so we don't need this one
// void * __va_cur_ptr(va_list &va, size_t size) {
// size = (size + 3) & 0xFFFFFFFC; // round to upper 4 bytes boundary
// uintptr_t * va_stk = (uintptr_t*) &va;
// uintptr_t * va_reg = 1 + (uintptr_t*) &va;
// uintptr_t * va_ndx = 2 + (uintptr_t*) &va;
// uintptr_t arr;
// if (*va_ndx <= MAX_ARGS_IN_REGISTERS * 4) {
// arr = *va_reg;
// } else {
// arr = *va_stk;
// }
// return (void*) (arr + *va_ndx - size);
// }
// reduced version when arguments are always 4 bytes
#define va_cur_ptr4(va,T) ( (T*) __va_cur_ptr4(va) )
void * __va_cur_ptr4(va_list &va) {
uintptr_t * va_stk = (uintptr_t*) &va;
uintptr_t * va_reg = 1 + (uintptr_t*) &va;
uintptr_t * va_ndx = 2 + (uintptr_t*) &va;
uintptr_t arr;
if (*va_ndx <= MAX_ARGS_IN_REGISTERS * 4) {
arr = *va_reg;
} else {
arr = *va_stk;
}
return (void*) (arr + *va_ndx - 4);
}
// Example of logs with 8 arguments (+1 static argument)
// We see that the first 5 are from low in the stack (local variables)
// while the last 8 are upper in the stack pushed by caller
//
// Note 64 bits arguments cannot be split between registers and stack
//
// >>> Reading a_ptr=0x3FFFFD44 *a_ptr=1
// >>> Reading a_ptr=0x3FFFFD48 *a_ptr=2
// >>> Reading a_ptr=0x3FFFFD4C *a_ptr=3
// >>> Reading a_ptr=0x3FFFFD50 *a_ptr=4
// >>> Reading a_ptr=0x3FFFFD54 *a_ptr=5
// >>> Reading a_ptr=0x3FFFFD70 *a_ptr=6
// >>> Reading a_ptr=0x3FFFFD74 *a_ptr=7
// >>> Reading a_ptr=0x3FFFFD78 *a_ptr=8
/*********************************************************************************************\
* Genral function to convert u64 to hex
\*********************************************************************************************/
// Simple function to print a 64 bits unsigned int
char * U64toHex(uint64_t value, char *str) {
// str must be at least 17 bytes long
str[16] = 0; // end of string
for (uint32_t i=0; i<16; i++) { // 16 digits
uint32_t n = value & 0x0F;
str[15 - i] = (n < 10) ? (char)n+'0' : (char)n-10+'A';
value = value >> 4;
}
return str;
}
// see https://stackoverflow.com/questions/6357031/how-do-you-convert-a-byte-array-to-a-hexadecimal-string-in-c
// char* ToHex_P(unsigned char * in, size_t insz, char * out, size_t outsz, char inbetween = '\0'); in tasmota_globals.h
char* ToHex_P(const unsigned char * in, size_t insz, char * out, size_t outsz, char inbetween = '\0') {
// ToHex_P(in, insz, out, outz) -> "12345667"
// ToHex_P(in, insz, out, outz, ' ') -> "12 34 56 67"
// ToHex_P(in, insz, out, outz, ':') -> "12:34:56:67"
static const char * hex PROGMEM = "0123456789ABCDEF";
int between = (inbetween) ? 3 : 2;
const unsigned char * pin = in;
char * pout = out;
for (; pin < in+insz; pout += between, pin++) {
pout[0] = pgm_read_byte(&hex[(pgm_read_byte(pin)>>4) & 0xF]);
pout[1] = pgm_read_byte(&hex[ pgm_read_byte(pin) & 0xF]);
if (inbetween) { pout[2] = inbetween; }
if (pout + 3 - out > outsz) { break; } // Better to truncate output string than overflow buffer
}
pout[(inbetween && insz) ? -1 : 0] = 0; // Discard last inbetween if any input
return out;
}
/*********************************************************************************************\
* snprintf extended
*
\*********************************************************************************************/
// get a fresh malloc allocated string based on the current pointer (can be in PROGMEM)
// It is the caller's responsibility to free the memory
char * copyStr(const char * str) {
if (str == nullptr) { return nullptr; }
char * cpy = (char*) malloc(strlen_P(str) + 1);
strcpy_P(cpy, str);
return cpy;
}
int32_t ext_vsnprintf_P(char * buf, size_t buf_len, const char * fmt_P, va_list va) {
va_list va_cpy;
va_copy(va_cpy, va);
#if defined(ESP8266) || defined(ESP32) // this works only for xtensa, other platforms needs va_list to be adapted
// iterate on fmt to extract arguments and patch them in place
char * fmt_cpy = copyStr(fmt_P);
if (fmt_cpy == nullptr) { return 0; }
char * fmt = fmt_cpy;
const uint32_t ALLOC_SIZE = 12;
static char * allocs[ALLOC_SIZE] = {}; // initialized to zeroes
uint32_t alloc_idx = 0;
static char hex[20]; // buffer used for 64 bits, favor RAM instead of stack to remove pressure
for (; *fmt != 0; ++fmt) {
int32_t decimals = -2; // default to 2 decimals and remove trailing zeros
int32_t * decimals_ptr = nullptr;
if (alloc_idx >= ALLOC_SIZE) { break; } // buffer is full, don't continue parsing
if (*fmt == '%') {
fmt++;
char * fmt_start = fmt;
if (*fmt == '\0') { break; } // end of string
if (*fmt == '%') { continue; } // actual '%' char
if (*fmt == '*') {
decimals = va_arg(va, int32_t); // skip width argument as int
decimals_ptr = va_cur_ptr4(va, int32_t); // pointer to value on stack
const char ** cur_val_ptr = va_cur_ptr4(va, const char*); // pointer to value on stack
fmt++;
// Serial.printf("> decimals=%d, decimals_ptr=0x%08X\n", decimals, decimals_ptr);
}
if (*fmt < 'A') {
decimals = strtol(fmt, nullptr, 10);
}
while (*fmt < 'A') { // brutal way to munch anything that is not a letter or '-' (or anything else)
// while ((*fmt >= '0' && *fmt <= '9') || (*fmt == '.') || (*fmt == '*') || (*fmt == '-' || (*fmt == ' ' || (*fmt == '+') || (*fmt == '#')))) {
fmt++;
}
if (*fmt == '_') { // extension
if (decimals_ptr) {
// Serial.printf(">2 decimals=%d, decimals_ptr=0x%08X\n", decimals, decimals_ptr);
*decimals_ptr = 0; // if '*' was used, make sure we replace the value with zero for snprintf()
*(fmt_start++) = '-'; // in this case replace with `%-*s`
*(fmt_start++) = '*';
}
for (; fmt_start <= fmt; fmt_start++) {
*fmt_start = '0';
}
// *fmt = '0';
fmt++;
uint32_t cur_val = va_arg(va, uint32_t); // current value
const char ** cur_val_ptr = va_cur_ptr4(va, const char*); // pointer to value on stack
char * new_val_str = (char*) "";
switch (*fmt) {
case 'H': // Hex, decimals indicates the length, default 2
{
if (decimals < 0) { decimals = 0; }
if (decimals > 0) {
char * hex_char = (char*) malloc(decimals*2 + 2);
ToHex_P((const uint8_t *)cur_val, decimals, hex_char, decimals*2 + 2);
new_val_str = hex_char;
allocs[alloc_idx++] = new_val_str;
// Serial.printf("> hex=%s\n", hex_char);
}
}
break;
case 'B': // Pointer to SBuffer
{
const SBuffer & buf = *(const SBuffer*)cur_val;
size_t buf_len = (&buf != nullptr) ? buf.len() : 0;
if (buf_len) {
char * hex_char = (char*) malloc(buf_len*2 + 2);
ToHex_P(buf.getBuffer(), buf_len, hex_char, buf_len*2 + 2);
new_val_str = hex_char;
allocs[alloc_idx++] = new_val_str;
}
}
break;
// case 'D':
// decimals = *(int32_t*)cur_val_ptr;
// break;
// `%_I` ouputs an IPv4 32 bits address passed as u32 into a decimal dotted format
case 'I': // Input is `uint32_t` 32 bits IP address, output is decimal dotted address
{
char * ip_str = (char*) malloc(16);
snprintf_P(ip_str, 16, PSTR("%u.%u.%u.%u"), cur_val & 0xFF, (cur_val >> 8) & 0xFF, (cur_val >> 16) & 0xFF, (cur_val >> 24) & 0xFF);
new_val_str = ip_str;
allocs[alloc_idx++] = new_val_str;
}
break;
// `%_f` or `%*_f` outputs a float with optionan number of decimals passed as first argument if `*` is present
// positive number of decimals means an exact number of decimals, can be `0` terminate
// negative number of decimals will suppress
// Ex:
// char c[128];
// float f = 3.141f;
// ext_vsnprintf_P(c; szeof(c), "%_f %*_f %*_f", &f, 4, 1f, -4, %f);
// --> c will be "3.14 3.1410 3.141"
// Note: float MUST be passed by address, because C alsays promoted float to double when in vararg
case 'f': // input is `float`, printed to float with 2 decimals
{
bool truncate = false;
if (decimals < 0) {
decimals = -decimals;
truncate = true;
}
float number = *(float*)cur_val;
if (isnan(number) || isinf(number)) {
new_val_str = (char*) "null";
} else {
dtostrf(*(float*)cur_val, (decimals + 2), decimals, hex);
if (truncate) {
uint32_t last = strlen(hex) - 1;
// remove trailing zeros
while (hex[last] == '0') {
hex[last--] = 0; // remove last char
}
// remove trailing dot
if (hex[last] == '.') {
hex[last] = 0;
}
}
new_val_str = copyStr(hex);
allocs[alloc_idx++] = new_val_str;
}
}
break;
// '%_X' outputs a 64 bits unsigned int to uppercase HEX with 16 digits
case 'X': // input is `uint64_t*`, printed as 16 hex digits (no prefix 0x)
{
U64toHex(*(uint64_t*)cur_val, hex);
new_val_str = copyStr(hex);
allocs[alloc_idx++] = new_val_str;
}
break;
// Trying to do String allocation alternatives, but not as interesting as I thought in the beginning
// case 's':
// {
// new_val_str = copyStr(((String*)cur_val)->c_str());
// allocs[alloc_idx++] = new_val_str;
// }
// break;
// case 'S':
// {
// funcString_t * func_str = (funcString_t*) cur_val;
// new_val_str = copyStr((*func_str)().c_str());
// allocs[alloc_idx++] = new_val_str;
// }
// break;
}
*cur_val_ptr = new_val_str;
*fmt = 's'; // replace `%_X` with `%0s` to display a string instead
} else {
va_arg(va, int32_t); // munch one 32 bits argument and leave it unchanged
// we take the hypothesis here that passing 64 bits arguments is always unsupported in ESP8266
}
}
}
#else // defined(ESP8266) || defined(ESP32)
#error "ext_printf is not suppoerted on this platform"
#endif // defined(ESP8266) || defined(ESP32)
// Serial.printf("> format_final=%s\n", fmt_cpy); Serial.flush();
int32_t ret = vsnprintf_P(buf, buf_len, fmt_cpy, va_cpy);
va_end(va_cpy);
// disallocated all temporary strings
for (uint32_t i = 0; i < alloc_idx; i++) {
free(allocs[i]); // it is ok to call free() on nullptr so we don't test for nullptr first
allocs[i] = nullptr;
}
free(fmt_cpy); // free the local copy of the format string
return ret;
}
int32_t ext_snprintf_P(char * buf, size_t buf_len, const char * fmt, ...) {
va_list va;
va_start(va, fmt);
int32_t ret = ext_vsnprintf_P(buf, buf_len, fmt, va);
va_end(va);
return ret;
}

View File

@ -0,0 +1,34 @@
/*
ext_printf.ino - Extended printf for Arduino objects
Copyright (C) 2021 Stephan Hadinger
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef EXT_PRINTF_H
#define EXT_PRINTF_H
#include <cstddef>
#include <cstdint>
#include <cstdarg>
int32_t ext_vsnprintf_P(char * buf, size_t buf_len, const char * fmt_P, va_list va);
int32_t ext_snprintf_P(char * buf, size_t buf_len, const char * fmt, ...);
char* ToHex_P(const unsigned char * in, size_t insz, char * out, size_t outsz, char inbetween);
// void test_ext_snprintf_P(void);
#endif // EXT_PRINTF_H

View File

@ -0,0 +1,110 @@
/*
ext_printf.ino - Extended printf for Arduino objects
Copyright (C) 2021 Stephan Hadinger
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "ext_printf.h"
#include <Arduino.h>
// DEBUG only
// String test_string(void) {
// String s("This is the string");
// return s;
// }
// String f_str(void) { return String("foobar"); }
// String k_str("foobar");
// void f1(String s) {
// Serial.printf("> %s\n", s.c_str());
// }
// void f2(void) {
// f1(f_str());
// }
// void test_snprintf1(void) {
// char c[100];
// snprintf_P(c, sizeof(c), PSTR("s1=%s, s2=%s"), k_str.c_str(), f_str().c_str());
// }
// void test_snprintf2(void) {
// char c[100];
// ext_snprintf_P(c, sizeof(c), PSTR("s1=%_s, s2=%_S"), &k_str, &f_str, &ResponseAppendTHD);
// }
void test_ext_snprintf_P(void) {
// test_snprintf1();
// test_snprintf2();
// if (0) {
// // testVarArg2("", 1, 2, 3, 4, 5, 6, 7, 8);
char c[128];
float fpi=-3333.1415926535f;
float f3 = 3333;
float f31 = 3333.1;
ext_snprintf_P(c, sizeof(c), "Int1 = %d, ip=%_I", 1, 0x10203040);
Serial.printf("--> out=%s\n", c);
ext_snprintf_P(c, sizeof(c), "Float default=%_f %_f", &f3, &fpi);
Serial.printf("--> out=%s\n", c);
ext_snprintf_P(c, sizeof(c), "Float default=%1_f, int(3)=%4_f, int(3)=%-4_f, int(3)=%-4_f, 6dec=%-8_f", &fpi, &f3, &f3, &f31, &fpi);
Serial.printf("--> out=%s\n", c);
ext_snprintf_P(c, sizeof(c), "Float default=%*_f, int(3)=%*_f, int(3)=%*_f, int(3)=%*_f, 6dec=%*_f", 1, &fpi, 4, &f3, -4, &f3, -4, &f31, -8, &fpi);
Serial.printf("--> out=%s\n", c);
uint64_t u641 = 0x1122334455667788LL;
uint64_t u642 = 0x0123456789ABCDEFLL;
uint64_t u643 = 0xFEDCBA9876543210LL;
ext_snprintf_P(c, sizeof(c), "Int64 0x%_X 0x%_X 0x%_X", &u641, &u642, &u643);
Serial.printf("--> out=%s\n", c);
// ext_snprintf_P(c, sizeof(c), "Float default=%*_f, int(3)=%*_f, int(3)=%*_f, int(3)=%*_f, 6dec=%*_f", &fpi, &f3, &f3, &f31, &fpi);
// String string("Foobar");
// ext_snprintf_P(c, sizeof(c), "String 0x%08X %_s", &string, &string);
// Serial.printf("--> out=%s\n", c);
// ext_snprintf_P(c, sizeof(c), "StringFunc 0x%08X %_S", &test_string, &test_string);
// Serial.printf("--> out=%s\n", c);
// uint64_t u64 = 0x123456789ABCDEFLL;
// testVarArg2("", u64, 2, 3, 4, 5, 6, 7, 8);
// // Serial.printf("+++ ld=%ld, lld=%lld\n", 1,2,3,4);
// // testVarArg("", 1, 2, 3, 4, 5, 6, 7, 8);
// }
// tprintf("%s", 12, "14");
}
// void tprintf(const char* format) // base function
// {
// Serial.printf("%s\n", format);
// }
// template<typename T, typename... Targs>
// void tprintf(const char* format, T value, Targs... Fargs) // recursive variadic function
// {
// for ( ; *format != '\0'; format++ ) {
// if ( *format == '%' ) {
// Serial.printf("%d", (uint32_t) value);
// tprintf(format+1, Fargs...); // recursive call
// return;
// }
// Serial.printf("%s", format);
// }
// }

View File

@ -10,8 +10,5 @@
"exclude": "tests",
"examples": "examples/*/*.ino",
"frameworks": "arduino",
"platforms": [
"atmelavr",
"espressif"
]
"platforms": ["espressif8266", "espressif32"]
}

View File

@ -25,19 +25,20 @@
#ifndef MQTT_MAX_PACKET_SIZE
//#define MQTT_MAX_PACKET_SIZE 128
//#define MQTT_MAX_PACKET_SIZE 1000 // Tasmota v5.11.1c
#define MQTT_MAX_PACKET_SIZE 1200 // Tasmota v8.1.0.8
#define MQTT_MAX_PACKET_SIZE 1200 // Tasmota v8.1.0.8
#endif
// MQTT_KEEPALIVE : keepAlive interval in Seconds
// Keepalive timeout for default MQTT Broker is 10s
#ifndef MQTT_KEEPALIVE
//#define MQTT_KEEPALIVE 10
#define MQTT_KEEPALIVE 30 // Tasmota v6.5.0.14 enabling AWS-iot
#define MQTT_KEEPALIVE 30 // Tasmota v6.5.0.14 enabling AWS-iot
#endif
// MQTT_SOCKET_TIMEOUT: socket timeout interval in Seconds
#ifndef MQTT_SOCKET_TIMEOUT
#define MQTT_SOCKET_TIMEOUT 15
//#define MQTT_SOCKET_TIMEOUT 15
#define MQTT_SOCKET_TIMEOUT 4 // Tasmota 20210120
#endif
// MQTT_MAX_TRANSFER_SIZE : limit how much data is passed to the network client

View File

@ -32,7 +32,7 @@ extern String EscapeJSONString(const char *str);
class JsonGeneratorArray {
public:
JsonGeneratorArray(): val("[]") {} // start with empty array
JsonGeneratorArray(): val(F("[]")) {} // start with empty array
void add(uint32_t uval32);
void add(int32_t uval32);
@ -53,7 +53,7 @@ protected:
class JsonGeneratorObject {
public:
JsonGeneratorObject(): val("{}") {} // start with empty object
JsonGeneratorObject(): val(F("{}")) {} // start with empty object
void add(const char* key, uint32_t uval32);
void add(const char* key, int32_t uval32);

View File

@ -6,5 +6,5 @@ sentence=Access 1-wire temperature sensors, memory and other chips.
paragraph= Mod of Paul Stoffregen code to support ESP32
category=Communication
url=http://www.pjrc.com/teensy/td_libs_OneWire.html
architectures=esp32
architectures=esp8266,esp32

View File

@ -11,5 +11,5 @@
"url": "https://github.com/arendst/Tasmota/lib/TasmotaModbus"
},
"frameworks": "arduino",
"platforms": "espressif8266"
"platforms": ["espressif8266", "espressif32"]
}

View File

@ -6,4 +6,4 @@ sentence=Basic modbus wrapper for TasmotaSerial for ESP8266.
paragraph=
category=Signal Input/Output
url=
architectures=esp8266
architectures=esp8266,esp32

View File

@ -24,7 +24,7 @@ TasmotaModbus::TasmotaModbus(int receive_pin, int transmit_pin) : TasmotaSerial(
mb_address = 0;
}
uint16_t CalculateCRC(uint8_t *frame, uint8_t num)
uint16_t TasmotaModbus::CalculateCRC(uint8_t *frame, uint8_t num)
{
uint16_t crc = 0xFFFF;
@ -81,30 +81,37 @@ bool TasmotaModbus::ReceiveReady()
uint8_t TasmotaModbus::ReceiveBuffer(uint8_t *buffer, uint8_t register_count)
{
mb_len = 0;
uint32_t last = millis();
while ((available() > 0) && (mb_len < (register_count *2) + 5) && (millis() - last < 10)) {
uint8_t data = (uint8_t)read();
if (!mb_len) { // Skip leading data as provided by hardware serial
if (mb_address == data) {
uint32_t timeout = millis() + 10;
while ((mb_len < (register_count *2) + 5) && (millis() < timeout)) {
if (available()) {
uint8_t data = (uint8_t)read();
if (!mb_len) { // Skip leading data as provided by hardware serial
if (mb_address == data) {
buffer[mb_len++] = data;
}
} else {
buffer[mb_len++] = data;
}
} else {
buffer[mb_len++] = data;
if (3 == mb_len) {
if (buffer[1] & 0x80) { // 01 84 02 f2 f1
return buffer[2]; // 1 = Illegal Function,
// 2 = Illegal Data Address,
// 3 = Illegal Data Value,
// 4 = Slave Error
// 5 = Acknowledge but not finished (no error)
// 6 = Slave Busy
// 8 = Memory Parity error
// 10 = Gateway Path Unavailable
// 11 = Gateway Target device failed to respond
if (3 == mb_len) {
if (buffer[1] & 0x80) { // 01 84 02 f2 f1
if (0 == buffer[2]) {
return 3; // 3 = Illegal Data Value,
}
return buffer[2]; // 1 = Illegal Function,
// 2 = Illegal Data Address,
// 3 = Illegal Data Value,
// 4 = Slave Error
// 5 = Acknowledge but not finished (no error)
// 6 = Slave Busy
// 8 = Memory Parity error
// 10 = Gateway Path Unavailable
// 11 = Gateway Target device failed to respond
}
}
}
timeout = millis() + 10;
}
last = millis();
}
if (mb_len < 7) { return 7; } // 7 = Not enough data

View File

@ -32,6 +32,8 @@ class TasmotaModbus : public TasmotaSerial {
int Begin(long speed = TM_MODBUS_BAUDRATE, int stop_bits = 1);
uint16_t CalculateCRC(uint8_t *frame, uint8_t num);
void Send(uint8_t device_address, uint8_t function_code, uint16_t start_address, uint16_t register_count);
bool ReceiveReady();

View File

@ -7,4 +7,3 @@ paragraph=Arduino library for FT5206 chip. Tested with ESP32
category=Communication
url=https://github.com/lewisxhe/FT5206_Library
architectures=*
architectures=esp32

View File

@ -10,6 +10,6 @@
"frameworks": "arduino",
"platforms":
[
"atmelavr"
"*"
]
}
}

View File

@ -6,4 +6,4 @@ sentence=ESP8266 library for Waveshare e-paper display.
paragraph=
category=Display
url=https://github.com/gemu2015/Sonoff-Tasmota/tree/displays/lib/esp-epaper-29-ws-20171230-gemu-1.0#
architectures=esp8266
architectures=*

View File

@ -338,7 +338,15 @@ float Adafruit_TSL2591::calculateLux(uint16_t ch0, uint16_t ch1)
// Alternate lux calculation 1
// See: https://github.com/adafruit/Adafruit_TSL2591_Library/issues/14
lux = ( ((float)ch0 - (float)ch1 )) * (1.0F - ((float)ch1/(float)ch0) ) / cpl;
if(ch0 > 0)
{
lux = ( ((float)ch0 - (float)ch1 )) * (1.0F - ((float)ch1/(float)ch0) ) / cpl;
}
else
{
lux = 0.0F;
}
// Alternate lux calculation 2
//lux = ( (float)ch0 - ( 1.7F * (float)ch1 ) ) / cpl;

View File

@ -6,4 +6,4 @@ sentence=Sensor driver for BME680 sensor
paragraph=Sensor driver for BME680 sensor
category=Sensor
url=
architectures=esp8266
architectures=esp8266,esp32

View File

@ -5,7 +5,7 @@
"type": "git"
},
"platforms": [
"atmelavr"
"*"
],
"frameworks": [
"arduino"
@ -28,4 +28,4 @@
],
"id": 11,
"description": "The I2C Device Library (I2Cdevlib) is a collection of uniform and well-documented classes to provide simple and intuitive interfaces to I2C devices."
}
}

View File

@ -9,5 +9,5 @@
"url": "https://github.com/jrowberg/i2cdevlib.git"
},
"frameworks": "arduino",
"platforms": "atmelavr"
"platforms": "*"
}

View File

@ -6,4 +6,4 @@ sentence=.
paragraph=
category=
url=
architectures=esp8266
architectures=esp8266,esp32

View File

@ -6,4 +6,4 @@ sentence=BearSSL implementation of the SSL/TLS protocol optimized for ESP8266 by
paragraph=
category=Other
url=https://github.com/earlephilhower/bearssl-esp8266.git
architectures=esp8266
architectures=esp8266,esp32

View File

@ -0,0 +1,9 @@
name=M5 Stack Core2 library
version=1.0
author=Gerhard Mutz
maintainer=Gerhard Mutz
sentence=Allows Tasmota to use Core2
paragraph=Allows Tasmota to Core2 for esp32
category=ESP32
url=
architectures=*

View File

@ -2,6 +2,75 @@
All notable changes to this project will be documented in this file.
## [1.1.0] - 2021-01-20
### Added
- `NimBLEDevice::setOwnAddrType` added to enable the use of random and random-resolvable addresses, by asukiaaa
- New examples for securing and authenticating client/server connections, by mblasee.
- `NimBLEAdvertiseing::SetMinPreferred` and `NimBLEAdvertiseing::SetMinPreferred` re-added.
- Conditional checks added for command line config options in `nimconfig.h` to support custom configuration in platformio.
- `NimBLEClient::setValue` Now takes an extra bool parameter `response` to enable the use of write with response (default = false).
- `NimBLEClient::getCharacteristic(uint16_t handle)` Enabling the use of the characteristic handle to be used to find
the NimBLERemoteCharacteristic object.
- `NimBLEHIDDevice` class added by wakwak-koba.
- `NimBLEServerCallbacks::onDisconnect` overloaded callback added to provide a ble_gap_conn_desc parameter for the application
to obtain information about the disconnected client.
- Conditional checks in `nimconfig.h` for command line defined macros to support platformio config settings.
### Changed
- `NimBLEAdvertising::start` now returns a bool value to indicate success/failure.
- Some asserts were removed in `NimBLEAdvertising::start` and replaced with better return code handling and logging.
- If a host reset event occurs, scanning and advertising will now only be restarted if their previous duration was indefinite.
- `NimBLERemoteCharacteristic::subscribe` and `NimBLERemoteCharacteristic::registerForNotify` will now set the callback
regardless of the existance of the CCCD and return true unless the descriptor write operation failed.
- Advertising tx power level is now sent in the advertisement packet instead of scan response.
- `NimBLEScan` When the scan ends the scan stopped flag is now set before calling the scan complete callback (if used)
this allows the starting of a new scan from the callback function.
### Fixed
- Sometimes `NimBLEClient::connect` would hang on the task block if no event arrived to unblock.
A time limit has been added to timeout appropriately.
- When getting descriptors for a characterisic the end handle of the service was used as a proxy for the characteristic end
handle. This would be rejected by some devices and has been changed to use the next characteristic handle as the end when possible.
- An exception could occur when deleting a client instance if a notification arrived while the attribute vectors were being
deleted. A flag has been added to prevent this.
- An exception could occur after a host reset event when the host re-synced if the tasks that were stopped during the event did
not finish processing. A yield has been added after re-syncing to allow tasks to finish before proceeding.
- Occasionally the controller would fail to send a disconnected event causing the client to indicate it is connected
and would be unable to reconnect. A timer has been added to reset the host/controller if it expires.
- Occasionally the call to start scanning would get stuck in a loop on BLE_HS_EBUSY, this loop has been removed.
- 16bit and 32bit UUID's in some cases were not discovered or compared correctly if the device
advertised them as 16/32bit but resolved them to 128bits. Both are now checked.
- `FreeRTOS` compile errors resolved in latest Ardruino core and IDF v3.3.
- Multiple instances of `time()` called inside critical sections caused sporadic crashes, these have been moved out of critical regions.
- Advertisement type now correctly set when using non-connectable (advertiser only) mode.
- Advertising payload length correction, now accounts for appearance.
- (Arduino) Ensure controller mode is set to BLE Only.
## [1.0.2] - 2020-09-13
### Changed

View File

@ -1,5 +1,7 @@
[Latest release ![Release Version](https://img.shields.io/github/release/h2zero/NimBLE-Arduino.svg?style=plastic)
![Release Date](https://img.shields.io/github/release-date/h2zero/NimBLE-Arduino.svg?style=plastic)](https://github.com/h2zero/NimBLE-Arduino/releases/latest/)
Need help? Have questions or suggestions? Join the [![Gitter](https://badges.gitter.im/NimBLE-Arduino/community.svg)](https://gitter.im/NimBLE-Arduino/community?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge)
<br/>
# NimBLE-Arduino
@ -57,6 +59,8 @@ Also see [Improvements_and_updates](docs/Improvements_and_updates.md) for inform
[Full API documentation and class list can be found here.](https://h2zero.github.io/esp-nimble-cpp/)
For added performance and optimizations see [Usage tips](docs/Usage_tips.md).
Check the Refactored_original_examples in the examples folder for highlights of the differences with the original library.
More advanced examples highlighting many available features are in examples/ NimBLE_Server, NimBLE_Client.
@ -68,9 +72,9 @@ such as increasing max connections, default is 3, absolute maximum connections i
<br/>
# Development Status
This Library is tracking the esp-nimble repo, nimble-1.2.0-idf master branch, currently [@95bd864.](https://github.com/espressif/esp-nimble)
This Library is tracking the esp-nimble repo, nimble-1.2.0-idf master branch, currently [@f4ae049.](https://github.com/espressif/esp-nimble)
Also tracking the NimBLE related changes in ESP-IDF, master branch, currently [@2ef4890.](https://github.com/espressif/esp-idf/tree/master/components/bt/host/nimble)
Also tracking the NimBLE related changes in ESP-IDF, master branch, currently [@3caa969.](https://github.com/espressif/esp-idf/tree/master/components/bt/host/nimble)
<br/>
# Acknowledgments

View File

@ -0,0 +1,93 @@
# 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.
<br>
`CONFIG_BT_NIMBLE_ROLE_OBSERVER_DISABLED`
If defined, NimBLE Scan functions will not be included.
- Reduces flash size by approx. 26kB.
<br>
`CONFIG_BT_NIMBLE_ROLE_PERIPHERAL_DISABLED`
If defined NimBLE Server functions will not be included.
- Reduces flash size by approx. 16kB.
<br>
`CONFIG_BT_NIMBLE_ROLE_BROADCASTER_DISABLED`
If defined, NimBLE Advertising functions will not be included.
- Reduces flash size by approx. 5kB.
<br>
`CONFIG_BT_NIMBLE_DEBUG`
If defined, enables debug log messages from the NimBLE host
- Uses approx. 32kB of flash memory.
<br>
`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.
<br>
`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.
<br>
`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.
<br>
`CONFIG_BT_NIMBLE_PINNED_TO_CORE`
Sets the core the NimBLE host stack will run on
- Options: 0 or 1
<br>
`CONFIG_BT_NIMBLE_TASK_STACK_SIZE`
Set the task stack size for the NimBLE core.
- Default is 4096
<br>
`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
<br>
`CONFIG_BT_NIMBLE_MAX_CONNECTIONS`
Sets the number of simultaneous connections (esp controller max is 9)
- Default value is 3
<br>
`CONFIG_BT_NIMBLE_MAX_BONDS`
Sets the number of devices allowed to store/bond with
- Default value is 3
<br>
`CONFIG_BT_NIMBLE_MAX_CCCDS`
Sets the maximum number of CCCD subscriptions to store
- Default value is 8
<br>
`CONFIG_BT_NIMBLE_SVC_GAP_DEVICE_NAME`
Set the default device name
- Default value is "nimble"
<br>

View File

@ -79,27 +79,24 @@ class MyAdvertisedDeviceCallbacks : public BLEAdvertisedDeviceCallbacks
return;
}
uint8_t *payLoad = advertisedDevice->getPayload();
BLEUUID eddyUUID = (uint16_t)0xfeaa;
BLEUUID checkUrlUUID = (uint16_t)0xfeaa;
if (advertisedDevice->getServiceUUID().equals(checkUrlUUID))
if (advertisedDevice->getServiceUUID().equals(eddyUUID))
{
if (payLoad[11] == 0x10)
std::string serviceData = advertisedDevice->getServiceData(eddyUUID);
if (serviceData[0] == 0x10)
{
Serial.println("Found an EddystoneURL beacon!");
BLEEddystoneURL foundEddyURL = BLEEddystoneURL();
std::string eddyContent((char *)&payLoad[11]); // incomplete EddystoneURL struct!
foundEddyURL.setData(eddyContent);
foundEddyURL.setData(serviceData);
std::string bareURL = foundEddyURL.getURL();
if (bareURL[0] == 0x00)
{
size_t payLoadLen = advertisedDevice->getPayloadLength();
Serial.println("DATA-->");
for (int idx = 0; idx < payLoadLen; idx++)
for (int idx = 0; idx < serviceData.length(); idx++)
{
Serial.printf("0x%08X ", payLoad[idx]);
Serial.printf("0x%08X ", serviceData[idx]);
}
Serial.println("\nInvalid Data");
return;
@ -110,23 +107,15 @@ class MyAdvertisedDeviceCallbacks : public BLEAdvertisedDeviceCallbacks
Serial.printf("TX power %d\n", foundEddyURL.getPower());
Serial.println("\n");
}
else if (payLoad[11] == 0x20)
else if (serviceData[0] == 0x20)
{
Serial.println("Found an EddystoneTLM beacon!");
BLEEddystoneTLM foundEddyURL = BLEEddystoneTLM();
std::string eddyContent((char *)&payLoad[11]); // incomplete EddystoneURL struct!
foundEddyURL.setData(serviceData);
eddyContent = "01234567890123";
for (int idx = 0; idx < 14; idx++)
{
eddyContent[idx] = payLoad[idx + 11];
}
foundEddyURL.setData(eddyContent);
Serial.printf("Reported battery voltage: %dmV\n", foundEddyURL.getVolt());
Serial.printf("Reported temperature from TLM class: %.2fC\n", (double)foundEddyURL.getTemp());
int temp = (int)payLoad[16] + (int)(payLoad[15] << 8);
int temp = (int)serviceData[5] + (int)(serviceData[4] << 8);
float calcTemp = temp / 256.0f;
Serial.printf("Reported temperature from data: %.2fC\n", calcTemp);
Serial.printf("Reported advertise count: %d\n", foundEddyURL.getCount());

View File

@ -2,10 +2,10 @@
/** NimBLE_Server Demo:
*
* Demonstrates many of the available features of the NimBLE client library.
*
*
* Created: on March 24 2020
* Author: H2zero
*
*
*/
#include <NimBLEDevice.h>
@ -19,15 +19,15 @@ static uint32_t scanTime = 0; /** 0 = scan forever */
/** None of these are required as they will be handled by the library with defaults. **
** Remove as you see fit for your needs */
** Remove as you see fit for your needs */
class ClientCallbacks : public NimBLEClientCallbacks {
void onConnect(NimBLEClient* pClient) {
Serial.println("Connected");
/** After connection we should change the parameters if we don't need fast response times.
* These settings are 150ms interval, 0 latency, 450ms timout.
* These settings are 150ms interval, 0 latency, 450ms timout.
* Timeout should be a multiple of the interval, minimum is 100ms.
* I find a multiple of 3-5 * the interval works best for quick response/reconnect.
* Min interval: 120 * 1.25ms = 150, Max interval: 120 * 1.25ms = 150, 0 latency, 60 * 10ms = 600ms timeout
* Min interval: 120 * 1.25ms = 150, Max interval: 120 * 1.25ms = 150, 0 latency, 60 * 10ms = 600ms timeout
*/
pClient->updateConnParams(120,120,0,60);
};
@ -37,9 +37,9 @@ class ClientCallbacks : public NimBLEClientCallbacks {
Serial.println(" Disconnected - Starting scan");
NimBLEDevice::getScan()->start(scanTime, scanEndedCB);
};
/** Called when the peripheral requests a change to the connection parameters.
* Return true to accept and apply them or false to reject and keep
* Return true to accept and apply them or false to reject and keep
* the currently used parameters. Default will return true.
*/
bool onConnParamsUpdateRequest(NimBLEClient* pClient, const ble_gap_upd_params* params) {
@ -55,7 +55,7 @@ class ClientCallbacks : public NimBLEClientCallbacks {
return true;
};
/********************* Security handled here **********************
****** Note: these are the same return values as defaults ********/
uint32_t onPassKeyRequest(){
@ -85,7 +85,7 @@ class ClientCallbacks : public NimBLEClientCallbacks {
/** Define a class to handle the callbacks when advertisments are received */
class AdvertisedDeviceCallbacks: public NimBLEAdvertisedDeviceCallbacks {
void onResult(NimBLEAdvertisedDevice* advertisedDevice) {
Serial.print("Advertised Device found: ");
Serial.println(advertisedDevice->toString().c_str());
@ -94,9 +94,9 @@ class AdvertisedDeviceCallbacks: public NimBLEAdvertisedDeviceCallbacks {
Serial.println("Found Our Service");
/** stop scan before connecting */
NimBLEDevice::getScan()->stop();
/** Save the device reference in a global for the client to use*/
/** Save the device reference in a global for the client to use*/
advDevice = advertisedDevice;
/** Ready to connect now */
/** Ready to connect now */
doConnect = true;
}
};
@ -105,7 +105,7 @@ class AdvertisedDeviceCallbacks: public NimBLEAdvertisedDeviceCallbacks {
/** Notification / Indication receiving handler callback */
void notifyCB(NimBLERemoteCharacteristic* pRemoteCharacteristic, uint8_t* pData, size_t length, bool isNotify){
std::string str = (isNotify == true) ? "Notification" : "Indication";
std::string str = (isNotify == true) ? "Notification" : "Indication";
str += " from ";
/** NimBLEAddress and NimBLEUUID have std::string operators */
str += std::string(pRemoteCharacteristic->getRemoteService()->getClient()->getPeerAddress());
@ -128,10 +128,10 @@ static ClientCallbacks clientCB;
/** Handles the provisioning of clients and connects / interfaces with the server */
bool connectToServer() {
NimBLEClient* pClient = nullptr;
/** Check if we have a client we should reuse first **/
if(NimBLEDevice::getClientListSize()) {
/** Special case when we already know this device, we send false as the
/** Special case when we already know this device, we send false as the
* second argument in connect() to prevent refreshing the service database.
* This saves considerable time and power.
*/
@ -142,7 +142,7 @@ bool connectToServer() {
return false;
}
Serial.println("Reconnected client");
}
}
/** We don't already have a client that knows this device,
* we will check for a client that is disconnected that we can use.
*/
@ -150,28 +150,28 @@ bool connectToServer() {
pClient = NimBLEDevice::getDisconnectedClient();
}
}
/** No client to reuse? Create a new one. */
if(!pClient) {
if(NimBLEDevice::getClientListSize() >= NIMBLE_MAX_CONNECTIONS) {
Serial.println("Max clients reached - no more connections available");
return false;
}
pClient = NimBLEDevice::createClient();
Serial.println("New client created");
pClient->setClientCallbacks(&clientCB, false);
/** Set initial connection parameters: These settings are 15ms interval, 0 latency, 120ms timout.
* These settings are safe for 3 clients to connect reliably, can go faster if you have less
/** Set initial connection parameters: These settings are 15ms interval, 0 latency, 120ms timout.
* These settings are safe for 3 clients to connect reliably, can go faster if you have less
* connections. Timeout should be a multiple of the interval, minimum is 100ms.
* Min interval: 12 * 1.25ms = 15, Max interval: 12 * 1.25ms = 15, 0 latency, 51 * 10ms = 510ms timeout
* Min interval: 12 * 1.25ms = 15, Max interval: 12 * 1.25ms = 15, 0 latency, 51 * 10ms = 510ms timeout
*/
pClient->setConnectionParams(12,12,0,51);
/** Set how long we are willing to wait for the connection to complete (seconds), default is 30. */
pClient->setConnectTimeout(5);
if (!pClient->connect(advDevice)) {
/** Created a client but failed to connect, don't need to keep it as it has no data */
@ -179,149 +179,147 @@ bool connectToServer() {
Serial.println("Failed to connect, deleted client");
return false;
}
}
}
if(!pClient->isConnected()) {
if (!pClient->connect(advDevice)) {
Serial.println("Failed to connect");
return false;
}
}
Serial.print("Connected to: ");
Serial.println(pClient->getPeerAddress().toString().c_str());
Serial.print("RSSI: ");
Serial.println(pClient->getRssi());
/** Now we can read/write/subscribe the charateristics of the services we are interested in */
NimBLERemoteService* pSvc = nullptr;
NimBLERemoteCharacteristic* pChr = nullptr;
NimBLERemoteDescriptor* pDsc = nullptr;
pSvc = pClient->getService("DEAD");
if(pSvc) { /** make sure it's not null */
pChr = pSvc->getCharacteristic("BEEF");
}
if(pChr) { /** make sure it's not null */
if(pChr->canRead()) {
Serial.print(pChr->getUUID().toString().c_str());
Serial.print(" Value: ");
Serial.println(pChr->readValue().c_str());
}
if(pChr->canWrite()) {
if(pChr->writeValue("Tasty")) {
Serial.print("Wrote new value to: ");
Serial.println(pChr->getUUID().toString().c_str());
}
else {
/** Disconnect if write failed */
pClient->disconnect();
return false;
}
if(pChr) { /** make sure it's not null */
if(pChr->canRead()) {
Serial.print("The value of: ");
Serial.print(pChr->getUUID().toString().c_str());
Serial.print(" is now: ");
Serial.print(" Value: ");
Serial.println(pChr->readValue().c_str());
}
}
/** registerForNotify() has been deprecated and replaced with subscribe() / unsubscribe().
* Subscribe parameter defaults are: notifications=true, notifyCallback=nullptr, response=false.
* Unsubscribe parameter defaults are: response=false.
*/
if(pChr->canNotify()) {
//if(!pChr->registerForNotify(notifyCB)) {
if(!pChr->subscribe(true, notifyCB)) {
/** Disconnect if subscribe failed */
pClient->disconnect();
return false;
if(pChr->canWrite()) {
if(pChr->writeValue("Tasty")) {
Serial.print("Wrote new value to: ");
Serial.println(pChr->getUUID().toString().c_str());
}
else {
/** Disconnect if write failed */
pClient->disconnect();
return false;
}
if(pChr->canRead()) {
Serial.print("The value of: ");
Serial.print(pChr->getUUID().toString().c_str());
Serial.print(" is now: ");
Serial.println(pChr->readValue().c_str());
}
}
/** registerForNotify() has been deprecated and replaced with subscribe() / unsubscribe().
* Subscribe parameter defaults are: notifications=true, notifyCallback=nullptr, response=false.
* Unsubscribe parameter defaults are: response=false.
*/
if(pChr->canNotify()) {
//if(!pChr->registerForNotify(notifyCB)) {
if(!pChr->subscribe(true, notifyCB)) {
/** Disconnect if subscribe failed */
pClient->disconnect();
return false;
}
}
else if(pChr->canIndicate()) {
/** Send false as first argument to subscribe to indications instead of notifications */
//if(!pChr->registerForNotify(notifyCB, false)) {
if(!pChr->subscribe(false, notifyCB)) {
/** Disconnect if subscribe failed */
pClient->disconnect();
return false;
}
}
}
else if(pChr->canIndicate()) {
/** Send false as first argument to subscribe to indications instead of notifications */
//if(!pChr->registerForNotify(notifyCB, false)) {
if(!pChr->subscribe(false, notifyCB)) {
/** Disconnect if subscribe failed */
pClient->disconnect();
return false;
}
}
}
else{
} else {
Serial.println("DEAD service not found.");
}
pSvc = pClient->getService("BAAD");
if(pSvc) { /** make sure it's not null */
pChr = pSvc->getCharacteristic("F00D");
}
if(pChr) { /** make sure it's not null */
if(pChr->canRead()) {
Serial.print(pChr->getUUID().toString().c_str());
Serial.print(" Value: ");
Serial.println(pChr->readValue().c_str());
}
pDsc = pChr->getDescriptor(NimBLEUUID("C01D"));
if(pDsc) { /** make sure it's not null */
Serial.print("Descriptor: ");
Serial.print(pDsc->getUUID().toString().c_str());
Serial.print(" Value: ");
Serial.println(pDsc->readValue().c_str());
}
if(pChr->canWrite()) {
if(pChr->writeValue("No tip!")) {
Serial.print("Wrote new value to: ");
Serial.println(pChr->getUUID().toString().c_str());
}
else {
/** Disconnect if write failed */
pClient->disconnect();
return false;
}
if(pChr) { /** make sure it's not null */
if(pChr->canRead()) {
Serial.print("The value of: ");
Serial.print(pChr->getUUID().toString().c_str());
Serial.print(" is now: ");
Serial.print(" Value: ");
Serial.println(pChr->readValue().c_str());
}
}
/** registerForNotify() has been deprecated and replaced with subscribe() / unsubscribe().
* Subscribe parameter defaults are: notifications=true, notifyCallback=nullptr, response=false.
* Unsubscribe parameter defaults are: response=false.
*/
if(pChr->canNotify()) {
//if(!pChr->registerForNotify(notifyCB)) {
if(!pChr->subscribe(true, notifyCB)) {
/** Disconnect if subscribe failed */
pClient->disconnect();
return false;
}
}
else if(pChr->canIndicate()) {
/** Send false as first argument to subscribe to indications instead of notifications */
//if(!pChr->registerForNotify(notifyCB, false)) {
if(!pChr->subscribe(false, notifyCB)) {
/** Disconnect if subscribe failed */
pClient->disconnect();
return false;
}
}
}
else{
pDsc = pChr->getDescriptor(NimBLEUUID("C01D"));
if(pDsc) { /** make sure it's not null */
Serial.print("Descriptor: ");
Serial.print(pDsc->getUUID().toString().c_str());
Serial.print(" Value: ");
Serial.println(pDsc->readValue().c_str());
}
if(pChr->canWrite()) {
if(pChr->writeValue("No tip!")) {
Serial.print("Wrote new value to: ");
Serial.println(pChr->getUUID().toString().c_str());
}
else {
/** Disconnect if write failed */
pClient->disconnect();
return false;
}
if(pChr->canRead()) {
Serial.print("The value of: ");
Serial.print(pChr->getUUID().toString().c_str());
Serial.print(" is now: ");
Serial.println(pChr->readValue().c_str());
}
}
/** registerForNotify() has been deprecated and replaced with subscribe() / unsubscribe().
* Subscribe parameter defaults are: notifications=true, notifyCallback=nullptr, response=false.
* Unsubscribe parameter defaults are: response=false.
*/
if(pChr->canNotify()) {
//if(!pChr->registerForNotify(notifyCB)) {
if(!pChr->subscribe(true, notifyCB)) {
/** Disconnect if subscribe failed */
pClient->disconnect();
return false;
}
}
else if(pChr->canIndicate()) {
/** Send false as first argument to subscribe to indications instead of notifications */
//if(!pChr->registerForNotify(notifyCB, false)) {
if(!pChr->subscribe(false, notifyCB)) {
/** Disconnect if subscribe failed */
pClient->disconnect();
return false;
}
}
}
} else {
Serial.println("BAAD service not found.");
}
Serial.println("Done with this device!");
return true;
}
@ -331,7 +329,7 @@ void setup (){
Serial.println("Starting NimBLE Client");
/** Initialize NimBLE, no device name spcified as we are not advertising */
NimBLEDevice::init("");
/** Set the IO capabilities of the device, each option will trigger a different pairing method.
* BLE_HS_IO_KEYBOARD_ONLY - Passkey pairing
* BLE_HS_IO_DISPLAY_YESNO - Numeric comparison pairing
@ -339,37 +337,37 @@ void setup (){
*/
//NimBLEDevice::setSecurityIOCap(BLE_HS_IO_KEYBOARD_ONLY); // use passkey
//NimBLEDevice::setSecurityIOCap(BLE_HS_IO_DISPLAY_YESNO); //use numeric comparison
/** 2 different ways to set security - both calls achieve the same result.
* no bonding, no man in the middle protection, secure connections.
*
* These are the default values, only shown here for demonstration.
*/
//NimBLEDevice::setSecurityAuth(false, false, true);
*
* These are the default values, only shown here for demonstration.
*/
//NimBLEDevice::setSecurityAuth(false, false, true);
NimBLEDevice::setSecurityAuth(/*BLE_SM_PAIR_AUTHREQ_BOND | BLE_SM_PAIR_AUTHREQ_MITM |*/ BLE_SM_PAIR_AUTHREQ_SC);
/** Optional: set the transmit power, default is 3db */
NimBLEDevice::setPower(ESP_PWR_LVL_P9); /** +9db */
/** Optional: set any devices you don't want to get advertisments from */
// NimBLEDevice::addIgnored(NimBLEAddress ("aa:bb:cc:dd:ee:ff"));
/** create new scan */
NimBLEScan* pScan = NimBLEDevice::getScan();
// NimBLEDevice::addIgnored(NimBLEAddress ("aa:bb:cc:dd:ee:ff"));
/** create new scan */
NimBLEScan* pScan = NimBLEDevice::getScan();
/** create a callback that gets called when advertisers are found */
pScan->setAdvertisedDeviceCallbacks(new AdvertisedDeviceCallbacks());
/** Set scan interval (how often) and window (how long) in milliseconds */
pScan->setInterval(45);
pScan->setWindow(15);
/** Active scan will gather scan response data from advertisers
* but will use more energy from both devices
*/
pScan->setActiveScan(true);
/** Start scanning for advertisers for the scan time specified (in seconds) 0 = forever
* Optional callback for when scanning stops.
* Optional callback for when scanning stops.
*/
pScan->start(scanTime, scanEndedCB);
}
@ -380,15 +378,15 @@ void loop (){
while(!doConnect){
delay(1);
}
doConnect = false;
/** Found a device we want to connect to, do it now */
if(connectToServer()) {
Serial.println("Success! we should now be getting notifications, scanning for more!");
} else {
Serial.println("Failed to connect, starting scan");
}
NimBLEDevice::getScan()->start(scanTime,scanEndedCB);
}

View File

@ -0,0 +1,91 @@
/** NimBLE_Secure_Client Demo:
*
* This example demonstrates the secure passkey protected conenction and communication between an esp32 server and an esp32 client.
* Please note that esp32 stores auth info in nvs memory. After a successful connection it is possible that a passkey change will be ineffective.
* To avoid this clear the memory of the esp32's between security testings. esptool.py is capable of this, example: esptool.py --port /dev/ttyUSB0 erase_flash.
*
* Created: on Jan 08 2021
* Author: mblasee
*/
#include <NimBLEDevice.h>
class ClientCallbacks : public NimBLEClientCallbacks
{
uint32_t onPassKeyRequest()
{
Serial.println("Client Passkey Request");
/** return the passkey to send to the server */
/** Change this to be different from NimBLE_Secure_Server if you want to test what happens on key mismatch */
return 123456;
};
};
static ClientCallbacks clientCB;
void setup()
{
Serial.begin(115200);
Serial.println("Starting NimBLE Client");
NimBLEDevice::init("");
NimBLEDevice::setPower(ESP_PWR_LVL_P9);
NimBLEDevice::setSecurityAuth(true, true, true);
NimBLEDevice::setSecurityIOCap(BLE_HS_IO_KEYBOARD_ONLY);
NimBLEScan *pScan = NimBLEDevice::getScan();
NimBLEScanResults results = pScan->start(5);
NimBLEUUID serviceUuid("ABCD");
for (int i = 0; i < results.getCount(); i++)
{
NimBLEAdvertisedDevice device = results.getDevice(i);
Serial.println(device.getName().c_str());
if (device.isAdvertisingService(serviceUuid))
{
NimBLEClient *pClient = NimBLEDevice::createClient();
pClient->setClientCallbacks(&clientCB, false);
if (pClient->connect(&device))
{
pClient->secureConnection();
NimBLERemoteService *pService = pClient->getService(serviceUuid);
if (pService != nullptr)
{
NimBLERemoteCharacteristic *pNonSecureCharacteristic = pService->getCharacteristic("1234");
if (pNonSecureCharacteristic != nullptr)
{
// Testing to read a non secured characteristic, you should be able to read this even if you have mismatching passkeys.
std::string value = pNonSecureCharacteristic->readValue();
// print or do whatever you need with the value
Serial.println(value.c_str());
}
NimBLERemoteCharacteristic *pSecureCharacteristic = pService->getCharacteristic("1235");
if (pSecureCharacteristic != nullptr)
{
// Testing to read a secured characteristic, you should be able to read this only if you have matching passkeys, otherwise you should
// get an error like this. E NimBLERemoteCharacteristic: "<< readValue rc=261"
// This means you are trying to do something without the proper permissions.
std::string value = pSecureCharacteristic->readValue();
// print or do whatever you need with the value
Serial.println(value.c_str());
}
}
}
else
{
// failed to connect
Serial.println("failed to connect");
}
NimBLEDevice::deleteClient(pClient);
}
}
}
void loop()
{
}

View File

@ -0,0 +1,37 @@
/** NimBLE_Secure_Server Demo:
*
* This example demonstrates the secure passkey protected conenction and communication between an esp32 server and an esp32 client.
* Please note that esp32 stores auth info in nvs memory. After a successful connection it is possible that a passkey change will be ineffective.
* To avoid this clear the memory of the esp32's between security testings. esptool.py is capable of this, example: esptool.py --port /dev/ttyUSB0 erase_flash.
*
* Created: on Jan 08 2021
* Author: mblasee
*/
#include <NimBLEDevice.h>
void setup() {
Serial.begin(115200);
Serial.println("Starting NimBLE Server");
NimBLEDevice::init("NimBLE");
NimBLEDevice::setPower(ESP_PWR_LVL_P9);
NimBLEDevice::setSecurityAuth(true, true, true);
NimBLEDevice::setSecurityPasskey(123456);
NimBLEDevice::setSecurityIOCap(BLE_HS_IO_DISPLAY_ONLY);
NimBLEServer *pServer = NimBLEDevice::createServer();
NimBLEService *pService = pServer->createService("ABCD");
NimBLECharacteristic *pNonSecureCharacteristic = pService->createCharacteristic("1234", NIMBLE_PROPERTY::READ );
NimBLECharacteristic *pSecureCharacteristic = pService->createCharacteristic("1235", NIMBLE_PROPERTY::READ | NIMBLE_PROPERTY::READ_ENC | NIMBLE_PROPERTY::READ_AUTHEN);
pService->start();
pNonSecureCharacteristic->setValue("Hello Non Secure BLE");
pSecureCharacteristic->setValue("Hello Secure BLE");
NimBLEAdvertising *pAdvertising = NimBLEDevice::getAdvertising();
pAdvertising->addServiceUUID("ABCD");
pAdvertising->start();
}
void loop() {
}

View File

@ -116,9 +116,9 @@ void setup() {
BLEAdvertising *pAdvertising = BLEDevice::getAdvertising();
pAdvertising->addServiceUUID(SERVICE_UUID);
pAdvertising->setScanResponse(false);
/**This method is removed as it was no longer useful and consumed advertising space
* pAdvertising->setMinPreferred(0x0); // set value to 0x00 to not advertise this parameter
*/
/** Note, this could be left out as that is the default value */
pAdvertising->setMinPreferred(0x0); // set value to 0x00 to not advertise this parameter
BLEDevice::startAdvertising();
Serial.println("Waiting a client connection to notify...");
}

View File

@ -44,10 +44,9 @@ void setup() {
BLEAdvertising *pAdvertising = BLEDevice::getAdvertising();
pAdvertising->addServiceUUID(SERVICE_UUID);
pAdvertising->setScanResponse(true);
/**These methods are removed as they are no longer useful and consumed advertising space
* pAdvertising->setMinPreferred(0x06); // functions that help with iPhone connections issue
* pAdvertising->setMinPreferred(0x12);
*/
pAdvertising->setMinPreferred(0x06); // functions that help with iPhone connections issue
pAdvertising->setMaxPreferred(0x12);
BLEDevice::startAdvertising();
Serial.println("Characteristic defined! Now you can read it in your phone!");
}

View File

@ -120,9 +120,9 @@ void setup() {
BLEAdvertising *pAdvertising = BLEDevice::getAdvertising();
pAdvertising->addServiceUUID(SERVICE_UUID);
pAdvertising->setScanResponse(false);
/**This method is removed it was no longer useful and consumed advertising space
* pAdvertising->setMinPreferred(0x0); // set value to 0x00 to not advertise this parameter
*/
/** Note, this could be left out as that is the default value */
pAdvertising->setMinPreferred(0x0); // set value to 0x00 to not advertise this parameter
BLEDevice::startAdvertising();
Serial.println("Waiting a client connection to notify...");
}

View File

@ -1,5 +1,5 @@
name=NimBLE-Arduino
version=1.0.2
version=1.1.0
author=h2zero
maintainer=h2zero <powellperalta@gmail.com>
sentence=Bluetooth low energy (BLE) library for arduino-esp32 based on NimBLE.

View File

@ -264,10 +264,14 @@ void FreeRTOS::Semaphore::setName(std::string name) {
* @param [in] length The amount of storage to allocate for the ring buffer.
* @param [in] type The type of buffer. One of RINGBUF_TYPE_NOSPLIT, RINGBUF_TYPE_ALLOWSPLIT, RINGBUF_TYPE_BYTEBUF.
*/
#if defined(ESP_IDF_VERSION) && !defined(ESP_IDF_VERSION_VAL) //Quick hack to detect if using IDF version that replaced ringbuf_type_t, ESP_IDF_VERSION_VAL is for IDF>4.0.0
Ringbuffer::Ringbuffer(size_t length, RingbufferType_t type) {
#ifdef ESP_IDF_VERSION //Quick hack to detect if using IDF version that replaced ringbuf_type_t
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(4, 0, 0)
Ringbuffer::Ringbuffer(size_t length, RingbufferType_t type) {
#else
Ringbuffer::Ringbuffer(size_t length, ringbuf_type_t type) {
Ringbuffer::Ringbuffer(size_t length, ringbuf_type_t type) {
#endif
#else
Ringbuffer::Ringbuffer(size_t length, ringbuf_type_t type) {
#endif
m_handle = ::xRingbufferCreate(length, type);
} // Ringbuffer

View File

@ -68,8 +68,12 @@ public:
*/
class Ringbuffer {
public:
#if defined(ESP_IDF_VERSION) && !defined(ESP_IDF_VERSION_VAL) //Quick hack to detect if using IDF version that replaced ringbuf_type_t, ESP_IDF_VERSION_VAL is for IDF>4.0.0
#ifdef ESP_IDF_VERSION //Quick hack to detect if using IDF version that replaced ringbuf_type_t
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(4, 0, 0)
Ringbuffer(size_t length, RingbufferType_t type = RINGBUF_TYPE_NOSPLIT);
#else
Ringbuffer(size_t length, ringbuf_type_t type = RINGBUF_TYPE_NOSPLIT);
#endif
#else
Ringbuffer(size_t length, ringbuf_type_t type = RINGBUF_TYPE_NOSPLIT);
#endif

View File

@ -37,7 +37,7 @@ NimBLE2904::NimBLE2904(NimBLECharacteristic* pCharacterisitic)
m_data.m_unit = 0;
m_data.m_description = 0;
setValue((uint8_t*) &m_data, sizeof(m_data));
} // BLE2902
} // BLE2904
/**

View File

@ -32,7 +32,7 @@ static const char* LOG_TAG = "NimBLEAdvertising";
/**
* @brief Construct a default advertising object.
*/
NimBLEAdvertising::NimBLEAdvertising() {
NimBLEAdvertising::NimBLEAdvertising() : m_slaveItvl() {
memset(&m_advData, 0, sizeof m_advData);
memset(&m_scanData, 0, sizeof m_scanData);
memset(&m_advParams, 0, sizeof m_advParams);
@ -41,15 +41,20 @@ NimBLEAdvertising::NimBLEAdvertising() {
m_advData.name = (uint8_t *)name;
m_advData.name_len = strlen(name);
m_advData.name_is_complete = 1;
m_scanData.tx_pwr_lvl_is_present = 1;
m_scanData.tx_pwr_lvl = NimBLEDevice::getPower();
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;
#else
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;
@ -58,6 +63,8 @@ NimBLEAdvertising::NimBLEAdvertising() {
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
@ -86,7 +93,6 @@ void NimBLEAdvertising::addServiceUUID(const char* serviceUUID) {
* @param [in] serviceUUID The UUID of the service to expose.
*/
void NimBLEAdvertising::removeServiceUUID(const NimBLEUUID &serviceUUID) {
//m_serviceUUIDs.erase(std::remove_if(m_serviceUUIDs.begin(), m_serviceUUIDs.end(),[serviceUUID](const NimBLEUUID &s) {return serviceUUID == s;}), m_serviceUUIDs.end());
for(auto it = m_serviceUUIDs.begin(); it != m_serviceUUIDs.end(); ++it) {
if((*it) == serviceUUID) {
m_serviceUUIDs.erase(it);
@ -112,11 +118,9 @@ void NimBLEAdvertising::setAppearance(uint16_t appearance) {
/**
* @brief Set the type of advertisment to use.
* @param [in] adv_type:
* * BLE_HCI_ADV_TYPE_ADV_IND (0) - indirect advertising
* * BLE_HCI_ADV_TYPE_ADV_DIRECT_IND_HD (1) - direct advertisng - high duty cycle
* * BLE_HCI_ADV_TYPE_ADV_SCAN_IND (2) - indirect scan response
* * BLE_HCI_ADV_TYPE_ADV_NONCONN_IND (3) - indirect advertisng - not connectable
* * BLE_HCI_ADV_TYPE_ADV_DIRECT_IND_LD (4) - direct advertising - low duty cycle
* * BLE_GAP_CONN_MODE_NON (0) - not connectable advertising
* * BLE_GAP_CONN_MODE_DIR (1) - directed connectable advertising
* * BLE_GAP_CONN_MODE_UND (2) - undirected connectable advertising
*/
void NimBLEAdvertising::setAdvertisementType(uint8_t adv_type){
m_advParams.conn_mode = adv_type;
@ -141,6 +145,64 @@ void NimBLEAdvertising::setMaxInterval(uint16_t maxinterval) {
} // setMaxInterval
/**
* @brief Set the advertised min connection interval preferred by this device.
* @param [in] mininterval the max interval value. Range = 0x0006 to 0x0C80.
* @details Values not within the range will cancel advertising of this data.\n
* Consumes 6 bytes of advertising space (combined with max interval).
*/
void NimBLEAdvertising::setMinPreferred(uint16_t mininterval) {
// invalid paramters, set the slave interval to null
if(mininterval < 0x0006 || mininterval > 0x0C80) {
m_advData.slave_itvl_range = nullptr;
return;
}
if(m_advData.slave_itvl_range == nullptr) {
m_advData.slave_itvl_range = m_slaveItvl;
}
m_slaveItvl[0] = mininterval;
m_slaveItvl[1] = mininterval >> 8;
uint16_t maxinterval = *(uint16_t*)(m_advData.slave_itvl_range+2);
// If mininterval is higher than the maxinterval make them the same
if(mininterval > maxinterval) {
m_slaveItvl[2] = m_slaveItvl[0];
m_slaveItvl[3] = m_slaveItvl[1];
}
} // setMinPreferred
/**
* @brief Set the advertised max connection interval preferred by this device.
* @param [in] maxinterval the max interval value. Range = 0x0006 to 0x0C80.
* @details Values not within the range will cancel advertising of this data.\n
* Consumes 6 bytes of advertising space (combined with min interval).
*/
void NimBLEAdvertising::setMaxPreferred(uint16_t maxinterval) {
// invalid paramters, set the slave interval to null
if(maxinterval < 0x0006 || maxinterval > 0x0C80) {
m_advData.slave_itvl_range = nullptr;
return;
}
if(m_advData.slave_itvl_range == nullptr) {
m_advData.slave_itvl_range = m_slaveItvl;
}
m_slaveItvl[2] = maxinterval;
m_slaveItvl[3] = maxinterval >> 8;
uint16_t mininterval = *(uint16_t*)(m_advData.slave_itvl_range);
// If mininterval is higher than the maxinterval make them the same
if(mininterval > maxinterval) {
m_slaveItvl[0] = m_slaveItvl[2];
m_slaveItvl[1] = m_slaveItvl[3];
}
} // setMaxPreferred
/**
* @brief Set if scan response is available.
* @param [in] set true = scan response available.
@ -156,7 +218,8 @@ void NimBLEAdvertising::setScanResponse(bool set) {
* @param [in] connectWhitelistOnly If true, only allow connections from those on the white list.
*/
void NimBLEAdvertising::setScanFilter(bool scanRequestWhitelistOnly, bool connectWhitelistOnly) {
NIMBLE_LOGD(LOG_TAG, ">> setScanFilter: scanRequestWhitelistOnly: %d, connectWhitelistOnly: %d", scanRequestWhitelistOnly, connectWhitelistOnly);
NIMBLE_LOGD(LOG_TAG, ">> setScanFilter: scanRequestWhitelistOnly: %d, connectWhitelistOnly: %d",
scanRequestWhitelistOnly, connectWhitelistOnly);
if (!scanRequestWhitelistOnly && !connectWhitelistOnly) {
m_advParams.filter_policy = BLE_HCI_ADV_FILT_NONE;
NIMBLE_LOGD(LOG_TAG, "<< setScanFilter");
@ -194,7 +257,8 @@ void NimBLEAdvertising::setAdvertisementData(NimBLEAdvertisementData& advertisem
(uint8_t*)advertisementData.getPayload().data(),
advertisementData.getPayload().length());
if (rc != 0) {
NIMBLE_LOGE(LOG_TAG, "ble_gap_adv_set_data: %d %s", rc, NimBLEUtils::returnCodeToString(rc));
NIMBLE_LOGE(LOG_TAG, "ble_gap_adv_set_data: %d %s",
rc, NimBLEUtils::returnCodeToString(rc));
}
m_customAdvData = true; // Set the flag that indicates we are using custom advertising data.
NIMBLE_LOGD(LOG_TAG, "<< setAdvertisementData");
@ -213,7 +277,8 @@ void NimBLEAdvertising::setScanResponseData(NimBLEAdvertisementData& advertiseme
(uint8_t*)advertisementData.getPayload().data(),
advertisementData.getPayload().length());
if (rc != 0) {
NIMBLE_LOGE(LOG_TAG, "ble_gap_adv_rsp_set_data: %d %s", rc, NimBLEUtils::returnCodeToString(rc));
NIMBLE_LOGE(LOG_TAG, "ble_gap_adv_rsp_set_data: %d %s",
rc, NimBLEUtils::returnCodeToString(rc));
}
m_customScanResponseData = true; // Set the flag that indicates we are using custom scan response data.
NIMBLE_LOGD(LOG_TAG, "<< setScanResponseData");
@ -225,13 +290,14 @@ void NimBLEAdvertising::setScanResponseData(NimBLEAdvertisementData& advertiseme
* @param [in] duration The duration, in seconds, to advertise, 0 == advertise forever.
* @param [in] advCompleteCB A pointer to a callback to be invoked when advertising ends.
*/
void NimBLEAdvertising::start(uint32_t duration, void (*advCompleteCB)(NimBLEAdvertising *pAdv)) {
NIMBLE_LOGD(LOG_TAG, ">> Advertising start: customAdvData: %d, customScanResponseData: %d", m_customAdvData, m_customScanResponseData);
bool NimBLEAdvertising::start(uint32_t duration, void (*advCompleteCB)(NimBLEAdvertising *pAdv)) {
NIMBLE_LOGD(LOG_TAG, ">> Advertising start: customAdvData: %d, customScanResponseData: %d",
m_customAdvData, m_customScanResponseData);
// If Host is not synced we cannot start advertising.
if(!NimBLEDevice::m_synced) {
NIMBLE_LOGE(LOG_TAG, "Host reset, wait for sync.");
return;
return false;
}
#if defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL)
@ -240,17 +306,21 @@ void NimBLEAdvertising::start(uint32_t duration, void (*advCompleteCB)(NimBLEAdv
if(!pServer->m_gattsStarted){
pServer->start();
} else if(pServer->getConnectedCount() >= NIMBLE_MAX_CONNECTIONS) {
NIMBLE_LOGW(LOG_TAG, "Max connections reached - not advertising");
return;
NIMBLE_LOGE(LOG_TAG, "Max connections reached - not advertising");
return false;
}
}
#endif
// If already advertising just return
if(ble_gap_adv_active()) {
return;
NIMBLE_LOGW(LOG_TAG, "Advertising already active");
return false;
}
// Save the duration incase of host reset so we can restart with the same params
m_duration = duration;
if(duration == 0){
duration = BLE_HS_FOREVER;
}
@ -260,16 +330,31 @@ void NimBLEAdvertising::start(uint32_t duration, void (*advCompleteCB)(NimBLEAdv
m_advCompCB = advCompleteCB;
m_advParams.disc_mode = BLE_GAP_DISC_MODE_GEN;
m_advData.flags = (BLE_HS_ADV_F_DISC_GEN | BLE_HS_ADV_F_BREDR_UNSUP);
if(m_advParams.conn_mode == BLE_GAP_CONN_MODE_NON) {
if(!m_scanResp) {
m_advParams.disc_mode = BLE_GAP_DISC_MODE_NON;
m_advData.flags = BLE_HS_ADV_F_BREDR_UNSUP;
}
}
int rc = 0;
if (!m_customAdvData && !m_advDataSet) {
//start with 3 bytes for the flags data
uint8_t payloadLen = 3;
uint8_t payloadLen = (2 + 1);
if(m_advData.appearance_is_present)
payloadLen += (2 + BLE_HS_ADV_APPEARANCE_LEN);
if(m_advData.tx_pwr_lvl_is_present)
payloadLen += (2 + 1);
if(m_advData.slave_itvl_range != nullptr)
payloadLen += (2 + 4);
for(auto &it : m_serviceUUIDs) {
if(it.getNative()->u.type == BLE_UUID_TYPE_16) {
int add = (m_advData.num_uuids16 > 0) ? 2 : 4;
if((payloadLen + add) > 31){
if((payloadLen + add) > BLE_HS_ADV_MAX_SZ){
m_advData.uuids16_is_complete = 0;
continue;
}
@ -278,7 +363,7 @@ void NimBLEAdvertising::start(uint32_t duration, void (*advCompleteCB)(NimBLEAdv
if(nullptr == (m_advData.uuids16 = (ble_uuid16_t*)realloc(m_advData.uuids16,
(m_advData.num_uuids16 + 1) * sizeof(ble_uuid16_t))))
{
NIMBLE_LOGE(LOG_TAG, "Error, no mem");
NIMBLE_LOGC(LOG_TAG, "Error, no mem");
abort();
}
memcpy(&m_advData.uuids16[m_advData.num_uuids16].value,
@ -290,7 +375,7 @@ void NimBLEAdvertising::start(uint32_t duration, void (*advCompleteCB)(NimBLEAdv
}
if(it.getNative()->u.type == BLE_UUID_TYPE_32) {
int add = (m_advData.num_uuids32 > 0) ? 4 : 6;
if((payloadLen + add) > 31){
if((payloadLen + add) > BLE_HS_ADV_MAX_SZ){
m_advData.uuids32_is_complete = 0;
continue;
}
@ -299,7 +384,7 @@ void NimBLEAdvertising::start(uint32_t duration, void (*advCompleteCB)(NimBLEAdv
if(nullptr == (m_advData.uuids32 = (ble_uuid32_t*)realloc(m_advData.uuids32,
(m_advData.num_uuids32 + 1) * sizeof(ble_uuid32_t))))
{
NIMBLE_LOGE(LOG_TAG, "Error, no mem");
NIMBLE_LOGC(LOG_TAG, "Error, no mem");
abort();
}
memcpy(&m_advData.uuids32[m_advData.num_uuids32].value,
@ -311,7 +396,7 @@ void NimBLEAdvertising::start(uint32_t duration, void (*advCompleteCB)(NimBLEAdv
}
if(it.getNative()->u.type == BLE_UUID_TYPE_128){
int add = (m_advData.num_uuids128 > 0) ? 16 : 18;
if((payloadLen + add) > 31){
if((payloadLen + add) > BLE_HS_ADV_MAX_SZ){
m_advData.uuids128_is_complete = 0;
continue;
}
@ -320,7 +405,7 @@ void NimBLEAdvertising::start(uint32_t duration, void (*advCompleteCB)(NimBLEAdv
if(nullptr == (m_advData.uuids128 = (ble_uuid128_t*)realloc(m_advData.uuids128,
(m_advData.num_uuids128 + 1) * sizeof(ble_uuid128_t))))
{
NIMBLE_LOGE(LOG_TAG, "Error, no mem");
NIMBLE_LOGC(LOG_TAG, "Error, no mem");
abort();
}
memcpy(&m_advData.uuids128[m_advData.num_uuids128].value,
@ -333,54 +418,74 @@ void 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 + m_advData.name_len) > 29) {
if((payloadLen + (2 + m_advData.name_len)) > BLE_HS_ADV_MAX_SZ) {
if(m_scanResp){
m_scanData.name = m_advData.name;
m_scanData.name_len = m_advData.name_len;
m_scanData.name_is_complete = m_advData.name_is_complete;
if(m_scanData.name_len > BLE_HS_ADV_MAX_SZ - 2) {
m_scanData.name_len = BLE_HS_ADV_MAX_SZ - 2;
m_scanData.name_is_complete = 0;
} else {
m_scanData.name_is_complete = 1;
}
m_advData.name = nullptr;
m_advData.name_len = 0;
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);
}
// if not using scan response just cut the name down
// leaving 2 bytes for the data specifier.
m_advData.name_len = (29 - payloadLen);
if(m_advData.name_len > (BLE_HS_ADV_MAX_SZ - payloadLen - 2)) {
m_advData.name_len = (BLE_HS_ADV_MAX_SZ - payloadLen - 2);
m_advData.name_is_complete = 0;
}
}
m_advData.name_is_complete = 0;
}
if(m_advData.name_len > 0) {
payloadLen += (m_advData.name_len + 2);
}
if(m_scanResp) {
// name length + type byte + length byte + tx power type + length + data
if((m_scanData.name_len + 5) > 31) {
// prioritize name data over tx power
m_scanData.tx_pwr_lvl_is_present = 0;
m_scanData.tx_pwr_lvl = 0;
// limit name to 29 to leave room for the data specifiers
if(m_scanData.name_len > 29) {
m_scanData.name_len = 29;
m_scanData.name_is_complete = false;
}
}
rc = ble_gap_adv_rsp_set_fields(&m_scanData);
if (rc != 0) {
NIMBLE_LOGC(LOG_TAG, "error setting scan response data; rc=%d, %s", rc, NimBLEUtils::returnCodeToString(rc));
abort();
switch(rc) {
case 0:
break;
case BLE_HS_EBUSY:
NIMBLE_LOGE(LOG_TAG, "Already advertising");
break;
case BLE_HS_EMSGSIZE:
NIMBLE_LOGE(LOG_TAG, "Scan data too long");
break;
default:
NIMBLE_LOGE(LOG_TAG, "Error setting scan response data; rc=%d, %s",
rc, NimBLEUtils::returnCodeToString(rc));
break;
}
// if not using scan response and there is room,
// put the tx power data into the advertisment
} else if (payloadLen < 29) {
m_advData.tx_pwr_lvl_is_present = 1;
m_advData.tx_pwr_lvl = NimBLEDevice::getPower();
}
rc = ble_gap_adv_set_fields(&m_advData);
if (rc != 0) {
NIMBLE_LOGC(LOG_TAG, "error setting advertisement data; rc=%d, %s", rc, NimBLEUtils::returnCodeToString(rc));
abort();
if(rc == 0) {
rc = ble_gap_adv_set_fields(&m_advData);
switch(rc) {
case 0:
break;
case BLE_HS_EBUSY:
NIMBLE_LOGE(LOG_TAG, "Already advertising");
break;
case BLE_HS_EMSGSIZE:
NIMBLE_LOGE(LOG_TAG, "Advertisement data too long");
break;
default:
NIMBLE_LOGE(LOG_TAG, "Error setting advertisement data; rc=%d, %s",
rc, NimBLEUtils::returnCodeToString(rc));
break;
}
}
if(m_advData.num_uuids128 > 0) {
@ -401,24 +506,54 @@ void NimBLEAdvertising::start(uint32_t duration, void (*advCompleteCB)(NimBLEAdv
m_advData.num_uuids16 = 0;
}
if(rc !=0) {
return false;
}
m_advDataSet = true;
}
#if defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL)
rc = ble_gap_adv_start(0, NULL, duration,
rc = ble_gap_adv_start(NimBLEDevice::m_own_addr_type, NULL, duration,
&m_advParams,
(pServer != nullptr) ? NimBLEServer::handleGapEvent : NimBLEAdvertising::handleGapEvent,
(pServer != nullptr) ? NimBLEServer::handleGapEvent :
NimBLEAdvertising::handleGapEvent,
(pServer != nullptr) ? (void*)pServer : (void*)this);
#else
rc = ble_gap_adv_start(0, NULL, duration,
rc = ble_gap_adv_start(NimBLEDevice::m_own_addr_type, NULL, duration,
&m_advParams, NimBLEAdvertising::handleGapEvent, this);
#endif
if (rc != 0) {
NIMBLE_LOGC(LOG_TAG, "Error enabling advertising; rc=%d, %s", rc, NimBLEUtils::returnCodeToString(rc));
abort();
switch(rc) {
case 0:
break;
case BLE_HS_EINVAL:
NIMBLE_LOGE(LOG_TAG, "Unable to advertise - Duration too long");
break;
case BLE_HS_EPREEMPTED:
NIMBLE_LOGE(LOG_TAG, "Unable to advertise - busy");
break;
case BLE_HS_ETIMEOUT_HCI:
case BLE_HS_EOS:
case BLE_HS_ECONTROLLER:
case BLE_HS_ENOTSYNCED:
NIMBLE_LOGE(LOG_TAG, "Unable to advertise - Host Reset");
break;
default:
NIMBLE_LOGE(LOG_TAG, "Error enabling advertising; rc=%d, %s",
rc, NimBLEUtils::returnCodeToString(rc));
break;
}
if(rc != 0) {
return false;
}
NIMBLE_LOGD(LOG_TAG, "<< Advertising start");
return true;
} // start
@ -427,9 +562,11 @@ void NimBLEAdvertising::start(uint32_t duration, void (*advCompleteCB)(NimBLEAdv
*/
void NimBLEAdvertising::stop() {
NIMBLE_LOGD(LOG_TAG, ">> stop");
int rc = ble_gap_adv_stop();
if (rc != 0 && rc != BLE_HS_EALREADY) {
NIMBLE_LOGE(LOG_TAG, "ble_gap_adv_stop rc=%d %s", rc, NimBLEUtils::returnCodeToString(rc));
NIMBLE_LOGE(LOG_TAG, "ble_gap_adv_stop rc=%d %s",
rc, NimBLEUtils::returnCodeToString(rc));
return;
}
@ -460,8 +597,17 @@ bool NimBLEAdvertising::isAdvertising() {
* Host reset seems to clear advertising data,
* we need clear the flag so it reloads it.
*/
void NimBLEAdvertising::onHostReset() {
void NimBLEAdvertising::onHostSync() {
NIMBLE_LOGD(LOG_TAG, "Host re-synced");
m_advDataSet = false;
// If we were advertising forever, restart it now
if(m_duration == 0) {
start(m_duration, m_advCompCB);
} else {
// Otherwise we should tell the app that advertising stopped.
advCompleteCB();
}
}
@ -475,6 +621,19 @@ int NimBLEAdvertising::handleGapEvent(struct ble_gap_event *event, void *arg) {
NimBLEAdvertising *pAdv = (NimBLEAdvertising*)arg;
if(event->type == BLE_GAP_EVENT_ADV_COMPLETE) {
switch(event->adv_complete.reason) {
// Don't call the callback if host reset, we want to
// preserve the active flag until re-sync to restart advertising.
case BLE_HS_ETIMEOUT_HCI:
case BLE_HS_EOS:
case BLE_HS_ECONTROLLER:
case BLE_HS_ENOTSYNCED:
NIMBLE_LOGC(LOG_TAG, "host reset, rc=%d", event->adv_complete.reason);
NimBLEDevice::onReset(event->adv_complete.reason);
return 0;
default:
break;
}
pAdv->advCompleteCB();
}
return 0;

View File

@ -77,7 +77,7 @@ public:
void addServiceUUID(const NimBLEUUID &serviceUUID);
void addServiceUUID(const char* serviceUUID);
void removeServiceUUID(const NimBLEUUID &serviceUUID);
void start(uint32_t duration = 0, void (*advCompleteCB)(NimBLEAdvertising *pAdv) = nullptr);
bool start(uint32_t duration = 0, void (*advCompleteCB)(NimBLEAdvertising *pAdv) = nullptr);
void stop();
void setAppearance(uint16_t appearance);
void setAdvertisementType(uint8_t adv_type);
@ -87,13 +87,15 @@ public:
void setScanFilter(bool scanRequestWhitelistOnly, bool connectWhitelistOnly);
void setScanResponseData(NimBLEAdvertisementData& advertisementData);
void setScanResponse(bool);
void setMinPreferred(uint16_t);
void setMaxPreferred(uint16_t);
void advCompleteCB();
bool isAdvertising();
private:
friend class NimBLEDevice;
void onHostReset();
void onHostSync();
static int handleGapEvent(struct ble_gap_event *event, void *arg);
ble_hs_adv_fields m_advData;
@ -104,8 +106,9 @@ private:
bool m_customScanResponseData;
bool m_scanResp;
bool m_advDataSet;
void (*m_advCompCB)(NimBLEAdvertising *pAdv);
void (*m_advCompCB)(NimBLEAdvertising *pAdv);
uint8_t m_slaveItvl[4];
uint32_t m_duration;
};
#endif // #if defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL)

View File

@ -473,9 +473,10 @@ void NimBLECharacteristic::setValue(const uint8_t* data, size_t length) {
return;
}
time_t t = time(nullptr);
portENTER_CRITICAL(&m_valMux);
m_value = std::string((char*)data, length);
m_timestamp = time(nullptr);
m_timestamp = t;
portEXIT_CRITICAL(&m_valMux);
NIMBLE_LOGD(LOG_TAG, "<< setValue");

View File

@ -24,6 +24,9 @@
#include <string>
#include <unordered_set>
#include "nimble/nimble_port.h"
static const char* LOG_TAG = "NimBLEClient";
static NimBLEClientCallbacks defaultCallbacks;
@ -56,11 +59,10 @@ static NimBLEClientCallbacks defaultCallbacks;
NimBLEClient::NimBLEClient(const NimBLEAddress &peerAddress) : m_peerAddress(peerAddress) {
m_pClientCallbacks = &defaultCallbacks;
m_conn_id = BLE_HS_CONN_HANDLE_NONE;
m_isConnected = false;
m_waitingToConnect = false;
m_connectTimeout = 30000;
m_deleteCallbacks = false;
m_pTaskData = nullptr;
m_connEstablished = false;
m_pConnParams.scan_itvl = 16; // Scan interval in 0.625ms units (NimBLE Default)
m_pConnParams.scan_window = 16; // Scan window in 0.625ms units (NimBLE Default)
@ -70,6 +72,9 @@ NimBLEClient::NimBLEClient(const NimBLEAddress &peerAddress) : m_peerAddress(pee
m_pConnParams.supervision_timeout = BLE_GAP_INITIAL_SUPERVISION_TIMEOUT; // timeout = 400*10ms = 4000ms
m_pConnParams.min_ce_len = BLE_GAP_INITIAL_CONN_MIN_CE_LEN; // Minimum length of connection event in 0.625ms units
m_pConnParams.max_ce_len = BLE_GAP_INITIAL_CONN_MAX_CE_LEN; // Maximum length of connection event in 0.625ms units
ble_npl_callout_init(&m_dcTimer, nimble_port_get_dflt_eventq(),
NimBLEClient::dcTimerCb, this);
} // NimBLEClient
@ -89,6 +94,20 @@ NimBLEClient::~NimBLEClient() {
} // ~NimBLEClient
/**
* @brief If we have asked to disconnect and the event does not
* occur within the supervision timeout + added delay, this will
* be called to reset the host in the case of a stalled controller.
*/
void NimBLEClient::dcTimerCb(ble_npl_event *event) {
/* NimBLEClient *pClient = (NimBLEClient*)event->arg;
NIMBLE_LOGC(LOG_TAG, "Timed out disconnecting from %s - resetting host",
std::string(pClient->getPeerAddress()).c_str());
*/
ble_hs_sched_reset(BLE_HS_ECONTROLLER);
}
/**
* @brief Delete all service objects created by this client and clear the vector.
*/
@ -164,70 +183,119 @@ bool NimBLEClient::connect(const NimBLEAddress &address, bool deleteAttibutes) {
return false;
}
if(ble_gap_conn_active()) {
NIMBLE_LOGE(LOG_TAG, "Connection in progress - must wait.");
if(isConnected() || m_connEstablished || m_pTaskData != nullptr) {
NIMBLE_LOGE(LOG_TAG, "Client busy, connected to %s, id=%d",
std::string(m_peerAddress).c_str(), getConnId());
return false;
}
if(!NimBLEDevice::getScan()->stop()) {
ble_addr_t peerAddr_t;
memcpy(&peerAddr_t.val, address.getNative(),6);
peerAddr_t.type = address.getType();
if(ble_gap_conn_find_by_addr(&peerAddr_t, NULL) == 0) {
NIMBLE_LOGE(LOG_TAG, "A connection to %s already exists",
address.toString().c_str());
return false;
}
if(address == NimBLEAddress("")) {
NIMBLE_LOGE(LOG_TAG, "Invalid peer address;(NULL)");
return false;
} else if(m_peerAddress != address) {
} else {
m_peerAddress = address;
}
ble_addr_t peerAddrt;
memcpy(&peerAddrt.val, m_peerAddress.getNative(),6);
peerAddrt.type = m_peerAddress.getType();
ble_task_data_t taskData = {this, xTaskGetCurrentTaskHandle(), 0, nullptr};
m_pTaskData = &taskData;
int rc = 0;
/* Try to connect the the advertiser. Allow 30 seconds (30000 ms) for
* timeout (default value of m_connectTimeout).
* Loop on BLE_HS_EBUSY if the scan hasn't stopped yet.
*/
do{
rc = ble_gap_connect(BLE_OWN_ADDR_PUBLIC, &peerAddrt, m_connectTimeout, &m_pConnParams,
NimBLEClient::handleGapEvent, this);
if(rc == BLE_HS_EBUSY) {
vTaskDelay(1 / portTICK_PERIOD_MS);
}
}while(rc == BLE_HS_EBUSY);
do {
rc = ble_gap_connect(NimBLEDevice::m_own_addr_type, &peerAddr_t,
m_connectTimeout, &m_pConnParams,
NimBLEClient::handleGapEvent, this);
switch (rc) {
case 0:
break;
if (rc != 0 && rc != BLE_HS_EDONE) {
NIMBLE_LOGE(LOG_TAG, "Error: Failed to connect to device; "
"addr=%s, rc=%d; %s",
std::string(m_peerAddress).c_str(),
rc, NimBLEUtils::returnCodeToString(rc));
case BLE_HS_EBUSY:
// Scan was still running, stop it and try again
if (!NimBLEDevice::getScan()->stop()) {
rc = BLE_HS_EUNKNOWN;
}
break;
case BLE_HS_EDONE:
// A connection to this device already exists, do not connect twice.
NIMBLE_LOGE(LOG_TAG, "Already connected to device; addr=%s",
std::string(m_peerAddress).c_str());
break;
case BLE_HS_EALREADY:
// Already attemting to connect to this device, cancel the previous
// attempt and report failure here so we don't get 2 connections.
NIMBLE_LOGE(LOG_TAG, "Already attempting to connect to %s - cancelling",
std::string(m_peerAddress).c_str());
ble_gap_conn_cancel();
break;
default:
NIMBLE_LOGE(LOG_TAG, "Failed to connect to %s, rc=%d; %s",
std::string(m_peerAddress).c_str(),
rc, NimBLEUtils::returnCodeToString(rc));
break;
}
} while (rc == BLE_HS_EBUSY);
if(rc != 0) {
m_pTaskData = nullptr;
m_waitingToConnect = false;
return false;
}
m_waitingToConnect = true;
// Wait for the connect timeout time +1 second for the connection to complete
if(ulTaskNotifyTake(pdTRUE, pdMS_TO_TICKS(m_connectTimeout + 1000)) == pdFALSE) {
m_pTaskData = nullptr;
// If a connection was made but no response from MTU exchange; disconnect
if(isConnected()) {
NIMBLE_LOGE(LOG_TAG, "Connect timeout - no response");
disconnect();
} else {
// workaround; if the controller doesn't cancel the connection
// at the timeout, cancel it here.
NIMBLE_LOGE(LOG_TAG, "Connect timeout - cancelling");
ble_gap_conn_cancel();
}
// Wait for the connection to complete.
ulTaskNotifyTake(pdTRUE, portMAX_DELAY);
if(taskData.rc != 0){
return false;
} else if(taskData.rc != 0){
NIMBLE_LOGE(LOG_TAG, "Connection failed; status=%d %s",
taskData.rc,
NimBLEUtils::returnCodeToString(taskData.rc));
// If the failure was not a result of a disconnection
// make sure we disconnect now to avoid dangling connections
if(isConnected()) {
disconnect();
}
return false;
} else {
NIMBLE_LOGI(LOG_TAG, "Connection established");
}
if(deleteAttibutes) {
deleteServices();
}
m_connEstablished = true;
m_pClientCallbacks->onConnect(this);
NIMBLE_LOGD(LOG_TAG, "<< connect()");
return true;
// Check if still connected before returning
return isConnected();
} // connect
@ -268,12 +336,39 @@ bool NimBLEClient::secureConnection() {
int NimBLEClient::disconnect(uint8_t reason) {
NIMBLE_LOGD(LOG_TAG, ">> disconnect()");
int rc = 0;
if(m_isConnected){
rc = ble_gap_terminate(m_conn_id, reason);
if(rc != 0){
NIMBLE_LOGE(LOG_TAG, "ble_gap_terminate failed: rc=%d %s", rc,
NimBLEUtils::returnCodeToString(rc));
if(isConnected()) {
// If the timer was already started, ignore this call.
if(ble_npl_callout_is_active(&m_dcTimer)) {
NIMBLE_LOGI(LOG_TAG, "Already disconnecting, timer started");
return BLE_HS_EALREADY;
}
ble_gap_conn_desc desc;
if(ble_gap_conn_find(m_conn_id, &desc) != 0){
NIMBLE_LOGI(LOG_TAG, "Connection ID not found");
return BLE_HS_EALREADY;
}
// We use a timer to detect a controller error in the event that it does
// not inform the stack when disconnection is complete.
// This is a common error in certain esp-idf versions.
// The disconnect timeout time is the supervison timeout time + 1 second.
// In the case that the event happenss shortly after the supervision timeout
// we don't want to prematurely reset the host.
ble_npl_time_t ticks;
ble_npl_time_ms_to_ticks((desc.supervision_timeout + 100) * 10, &ticks);
ble_npl_callout_reset(&m_dcTimer, ticks);
rc = ble_gap_terminate(m_conn_id, reason);
if (rc != 0) {
if(rc != BLE_HS_EALREADY) {
ble_npl_callout_stop(&m_dcTimer);
}
NIMBLE_LOGE(LOG_TAG, "ble_gap_terminate failed: rc=%d %s",
rc, NimBLEUtils::returnCodeToString(rc));
}
} else {
NIMBLE_LOGD(LOG_TAG, "Not connected to any peers");
}
NIMBLE_LOGD(LOG_TAG, "<< disconnect()");
@ -283,12 +378,12 @@ int NimBLEClient::disconnect(uint8_t reason) {
/**
* @brief Set the connection paramaters to use when connecting to a server.
* @param [in] minInterval minimum connection interval in 0.625ms units.
* @param [in] maxInterval maximum connection interval in 0.625ms units.
* @param [in] latency number of packets allowed to skip (extends max interval)
* @param [in] timeout the timeout time in 10ms units before disconnecting
* @param [in] scanInterval the scan interval to use when attempting to connect in 0.625ms units.
* @param [in] scanWindow the scan window to use when attempting to connect in 0.625ms units.
* @param [in] minInterval The minimum connection interval in 1.25ms units.
* @param [in] maxInterval The maximum connection interval in 1.25ms units.
* @param [in] latency The number of packets allowed to skip (extends max interval).
* @param [in] timeout The timeout time in 10ms units before disconnecting.
* @param [in] scanInterval The scan interval to use when attempting to connect in 0.625ms units.
* @param [in] scanWindow The scan window to use when attempting to connect in 0.625ms units.
*/
void NimBLEClient::setConnectionParams(uint16_t minInterval, uint16_t maxInterval,
uint16_t latency, uint16_t timeout,
@ -315,10 +410,10 @@ void NimBLEClient::setConnectionParams(uint16_t minInterval, uint16_t maxInterva
/**
* @brief Update the connection parameters:
* * Can only be used after a connection has been established.
* @param [in] minInterval minimum connection interval in 0.625ms units.
* @param [in] maxInterval maximum connection interval in 0.625ms units.
* @param [in] latency number of packets allowed to skip (extends max interval)
* @param [in] timeout the timeout time in 10ms units before disconnecting
* @param [in] minInterval The minimum connection interval in 1.25ms units.
* @param [in] maxInterval The maximum connection interval in 1.25ms units.
* @param [in] latency The number of packets allowed to skip (extends max interval).
* @param [in] timeout The timeout time in 10ms units before disconnecting.
*/
void NimBLEClient::updateConnParams(uint16_t minInterval, uint16_t maxInterval,
uint16_t latency, uint16_t timeout)
@ -454,6 +549,16 @@ NimBLERemoteService* NimBLEClient::getService(const NimBLEUUID &uuid) {
if(m_servicesVector.size() > prev_size) {
return m_servicesVector.back();
}
// If the request was successful but 16/32 bit service not found
// try again with the 128 bit uuid.
if(uuid.bitSize() == BLE_UUID_TYPE_16 ||
uuid.bitSize() == BLE_UUID_TYPE_32)
{
NimBLEUUID uuid128(uuid);
uuid128.to128();
return getService(uuid128);
}
}
NIMBLE_LOGD(LOG_TAG, "<< getService: not found");
@ -510,7 +615,7 @@ bool NimBLEClient::retrieveServices(const NimBLEUUID *uuid_filter) {
NIMBLE_LOGD(LOG_TAG, ">> retrieveServices");
if(!m_isConnected){
if(!isConnected()){
NIMBLE_LOGE(LOG_TAG, "Disconnected, could not retrieve services -aborting");
return false;
}
@ -618,10 +723,11 @@ std::string NimBLEClient::getValue(const NimBLEUUID &serviceUUID, const NimBLEUU
* @param [in] serviceUUID The service that owns the characteristic.
* @param [in] characteristicUUID The characteristic whose value we wish to write.
* @param [in] value The value to write to the characteristic.
* @param [in] response If true, uses write with response operation.
* @returns true if successful otherwise false
*/
bool NimBLEClient::setValue(const NimBLEUUID &serviceUUID, const NimBLEUUID &characteristicUUID,
const std::string &value)
const std::string &value, bool response)
{
NIMBLE_LOGD(LOG_TAG, ">> setValue: serviceUUID: %s, characteristicUUID: %s",
serviceUUID.toString().c_str(), characteristicUUID.toString().c_str());
@ -632,7 +738,7 @@ bool NimBLEClient::setValue(const NimBLEUUID &serviceUUID, const NimBLEUUID &cha
if(pService != nullptr) {
NimBLERemoteCharacteristic* pChar = pService->getCharacteristic(characteristicUUID);
if(pChar != nullptr) {
ret = pChar->writeValue(value);
ret = pChar->writeValue(value, response);
}
}
@ -641,6 +747,31 @@ bool NimBLEClient::setValue(const NimBLEUUID &serviceUUID, const NimBLEUUID &cha
} // setValue
/**
* @brief Get the remote characteristic with the specified handle.
* @param [in] handle The handle of the desired characteristic.
* @returns The matching remote characteristic, nullptr otherwise.
*/
NimBLERemoteCharacteristic* NimBLEClient::getCharacteristic(const uint16_t handle)
{
NimBLERemoteService *pService = nullptr;
for(auto it = m_servicesVector.begin(); it != m_servicesVector.end(); ++it) {
if ((*it)->getStartHandle() <= handle && handle <= (*it)->getEndHandle()) {
pService = *it;
break;
}
}
if (pService != nullptr) {
for (auto it = pService->begin(); it != pService->end(); ++it) {
if ((*it)->getHandle() == handle) {
return *it;
}
}
}
return nullptr;
}
/**
* @brief Get the current mtu of this connection.
@ -656,7 +787,8 @@ uint16_t NimBLEClient::getMTU() {
* @param [in] event The event structure sent by the NimBLE stack.
* @param [in] arg A pointer to the client instance that registered for this callback.
*/
/*STATIC*/ int NimBLEClient::handleGapEvent(struct ble_gap_event *event, void *arg) {
/*STATIC*/
int NimBLEClient::handleGapEvent(struct ble_gap_event *event, void *arg) {
NimBLEClient* client = (NimBLEClient*)arg;
int rc;
@ -665,61 +797,67 @@ uint16_t NimBLEClient::getMTU() {
switch(event->type) {
case BLE_GAP_EVENT_DISCONNECT: {
if(!client->m_isConnected)
return 0;
if(client->m_conn_id != event->disconnect.conn.conn_handle)
return 0;
client->m_isConnected = false;
client->m_waitingToConnect=false;
// Remove the device from ignore list so we will scan it again
NimBLEDevice::removeIgnored(client->m_peerAddress);
NIMBLE_LOGI(LOG_TAG, "disconnect; reason=%d, %s", event->disconnect.reason,
NimBLEUtils::returnCodeToString(event->disconnect.reason));
rc = event->disconnect.reason;
// If Host reset tell the device now before returning to prevent
// any errors caused by calling host functions before resyncing.
switch(event->disconnect.reason) {
case BLE_HS_ETIMEOUT_HCI:
case BLE_HS_EOS:
switch(rc) {
case BLE_HS_ECONTROLLER:
case BLE_HS_ETIMEOUT_HCI:
case BLE_HS_ENOTSYNCED:
NIMBLE_LOGC(LOG_TAG, "Disconnect - host reset, rc=%d", event->disconnect.reason);
NimBLEDevice::onReset(event->disconnect.reason);
case BLE_HS_EOS:
NIMBLE_LOGC(LOG_TAG, "Disconnect - host reset, rc=%d", rc);
NimBLEDevice::onReset(rc);
break;
default:
// Check that the event is for this client.
if(client->m_conn_id != event->disconnect.conn.conn_handle) {
return 0;
}
break;
}
//client->m_conn_id = BLE_HS_CONN_HANDLE_NONE;
// Stop the disconnect timer since we are now disconnected.
ble_npl_callout_stop(&client->m_dcTimer);
// Remove the device from ignore list so we will scan it again
NimBLEDevice::removeIgnored(client->m_peerAddress);
// No longer connected, clear the connection ID.
client->m_conn_id = BLE_HS_CONN_HANDLE_NONE;
// If we received a connected event but did not get established (no PDU)
// then a disconnect event will be sent but we should not send it to the
// app for processing. Instead we will ensure the task is released
// and report the error.
if(!client->m_connEstablished)
break;
NIMBLE_LOGI(LOG_TAG, "disconnect; reason=%d, %s",
rc, NimBLEUtils::returnCodeToString(rc));
client->m_connEstablished = false;
client->m_pClientCallbacks->onDisconnect(client);
rc = event->disconnect.reason;
break;
} // BLE_GAP_EVENT_DISCONNECT
case BLE_GAP_EVENT_CONNECT: {
if(!client->m_waitingToConnect)
// If we aren't waiting for this connection response
// we should drop the connection immediately.
if(client->isConnected() || client->m_pTaskData == nullptr) {
ble_gap_terminate(event->connect.conn_handle, BLE_ERR_REM_USER_CONN_TERM);
return 0;
}
//if(client->m_conn_id != BLE_HS_CONN_HANDLE_NONE)
// return 0;
client->m_waitingToConnect=false;
if (event->connect.status == 0) {
client->m_isConnected = true;
NIMBLE_LOGD(LOG_TAG, "Connection established");
rc = event->connect.status;
if (rc == 0) {
NIMBLE_LOGI(LOG_TAG, "Connected event");
client->m_conn_id = event->connect.conn_handle;
rc = ble_gattc_exchange_mtu(client->m_conn_id, NULL,NULL);
if(rc != 0) {
NIMBLE_LOGE(LOG_TAG, "ble_gattc_exchange_mtu: rc=%d %s",rc,
NimBLEUtils::returnCodeToString(rc));
NIMBLE_LOGE(LOG_TAG, "MTU exchange error; rc=%d %s",
rc, NimBLEUtils::returnCodeToString(rc));
break;
}
@ -727,14 +865,10 @@ uint16_t NimBLEClient::getMTU() {
// scanning since we are already connected to it
NimBLEDevice::addIgnored(client->m_peerAddress);
} else {
NIMBLE_LOGE(LOG_TAG, "Error: Connection failed; status=%d %s",
event->connect.status,
NimBLEUtils::returnCodeToString(event->connect.status));
client->m_isConnected = false;
rc = event->connect.status;
client->m_conn_id = BLE_HS_CONN_HANDLE_NONE;
break;
}
return 0;
} // BLE_GAP_EVENT_CONNECT
@ -742,7 +876,14 @@ uint16_t NimBLEClient::getMTU() {
if(client->m_conn_id != event->notify_rx.conn_handle)
return 0;
NIMBLE_LOGD(LOG_TAG, "Notify Recieved for handle: %d",event->notify_rx.attr_handle);
// If a notification comes before this flag is set we might
// access a vector while it is being cleared in connect()
if(!client->m_connEstablished) {
return 0;
}
NIMBLE_LOGD(LOG_TAG, "Notify Recieved for handle: %d",
event->notify_rx.attr_handle);
for(auto &it: client->m_servicesVector) {
// Dont waste cycles searching services without this handle in its range
@ -752,8 +893,8 @@ uint16_t NimBLEClient::getMTU() {
auto cVector = &it->m_characteristicVector;
NIMBLE_LOGD(LOG_TAG, "checking service %s for handle: %d",
it->getUUID().toString().c_str(),
event->notify_rx.attr_handle);
it->getUUID().toString().c_str(),
event->notify_rx.attr_handle);
auto characteristic = cVector->cbegin();
for(; characteristic != cVector->cend(); ++characteristic) {
@ -762,16 +903,19 @@ uint16_t NimBLEClient::getMTU() {
}
if(characteristic != cVector->cend()) {
NIMBLE_LOGD(LOG_TAG, "Got Notification for characteristic %s", (*characteristic)->toString().c_str());
NIMBLE_LOGD(LOG_TAG, "Got Notification for characteristic %s",
(*characteristic)->toString().c_str());
time_t t = time(nullptr);
portENTER_CRITICAL(&(*characteristic)->m_valMux);
(*characteristic)->m_value = std::string((char *)event->notify_rx.om->om_data, event->notify_rx.om->om_len);
(*characteristic)->m_timestamp = time(nullptr);
(*characteristic)->m_value = std::string((char *)event->notify_rx.om->om_data,
event->notify_rx.om->om_len);
(*characteristic)->m_timestamp = t;
portEXIT_CRITICAL(&(*characteristic)->m_valMux);
if ((*characteristic)->m_notifyCallback != nullptr) {
NIMBLE_LOGD(LOG_TAG, "Invoking callback for notification on characteristic %s",
(*characteristic)->toString().c_str());
(*characteristic)->toString().c_str());
(*characteristic)->m_notifyCallback(*characteristic, event->notify_rx.om->om_data,
event->notify_rx.om->om_len,
!event->notify_rx.indication);
@ -790,10 +934,10 @@ uint16_t NimBLEClient::getMTU() {
}
NIMBLE_LOGD(LOG_TAG, "Peer requesting to update connection parameters");
NIMBLE_LOGD(LOG_TAG, "MinInterval: %d, MaxInterval: %d, Latency: %d, Timeout: %d",
event->conn_update_req.peer_params->itvl_min,
event->conn_update_req.peer_params->itvl_max,
event->conn_update_req.peer_params->latency,
event->conn_update_req.peer_params->supervision_timeout);
event->conn_update_req.peer_params->itvl_min,
event->conn_update_req.peer_params->itvl_max,
event->conn_update_req.peer_params->latency,
event->conn_update_req.peer_params->supervision_timeout);
rc = client->m_pClientCallbacks->onConnParamsUpdateRequest(client,
event->conn_update_req.peer_params) ? 0 : BLE_ERR_CONN_PARMS;
@ -827,7 +971,9 @@ uint16_t NimBLEClient::getMTU() {
return 0;
}
if(event->enc_change.status == 0 || event->enc_change.status == (BLE_HS_ERR_HCI_BASE + BLE_ERR_PINKEY_MISSING)) {
if(event->enc_change.status == 0 ||
event->enc_change.status == (BLE_HS_ERR_HCI_BASE + BLE_ERR_PINKEY_MISSING))
{
struct ble_gap_conn_desc desc;
rc = ble_gap_conn_find(event->enc_change.conn_handle, &desc);
assert(rc == 0);
@ -922,7 +1068,9 @@ uint16_t NimBLEClient::getMTU() {
if(client->m_pTaskData != nullptr) {
client->m_pTaskData->rc = rc;
xTaskNotifyGive(client->m_pTaskData->task);
if(client->m_pTaskData->task) {
xTaskNotifyGive(client->m_pTaskData->task);
}
client->m_pTaskData = nullptr;
}
@ -935,7 +1083,7 @@ uint16_t NimBLEClient::getMTU() {
* @return True if we are connected and false if we are not connected.
*/
bool NimBLEClient::isConnected() {
return m_isConnected;
return m_conn_id != BLE_HS_CONN_HANDLE_NONE;
} // isConnected

View File

@ -30,6 +30,7 @@
#include <string>
class NimBLERemoteService;
class NimBLERemoteCharacteristic;
class NimBLEClientCallbacks;
class NimBLEAdvertisedDevice;
@ -54,7 +55,8 @@ public:
size_t deleteService(const NimBLEUUID &uuid);
std::string getValue(const NimBLEUUID &serviceUUID, const NimBLEUUID &characteristicUUID);
bool setValue(const NimBLEUUID &serviceUUID, const NimBLEUUID &characteristicUUID,
const std::string &value);
const std::string &value, bool response = false);
NimBLERemoteCharacteristic* getCharacteristic(const uint16_t handle);
bool isConnected();
void setClientCallbacks(NimBLEClientCallbacks *pClientCallbacks,
bool deleteCallbacks = true);
@ -82,16 +84,17 @@ private:
const struct ble_gatt_error *error,
const struct ble_gatt_svc *service,
void *arg);
static void dcTimerCb(ble_npl_event *event);
bool retrieveServices(const NimBLEUUID *uuid_filter = nullptr);
NimBLEAddress m_peerAddress;
uint16_t m_conn_id;
bool m_isConnected;
bool m_waitingToConnect;
bool m_connEstablished;
bool m_deleteCallbacks;
int32_t m_connectTimeout;
NimBLEClientCallbacks* m_pClientCallbacks;
ble_task_data_t *m_pTaskData;
ble_task_data_t* m_pTaskData;
ble_npl_callout m_dcTimer;
std::vector<NimBLERemoteService*> m_servicesVector;

View File

@ -25,6 +25,7 @@
#include "nimble/nimble_port.h"
#include "nimble/nimble_port_freertos.h"
#include "host/ble_hs.h"
#include "host/ble_hs_pvcy.h"
#include "host/util/util.h"
#include "services/gap/ble_svc_gap.h"
#include "services/gatt/ble_svc_gatt.h"
@ -60,6 +61,7 @@ std::list <NimBLEClient*> NimBLEDevice::m_cList;
#endif
std::list <NimBLEAddress> NimBLEDevice::m_ignoreList;
NimBLESecurityCallbacks* NimBLEDevice::m_securityCallbacks = nullptr;
uint8_t NimBLEDevice::m_own_addr_type = BLE_OWN_ADDR_PUBLIC;
/**
@ -144,8 +146,8 @@ void NimBLEDevice::stopAdvertising() {
#if defined(CONFIG_BT_NIMBLE_ROLE_CENTRAL)
/* STATIC */ NimBLEClient* NimBLEDevice::createClient(NimBLEAddress peerAddress) {
if(m_cList.size() >= NIMBLE_MAX_CONNECTIONS) {
NIMBLE_LOGW("Number of clients exceeds Max connections. Max=(%d)",
NIMBLE_MAX_CONNECTIONS);
NIMBLE_LOGW(LOG_TAG,"Number of clients exceeds Max connections. Cur=%d Max=%d",
m_cList.size(), NIMBLE_MAX_CONNECTIONS);
}
NimBLEClient* pClient = new NimBLEClient(peerAddress);
@ -165,26 +167,31 @@ void NimBLEDevice::stopAdvertising() {
return false;
}
// Set the connection established flag to false to stop notifications
// from accessing the attribute vectors while they are being deleted.
pClient->m_connEstablished = false;
int rc =0;
if(pClient->m_isConnected) {
if(pClient->isConnected()) {
rc = pClient->disconnect();
if (rc != 0 && rc != BLE_HS_EALREADY && rc != BLE_HS_ENOTCONN) {
return false;
}
while(pClient->m_isConnected) {
vTaskDelay(10);
while(pClient->isConnected()) {
taskYIELD();
}
}
// Since we set the flag to false the app will not get a callback
// in the disconnect event so we call it here for good measure.
pClient->m_pClientCallbacks->onDisconnect(pClient);
if(pClient->m_waitingToConnect) {
} else if(pClient->m_pTaskData != nullptr) {
rc = ble_gap_conn_cancel();
if (rc != 0 && rc != BLE_HS_EALREADY) {
return false;
}
while(pClient->m_waitingToConnect) {
vTaskDelay(10);
while(pClient->m_pTaskData != nullptr) {
taskYIELD();
}
}
@ -405,30 +412,16 @@ void NimBLEDevice::stopAdvertising() {
m_synced = false;
#if defined(CONFIG_BT_NIMBLE_ROLE_OBSERVER)
if(m_pScan != nullptr) {
m_pScan->onHostReset();
}
#endif
/* Not needed
if(m_pServer != nullptr) {
m_pServer->onHostReset();
}
for(auto it = m_cList.cbegin(); it != m_cList.cend(); ++it) {
(*it)->onHostReset();
}
*/
#if defined(CONFIG_BT_NIMBLE_ROLE_BROADCASTER)
if(m_bleAdvertising != nullptr) {
m_bleAdvertising->onHostReset();
}
#endif
NIMBLE_LOGC(LOG_TAG, "Resetting state; reason=%d, %s", reason,
NimBLEUtils::returnCodeToString(reason));
#if defined(CONFIG_BT_NIMBLE_ROLE_OBSERVER)
if(initialized) {
if(m_pScan != nullptr) {
m_pScan->onHostReset();
}
}
#endif
} // onReset
@ -448,20 +441,22 @@ void NimBLEDevice::stopAdvertising() {
int rc = ble_hs_util_ensure_addr(0);
assert(rc == 0);
// Yield for houskeeping before returning to operations.
// Occasionally triggers exception without.
taskYIELD();
m_synced = true;
if(initialized) {
#if defined(CONFIG_BT_NIMBLE_ROLE_OBSERVER)
if(m_pScan != nullptr) {
// Restart scanning with the last values sent, allow to clear results.
m_pScan->start(m_pScan->m_duration, m_pScan->m_scanCompleteCB);
m_pScan->onHostSync();
}
#endif
#if defined(CONFIG_BT_NIMBLE_ROLE_BROADCASTER)
if(m_bleAdvertising != nullptr) {
// Restart advertisng, parameters should already be set.
m_bleAdvertising->start();
m_bleAdvertising->onHostSync();
}
#endif
}
@ -705,6 +700,35 @@ void NimBLEDevice::setSecurityCallbacks(NimBLESecurityCallbacks* callbacks) {
} // setSecurityCallbacks
/**
* @brief Set the own address type.
* @param [in] own_addr_type Own Bluetooth Device address type.\n
* The available bits are defined as:
* * 0x00: BLE_OWN_ADDR_PUBLIC
* * 0x01: BLE_OWN_ADDR_RANDOM
* * 0x02: BLE_OWN_ADDR_RPA_PUBLIC_DEFAULT
* * 0x03: BLE_OWN_ADDR_RPA_RANDOM_DEFAULT
* @param [in] useNRPA If true, and address type is random, uses a non-resolvable random address.
*/
void NimBLEDevice::setOwnAddrType(uint8_t own_addr_type, bool useNRPA) {
m_own_addr_type = own_addr_type;
switch (own_addr_type) {
case BLE_OWN_ADDR_PUBLIC:
ble_hs_pvcy_rpa_config(NIMBLE_HOST_DISABLE_PRIVACY);
break;
case BLE_OWN_ADDR_RANDOM:
setSecurityInitKey(BLE_SM_PAIR_KEY_DIST_ENC | BLE_SM_PAIR_KEY_DIST_ID);
ble_hs_pvcy_rpa_config(useNRPA ? NIMBLE_HOST_ENABLE_NRPA : NIMBLE_HOST_ENABLE_RPA);
break;
case BLE_OWN_ADDR_RPA_PUBLIC_DEFAULT:
case BLE_OWN_ADDR_RPA_RANDOM_DEFAULT:
setSecurityInitKey(BLE_SM_PAIR_KEY_DIST_ENC | BLE_SM_PAIR_KEY_DIST_ID);
ble_hs_pvcy_rpa_config(NIMBLE_HOST_ENABLE_RPA);
break;
}
} // setOwnAddrType
/**
* @brief Start the connection securing and authorization for this connection.
* @param conn_id The connection id of the peer device.

View File

@ -116,6 +116,7 @@ public:
static void setSecurityPasskey(uint32_t pin);
static uint32_t getSecurityPasskey();
static void setSecurityCallbacks(NimBLESecurityCallbacks* pCallbacks);
static void setOwnAddrType(uint8_t own_addr_type, bool useNRPA=false);
static int startSecurity(uint16_t conn_id);
static int setMTU(uint16_t mtu);
static uint16_t getMTU();
@ -182,6 +183,7 @@ private:
static uint32_t m_passkey;
static ble_gap_event_listener m_listener;
static gap_event_handler m_customGapHandler;
static uint8_t m_own_addr_type;
};

View File

@ -0,0 +1,251 @@
/*
* NimBLEHIDDevice.cpp
*
* Created: on Oct 06 2020
* Author wakwak-koba
*
* Originally:
*
* BLEHIDDevice.cpp
*
* Created on: Jan 03, 2018
* Author: chegewara
*/
#include "sdkconfig.h"
#if defined(CONFIG_BT_ENABLED)
#include "nimconfig.h"
#if defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL)
#include "NimBLEHIDDevice.h"
#include "NimBLE2904.h"
/**
* @brief Construct a default NimBLEHIDDevice object.
* @param [in] server A pointer to the server instance this HID Device will use.
*/
NimBLEHIDDevice::NimBLEHIDDevice(NimBLEServer* server) {
/*
* Here we create mandatory services described in bluetooth specification
*/
m_deviceInfoService = server->createService(NimBLEUUID((uint16_t) 0x180a));
m_hidService = server->createService(NimBLEUUID((uint16_t) 0x1812), 40);
m_batteryService = server->createService(NimBLEUUID((uint16_t) 0x180f));
/*
* Mandatory characteristic for device info service
*/
m_pnpCharacteristic = m_deviceInfoService->createCharacteristic((uint16_t) 0x2a50, NIMBLE_PROPERTY::READ);
/*
* Mandatory characteristics for HID service
*/
m_hidInfoCharacteristic = m_hidService->createCharacteristic((uint16_t) 0x2a4a, NIMBLE_PROPERTY::READ);
m_reportMapCharacteristic = m_hidService->createCharacteristic((uint16_t) 0x2a4b, NIMBLE_PROPERTY::READ);
m_hidControlCharacteristic = m_hidService->createCharacteristic((uint16_t) 0x2a4c, NIMBLE_PROPERTY::WRITE_NR);
m_protocolModeCharacteristic = m_hidService->createCharacteristic((uint16_t) 0x2a4e, NIMBLE_PROPERTY::WRITE_NR | NIMBLE_PROPERTY::READ);
/*
* Mandatory battery level characteristic with notification and presence descriptor
*/
m_batteryLevelCharacteristic = m_batteryService->createCharacteristic((uint16_t) 0x2a19, NIMBLE_PROPERTY::READ | NIMBLE_PROPERTY::NOTIFY);
NimBLE2904* batteryLevelDescriptor = (NimBLE2904*)m_batteryLevelCharacteristic->createDescriptor((uint16_t) 0x2904);
batteryLevelDescriptor->setFormat(NimBLE2904::FORMAT_UINT8);
batteryLevelDescriptor->setNamespace(1);
batteryLevelDescriptor->setUnit(0x27ad);
/*
* This value is setup here because its default value in most usage cases, its very rare to use boot mode
* and we want to simplify library using as much as possible
*/
const uint8_t pMode[] = { 0x01 };
protocolMode()->setValue((uint8_t*) pMode, 1);
}
NimBLEHIDDevice::~NimBLEHIDDevice() {
}
/**
* @brief Set the report map data formatting information.
* @param [in] map A pointer to an array with the values to set.
* @param [in] size The number of values in the array.
*/
void NimBLEHIDDevice::reportMap(uint8_t* map, uint16_t size) {
m_reportMapCharacteristic->setValue(map, size);
}
/**
* @brief Start the HID device services.\n
* This function called when all the services have been created.
*/
void NimBLEHIDDevice::startServices() {
m_deviceInfoService->start();
m_hidService->start();
m_batteryService->start();
}
/**
* @brief Create a manufacturer characteristic (this characteristic is optional).
*/
NimBLECharacteristic* NimBLEHIDDevice::manufacturer() {
m_manufacturerCharacteristic = m_deviceInfoService->createCharacteristic((uint16_t) 0x2a29, NIMBLE_PROPERTY::READ);
return m_manufacturerCharacteristic;
}
/**
* @brief Set manufacturer name
* @param [in] name The manufacturer name of this HID device.
*/
void NimBLEHIDDevice::manufacturer(std::string name) {
m_manufacturerCharacteristic->setValue(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] pid The product ID number.
* @param [in] version The produce version number.
*/
void NimBLEHIDDevice::pnp(uint8_t sig, uint16_t vid, uint16_t pid, uint16_t version) {
uint8_t pnp[] = { sig, (uint8_t) (vid >> 8), (uint8_t) vid, (uint8_t) (pid >> 8), (uint8_t) pid, (uint8_t) (version >> 8), (uint8_t) version };
m_pnpCharacteristic->setValue(pnp, sizeof(pnp));
}
/**
* @brief Sets the HID Information characteristic value.
* @param [in] country The country code for the device.
* @param [in] flags The HID Class Specification release number to use.
*/
void NimBLEHIDDevice::hidInfo(uint8_t country, uint8_t flags) {
uint8_t info[] = { 0x11, 0x1, country, flags };
m_hidInfoCharacteristic->setValue(info, sizeof(info));
}
/**
* @brief Create input report characteristic
* @param [in] reportID input report ID, the same as in report map for input object related to the characteristic
* @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);
uint8_t desc1_val[] = { reportID, 0x01 };
inputReportDescriptor->setValue((uint8_t*) desc1_val, 2);
return inputReportCharacteristic;
}
/**
* @brief Create output report characteristic
* @param [in] reportID Output report ID, the same as in report map for output object related to the characteristic
* @return Pointer to new output report characteristic
*/
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);
uint8_t desc1_val[] = { reportID, 0x02 };
outputReportDescriptor->setValue((uint8_t*) desc1_val, 2);
return outputReportCharacteristic;
}
/**
* @brief Create feature report characteristic.
* @param [in] reportID Feature report ID, the same as in report map for feature object related to the characteristic
* @return Pointer to new feature report characteristic
*/
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);
uint8_t desc1_val[] = { reportID, 0x03 };
featureReportDescriptor->setValue((uint8_t*) desc1_val, 2);
return featureReportCharacteristic;
}
/**
* @brief Creates a keyboard boot input report characteristic
*/
NimBLECharacteristic* NimBLEHIDDevice::bootInput() {
return m_hidService->createCharacteristic((uint16_t) 0x2a22, NIMBLE_PROPERTY::NOTIFY);
}
/**
* @brief Create a keyboard boot output report characteristic
*/
NimBLECharacteristic* NimBLEHIDDevice::bootOutput() {
return m_hidService->createCharacteristic((uint16_t) 0x2a32, NIMBLE_PROPERTY::READ | NIMBLE_PROPERTY::WRITE | NIMBLE_PROPERTY::WRITE_NR);
}
/**
* @brief Returns a pointer to the HID control point characteristic.
*/
NimBLECharacteristic* NimBLEHIDDevice::hidControl() {
return m_hidControlCharacteristic;
}
/**
* @brief Returns a pointer to the protocol mode characteristic.
*/
NimBLECharacteristic* NimBLEHIDDevice::protocolMode() {
return m_protocolModeCharacteristic;
}
/**
* @brief Set the battery level characteristic value.
* @param [in] level The battery level value.
*/
void NimBLEHIDDevice::setBatteryLevel(uint8_t level) {
m_batteryLevelCharacteristic->setValue(&level, 1);
}
/*
* @brief Returns battery level characteristic
* @ return battery level characteristic
*//*
BLECharacteristic* BLEHIDDevice::batteryLevel() {
return m_batteryLevelCharacteristic;
}
BLECharacteristic* BLEHIDDevice::reportMap() {
return m_reportMapCharacteristic;
}
BLECharacteristic* BLEHIDDevice::pnp() {
return m_pnpCharacteristic;
}
BLECharacteristic* BLEHIDDevice::hidInfo() {
return m_hidInfoCharacteristic;
}
*/
/**
* @brief Returns a pointer to the device information service.
*/
NimBLEService* NimBLEHIDDevice::deviceInfo() {
return m_deviceInfoService;
}
/**
* @brief Returns a pointer to the HID service.
*/
NimBLEService* NimBLEHIDDevice::hidService() {
return m_hidService;
}
/**
* @brief @brief Returns a pointer to the battery service.
*/
NimBLEService* NimBLEHIDDevice::batteryService() {
return m_batteryService;
}
#endif // #if defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL)
#endif // #if defined(CONFIG_BT_ENABLED)

View File

@ -0,0 +1,89 @@
/*
* NimBLEHIDDevice.h
*
* Created: on Oct 06 2020
* Author wakwak-koba
*
* Originally:
*
* BLEHIDDevice.h
*
* Created on: Jan 03, 2018
* Author: chegewara
*/
#ifndef _BLEHIDDEVICE_H_
#define _BLEHIDDEVICE_H_
#include "sdkconfig.h"
#if defined(CONFIG_BT_ENABLED)
#include "nimconfig.h"
#if defined(CONFIG_BT_NIMBLE_ROLE_BROADCASTER)
#include "NimBLECharacteristic.h"
#include "NimBLEService.h"
#include "NimBLEDescriptor.h"
#include "HIDTypes.h"
#define GENERIC_HID 0x03C0
#define HID_KEYBOARD 0x03C1
#define HID_MOUSE 0x03C2
#define HID_JOYSTICK 0x03C3
#define HID_GAMEPAD 0x03C4
#define HID_TABLET 0x03C5
#define HID_CARD_READER 0x03C6
#define HID_DIGITAL_PEN 0x03C7
#define HID_BARCODE 0x03C8
/**
* @brief A model of a %BLE Human Interface Device.
*/
class NimBLEHIDDevice {
public:
NimBLEHIDDevice(NimBLEServer*);
virtual ~NimBLEHIDDevice();
void reportMap(uint8_t* map, uint16_t);
void startServices();
NimBLEService* deviceInfo();
NimBLEService* hidService();
NimBLEService* batteryService();
NimBLECharacteristic* manufacturer();
void manufacturer(std::string name);
//NimBLECharacteristic* pnp();
void pnp(uint8_t sig, uint16_t vid, uint16_t pid, uint16_t version);
//NimBLECharacteristic* hidInfo();
void hidInfo(uint8_t country, uint8_t flags);
//NimBLECharacteristic* batteryLevel();
void setBatteryLevel(uint8_t level);
//NimBLECharacteristic* reportMap();
NimBLECharacteristic* hidControl();
NimBLECharacteristic* inputReport(uint8_t reportID);
NimBLECharacteristic* outputReport(uint8_t reportID);
NimBLECharacteristic* featureReport(uint8_t reportID);
NimBLECharacteristic* protocolMode();
NimBLECharacteristic* bootInput();
NimBLECharacteristic* bootOutput();
private:
NimBLEService* m_deviceInfoService; //0x180a
NimBLEService* m_hidService; //0x1812
NimBLEService* m_batteryService = 0; //0x180f
NimBLECharacteristic* m_manufacturerCharacteristic; //0x2a29
NimBLECharacteristic* m_pnpCharacteristic; //0x2a50
NimBLECharacteristic* m_hidInfoCharacteristic; //0x2a4a
NimBLECharacteristic* m_reportMapCharacteristic; //0x2a4b
NimBLECharacteristic* m_hidControlCharacteristic; //0x2a4c
NimBLECharacteristic* m_protocolModeCharacteristic; //0x2a4e
NimBLECharacteristic* m_batteryLevelCharacteristic; //0x2a19
};
#endif // CONFIG_BT_NIMBLE_ROLE_BROADCASTER
#endif // CONFIG_BT_ENABLED
#endif /* _BLEHIDDEVICE_H_ */

View File

@ -38,7 +38,7 @@ static const char* LOG_TAG = "NimBLERemoteCharacteristic";
NimBLERemoteCharacteristic::NimBLERemoteCharacteristic(NimBLERemoteService *pRemoteService,
const struct ble_gatt_chr *chr)
{
NIMBLE_LOGD(LOG_TAG, ">> NimBLERemoteCharacteristic()");
switch (chr->uuid.u.type) {
case BLE_UUID_TYPE_16:
m_uuid = NimBLEUUID(chr->uuid.u16.value);
@ -50,7 +50,6 @@ static const char* LOG_TAG = "NimBLERemoteCharacteristic";
m_uuid = NimBLEUUID(const_cast<ble_uuid128_t*>(&chr->uuid.u128));
break;
default:
m_uuid = nullptr;
break;
}
@ -61,6 +60,8 @@ static const char* LOG_TAG = "NimBLERemoteCharacteristic";
m_notifyCallback = nullptr;
m_timestamp = 0;
m_valMux = portMUX_INITIALIZER_UNLOCKED;
NIMBLE_LOGD(LOG_TAG, "<< NimBLERemoteCharacteristic(): %s", m_uuid.toString().c_str());
} // NimBLERemoteCharacteristic
@ -208,15 +209,21 @@ int NimBLERemoteCharacteristic::descriptorDiscCB(uint16_t conn_handle,
bool NimBLERemoteCharacteristic::retrieveDescriptors(const NimBLEUUID *uuid_filter) {
NIMBLE_LOGD(LOG_TAG, ">> retrieveDescriptors() for characteristic: %s", getUUID().toString().c_str());
uint16_t endHandle = getRemoteService()->getEndHandle(this);
if(m_handle >= endHandle) {
return false;
}
int rc = 0;
ble_task_data_t taskData = {this, xTaskGetCurrentTaskHandle(), 0, nullptr};
desc_filter_t filter = {uuid_filter, &taskData};
rc = ble_gattc_disc_all_dscs(getRemoteService()->getClient()->getConnId(),
m_handle,
getRemoteService()->getEndHandle(),
endHandle,
NimBLERemoteCharacteristic::descriptorDiscCB,
&filter);
if (rc != 0) {
NIMBLE_LOGE(LOG_TAG, "ble_gattc_disc_all_chrs: rc=%d %s", rc, NimBLEUtils::returnCodeToString(rc));
return false;
@ -225,12 +232,13 @@ bool NimBLERemoteCharacteristic::retrieveDescriptors(const NimBLEUUID *uuid_filt
ulTaskNotifyTake(pdTRUE, portMAX_DELAY);
if(taskData.rc != 0) {
NIMBLE_LOGE(LOG_TAG, "ble_gattc_disc_all_chrs: startHandle:%d endHandle:%d taskData.rc=%d %s", m_handle, endHandle, taskData.rc, NimBLEUtils::returnCodeToString(0x0100+taskData.rc));
return false;
}
return true;
NIMBLE_LOGD(LOG_TAG, "<< retrieveDescriptors(): Found %d descriptors.", m_descriptorVector.size());
} // getDescriptors
} // retrieveDescriptors
/**
@ -243,7 +251,7 @@ NimBLERemoteDescriptor* NimBLERemoteCharacteristic::getDescriptor(const NimBLEUU
for(auto &it: m_descriptorVector) {
if(it->getUUID() == uuid) {
NIMBLE_LOGD(LOG_TAG, "<< getDescriptor: found");
NIMBLE_LOGD(LOG_TAG, "<< getDescriptor: found the descriptor with uuid: %s", uuid.toString().c_str());
return it;
}
}
@ -253,7 +261,18 @@ NimBLERemoteDescriptor* NimBLERemoteCharacteristic::getDescriptor(const NimBLEUU
if(m_descriptorVector.size() > prev_size) {
return m_descriptorVector.back();
}
// If the request was successful but 16/32 bit descriptor not found
// try again with the 128 bit uuid.
if(uuid.bitSize() == BLE_UUID_TYPE_16 ||
uuid.bitSize() == BLE_UUID_TYPE_32)
{
NimBLEUUID uuid128(uuid);
uuid128.to128();
return getDescriptor(uuid128);
}
}
NIMBLE_LOGD(LOG_TAG, "<< getDescriptor: Not found");
return nullptr;
} // getDescriptor
@ -447,9 +466,10 @@ std::string NimBLERemoteCharacteristic::readValue(time_t *timestamp) {
}
} while(rc != 0 && retryCount--);
time_t t = time(nullptr);
portENTER_CRITICAL(&m_valMux);
m_value = value;
m_timestamp = time(nullptr);
m_timestamp = t;
if(timestamp != nullptr) {
*timestamp = m_timestamp;
}
@ -506,19 +526,19 @@ int NimBLERemoteCharacteristic::onReadCB(uint16_t conn_handle,
* @param [in] notifyCallback A callback to be invoked for a notification.
* @param [in] response If write response required set this to true.
* If NULL is provided then no callback is performed.
* @return true if successful.
* @return false if writing to the descriptor failed.
*/
bool NimBLERemoteCharacteristic::setNotify(uint16_t val, notify_callback notifyCallback, bool response) {
NIMBLE_LOGD(LOG_TAG, ">> setNotify(): %s, %02x", toString().c_str(), val);
m_notifyCallback = notifyCallback;
NimBLERemoteDescriptor* desc = getDescriptor(NimBLEUUID((uint16_t)0x2902));
if(desc == nullptr) {
NIMBLE_LOGE(LOG_TAG, "<< setNotify(): Could not get descriptor");
return false;
NIMBLE_LOGW(LOG_TAG, "<< setNotify(): Callback set, CCCD not found");
return true;
}
m_notifyCallback = notifyCallback;
NIMBLE_LOGD(LOG_TAG, "<< setNotify()");
return desc->writeValue((uint8_t *)&val, 2, response);
@ -531,7 +551,7 @@ bool NimBLERemoteCharacteristic::setNotify(uint16_t val, notify_callback notifyC
* @param [in] notifyCallback A callback to be invoked for a notification.
* @param [in] response If true, require a write response from the descriptor write operation.
* If NULL is provided then no callback is performed.
* @return true if successful.
* @return false if writing to the descriptor failed.
*/
bool NimBLERemoteCharacteristic::subscribe(bool notifications, notify_callback notifyCallback, bool response) {
if(notifications) {
@ -545,7 +565,7 @@ bool NimBLERemoteCharacteristic::subscribe(bool notifications, notify_callback n
/**
* @brief Unsubscribe for notifications or indications.
* @param [in] response bool if true, require a write response from the descriptor write operation.
* @return true if successful.
* @return false if writing to the descriptor failed.
*/
bool NimBLERemoteCharacteristic::unsubscribe(bool response) {
return setNotify(0x00, nullptr, response);

View File

@ -31,6 +31,7 @@ static const char* LOG_TAG = "NimBLERemoteDescriptor";
NimBLERemoteDescriptor::NimBLERemoteDescriptor(NimBLERemoteCharacteristic* pRemoteCharacteristic,
const struct ble_gatt_dsc *dsc)
{
NIMBLE_LOGD(LOG_TAG, ">> NimBLERemoteDescriptor()");
switch (dsc->uuid.u.type) {
case BLE_UUID_TYPE_16:
m_uuid = NimBLEUUID(dsc->uuid.u16.value);
@ -42,12 +43,13 @@ NimBLERemoteDescriptor::NimBLERemoteDescriptor(NimBLERemoteCharacteristic* pRemo
m_uuid = NimBLEUUID(const_cast<ble_uuid128_t*>(&dsc->uuid.u128));
break;
default:
m_uuid = nullptr;
break;
}
m_handle = dsc->handle;
m_pRemoteCharacteristic = pRemoteCharacteristic;
NIMBLE_LOGD(LOG_TAG, "<< NimBLERemoteDescriptor(): %s", m_uuid.toString().c_str());
}

View File

@ -44,12 +44,11 @@ NimBLERemoteService::NimBLERemoteService(NimBLEClient* pClient, const struct ble
m_uuid = NimBLEUUID(const_cast<ble_uuid128_t*>(&service->uuid.u128));
break;
default:
m_uuid = nullptr;
break;
}
m_startHandle = service->start_handle;
m_endHandle = service->end_handle;
NIMBLE_LOGD(LOG_TAG, "<< NimBLERemoteService()");
NIMBLE_LOGD(LOG_TAG, "<< NimBLERemoteService(): %s", m_uuid.toString().c_str());
}
@ -95,8 +94,11 @@ NimBLERemoteCharacteristic* NimBLERemoteService::getCharacteristic(const char* u
* @return A pointer to the characteristic object, or nullptr if not found.
*/
NimBLERemoteCharacteristic* NimBLERemoteService::getCharacteristic(const NimBLEUUID &uuid) {
NIMBLE_LOGD(LOG_TAG, ">> getCharacteristic: uuid: %s", uuid.toString().c_str());
for(auto &it: m_characteristicVector) {
if(it->getUUID() == uuid) {
NIMBLE_LOGD(LOG_TAG, "<< getCharacteristic: found the characteristic with uuid: %s", uuid.toString().c_str());
return it;
}
}
@ -106,8 +108,19 @@ NimBLERemoteCharacteristic* NimBLERemoteService::getCharacteristic(const NimBLEU
if(m_characteristicVector.size() > prev_size) {
return m_characteristicVector.back();
}
// If the request was successful but 16/32 bit characteristic not found
// try again with the 128 bit uuid.
if(uuid.bitSize() == BLE_UUID_TYPE_16 ||
uuid.bitSize() == BLE_UUID_TYPE_32)
{
NimBLEUUID uuid128(uuid);
uuid128.to128();
return getCharacteristic(uuid128);
}
}
NIMBLE_LOGD(LOG_TAG, "<< getCharacteristic: not found");
return nullptr;
} // getCharacteristic
@ -236,6 +249,23 @@ uint16_t NimBLERemoteService::getEndHandle() {
return m_endHandle;
} // getEndHandle
/**
* @brief Get the end handle of specified NimBLERemoteCharacteristic.
*/
uint16_t NimBLERemoteService::getEndHandle(NimBLERemoteCharacteristic *pCharacteristic) {
uint16_t endHandle = m_endHandle;
for(auto &it: m_characteristicVector) {
uint16_t defHandle = it->getDefHandle() - 1;
if(defHandle > pCharacteristic->getDefHandle() && endHandle > defHandle) {
endHandle = defHandle;
}
}
return endHandle;
} // getEndHandle
/**
* @brief Get the service start handle.

View File

@ -70,6 +70,7 @@ private:
uint16_t getStartHandle();
uint16_t getEndHandle();
uint16_t getEndHandle(NimBLERemoteCharacteristic *pCharacteristic);
void releaseSemaphores();
// Properties

View File

@ -30,7 +30,6 @@ static const char* LOG_TAG = "NimBLEScan";
* @brief Scan constuctor.
*/
NimBLEScan::NimBLEScan() {
m_own_addr_type = 0;
m_scan_params.filter_policy = BLE_HCI_SCAN_FILT_NO_WL;
m_scan_params.passive = 1; // If set, dont send scan requests to advertisers (i.e., dont request additional advertising data).
m_scan_params.itvl = 0; // This is defined as the time interval from when the Controller started its last LE scan until it begins the subsequent LE scan. (units=0.625 msec)
@ -38,9 +37,10 @@ NimBLEScan::NimBLEScan() {
m_scan_params.limited = 0; // If set, only discover devices in limited discoverable mode.
m_scan_params.filter_duplicates = 0; // If set, the controller ignores all but the first advertisement from each device.
m_pAdvertisedDeviceCallbacks = nullptr;
m_stopped = true;
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
}
@ -63,8 +63,8 @@ NimBLEScan::~NimBLEScan() {
switch(event->type) {
case BLE_GAP_EVENT_DISC: {
if(pScan->m_stopped) {
NIMBLE_LOGE(LOG_TAG, "Scan stop called, ignoring results.");
if(pScan->m_ignoreResults) {
NIMBLE_LOGE(LOG_TAG, "Scan op in progress - ignoring results");
return 0;
}
@ -129,7 +129,6 @@ NimBLEScan::~NimBLEScan() {
pScan->m_scanCompleteCB(pScan->m_scanResults);
}
pScan->m_stopped = true;
if(pScan->m_pTaskData != nullptr) {
pScan->m_pTaskData->rc = event->disc_complete.reason;
xTaskNotifyGive(pScan->m_pTaskData->task);
@ -238,7 +237,7 @@ void NimBLEScan::setWindow(uint16_t windowMSecs) {
* @return true if scanning or scan starting.
*/
bool NimBLEScan::isScanning() {
return !m_stopped;
return ble_gap_disc_active();
}
@ -252,25 +251,6 @@ bool NimBLEScan::isScanning() {
bool NimBLEScan::start(uint32_t duration, void (*scanCompleteCB)(NimBLEScanResults), bool is_continue) {
NIMBLE_LOGD(LOG_TAG, ">> start(duration=%d)", duration);
// If Host is not synced we cannot start scanning.
if(!NimBLEDevice::m_synced) {
NIMBLE_LOGC(LOG_TAG, "Host reset, wait for sync.");
return false;
}
if(ble_gap_conn_active()) {
NIMBLE_LOGE(LOG_TAG, "Connection in progress - must wait.");
return false;
}
// If we are already scanning don't start again or we will get stuck on the semaphore.
if(!m_stopped || ble_gap_disc_active()) { // double check - can cause host reset.
NIMBLE_LOGE(LOG_TAG, "Scan already in progress");
return false;
}
m_stopped = false;
// Save the callback to be invoked when the scan completes.
m_scanCompleteCB = scanCompleteCB;
// Save the duration in the case that the host is reset so we can reuse it.
@ -281,32 +261,51 @@ bool NimBLEScan::start(uint32_t duration, void (*scanCompleteCB)(NimBLEScanResul
duration = BLE_HS_FOREVER;
}
else{
duration = duration*1000; // convert duration to milliseconds
// convert duration to milliseconds
duration = duration * 1000;
}
// if we are connecting to devices that are advertising even after being connected, multiconnecting peripherals
// then we should not clear vector or we will connect the same device few times
// Set the flag to ignore the results while we are deleting the vector
if(!is_continue) {
clearResults();
m_ignoreResults = true;
}
int rc = 0;
do{
rc = ble_gap_disc(m_own_addr_type, duration, &m_scan_params,
NimBLEScan::handleGapEvent, this);
if(rc == BLE_HS_EBUSY) {
vTaskDelay(1 / portTICK_PERIOD_MS);
}
} while(rc == BLE_HS_EBUSY);
int rc = ble_gap_disc(NimBLEDevice::m_own_addr_type, duration, &m_scan_params,
NimBLEScan::handleGapEvent, this);
if (rc != 0 && rc != BLE_HS_EDONE) {
NIMBLE_LOGE(LOG_TAG, "Error initiating GAP discovery procedure; rc=%d, %s",
rc, NimBLEUtils::returnCodeToString(rc));
m_stopped = true;
switch(rc) {
case 0:
if(!is_continue) {
clearResults();
}
break;
case BLE_HS_EALREADY:
break;
case BLE_HS_EBUSY:
NIMBLE_LOGE(LOG_TAG, "Unable to scan - connection in progress.");
break;
case BLE_HS_ETIMEOUT_HCI:
case BLE_HS_EOS:
case BLE_HS_ECONTROLLER:
case BLE_HS_ENOTSYNCED:
NIMBLE_LOGC(LOG_TAG, "Unable to scan - Host Reset");
break;
default:
NIMBLE_LOGE(LOG_TAG, "Error initiating GAP discovery procedure; rc=%d, %s",
rc, NimBLEUtils::returnCodeToString(rc));
break;
}
m_ignoreResults = false;
NIMBLE_LOGD(LOG_TAG, "<< start()");
if(rc != 0 && rc != BLE_HS_EALREADY) {
return false;
}
NIMBLE_LOGD(LOG_TAG, "<< start()");
return true;
} // start
@ -347,8 +346,6 @@ bool NimBLEScan::stop() {
return false;
}
m_stopped = true;
if (rc != BLE_HS_EALREADY && m_scanCompleteCB != nullptr) {
m_scanCompleteCB(m_scanResults);
}
@ -381,13 +378,25 @@ void NimBLEScan::erase(const NimBLEAddress &address) {
/**
* @brief If the host reset the scan will have stopped so we should set the flag as stopped.
* @brief Called when host reset, we set a flag to stop scanning until synced.
*/
void NimBLEScan::onHostReset() {
m_stopped = true;
m_ignoreResults = true;
}
/**
* @brief If the host reset and re-synced this is called.
* If the application was scanning indefinitely with a callback, restart it.
*/
void NimBLEScan::onHostSync() {
m_ignoreResults = false;
if(m_duration == 0 && m_pAdvertisedDeviceCallbacks != nullptr) {
start(m_duration, m_scanCompleteCB);
}
}
/**
* @brief Get the results of the scan.
* @return NimBLEScanResults object.

View File

@ -83,12 +83,12 @@ private:
~NimBLEScan();
static int handleGapEvent(ble_gap_event* event, void* arg);
void onHostReset();
void onHostSync();
NimBLEAdvertisedDeviceCallbacks* m_pAdvertisedDeviceCallbacks = nullptr;
void (*m_scanCompleteCB)(NimBLEScanResults scanResults);
ble_gap_disc_params m_scan_params;
uint8_t m_own_addr_type;
bool m_stopped;
bool m_ignoreResults;
bool m_wantDuplicates;
NimBLEScanResults m_scanResults;
uint32_t m_duration;

View File

@ -296,6 +296,7 @@ size_t NimBLEServer::getConnectedCount() {
}
server->m_pServerCallbacks->onDisconnect(server);
server->m_pServerCallbacks->onDisconnect(server, &event->disconnect.conn);
if(server->m_advertiseOnDisconnect) {
server->startAdvertising();
@ -620,7 +621,13 @@ uint16_t NimBLEServer::getPeerMTU(uint16_t conn_id) {
/**
* Update connection parameters can be called only after connection has been established
* @brief Request an Update the connection parameters:
* * Can only be used after a connection has been established.
* @param [in] conn_handle The connection handle of the peer to send the request to.
* @param [in] minInterval The minimum connection interval in 1.25ms units.
* @param [in] maxInterval The maximum connection interval in 1.25ms units.
* @param [in] latency The number of packets allowed to skip (extends max interval).
* @param [in] timeout The timeout time in 10ms units before disconnecting.
*/
void NimBLEServer::updateConnParams(uint16_t conn_handle,
uint16_t minInterval, uint16_t maxInterval,
@ -658,6 +665,10 @@ void NimBLEServerCallbacks::onDisconnect(NimBLEServer* pServer) {
NIMBLE_LOGD("NimBLEServerCallbacks", "onDisconnect(): Default");
} // onDisconnect
void NimBLEServerCallbacks::onDisconnect(NimBLEServer* pServer, ble_gap_conn_desc* desc) {
NIMBLE_LOGD("NimBLEServerCallbacks", "onDisconnect(): Default");
} // onDisconnect
uint32_t NimBLEServerCallbacks::onPassKeyRequest(){
NIMBLE_LOGD("NimBLEServerCallbacks", "onPassKeyRequest: default: 123456");
return 123456;

View File

@ -114,6 +114,15 @@ public:
*/
virtual void onDisconnect(NimBLEServer* pServer);
/**
* @brief Handle a client disconnection.
* This is called when a client discconnects.
* @param [in] pServer A pointer to the %BLE server that received the client disconnection.
* @param [in] desc A pointer to the connection description structure containig information
* about the connection.
*/
virtual void onDisconnect(NimBLEServer* pServer, ble_gap_conn_desc* desc);
/**
* @brief Called when a client requests a passkey for pairing.
* @return The passkey to be sent to the client.

View File

@ -264,6 +264,37 @@ std::string NimBLEUUID::toString() const {
*/
bool NimBLEUUID::operator ==(const NimBLEUUID & rhs) const {
if(m_valueSet && rhs.m_valueSet) {
NIMBLE_LOGD(LOG_TAG,"Comparing UUIDs; type %u to %u; UUID %s to %s",
m_uuid.u.type, rhs.m_uuid.u.type,
this->toString().c_str(), rhs.toString().c_str());
if(m_uuid.u.type != rhs.m_uuid.u.type) {
uint8_t uuidBase[16] = {
0xfb, 0x34, 0x9b, 0x5f, 0x80, 0x00, 0x00, 0x80,
0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
};
if(m_uuid.u.type == BLE_UUID_TYPE_128){
if(rhs.m_uuid.u.type == BLE_UUID_TYPE_16){
memcpy(uuidBase+12, &rhs.m_uuid.u16.value, 2);
} else if (rhs.m_uuid.u.type == BLE_UUID_TYPE_32){
memcpy(uuidBase+12, &rhs.m_uuid.u32.value, 4);
}
return memcmp(m_uuid.u128.value,uuidBase,16) == 0;
} else if(rhs.m_uuid.u.type == BLE_UUID_TYPE_128) {
if(m_uuid.u.type == BLE_UUID_TYPE_16){
memcpy(uuidBase+12, &m_uuid.u16.value, 2);
} else if (m_uuid.u.type == BLE_UUID_TYPE_32){
memcpy(uuidBase+12, &m_uuid.u32.value, 4);
}
return memcmp(rhs.m_uuid.u128.value,uuidBase,16) == 0;
} else {
return false;
}
}
return ble_uuid_cmp(&m_uuid.u, &rhs.m_uuid.u) == 0;
}

View File

@ -19,6 +19,13 @@
* under the License.
*/
/*
* This file has been modified by Ryan Powell, aka h2zero.
* The modifications are for the purpose of improving performance and support
* for Esprssif versions used by the ardruino-esp32 core that are less current
* than the esp-idf releases.
*/
#include <assert.h>
#include "sysinit/sysinit.h"
#include "nimble/hci_common.h"
@ -30,8 +37,10 @@
#include "esp_bt.h"
#include "freertos/semphr.h"
#include "esp_compiler.h"
/* IPC is used to improve performance when calls come from a processor not running the NimBLE stack */
/* but does not exist for solo */
#ifndef CONFIG_FREERTOS_UNICORE
#include "esp_ipc.h"
#include "esp_ipc.h"
#endif
#define NIMBLE_VHCI_TIMEOUT_MS 2000
@ -81,31 +90,40 @@ void ble_hci_trans_cfg_hs(ble_hci_trans_rx_cmd_fn *cmd_cb,
ble_hci_rx_acl_hs_arg = acl_arg;
}
void ble_hci_trans_hs_cmd_tx_on_core_0(void *arg)
/* Added; Called from the core NimBLE is running on, not used for unicore */
#ifndef CONFIG_FREERTOS_UNICORE
void ble_hci_trans_hs_cmd_tx_on_core(void *arg)
{
uint8_t *cmd = arg;
uint16_t len = BLE_HCI_CMD_HDR_LEN + cmd[3] + 1;
esp_vhci_host_send_packet(cmd, len);
// Ugly but necessary as the arduino core does not provide enough IPC stack for variables.
esp_vhci_host_send_packet((uint8_t*)arg, *((uint8_t*)arg + 3) + 1 + BLE_HCI_CMD_HDR_LEN);
}
#endif
/* Modified to use ipc calls in arduino to correct performance issues */
int ble_hci_trans_hs_cmd_tx(uint8_t *cmd)
{
uint16_t len;
uint8_t rc = 0;
assert(cmd != NULL);
*cmd = BLE_HCI_UART_H4_CMD;
len = BLE_HCI_CMD_HDR_LEN + cmd[3] + 1;
if (!esp_vhci_host_check_send_available()) {
ESP_LOGD(TAG, "Controller not ready to receive packets");
}
if (xSemaphoreTake(vhci_send_sem, NIMBLE_VHCI_TIMEOUT_MS / portTICK_PERIOD_MS) == pdTRUE) {
if (xPortGetCoreID() != 0) {
/* esp_ipc_call_blocking does not exist for solo */
#ifndef CONFIG_FREERTOS_UNICORE
esp_ipc_call_blocking(0, ble_hci_trans_hs_cmd_tx_on_core_0, cmd);
#endif
if (xPortGetCoreID() != CONFIG_BT_NIMBLE_PINNED_TO_CORE) {
esp_ipc_call_blocking(CONFIG_BT_NIMBLE_PINNED_TO_CORE,
ble_hci_trans_hs_cmd_tx_on_core, cmd);
} else {
ble_hci_trans_hs_cmd_tx_on_core_0(cmd);
esp_vhci_host_send_packet(cmd, len);
}
#else /* Unicore */
esp_vhci_host_send_packet(cmd, len);
#endif
} else {
rc = BLE_HS_ETIMEOUT_HCI;
}
@ -124,21 +142,21 @@ int ble_hci_trans_ll_evt_tx(uint8_t *hci_ev)
return rc;
}
void ble_hci_trans_hs_acl_tx_on_core_0(void *arg)
/* Added; Called from the core NimBLE is running on, not used for unicore */
#ifndef CONFIG_FREERTOS_UNICORE
void ble_hci_trans_hs_acl_tx_on_core(void *arg)
{
uint8_t data[MYNEWT_VAL(BLE_ACL_BUF_SIZE) + 1];
struct os_mbuf *om = arg;
uint16_t len = 1 + OS_MBUF_PKTLEN(om);
data[0] = BLE_HCI_UART_H4_ACL;
os_mbuf_copydata(om, 0, OS_MBUF_PKTLEN(om), &data[1]);
esp_vhci_host_send_packet(data, len);
// Ugly but necessary as the arduino core does not provide enough IPC stack for variables.
esp_vhci_host_send_packet((uint8_t*)arg + 2, *(uint16_t*)arg);
}
#endif
/* Modified to use ipc calls in arduino to correct performance issues */
int ble_hci_trans_hs_acl_tx(struct os_mbuf *om)
{
uint8_t rc = 0;
uint16_t len = 0;
uint8_t data[MYNEWT_VAL(BLE_ACL_BUF_SIZE) + 3], rc = 0;
bool tx_using_nimble_core = 0;
/* If this packet is zero length, just free it */
if (OS_MBUF_PKTLEN(om) == 0) {
os_mbuf_free_chain(om);
@ -149,14 +167,36 @@ int ble_hci_trans_hs_acl_tx(struct os_mbuf *om)
ESP_LOGD(TAG, "Controller not ready to receive packets");
}
if (xSemaphoreTake(vhci_send_sem, NIMBLE_VHCI_TIMEOUT_MS / portTICK_PERIOD_MS) == pdTRUE) {
if (xPortGetCoreID() != 0) {
len = 1 + OS_MBUF_PKTLEN(om);
/* Don't check core ID if unicore */
#ifndef CONFIG_FREERTOS_UNICORE
esp_ipc_call_blocking(0, ble_hci_trans_hs_acl_tx_on_core_0, om);
tx_using_nimble_core = xPortGetCoreID() != CONFIG_BT_NIMBLE_PINNED_TO_CORE;
if (tx_using_nimble_core) {
data[0] = len;
data[1] = (len >> 8);
data[2] = BLE_HCI_UART_H4_ACL;
os_mbuf_copydata(om, 0, OS_MBUF_PKTLEN(om), &data[3]);
} else {
data[0] = BLE_HCI_UART_H4_ACL;
os_mbuf_copydata(om, 0, OS_MBUF_PKTLEN(om), &data[1]);
}
#else /* Unicore */
data[0] = BLE_HCI_UART_H4_ACL;
os_mbuf_copydata(om, 0, OS_MBUF_PKTLEN(om), &data[1]);
#endif
if (xSemaphoreTake(vhci_send_sem, NIMBLE_VHCI_TIMEOUT_MS / portTICK_PERIOD_MS) == pdTRUE) {
/* esp_ipc_call_blocking does not exist for solo */
#ifndef CONFIG_FREERTOS_UNICORE
if (tx_using_nimble_core) {
esp_ipc_call_blocking(CONFIG_BT_NIMBLE_PINNED_TO_CORE,
ble_hci_trans_hs_acl_tx_on_core, data);
} else {
ble_hci_trans_hs_acl_tx_on_core_0(om);
esp_vhci_host_send_packet(data, len);
}
#else /* Unicore */
esp_vhci_host_send_packet(data, len);
#endif
} else {
rc = BLE_HS_ETIMEOUT_HCI;
}
@ -367,6 +407,13 @@ static int host_rcv_pkt(uint8_t *data, uint16_t len)
totlen = BLE_HCI_EVENT_HDR_LEN + data[2];
assert(totlen <= UINT8_MAX + BLE_HCI_EVENT_HDR_LEN);
if (totlen > MYNEWT_VAL(BLE_HCI_EVT_BUF_SIZE)) {
ESP_LOGE(TAG, "Received HCI data length at host (%d) exceeds maximum configured HCI event buffer size (%d).",
totlen, MYNEWT_VAL(BLE_HCI_EVT_BUF_SIZE));
ble_hs_sched_reset(BLE_HS_ECONTROLLER);
return 0;
}
if (data[1] == BLE_HCI_EVCODE_HW_ERROR) {
assert(0);
}
@ -476,6 +523,9 @@ 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) {
@ -485,6 +535,7 @@ esp_err_t esp_nimble_hci_and_controller_init(void)
if ((ret = esp_bt_controller_enable(ESP_BT_MODE_BLE)) != ESP_OK) {
return ret;
}
return esp_nimble_hci_init();
}

View File

@ -483,6 +483,10 @@
#define MYNEWT_VAL_BLE_L2CAP_COC_MAX_NUM CONFIG_BT_NIMBLE_L2CAP_COC_MAX_NUM
#endif
#ifndef MYNEWT_VAL_BLE_L2CAP_COC_MPS
#define MYNEWT_VAL_BLE_L2CAP_COC_MPS (MYNEWT_VAL_MSYS_1_BLOCK_SIZE - 8)
#endif
#ifndef MYNEWT_VAL_BLE_L2CAP_JOIN_RX_FRAGS
#define MYNEWT_VAL_BLE_L2CAP_JOIN_RX_FRAGS (1)
#endif

View File

@ -76,7 +76,7 @@ ble_eddystone_set_adv_data_gen(struct ble_hs_adv_fields *adv_fields,
if (adv_fields->num_uuids16 > BLE_EDDYSTONE_MAX_UUIDS16) {
return BLE_HS_EINVAL;
}
if (svc_data_len > BLE_EDDYSTONE_MAX_SVC_DATA_LEN) {
if (svc_data_len > (BLE_EDDYSTONE_MAX_SVC_DATA_LEN - BLE_EDDYSTONE_SVC_DATA_BASE_SZ)) {
return BLE_HS_EINVAL;
}
if (adv_fields->num_uuids16 > 0 && !adv_fields->uuids16_is_complete) {

View File

@ -1017,7 +1017,7 @@ ble_gap_master_failed(int status)
#endif
default:
BLE_HS_DBG_ASSERT(0);
//BLE_HS_DBG_ASSERT(0);
break;
}
}
@ -1458,8 +1458,8 @@ ble_gap_rx_periodic_adv_rpt(struct hci_le_subev_periodic_adv_rpt *evt)
{
struct ble_hs_periodic_sync *psync;
struct ble_gap_event event;
ble_gap_event_fn *cb;
void *cb_arg;
ble_gap_event_fn *cb = NULL;
void *cb_arg = NULL;
ble_hs_lock();
psync = ble_hs_periodic_sync_find_by_handle(evt->sync_handle);

View File

@ -470,29 +470,52 @@ ble_hs_conn_timer(void)
int32_t time_diff;
uint16_t conn_handle;
conn_handle = BLE_HS_CONN_HANDLE_NONE;
next_exp_in = BLE_HS_FOREVER;
now = ble_npl_time_get();
for (;;) {
conn_handle = BLE_HS_CONN_HANDLE_NONE;
next_exp_in = BLE_HS_FOREVER;
now = ble_npl_time_get();
ble_hs_lock();
ble_hs_lock();
/* This loop performs one of two tasks:
* 1. Determine if any connections need to be terminated due to timeout.
* If so, break out of the loop and terminate the connection. This
* function will need to be executed again.
* 2. Otherwise, determine when the next timeout will occur.
*/
SLIST_FOREACH(conn, &ble_hs_conns, bhc_next) {
if (!(conn->bhc_flags & BLE_HS_CONN_F_TERMINATING)) {
/* This loop performs one of two tasks:
* 1. Determine if any connections need to be terminated due to timeout.
* If so, break out of the loop and terminate the connection. This
* function will need to be executed again.
* 2. Otherwise, determine when the next timeout will occur.
*/
SLIST_FOREACH(conn, &ble_hs_conns, bhc_next) {
if (!(conn->bhc_flags & BLE_HS_CONN_F_TERMINATING)) {
#if MYNEWT_VAL(BLE_L2CAP_RX_FRAG_TIMEOUT) != 0
/* Check each connection's rx fragment timer. If too much time
* passes after a partial packet is received, the connection is
* terminated.
*/
if (conn->bhc_rx_chan != NULL) {
time_diff = conn->bhc_rx_timeout - now;
/* Check each connection's rx fragment timer. If too much time
* passes after a partial packet is received, the connection is
* terminated.
*/
if (conn->bhc_rx_chan != NULL) {
time_diff = conn->bhc_rx_timeout - now;
if (time_diff <= 0) {
/* ACL reassembly has timed out. Remember the connection
* handle so it can be terminated after the mutex is
* unlocked.
*/
conn_handle = conn->bhc_handle;
break;
}
/* Determine if this connection is the soonest to time out. */
if (time_diff < next_exp_in) {
next_exp_in = time_diff;
}
}
#endif
#if BLE_HS_ATT_SVR_QUEUED_WRITE_TMO
/* Check each connection's rx queued write timer. If too much
* time passes after a prep write is received, the queue is
* cleared.
*/
time_diff = ble_att_svr_ticks_until_tmo(&conn->bhc_att_svr, now);
if (time_diff <= 0) {
/* ACL reassembly has timed out. Remember the connection
* handle so it can be terminated after the mutex is
@ -506,45 +529,22 @@ ble_hs_conn_timer(void)
if (time_diff < next_exp_in) {
next_exp_in = time_diff;
}
}
#endif
#if BLE_HS_ATT_SVR_QUEUED_WRITE_TMO
/* Check each connection's rx queued write timer. If too much
* time passes after a prep write is received, the queue is
* cleared.
*/
time_diff = ble_att_svr_ticks_until_tmo(&conn->bhc_att_svr, now);
if (time_diff <= 0) {
/* ACL reassembly has timed out. Remember the connection
* handle so it can be terminated after the mutex is
* unlocked.
*/
conn_handle = conn->bhc_handle;
break;
}
/* Determine if this connection is the soonest to time out. */
if (time_diff < next_exp_in) {
next_exp_in = time_diff;
}
#endif
}
ble_hs_unlock();
/* If a connection has timed out, terminate it. We need to repeatedly
* call this function again to determine when the next timeout is.
*/
if (conn_handle != BLE_HS_CONN_HANDLE_NONE) {
ble_gap_terminate(conn_handle, BLE_ERR_REM_USER_CONN_TERM);
continue;
}
return next_exp_in;
}
ble_hs_unlock();
/* If a connection has timed out, terminate it. We need to recursively
* call this function again to determine when the next timeout is. This
* is a tail-recursive call, so it should be optimized to execute in the
* same stack frame.
*/
if (conn_handle != BLE_HS_CONN_HANDLE_NONE) {
ble_gap_terminate(conn_handle, BLE_ERR_REM_USER_CONN_TERM);
return ble_hs_conn_timer();
}
return next_exp_in;
}
int

View File

@ -180,7 +180,9 @@ static int
get_nvs_db_attribute(int obj_type, bool empty, void *value, int num_value)
{
union ble_store_value cur = {0};
#if MYNEWT_VAL(BLE_HOST_BASED_PRIVACY)
struct ble_hs_dev_records p_dev_rec = {0};
#endif
esp_err_t err;
int i, count = 0, max_limit = 0;
char key_string[NIMBLE_NVS_STR_NAME_MAX_LEN];
@ -190,11 +192,15 @@ get_nvs_db_attribute(int obj_type, bool empty, void *value, int num_value)
for (i = 1; i <= max_limit; i++) {
get_nvs_key_string(obj_type, i, key_string);
#if MYNEWT_VAL(BLE_HOST_BASED_PRIVACY)
if (obj_type != BLE_STORE_OBJ_TYPE_PEER_DEV_REC) {
#endif
err = get_nvs_db_value(obj_type, key_string, &cur);
#if MYNEWT_VAL(BLE_HOST_BASED_PRIVACY)
} else {
err = get_nvs_peer_record(key_string, &p_dev_rec);
}
#endif
/* Check if the user is searching for empty index to write to */
if (err == ESP_ERR_NVS_NOT_FOUND) {
if (empty) {
@ -206,10 +212,13 @@ get_nvs_db_attribute(int obj_type, bool empty, void *value, int num_value)
/* If user has provided value, then the purpose is to find
* non-matching entry from NVS */
if (value) {
#if MYNEWT_VAL(BLE_HOST_BASED_PRIVACY)
if (obj_type == BLE_STORE_OBJ_TYPE_PEER_DEV_REC) {
err = get_nvs_matching_index(&p_dev_rec, value, num_value,
sizeof(struct ble_hs_dev_records));
} else {
} else
#endif
{
if (obj_type != BLE_STORE_OBJ_TYPE_CCCD) {
err = get_nvs_matching_index(&cur.sec, value, num_value,
sizeof(struct ble_store_value_sec));
@ -376,7 +385,9 @@ populate_db_from_nvs(int obj_type, void *dst, int *db_num)
{
uint8_t *db_item = (uint8_t *)dst;
union ble_store_value cur = {0};
#if MYNEWT_VAL(BLE_HOST_BASED_PRIVACY)
struct ble_hs_dev_records p_dev_rec = {0};
#endif
esp_err_t err;
int i;
@ -385,8 +396,9 @@ populate_db_from_nvs(int obj_type, void *dst, int *db_num)
for (i = 1; i <= get_nvs_max_obj_value(obj_type); i++) {
get_nvs_key_string(obj_type, i, key_string);
#if MYNEWT_VAL(BLE_HOST_BASED_PRIVACY)
if (obj_type != BLE_STORE_OBJ_TYPE_PEER_DEV_REC) {
#endif
err = get_nvs_db_value(obj_type, key_string, &cur);
if (err == ESP_ERR_NVS_NOT_FOUND) {
continue;
@ -394,6 +406,7 @@ populate_db_from_nvs(int obj_type, void *dst, int *db_num)
ESP_LOGE(TAG, "NVS read operation failed !!");
return -1;
}
#if MYNEWT_VAL(BLE_HOST_BASED_PRIVACY)
} else {
err = get_nvs_peer_record(key_string, &p_dev_rec);
if (err == ESP_ERR_NVS_NOT_FOUND) {
@ -410,7 +423,9 @@ populate_db_from_nvs(int obj_type, void *dst, int *db_num)
memcpy(db_item, &p_dev_rec, sizeof(struct ble_hs_dev_records));
db_item += sizeof(struct ble_hs_dev_records);
(*db_num)++;
} else {
} else
#endif
{
if (obj_type == BLE_STORE_OBJ_TYPE_CCCD) {
ESP_LOGD(TAG, "CCCD in RAM is filled up from NVS index = %d", i);
memcpy(db_item, &cur.cccd, sizeof(struct ble_store_value_cccd));
@ -492,6 +507,11 @@ int ble_store_config_persist_cccds(void)
union ble_store_value val;
nvs_count = get_nvs_db_attribute(BLE_STORE_OBJ_TYPE_CCCD, 0, NULL, 0);
if (nvs_count == -1) {
ESP_LOGE(TAG, "NVS operation failed while persisting CCCD");
return BLE_HS_ESTORE_FAIL;
}
if (nvs_count < ble_store_config_num_cccds) {
/* NVS db count less than RAM count, write operation */
@ -518,6 +538,11 @@ int ble_store_config_persist_peer_secs(void)
union ble_store_value val;
nvs_count = get_nvs_db_attribute(BLE_STORE_OBJ_TYPE_PEER_SEC, 0, NULL, 0);
if (nvs_count == -1) {
ESP_LOGE(TAG, "NVS operation failed while persisting peer sec");
return BLE_HS_ESTORE_FAIL;
}
if (nvs_count < ble_store_config_num_peer_secs) {
/* NVS db count less than RAM count, write operation */
@ -544,6 +569,11 @@ int ble_store_config_persist_our_secs(void)
union ble_store_value val;
nvs_count = get_nvs_db_attribute(BLE_STORE_OBJ_TYPE_OUR_SEC, 0, NULL, 0);
if (nvs_count == -1) {
ESP_LOGE(TAG, "NVS operation failed while persisting our sec");
return BLE_HS_ESTORE_FAIL;
}
if (nvs_count < ble_store_config_num_our_secs) {
/* NVS db count less than RAM count, write operation */
@ -573,7 +603,13 @@ int ble_store_persist_peer_records(void)
struct ble_hs_dev_records *peer_dev_rec = ble_rpa_get_peer_dev_records();
nvs_count = get_nvs_db_attribute(BLE_STORE_OBJ_TYPE_PEER_DEV_REC, 0, NULL, 0);
if (nvs_count == -1) {
ESP_LOGE(TAG, "NVS operation failed while persisting peer_dev_rec");
return BLE_HS_ESTORE_FAIL;
}
if (nvs_count < ble_store_num_peer_dev_rec) {
/* NVS db count less than RAM count, write operation */
ESP_LOGD(TAG, "Persisting peer dev record to NVS...");
peer_rec = peer_dev_rec[ble_store_num_peer_dev_rec - 1];

View File

@ -15,8 +15,13 @@
* 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) */
#if defined(CONFIG_BT_NIMBLE_TASK_STACK_SIZE) || defined(CONFIG_NIMBLE_TASK_STACK_SIZE)
/* 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
@ -51,22 +56,30 @@
/** @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 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 Comment out if not using NimBLE Server functions \n
* Reduces flash size by approx. 16kB.
*/
// #define CONFIG_BT_NIMBLE_ROLE_PERIPHERAL
#ifndef CONFIG_BT_NIMBLE_ROLE_PERIPHERAL_DISABLED
#define CONFIG_BT_NIMBLE_ROLE_PERIPHERAL
#endif
/** @brief Comment out if not using NimBLE Advertising functions \n
* Reduces flash size by approx. 5kB.
*/
// #define CONFIG_BT_NIMBLE_ROLE_BROADCASTER
#ifndef CONFIG_BT_NIMBLE_ROLE_BROADCASTER_DISABLED
#define CONFIG_BT_NIMBLE_ROLE_BROADCASTER
#endif
/* Uncomment to see debug log messages from the NimBLE host
* Uses approx. 32kB of flash memory.
@ -89,29 +102,46 @@
// #define CONFIG_NIMBLE_CPP_ENABLE_ADVERTISMENT_TYPE_TEXT
/** @brief Sets the core NimBLE host runs on */
#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
/** @brief Set if CCCD's and bond data should be stored in NVS */
#define CONFIG_BT_NIMBLE_NVS_PERSIST 0
#define CONFIG_BT_NIMBLE_NVS_PERSIST 1
/** @brief Allow legacy paring */
#define CONFIG_BT_NIMBLE_SM_LEGACY 1
@ -119,9 +149,6 @@
/** @brief Allow BLE secure connections */
#define CONFIG_BT_NIMBLE_SM_SC 1
/** @brief Default device name */
#define CONFIG_BT_NIMBLE_SVC_GAP_DEVICE_NAME "nimble"
/** @brief Max device name length (bytes) */
#define CONFIG_BT_NIMBLE_GAP_DEVICE_NAME_MAX_LEN 31
@ -154,7 +181,6 @@
*/
#define CONFIG_BT_NIMBLE_MSYS1_BLOCK_COUNT 12
/** @brief Random address refresh time in seconds */
#define CONFIG_BT_NIMBLE_RPA_TIMEOUT 900

View File

@ -34,8 +34,8 @@ void CRtspSession::Init()
bool CRtspSession::ParseRtspRequest(char const * aRequest, unsigned aRequestSize)
{
char CmdName[RTSP_PARAM_STRING_MAX];
static char CurRequest[RTSP_BUFFER_SIZE]; // Note: we assume single threaded, this large buf we keep off of the tiny stack
// char CmdName[RTSP_PARAM_STRING_MAX];
//char CurRequest[RTSP_BUFFER_SIZE]; // Note: we assume single threaded, this large buf we keep off of the tiny stack
unsigned CurRequestSize;
Init();
@ -45,7 +45,7 @@ bool CRtspSession::ParseRtspRequest(char const * aRequest, unsigned aRequestSize
// check whether the request contains information about the RTP/RTCP UDP client ports (SETUP command)
char * ClientPortPtr;
char * TmpPtr;
static char CP[1024];
char CP[128]; //static char CP[1024];
char * pCP;
ClientPortPtr = strstr(CurRequest,"client_port");
@ -230,7 +230,7 @@ RTSP_CMD_TYPES CRtspSession::Handle_RtspRequest(char const * aRequest, unsigned
void CRtspSession::Handle_RtspOPTION()
{
static char Response[1024]; // Note: we assume single threaded, this large buf we keep off of the tiny stack
//static char Response[1024]; // Note: we assume single threaded, this large buf we keep off of the tiny stack
snprintf(Response,sizeof(Response),
"RTSP/1.0 200 OK\r\nCSeq: %s\r\n"
@ -241,9 +241,9 @@ void CRtspSession::Handle_RtspOPTION()
void CRtspSession::Handle_RtspDESCRIBE()
{
static char Response[1024]; // Note: we assume single threaded, this large buf we keep off of the tiny stack
static char SDPBuf[1024];
static char URLBuf[1024];
//static char Response[1024]; // Note: we assume single threaded, this large buf we keep off of the tiny stack
char SDPBuf[128]; //static char SDPBuf[1024];
char URLBuf[128]; //static char URLBuf[1024];
// check whether we know a stream with the URL which is requested
m_StreamID = -1; // invalid URL
@ -261,7 +261,7 @@ void CRtspSession::Handle_RtspDESCRIBE()
};
// simulate DESCRIBE server response
static char OBuf[256];
// static char OBuf[256];
char * ColonPtr;
strcpy(OBuf,m_URLHostPort);
ColonPtr = strstr(OBuf,":");
@ -305,8 +305,8 @@ void CRtspSession::Handle_RtspDESCRIBE()
void CRtspSession::Handle_RtspSETUP()
{
static char Response[1024];
static char Transport[255];
//static char Response[1024];
//static char Transport[255];
// init RTP streamer transport type (UDP or TCP) and ports for UDP transport
m_Streamer->InitTransport(m_ClientRTPPort,m_ClientRTCPPort,m_TcpTransport);
@ -336,7 +336,7 @@ void CRtspSession::Handle_RtspSETUP()
void CRtspSession::Handle_RtspPLAY()
{
static char Response[1024];
//static char Response[1024];
// simulate SETUP server response
snprintf(Response,sizeof(Response),
@ -354,10 +354,10 @@ void CRtspSession::Handle_RtspPLAY()
char const * CRtspSession::DateHeader()
{
static char buf[200];
//static char buf[200];
time_t tt = time(NULL);
strftime(buf, sizeof buf, "Date: %a, %b %d %Y %H:%M:%S GMT", gmtime(&tt));
return buf;
strftime(session_buf, sizeof(session_buf), "Date: %a, %b %d %Y %H:%M:%S GMT", gmtime(&tt));
return session_buf;
}
int CRtspSession::GetStreamID()
@ -375,7 +375,7 @@ bool CRtspSession::handleRequests(uint32_t readTimeoutMs)
if(m_stopped)
return false; // Already closed down
static char RecvBuf[RTSP_BUFFER_SIZE]; // Note: we assume single threaded, this large buf we keep off of the tiny stack
//char RecvBuf[RTSP_BUFFER_SIZE]; // Note: we assume single threaded, this large buf we keep off of the tiny stack
memset(RecvBuf,0x00,sizeof(RecvBuf));
int res = socketread(m_RtspClient,RecvBuf,sizeof(RecvBuf), readTimeoutMs);

View File

@ -70,4 +70,11 @@ private:
char m_CSeq[RTSP_PARAM_STRING_MAX]; // RTSP command sequence number
char m_URLHostPort[MAX_HOSTNAME_LEN]; // host:port part of the URL
unsigned m_ContentLength; // SDP string size
char CurRequest[RTSP_BUFFER_SIZE];
char RecvBuf[RTSP_BUFFER_SIZE];
char session_buf[128];
char CmdName[RTSP_PARAM_STRING_MAX];
char Transport[255];
char Response[1024];
char OBuf[256];
};

View File

@ -48,7 +48,7 @@ int CStreamer::SendRtpPacket(unsigned const char * jpeg, int jpegLen, int fragme
bool includeQuantTbl = quant0tbl && quant1tbl && fragmentOffset == 0;
uint8_t q = includeQuantTbl ? 128 : 0x5e;
static char RtpBuf[2048]; // Note: we assume single threaded, this large buf we keep off of the tiny stack
//static char RtpBuf[2048]; // Note: we assume single threaded, this large buf we keep off of the tiny stack
int RtpPacketSize = fragmentLen + KRtpHeaderSize + KJpegHeaderSize + (includeQuantTbl ? (4 + 64 * 2) : 0);
memset(RtpBuf,0x00,sizeof(RtpBuf));

View File

@ -39,6 +39,7 @@ private:
u_short m_width; // image data info
u_short m_height;
char RtpBuf[2048];
};

7
lib/libesp32/rtsp/LICENSE Executable file
View File

@ -0,0 +1,7 @@
Copyright 2018 S. Kevin Hester-Chow, kevinh@geeksville.com (MIT License)
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

93
lib/libesp32/rtsp/README.md Executable file
View File

@ -0,0 +1,93 @@
# Micro-RTSP
This is a small library which can be used to serve up RTSP streams from
resource constrained MCUs. It lets you trivially make a $10 open source
RTSP video stream camera.
# Usage
This library works for ESP32/arduino targets but also for most any posixish platform.
## Example arduino/ESP32 usage
This library will work standalone, but it is _super_ easy to use if your app is platform.io based.
Just "pio lib install Micro-RTSP" to pull the latest version from their library server. If you want to use the OV2640
camera support you'll need to be targeting the espressif32 platform in your project.
See the [example platform.io app](/examples). It should build and run on virtually any of the $10
ESP32-CAM boards (such as M5CAM). The relevant bit of the code is included below. In short:
1. Listen for a TCP connection on the RTSP port with accept()
2. When a connection comes in, create a CRtspSession and OV2640Streamer camera streamer objects.
3. While the connection remains, call session->handleRequests(0) to handle any incoming client requests.
4. Every 100ms or so call session->broadcastCurrentFrame() to send new frames to any clients.
```
void loop()
{
uint32_t msecPerFrame = 100;
static uint32_t lastimage = millis();
// If we have an active client connection, just service that until gone
// (FIXME - support multiple simultaneous clients)
if(session) {
session->handleRequests(0); // we don't use a timeout here,
// instead we send only if we have new enough frames
uint32_t now = millis();
if(now > lastimage + msecPerFrame || now < lastimage) { // handle clock rollover
session->broadcastCurrentFrame(now);
lastimage = now;
// check if we are overrunning our max frame rate
now = millis();
if(now > lastimage + msecPerFrame)
printf("warning exceeding max frame rate of %d ms\n", now - lastimage);
}
if(session->m_stopped) {
delete session;
delete streamer;
session = NULL;
streamer = NULL;
}
}
else {
client = rtspServer.accept();
if(client) {
//streamer = new SimStreamer(&client, true); // our streamer for UDP/TCP based RTP transport
streamer = new OV2640Streamer(&client, cam); // our streamer for UDP/TCP based RTP transport
session = new CRtspSession(&client, streamer); // our threads RTSP session and state
}
}
}
```
## Example posix/linux usage
There is a small standalone example [here](/test/RTSPTestServer.cpp). You can build it by following [these](/test/README.md) directions. The usage of the two key classes (CRtspSession and SimStreamer) are very similar to to the ESP32 usage.
## Supporting new camera devices
Supporting new camera devices is quite simple. See OV2640Streamer for an example and implement streamImage()
by reading a frame from your camera.
# Structure and design notes
# Issues and sending pull requests
Please report issues and send pull requests. I'll happily reply. ;-)
# Credits
The server code was initially based on a great 2013 [tutorial](https://www.medialan.de/usecase0001.html) by Medialan.
# License
Copyright 2018 S. Kevin Hester-Chow, kevinh@geeksville.com (MIT License)
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

View File

@ -0,0 +1,9 @@
name=Micro-RTSP
version=0.1.6
author=Kevin Hester
maintainer=Kevin Hester <kevinh@geeksville.com>
sentence=Mikro RTSP server for mikros
paragraph=A small/efficient RTSP server for ESP32 and other micros
category=Data Storage
url=https://github.com/geeksville/Micro-RTSP.git
architectures=*

View File

@ -3,26 +3,32 @@ import os
import shutil
import gzip
OUTPUT_DIR = "build_output{}".format(os.path.sep)
platform = env.PioPlatform()
board = env.BoardConfig()
mcu = board.get("build.mcu", "esp32")
# gzip only for ESP8266
if env["PIOPLATFORM"] != "espressif32":
def bin_gzip(source, target, env):
variant = str(target[0]).split(os.path.sep)[2]
# create string with location and file names based on variant
bin_file = "{}firmware{}{}.bin".format(OUTPUT_DIR, os.path.sep, variant)
gzip_file = "{}firmware{}{}.bin.gz".format(OUTPUT_DIR, os.path.sep, variant)
OUTPUT_DIR = "build_output{}".format(os.path.sep)
# check if new target files exist and remove if necessary
if os.path.isfile(gzip_file): os.remove(gzip_file)
def bin_gzip(source, target, env):
variant = str(target[0]).split(os.path.sep)[2]
# write gzip firmware file
with open(bin_file,"rb") as fp:
with gzip.open(gzip_file, "wb", compresslevel = 9) as f:
shutil.copyfileobj(fp, f)
ORG_FIRMWARE_SIZE = os.stat(bin_file).st_size
GZ_FIRMWARE_SIZE = os.stat(gzip_file).st_size
# create string with location and file names based on variant
bin_file = "{}firmware{}{}.bin".format(OUTPUT_DIR, os.path.sep, variant)
gzip_file = "{}firmware{}{}.bin.gz".format(OUTPUT_DIR, os.path.sep, variant)
print("Compression reduced firmware size by {:.0f}% (was {} bytes, now {} bytes)".format((GZ_FIRMWARE_SIZE / ORG_FIRMWARE_SIZE) * 100, ORG_FIRMWARE_SIZE, GZ_FIRMWARE_SIZE))
# check if new target files exist and remove if necessary
if os.path.isfile(gzip_file): os.remove(gzip_file)
env.AddPostAction("$BUILD_DIR/${PROGNAME}.bin", [bin_gzip])
# write gzip firmware file
with open(bin_file,"rb") as fp:
with gzip.open(gzip_file, "wb", compresslevel = 9) as f:
shutil.copyfileobj(fp, f)
ORG_FIRMWARE_SIZE = os.stat(bin_file).st_size
GZ_FIRMWARE_SIZE = os.stat(gzip_file).st_size
print("Compression reduced firmware size by {:.0f}% (was {} bytes, now {} bytes)".format((GZ_FIRMWARE_SIZE / ORG_FIRMWARE_SIZE) * 100, ORG_FIRMWARE_SIZE, GZ_FIRMWARE_SIZE))
env.AddPostAction("$BUILD_DIR/${PROGNAME}.bin", [bin_gzip])

View File

@ -37,6 +37,7 @@ default_envs =
; tasmota32-ircustom
; tasmota32solo1
; tasmota32-odroidgo
; tasmota32-core2
[common]
@ -165,7 +166,7 @@ lib_extra_dirs =
; *** EXPERIMENTAL Tasmota version for ESP32solo1 (used in some Xiaomi devices)
[env:tasmota32solo1]
extends = env:tasmota32
platform_packages = framework-arduinoespressif32 @ https://github.com/Jason2866/esp32-arduino-lib-builder/raw/framework-arduinoespressif32/framework-arduinoespressif32-release_v3.3-solo1-4b325f52e.tar.gz
platform_packages = framework-arduinoespressif32 @ https://github.com/Jason2866/esp32-arduino-lib-builder/raw/framework-arduinoespressif32/framework-arduinoespressif32-solo1-release_v3.3-7e63061fa.tar.gz
platformio/tool-mklittlefs @ ~1.203.200522
platformio/tool-esptoolpy @ ~1.30000.0
build_unflags = ${esp32_defaults.build_unflags}

View File

@ -9,6 +9,7 @@ default_envs = ${build_envs.default_envs}
; tasmota32
; tasmota32-webcam
; tasmota32-odroidgo
; tasmota32-core2
; tasmota32-minimal
; tasmota32-lite
; tasmota32-knx
@ -86,11 +87,13 @@ build_flags = ${esp_defaults.build_flags}
-Dsint16_t=int16_t
-Dmemcpy_P=memcpy
-Dmemcmp_P=memcmp
;for TLS we can afford compiling for 4K RSA keys
-DUSE_4K_RSA
[core32]
platform = espressif32 @ 2.1.0
platform_packages = framework-arduinoespressif32 @ https://github.com/Jason2866/arduino-esp32/releases/download/1.0.5-rc4/esp32-1.0.5-rc4.zip
platform = espressif32 @ 3.0.0
platform_packages = framework-arduinoespressif32 @ https://github.com/Jason2866/arduino-esp32/releases/download/1.0.5-rc6/esp32-1.0.5-rc6.zip
platformio/tool-mklittlefs @ ~1.203.200522
build_unflags = ${esp32_defaults.build_unflags}
build_flags = ${esp32_defaults.build_flags}

View File

@ -40,10 +40,24 @@ lib_extra_dirs = lib/libesp32, lib/lib_basic
extends = env:tasmota32
board = odroid_esp32
board_build.f_cpu = 240000000L
board_build.flash_mode = qio
board_build.f_flash = 80000000L
upload_speed = 2000000
board_build.partitions = esp32_partition_app1984k_spiffs12M.csv
build_flags = ${common32.build_flags} -DBOARD_HAS_PSRAM -mfix-esp32-psram-cache-issue -DFIRMWARE_ODROID_GO
lib_extra_dirs = lib/libesp32, lib/lib_basic, lib/lib_i2c, lib/lib_rf, lib/lib_div, lib/lib_ssl, lib/lib_display
[env:tasmota32-core2]
extends = env:tasmota32
board = odroid_esp32
board_build.f_cpu = 240000000L
board_build.flash_mode = qio
board_build.f_flash = 80000000L
upload_speed = 2000000
board_build.partitions = esp32_partition_app1984k_spiffs12M.csv
build_flags = ${common32.build_flags} -DBOARD_HAS_PSRAM -mfix-esp32-psram-cache-issue -DFIRMWARE_M5STACK_CORE2
lib_extra_dirs = lib/libesp32, lib/lib_basic, lib/lib_i2c, lib/lib_rf, lib/lib_div, lib/lib_ssl, lib/lib_display, lib/lib_audio
[env:tasmota32-minimal]
extends = env:tasmota32
build_flags = ${common32.build_flags} -DFIRMWARE_MINIMAL

View File

@ -804,6 +804,7 @@ extern "C" {
return 0;
}
#ifndef USE_MQTT_TLS_DROP_OLD_FINGERPRINT
// No match under new algorithm, do some basic checking on the key.
//
// RSA keys normally have an e value of 65537, which is three bytes long.
@ -838,6 +839,9 @@ extern "C" {
pubkeyfingerprint_pubkey_fingerprint(xc, false);
return 0;
#else // USE_TLS_OLD_FINGERPRINT_COMPAT
return 1; // no match, error
#endif // USE_TLS_OLD_FINGERPRINT_COMPAT
} else {
// Default (no validation at all) or no errors in prior checks = success.
return 0;

View File

@ -156,6 +156,7 @@
#define D_JSON_SERIALRECEIVED "SerialReceived"
#define D_JSON_SET "Set"
#define D_JSON_SIGNAL "Signal"
#define D_JSON_SIZE "Size"
#define D_JSON_SPEED "Speed"
#define D_JSON_SPEED_UNIT "SpeedUnit"
#define D_JSON_SSID "SSId"
@ -180,6 +181,7 @@
#define D_JSON_TOTAL_START_TIME "TotalStartTime"
#define D_JSON_TVOC "TVOC"
#define D_JSON_TYPE "Type"
#define D_JSON_UID "UID"
#define D_JSON_UPTIME "Uptime"
#define D_JSON_UTC_TIME "UTC"
#define D_JSON_UV_INDEX "UvIndex"
@ -347,7 +349,11 @@
#define D_CMND_CPU_FREQUENCY "CpuFrequency"
#endif // ESP32
// Commands xdrv_01_mqtt.ino
// Commands xdrv_02_mqtt.ino
#define D_SO_MQTTJSONONLY "MqttJSONOnly"
#define D_SO_MQTTTLS "MqttTLS"
#define D_SO_MQTTNORETAIN "MqttNoRetain"
#define D_SO_MQTTDETACHRELAY "MqttDetachRelay"
#define D_CMND_MQTTLOG "MqttLog"
#define D_CMND_MQTTHOST "MqttHost"
#define D_CMND_MQTTPORT "MqttPort"
@ -373,7 +379,7 @@
#define D_CMND_SENSORRETAIN "SensorRetain"
#define D_CMND_PUBLISH "Publish"
// Commands xdrv_02_webserver.ino
// Commands xdrv_01_webserver.ino
#define D_CMND_WEBSERVER "Webserver"
#define D_JSON_WEBSERVER_MODE "WebServerMode"
#define D_JSON_ACTIVE_FOR "Active for"
@ -418,6 +424,13 @@
#define D_JSON_MAXENERGYREACHED "MaxEnergyReached"
// Commands xdrv_04_light.ino
#define D_SO_CHANNELREMAP "ChannelRemap" // SO37
#define D_SO_MULTIPWM "MultiPWM" // SO68
#define D_SO_ALEXACTRANGE "AlexaCTRange" // SO82
#define D_SO_POWERONFADE "PowerOnFade" // SO91
#define D_SO_PWMCT "PWMCT" // SO92
#define D_SO_WHITEBLEND "WhiteBlend" // SO105
#define D_SO_VIRTUALCT "VirtualCT" // SO106
#define D_CMND_CHANNEL "Channel"
#define D_CMND_COLOR "Color"
#define D_CMND_COLORTEMPERATURE "CT"
@ -520,7 +533,16 @@
// Commands xdrv_23_zigbee.ino
#define D_PRFX_ZB "Zb"
#define D_ZIGBEE_NOT_STARTED "Zigbee not started"
#define D_SO_ZIGBEE_NAMEKEY "NameKey"
#define D_SO_ZIGBEE_DEVICETOPIC "DeviceTopic"
#define D_SO_ZIGBEE_NOPREFIX "NoPrefix"
#define D_SO_ZIGBEE_ENDPOINTSUFFIX "EndpointSuffix"
#define D_SO_ZIGBEE_NOAUTOBIND "NoAutoBind"
#define D_SO_ZIGBEE_NAMETOPIC "NameTopic"
#define D_SO_ZIGBEE_ENDPOINTTOPIC "EndpointTopic"
#define D_SO_ZIGBEE_NOAUTOQUERY "NoAutoQuery"
#define D_SO_ZIGBEE_ZBRECEIVEDTOPIC "ReceivedTopic"
#define D_SO_ZIGBEE_OMITDEVICE "OmitDevice"
#define D_CMND_ZIGBEE_PERMITJOIN "PermitJoin"
#define D_CMND_ZIGBEE_STATUS "Status"
#define D_CMND_ZIGBEE_RESET "Reset"
@ -727,7 +749,6 @@ const char S_JSON_COMMAND_INDEX_NVALUE[] PROGMEM = "{\"%s%d\":%d}";
const char S_JSON_COMMAND_INDEX_LVALUE[] PROGMEM = "{\"%s%d\":%lu}";
const char S_JSON_COMMAND_INDEX_SVALUE[] PROGMEM = "{\"%s%d\":\"%s\"}";
const char S_JSON_COMMAND_INDEX_ASTERISK[] PROGMEM = "{\"%s%d\":\"" D_ASTERISK_PWD "\"}";
const char S_JSON_COMMAND_INDEX_SVALUE_SVALUE[] PROGMEM = "{\"%s%d\":\"%s%s\"}";
const char S_JSON_SENSOR_INDEX_NVALUE[] PROGMEM = "{\"" D_CMND_SENSOR "%d\":%d}";
const char S_JSON_SENSOR_INDEX_SVALUE[] PROGMEM = "{\"" D_CMND_SENSOR "%d\":\"%s\"}";
@ -737,6 +758,7 @@ const char S_JSON_DRIVER_INDEX_SVALUE[] PROGMEM = "{\"" D_CMND_DRIVE
const char S_JSON_SVALUE_ACTION_SVALUE[] PROGMEM = "{\"%s\":{\"Action\":\"%s\"}}";
const char JSON_SNS_F_TEMP[] PROGMEM = ",\"%s\":{\"" D_JSON_TEMPERATURE "\":%*_f}";
const char JSON_SNS_TEMP[] PROGMEM = ",\"%s\":{\"" D_JSON_TEMPERATURE "\":%s}";
const char JSON_SNS_ILLUMINANCE[] PROGMEM = ",\"%s\":{\"" D_JSON_ILLUMINANCE "\":%d}";
@ -769,7 +791,9 @@ const float kSpeedConversionFactor[] = {1, // none
// xdrv_02_webserver.ino
#ifdef USE_WEBSERVER
// {s} = <tr><th>, {m} = </th><td>, {e} = </td></tr>
const char HTTP_SNS_TEMP[] PROGMEM = "{s}%s " D_TEMPERATURE "{m}%s " D_UNIT_DEGREE "%c{e}";
const char HTTP_SNS_F_TEMP[] PROGMEM = "{s}%s " D_TEMPERATURE "{m}%*_f " D_UNIT_DEGREE "%c{e}";
//const char HTTP_SNS_TEMP[] PROGMEM = "{s}%s " D_TEMPERATURE "{m}%s " D_UNIT_DEGREE "%c{e}";
const char HTTP_SNS_HUM[] PROGMEM = "{s}%s " D_HUMIDITY "{m}%s " D_UNIT_PERCENT "{e}";
const char HTTP_SNS_DEW[] PROGMEM = "{s}%s " D_DEWPOINT "{m}%s " D_UNIT_DEGREE "%c{e}";
const char HTTP_SNS_PRESSURE[] PROGMEM = "{s}%s " D_PRESSURE "{m}%s " "%s{e}";
@ -782,6 +806,7 @@ const char HTTP_SNS_GALLONS[] PROGMEM = "{s}%s " D_TOTAL_USAGE "{
const char HTTP_SNS_GPM[] PROGMEM = "{s}%s " D_FLOW_RATE "{m}%s " D_UNIT_GALLONS_PER_MIN "{e}";
const char HTTP_SNS_MOISTURE[] PROGMEM = "{s}%s " D_MOISTURE "{m}%d " D_UNIT_PERCENT "{e}";
const char HTTP_SNS_RANGE[] PROGMEM = "{s}%s " D_RANGE "{m}%d" "{e}";
const char HTTP_SNS_DISTANCE[] PROGMEM = "{s}%s " D_DISTANCE "{m}%d " D_UNIT_MILLIMETER "{e}";
const char HTTP_SNS_VOLTAGE[] PROGMEM = "{s}" D_VOLTAGE "{m}%s " D_UNIT_VOLT "{e}";
const char HTTP_SNS_CURRENT[] PROGMEM = "{s}" D_CURRENT "{m}%s " D_UNIT_AMPERE "{e}";
const char HTTP_SNS_POWER[] PROGMEM = "{s}" D_POWERUSAGE "{m}%s " D_UNIT_WATT "{e}";

View File

@ -782,6 +782,11 @@
#define D_SENSOR_SSD1331_CS "SSD1331 CS"
#define D_SENSOR_SSD1331_DC "SSD1331 DC"
#define D_SENSOR_SDCARD_CS "SDCard CS"
#define D_SENSOR_WIEGAND_D0 "Wiegand D0"
#define D_SENSOR_WIEGAND_D1 "Wiegand D1"
#define D_SENSOR_NEOPOOL_TX "NeoPool Tx"
#define D_SENSOR_NEOPOOL_RX "NeoPool Rx"
// Units
#define D_UNIT_AMPERE "A"
@ -953,4 +958,56 @@
#define D_FP_PASSVERIFY "Wagwoord geverifieer" // 0x21 Verify the fingerprint passed
#define D_FP_UNKNOWNERROR "Fout" // Any other error
// xsns_83_neopool.ino
#define D_NEOPOOL_MACH_NONE "NeoPool" // Machine names
#define D_NEOPOOL_MACH_HIDROLIFE "Hidrolife (yellow)"
#define D_NEOPOOL_MACH_AQUASCENIC "Aquascenic (blue)"
#define D_NEOPOOL_MACH_OXILIFE "Oxilife (green)"
#define D_NEOPOOL_MACH_BIONET "Bionet (light blue)"
#define D_NEOPOOL_MACH_HIDRONISER "Hidroniser (red)"
#define D_NEOPOOL_MACH_UVSCENIC "UVScenic (lilac)"
#define D_NEOPOOL_MACH_STATION "Station (orange)"
#define D_NEOPOOL_MACH_BRILIX "Brilix"
#define D_NEOPOOL_MACH_GENERIC "Generic"
#define D_NEOPOOL_MACH_BAYROL "Bayrol"
#define D_NEOPOOL_MACH_HAY "Hay"
#define D_NEOPOOL_FILTRATION_MANUAL "Manual" // Filtration modes
#define D_NEOPOOL_FILTRATION_AUTO "Auto"
#define D_NEOPOOL_FILTRATION_HEATING "Heating"
#define D_NEOPOOL_FILTRATION_SMART "Smart"
#define D_NEOPOOL_FILTRATION_INTELLIGENT "Intelligent"
#define D_NEOPOOL_FILTRATION_BACKWASH "Backwash"
#define D_NEOPOOL_FILTRATION_NONE "" // Filtration speed level
#define D_NEOPOOL_FILTRATION_SLOW "slow"
#define D_NEOPOOL_FILTRATION_MEDIUM "medium"
#define D_NEOPOOL_FILTRATION_FAST "fast"
#define D_NEOPOOL_TYPE "Type" // Sensor & relais names
#define D_NEOPOOL_REDOX "Redox"
#define D_NEOPOOL_CHLORINE "Chlorine"
#define D_NEOPOOL_CONDUCTIVITY "Conductivity"
#define D_NEOPOOL_IONIZATION "Ionization"
#define D_NEOPOOL_HYDROLYSIS "Hydrolysis"
#define D_NEOPOOL_RELAY "Relay"
#define D_NEOPOOL_RELAY_FILTRATION "Filtration"
#define D_NEOPOOL_RELAY_LIGHT "Light"
#define D_NEOPOOL_RELAY_PH_ACID "Acid pump"
#define D_NEOPOOL_RELAY_PH_BASE "Base pump"
#define D_NEOPOOL_RELAY_RX "Redox level"
#define D_NEOPOOL_RELAY_CL "Chlorine pump"
#define D_NEOPOOL_RELAY_CD "Brine pump"
#define D_NEOPOOL_TIME "Time"
#define D_NEOPOOL_FILT_MODE "Filtration"
#define D_NEOPOOL_POLARIZATION "Pol" // Sensor status
#define D_NEOPOOL_PR_OFF "PrOff"
#define D_NEOPOOL_SETPOINT_OK "Ok"
#define D_NEOPOOL_COVER "Cover"
#define D_NEOPOOL_SHOCK "Shock"
#define D_NEOPOOL_ALARM "! "
#define D_NEOPOOL_LOW "Low"
#define D_NEOPOOL_FLOW1 "FL1"
#define D_NEOPOOL_FLOW2 "FL2"
#define D_NEOPOOL_PH_HIGH "too high" // ph Alarms
#define D_NEOPOOL_PH_LOW "too low"
#define D_NEOPOOL_PUMP_TIME_EXCEEDED "pump time exceeded"
#endif // _LANGUAGE_AF_AF_H_

View File

@ -781,6 +781,11 @@
#define D_SENSOR_SSD1331_CS "SSD1331 CS"
#define D_SENSOR_SSD1331_DC "SSD1331 DC"
#define D_SENSOR_SDCARD_CS "SDCard CS"
#define D_SENSOR_WIEGAND_D0 "Wiegand D0"
#define D_SENSOR_WIEGAND_D1 "Wiegand D1"
#define D_SENSOR_NEOPOOL_TX "NeoPool Tx"
#define D_SENSOR_NEOPOOL_RX "NeoPool Rx"
// Units
#define D_UNIT_AMPERE "A"
@ -952,4 +957,56 @@
#define D_FP_PASSVERIFY "Password verified" // 0x21 Verify the fingerprint passed
#define D_FP_UNKNOWNERROR "Error" // Any other error
// xsns_83_neopool.ino
#define D_NEOPOOL_MACH_NONE "NeoPool" // Machine names
#define D_NEOPOOL_MACH_HIDROLIFE "Hidrolife (yellow)"
#define D_NEOPOOL_MACH_AQUASCENIC "Aquascenic (blue)"
#define D_NEOPOOL_MACH_OXILIFE "Oxilife (green)"
#define D_NEOPOOL_MACH_BIONET "Bionet (light blue)"
#define D_NEOPOOL_MACH_HIDRONISER "Hidroniser (red)"
#define D_NEOPOOL_MACH_UVSCENIC "UVScenic (lilac)"
#define D_NEOPOOL_MACH_STATION "Station (orange)"
#define D_NEOPOOL_MACH_BRILIX "Brilix"
#define D_NEOPOOL_MACH_GENERIC "Generic"
#define D_NEOPOOL_MACH_BAYROL "Bayrol"
#define D_NEOPOOL_MACH_HAY "Hay"
#define D_NEOPOOL_FILTRATION_MANUAL "Manual" // Filtration modes
#define D_NEOPOOL_FILTRATION_AUTO "Auto"
#define D_NEOPOOL_FILTRATION_HEATING "Heating"
#define D_NEOPOOL_FILTRATION_SMART "Smart"
#define D_NEOPOOL_FILTRATION_INTELLIGENT "Intelligent"
#define D_NEOPOOL_FILTRATION_BACKWASH "Backwash"
#define D_NEOPOOL_FILTRATION_NONE "" // Filtration speed level
#define D_NEOPOOL_FILTRATION_SLOW "slow"
#define D_NEOPOOL_FILTRATION_MEDIUM "medium"
#define D_NEOPOOL_FILTRATION_FAST "fast"
#define D_NEOPOOL_TYPE "Type" // Sensor & relais names
#define D_NEOPOOL_REDOX "Redox"
#define D_NEOPOOL_CHLORINE "Chlorine"
#define D_NEOPOOL_CONDUCTIVITY "Conductivity"
#define D_NEOPOOL_IONIZATION "Ionization"
#define D_NEOPOOL_HYDROLYSIS "Hydrolysis"
#define D_NEOPOOL_RELAY "Relay"
#define D_NEOPOOL_RELAY_FILTRATION "Filtration"
#define D_NEOPOOL_RELAY_LIGHT "Light"
#define D_NEOPOOL_RELAY_PH_ACID "Acid pump"
#define D_NEOPOOL_RELAY_PH_BASE "Base pump"
#define D_NEOPOOL_RELAY_RX "Redox level"
#define D_NEOPOOL_RELAY_CL "Chlorine pump"
#define D_NEOPOOL_RELAY_CD "Brine pump"
#define D_NEOPOOL_TIME "Time"
#define D_NEOPOOL_FILT_MODE "Filtration"
#define D_NEOPOOL_POLARIZATION "Pol" // Sensor status
#define D_NEOPOOL_PR_OFF "PrOff"
#define D_NEOPOOL_SETPOINT_OK "Ok"
#define D_NEOPOOL_COVER "Cover"
#define D_NEOPOOL_SHOCK "Shock"
#define D_NEOPOOL_ALARM "! "
#define D_NEOPOOL_LOW "Low"
#define D_NEOPOOL_FLOW1 "FL1"
#define D_NEOPOOL_FLOW2 "FL2"
#define D_NEOPOOL_PH_HIGH "too high" // ph Alarms
#define D_NEOPOOL_PH_LOW "too low"
#define D_NEOPOOL_PUMP_TIME_EXCEEDED "pump time exceeded"
#endif // _LANGUAGE_BG_BG_H_

View File

@ -782,6 +782,11 @@
#define D_SENSOR_SSD1331_CS "SSD1331 CS"
#define D_SENSOR_SSD1331_DC "SSD1331 DC"
#define D_SENSOR_SDCARD_CS "SDCard CS"
#define D_SENSOR_WIEGAND_D0 "Wiegand D0"
#define D_SENSOR_WIEGAND_D1 "Wiegand D1"
#define D_SENSOR_NEOPOOL_TX "NeoPool Tx"
#define D_SENSOR_NEOPOOL_RX "NeoPool Rx"
// Units
#define D_UNIT_AMPERE "A"
@ -953,4 +958,56 @@
#define D_FP_PASSVERIFY "Password verified" // 0x21 Verify the fingerprint passed
#define D_FP_UNKNOWNERROR "Error" // Any other error
// xsns_83_neopool.ino
#define D_NEOPOOL_MACH_NONE "NeoPool" // Machine names
#define D_NEOPOOL_MACH_HIDROLIFE "Hidrolife (yellow)"
#define D_NEOPOOL_MACH_AQUASCENIC "Aquascenic (blue)"
#define D_NEOPOOL_MACH_OXILIFE "Oxilife (green)"
#define D_NEOPOOL_MACH_BIONET "Bionet (light blue)"
#define D_NEOPOOL_MACH_HIDRONISER "Hidroniser (red)"
#define D_NEOPOOL_MACH_UVSCENIC "UVScenic (lilac)"
#define D_NEOPOOL_MACH_STATION "Station (orange)"
#define D_NEOPOOL_MACH_BRILIX "Brilix"
#define D_NEOPOOL_MACH_GENERIC "Generic"
#define D_NEOPOOL_MACH_BAYROL "Bayrol"
#define D_NEOPOOL_MACH_HAY "Hay"
#define D_NEOPOOL_FILTRATION_MANUAL "Manual" // Filtration modes
#define D_NEOPOOL_FILTRATION_AUTO "Auto"
#define D_NEOPOOL_FILTRATION_HEATING "Heating"
#define D_NEOPOOL_FILTRATION_SMART "Smart"
#define D_NEOPOOL_FILTRATION_INTELLIGENT "Intelligent"
#define D_NEOPOOL_FILTRATION_BACKWASH "Backwash"
#define D_NEOPOOL_FILTRATION_NONE "" // Filtration speed level
#define D_NEOPOOL_FILTRATION_SLOW "slow"
#define D_NEOPOOL_FILTRATION_MEDIUM "medium"
#define D_NEOPOOL_FILTRATION_FAST "fast"
#define D_NEOPOOL_TYPE "Type" // Sensor & relais names
#define D_NEOPOOL_REDOX "Redox"
#define D_NEOPOOL_CHLORINE "Chlorine"
#define D_NEOPOOL_CONDUCTIVITY "Conductivity"
#define D_NEOPOOL_IONIZATION "Ionization"
#define D_NEOPOOL_HYDROLYSIS "Hydrolysis"
#define D_NEOPOOL_RELAY "Relay"
#define D_NEOPOOL_RELAY_FILTRATION "Filtration"
#define D_NEOPOOL_RELAY_LIGHT "Light"
#define D_NEOPOOL_RELAY_PH_ACID "Acid pump"
#define D_NEOPOOL_RELAY_PH_BASE "Base pump"
#define D_NEOPOOL_RELAY_RX "Redox level"
#define D_NEOPOOL_RELAY_CL "Chlorine pump"
#define D_NEOPOOL_RELAY_CD "Brine pump"
#define D_NEOPOOL_TIME "Time"
#define D_NEOPOOL_FILT_MODE "Filtration"
#define D_NEOPOOL_POLARIZATION "Pol" // Sensor status
#define D_NEOPOOL_PR_OFF "PrOff"
#define D_NEOPOOL_SETPOINT_OK "Ok"
#define D_NEOPOOL_COVER "Cover"
#define D_NEOPOOL_SHOCK "Shock"
#define D_NEOPOOL_ALARM "! "
#define D_NEOPOOL_LOW "Low"
#define D_NEOPOOL_FLOW1 "FL1"
#define D_NEOPOOL_FLOW2 "FL2"
#define D_NEOPOOL_PH_HIGH "too high" // ph Alarms
#define D_NEOPOOL_PH_LOW "too low"
#define D_NEOPOOL_PUMP_TIME_EXCEEDED "pump time exceeded"
#endif // _LANGUAGE_CS_CZ_H_

View File

@ -782,6 +782,11 @@
#define D_SENSOR_SSD1331_CS "SSD1331 CS"
#define D_SENSOR_SSD1331_DC "SSD1331 DC"
#define D_SENSOR_SDCARD_CS "SDCard CS"
#define D_SENSOR_WIEGAND_D0 "Wiegand D0"
#define D_SENSOR_WIEGAND_D1 "Wiegand D1"
#define D_SENSOR_NEOPOOL_TX "NeoPool Tx"
#define D_SENSOR_NEOPOOL_RX "NeoPool Rx"
// Units
#define D_UNIT_AMPERE "A"
@ -953,4 +958,56 @@
#define D_FP_PASSVERIFY "Übereinstimmung" // 0x21 Verify the fingerprint passed
#define D_FP_UNKNOWNERROR "Fehler" // Any other error
// xsns_83_neopool.ino
#define D_NEOPOOL_MACH_NONE "NeoPool" // Machine names
#define D_NEOPOOL_MACH_HIDROLIFE "Hidrolife (Gelb)"
#define D_NEOPOOL_MACH_AQUASCENIC "Aquascenic (Blau)"
#define D_NEOPOOL_MACH_OXILIFE "Oxilife (Grün)"
#define D_NEOPOOL_MACH_BIONET "Bionet (Hellblau)"
#define D_NEOPOOL_MACH_HIDRONISER "Hidroniser (Rot)"
#define D_NEOPOOL_MACH_UVSCENIC "UVScenic (Lila)"
#define D_NEOPOOL_MACH_STATION "Station (Orange)"
#define D_NEOPOOL_MACH_BRILIX "Brilix"
#define D_NEOPOOL_MACH_GENERIC "Generic"
#define D_NEOPOOL_MACH_BAYROL "Bayrol"
#define D_NEOPOOL_MACH_HAY "Hay"
#define D_NEOPOOL_FILTRATION_MANUAL "Manuell" // Filtration modes
#define D_NEOPOOL_FILTRATION_AUTO "Auto"
#define D_NEOPOOL_FILTRATION_HEATING "Heizung"
#define D_NEOPOOL_FILTRATION_SMART "Smart"
#define D_NEOPOOL_FILTRATION_INTELLIGENT "Intelligent"
#define D_NEOPOOL_FILTRATION_BACKWASH "Rückspülung"
#define D_NEOPOOL_FILTRATION_NONE "" // Filtration speed level
#define D_NEOPOOL_FILTRATION_SLOW "Langsam"
#define D_NEOPOOL_FILTRATION_MEDIUM "Mittel"
#define D_NEOPOOL_FILTRATION_FAST "Schnell"
#define D_NEOPOOL_TYPE "Typ" // Sensor & relais names
#define D_NEOPOOL_REDOX "Redox"
#define D_NEOPOOL_CHLORINE "Chlor"
#define D_NEOPOOL_CONDUCTIVITY "Konduktivität"
#define D_NEOPOOL_IONIZATION "Ionisierung"
#define D_NEOPOOL_HYDROLYSIS "Hydrolyse"
#define D_NEOPOOL_RELAY "Relais"
#define D_NEOPOOL_RELAY_FILTRATION "Filtration"
#define D_NEOPOOL_RELAY_LIGHT "Licht"
#define D_NEOPOOL_RELAY_PH_ACID "Säurepumpe"
#define D_NEOPOOL_RELAY_PH_BASE "Laugenpumpe"
#define D_NEOPOOL_RELAY_RX "Redox Pegel"
#define D_NEOPOOL_RELAY_CL "Chlorpumpe"
#define D_NEOPOOL_RELAY_CD "Salzwasserpumpe"
#define D_NEOPOOL_TIME "Zeit"
#define D_NEOPOOL_FILT_MODE "Filtration"
#define D_NEOPOOL_POLARIZATION "Pol" // Sensor status
#define D_NEOPOOL_PR_OFF "PrAus"
#define D_NEOPOOL_SETPOINT_OK "Ok"
#define D_NEOPOOL_COVER "Abdeckung"
#define D_NEOPOOL_SHOCK "Shock"
#define D_NEOPOOL_ALARM "! "
#define D_NEOPOOL_LOW "Niedrig"
#define D_NEOPOOL_FLOW1 "FL1"
#define D_NEOPOOL_FLOW2 "FL2"
#define D_NEOPOOL_PH_HIGH "zu hoch" // ph Alarms
#define D_NEOPOOL_PH_LOW "zu niedrig"
#define D_NEOPOOL_PUMP_TIME_EXCEEDED "Pumpzeit überschritten"
#endif // _LANGUAGE_DE_DE_H_

View File

@ -782,6 +782,11 @@
#define D_SENSOR_SSD1331_CS "SSD1331 CS"
#define D_SENSOR_SSD1331_DC "SSD1331 DC"
#define D_SENSOR_SDCARD_CS "SDCard CS"
#define D_SENSOR_WIEGAND_D0 "Wiegand D0"
#define D_SENSOR_WIEGAND_D1 "Wiegand D1"
#define D_SENSOR_NEOPOOL_TX "NeoPool Tx"
#define D_SENSOR_NEOPOOL_RX "NeoPool Rx"
// Units
#define D_UNIT_AMPERE "A"
@ -953,4 +958,56 @@
#define D_FP_PASSVERIFY "Password verified" // 0x21 Verify the fingerprint passed
#define D_FP_UNKNOWNERROR "Error" // Any other error
// xsns_83_neopool.ino
#define D_NEOPOOL_MACH_NONE "NeoPool" // Machine names
#define D_NEOPOOL_MACH_HIDROLIFE "Hidrolife (yellow)"
#define D_NEOPOOL_MACH_AQUASCENIC "Aquascenic (blue)"
#define D_NEOPOOL_MACH_OXILIFE "Oxilife (green)"
#define D_NEOPOOL_MACH_BIONET "Bionet (light blue)"
#define D_NEOPOOL_MACH_HIDRONISER "Hidroniser (red)"
#define D_NEOPOOL_MACH_UVSCENIC "UVScenic (lilac)"
#define D_NEOPOOL_MACH_STATION "Station (orange)"
#define D_NEOPOOL_MACH_BRILIX "Brilix"
#define D_NEOPOOL_MACH_GENERIC "Generic"
#define D_NEOPOOL_MACH_BAYROL "Bayrol"
#define D_NEOPOOL_MACH_HAY "Hay"
#define D_NEOPOOL_FILTRATION_MANUAL "Manual" // Filtration modes
#define D_NEOPOOL_FILTRATION_AUTO "Auto"
#define D_NEOPOOL_FILTRATION_HEATING "Heating"
#define D_NEOPOOL_FILTRATION_SMART "Smart"
#define D_NEOPOOL_FILTRATION_INTELLIGENT "Intelligent"
#define D_NEOPOOL_FILTRATION_BACKWASH "Backwash"
#define D_NEOPOOL_FILTRATION_NONE "" // Filtration speed level
#define D_NEOPOOL_FILTRATION_SLOW "slow"
#define D_NEOPOOL_FILTRATION_MEDIUM "medium"
#define D_NEOPOOL_FILTRATION_FAST "fast"
#define D_NEOPOOL_TYPE "Type" // Sensor & relais names
#define D_NEOPOOL_REDOX "Redox"
#define D_NEOPOOL_CHLORINE "Chlorine"
#define D_NEOPOOL_CONDUCTIVITY "Conductivity"
#define D_NEOPOOL_IONIZATION "Ionization"
#define D_NEOPOOL_HYDROLYSIS "Hydrolysis"
#define D_NEOPOOL_RELAY "Relay"
#define D_NEOPOOL_RELAY_FILTRATION "Filtration"
#define D_NEOPOOL_RELAY_LIGHT "Light"
#define D_NEOPOOL_RELAY_PH_ACID "Acid pump"
#define D_NEOPOOL_RELAY_PH_BASE "Base pump"
#define D_NEOPOOL_RELAY_RX "Redox level"
#define D_NEOPOOL_RELAY_CL "Chlorine pump"
#define D_NEOPOOL_RELAY_CD "Brine pump"
#define D_NEOPOOL_TIME "Time"
#define D_NEOPOOL_FILT_MODE "Filtration"
#define D_NEOPOOL_POLARIZATION "Pol" // Sensor status
#define D_NEOPOOL_PR_OFF "PrOff"
#define D_NEOPOOL_SETPOINT_OK "Ok"
#define D_NEOPOOL_COVER "Cover"
#define D_NEOPOOL_SHOCK "Shock"
#define D_NEOPOOL_ALARM "! "
#define D_NEOPOOL_LOW "Low"
#define D_NEOPOOL_FLOW1 "FL1"
#define D_NEOPOOL_FLOW2 "FL2"
#define D_NEOPOOL_PH_HIGH "too high" // ph Alarms
#define D_NEOPOOL_PH_LOW "too low"
#define D_NEOPOOL_PUMP_TIME_EXCEEDED "pump time exceeded"
#endif // _LANGUAGE_EL_GR_H_

View File

@ -782,6 +782,11 @@
#define D_SENSOR_SSD1331_CS "SSD1331 CS"
#define D_SENSOR_SSD1331_DC "SSD1331 DC"
#define D_SENSOR_SDCARD_CS "SDCard CS"
#define D_SENSOR_WIEGAND_D0 "Wiegand D0"
#define D_SENSOR_WIEGAND_D1 "Wiegand D1"
#define D_SENSOR_NEOPOOL_TX "NeoPool Tx"
#define D_SENSOR_NEOPOOL_RX "NeoPool Rx"
// Units
#define D_UNIT_AMPERE "A"
@ -953,4 +958,56 @@
#define D_FP_PASSVERIFY "Password verified" // 0x21 Verify the fingerprint passed
#define D_FP_UNKNOWNERROR "Error" // Any other error
// xsns_83_neopool.ino
#define D_NEOPOOL_MACH_NONE "NeoPool" // Machine names
#define D_NEOPOOL_MACH_HIDROLIFE "Hidrolife (yellow)"
#define D_NEOPOOL_MACH_AQUASCENIC "Aquascenic (blue)"
#define D_NEOPOOL_MACH_OXILIFE "Oxilife (green)"
#define D_NEOPOOL_MACH_BIONET "Bionet (light blue)"
#define D_NEOPOOL_MACH_HIDRONISER "Hidroniser (red)"
#define D_NEOPOOL_MACH_UVSCENIC "UVScenic (lilac)"
#define D_NEOPOOL_MACH_STATION "Station (orange)"
#define D_NEOPOOL_MACH_BRILIX "Brilix"
#define D_NEOPOOL_MACH_GENERIC "Generic"
#define D_NEOPOOL_MACH_BAYROL "Bayrol"
#define D_NEOPOOL_MACH_HAY "Hay"
#define D_NEOPOOL_FILTRATION_MANUAL "Manual" // Filtration modes
#define D_NEOPOOL_FILTRATION_AUTO "Auto"
#define D_NEOPOOL_FILTRATION_HEATING "Heating"
#define D_NEOPOOL_FILTRATION_SMART "Smart"
#define D_NEOPOOL_FILTRATION_INTELLIGENT "Intelligent"
#define D_NEOPOOL_FILTRATION_BACKWASH "Backwash"
#define D_NEOPOOL_FILTRATION_NONE "" // Filtration speed level
#define D_NEOPOOL_FILTRATION_SLOW "slow"
#define D_NEOPOOL_FILTRATION_MEDIUM "medium"
#define D_NEOPOOL_FILTRATION_FAST "fast"
#define D_NEOPOOL_TYPE "Type" // Sensor & relais names
#define D_NEOPOOL_REDOX "Redox"
#define D_NEOPOOL_CHLORINE "Chlorine"
#define D_NEOPOOL_CONDUCTIVITY "Conductivity"
#define D_NEOPOOL_IONIZATION "Ionization"
#define D_NEOPOOL_HYDROLYSIS "Hydrolysis"
#define D_NEOPOOL_RELAY "Relay"
#define D_NEOPOOL_RELAY_FILTRATION "Filtration"
#define D_NEOPOOL_RELAY_LIGHT "Light"
#define D_NEOPOOL_RELAY_PH_ACID "Acid pump"
#define D_NEOPOOL_RELAY_PH_BASE "Base pump"
#define D_NEOPOOL_RELAY_RX "Redox level"
#define D_NEOPOOL_RELAY_CL "Chlorine pump"
#define D_NEOPOOL_RELAY_CD "Brine pump"
#define D_NEOPOOL_TIME "Time"
#define D_NEOPOOL_FILT_MODE "Filtration"
#define D_NEOPOOL_POLARIZATION "Pol" // Sensor status
#define D_NEOPOOL_PR_OFF "PrOff"
#define D_NEOPOOL_SETPOINT_OK "Ok"
#define D_NEOPOOL_COVER "Cover"
#define D_NEOPOOL_SHOCK "Shock"
#define D_NEOPOOL_ALARM "! "
#define D_NEOPOOL_LOW "Low"
#define D_NEOPOOL_FLOW1 "FL1"
#define D_NEOPOOL_FLOW2 "FL2"
#define D_NEOPOOL_PH_HIGH "too high" // ph Alarms
#define D_NEOPOOL_PH_LOW "too low"
#define D_NEOPOOL_PUMP_TIME_EXCEEDED "pump time exceeded"
#endif // _LANGUAGE_EN_GB_H_

View File

@ -782,6 +782,11 @@
#define D_SENSOR_SSD1331_CS "SSD1331 CS"
#define D_SENSOR_SSD1331_DC "SSD1331 DC"
#define D_SENSOR_SDCARD_CS "SDCard CS"
#define D_SENSOR_WIEGAND_D0 "Wiegand D0"
#define D_SENSOR_WIEGAND_D1 "Wiegand D1"
#define D_SENSOR_NEOPOOL_TX "NeoPool Tx"
#define D_SENSOR_NEOPOOL_RX "NeoPool Rx"
// Units
#define D_UNIT_AMPERE "A"
@ -953,4 +958,56 @@
#define D_FP_PASSVERIFY "Clave Correcta" // 0x21 Verify the fingerprint passed
#define D_FP_UNKNOWNERROR "Error" // Any other error
// xsns_83_neopool.ino
#define D_NEOPOOL_MACH_NONE "NeoPool" // Machine names
#define D_NEOPOOL_MACH_HIDROLIFE "Hidrolife (yellow)"
#define D_NEOPOOL_MACH_AQUASCENIC "Aquascenic (blue)"
#define D_NEOPOOL_MACH_OXILIFE "Oxilife (green)"
#define D_NEOPOOL_MACH_BIONET "Bionet (light blue)"
#define D_NEOPOOL_MACH_HIDRONISER "Hidroniser (red)"
#define D_NEOPOOL_MACH_UVSCENIC "UVScenic (lilac)"
#define D_NEOPOOL_MACH_STATION "Station (orange)"
#define D_NEOPOOL_MACH_BRILIX "Brilix"
#define D_NEOPOOL_MACH_GENERIC "Generic"
#define D_NEOPOOL_MACH_BAYROL "Bayrol"
#define D_NEOPOOL_MACH_HAY "Hay"
#define D_NEOPOOL_FILTRATION_MANUAL "Manual" // Filtration modes
#define D_NEOPOOL_FILTRATION_AUTO "Auto"
#define D_NEOPOOL_FILTRATION_HEATING "Heating"
#define D_NEOPOOL_FILTRATION_SMART "Smart"
#define D_NEOPOOL_FILTRATION_INTELLIGENT "Intelligent"
#define D_NEOPOOL_FILTRATION_BACKWASH "Backwash"
#define D_NEOPOOL_FILTRATION_NONE "" // Filtration speed level
#define D_NEOPOOL_FILTRATION_SLOW "slow"
#define D_NEOPOOL_FILTRATION_MEDIUM "medium"
#define D_NEOPOOL_FILTRATION_FAST "fast"
#define D_NEOPOOL_TYPE "Type" // Sensor & relais names
#define D_NEOPOOL_REDOX "Redox"
#define D_NEOPOOL_CHLORINE "Chlorine"
#define D_NEOPOOL_CONDUCTIVITY "Conductivity"
#define D_NEOPOOL_IONIZATION "Ionization"
#define D_NEOPOOL_HYDROLYSIS "Hydrolysis"
#define D_NEOPOOL_RELAY "Relay"
#define D_NEOPOOL_RELAY_FILTRATION "Filtration"
#define D_NEOPOOL_RELAY_LIGHT "Light"
#define D_NEOPOOL_RELAY_PH_ACID "Acid pump"
#define D_NEOPOOL_RELAY_PH_BASE "Base pump"
#define D_NEOPOOL_RELAY_RX "Redox level"
#define D_NEOPOOL_RELAY_CL "Chlorine pump"
#define D_NEOPOOL_RELAY_CD "Brine pump"
#define D_NEOPOOL_TIME "Time"
#define D_NEOPOOL_FILT_MODE "Filtration"
#define D_NEOPOOL_POLARIZATION "Pol" // Sensor status
#define D_NEOPOOL_PR_OFF "PrOff"
#define D_NEOPOOL_SETPOINT_OK "Ok"
#define D_NEOPOOL_COVER "Cover"
#define D_NEOPOOL_SHOCK "Shock"
#define D_NEOPOOL_ALARM "! "
#define D_NEOPOOL_LOW "Low"
#define D_NEOPOOL_FLOW1 "FL1"
#define D_NEOPOOL_FLOW2 "FL2"
#define D_NEOPOOL_PH_HIGH "too high" // ph Alarms
#define D_NEOPOOL_PH_LOW "too low"
#define D_NEOPOOL_PUMP_TIME_EXCEEDED "pump time exceeded"
#endif // _LANGUAGE_ES_ES_H_

View File

@ -778,6 +778,11 @@
#define D_SENSOR_SSD1331_CS "SSD1331 CS"
#define D_SENSOR_SSD1331_DC "SSD1331 DC"
#define D_SENSOR_SDCARD_CS "CarteSD CS"
#define D_SENSOR_WIEGAND_D0 "Wiegand D0"
#define D_SENSOR_WIEGAND_D1 "Wiegand D1"
#define D_SENSOR_NEOPOOL_TX "NeoPool Tx"
#define D_SENSOR_NEOPOOL_RX "NeoPool Rx"
// Units
#define D_UNIT_AMPERE "A"
@ -947,4 +952,56 @@
#define D_FP_PASSVERIFY "Mot-de-passe vérifié" // 0x21 Verify the fingerprint passed
#define D_FP_UNKNOWNERROR "Erreur" // Any other error
// xsns_83_neopool.ino
#define D_NEOPOOL_MACH_NONE "NeoPool" // Machine names
#define D_NEOPOOL_MACH_HIDROLIFE "Hidrolife (yellow)"
#define D_NEOPOOL_MACH_AQUASCENIC "Aquascenic (blue)"
#define D_NEOPOOL_MACH_OXILIFE "Oxilife (green)"
#define D_NEOPOOL_MACH_BIONET "Bionet (light blue)"
#define D_NEOPOOL_MACH_HIDRONISER "Hidroniser (red)"
#define D_NEOPOOL_MACH_UVSCENIC "UVScenic (lilac)"
#define D_NEOPOOL_MACH_STATION "Station (orange)"
#define D_NEOPOOL_MACH_BRILIX "Brilix"
#define D_NEOPOOL_MACH_GENERIC "Generic"
#define D_NEOPOOL_MACH_BAYROL "Bayrol"
#define D_NEOPOOL_MACH_HAY "Hay"
#define D_NEOPOOL_FILTRATION_MANUAL "Manual" // Filtration modes
#define D_NEOPOOL_FILTRATION_AUTO "Auto"
#define D_NEOPOOL_FILTRATION_HEATING "Heating"
#define D_NEOPOOL_FILTRATION_SMART "Smart"
#define D_NEOPOOL_FILTRATION_INTELLIGENT "Intelligent"
#define D_NEOPOOL_FILTRATION_BACKWASH "Backwash"
#define D_NEOPOOL_FILTRATION_NONE "" // Filtration speed level
#define D_NEOPOOL_FILTRATION_SLOW "slow"
#define D_NEOPOOL_FILTRATION_MEDIUM "medium"
#define D_NEOPOOL_FILTRATION_FAST "fast"
#define D_NEOPOOL_TYPE "Type" // Sensor & relais names
#define D_NEOPOOL_REDOX "Redox"
#define D_NEOPOOL_CHLORINE "Chlorine"
#define D_NEOPOOL_CONDUCTIVITY "Conductivity"
#define D_NEOPOOL_IONIZATION "Ionization"
#define D_NEOPOOL_HYDROLYSIS "Hydrolysis"
#define D_NEOPOOL_RELAY "Relay"
#define D_NEOPOOL_RELAY_FILTRATION "Filtration"
#define D_NEOPOOL_RELAY_LIGHT "Light"
#define D_NEOPOOL_RELAY_PH_ACID "Acid pump"
#define D_NEOPOOL_RELAY_PH_BASE "Base pump"
#define D_NEOPOOL_RELAY_RX "Redox level"
#define D_NEOPOOL_RELAY_CL "Chlorine pump"
#define D_NEOPOOL_RELAY_CD "Brine pump"
#define D_NEOPOOL_TIME "Time"
#define D_NEOPOOL_FILT_MODE "Filtration"
#define D_NEOPOOL_POLARIZATION "Pol" // Sensor status
#define D_NEOPOOL_PR_OFF "PrOff"
#define D_NEOPOOL_SETPOINT_OK "Ok"
#define D_NEOPOOL_COVER "Cover"
#define D_NEOPOOL_SHOCK "Shock"
#define D_NEOPOOL_ALARM "! "
#define D_NEOPOOL_LOW "Low"
#define D_NEOPOOL_FLOW1 "FL1"
#define D_NEOPOOL_FLOW2 "FL2"
#define D_NEOPOOL_PH_HIGH "too high" // ph Alarms
#define D_NEOPOOL_PH_LOW "too low"
#define D_NEOPOOL_PUMP_TIME_EXCEEDED "pump time exceeded"
#endif // _LANGUAGE_FR_FR_H_

View File

@ -782,6 +782,11 @@
#define D_SENSOR_SSD1331_CS "SSD1331 CS"
#define D_SENSOR_SSD1331_DC "SSD1331 DC"
#define D_SENSOR_SDCARD_CS "SDCard CS"
#define D_SENSOR_WIEGAND_D0 "Wiegand D0"
#define D_SENSOR_WIEGAND_D1 "Wiegand D1"
#define D_SENSOR_NEOPOOL_TX "NeoPool Tx"
#define D_SENSOR_NEOPOOL_RX "NeoPool Rx"
// Units
#define D_UNIT_AMPERE "A"
@ -953,4 +958,56 @@
#define D_FP_PASSVERIFY "Password verified" // 0x21 Verify the fingerprint passed
#define D_FP_UNKNOWNERROR "Error" // Any other error
// xsns_83_neopool.ino
#define D_NEOPOOL_MACH_NONE "NeoPool" // Machine names
#define D_NEOPOOL_MACH_HIDROLIFE "Hidrolife (yellow)"
#define D_NEOPOOL_MACH_AQUASCENIC "Aquascenic (blue)"
#define D_NEOPOOL_MACH_OXILIFE "Oxilife (green)"
#define D_NEOPOOL_MACH_BIONET "Bionet (light blue)"
#define D_NEOPOOL_MACH_HIDRONISER "Hidroniser (red)"
#define D_NEOPOOL_MACH_UVSCENIC "UVScenic (lilac)"
#define D_NEOPOOL_MACH_STATION "Station (orange)"
#define D_NEOPOOL_MACH_BRILIX "Brilix"
#define D_NEOPOOL_MACH_GENERIC "Generic"
#define D_NEOPOOL_MACH_BAYROL "Bayrol"
#define D_NEOPOOL_MACH_HAY "Hay"
#define D_NEOPOOL_FILTRATION_MANUAL "Manual" // Filtration modes
#define D_NEOPOOL_FILTRATION_AUTO "Auto"
#define D_NEOPOOL_FILTRATION_HEATING "Heating"
#define D_NEOPOOL_FILTRATION_SMART "Smart"
#define D_NEOPOOL_FILTRATION_INTELLIGENT "Intelligent"
#define D_NEOPOOL_FILTRATION_BACKWASH "Backwash"
#define D_NEOPOOL_FILTRATION_NONE "" // Filtration speed level
#define D_NEOPOOL_FILTRATION_SLOW "slow"
#define D_NEOPOOL_FILTRATION_MEDIUM "medium"
#define D_NEOPOOL_FILTRATION_FAST "fast"
#define D_NEOPOOL_TYPE "Type" // Sensor & relais names
#define D_NEOPOOL_REDOX "Redox"
#define D_NEOPOOL_CHLORINE "Chlorine"
#define D_NEOPOOL_CONDUCTIVITY "Conductivity"
#define D_NEOPOOL_IONIZATION "Ionization"
#define D_NEOPOOL_HYDROLYSIS "Hydrolysis"
#define D_NEOPOOL_RELAY "Relay"
#define D_NEOPOOL_RELAY_FILTRATION "Filtration"
#define D_NEOPOOL_RELAY_LIGHT "Light"
#define D_NEOPOOL_RELAY_PH_ACID "Acid pump"
#define D_NEOPOOL_RELAY_PH_BASE "Base pump"
#define D_NEOPOOL_RELAY_RX "Redox level"
#define D_NEOPOOL_RELAY_CL "Chlorine pump"
#define D_NEOPOOL_RELAY_CD "Brine pump"
#define D_NEOPOOL_TIME "Time"
#define D_NEOPOOL_FILT_MODE "Filtration"
#define D_NEOPOOL_POLARIZATION "Pol" // Sensor status
#define D_NEOPOOL_PR_OFF "PrOff"
#define D_NEOPOOL_SETPOINT_OK "Ok"
#define D_NEOPOOL_COVER "Cover"
#define D_NEOPOOL_SHOCK "Shock"
#define D_NEOPOOL_ALARM "! "
#define D_NEOPOOL_LOW "Low"
#define D_NEOPOOL_FLOW1 "FL1"
#define D_NEOPOOL_FLOW2 "FL2"
#define D_NEOPOOL_PH_HIGH "too high" // ph Alarms
#define D_NEOPOOL_PH_LOW "too low"
#define D_NEOPOOL_PUMP_TIME_EXCEEDED "pump time exceeded"
#endif // _LANGUAGE_HE_HE_H_

View File

@ -782,6 +782,11 @@
#define D_SENSOR_SSD1331_CS "SSD1331 CS"
#define D_SENSOR_SSD1331_DC "SSD1331 DC"
#define D_SENSOR_SDCARD_CS "SDCard CS"
#define D_SENSOR_WIEGAND_D0 "Wiegand D0"
#define D_SENSOR_WIEGAND_D1 "Wiegand D1"
#define D_SENSOR_NEOPOOL_TX "NeoPool Tx"
#define D_SENSOR_NEOPOOL_RX "NeoPool Rx"
// Units
#define D_UNIT_AMPERE "A"
@ -953,4 +958,56 @@
#define D_FP_PASSVERIFY "Password verified" // 0x21 Verify the fingerprint passed
#define D_FP_UNKNOWNERROR "Error" // Any other error
// xsns_83_neopool.ino
#define D_NEOPOOL_MACH_NONE "NeoPool" // Machine names
#define D_NEOPOOL_MACH_HIDROLIFE "Hidrolife (yellow)"
#define D_NEOPOOL_MACH_AQUASCENIC "Aquascenic (blue)"
#define D_NEOPOOL_MACH_OXILIFE "Oxilife (green)"
#define D_NEOPOOL_MACH_BIONET "Bionet (light blue)"
#define D_NEOPOOL_MACH_HIDRONISER "Hidroniser (red)"
#define D_NEOPOOL_MACH_UVSCENIC "UVScenic (lilac)"
#define D_NEOPOOL_MACH_STATION "Station (orange)"
#define D_NEOPOOL_MACH_BRILIX "Brilix"
#define D_NEOPOOL_MACH_GENERIC "Generic"
#define D_NEOPOOL_MACH_BAYROL "Bayrol"
#define D_NEOPOOL_MACH_HAY "Hay"
#define D_NEOPOOL_FILTRATION_MANUAL "Manual" // Filtration modes
#define D_NEOPOOL_FILTRATION_AUTO "Auto"
#define D_NEOPOOL_FILTRATION_HEATING "Heating"
#define D_NEOPOOL_FILTRATION_SMART "Smart"
#define D_NEOPOOL_FILTRATION_INTELLIGENT "Intelligent"
#define D_NEOPOOL_FILTRATION_BACKWASH "Backwash"
#define D_NEOPOOL_FILTRATION_NONE "" // Filtration speed level
#define D_NEOPOOL_FILTRATION_SLOW "slow"
#define D_NEOPOOL_FILTRATION_MEDIUM "medium"
#define D_NEOPOOL_FILTRATION_FAST "fast"
#define D_NEOPOOL_TYPE "Type" // Sensor & relais names
#define D_NEOPOOL_REDOX "Redox"
#define D_NEOPOOL_CHLORINE "Chlorine"
#define D_NEOPOOL_CONDUCTIVITY "Conductivity"
#define D_NEOPOOL_IONIZATION "Ionization"
#define D_NEOPOOL_HYDROLYSIS "Hydrolysis"
#define D_NEOPOOL_RELAY "Relay"
#define D_NEOPOOL_RELAY_FILTRATION "Filtration"
#define D_NEOPOOL_RELAY_LIGHT "Light"
#define D_NEOPOOL_RELAY_PH_ACID "Acid pump"
#define D_NEOPOOL_RELAY_PH_BASE "Base pump"
#define D_NEOPOOL_RELAY_RX "Redox level"
#define D_NEOPOOL_RELAY_CL "Chlorine pump"
#define D_NEOPOOL_RELAY_CD "Brine pump"
#define D_NEOPOOL_TIME "Time"
#define D_NEOPOOL_FILT_MODE "Filtration"
#define D_NEOPOOL_POLARIZATION "Pol" // Sensor status
#define D_NEOPOOL_PR_OFF "PrOff"
#define D_NEOPOOL_SETPOINT_OK "Ok"
#define D_NEOPOOL_COVER "Cover"
#define D_NEOPOOL_SHOCK "Shock"
#define D_NEOPOOL_ALARM "! "
#define D_NEOPOOL_LOW "Low"
#define D_NEOPOOL_FLOW1 "FL1"
#define D_NEOPOOL_FLOW2 "FL2"
#define D_NEOPOOL_PH_HIGH "too high" // ph Alarms
#define D_NEOPOOL_PH_LOW "too low"
#define D_NEOPOOL_PUMP_TIME_EXCEEDED "pump time exceeded"
#endif // _LANGUAGE_HU_HU_H_

View File

@ -1,7 +1,7 @@
/*
it-IT.h - localization for Italian - Italy for Tasmota
Copyright (C) 2021 Gennaro Tortone - some mods by Antonio Fragola - Updated by bovirus - rev. 09.01.2021
Copyright (C) 2021 Gennaro Tortone - some mods by Antonio Fragola - Updated by bovirus - rev. 22.01.2021
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
@ -782,6 +782,10 @@
#define D_SENSOR_SSD1331_CS "SSD1331 - CS"
#define D_SENSOR_SSD1331_DC "SSD1331 - DC"
#define D_SENSOR_SDCARD_CS "Scheda SD - CS"
#define D_SENSOR_WIEGAND_D0 "Wiegand - D0"
#define D_SENSOR_WIEGAND_D1 "Wiegand - D1"
#define D_SENSOR_NEOPOOL_TX "NeoPool Tx"
#define D_SENSOR_NEOPOOL_RX "NeoPool Rx"
// Units
#define D_UNIT_AMPERE "A"
@ -953,4 +957,56 @@
#define D_FP_PASSVERIFY "Password verificata" // 0x21 Verify the fingerprint passed
#define D_FP_UNKNOWNERROR "Errore" // Any other error
// xsns_83_neopool.ino
#define D_NEOPOOL_MACH_NONE "NeoPool" // Machine names
#define D_NEOPOOL_MACH_HIDROLIFE "Hidrolife (giallo)"
#define D_NEOPOOL_MACH_AQUASCENIC "Aquascenic (blu)"
#define D_NEOPOOL_MACH_OXILIFE "Oxilife (verde)"
#define D_NEOPOOL_MACH_BIONET "Bionet (azzurro)"
#define D_NEOPOOL_MACH_HIDRONISER "Hidroniser (rosso)"
#define D_NEOPOOL_MACH_UVSCENIC "UVScenic (lilac)"
#define D_NEOPOOL_MACH_STATION "Station (arancio)"
#define D_NEOPOOL_MACH_BRILIX "Brilix"
#define D_NEOPOOL_MACH_GENERIC "Generico"
#define D_NEOPOOL_MACH_BAYROL "Bayrol"
#define D_NEOPOOL_MACH_HAY "Hay"
#define D_NEOPOOL_FILTRATION_MANUAL "Manuale" // Filtration modes
#define D_NEOPOOL_FILTRATION_AUTO "Automatico"
#define D_NEOPOOL_FILTRATION_HEATING "Riscaldamento"
#define D_NEOPOOL_FILTRATION_SMART "Rapido"
#define D_NEOPOOL_FILTRATION_INTELLIGENT "Intelligente"
#define D_NEOPOOL_FILTRATION_BACKWASH "Contro lavaggio"
#define D_NEOPOOL_FILTRATION_NONE "" // Filtration speed level
#define D_NEOPOOL_FILTRATION_SLOW "lento"
#define D_NEOPOOL_FILTRATION_MEDIUM "medio"
#define D_NEOPOOL_FILTRATION_FAST "veloce"
#define D_NEOPOOL_TYPE "Tipo" // Sensor & relais names
#define D_NEOPOOL_REDOX "Redox"
#define D_NEOPOOL_CHLORINE "Cloro"
#define D_NEOPOOL_CONDUCTIVITY "Conduttività"
#define D_NEOPOOL_IONIZATION "Ionizzazione"
#define D_NEOPOOL_HYDROLYSIS "Idrolisi"
#define D_NEOPOOL_RELAY "Relay"
#define D_NEOPOOL_RELAY_FILTRATION "Filtrazione"
#define D_NEOPOOL_RELAY_LIGHT "Luce"
#define D_NEOPOOL_RELAY_PH_ACID "Pompa per acido"
#define D_NEOPOOL_RELAY_PH_BASE "Popa base"
#define D_NEOPOOL_RELAY_RX "Livello Redox"
#define D_NEOPOOL_RELAY_CL "Pompa cloro"
#define D_NEOPOOL_RELAY_CD "Pompa salamoia"
#define D_NEOPOOL_TIME "Orario"
#define D_NEOPOOL_FILT_MODE "Filtrazione"
#define D_NEOPOOL_POLARIZATION "Pol" // Sensor status
#define D_NEOPOOL_PR_OFF "PrOff"
#define D_NEOPOOL_SETPOINT_OK "OK"
#define D_NEOPOOL_COVER "Copertura"
#define D_NEOPOOL_SHOCK "Shock"
#define D_NEOPOOL_ALARM "! "
#define D_NEOPOOL_LOW "Low"
#define D_NEOPOOL_FLOW1 "FL1"
#define D_NEOPOOL_FLOW2 "FL2"
#define D_NEOPOOL_PH_HIGH "troppo alto" // ph Alarms
#define D_NEOPOOL_PH_LOW "troppo basso"
#define D_NEOPOOL_PUMP_TIME_EXCEEDED "tempo pompa superato"
#endif // _LANGUAGE_IT_IT_H_

View File

@ -782,6 +782,11 @@
#define D_SENSOR_SSD1331_CS "SSD1331 CS"
#define D_SENSOR_SSD1331_DC "SSD1331 DC"
#define D_SENSOR_SDCARD_CS "SDCard CS"
#define D_SENSOR_WIEGAND_D0 "Wiegand D0"
#define D_SENSOR_WIEGAND_D1 "Wiegand D1"
#define D_SENSOR_NEOPOOL_TX "NeoPool Tx"
#define D_SENSOR_NEOPOOL_RX "NeoPool Rx"
// Units
#define D_UNIT_AMPERE "A"
@ -953,4 +958,56 @@
#define D_FP_PASSVERIFY "Password verified" // 0x21 Verify the fingerprint passed
#define D_FP_UNKNOWNERROR "Error" // Any other error
// xsns_83_neopool.ino
#define D_NEOPOOL_MACH_NONE "NeoPool" // Machine names
#define D_NEOPOOL_MACH_HIDROLIFE "Hidrolife (yellow)"
#define D_NEOPOOL_MACH_AQUASCENIC "Aquascenic (blue)"
#define D_NEOPOOL_MACH_OXILIFE "Oxilife (green)"
#define D_NEOPOOL_MACH_BIONET "Bionet (light blue)"
#define D_NEOPOOL_MACH_HIDRONISER "Hidroniser (red)"
#define D_NEOPOOL_MACH_UVSCENIC "UVScenic (lilac)"
#define D_NEOPOOL_MACH_STATION "Station (orange)"
#define D_NEOPOOL_MACH_BRILIX "Brilix"
#define D_NEOPOOL_MACH_GENERIC "Generic"
#define D_NEOPOOL_MACH_BAYROL "Bayrol"
#define D_NEOPOOL_MACH_HAY "Hay"
#define D_NEOPOOL_FILTRATION_MANUAL "Manual" // Filtration modes
#define D_NEOPOOL_FILTRATION_AUTO "Auto"
#define D_NEOPOOL_FILTRATION_HEATING "Heating"
#define D_NEOPOOL_FILTRATION_SMART "Smart"
#define D_NEOPOOL_FILTRATION_INTELLIGENT "Intelligent"
#define D_NEOPOOL_FILTRATION_BACKWASH "Backwash"
#define D_NEOPOOL_FILTRATION_NONE "" // Filtration speed level
#define D_NEOPOOL_FILTRATION_SLOW "slow"
#define D_NEOPOOL_FILTRATION_MEDIUM "medium"
#define D_NEOPOOL_FILTRATION_FAST "fast"
#define D_NEOPOOL_TYPE "Type" // Sensor & relais names
#define D_NEOPOOL_REDOX "Redox"
#define D_NEOPOOL_CHLORINE "Chlorine"
#define D_NEOPOOL_CONDUCTIVITY "Conductivity"
#define D_NEOPOOL_IONIZATION "Ionization"
#define D_NEOPOOL_HYDROLYSIS "Hydrolysis"
#define D_NEOPOOL_RELAY "Relay"
#define D_NEOPOOL_RELAY_FILTRATION "Filtration"
#define D_NEOPOOL_RELAY_LIGHT "Light"
#define D_NEOPOOL_RELAY_PH_ACID "Acid pump"
#define D_NEOPOOL_RELAY_PH_BASE "Base pump"
#define D_NEOPOOL_RELAY_RX "Redox level"
#define D_NEOPOOL_RELAY_CL "Chlorine pump"
#define D_NEOPOOL_RELAY_CD "Brine pump"
#define D_NEOPOOL_TIME "Time"
#define D_NEOPOOL_FILT_MODE "Filtration"
#define D_NEOPOOL_POLARIZATION "Pol" // Sensor status
#define D_NEOPOOL_PR_OFF "PrOff"
#define D_NEOPOOL_SETPOINT_OK "Ok"
#define D_NEOPOOL_COVER "Cover"
#define D_NEOPOOL_SHOCK "Shock"
#define D_NEOPOOL_ALARM "! "
#define D_NEOPOOL_LOW "Low"
#define D_NEOPOOL_FLOW1 "FL1"
#define D_NEOPOOL_FLOW2 "FL2"
#define D_NEOPOOL_PH_HIGH "too high" // ph Alarms
#define D_NEOPOOL_PH_LOW "too low"
#define D_NEOPOOL_PUMP_TIME_EXCEEDED "pump time exceeded"
#endif // _LANGUAGE_KO_KO_H_

View File

@ -782,6 +782,11 @@
#define D_SENSOR_SSD1331_CS "SSD1331 CS"
#define D_SENSOR_SSD1331_DC "SSD1331 DC"
#define D_SENSOR_SDCARD_CS "SDCard CS"
#define D_SENSOR_WIEGAND_D0 "Wiegand D0"
#define D_SENSOR_WIEGAND_D1 "Wiegand D1"
#define D_SENSOR_NEOPOOL_TX "NeoPool Tx"
#define D_SENSOR_NEOPOOL_RX "NeoPool Rx"
// Units
#define D_UNIT_AMPERE "A"
@ -953,4 +958,56 @@
#define D_FP_PASSVERIFY "Password verified" // 0x21 Verify the fingerprint passed
#define D_FP_UNKNOWNERROR "Error" // Any other error
// xsns_83_neopool.ino
#define D_NEOPOOL_MACH_NONE "NeoPool" // Machine names
#define D_NEOPOOL_MACH_HIDROLIFE "Hidrolife (yellow)"
#define D_NEOPOOL_MACH_AQUASCENIC "Aquascenic (blue)"
#define D_NEOPOOL_MACH_OXILIFE "Oxilife (green)"
#define D_NEOPOOL_MACH_BIONET "Bionet (light blue)"
#define D_NEOPOOL_MACH_HIDRONISER "Hidroniser (red)"
#define D_NEOPOOL_MACH_UVSCENIC "UVScenic (lilac)"
#define D_NEOPOOL_MACH_STATION "Station (orange)"
#define D_NEOPOOL_MACH_BRILIX "Brilix"
#define D_NEOPOOL_MACH_GENERIC "Generic"
#define D_NEOPOOL_MACH_BAYROL "Bayrol"
#define D_NEOPOOL_MACH_HAY "Hay"
#define D_NEOPOOL_FILTRATION_MANUAL "Manual" // Filtration modes
#define D_NEOPOOL_FILTRATION_AUTO "Auto"
#define D_NEOPOOL_FILTRATION_HEATING "Heating"
#define D_NEOPOOL_FILTRATION_SMART "Smart"
#define D_NEOPOOL_FILTRATION_INTELLIGENT "Intelligent"
#define D_NEOPOOL_FILTRATION_BACKWASH "Backwash"
#define D_NEOPOOL_FILTRATION_NONE "" // Filtration speed level
#define D_NEOPOOL_FILTRATION_SLOW "slow"
#define D_NEOPOOL_FILTRATION_MEDIUM "medium"
#define D_NEOPOOL_FILTRATION_FAST "fast"
#define D_NEOPOOL_TYPE "Type" // Sensor & relais names
#define D_NEOPOOL_REDOX "Redox"
#define D_NEOPOOL_CHLORINE "Chlorine"
#define D_NEOPOOL_CONDUCTIVITY "Conductivity"
#define D_NEOPOOL_IONIZATION "Ionization"
#define D_NEOPOOL_HYDROLYSIS "Hydrolysis"
#define D_NEOPOOL_RELAY "Relay"
#define D_NEOPOOL_RELAY_FILTRATION "Filtration"
#define D_NEOPOOL_RELAY_LIGHT "Light"
#define D_NEOPOOL_RELAY_PH_ACID "Acid pump"
#define D_NEOPOOL_RELAY_PH_BASE "Base pump"
#define D_NEOPOOL_RELAY_RX "Redox level"
#define D_NEOPOOL_RELAY_CL "Chlorine pump"
#define D_NEOPOOL_RELAY_CD "Brine pump"
#define D_NEOPOOL_TIME "Time"
#define D_NEOPOOL_FILT_MODE "Filtration"
#define D_NEOPOOL_POLARIZATION "Pol" // Sensor status
#define D_NEOPOOL_PR_OFF "PrOff"
#define D_NEOPOOL_SETPOINT_OK "Ok"
#define D_NEOPOOL_COVER "Cover"
#define D_NEOPOOL_SHOCK "Shock"
#define D_NEOPOOL_ALARM "! "
#define D_NEOPOOL_LOW "Low"
#define D_NEOPOOL_FLOW1 "FL1"
#define D_NEOPOOL_FLOW2 "FL2"
#define D_NEOPOOL_PH_HIGH "too high" // ph Alarms
#define D_NEOPOOL_PH_LOW "too low"
#define D_NEOPOOL_PUMP_TIME_EXCEEDED "pump time exceeded"
#endif // _LANGUAGE_NL_NL_H_

View File

@ -782,6 +782,11 @@
#define D_SENSOR_SSD1331_CS "SSD1331 CS"
#define D_SENSOR_SSD1331_DC "SSD1331 DC"
#define D_SENSOR_SDCARD_CS "SDCard CS"
#define D_SENSOR_WIEGAND_D0 "Wiegand D0"
#define D_SENSOR_WIEGAND_D1 "Wiegand D1"
#define D_SENSOR_NEOPOOL_TX "NeoPool Tx"
#define D_SENSOR_NEOPOOL_RX "NeoPool Rx"
// Units
#define D_UNIT_AMPERE "A"
@ -953,4 +958,56 @@
#define D_FP_PASSVERIFY "Password verified" // 0x21 Verify the fingerprint passed
#define D_FP_UNKNOWNERROR "Error" // Any other error
// xsns_83_neopool.ino
#define D_NEOPOOL_MACH_NONE "NeoPool" // Machine names
#define D_NEOPOOL_MACH_HIDROLIFE "Hidrolife (yellow)"
#define D_NEOPOOL_MACH_AQUASCENIC "Aquascenic (blue)"
#define D_NEOPOOL_MACH_OXILIFE "Oxilife (green)"
#define D_NEOPOOL_MACH_BIONET "Bionet (light blue)"
#define D_NEOPOOL_MACH_HIDRONISER "Hidroniser (red)"
#define D_NEOPOOL_MACH_UVSCENIC "UVScenic (lilac)"
#define D_NEOPOOL_MACH_STATION "Station (orange)"
#define D_NEOPOOL_MACH_BRILIX "Brilix"
#define D_NEOPOOL_MACH_GENERIC "Generic"
#define D_NEOPOOL_MACH_BAYROL "Bayrol"
#define D_NEOPOOL_MACH_HAY "Hay"
#define D_NEOPOOL_FILTRATION_MANUAL "Manual" // Filtration modes
#define D_NEOPOOL_FILTRATION_AUTO "Auto"
#define D_NEOPOOL_FILTRATION_HEATING "Heating"
#define D_NEOPOOL_FILTRATION_SMART "Smart"
#define D_NEOPOOL_FILTRATION_INTELLIGENT "Intelligent"
#define D_NEOPOOL_FILTRATION_BACKWASH "Backwash"
#define D_NEOPOOL_FILTRATION_NONE "" // Filtration speed level
#define D_NEOPOOL_FILTRATION_SLOW "slow"
#define D_NEOPOOL_FILTRATION_MEDIUM "medium"
#define D_NEOPOOL_FILTRATION_FAST "fast"
#define D_NEOPOOL_TYPE "Type" // Sensor & relais names
#define D_NEOPOOL_REDOX "Redox"
#define D_NEOPOOL_CHLORINE "Chlorine"
#define D_NEOPOOL_CONDUCTIVITY "Conductivity"
#define D_NEOPOOL_IONIZATION "Ionization"
#define D_NEOPOOL_HYDROLYSIS "Hydrolysis"
#define D_NEOPOOL_RELAY "Relay"
#define D_NEOPOOL_RELAY_FILTRATION "Filtration"
#define D_NEOPOOL_RELAY_LIGHT "Light"
#define D_NEOPOOL_RELAY_PH_ACID "Acid pump"
#define D_NEOPOOL_RELAY_PH_BASE "Base pump"
#define D_NEOPOOL_RELAY_RX "Redox level"
#define D_NEOPOOL_RELAY_CL "Chlorine pump"
#define D_NEOPOOL_RELAY_CD "Brine pump"
#define D_NEOPOOL_TIME "Time"
#define D_NEOPOOL_FILT_MODE "Filtration"
#define D_NEOPOOL_POLARIZATION "Pol" // Sensor status
#define D_NEOPOOL_PR_OFF "PrOff"
#define D_NEOPOOL_SETPOINT_OK "Ok"
#define D_NEOPOOL_COVER "Cover"
#define D_NEOPOOL_SHOCK "Shock"
#define D_NEOPOOL_ALARM "! "
#define D_NEOPOOL_LOW "Low"
#define D_NEOPOOL_FLOW1 "FL1"
#define D_NEOPOOL_FLOW2 "FL2"
#define D_NEOPOOL_PH_HIGH "too high" // ph Alarms
#define D_NEOPOOL_PH_LOW "too low"
#define D_NEOPOOL_PUMP_TIME_EXCEEDED "pump time exceeded"
#endif // _LANGUAGE_PL_PL_D_H_

View File

@ -782,6 +782,11 @@
#define D_SENSOR_SSD1331_CS "SSD1331 CS"
#define D_SENSOR_SSD1331_DC "SSD1331 DC"
#define D_SENSOR_SDCARD_CS "SDCard CS"
#define D_SENSOR_WIEGAND_D0 "Wiegand D0"
#define D_SENSOR_WIEGAND_D1 "Wiegand D1"
#define D_SENSOR_NEOPOOL_TX "NeoPool Tx"
#define D_SENSOR_NEOPOOL_RX "NeoPool Rx"
// Units
#define D_UNIT_AMPERE "A"
@ -953,4 +958,56 @@
#define D_FP_PASSVERIFY "Password verified" // 0x21 Verify the fingerprint passed
#define D_FP_UNKNOWNERROR "Error" // Any other error
// xsns_83_neopool.ino
#define D_NEOPOOL_MACH_NONE "NeoPool" // Machine names
#define D_NEOPOOL_MACH_HIDROLIFE "Hidrolife (yellow)"
#define D_NEOPOOL_MACH_AQUASCENIC "Aquascenic (blue)"
#define D_NEOPOOL_MACH_OXILIFE "Oxilife (green)"
#define D_NEOPOOL_MACH_BIONET "Bionet (light blue)"
#define D_NEOPOOL_MACH_HIDRONISER "Hidroniser (red)"
#define D_NEOPOOL_MACH_UVSCENIC "UVScenic (lilac)"
#define D_NEOPOOL_MACH_STATION "Station (orange)"
#define D_NEOPOOL_MACH_BRILIX "Brilix"
#define D_NEOPOOL_MACH_GENERIC "Generic"
#define D_NEOPOOL_MACH_BAYROL "Bayrol"
#define D_NEOPOOL_MACH_HAY "Hay"
#define D_NEOPOOL_FILTRATION_MANUAL "Manual" // Filtration modes
#define D_NEOPOOL_FILTRATION_AUTO "Auto"
#define D_NEOPOOL_FILTRATION_HEATING "Heating"
#define D_NEOPOOL_FILTRATION_SMART "Smart"
#define D_NEOPOOL_FILTRATION_INTELLIGENT "Intelligent"
#define D_NEOPOOL_FILTRATION_BACKWASH "Backwash"
#define D_NEOPOOL_FILTRATION_NONE "" // Filtration speed level
#define D_NEOPOOL_FILTRATION_SLOW "slow"
#define D_NEOPOOL_FILTRATION_MEDIUM "medium"
#define D_NEOPOOL_FILTRATION_FAST "fast"
#define D_NEOPOOL_TYPE "Type" // Sensor & relais names
#define D_NEOPOOL_REDOX "Redox"
#define D_NEOPOOL_CHLORINE "Chlorine"
#define D_NEOPOOL_CONDUCTIVITY "Conductivity"
#define D_NEOPOOL_IONIZATION "Ionization"
#define D_NEOPOOL_HYDROLYSIS "Hydrolysis"
#define D_NEOPOOL_RELAY "Relay"
#define D_NEOPOOL_RELAY_FILTRATION "Filtration"
#define D_NEOPOOL_RELAY_LIGHT "Light"
#define D_NEOPOOL_RELAY_PH_ACID "Acid pump"
#define D_NEOPOOL_RELAY_PH_BASE "Base pump"
#define D_NEOPOOL_RELAY_RX "Redox level"
#define D_NEOPOOL_RELAY_CL "Chlorine pump"
#define D_NEOPOOL_RELAY_CD "Brine pump"
#define D_NEOPOOL_TIME "Time"
#define D_NEOPOOL_FILT_MODE "Filtration"
#define D_NEOPOOL_POLARIZATION "Pol" // Sensor status
#define D_NEOPOOL_PR_OFF "PrOff"
#define D_NEOPOOL_SETPOINT_OK "Ok"
#define D_NEOPOOL_COVER "Cover"
#define D_NEOPOOL_SHOCK "Shock"
#define D_NEOPOOL_ALARM "! "
#define D_NEOPOOL_LOW "Low"
#define D_NEOPOOL_FLOW1 "FL1"
#define D_NEOPOOL_FLOW2 "FL2"
#define D_NEOPOOL_PH_HIGH "too high" // ph Alarms
#define D_NEOPOOL_PH_LOW "too low"
#define D_NEOPOOL_PUMP_TIME_EXCEEDED "pump time exceeded"
#endif // _LANGUAGE_PT_BR_H_

View File

@ -782,6 +782,11 @@
#define D_SENSOR_SSD1331_CS "SSD1331 CS"
#define D_SENSOR_SSD1331_DC "SSD1331 DC"
#define D_SENSOR_SDCARD_CS "SDCard CS"
#define D_SENSOR_WIEGAND_D0 "Wiegand D0"
#define D_SENSOR_WIEGAND_D1 "Wiegand D1"
#define D_SENSOR_NEOPOOL_TX "NeoPool Tx"
#define D_SENSOR_NEOPOOL_RX "NeoPool Rx"
// Units
#define D_UNIT_AMPERE "A"
@ -953,4 +958,56 @@
#define D_FP_PASSVERIFY "Password verified" // 0x21 Verify the fingerprint passed
#define D_FP_UNKNOWNERROR "Error" // Any other error
// xsns_83_neopool.ino
#define D_NEOPOOL_MACH_NONE "NeoPool" // Machine names
#define D_NEOPOOL_MACH_HIDROLIFE "Hidrolife (yellow)"
#define D_NEOPOOL_MACH_AQUASCENIC "Aquascenic (blue)"
#define D_NEOPOOL_MACH_OXILIFE "Oxilife (green)"
#define D_NEOPOOL_MACH_BIONET "Bionet (light blue)"
#define D_NEOPOOL_MACH_HIDRONISER "Hidroniser (red)"
#define D_NEOPOOL_MACH_UVSCENIC "UVScenic (lilac)"
#define D_NEOPOOL_MACH_STATION "Station (orange)"
#define D_NEOPOOL_MACH_BRILIX "Brilix"
#define D_NEOPOOL_MACH_GENERIC "Generic"
#define D_NEOPOOL_MACH_BAYROL "Bayrol"
#define D_NEOPOOL_MACH_HAY "Hay"
#define D_NEOPOOL_FILTRATION_MANUAL "Manual" // Filtration modes
#define D_NEOPOOL_FILTRATION_AUTO "Auto"
#define D_NEOPOOL_FILTRATION_HEATING "Heating"
#define D_NEOPOOL_FILTRATION_SMART "Smart"
#define D_NEOPOOL_FILTRATION_INTELLIGENT "Intelligent"
#define D_NEOPOOL_FILTRATION_BACKWASH "Backwash"
#define D_NEOPOOL_FILTRATION_NONE "" // Filtration speed level
#define D_NEOPOOL_FILTRATION_SLOW "slow"
#define D_NEOPOOL_FILTRATION_MEDIUM "medium"
#define D_NEOPOOL_FILTRATION_FAST "fast"
#define D_NEOPOOL_TYPE "Type" // Sensor & relais names
#define D_NEOPOOL_REDOX "Redox"
#define D_NEOPOOL_CHLORINE "Chlorine"
#define D_NEOPOOL_CONDUCTIVITY "Conductivity"
#define D_NEOPOOL_IONIZATION "Ionization"
#define D_NEOPOOL_HYDROLYSIS "Hydrolysis"
#define D_NEOPOOL_RELAY "Relay"
#define D_NEOPOOL_RELAY_FILTRATION "Filtration"
#define D_NEOPOOL_RELAY_LIGHT "Light"
#define D_NEOPOOL_RELAY_PH_ACID "Acid pump"
#define D_NEOPOOL_RELAY_PH_BASE "Base pump"
#define D_NEOPOOL_RELAY_RX "Redox level"
#define D_NEOPOOL_RELAY_CL "Chlorine pump"
#define D_NEOPOOL_RELAY_CD "Brine pump"
#define D_NEOPOOL_TIME "Time"
#define D_NEOPOOL_FILT_MODE "Filtration"
#define D_NEOPOOL_POLARIZATION "Pol" // Sensor status
#define D_NEOPOOL_PR_OFF "PrOff"
#define D_NEOPOOL_SETPOINT_OK "Ok"
#define D_NEOPOOL_COVER "Cover"
#define D_NEOPOOL_SHOCK "Shock"
#define D_NEOPOOL_ALARM "! "
#define D_NEOPOOL_LOW "Low"
#define D_NEOPOOL_FLOW1 "FL1"
#define D_NEOPOOL_FLOW2 "FL2"
#define D_NEOPOOL_PH_HIGH "too high" // ph Alarms
#define D_NEOPOOL_PH_LOW "too low"
#define D_NEOPOOL_PUMP_TIME_EXCEEDED "pump time exceeded"
#endif // _LANGUAGE_PT_PT_H_

Some files were not shown because too many files have changed in this diff Show More