Merge branch 'development' into prerelease-14.4.0

This commit is contained in:
Theo Arends 2024-12-08 14:55:14 +01:00
commit f62080bc54
320 changed files with 16769 additions and 7191 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 with Tasmota core ESP8266 V.2.7.8
- [ ] The code change is tested and works with Tasmota core ESP32 V.3.1.0.240926
- [ ] The code change is tested and works with Tasmota core ESP32 V.3.1.0.241206
- [ ] 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

@ -25,7 +25,7 @@ jobs:
fail-fast: true
matrix:
variant:
- tasmota32solo1-safeboot
- tasmota32-webcam
steps:
- uses: actions/checkout@v4
- name: Set up Python
@ -53,7 +53,7 @@ jobs:
fail-fast: true
matrix:
variant:
- tasmota32-webcam
- tasmota32solo1-safeboot
steps:
- uses: actions/checkout@v4
- name: Set up Python

View File

@ -283,7 +283,7 @@ Note: the `minimal` variant is not listed as it shouldn't be used outside of the
| USE_SONOFF_SPM | | / x | | | | |
| USE_DISPLAY_TM1621_SONOFF | | / x | | | | |
| USE_SHELLY_PRO | | / x | | | | |
| USE_DALI | | / - | | | | |
| USE_DALI | | / x | | | | |
| USE_DINGTIAN_RELAY | | / - | | | | |
| USE_MATTER_DEVICE | | / x | | | | | See SetOption151 |

View File

@ -3,6 +3,147 @@ All notable changes to this project will be documented in this file.
## [Released]
## [14.4.0] 20241212
- Release Rudolph
## [14.3.0.7] 20241212
### Added
- Support for TM1640 based IoTTimer by Stefan Oskamp (#21376)
- Command `SetOption161 1` to disable display of state text (#22515)
- ESP32 new BLE filters by name and minimum RSSI (#22530)
- ESP32 Hybrid compile take custom boards settings in account (#22542)
- ESP32 ULP lp_core to Berry ULP module (#22567)
- Shelly 1 Gen3 template {"NAME":"Shelly 1 Gen3","GPIO":[0,0,0,4736,0,224,0,0,1,1,192,0,0,0,0,0,0,0,0,576,1,1],"FLAG":0,"BASE":1,"CMND":"AdcGpio3 10000,10000,4000"}
- Shelly 1PM Gen3 template {"NAME":"Shelly 1PM Gen3","GPIO":[0,32,0,4736,224,0,3200,8161,576,1,192,0,0,0,0,0,0,0,0,1,1,1],"FLAG":0,"BASE":1,"CMND":"AdcGpio3 10000,10000,4000"}
- Shelly 2PM Gen3 template {"NAME":"Shelly 2PM Gen3","GPIO":[9472,3458,576,225,4736,224,640,608,1,1,193,0,0,0,0,0,0,0,192,32,1,1],"FLAG":0,"BASE":1,"CMND":"AdcGpio4 10000,10000,4000"}
- Shelly i4 Gen3 template {"NAME":"Shelly i4 Gen3","GPIO":[0,0,0,4736,32,195,194,193,1,1,192,0,0,0,0,0,0,0,0,0,1,1],"FLAG":0,"BASE":1,"CMND":"AdcGpio3 10000,10000,4000}
- Show Active Power Total with any multi-phase energy monitoring (#22579)
- Command `SetOption162 1` to disable adding export energy to energy today (#22578)
- ESP32 support for WPA2/3 Enterprise conditional in core v3.1.0.241206 (#22600)
- Support for Sonoff POWCT Energy Export Active (#22596)
### Breaking Changed
- ESP32 ArtNet switches from GRB to RGB encoding (#22556)
### Changed
- ESP32 max number of supported switches/buttons/relays from 28 to 32
- ESP32 max number of interlocks from 14 to 16
- ESP32 Platform from 2024.11.30 to 2024.11.31, Framework (Arduino Core) from v3.1.0.241030 to v3.1.0.241117 and IDF to 5.3.1.241024 (#22504)
- Prevent active BLE operations with unencrypted MI-format beacons (#22453)
- ESP32 replaced NeoPixelBus with TasmotaLED (#22556)
- ESP32 Platform from 2024.11.31 to 2024.12.30, Framework (Arduino Core) from v3.1.0.241117 to v3.1.0.241206 and IDF to 5.3.2 (#22600)
### Fixed
- ESP32 upgrade by file upload response based on file size (#22500)
- Wrong GUI Module and Template drop down list indexes regression
- Use HTML escape on File System Edit File load (#22492)
- Magic switch applying masking window to any power change (#22535)
- Shift595 output offsets and restart relay toggles
- Shutter wrong power ON state (#22548)
- ESP32-C2 TasmotaLED from not present I2S to SPI (#22575)
- KNX Scenes index change regression from v14.2.0.4 (#22405)
- Add GUI submenu headers and refresh configuration button text (#22592)
### Removed
## [14.3.0.6] 20241116
### Added
- Add command ``WebColor20`` to control color of Button when Off
### Fixed
- Matter provisioning with matter.js controller (#22470)
- Prevent crashing when `display.ini` is missing end `#` (#22471)
## [14.3.0.5] 20241111
### Added
- ESP32 MI32 legacy add config operations (#22458)
### Changed
- Redesign GUI adding feedback to buttons, shutters and lights
- Use command `WebButton1` to change GUI shutter 1 name
### Removed
- Command ``SetOption161 1`` to disable web page slider updates by commands
## [14.3.0.4] 20241111
### Added
- DALI command `DaliGroupSliders 0..16` to show GUI group sliders with feedback disabling `DaliLight`
- Support for I2C over Serial (#22444)
- Support KNX for scripts (#22429)
- Support deep sleep (standby) for VL53L0X (#22441)
- Support for MS5837 pressure and temperature sensor (#22376)
- Berry add I2C read16/write16 supporting Little Endian (#22448)
- Berry drivers for PCA9535 (generic and in SenseCAP D1) (#22451)
- Shelly DALI Dimmer Gen3 template {"NAME":"Shelly DALI Dimmer Gen3","GPIO":[34,4736,0,3840,11360,11392,128,129,0,1,576,0,0,0,0,0,0,0,0,1,1,1],"FLAG":0,"BASE":1,"CMND":"AdcGpio1 10000,10000,4000}
### Changed
- AHT1X/AHT2X/AHT3X ready for virtual I2C (#22427)
- SGP4X ready for virtual I2C (#22427)
- SCD40 reduce logging levels (#22443)
- SCD40 ready for virtual I2C (#22443)
- Unit (k)VAr(h) to (k)var(h) (#22435)
### Fixed
- ESP32-S3 UART output mode for Tx (#22426)
- Mitsubishi Electric HVAC Standby Stage for MiElHVAC (#22430)
- FUNC_COMMAND linked list command buffer corruption by shutter driver
- ESP32, ESP32-S2 and ESP32-S3 re-enable touch buttons (#22446)
## [14.3.0.3] 20241031
### Added
- Support for I2C over Serial, preliminary stub (#22388)
### Changed
- ESP32 Platform from 2024.10.30 to 2024.11.30, Framework (Arduino Core) from v3.1.0.241023 to v3.1.0.241030 and IDF to 5.3.1.241024 (#22384)
- ESP32 LVGL library from v9.2.0 to v9.2.2 (#22385)
- Refactored `i2c_enabled` as array (#22387)
### Fixed
- ESP32 Arduino Core IPv6 zones used by Matter (#22378)
## [14.3.0.2] 20241030
### Added
- DALI command `DaliGear` to set max found gear to speed up scan response
- DALI command `DaliGroup` to add gear to groups
- DALI command `DaliTarget` to set light control broadcast, group number or gear number
- Mitsubishi Electric HVAC Operation time for MiElHVAC (#22334)
- Mitsubishi Electric HVAC Outdoor Temperature for MiElHVAC (#22345)
- Mitsubishi Electric HVAC Compressor Frequency for MiElHVAC (#22347)
- SolaxX1 Meter mode (#22330)
- DALI inverted signal configuration using GPIO DALI RX_i/TX_i
- Support for Shelly DALI Dimmer Gen3 (See tips and template in file xdrv_75_dali.ino)
- HASPmota `haspmota.get_pages()` to get the sorted list of pages (#22358)
- Support for US AQI and EPA AQI in PMS5003x sensors (#22294)
- HLK-LD2410 Engineering mode (#21880)
- Support for HLK-LD2410S 24GHz smart wave motion sensor (#22253)
- Mitsubishi Electric HVAC Auto Clear Remote Temp for MiElHVAC (#22370)
- Command ``SetOption161 1`` to disable web page slider updates by commands
### Changed
- DALI renamed commands `DaliCommission` to `DaliScan` and `DaliWeb` to `DaliLight`
- DALI set Tasmota light control as default
- ESP32 Framework (Arduino Core) from v3.1.0.241015 to v3.1.0.241023 (#22351)
- Shutter optimized behavior to publish shutter data with sensor request (#22353)
### Fixed
- Ethernet on -DFRAMEWORK_ARDUINO_ITEAD framework regression from v14.3.0 (#22367)
- Alexa Hue with multiple devices (#22383)
### Removed
- DALI inverted signal configuration using compile time defines
## [14.3.0.1] 20241022
### Added
- BLE track devices with RPA (#22300)
- DALI support for short addresses and groups
### Changed
- ESP32 platform update from 2024.09.30 to 2024.10.30 and Framework (Arduino Core) from v3.1.0.240926 to v3.1.0.241015 (#22299)
- HASPmota support for page delete and object updates (#22311)
### Fixed
- EQ3 TRV firmware version 1.46 fails if the default true is used in subscribe on the notify characteristic (#22328)
## [14.3.0] 20241015
- Release Robert
@ -16,11 +157,11 @@ All notable changes to this project will be documented in this file.
- Command ``DaliSend <address>|<address+256>,<command>`` to send command (address+256 is repeat) on DALI bus
- Command ``DaliQuery <address>|<address+256>,<command>`` to send command (address+256 is repeat) on DALI bus and wait up to DALI_TIMEOUT ms for response
- Berry Serial `config` to change parity on-the-fly for RS-485 (#22285)
- Misubishi Electric HVAC Heat/Dry/Cool Auto operation mode (#22216)
- Misubishi Electric HVAC Bridge to HomeBridge/Homekit locally (#22236)
- Misubishi Electric HVAC Air Direction Control (#22241)
- Misubishi Electric HVAC prohibit function (#22269)
- Misubishi Electric HVAC compressor map and operation power and energy (#22290)
- Mitsubishi Electric HVAC Heat/Dry/Cool Auto operation mode (#22216)
- Mitsubishi Electric HVAC Bridge to HomeBridge/Homekit locally (#22236)
- Mitsubishi Electric HVAC Air Direction Control (#22241)
- Mitsubishi Electric HVAC prohibit function (#22269)
- Mitsubishi Electric HVAC compressor map and operation power and energy (#22290)
### Changed
- ESP32 platform update from 2024.09.10 to 2024.09.30 and Framework (Arduino Core) from v3.0.5 to v3.1.0.240926 (#22203)

View File

@ -14,7 +14,7 @@ If you like **Tasmota**, give it a star, or fork it and contribute!
[![GitHub forks](https://img.shields.io/github/forks/arendst/Tasmota.svg?style=social&label=Fork)](https://github.com/arendst/Tasmota/network)
[![donate](https://img.shields.io/badge/donate-PayPal-blue.svg)](https://paypal.me/tasmota)
See [CHANGELOG.md](https://github.com/arendst/Tasmota/blob/development/tasmota/CHANGELOG.md) for changes since last release.
See [CHANGELOG.md](https://github.com/arendst/Tasmota/blob/development/CHANGELOG.md) for changes since last release.
## Development

View File

@ -128,5 +128,6 @@ Index | Define | Driver | Device | Address(es) | Bus2 | Descrip
88 | USE_QMP6988 | xsns_28 | QMP6988 | 0x56, 0x70 | Yes | Pressure and temperature sensor
89 | USE_HX711_M5SCALES | xsns_34 | M5SCALES | 0x26 | Yes | M5Unit (Mini)Scales(HX711 STM32) U177
90 | USE_RX8010 | xdrv_56 | RX8010 | 0x32 | Yes | RX8010 RTC from IOTTIMER
91 | USE_MS5837 | xsns_116 | MS5837 | 0x76 | | Pressure and temperature sensor
NOTE: Bus2 supported on ESP32 only.

View File

@ -36,9 +36,9 @@ While fallback or downgrading is common practice it was never supported due to S
This release will be supported from ESP8266/Arduino library Core version **2.7.8** due to reported security and stability issues on previous Core version. This will also support gzipped binaries.
This release will be supported from ESP32/Arduino library Core version **v3.1.0.240926**.
This release will be supported from ESP32/Arduino library Core version **v3.1.0.241206**.
Support of ESP8266 Core versions before 2.7.8 and ESP32 Core versions before v3.1.0.240926 have been removed.
Support of ESP8266 Core versions before 2.7.8 and ESP32 Core versions before v3.1.0.241206 have been removed.
## Support of TLS
@ -75,12 +75,12 @@ Latest released binaries can be downloaded from
- http://ota.tasmota.com/tasmota/release
Historical binaries can be downloaded from
- http://ota.tasmota.com/tasmota/release-14.3.0
- http://ota.tasmota.com/tasmota/release-14.4.0
The latter links can be used for OTA upgrades too like ``OtaUrl http://ota.tasmota.com/tasmota/release/tasmota.bin.gz``
### ESP32, ESP32-C2, ESP32-C3, ESP32-C6, ESP32-S2 and ESP32-S3 based
The following binary downloads have been compiled with ESP32/Arduino library core version **v3.1.0.240926**.
The following binary downloads have been compiled with ESP32/Arduino library core version **v3.1.0.241206**.
- **tasmota32.bin** = The Tasmota version with most drivers including additional sensors and KNX for 4M+ flash. **RECOMMENDED RELEASE BINARY**
- **tasmota32solo1.bin** = The Tasmota version with most drivers including additional sensors and KNX for single core ESP32 and 4M+ flash.
@ -104,7 +104,7 @@ Latest released binaries can be downloaded from
- https://ota.tasmota.com/tasmota32/release
Historical binaries can be downloaded from
- https://ota.tasmota.com/tasmota32/release-14.3.0
- https://ota.tasmota.com/tasmota32/release-14.4.0
The latter links can be used for OTA upgrades too like ``OtaUrl https://ota.tasmota.com/tasmota32/release/tasmota32.bin``
@ -114,96 +114,83 @@ The latter links can be used for OTA upgrades too like ``OtaUrl https://ota.tasm
[Complete list](BUILDS.md) of available feature and sensors.
## Changelog v14.3.0 Robert
## Changelog v14.4.0 Rudolph
### Added
- Command ``SetOption69 1`` to enable Serial Bridge inverted Receive [#22000](https://github.com/arendst/Tasmota/issues/22000)
- Command ``DaliWeb 1`` to enable light control for DALI broadcast address
- Command ``DaliSend <address>|<address+256>,<command>`` to send command (address+256 is repeat) on DALI bus
- Command ``DaliQuery <address>|<address+256>,<command>`` to send command (address+256 is repeat) on DALI bus and wait up to DALI_TIMEOUT ms for response
- HX711 optional calibration precision option on command ``Sensor34 2 <weight in gram> <precision>`` where `<precision>` is 1 to 20 [#13983](https://github.com/arendst/Tasmota/issues/13983)
- ESP8266 support for one-wire M1601 temperature sensor on DS18x20 GPIO [#21376](https://github.com/arendst/Tasmota/issues/21376)
- ESP8266 support for I2C CLK on GPIO16 [#22199](https://github.com/arendst/Tasmota/issues/22199)
- Support for I2C M5Unit (Mini)Scales using HX711 driver
- Support for DALI on ESP8266
- Support for RX8010 RTC as used in IOTTIMER [#21376](https://github.com/arendst/Tasmota/issues/21376)
- Support for BL0906 up to 6 channel energy monitor as used in Athom EM2/EM6 [#22167](https://github.com/arendst/Tasmota/issues/22167)
- Support for Sonoff SPM v1.3.0 [#13447](https://github.com/arendst/Tasmota/issues/13447)
- Energy command ``PowerSet 60,230`` to calibrate both Current and Power with known resistive load of 60W at 230V using calibrated Voltage
- Energy command ``CurrentSet 60,230`` to calibrate both Power and Current with known resistive load of 60W at 230V using calibrated Voltage
- ESP8266 experimental support for second I2C bus
- MQTT warning if trying to connect without TLS on a port that normally uses TLS [#22175](https://github.com/arendst/Tasmota/issues/22175)
- Energy Log level 4 message when (Calculated) Apparent Power is less than Active Power indicating wrong calibration [#20653](https://github.com/arendst/Tasmota/issues/20653)
- Support nexus protocol and calculation of separation limit to rc-switch library [#21886](https://github.com/arendst/Tasmota/issues/21886)
- KNX additional KnxTx functions and define KNX_USE_DPT9 [#22071](https://github.com/arendst/Tasmota/issues/22071)
- SML multi TRX line [#22056](https://github.com/arendst/Tasmota/issues/22056)
- Misubishi Electric HVAC Heat/Dry/Cool Auto operation mode [#22216](https://github.com/arendst/Tasmota/issues/22216)
- Misubishi Electric HVAC Bridge to HomeBridge/Homekit locally [#22236](https://github.com/arendst/Tasmota/issues/22236)
- Misubishi Electric HVAC Air Direction Control [#22241](https://github.com/arendst/Tasmota/issues/22241)
- Misubishi Electric HVAC prohibit function [#22269](https://github.com/arendst/Tasmota/issues/22269)
- Misubishi Electric HVAC compressor map and operation power and energy [#22290](https://github.com/arendst/Tasmota/issues/22290)
- Zigbee Koenkk firmware 20240710 for Sonoff Zigbee ZBPro [#22076](https://github.com/arendst/Tasmota/issues/22076)
- Berry Zigbee improvements to prepare Matter [#22083](https://github.com/arendst/Tasmota/issues/22083)
- Berry virtual Energy driver [#22134](https://github.com/arendst/Tasmota/issues/22134)
- Berry improve `int64` constructor [#22172](https://github.com/arendst/Tasmota/issues/22172)
- Berry Serial `config` to change parity on-the-fly for RS-485 [#22285](https://github.com/arendst/Tasmota/issues/22285)
- LVGL port `colorwheel` from LVGL 8 [#22244](https://github.com/arendst/Tasmota/issues/22244)
- HASPmota `cpicker` and `msgbox` [#22244](https://github.com/arendst/Tasmota/issues/22244)
- Matter support for Zigbee Temperature, Humidity and Pressure sensors [#22084](https://github.com/arendst/Tasmota/issues/22084)
- Matter support for Zigbee Occupancy and Light 0/1/2 (OnOff / Dimmer / White Color Temperature) [#22110](https://github.com/arendst/Tasmota/issues/22110)
- Command `WebColor20` to control color of Button when Off
- Command `SetOption161 1` to disable display of state text [#22515](https://github.com/arendst/Tasmota/issues/22515)
- Command `SetOption162 1` to disable adding export energy to energy today [#22578](https://github.com/arendst/Tasmota/issues/22578)
- DALI support for short addresses (gear) and groups
- DALI command `DaliGear` to set max found gear to speed up scan response
- DALI command `DaliGroup` to add gear to groups
- DALI command `DaliTarget` to set light control broadcast, group number or gear number
- DALI command `DaliGroupSliders 0..16` to show GUI group sliders with feedback disabling `DaliLight`
- DALI inverted signal configuration using GPIO DALI RX_i/TX_i
- Support for I2C over Serial [#22444](https://github.com/arendst/Tasmota/issues/22444)
- Support KNX for scripts [#22429](https://github.com/arendst/Tasmota/issues/22429)
- Support deep sleep (standby) for VL53L0X [#22441](https://github.com/arendst/Tasmota/issues/22441)
- Support for Shelly DALI Dimmer Gen3
- Support for HLK-LD2410S 24GHz smart wave motion sensor [#22253](https://github.com/arendst/Tasmota/issues/22253)
- Support for US AQI and EPA AQI in PMS5003x sensors [#22294](https://github.com/arendst/Tasmota/issues/22294)
- Support for MS5837 pressure and temperature sensor [#22376](https://github.com/arendst/Tasmota/issues/22376)
- Support for TM1640 based IoTTimer by Stefan Oskamp [#21376](https://github.com/arendst/Tasmota/issues/21376)
- Support for Sonoff POWCT Energy Export Active [#22596](https://github.com/arendst/Tasmota/issues/22596)
- HLK-LD2410 Engineering mode [#21880](https://github.com/arendst/Tasmota/issues/21880)
- Mitsubishi Electric HVAC Operation time for MiElHVAC [#22334](https://github.com/arendst/Tasmota/issues/22334)
- Mitsubishi Electric HVAC Outdoor Temperature for MiElHVAC [#22345](https://github.com/arendst/Tasmota/issues/22345)
- Mitsubishi Electric HVAC Compressor Frequency for MiElHVAC [#22347](https://github.com/arendst/Tasmota/issues/22347)
- Mitsubishi Electric HVAC Auto Clear Remote Temp for MiElHVAC [#22370](https://github.com/arendst/Tasmota/issues/22370)
- SolaxX1 Meter mode [#22330](https://github.com/arendst/Tasmota/issues/22330)
- Show Active Power Total with any multi-phase energy monitoring [#22579](https://github.com/arendst/Tasmota/issues/22579)
- ESP32 support for WPA2/3 Enterprise conditional in core v3.1.0.241206 [#22600](https://github.com/arendst/Tasmota/issues/22600)
- ESP32 ULP lp_core to Berry ULP module (#22567)[#22567](https://github.com/arendst/Tasmota/issues/22567)
- ESP32 new BLE filters by name and minimum RSSI [#22530](https://github.com/arendst/Tasmota/issues/22530)
- ESP32 Hybrid compile take custom boards settings in account [#22542](https://github.com/arendst/Tasmota/issues/22542)
- ESP32 MI32 legacy add config operations [#22458](https://github.com/arendst/Tasmota/issues/22458)
- BLE track devices with RPA [#22300](https://github.com/arendst/Tasmota/issues/22300)
- Berry add I2C read16/write16 supporting Little Endian [#22448](https://github.com/arendst/Tasmota/issues/22448)
- Berry drivers for PCA9535 (generic and in SenseCAP D1) [#22451](https://github.com/arendst/Tasmota/issues/22451)
- HASPmota `haspmota.get_pages()` to get the sorted list of pages [#22358](https://github.com/arendst/Tasmota/issues/22358)
### Breaking Changed
- Berry make `energy` modules changes from #21887 backwards compatible [#22046](https://github.com/arendst/Tasmota/issues/22046)
- ESP32 ArtNet switches from GRB to RGB encoding [#22556](https://github.com/arendst/Tasmota/issues/22556)
### Changed
- ESP8266 platform update from 2024.06.00 to 2024.09.00 and Framework (Arduino Core) from v2.7.7 to v2.7.8 [#22199](https://github.com/arendst/Tasmota/issues/22199)
- ESP32 platform update from 2024.08.10 to 2024.09.30 and Framework (Arduino Core) from v3.0.4 to v3.1.0.240926 [#22203](https://github.com/arendst/Tasmota/issues/22203)
- ESP32 LVGL library from v9.1.0 to v9.2.0 [#22031](https://github.com/arendst/Tasmota/issues/22031)
- GPIOViewer from v1.5.5 to v1.5.6
- Add command entered to command error and command unknown message
- Refactored I2C drivers HTU21, BH1750, SHT3x, iAQ and HYT
- Energy BL09xx command ``CurrentSet`` input changed from Ampere to milliAmpere
- Command ``DaliDimmer`` range from 0..254 to 0..100
- Energy force Apparent Power equals Active Power when (Calculated) Apparent Power is less than Active Power [#20653](https://github.com/arendst/Tasmota/issues/20653)
- Refactor and fix PID sensor (PID_USE_LOCAL_SENSOR) read race condition [#22162](https://github.com/arendst/Tasmota/issues/22162)
- SCD30 Lowered I2C clock from 100k to 50k [#15438](https://github.com/arendst/Tasmota/issues/15438)
- HASPmota `delete` instead of `delete()` [#22245](https://github.com/arendst/Tasmota/issues/22245)
- ESP32 Platform from 2024.09.30 to 2024.12.30, Framework (Arduino Core) from v3.1.0.240926 to v3.1.0.241206 and IDF to 5.3.2 [#22600](https://github.com/arendst/Tasmota/issues/22600)
- ESP32 LVGL library from v9.2.0 to v9.2.2 [#22385](https://github.com/arendst/Tasmota/issues/22385)
- ESP32 replaced NeoPixelBus with TasmotaLED [#22556](https://github.com/arendst/Tasmota/issues/22556)
- Redesign GUI adding feedback to buttons, shutters and lights
- Add GUI submenu headers and refresh configuration button text (#22592)
- Use command `WebButton1` to change GUI shutter 1 name
- Unit (k)VAr(h) to (k)var(h) [#22435](https://github.com/arendst/Tasmota/issues/22435)
- AHT1X/AHT2X/AHT3X ready for virtual I2C [#22427](https://github.com/arendst/Tasmota/issues/22427)
- SGP4X ready for virtual I2C [#22427](https://github.com/arendst/Tasmota/issues/22427)
- SCD40 reduce logging levels [#22443](https://github.com/arendst/Tasmota/issues/22443)
- SCD40 ready for virtual I2C [#22443](https://github.com/arendst/Tasmota/issues/22443)
- Refactored `i2c_enabled` as array [#22387](https://github.com/arendst/Tasmota/issues/22387)
- DALI renamed commands `DaliCommission` to `DaliScan` and `DaliWeb` to `DaliLight`
- DALI set Tasmota light control as default
- Shutter optimized behavior to publish shutter data with sensor request [#22353](https://github.com/arendst/Tasmota/issues/22353)
- Prevent active BLE operations with unencrypted MI-format beacons [#22453](https://github.com/arendst/Tasmota/issues/22453)
- ESP32 max number of supported switches/buttons/relays from 28 to 32
- ESP32 max number of interlocks from 14 to 16
- HASPmota support for page delete and object updates [#22311](https://github.com/arendst/Tasmota/issues/22311)
### Fixed
- DALI received data decoding
- Compilation exception when metrics not found [#22170](https://github.com/arendst/Tasmota/issues/22170)
- Crash when calling TasmotaSerial destructor when initialized with incorrect arguments [#22036](https://github.com/arendst/Tasmota/issues/22036)
- Energy calculation [#20653](https://github.com/arendst/Tasmota/issues/20653)
- SML trx pin error [#22119](https://github.com/arendst/Tasmota/issues/22119)
- Shutter timing registers overflow [#21966](https://github.com/arendst/Tasmota/issues/21966)
- Shutter missing HOLD on shutterbutton [#22108](https://github.com/arendst/Tasmota/issues/22108)
- Shutter remaining issues on shutterinvert [#22120](https://github.com/arendst/Tasmota/issues/22120)
- PZEM continue energy monitoring when one phase fails [#21968](https://github.com/arendst/Tasmota/issues/21968)
- BearSSL panic on ESP8266 in rare conditions [#22017](https://github.com/arendst/Tasmota/issues/22017)
- ModbusBridge request and response logic [#22075](https://github.com/arendst/Tasmota/issues/22075)
- Sonoff WTS01 temperature sensor shows incorrect negative temperature [#19373](https://github.com/arendst/Tasmota/issues/19373)
- Autoconf prevent 'init.bat' from stopping on empty lines [#22158](https://github.com/arendst/Tasmota/issues/22158)
- Zigbee extend timeout for MCU reboot from 5s to 10s [#22009](https://github.com/arendst/Tasmota/issues/22009)
- Zigbee avoid disabling console serial on ESP32 and improved log messages [#22082](https://github.com/arendst/Tasmota/issues/22082)
- Zigbee flashing CC2562P with latest firmware [#22117](https://github.com/arendst/Tasmota/issues/22117)
- ESP32 Range Extender compile error with core 3.x [#22205](https://github.com/arendst/Tasmota/issues/22205)
- ESP32 DALI compile error with core 3.x [#22214](https://github.com/arendst/Tasmota/issues/22214)
- ESP32 Ethernet using EthClockMode 3 [#22248](https://github.com/arendst/Tasmota/issues/22248)
- ESP32 disable SPI DMA for uDisplay (broken since esp-idf 5.3 (core 3.1.0)) [#22264](https://github.com/arendst/Tasmota/issues/22264)
- Berry avoid `readbytes()` from crashing when file is too large [#22057](https://github.com/arendst/Tasmota/issues/22057)
- Berry energy missing attributes [#22116](https://github.com/arendst/Tasmota/issues/22116)
- Berry I2C to prepare M5Stack I2C STM32 based devices [#22143](https://github.com/arendst/Tasmota/issues/22143)
- Berry improve `persist` dirty data handling [#22246](https://github.com/arendst/Tasmota/issues/22246)
- ESP32-S3 uDisplay force cache writes to RGB display [#22222](https://github.com/arendst/Tasmota/issues/22222)
- LVGL Added OpenHASP icons to font `montserrat-28` [#22048](https://github.com/arendst/Tasmota/issues/22048)
- LVGL compilation of lv_menu [#22188](https://github.com/arendst/Tasmota/issues/22188)
- HASPmota broken `changed` event [#22194](https://github.com/arendst/Tasmota/issues/22194)
- HASPmota error when page '1' is not defined [#22220](https://github.com/arendst/Tasmota/issues/22220)
- Matter fixed UI bug when no endpoints configured [#22008](https://github.com/arendst/Tasmota/issues/22008)
- Matter fix when Rules are disabled [#22016](https://github.com/arendst/Tasmota/issues/22016)
- Matter fail to report Shutter status if no shutter is configured in Tasmota [#22049](https://github.com/arendst/Tasmota/issues/22049)
- Matter fix Waterleak broken after Berry solidification optimisation #21885 [#22052](https://github.com/arendst/Tasmota/issues/22052)
- FUNC_COMMAND linked list command buffer corruption by shutter driver
- Shift595 output offsets and restart relay toggles
- Use HTML escape on File System Edit File load [#22492](https://github.com/arendst/Tasmota/issues/22492)
- Prevent crashing when `display.ini` is missing end `#` [#22471](https://github.com/arendst/Tasmota/issues/22471)
- KNX Scenes index change regression from v14.2.0.4 [#22405](https://github.com/arendst/Tasmota/issues/22405)
- Magic switch applying masking window to any power change [#22535](https://github.com/arendst/Tasmota/issues/22535)
- Shutter wrong power ON state [#22548](https://github.com/arendst/Tasmota/issues/22548)
- Alexa Hue with multiple devices [#22383](https://github.com/arendst/Tasmota/issues/22383)
- Mitsubishi Electric HVAC Standby Stage for MiElHVAC [#22430](https://github.com/arendst/Tasmota/issues/22430)
- EQ3 TRV firmware version 1.46 fails if the default true is used in subscribe on the notify characteristic [#22328](https://github.com/arendst/Tasmota/issues/22328)
- Ethernet on -DFRAMEWORK_ARDUINO_ITEAD framework regression from v14.3.0 [#22367](https://github.com/arendst/Tasmota/issues/22367)
- ESP32 Upgrade by file upload response based on file size [#22500](https://github.com/arendst/Tasmota/issues/22500)
- ESP32 Arduino Core IPv6 zones used by Matter [#22378](https://github.com/arendst/Tasmota/issues/22378)
- ESP32, ESP32-S2 and ESP32-S3 re-enable touch buttons [#22446](https://github.com/arendst/Tasmota/issues/22446)
- ESP32-S3 UART output mode for Tx [#22426](https://github.com/arendst/Tasmota/issues/22426)
- Matter provisioning with matter.js controller [#22470](https://github.com/arendst/Tasmota/issues/22470)
### Removed
- ESP8266 Analog input support using energy driver as only one channel is available
- Berry remove reuse of methods for interface-like code reuse #21500 [#22055](https://github.com/arendst/Tasmota/issues/22055)
- Berry Zigbee removed test code [#22263](https://github.com/arendst/Tasmota/issues/22263)

View File

@ -5,7 +5,7 @@
# Templates
Find below the available templates as of October 2024. More template information can be found in the [Tasmota Device Templates Repository](http://blakadder.github.io/templates)
Find below the available templates as of December 2024. More template information can be found in the [Tasmota Device Templates Repository](http://blakadder.github.io/templates)
## Adapter Board
```

View File

@ -59,7 +59,7 @@ typedef enum WiFiPhyMode
class WiFiHelper {
public:
#ifdef ESP32
static wl_status_t begin(const char* wpa2_ssid, wpa2_auth_method_t method, const char* wpa2_identity=NULL, const char* wpa2_username=NULL, const char *wpa2_password=NULL, const char* ca_pem=NULL, const char* client_crt=NULL, const char* client_key=NULL, int32_t channel=0, const uint8_t* bssid=0, bool connect=true);
static wl_status_t begin(const char* wpa2_ssid, wpa2_auth_method_t method, const char* wpa2_identity=NULL, const char* wpa2_username=NULL, const char *wpa2_password=NULL, const char* ca_pem=NULL, const char* client_crt=NULL, const char* client_key=NULL, int ttls_phase2_type=-1, int32_t channel=0, const uint8_t* bssid=0, bool connect=true);
#endif
static wl_status_t begin(const char* ssid, const char *passphrase = NULL, int32_t channel = 0, const uint8_t* bssid = NULL, bool connect = true);
static wl_status_t begin(char* ssid, char *passphrase = NULL, int32_t channel = 0, const uint8_t* bssid = NULL, bool connect = true);
@ -94,4 +94,4 @@ public:
#endif // WIFIHELPER_H
#endif // WIFIHELPER_H

View File

@ -34,12 +34,14 @@ ip_addr_t dns_save6[DNS_MAX_SERVERS] = {}; // IPv6 DNS servers
#include "tasmota_options.h"
#include "lwip/dns.h"
wl_status_t WiFiHelper::begin(const char* wpa2_ssid, wpa2_auth_method_t method, const char* wpa2_identity, const char* wpa2_username, const char *wpa2_password, const char* ca_pem, const char* client_crt, const char* client_key, int32_t channel, const uint8_t* bssid, bool connect) {
#if CONFIG_ESP_WIFI_ENTERPRISE_SUPPORT
wl_status_t WiFiHelper::begin(const char *wpa2_ssid, wpa2_auth_method_t method, const char *wpa2_identity, const char *wpa2_username, const char *wpa2_password, const char *ca_pem, const char *client_crt, const char *client_key, int ttls_phase2_type, int32_t channel, const uint8_t *bssid, bool connect) {
WiFiHelper::scrubDNS();
wl_status_t ret = WiFi.begin(wpa2_ssid, method, wpa2_identity, wpa2_username, wpa2_password, ca_pem, client_crt, client_key, channel, bssid, connect);
wl_status_t ret = WiFi.begin(wpa2_ssid, method, wpa2_identity, wpa2_username, wpa2_password, ca_pem, client_crt, client_key, ttls_phase2_type, channel, bssid, connect);
WiFiHelper::scrubDNS();
return ret;
}
#endif // CONFIG_ESP_WIFI_ENTERPRISE_SUPPORT
wl_status_t WiFiHelper::begin(const char* ssid, const char *passphrase, int32_t channel, const uint8_t* bssid, bool connect) {
WiFiHelper::scrubDNS();

View File

@ -58,6 +58,10 @@
#include "AudioGeneratorMIDI.h"
#if defined(ESP32)
// Do not build, Espressif's GCC8+ has a compiler bug
#else // __GNUC__ == 8
#pragma GCC optimize ("O3")
#define TSF_NO_STDIO
@ -637,3 +641,4 @@ void AudioGeneratorMIDI::MakeStreamFromAFS(AudioFileSource *src, tsf_stream *afs
afs->size = &afs_size;
}
#endif //__GNUC__ == 8

View File

@ -21,6 +21,10 @@
#ifndef _AUDIOGENERATORMIDI_H
#define _AUDIOGENERATORMIDI_H
#if defined(ESP32)
// Do not build, Espressif's GCC8+ has a compiler bug
#else // __GNUC__ == 8
#include "AudioGenerator.h"
#define TSF_NO_STDIO
@ -90,7 +94,7 @@ class AudioGeneratorMIDI : public AudioGenerator
unsigned long earliest_time = 0;
struct tonegen_status { /* current status of a tone generator */
bool playing; /* is it playing? */
bool playing; /* is it playing? */
char track; /* if so, which track is the note from? */
char note; /* what note is playing? */
char instrument; /* what instrument? */
@ -176,6 +180,7 @@ class AudioGeneratorMIDI : public AudioGenerator
short samplesRendered[256];
};
#endif //__GNUC__ == 8
#endif

View File

@ -42,6 +42,16 @@
*/
// ESP32 as of 3.x has a compiler bug in this section, with the G++ generated assembly
// being illegal. There's nothing wrong with the code here, it just looks like an
// Xtensa backend issue. Until that's fixed, no MIDI for you!
///home/earle/Arduino/libraries/ESP8266Audio/src/libtinysoundfont/tsf.h: In function 'void tsf_channel_midi_control(tsf*, int, int, int)':
// /home/earle/Arduino/libraries/ESP8266Audio/src/libtinysoundfont/tsf.h:2101:1: error: insn does not satisfy its constraints:
// 2101 | }
// | ^
#if !defined(ESP32)
#ifndef TSF_INCLUDE_TSF_INL
#define TSF_INCLUDE_TSF_INL
@ -2149,3 +2159,5 @@ TSFDEF float tsf_channel_get_tuning(tsf* f, int channel)
#endif
#endif //TSF_IMPLEMENTATION
#endif // ! ESP32

View File

@ -69,7 +69,7 @@ typedef struct {
rmt_symbol_word_t reset_code;
} rmt_led_strip_encoder_t;
static size_t rmt_encode_led_strip(rmt_encoder_t *encoder, rmt_channel_handle_t channel, const void *primary_data, size_t data_size, rmt_encode_state_t *ret_state)
static IRAM_ATTR size_t rmt_encode_led_strip(rmt_encoder_t *encoder, rmt_channel_handle_t channel, const void *primary_data, size_t data_size, rmt_encode_state_t *ret_state)
{
rmt_led_strip_encoder_t *led_encoder = __containerof(encoder, rmt_led_strip_encoder_t, base);
rmt_encoder_handle_t bytes_encoder = led_encoder->bytes_encoder;

View File

@ -0,0 +1,17 @@
{
"name": "TasmotaLED",
"version": "0.1",
"keywords": [
"ws2816", "sk6812", "leds"
],
"description": "Lightweight implementation for adressable leds.",
"repository":
{
"type": "git",
"url": "https://github.com/arendst/Tasmota/lib/lib_basic/TasmotaLED"
},
"frameworks": "arduino",
"platforms": [
"espressif32"
]
}

View File

@ -0,0 +1,237 @@
/*
TasmotaLED.cpp - Lightweight implementation for adressable leds.
Copyright (C) 2024 Stephan Hadinger
This library 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 <Arduino.h>
#ifdef ESP32
#include "TasmotaLEDPusher.h"
#include "TasmotaLED.h"
// DRAM_ATTR to force in IRAM because we use this in show loop
static const DRAM_ATTR uint8_t TASMOTALED_CHANNEL_ORDERS[6][3] = {
{1, 0, 2}, // GRB (0)
{2, 0, 1}, // GBR (1)
{0, 1, 2}, // RGB (2)
{0, 2, 1}, // RBG (3)
{2, 1, 0}, // BRG (4)
{1, 2, 0} // BGR (5)
};
static const TasmotaLED_Timing TasmotaLED_Timings[] = {
// WS2812
// RmtBit0 0x00228010 RmtBit1 0x00128020 RmtReset 0x800207D0
{
.T0H = 400,
.T0L = 850,
.T1H = 800,
.T1L = 450,
.Reset = 80000 // it is 50000 for WS2812, but for compatibility with SK6812, we raise to 80000
},
// SK6812
// RmtBit0 0x0024800C RmtBit1 0x00188018 RmtReset 0x80020C80
{
.T0H = 300,
.T0L = 900,
.T1H = 600,
.T1L = 600,
.Reset = 80000
},
};
// enable AddLog
extern void AddLog(uint32_t loglevel, PGM_P formatP, ...);
enum LoggingLevels {LOG_LEVEL_NONE, LOG_LEVEL_ERROR, LOG_LEVEL_INFO, LOG_LEVEL_DEBUG, LOG_LEVEL_DEBUG_MORE};
TasmotaLED::TasmotaLED(uint16_t type, uint16_t num_leds) :
_type(type),
_pixel_order((type >> 4) & 0x07),
_w_before(type & 0x08),
_timing((type >> 8) & 0xFF),
_started(false),
_dirty(true),
_raw_format(false),
_pixel_count(num_leds),
_buf_work(nullptr),
_buf_show(nullptr),
_pixel_matrix(&TASMOTALED_CHANNEL_ORDERS[0]),
_pusher(nullptr)
{
if (_timing > (TasmotaLed_TimingEnd >> 8)) {
_timing = 0;
}
switch (_type & 0x0F) {
// case TasmotaLed_1_W:
// _pixel_size = 1;
// break;
case TasmotaLed_4_WRGB:
_pixel_size = 4;
break;
case TasmotaLed_3_RGB:
default: // fallback
_pixel_size = 3;
break;
}
_pixel_matrix = &TASMOTALED_CHANNEL_ORDERS[_pixel_order];
_buf_work = new uint8_t[_pixel_count * _pixel_size];
memset(_buf_work, 0, _pixel_count * _pixel_size);
_buf_show = new uint8_t[_pixel_count * _pixel_size];
memset(_buf_show, 0, _pixel_count * _pixel_size);
// AddLog(LOG_LEVEL_DEBUG, "LED: type=0x%04X pixel_order=0x%02X _timing=%i ", _type, _pixel_order, _timing);
}
TasmotaLED::~TasmotaLED() {
if (_pusher) {
delete _pusher;
_pusher = nullptr;
}
delete _buf_work;
_buf_work = nullptr;
delete _buf_show;
_buf_show = nullptr;
}
// Color is passed as 0xWWRRGGBB and copied as WWRRGGBB in _buf_work
void TasmotaLED::ClearTo(uint32_t wrgb, int32_t first, int32_t last) {
// adjust first and last to be in range of 0 to _pixel_count-1
if (first <0) { first += _pixel_count; }
if (last <0) { last += _pixel_count; }
if (first < 0) { first = 0; }
if (last >= _pixel_count) { last = _pixel_count - 1; }
if (first > last) { return; }
// adjust to pixel format
uint8_t b0 = (wrgb >> 24) & 0xFF;
uint8_t b1 = (wrgb >> 16) & 0xFF;
uint8_t b2 = (wrgb >> 8) & 0xFF;
uint8_t b3 = (wrgb ) & 0xFF;
if ((b0 | b1 | b2 | b3) == 0) {
// special version for clearing to black
memset(_buf_work + first * _pixel_size, 0, (last - first + 1) * _pixel_size);
} else {
// fill sub-buffer with RRGGBB or WWRRGGBB (or raw)
uint8_t *buf = _buf_work + first * _pixel_size;
for (uint32_t i = first; i <= last; i++) {
if (_pixel_size == 4) { *buf++ = b0;}
*buf++ = b1;
*buf++ = b2;
*buf++ = b3;
}
}
}
void TasmotaLED::Show(void) {
if (_pusher) {
_dirty = false; // we don't use the _dirty attribute and always show
// copy the input buffer to the work buffer in format to be understood by LED strip
if (_raw_format) {
memmove(_buf_show, _buf_work, _pixel_count * _pixel_size); // copy buffer in next buffer so we start with the current content
} else {
uint8_t *buf_from = _buf_work;
uint8_t *buf_to = _buf_show;
if (_pixel_size == 3) {
// copying with swapping 512 pixels (1536 bytes) takes 124 microseconds to copy, so it's negligeable
for (uint32_t i = 0; i < _pixel_count; i++) {
buf_to[(*_pixel_matrix)[0]] = buf_from[0]; // R
buf_to[(*_pixel_matrix)[1]] = buf_from[1]; // G
buf_to[(*_pixel_matrix)[2]] = buf_from[2]; // B
buf_to += 3;
buf_from += 3;
}
} else if (_pixel_size == 4) {
for (uint32_t i = 0; i < _pixel_count; i++) {
if (_w_before) { *buf_to++ = buf_from[3]; }
buf_to[(*_pixel_matrix)[0]] = buf_from[0]; // R
buf_to[(*_pixel_matrix)[1]] = buf_from[1]; // G
buf_to[(*_pixel_matrix)[2]] = buf_from[2]; // B
if (!_w_before) { *buf_to++ = buf_from[3]; }
buf_to += 3; // one increment already happened
buf_from += 4;
}
}
}
_pusher->Push(_buf_show); // push to leds
}
}
void TasmotaLED::SetPixelColor(int32_t index, uint32_t wrgb) {
if (index < 0) { index += _pixel_count; }
if ((index >= 0) && (index < _pixel_count)) {
uint8_t *buf = _buf_work + index * _pixel_size;
uint8_t b0 = (wrgb >> 24) & 0xFF;
uint8_t b1 = (wrgb >> 16) & 0xFF;
uint8_t b2 = (wrgb >> 8) & 0xFF;
uint8_t b3 = (wrgb ) & 0xFF;
if (_pixel_size == 4) { *buf++ = b0;}
*buf++ = b1;
*buf++ = b2;
*buf++ = b3;
_dirty = true;
}
}
uint32_t TasmotaLED::GetPixelColor(int32_t index) {
if (index < 0) { index += _pixel_count; }
if ((index >= 0) && (index < _pixel_count)) {
uint8_t *buf = _buf_work + index * _pixel_size;
uint32_t wrgb = 0;
if (_pixel_size == 4) { wrgb = (*buf++) << 24; }
wrgb |= (*buf++) << 16;
wrgb |= (*buf++) << 8;
wrgb |= (*buf++);
return wrgb;
} else {
return 0;
}
}
void TasmotaLED::SetPusher(TasmotaLEDPusher *pusher) {
if (_pusher) {
delete _pusher;
}
_pusher = pusher;
_started = false;
}
bool TasmotaLED::Begin(void) {
if (_pusher) {
if (_started) {
return true;
} else {
const TasmotaLED_Timing * timing = &TasmotaLED_Timings[_timing];
// AddLog(LOG_LEVEL_DEBUG, "LED: T0H=%i T0L=%i T1H=%i T1L=%i Reset=%i", timing.T0H, timing.T0L, timing.T1H, timing.T1L, timing.Reset);
return _pusher->Begin(_pixel_count, _pixel_size, timing);
}
} else {
return false;
}
}
bool TasmotaLED::CanShow(void) const {
if (_pusher) {
return _pusher->CanShow();
}
return false;
}
#endif // ESP32

View File

@ -0,0 +1,129 @@
/*
TasmotaLED.h - Lightweight implementation for adressable leds.
Copyright (C) 2024 Stephan Hadinger
This library 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 __TASMOTALED_H__
#define __TASMOTALED_H__
enum TasmotaLEDTypesEncoding : uint16_t {
// bits 0..3 encode for number of bytes per pixel
TasmotaLed_1_W = 0x0, // 1 byte per pixel (not used yet)
TasmotaLed_3_RGB = 0x1, // 3 bytes per pixel
TasmotaLed_4_WRGB = 0x2, // 4 bytes per pixel
// bits 4..6 encode for pixel order
TasmotaLed_GRB = 0b000 << 4,
TasmotaLed_GBR = 0b001 << 4,
TasmotaLed_RGB = 0b010 << 4,
TasmotaLed_RBG = 0b011 << 4,
TasmotaLed_BRG = 0b100 << 4,
TasmotaLed_BGR = 0b101 << 4,
// bit 7 sets the position for W channel
TasmotaLed_xxxW = 0b0 << 7, // W channel after color
TasmotaLed_Wxxx = 0b1 << 7, // W channel before color
// bits 8..15 encode for timing specifics
TasmotaLed_WS2812 = 0 << 8,
TasmotaLed_SK6812 = 1 << 8,
TasmotaLed_TimingEnd = 2 << 8,
};
enum TasmotaLEDHardware : uint32_t {
// low-order bits are reserved for channels numbers and hardware flags - currenlty not useds
// bits 16..23
TasmotaLed_HW_Default = 0x0 << 16,
TasmotaLed_RMT = 1 << 16,
TasmotaLed_SPI = 2 << 16,
TasmotaLed_I2S = 3 << 16,
TasmotaLed_HW_None = 0xFF << 16, // indicates that the specified HW is not supported
};
// Below is the encoding for full strips
// We need to keep backwards compatibility so:
// 0 = WS2812 (GRB)
// 1 = SK6812 with White (GRBW)
enum TasmotaLEDTypes : uint16_t {
ws2812_grb = TasmotaLed_3_RGB | TasmotaLed_GRB | TasmotaLed_WS2812, // 1 for backwards compatibility
sk6812_grbw = TasmotaLed_4_WRGB | TasmotaLed_GRB | TasmotaLed_xxxW | TasmotaLed_SK6812, // 2 for backwards compatibility
sk6812_grb = TasmotaLed_3_RGB | TasmotaLed_GRB | TasmotaLed_SK6812,
};
#ifdef __cplusplus
/*******************************************************************************************\
* class TasmotaLED
*
* This class is a lightweight replacement for NeoPixelBus library with a smaller
* implementation focusing only on pushing a buffer to the leds.
*
* It supports:
* - RMT and I2S hardware support
* Possible enhancements could be considered with SPI and Serial
* - Led size of 3 bytes (GRB) and 4 bytes (GRBW)
* APIs take 0xRRGGBB or 0xRRGGBBWW as input
* but Internal buffers use GGRRBB and GGRRBBWW
* - Led type of WS2812 and SK6812
* - There is no buffer swapping, the working buffer is copied to an internal
* buffer just before display, so you can keep a reference to the buffer
* and modify it without having to worry about the display
* - buffer is cleared at start
* - "Dirty" is kept for API compatibility with NeoPixelBus but is glbally ignored
* so any call to `Show()` pushes the pixels even if they haven't changed.
* Control for dirty pixels should be done by the caller if required.
* - We tried to keep as close as possible to NeoPixelBus method names to ease transition
\*******************************************************************************************/
class TasmotaLEDPusher; // forward definition
class TasmotaLED {
public:
TasmotaLED(uint16_t type, uint16_t num_leds);
~TasmotaLED();
bool Begin(void);
void SetPusher(TasmotaLEDPusher *pusher); // needs to be called before `Begin()`, sets the hardware implementation
void Show(void); // pushes the pixels to the LED strip
inline void SetRawFormat(bool raw_format) { _raw_format = raw_format; }
void ClearTo(uint32_t rgbw, int32_t first = 0, int32_t last = -1);
void SetPixelColor(int32_t index, uint32_t wrgb);
uint32_t GetPixelColor(int32_t index);
uint8_t GetType(void) const { return _type; }
uint16_t PixelCount(void) const { return _pixel_count; }
uint8_t PixelSize(void) const { return _pixel_size; }
inline uint8_t * Pixels(void) const { return _buf_work; }
inline bool IsDirty(void) const { return _dirty; }
inline void Dirty(void) { _dirty = true; }
bool CanShow(void) const;
protected:
uint16_t _type; // the composite type
uint8_t _pixel_order; // permutation between RGB and position of W
bool _w_before; // true if W channel comes first (4 channels only)
uint8_t _timing; // timing code for strip, 0=WS2812, 1=SK6812...
bool _started; // true if the hardware implementation is configured
bool _dirty; // for NeoPixelBus compatibility, but ignored by `Push()`
bool _raw_format; // if true, copy raw to leds, if false, convert from RGB to GRB or LED format
uint16_t _pixel_count; // how many pixels in the strip
uint8_t _pixel_size; // how many bytes per pixels, only 3 and 4 are supported
uint8_t *_buf_work; // buffer used to draw into, can be modified directly by the caller
uint8_t *_buf_show; // copy of the buffer used to push to leds, private to this class
const uint8_t (*_pixel_matrix)[3]; // pointer to the pixer_order_matrix
TasmotaLEDPusher *_pusher; // pixels pusher implementation based on hardware (RMT, I2S...)
};
#endif // __cplusplus
#endif // __TASMOTALED_H__

View File

@ -0,0 +1,97 @@
/*
TasmotaLEDPusher.cpp - Implementation to push Leds via hardware acceleration
Copyright (C) 2024 Stephan Hadinger
This library 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/>.
*/
#ifdef ESP32
#include "TasmotaLEDPusher.h"
#include "TasmotaLED.h"
//**************************************************************************************************************
// enable AddLog support within a C++ library
extern void AddLog(uint32_t loglevel, PGM_P formatP, ...);
enum LoggingLevels {LOG_LEVEL_NONE, LOG_LEVEL_ERROR, LOG_LEVEL_INFO, LOG_LEVEL_DEBUG, LOG_LEVEL_DEBUG_MORE};
//**************************************************************************************************************
// convert to the appropriate hardware acceleration based on capacities of the SOC
uint32_t TasmotaLEDPusher::ResolveHardware(uint32_t hw) {
uint32_t hw_orig = hw;
// Step 1. discard any unsupported hardware, and replace with TasmotaLed_HW_Default
uint32_t hw_type = hw & 0xFF0000; // discard bits 0..15
#if !TASMOTALED_HARDWARE_RMT
if (hw_type == TasmotaLed_RMT) {
hw = TasmotaLed_HW_None;
}
#endif // TASMOTALED_HARDWARE_RMT
#if !TASMOTALED_HARDWARE_SPI
if (hw_type == TasmotaLed_SPI) {
hw = TasmotaLed_HW_None;
}
#endif // TASMOTALED_HARDWARE_SPI
#if !TASMOTALED_HARDWARE_I2S
if (hw_type == TasmotaLed_I2S) {
hw = TasmotaLed_HW_None;
}
#endif // TASMOTALED_HARDWARE_I2S
// Step 2. If TasmotaLed_HW_Default, find a suitable scheme, RMT preferred
#if TASMOTALED_HARDWARE_RMT
if ((hw & 0xFF0000) == TasmotaLed_HW_Default) {
hw = TasmotaLed_RMT;
}
#endif // TASMOTALED_HARDWARE_RMT
#if TASMOTALED_HARDWARE_I2S
if ((hw & 0xFF0000) == TasmotaLed_HW_Default) {
hw = TasmotaLed_I2S;
}
#endif // TASMOTALED_HARDWARE_I2S
#if TASMOTALED_HARDWARE_SPI
if ((hw & 0xFF0000) == TasmotaLed_HW_Default) {
hw = TasmotaLed_SPI;
}
#endif // TASMOTALED_HARDWARE_SPI
return hw;
}
TasmotaLEDPusher * TasmotaLEDPusher::Create(uint32_t hw, int8_t gpio) {
TasmotaLEDPusher * pusher = nullptr;
hw = TasmotaLEDPusher::ResolveHardware(hw);
switch (hw & 0XFF0000) {
#if TASMOTALED_HARDWARE_RMT
case TasmotaLed_RMT:
pusher = new TasmotaLEDPusherRMT(gpio);
AddLog(LOG_LEVEL_DEBUG, "LED: RMT gpio %i", gpio);
break;
#endif // TASMOTALED_HARDWARE_RMT
#if TASMOTALED_HARDWARE_SPI
case TasmotaLed_SPI:
pusher = new TasmotaLEDPusherSPI(gpio);
AddLog(LOG_LEVEL_DEBUG, "LED: SPI gpio %i", gpio);
break;
#endif // TASMOTALED_HARDWARE_SPI
default:
break;
}
return pusher;
}
#endif // ESP32

View File

@ -0,0 +1,161 @@
/*
TasmotaLEDPusher.h - Abstract class for Leds pusher (RMT, SPI, I2S...)
Copyright (C) 2024 Stephan Hadinger
This library 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 __TASMOTALEDPUSHER_H__
#define __TASMOTALEDPUSHER_H__
#include <Arduino.h>
// Below are flags to enable of disable each hardware support: RMT, I2S, SPI
// By default, only enable RMT support, and SPI is used as fallback if no protocol works
//
// Use de defines below:
// #define TASMOTALED_HARDWARE_RMT 0/1
// #define TASMOTALED_HARDWARE_I2S 0/1
// #define TASMOTALED_HARDWARE_SPI 0/1
//
#ifndef TASMOTALED_HARDWARE_RMT
#define TASMOTALED_HARDWARE_RMT 1
#endif
#ifndef TASMOTALED_HARDWARE_I2S
#define TASMOTALED_HARDWARE_I2S 0
#endif
#ifndef TASMOTALED_HARDWARE_SPI
#define TASMOTALED_HARDWARE_SPI 0
#endif
// Disable any hardware if not supported by the SOC
#if TASMOTALED_HARDWARE_RMT && !defined(SOC_RMT_SUPPORTED)
#undef TASMOTALED_HARDWARE_RMT
#define TASMOTALED_HARDWARE_RMT 0
#endif
#if TASMOTALED_HARDWARE_I2S && !defined(SOC_I2S_SUPPORTED)
#undef TASMOTALED_HARDWARE_I2S
#define TASMOTALED_HARDWARE_I2S 0
#endif
#if TASMOTALED_HARDWARE_SPI && !defined(SOC_GPSPI_SUPPORTED)
#undef TASMOTALED_HARDWARE_SPI
#define TASMOTALED_HARDWARE_SPI 0
#endif
// if no protocol is defined, use SPI as fallback
#if !TASMOTALED_HARDWARE_RMT && !TASMOTALED_HARDWARE_I2S && !TASMOTALED_HARDWARE_SPI
#undef TASMOTALED_HARDWARE_SPI
#define TASMOTALED_HARDWARE_SPI 1
#endif
// Timing structure for LEDS - in nanoseconds
// It is passed by TasmotaLed to the pushers
typedef struct TasmotaLED_Timing {
uint16_t T0H, T0L, T1H, T1L;
uint32_t Reset;
} TasmotaLED_Timing;
/*******************************************************************************************\
* class TasmotaLEDPusher
*
* This is an virtual abstract class for Leds pusher (RMT, SPI, I2S...)
*
* Below are interfaces for current implementations
\*******************************************************************************************/
class TasmotaLEDPusher {
public:
TasmotaLEDPusher() : _pixel_count(0), _pixel_size(0), _led_timing(nullptr) {};
virtual ~TasmotaLEDPusher() {};
virtual bool Begin(uint16_t pixel_count, uint16_t pixel_size, const TasmotaLED_Timing * led_timing) {
_pixel_count = pixel_count;
_pixel_size = pixel_size;
_led_timing = led_timing;
return true;
}
virtual bool Push(uint8_t *buf) = 0;
virtual bool CanShow(void) = 0;
static uint32_t ResolveHardware(uint32_t hw); // convert to the appropriate hardware acceleration based on capacities of the SOC
static TasmotaLEDPusher * Create(uint32_t hw, int8_t gpio); // create instance for the provided type, or nullptr if failed
protected:
uint16_t _pixel_count;
uint16_t _pixel_size;
const TasmotaLED_Timing * _led_timing;
};
/*******************************************************************************************\
* class TasmotaLEDPusherRMT
*
* Implementation based on RMT driver
\*******************************************************************************************/
#if TASMOTALED_HARDWARE_RMT
#include "driver/rmt_tx.h"
class TasmotaLEDPusherRMT : public TasmotaLEDPusher {
public:
TasmotaLEDPusherRMT(int8_t pin) : _pin(pin) {};
~TasmotaLEDPusherRMT();
bool Begin(uint16_t pixel_count, uint16_t pixel_size, const TasmotaLED_Timing * led_timing) override;
bool Push(uint8_t *buf) override;
bool CanShow(void) override;
protected:
int8_t _pin;
rmt_transmit_config_t _tx_config = {};
rmt_channel_handle_t _channel = nullptr;;
rmt_encoder_handle_t _led_encoder = nullptr;
};
#endif // TASMOTALED_HARDWARE_RMT
/*******************************************************************************************\
* class TasmotaLEDPusherSPI
*
* Implementation based on SPI driver, mandatory for C2
\*******************************************************************************************/
#if TASMOTALED_HARDWARE_SPI
#include <driver/spi_master.h>
typedef struct led_strip_spi_obj_t {
uint8_t * pixel_buf;
uint16_t strip_len;
uint8_t bytes_per_pixel;
spi_host_device_t spi_host;
spi_device_handle_t spi_device;
spi_transaction_t tx_conf; // transaction in process if any
} led_strip_spi_obj;
class TasmotaLEDPusherSPI : public TasmotaLEDPusher {
public:
TasmotaLEDPusherSPI(int8_t pin) : _pin(pin), _spi_strip({}) {};
~TasmotaLEDPusherSPI();
bool Begin(uint16_t pixel_count, uint16_t pixel_size, const TasmotaLED_Timing * led_timing) override;
bool Push(uint8_t *buf) override;
bool CanShow(void) override;
protected:
int8_t _pin;
struct led_strip_spi_obj_t _spi_strip;
};
#endif // TASMOTALED_HARDWARE_SPI
#endif // __TASMOTALEDPUSHER_H__

View File

@ -0,0 +1,240 @@
/*
TasmotaLEDPusherRMT.cpp - Implementation to push Leds via RMT channel
Copyright (C) 2024 Stephan Hadinger
This library 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/>.
*/
#ifdef ESP32
#include "TasmotaLEDPusher.h"
#include "TasmotaLED.h"
#if TASMOTALED_HARDWARE_RMT
#include <rom/gpio.h>
#include <esp_check.h>
//**************************************************************************************************************
// enable AddLog support within a C++ library
extern void AddLog(uint32_t loglevel, PGM_P formatP, ...);
enum LoggingLevels {LOG_LEVEL_NONE, LOG_LEVEL_ERROR, LOG_LEVEL_INFO, LOG_LEVEL_DEBUG, LOG_LEVEL_DEBUG_MORE};
//**************************************************************************************************************
/*******************************************************************************************\
* Implementation for TasmotaLEDPusherRMT
*
* Code mostly copied from Tasmota patch to NeoPixelBus applied to support esp-idf 5.x
* itself inspired from esp-idf example for RMT encoder from
* https://github.com/espressif/esp-idf/tree/v5.3.1/examples/peripherals/rmt/ir_nec_transceiver
\*******************************************************************************************/
#define RMT_LED_STRIP_RESOLUTION_HZ 40000000 // 40MHz resolution, steps of 25 nanoseconds
// structure used to pass arguments to `rmt_new_led_strip_encoder`
// currently only the encoder resolution in Hz
typedef struct {
uint32_t resolution; /*!< Encoder resolution, in Hz */
} led_strip_encoder_config_t;
// structure used to store all the necessary information for the RMT encoder
typedef struct {
rmt_encoder_t base;
rmt_encoder_t *bytes_encoder;
rmt_encoder_t *copy_encoder;
int32_t state;
rmt_symbol_word_t reset_code;
} rmt_led_strip_encoder_t;
static IRAM_ATTR size_t rmt_encode_led_strip(rmt_encoder_t *encoder, rmt_channel_handle_t channel, const void *primary_data, size_t data_size, rmt_encode_state_t *ret_state)
{
rmt_led_strip_encoder_t *led_encoder = __containerof(encoder, rmt_led_strip_encoder_t, base);
rmt_encoder_handle_t bytes_encoder = led_encoder->bytes_encoder;
rmt_encoder_handle_t copy_encoder = led_encoder->copy_encoder;
rmt_encode_state_t session_state = RMT_ENCODING_RESET;
rmt_encode_state_t state = RMT_ENCODING_RESET;
size_t encoded_symbols = 0;
switch (led_encoder->state) {
case 0: // send RGB data
encoded_symbols += bytes_encoder->encode(bytes_encoder, channel, primary_data, data_size, &session_state);
if (session_state & RMT_ENCODING_COMPLETE) {
led_encoder->state = 1; // switch to next state when current encoding session finished
}
if (session_state & RMT_ENCODING_MEM_FULL) {
state = static_cast<rmt_encode_state_t>(static_cast<uint8_t>(state) | static_cast<uint8_t>(RMT_ENCODING_MEM_FULL));
goto out; // yield if there's no free space for encoding artifacts
}
// fall-through
case 1: // send reset code
encoded_symbols += copy_encoder->encode(copy_encoder, channel, &led_encoder->reset_code, sizeof(led_encoder->reset_code), &session_state);
if (session_state & RMT_ENCODING_COMPLETE) {
led_encoder->state = RMT_ENCODING_RESET; // back to the initial encoding session
state = static_cast<rmt_encode_state_t>(static_cast<uint8_t>(state) | static_cast<uint8_t>(RMT_ENCODING_COMPLETE));
}
if (session_state & RMT_ENCODING_MEM_FULL) {
state = static_cast<rmt_encode_state_t>(static_cast<uint8_t>(state) | static_cast<uint8_t>(RMT_ENCODING_MEM_FULL));
goto out; // yield if there's no free space for encoding artifacts
}
}
out:
*ret_state = state;
return encoded_symbols;
}
static esp_err_t rmt_del_led_strip_encoder(rmt_encoder_t *encoder) {
rmt_led_strip_encoder_t *led_encoder = __containerof(encoder, rmt_led_strip_encoder_t, base);
rmt_del_encoder(led_encoder->bytes_encoder);
rmt_del_encoder(led_encoder->copy_encoder);
delete led_encoder;
return ESP_OK;
}
static esp_err_t rmt_led_strip_encoder_reset(rmt_encoder_t *encoder) {
rmt_led_strip_encoder_t *led_encoder = __containerof(encoder, rmt_led_strip_encoder_t, base);
rmt_encoder_reset(led_encoder->bytes_encoder);
rmt_encoder_reset(led_encoder->copy_encoder);
led_encoder->state = RMT_ENCODING_RESET;
return ESP_OK;
}
static esp_err_t rmt_new_led_strip_encoder(const led_strip_encoder_config_t *config, rmt_encoder_handle_t *ret_encoder, rmt_symbol_word_t bit0, rmt_symbol_word_t bit1, rmt_symbol_word_t reset_code) {
static const char* TAG = "TASMOTA_RMT";
esp_err_t ret = ESP_OK;
rmt_led_strip_encoder_t *led_encoder = NULL;
rmt_bytes_encoder_config_t bytes_encoder_config;
rmt_copy_encoder_config_t copy_encoder_config = {};
ESP_GOTO_ON_FALSE(config && ret_encoder, ESP_ERR_INVALID_ARG, err, TAG, "invalid argument");
led_encoder = new rmt_led_strip_encoder_t();
ESP_GOTO_ON_FALSE(led_encoder, ESP_ERR_NO_MEM, err, TAG, "no mem for led strip encoder");
led_encoder->base.encode = rmt_encode_led_strip;
led_encoder->base.del = rmt_del_led_strip_encoder;
led_encoder->base.reset = rmt_led_strip_encoder_reset;
led_encoder->reset_code = reset_code;
bytes_encoder_config.bit0 = bit0;
bytes_encoder_config.bit1 = bit1;
bytes_encoder_config.flags.msb_first = 1; // WS2812 transfer bit order: G7...G0R7...R0B7...B0 - TODO: more checks
ESP_GOTO_ON_ERROR(rmt_new_bytes_encoder(&bytes_encoder_config, &led_encoder->bytes_encoder), err, TAG, "create bytes encoder failed");
ESP_GOTO_ON_ERROR(rmt_new_copy_encoder(&copy_encoder_config, &led_encoder->copy_encoder), err, TAG, "create copy encoder failed");
*ret_encoder = &led_encoder->base;
return ret;
err:
AddLog(LOG_LEVEL_INFO, "RMT: could not init led encoder");
if (led_encoder) {
if (led_encoder->bytes_encoder) { rmt_del_encoder(led_encoder->bytes_encoder); }
if (led_encoder->copy_encoder) { rmt_del_encoder(led_encoder->copy_encoder); }
delete led_encoder;
}
return ret;
}
TasmotaLEDPusherRMT::~TasmotaLEDPusherRMT() {
if (_channel) {
rmt_tx_wait_all_done(_channel, 10000 / portTICK_PERIOD_MS);
rmt_del_channel(_channel);
_channel = nullptr;
}
if (_pin >= 0) {
gpio_matrix_out(_pin, 0x100, false, false);
pinMode(_pin, INPUT);
_pin = -1;
}
}
bool TasmotaLEDPusherRMT::Begin(uint16_t pixel_count, uint16_t pixel_size, const TasmotaLED_Timing * led_timing) {
TasmotaLEDPusher::Begin(pixel_count, pixel_size, led_timing);
esp_err_t ret = ESP_OK;
rmt_tx_channel_config_t config = {};
config.clk_src = RMT_CLK_SRC_DEFAULT;
config.gpio_num = static_cast<gpio_num_t>(_pin);
config.mem_block_symbols = 192; // memory block size, 64 * 4 = 256 Bytes
config.resolution_hz = RMT_LED_STRIP_RESOLUTION_HZ; // 40 MHz tick resolution, i.e., 1 tick = 0.025 µs or 25 ns
config.trans_queue_depth = 4; // set the number of transactions that can pend in the background
config.flags.invert_out = false; // do not invert output signal
config.flags.with_dma = false; // do not need DMA backend
ret = rmt_new_tx_channel(&config, &_channel);
if (ret != ESP_OK) {
AddLog(LOG_LEVEL_INFO, "RMT: cannot initialize Gpio %i err=%i", _pin, ret);
return false;
}
led_strip_encoder_config_t encoder_config = {
.resolution = RMT_LED_STRIP_RESOLUTION_HZ,
};
_tx_config.loop_count = 0; // no loop
rmt_symbol_word_t RmtBit0 = {
.duration0 = (uint16_t) (led_timing->T0H * (RMT_LED_STRIP_RESOLUTION_HZ / 1000000) / 1000),
.level0 = 1,
.duration1 = (uint16_t) (led_timing->T0L * (RMT_LED_STRIP_RESOLUTION_HZ / 1000000) / 1000),
.level1 = 0,
};
rmt_symbol_word_t RmtBit1 = {
.duration0 = (uint16_t) (led_timing->T1H * (RMT_LED_STRIP_RESOLUTION_HZ / 1000000) / 1000),
.level0 = 1,
.duration1 = (uint16_t) (led_timing->T1L * (RMT_LED_STRIP_RESOLUTION_HZ / 1000000) / 1000),
.level1 = 0,
};
rmt_symbol_word_t RmtReset = {
.duration0 = (uint16_t) (led_timing->Reset * (RMT_LED_STRIP_RESOLUTION_HZ / 1000000) / 1000),
.level0 = 0,
.duration1 = 50 * (RMT_LED_STRIP_RESOLUTION_HZ / 1000000) / 1000,
.level1 = 1,
};
// AddLog(LOG_LEVEL_INFO, "RMT: RmtBit0 0x%08X RmtBit1 0x%08X RmtReset 0x%08X", RmtBit0.val, RmtBit1.val, RmtReset.val);
ret = rmt_new_led_strip_encoder(&encoder_config, &_led_encoder, RmtBit0, RmtBit1, RmtReset);
if (ret != ESP_OK) {
// AddLog(LOG_LEVEL_INFO, "RMT: cannot initialize led strip encoder err=%i", ret);
return false;
}
ret = rmt_enable(_channel);
if (ret != ESP_OK) {
// AddLog(LOG_LEVEL_INFO, "RMT: cannot enable channel err=%i", ret);
return false;
}
return true;
}
bool TasmotaLEDPusherRMT::CanShow(void) {
if (_channel) {
return (ESP_OK == rmt_tx_wait_all_done(_channel, 0));
} else {
return false;
}
}
bool TasmotaLEDPusherRMT::Push(uint8_t *buf) {
// wait for not actively sending data
// this will time out at 1 second, an arbitrarily long period of time
// and do nothing if this happens
esp_err_t ret = rmt_tx_wait_all_done(_channel, 1000 / portTICK_PERIOD_MS);
if (ESP_OK == ret) {
// now start the RMT transmit with the editing buffer before we swap
ret = rmt_transmit(_channel, _led_encoder, buf, _pixel_count * _pixel_size, &_tx_config);
if (ESP_OK != ret) {
AddLog(LOG_LEVEL_DEBUG, "RMT: cannot transmit err=%i", ret);
return false;
}
}
return true;
}
#endif // TASMOTALED_HARDWARE_RMT
#endif // ESP32

View File

@ -0,0 +1,191 @@
/*
TasmotaLEDPusherRMT.cpp - Implementation to push Leds via SPI channel
Copyright (C) 2024 Stephan Hadinger
This library 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/>.
*/
#ifdef ESP32
#include "TasmotaLEDPusher.h"
#include "TasmotaLED.h"
#if TASMOTALED_HARDWARE_SPI
#include <rom/gpio.h>
//**************************************************************************************************************
// enable AddLog support within a C++ library
extern void AddLog(uint32_t loglevel, PGM_P formatP, ...);
enum LoggingLevels {LOG_LEVEL_NONE, LOG_LEVEL_ERROR, LOG_LEVEL_INFO, LOG_LEVEL_DEBUG, LOG_LEVEL_DEBUG_MORE};
//**************************************************************************************************************
/*******************************************************************************************\
* Implementation for TasmotaLEDPusherSPI
*
\*******************************************************************************************/
#define LED_STRIP_SPI_DEFAULT_RESOLUTION (25 * 100 * 1000) // 2.5MHz resolution
#define LED_STRIP_SPI_DEFAULT_TRANS_QUEUE_SIZE 4
#define SPI_BYTES_PER_COLOR_BYTE 3
#define SPI_BITS_PER_COLOR_BYTE (SPI_BYTES_PER_COLOR_BYTE * 8)
static void __led_strip_spi_bit(uint8_t data, uint8_t *buf)
{
// Each color of 1 bit is represented by 3 bits of SPI, low_level:100 ,high_level:110
// So a color byte occupies 3 bytes of SPI.
buf[0] = (data & BIT(5) ? BIT(1) | BIT(0) : BIT(1)) | (data & BIT(6) ? BIT(4) | BIT(3) : BIT(4)) | (data & BIT(7) ? BIT(7) | BIT(6) : BIT(7));
buf[1] = (BIT(0)) | (data & BIT(3) ? BIT(3) | BIT(2) : BIT(3)) | (data & BIT(4) ? BIT(6) | BIT(5) : BIT(6));
buf[2] = (data & BIT(0) ? BIT(2) | BIT(1) : BIT(2)) | (data & BIT(1) ? BIT(5) | BIT(4) : BIT(5)) | (data & BIT(2) ? BIT(7) : 0x00);
}
esp_err_t led_strip_spi_refresh(led_strip_spi_obj * spi_strip)
{
spi_strip->tx_conf.length = spi_strip->strip_len * spi_strip->bytes_per_pixel * SPI_BITS_PER_COLOR_BYTE;
spi_strip->tx_conf.tx_buffer = spi_strip->pixel_buf;
spi_strip->tx_conf.rx_buffer = NULL;
spi_device_transmit(spi_strip->spi_device, &spi_strip->tx_conf);
return ESP_OK;
}
void led_strip_transmit_buffer(led_strip_spi_obj * spi_strip, uint8_t * buffer_rgbw) {
// Timing for 512 pixels (extreme test)
// Copying to buffer: 418 us
// sending pixels: 16.2 ms
uint8_t * buf = buffer_rgbw;
uint8_t * pix_buf = spi_strip->pixel_buf;
for (int i = 0; i < spi_strip->strip_len; i++) {
// LED_PIXEL_FORMAT_GRB takes 72bits(9bytes)
__led_strip_spi_bit(*buf++, pix_buf); pix_buf += SPI_BYTES_PER_COLOR_BYTE;
__led_strip_spi_bit(*buf++, pix_buf); pix_buf += SPI_BYTES_PER_COLOR_BYTE;
__led_strip_spi_bit(*buf++, pix_buf); pix_buf += SPI_BYTES_PER_COLOR_BYTE;
if (spi_strip->bytes_per_pixel > 3) {
__led_strip_spi_bit(*buf++, pix_buf); pix_buf += SPI_BYTES_PER_COLOR_BYTE;
}
}
/* Refresh the strip to send data */
led_strip_spi_refresh(spi_strip);
}
TasmotaLEDPusherSPI::~TasmotaLEDPusherSPI() {
if (_spi_strip.spi_device) {
spi_bus_remove_device(_spi_strip.spi_device);
}
if (_spi_strip.spi_host) {
spi_bus_free(_spi_strip.spi_host);
}
if (_pin >= 0) {
gpio_matrix_out(_pin, 0x100, false, false);
pinMode(_pin, INPUT);
_pin = -1;
}
}
bool TasmotaLEDPusherSPI::Begin(uint16_t pixel_count, uint16_t pixel_size, const TasmotaLED_Timing * led_timing) {
TasmotaLEDPusher::Begin(pixel_count, pixel_size, led_timing);
_spi_strip.bytes_per_pixel = _pixel_size;
_spi_strip.strip_len = _pixel_count;
esp_err_t err = ESP_OK;
uint32_t mem_caps = MALLOC_CAP_DEFAULT;
// spi_clock_source_t clk_src = SPI_CLK_SRC_DEFAULT;
spi_bus_config_t spi_bus_cfg;
spi_device_interface_config_t spi_dev_cfg;
spi_host_device_t spi_host = SPI2_HOST;
bool with_dma = true; /// TODO: pass value or compute based on pixelcount
int clock_resolution_khz = 0;
if (with_dma) { // TODO
// DMA buffer must be placed in internal SRAM
mem_caps |= MALLOC_CAP_INTERNAL | MALLOC_CAP_DMA;
}
_spi_strip.pixel_buf = (uint8_t *)heap_caps_calloc(1, _pixel_count * _pixel_size * SPI_BYTES_PER_COLOR_BYTE, mem_caps);
if (_spi_strip.pixel_buf == nullptr) {
AddLog(LOG_LEVEL_INFO, PSTR("LED: Error no mem for spi strip"));
goto err;
}
spi_bus_cfg = {
.mosi_io_num = _pin,
//Only use MOSI to generate the signal, set -1 when other pins are not used.
.miso_io_num = -1,
.sclk_io_num = -1,
.quadwp_io_num = -1,
.quadhd_io_num = -1,
.max_transfer_sz = _pixel_count * _pixel_size * SPI_BYTES_PER_COLOR_BYTE,
};
err = spi_bus_initialize(spi_host, &spi_bus_cfg, with_dma ? SPI_DMA_CH_AUTO : SPI_DMA_DISABLED);
if (err != ESP_OK) {
AddLog(LOG_LEVEL_INFO, PSTR("LED: Error create SPI bus failed"));
goto err;
}
_spi_strip.spi_host = spi_host; // confirmed working, so keep it's value to free it later
spi_dev_cfg = {
.command_bits = 0,
.address_bits = 0,
.dummy_bits = 0,
.mode = 0,
//set -1 when CS is not used
.clock_source = SPI_CLK_SRC_DEFAULT, // clk_src,
.clock_speed_hz = LED_STRIP_SPI_DEFAULT_RESOLUTION,
.spics_io_num = -1,
.queue_size = LED_STRIP_SPI_DEFAULT_TRANS_QUEUE_SIZE,
};
err = spi_bus_add_device(_spi_strip.spi_host, &spi_dev_cfg, &_spi_strip.spi_device);
if (err != ESP_OK) {
// AddLog(LOG_LEVEL_INFO, "LED: Error failed to add spi device");
goto err;
}
spi_device_get_actual_freq(_spi_strip.spi_device, &clock_resolution_khz);
if (err != ESP_OK) {
// AddLog(LOG_LEVEL_INFO, "LED: Error failed to get spi frequency");
goto err;
}
// TODO: ideally we should decide the SPI_BYTES_PER_COLOR_BYTE by the real clock resolution
// But now, let's fixed the resolution, the downside is, we don't support a clock source whose frequency is not multiple of LED_STRIP_SPI_DEFAULT_RESOLUTION
if (clock_resolution_khz != LED_STRIP_SPI_DEFAULT_RESOLUTION / 1000) {
// AddLog(LOG_LEVEL_INFO, "LED: Error unsupported clock resolution: %dKHz", clock_resolution_khz);
goto err;
}
return true;
err:
if (_spi_strip.spi_device) {
spi_bus_remove_device(_spi_strip.spi_device);
}
if (_spi_strip.spi_host) {
spi_bus_free(_spi_strip.spi_host);
}
return false;
}
bool TasmotaLEDPusherSPI::CanShow(void) {
return true; // TODO
}
bool TasmotaLEDPusherSPI::Push(uint8_t *buf) {
if (CanShow()) {
led_strip_transmit_buffer(&_spi_strip, buf);
}
return true;
}
#endif // TASMOTALED_HARDWARE_SPI
#endif // ESP32

View File

@ -680,14 +680,15 @@ uDisplay::uDisplay(char *lp) : Renderer(800, 600) {
if (*lp == '\n' || *lp == ' ') { // Add space char
lp++;
} else {
lp = strchr(lp, '\n');
if (!lp) {
lp = strchr(lp, ' ');
if (!lp) {
char *lp1;
lp1 = strchr(lp, '\n');
if (!lp1) {
lp1 = strchr(lp, ' ');
if (!lp1) {
break;
}
}
lp++;
lp = lp1 + 1;
}
}

View File

@ -558,6 +558,8 @@ void ESPKNXIP::__loop_knx()
DEBUG_PRINTLN(F(""));
udp.flush();
knx_ip_pkt_t *knx_pkt = (knx_ip_pkt_t *)buf;
DEBUG_PRINT(F("ST: 0x"));

View File

@ -1,6 +1,6 @@
/**************************************************************************/
/*!
@file Adafruit_TSL2591.h
/*!
@file Adafruit_TSL2591.h
@author KT0WN (adafruit.com)
This is a library for the Adafruit TSL2591 breakout board

View File

@ -0,0 +1,258 @@
#include "MS5837.h"
#include <Wire.h>
const uint8_t MS5837_ADDR = 0x76;
const uint8_t MS5837_RESET = 0x1E;
const uint8_t MS5837_ADC_READ = 0x00;
const uint8_t MS5837_PROM_READ = 0xA0;
const uint8_t MS5837_CONVERT_D1_8192 = 0x4A;
const uint8_t MS5837_CONVERT_D2_8192 = 0x5A;
const float MS5837::Pa = 100.0f;
const float MS5837::bar = 0.001f;
const float MS5837::mbar = 1.0f;
const uint8_t MS5837::MS5837_30BA = 0;
const uint8_t MS5837::MS5837_02BA = 1;
const uint8_t MS5837::MS5837_UNRECOGNISED = 255;
const uint8_t MS5837_02BA01 = 0x00; // Sensor version: From MS5837_02BA datasheet Version PROM Word 0
const uint8_t MS5837_02BA21 = 0x15; // Sensor version: From MS5837_02BA datasheet Version PROM Word 0
const uint8_t MS5837_30BA26 = 0x1A; // Sensor version: From MS5837_30BA datasheet Version PROM Word 0
MS5837::MS5837() {
fluidDensity = 1029;
}
bool MS5837::begin(TwoWire &wirePort) {
return (init(wirePort));
}
bool MS5837::init(TwoWire &wirePort) {
_i2cPort = &wirePort; //Grab which port the user wants us to use
// Reset the MS5837, per datasheet
_i2cPort->beginTransmission(MS5837_ADDR);
_i2cPort->write(MS5837_RESET);
_i2cPort->endTransmission();
// Wait for reset to complete
delay(10);
// Read calibration values and CRC
for ( uint8_t i = 0 ; i < 7 ; i++ ) {
_i2cPort->beginTransmission(MS5837_ADDR);
_i2cPort->write(MS5837_PROM_READ+i*2);
_i2cPort->endTransmission();
_i2cPort->requestFrom(MS5837_ADDR, (uint8_t)2);
C[i] = (_i2cPort->read() << 8) | _i2cPort->read();
}
// Verify that data is correct with CRC
uint8_t crcRead = C[0] >> 12;
uint8_t crcCalculated = crc4(C);
if ( crcCalculated != crcRead ) {
return false; // CRC fail
}
uint8_t version = (C[0] >> 5) & 0x7F; // Extract the sensor version from PROM Word 0
// Set _model according to the sensor version
if (version == MS5837_02BA01)
{
_model = MS5837_02BA;
}
else if (version == MS5837_02BA21)
{
_model = MS5837_02BA;
}
else if (version == MS5837_30BA26)
{
_model = MS5837_30BA;
}
else
{
_model = MS5837_UNRECOGNISED;
}
// The sensor has passed the CRC check, so we should return true even if
// the sensor version is unrecognised.
// (The MS5637 has the same address as the MS5837 and will also pass the CRC check)
// (but will hopefully be unrecognised.)
return true;
}
void MS5837::setModel(uint8_t model) {
_model = model;
}
uint8_t MS5837::getModel() {
return (_model);
}
void MS5837::setFluidDensity(float density) {
fluidDensity = density;
}
void MS5837::read() {
//Check that _i2cPort is not NULL (i.e. has the user forgoten to call .init or .begin?)
if (_i2cPort == NULL)
{
return;
}
// Request D1 conversion
_i2cPort->beginTransmission(MS5837_ADDR);
_i2cPort->write(MS5837_CONVERT_D1_8192);
_i2cPort->endTransmission();
delay(20); // Max conversion time per datasheet
_i2cPort->beginTransmission(MS5837_ADDR);
_i2cPort->write(MS5837_ADC_READ);
_i2cPort->endTransmission();
_i2cPort->requestFrom(MS5837_ADDR, (uint8_t)3);
D1_pres = 0;
D1_pres = _i2cPort->read();
D1_pres = (D1_pres << 8) | _i2cPort->read();
D1_pres = (D1_pres << 8) | _i2cPort->read();
// Request D2 conversion
_i2cPort->beginTransmission(MS5837_ADDR);
_i2cPort->write(MS5837_CONVERT_D2_8192);
_i2cPort->endTransmission();
delay(20); // Max conversion time per datasheet
_i2cPort->beginTransmission(MS5837_ADDR);
_i2cPort->write(MS5837_ADC_READ);
_i2cPort->endTransmission();
_i2cPort->requestFrom(MS5837_ADDR, (uint8_t)3);
D2_temp = 0;
D2_temp = _i2cPort->read();
D2_temp = (D2_temp << 8) | _i2cPort->read();
D2_temp = (D2_temp << 8) | _i2cPort->read();
calculate();
}
void MS5837::calculate() {
// Given C1-C6 and D1, D2, calculated TEMP and P
// Do conversion first and then second order temp compensation
int32_t dT = 0;
int64_t SENS = 0;
int64_t OFF = 0;
int32_t SENSi = 0;
int32_t OFFi = 0;
int32_t Ti = 0;
int64_t OFF2 = 0;
int64_t SENS2 = 0;
// Terms called
dT = D2_temp-uint32_t(C[5])*256l;
if ( _model == MS5837_02BA ) {
SENS = int64_t(C[1])*65536l+(int64_t(C[3])*dT)/128l;
OFF = int64_t(C[2])*131072l+(int64_t(C[4])*dT)/64l;
P = (D1_pres*SENS/(2097152l)-OFF)/(32768l);
} else {
SENS = int64_t(C[1])*32768l+(int64_t(C[3])*dT)/256l;
OFF = int64_t(C[2])*65536l+(int64_t(C[4])*dT)/128l;
P = (D1_pres*SENS/(2097152l)-OFF)/(8192l);
}
// Temp conversion
TEMP = 2000l+int64_t(dT)*C[6]/8388608LL;
//Second order compensation
if ( _model == MS5837_02BA ) {
if((TEMP/100)<20){ //Low temp
Ti = (11*int64_t(dT)*int64_t(dT))/(34359738368LL);
OFFi = (31*(TEMP-2000)*(TEMP-2000))/8;
SENSi = (63*(TEMP-2000)*(TEMP-2000))/32;
}
} else {
if((TEMP/100)<20){ //Low temp
Ti = (3*int64_t(dT)*int64_t(dT))/(8589934592LL);
OFFi = (3*(TEMP-2000)*(TEMP-2000))/2;
SENSi = (5*(TEMP-2000)*(TEMP-2000))/8;
if((TEMP/100)<-15){ //Very low temp
OFFi = OFFi+7*(TEMP+1500l)*(TEMP+1500l);
SENSi = SENSi+4*(TEMP+1500l)*(TEMP+1500l);
}
}
else if((TEMP/100)>=20){ //High temp
Ti = 2*(dT*dT)/(137438953472LL);
OFFi = (1*(TEMP-2000)*(TEMP-2000))/16;
SENSi = 0;
}
}
OFF2 = OFF-OFFi; //Calculate pressure and temp second order
SENS2 = SENS-SENSi;
TEMP = (TEMP-Ti);
if ( _model == MS5837_02BA ) {
P = (((D1_pres*SENS2)/2097152l-OFF2)/32768l);
} else {
P = (((D1_pres*SENS2)/2097152l-OFF2)/8192l);
}
}
float MS5837::pressure(float conversion) {
if ( _model == MS5837_02BA ) {
return P*conversion/100.0f;
}
else {
return P*conversion/10.0f;
}
}
float MS5837::temperature() {
return TEMP/100.0f;
}
// The pressure sensor measures absolute pressure, so it will measure the atmospheric pressure + water pressure
// We subtract the atmospheric pressure to calculate the depth with only the water pressure
// The average atmospheric pressure of 101300 pascal is used for the calcuation, but atmospheric pressure varies
// If the atmospheric pressure is not 101300 at the time of reading, the depth reported will be offset
// In order to calculate the correct depth, the actual atmospheric pressure should be measured once in air, and
// that value should subtracted for subsequent depth calculations.
float MS5837::depth() {
return (pressure(MS5837::Pa)-101300)/(fluidDensity*9.80665f);
}
float MS5837::altitude() {
return (1-pow((pressure()/1013.25f),.190284f))*145366.45f*.3048f;
}
uint8_t MS5837::crc4(uint16_t n_prom[]) {
uint16_t n_rem = 0;
n_prom[0] = ((n_prom[0]) & 0x0FFF);
n_prom[7] = 0;
for ( uint8_t i = 0 ; i < 16; i++ ) {
if ( i%2 == 1 ) {
n_rem ^= (uint16_t)((n_prom[i>>1]) & 0x00FF);
} else {
n_rem ^= (uint16_t)(n_prom[i>>1] >> 8);
}
for ( uint8_t n_bit = 8 ; n_bit > 0 ; n_bit-- ) {
if ( n_rem & 0x8000 ) {
n_rem = (n_rem << 1) ^ 0x3000;
} else {
n_rem = (n_rem << 1);
}
}
}
n_rem = ((n_rem >> 12) & 0x000F);
return n_rem ^ 0x00;
}

View File

@ -0,0 +1,113 @@
/* Blue Robotics Arduino MS5837-30BA Pressure/Temperature Sensor Library
------------------------------------------------------------
Title: Blue Robotics Arduino MS5837-30BA Pressure/Temperature Sensor Library
Description: This library provides utilities to communicate with and to
read data from the Measurement Specialties MS5837-30BA pressure/temperature
sensor.
Authors: Rustom Jehangir, Blue Robotics Inc.
Adam Šimko, Blue Robotics Inc.
-------------------------------
The MIT License (MIT)
Copyright (c) 2015 Blue Robotics Inc.
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.
-------------------------------*/
#ifndef MS5837_H_BLUEROBOTICS
#define MS5837_H_BLUEROBOTICS
#include "Arduino.h"
#include <Wire.h>
class MS5837 {
public:
static const float Pa;
static const float bar;
static const float mbar;
static const uint8_t MS5837_30BA;
static const uint8_t MS5837_02BA;
static const uint8_t MS5837_UNRECOGNISED;
MS5837();
bool init(TwoWire &wirePort = Wire);
bool begin(TwoWire &wirePort = Wire); // Calls init()
/** Set model of MS5837 sensor. Valid options are MS5837::MS5837_30BA (default)
* and MS5837::MS5837_02BA.
*/
void setModel(uint8_t model);
uint8_t getModel();
/** Provide the density of the working fluid in kg/m^3. Default is for
* seawater. Should be 997 for freshwater.
*/
void setFluidDensity(float density);
/** The read from I2C takes up to 40 ms, so use sparingly is possible.
*/
void read();
/** Pressure returned in mbar or mbar*conversion rate.
*/
float pressure(float conversion = 1.0f);
/** Temperature returned in deg C.
*/
float temperature();
/** Depth returned in meters (valid for operation in incompressible
* liquids only. Uses density that is set for fresh or seawater.
*/
float depth();
/** Altitude returned in meters (valid for operation in air only).
*/
float altitude();
uint8_t crc4(uint16_t n_prom[]);
private:
//This stores the requested i2c port
TwoWire * _i2cPort;
uint16_t C[8];
uint32_t D1_pres, D2_temp;
int32_t TEMP;
int32_t P;
uint8_t _model;
float fluidDensity;
/** Performs calculations per the sensor data sheet for conversion and
* second order compensation.
*/
void calculate();
//uint8_t crc4(uint16_t n_prom[]);
};
#endif

View File

@ -47,7 +47,9 @@
#include "c_types.h"
#endif
#include <core_version.h>
#if __has_include("core_version.h") // ESP32 Stage has no core_version.h file. Disable include via PlatformIO Option
#include <core_version.h> // Arduino_Esp8266 version information (ARDUINO_ESP8266_RELEASE and ARDUINO_ESP8266_RELEASE_2_7_1)
#endif // ESP32_STAGE
#undef DEBUG_TLS
#ifdef DEBUG_TLS

View File

@ -20,7 +20,9 @@
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include <core_version.h>
#if __has_include("core_version.h") // ESP32 Stage has no core_version.h file. Disable include via PlatformIO Option
#include <core_version.h> // Arduino_Esp8266 version information (ARDUINO_ESP8266_RELEASE and ARDUINO_ESP8266_RELEASE_2_7_1)
#endif // ESP32_STAGE
#ifndef wificlientlightbearssl_h
#define wificlientlightbearssl_h

View File

@ -75,7 +75,7 @@
#if !defined(ESP_MAIL_DISABLE_NATIVE_ETHERNET)
#if defined(ESP32) && __has_include(<esp_eth_driver.h>)
#if defined(ESP32) && defined(CONFIG_ETH_ENABLED)
#include <ETH.h>
#define ESP_MAIL_ETH_IS_AVAILABLE
#elif defined(ESP8266) && defined(ESP8266_CORE_SDK_V3_X_X)
@ -128,4 +128,4 @@
#define WiFI_CONNECTED false
#endif
#endif
#endif

View File

@ -152,7 +152,7 @@ BERRY_LOCAL const bntvmodule_t* const be_module_table[] = {
&be_native_module(unishox),
#endif // USE_UNISHOX_COMPRESSION
#ifdef USE_WS2812
#if defined(USE_WS2812) && !defined(USE_WS2812_FORCE_NEOPIXELBUS)
&be_native_module(animate),
#endif // USE_WS2812
@ -178,7 +178,7 @@ BERRY_LOCAL const bntvmodule_t* const be_module_table[] = {
&be_native_module(partition_core),
&be_native_module(crc),
&be_native_module(crypto),
#if defined(USE_BERRY_ULP) && ((CONFIG_IDF_TARGET_ESP32) || defined(CONFIG_IDF_TARGET_ESP32S2) || defined(CONFIG_IDF_TARGET_ESP32S3))
#if defined(USE_BERRY_ULP) && defined(CONFIG_ULP_COPROC_ENABLED)
&be_native_module(ULP),
#endif // USE_BERRY_ULP
#if defined(USE_BERRY_TF_LITE)
@ -293,7 +293,7 @@ BERRY_LOCAL bclass_array be_class_table = {
#ifdef USE_BERRY_TCPSERVER
&be_native_class(tcpserver),
#endif // USE_BERRY_TCPSERVER
#ifdef USE_WS2812
#if defined(USE_WS2812) && !defined(USE_WS2812_FORCE_NEOPIXELBUS)
&be_native_class(Leds_ntv),
&be_native_class(Leds),
#endif // USE_WS2812

View File

@ -45,10 +45,10 @@ extern "C" {
uint32_t g2 = (color_b >> 8) & 0xFF;
uint32_t b2 = (color_b ) & 0xFF;
uint32_t a2 = (color_b >> 24) & 0xFF;
uint32_t r3 = changeUIntScale(alpha, 0, 255, r2, r);
uint32_t g3 = changeUIntScale(alpha, 0, 255, g2, g);
uint32_t b3 = changeUIntScale(alpha, 0, 255, b2, b);
uint32_t a3 = changeUIntScale(alpha, 0, 255, a2, a);
uint8_t r3 = changeUIntScale(alpha, 0, 255, r2, r);
uint8_t g3 = changeUIntScale(alpha, 0, 255, g2, g);
uint8_t b3 = changeUIntScale(alpha, 0, 255, b2, b);
uint8_t a3 = changeUIntScale(alpha, 0, 255, a2, a);
uint32_t rgb = (a3 << 24) | (r3 << 16) | (g3 << 8) | b3;
be_pushint(vm, rgb);
be_return(vm);
@ -97,9 +97,9 @@ extern "C" {
uint32_t fore_g = (fore_argb >> 8) & 0xFF;
uint32_t back_b = (back_argb ) & 0xFF;
uint32_t fore_b = (fore_argb ) & 0xFF;
uint32_t dest_r_new = changeUIntScale(fore_alpha, 0, 255, fore_r, back_r);
uint32_t dest_g_new = changeUIntScale(fore_alpha, 0, 255, fore_g, back_g);
uint32_t dest_b_new = changeUIntScale(fore_alpha, 0, 255, fore_b, back_b);
uint8_t dest_r_new = changeUIntScale(fore_alpha, 0, 255, fore_r, back_r);
uint8_t dest_g_new = changeUIntScale(fore_alpha, 0, 255, fore_g, back_g);
uint8_t dest_b_new = changeUIntScale(fore_alpha, 0, 255, fore_b, back_b);
dest_rgb_new = (dest_r_new << 16) | (dest_g_new << 8) | dest_b_new;
}
dest[i] = dest_rgb_new;
@ -135,7 +135,7 @@ extern "C" {
// Leds_frame.paste_pixels(neopixel:bytes(), led_buffer:bytes(), bri:int 0..100, gamma:bool)
//
// Copy from ARGB buffer to GRB
// Copy from ARGB buffer to RGB
int32_t be_leds_paste_pixels(bvm *vm);
int32_t be_leds_paste_pixels(bvm *vm) {
int32_t top = be_top(vm); // Get the number of arguments
@ -162,8 +162,8 @@ extern "C" {
uint32_t src_r = (src_argb >> 16) & 0xFF;
uint32_t src_g = (src_argb >> 8) & 0xFF;
uint32_t src_b = (src_argb ) & 0xFF;
dest_buf[i * 3 + 0] = src_g;
dest_buf[i * 3 + 1] = src_r;
dest_buf[i * 3 + 0] = src_r;
dest_buf[i * 3 + 1] = src_g;
dest_buf[i * 3 + 2] = src_b;
}
}

View File

@ -197,8 +197,8 @@ matter.IM_WriteResponse = Matter_IM_WriteResponse
# This version pull the attributes in lazy mode, only when response is computed
#################################################################################
class Matter_IM_ReportData_Pull : Matter_IM_Message
static var MAX_MESSAGE = 1200 # max bytes size for a single TLV worklaod
# the maximum MTU is 1280, which leaves 80 bytes for the rest of the message
static var MAX_MESSAGE = 1150 # max bytes size for a single TLV worklaod
# the maximum MTU is 1280, which leaves 130 bytes for the rest of the message
# section 4.4.4 (p. 114)
# note: `self.data` (bytes or nil) is containing any remaining responses that could not fit in previous packets
var generator_or_arr # a PathGenerator or an array of PathGenerator

View File

@ -375,7 +375,7 @@ class Matter_Frame
# recompute nonce
var n = self.message_handler._n_bytes # use cached bytes() object to avoid allocation
n.clear()
n.add(self.flags, 1)
n.add(self.sec_flags, 1)
n.add(self.message_counter, 4)
if self.source_node_id
n .. self.source_node_id
@ -426,7 +426,7 @@ class Matter_Frame
# recompute nonce
var n = self.message_handler._n_bytes # use cached bytes() object to avoid allocation
n.clear()
n.add(self.flags, 1)
n.add(self.sec_flags, 1)
n.add(self.message_counter, 4)
if session.is_CASE() && session.get_device_id()
n .. session.get_device_id()

View File

@ -1095,7 +1095,7 @@ be_local_class(Matter_IM_ReportData_Pull,
{ be_const_key_weak(status_ok_received, -1), be_const_closure(class_Matter_IM_ReportData_Pull_status_ok_received_closure) },
{ be_const_key_weak(data_ev, -1), be_const_var(4) },
{ be_const_key_weak(suppress_response, 8), be_const_var(3) },
{ be_const_key_weak(MAX_MESSAGE, -1), be_const_int(1200) },
{ be_const_key_weak(MAX_MESSAGE, -1), be_const_int(1150) },
{ be_const_key_weak(set_suppress_response, -1), be_const_closure(class_Matter_IM_ReportData_Pull_set_suppress_response_closure) },
{ be_const_key_weak(set_subscription_id, -1), be_const_closure(class_Matter_IM_ReportData_Pull_set_subscription_id_closure) },
{ be_const_key_weak(generator_or_arr, 5), be_const_var(0) },

View File

@ -16,7 +16,7 @@ static const bvalue be_ktab_class_Matter_Frame[73] = {
/* K7 */ be_nested_str_weak(_n_bytes),
/* K8 */ be_nested_str_weak(clear),
/* K9 */ be_nested_str_weak(add),
/* K10 */ be_nested_str_weak(flags),
/* K10 */ be_nested_str_weak(sec_flags),
/* K11 */ be_const_int(1),
/* K12 */ be_nested_str_weak(message_counter),
/* K13 */ be_nested_str_weak(is_CASE),
@ -27,12 +27,12 @@ static const bvalue be_ktab_class_Matter_Frame[73] = {
/* K18 */ be_const_int(0),
/* K19 */ be_nested_str_weak(remote_ip),
/* K20 */ be_nested_str_weak(remote_port),
/* K21 */ be_nested_str_weak(flag_s),
/* K22 */ be_nested_str_weak(flag_dsiz),
/* K23 */ be_const_int(3),
/* K24 */ be_nested_str_weak(local_session_id),
/* K25 */ be_const_int(2),
/* K26 */ be_nested_str_weak(sec_flags),
/* K21 */ be_nested_str_weak(flags),
/* K22 */ be_nested_str_weak(flag_s),
/* K23 */ be_nested_str_weak(flag_dsiz),
/* K24 */ be_const_int(3),
/* K25 */ be_nested_str_weak(local_session_id),
/* K26 */ be_const_int(2),
/* K27 */ be_nested_str_weak(sec_p),
/* K28 */ be_nested_str_weak(sec_c),
/* K29 */ be_nested_str_weak(sec_sesstype),
@ -260,83 +260,83 @@ be_local_closure(class_Matter_Frame_encode_frame, /* name */
0x00100805, // 000B ADD R4 R4 R5
0x7C0C0200, // 000C CALL R3 1
0x5C080600, // 000D MOVE R2 R3
0x880C010A, // 000E GETMBR R3 R0 K10
0x880C0115, // 000E GETMBR R3 R0 K21
0x4C100000, // 000F LDNIL R4
0x1C0C0604, // 0010 EQ R3 R3 R4
0x780E000D, // 0011 JMPF R3 #0020
0x90021512, // 0012 SETMBR R0 K10 K18
0x880C0115, // 0013 GETMBR R3 R0 K21
0x90022B12, // 0012 SETMBR R0 K21 K18
0x880C0116, // 0013 GETMBR R3 R0 K22
0x780E0003, // 0014 JMPF R3 #0019
0x880C010A, // 0015 GETMBR R3 R0 K10
0x880C0115, // 0015 GETMBR R3 R0 K21
0x54120003, // 0016 LDINT R4 4
0x300C0604, // 0017 OR R3 R3 R4
0x90021403, // 0018 SETMBR R0 K10 R3
0x880C0116, // 0019 GETMBR R3 R0 K22
0x90022A03, // 0018 SETMBR R0 K21 R3
0x880C0117, // 0019 GETMBR R3 R0 K23
0x780E0004, // 001A JMPF R3 #0020
0x880C010A, // 001B GETMBR R3 R0 K10
0x88100116, // 001C GETMBR R4 R0 K22
0x2C100917, // 001D AND R4 R4 K23
0x880C0115, // 001B GETMBR R3 R0 K21
0x88100117, // 001C GETMBR R4 R0 K23
0x2C100918, // 001D AND R4 R4 K24
0x300C0604, // 001E OR R3 R3 R4
0x90021403, // 001F SETMBR R0 K10 R3
0x90022A03, // 001F SETMBR R0 K21 R3
0x8C0C0509, // 0020 GETMET R3 R2 K9
0x8814010A, // 0021 GETMBR R5 R0 K10
0x88140115, // 0021 GETMBR R5 R0 K21
0x5818000B, // 0022 LDCONST R6 K11
0x7C0C0600, // 0023 CALL R3 3
0x8C0C0509, // 0024 GETMET R3 R2 K9
0x88140118, // 0025 GETMBR R5 R0 K24
0x88140119, // 0025 GETMBR R5 R0 K25
0x78160001, // 0026 JMPF R5 #0029
0x88140118, // 0027 GETMBR R5 R0 K24
0x88140119, // 0027 GETMBR R5 R0 K25
0x70020000, // 0028 JMP #002A
0x58140012, // 0029 LDCONST R5 K18
0x58180019, // 002A LDCONST R6 K25
0x5818001A, // 002A LDCONST R6 K26
0x7C0C0600, // 002B CALL R3 3
0x880C011A, // 002C GETMBR R3 R0 K26
0x880C010A, // 002C GETMBR R3 R0 K10
0x4C100000, // 002D LDNIL R4
0x1C0C0604, // 002E EQ R3 R3 R4
0x780E0013, // 002F JMPF R3 #0044
0x90023512, // 0030 SETMBR R0 K26 K18
0x90021512, // 0030 SETMBR R0 K10 K18
0x880C011B, // 0031 GETMBR R3 R0 K27
0x780E0003, // 0032 JMPF R3 #0037
0x880C011A, // 0033 GETMBR R3 R0 K26
0x880C010A, // 0033 GETMBR R3 R0 K10
0x5412007F, // 0034 LDINT R4 128
0x300C0604, // 0035 OR R3 R3 R4
0x90023403, // 0036 SETMBR R0 K26 R3
0x90021403, // 0036 SETMBR R0 K10 R3
0x880C011C, // 0037 GETMBR R3 R0 K28
0x780E0003, // 0038 JMPF R3 #003D
0x880C011A, // 0039 GETMBR R3 R0 K26
0x880C010A, // 0039 GETMBR R3 R0 K10
0x5412003F, // 003A LDINT R4 64
0x300C0604, // 003B OR R3 R3 R4
0x90023403, // 003C SETMBR R0 K26 R3
0x90021403, // 003C SETMBR R0 K10 R3
0x880C011D, // 003D GETMBR R3 R0 K29
0x780E0004, // 003E JMPF R3 #0044
0x880C011A, // 003F GETMBR R3 R0 K26
0x880C010A, // 003F GETMBR R3 R0 K10
0x8810011D, // 0040 GETMBR R4 R0 K29
0x2C100917, // 0041 AND R4 R4 K23
0x2C100918, // 0041 AND R4 R4 K24
0x300C0604, // 0042 OR R3 R3 R4
0x90023403, // 0043 SETMBR R0 K26 R3
0x90021403, // 0043 SETMBR R0 K10 R3
0x8C0C0509, // 0044 GETMET R3 R2 K9
0x8814011A, // 0045 GETMBR R5 R0 K26
0x8814010A, // 0045 GETMBR R5 R0 K10
0x5818000B, // 0046 LDCONST R6 K11
0x7C0C0600, // 0047 CALL R3 3
0x8C0C0509, // 0048 GETMET R3 R2 K9
0x8814010C, // 0049 GETMBR R5 R0 K12
0x541A0003, // 004A LDINT R6 4
0x7C0C0600, // 004B CALL R3 3
0x880C0115, // 004C GETMBR R3 R0 K21
0x880C0116, // 004C GETMBR R3 R0 K22
0x780E0001, // 004D JMPF R3 #0050
0x880C011E, // 004E GETMBR R3 R0 K30
0x400C0403, // 004F CONNECT R3 R2 R3
0x880C0116, // 0050 GETMBR R3 R0 K22
0x880C0117, // 0050 GETMBR R3 R0 K23
0x1C0C070B, // 0051 EQ R3 R3 K11
0x780E0001, // 0052 JMPF R3 #0055
0x880C011F, // 0053 GETMBR R3 R0 K31
0x400C0403, // 0054 CONNECT R3 R2 R3
0x880C0116, // 0055 GETMBR R3 R0 K22
0x1C0C0719, // 0056 EQ R3 R3 K25
0x880C0117, // 0055 GETMBR R3 R0 K23
0x1C0C071A, // 0056 EQ R3 R3 K26
0x780E0003, // 0057 JMPF R3 #005C
0x8C0C0509, // 0058 GETMET R3 R2 K9
0x88140120, // 0059 GETMBR R5 R0 K32
0x58180019, // 005A LDCONST R6 K25
0x5818001A, // 005A LDCONST R6 K26
0x7C0C0600, // 005B CALL R3 3
0x600C000C, // 005C GETGBL R3 G12
0x5C100400, // 005D MOVE R4 R2
@ -362,7 +362,7 @@ be_local_closure(class_Matter_Frame_encode_frame, /* name */
0x880C0124, // 0071 GETMBR R3 R0 K36
0x780E0002, // 0072 JMPF R3 #0076
0x880C0121, // 0073 GETMBR R3 R0 K33
0x300C0719, // 0074 OR R3 R3 K25
0x300C071A, // 0074 OR R3 R3 K26
0x90024203, // 0075 SETMBR R0 K33 R3
0x880C0125, // 0076 GETMBR R3 R0 K37
0x780E0002, // 0077 JMPF R3 #007B
@ -387,11 +387,11 @@ be_local_closure(class_Matter_Frame_encode_frame, /* name */
0x2C140A06, // 008A AND R5 R5 R6
0x70020000, // 008B JMP #008D
0x58140012, // 008C LDCONST R5 K18
0x58180019, // 008D LDCONST R6 K25
0x5818001A, // 008D LDCONST R6 K26
0x7C0C0600, // 008E CALL R3 3
0x8C0C0509, // 008F GETMET R3 R2 K9
0x88140128, // 0090 GETMBR R5 R0 K40
0x58180019, // 0091 LDCONST R6 K25
0x5818001A, // 0091 LDCONST R6 K26
0x7C0C0600, // 0092 CALL R3 3
0x880C0124, // 0093 GETMBR R3 R0 K36
0x780E0003, // 0094 JMPF R3 #0099
@ -436,8 +436,8 @@ be_local_closure(class_Matter_Frame_decode_header, /* name */
0x58140012, // 0003 LDCONST R5 K18
0x5818000B, // 0004 LDCONST R6 K11
0x7C0C0600, // 0005 CALL R3 3
0x90021403, // 0006 SETMBR R0 K10 R3
0x880C010A, // 0007 GETMBR R3 R0 K10
0x90022A03, // 0006 SETMBR R0 K21 R3
0x880C0115, // 0007 GETMBR R3 R0 K21
0x541200F7, // 0008 LDINT R4 248
0x2C0C0604, // 0009 AND R3 R3 R4
0x200C0712, // 000A NE R3 R3 K18
@ -445,28 +445,28 @@ be_local_closure(class_Matter_Frame_decode_header, /* name */
0x500C0000, // 000C LDBOOL R3 0 0
0x80040600, // 000D RET 1 R3
0x8C0C052C, // 000E GETMET R3 R2 K44
0x58140019, // 000F LDCONST R5 K25
0x5814001A, // 000F LDCONST R5 K26
0x5818000B, // 0010 LDCONST R6 K11
0x7C0C0600, // 0011 CALL R3 3
0x90022A03, // 0012 SETMBR R0 K21 R3
0x90022C03, // 0012 SETMBR R0 K22 R3
0x8C0C052C, // 0013 GETMET R3 R2 K44
0x58140012, // 0014 LDCONST R5 K18
0x58180019, // 0015 LDCONST R6 K25
0x5818001A, // 0015 LDCONST R6 K26
0x7C0C0600, // 0016 CALL R3 3
0x90022C03, // 0017 SETMBR R0 K22 R3
0x880C0116, // 0018 GETMBR R3 R0 K22
0x1C0C0717, // 0019 EQ R3 R3 K23
0x90022E03, // 0017 SETMBR R0 K23 R3
0x880C0117, // 0018 GETMBR R3 R0 K23
0x1C0C0718, // 0019 EQ R3 R3 K24
0x780E0001, // 001A JMPF R3 #001D
0x500C0000, // 001B LDBOOL R3 0 0
0x80040600, // 001C RET 1 R3
0x8C0C052B, // 001D GETMET R3 R2 K43
0x58140017, // 001E LDCONST R5 K23
0x58140018, // 001E LDCONST R5 K24
0x5818000B, // 001F LDCONST R6 K11
0x7C0C0600, // 0020 CALL R3 3
0x90023403, // 0021 SETMBR R0 K26 R3
0x90021403, // 0021 SETMBR R0 K10 R3
0x8C0C052C, // 0022 GETMET R3 R2 K44
0x54160007, // 0023 LDINT R5 8
0x08162E05, // 0024 MUL R5 K23 R5
0x08163005, // 0024 MUL R5 K24 R5
0x541A0006, // 0025 LDINT R6 7
0x00140A06, // 0026 ADD R5 R5 R6
0x5818000B, // 0027 LDCONST R6 K11
@ -474,7 +474,7 @@ be_local_closure(class_Matter_Frame_decode_header, /* name */
0x90023603, // 0029 SETMBR R0 K27 R3
0x8C0C052C, // 002A GETMET R3 R2 K44
0x54160007, // 002B LDINT R5 8
0x08162E05, // 002C MUL R5 K23 R5
0x08163005, // 002C MUL R5 K24 R5
0x541A0005, // 002D LDINT R6 6
0x00140A06, // 002E ADD R5 R5 R6
0x5818000B, // 002F LDCONST R6 K11
@ -482,7 +482,7 @@ be_local_closure(class_Matter_Frame_decode_header, /* name */
0x90023803, // 0031 SETMBR R0 K28 R3
0x8C0C052C, // 0032 GETMET R3 R2 K44
0x54160007, // 0033 LDINT R5 8
0x08162E05, // 0034 MUL R5 K23 R5
0x08163005, // 0034 MUL R5 K24 R5
0x541A0004, // 0035 LDINT R6 5
0x00140A06, // 0036 ADD R5 R5 R6
0x5818000B, // 0037 LDCONST R6 K11
@ -490,8 +490,8 @@ be_local_closure(class_Matter_Frame_decode_header, /* name */
0x90025A03, // 0039 SETMBR R0 K45 R3
0x8C0C052C, // 003A GETMET R3 R2 K44
0x54160007, // 003B LDINT R5 8
0x08162E05, // 003C MUL R5 K23 R5
0x58180019, // 003D LDCONST R6 K25
0x08163005, // 003C MUL R5 K24 R5
0x5818001A, // 003D LDCONST R6 K26
0x7C0C0600, // 003E CALL R3 3
0x90023A03, // 003F SETMBR R0 K29 R3
0x880C011D, // 0040 GETMBR R3 R0 K29
@ -501,9 +501,9 @@ be_local_closure(class_Matter_Frame_decode_header, /* name */
0x80040600, // 0044 RET 1 R3
0x8C0C052B, // 0045 GETMET R3 R2 K43
0x5814000B, // 0046 LDCONST R5 K11
0x58180019, // 0047 LDCONST R6 K25
0x5818001A, // 0047 LDCONST R6 K26
0x7C0C0600, // 0048 CALL R3 3
0x90023003, // 0049 SETMBR R0 K24 R3
0x90023203, // 0049 SETMBR R0 K25 R3
0x8C0C052B, // 004A GETMET R3 R2 K43
0x54160003, // 004B LDINT R5 4
0x541A0003, // 004C LDINT R6 4
@ -511,7 +511,7 @@ be_local_closure(class_Matter_Frame_decode_header, /* name */
0x90021803, // 004E SETMBR R0 K12 R3
0x540E0007, // 004F LDINT R3 8
0x00040203, // 0050 ADD R1 R1 R3
0x880C0115, // 0051 GETMBR R3 R0 K21
0x880C0116, // 0051 GETMBR R3 R0 K22
0x780E0006, // 0052 JMPF R3 #005A
0x540E0006, // 0053 LDINT R3 7
0x000C0203, // 0054 ADD R3 R1 R3
@ -520,7 +520,7 @@ be_local_closure(class_Matter_Frame_decode_header, /* name */
0x90023C03, // 0057 SETMBR R0 K30 R3
0x540E0007, // 0058 LDINT R3 8
0x00040203, // 0059 ADD R1 R1 R3
0x880C0116, // 005A GETMBR R3 R0 K22
0x880C0117, // 005A GETMBR R3 R0 K23
0x1C0C070B, // 005B EQ R3 R3 K11
0x780E0007, // 005C JMPF R3 #0065
0x540E0006, // 005D LDINT R3 7
@ -531,22 +531,22 @@ be_local_closure(class_Matter_Frame_decode_header, /* name */
0x540E0007, // 0062 LDINT R3 8
0x00040203, // 0063 ADD R1 R1 R3
0x70020008, // 0064 JMP #006E
0x880C0116, // 0065 GETMBR R3 R0 K22
0x1C0C0719, // 0066 EQ R3 R3 K25
0x880C0117, // 0065 GETMBR R3 R0 K23
0x1C0C071A, // 0066 EQ R3 R3 K26
0x780E0005, // 0067 JMPF R3 #006E
0x8C0C052B, // 0068 GETMET R3 R2 K43
0x5C140200, // 0069 MOVE R5 R1
0x58180019, // 006A LDCONST R6 K25
0x5818001A, // 006A LDCONST R6 K26
0x7C0C0600, // 006B CALL R3 3
0x90024003, // 006C SETMBR R0 K32 R3
0x00040319, // 006D ADD R1 R1 K25
0x0004031A, // 006D ADD R1 R1 K26
0x880C012D, // 006E GETMBR R3 R0 K45
0x780E0005, // 006F JMPF R3 #0076
0x8C0C052B, // 0070 GETMET R3 R2 K43
0x5C140200, // 0071 MOVE R5 R1
0x58180019, // 0072 LDCONST R6 K25
0x5818001A, // 0072 LDCONST R6 K26
0x7C0C0600, // 0073 CALL R3 3
0x00100719, // 0074 ADD R4 R3 K25
0x0010071A, // 0074 ADD R4 R3 K26
0x00040204, // 0075 ADD R1 R1 R4
0x90020801, // 0076 SETMBR R0 K4 R1
0x500C0200, // 0077 LDBOOL R3 1 0
@ -620,16 +620,16 @@ be_local_closure(class_Matter_Frame_build_response, /* name */
0x900E2604, // 000A SETMBR R3 K19 R4
0x88100114, // 000B GETMBR R4 R0 K20
0x900E2804, // 000C SETMBR R3 K20 R4
0x88100115, // 000D GETMBR R4 R0 K21
0x88100116, // 000D GETMBR R4 R0 K22
0x78120003, // 000E JMPF R4 #0013
0x900E2D0B, // 000F SETMBR R3 K22 K11
0x900E2F0B, // 000F SETMBR R3 K23 K11
0x8810011E, // 0010 GETMBR R4 R0 K30
0x900E3E04, // 0011 SETMBR R3 K31 R4
0x70020000, // 0012 JMP #0014
0x900E2D12, // 0013 SETMBR R3 K22 K18
0x900E2F12, // 0013 SETMBR R3 K23 K18
0x88100100, // 0014 GETMBR R4 R0 K0
0x900E0004, // 0015 SETMBR R3 K0 R4
0x88100118, // 0016 GETMBR R4 R0 K24
0x88100119, // 0016 GETMBR R4 R0 K25
0x20100912, // 0017 NE R4 R4 K18
0x7812000D, // 0018 JMPF R4 #0027
0x88100100, // 0019 GETMBR R4 R0 K0
@ -644,14 +644,14 @@ be_local_closure(class_Matter_Frame_build_response, /* name */
0x900E1804, // 0022 SETMBR R3 K12 R4
0x88100100, // 0023 GETMBR R4 R0 K0
0x88100932, // 0024 GETMBR R4 R4 K50
0x900E3004, // 0025 SETMBR R3 K24 R4
0x900E3204, // 0025 SETMBR R3 K25 R4
0x70020005, // 0026 JMP #002D
0x88100100, // 0027 GETMBR R4 R0 K0
0x88100934, // 0028 GETMBR R4 R4 K52
0x8C100935, // 0029 GETMET R4 R4 K53
0x7C100200, // 002A CALL R4 1
0x900E1804, // 002B SETMBR R3 K12 R4
0x900E3112, // 002C SETMBR R3 K24 K18
0x900E3312, // 002C SETMBR R3 K25 K18
0x88100125, // 002D GETMBR R4 R0 K37
0x78120001, // 002E JMPF R4 #0031
0x58100012, // 002F LDCONST R4 K18
@ -673,7 +673,7 @@ be_local_closure(class_Matter_Frame_build_response, /* name */
0x70020000, // 003F JMP #0041
0x58100012, // 0040 LDCONST R4 K18
0x900E4604, // 0041 SETMBR R3 K35 R4
0x88100718, // 0042 GETMBR R4 R3 K24
0x88100719, // 0042 GETMBR R4 R3 K25
0x1C100912, // 0043 EQ R4 R4 K18
0x78120013, // 0044 JMPF R4 #0059
0xB8125C00, // 0045 GETNGBL R4 K46
@ -691,10 +691,10 @@ be_local_closure(class_Matter_Frame_build_response, /* name */
0x60180018, // 0051 GETGBL R6 G24
0x581C0039, // 0052 LDCONST R7 K57
0x88200700, // 0053 GETMBR R8 R3 K0
0x88201118, // 0054 GETMBR R8 R8 K24
0x88201119, // 0054 GETMBR R8 R8 K25
0x5C240800, // 0055 MOVE R9 R4
0x7C180600, // 0056 CALL R6 3
0x581C0017, // 0057 LDCONST R7 K23
0x581C0018, // 0057 LDCONST R7 K24
0x7C140400, // 0058 CALL R5 2
0x80040600, // 0059 RET 1 R3
})
@ -731,7 +731,7 @@ be_local_closure(class_Matter_Frame_decrypt, /* name */
0x781E002B, // 0008 JMPF R7 #0035
0xB81E7000, // 0009 GETNGBL R7 K56
0x5820003B, // 000A LDCONST R8 K59
0x58240019, // 000B LDCONST R9 K25
0x5824001A, // 000B LDCONST R9 K26
0x7C1C0400, // 000C CALL R7 2
0x8C1C053C, // 000D GETMET R7 R2 K60
0x7C1C0200, // 000E CALL R7 1
@ -741,7 +741,7 @@ be_local_closure(class_Matter_Frame_decrypt, /* name */
0x60240015, // 0012 GETGBL R9 G21
0x7C240000, // 0013 CALL R9 0
0x8C241309, // 0014 GETMET R9 R9 K9
0x882C0118, // 0015 GETMBR R11 R0 K24
0x882C0119, // 0015 GETMBR R11 R0 K25
0x5431FFFD, // 0016 LDINT R12 -2
0x7C240600, // 0017 CALL R9 3
0x542A0004, // 0018 LDINT R10 5
@ -761,10 +761,10 @@ be_local_closure(class_Matter_Frame_decrypt, /* name */
0x8C30193F, // 0026 GETMET R12 R12 K63
0x5C381400, // 0027 MOVE R14 R10
0x5C3C1200, // 0028 MOVE R15 R9
0x58400019, // 0029 LDCONST R16 K25
0x5840001A, // 0029 LDCONST R16 K26
0x7C300800, // 002A CALL R12 4
0x5C2C1800, // 002B MOVE R11 R12
0x40322517, // 002C CONNECT R12 K18 K23
0x40322518, // 002C CONNECT R12 K18 K24
0x88340103, // 002D GETMBR R13 R0 K3
0x94301A0C, // 002E GETIDX R12 R13 R12
0x0030180B, // 002F ADD R12 R12 R11
@ -832,7 +832,7 @@ be_local_closure(class_Matter_Frame_decrypt, /* name */
0x70020003, // 006D JMP #0072
0xB8267000, // 006E GETNGBL R9 K56
0x58280042, // 006F LDCONST R10 K66
0x582C0017, // 0070 LDCONST R11 K23
0x582C0018, // 0070 LDCONST R11 K24
0x7C240400, // 0071 CALL R9 2
0x80041000, // 0072 RET 1 R8
})
@ -867,13 +867,13 @@ be_local_closure(class_Matter_Frame_build_standalone_ack, /* name */
0x900A2603, // 0006 SETMBR R2 K19 R3
0x880C0114, // 0007 GETMBR R3 R0 K20
0x900A2803, // 0008 SETMBR R2 K20 R3
0x880C0115, // 0009 GETMBR R3 R0 K21
0x880C0116, // 0009 GETMBR R3 R0 K22
0x780E0003, // 000A JMPF R3 #000F
0x900A2D0B, // 000B SETMBR R2 K22 K11
0x900A2F0B, // 000B SETMBR R2 K23 K11
0x880C011E, // 000C GETMBR R3 R0 K30
0x900A3E03, // 000D SETMBR R2 K31 R3
0x70020000, // 000E JMP #0010
0x900A2D12, // 000F SETMBR R2 K22 K18
0x900A2F12, // 000F SETMBR R2 K23 K18
0x880C0100, // 0010 GETMBR R3 R0 K0
0x900A0003, // 0011 SETMBR R2 K0 R3
0x880C0100, // 0012 GETMBR R3 R0 K0
@ -882,7 +882,7 @@ be_local_closure(class_Matter_Frame_build_standalone_ack, /* name */
0x900A1803, // 0015 SETMBR R2 K12 R3
0x880C0100, // 0016 GETMBR R3 R0 K0
0x880C0732, // 0017 GETMBR R3 R3 K50
0x900A3003, // 0018 SETMBR R2 K24 R3
0x900A3203, // 0018 SETMBR R2 K25 R3
0x880C0125, // 0019 GETMBR R3 R0 K37
0x780E0001, // 001A JMPF R3 #001D
0x580C0012, // 001B LDCONST R3 K18
@ -939,7 +939,7 @@ be_local_closure(class_Matter_Frame_initiate_response, /* name */
0x90122606, // 000A SETMBR R4 K19 R6
0x88180345, // 000B GETMBR R6 R1 K69
0x90122806, // 000C SETMBR R4 K20 R6
0x90122D12, // 000D SETMBR R4 K22 K18
0x90122F12, // 000D SETMBR R4 K23 K18
0x90120001, // 000E SETMBR R4 K0 R1
0x78060008, // 000F JMPF R1 #0019
0x88180332, // 0010 GETMBR R6 R1 K50
@ -949,13 +949,13 @@ be_local_closure(class_Matter_Frame_initiate_response, /* name */
0x7C180200, // 0014 CALL R6 1
0x90121806, // 0015 SETMBR R4 K12 R6
0x88180332, // 0016 GETMBR R6 R1 K50
0x90123006, // 0017 SETMBR R4 K24 R6
0x90123206, // 0017 SETMBR R4 K25 R6
0x70020004, // 0018 JMP #001E
0x88180334, // 0019 GETMBR R6 R1 K52
0x8C180D35, // 001A GETMET R6 R6 K53
0x7C180200, // 001B CALL R6 1
0x90121806, // 001C SETMBR R4 K12 R6
0x90123112, // 001D SETMBR R4 K24 K18
0x90123312, // 001D SETMBR R4 K25 K18
0x90124B0B, // 001E SETMBR R4 K37 K11
0x90124C02, // 001F SETMBR R4 K38 R2
0x88180346, // 0020 GETMBR R6 R1 K70
@ -1013,14 +1013,14 @@ be_local_closure(class_Matter_Frame_decode_payload, /* name */
0x8C0C052C, // 000F GETMET R3 R2 K44
0x54160007, // 0010 LDINT R5 8
0x08140205, // 0011 MUL R5 R1 R5
0x00140B17, // 0012 ADD R5 R5 K23
0x00140B18, // 0012 ADD R5 R5 K24
0x5818000B, // 0013 LDCONST R6 K11
0x7C0C0600, // 0014 CALL R3 3
0x90028E03, // 0015 SETMBR R0 K71 R3
0x8C0C052C, // 0016 GETMET R3 R2 K44
0x54160007, // 0017 LDINT R5 8
0x08140205, // 0018 MUL R5 R1 R5
0x00140B19, // 0019 ADD R5 R5 K25
0x00140B1A, // 0019 ADD R5 R5 K26
0x5818000B, // 001A LDCONST R6 K11
0x7C0C0600, // 001B CALL R3 3
0x90024603, // 001C SETMBR R0 K35 R3
@ -1043,8 +1043,8 @@ be_local_closure(class_Matter_Frame_decode_payload, /* name */
0x7C0C0600, // 002D CALL R3 3
0x90024C03, // 002E SETMBR R0 K38 R3
0x8C0C052B, // 002F GETMET R3 R2 K43
0x00140319, // 0030 ADD R5 R1 K25
0x58180019, // 0031 LDCONST R6 K25
0x0014031A, // 0030 ADD R5 R1 K26
0x5818001A, // 0031 LDCONST R6 K26
0x7C0C0600, // 0032 CALL R3 3
0x90024E03, // 0033 SETMBR R0 K39 R3
0x880C0125, // 0034 GETMBR R3 R0 K37
@ -1056,7 +1056,7 @@ be_local_closure(class_Matter_Frame_decode_payload, /* name */
0x8C0C052B, // 003A GETMET R3 R2 K43
0x54160003, // 003B LDINT R5 4
0x00140205, // 003C ADD R5 R1 R5
0x58180019, // 003D LDCONST R6 K25
0x5818001A, // 003D LDCONST R6 K26
0x7C0C0600, // 003E CALL R3 3
0x90025003, // 003F SETMBR R0 K40 R3
0x540E0005, // 0040 LDINT R3 6
@ -1065,10 +1065,10 @@ be_local_closure(class_Matter_Frame_decode_payload, /* name */
0x780E0005, // 0043 JMPF R3 #004A
0x8C0C052B, // 0044 GETMET R3 R2 K43
0x5C140200, // 0045 MOVE R5 R1
0x58180019, // 0046 LDCONST R6 K25
0x5818001A, // 0046 LDCONST R6 K26
0x7C0C0600, // 0047 CALL R3 3
0x90029003, // 0048 SETMBR R0 K72 R3
0x00040319, // 0049 ADD R1 R1 K25
0x0004031A, // 0049 ADD R1 R1 K26
0x880C0124, // 004A GETMBR R3 R0 K36
0x780E0006, // 004B JMPF R3 #0053
0x8C0C052B, // 004C GETMET R3 R2 K43
@ -1082,9 +1082,9 @@ be_local_closure(class_Matter_Frame_decode_payload, /* name */
0x780E0005, // 0054 JMPF R3 #005B
0x8C0C052B, // 0055 GETMET R3 R2 K43
0x5C140200, // 0056 MOVE R5 R1
0x58180019, // 0057 LDCONST R6 K25
0x5818001A, // 0057 LDCONST R6 K26
0x7C0C0600, // 0058 CALL R3 3
0x00100719, // 0059 ADD R4 R3 K25
0x0010071A, // 0059 ADD R4 R3 K26
0x00040204, // 005A ADD R1 R1 R4
0x90025401, // 005B SETMBR R0 K42 R1
0x80040000, // 005C RET 1 R0

View File

@ -1,6 +1,6 @@
/********************************************************************
* Tasmota lib
*
*
* To use: `import MI32`
*******************************************************************/
#include "be_constobj.h"
@ -25,6 +25,9 @@ BE_FUNC_CTYPE_DECLARE(be_MI32_set_hum, "", "ii");
extern void be_MI32_set_temp(int slot, int temp_val);
BE_FUNC_CTYPE_DECLARE(be_MI32_set_temp, "", "ii");
extern bbool be_MI32_widget(const char *sbuf, void* function);
BE_FUNC_CTYPE_DECLARE(be_MI32_widget, "b", "s[c]");
#include "be_fixed_MI32.h"
/* @const_object_info_begin
@ -35,12 +38,13 @@ module MI32 (scope: global) {
set_bat, ctype_func(be_MI32_set_bat)
set_hum, ctype_func(be_MI32_set_hum)
set_temp, ctype_func(be_MI32_set_temp)
widget, ctype_func(be_MI32_widget)
}
@const_object_info_end */
/********************************************************************
* Tasmota lib
*
*
* To use: `import BLE`
*******************************************************************/

View File

@ -6,7 +6,7 @@
#include "be_constobj.h"
#include "be_mapping.h"
#if defined(USE_BERRY_ULP) && (defined(CONFIG_IDF_TARGET_ESP32) || defined(CONFIG_IDF_TARGET_ESP32S2) || defined(CONFIG_IDF_TARGET_ESP32S3))
#if defined(USE_BERRY_ULP) && defined(CONFIG_ULP_COPROC_ENABLED)
extern void be_ULP_run(int32_t entry);
BE_FUNC_CTYPE_DECLARE(be_ULP_run, "", "[i]");

View File

@ -1,5 +1,5 @@
/********************************************************************
* Tasmota LVGL lv_signal_bars widget
* Drivers for AXP192 and AXP202 I2C Solidified
*******************************************************************/
#include "solidify/solidified_i2c_axp192.h"
#include "solidify/solidified_i2c_axp202.h"

View File

@ -7,7 +7,9 @@
#ifdef USE_WS2812
extern int be_neopixelbus_call_native(bvm *vm);
#include "TasmotaLED.h"
extern int be_tasmotaled_call_native(bvm *vm);
extern int be_leds_blend_color(bvm *vm);
extern int be_leds_apply_bri_gamma(bvm *vm);
@ -16,10 +18,15 @@ class be_class_Leds_ntv (scope: global, name: Leds_ntv, strings: weak) {
_p, var
_t, var
WS2812_GRB, int(1)
SK6812_GRBW, int(2)
WS2812_GRB, int(ws2812_grb)
SK6812_GRBW, int(sk6812_grbw)
SK6812_GRB, int(sk6812_grb)
call_native, func(be_neopixelbus_call_native)
RMT, int(TasmotaLed_RMT)
SPI, int(TasmotaLed_SPI)
I2S, int(TasmotaLed_I2S)
call_native, func(be_tasmotaled_call_native)
blend_color, static_func(be_leds_blend_color)
apply_bri_gamma, static_func(be_leds_apply_bri_gamma)

View File

@ -124,7 +124,7 @@ autoconf_module.init = def (m)
# Displays a "Autoconf" button on the configuration page
def web_add_config_button()
import webserver
webserver.content_send("<p><form id=ac action='ac' style='display: block;' method='get'><button>Auto-configuration</button></form></p>")
webserver.content_send("<p><form id=ac action='ac' style='display: block;' method='get'><button>Auto-Conf</button></form></p>")
end

View File

@ -56,6 +56,15 @@ class I2C_Driver
def write8(reg, val)
return self.wire.write(self.addr, reg, val, 1)
end
#- write register with 16 bits value -#
def write16(reg, val)
return self.wire.write(self.addr, reg, val, 2)
end
#- write register with 16 bits value, Little Endian -#
def write16LE(reg, val)
val = ((val & 0xFF) << 8) | ((val & 0xFF00) >> 8)
return self.write16(reg, val)
end
# Set or clear a specific bit in a register
# write_bit(reg:int, bit:int, state:bool) -> nil
@ -94,6 +103,11 @@ class I2C_Driver
var buf = self.wire.read_bytes(self.addr, reg, 2)
return (buf[0] << 8) + buf[1]
end
# read 16 bits Little Endian
def read16LE(reg)
var buf = self.wire.read_bytes(self.addr, reg, 2)
return (buf[1] << 8) + buf[0]
end
# read 24 bits
def read24(reg)
var buf = self.wire.read_bytes(self.addr, reg, 3)

View File

@ -31,8 +31,8 @@ class Leds : Leds_ntv
# leds:int = number of leds of the strip
# gpio:int (optional) = GPIO for NeoPixel. If not specified, takes the WS2812 gpio
# typ:int (optional) = Type of LED, defaults to WS2812 RGB
# rmt:int (optional) = RMT hardware channel to use, leave default unless you have a good reason
def init(leds, gpio_phy, typ, rmt) # rmt is optional
# hardware:int (optional) = hardware support (Leds.RMT, Leds.SPI)
def init(leds, gpio_phy, typ, hardware)
import gpio
self.gamma = true # gamma is enabled by default, it should be disabled explicitly if needed
if (gpio_phy == nil) || (gpio_phy == gpio.pin(gpio.WS2812, 0))
@ -47,7 +47,7 @@ class Leds : Leds_ntv
self.bri = 127 # 50% brightness by default
# initialize the structure
self.ctor(self.leds, gpio_phy, typ, rmt)
self.ctor(self.leds, gpio_phy, typ, hardware)
end
if self._p == nil raise "internal_error", "couldn't not initialize noepixelbus" end
@ -56,44 +56,6 @@ class Leds : Leds_ntv
self.begin()
end
# assign RMT
static def assign_rmt(gpio_phy)
gpio_phy = int(gpio_phy)
if gpio_phy < 0 raise "value_error", "invalid GPIO number" end
import global
var rmt
# if "_rmt" is not initialized, set to an array of GPIO of size MAX_RMT
if !global.contains("_rmt")
rmt = []
global._rmt = rmt
for i:0..gpio.MAX_RMT-1
rmt.push(-1)
end
# if default WS2812 is set, assign RMT0
if gpio.pin_used(gpio.WS2812, 0)
rmt[0] = gpio.pin(gpio.WS2812, 0)
end
end
rmt = global._rmt
# find an already assigned slot or try to assign a new one
var i = 0
var first_free = -1
while i < gpio.MAX_RMT
var elt = rmt[i]
if elt == gpio_phy return i end # already assigned
if elt < 0 && first_free < 0 first_free = i end # found a free slot
i += 1
end
if first_free >= 0
rmt[first_free] = gpio_phy
return first_free
end
# no more slot
raise "internal_error", "no more RMT channel available"
end
def clear()
self.clear_to(0x000000)
self.show()
@ -109,17 +71,14 @@ class Leds : Leds_ntv
return self.bri
end
def ctor(leds, gpio_phy, typ, rmt)
def ctor(leds, gpio_phy, typ, hardware)
if gpio_phy == nil
self.call_native(0) # native driver
else
if typ == nil
typ = self.WS2812_GRB
end
if rmt == nil
rmt = self.assign_rmt(gpio_phy)
end
self.call_native(0, leds, gpio_phy, typ, rmt)
self.call_native(0, leds, gpio_phy, typ, hardware)
end
end
def begin()
@ -155,9 +114,13 @@ class Leds : Leds_ntv
def pixel_offset()
return 0
end
def clear_to(col, bri)
def clear_to(col, bri, index, len)
if (bri == nil) bri = self.bri end
self.call_native(9, self.to_gamma(col, bri))
if index != nil && len != nil
self.call_native(9, self.to_gamma(col, bri), index, len)
else
self.call_native(9, self.to_gamma(col, bri))
end
end
def set_pixel_color(idx, col, bri)
if (bri == nil) bri = self.bri end
@ -403,15 +366,15 @@ anim()
#-
var s = Leds_matrix(5, 5, gpio.pin(gpio.WS2812, 1))
var s = Leds(25, gpio.pin(gpio.WS2812, 1)).create_matrix(5, 5)
s.set_alternate(true)
s.clear_to(0x300000)
s.clear_to(0x400000)
s.show()
x = 0
y = 0
def anim()
s.clear_to(0x300000)
s.clear_to(0x400000)
s.set_matrix_pixel_color(x, y, 0x004000)
s.show()
y = (y + 1) % 5

View File

@ -121,7 +121,7 @@ static const bvalue be_ktab_class_Autoconf[126] = {
/* K113 */ be_nested_str(CFG_X3A_X20loaded_X20_X27_X25s_X27),
/* K114 */ be_nested_str(files),
/* K115 */ be_nested_str(CFG_X3A_X20exception_X20_X27_X25s_X27_X20_X2D_X20_X27_X25s_X27),
/* K116 */ be_nested_str(_X3Cp_X3E_X3Cform_X20id_X3Dac_X20action_X3D_X27ac_X27_X20style_X3D_X27display_X3A_X20block_X3B_X27_X20method_X3D_X27get_X27_X3E_X3Cbutton_X3EAuto_X2Dconfiguration_X3C_X2Fbutton_X3E_X3C_X2Fform_X3E_X3C_X2Fp_X3E),
/* K116 */ be_nested_str(_X3Cp_X3E_X3Cform_X20id_X3Dac_X20action_X3D_X27ac_X27_X20style_X3D_X27display_X3A_X20block_X3B_X27_X20method_X3D_X27get_X27_X3E_X3Cbutton_X3EAuto_X2DConf_X3C_X2Fbutton_X3E_X3C_X2Fform_X3E_X3C_X2Fp_X3E),
/* K117 */ be_nested_str(add_driver),
/* K118 */ be_nested_str(CFG_X3A_X20multiple_X20autoconf_X20files_X20found_X2C_X20aborting_X20_X28_X27_X25s_X27_X20_X2B_X20_X27_X25s_X27_X29),
/* K119 */ be_nested_str(CFG_X3A_X20No_X20_X27_X2A_X2Eautoconf_X27_X20file_X20found),

View File

@ -3,81 +3,33 @@
* Generated code, don't edit *
\********************************************************************/
#include "be_constobj.h"
// compact class 'I2C_Driver' ktab size: 19, total: 60 (saved 328 bytes)
static const bvalue be_ktab_class_I2C_Driver[19] = {
/* K0 */ be_const_int(0),
/* K1 */ be_const_int(1),
/* K2 */ be_nested_str(write8),
/* K3 */ be_nested_str(read8),
/* K4 */ be_nested_str(wire),
/* K5 */ be_nested_str(read_bytes),
/* K6 */ be_nested_str(addr),
/* K7 */ be_const_int(2),
/* K8 */ be_const_int(3),
/* K9 */ be_nested_str(tasmota),
/* K10 */ be_nested_str(i2c_enabled),
/* K11 */ be_nested_str(wire_scan),
/* K12 */ be_nested_str(function),
/* K13 */ be_nested_str(name),
/* K14 */ be_nested_str(I2C_X3A),
/* K15 */ be_nested_str(detected_X20on_X20bus),
/* K16 */ be_nested_str(bus),
/* K17 */ be_nested_str(write),
// compact class 'I2C_Driver' ktab size: 20, total: 71 (saved 408 bytes)
static const bvalue be_ktab_class_I2C_Driver[20] = {
/* K0 */ be_nested_str(wire),
/* K1 */ be_nested_str(read_bytes),
/* K2 */ be_nested_str(addr),
/* K3 */ be_const_int(0),
/* K4 */ be_const_int(1),
/* K5 */ be_const_int(2),
/* K6 */ be_const_int(3),
/* K7 */ be_nested_str(tasmota),
/* K8 */ be_nested_str(i2c_enabled),
/* K9 */ be_nested_str(wire_scan),
/* K10 */ be_nested_str(function),
/* K11 */ be_nested_str(name),
/* K12 */ be_nested_str(I2C_X3A),
/* K13 */ be_nested_str(detected_X20on_X20bus),
/* K14 */ be_nested_str(bus),
/* K15 */ be_nested_str(write8),
/* K16 */ be_nested_str(read8),
/* K17 */ be_nested_str(write16),
/* K18 */ be_nested_str(read),
/* K19 */ be_nested_str(write),
};
extern const bclass be_class_I2C_Driver;
/********************************************************************
** Solidified function: write_bit
********************************************************************/
be_local_closure(class_I2C_Driver_write_bit, /* name */
be_nested_proto(
11, /* nstack */
4, /* argc */
10, /* varg */
0, /* has upvals */
NULL, /* no upvals */
0, /* has sup protos */
NULL, /* no sub protos */
1, /* has constants */
&be_ktab_class_I2C_Driver, /* shared constants */
&be_const_str_write_bit,
&be_const_str_solidified,
( &(const binstruction[26]) { /* code */
0x14100500, // 0000 LT R4 R2 K0
0x74120002, // 0001 JMPT R4 #0005
0x54120006, // 0002 LDINT R4 7
0x24100404, // 0003 GT R4 R2 R4
0x78120000, // 0004 JMPF R4 #0006
0x80000800, // 0005 RET 0
0x38120202, // 0006 SHL R4 K1 R2
0x780E0007, // 0007 JMPF R3 #0010
0x8C140102, // 0008 GETMET R5 R0 K2
0x5C1C0200, // 0009 MOVE R7 R1
0x8C200103, // 000A GETMET R8 R0 K3
0x5C280200, // 000B MOVE R10 R1
0x7C200400, // 000C CALL R8 2
0x30201004, // 000D OR R8 R8 R4
0x7C140600, // 000E CALL R5 3
0x70020008, // 000F JMP #0019
0x8C140102, // 0010 GETMET R5 R0 K2
0x5C1C0200, // 0011 MOVE R7 R1
0x8C200103, // 0012 GETMET R8 R0 K3
0x5C280200, // 0013 MOVE R10 R1
0x7C200400, // 0014 CALL R8 2
0x542600FE, // 0015 LDINT R9 255
0x04241204, // 0016 SUB R9 R9 R4
0x2C201009, // 0017 AND R8 R8 R9
0x7C140600, // 0018 CALL R5 3
0x80000000, // 0019 RET 0
})
)
);
/*******************************************************************/
/********************************************************************
** Solidified function: read32
********************************************************************/
@ -95,24 +47,24 @@ be_local_closure(class_I2C_Driver_read32, /* name */
&be_const_str_read32,
&be_const_str_solidified,
( &(const binstruction[20]) { /* code */
0x88080104, // 0000 GETMBR R2 R0 K4
0x8C080505, // 0001 GETMET R2 R2 K5
0x88100106, // 0002 GETMBR R4 R0 K6
0x88080100, // 0000 GETMBR R2 R0 K0
0x8C080501, // 0001 GETMET R2 R2 K1
0x88100102, // 0002 GETMBR R4 R0 K2
0x5C140200, // 0003 MOVE R5 R1
0x541A0003, // 0004 LDINT R6 4
0x7C080800, // 0005 CALL R2 4
0x940C0500, // 0006 GETIDX R3 R2 K0
0x940C0503, // 0006 GETIDX R3 R2 K3
0x54120017, // 0007 LDINT R4 24
0x380C0604, // 0008 SHL R3 R3 R4
0x94100501, // 0009 GETIDX R4 R2 K1
0x94100504, // 0009 GETIDX R4 R2 K4
0x5416000F, // 000A LDINT R5 16
0x38100805, // 000B SHL R4 R4 R5
0x000C0604, // 000C ADD R3 R3 R4
0x94100507, // 000D GETIDX R4 R2 K7
0x94100505, // 000D GETIDX R4 R2 K5
0x54160007, // 000E LDINT R5 8
0x38100805, // 000F SHL R4 R4 R5
0x000C0604, // 0010 ADD R3 R3 R4
0x94100508, // 0011 GETIDX R4 R2 K8
0x94100506, // 0011 GETIDX R4 R2 K6
0x000C0604, // 0012 ADD R3 R3 R4
0x80040600, // 0013 RET 1 R3
})
@ -121,115 +73,6 @@ be_local_closure(class_I2C_Driver_read32, /* name */
/*******************************************************************/
/********************************************************************
** Solidified function: read13
********************************************************************/
be_local_closure(class_I2C_Driver_read13, /* name */
be_nested_proto(
7, /* nstack */
2, /* argc */
10, /* varg */
0, /* has upvals */
NULL, /* no upvals */
0, /* has sup protos */
NULL, /* no sub protos */
1, /* has constants */
&be_ktab_class_I2C_Driver, /* shared constants */
&be_const_str_read13,
&be_const_str_solidified,
( &(const binstruction[12]) { /* code */
0x88080104, // 0000 GETMBR R2 R0 K4
0x8C080505, // 0001 GETMET R2 R2 K5
0x88100106, // 0002 GETMBR R4 R0 K6
0x5C140200, // 0003 MOVE R5 R1
0x58180007, // 0004 LDCONST R6 K7
0x7C080800, // 0005 CALL R2 4
0x940C0500, // 0006 GETIDX R3 R2 K0
0x54120004, // 0007 LDINT R4 5
0x380C0604, // 0008 SHL R3 R3 R4
0x94100501, // 0009 GETIDX R4 R2 K1
0x000C0604, // 000A ADD R3 R3 R4
0x80040600, // 000B RET 1 R3
})
)
);
/*******************************************************************/
/********************************************************************
** Solidified function: read24
********************************************************************/
be_local_closure(class_I2C_Driver_read24, /* name */
be_nested_proto(
7, /* nstack */
2, /* argc */
10, /* varg */
0, /* has upvals */
NULL, /* no upvals */
0, /* has sup protos */
NULL, /* no sub protos */
1, /* has constants */
&be_ktab_class_I2C_Driver, /* shared constants */
&be_const_str_read24,
&be_const_str_solidified,
( &(const binstruction[16]) { /* code */
0x88080104, // 0000 GETMBR R2 R0 K4
0x8C080505, // 0001 GETMET R2 R2 K5
0x88100106, // 0002 GETMBR R4 R0 K6
0x5C140200, // 0003 MOVE R5 R1
0x58180008, // 0004 LDCONST R6 K8
0x7C080800, // 0005 CALL R2 4
0x940C0500, // 0006 GETIDX R3 R2 K0
0x5412000F, // 0007 LDINT R4 16
0x380C0604, // 0008 SHL R3 R3 R4
0x94100501, // 0009 GETIDX R4 R2 K1
0x54160007, // 000A LDINT R5 8
0x38100805, // 000B SHL R4 R4 R5
0x000C0604, // 000C ADD R3 R3 R4
0x94100507, // 000D GETIDX R4 R2 K7
0x000C0604, // 000E ADD R3 R3 R4
0x80040600, // 000F RET 1 R3
})
)
);
/*******************************************************************/
/********************************************************************
** Solidified function: read14
********************************************************************/
be_local_closure(class_I2C_Driver_read14, /* name */
be_nested_proto(
7, /* nstack */
2, /* argc */
10, /* varg */
0, /* has upvals */
NULL, /* no upvals */
0, /* has sup protos */
NULL, /* no sub protos */
1, /* has constants */
&be_ktab_class_I2C_Driver, /* shared constants */
&be_const_str_read14,
&be_const_str_solidified,
( &(const binstruction[12]) { /* code */
0x88080104, // 0000 GETMBR R2 R0 K4
0x8C080505, // 0001 GETMET R2 R2 K5
0x88100106, // 0002 GETMBR R4 R0 K6
0x5C140200, // 0003 MOVE R5 R1
0x58180007, // 0004 LDCONST R6 K7
0x7C080800, // 0005 CALL R2 4
0x940C0500, // 0006 GETIDX R3 R2 K0
0x54120005, // 0007 LDINT R4 6
0x380C0604, // 0008 SHL R3 R3 R4
0x94100501, // 0009 GETIDX R4 R2 K1
0x000C0604, // 000A ADD R3 R3 R4
0x80040600, // 000B RET 1 R3
})
)
);
/*******************************************************************/
/********************************************************************
** Solidified function: init
********************************************************************/
@ -250,45 +93,45 @@ be_local_closure(class_I2C_Driver_init, /* name */
0x4C100000, // 0000 LDNIL R4
0x20100604, // 0001 NE R4 R3 R4
0x78120005, // 0002 JMPF R4 #0009
0xB8121200, // 0003 GETNGBL R4 K9
0x8C10090A, // 0004 GETMET R4 R4 K10
0xB8120E00, // 0003 GETNGBL R4 K7
0x8C100908, // 0004 GETMET R4 R4 K8
0x5C180600, // 0005 MOVE R6 R3
0x7C100400, // 0006 CALL R4 2
0x74120000, // 0007 JMPT R4 #0009
0x80000800, // 0008 RET 0
0x90020C02, // 0009 SETMBR R0 K6 R2
0xB8121200, // 000A GETNGBL R4 K9
0x8C10090B, // 000B GETMET R4 R4 K11
0x88180106, // 000C GETMBR R6 R0 K6
0x90020402, // 0009 SETMBR R0 K2 R2
0xB8120E00, // 000A GETNGBL R4 K7
0x8C100909, // 000B GETMET R4 R4 K9
0x88180102, // 000C GETMBR R6 R0 K2
0x7C100400, // 000D CALL R4 2
0x90020804, // 000E SETMBR R0 K4 R4
0x88100104, // 000F GETMBR R4 R0 K4
0x90020004, // 000E SETMBR R0 K0 R4
0x88100100, // 000F GETMBR R4 R0 K0
0x78120019, // 0010 JMPF R4 #002B
0x60100004, // 0011 GETGBL R4 G4
0x5C140200, // 0012 MOVE R5 R1
0x7C100200, // 0013 CALL R4 1
0x1C10090C, // 0014 EQ R4 R4 K12
0x1C10090A, // 0014 EQ R4 R4 K10
0x78120004, // 0015 JMPF R4 #001B
0x5C100200, // 0016 MOVE R4 R1
0x5C140000, // 0017 MOVE R5 R0
0x7C100200, // 0018 CALL R4 1
0x90021A04, // 0019 SETMBR R0 K13 R4
0x90021604, // 0019 SETMBR R0 K11 R4
0x70020000, // 001A JMP #001C
0x90021A01, // 001B SETMBR R0 K13 R1
0x8810010D, // 001C GETMBR R4 R0 K13
0x90021601, // 001B SETMBR R0 K11 R1
0x8810010B, // 001C GETMBR R4 R0 K11
0x4C140000, // 001D LDNIL R5
0x1C100805, // 001E EQ R4 R4 R5
0x78120001, // 001F JMPF R4 #0022
0x4C100000, // 0020 LDNIL R4
0x90020804, // 0021 SETMBR R0 K4 R4
0x88100104, // 0022 GETMBR R4 R0 K4
0x90020004, // 0021 SETMBR R0 K0 R4
0x88100100, // 0022 GETMBR R4 R0 K0
0x78120006, // 0023 JMPF R4 #002B
0x60100001, // 0024 GETGBL R4 G1
0x5814000E, // 0025 LDCONST R5 K14
0x8818010D, // 0026 GETMBR R6 R0 K13
0x581C000F, // 0027 LDCONST R7 K15
0x88200104, // 0028 GETMBR R8 R0 K4
0x88201110, // 0029 GETMBR R8 R8 K16
0x5814000C, // 0025 LDCONST R5 K12
0x8818010B, // 0026 GETMBR R6 R0 K11
0x581C000D, // 0027 LDCONST R7 K13
0x88200100, // 0028 GETMBR R8 R0 K0
0x8820110E, // 0029 GETMBR R8 R8 K14
0x7C100800, // 002A CALL R4 4
0x80000000, // 002B RET 0
})
@ -298,11 +141,99 @@ be_local_closure(class_I2C_Driver_init, /* name */
/********************************************************************
** Solidified function: write8
** Solidified function: read24
********************************************************************/
be_local_closure(class_I2C_Driver_write8, /* name */
be_local_closure(class_I2C_Driver_read24, /* name */
be_nested_proto(
9, /* nstack */
7, /* nstack */
2, /* argc */
10, /* varg */
0, /* has upvals */
NULL, /* no upvals */
0, /* has sup protos */
NULL, /* no sub protos */
1, /* has constants */
&be_ktab_class_I2C_Driver, /* shared constants */
&be_const_str_read24,
&be_const_str_solidified,
( &(const binstruction[16]) { /* code */
0x88080100, // 0000 GETMBR R2 R0 K0
0x8C080501, // 0001 GETMET R2 R2 K1
0x88100102, // 0002 GETMBR R4 R0 K2
0x5C140200, // 0003 MOVE R5 R1
0x58180006, // 0004 LDCONST R6 K6
0x7C080800, // 0005 CALL R2 4
0x940C0503, // 0006 GETIDX R3 R2 K3
0x5412000F, // 0007 LDINT R4 16
0x380C0604, // 0008 SHL R3 R3 R4
0x94100504, // 0009 GETIDX R4 R2 K4
0x54160007, // 000A LDINT R5 8
0x38100805, // 000B SHL R4 R4 R5
0x000C0604, // 000C ADD R3 R3 R4
0x94100505, // 000D GETIDX R4 R2 K5
0x000C0604, // 000E ADD R3 R3 R4
0x80040600, // 000F RET 1 R3
})
)
);
/*******************************************************************/
/********************************************************************
** Solidified function: write_bit
********************************************************************/
be_local_closure(class_I2C_Driver_write_bit, /* name */
be_nested_proto(
11, /* nstack */
4, /* argc */
10, /* varg */
0, /* has upvals */
NULL, /* no upvals */
0, /* has sup protos */
NULL, /* no sub protos */
1, /* has constants */
&be_ktab_class_I2C_Driver, /* shared constants */
&be_const_str_write_bit,
&be_const_str_solidified,
( &(const binstruction[26]) { /* code */
0x14100503, // 0000 LT R4 R2 K3
0x74120002, // 0001 JMPT R4 #0005
0x54120006, // 0002 LDINT R4 7
0x24100404, // 0003 GT R4 R2 R4
0x78120000, // 0004 JMPF R4 #0006
0x80000800, // 0005 RET 0
0x38120802, // 0006 SHL R4 K4 R2
0x780E0007, // 0007 JMPF R3 #0010
0x8C14010F, // 0008 GETMET R5 R0 K15
0x5C1C0200, // 0009 MOVE R7 R1
0x8C200110, // 000A GETMET R8 R0 K16
0x5C280200, // 000B MOVE R10 R1
0x7C200400, // 000C CALL R8 2
0x30201004, // 000D OR R8 R8 R4
0x7C140600, // 000E CALL R5 3
0x70020008, // 000F JMP #0019
0x8C14010F, // 0010 GETMET R5 R0 K15
0x5C1C0200, // 0011 MOVE R7 R1
0x8C200110, // 0012 GETMET R8 R0 K16
0x5C280200, // 0013 MOVE R10 R1
0x7C200400, // 0014 CALL R8 2
0x542600FE, // 0015 LDINT R9 255
0x04241204, // 0016 SUB R9 R9 R4
0x2C201009, // 0017 AND R8 R8 R9
0x7C140600, // 0018 CALL R5 3
0x80000000, // 0019 RET 0
})
)
);
/*******************************************************************/
/********************************************************************
** Solidified function: write16LE
********************************************************************/
be_local_closure(class_I2C_Driver_write16LE, /* name */
be_nested_proto(
7, /* nstack */
3, /* argc */
10, /* varg */
0, /* has upvals */
@ -311,17 +242,24 @@ be_local_closure(class_I2C_Driver_write8, /* name */
NULL, /* no sub protos */
1, /* has constants */
&be_ktab_class_I2C_Driver, /* shared constants */
&be_const_str_write8,
&be_const_str_write16LE,
&be_const_str_solidified,
( &(const binstruction[ 8]) { /* code */
0x880C0104, // 0000 GETMBR R3 R0 K4
0x8C0C0711, // 0001 GETMET R3 R3 K17
0x88140106, // 0002 GETMBR R5 R0 K6
0x5C180200, // 0003 MOVE R6 R1
0x5C1C0400, // 0004 MOVE R7 R2
0x58200001, // 0005 LDCONST R8 K1
0x7C0C0A00, // 0006 CALL R3 5
0x80040600, // 0007 RET 1 R3
( &(const binstruction[15]) { /* code */
0x540E00FE, // 0000 LDINT R3 255
0x2C0C0403, // 0001 AND R3 R2 R3
0x54120007, // 0002 LDINT R4 8
0x380C0604, // 0003 SHL R3 R3 R4
0x5412FEFF, // 0004 LDINT R4 65280
0x2C100404, // 0005 AND R4 R2 R4
0x54160007, // 0006 LDINT R5 8
0x3C100805, // 0007 SHR R4 R4 R5
0x300C0604, // 0008 OR R3 R3 R4
0x5C080600, // 0009 MOVE R2 R3
0x8C0C0111, // 000A GETMET R3 R0 K17
0x5C140200, // 000B MOVE R5 R1
0x5C180400, // 000C MOVE R6 R2
0x7C0C0600, // 000D CALL R3 3
0x80040600, // 000E RET 1 R3
})
)
);
@ -345,11 +283,11 @@ be_local_closure(class_I2C_Driver_read8, /* name */
&be_const_str_read8,
&be_const_str_solidified,
( &(const binstruction[ 7]) { /* code */
0x88080104, // 0000 GETMBR R2 R0 K4
0x88080100, // 0000 GETMBR R2 R0 K0
0x8C080512, // 0001 GETMET R2 R2 K18
0x88100106, // 0002 GETMBR R4 R0 K6
0x88100102, // 0002 GETMBR R4 R0 K2
0x5C140200, // 0003 MOVE R5 R1
0x58180001, // 0004 LDCONST R6 K1
0x58180004, // 0004 LDCONST R6 K4
0x7C080800, // 0005 CALL R2 4
0x80040400, // 0006 RET 1 R2
})
@ -358,6 +296,76 @@ be_local_closure(class_I2C_Driver_read8, /* name */
/*******************************************************************/
/********************************************************************
** Solidified function: read14
********************************************************************/
be_local_closure(class_I2C_Driver_read14, /* name */
be_nested_proto(
7, /* nstack */
2, /* argc */
10, /* varg */
0, /* has upvals */
NULL, /* no upvals */
0, /* has sup protos */
NULL, /* no sub protos */
1, /* has constants */
&be_ktab_class_I2C_Driver, /* shared constants */
&be_const_str_read14,
&be_const_str_solidified,
( &(const binstruction[12]) { /* code */
0x88080100, // 0000 GETMBR R2 R0 K0
0x8C080501, // 0001 GETMET R2 R2 K1
0x88100102, // 0002 GETMBR R4 R0 K2
0x5C140200, // 0003 MOVE R5 R1
0x58180005, // 0004 LDCONST R6 K5
0x7C080800, // 0005 CALL R2 4
0x940C0503, // 0006 GETIDX R3 R2 K3
0x54120005, // 0007 LDINT R4 6
0x380C0604, // 0008 SHL R3 R3 R4
0x94100504, // 0009 GETIDX R4 R2 K4
0x000C0604, // 000A ADD R3 R3 R4
0x80040600, // 000B RET 1 R3
})
)
);
/*******************************************************************/
/********************************************************************
** Solidified function: read16LE
********************************************************************/
be_local_closure(class_I2C_Driver_read16LE, /* name */
be_nested_proto(
7, /* nstack */
2, /* argc */
10, /* varg */
0, /* has upvals */
NULL, /* no upvals */
0, /* has sup protos */
NULL, /* no sub protos */
1, /* has constants */
&be_ktab_class_I2C_Driver, /* shared constants */
&be_const_str_read16LE,
&be_const_str_solidified,
( &(const binstruction[12]) { /* code */
0x88080100, // 0000 GETMBR R2 R0 K0
0x8C080501, // 0001 GETMET R2 R2 K1
0x88100102, // 0002 GETMBR R4 R0 K2
0x5C140200, // 0003 MOVE R5 R1
0x58180005, // 0004 LDCONST R6 K5
0x7C080800, // 0005 CALL R2 4
0x940C0504, // 0006 GETIDX R3 R2 K4
0x54120007, // 0007 LDINT R4 8
0x380C0604, // 0008 SHL R3 R3 R4
0x94100503, // 0009 GETIDX R4 R2 K3
0x000C0604, // 000A ADD R3 R3 R4
0x80040600, // 000B RET 1 R3
})
)
);
/*******************************************************************/
/********************************************************************
** Solidified function: read12
********************************************************************/
@ -375,16 +383,82 @@ be_local_closure(class_I2C_Driver_read12, /* name */
&be_const_str_read12,
&be_const_str_solidified,
( &(const binstruction[12]) { /* code */
0x88080104, // 0000 GETMBR R2 R0 K4
0x8C080505, // 0001 GETMET R2 R2 K5
0x88100106, // 0002 GETMBR R4 R0 K6
0x88080100, // 0000 GETMBR R2 R0 K0
0x8C080501, // 0001 GETMET R2 R2 K1
0x88100102, // 0002 GETMBR R4 R0 K2
0x5C140200, // 0003 MOVE R5 R1
0x58180007, // 0004 LDCONST R6 K7
0x58180005, // 0004 LDCONST R6 K5
0x7C080800, // 0005 CALL R2 4
0x940C0500, // 0006 GETIDX R3 R2 K0
0x940C0503, // 0006 GETIDX R3 R2 K3
0x54120003, // 0007 LDINT R4 4
0x380C0604, // 0008 SHL R3 R3 R4
0x94100501, // 0009 GETIDX R4 R2 K1
0x94100504, // 0009 GETIDX R4 R2 K4
0x000C0604, // 000A ADD R3 R3 R4
0x80040600, // 000B RET 1 R3
})
)
);
/*******************************************************************/
/********************************************************************
** Solidified function: write8
********************************************************************/
be_local_closure(class_I2C_Driver_write8, /* name */
be_nested_proto(
9, /* nstack */
3, /* argc */
10, /* varg */
0, /* has upvals */
NULL, /* no upvals */
0, /* has sup protos */
NULL, /* no sub protos */
1, /* has constants */
&be_ktab_class_I2C_Driver, /* shared constants */
&be_const_str_write8,
&be_const_str_solidified,
( &(const binstruction[ 8]) { /* code */
0x880C0100, // 0000 GETMBR R3 R0 K0
0x8C0C0713, // 0001 GETMET R3 R3 K19
0x88140102, // 0002 GETMBR R5 R0 K2
0x5C180200, // 0003 MOVE R6 R1
0x5C1C0400, // 0004 MOVE R7 R2
0x58200004, // 0005 LDCONST R8 K4
0x7C0C0A00, // 0006 CALL R3 5
0x80040600, // 0007 RET 1 R3
})
)
);
/*******************************************************************/
/********************************************************************
** Solidified function: read13
********************************************************************/
be_local_closure(class_I2C_Driver_read13, /* name */
be_nested_proto(
7, /* nstack */
2, /* argc */
10, /* varg */
0, /* has upvals */
NULL, /* no upvals */
0, /* has sup protos */
NULL, /* no sub protos */
1, /* has constants */
&be_ktab_class_I2C_Driver, /* shared constants */
&be_const_str_read13,
&be_const_str_solidified,
( &(const binstruction[12]) { /* code */
0x88080100, // 0000 GETMBR R2 R0 K0
0x8C080501, // 0001 GETMET R2 R2 K1
0x88100102, // 0002 GETMBR R4 R0 K2
0x5C140200, // 0003 MOVE R5 R1
0x58180005, // 0004 LDCONST R6 K5
0x7C080800, // 0005 CALL R2 4
0x940C0503, // 0006 GETIDX R3 R2 K3
0x54120004, // 0007 LDINT R4 5
0x380C0604, // 0008 SHL R3 R3 R4
0x94100504, // 0009 GETIDX R4 R2 K4
0x000C0604, // 000A ADD R3 R3 R4
0x80040600, // 000B RET 1 R3
})
@ -410,16 +484,16 @@ be_local_closure(class_I2C_Driver_read16, /* name */
&be_const_str_read16,
&be_const_str_solidified,
( &(const binstruction[12]) { /* code */
0x88080104, // 0000 GETMBR R2 R0 K4
0x8C080505, // 0001 GETMET R2 R2 K5
0x88100106, // 0002 GETMBR R4 R0 K6
0x88080100, // 0000 GETMBR R2 R0 K0
0x8C080501, // 0001 GETMET R2 R2 K1
0x88100102, // 0002 GETMBR R4 R0 K2
0x5C140200, // 0003 MOVE R5 R1
0x58180007, // 0004 LDCONST R6 K7
0x58180005, // 0004 LDCONST R6 K5
0x7C080800, // 0005 CALL R2 4
0x940C0500, // 0006 GETIDX R3 R2 K0
0x940C0503, // 0006 GETIDX R3 R2 K3
0x54120007, // 0007 LDINT R4 8
0x380C0604, // 0008 SHL R3 R3 R4
0x94100501, // 0009 GETIDX R4 R2 K1
0x94100504, // 0009 GETIDX R4 R2 K4
0x000C0604, // 000A ADD R3 R3 R4
0x80040600, // 000B RET 1 R3
})
@ -428,27 +502,61 @@ be_local_closure(class_I2C_Driver_read16, /* name */
/*******************************************************************/
/********************************************************************
** Solidified function: write16
********************************************************************/
be_local_closure(class_I2C_Driver_write16, /* name */
be_nested_proto(
9, /* nstack */
3, /* argc */
10, /* varg */
0, /* has upvals */
NULL, /* no upvals */
0, /* has sup protos */
NULL, /* no sub protos */
1, /* has constants */
&be_ktab_class_I2C_Driver, /* shared constants */
&be_const_str_write16,
&be_const_str_solidified,
( &(const binstruction[ 8]) { /* code */
0x880C0100, // 0000 GETMBR R3 R0 K0
0x8C0C0713, // 0001 GETMET R3 R3 K19
0x88140102, // 0002 GETMBR R5 R0 K2
0x5C180200, // 0003 MOVE R6 R1
0x5C1C0400, // 0004 MOVE R7 R2
0x58200005, // 0005 LDCONST R8 K5
0x7C0C0A00, // 0006 CALL R3 5
0x80040600, // 0007 RET 1 R3
})
)
);
/*******************************************************************/
/********************************************************************
** Solidified class: I2C_Driver
********************************************************************/
be_local_class(I2C_Driver,
3,
NULL,
be_nested_map(13,
be_nested_map(16,
( (struct bmapnode*) &(const bmapnode[]) {
{ be_const_key(write_bit, -1), be_const_closure(class_I2C_Driver_write_bit_closure) },
{ be_const_key(addr, -1), be_const_var(1) },
{ be_const_key(read32, -1), be_const_closure(class_I2C_Driver_read32_closure) },
{ be_const_key(write16, 15), be_const_closure(class_I2C_Driver_write16_closure) },
{ be_const_key(addr, -1), be_const_var(1) },
{ be_const_key(init, 1), be_const_closure(class_I2C_Driver_init_closure) },
{ be_const_key(write_bit, -1), be_const_closure(class_I2C_Driver_write_bit_closure) },
{ be_const_key(read14, -1), be_const_closure(class_I2C_Driver_read14_closure) },
{ be_const_key(name, -1), be_const_var(2) },
{ be_const_key(read8, -1), be_const_closure(class_I2C_Driver_read8_closure) },
{ be_const_key(write16LE, 11), be_const_closure(class_I2C_Driver_write16LE_closure) },
{ be_const_key(read16LE, -1), be_const_closure(class_I2C_Driver_read16LE_closure) },
{ be_const_key(read12, -1), be_const_closure(class_I2C_Driver_read12_closure) },
{ be_const_key(wire, 5), be_const_var(0) },
{ be_const_key(write8, -1), be_const_closure(class_I2C_Driver_write8_closure) },
{ be_const_key(read13, -1), be_const_closure(class_I2C_Driver_read13_closure) },
{ be_const_key(read16, -1), be_const_closure(class_I2C_Driver_read16_closure) },
{ be_const_key(read14, -1), be_const_closure(class_I2C_Driver_read14_closure) },
{ be_const_key(read24, 12), be_const_closure(class_I2C_Driver_read24_closure) },
{ be_const_key(name, 4), be_const_var(2) },
{ be_const_key(write8, -1), be_const_closure(class_I2C_Driver_write8_closure) },
{ be_const_key(wire, 8), be_const_var(0) },
{ be_const_key(read8, -1), be_const_closure(class_I2C_Driver_read8_closure) },
{ be_const_key(read12, -1), be_const_closure(class_I2C_Driver_read12_closure) },
{ be_const_key(init, -1), be_const_closure(class_I2C_Driver_init_closure) },
{ be_const_key(read24, -1), be_const_closure(class_I2C_Driver_read24_closure) },
})),
(bstring*) &be_const_str_I2C_Driver
);

File diff suppressed because it is too large Load Diff

View File

@ -31,6 +31,7 @@ idf_component_register(
"esp32c2"
"esp32c3"
"esp32c6"
"esp32h2"
INCLUDE_DIRS
"src"
SRCS
@ -58,6 +59,7 @@ idf_component_register(
REQUIRES
bt
nvs_flash
driver
PRIV_REQUIRES
${ESP_NIMBLE_PRIV_REQUIRES}
)

View File

@ -33,7 +33,7 @@ config NIMBLE_CPP_ENABLE_RETURN_CODE_TEXT
Enabling this option will display return code values as text
messages in the debug log. This will use approximately 8kB
of flash memory.
config NIMBLE_CPP_ENABLE_GAP_EVENT_CODE_TEXT
bool "Show NimBLE gap events as text in debug log."
default "n"
@ -47,7 +47,7 @@ config NIMBLE_CPP_ENABLE_ADVERTISEMENT_TYPE_TEXT
default "n"
help
Enabling this option will display advertisment types recieved
while scanning as text messages in the debug log.
while scanning as text messages in the debug log.
This will use approximately 250 bytes of flash memory.
config NIMBLE_CPP_ATT_VALUE_TIMESTAMP_ENABLED
@ -68,5 +68,12 @@ config NIMBLE_CPP_ATT_VALUE_INIT_LENGTH
when the constructor is called. This is also the size used when a remote
characteristic or descriptor is constructed before a value is read/notifed.
Increasing this will reduce reallocations but increase memory footprint.
config NIMBLE_CPP_DEBUG_ASSERT_ENABLED
bool "Enable debug asserts."
default "n"
help
Enabling this option will add debug asserts to the NimBLE CPP library.
This will use approximately 1kB of flash memory.
endmenu

View File

@ -66,7 +66,7 @@ If false the service is only removed from visibility by clients. The pointers to
# Advertising
`NimBLEAdvertising::start`
Now takes 2 optional parameters, the first is the duration to advertise for (in seconds), the second is a callback that is invoked when advertising ends and takes a pointer to a `NimBLEAdvertising` object (similar to the `NimBLEScan::start` API).
Now takes 2 optional parameters, the first is the duration to advertise for (in milliseconds), the second is a callback that is invoked when advertising ends and takes a pointer to a `NimBLEAdvertising` object (similar to the `NimBLEScan::start` API).
This provides an opportunity to update the advertisement data if desired.

View File

@ -255,7 +255,7 @@ Calling `NimBLEAdvertising::setAdvertisementData` will entirely replace any data
> BLEAdvertising::start (NimBLEAdvertising::start)
Now takes 2 optional parameters, the first is the duration to advertise for (in seconds), the second is a callback that is invoked when advertising ends and takes a pointer to a `NimBLEAdvertising` object (similar to the `NimBLEScan::start` API).
Now takes 2 optional parameters, the first is the duration to advertise for (in milliseconds), the second is a callback that is invoked when advertising ends and takes a pointer to a `NimBLEAdvertising` object (similar to the `NimBLEScan::start` API).
This provides an opportunity to update the advertisement data if desired.
<br/>
@ -383,18 +383,23 @@ The security callback methods are now incorporated in the `NimBLEServerCallbacks
The callback methods are:
> `bool onConfirmPIN(uint32_t pin)`
> `bool onConfirmPIN(const NimBLEConnInfo& connInfo, uint32_t pin)`
Receives the pin when using numeric comparison authentication, `return true;` to accept.
Receives the pin when using numeric comparison authentication.
Call `NimBLEDevice::injectConfirmPIN(connInfo, true);` to accept or `NimBLEDevice::injectConfirmPIN(connInfo, false);` to reject.
<br/>
> `uint32_t onPassKeyRequest()`
> `void onPassKeyEntry(const NimBLEConnInfo& connInfo)`
For server callback; return the passkey expected from the client.
For client callback; return the passkey to send to the server.
Client callback; client should respond with the passkey (pin) by calling `NimBLEDevice::injectPassKey(connInfo, 123456);`
<br/>
> `void onAuthenticationComplete(NimBLEConnInfo& connInfo)`
> `uint32_t onPassKeyDisplay()`
Server callback; should return the passkey (pin) expected from the client.
<br/>
> `void onAuthenticationComplete(const NimBLEConnInfo& connInfo)`
Authentication complete, success or failed information is available from the `NimBLEConnInfo` methods.
<br/>

View File

@ -39,20 +39,22 @@ class ClientCallbacks : public NimBLEClientCallbacks {
/********************* Security handled here **********************
****** Note: these are the same return values as defaults ********/
uint32_t onPassKeyRequest(){
printf("Client Passkey Request\n");
/** return the passkey to send to the server */
return 123456;
}
void onPassKeyEntry(const NimBLEConnInfo& connInfo){
printf("Server Passkey Entry\n");
/** This should prompt the user to enter the passkey displayed
* on the peer device.
*/
NimBLEDevice::injectPassKey(connInfo, 123456);
};
bool onConfirmPIN(uint32_t pass_key){
printf("The passkey YES/NO number: %" PRIu32"\n", pass_key);
/** Return false if passkeys don't match. */
return true;
}
void onConfirmPIN(const NimBLEConnInfo& connInfo, uint32_t pass_key){
printf("The passkey YES/NO number: %" PRIu32 "\n", pass_key);
/** Inject false if passkeys don't match. */
NimBLEDevice::injectConfirmPIN(connInfo, true);
};
/** Pairing process complete, we can check the results in connInfo */
void onAuthenticationComplete(NimBLEConnInfo& connInfo){
void onAuthenticationComplete(const NimBLEConnInfo& connInfo){
if(!connInfo.isEncrypted()) {
printf("Encrypt connection failed - disconnecting\n");
/** Find the client with the connection handle provided in desc */
@ -146,8 +148,8 @@ bool connectToServer() {
* Min interval: 12 * 1.25ms = 15, Max interval: 12 * 1.25ms = 15, 0 latency, 12 * 10ms = 120ms timeout
*/
pClient->setConnectionParams(6,6,0,15);
/** Set how long we are willing to wait for the connection to complete (seconds), default is 30. */
pClient->setConnectTimeout(5);
/** Set how long we are willing to wait for the connection to complete (milliseconds), default is 30000. */
pClient->setConnectTimeout(5 * 1000);
if (!pClient->connect(advDevice)) {
@ -358,7 +360,7 @@ void app_main (void){
* but will use more energy from both devices
*/
pScan->setActiveScan(true);
/** Start scanning for advertisers for the scan time specified (in seconds) 0 = forever
/** Start scanning for advertisers for the scan time specified (in milliseconds) 0 = forever
* Optional callback for when scanning stops.
*/
pScan->start(scanTime);

View File

@ -44,21 +44,21 @@ class ServerCallbacks: public NimBLEServerCallbacks {
/********************* Security handled here **********************
****** Note: these are the same return values as defaults ********/
uint32_t onPassKeyRequest(){
printf("Server Passkey Request\n");
uint32_t onPassKeyDisplay(){
printf("Server Passkey Display\n");
/** This should return a random 6 digit number for security
* or make your own static passkey as done here.
*/
return 123456;
};
bool onConfirmPIN(uint32_t pass_key){
printf("The passkey YES/NO number: %" PRIu32"\n", pass_key);
/** Return false if passkeys don't match. */
return true;
void onConfirmPIN(const NimBLEConnInfo& connInfo, uint32_t pass_key){
printf("The passkey YES/NO number: %" PRIu32 "\n", pass_key);
/** Inject false if passkeys don't match. */
NimBLEDevice::injectConfirmPIN(connInfo, true);
};
void onAuthenticationComplete(NimBLEConnInfo& connInfo){
void onAuthenticationComplete(const NimBLEConnInfo& connInfo){
/** Check that encryption was successful, if not we disconnect the client */
if(!connInfo.isEncrypted()) {
NimBLEDevice::getServer()->disconnect(connInfo.getConnHandle());

View File

@ -154,7 +154,7 @@ void app_main (void) {
*/
pScan->setActiveScan(true);
/* Start scanning for advertisers for the scan time specified (in seconds) 0 = forever
/* Start scanning for advertisers for the scan time specified (in milliseconds) 0 = forever
* Optional callback for when scanning stops.
*/
pScan->start(scanTime);

View File

@ -1,48 +0,0 @@
/*
* NimBLE Scan active/passive switching demo
*
* Demonstrates the use of the scan callbacks while alternating between passive and active scanning.
*/
#include "NimBLEDevice.h"
int scanTime = 5 * 1000; // In milliseconds, 0 = scan forever
BLEScan* pBLEScan;
bool active = false;
class scanCallbacks: public NimBLEScanCallbacks {
void onDiscovered(NimBLEAdvertisedDevice* advertisedDevice) {
Serial.printf("Discovered Advertised Device: %s \n", advertisedDevice->toString().c_str());
}
void onResult(NimBLEAdvertisedDevice* advertisedDevice) {
Serial.printf("Advertised Device Result: %s \n", advertisedDevice->toString().c_str());
}
void onScanEnd(NimBLEScanResults results){
Serial.println("Scan Ended");
active = !active;
pBLEScan->setActiveScan(active);
Serial.printf("scan start, active = %u\n", active);
pBLEScan->start(scanTime);
}
};
void setup() {
Serial.begin(115200);
Serial.println("Scanning...");
NimBLEDevice::init("");
pBLEScan = NimBLEDevice::getScan();
pBLEScan->setScanCallbacks(new scanCallbacks());
pBLEScan->setActiveScan(active);
pBLEScan->setInterval(100);
pBLEScan->setWindow(99);
pBLEScan->start(scanTime);
}
void loop() {
}

View File

@ -0,0 +1,7 @@
# The following lines of boilerplate have to be in your project's
# CMakeLists in this exact order for cmake to work correctly
cmake_minimum_required(VERSION 3.5)
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
set(SUPPORTED_TARGETS esp32)
project(NimBLE_server_get_client_name)

View File

@ -0,0 +1,4 @@
set(COMPONENT_SRCS "main.cpp")
set(COMPONENT_ADD_INCLUDEDIRS ".")
register_component()

View File

@ -0,0 +1,83 @@
/** NimBLE_server_get_client_name
*
* Demonstrates 2 ways for the server to read the device name from the connected client.
*
* Created: on June 24 2024
* Author: H2zero
*
*/
#include <NimBLEDevice.h>
// See the following for generating UUIDs:
// https://www.uuidgenerator.net/
#define SERVICE_UUID "4fafc201-1fb5-459e-8fcc-c5c9c331914b"
#define CHARACTERISTIC_UUID "beb5483e-36e1-4688-b7f5-ea07361b26a8"
#define ENC_CHARACTERISTIC_UUID "9551f35b-8d91-42e4-8f7e-1358dfe272dc"
NimBLEServer* pServer;
class ServerCallbacks : public NimBLEServerCallbacks {
// Same as before but now includes the name parameter
void onConnect(NimBLEServer* pServer, NimBLEConnInfo& connInfo, std::string& name) override {
printf("Client address: %s Name: %s\n", connInfo.getAddress().toString().c_str(), name.c_str());
}
// Same as before but now includes the name parameter
void onAuthenticationComplete(const NimBLEConnInfo& connInfo, const std::string& name) override {
if (!connInfo.isEncrypted()) {
NimBLEDevice::getServer()->disconnect(connInfo.getConnHandle());
printf("Encrypt connection failed - disconnecting client\n");
return;
}
printf("Encrypted Client address: %s Name: %s\n", connInfo.getAddress().toString().c_str(), name.c_str());
}
};
extern "C" void app_main(void) {
printf("Starting BLE Server!\n");
NimBLEDevice::init("Connect to me!");
NimBLEDevice::setSecurityAuth(true, false, true); // Enable bonding to see full name on phones.
pServer = NimBLEDevice::createServer();
NimBLEService* pService = pServer->createService(SERVICE_UUID);
NimBLECharacteristic* pCharacteristic =
pService->createCharacteristic(CHARACTERISTIC_UUID, NIMBLE_PROPERTY::READ | NIMBLE_PROPERTY::WRITE);
pCharacteristic->setValue("Hello World says NimBLE!");
NimBLECharacteristic* pEncCharacteristic = pService->createCharacteristic(
ENC_CHARACTERISTIC_UUID,
(NIMBLE_PROPERTY::READ | NIMBLE_PROPERTY::WRITE | NIMBLE_PROPERTY::READ_ENC | NIMBLE_PROPERTY::WRITE_ENC));
pEncCharacteristic->setValue("Hello World says NimBLE Encrypted");
pService->start();
pServer->setCallbacks(new ServerCallbacks());
pServer->getPeerNameOnConnect(true); // Setting this will enable the onConnect callback that provides the name.
BLEAdvertising* pAdvertising = NimBLEDevice::getAdvertising();
pAdvertising->addServiceUUID(SERVICE_UUID);
pAdvertising->setScanResponse(true);
pAdvertising->start();
printf("Advertising started, connect with your phone.\n");
while (true) {
auto clientCount = pServer->getConnectedCount();
if (clientCount) {
printf("Connected clients:\n");
for (auto i = 0; i < clientCount; ++i) {
NimBLEConnInfo peerInfo = pServer->getPeerInfo(i);
printf("Client address: %s Name: %s\n", peerInfo.getAddress().toString().c_str(),
// This function blocks until the name is retrieved, so cannot be used in callback functions.
pServer->getPeerName(peerInfo).c_str());
}
}
vTaskDelay(pdMS_TO_TICKS(10000));
}
}

View File

@ -51,17 +51,28 @@ class MyClientCallback : public BLEClientCallbacks {
}
/***************** New - Security handled here ********************
****** Note: these are the same return values as defaults ********/
uint32_t onPassKeyRequest(){
printf("Client PassKeyRequest\n");
return 123456;
}
bool onConfirmPIN(uint32_t pass_key){
printf("The passkey YES/NO number: %" PRIu32"\n", pass_key);
return true;
}
void onPassKeyEntry(const NimBLEConnInfo& connInfo){
printf("Server Passkey Entry\n");
/** This should prompt the user to enter the passkey displayed
* on the peer device.
*/
NimBLEDevice::injectPassKey(connInfo, 123456);
};
void onAuthenticationComplete(BLEConnInfo& connInfo){
printf("Starting BLE work!\n");
void onConfirmPIN(const NimBLEConnInfo& connInfo, uint32_t pass_key){
printf("The passkey YES/NO number: %" PRIu32 "\n", pass_key);
/** Inject false if passkeys don't match. */
NimBLEDevice::injectConfirmPIN(connInfo, true);
};
/** Pairing process complete, we can check the results in connInfo */
void onAuthenticationComplete(const NimBLEConnInfo& connInfo){
if(!connInfo.isEncrypted()) {
printf("Encrypt connection failed - disconnecting\n");
/** Find the client with the connection handle provided in desc */
NimBLEDevice::getClientByID(connInfo.getConnHandle())->disconnect();
return;
}
}
/*******************************************************************/
};

View File

@ -57,19 +57,29 @@ class MyServerCallbacks: public BLEServerCallbacks {
}
/***************** New - Security handled here ********************
****** Note: these are the same return values as defaults ********/
uint32_t onPassKeyRequest(){
printf("Server PassKeyRequest\n");
return 123456;
}
uint32_t onPassKeyDisplay(){
printf("Server Passkey Display\n");
/** This should return a random 6 digit number for security
* or make your own static passkey as done here.
*/
return 123456;
};
bool onConfirmPIN(uint32_t pass_key){
printf("The passkey YES/NO number: %" PRIu32"\n", pass_key);
return true;
}
void onConfirmPIN(const NimBLEConnInfo& connInfo, uint32_t pass_key){
printf("The passkey YES/NO number: %" PRIu32 "\n", pass_key);
/** Inject false if passkeys don't match. */
NimBLEDevice::injectConfirmPIN(connInfo, true);
};
void onAuthenticationComplete(BLEConnInfo& connInfo){
printf("Starting BLE work!\n");
}
void onAuthenticationComplete(const NimBLEConnInfo& connInfo){
/** Check that encryption was successful, if not we disconnect the client */
if(!connInfo.isEncrypted()) {
NimBLEDevice::getServer()->disconnect(connInfo.getConnHandle());
printf("Encrypt connection failed - disconnecting client\n");
return;
}
printf("Starting BLE work!");
};
/*******************************************************************/
};
@ -128,7 +138,6 @@ void app_main(void) {
NIMBLE_PROPERTY::INDICATE
);
// https://www.bluetooth.com/specifications/gatt/viewer?attributeXmlFile=org.bluetooth.descriptor.gatt.client_characteristic_configuration.xml
// Create a BLE Descriptor
/***************************************************
NOTE: DO NOT create a 2902 descriptor.

View File

@ -59,19 +59,29 @@ class MyServerCallbacks: public BLEServerCallbacks {
}
/***************** New - Security handled here ********************
****** Note: these are the same return values as defaults ********/
uint32_t onPassKeyRequest(){
printf("Server PassKeyRequest\n");
return 123456;
}
uint32_t onPassKeyDisplay(){
printf("Server Passkey Display\n");
/** This should return a random 6 digit number for security
* or make your own static passkey as done here.
*/
return 123456;
};
bool onConfirmPIN(uint32_t pass_key){
printf("The passkey YES/NO number: %" PRIu32"\n", pass_key);
return true;
}
void onConfirmPIN(const NimBLEConnInfo& connInfo, uint32_t pass_key){
printf("The passkey YES/NO number: %" PRIu32 "\n", pass_key);
/** Inject false if passkeys don't match. */
NimBLEDevice::injectConfirmPIN(connInfo, true);
};
void onAuthenticationComplete(BLEConnInfo& connInfo){
printf("Starting BLE work!\n");
}
void onAuthenticationComplete(const NimBLEConnInfo& connInfo){
/** Check that encryption was successful, if not we disconnect the client */
if(!connInfo.isEncrypted()) {
NimBLEDevice::getServer()->disconnect(connInfo.getConnHandle());
printf("Encrypt connection failed - disconnecting client\n");
return;
}
printf("Starting BLE work!");
};
/*******************************************************************/
};

View File

@ -0,0 +1,17 @@
{
"name": "esp-nimble-cpp",
"version": "1.5.0",
"description": "NimBLE, BLE stack for the Espressif ESP32, ESP32-S and ESP32-C series of SoCs",
"keywords": [
"BLE",
"espidf",
"arduino",
"espressif",
"esp32"
],
"license": "LGPL-2.1-or-later",
"repository": {
"type": "git",
"url": "https://github.com/h2zero/esp-nimble-cpp"
}
}

View File

@ -12,10 +12,6 @@
* Author: kolban
*/
/*
* See also:
* https://www.bluetooth.com/specifications/gatt/viewer?attributeXmlFile=org.bluetooth.descriptor.gatt.characteristic_presentation_format.xml
*/
#include "nimconfig.h"
#if defined(CONFIG_BT_ENABLED) && defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL)

View File

@ -33,9 +33,6 @@ struct BLE2904_Data {
* @brief Descriptor for Characteristic Presentation Format.
*
* This is a convenience descriptor for the Characteristic Presentation Format which has a UUID of 0x2904.
*
* See also:
* https://www.bluetooth.com/specifications/gatt/viewer?attributeXmlFile=org.bluetooth.descriptor.gatt.characteristic_presentation_format.xml
*/
class NimBLE2904: public NimBLEDescriptor {
public:

View File

@ -138,6 +138,15 @@ uint8_t NimBLEAddress::getType() const {
} // getType
/**
* @brief Determine if this address is a Resolvable Private Address.
* @return True if the address is a RPA.
*/
bool NimBLEAddress::isRpa() const {
return (m_addrType && ((m_address[5] & 0xc0) == 0x40));
} // isRpa
/**
* @brief Convert a BLE address to a string.
*

View File

@ -43,6 +43,7 @@ public:
NimBLEAddress(uint8_t address[6], uint8_t type = BLE_ADDR_PUBLIC);
NimBLEAddress(const std::string &stringAddress, uint8_t type = BLE_ADDR_PUBLIC);
NimBLEAddress(const uint64_t &address, uint8_t type = BLE_ADDR_PUBLIC);
bool isRpa() const;
bool equals(const NimBLEAddress &otherAddress) const;
const uint8_t* getNative() const;
std::string toString() const;

View File

@ -203,6 +203,24 @@ std::string NimBLEAdvertisedDevice::getURI() {
return "";
} // getURI
/**
* @brief Get the data from any type available in the advertisement
* @param [in] type The advertised data type BLE_HS_ADV_TYPE
* @return The data available under the type `type`
*/
std::string NimBLEAdvertisedDevice::getPayloadByType(uint16_t type) {
size_t data_loc = 0;
if(findAdvField(type, 0, &data_loc) > 0) {
ble_hs_adv_field *field = (ble_hs_adv_field *)&m_payload[data_loc];
if(field->length > 1) {
return std::string((char*)field->value, field->length - 1);
}
}
return "";
} // getPayloadByType
/**
* @brief Get the advertised name.
@ -556,6 +574,14 @@ bool NimBLEAdvertisedDevice::haveURI() {
return findAdvField(BLE_HS_ADV_TYPE_URI) > 0;
} // haveURI
/**
* @brief Does this advertisement have a adv type `type`?
* @return True if there is a `type` present.
*/
bool NimBLEAdvertisedDevice::haveType(uint16_t type) {
return findAdvField(type) > 0;
}
/**
* @brief Does the advertisement contain a target address?

View File

@ -53,6 +53,7 @@ public:
uint8_t getManufacturerDataCount();
std::string getManufacturerData(uint8_t index = 0);
std::string getURI();
std::string getPayloadByType(uint16_t type);
/**
* @brief A template to convert the service data to <type\>.
@ -134,6 +135,7 @@ public:
bool haveAdvInterval();
bool haveTargetAddress();
bool haveURI();
bool haveType(uint16_t type);
std::string toString();
bool isConnectable();
bool isLegacyAdvertisement();

View File

@ -96,8 +96,8 @@ void NimBLEAdvertising::addServiceUUID(const char* serviceUUID) {
/**
* @brief Add a service uuid to exposed list of services.
* @param [in] serviceUUID The UUID of the service to expose.
* @brief Remove a service UUID from the advertisment.
* @param [in] serviceUUID The UUID of the service to remove.
*/
void NimBLEAdvertising::removeServiceUUID(const NimBLEUUID &serviceUUID) {
for(auto it = m_serviceUUIDs.begin(); it != m_serviceUUIDs.end(); ++it) {
@ -110,10 +110,17 @@ void NimBLEAdvertising::removeServiceUUID(const NimBLEUUID &serviceUUID) {
} // addServiceUUID
/**
* @brief Remove all service UUIDs from the advertisment.
*/
void NimBLEAdvertising::removeServices() {
std::vector<NimBLEUUID>().swap(m_serviceUUIDs);
m_advDataSet = false;
} // removeServices
/**
* @brief Set the device appearance in the advertising data.
* The codes for distinct appearances can be found here:\n
* https://www.bluetooth.com/specifications/gatt/viewer?attributeXmlFile=org.bluetooth.characteristic.gap.appearance.xml.
* @param [in] appearance The appearance of the device in the advertising data.
*/
void NimBLEAdvertising::setAppearance(uint16_t appearance) {
@ -137,7 +144,7 @@ void NimBLEAdvertising::addTxPower() {
* @param [in] name The name to advertise.
*/
void NimBLEAdvertising::setName(const std::string &name) {
m_name.assign(name.begin(), name.end());
std::vector<uint8_t>(name.begin(), name.end()).swap(m_name);
m_advData.name = &m_name[0];
m_advData.name_len = m_name.size();
m_advDataSet = false;
@ -149,7 +156,7 @@ void NimBLEAdvertising::setName(const std::string &name) {
* @param [in] data The data to advertise.
*/
void NimBLEAdvertising::setManufacturerData(const std::string &data) {
m_mfgData.assign(data.begin(), data.end());
std::vector<uint8_t>(data.begin(), data.end()).swap(m_mfgData);
m_advData.mfg_data = &m_mfgData[0];
m_advData.mfg_data_len = m_mfgData.size();
m_advDataSet = false;
@ -173,7 +180,7 @@ void NimBLEAdvertising::setManufacturerData(const std::vector<uint8_t> &data) {
* @param [in] uri The URI to advertise.
*/
void NimBLEAdvertising::setURI(const std::string &uri) {
m_uri.assign(uri.begin(), uri.end());
std::vector<uint8_t>(uri.begin(), uri.end()).swap(m_uri);
m_advData.uri = &m_uri[0];
m_advData.uri_len = m_uri.size();
m_advDataSet = false;
@ -189,7 +196,8 @@ void NimBLEAdvertising::setURI(const std::string &uri) {
void NimBLEAdvertising::setServiceData(const NimBLEUUID &uuid, const std::string &data) {
switch (uuid.bitSize()) {
case 16: {
m_svcData16.assign((uint8_t*)&uuid.getNative()->u16.value, (uint8_t*)&uuid.getNative()->u16.value + 2);
std::vector<uint8_t>((uint8_t*)&uuid.getNative()->u16.value,
(uint8_t*)&uuid.getNative()->u16.value + 2).swap(m_svcData16);
m_svcData16.insert(m_svcData16.end(), data.begin(), data.end());
m_advData.svc_data_uuid16 = (uint8_t*)&m_svcData16[0];
m_advData.svc_data_uuid16_len = (data.length() > 0) ? m_svcData16.size() : 0;
@ -197,7 +205,8 @@ void NimBLEAdvertising::setServiceData(const NimBLEUUID &uuid, const std::string
}
case 32: {
m_svcData32.assign((uint8_t*)&uuid.getNative()->u32.value, (uint8_t*)&uuid.getNative()->u32.value + 4);
std::vector<uint8_t>((uint8_t*)&uuid.getNative()->u32.value,
(uint8_t*)&uuid.getNative()->u32.value + 4).swap(m_svcData32);
m_svcData32.insert(m_svcData32.end(), data.begin(), data.end());
m_advData.svc_data_uuid32 = (uint8_t*)&m_svcData32[0];
m_advData.svc_data_uuid32_len = (data.length() > 0) ? m_svcData32.size() : 0;
@ -205,7 +214,8 @@ void NimBLEAdvertising::setServiceData(const NimBLEUUID &uuid, const std::string
}
case 128: {
m_svcData128.assign(uuid.getNative()->u128.value, uuid.getNative()->u128.value + 16);
std::vector<uint8_t>(uuid.getNative()->u128.value,
uuid.getNative()->u128.value + 16).swap(m_svcData128);
m_svcData128.insert(m_svcData128.end(), data.begin(), data.end());
m_advData.svc_data_uuid128 = (uint8_t*)&m_svcData128[0];
m_advData.svc_data_uuid128_len = (data.length() > 0) ? m_svcData128.size() : 0;
@ -402,7 +412,7 @@ void NimBLEAdvertising::setScanResponseData(NimBLEAdvertisementData& advertiseme
* @param [in] dirAddr The address of a peer to directly advertise to.
* @return True if advertising started successfully.
*/
bool NimBLEAdvertising::start(uint32_t duration, void (*advCompleteCB)(NimBLEAdvertising *pAdv), NimBLEAddress* dirAddr) {
bool NimBLEAdvertising::start(uint32_t duration, advCompleteCB_t advCompleteCB, NimBLEAddress* dirAddr) {
NIMBLE_LOGD(LOG_TAG, ">> Advertising start: customAdvData: %d, customScanResponseData: %d",
m_customAdvData, m_customScanResponseData);
@ -490,8 +500,8 @@ bool NimBLEAdvertising::start(uint32_t duration, void (*advCompleteCB)(NimBLEAdv
if(nullptr == (m_advData.uuids16 = (ble_uuid16_t*)realloc((void*)m_advData.uuids16,
(m_advData.num_uuids16 + 1) * sizeof(ble_uuid16_t))))
{
NIMBLE_LOGC(LOG_TAG, "Error, no mem");
abort();
NIMBLE_LOGE(LOG_TAG, "Error, no mem");
return false;
}
memcpy((void*)&m_advData.uuids16[m_advData.num_uuids16],
&it.getNative()->u16, sizeof(ble_uuid16_t));
@ -509,8 +519,8 @@ bool NimBLEAdvertising::start(uint32_t duration, void (*advCompleteCB)(NimBLEAdv
if(nullptr == (m_advData.uuids32 = (ble_uuid32_t*)realloc((void*)m_advData.uuids32,
(m_advData.num_uuids32 + 1) * sizeof(ble_uuid32_t))))
{
NIMBLE_LOGC(LOG_TAG, "Error, no mem");
abort();
NIMBLE_LOGE(LOG_TAG, "Error, no mem");
return false;
}
memcpy((void*)&m_advData.uuids32[m_advData.num_uuids32],
&it.getNative()->u32, sizeof(ble_uuid32_t));
@ -528,8 +538,8 @@ bool NimBLEAdvertising::start(uint32_t duration, void (*advCompleteCB)(NimBLEAdv
if(nullptr == (m_advData.uuids128 = (ble_uuid128_t*)realloc((void*)m_advData.uuids128,
(m_advData.num_uuids128 + 1) * sizeof(ble_uuid128_t))))
{
NIMBLE_LOGC(LOG_TAG, "Error, no mem");
abort();
NIMBLE_LOGE(LOG_TAG, "Error, no mem");
return false;
}
memcpy((void*)&m_advData.uuids128[m_advData.num_uuids128],
&it.getNative()->u128, sizeof(ble_uuid128_t));
@ -762,7 +772,7 @@ int NimBLEAdvertising::handleGapEvent(struct ble_gap_event *event, void *arg) {
case BLE_HS_EOS:
case BLE_HS_ECONTROLLER:
case BLE_HS_ENOTSYNCED:
NIMBLE_LOGC(LOG_TAG, "host reset, rc=%d", event->adv_complete.reason);
NIMBLE_LOGE(LOG_TAG, "host reset, rc=%d", event->adv_complete.reason);
NimBLEDevice::onReset(event->adv_complete.reason);
return 0;
default:
@ -800,9 +810,6 @@ void NimBLEAdvertisementData::addData(char * data, size_t length) {
/**
* @brief Set the appearance.
* @param [in] appearance The appearance code value.
*
* See also:
* https://www.bluetooth.com/specifications/gatt/viewer?attributeXmlFile=org.bluetooth.characteristic.gap.appearance.xml
*/
void NimBLEAdvertisementData::setAppearance(uint16_t appearance) {
char cdata[2];
@ -1069,4 +1076,12 @@ std::string NimBLEAdvertisementData::getPayload() {
return m_payload;
} // getPayload
/**
* @brief Clear the advertisement data for reuse.
*/
void NimBLEAdvertisementData::clearData() {
m_payload.clear();
}
#endif /* CONFIG_BT_ENABLED && CONFIG_BT_NIMBLE_ROLE_BROADCASTER && !CONFIG_BT_NIMBLE_EXT_ADV */

View File

@ -33,6 +33,7 @@
#include "NimBLEUUID.h"
#include "NimBLEAddress.h"
#include <functional>
#include <vector>
/* COMPATIBILITY - DO NOT USE */
@ -44,6 +45,9 @@
#define ESP_BLE_ADV_FLAG_NON_LIMIT_DISC (0x00 )
/* ************************* */
class NimBLEAdvertising;
typedef std::function<void(NimBLEAdvertising*)> advCompleteCB_t;
/**
* @brief Advertisement data set by the programmer to be published by the %BLE server.
@ -72,6 +76,7 @@ public:
void addTxPower();
void setPreferredParams(uint16_t min, uint16_t max);
std::string getPayload(); // Retrieve the current advert payload.
void clearData(); // Clear the advertisement data.
private:
friend class NimBLEAdvertising;
@ -92,7 +97,8 @@ public:
void addServiceUUID(const NimBLEUUID &serviceUUID);
void addServiceUUID(const char* serviceUUID);
void removeServiceUUID(const NimBLEUUID &serviceUUID);
bool start(uint32_t duration = 0, void (*advCompleteCB)(NimBLEAdvertising *pAdv) = nullptr, NimBLEAddress* dirAddr = nullptr);
bool start(uint32_t duration = 0, advCompleteCB_t advCompleteCB = nullptr, NimBLEAddress* dirAddr = nullptr);
void removeServices();
bool stop();
void setAppearance(uint16_t appearance);
void setName(const std::string &name);
@ -129,7 +135,7 @@ private:
bool m_customScanResponseData;
bool m_scanResp;
bool m_advDataSet;
void (*m_advCompCB)(NimBLEAdvertising *pAdv);
advCompleteCB_t m_advCompCB{nullptr};
uint8_t m_slaveItvl[4];
uint32_t m_duration;
std::vector<uint8_t> m_svcData16;

View File

@ -24,6 +24,7 @@
#include <string>
#include <vector>
#include <ctime>
#ifndef CONFIG_NIMBLE_CPP_ATT_VALUE_TIMESTAMP_ENABLED
# define CONFIG_NIMBLE_CPP_ATT_VALUE_TIMESTAMP_ENABLED 0
@ -41,7 +42,6 @@
# error CONFIG_NIMBLE_CPP_ATT_VALUE_INIT_LENGTH cannot be less than 1; Range = 1 : 512
#endif
/* Used to determine if the type passed to a template has a c_str() and length() method. */
template <typename T, typename = void, typename = void>
struct Has_c_str_len : std::false_type {};
@ -266,7 +266,8 @@ public:
/** @brief Subscript operator */
uint8_t operator [](int pos) const {
assert(pos < m_attr_len && "out of range"); return m_attr_value[pos]; }
NIMBLE_CPP_DEBUG_ASSERT(pos < m_attr_len);
return m_attr_value[pos]; }
/** @brief Operator; Get the value as a std::vector<uint8_t>. */
operator std::vector<uint8_t>() const {
@ -311,7 +312,7 @@ public:
inline NimBLEAttValue::NimBLEAttValue(uint16_t init_len, uint16_t max_len) {
m_attr_value = (uint8_t*)calloc(init_len + 1, 1);
assert(m_attr_value && "No Mem");
NIMBLE_CPP_DEBUG_ASSERT(m_attr_value);
m_attr_max_len = std::min(BLE_ATT_ATTR_MAX_LEN, (int)max_len);
m_attr_len = 0;
m_capacity = init_len;
@ -354,7 +355,7 @@ inline NimBLEAttValue& NimBLEAttValue::operator =(const NimBLEAttValue & source)
inline void NimBLEAttValue::deepCopy(const NimBLEAttValue & source) {
uint8_t* res = (uint8_t*)realloc( m_attr_value, source.m_capacity + 1);
assert(res && "deepCopy: realloc failed");
NIMBLE_CPP_DEBUG_ASSERT(res);
ble_npl_hw_enter_critical();
m_attr_value = res;
@ -389,7 +390,7 @@ inline bool NimBLEAttValue::setValue(const uint8_t *value, uint16_t len) {
res = (uint8_t*)realloc(m_attr_value, (len + 1));
m_capacity = len;
}
assert(res && "setValue: realloc failed");
NIMBLE_CPP_DEBUG_ASSERT(res);
#if CONFIG_NIMBLE_CPP_ATT_VALUE_TIMESTAMP_ENABLED
time_t t = time(nullptr);
@ -424,7 +425,7 @@ inline NimBLEAttValue& NimBLEAttValue::append(const uint8_t *value, uint16_t len
res = (uint8_t*)realloc(m_attr_value, (new_len + 1));
m_capacity = new_len;
}
assert(res && "append: realloc failed");
NIMBLE_CPP_DEBUG_ASSERT(res);
#if CONFIG_NIMBLE_CPP_ATT_VALUE_TIMESTAMP_ENABLED
time_t t = time(nullptr);

View File

@ -87,9 +87,7 @@ NimBLEDescriptor* NimBLECharacteristic::createDescriptor(const char* uuid, uint3
*/
NimBLEDescriptor* NimBLECharacteristic::createDescriptor(const NimBLEUUID &uuid, uint32_t properties, uint16_t max_len) {
NimBLEDescriptor* pDescriptor = nullptr;
if(uuid == NimBLEUUID(uint16_t(0x2902))) {
assert(0 && "0x2902 descriptors cannot be manually created");
} else if (uuid == NimBLEUUID(uint16_t(0x2904))) {
if (uuid == NimBLEUUID(uint16_t(0x2904))) {
pDescriptor = new NimBLE2904(this);
} else {
pDescriptor = new NimBLEDescriptor(uuid, properties, max_len, this);
@ -266,7 +264,7 @@ int NimBLECharacteristic::handleGapEvent(uint16_t conn_handle, uint16_t attr_han
NIMBLE_LOGW(LOG_TAG, "Conn_handle (%d) is above the maximum value (%d)", conn_handle, BLE_HCI_LE_CONN_HANDLE_MAX);
return BLE_ATT_ERR_INVALID_HANDLE;
}
const ble_uuid_t *uuid;
int rc;
NimBLEConnInfo peerInfo;
@ -279,12 +277,12 @@ int NimBLECharacteristic::handleGapEvent(uint16_t conn_handle, uint16_t attr_han
if(ble_uuid_cmp(uuid, &pCharacteristic->getUUID().getNative()->u) == 0){
switch(ctxt->op) {
case BLE_GATT_ACCESS_OP_READ_CHR: {
rc = ble_gap_conn_find(conn_handle, &peerInfo.m_desc);
assert(rc == 0);
ble_gap_conn_find(conn_handle, &peerInfo.m_desc);
// If the packet header is only 8 bytes this is a follow up of a long read
// so we don't want to call the onRead() callback again.
if(ctxt->om->om_pkthdr_len > 8 ||
conn_handle == BLE_HS_CONN_HANDLE_NONE ||
pCharacteristic->m_value.size() <= (ble_att_mtu(peerInfo.m_desc.conn_handle) - 3)) {
pCharacteristic->m_pCallbacks->onRead(pCharacteristic, peerInfo);
}
@ -316,8 +314,8 @@ int NimBLECharacteristic::handleGapEvent(uint16_t conn_handle, uint16_t attr_han
len += next->om_len;
next = SLIST_NEXT(next, om_next);
}
rc = ble_gap_conn_find(conn_handle, &peerInfo.m_desc);
assert(rc == 0);
ble_gap_conn_find(conn_handle, &peerInfo.m_desc);
pCharacteristic->setValue(buf, len);
pCharacteristic->m_pCallbacks->onWrite(pCharacteristic, peerInfo);
return 0;

View File

@ -111,7 +111,7 @@ NimBLEClient::~NimBLEClient() {
*/
void NimBLEClient::dcTimerCb(ble_npl_event *event) {
/* NimBLEClient *pClient = (NimBLEClient*)event->arg;
NIMBLE_LOGC(LOG_TAG, "Timed out disconnecting from %s - resetting host",
NIMBLE_LOGE(LOG_TAG, "Timed out disconnecting from %s - resetting host",
std::string(pClient->getPeerAddress()).c_str());
*/
ble_hs_sched_reset(BLE_HS_ECONTROLLER);
@ -189,7 +189,7 @@ bool NimBLEClient::connect(const NimBLEAddress &address, bool deleteAttributes)
NIMBLE_LOGD(LOG_TAG, ">> connect(%s)", address.toString().c_str());
if(!NimBLEDevice::m_synced) {
NIMBLE_LOGC(LOG_TAG, "Host reset, wait for sync.");
NIMBLE_LOGE(LOG_TAG, "Host reset, wait for sync.");
return false;
}
@ -458,9 +458,6 @@ void NimBLEClient::setConnectionParams(uint16_t minInterval, uint16_t maxInterva
// These are not used by NimBLE at this time - Must leave at defaults
//m_pConnParams->min_ce_len = minConnTime; // Minimum length of connection event in 0.625ms units
//m_pConnParams->max_ce_len = maxConnTime; // Maximum length of connection event in 0.625ms units
int rc = NimBLEUtils::checkConnParams(&m_pConnParams);
assert(rc == 0 && "Invalid Connection parameters");
} // setConnectionParams
@ -551,6 +548,66 @@ uint16_t NimBLEClient::getConnId() {
return m_conn_id;
} // getConnId
/**
* @brief Clear the connection information for this client.
* @note This is designed to be used to reset the connection information after
* calling setConnection(), and should not be used to disconnect from a
* peer. To disconnect from a peer, use disconnect().
*/
void NimBLEClient::clearConnection() {
m_conn_id = BLE_HS_CONN_HANDLE_NONE;
m_connEstablished = false;
m_peerAddress = NimBLEAddress();
} // clearConnection
/**
* @brief Set the connection information for this client.
* @param [in] connInfo The connection information.
* @return True on success.
* @note Sets the connection established flag to true.
* @note If the client is already connected to a peer, this will return false.
* @note This is designed to be used when a connection is made outside of the
* NimBLEClient class, such as when a connection is made by the
* NimBLEServer class and the client is passed the connection id. This use
* enables the GATT Server to read the name of the device that has
* connected to it.
*/
bool NimBLEClient::setConnection(NimBLEConnInfo &connInfo) {
if (isConnected() || m_connEstablished) {
NIMBLE_LOGE(LOG_TAG, "Already connected");
return false;
}
m_peerAddress = connInfo.getAddress();
m_conn_id = connInfo.getConnHandle();
m_connEstablished = true;
return true;
} // setConnection
/**
* @brief Set the connection information for this client.
* @param [in] conn_id The connection id.
* @note Sets the connection established flag to true.
* @note This is designed to be used when a connection is made outside of the
* NimBLEClient class, such as when a connection is made by the
* NimBLEServer class and the client is passed the connection id. This use
* enables the GATT Server to read the name of the device that has
* connected to it.
* @note If the client is already connected to a peer, this will return false.
* @note This will look up the peer address using the connection id.
*/
bool NimBLEClient::setConnection(uint16_t conn_id) {
// we weren't provided the peer address, look it up using ble_gap_conn_find
NimBLEConnInfo connInfo;
int rc = ble_gap_conn_find(m_conn_id, &connInfo.m_desc);
if (rc != 0) {
NIMBLE_LOGE(LOG_TAG, "Connection info not found");
return false;
}
return setConnection(connInfo);
} // setConnection
/**
* @brief Retrieve the address of the peer.
@ -945,7 +1002,7 @@ int NimBLEClient::handleGapEvent(struct ble_gap_event *event, void *arg) {
case BLE_HS_ETIMEOUT_HCI:
case BLE_HS_ENOTSYNCED:
case BLE_HS_EOS:
NIMBLE_LOGC(LOG_TAG, "Disconnect - host reset, rc=%d", rc);
NIMBLE_LOGE(LOG_TAG, "Disconnect - host reset, rc=%d", rc);
NimBLEDevice::onReset(rc);
break;
default:
@ -1111,7 +1168,11 @@ int NimBLEClient::handleGapEvent(struct ble_gap_event *event, void *arg) {
{
NimBLEConnInfo peerInfo;
rc = ble_gap_conn_find(event->enc_change.conn_handle, &peerInfo.m_desc);
assert(rc == 0);
if (rc != 0) {
NIMBLE_LOGE(LOG_TAG, "Connection info not found");
rc = 0;
break;
}
if (event->enc_change.status == (BLE_HS_ERR_HCI_BASE + BLE_ERR_PINKEY_MISSING)) {
// Key is missing, try deleting.
@ -1125,6 +1186,19 @@ int NimBLEClient::handleGapEvent(struct ble_gap_event *event, void *arg) {
break;
} //BLE_GAP_EVENT_ENC_CHANGE
case BLE_GAP_EVENT_IDENTITY_RESOLVED: {
NimBLEConnInfo peerInfo;
rc = ble_gap_conn_find(event->identity_resolved.conn_handle, &peerInfo.m_desc);
if (rc != 0) {
NIMBLE_LOGE(LOG_TAG, "Connection info not found");
rc = 0;
break;
}
pClient->m_pClientCallbacks->onIdentity(peerInfo);
break;
} // BLE_GAP_EVENT_IDENTITY_RESOLVED
case BLE_GAP_EVENT_MTU: {
if(pClient->m_conn_id != event->mtu.conn_handle){
return 0;
@ -1143,20 +1217,17 @@ int NimBLEClient::handleGapEvent(struct ble_gap_event *event, void *arg) {
if(pClient->m_conn_id != event->passkey.conn_handle)
return 0;
if (event->passkey.params.action == BLE_SM_IOACT_DISP) {
pkey.action = event->passkey.params.action;
pkey.passkey = NimBLEDevice::m_passkey; // This is the passkey to be entered on peer
rc = ble_sm_inject_io(event->passkey.conn_handle, &pkey);
NIMBLE_LOGD(LOG_TAG, "ble_sm_inject_io result: %d", rc);
NimBLEConnInfo peerInfo;
rc = ble_gap_conn_find(event->passkey.conn_handle, &peerInfo.m_desc);
if (rc != 0) {
NIMBLE_LOGE(LOG_TAG, "Connection info not found");
rc = 0;
break;
}
} else if (event->passkey.params.action == BLE_SM_IOACT_NUMCMP) {
if (event->passkey.params.action == BLE_SM_IOACT_NUMCMP) {
NIMBLE_LOGD(LOG_TAG, "Passkey on device's display: %" PRIu32, event->passkey.params.numcmp);
pkey.action = event->passkey.params.action;
pkey.numcmp_accept = pClient->m_pClientCallbacks->onConfirmPIN(event->passkey.params.numcmp);
rc = ble_sm_inject_io(event->passkey.conn_handle, &pkey);
NIMBLE_LOGD(LOG_TAG, "ble_sm_inject_io result: %d", rc);
pClient->m_pClientCallbacks->onConfirmPIN(peerInfo, event->passkey.params.numcmp);
//TODO: Handle out of band pairing
} else if (event->passkey.params.action == BLE_SM_IOACT_OOB) {
static uint8_t tem_oob[16] = {0};
@ -1169,12 +1240,7 @@ int NimBLEClient::handleGapEvent(struct ble_gap_event *event, void *arg) {
////////
} else if (event->passkey.params.action == BLE_SM_IOACT_INPUT) {
NIMBLE_LOGD(LOG_TAG, "Enter the passkey");
pkey.action = event->passkey.params.action;
pkey.passkey = pClient->m_pClientCallbacks->onPassKeyRequest();
rc = ble_sm_inject_io(event->passkey.conn_handle, &pkey);
NIMBLE_LOGD(LOG_TAG, "ble_sm_inject_io result: %d", rc);
pClient->m_pClientCallbacks->onPassKeyEntry(peerInfo);
} else if (event->passkey.params.action == BLE_SM_IOACT_NONE) {
NIMBLE_LOGD(LOG_TAG, "No passkey action required");
}
@ -1261,17 +1327,22 @@ bool NimBLEClientCallbacks::onConnParamsUpdateRequest(NimBLEClient* pClient, con
return true;
}
uint32_t NimBLEClientCallbacks::onPassKeyRequest(){
NIMBLE_LOGD("NimBLEClientCallbacks", "onPassKeyRequest: default: 123456");
return 123456;
}
void NimBLEClientCallbacks::onPassKeyEntry(const NimBLEConnInfo& connInfo){
NIMBLE_LOGD("NimBLEClientCallbacks", "onPassKeyEntry: default: 123456");
NimBLEDevice::injectPassKey(connInfo, 123456);
} //onPassKeyEntry
void NimBLEClientCallbacks::onAuthenticationComplete(NimBLEConnInfo& peerInfo){
void NimBLEClientCallbacks::onAuthenticationComplete(const NimBLEConnInfo& connInfo){
NIMBLE_LOGD("NimBLEClientCallbacks", "onAuthenticationComplete: default");
}
bool NimBLEClientCallbacks::onConfirmPIN(uint32_t pin){
void NimBLEClientCallbacks::onIdentity(const NimBLEConnInfo& connInfo){
NIMBLE_LOGD("NimBLEClientCallbacks", "onIdentity: default");
} // onIdentity
void NimBLEClientCallbacks::onConfirmPIN(const NimBLEConnInfo& connInfo, uint32_t pin){
NIMBLE_LOGD("NimBLEClientCallbacks", "onConfirmPIN: default: true");
return true;
NimBLEDevice::injectConfirmPIN(connInfo, true);
}
#endif /* CONFIG_BT_ENABLED && CONFIG_BT_NIMBLE_ROLE_CENTRAL */

View File

@ -61,6 +61,9 @@ public:
bool deleteCallbacks = true);
std::string toString();
uint16_t getConnId();
void clearConnection();
bool setConnection(NimBLEConnInfo &conn_info);
bool setConnection(uint16_t conn_id);
uint16_t getMTU();
bool secureConnection();
void setConnectTimeout(uint32_t timeout);
@ -144,23 +147,29 @@ public:
/**
* @brief Called when server requests a passkey for pairing.
* @return The passkey to be sent to the server.
* @param [in] connInfo A reference to a NimBLEConnInfo instance containing the peer info.
*/
virtual uint32_t onPassKeyRequest();
virtual void onPassKeyEntry(const NimBLEConnInfo& connInfo);
/**
* @brief Called when the pairing procedure is complete.
* @param [in] connInfo A reference to a NimBLEConnInfo instance containing the peer info.\n
* This can be used to check the status of the connection encryption/pairing.
*/
virtual void onAuthenticationComplete(NimBLEConnInfo& connInfo);
virtual void onAuthenticationComplete(const NimBLEConnInfo& connInfo);
/**
* @brief Called when using numeric comparision for pairing.
* @param [in] connInfo A reference to a NimBLEConnInfo instance containing the peer info.
* @param [in] pin The pin to compare with the server.
* @return True to accept the pin.
*/
virtual bool onConfirmPIN(uint32_t pin);
virtual void onConfirmPIN(const NimBLEConnInfo& connInfo, uint32_t pin);
/**
* @brief Called when the peer identity address is resolved.
* @param [in] connInfo A reference to a NimBLEConnInfo instance with information
*/
virtual void onIdentity(const NimBLEConnInfo& connInfo);
};
#endif /* CONFIG_BT_ENABLED && CONFIG_BT_NIMBLE_ROLE_CENTRAL */

View File

@ -12,47 +12,47 @@ friend class NimBLEClient;
friend class NimBLECharacteristic;
friend class NimBLEDescriptor;
ble_gap_conn_desc m_desc;
NimBLEConnInfo() { m_desc = {}; }
NimBLEConnInfo(ble_gap_conn_desc desc) { m_desc = desc; }
ble_gap_conn_desc m_desc{};
NimBLEConnInfo(){};
NimBLEConnInfo(ble_gap_conn_desc desc) { m_desc = desc; }
public:
/** @brief Gets the over-the-air address of the connected peer */
NimBLEAddress getAddress() { return NimBLEAddress(m_desc.peer_ota_addr); }
NimBLEAddress getAddress() const { return NimBLEAddress(m_desc.peer_ota_addr); }
/** @brief Gets the ID address of the connected peer */
NimBLEAddress getIdAddress() { return NimBLEAddress(m_desc.peer_id_addr); }
NimBLEAddress getIdAddress() const { return NimBLEAddress(m_desc.peer_id_addr); }
/** @brief Gets the connection handle of the connected peer */
uint16_t getConnHandle() { return m_desc.conn_handle; }
/** @brief Gets the connection handle (also known as the connection id) of the connected peer */
uint16_t getConnHandle() const { return m_desc.conn_handle; }
/** @brief Gets the connection interval for this connection (in 1.25ms units) */
uint16_t getConnInterval() { return m_desc.conn_itvl; }
uint16_t getConnInterval() const { return m_desc.conn_itvl; }
/** @brief Gets the supervision timeout for this connection (in 10ms units) */
uint16_t getConnTimeout() { return m_desc.supervision_timeout; }
uint16_t getConnTimeout() const { return m_desc.supervision_timeout; }
/** @brief Gets the allowable latency for this connection (unit = number of intervals) */
uint16_t getConnLatency() { return m_desc.conn_latency; }
uint16_t getConnLatency() const { return m_desc.conn_latency; }
/** @brief Gets the maximum transmission unit size for this connection (in bytes) */
uint16_t getMTU() { return ble_att_mtu(m_desc.conn_handle); }
uint16_t getMTU() const { return ble_att_mtu(m_desc.conn_handle); }
/** @brief Check if we are in the master role in this connection */
bool isMaster() { return (m_desc.role == BLE_GAP_ROLE_MASTER); }
bool isMaster() const { return (m_desc.role == BLE_GAP_ROLE_MASTER); }
/** @brief Check if we are in the slave role in this connection */
bool isSlave() { return (m_desc.role == BLE_GAP_ROLE_SLAVE); }
bool isSlave() const { return (m_desc.role == BLE_GAP_ROLE_SLAVE); }
/** @brief Check if we are connected to a bonded peer */
bool isBonded() { return (m_desc.sec_state.bonded == 1); }
bool isBonded() const { return (m_desc.sec_state.bonded == 1); }
/** @brief Check if the connection in encrypted */
bool isEncrypted() { return (m_desc.sec_state.encrypted == 1); }
bool isEncrypted() const { return (m_desc.sec_state.encrypted == 1); }
/** @brief Check if the the connection has been authenticated */
bool isAuthenticated() { return (m_desc.sec_state.authenticated == 1); }
bool isAuthenticated() const { return (m_desc.sec_state.authenticated == 1); }
/** @brief Gets the key size used to encrypt the connection */
uint8_t getSecKeySize() { return m_desc.sec_state.key_size; }
uint8_t getSecKeySize() const { return m_desc.sec_state.key_size; }
};
#endif

View File

@ -55,7 +55,14 @@ NimBLEDescriptor::NimBLEDescriptor(NimBLEUUID uuid, uint16_t properties, uint16_
m_pCharacteristic = pCharacteristic;
m_pCallbacks = &defaultCallbacks; // No initial callback.
m_properties = 0;
m_removed = 0;
// Check if this is the client configuration descriptor and set to removed if true.
if (uuid == NimBLEUUID((uint16_t)0x2902)) {
NIMBLE_LOGW(LOG_TAG, "Manually created 2902 descriptor has no functionality; please remove.");
m_removed = 1;
} else {
m_removed = 0;
}
if (properties & BLE_GATT_CHR_F_READ) { // convert uint16_t properties to uint8_t
m_properties |= BLE_ATT_F_READ;
@ -155,7 +162,7 @@ int NimBLEDescriptor::handleGapEvent(uint16_t conn_handle, uint16_t attr_handle,
const ble_uuid_t *uuid;
int rc;
NimBLEConnInfo peerInfo;
NimBLEConnInfo peerInfo{};
NimBLEDescriptor* pDescriptor = (NimBLEDescriptor*)arg;
NIMBLE_LOGD(LOG_TAG, "Descriptor %s %s event", pDescriptor->getUUID().toString().c_str(),
@ -165,12 +172,12 @@ int NimBLEDescriptor::handleGapEvent(uint16_t conn_handle, uint16_t attr_handle,
if(ble_uuid_cmp(uuid, &pDescriptor->getUUID().getNative()->u) == 0){
switch(ctxt->op) {
case BLE_GATT_ACCESS_OP_READ_DSC: {
rc = ble_gap_conn_find(conn_handle, &peerInfo.m_desc);
assert(rc == 0);
ble_gap_conn_find(conn_handle, &peerInfo.m_desc);
// If the packet header is only 8 bytes this is a follow up of a long read
// so we don't want to call the onRead() callback again.
if(ctxt->om->om_pkthdr_len > 8 ||
conn_handle == BLE_HS_CONN_HANDLE_NONE ||
pDescriptor->m_value.size() <= (ble_att_mtu(peerInfo.getConnHandle()) - 3)) {
pDescriptor->m_pCallbacks->onRead(pDescriptor, peerInfo);
}
@ -182,11 +189,9 @@ int NimBLEDescriptor::handleGapEvent(uint16_t conn_handle, uint16_t attr_handle,
}
case BLE_GATT_ACCESS_OP_WRITE_DSC: {
rc = ble_gap_conn_find(conn_handle, &peerInfo.m_desc);
assert(rc == 0);
ble_gap_conn_find(conn_handle, &peerInfo.m_desc);
uint16_t att_max_len = pDescriptor->m_value.max_size();
if (ctxt->om->om_len > att_max_len) {
return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN;
}

View File

@ -299,7 +299,7 @@ size_t NimBLEDevice::getClientListSize() {
/**
* @brief Get a reference to a client by connection ID.
* @param [in] conn_id The client connection ID to search for.
* @return A pointer to the client object with the spcified connection ID.
* @return A pointer to the client object with the specified connection ID or nullptr.
*/
/* STATIC */
NimBLEClient* NimBLEDevice::getClientByID(uint16_t conn_id) {
@ -308,7 +308,7 @@ NimBLEClient* NimBLEDevice::getClientByID(uint16_t conn_id) {
return (*it);
}
}
assert(0);
return nullptr;
} // getClientByID
@ -444,12 +444,13 @@ int NimBLEDevice::getPower() {
*/
/* STATIC*/
NimBLEAddress NimBLEDevice::getAddress() {
ble_addr_t addr = {BLE_ADDR_PUBLIC, 0};
ble_addr_t addr = {m_own_addr_type, 0};
if(BLE_HS_ENOADDR == ble_hs_id_copy_addr(BLE_ADDR_PUBLIC, addr.val, NULL)) {
NIMBLE_LOGD(LOG_TAG, "Public address not found, checking random");
addr.type = BLE_ADDR_RANDOM;
ble_hs_id_copy_addr(BLE_ADDR_RANDOM, addr.val, NULL);
if(BLE_HS_ENOADDR == ble_hs_id_copy_addr(m_own_addr_type, addr.val, NULL)) {
// NIMBLE_LOGD(LOG_TAG, "Public address not found, checking random");
// addr.type = BLE_ADDR_RANDOM;
// ble_hs_id_copy_addr(BLE_ADDR_RANDOM, addr.val, NULL);
return NimBLEAddress(); // return blank to report error
}
return NimBLEAddress(addr);
@ -567,10 +568,16 @@ int NimBLEDevice::getNumBonds() {
/**
* @brief Deletes all bonding information.
* @returns true on success, false on failure.
*/
/*STATIC*/
void NimBLEDevice::deleteAllBonds() {
ble_store_clear();
bool NimBLEDevice::deleteAllBonds() {
int rc = ble_store_clear();
if (rc != 0) {
NIMBLE_LOGE(LOG_TAG, "Failed to delete all bonds; rc=%d", rc);
return false;
}
return true;
}
@ -685,6 +692,7 @@ bool NimBLEDevice::whiteListAdd(const NimBLEAddress & address) {
int rc = ble_gap_wl_set(&wlVec[0], wlVec.size());
if (rc != 0) {
NIMBLE_LOGE(LOG_TAG, "Failed adding to whitelist rc=%d", rc);
m_whiteList.pop_back();
return false;
}
@ -771,7 +779,7 @@ void NimBLEDevice::onReset(int reason)
m_synced = false;
NIMBLE_LOGC(LOG_TAG, "Resetting state; reason=%d, %s", reason,
NIMBLE_LOGE(LOG_TAG, "Resetting state; reason=%d, %s", reason,
NimBLEUtils::returnCodeToString(reason));
#if defined(CONFIG_BT_NIMBLE_ROLE_OBSERVER)
@ -799,7 +807,10 @@ void NimBLEDevice::onSync(void)
/* Make sure we have proper identity address set (public preferred) */
int rc = ble_hs_util_ensure_addr(0);
assert(rc == 0);
if (rc != 0) {
NIMBLE_LOGE(LOG_TAG, "error ensuring address; rc=%d", rc);
return;
}
#ifndef ESP_PLATFORM
rc = ble_hs_id_infer_auto(m_own_addr_type, &m_own_addr_type);
@ -871,9 +882,11 @@ void NimBLEDevice::init(const std::string &deviceName) {
ESP_ERROR_CHECK(errRc);
#if CONFIG_IDF_TARGET_ESP32
esp_bt_controller_mem_release(ESP_BT_MODE_CLASSIC_BT);
#endif
#if ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(5, 0, 0)
#if ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(5, 0, 0) | !defined(CONFIG_NIMBLE_CPP_IDF)
esp_bt_controller_config_t bt_cfg = BT_CONTROLLER_INIT_CONFIG_DEFAULT();
# if defined (CONFIG_IDF_TARGET_ESP32C3) || defined(CONFIG_IDF_TARGET_ESP32S3)
bt_cfg.bluetooth_mode = ESP_BT_MODE_BLE;
@ -902,14 +915,16 @@ void NimBLEDevice::init(const std::string &deviceName) {
ble_hs_cfg.sm_bonding = 0;
ble_hs_cfg.sm_mitm = 0;
ble_hs_cfg.sm_sc = 1;
ble_hs_cfg.sm_our_key_dist = 1;
ble_hs_cfg.sm_their_key_dist = 3;
ble_hs_cfg.sm_our_key_dist = BLE_SM_PAIR_KEY_DIST_ENC | BLE_SM_PAIR_KEY_DIST_ID;
ble_hs_cfg.sm_their_key_dist = BLE_SM_PAIR_KEY_DIST_ENC | BLE_SM_PAIR_KEY_DIST_ID;
ble_hs_cfg.store_status_cb = ble_store_util_status_rr; /*TODO: Implement handler for this*/
// Set the device name.
rc = ble_svc_gap_device_name_set(deviceName.c_str());
assert(rc == 0);
if (rc != 0) {
NIMBLE_LOGE(LOG_TAG, "ble_svc_gap_device_name_set() failed; rc=%d", rc);
}
ble_store_config_init();
@ -935,13 +950,13 @@ void NimBLEDevice::deinit(bool clearAll) {
int ret = nimble_port_stop();
if (ret == 0) {
nimble_port_deinit();
#ifdef ESP_PLATFORM
#if ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(5, 0, 0)
#ifdef CONFIG_NIMBLE_CPP_IDF
# if ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(5, 0, 0)
ret = esp_nimble_hci_and_controller_deinit();
if (ret != ESP_OK) {
NIMBLE_LOGE(LOG_TAG, "esp_nimble_hci_and_controller_deinit() failed with error: %d", ret);
}
#endif
# endif
#endif
initialized = false;
m_synced = false;
@ -1150,6 +1165,43 @@ int NimBLEDevice::startSecurity(uint16_t conn_id) {
} // startSecurity
/**
* @brief Inject the provided passkey into the Security Manager
* @param [in] peerInfo Connection information for the peer
* @param [in] pin The 6-digit pin to inject
* @return true if the passkey was injected successfully.
*/
bool NimBLEDevice::injectPassKey(const NimBLEConnInfo& peerInfo, uint32_t pin) {
int rc = 0;
struct ble_sm_io pkey = {0,0};
pkey.action = BLE_SM_IOACT_INPUT;
pkey.passkey = pin;
rc = ble_sm_inject_io(peerInfo.getConnHandle(), &pkey);
NIMBLE_LOGD(LOG_TAG, "BLE_SM_IOACT_INPUT; ble_sm_inject_io result: %d", rc);
return rc == 0;
}
/**
* @brief Inject the provided numeric comparison response into the Security Manager
* @param [in] peerInfo Connection information for the peer
* @param [in] accept Whether the user confirmed or declined the comparison
*/
bool NimBLEDevice::injectConfirmPIN(const NimBLEConnInfo& peerInfo, bool accept) {
int rc = 0;
struct ble_sm_io pkey = {0,0};
pkey.action = BLE_SM_IOACT_NUMCMP;
pkey.numcmp_accept = accept;
rc = ble_sm_inject_io(peerInfo.getConnHandle(), &pkey);
NIMBLE_LOGD(LOG_TAG, "BLE_SM_IOACT_NUMCMP; ble_sm_inject_io result: %d", rc);
return rc == 0;
}
/**
* @brief Check if the device address is on our ignore list.
* @param [in] address The address to look for.
@ -1202,10 +1254,22 @@ void NimBLEDevice::setCustomGapHandler(gap_event_handler handler) {
int rc = ble_gap_event_listener_register(&m_listener, m_customGapHandler, NULL);
if(rc == BLE_HS_EALREADY){
NIMBLE_LOGI(LOG_TAG, "Already listening to GAP events.");
} else if (rc != 0) {
NIMBLE_LOGE(LOG_TAG, "ble_gap_event_listener_register: rc=%d %s", rc, NimBLEUtils::returnCodeToString(rc));
}
else{
assert(rc == 0);
}
} // setCustomGapHandler
#endif // CONFIG_BT_ENABLED
#if CONFIG_NIMBLE_CPP_DEBUG_ASSERT_ENABLED || __DOXYGEN__
/**
* @brief Debug assert - weak function.
* @param [in] file The file where the assert occurred.
* @param [in] line The line number where the assert occurred.
*/
void nimble_cpp_assert(const char *file, unsigned line) {
NIMBLE_LOGC("", "Assertion failed at %s:%u\n", file, line);
abort();
}
#endif // CONFIG_NIMBLE_CPP_DEBUG_ASSERT_ENABLED
#endif // CONFIG_BT_ENABLED

View File

@ -136,6 +136,8 @@ public:
static void setSecurityPasskey(uint32_t pin);
static uint32_t getSecurityPasskey();
static int startSecurity(uint16_t conn_id);
static bool injectConfirmPIN(const NimBLEConnInfo& peerInfo, bool accept);
static bool injectPassKey(const NimBLEConnInfo& peerInfo, uint32_t pin);
static int setMTU(uint16_t mtu);
static uint16_t getMTU();
static bool isIgnored(const NimBLEAddress &address);
@ -172,7 +174,7 @@ public:
static bool deleteBond(const NimBLEAddress &address);
static int getNumBonds();
static bool isBonded(const NimBLEAddress &address);
static void deleteAllBonds();
static bool deleteAllBonds();
static NimBLEAddress getBondedAddress(int index);
#endif

View File

@ -81,7 +81,7 @@ uint16_t NimBLEEddystoneTLM::getVolt() {
* @return The temperature value.
*/
float NimBLEEddystoneTLM::getTemp() {
return ENDIAN_CHANGE_U16(m_eddystoneData.temp) / 256.0f;
return (int16_t)ENDIAN_CHANGE_U16(m_eddystoneData.temp) / 256.0f;
} // getTemp
/**
@ -203,7 +203,7 @@ void NimBLEEddystoneTLM::setVolt(uint16_t volt) {
* @param [in] temp The temperature value.
*/
void NimBLEEddystoneTLM::setTemp(float temp) {
m_eddystoneData.temp = (uint16_t)temp;
m_eddystoneData.temp = ENDIAN_CHANGE_U16((int16_t)(temp * 256.0f));
} // setTemp

View File

@ -341,7 +341,7 @@ int NimBLEExtAdvertising::handleGapEvent(struct ble_gap_event *event, void *arg)
case BLE_HS_EOS:
case BLE_HS_ECONTROLLER:
case BLE_HS_ENOTSYNCED:
NIMBLE_LOGC(LOG_TAG, "host reset, rc = %d", event->adv_complete.reason);
NIMBLE_LOGE(LOG_TAG, "host reset, rc = %d", event->adv_complete.reason);
NimBLEDevice::onReset(event->adv_complete.reason);
return 0;
default:
@ -623,9 +623,6 @@ void NimBLEExtAdvertisement::addData(const uint8_t * data, size_t length) {
/**
* @brief Set the appearance.
* @param [in] appearance The appearance code value.
*
* See also:
* https://www.bluetooth.com/specifications/gatt/viewer?attributeXmlFile=org.bluetooth.characteristic.gap.appearance.xml
*/
void NimBLEExtAdvertisement::setAppearance(uint16_t appearance) {
char cdata[2];

View File

@ -26,28 +26,34 @@ 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));
m_batteryService = server->createService(NimBLEUUID((uint16_t) 0x180f));
m_deviceInfoService = server->createService(NimBLEUUID((uint16_t)0x180a));
m_hidService = server->createService(NimBLEUUID((uint16_t)0x1812));
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);
m_pnpCharacteristic = m_deviceInfoService->createCharacteristic((uint16_t)0x2a50, NIMBLE_PROPERTY::READ);
/*
* Non-mandatory characteristics for device info service
* Will be created on demand
*/
m_manufacturerCharacteristic = nullptr;
/*
* 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);
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);
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);
@ -56,8 +62,8 @@ NimBLEHIDDevice::NimBLEHIDDevice(NimBLEServer* server) {
* 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);
const uint8_t pMode[] = {0x01};
protocolMode()->setValue((uint8_t*)pMode, 1);
}
NimBLEHIDDevice::~NimBLEHIDDevice() {
@ -86,7 +92,10 @@ void NimBLEHIDDevice::startServices() {
* @brief Create a manufacturer characteristic (this characteristic is optional).
*/
NimBLECharacteristic* NimBLEHIDDevice::manufacturer() {
m_manufacturerCharacteristic = m_deviceInfoService->createCharacteristic((uint16_t) 0x2a29, NIMBLE_PROPERTY::READ);
if (m_manufacturerCharacteristic == nullptr) {
m_manufacturerCharacteristic = m_deviceInfoService->createCharacteristic((uint16_t)0x2a29, NIMBLE_PROPERTY::READ);
}
return m_manufacturerCharacteristic;
}
@ -95,7 +104,7 @@ NimBLECharacteristic* NimBLEHIDDevice::manufacturer() {
* @param [in] name The manufacturer name of this HID device.
*/
void NimBLEHIDDevice::manufacturer(std::string name) {
m_manufacturerCharacteristic->setValue(name);
manufacturer()->setValue(name);
}
/**
@ -106,7 +115,15 @@ void NimBLEHIDDevice::manufacturer(std::string name) {
* @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 };
uint8_t pnp[] = {
sig,
((uint8_t*)&vid)[0],
((uint8_t*)&vid)[1],
((uint8_t*)&pid)[0],
((uint8_t*)&pid)[1],
((uint8_t*)&version)[0],
((uint8_t*)&version)[1]
};
m_pnpCharacteristic->setValue(pnp, sizeof(pnp));
}
@ -116,7 +133,7 @@ void NimBLEHIDDevice::pnp(uint8_t sig, uint16_t vid, uint16_t pid, uint16_t vers
* @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 };
uint8_t info[] = {0x11, 0x1, country, flags};
m_hidInfoCharacteristic->setValue(info, sizeof(info));
}
@ -126,11 +143,11 @@ void NimBLEHIDDevice::hidInfo(uint8_t country, uint8_t flags) {
* @return pointer to new input report characteristic
*/
NimBLECharacteristic* NimBLEHIDDevice::inputReport(uint8_t reportID) {
NimBLECharacteristic* inputReportCharacteristic = m_hidService->createCharacteristic((uint16_t) 0x2a4d, NIMBLE_PROPERTY::READ | NIMBLE_PROPERTY::NOTIFY | NIMBLE_PROPERTY::READ_ENC);
NimBLEDescriptor* inputReportDescriptor = inputReportCharacteristic->createDescriptor((uint16_t) 0x2908, NIMBLE_PROPERTY::READ | NIMBLE_PROPERTY::READ_ENC);
NimBLECharacteristic *inputReportCharacteristic = m_hidService->createCharacteristic((uint16_t)0x2a4d, NIMBLE_PROPERTY::READ | NIMBLE_PROPERTY::NOTIFY | NIMBLE_PROPERTY::READ_ENC);
NimBLEDescriptor *inputReportDescriptor = inputReportCharacteristic->createDescriptor((uint16_t)0x2908, NIMBLE_PROPERTY::READ | NIMBLE_PROPERTY::READ_ENC);
uint8_t desc1_val[] = { reportID, 0x01 };
inputReportDescriptor->setValue((uint8_t*) desc1_val, 2);
uint8_t desc1_val[] = {reportID, 0x01};
inputReportDescriptor->setValue((uint8_t*)desc1_val, 2);
return inputReportCharacteristic;
}
@ -141,11 +158,11 @@ NimBLECharacteristic* NimBLEHIDDevice::inputReport(uint8_t reportID) {
* @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, NIMBLE_PROPERTY::READ | NIMBLE_PROPERTY::WRITE | NIMBLE_PROPERTY::READ_ENC | NIMBLE_PROPERTY::WRITE_ENC);
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, NIMBLE_PROPERTY::READ | NIMBLE_PROPERTY::WRITE | NIMBLE_PROPERTY::READ_ENC | NIMBLE_PROPERTY::WRITE_ENC);
uint8_t desc1_val[] = { reportID, 0x02 };
outputReportDescriptor->setValue((uint8_t*) desc1_val, 2);
uint8_t desc1_val[] = {reportID, 0x02};
outputReportDescriptor->setValue((uint8_t*)desc1_val, 2);
return outputReportCharacteristic;
}
@ -156,11 +173,11 @@ NimBLECharacteristic* NimBLEHIDDevice::outputReport(uint8_t reportID) {
* @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, NIMBLE_PROPERTY::READ | NIMBLE_PROPERTY::WRITE | NIMBLE_PROPERTY::READ_ENC | NIMBLE_PROPERTY::WRITE_ENC);
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, NIMBLE_PROPERTY::READ | NIMBLE_PROPERTY::WRITE | NIMBLE_PROPERTY::READ_ENC | NIMBLE_PROPERTY::WRITE_ENC);
uint8_t desc1_val[] = { reportID, 0x03 };
featureReportDescriptor->setValue((uint8_t*) desc1_val, 2);
uint8_t desc1_val[] = {reportID, 0x03};
featureReportDescriptor->setValue((uint8_t*)desc1_val, 2);
return featureReportCharacteristic;
}
@ -169,14 +186,14 @@ NimBLECharacteristic* NimBLEHIDDevice::featureReport(uint8_t reportID) {
* @brief Creates a keyboard boot input report characteristic
*/
NimBLECharacteristic* NimBLEHIDDevice::bootInput() {
return m_hidService->createCharacteristic((uint16_t) 0x2a22, NIMBLE_PROPERTY::NOTIFY);
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);
return m_hidService->createCharacteristic((uint16_t)0x2a32, NIMBLE_PROPERTY::READ | NIMBLE_PROPERTY::WRITE | NIMBLE_PROPERTY::WRITE_NR);
}
/**

View File

@ -33,6 +33,7 @@
#define HID_DIGITAL_PEN 0x03C7
#define HID_BARCODE 0x03C8
#define PNPVersionField(MajorVersion, MinorVersion, PatchVersion) ((MajorVersion << 16) & 0xFF00) | ((MinorVersion << 8) & 0x00F0) | (PatchVersion & 0x000F)
/**
* @brief A model of a %BLE Human Interface Device.

View File

@ -14,6 +14,7 @@
#if defined(CONFIG_NIMBLE_CPP_IDF) // using esp-idf
# include "esp_log.h"
# include "console/console.h"
# ifndef CONFIG_NIMBLE_CPP_LOG_LEVEL
# define CONFIG_NIMBLE_CPP_LOG_LEVEL 0
# endif
@ -35,22 +36,6 @@
# define NIMBLE_LOGE(tag, format, ...) \
NIMBLE_CPP_LOG_PRINT(ESP_LOG_ERROR, tag, format, ##__VA_ARGS__)
# define NIMBLE_LOGC(tag, format, ...) \
NIMBLE_CPP_LOG_PRINT(ESP_LOG_ERROR, tag, format, ##__VA_ARGS__)
// These defines pollute the global namespace and conflict with Tasmota and basically always turn on `seriallog 3`
#ifdef LOG_LEVEL_DEBUG
#undef LOG_LEVEL_DEBUG
#endif
#ifdef LOG_LEVEL_INFO
#undef LOG_LEVEL_INFO
#endif
#ifdef LOG_LEVEL_ERROR
#undef LOG_LEVEL_ERROR
#endif
#else // using Arduino
# include "nimble/porting/nimble/include/syscfg/syscfg.h"
# include "nimble/console/console.h"
@ -82,12 +67,13 @@
# if CONFIG_NIMBLE_CPP_LOG_LEVEL >= 1
# define NIMBLE_LOGE( tag, format, ... ) console_printf("E %s: " format "\n", tag, ##__VA_ARGS__)
# define NIMBLE_LOGC( tag, format, ... ) console_printf("CRIT %s: " format "\n", tag, ##__VA_ARGS__)
# else
# define NIMBLE_LOGE( tag, format, ... ) (void)tag
# define NIMBLE_LOGC( tag, format, ... ) (void)tag
# endif
#endif /* CONFIG_NIMBLE_CPP_IDF */
#define NIMBLE_LOGC( tag, format, ... ) console_printf("CRIT %s: " format "\n", tag, ##__VA_ARGS__)
#endif /* CONFIG_BT_ENABLED */
#endif /* MAIN_NIMBLELOG_H_ */

View File

@ -359,7 +359,7 @@ bool NimBLEScan::start(uint32_t duration, bool is_continue) {
case BLE_HS_EOS:
case BLE_HS_ECONTROLLER:
case BLE_HS_ENOTSYNCED:
NIMBLE_LOGC(LOG_TAG, "Unable to scan - Host Reset");
NIMBLE_LOGE(LOG_TAG, "Unable to scan - Host Reset");
break;
default:
@ -459,7 +459,7 @@ void NimBLEScan::onHostSync() {
/**
* @brief Start scanning and block until scanning has been completed.
* @param [in] duration The duration in seconds for which to scan.
* @param [in] duration The duration in milliseconds for which to scan.
* @param [in] is_continue Set to true to save previous scan results, false to clear them.
* @return The scan results.
*/

View File

@ -27,6 +27,11 @@
#include "nimble/nimble/host/services/gatt/include/services/gatt/ble_svc_gatt.h"
#endif
#include <limits.h>
#define NIMBLE_SERVER_GET_PEER_NAME_ON_CONNECT_CB 0
#define NIMBLE_SERVER_GET_PEER_NAME_ON_AUTH_CB 1
static const char* LOG_TAG = "NimBLEServer";
static NimBLEServerCallbacks defaultCallbacks;
@ -47,6 +52,7 @@ NimBLEServer::NimBLEServer() {
#endif
m_svcChanged = false;
m_deleteCallbacks = true;
m_getPeerNameOnConnect = false;
} // NimBLEServer
@ -186,9 +192,8 @@ void NimBLEServer::start() {
int rc = ble_gatts_start();
if (rc != 0) {
NIMBLE_LOGE(LOG_TAG, "ble_gatts_start; rc=%d, %s", rc,
NimBLEUtils::returnCodeToString(rc));
abort();
NIMBLE_LOGE(LOG_TAG, "ble_gatts_start; rc=%d, %s", rc, NimBLEUtils::returnCodeToString(rc));
return;
}
#if CONFIG_NIMBLE_CPP_LOG_LEVEL >= 4
@ -215,7 +220,9 @@ void NimBLEServer::start() {
if(svc->m_removed == 0) {
rc = ble_gatts_find_svc(&svc->getUUID().getNative()->u, &svc->m_handle);
if(rc != 0) {
abort();
NIMBLE_LOGW(LOG_TAG, "GATT Server started without service: %s, Service %s",
svc->getUUID().toString().c_str(), svc->isStarted() ? "missing" : "not started");
continue; // Skip this service as it was not started
}
}
@ -252,6 +259,15 @@ int NimBLEServer::disconnect(uint16_t connId, uint8_t reason) {
return rc;
} // disconnect
/**
* @brief Disconnect the specified client with optional reason.
* @param [in] connInfo Connection of the client to disconnect.
* @param [in] reason code for disconnecting.
* @return NimBLE host return code.
*/
int NimBLEServer::disconnect(const NimBLEConnInfo &connInfo, uint8_t reason) {
return disconnect(connInfo.getConnHandle(), reason);
} // disconnect
#if !CONFIG_BT_NIMBLE_EXT_ADV || defined(_DOXYGEN_)
/**
@ -263,6 +279,15 @@ void NimBLEServer::advertiseOnDisconnect(bool aod) {
} // advertiseOnDisconnect
#endif
/**
* @brief Set the server to automatically read the name from the connected peer before
* the onConnect callback is called and enables the override callback with name parameter.
* @param [in] enable Enable reading the connected peer name upon connection.
*/
void NimBLEServer::getPeerNameOnConnect(bool enable) {
m_getPeerNameOnConnect = enable;
} // getPeerNameOnConnect
/**
* @brief Return the number of connected clients.
* @return The number of connected clients.
@ -328,6 +353,113 @@ NimBLEConnInfo NimBLEServer::getPeerIDInfo(uint16_t id) {
return peerInfo;
} // getPeerIDInfo
/**
* @brief Callback that is called after reading from the peer name characteristic.
* @details This will check the task pointer in the task data struct to determine
* the action to take once the name has been read. If there is a task waiting then
* it will be woken, if not, the the RC value is checked to determine which callback
* should be called.
*/
int NimBLEServer::peerNameCB(uint16_t conn_handle,
const struct ble_gatt_error *error,
struct ble_gatt_attr *attr,
void *arg) {
ble_task_data_t *pTaskData = (ble_task_data_t*)arg;
std::string *name = (std::string*)pTaskData->buf;
int rc = error->status;
if (rc == 0) {
if (attr) {
name->append(OS_MBUF_DATA(attr->om, char*), OS_MBUF_PKTLEN(attr->om));
return rc;
}
}
if (rc == BLE_HS_EDONE) {
// No ask means this was read for a callback.
if (pTaskData->task == nullptr) {
NimBLEServer* pServer = (NimBLEServer*)pTaskData->pATT;
NimBLEConnInfo peerInfo{};
ble_gap_conn_find(conn_handle, &peerInfo.m_desc);
// Use the rc value as a flag to indicate which callback should be called.
if (pTaskData->rc == NIMBLE_SERVER_GET_PEER_NAME_ON_CONNECT_CB) {
pServer->m_pServerCallbacks->onConnect(pServer, peerInfo, *name);
} else if (pTaskData->rc == NIMBLE_SERVER_GET_PEER_NAME_ON_AUTH_CB) {
pServer->m_pServerCallbacks->onAuthenticationComplete(peerInfo, *name);
}
}
} else {
NIMBLE_LOGE(LOG_TAG, "NimBLEServerPeerNameCB rc=%d; %s", rc, NimBLEUtils::returnCodeToString(rc));
}
if (pTaskData->task != nullptr) {
pTaskData->rc = rc;
xTaskNotifyGive(pTaskData->task);
} else {
// If the read was triggered for callback use then these were allocated.
delete name;
delete pTaskData;
}
return rc;
}
/**
* @brief Internal method that sends the read command.
*/
std::string NimBLEServer::getPeerNameInternal(uint16_t conn_handle, TaskHandle_t task, int cb_type) {
std::string *buf = new std::string{};
ble_task_data_t *taskData = new ble_task_data_t{this, task, cb_type, buf};
ble_uuid16_t uuid {{BLE_UUID_TYPE_16}, BLE_SVC_GAP_CHR_UUID16_DEVICE_NAME};
int rc = ble_gattc_read_by_uuid(conn_handle,
1,
0xffff,
((ble_uuid_t*)&uuid),
NimBLEServer::peerNameCB,
taskData);
if (rc != 0) {
NIMBLE_LOGE(LOG_TAG, "ble_gattc_read_by_uuid rc=%d, %s", rc, NimBLEUtils::returnCodeToString(rc));
NimBLEConnInfo peerInfo{};
ble_gap_conn_find(conn_handle, &peerInfo.m_desc);
if (cb_type == NIMBLE_SERVER_GET_PEER_NAME_ON_CONNECT_CB) {
m_pServerCallbacks->onConnect(this, peerInfo, *buf);
} else if (cb_type == NIMBLE_SERVER_GET_PEER_NAME_ON_AUTH_CB) {
m_pServerCallbacks->onAuthenticationComplete(peerInfo, *buf);
}
delete buf;
delete taskData;
} else if (task != nullptr) {
#ifdef ulTaskNotifyValueClear
// Clear the task notification value to ensure we block
ulTaskNotifyValueClear(task, ULONG_MAX);
#endif
ulTaskNotifyTake(pdTRUE, portMAX_DELAY);
rc = taskData->rc;
std::string name{*(std::string*)taskData->buf};
delete buf;
delete taskData;
if (rc != 0 && rc != BLE_HS_EDONE) {
NIMBLE_LOGE(LOG_TAG, "getPeerName rc=%d %s", rc, NimBLEUtils::returnCodeToString(rc));
}
return name;
}
// TaskData and name buffer will be deleted in the callback.
return "";
}
/**
* @brief Get the name of the connected peer.
* @param connInfo A reference to a NimBLEConnInfo instance to read the name from.
* @returns A string containing the name.
* @note This is a blocking call and should NOT be called from any callbacks!
*/
std::string NimBLEServer::getPeerName(const NimBLEConnInfo& connInfo) {
std::string name = getPeerNameInternal(connInfo.getConnHandle(), xTaskGetCurrentTaskHandle());
return name;
}
/**
* @brief Handle a GATT Server Event.
@ -354,16 +486,22 @@ int NimBLEServer::handleGapEvent(struct ble_gap_event *event, void *arg) {
#if !CONFIG_BT_NIMBLE_EXT_ADV
NimBLEDevice::startAdvertising();
#endif
}
else {
pServer->m_connectedPeersVec.push_back(event->connect.conn_handle);
} else {
rc = ble_gap_conn_find(event->connect.conn_handle, &peerInfo.m_desc);
if (rc != 0) {
return 0;
}
pServer->m_pServerCallbacks->onConnect(pServer, peerInfo);
pServer->m_connectedPeersVec.push_back(event->connect.conn_handle);
if (pServer->m_getPeerNameOnConnect) {
pServer->getPeerNameInternal(event->connect.conn_handle,
nullptr,
NIMBLE_SERVER_GET_PEER_NAME_ON_CONNECT_CB);
} else {
pServer->m_pServerCallbacks->onConnect(pServer, peerInfo);
}
}
return 0;
@ -378,7 +516,7 @@ int NimBLEServer::handleGapEvent(struct ble_gap_event *event, void *arg) {
case BLE_HS_EOS:
case BLE_HS_ECONTROLLER:
case BLE_HS_ENOTSYNCED:
NIMBLE_LOGC(LOG_TAG, "Disconnect - host reset, rc=%d", event->disconnect.reason);
NIMBLE_LOGE(LOG_TAG, "Disconnect - host reset, rc=%d", event->disconnect.reason);
NimBLEDevice::onReset(event->disconnect.reason);
break;
default:
@ -514,10 +652,26 @@ int NimBLEServer::handleGapEvent(struct ble_gap_event *event, void *arg) {
return BLE_ATT_ERR_INVALID_HANDLE;
}
pServer->m_pServerCallbacks->onAuthenticationComplete(peerInfo);
if (pServer->m_getPeerNameOnConnect) {
pServer->getPeerNameInternal(event->enc_change.conn_handle,
nullptr,
NIMBLE_SERVER_GET_PEER_NAME_ON_AUTH_CB);
} else {
pServer->m_pServerCallbacks->onAuthenticationComplete(peerInfo);
}
return 0;
} // BLE_GAP_EVENT_ENC_CHANGE
case BLE_GAP_EVENT_IDENTITY_RESOLVED: {
rc = ble_gap_conn_find(event->identity_resolved.conn_handle, &peerInfo.m_desc);
if(rc != 0) {
return BLE_ATT_ERR_INVALID_HANDLE;
}
pServer->m_pServerCallbacks->onIdentity(peerInfo);
return 0;
} // BLE_GAP_EVENT_IDENTITY_RESOLVED
case BLE_GAP_EVENT_PASSKEY_ACTION: {
struct ble_sm_io pkey = {0,0};
@ -528,19 +682,20 @@ int NimBLEServer::handleGapEvent(struct ble_gap_event *event, void *arg) {
// if the (static)passkey is the default, check the callback for custom value
// both values default to the same.
if(pkey.passkey == 123456) {
pkey.passkey = pServer->m_pServerCallbacks->onPassKeyRequest();
pkey.passkey = pServer->m_pServerCallbacks->onPassKeyDisplay();
}
rc = ble_sm_inject_io(event->passkey.conn_handle, &pkey);
NIMBLE_LOGD(LOG_TAG, "BLE_SM_IOACT_DISP; ble_sm_inject_io result: %d", rc);
} else if (event->passkey.params.action == BLE_SM_IOACT_NUMCMP) {
NIMBLE_LOGD(LOG_TAG, "Passkey on device's display: %" PRIu32, event->passkey.params.numcmp);
pkey.action = event->passkey.params.action;
pkey.numcmp_accept = pServer->m_pServerCallbacks->onConfirmPIN(event->passkey.params.numcmp);
rc = ble_sm_inject_io(event->passkey.conn_handle, &pkey);
NIMBLE_LOGD(LOG_TAG, "BLE_SM_IOACT_NUMCMP; ble_sm_inject_io result: %d", rc);
rc = ble_gap_conn_find(event->passkey.conn_handle, &peerInfo.m_desc);
if(rc != 0) {
return BLE_ATT_ERR_INVALID_HANDLE;
}
pServer->m_pServerCallbacks->onConfirmPIN(peerInfo, event->passkey.params.numcmp);
//TODO: Handle out of band pairing
} else if (event->passkey.params.action == BLE_SM_IOACT_OOB) {
static uint8_t tem_oob[16] = {0};
@ -551,14 +706,6 @@ int NimBLEServer::handleGapEvent(struct ble_gap_event *event, void *arg) {
rc = ble_sm_inject_io(event->passkey.conn_handle, &pkey);
NIMBLE_LOGD(LOG_TAG, "BLE_SM_IOACT_OOB; ble_sm_inject_io result: %d", rc);
//////////////////////////////////
} else if (event->passkey.params.action == BLE_SM_IOACT_INPUT) {
NIMBLE_LOGD(LOG_TAG, "Enter the passkey");
pkey.action = event->passkey.params.action;
pkey.passkey = pServer->m_pServerCallbacks->onPassKeyRequest();
rc = ble_sm_inject_io(event->passkey.conn_handle, &pkey);
NIMBLE_LOGD(LOG_TAG, "BLE_SM_IOACT_INPUT; ble_sm_inject_io result: %d", rc);
} else if (event->passkey.params.action == BLE_SM_IOACT_NONE) {
NIMBLE_LOGD(LOG_TAG, "No passkey action required");
}
@ -842,6 +989,10 @@ void NimBLEServerCallbacks::onConnect(NimBLEServer* pServer, NimBLEConnInfo& con
NIMBLE_LOGD("NimBLEServerCallbacks", "onConnect(): Default");
} // onConnect
void NimBLEServerCallbacks::onConnect(NimBLEServer* pServer, NimBLEConnInfo& connInfo, std::string& name) {
NIMBLE_LOGD("NimBLEServerCallbacks", "onConnect(): Default");
} // onConnect
void NimBLEServerCallbacks::onDisconnect(NimBLEServer* pServer,
NimBLEConnInfo& connInfo, int reason) {
NIMBLE_LOGD("NimBLEServerCallbacks", "onDisconnect(): Default");
@ -851,18 +1002,26 @@ void NimBLEServerCallbacks::onMTUChange(uint16_t MTU, NimBLEConnInfo& connInfo)
NIMBLE_LOGD("NimBLEServerCallbacks", "onMTUChange(): Default");
} // onMTUChange
uint32_t NimBLEServerCallbacks::onPassKeyRequest(){
NIMBLE_LOGD("NimBLEServerCallbacks", "onPassKeyRequest: default: 123456");
uint32_t NimBLEServerCallbacks::onPassKeyDisplay(){
NIMBLE_LOGD("NimBLEServerCallbacks", "onPassKeyDisplay: default: 123456");
return 123456;
} //onPassKeyRequest
} //onPassKeyDisplay
void NimBLEServerCallbacks::onAuthenticationComplete(NimBLEConnInfo& connInfo){
void NimBLEServerCallbacks::onConfirmPIN(const NimBLEConnInfo& connInfo, uint32_t pin){
NIMBLE_LOGD("NimBLEServerCallbacks", "onConfirmPIN: default: true");
NimBLEDevice::injectConfirmPIN(connInfo, true);
} // onConfirmPIN
void NimBLEServerCallbacks::onIdentity(const NimBLEConnInfo& connInfo){
NIMBLE_LOGD("NimBLEServerCallbacks", "onIdentity: default");
} // onIdentity
void NimBLEServerCallbacks::onAuthenticationComplete(const NimBLEConnInfo& connInfo){
NIMBLE_LOGD("NimBLEServerCallbacks", "onAuthenticationComplete: default");
} // onAuthenticationComplete
bool NimBLEServerCallbacks::onConfirmPIN(uint32_t pin){
NIMBLE_LOGD("NimBLEServerCallbacks", "onConfirmPIN: default: true");
return true;
} // onConfirmPIN
void NimBLEServerCallbacks::onAuthenticationComplete(const NimBLEConnInfo& connInfo, const std::string& name){
NIMBLE_LOGD("NimBLEServerCallbacks", "onAuthenticationComplete: default");
} // onAuthenticationComplete
#endif /* CONFIG_BT_ENABLED && CONFIG_BT_NIMBLE_ROLE_PERIPHERAL */

View File

@ -69,6 +69,8 @@ public:
NimBLEService* getServiceByHandle(uint16_t handle);
int disconnect(uint16_t connID,
uint8_t reason = BLE_ERR_REM_USER_CONN_TERM);
int disconnect(const NimBLEConnInfo &connInfo,
uint8_t reason = BLE_ERR_REM_USER_CONN_TERM);
void updateConnParams(uint16_t conn_handle,
uint16_t minInterval, uint16_t maxInterval,
uint16_t latency, uint16_t timeout);
@ -78,6 +80,8 @@ public:
NimBLEConnInfo getPeerInfo(size_t index);
NimBLEConnInfo getPeerInfo(const NimBLEAddress& address);
NimBLEConnInfo getPeerIDInfo(uint16_t id);
std::string getPeerName(const NimBLEConnInfo& connInfo);
void getPeerNameOnConnect(bool enable);
#if !CONFIG_BT_NIMBLE_EXT_ADV || defined(_DOXYGEN_)
void advertiseOnDisconnect(bool);
#endif
@ -98,6 +102,7 @@ private:
#if !CONFIG_BT_NIMBLE_EXT_ADV
bool m_advertiseOnDisconnect;
#endif
bool m_getPeerNameOnConnect;
bool m_svcChanged;
NimBLEServerCallbacks* m_pServerCallbacks;
bool m_deleteCallbacks;
@ -110,10 +115,14 @@ private:
std::vector<NimBLECharacteristic*> m_notifyChrVec;
static int handleGapEvent(struct ble_gap_event *event, void *arg);
static int peerNameCB(uint16_t conn_handle, const struct ble_gatt_error *error,
struct ble_gatt_attr *attr, void *arg);
std::string getPeerNameInternal(uint16_t conn_handle, TaskHandle_t task, int cb_type = -1);
void serviceChanged();
void resetGATT();
bool setIndicateWait(uint16_t conn_handle);
void clearIndicateWait(uint16_t conn_handle);
}; // NimBLEServer
@ -128,11 +137,21 @@ public:
* @brief Handle a client connection.
* This is called when a client connects.
* @param [in] pServer A pointer to the %BLE server that received the client connection.
* @param [in] connInfo A reference to a NimBLEConnInfo instance with information
* @param [in] connInfo A reference to a NimBLEConnInfo instance with information.
* about the peer connection parameters.
*/
virtual void onConnect(NimBLEServer* pServer, NimBLEConnInfo& connInfo);
/**
* @brief Handle a client connection.
* This is called when a client connects.
* @param [in] pServer A pointer to the %BLE server that received the client connection.
* @param [in] connInfo A reference to a NimBLEConnInfo instance with information.
* @param [in] name The name of the connected peer device.
* about the peer connection parameters.
*/
virtual void onConnect(NimBLEServer* pServer, NimBLEConnInfo& connInfo, std::string& name);
/**
* @brief Handle a client disconnection.
* This is called when a client discconnects.
@ -152,24 +171,39 @@ public:
virtual void onMTUChange(uint16_t MTU, NimBLEConnInfo& connInfo);
/**
* @brief Called when a client requests a passkey for pairing.
* @brief Called when a client requests a passkey for pairing (display).
* @return The passkey to be sent to the client.
*/
virtual uint32_t onPassKeyRequest();
virtual uint32_t onPassKeyDisplay();
/**
* @brief Called when using numeric comparision for pairing.
* @param [in] connInfo A reference to a NimBLEConnInfo instance with information
* Should be passed back to NimBLEDevice::injectConfirmPIN
* @param [in] pin The pin to compare with the client.
*/
virtual void onConfirmPIN(const NimBLEConnInfo& connInfo, uint32_t pin);
/**
* @brief Called when the pairing procedure is complete.
* @param [in] connInfo A reference to a NimBLEConnInfo instance with information
* about the peer connection parameters.
*/
virtual void onAuthenticationComplete(NimBLEConnInfo& connInfo);
virtual void onAuthenticationComplete(const NimBLEConnInfo& connInfo);
/**
* @brief Called when using numeric comparision for pairing.
* @param [in] pin The pin to compare with the client.
* @return True to accept the pin.
* @brief Called when the pairing procedure is complete.
* @param [in] connInfo A reference to a NimBLEConnInfo instance with information
* @param [in] name The name of the connected peer device.
* about the peer connection parameters.
*/
virtual bool onConfirmPIN(uint32_t pin);
virtual void onAuthenticationComplete(const NimBLEConnInfo& connInfo, const std::string& name);
/**
* @brief Called when the peer identity address is resolved.
* @param [in] connInfo A reference to a NimBLEConnInfo instance with information
*/
virtual void onIdentity(const NimBLEConnInfo& connInfo);
}; // NimBLEServerCallbacks
#endif /* CONFIG_BT_ENABLED && CONFIG_BT_NIMBLE_ROLE_PERIPHERAL */

View File

@ -126,7 +126,7 @@ bool NimBLEService::start() {
// Nimble requires an array of services to be sent to the api
// Since we are adding 1 at a time we create an array of 2 and set the type
// of the second service to 0 to indicate the end of the array.
ble_gatt_svc_def* svc = new ble_gatt_svc_def[2];
ble_gatt_svc_def* svc = new ble_gatt_svc_def[2]{};
ble_gatt_chr_def* pChr_a = nullptr;
ble_gatt_dsc_def* pDsc_a = nullptr;
@ -188,7 +188,7 @@ bool NimBLEService::start() {
pChr_a[i].descriptors = NULL;
} else {
// Must have last descriptor uuid = 0 so we have to create 1 extra
pDsc_a = new ble_gatt_dsc_def[numDscs+1];
pDsc_a = new ble_gatt_dsc_def[numDscs+1]{};
int d = 0;
for(auto dsc_it = (*chr_it)->m_dscVec.begin(); dsc_it != (*chr_it)->m_dscVec.end(); ++dsc_it ) {
if((*dsc_it)->m_removed > 0) {
@ -434,4 +434,14 @@ NimBLEServer* NimBLEService::getServer() {
return NimBLEDevice::getServer();
}// getServer
/**
* @brief Checks if the service has been started.
* @return True if the service has been started.
*/
bool NimBLEService::isStarted() {
return m_pSvcDef != nullptr;
}
#endif /* CONFIG_BT_ENABLED && CONFIG_BT_NIMBLE_ROLE_PERIPHERAL */

View File

@ -44,7 +44,7 @@ public:
uint16_t getHandle();
std::string toString();
void dump();
bool isStarted();
bool start();
NimBLECharacteristic* createCharacteristic(const char* uuid,

View File

@ -16,45 +16,6 @@
static const char* LOG_TAG = "NimBLEUtils";
/**
* @brief A function for checking validity of connection parameters.
* @param [in] params A pointer to the structure containing the parameters to check.
* @return valid == 0 or error code.
*/
int NimBLEUtils::checkConnParams(ble_gap_conn_params* params) {
/* Check connection interval min */
if ((params->itvl_min < BLE_HCI_CONN_ITVL_MIN) ||
(params->itvl_min > BLE_HCI_CONN_ITVL_MAX)) {
return BLE_ERR_INV_HCI_CMD_PARMS;
}
/* Check connection interval max */
if ((params->itvl_max < BLE_HCI_CONN_ITVL_MIN) ||
(params->itvl_max > BLE_HCI_CONN_ITVL_MAX) ||
(params->itvl_max < params->itvl_min)) {
return BLE_ERR_INV_HCI_CMD_PARMS;
}
/* Check connection latency */
if (params->latency > BLE_HCI_CONN_LATENCY_MAX) {
return BLE_ERR_INV_HCI_CMD_PARMS;
}
/* Check supervision timeout */
if ((params->supervision_timeout < BLE_HCI_CONN_SPVN_TIMEOUT_MIN) ||
(params->supervision_timeout > BLE_HCI_CONN_SPVN_TIMEOUT_MAX)) {
return BLE_ERR_INV_HCI_CMD_PARMS;
}
/* Check connection event length */
if (params->min_ce_len > params->max_ce_len) {
return BLE_ERR_INV_HCI_CMD_PARMS;
}
return 0;
}
/**
* @brief Converts a return code from the NimBLE stack to a text string.
* @param [in] rc The return code to convert.

View File

@ -43,7 +43,6 @@ public:
static char* buildHexData(uint8_t* target, const uint8_t* source, uint8_t length);
static const char* advTypeToString(uint8_t advType);
static const char* returnCodeToString(int rc);
static int checkConnParams(ble_gap_conn_params* params);
};

View File

@ -32,6 +32,18 @@
# endif
#endif
#if CONFIG_NIMBLE_CPP_DEBUG_ASSERT_ENABLED && !defined NDEBUG
void nimble_cpp_assert(const char *file, unsigned line) __attribute((weak, noreturn));
# define NIMBLE_ATT_VAL_FILE (__builtin_strrchr(__FILE__, '/') ? \
__builtin_strrchr (__FILE__, '/') + 1 : __FILE__)
# define NIMBLE_CPP_DEBUG_ASSERT(cond) \
if (!(cond)) { \
nimble_cpp_assert(NIMBLE_ATT_VAL_FILE, __LINE__); \
}
#else
# define NIMBLE_CPP_DEBUG_ASSERT(cond) (void(0))
#endif
#endif /* CONFIG_BT_ENABLED */
#ifdef _DOXYGEN_

View File

@ -1501,6 +1501,7 @@ set_button_text|lv.obj, string||[lv_list_set_button_text](https://docs.lvgl.io/9
Method|Arguments|Return type|LVGL equivalent
:---|:---|:---|:---
get_anim||lv.anim|[lv_animimg_get_anim](https://docs.lvgl.io/9.0/search.html?q=lv_animimg_get_anim)
get_duration||int|[lv_animimg_get_duration](https://docs.lvgl.io/9.0/search.html?q=lv_animimg_get_duration)
get_repeat_count||int|[lv_animimg_get_repeat_count](https://docs.lvgl.io/9.0/search.html?q=lv_animimg_get_repeat_count)
get_src_count||int|[lv_animimg_get_src_count](https://docs.lvgl.io/9.0/search.html?q=lv_animimg_get_src_count)

View File

@ -818,6 +818,7 @@ const be_ntv_func_def_t lv_timer_func[] = {
/* `lv_animimg` methods */
#ifdef BE_LV_WIDGET_ANIMIMG
const be_ntv_func_def_t lv_animimg_func[] = {
{ "get_anim", { (const void*) &lv_animimg_get_anim, "lv.anim", "(lv.obj)" } },
{ "get_duration", { (const void*) &lv_animimg_get_duration, "i", "(lv.obj)" } },
{ "get_repeat_count", { (const void*) &lv_animimg_get_repeat_count, "i", "(lv.obj)" } },
{ "get_src_count", { (const void*) &lv_animimg_get_src_count, "i", "(lv.obj)" } },

View File

@ -1013,6 +1013,7 @@ const void ** lv_animimg_get_src(lv_obj_t * img)
uint8_t lv_animimg_get_src_count(lv_obj_t * img)
uint32_t lv_animimg_get_duration(lv_obj_t * img)
uint32_t lv_animimg_get_repeat_count(lv_obj_t * img)
lv_anim_t * lv_animimg_get_anim(lv_obj_t * img)
// ../../lvgl/src/widgets/arc/lv_arc.h
lv_obj_t * lv_arc_create(lv_obj_t * parent)
@ -1061,7 +1062,7 @@ lv_obj_t * lv_button_create(lv_obj_t * parent)
// ../../lvgl/src/widgets/buttonmatrix/lv_buttonmatrix.h
lv_obj_t * lv_buttonmatrix_create(lv_obj_t * parent)
void lv_buttonmatrix_set_map(lv_obj_t * obj, const char * map[])
void lv_buttonmatrix_set_map(lv_obj_t * obj, const char * const map[])
void lv_buttonmatrix_set_ctrl_map(lv_obj_t * obj, const lv_buttonmatrix_ctrl_t ctrl_map[])
void lv_buttonmatrix_set_selected_button(lv_obj_t * obj, uint32_t btn_id)
void lv_buttonmatrix_set_button_ctrl(lv_obj_t * obj, uint32_t btn_id, lv_buttonmatrix_ctrl_t ctrl)
@ -1070,7 +1071,7 @@ void lv_buttonmatrix_set_button_ctrl_all(lv_obj_t * obj, lv_buttonmatrix_ctrl_t
void lv_buttonmatrix_clear_button_ctrl_all(lv_obj_t * obj, lv_buttonmatrix_ctrl_t ctrl)
void lv_buttonmatrix_set_button_width(lv_obj_t * obj, uint32_t btn_id, uint32_t width)
void lv_buttonmatrix_set_one_checked(lv_obj_t * obj, bool en)
const char ** lv_buttonmatrix_get_map(const lv_obj_t * obj)
const char * const * lv_buttonmatrix_get_map(const lv_obj_t * obj)
uint32_t lv_buttonmatrix_get_selected_button(const lv_obj_t * obj)
const char * lv_buttonmatrix_get_button_text(const lv_obj_t * obj, uint32_t btn_id)
bool lv_buttonmatrix_has_button_ctrl(lv_obj_t * obj, uint32_t btn_id, lv_buttonmatrix_ctrl_t ctrl)
@ -1092,7 +1093,7 @@ lv_result_t lv_calendar_get_pressed_date(const lv_obj_t * calendar, lv_calendar_
// ../../lvgl/src/widgets/calendar/lv_calendar_chinese.h
void lv_calendar_set_chinese_mode(lv_obj_t * obj, bool en)
const char * lv_calendar_get_day_name(lv_calendar_date_t * gregorian)
lv_calendar_chinese_t lv_calendar_gregorian_to_chinese(lv_calendar_date_t * gregorian)
void lv_calendar_gregorian_to_chinese(lv_calendar_date_t * gregorian_time, lv_calendar_chinese_t * chinese_time)
// ../../lvgl/src/widgets/calendar/lv_calendar_header_arrow.h
lv_obj_t * lv_calendar_header_arrow_create(lv_obj_t * parent)
@ -1220,11 +1221,11 @@ lv_obj_t * lv_keyboard_create(lv_obj_t * parent)
void lv_keyboard_set_textarea(lv_obj_t * kb, lv_obj_t * ta)
void lv_keyboard_set_mode(lv_obj_t * kb, lv_keyboard_mode_t mode)
void lv_keyboard_set_popovers(lv_obj_t * kb, bool en)
void lv_keyboard_set_map(lv_obj_t * kb, lv_keyboard_mode_t mode, const char * map[], const lv_buttonmatrix_ctrl_t ctrl_map[])
void lv_keyboard_set_map(lv_obj_t * kb, lv_keyboard_mode_t mode, const char * const map[], const lv_buttonmatrix_ctrl_t ctrl_map[])
lv_obj_t * lv_keyboard_get_textarea(const lv_obj_t * kb)
lv_keyboard_mode_t lv_keyboard_get_mode(const lv_obj_t * kb)
bool lv_keyboard_get_popovers(const lv_obj_t * obj)
const char ** lv_keyboard_get_map_array(const lv_obj_t * kb)
const char * const * lv_keyboard_get_map_array(const lv_obj_t * kb)
uint32_t lv_keyboard_get_selected_button(const lv_obj_t * obj)
const char * lv_keyboard_get_button_text(const lv_obj_t * obj, uint32_t btn_id)

View File

@ -407,7 +407,7 @@ class type_mapper_class:
"lv_roller_mode_t": "i",
"lv_table_cell_ctrl_t": "i",
"lv_calendar_chinese_t": "c",
# "lv_calendar_chinese_t": "c",
# adding ad-hoc colorwheel from LVGL8 to LVGL9
"lv_colorwheel_mode_t": "i",
@ -459,6 +459,7 @@ class type_mapper_class:
# "char **": "lv_str_arr", # treat as a simple pointer, decoding needs to be done at Berry level
"constchar **": "c", # treat as a simple pointer, decoding needs to be done at Berry level
"void * []": "c", # treat as a simple pointer, decoding needs to be done at Berry level
"constchar * *": "c",
# callbacks
"lv_group_focus_cb_t": "lv_group_focus_cb",

View File

@ -136,7 +136,8 @@ class lvh_root
return def () end
end
def _delete()
# to be overriden
# remove from page
self._page.remove_obj(self.id)
end
#################################################################################
@ -668,8 +669,7 @@ class lvh_obj : lvh_root
self.remove_text_rule()
if (self._lv_label) self._lv_label.del() self._lv_label = nil end
if (self._lv_obj) self._lv_obj.del() self._lv_obj = nil end
# remove from page
self._page.remove_obj(self.id)
super(self)._delete()
end
#====================================================================
@ -880,7 +880,7 @@ class lvh_obj : lvh_root
def get_text_color(style_modifier)
return self._lv_obj.get_style_text_color(style_modifier)
end
def set_value_color(t) self.set_text_color(t) end
def set_value_color(t, style_modifier) self.set_text_color(t, style_modifier) end
def get_value_color() return self.get_value_color() end
#====================================================================
@ -1869,6 +1869,7 @@ class lvh_scale_section : lvh_root
self._style10 = nil
self._style30.del()
self._style30 = nil
super(self)._delete()
end
#- ------------------------------------------------------------#
@ -2186,10 +2187,10 @@ class lvh_chart : lvh_obj
end
def set_series1_color(color)
self._lv_obj.set_series_color(self._ser1, color)
self._lv_obj.set_series_color(self._ser1, self.parse_color(color))
end
def set_series2_color(color)
self._lv_obj.set_series_color(self._ser2, color)
self._lv_obj.set_series_color(self._ser2, self.parse_color(color))
end
def set_h_div_line_count(_h_div)
self._h_div = _h_div
@ -2365,14 +2366,43 @@ class lvh_page
# create a global for this page of form p<page_number>, ex `p1`
# create a global for the page attributes as p<page_number>b0, ex `p1b0`
global.("p" + str(self._page_id)) = self
global.("p" + str(self._page_id) + "b0") = obj_scr
global.(f"p{self._page_id}") = self
global.(f"p{self._page_id}b0") = obj_scr
end
#####################################################################
# General Setters and Getters
#####################################################################
#- ------------------------------------------------------------#
# Internal utility functions
#
# Mapping of virtual attributes
#
#- ------------------------------------------------------------#
# `member` virtual getter
#- ------------------------------------------------------------#
def member(k)
import string
import introspect
if string.startswith(k, "set_") || string.startswith(k, "get_") return end
# if attribute name is in ignore list, abort
# if self._attr_ignore.find(k) != nil return end
# we don't need an ignore list for pages
# first check if there is a method named `get_X()`
var f = introspect.get(self, "get_" + k) # call self method
if type(f) == 'function'
# print(f">>>: getmember local method get_{k}")
return f(self)
end
# fallback to exception if attribute unknown or not a function
return module("undefined")
end
#====================================================================
# retrieve lvgl screen object for this page
#====================================================================
@ -2413,6 +2443,44 @@ class lvh_page
end
end
#====================================================================
# `delete` special attribute used to delete the object
#====================================================================
def get_clear()
self._clear()
return def () end
end
def _clear()
# iterate on all objects and try to delete
# we first get a copy of all ids so we can delete and continue iterating
# without fearing about an infinite loop
var ids = []
for id: self._obj_id.keys()
ids.push(id)
end
# we iterate until the array is empty
var idx = 0
while idx < size(ids)
var page_id = ids[idx]
if (page_id != 0) && self._obj_id.contains(page_id)
# first check if the id is still in the page - it could have been already removed if it's a sub-object
self._obj_id[page_id]._delete()
end
idx += 1
end
self._obj_id = {} # clear map
end
def get_delete()
self._delete()
return def () end
end
def _delete()
# remove from page, also change page if this is the current one
self._hm._remove_page(self._page_id)
# clear content
self._clear()
end
#====================================================================
# `show` transition from one page to another
# duration: in ms, default 500 ms
@ -2477,6 +2545,7 @@ class HASPmota
# haspmota objects
var lvh_pages # (list of lvg_page) list of pages
var lvh_page_cur_idx # (int) current page index number
var lvh_page_cur_idx_parsing # (int) index of the current page related to parsing JSONL, can be different from the displayed page
# regex patterns
var re_page_target # compiled regex for action `p<number>`
# specific event_cb handling for less memory usage since we are registering a lot of callbacks
@ -2609,6 +2678,18 @@ class HASPmota
def get_page_cur()
return self.lvh_pages[self.lvh_page_cur_idx]
end
#====================================================================
# return an array of all pages numbers
#====================================================================
def get_pages()
return self.pages_list_sorted(nil)
end
#====================================================================
# return the current page being parsed with JSONL as `lvh_page` object
#====================================================================
def get_page_cur_parsing()
return self.lvh_pages[self.lvh_page_cur_idx_parsing]
end
#====================================================================
# load JSONL template
@ -2633,12 +2714,12 @@ class HASPmota
if tasmota.loglevel(4)
tasmota.log(f"HSP: parsing line '{jsonl[0]}'", 4)
end
self.parse_page(jline) # parse page first to create any page related objects, may change self.lvh_page_cur_idx
self.parse_page(jline) # parse page first to create any page related objects, may change self.lvh_page_cur_idx_parsing
# objects are created in the current page
if (self.lvh_pages == nil)
raise "value_error", "no page 'id' defined"
end
self.parse_obj(jline, self.lvh_pages[self.lvh_page_cur_idx]) # then parse object within this page
self.parse_obj(jline, self.lvh_pages[self.lvh_page_cur_idx_parsing]) # then parse object within this page
else
# check if it's invalid json
if size(string.tr(jsonl[0], " \t", "")) > 0
@ -2669,7 +2750,7 @@ class HASPmota
var jline = json.load(j)
if type(jline) == 'instance'
self.parse_page(jline) # parse page first to create any page related objects, may change self.lvh_page_cur_idx
self.parse_page(jline) # parse page first to create any page related objects, may change self.lvh_page_cur_idx_parsing
# objects are created in the current page
self.parse_obj(jline, self.lvh_pages[self.lvh_page_cur_idx]) # then parse object within this page
else
@ -2757,37 +2838,60 @@ class HASPmota
# Execute a page changing action from string `action`
#
# Arg1 `action` can be `prev`, `next`, `back` or `p<number>`
# Returns: nil
# of `delete` if we are deleting the current page
# duration: in ms, default 500 ms
# anim: -1 right to left, 1 left to right (default), `nil` auto, 0 none
# Returns: the target page object if changed, or `nil` if still on same page
#====================================================================
def page_show(action)
def page_show(action, anim, duration)
# resolve between page numbers
# p1 is either a number or nil (stored value)
# p2 is the default value
# l is the list of page ids
def to_page_resolve(p1, p_def, l)
if (p1 != nil) && (l.find(p1) != nil)
return p1
else
return p_def
end
end
# action can be `prev`, `next`, `back`, or `p<number>` like `p1`
var to_page = nil
var cur_page = self.lvh_pages[self.lvh_page_cur_idx]
var cur_page = self.get_page_cur()
var sorted_pages_list = self.pages_list_sorted(self.lvh_page_cur_idx)
if size(sorted_pages_list) <= 1 return end # if only 1 page, do nothing
if size(sorted_pages_list) <= 1 # if only 1 page, do nothing
return nil
end
# handle prev/next/back values
# get the corresponding value from page object,
# if absent, revert to next page, previous page and page 1
# print("sorted_pages_list",sorted_pages_list)
if action == 'prev'
to_page = int(cur_page.prev)
if to_page == nil to_page = sorted_pages_list[-1] end # if no prev, take the previous page
to_page = to_page_resolve(int(cur_page.prev), sorted_pages_list[-1], sorted_pages_list)
elif action == 'next'
to_page = int(cur_page.next)
if to_page == nil to_page = sorted_pages_list[1] end # if no next, take the next page
to_page = to_page_resolve(int(cur_page.next), sorted_pages_list[1], sorted_pages_list)
elif action == 'back'
to_page = int(cur_page.back)
if to_page == nil # if no back, take first page
to_page = self.pages_list_sorted(nil)[0]
end
to_page = to_page_resolve(int(cur_page.back), self.pages_list_sorted(nil)[0], sorted_pages_list)
elif action == 'delete'
to_page = to_page_resolve(int(cur_page.back), self.pages_list_sorted(nil)[0], sorted_pages_list)
if (to_page == cur_page.id())
to_page = to_page_resolve(int(cur_page.next), sorted_pages_list[1], sorted_pages_list)
end
elif self.re_page_target.match(action)
# action is supposed to be `p<number>` format
to_page = int(action[1..-1]) # just skip first char and convert the rest to a string
to_page = to_page_resolve(int(action[1..-1]), nil #-default to nil-#, sorted_pages_list)
end
# print("to_page=",to_page)
if to_page != nil && to_page > 0 # we have a target
self.lvh_pages[to_page].show() # switch to the target page
# print(f"{action=} {to_page=}")
if (to_page != nil) && (to_page > 0) # we have a target
var to_page_obj = self.lvh_pages[to_page]
# print(f"{to_page_obj.id()=}")
if (to_page_obj != nil)
to_page_obj.show(anim, duration)
end
return to_page_obj
end
end
@ -2800,7 +2904,11 @@ class HASPmota
def parse_page(jline)
if jline.has("page") && type(jline["page"]) == 'int'
var page = int(jline["page"])
self.lvh_page_cur_idx = page # change current page
# print(f">>> parsing page {page}")
self.lvh_page_cur_idx_parsing = page # change current page
if (self.lvh_page_cur_idx == nil) # also set current page if we haven't any yet
self.lvh_page_cur_idx = page
end
# create the page object if it doesn't exist already
if !self.lvh_pages.contains(page)
@ -2810,7 +2918,7 @@ class HASPmota
# check if there is "id":0
if jline.find("id") == 0
var lvh_page_cur = self.get_page_cur()
var lvh_page_cur = self.get_page_cur_parsing()
lvh_page_cur.prev = int(jline.find("prev", nil))
lvh_page_cur.next = int(jline.find("next", nil))
lvh_page_cur.back = int(jline.find("back", nil))
@ -2818,6 +2926,36 @@ class HASPmota
end
end
#====================================================================
# Remove page by id
#
# Should not be called directly. Indirectly called by `p<x>.delete`
#
# Only removes reference to the page at root level
# Change the active page if needed
#====================================================================
def _remove_page(page_id)
# check if we remove the active page
var cur_page_id = self.get_page_cur().id()
if (page_id == cur_page_id)
# if we try to delete the current page, move do main page
var to_page_obj = self.page_show("delete", 0, 0 #-no animation-#) # get the target page as result
if (to_page_obj == nil) # we didn't change page
return
end
end
# also update lvh_page_cur_idx_parsing, if we removed the current parsing page
if (self.lvh_page_cur_idx_parsing == page_id)
self.lvh_page_cur_idx_parsing = self.lvh_page_cur_idx
end
# remove object from page object
if self.lvh_pages.contains(page_id)
self.lvh_pages.remove(page_id)
end
# remove global for page
global.(f"p{page_id}") = nil
end
#====================================================================
# Event CB handling
#====================================================================
@ -2856,65 +2994,70 @@ class HASPmota
#====================================================================
# Parse single object
#
# The object may be pre-existing or brand new
#====================================================================
def parse_obj(jline, page)
import global
import introspect
var obj_id = int(jline.find("id")) # id number or nil
var obj_type = str(jline.find("obj")) # obj class or nil
var obj_lvh # lvgl object created
var lvh_page_cur = self.get_page_cur() # current page object
var obj_type = jline.find("obj") # obj class or nil
obj_type = (obj_type != nil) ? str(obj_type) : nil
var lvh_page_cur = self.get_page_cur_parsing() # current page object, cannot be nil
# first run any Berry code embedded
var berry_run = str(jline.find("berry_run"))
var func_compiled
if berry_run != "nil"
try
func_compiled = compile(berry_run)
except .. as e,m
print(format("HSP: unable to compile berry code \"%s\" - '%s' - %s", berry_run, e, m))
# Step 1. Check the id for valid range
# 'obj_id' must be between 1 and 254
if (obj_id != nil) && (obj_id < 0 || obj_id > 254)
if (obj_id != 0) || (obj_type == nil)
# if `obj_id` is not `nil` and not `0`, it must have `obj_type` not set to `nil`
print(f"HSP: invalid 'id': {obj_id} for 'obj': {obj_type}")
return
end
end
# if line contains botn 'obj' and 'id', create the object
if obj_type != "nil" && obj_id != nil
# 'obj_id' must be between 1 and 254
if obj_id < 1 || obj_id > 254
print("HSP: invalid 'id': " + str(obj_id) + " for 'obj':" + obj_type)
return
end
# Step 2. Check if the p<>b<> object already exists
# `prev_obj` contains the pre-existing object, or `nil` if we create a new object
var obj_lvh = lvh_page_cur.get_obj(obj_id) # get reference of object or `nil` if new object
# Step 3. Create object instance if required
if (obj_type != nil) && (obj_id != nil) && (obj_lvh == nil)
# Step 3.a. extract the LVGL parent object to create the object in the appropriate lvgl screen
# Result in `parent_lvgl`
# extract haspmota class, prefix with `lvh_`. Ex: `btn` becomes `lvh_btn`
# extract parent
var parent_lvgl
var parent_id = int(jline.find("parentid"))
var parent_id = int(jline.find("parentid")) # id of parent object, or `nil`
var parent_obj # parent HASPmota object
var parent_lvgl # lvgl object of parent object
var parent_obj
if parent_id != nil
parent_obj = lvh_page_cur.get_obj(parent_id) # get parent object
if parent_obj != nil parent_lvgl = parent_obj._lv_obj end # parent
if parent_obj != nil
parent_lvgl = parent_obj._lv_obj
end # parent
end
if parent_lvgl == nil
parent_lvgl = lvh_page_cur.get_scr() # if not parent, use the current page screen
end
# Step 3.b. Get the HASPmota class object for the `obj` class
# check if a class with the requested name exists
# first look for a class with name `lvh_<name>` exists
var obj_class = introspect.get(self, "lvh_" + obj_type)
var lv_instance = nil # allows to pre-instanciate the object
var lv_instance # allows to pre-instanciate the object
# there is no lvh_X class, try to load the class name from the global namespace
# Step 3.c. if no native `lvh_<obj>` is found, try the class name from the global namespace
if obj_class == nil
# if not found, check if a LVGL class with name `lv_<name>` exists
var lv_cl = introspect.get(global, obj_type)
if lv_cl != nil && type(lv_cl) == 'class'
if (lv_cl != nil) && (type(lv_cl) == 'class')
lv_instance = lv_cl(parent_lvgl)
obj_class = self.lvh_obj # use the basic lvh_obj component to encapsulate
end
end
# still not found, try to load a module with the name of the class
# Step 3.d. if not found, try to load a module with the name of the class
if obj_class == nil
var lv_cl = introspect.module(obj_type)
if lv_cl != nil && type(lv_cl) == 'class'
@ -2923,18 +3066,55 @@ class HASPmota
end
end
# Step 3.e. if none found, raise an error and abort
if obj_class == nil
print("HSP: Cannot find object of type " + str(obj_type))
print(f"HSP: Cannot find object of type {obj_type}")
return
end
# instanciate the object, passing the lvgl screen as parent object
# Step 3.f. instanciate the object, passing the lvgl screen as parent object
obj_lvh = obj_class(parent_lvgl, page, jline, lv_instance, parent_obj)
# add object to page object
# Step 3.g. Add object to page object
lvh_page_cur.add_obj(obj_id, obj_lvh)
end
# Step 4. if "id" is 0, get the screen object
if obj_id == 0
if (obj_type != nil)
print(f"HSP: cannot specify 'obj':'{obj_type}' for 'id':0")
return
end
obj_lvh = self.get_page_cur_parsing().get_obj(0) # get object id '0'
end
# Step 5. apply attributes
# set attributes
# try every attribute, if not supported it is silently ignored
if (obj_lvh != nil)
for k:jline.keys()
obj_lvh.(k) = jline[k]
end
end
# Step 6. apply post-config
# finally call 'post_config()' when all attributes are set, which gives an opportunity to clean or refresh
if (obj_lvh != nil)
obj_lvh.post_config()
end
# Step 7. run any Berry code embedded
# `func_compiled` contains compiled code, that will be run once the object is complete, or `nil` if no code
# `berry_run` contains the actual source code, used only for logging
var func_compiled
var berry_run = str(jline.find("berry_run"))
if berry_run != "nil"
try
func_compiled = compile(berry_run)
except .. as e,m
print(format("HSP: unable to compile berry code \"%s\" - '%s' - %s", berry_run, e, m))
end
end
if func_compiled != nil
try
# run the compiled code once
@ -2947,26 +3127,6 @@ class HASPmota
end
end
if obj_id == nil return end # if no object id, ignore line
if obj_id == 0 && obj_type != "nil"
print("HSP: cannot specify 'obj' for 'id':0")
return
end
# if id==0, retrieve the 'scr' object of the current page
if obj_id == 0
obj_lvh = self.get_page_cur().get_obj(0) # get object id '0'
end
# set attributes
# try every attribute, if not supported it is silently ignored
for k:jline.keys()
# introspect.set(obj, k, jline[k])
obj_lvh.(k) = jline[k]
end
# finally call 'post_config()' when all attributes are set, which gives an opportunity to clean or refresh
obj_lvh.post_config()
end
end
haspmota.HASPmota = HASPmota

View File

@ -1,6 +1,6 @@
{
"name": "lvgl",
"version": "9.2.0",
"version": "9.2.2",
"keywords": "graphics, gui, embedded, tft, lvgl",
"description": "Graphics library to create embedded GUI with easy-to-use graphical elements, beautiful visual effects and low memory footprint. It offers anti-aliasing, opacity, and animations using only one frame buffer.",
"repository": {

View File

@ -1,5 +1,5 @@
name=lvgl
version=9.2.0
version=9.2.2
author=kisvegabor
maintainer=kisvegabor,embeddedt,pete-pjb
sentence=Full-featured Graphics Library for Embedded Systems

View File

@ -1,6 +1,6 @@
/**
* @file lv_conf.h
* Configuration file for v9.2.0
* Configuration file for v9.2.2
*/
/*
@ -95,6 +95,14 @@
#if LV_USE_OS == LV_OS_CUSTOM
#define LV_OS_CUSTOM_INCLUDE <stdint.h>
#endif
#if LV_USE_OS == LV_OS_FREERTOS
/*
* Unblocking an RTOS task with a direct notification is 45% faster and uses less RAM
* than unblocking a task using an intermediary object such as a binary semaphore.
* RTOS task notifications can only be used when there is only one task that can be the recipient of the event.
*/
#define LV_USE_FREERTOS_TASK_NOTIFY 1
#endif
/*========================
* RENDERING CONFIGURATION
@ -205,10 +213,16 @@
#endif
/* Use NXP's PXP on iMX RTxxx platforms. */
#define LV_USE_DRAW_PXP 0
#define LV_USE_PXP 0
#if LV_USE_DRAW_PXP
#if LV_USE_OS
#if LV_USE_PXP
/* Use PXP for drawing.*/
#define LV_USE_DRAW_PXP 1
/* Use PXP to rotate display.*/
#define LV_USE_ROTATE_PXP 0
#if LV_USE_DRAW_PXP && LV_USE_OS
/* Use additional draw thread for PXP processing.*/
#define LV_USE_PXP_DRAW_THREAD 1
#endif

View File

@ -8,7 +8,7 @@
#define LVGL_VERSION_MAJOR 9
#define LVGL_VERSION_MINOR 2
#define LVGL_VERSION_PATCH 0
#define LVGL_VERSION_PATCH 2
#define LVGL_VERSION_INFO ""
#endif /* LVGL_VERSION_H */

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