Merge branch 'development' into prerelease-12.4

This commit is contained in:
Theo Arends 2023-02-13 16:22:03 +01:00
commit dafa28f8ba
380 changed files with 63690 additions and 7479 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.4.9
- [ ] The code change is tested and works with Tasmota core ESP32 V.2.0.5
- [ ] The code change is tested and works with Tasmota core ESP32 V.2.0.6
- [ ] 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

@ -58,7 +58,7 @@ jobs:
matrix:
variant:
- tasmota
- tasmota4M
- tasmota-4M
- tasmota-minimal
- tasmota-display
- tasmota-ir
@ -200,7 +200,7 @@ jobs:
mkdir -p ./firmware/map
[ ! -f ./mv_firmware/map/* ] || mv ./mv_firmware/map/* ./firmware/map/
[ ! -f ./mv_firmware/firmware/tasmota.* ] || mv ./mv_firmware/firmware/tasmota.* ./firmware/tasmota/
[ ! -f ./mv_firmware/firmware/tasmota4M.* ] || mv ./mv_firmware/firmware/tasmota4M.* ./firmware/tasmota/
[ ! -f ./mv_firmware/firmware/tasmota-4M.* ] || mv ./mv_firmware/firmware/tasmota-4M.* ./firmware/tasmota/
[ ! -f ./mv_firmware/firmware/tasmota-sensors.* ] || mv ./mv_firmware/firmware/tasmota-sensors.* ./firmware/tasmota/
[ ! -f ./mv_firmware/firmware/tasmota-minimal.bin.gz ] || mv ./mv_firmware/firmware/tasmota-minimal.bin.gz ./firmware/tasmota/
[ ! -f ./mv_firmware/firmware/tasmota-lite.* ] || mv ./mv_firmware/firmware/tasmota-lite.* ./firmware/tasmota/

View File

@ -57,7 +57,7 @@ jobs:
matrix:
variant:
- tasmota
- tasmota4M
- tasmota-4M
- tasmota-minimal
- tasmota-display
- tasmota-ir
@ -208,7 +208,7 @@ jobs:
mkdir -p ./release-firmware/map
[ ! -f ./mv_firmware/map/* ] || mv ./mv_firmware/map/* ./release-firmware/map/
[ ! -f ./mv_firmware/firmware/tasmota.* ] || mv ./mv_firmware/firmware/tasmota.* ./release-firmware/tasmota/
[ ! -f ./mv_firmware/firmware/tasmota4M.* ] || mv ./mv_firmware/firmware/tasmota4M.* ./release-firmware/tasmota/
[ ! -f ./mv_firmware/firmware/tasmota-4M.* ] || mv ./mv_firmware/firmware/tasmota-4M.* ./release-firmware/tasmota/
[ ! -f ./mv_firmware/firmware/tasmota-sensors.* ] || mv ./mv_firmware/firmware/tasmota-sensors.* ./release-firmware/tasmota/
[ ! -f ./mv_firmware/firmware/tasmota-minimal.bin.gz ] || mv ./mv_firmware/firmware/tasmota-minimal.bin.gz ./release-firmware/tasmota/
[ ! -f ./mv_firmware/firmware/tasmota-lite.* ] || mv ./mv_firmware/firmware/tasmota-lite.* ./release-firmware/tasmota/

View File

@ -81,7 +81,7 @@ jobs:
matrix:
variant:
- tasmota
- tasmota4M
- tasmota-4M
- tasmota-display
- tasmota-ir
- tasmota-knx
@ -113,10 +113,6 @@ jobs:
- tasmota32s3-safeboot
- tasmota32s3cdc-safeboot
steps:
- name: Sleep a while, try to start MacOS / Windows CI env first
uses: jakejarvis/wait-action@master
with:
time: '1m'
- uses: actions/checkout@v3
- name: Set up Python
uses: actions/setup-python@v4

1
.gitignore vendored
View File

@ -34,5 +34,6 @@ lib/libesp32/berry/berry
.vscode/.browse.c_cpp.db*
.vscode/c_cpp_properties.json
.vscode/launch.json
.vscode/settings.json
*.bak
*.code-workspace

75
.vscode/settings.json vendored Normal file
View File

@ -0,0 +1,75 @@
{
"platformio-ide.toolbar": [
{
"text": "$(home)",
"tooltip": "PlatformIO: Home",
"commands": [
{
"id": "platformio-ide.runPIOCoreCommand",
"args": "pio home"
}
]
},
{
"text": "$(trash)",
"tooltip": "PlatformIO: Clean All",
"commands": [
{
"id": "workbench.action.tasks.runTask",
"args": "PlatformIO: Clean All"
}
]
},
{
"text": "$(check)",
"tooltip": "PlatformIO: Build",
"commands": [
{
"id": "workbench.action.tasks.runTask",
"args": "PlatformIO: Build"
}
]
},
{
"text": "$(zap)",
"tooltip": "PlatformIO: Build and Upload",
"commands": [
{
"id": "workbench.action.tasks.runTask",
"args": "PlatformIO: Upload"
}
]
},
{
"text": "$(flame)",
"tooltip": "PlatformIO: Build, Erase and Upload",
"commands": [
{
"id": "platformio-ide.runPIOCoreCommand",
"args": "pio run -t erase_upload"
}
]
},
{
"text": "$(device-desktop)",
"tooltip": "PlatformIO: Serial Monitor",
"commands": [
{
"id": "workbench.action.tasks.runTask",
"args": "PlatformIO: Monitor"
}
]
},
{
"text": "$(refresh)",
"tooltip": "PlatformIO: Rebuild IntelliSense Index",
"commands": [
{
"id": "workbench.action.tasks.runTask",
"args": "PlatformIO: Rebuild IntelliSense Index"
}
]
}
]
}

5
API.md
View File

@ -1,4 +1,7 @@
<img src="/tools/logo/TASMOTA_FullLogo_Vector.svg" alt="Logo" align="right" height="76"/>
<picture>
<source media="(prefers-color-scheme: dark)" srcset="./tools/logo/TASMOTA_FullLogo_Vector_White.svg">
<img alt="Logo" src="./tools/logo/TASMOTA_FullLogo_Vector.svg" align="right" height="76">
</picture>
# Basic API information

View File

@ -89,7 +89,8 @@ Note: `minimal` variant is not listed as it shouldn't be used outside of the [up
| USE_BL09XX | - | x / x | x | x | - | - |
| USE_TELEINFO | - | - / - | - | - | - | - |
| USE_IEM3000 | - | - / - | - | - | - | - |
| USE_WE517 | - | - / - | - | - | - | - |
| USE_WE517 | - | - / x | - | - | - | - |
| USE_MODBUS_ENERGY | - | - / x | - | - | - | - |
| | | | | | | |
| USE_ADC_VCC | x | - / - | - | - | x | - |
| USE_COUNTER | - | x / x | x | x | - | x |
@ -117,10 +118,12 @@ Note: `minimal` variant is not listed as it shouldn't be used outside of the [up
| USE_MGS | - | - / x | - | x | - | - |
| USE_SGP30 | - | - / x | - | x | - | - |
| USE_SGP40 | - | - / x | - | x | - | - |
| USE_SEN5X | - | - / x | - | x | - | - |
| USE_SI1145 | - | - / - | - | - | - | - |
| USE_LM75AD | - | - / x | - | x | - | - |
| USE_APDS9960 | - | - / - | - | - | - | - |
| USE_MCP230xx | - | - / - | - | - | - | - |
| USE_PCA9632 | - | - / - | - | - | - | - |
| USE_PCA9685 | - | - / - | - | - | - | - |
| USE_MPR121 | - | - / - | - | - | - | - |
| USE_CCS811 | - | - / - | - | x | - | - |
@ -246,6 +249,7 @@ Note: `minimal` variant is not listed as it shouldn't be used outside of the [up
| USE_DISPLAY_ST7789 | - | - / - | - | - | - | x |
| USE_DISPLAY_TM1637 | - | - / - | - | - | - | x |
| USE_DISPLAY_TM1621_SONOFF | - | - / x | - | - | - | - |
| USE_DISPLAY_TM1650 | - | - / - | - | - | - | - |
| | | | | | | |
| USE_FT5206 | - | - / - | - | - | - | - |
| USE_FTC532 | - | - / - | - | - | - | - |

View File

@ -3,6 +3,102 @@ All notable changes to this project will be documented in this file.
## [Released]
## [12.4.0] 20230215
- Release Peter
## [12.3.1.6] 20230215
### Added
- ESP32 preliminary support for Matter protocol, milestone 1 (commissioning) by Stephan Hadinger
- Basic support for Shelly Pro 4PM
### Breaking Changed
- TM1638 button and led support are handled as virtual switches and relays (#11031)
### Fixed
- ESP8266 Fix TLS SNI which would prevent AWS IoT connection (#17936)
## [12.3.1.5] 20230208
### Added
- ESP32 support for eigth energy phases/channels
- ESP32 command ``EnergyCols 1..8`` to change number of GUI columns
- ESP32 command ``EnergyDisplay 1..3`` to change GUI column presentation
- Support for SEN5X gas and air quality sensor by Tyeth Gundry (#17736)
- Berry add ``mdns`` advanced features and query
- ESP32 support for Biomine BioPDU 625x12 (#17857)
### Breaking Changed
- Berry energy_ctypes changed with new energy driver
- Berry energy_ctypes fixed accordingly
### Changed
- Energy refactoring preparing for ESP32 phase/channel extension
### Fixed
- ADE7953 when calibration data for second channel is used regression from v12.2.0.2
- Shelly Pro 1/2 relay click at restart regression from v12.3.1.4
- Zigbee extend plug-in modifiers to 16 bits
- Broken I2C priority regression from v12.3.1.3 (#17810)
- Energy usage and return migrated too small (/10000) regression from v12.3.1.3
## [12.3.1.4] 20230127
### Added
- Berry ``crypto.EC_P256`` ECDSA signature (required by Matter protocol)
- Berry add up flag to ``tasmota.wifi()`` and ``tasmota.eth()``, always return MAC
## [12.3.1.3] 20230115
### Added
- Support for PCA9632 4-channel 8-bit PWM driver as light driver by Pascal Heinrich (#17557)
- Berry `bytes()` now evaluates to `false` if empty
- Berry ``crypto.AES_CCM`` (required by Matter protocol)
- ESP32 support for BMPxxx sensors on two I2C busses (#17643)
- Berry add implicit ``_class`` parameter to static methods
### Changed
- Energy totals max supported value from +/-21474.83647 to +/-2147483.647 kWh
- Removed delays in TasmotaSerial and TasmotaModbus Tx enable switching
- Increase rule event buffer from 100 to 256 characters (#16943)
- All calls to atof() into CharToFloat() reducing code size by 8k
- Keep webserver enabled on command ``upload``
### Fixed
- Energy dummy switched voltage and power regression from v12.2.0.2
- Orno WE517 modbus serial config 8E1 setting (#17545)
- No IP address shown when in AP mode regression from v12.3.1.1 (#17599)
- Rename ``tasmota4M.bin`` to ``tasmota-4M.bin`` to solve use of ``tasmota-minimal.bin`` (#17674)
- DNS lookup for ``upload`` from ota server using http regression from v12.3.1.1
## [12.3.1.2] 20221231
### Added
- Berry crypto add ``EC_P256`` and ``PBKDF2_HMAC_SHA256`` algorithms required by Matter protocol
- Berry crypto add ``random`` to generate series of random bytes
- Berry crypto add ``HKDF_HMAC_SHA256``
- Support for up to 3 single phase modbus energy monitoring device using generic Energy Modbus driver
- Berry crypto add ``SPAKE2P_Matter`` for Matter support
- Support for IPv6 only networks on Ethernet (not yet Wifi)
- Support for TM1650 display as used in some clocks by Stefan Oskamp (#17594)
### Changed
- ESP32 Framework (Core) from v2.0.5.4 to v2.0.6 (IPv6 support)
- Tasmota OTA scripts now support both unzipped and gzipped file uploads (#17378)
- NTP default servers to dual-stack (IPv4/IPv6)
- Revert TuyaMcu rewrite by btsimonh as lack of support
### Fixed
- Shutter default motorstop set to 0 (#17403)
- Shutter default tilt configuration (#17484)
- Modbus transmit enable GPIO enabled once during write buffer
- ESP8266 set GPIO's to input on power on fixing relay spikes (#17531)
## [12.3.1.1] 20221221
### Added
- Support for IPv6 DNS records (AAAA) and IPv6 ``Ping`` for ESP32 and ESP8266 (#17417)
- Berry support for ``crypto.SHA256`` (#17430)
- Support for RGB displays (#17414)
- Berry add crypto AES_CTR, HDMAC_SHA256, MD5
### Changed
- ESP32 Framework (Core) from v2.0.5.3 to v2.0.5.4 (IPv6 support)
## [12.3.1] 20221216
- Release Percy

View File

@ -1,4 +1,7 @@
<img src="/tools/logo/TASMOTA_FullLogo_Vector.svg" alt="Logo" align="right" height="76"/>
<picture>
<source media="(prefers-color-scheme: dark)" srcset="./tools/logo/TASMOTA_FullLogo_Vector_White.svg">
<img alt="Logo" src="./tools/logo/TASMOTA_FullLogo_Vector.svg" align="right" height="76">
</picture>
# Code Owners
@ -72,6 +75,7 @@ In addition to @arendst the following code is mainly owned by:
| xdrv_61_ds3502 | f-reiling
| xdrv_62_improv | @arendst
| xdrv_63_modbus_bridge | @jeroenst
| xdrv_64_pca9632 | Pascal Heinrich
| |
| xdrv_79_esp32_ble | @staars, @btsimonh
| xdrv_81_esp32_webcam | @gemu, @philrich
@ -194,6 +198,7 @@ In addition to @arendst the following code is mainly owned by:
| xsns_100_ina3221 | @barbudor
| xsns_101_hmc5883l | Andreas Achtzehn
| xsns_102_ld2410 | @arendst
| xsns_103_sen5x | @tyeth
| |
| Libraries |
| |

View File

@ -1,4 +1,7 @@
<img src="/tools/logo/TASMOTA_FullLogo_Vector.svg" alt="Logo" align="right" height="76"/>
<picture>
<source media="(prefers-color-scheme: dark)" srcset="./tools/logo/TASMOTA_FullLogo_Vector_White.svg">
<img alt="Logo" src="./tools/logo/TASMOTA_FullLogo_Vector.svg" align="right" height="76">
</picture>
# Contributing

View File

@ -1,4 +1,4 @@
![Tasmota logo](https://github.com/arendst/Tasmota/blob/development/tools/logo/TASMOTA_FullLogo_Vector.svg)
![Tasmota logo](/tools/logo/TASMOTA_FullLogo_Vector.svg#gh-light-mode-only)![Tasmota logo](/tools/logo/TASMOTA_FullLogo_Vector_White.svg#gh-dark-mode-only)
Alternative firmware for [ESP8266](https://en.wikipedia.org/wiki/ESP8266) based devices with **easy configuration using webUI, OTA updates, automation using timers or rules, expandability and entirely local control over MQTT, HTTP, Serial or KNX**.
_Written for PlatformIO with limited support for Arduino IDE._
@ -13,6 +13,7 @@ If you like **Tasmota**, give it a star, or fork it and contribute!
[![GitHub stars](https://img.shields.io/github/stars/arendst/Tasmota.svg?style=social&label=Star)](https://github.com/arendst/Tasmota/stargazers)
[![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)
[![donate](https://img.shields.io/badge/donate-buy%20me%20a%20coffee-yellow.svg)](https://www.buymeacoffee.com/tasmota)
See [CHANGELOG.md](https://github.com/arendst/Tasmota/blob/development/tasmota/CHANGELOG.md) for changes since last release.

View File

@ -9,8 +9,8 @@ The following table lists the supported I2C devices
Index | Define | Driver | Device | Address(es) | Description
------|---------------------|----------|----------|-------------|-----------------------------------------------
1 | USE_PCA9685 | xdrv_15 | PCA9685 | 0x40 - 0x47 | 16-channel 12-bit pwm driver
2 | USE_PCF8574 | xdrv_28 | PCF8574 | 0x20 - 0x26 | 8-bit I/O expander
2 | USE_PCF8574 | xdrv_28 | PCF8574A | 0x39 - 0x3F | 8-bit I/O expander
2 | USE_PCF8574 | xdrv_28 | PCF8574 | 0x20 - 0x26 | 8-bit I/O expander (address range overridable)
2 | USE_PCF8574 | xdrv_28 | PCF8574A | 0x39 - 0x3F | 8-bit I/O expander (address range overridable)
3 | USE_DISPLAY_LCD | xdsp_01 | | 0x27, 0x3F | LCD display
4 | USE_DISPLAY_SSD1306 | xdsp_02 | SSD1306 | 0x3C - 0x3D | Oled display
5 | USE_DISPLAY_MATRIX | xdsp_03 | HT16K33 | 0x70 - 0x77 | 8x8 led matrix
@ -45,6 +45,7 @@ Index | Define | Driver | Device | Address(es) | Description
22 | USE_MCP230xx | xsns_29 | MCP23017 | 0x20 - 0x26 | 16-bit I/O expander
23 | USE_MPR121 | xsns_30 | MPR121 | 0x5A - 0x5D | Proximity capacitive touch sensor
24 | USE_CCS811 | xsns_31 | CCS811 | 0x5A | Gas (TVOC) and air quality sensor
24' | USE_CCS811_V2 | xsns_31 | CCS811 | 0x5A - 0x5B | Gas (TVOC) and air quality sensor
25 | USE_MPU6050 | xsns_32 | MPU6050 | 0x68 - 0x69 | 3-axis gyroscope and temperature sensor
26 | USE_DS3231 | xsns_33 | DS3231 | 0x68 | Real time clock
27 | USE_MGC3130 | xsns_36 | MGC3130 | 0x42 | Electric field sensor
@ -60,7 +61,7 @@ Index | Define | Driver | Device | Address(es) | Description
37 | USE_24C256 | xdrv_10 | 24C256 | 0x50 | Scripter EEPROM storage
38 | USE_DISPLAY_ILI9488 | xdsp_08 | FT6236 | 0x38 | Touch panel controller
39 | USE_DISPLAY_RA8876 | xdsp_10 | FT5316 | 0x38 | Touch panel controller
40 | USE_TSL2591 | xsns_57 | TLS2591 | 0x29 | Light intensity sensor
40 | USE_TSL2591 | xsns_57 | TSL2591 | 0x29 | Light intensity sensor
41 | USE_DHT12 | xsns_58 | DHT12 | 0x5C | Temperature and humidity sensor
42 | USE_DS1624 | xsns_59 | DS1621 | 0x48 - 0x4F | Temperature sensor
42 | USE_DS1624 | xsns_59 | DS1624 | 0x48 - 0x4F | Temperature sensor
@ -108,3 +109,6 @@ Index | Define | Driver | Device | Address(es) | Description
71 | USE_QMC5883L | xsns_33 | QMC5883L | 0x0D | Magnetic Field Sensor
72 | USE_INA3221 | xsns_100 | INA3221 | 0x40-0x43 | 3-channels Voltage and Current sensor
73 | USE_HMC5883L | xsns_101 | HMC5883L | 0x1E | 3-channels Magnetic Field Sensor
74 | USE_DISPLAY_TM1650 | xdsp_20 | TM1650 | 0x24 - 0x27, 0x34 - 0x37 | Four-digit seven-segment LED controller
75 | USE_PCA9632 | xdrv_64 | PCA9632 | 0x60 | 4-channel 4-bit pwm driver
76 | USE_SEN5X | xsns_103 | SEN5X | 0x69 | Gas (VOC/NOx index) and air quality (PPM <1,<2.5,<4,<10)

View File

@ -1,4 +1,4 @@
![Tasmota logo](/tools/logo/TASMOTA_FullLogo_Vector.svg)
![Tasmota logo](/tools/logo/TASMOTA_FullLogo_Vector.svg#gh-light-mode-only)![Tasmota logo](/tools/logo/TASMOTA_FullLogo_Vector_White.svg#gh-dark-mode-only)
Alternative firmware for [ESP8266](https://en.wikipedia.org/wiki/ESP8266) and [ESP32](https://en.wikipedia.org/wiki/ESP32) based devices with **easy configuration using webUI, OTA updates, automation using timers or rules, expandability and entirely local control over MQTT, HTTP, Serial or KNX**.
_Written for PlatformIO._
@ -24,6 +24,7 @@ If you like **Tasmota**, give it a star, or fork it and contribute!
[![GitHub stars](https://img.shields.io/github/stars/arendst/Tasmota.svg?style=social&label=Star)](https://github.com/arendst/Tasmota/stargazers)
[![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)
[![donate](https://img.shields.io/badge/donate-buy%20me%20a%20coffee-yellow.svg)](https://www.buymeacoffee.com/tasmota)
See [RELEASENOTES.md](https://github.com/arendst/Tasmota/blob/master/RELEASENOTES.md) for release information.
@ -46,7 +47,7 @@ Note that there is a chance, as with any upgrade, that the device may not functi
## Disclaimer
st:warning: **DANGER OF ELECTROCUTION** :warning:
:warning: **DANGER OF ELECTROCUTION** :warning:
If your device connects to mains electricity (AC power) there is danger of electrocution if not installed properly. If you don't know how to install it, please call an electrician (***Beware:*** certain countries prohibit installation without a licensed electrician present). Remember: _**SAFETY FIRST**_. It is not worth the risk to yourself, your family and your home if you don't know exactly what you are doing. Never tinker or try to flash a device using the serial programming interface while it is connected to MAINS ELECTRICITY (AC power).
@ -125,6 +126,7 @@ You can contribute to Tasmota by
- Contributing missing [documentation](https://tasmota.github.io/docs) for features and devices
[![donate](https://img.shields.io/badge/donate-PayPal-blue.svg)](https://paypal.me/tasmota)
[![donate](https://img.shields.io/badge/donate-buy%20me%20a%20coffee-yellow.svg)](https://www.buymeacoffee.com/tasmota)
## Credits
@ -170,4 +172,4 @@ People helping to keep the show on the road:
## License
This program is licensed under GPL-3.0
This program is licensed under GPL-3.0

View File

@ -1,4 +1,7 @@
<img src="https://github.com/arendst/Tasmota/blob/master/tools/logo/TASMOTA_FullLogo_Vector.svg" alt="Logo" align="right" height="76"/>
<picture>
<source media="(prefers-color-scheme: dark)" srcset="./tools/logo/TASMOTA_FullLogo_Vector_White.svg">
<img alt="Logo" src="./tools/logo/TASMOTA_FullLogo_Vector.svg" align="right" height="76">
</picture>
# RELEASE NOTES
@ -33,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.4.9** 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 **2.0.5.3**.
This release will be supported from ESP32/Arduino library Core version **2.0.6**.
Support of ESP8266 Core versions before 2.7.4.9 and ESP32 Core versions before 2.0.5.3 have been removed.
Support of ESP8266 Core versions before 2.7.4.9 and ESP32 Core versions before 2.0.6 have been removed.
## Support of TLS
@ -55,7 +58,7 @@ Easy initial installation of Tasmota can be performed using the [Tasmota WebInst
The following binary downloads have been compiled with ESP8266/Arduino library core version **2.7.4.9**.
- **tasmota.bin** = The Tasmota version with most drivers for 1M+ flash. **RECOMMENDED RELEASE BINARY**
- **tasmota4M.bin** = The Tasmota version with most drivers and filesystem for 4M+ flash.
- **tasmota-4M.bin** = The Tasmota version with most drivers and filesystem for 4M+ flash.
- **tasmota-AD.bin** to **tasmota-VN.bin** = The Tasmota version in different languages for 1M+ flash.
- **tasmota-lite.bin** = The Lite version without most drivers and sensors for 1M+ flash.
- **tasmota-knx.bin** = The Knx version without some features but adds KNX support for 1M+ flash.
@ -72,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-12.3.1
- http://ota.tasmota.com/tasmota/release-12.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-C3, ESP32-S2 and ESP32-S3 based
The following binary downloads have been compiled with ESP32/Arduino library core version **2.0.5.3**.
The following binary downloads have been compiled with ESP32/Arduino library core version **2.0.6**.
- **tasmota32.bin** = The Tasmota version with most drivers including additional sensors and KNX for 4M+ flash. **RECOMMENDED RELEASE BINARY**
- **tasmota32xy.bin** = The Tasmota version with most drivers including additional sensors and KNX for ESP32-C3/S2/S3 and 4M+ flash.
@ -97,7 +100,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-12.3.1
- https://ota.tasmota.com/tasmota32/release-12.4.0
The latter links can be used for OTA upgrades too like ``OtaUrl https://ota.tasmota.com/tasmota32/release/tasmota32.bin``
@ -107,71 +110,47 @@ 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 v12.3.1 Percy
## Changelog v12.4.0 Peter
### Added
- Command ``SetOption35 0..255`` to skip number of received messages in Serial Bridge (default 0) [#17140](https://github.com/arendst/Tasmota/issues/17140)
- Command ``SetOption47 1..255`` to delay power on relay state in seconds reducing power surge. ``SO47 1`` delays until network connected. ``SO47 2`` delays until mqtt connected
- Command ``RgxClients`` for range extender clients list [#17048](https://github.com/arendst/Tasmota/issues/17048)
- Command ``RgxPort [tcp|udp], gateway_port, client_mac, client_port`` for range extender port forwardings [#17092](https://github.com/arendst/Tasmota/issues/17092)
- Command ``SSerialBuffer 256..SERIAL_BRIDGE_BUFFER_SIZE`` to change serial bridge rx buffer size [#17120](https://github.com/arendst/Tasmota/issues/17120)
- Command ``SwitchMode 16`` sending only MQTT message on inverted switch change [#17028](https://github.com/arendst/Tasmota/issues/17028)
- Command NeoPool ``NPFiltration 2`` toggle [#16859](https://github.com/arendst/Tasmota/issues/16859)
- Optional define ``SERIAL_BRIDGE_BUFFER_SIZE`` to set Serial Bridge internal buffer size (Default ESP8266 = 256, ESP32 = 800)
- Support for two phase power calibration using commands ``PowerSet2``, ``VoltageSet2`` and ``CurrentSet2``
- Support for HLK-LD2410 24GHz smart wave motion sensor
- Support for Shelly Pro 1/1PM and 2/2PM [#16773](https://github.com/arendst/Tasmota/issues/16773)
- Support for up to four DS18x20 GPIOs by md5sum-as [#16833](https://github.com/arendst/Tasmota/issues/16833)
- Support for Digital Addressable Lighting Interface (DALI) by Andrei Kazmirtsuk [#16938](https://github.com/arendst/Tasmota/issues/16938)
- Support for NTAG2xx tags read and write on PN532 NFC reader [#16939](https://github.com/arendst/Tasmota/issues/16939)
- Support for Plantower PMSx003T AQI models with temperature and humidity [#16971](https://github.com/arendst/Tasmota/issues/16971)
- Support for BP1658CJ RGBCW led bulbs like Orein OS0100411267 by Cossid [#17011](https://github.com/arendst/Tasmota/issues/17011)
- Support for Dingtian x595 shift register based relay boards by Barbudor [#17032](https://github.com/arendst/Tasmota/issues/17032)
- Support for HMC5883L 3-Axis Digital Compass sensor by Andreas Achtzehn [#17069](https://github.com/arendst/Tasmota/issues/17069)
- Support for ME007-ULS narrow FoV ultrasonic distance sensor by Mathias Buder [#17376](https://github.com/arendst/Tasmota/issues/17376)
- WS2812 and Light ArtNet DMX control over UDP port 6454 [#17059](https://github.com/arendst/Tasmota/issues/17059)
- Teleinfo TEMPO (BBR) contract [#17160](https://github.com/arendst/Tasmota/issues/17160)
- Serial Modbus transmit enable GPIOs to all modbus energy drivers and modbus bridge [#17247](https://github.com/arendst/Tasmota/issues/17247)
- Berry ``bytes().setbytes()`` method [#16892](https://github.com/arendst/Tasmota/issues/16892)
- Berry ``bytes().reverse()`` method [#16977](https://github.com/arendst/Tasmota/issues/16977)
- Berry ``mdns`` module [#17202](https://github.com/arendst/Tasmota/issues/17202)
- Zigbee router firmware for Sonoff ZBBridgePro [#16900](https://github.com/arendst/Tasmota/issues/16900)
- ESP32 Support for DMX ArtNet Led matrix animations [#16984](https://github.com/arendst/Tasmota/issues/16984)
- ESP32 DS18x20 parasitic power usage when defining W1_PARASITE_POWER [#17112](https://github.com/arendst/Tasmota/issues/17112)
- Support for up to 3 (ESP8266) or 8 (ESP32) phase modbus energy monitoring device using generic Energy Modbus driver
- Support for RGB displays [#17414](https://github.com/arendst/Tasmota/issues/17414)
- Support for IPv6 DNS records (AAAA) and IPv6 ``Ping`` for ESP32 and ESP8266 [#17417](https://github.com/arendst/Tasmota/issues/17417)
- Support for IPv6 only networks on Ethernet (not yet Wifi)
- Support for TM1650 display as used in some clocks by Stefan Oskamp [#17594](https://github.com/arendst/Tasmota/issues/17594)
- Support for PCA9632 4-channel 8-bit PWM driver as light driver by Pascal Heinrich [#17557](https://github.com/arendst/Tasmota/issues/17557)
- support for SEN5X gas and air quality sensor by Tyeth Gundry [#17736](https://github.com/arendst/Tasmota/issues/17736)
- Basic support for Shelly Pro 4PM
- Berry support for ``crypto.SHA256`` [#17430](https://github.com/arendst/Tasmota/issues/17430)
- Berry crypto add ``EC_P256`` and ``PBKDF2_HMAC_SHA256`` algorithms required by Matter protocol [#17473](https://github.com/arendst/Tasmota/issues/17473)
- Berry crypto add ``random`` to generate series of random bytes
- Berry crypto add ``HKDF_HMAC_SHA256``
- Berry crypto add ``SPAKE2P_Matter`` for Matter support
- Berry add ``mdns`` advanced features and query
- ESP32 command ``EnergyCols 1..8`` to change number of GUI columns
- ESP32 command ``EnergyDisplay 1..3`` to change GUI column presentation
- ESP32 support for eigth energy phases/channels
- ESP32 support for BMPxxx sensors on two I2C busses [#17643](https://github.com/arendst/Tasmota/issues/17643)
- ESP32 support for Biomine BioPDU 625x12 [#17857](https://github.com/arendst/Tasmota/issues/17857)
- ESP32 preliminary support for Matter protocol, milestone 1 (commissioning) by Stephan Hadinger
### Breaking Changed
- Redesign distance sensors VL53LXX, TOF10120, HRXL and DYP to use cm instead of mm [#17021](https://github.com/arendst/Tasmota/issues/17021)
- TM1638 button and led support are handled as virtual switches and relays [#11031](https://github.com/arendst/Tasmota/issues/11031)
### Changed
- TasmotaSerial library from v3.5.0 to v3.6.0
- ESP32 Framework (Core) from v2.0.5 to v2.0.5.3
- ESP32 LVGL library from v8.3.2 to v8.3.3 (no functional change)
- ESP32 NimBLE library from v1.4.0 to v1.4.1 [#16775](https://github.com/arendst/Tasmota/issues/16775)
- Serial Bridge default internal serial rx buffer size from 64 to 256 [#17120](https://github.com/arendst/Tasmota/issues/17120)
- DS18x20 ``DS18Alias`` to ``DS18Sens`` [#16833](https://github.com/arendst/Tasmota/issues/16833)
- Compiling with reduced boards manifests in favour of Autoconfig [#16848](https://github.com/arendst/Tasmota/issues/16848)
- ADE7953 monitoring from instant power to accumulated energy [#16941](https://github.com/arendst/Tasmota/issues/16941)
- WS2812 sends signal to only ``Pixels`` leds instead of sending to 512 leds [#17055](https://github.com/arendst/Tasmota/issues/17055)
- AC PWM dimmer lineair power distribution [#17177](https://github.com/arendst/Tasmota/issues/17177)
- Zigbee improved Aqara plug support and completed cluster 0x0702 [#17073](https://github.com/arendst/Tasmota/issues/17073)
- Removed leading spaces on commands ``(S)SerialSend1 to 6`` but keep on duplicate commands ``(S)SerialSend11 to 16`` [#16723](https://github.com/arendst/Tasmota/issues/16723
- Shutter bug fixes and functionality upgrade [#17380](https://github.com/arendst/Tasmota/issues/17380
- MQTT now uses Tasmota's DNS resolver instead of LWIP [#17387](https://github.com/arendst/Tasmota/issues/17387
- ESP32 initial otaurl from http to https
- ESP32 Framework (Core) from v2.0.5.3 to v2.0.6 (IPv6 support)
- Energy totals max supported value from +/-21474.83647 to +/-2147483.647 kWh
- Removed delays in TasmotaSerial and TasmotaModbus Tx enable switching
- Keep webserver enabled on command ``upload``
- Better support for virtual buttons and switches up to a total of 28
- Increase rule event buffer from 100 to 256 characters [#16943](https://github.com/arendst/Tasmota/issues/16943)
- Tasmota OTA scripts now support both unzipped and gzipped file uploads [#17378](https://github.com/arendst/Tasmota/issues/17378)
### Fixed
- TasmotaSerial ``read(buffer, size)`` regression from v9.3.0
- Serial bridge default serial configuration from 5N1 to 8N1 regression from v10.1.0.3
- BP5758D red channel corruption regression from v12.1.1.6 [#16850](https://github.com/arendst/Tasmota/issues/16850)
- Deduplicate code and fix %timer n% rule regression from v12.2.0 [#16914](https://github.com/arendst/Tasmota/issues/16914)
- Serial initialization for baudrate and config [#16970](https://github.com/arendst/Tasmota/issues/16970)
- ModbusBridge buffer overflow [#16979](https://github.com/arendst/Tasmota/issues/16979)
- ModbusBridge baudrates over 76500 baud [#17106](https://github.com/arendst/Tasmota/issues/17106)
- SenseAir S8 module detection [#17033](https://github.com/arendst/Tasmota/issues/17033)
- Analog MQ exception 28 on restart [#17271](https://github.com/arendst/Tasmota/issues/17271)
- RCSwitch exception 0/6 on some protocols [#17285](https://github.com/arendst/Tasmota/issues/17285)
- ESP32 exception 28 when RtcNtpServer is enabled on restart [#17338](https://github.com/arendst/Tasmota/issues/17338)
- ESP8266 zigbee exception 3 regression from v12.3.0 [#17397](https://github.com/arendst/Tasmota/issues/17397)
### Removed
- Define ``USE_PN532_DATA_RAW`` from NFC reader [#16939](https://github.com/arendst/Tasmota/issues/16939)
- Modbus transmit enable GPIO enabled once during write buffer
- Energy dummy switched voltage and power regression from v12.2.0.2
- Shutter default motorstop set to 0 [#17403](https://github.com/arendst/Tasmota/issues/17403)
- Shutter default tilt configuration [#17484](https://github.com/arendst/Tasmota/issues/17484)
- Orno WE517 modbus serial config 8E1 setting [#17545](https://github.com/arendst/Tasmota/issues/17545)
- Rename ``tasmota4M.bin`` to ``tasmota-4M.bin`` to solve use of ``tasmota-minimal.bin`` [#17674](https://github.com/arendst/Tasmota/issues/17674)
- ESP8266 set GPIO's to input on power on fixing relay spikes [#17531](https://github.com/arendst/Tasmota/issues/17531)
- ESP8266 TLS SNI which would prevent AWS IoT connection [#17936](https://github.com/arendst/Tasmota/issues/17936)

View File

@ -1,4 +1,7 @@
<img src="/tools/logo/TASMOTA_FullLogo_Vector.svg" alt="Logo" align="right" height="76"/>
<picture>
<source media="(prefers-color-scheme: dark)" srcset="./tools/logo/TASMOTA_FullLogo_Vector_White.svg">
<img alt="Logo" src="./tools/logo/TASMOTA_FullLogo_Vector.svg" align="right" height="76">
</picture>
# Templates

View File

@ -1,8 +1,11 @@
<img src="/tools/logo/TASMOTA_FullLogo_Vector.svg" alt="Logo" align="right" height="76"/>
<picture>
<source media="(prefers-color-scheme: dark)" srcset="./tools/logo/TASMOTA_FullLogo_Vector_White.svg">
<img alt="Logo" src="./tools/logo/TASMOTA_FullLogo_Vector.svg" align="right" height="76">
</picture>
# Templates
Find below the available templates as of December 2022. More template information can be found in the [Tasmota Device Templates Repository](http://blakadder.github.io/templates)
Find below the available templates as of February 2023. More template information can be found in the [Tasmota Device Templates Repository](http://blakadder.github.io/templates)
## Adapter Board
```
@ -21,6 +24,7 @@ Kogan 5-Stage 3S {"NAME":"Kogan Air Purifier 3S","GPIO":[0,2272,0,23
## Air Quality Sensor
```
AirGradient Pro Version DIY {"NAME":"AirGradient Pro","GPIO":[1600,1,1632,0,640,608,1,1,1664,1,1696,1,1,1],"FLAG":0,"BASE":18}
Tuya PM Box {"NAME":"PM Box","GPIO":[1,1,1,1,1,1,0,0,1,1,1,1,1,0],"FLAG":0,"BASE":54,"CMND":"TuyaMCU 99,91 | TuyaMCU 71,2 | TuyaMCU 73,3 | HumRes 1 | TempRes 1 "}
```
## Aroma Diffuser
@ -46,6 +50,7 @@ GL.iNet POE Ethernet {"NAME":"GL-S10 v1.0","GPIO":[32,0,0,0,0,0,0,0,321,
## CCT
```
3W KLG 320lm {"NAME":"3W_KLG","GPIO":[0,0,0,0,416,419,0,0,417,0,418,0,0,0],"FLAG":0,"BASE":18}
AICase 800lm {"NAME":"AICase Smart L","GPIO":[0,0,0,0,0,416,0,0,0,417,0,0,0,0],"FLAG":0,"BASE":18}
AiYaTo 12W {"NAME":"AiYaTo-CW","GPIO":[0,0,0,0,416,0,0,0,0,417,0,0,0,0],"FLAG":0,"BASE":18}
Ajax Online 380lm {"NAME":"AjaxOnline","GPIO":[32,0,0,0,0,416,0,0,417,0,0,0,0,0],"FLAG":0,"BASE":38}
@ -297,7 +302,7 @@ Silicognition wESP32 {"NAME":"wESP32","GPIO":[0,0,1,0,1,1,0,0,1,1,1,1,55
TZT ESP8266 Weather Station Kit {"NAME":"TZT Weather Station","GPIO":[32,0,640,0,1,1184,0,0,1,1,608,1,1,1],"FLAG":0,"BASE":18}
Wemos D1 Mini ESP32 {"NAME":"Wemos D1 Mini ESP32","GPIO":[0,0,1,0,1,1,0,0,1,1,1,1,1,1,1,1,0,1,1,1,0,1,1,1,0,0,0,0,1,1,1,1,0,0,0,0],"FLAG":0,"BASE":1}
Wemos LOLIN32 Lite V1.0.0 (ESP32) {"NAME":"Wemos LOLIN32 Lite V1.0.0","GPIO":[1,0,1,0,1,1,0,0,1,1,1,1,1,1,1,1,0,0,0,1,0,1,1,1,0,0,0,0,1,1,1,1,0,0,0,0],"FLAG":0,"BASE":1}
Wireless Tag ESP32 Ethernet {"NAME":"WT32-ETH01","GPIO":[1,1,1,1,1,1,0,0,1,0,1,1,3840,576,5600,0,0,0,0,5568,0,0,0,0,0,0,0,0,1,1,0,1,1,0,0,1],"FLAG":0,"BASE":1}
Wireless Tag ESP32 Ethernet {"NAME":"WT32_ETH01","GPIO":[1,1,1,1,1,1,0,0,1,0,1,1,3840,576,5600,0,0,0,0,5568,0,0,0,0,0,0,0,0,1,1,0,1,1,0,0,1],"FLAG":0,"BASE":1}
Witty Cloud {"NAME":"Witty Cloud","GPIO":[1,1,320,1,32,1,0,0,417,418,1,416,1,4704],"FLAG":0,"BASE":32}
Yison ESP-01/ESP-202 {"NAME":"Yison Dev Board","GPIO":[259,544,258,1,260,261,1,1,416,418,257,417,256,1],"FLAG":0,"BASE":18}
```
@ -395,9 +400,9 @@ CE Smart Home CFW500D-3W 3 Way {"NAME":"CE-WF500D-3W","GPIO":[0,0,0,0,0,0,0,0,0
CNSKOU Touch {"NAME":"CNSKOU Dimmer Switch","GPIO":[0,0,0,0,0,0,0,0,0,0,290,0,0,0],"FLAG":0,"BASE":54}
Eva Logik {"NAME":"WF31 Dimmer","GPIO":[1,2272,1,2304,1,1,0,0,1,1,1,1,1,0],"FLAG":0,"BASE":54}
Eva Logik 3 Way {"NAME":"EL WF31T","GPIO":[1,2272,1,2304,1,1,0,0,1,1,1,1,1,0],"FLAG":0,"BASE":54}
Feit Electric Smart {"NAME":"Feit DIM/WIFI","GPIO":[1,2272,1,2304,1,1,0,0,1,0,1,0,1,0],"FLAG":0,"BASE":54}
Feit Electric Smart {"NAME":"Feit DIM/WIFI","GPIO":[0,2272,0,2304,0,0,0,0,0,0,0,0,0,0],"FLAG":0,"BASE":54}
Globe 3 Way {"NAME":"Globe Dimmer","GPIO":[0,2272,0,2304,0,0,0,0,0,0,0,0,0,0],"FLAG":0,"BASE":54}
Gosund SW2 {"NAME":"Gosund Dimmer","GPIO":[1,3200,1,3232,32,0,1,1,320,576,416,1,1,0],"FLAG":0,"BASE":18}
Gosund {"NAME":"Gosund Dimmer","GPIO":[1,3200,1,3232,32,0,1,1,320,576,416,1,1,0],"FLAG":0,"BASE":18}
iLintek / Lumary {"NAME":"L-DS100","GPIO":[1,2272,1,2304,1,1,0,0,1,1,1,1,1,0],"FLAG":0,"BASE":54}
iSwitch Touch Switch {"NAME":"iSwitchOZ Dimmer","GPIO":[0,0,0,0,0,0,0,0,0,0,290,0,0,0],"FLAG":0,"BASE":54}
Martin Jerry SD01 {"NAME":"MJ-SD01 Dimmer","GPIO":[34,33,0,323,576,322,0,0,321,416,320,96,256,0],"FLAG":0,"BASE":73}
@ -683,7 +688,7 @@ RGB 12-24V {"NAME":"WS03","GPIO":[0,0,0,0,0,0,0,0,418,417,416,
RGB+CCT 12-24V {"NAME":"WS05","GPIO":[0,0,0,0,0,420,0,0,418,417,416,419,0,0],"FLAG":0,"BASE":18}
RGBW 12-24V {"NAME":"*WS04","GPIO":[0,0,0,0,0,0,0,0,417,418,416,419,0,0],"FLAG":0,"BASE":18}
Shelly RGBW2 {"NAME":"Shelly RGBW2","GPIO":[0,0,288,0,419,1,0,0,416,32,418,417,0,0],"FLAG":0,"BASE":18}
Spectrum Smart RGBCCT {"NAME":"Spectrum Smart WOJ+05641","GPIO":[32,0,0,0,0,416,0,0,0,417,0,0,0,0],"FLAG":0,"BASE":18}
Spectrum Smart RGBCCT {"NAME":"Spectrum Smart RGB CCT Controller","GPIO":[32,0,0,0,416,419,0,0,417,420,418,0,0,0],"FLAG":0,"BASE":18}
Xunata Led Controller High Voltage 110/220V {"NAME":"KL-LED-WIFI-AC","GPIO":[0,0,0,0,0,0,0,0,0,416,0,0,0,0],"FLAG":0,"BASE":18}
ZJ-WF-ESP-A v1.1 {"NAME":"RGB2","GPIO":[0,0,0,0,0,0,0,0,417,416,418,0,0,0],"FLAG":0,"BASE":18}
```
@ -691,7 +696,9 @@ ZJ-WF-ESP-A v1.1 {"NAME":"RGB2","GPIO":[0,0,0,0,0,0,0,0,417,416,418,
## LED Strip
```
Aldi Casalux RGB {"NAME":"DW-RGB-WI01","GPIO":[1088,0,0,0,416,0,0,0,417,0,418,0,0,0],"FLAG":0,"BASE":18}
Arlec Smart 1m CCT LED Strip Light {"NAME":"ALD155HA","GPIO":[0,0,1088,0,0,416,0,0,0,417,0,0,0,0],"FLAG":0,"BASE":18}
Arlec Smart 2m LED Colour Changing Strip Light {"NAME":"Arlec_Light_Strip","GPIO":[1,1,1088,1,416,419,1,1,417,420,418,0,1,1],"FLAG":0,"BASE":18}
Arlec Smart 5m White & Colour Changing {"NAME":"ALD556HA","GPIO":[0,0,1088,0,416,419,0,0,417,420,418,0,0,0],"FLAG":0,"BASE":18}
B.K. Licht 2m RGB {"NAME":"RGBW-Strip","GPIO":[0,0,0,0,416,32,0,0,417,0,418,0,0,0],"FLAG":0,"BASE":18}
BAZZ 10 ft RGBW {"NAME":"BAZZ U183MRGBWWF RGBW LED Strip","GPIO":[32,0,0,0,416,419,0,0,417,0,418,0,0,0],"FLAG":0,"BASE":18}
BlitzWolf BW-LT11 {"NAME":"BW-LT11 Strip","GPIO":[32,0,0,0,416,419,0,0,417,0,418,0,0,0],"FLAG":0,"BASE":18}
@ -771,7 +778,7 @@ BrilliantSmart RGB Garden Kit {"NAME":"Brilliant Gard","GPIO":[0,0,0,0,416,0,0,
Connect SmartHome CSH-FSTN12 {"NAME":"CSH-FSTN12","GPIO":[0,0,0,0,416,0,0,0,417,0,418,0,0,0],"FLAG":0,"BASE":18}
Deta 18W 1900lm T8 Tube {"NAME":"DETA Smart LED","GPIO":[0,0,0,0,0,0,0,0,0,0,416,0,0,0],"FLAG":0,"BASE":18}
electriQ MOODL Ambiance Lamp {"NAME":"ElectriQ MOODL","GPIO":[0,4640,0,0,0,0,0,0,0,0,0,0,0,0],"FLAG":0,"BASE":18}
Gosund Table Lamp {"NAME":"Gosund LED Light","GPIO":[0,0,0,0,0,418,0,0,417,419,0,416,0,0],"FLAG":0,"BASE":18}
Gosund Table Lamp {"NAME":"Gosund LB3","GPIO":[0,0,0,0,0,418,0,0,417,419,0,416,0,0],"FLAG":0,"BASE":18}
Hama Wall Light Square, 10 cm, IP 44 {"NAME":"Hama Wifi Wall Light","GPIO":[0,0,0,0,0,416,0,0,0,417,0,0,0,1],"FLAG":0,"BASE":18}
Hugoai Table Lamp {"NAME":"HG02","GPIO":[1,1,1,1,1,1,0,0,1,1,1,1,1,0],"FLAG":0,"BASE":54,"CMND":"TuyaMCU 11,20 | TuyaMCU 26,21 | TuyaMCU 21,22 | TuyaMCU 23,23 | TuyaMCU 24,24 | DimmerRange 34,1000"}
Iwoole Table Lamp {"NAME":"GLOBELAMP","GPIO":[0,0,0,0,419,0,0,0,417,418,416,0,0,0],"FLAG":0,"BASE":18}
@ -824,7 +831,7 @@ Kogan Pet Fountain {"NAME":"Pet Fountain","GPIO":[0,2272,0,2304,0,0,0,
LED Starry Sky Projector Light {"NAME":"STAR PROJECTOR","GPIO":[0,0,0,0,416,0,0,0,417,0,418,0,0,0],"FLAG":0,"BASE":18}
Liectroux C30B Robot Vacuum {"NAME":"Liectroux C30B","GPIO":[1,1,1,1,1,1,0,0,1,1,1,1,1,0],"FLAG":0,"BASE":54}
Mosquito Killer Lamp {"NAME":"MosquitoKiller","GPIO":[32,0,0,0,0,0,0,0,416,320,0,0,0,0],"FLAG":0,"BASE":18}
NEO Coolcam Mouse Trap {"NAME":"Neo Mouse Trap","GPIO":[1,2272,1,2304,1,1,0,0,1,1,1,1,1,0],"FLAG":0,"BASE":54}
NEO Coolcam Mouse Trap {"NAME":"NAS-MA01W","GPIO":[1,1,1,1,1,1,0,0,1,1,1,1,1,0],"FLAG":0,"BASE":54,"CMND":"TuyaMCU 51,51 | TuyaMCU 99,11"}
PCI-e Desktop PC Remote Control {"NAME":"PC-Switch-01","GPIO":[32,0,0,0,0,0,0,0,224,544,0,0,0,0],"FLAG":0,"BASE":18}
Petoneer Smart Dot Cat Toy {"NAME":"Petoneer Smart Dot","GPIO":[0,2272,0,2304,0,0,0,0,0,0,0,0,0,0],"FLAG":0,"BASE":54}
Proscenic T21 Air Fryer {"NAME":"Proscenic T21","GPIO":[0,2272,0,2304,0,0,0,0,0,0,0,0,0,0],"FLAG":0,"BASE":54}
@ -853,11 +860,6 @@ Sinilink MODBUS Interface {"NAME":"XY-WFPOW","GPIO":[0,8768,544,8800,32,0,0,0
TYWE2S Replacement {"NAME":"ESP-02S","GPIO":[1,1,1,1,1,1,0,0,1,1,1,0,0,1],"FLAG":0,"BASE":18}
```
## Module Switch
```
Moes Mini 3 Gang 1/2 Way {"NAME":"Moes MS-104C","GPIO":[0,0,0,34,32,33,0,0,224,225,226,0,0,0],"FLAG":0,"BASE":18}
```
## Motion Sensor
```
DP-WP001 PIR {"NAME":"TUYA PIR","GPIO":[1,2272,1,2304,1,1,0,0,1,1,1,1,1,0],"FLAG":0,"BASE":54}
@ -899,7 +901,7 @@ Dewenwils Heavy Duty 40A {"NAME":"Dewenwils HOWT01A","GPIO":[0,0,544,0,0,0,0
ECF-SOP03 {"NAME":"Outdoor3Outlet","GPIO":[0,0,0,226,320,0,0,0,224,32,225,0,0,0],"FLAG":0,"BASE":18}
Ecoolbuy 4 Socket IP44 {"NAME":"ECCOLBUY 4","GPIO":[0,0,0,0,225,226,0,0,224,321,32,0,227,0],"FLAG":0,"BASE":18}
Edimax 2AC {"NAME":"EDI SP-1122WTO","GPIO":[0,0,0,0,225,576,0,0,224,0,32,0,0,0],"FLAG":0,"BASE":18}
Emax IP44 {"NAME":"Emax Smart Socket","GPIO":[0,0,0,0,320,0,0,0,224,0,32,0,0,0],"FLAG":0,"BASE":18}
Emax IP44 {"NAME":"Emax Smart Socket","GPIO":[0,0,0,0,320,0,0,0,224,321,32,0,0,0],"FLAG":0,"BASE":18}
Energizer 2AC Weather Resistant {"NAME":"Energizer EOX3-1001-BLK","GPIO":[0,0,0,0,320,576,0,0,224,0,32,0,0,0],"FLAG":0,"BASE":18}
Etekcity {"NAME":"ES015-TB","GPIO":[0,0,0,0,224,225,288,0,2656,2688,32,2592,289,0],"FLAG":0,"BASE":18}
Feit Electric PLUG/WIFI/WP {"NAME":"Prime Smart ou","GPIO":[0,1,0,1,544,320,0,0,224,32,0,0,0,0],"FLAG":0,"BASE":18}
@ -1053,6 +1055,7 @@ BAW {"NAME":"BAW TPSWIFI-10","GPIO":[0,0,0,0,320,0,0,0,
Bawoo {"NAME":"Bawoo S120","GPIO":[0,0,0,0,288,0,0,0,224,32,0,0,0,0],"FLAG":0,"BASE":18}
Be HiTech 16A {"NAME":"Be HiTech","GPIO":[0,0,0,288,0,2720,0,0,2624,32,2656,224,0,0],"FLAG":0,"BASE":18}
Bearware 303492 3AC+2USB {"NAME":"Bearware 30349","GPIO":[0,320,0,32,225,226,0,0,227,224,544,0,0,0],"FLAG":0,"BASE":18}
Beghelli Dom-e Presa Smart IT {"NAME":"Beghelli DOM-E Smart Plug","GPIO":[32,0,0,0,2720,2656,0,0,2624,544,224,0,0,0],"FLAG":0,"BASE":18}
Bestek MRJ1011 {"NAME":"BestekMRJ1011","GPIO":[0,0,0,0,320,0,0,0,224,32,0,0,0,0],"FLAG":0,"BASE":1}
BlitzWolf 1200W Dual {"NAME":"BlitzWolf SHP3","GPIO":[320,0,321,0,225,2720,0,0,2624,33,2656,224,32,0],"FLAG":0,"BASE":45}
BlitzWolf 16A Dual {"NAME":"SHP7 v2","GPIO":[33,576,320,2624,2720,2656,0,0,32,321,224,0,225,0],"FLAG":0,"BASE":45}
@ -1106,6 +1109,8 @@ Coosa SP1 {"NAME":"COOSA SP1","GPIO":[321,1,320,1,0,2720,0,0,
CooWoo {"NAME":"CooWoo AW01","GPIO":[0,0,0,0,288,160,0,0,256,0,0,0,0,0],"FLAG":0,"BASE":18}
CozyLife HomeKit 16A {"NAME":"CozyLife 16A","GPIO":[0,32,0,0,0,0,0,0,0,0,0,0,0,0,0,2720,0,0,2656,576,0,224,2624,0,0,0,0,0,0,0,0,0,0,0,0,0],"FLAG":0,"BASE":1}
CrazyLynX WiFi {"NAME":"CrazyLynX","GPIO":[0,0,0,0,321,320,0,0,224,32,0,0,0,4704],"FLAG":0,"BASE":18}
CurrySmarter 16A {"NAME":"CurrySmarter 16A","GPIO":[0,0,0,32,0,0,0,0,0,320,224,0,0,0],"FLAG":0,"BASE":18}
CurrySmarter 16A Power Monitoring {"NAME":"Currysmarter XH-TW2P","GPIO":[0,0,0,2624,32,320,0,0,224,2720,2656,0,0,0],"FLAG":0,"BASE":18}
CYYLTF BIFANS J23 {"NAME":"CYYLTD BIFANS J23","GPIO":[0,0,0,0,288,0,0,0,224,32,0,0,0,0],"FLAG":0,"BASE":18}
CZ100 10A {"NAME":"ASZKJ","GPIO":[320,0,321,0,0,0,0,0,0,32,0,224,0,0],"FLAG":0,"BASE":18}
D3D Smart Plug with USB & Power Monitor {"NAME":"D3D FLHS-ZN04","GPIO":[321,1,320,2624,1,2688,1,1,1,32,2656,224,1,1],"FLAG":0,"BASE":18}
@ -1187,13 +1192,13 @@ Globe Smart {"NAME":"GlobeSmartPlug","GPIO":[0,0,0,0,320,0,0,0,
GoldenDot Mini {"NAME":"GoldenDot Mini","GPIO":[0,32,0,0,0,0,0,0,0,321,224,0,0,0],"FLAG":0,"BASE":52}
GoldenDot with ADC {"NAME":"W-US003-Power","GPIO":[320,0,0,0,0,0,0,0,0,32,0,224,0,4896],"FLAG":0,"BASE":18}
Goliath 16A {"NAME":"GOLIATH AV-SSTE01","GPIO":[0,0,320,0,0,0,0,0,0,32,0,224,0,0],"FLAG":0,"BASE":18}
Gosund {"NAME":"Gosund EP2","GPIO":[320,1,576,1,2656,2720,0,0,2624,32,0,224,0,0],"FLAG":0,"BASE":45}
Gosund {"NAME":"Gosund EP2","GPIO":[576,1,320,1,2656,2720,0,0,2624,32,0,224,0,0],"FLAG":0,"BASE":45}
Gosund {"NAME":"Gosund SP111","GPIO":[320,0,321,0,0,2720,0,0,2624,32,2656,224,0,0],"FLAG":0,"BASE":18}
Gosund {"NAME":"SHP5","GPIO":[321,3072,320,3104,1,225,0,0,1,1,224,1,32,0],"FLAG":0,"BASE":18}
Gosund 13A {"NAME":"Gosund UP111","GPIO":[0,320,0,32,2720,2656,0,0,2624,576,224,0,0,0],"FLAG":0,"BASE":18}
Gosund 2 in 1 {"NAME":"Gosund WP212","GPIO":[321,288,544,0,224,2720,0,0,2624,32,2656,225,33,0],"FLAG":0,"BASE":18}
Gosund Dual {"NAME":"Gosund SP211","GPIO":[33,576,320,2624,2720,2656,0,0,32,321,224,0,225,0],"FLAG":0,"BASE":18}
Gosund SP1 {"NAME":"Gosund SP1 v23","GPIO":[0,321,0,32,2720,2656,0,0,2624,320,224,0,0,0],"FLAG":0,"BASE":55}
Gosund SP111 {"NAME":"Gosund SP111","GPIO":[320,0,321,0,0,2720,0,0,2624,32,2656,224,0,0],"FLAG":0,"BASE":18}
Gosund SP111 v1.1 {"NAME":"Gosund SP111 v1.1","GPIO":[320,0,576,0,2656,2720,0,0,2624,32,0,224,0,0],"FLAG":0,"BASE":45}
Gosund SP111 v1.4 {"NAME":"Gosund SP111 v1.4","GPIO":[321,1,320,1,0,2720,0,0,2624,32,2656,224,0,0],"FLAG":0,"BASE":45}
Gosund SP112 {"NAME":"Gosund 112v3.4","GPIO":[320,0,321,0,2656,2720,0,0,2624,257,224,0,0,4800],"FLAG":0,"BASE":18}
@ -1224,10 +1229,12 @@ HiHome {"NAME":"HiHome WPP-10S","GPIO":[32,0,0,0,2720,2656
HiHome {"NAME":"HIhome WPP-10S","GPIO":[320,0,576,1,0,2720,0,0,2624,32,2656,224,0,0],"FLAG":0,"BASE":49}
HiHome {"NAME":"HiHome WPP-16T","GPIO":[32,320,1,1,2720,2656,0,0,33,1,225,2592,224,4704],"FLAG":0,"BASE":18}
HIPER IoT P01 {"NAME":"HIPER IoT P01","GPIO":[0,0,0,0,0,320,0,0,224,32,0,0,0,0],"FLAG":0,"BASE":18}
HIPER IoT P05 {"NAME":"HIPER IoT P05","GPIO":[0,320,0,32,0,0,0,0,0,224,0,0,0,0],"FLAG":0,"BASE":18}
hiwild W-US002 {"NAME":"W-US002","GPIO":[0,32,0,0,0,0,0,0,0,288,224,0,576,0],"FLAG":0,"BASE":18}
HLT-309 {"NAME":"HLT-309","GPIO":[0,0,0,0,0,0,0,0,0,32,0,224,0,0],"FLAG":0,"BASE":18}
Hoin 10A {"NAME":"NIOH XS-SSC01","GPIO":[0,32,0,0,0,0,0,0,0,320,224,0,0,0],"FLAG":0,"BASE":18}
Hombli Socket Duo {"NAME":"HombliSocketDuo","GPIO":[33,0,0,0,0,0,0,0,0,544,224,225,32,0],"FLAG":0,"BASE":18}
Homecube {"NAME":"Homecube SP1","GPIO":[0,321,0,32,2720,2656,0,0,2624,320,224,0,0,0],"FLAG":0,"BASE":55}
HomeMate 16A Heavy Duty {"NAME":"HMLPG16","GPIO":[0,288,0,32,2720,2656,0,0,2624,544,224,0,0,0],"FLAG":0,"BASE":18}
Houzetek {"NAME":"AWP07L","GPIO":[320,0,0,0,0,2720,0,0,2624,32,2656,224,0,0],"FLAG":0,"BASE":18}
HS108 {"NAME":"HS108","GPIO":[0,32,0,0,0,0,0,0,0,320,224,0,0,4704],"FLAG":0,"BASE":18}
@ -1318,11 +1325,12 @@ Luminea ZX-2820 {"NAME":"ZX2820-675","GPIO":[0,0,0,32,2688,2656,0,0
Luminea ZX-2858 {"NAME":"ZX2858-675","GPIO":[32,0,0,0,2688,2656,0,0,2624,320,224,0,0,0],"FLAG":0,"BASE":65}
Lunvon 2000W {"NAME":"Lunvon Smart Plug","GPIO":[32,0,0,0,0,0,288,224,0,0,0,0,0,0],"FLAG":0,"BASE":18}
Lunvon 3000W {"NAME":"Lunvon Smart Plug","GPIO":[32,0,0,0,0,0,288,224,0,0,0,0,0,0],"FLAG":0,"BASE":18}
Marmitek Power SI {"NAME":"Marmitek PowerSi","GPIO":[0,0,0,32,2720,0,0,0,0,288,224,0,0,0],"FLAG":0,"BASE":18}
Martin Jerry V01 {"NAME":"MJ V01","GPIO":[0,0,0,0,320,0,0,0,224,32,0,0,0,0],"FLAG":0,"BASE":18}
Martin Jerry XS-SSA01 {"NAME":"MJ_XS-SSA01","GPIO":[0,32,0,0,0,0,0,0,0,320,224,0,0,0],"FLAG":0,"BASE":18}
Maxcio 16A Mini {"NAME":"MAXCIO RMC021","GPIO":[0,0,0,32,2720,2656,0,0,2624,544,224,0,0,0],"FLAG":0,"BASE":1}
Maxcio W-UK007S {"NAME":"Maxcio","GPIO":[320,0,1,0,0,2720,0,0,2624,32,2656,224,0,0],"FLAG":0,"BASE":45}
Maxcio W-US002S {"NAME":"W-US002S","GPIO":[321,0,320,0,0,2720,0,0,2624,32,2656,224,0,0],"FLAG":0,"BASE":45}
Maxcio W-US002S {"NAME":"W-US002S","GPIO":[0,32,0,544,2688,2656,0,0,2592,288,224,0,0,0],"FLAG":0,"BASE":18}
Maxcio W-US003 {"NAME":"W-US003","GPIO":[1,32,1,1,1,1,0,0,1,225,224,1,1,0],"FLAG":0,"BASE":18}
Maxcio YX-DE02 {"NAME":"Maxcio DE02","GPIO":[0,32,0,224,320,225,0,0,416,417,418,0,0,0],"FLAG":0,"BASE":18}
Maxcio YX-DE04 {"NAME":"Maxcio YX-DE04","GPIO":[1,32,1,224,320,419,0,0,416,417,418,1,1,4704],"FLAG":0,"BASE":18}
@ -1408,6 +1416,7 @@ PrimeCables {"NAME":"CAB-LA-WF4","GPIO":[0,0,0,320,321,0,0,0,0,
PrimeCables 2 USB Mini {"NAME":"CAB-LA-WF4-G2","GPIO":[0,0,0,0,0,320,0,0,224,32,0,0,0,0],"FLAG":0,"BASE":18}
Prokord {"NAME":"PSH-WS007-EU","GPIO":[0,0,320,1,0,2720,0,0,2624,32,2656,224,0,0],"FLAG":0,"BASE":45}
Prokord 16A {"NAME":"PROKORD PSH-WS021-EU","GPIO":[0,0,320,0,0,0,0,0,0,32,0,224,0,0],"FLAG":0,"BASE":18}
QNCX {"NAME":"QNCX","GPIO":[0,0,0,32,2720,2656,0,0,2624,320,224,0,0,0],"FLAG":0,"BASE":18}
Qnect 16A {"NAME":"Qnect QN-WP01","GPIO":[0,0,0,0,0,224,0,0,288,0,32,0,0,0],"FLAG":0,"BASE":18}
qnect 16A {"NAME":"QNECT QN-WP01E","GPIO":[0,0,0,32,2688,2656,0,0,2624,288,224,0,0,0],"FLAG":0,"BASE":18}
Qualitel Mini {"NAME":"Qualitel HG01WT","GPIO":[320,0,321,0,0,2688,0,0,2624,32,2656,224,0,0],"FLAG":0,"BASE":18}
@ -1523,6 +1532,7 @@ Vettora Smart Plug {"NAME":"Vettora","GPIO":[0,0,0,32,0,0,0,0,0,320,22
Vingo {"NAME":"Karpal-01","GPIO":[0,0,0,0,0,320,0,0,224,32,0,0,0,0],"FLAG":0,"BASE":18}
Vivanco 39625 Smart Home Power Adapter {"NAME":"Vivianco","GPIO":[0,0,0,32,2688,2656,0,0,2624,288,224,0,0,0],"FLAG":0,"BASE":18}
Vivitar {"NAME":"Vivitar HA1003","GPIO":[576,0,320,0,0,0,0,0,0,32,0,224,0,0],"FLAG":0,"BASE":18}
Vivitar {"NAME":"Vivitar HA1004","GPIO":[576,0,0,0,0,320,0,0,224,0,0,0,0,0],"FLAG":0,"BASE":18}
Vivitar HA-1006 {"NAME":"HA-1006","GPIO":[0,0,0,0,320,0,0,0,224,64,0,0,0,0],"FLAG":0,"BASE":18}
Vivitar HA-1006-AU {"NAME":"HA-1006-AU","GPIO":[0,0,0,0,320,0,0,0,224,64,0,0,0,0],"FLAG":0,"BASE":18}
Vivitar Plug {"NAME":"HA-1005N-AU","GPIO":[0,0,0,0,544,0,0,0,224,64,0,0,0,0],"FLAG":0,"BASE":18}
@ -1541,6 +1551,7 @@ Wipro 16A {"NAME":"Wip-DSP1160","GPIO":[0,0,0,32,2720,2656,0,
Wisdom Dual {"NAME":"ZY-ACU02","GPIO":[0,0,0,544,225,224,0,0,320,32,321,0,0,0],"FLAG":0,"BASE":18}
WiZ 10A {"NAME":"WIZ 9290024276","GPIO":[0,0,0,0,544,320,0,0,224,32,0,0,0,0],"FLAG":0,"BASE":18}
WL-SC01 {"NAME":"WL-SC01","GPIO":[0,0,0,0,320,0,0,0,224,0,32,0,0,0],"FLAG":0,"BASE":1}
Woox EU {"NAME":"WOOX R6113","GPIO":[0,2624,0,320,32,289,0,0,2656,224,2720,0,0,0],"FLAG":0,"BASE":18}
WOOX R4026 {"NAME":"WOOX R4026","GPIO":[0,0,0,32,0,0,0,0,0,320,224,0,0,0],"FLAG":0,"BASE":18}
WOOX R4785 {"NAME":"WOOXR4785","GPIO":[0,0,0,0,288,224,0,0,0,32,0,0,0,0],"FLAG":0,"BASE":18}
WOOX R5024 {"NAME":"WOOX5024","GPIO":[0,0,320,0,0,0,0,0,0,32,0,224,0,0],"FLAG":0,"BASE":18}
@ -1614,7 +1625,7 @@ Calex 4AC 2USB {"NAME":"Calex Power Strip 429228","GPIO":[0,320,0,
Calex 4AC 4USB {"NAME":"Calex Power Strip 429228","GPIO":[0,0,0,288,224,226,0,0,0,228,227,225,32,0],"FLAG":0,"BASE":18}
CE Smart Home {"NAME":"CE Power Strip","GPIO":[288,0,0,0,227,228,0,0,225,226,224,0,32,0],"FLAG":0,"BASE":18}
CE Smart Home Garden Stake {"NAME":"CE Power Stake","GPIO":[0,0,0,0,320,321,0,0,224,32,0,0,0,0],"FLAG":0,"BASE":18}
CRST LTS-4G-W {"NAME":"CRST LTS-4G-W","GPIO":[0,0,0,0,227,0,0,0,225,226,224,0,0,0],"FLAG":0,"BASE":18}
CRST LTS-4G-W {"NAME":"CRST LTS-4G-W","GPIO":[544,0,0,0,227,32,0,0,225,226,224,0,0,0],"FLAG":0,"BASE":18}
Curv 4 Plug {"NAME":"CURV","GPIO":[0,32,0,228,0,0,1,1,227,225,226,224,0,1],"FLAG":0,"BASE":18}
Deltaco SH-P03USB {"NAME":"Deltaco SH-P03","GPIO":[320,0,0,0,0,226,0,0,224,32,225,227,0,0],"FLAG":0,"BASE":18}
Digma DiPlug Strip 40 {"NAME":"DiPlug Strip 40","GPIO":[1,320,1,32,225,224,1,1,226,227,260,1,1,1],"FLAG":0,"BASE":18}
@ -1646,6 +1657,7 @@ Idinio 3AC 4USB {"NAME":"Idinio-140135","GPIO":[0,320,0,32,225,224,
Jinvoo 4AC+2USB {"NAME":"JINVOO Model SM-SO306-2A","GPIO":[320,0,0,0,259,32,0,0,257,258,256,0,228,0],"FLAG":0,"BASE":18}
KaBuM! Filtro de Linha {"NAME":"Kabum Power Strip","GPIO":[0,544,0,32,227,228,0,0,0,225,226,224,320,0],"FLAG":0,"BASE":18}
KMC 5AC 3USB QC {"NAME":"KMC 5-Outlet","GPIO":[320,0,0,0,228,160,0,0,225,224,226,0,227,0],"FLAG":0,"BASE":18}
KMC Smart Strip 3 {"NAME":"KMC 3-Outlet (20307)","GPIO":[0,320,0,32,0,226,0,0,224,0,225,0,0,0],"FLAG":0,"BASE":18}
Kogan Power Strip USB Ports & Energy Meter {"NAME":"Generic","GPIO":[64,320,0,227,2720,2656,0,0,2624,225,226,224,0,0],"FLAG":0,"BASE":18}
Konesky Type 1 {"NAME":"Konesky","GPIO":[0,0,0,0,228,225,0,0,227,32,226,224,0,0],"FLAG":0,"BASE":18}
Koogeek KLOE4 {"NAME":"Koogeek KLOE4","GPIO":[0,320,0,32,225,224,0,0,226,227,228,0,0,4704],"FLAG":0,"BASE":18}
@ -1680,7 +1692,7 @@ Surge Protector 3AC 2USB {"NAME":"C158","GPIO":[260,0,0,0,261,230,0,0,224,0,
SWB1 {"NAME":"SWB1","GPIO":[288,0,0,0,0,227,0,0,224,32,225,226,0,0],"FLAG":0,"BASE":18}
SWB2 3AC + 2USB {"NAME":"SWB2","GPIO":[576,1,0,1,0,226,0,0,224,32,225,227,0,0],"FLAG":0,"BASE":18}
Swisstone 4AC 4USB {"NAME":"Swisstone SH140","GPIO":[0,576,0,32,225,224,0,0,226,227,228,0,0,0],"FLAG":0,"BASE":18}
Sygonix 4AC {"NAME":"Sygonix SY-4538254","GPIO":[576,32,0,231,229,230,0,0,225,224,226,228,259,0],"FLAG":0,"BASE":18}
Sygonix 4AC {"NAME":"Sygonix SY-4538254","GPIO":[544,32,0,8678,8676,8679,0,0,8672,8673,8675,8677,8706,0],"FLAG":0,"BASE":18}
TCP Smart 4AC+USB {"NAME":"TCP WPS4WUK","GPIO":[1,320,0,32,226,227,0,0,225,224,228,0,0,1],"FLAG":0,"BASE":18}
Teckin SS30 {"NAME":"Teckin SS30","GPIO":[288,1,1,321,256,32,0,0,258,257,259,1,228,0],"FLAG":0,"BASE":18}
Tellur 3AC 4USB {"NAME":"Tellur","GPIO":[0,320,0,32,225,224,0,0,0,226,227,0,0,4704],"FLAG":0,"BASE":18}
@ -1939,6 +1951,7 @@ cod.m WLAN Pixel Controller v0.8 {"NAME":"cod.m Pixel Controller V0.8","GPIO":[
ESP01 NeoPixel Ring {"NAME":"ESP-01S-RGB-LED-v1.0","GPIO":[1,1,1376,1,0,0,0,0,0,0,0,0,0,0],"FLAG":0,"BASE":18}
H803WF 2048 Pixel 5V-24V {"NAME":"H803WF","GPIO":[0,0,0,0,3840,3840,0,0,3872,1376,0,3872,0,0],"FLAG":0,"BASE":18}
IOTMCU {"NAME":"IOTMCU_ESP-12S-RGB-LED-v1","GPIO":[1,1,1,1,0,1376,0,0,1,1088,32,0,0,0],"FLAG":0,"BASE":18}
LifeSmart Cololight PRO Hexagonal {"NAME":"Cololight PRO","GPIO":[0,0,0,0,32,0,0,0,0,33,0,0,1376,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4704,0,0,0,0,0,0],"FLAG":0,"BASE":1}
SP501E WS2812B {"NAME":"SP501E","GPIO":[0,32,0,1376,0,0,0,0,0,0,0,0,0,0],"FLAG":0,"BASE":18}
```
@ -2122,11 +2135,6 @@ Zemismart A19 10W {"NAME":"Zemism_E27_A19","GPIO":[0,0,0,0,0,0,0,0,0,
Zilotek A19 800lm {"NAME":"Zilotek RGBW","GPIO":[0,0,0,0,2912,416,0,0,417,2976,2944,0,0,0],"FLAG":0,"BASE":18}
```
## Relay
```
LilyGo T-Relay 8 {"NAME":"LilyGo ESP32 Relay 8","GPIO":[1,1,1,1,1,231,1,1,227,226,1,1,1,1,230,229,0,228,1,1,0,544,1,1,0,0,0,0,225,224,1,1,1,0,0,1],"FLAG":0,"BASE":1}
```
## Relay Board
```
2 Channel Tuya {"NAME":"TY-DIY-S02","GPIO":[1,1,1,1,1,1,0,0,1,1,1,1,1,0],"FLAG":0,"BASE":54,"CMND":"TuyaMCU 11,1 | TuyaMCU 12,2 | TuyaMCU 13,13 | TuyaMCU 1,101"}
@ -2171,8 +2179,10 @@ LC Technology 5V/8-80V 1 Channel {"NAME":"LC-Relay-ESP12-1R-MV","GPIO":[1,1,544
LC Technology 5V/8-80V 1 Channel {"NAME":"LC-Relay-ESP12-1R-D8","GPIO":[1,1,544,1,1,224,1,1,1,1,1,1,321,1],"FLAG":0,"BASE":18}
LC Technology AC90V-250V 1 Channel {"NAME":"ESP32_Relay_AC_X1","GPIO":[1,1,1,1,1,1,1,1,1,1,1,1,224,1,1,1,0,1,1,544,0,1,1,1,0,0,0,0,1,1,1,1,1,0,0,1],"FLAG":0,"BASE":1}
LC Technology DC5-60V 1 Channel {"NAME":"ESP32_Relay_X1","GPIO":[1,1,1,1,1,1,1,1,1,1,1,1,224,1,1,1,0,1,1,544,0,1,1,1,0,0,0,0,1,1,1,1,1,0,0,1],"FLAG":0,"BASE":1}
LC Technology DC5-60V 4 Channel {"NAME":"ESP32_Relay_X4","GPIO":[1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,1,1,1,0,226,227,1,0,0,0,0,224,225,1,1,1,0,0,1],"FLAG":0,"BASE":1}
LC Technology DC5-60V 2 Channel {"NAME":"ESP32_Relay_X2","GPIO":[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,544,0,225,224,0,0,0,0,0,0,0,0,0,0,0,0,0],"FLAG":0,"BASE":1}
LC Technology DC5-60V 4 Channel {"NAME":"ESP32_Relay_X4","GPIO":[1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,1,1,544,0,226,227,1,0,0,0,0,224,225,1,1,1,0,0,1],"FLAG":0,"BASE":1}
LC Technology ESP8266 5V {"NAME":"ESP8266-01S","GPIO":[224,3200,0,3232,0,0,0,0,0,0,0,0,0,4704],"FLAG":0,"BASE":18}
LilyGo T-Relay 8 {"NAME":"LilyGo ESP32 Relay 8","GPIO":[1,1,1,1,1,231,1,1,227,226,1,1,1,1,230,229,0,228,1,1,0,544,1,1,0,0,0,0,225,224,1,1,1,0,0,1],"FLAG":0,"BASE":1}
LilyGO TTGO 4 Channel ESP32 {"NAME":"T-Relay ESP32","GPIO":[0,0,1,0,1,227,0,0,1,1,1,1,0,0,226,225,0,224,1,1,0,544,1,1,0,0,0,0,1,1,1,1,1,0,0,1],"FLAG":0,"BASE":1}
LinkNode R4 {"NAME":"LinkNode R4","GPIO":[0,0,0,0,0,0,0,0,224,225,226,0,227,0],"FLAG":0,"BASE":18}
LinkNode R8 {"NAME":"LinkNode R8","GPIO":[0,0,0,0,228,229,0,231,226,227,225,230,224,0],"FLAG":0,"BASE":18}
@ -2194,11 +2204,6 @@ Yunshan 7-30V 10A {"NAME":"Yunshan 10A","GPIO":[32,1,288,1,224,161,0,
## Relay Module
```
2 CH Smart Switch {"NAME":"Generic","GPIO":[32,1,1,1,1,225,33,1,224,288,1,1,1,1],"FLAG":0,"BASE":18}
Aubess Power Monitor Switch 16A {"NAME":"Aubess with (BL0942)","GPIO":[0,3200,0,7520,0,0,0,0,0,0,224,0,0,0],"FLAG":0,"BASE":18}
Mini Smart Switch {"NAME":"SmartSwitch-MK601","GPIO":[0,0,0,160,32,224,0,0,0,0,288,0,0,0],"FLAG":0,"BASE":18}
Moes 10A {"NAME":"Moes MS-101","GPIO":[0,0,0,0,0,320,0,0,224,32,0,0,0,0],"FLAG":0,"BASE":18}
Smarsecur Smart Switch {"NAME":"ESP-02S","GPIO":[0,0,0,32,0,0,0,0,0,0,224,0,0,0],"FLAG":0,"BASE":18}
```
## Sensor
@ -2242,7 +2247,7 @@ AGL 2 Gang {"NAME":"AGL WiFi 02","GPIO":[0,0,544,0,0,33,0,0,22
AGL 3 Gang {"NAME":"AGL WiFi 03","GPIO":[0,0,544,0,34,33,0,0,225,224,226,0,32,0],"FLAG":0,"BASE":18}
Aoycocr SW1 {"NAME":"Aoycocr SW1","GPIO":[576,1,321,1,1,1,1,1,320,32,1,224,1,1],"FLAG":0,"BASE":18}
APPIO 1 Gang Switch {"NAME":"Appio-9608","GPIO":[0,0,0,0,0,32,0,0,0,0,0,224,288,0],"FLAG":0,"BASE":18}
Appio 3 Gang Touch {"NAME":"Appio 9611","GPIO":[0,0,0,0,226,33,0,0,32,224,34,225,288,0],"FLAG":0,"BASE":18}
Appio 3 Gang Touch {"NAME":"Appio-9611","GPIO":[0,0,0,0,224,33,0,0,34,226,32,225,288,0],"FLAG":0,"BASE":18}
Athom 1 Gang {"NAME":"Athom SW011EU","GPIO":[576,0,0,32,0,0,0,0,0,224,288,0,0,0],"FLAG":0,"BASE":1}
Athom 1 Gang {"NAME":"Athom SW031US","GPIO":[576,0,0,32,0,0,0,0,0,224,288,0,0,0],"FLAG":0,"BASE":1}
Athom 1 Gang No Neutral {"NAME":"Athom SW111EU","GPIO":[576,0,0,32,0,0,0,0,0,224,288,0,0,0],"FLAG":0,"BASE":1}
@ -2351,6 +2356,7 @@ Kuled KS602S {"NAME":"KULED","GPIO":[32,1,1,1,1,1,0,0,224,320,1,
Kygne CD-301 {"NAME":"KYGNE Touch","GPIO":[0,0,0,0,288,289,0,0,224,32,0,0,0,0],"FLAG":0,"BASE":10}
Laghten SS02S {"NAME":"Laghten SS02S","GPIO":[0,0,0,0,288,321,0,0,224,32,0,0,0,0],"FLAG":0,"BASE":18}
LCARE Modular 2 Gang {"NAME":"2SW1-In","GPIO":[32,1,1,1,0,225,33,0,224,320,0,0,0,0],"FLAG":0,"BASE":29}
LerLink 1 Gang {"NAME":"LerLink X801","GPIO":[0,0,0,0,32,0,0,0,224,320,0,0,0,1],"FLAG":0,"BASE":18}
LerLink 1 Gang No Neutral {"NAME":"LerLink X801-L","GPIO":[0,0,0,0,32,0,0,0,224,320,0,0,0,1],"FLAG":0,"BASE":18}
Lerlink 2 Gang {"NAME":"Lerlink X802A","GPIO":[0,0,0,33,32,0,0,0,224,288,225,0,0,0],"FLAG":0,"BASE":18}
LerLink 2 Gang No Neutral {"NAME":"Lonsonho 2gang","GPIO":[0,0,0,33,32,0,0,0,224,288,225,0,0,0],"FLAG":0,"BASE":18}
@ -2422,6 +2428,7 @@ MoKo Scene Life {"NAME":"Moko Smart Swi","GPIO":[576,0,0,0,418,417,
MoKo Smart Life {"NAME":"Moko Switch (Single)","GPIO":[544,0,0,32,224,0,0,0,0,0,320,0,0,0],"FLAG":0,"BASE":59}
NaamaSmart KS602 {"NAME":"KS-602","GPIO":[32,0,0,0,0,0,0,0,224,576,0,0,0,0],"FLAG":0,"BASE":18}
Nedis Dual {"NAME":"SM-SW102U-2","GPIO":[576,0,0,33,225,0,0,0,32,224,0,0,0,4704],"FLAG":0,"BASE":18}
NEO Coolcam 2Ch Touch Light {"NAME":"Neo NAS-SC01W-2","GPIO":[0,0,0,0,225,0,0,0,32,224,33,0,544,0],"FLAG":0,"BASE":18}
Nexete DS-123 {"NAME":"DS-123","GPIO":[544,321,1,32,224,33,0,0,1,225,320,1,1,0],"FLAG":0,"BASE":18}
Nexete DS-123 Single {"NAME":"DS-123","GPIO":[544,0,1,33,0,32,0,0,1,224,320,1,1,0],"FLAG":0,"BASE":18}
Novadigital Interruptor Touch Led 1 Boto {"NAME":"Nova Digital Switch 1 Gang","GPIO":[544,0,0,32,224,0,0,0,0,0,288,0,0,0],"FLAG":0,"BASE":18}
@ -2555,6 +2562,7 @@ ZUCZUG 3 Gang {"NAME":"2ph105626a x3","GPIO":[0,288,0,32,34,33,0,
## Switch Module
```
2 CH Smart Switch {"NAME":"Generic","GPIO":[32,1,1,1,1,225,33,1,224,288,1,1,1,1],"FLAG":0,"BASE":18}
AGL Modulo Relay 01 Canal {"NAME":"AGL-Basic","GPIO":[0,1,0,0,224,32,0,0,0,0,320,0,0,0],"FLAG":0,"BASE":18}
Albohes 2 Channel {"NAME":"Albohes SH-08","GPIO":[0,3200,33,3232,321,320,0,0,224,544,32,0,225,1],"FLAG":0,"BASE":18}
Athom 10A {"NAME":"CB01-TAS-1","GPIO":[0,0,0,32,320,0,0,0,0,224,0,0,0,1],"FLAG":0,"BASE":18}
@ -2563,6 +2571,7 @@ Athom 3-Way Mini Relay {"NAME":"RS01-TAS-1","GPIO":[0,0,0,32,576,0,0,0,0,2
Athom 4Ch Inching/Self-locking 10A {"NAME":"Athom R04","GPIO":[1,1,1,1,32,576,1,1,226,227,225,1,224,0],"FLAG":0,"BASE":18}
Athom 4Ch Inching/Self-locking 30A {"NAME":"Athom R04-30A","GPIO":[1,1,1,1,32,576,1,1,226,227,225,1,224,0],"FLAG":0,"BASE":18}
ATMS1601 230VAC DIN Timer/Switch {"NAME":"ATMS1601","GPIO":[1,1,1,1,544,320,1,1,224,32,1,1,1,1],"FLAG":0,"BASE":18}
Aubess Power Monitor Switch 16A {"NAME":"Aubess with (BL0942)","GPIO":[0,3200,0,7520,0,0,0,0,160,0,224,0,0,0],"FLAG":0,"BASE":18}
BH OnOfre Dual Rev5 Silent Edition {"NAME":"bhonofre","GPIO":[0,0,0,0,225,224,0,0,160,161,0,0,0,0],"FLAG":0,"BASE":18}
BlitzWolf BW-SS1 {"NAME":"BW-SS1","GPIO":[1,1,1,1,544,224,0,0,1,32,1,1,0,0],"FLAG":0,"BASE":18}
BlitzWolf BW-SS5 1 Gang {"NAME":"BlitzWolf SS5 1 Gang","GPIO":[0,0,0,0,288,0,0,0,160,224,0,0,0,0],"FLAG":0,"BASE":18}
@ -2589,8 +2598,12 @@ LoraTap 10A {"NAME":"LoraTap RR400W","GPIO":[0,0,0,0,544,0,0,0,
LoraTap RR500W {"NAME":"LoraTap RR500W","GPIO":[544,0,0,0,160,0,0,0,32,224,0,0,320,0],"FLAG":0,"BASE":18}
LoveAnna AC85-250V 10A {"NAME":"2xSwitch No RF LoveAnna","GPIO":[32,0,0,0,0,225,33,0,224,320,0,0,0,0],"FLAG":0,"BASE":18}
Luani HVIO {"NAME":"Luani HVIO","GPIO":[0,1,1,1,224,225,0,0,160,161,1,288,0,4704],"FLAG":0,"BASE":35}
Luminea {"NAME":"Luminea NX-4651","GPIO":[0,0,0,0,288,0,0,0,160,224,0,0,0,0],"FLAG":0,"BASE":18}
Milfra Smart {"NAME":"Milfra Smart Module TB41","GPIO":[576,0,0,225,2688,2656,0,0,2592,193,480,224,192,0],"FLAG":0,"BASE":18}
Mini Smart Switch {"NAME":"SmartSwitch-MK601","GPIO":[0,0,0,160,32,224,0,0,0,0,288,0,0,0],"FLAG":0,"BASE":18}
Moes {"NAME":"Moes MS-104B","GPIO":[0,0,32,0,480,0,0,0,161,160,224,225,0,0],"FLAG":0,"BASE":18}
Moes 10A {"NAME":"Moes MS-101","GPIO":[0,0,0,0,0,320,0,0,224,32,0,0,0,0],"FLAG":0,"BASE":18}
Moes Mini 3 Gang 1/2 Way {"NAME":"Moes MS-104C","GPIO":[0,0,0,34,32,33,0,0,224,225,226,0,0,0],"FLAG":0,"BASE":18}
Nedis 10A {"NAME":"Nedis WIFIPS10WT","GPIO":[0,0,0,0,224,0,0,0,32,321,0,288,0,0],"FLAG":0,"BASE":18}
Nova Digital Basic 1 MS101 {"NAME":"NovaDigBasic1","GPIO":[0,1,0,1,320,0,0,0,224,32,0,0,0,0],"FLAG":0,"BASE":18}
PPA Contatto Wi-Fi {"NAME":"PPA Contatto","GPIO":[0,0,32,0,224,162,0,0,288,225,0,0,0,0],"FLAG":0,"BASE":18}
@ -2605,11 +2618,12 @@ Shelly 2 {"NAME":"Shelly 2","GPIO":[0,2752,0,2784,224,225,0,
Shelly 2.5 {"NAME":"Shelly 2.5","GPIO":[320,0,0,0,224,193,0,0,640,192,608,225,3456,4736],"FLAG":0,"BASE":18}
Shelly EM {"NAME":"Shelly EM","GPIO":[0,0,0,0,0,0,0,0,640,3457,608,224,8832,1],"FLAG":0,"BASE":18}
Shelly i3 Action and Scenes Activation Device {"NAME":"Shelly i3","GPIO":[0,0,0,0,0,320,0,0,193,194,192,0,0,4736],"FLAG":0,"BASE":18}
Shelly Plus 1 {"NAME":"Shelly Plus 1 ","GPIO":[0,0,0,0,192,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,224,0,0,0,0,0,0,0,0,0,0,0,0,0],"FLAG":0,"BASE":1}
Shelly Plus 1 {"NAME":"Shelly Plus 1 ","GPIO":[288,0,0,0,192,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,32,224,0,0,0,0,0,4736,4705,0,0,0,0,0,0],"FLAG":0,"BASE":1}
Shelly Plus 1PM {"NAME":"Shelly Plus 1PM","GPIO":[0,0,0,0,192,2720,0,0,0,0,0,0,0,0,2656,0,0,0,0,2624,0,32,224,0,0,0,0,0,0,0,0,0,0,0,0,0],"FLAG":0,"BASE":1}
Shelly Plus 2PM {"NAME":"Shelly Plus 2PM PCB v0.1.9","GPIO":[320,0,0,0,32,192,0,0,225,224,0,0,0,0,193,0,0,0,0,0,0,608,640,3456,0,0,0,0,0,0,0,4736,0,0,0,0],"FLAG":0,"BASE":1,"CMND":"AdcParam1 2,10000,10000,3350"}
Shelly Plus 2PM {"NAME":"Shelly Plus 2PM PCB v0.1.9","GPIO":[320,0,0,0,34,192,0,0,225,224,0,0,0,0,193,0,0,0,0,0,0,608,640,3458,0,0,0,0,0,9472,0,4736,0,0,0,0],"FLAG":0,"BASE":1}
Shelly Plus i4 {"NAME":"Shelly Plus i4","GPIO":[0,0,0,0,0,0,0,0,192,0,193,0,0,0,0,0,0,0,0,0,0,0,195,194,0,0,0,0,0,0,0,0,0,0,0,0],"FLAG":0,"BASE":1,"CMND":"SwitchMode1 1 | SwitchMode2 1 | SwitchMode3 1 | SwitchMode4 1 | SwitchTopic 0 | SetOption114 1"}
Sinilink USB {"NAME":"XY-WFUSB","GPIO":[1,1,0,1,32,224,0,0,0,0,320,0,544,0],"FLAG":0,"BASE":18}
Smarsecur Smart Switch {"NAME":"ESP-02S","GPIO":[0,0,0,32,0,0,0,0,0,0,224,0,0,0],"FLAG":0,"BASE":18}
Smart Home SS-8839-01 {"NAME":"SS-8839-01","GPIO":[0,1,0,1,224,0,0,0,32,321,0,320,0,0],"FLAG":0,"BASE":18}
Sonoff 4CH (R2) {"NAME":"Sonoff 4CH","GPIO":[32,1,1,1,226,225,33,34,224,320,35,227,0,0],"FLAG":0,"BASE":7}
Sonoff 4CH Pro (R2) {"NAME":"Sonoff 4CH Pro","GPIO":[32,1,1,1,226,225,33,34,224,320,35,227,0,0],"FLAG":0,"BASE":23}
@ -2624,6 +2638,7 @@ Sonoff Dual R3 {"NAME":"Sonoff Dual R3","GPIO":[32,0,0,0,0,0,0,0,0
Sonoff Dual R3 Lite {"NAME":"Sonoff Dual R3 Lite","GPIO":[32,0,0,0,0,0,0,0,0,576,225,0,0,0,0,0,0,0,0,0,0,0,0,224,0,0,0,0,160,161,0,0,0,0,0,0],"FLAG":0,"BASE":1}
Sonoff Dual R3 v2 {"NAME":"Sonoff Dual R3 v2","GPIO":[32,0,0,0,0,0,0,0,0,576,225,0,0,0,0,0,0,0,0,0,0,3200,8128,224,0,0,0,0,160,161,0,0,0,0,0,0],"FLAG":0,"BASE":1}
Sonoff Mini {"NAME":"Sonoff Mini","GPIO":[32,0,0,0,160,0,0,0,224,320,0,0,1,0],"FLAG":0,"BASE":1}
Sonoff Mini Extreme {"NAME":"Sonoff MINIR4","GPIO":[32,0,0,0,0,0,0,0,0,0,0,0,0,0,0,576,0,0,0,0,0,0,224,160,0,0,0,0,0,0,0,0,0,0,0,0],"FLAG":0,"BASE":1}
Sonoff Mini R2 {"NAME":"Sonoff MINIR2","GPIO":[32,0,0,0,160,0,0,0,224,544,0,0,0,0],"FLAG":0,"BASE":1}
Sonoff Pow {"NAME":"Sonoff Pow","GPIO":[32,0,0,0,0,2592,0,0,224,2656,2688,288,0,0],"FLAG":0,"BASE":6}
Sonoff POW Elite 16A {"NAME":"Sonoff POWR316D","GPIO":[32,0,0,0,0,576,0,0,0,224,9280,0,3104,0,320,0,0,0,0,0,0,9184,9248,9216,0,0,0,0,0,0,0,0,0,0,0,0],"FLAG":0,"BASE":1}
@ -2644,6 +2659,7 @@ SUPLA inCan by Espablo {"NAME":"Supla Espablo","GPIO":[0,1,1312,1,32,224,0
SW-R03 {"NAME":"SW-R03","GPIO":[0,0,0,0,0,0,0,0,224,32,0,0,0,0],"FLAG":0,"BASE":18}
Switch Module 2x5A {"NAME":"QS-WIFI-S04-2C","GPIO":[1,1,32,1,480,0,0,0,161,160,224,225,0,0],"FLAG":0,"BASE":18}
Syrotech {"NAME":"Syrotech SY-LS80","GPIO":[0,0,0,0,224,32,0,0,0,320,320,0,0,0],"FLAG":0,"BASE":18}
Tinxy 1 Node 16A for AC/Geyser {"NAME":"Tnxy16A","GPIO":[32,0,0,0,160,224,0,0,288,0,416,0,0,0],"FLAG":0,"BASE":18}
Tinxy Single Node 7A {"NAME":"Tnxy07A","GPIO":[32,0,0,0,160,224,0,0,288,0,0,0,0,0],"FLAG":0,"BASE":18}
WL-SW01_10 {"NAME":"WL-SW01_10","GPIO":[32,3232,0,3200,0,0,0,0,224,320,0,0,0,0],"FLAG":0,"BASE":1}
Woox Integrational Switch {"NAME":"WOOXR4967","GPIO":[0,0,0,1,320,224,0,0,0,32,0,0,0,0],"FLAG":0,"BASE":18}
@ -2664,7 +2680,7 @@ Shelly Add-on {"NAME":"Shelly 1 Temp ","GPIO":[1344,0,0,1312,224,
```
Floor Heating or Water/Gas Boiler {"NAME":"ME81H Thermostat","GPIO":[1,1,1,1,1,1,0,0,1,1,1,1,1,0],"FLAG":0,"BASE":54}
Lytko 101 {"NAME":"Lytko 101","GPIO":[1,7584,1312,1,1792,1824,0,0,1,1,1,224,1,4736],"FLAG":0,"BASE":18}
Moes Floor Heating or Water/Gas Boiler Wall {"NAME":"WHT-HY609-GB-WH-MS","GPIO":[0,2304,0,2272,0,0,0,0,0,0,0,0,0,0],"FLAG":0,"BASE":54}
Moes Floor Heating or Water/Gas Boiler Wall {"NAME":"WHT-HY609-GB-WH-MS","GPIO":[0,2304,0,2272,0,0,0,0,0,0,0,0,0,0],"FLAG":0,"BASE":54,"CMND":"tuyamcu 11,1 | tuyamcu 71,3 | tuyamcu 72,2 | tuyamcu 12,102"}
Mysa V1 Electric Baseboard Heater {"NAME":"Mysa Thermostat","GPIO":[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,224,0,0,0,0,0,0,640,608,0,0,0,0,0,0],"FLAG":0,"BASE":1}
```
@ -2695,15 +2711,16 @@ CE Smart Home LQ-2-W3 {"NAME":"LITESUN","GPIO":[0,0,0,0,544,32,0,0,224,0,
DETA Double GPO + USB {"NAME":"DETA 6920HA","GPIO":[0,0,0,3104,32,288,0,0,33,224,225,0,0,0],"FLAG":0,"BASE":18}
Deta Double Power Point {"NAME":"DETA 2G GPO","GPIO":[0,0,0,0,544,0,0,0,65,224,225,0,64,0],"FLAG":0,"BASE":18}
DETA Outdoor Double Powerpoint {"NAME":"DETA 6294HA","GPIO":[0,0,0,3104,32,288,0,0,33,224,225,0,0,0],"FLAG":0,"BASE":18}
Deta Single Power Point {"NAME":"DETA 2G GPO USB","GPIO":[0,0,0,2720,64,576,0,0,65,224,225,0,0,0],"FLAG":0,"BASE":18}
Deta Single Power Point {"NAME":"DETA 1G GPO","GPIO":[0,0,0,3104,64,576,0,0,0,224,0,0,0,0],"FLAG":0,"BASE":18}
Ener-J 13A Twin Wall Sockets with USB {"NAME":"Ener-J 2-Gang ","GPIO":[32,0,0,0,0,224,33,0,225,320,0,0,0,0],"FLAG":0,"BASE":18}
Gosund {"NAME":"Gosund WO1","GPIO":[320,0,576,0,2656,2720,0,0,2624,321,225,224,0,4704],"FLAG":0,"BASE":18}
Gosund USB Charger {"NAME":"Gosund WO2","GPIO":[320,0,576,0,0,257,0,0,0,32,0,224,0,0],"FLAG":0,"BASE":18}
Hevolta Glasense {"NAME":"Hevolta Socket","GPIO":[0,0,0,0,288,289,0,0,224,32,0,0,0,0],"FLAG":0,"BASE":18}
Kapok T16 {"NAME":"tiltech-t16","GPIO":[0,320,0,32,192,0,0,0,224,225,0,0,0,0],"FLAG":0,"BASE":18}
Kesen KS-604 {"NAME":"KS-604","GPIO":[1,1,288,1,1,33,0,0,225,224,1,1,32,0],"FLAG":0,"BASE":18}
Kesen KS-604S {"NAME":"KS-604S","GPIO":[1,1,258,1,1,33,0,0,225,224,1,1,32,4704],"FLAG":0,"BASE":18}
Keygma KS-15TW {"NAME":"Keygma KS-15T","GPIO":[0,0,0,0,0,320,0,0,224,160,0,0,0,0],"FLAG":0,"BASE":18}
Knightsbridge Dual Waterproof {"NAME":"Knightsbridge Dual Socket","GPIO":[321,544,320,544,225,0,0,0,0,33,0,224,32,0],"FLAG":0,"BASE":18}
Knightsbridge Dual Waterproof {"NAME":"Knightsbridge Dual Socket","GPIO":[321,544,320,544,225,2720,0,0,2624,33,2656,224,32,0],"FLAG":0,"BASE":18}
KS-621 {"NAME":"KS-621","GPIO":[32,0,0,0,2688,2656,0,0,2592,288,0,224,65,1],"FLAG":0,"BASE":18}
Makegood MG-AUWF01 {"NAME":"MG-AUWF01","GPIO":[320,161,544,323,2720,2656,0,0,2624,225,321,224,160,0],"FLAG":0,"BASE":18}
Makegood MG-UKWSG01 {"NAME":"Aseer 2-Gang","GPIO":[321,160,544,323,2720,2656,0,0,2624,224,320,225,161,0],"FLAG":0,"BASE":18}

View File

@ -42,7 +42,7 @@ $target_file = "tasmota/".$image;
$hostname = $_SERVER['SERVER_NAME'];
if (move_uploaded_file($_FILES["file"]["tmp_name"], $target_file)) {
if (strpos($target_file, "tasmota32")) {
if (strpos($target_file, "tasmota32") | strpos($target_file, ".gz")) {
echo "The file $image has been uploaded to OTA server $hostname. \n";
} else {
gzCompressFile($target_file);

View File

@ -1,7 +0,0 @@
name=DnsClient
version=1.0
author=MCQN Ltd, Theo Arends
maintainer=Theo
sentence=Dns client allowing timeout selection.
paragraph=This class uses WifiUdp.
architectures=esp8266,esp32

View File

@ -1,334 +0,0 @@
/*
DnsClient.cpp - DNS client for Arduino
SPDX-FileCopyrightText: 2009-2010 MCQN Ltd. and Theo Arends
SPDX-License-Identifier: GPL-3.0-only
*/
// Arduino DNS client for WizNet5100-based Ethernet shield
// (c) Copyright 2009-2010 MCQN Ltd.
// Released under Apache License, version 2.0
#include "DnsClient.h"
// Various flags and header field values for a DNS message
#define UDP_HEADER_SIZE 8
#define DNS_HEADER_SIZE 12
#define TTL_SIZE 4
#define QUERY_FLAG (0)
#define RESPONSE_FLAG (1<<15)
#define QUERY_RESPONSE_MASK (1<<15)
#define OPCODE_STANDARD_QUERY (0)
#define OPCODE_INVERSE_QUERY (1<<11)
#define OPCODE_STATUS_REQUEST (2<<11)
#define OPCODE_MASK (15<<11)
#define AUTHORITATIVE_FLAG (1<<10)
#define TRUNCATION_FLAG (1<<9)
#define RECURSION_DESIRED_FLAG (1<<8)
#define RECURSION_AVAILABLE_FLAG (1<<7)
#define RESP_NO_ERROR (0)
#define RESP_FORMAT_ERROR (1)
#define RESP_SERVER_FAILURE (2)
#define RESP_NAME_ERROR (3)
#define RESP_NOT_IMPLEMENTED (4)
#define RESP_REFUSED (5)
#define RESP_MASK (15)
#define TYPE_A (0x0001)
#define CLASS_IN (0x0001)
#define LABEL_COMPRESSION_MASK (0xC0)
// Port number that DNS servers listen on
#define DNS_PORT 53
// Possible return codes from ProcessResponse
#define SUCCESS 1
#define TIMED_OUT -1
#define INVALID_SERVER -2
#define TRUNCATED -3
#define INVALID_RESPONSE -4
#ifndef htons
#define htons(x) ( ((x)<< 8 & 0xFF00) | ((x)>> 8 & 0x00FF) )
#endif
void DNSClient::begin(const IPAddress& aDNSServer) {
iDNSServer = aDNSServer;
iRequestId = 0;
}
void DNSClient::setTimeout(uint32_t aTimeout) {
iTimeout = aTimeout;
}
int DNSClient::getHostByName(const char* aHostname, IPAddress& aResult) {
// See if it's a numeric IP address
if (aResult.fromString(aHostname)) {
// It is, our work here is done
return SUCCESS;
}
// Check we've got a valid DNS server to use
if ((0xFFFFFFFF == (uint32_t)iDNSServer) || (0 == (uint32_t)iDNSServer)) {
return INVALID_SERVER;
}
int ret = 0;
// Find a socket to use
if (iUdp.begin(1024+(millis() & 0xF)) == 1) {
// Try up to three times
int retries = 0;
// while ((retries < 3) && (ret <= 0)) {
// Send DNS request
ret = iUdp.beginPacket(iDNSServer, DNS_PORT);
if (ret != 0) {
// Now output the request data
ret = BuildRequest(aHostname);
if (ret != 0) {
// And finally send the request
ret = iUdp.endPacket();
if (ret != 0) {
// Now wait for a response
int wait_retries = 0;
ret = TIMED_OUT;
while ((wait_retries < 3) && (ret == TIMED_OUT)) {
ret = ProcessResponse(iTimeout, aResult);
wait_retries++;
}
}
}
}
retries++;
// }
// We're done with the socket now
iUdp.stop();
}
return ret;
}
int DNSClient::BuildRequest(const char* aName) {
// Build header
// 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
// | ID |
// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
// |QR| Opcode |AA|TC|RD|RA| Z | RCODE |
// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
// | QDCOUNT |
// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
// | ANCOUNT |
// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
// | NSCOUNT |
// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
// | ARCOUNT |
// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
// As we only support one request at a time at present, we can simplify
// some of this header
iRequestId = millis(); // generate a random ID
uint16_t twoByteBuffer;
// FIXME We should also check that there's enough space available to write to, rather
// FIXME than assume there's enough space (as the code does at present)
iUdp.write((uint8_t*)&iRequestId, sizeof(iRequestId));
twoByteBuffer = htons(QUERY_FLAG | OPCODE_STANDARD_QUERY | RECURSION_DESIRED_FLAG);
iUdp.write((uint8_t*)&twoByteBuffer, sizeof(twoByteBuffer));
twoByteBuffer = htons(1); // One question record
iUdp.write((uint8_t*)&twoByteBuffer, sizeof(twoByteBuffer));
twoByteBuffer = 0; // Zero answer records
iUdp.write((uint8_t*)&twoByteBuffer, sizeof(twoByteBuffer));
iUdp.write((uint8_t*)&twoByteBuffer, sizeof(twoByteBuffer));
// and zero additional records
iUdp.write((uint8_t*)&twoByteBuffer, sizeof(twoByteBuffer));
// Build question
const char* start =aName;
const char* end =start;
uint8_t len;
// Run through the name being requested
while (*end) {
// Find out how long this section of the name is
end = start;
while (*end && (*end != '.') ) {
end++;
}
if (end-start > 0) {
// Write out the size of this section
len = end-start;
iUdp.write(&len, sizeof(len));
// And then write out the section
iUdp.write((uint8_t*)start, end-start);
}
start = end+1;
}
// We've got to the end of the question name, so terminate it with a zero-length section
len = 0;
iUdp.write(&len, sizeof(len));
// Finally the type and class of question
twoByteBuffer = htons(TYPE_A);
iUdp.write((uint8_t*)&twoByteBuffer, sizeof(twoByteBuffer));
twoByteBuffer = htons(CLASS_IN); // Internet class of question
iUdp.write((uint8_t*)&twoByteBuffer, sizeof(twoByteBuffer));
// Success! Everything buffered okay
return SUCCESS;
}
int DNSClient::ProcessResponse(uint32_t aTimeout, IPAddress& aAddress) {
uint32_t startTime = millis();
// Wait for a response packet
while(iUdp.parsePacket() <= 0) {
if ((millis() - startTime) > aTimeout) {
return TIMED_OUT;
}
delay(20);
}
// We've had a reply!
// Read the UDP header
uint8_t header[DNS_HEADER_SIZE]; // Enough space to reuse for the DNS header
// Check that it's a response from the right server and the right port
if ( (iDNSServer != iUdp.remoteIP()) || (iUdp.remotePort() != DNS_PORT) ) {
// It's not from who we expected
return INVALID_SERVER;
}
// Read through the rest of the response
if (iUdp.available() < DNS_HEADER_SIZE) {
return TRUNCATED;
}
iUdp.read(header, DNS_HEADER_SIZE);
uint16_t staging; // Staging used to avoid type-punning warnings
memcpy(&staging, &header[2], sizeof(uint16_t));
uint16_t header_flags = htons(staging);
memcpy(&staging, &header[0], sizeof(uint16_t));
// Check that it's a response to this request
if ( (iRequestId != staging) || ((header_flags & QUERY_RESPONSE_MASK) != (uint16_t)RESPONSE_FLAG) ) {
// Mark the entire packet as read
iUdp.flush();
return INVALID_RESPONSE;
}
// Check for any errors in the response (or in our request)
// although we don't do anything to get round these
if ( (header_flags & TRUNCATION_FLAG) || (header_flags & RESP_MASK) ) {
// Mark the entire packet as read
iUdp.flush();
return -5; // INVALID_RESPONSE;
}
// And make sure we've got (at least) one answer
memcpy(&staging, &header[6], sizeof(uint16_t));
uint16_t answerCount = htons(staging);
if (answerCount == 0 ) {
// Mark the entire packet as read
iUdp.flush();
return -6; // INVALID_RESPONSE;
}
// Skip over any questions
memcpy(&staging, &header[4], sizeof(uint16_t));
for (uint32_t i = 0; i < htons(staging); i++) {
// Skip over the name
uint8_t len;
do {
iUdp.read(&len, sizeof(len));
if (len > 0) {
// Don't need to actually read the data out for the string, just
// advance ptr to beyond it
while(len--) {
iUdp.read(); // we don't care about the returned byte
}
}
} while (len != 0);
// Now jump over the type and class
for (uint32_t i = 0; i < 4; i++) {
iUdp.read(); // we don't care about the returned byte
}
}
// Now we're up to the bit we're interested in, the answer
// There might be more than one answer (although we'll just use the first
// type A answer) and some authority and additional resource records but
// we're going to ignore all of them.
for (uint32_t i = 0; i < answerCount; i++) {
// Skip the name
uint8_t len;
do {
iUdp.read(&len, sizeof(len));
if ((len & LABEL_COMPRESSION_MASK) == 0) {
// It's just a normal label
if (len > 0) {
// And it's got a length
// Don't need to actually read the data out for the string,
// just advance ptr to beyond it
while(len--) {
iUdp.read(); // we don't care about the returned byte
}
}
} else {
// This is a pointer to a somewhere else in the message for the
// rest of the name. We don't care about the name, and RFC1035
// says that a name is either a sequence of labels ended with a
// 0 length octet or a pointer or a sequence of labels ending in
// a pointer. Either way, when we get here we're at the end of
// the name
// Skip over the pointer
iUdp.read(); // we don't care about the returned byte
// And set len so that we drop out of the name loop
len = 0;
}
} while (len != 0);
// Check the type and class
uint16_t answerType;
uint16_t answerClass;
iUdp.read((uint8_t*)&answerType, sizeof(answerType));
iUdp.read((uint8_t*)&answerClass, sizeof(answerClass));
// Ignore the Time-To-Live as we don't do any caching
for (uint32_t i = 0; i < TTL_SIZE; i++) {
iUdp.read(); // We don't care about the returned byte
}
// And read out the length of this answer
// Don't need header_flags anymore, so we can reuse it here
iUdp.read((uint8_t*)&header_flags, sizeof(header_flags));
if ( (htons(answerType) == TYPE_A) && (htons(answerClass) == CLASS_IN) ) {
if (htons(header_flags) != 4) {
// It's a weird size
// Mark the entire packet as read
iUdp.flush();
return -9; // INVALID_RESPONSE;
}
iUdp.read(aAddress.raw_address(), 4);
// uint32_t address;
// iUdp.read((uint8_t*)&address, sizeof(address));
// aAddress = (IPAddress)address;
// Check we've got a valid address
if ((0xFFFFFFFF != (uint32_t)aAddress) && (0 != (uint32_t)aAddress)) {
return SUCCESS;
}
} else {
// This isn't an answer type we're after, move onto the next one
for (uint32_t i = 0; i < htons(header_flags); i++) {
iUdp.read(); // we don't care about the returned byte
}
}
}
// Mark the entire packet as read
iUdp.flush();
// If we get here then we haven't found an answer
return -10; // INVALID_RESPONSE;
}

View File

@ -1,42 +0,0 @@
/*
DnsClient.h - DNS client for Arduino
SPDX-FileCopyrightText: 2009-2010 MCQN Ltd. and Theo Arends
SPDX-License-Identifier: GPL-3.0-only
*/
// Arduino DNS client for WizNet5100-based Ethernet shield
// (c) Copyright 2009-2010 MCQN Ltd.
// Released under Apache License, version 2.0
#ifndef DNSClient_h
#define DNSClient_h
#include <Arduino.h>
#include <IPAddress.h>
#include <WiFiUdp.h>
class DNSClient {
public:
void begin(const IPAddress& aDNSServer);
void setTimeout(uint32_t aTimeout = 1000);
/* Resolve the given hostname to an IP address.
@param aHostname Name to be resolved
@param aResult IPAddress structure to store the returned IP address
@result 1 if aIPAddrString was successfully converted to an IP address, else error code
*/
int getHostByName(const char* aHostname, IPAddress& aResult);
protected:
int BuildRequest(const char* aName);
int ProcessResponse(uint32_t aTimeout, IPAddress& aAddress);
IPAddress iDNSServer;
uint16_t iRequestId;
uint16_t iTimeout = 1000;
WiFiUDP iUdp;
};
#endif

View File

@ -46,7 +46,6 @@ static uint32_t tasmota_serial_uart_bitmap = 0; // Assigned UARTs
TasmotaSerial::TasmotaSerial(int receive_pin, int transmit_pin, int hardware_fallback, int nwmode, int buffer_size) {
m_valid = false;
m_tx_enable_valid = false;
m_hardserial = false;
m_hardswap = false;
m_overflow = false;
@ -56,6 +55,7 @@ TasmotaSerial::TasmotaSerial(int receive_pin, int transmit_pin, int hardware_fal
serial_buffer_size = buffer_size;
m_rx_pin = receive_pin;
m_tx_pin = transmit_pin;
m_tx_enable_pin = -1;
m_in_pos = 0;
m_out_pos = 0;
#ifdef ESP8266
@ -134,12 +134,9 @@ bool TasmotaSerial::isValidGPIOpin(int pin) {
void TasmotaSerial::setTransmitEnablePin(int tx_enable_pin) {
if ((tx_enable_pin > -1) && isValidGPIOpin(tx_enable_pin)) {
m_tx_enable_valid = true;
m_tx_enable_pin = tx_enable_pin;
pinMode(m_tx_enable_pin, OUTPUT);
digitalWrite(m_tx_enable_pin, LOW);
} else {
m_tx_enable_valid = false;
}
}
@ -425,7 +422,7 @@ void IRAM_ATTR TasmotaSerial::_fast_write(uint8_t b) {
size_t TasmotaSerial::write(uint8_t b) {
if (!m_hardserial && (-1 == m_tx_pin)) { return 0; }
if (m_tx_enable_valid) {
if (m_tx_enable_pin > -1) {
digitalWrite(m_tx_enable_pin, HIGH);
}
size_t size = 0;
@ -462,8 +459,7 @@ size_t TasmotaSerial::write(uint8_t b) {
}
size = 1;
}
if (m_tx_enable_valid) {
delay(1);
if (m_tx_enable_pin > -1) {
digitalWrite(m_tx_enable_pin, LOW);
}
return size;

View File

@ -95,7 +95,6 @@ class TasmotaSerial : public Stream {
uint32_t m_out_pos;
uint32_t serial_buffer_size = TM_SERIAL_BUFFER_SIZE;
bool m_valid;
bool m_tx_enable_valid;
bool m_nwmode;
bool m_hardserial;
bool m_hardswap;

View File

@ -27,9 +27,15 @@ enum LoggingLevels {LOG_LEVEL_NONE, LOG_LEVEL_ERROR, LOG_LEVEL_INFO, LOG_LEVEL_D
//#define TASMOTAMODBUSDEBUG
#define TASMOTA_MODBUS_TX_ENABLE // Use local Tx enable on write buffer
TasmotaModbus::TasmotaModbus(int receive_pin, int transmit_pin, int tx_enable_pin) : TasmotaSerial(receive_pin, transmit_pin, 2)
{
setTransmitEnablePin(tx_enable_pin);
#ifdef TASMOTA_MODBUS_TX_ENABLE
mb_tx_enable_pin = tx_enable_pin; // Use local Tx enable on write buffer
#else
setTransmitEnablePin(tx_enable_pin); // Use TasmotaSerial Tx enable on write byte
#endif // TASMOTA_MODBUS_TX_ENABLE
mb_address = 0;
}
@ -58,6 +64,12 @@ int TasmotaModbus::Begin(long speed, uint32_t config)
if (begin(speed, config)) {
result = 1;
if (hardwareSerial()) { result = 2; }
#ifdef TASMOTA_MODBUS_TX_ENABLE
if (mb_tx_enable_pin > -1) {
pinMode(mb_tx_enable_pin, OUTPUT);
digitalWrite(mb_tx_enable_pin, LOW);
}
#endif // TASMOTA_MODBUS_TX_ENABLE
}
return result;
}
@ -143,14 +155,25 @@ uint8_t TasmotaModbus::Send(uint8_t device_address, uint8_t function_code, uint1
buf = (uint8_t *)malloc(bufsize);
memset(buf, 0, bufsize);
uint16_t i;
for (i = 0; i < framepointer;i++)
for (i = 0; i < framepointer;i++) {
snprintf((char *)&buf[i*3], (bufsize-i*3), "%02X ",frame[i]);
}
AddLog(LOG_LEVEL_DEBUG_MORE, PSTR("MBS: Serial Send: %s"), buf);
free(buf);
#endif
flush();
#ifdef TASMOTA_MODBUS_TX_ENABLE
if (mb_tx_enable_pin > -1) {
digitalWrite(mb_tx_enable_pin, HIGH);
}
#endif // TASMOTA_MODBUS_TX_ENABLE
write(frame, framepointer);
#ifdef TASMOTA_MODBUS_TX_ENABLE
if (mb_tx_enable_pin > -1) {
digitalWrite(mb_tx_enable_pin, LOW);
}
#endif // TASMOTA_MODBUS_TX_ENABLE
free(frame);
return 0;
}

View File

@ -66,6 +66,7 @@ class TasmotaModbus : public TasmotaSerial {
uint8_t ReceiveCount(void) { return mb_len; }
private:
int mb_tx_enable_pin;
uint8_t mb_address;
uint8_t mb_len;
};

View File

@ -86,6 +86,10 @@ void Renderer::Begin(int16_t p1,int16_t p2,int16_t p3) {
}
void Renderer::Sleep(void) {
}
void Renderer::Updateframe() {
}

View File

@ -35,7 +35,7 @@ typedef struct LVGL_PARAMS {
uint8_t use_dma : 1;
uint8_t swap_color : 1;
uint8_t async_dma : 1; // force DMA completion before returning, avoid conflict with other devices on same bus. If set you should make sure the display is the only device on the bus
uint8_t resvd_1 : 1;
uint8_t busy_invert : 1;
uint8_t resvd_2 : 1;
uint8_t resvd_3 : 1;
uint8_t resvd_4 : 1;
@ -86,6 +86,7 @@ public:
virtual uint16_t bgcol(void);
virtual int8_t color_type(void);
virtual void Splash(void);
virtual void Sleep(void);
virtual char *devname(void);
virtual LVGL_PARAMS *lvgl_pars(void);
virtual void ep_update_mode(uint8_t mode);

View File

@ -28,6 +28,11 @@
#include "epd2in9.h"
#define EPD_29_V2
//#define BUSY_PIN 16
Epd::Epd(int16_t width, int16_t height) :
Paint(width,height) {
}
@ -36,30 +41,35 @@ void Epd::DisplayOnff(int8_t on) {
}
void Epd::Updateframe() {
#ifdef EPD_29_V2
if (mode == DISPLAY_INIT_PARTIAL) {
SetFrameMemory_Partial(framebuffer, 0, 0, EPD_WIDTH,EPD_HEIGHT);
DisplayFrame_Partial();
} else {
SetFrameMemory(framebuffer, 0, 0, EPD_WIDTH,EPD_HEIGHT);
DisplayFrame();
}
#else
SetFrameMemory(framebuffer, 0, 0, EPD_WIDTH,EPD_HEIGHT);
DisplayFrame();
#endif
//Serial.printf("update\n");
}
#define DISPLAY_INIT_MODE 0
#define DISPLAY_INIT_PARTIAL 1
#define DISPLAY_INIT_FULL 2
void Epd::DisplayInit(int8_t p,int8_t size,int8_t rot,int8_t font) {
// ignore update mode
if (p==DISPLAY_INIT_PARTIAL) {
if (p == DISPLAY_INIT_PARTIAL) {
Init(lut_partial_update);
//ClearFrameMemory(0xFF); // bit set = white, bit reset = black
DisplayFrame();
delay(500);
delay_busy(500);
return;
//Serial.printf("partial\n");
} else if (p==DISPLAY_INIT_FULL) {
} else if (p == DISPLAY_INIT_FULL) {
Init(lut_full_update);
//ClearFrameMemory(0xFF); // bit set = white, bit reset = black
DisplayFrame();
delay(3500);
delay_busy(3500);
//Serial.printf("full\n");
return;
} else {
@ -78,25 +88,31 @@ void Epd::DisplayInit(int8_t p,int8_t size,int8_t rot,int8_t font) {
disp_bpp = 1;
}
void Epd::Begin(int16_t cs,int16_t mosi,int16_t sclk) {
cs_pin=cs;
mosi_pin=mosi;
sclk_pin=sclk;
void Epd::Begin(int16_t cs,int16_t mosi,int16_t sclk, int16_t rst, int16_t busy) {
cs_pin = cs;
mosi_pin = mosi;
sclk_pin = sclk;
rst_pin = rst;
busy_pin = busy;
#ifdef BUSY_PIN
busy_pin = BUSY_PIN;
#endif
}
void Epd::Init(int8_t p) {
if (p==DISPLAY_INIT_PARTIAL) {
if (p == DISPLAY_INIT_PARTIAL) {
Init(lut_partial_update);
} else {
Init(lut_full_update);
}
mode = p;
ClearFrameMemory(0xFF);
DisplayFrame();
if (p==DISPLAY_INIT_PARTIAL) {
delay(350);
if (p == DISPLAY_INIT_PARTIAL) {
delay_busy(350);
} else {
delay(3500);
delay_busy(3500);
}
}
@ -112,6 +128,9 @@ int Epd::Init(const unsigned char* lut) {
sclk_pin=pin[GPIO_SSPI_SCLK];
*/
if (framebuffer) {
// free(framebuffer);
}
framebuffer = (uint8_t*)malloc(EPD_WIDTH * EPD_HEIGHT / 8);
if (!framebuffer) return -1;
@ -123,9 +142,46 @@ int Epd::Init(const unsigned char* lut) {
digitalWrite(mosi_pin,LOW);
digitalWrite(sclk_pin,LOW);
if (rst_pin >= 0) {
pinMode(rst_pin, OUTPUT);
digitalWrite(rst_pin, HIGH);
}
if (busy_pin >= 0) {
pinMode(busy_pin, INPUT_PULLUP);
}
width = EPD_WIDTH;
height = EPD_HEIGHT;
#ifdef EPD_29_V2
/* EPD hardware init start */
Reset();
SendCommand(0x12); //SWRESET
delay_busy(100);
SendCommand(0x01); //Driver output control
SendData(0x27);
SendData(0x01);
SendData(0x00);
SendCommand(0x11); //data entry mode
SendData(0x03);
SetMemoryArea(0, 0, width-1, height-1);
SendCommand(0x21); // Display update control
SendData(0x00);
SendData(0x80);
SetMemoryPointer(0, 0);
delay_busy(10);
SetLut_by_host(lut_full_update);
mode = DISPLAY_INIT_FULL;
#else
/* EPD hardware init start */
this->lut = lut;
Reset();
@ -146,6 +202,7 @@ int Epd::Init(const unsigned char* lut) {
SendCommand(DATA_ENTRY_MODE_SETTING);
SendData(0x03); // X increment; Y increment
SetLut(this->lut);
#endif
/* EPD hardware init end */
return 0;
}
@ -168,28 +225,63 @@ void Epd::SendData(unsigned char data) {
// SpiTransfer(data);
}
/**
* @brief: Wait until the busy_pin goes LOW
*/
void Epd::WaitUntilIdle(void) {
return;
//while(DigitalRead(busy_pin) == HIGH) { //LOW: idle, HIGH: busy
// DelayMs(100);
//}
void Epd::delay_busy(uint32_t wait) {
if (busy_pin >= 0) {
while (digitalRead(busy_pin) == HIGH) { //LOW: idle, HIGH: busy
delay(10);
}
} else {
delay(wait);
}
}
/**
* @brief: module reset.
* often used to awaken the module in deep sleep,
* see Epd::Sleep();
*/
void Epd::Reset(void) {
//DigitalWrite(reset_pin, LOW); //module reset
//delay(200);
//DigitalWrite(reset_pin, HIGH);
//delay(200);
if (rst_pin >= 0) {
digitalWrite(rst_pin, LOW); //module reset
delay(200);
digitalWrite(rst_pin, HIGH);
delay(200);
} else {
SendCommand(0x12);
}
}
#ifdef EPD_29_V2
void Epd::SetLut(const unsigned char *lut) {
unsigned char count;
SendCommand(0x32);
for(count=0; count<153; count++)
SendData(lut[count]);
delay_busy(50);
}
void Epd::SetLut_by_host(const unsigned char *lut) {
SetLut((unsigned char *)lut);
SendCommand(0x3f);
SendData(*(lut+153));
SendCommand(0x03); // gate voltage
SendData(*(lut+154));
SendCommand(0x04); // source voltage
SendData(*(lut+155)); // VSH
SendData(*(lut+156)); // VSH2
SendData(*(lut+157)); // VSL
SendCommand(0x2c); // VCOM
SendData(*(lut+158));
}
#else
void Epd::SetLut_by_host(const unsigned char *lut) {
}
/**
* @brief: set the look-up table register
*/
@ -201,6 +293,7 @@ void Epd::SetLut(const unsigned char* lut) {
SendData(this->lut[i]);
}
}
#endif
/**
* @brief: put an image buffer to the frame memory.
@ -213,6 +306,7 @@ void Epd::SetFrameMemory(
uint16_t image_width,
uint16_t image_height
) {
uint16_t x_end;
uint16_t y_end;
@ -303,13 +397,118 @@ void Epd::ClearFrameMemory(unsigned char color) {
* set the other memory area.
*/
void Epd::DisplayFrame(void) {
SendCommand(DISPLAY_UPDATE_CONTROL_2);
SendCommand(DISPLAY_UPDATE_CONTROL_2); // 0x22
#ifdef EPD_29_V2
SendData(0xC7);
#else
SendData(0xC4);
SendCommand(MASTER_ACTIVATION);
#endif
SendCommand(MASTER_ACTIVATION); // 0x20
#ifndef EPD_29_V2
SendCommand(TERMINATE_FRAME_READ_WRITE);
WaitUntilIdle();
#endif
delay_busy(10);
}
void Epd::DisplayFrame_Partial(void) {
SendCommand(0x22);
SendData(0x0F);
SendCommand(0x20);
delay_busy(10);
}
#ifdef EPD_29_V2
void Epd::SetFrameMemory_Partial(const unsigned char* image_buffer, int x, int y, int image_width, int image_height) {
int x_end;
int y_end;
if (
image_buffer == NULL ||
x < 0 || image_width < 0 ||
y < 0 || image_height < 0
) {
return;
}
/* x point must be the multiple of 8 or the last 3 bits will be ignored */
x &= 0xF8;
image_width &= 0xF8;
if (x + image_width >= this->width) {
x_end = this->width - 1;
} else {
x_end = x + image_width - 1;
}
if (y + image_height >= this->height) {
y_end = this->height - 1;
} else {
y_end = y + image_height - 1;
}
if (rst_pin >= 0) {
digitalWrite(rst_pin, LOW);
delay(2);
digitalWrite(rst_pin, HIGH);
delay(2);
} else {
SendCommand(0x12);
}
SetLut(lut_partial_update);
SendCommand(0x37);
SendData(0x00);
SendData(0x00);
SendData(0x00);
SendData(0x00);
SendData(0x00);
SendData(0x40);
SendData(0x00);
SendData(0x00);
SendData(0x00);
SendData(0x00);
SendCommand(0x3C); //BorderWavefrom
SendData(0x80);
SendCommand(0x22);
SendData(0xC0);
SendCommand(0x20);
delay_busy(100);
SetMemoryArea(x, y, x_end, y_end);
SetMemoryPointer(x, y);
SendCommand(0x24);
/* send the image data */
for (int j = 0; j < y_end - y + 1; j++) {
for (int i = 0; i < (x_end - x + 1) / 8; i++) {
SendData(image_buffer[i + j * (image_width / 8)]^0xff);
}
}
}
/**
* @brief: private function to specify the memory area for data R/W
*/
void Epd::SetMemoryArea(int x_start, int y_start, int x_end, int y_end) {
SendCommand(0x44);
/* x point must be the multiple of 8 or the last 3 bits will be ignored */
SendData((x_start >> 3) & 0xFF);
SendData((x_end >> 3) & 0xFF);
SendCommand(0x45);
SendData(y_start & 0xFF);
SendData((y_start >> 8) & 0xFF);
SendData(y_end & 0xFF);
SendData((y_end >> 8) & 0xFF);
}
#else
void Epd::SetFrameMemory_Partial(
const unsigned char* image_buffer,
int x,
int y,
int image_width,
int image_height
) {
}
/**
* @brief: private function to specify the memory area for data R/W
*/
@ -324,7 +523,24 @@ void Epd::SetMemoryArea(int x_start, int y_start, int x_end, int y_end) {
SendData(y_end & 0xFF);
SendData((y_end >> 8) & 0xFF);
}
#endif
#ifdef EPD_29_V2
/**
* @brief: private function to specify the start point for data R/W
*/
void Epd::SetMemoryPointer(int x, int y) {
SendCommand(0x4E);
/* x point must be the multiple of 8 or the last 3 bits will be ignored */
SendData((x >> 3) & 0xFF);
SendCommand(0x4F);
SendData(y & 0xFF);
SendData((y >> 8) & 0xFF);
delay_busy(10);
}
#else
/**
* @brief: private function to specify the start point for data R/W
*/
@ -335,8 +551,9 @@ void Epd::SetMemoryPointer(int x, int y) {
SendCommand(SET_RAM_Y_ADDRESS_COUNTER);
SendData(y & 0xFF);
SendData((y >> 8) & 0xFF);
WaitUntilIdle();
delay_busy(10);
}
#endif
/**
* @brief: After this command is transmitted, the chip would enter the
@ -346,9 +563,59 @@ void Epd::SetMemoryPointer(int x, int y) {
*/
void Epd::Sleep() {
SendCommand(DEEP_SLEEP_MODE);
WaitUntilIdle();
delay_busy(10);
}
#ifdef EPD_29_V2
const unsigned char lut_partial_update[159] =
{
0x0,0x40,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
0x80,0x80,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
0x40,0x40,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
0x0,0x80,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
0x0A,0x0,0x0,0x0,0x0,0x0,0x2,
0x1,0x0,0x0,0x0,0x0,0x0,0x0,
0x1,0x0,0x0,0x0,0x0,0x0,0x0,
0x0,0x0,0x0,0x0,0x0,0x0,0x0,
0x0,0x0,0x0,0x0,0x0,0x0,0x0,
0x0,0x0,0x0,0x0,0x0,0x0,0x0,
0x0,0x0,0x0,0x0,0x0,0x0,0x0,
0x0,0x0,0x0,0x0,0x0,0x0,0x0,
0x0,0x0,0x0,0x0,0x0,0x0,0x0,
0x0,0x0,0x0,0x0,0x0,0x0,0x0,
0x0,0x0,0x0,0x0,0x0,0x0,0x0,
0x0,0x0,0x0,0x0,0x0,0x0,0x0,
0x22,0x22,0x22,0x22,0x22,0x22,0x0,0x0,0x0,
0x22,0x17,0x41,0xB0,0x32,0x36,
};
const unsigned char lut_full_update[159] =
{
0x80, 0x66, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x40, 0x0, 0x0, 0x0,
0x10, 0x66, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x20, 0x0, 0x0, 0x0,
0x80, 0x66, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x40, 0x0, 0x0, 0x0,
0x10, 0x66, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x20, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x14, 0x8, 0x0, 0x0, 0x0, 0x0, 0x1,
0xA, 0xA, 0x0, 0xA, 0xA, 0x0, 0x1,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x14, 0x8, 0x0, 0x1, 0x0, 0x0, 0x1,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x0, 0x0, 0x0,
0x22, 0x17, 0x41, 0x0, 0x32, 0x36
};
#else
const unsigned char lut_full_update[] =
{
0x02, 0x02, 0x01, 0x11, 0x12, 0x12, 0x22, 0x22,
@ -364,7 +631,7 @@ const unsigned char lut_partial_update[] =
0x00, 0x00, 0x00, 0x00, 0x13, 0x14, 0x44, 0x12,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00
};
#endif // EPD_29_V2
#define PIN_OUT_SET 0x60000304
#define PIN_OUT_CLEAR 0x60000308

View File

@ -30,6 +30,10 @@
#include "epdpaint.h"
#define DISPLAY_INIT_MODE 0
#define DISPLAY_INIT_PARTIAL 1
#define DISPLAY_INIT_FULL 2
// Display resolution
#define EPD_WIDTH 128
#define EPD_HEIGHT 296
@ -91,21 +95,27 @@ public:
void DisplayOnff(int8_t on);
void DisplayInit(int8_t p,int8_t size,int8_t rot,int8_t font);
void Begin(int16_t p1,int16_t p2,int16_t p3);
void Begin(int16_t cs,int16_t mosi,int16_t sclk, int16_t rst = -1, int16_t busy = -1);
void Updateframe();
private:
unsigned int reset_pin;
unsigned int dc_pin;
unsigned int busy_pin;
const unsigned char* lut;
unsigned int cs_pin;
signed int rst_pin;
signed int busy_pin;
unsigned int mosi_pin;
unsigned int sclk_pin;
unsigned char mode;
void delay_busy(uint32_t wait);
void SetLut(const unsigned char* lut);
void SetMemoryArea(int x_start, int y_start, int x_end, int y_end);
void SetMemoryPointer(int x, int y);
void SetLut_by_host(const unsigned char* lut);
void SetFrameMemory_Partial(const unsigned char* image_buffer,int x,int y,int image_width,int image_height);
void DisplayFrame_Partial(void);
//void fastSPIwrite(uint8_t d,uint8_t dc);
};

290
lib/lib_display/GT911/GT911.cpp Executable file
View File

@ -0,0 +1,290 @@
#include <Arduino.h>
#include <stdint.h>
#include "GT911.h"
#undef log_d
#define log_d
#undef log_e
#define log_e
#ifdef ESP8266
#define ESP_OK 0
#define ESP_FAIL -1
#endif
//#define log_d Serial.printf
GT911::GT911() {}
volatile uint8_t gt911_irq_trigger = 0;
void ICACHE_RAM_ATTR ___GT911IRQ___()
{
noInterrupts();
gt911_irq_trigger = 1;
interrupts();
}
int32_t GT911::begin(TwoWire *use_wire, int8_t pin_int, int8_t pin_res, uint16_t xs, uint16_t ys)
{
log_d("GT911: Initialization");
if (pin_int >= 0) {
pinMode(pin_int, INPUT); // Startup sequence PIN part
}
if (pin_res >= 0) {
pinMode(pin_res, OUTPUT); // Startup sequence PIN part
digitalWrite(pin_res, 0);
delay(1);
digitalWrite(pin_res, 1);
}
delay(100);
wire = use_wire;
wire->beginTransmission(0x14);
if (wire->endTransmission())
{
wire->beginTransmission(0x5D);
if (wire->endTransmission())
{
log_e("Touch screen IIC connection error");
return ESP_FAIL;
}
_iic_addr = 0x5D;
}
if (pin_int >= 0) {
attachInterrupt(pin_int, ___GT911IRQ___, FALLING);
}
readBlockData(configBuf, GT911_CONFIG_START, GT911_CONFIG_SIZE);
uint16_t curx = configBuf[GT911_X_OUTPUT_MAX_LOW - GT911_CONFIG_START] | (configBuf[GT911_X_OUTPUT_MAX_HIGH - GT911_CONFIG_START] << 8);
uint16_t cury = configBuf[GT911_Y_OUTPUT_MAX_LOW - GT911_CONFIG_START] | (configBuf[GT911_Y_OUTPUT_MAX_HIGH - GT911_CONFIG_START] << 8);
if (curx != xs || cury != ys) {
setResolution(xs, ys);
}
log_d("GT911: initialized");
return ESP_OK;
}
void GT911::write(uint16_t addr, uint8_t data)
{
wire->beginTransmission(_iic_addr);
wire->write((uint8_t)(addr >> 8));
wire->write((uint8_t)addr);
wire->write(data);
wire->endTransmission(true);
}
void GT911::write(uint16_t addr, const uint8_t *data, uint16_t len)
{
wire->beginTransmission(_iic_addr);
wire->write((uint8_t)(addr >> 8));
wire->write((uint8_t)addr);
wire->write(data, len);
wire->endTransmission(true);
}
uint8_t GT911::read(uint16_t addr)
{
wire->flush();
wire->beginTransmission(_iic_addr);
wire->write((uint8_t)(addr >> 8));
wire->write((uint8_t)addr);
wire->endTransmission(false);
wire->requestFrom((uint8_t)_iic_addr, (uint8_t)1);
return wire->read();
}
void GT911::read(uint16_t addr, uint8_t *buf, uint16_t len)
{
wire->flush();
wire->beginTransmission(_iic_addr);
wire->write((uint8_t)(addr >> 8));
wire->write((uint8_t)addr);
wire->endTransmission(false);
wire->requestFrom((int)_iic_addr, (int)len);
wire->readBytes(buf, len);
}
void GT911::readBlockData(uint8_t *buf, uint16_t reg, uint8_t size) {
wire->beginTransmission(_iic_addr);
wire->write(highByte(reg));
wire->write(lowByte(reg));
wire->endTransmission();
wire->requestFrom(_iic_addr, size);
for (uint8_t i = 0; i < size; i++) {
buf[i] = wire->read();
}
}
void GT911::calculateChecksum() {
uint8_t checksum = 0;
for (uint8_t i = 0; i < GT911_CONFIG_SIZE - 1 ; i++) {
checksum += configBuf[i];
}
checksum = (~checksum) + 1;
configBuf[GT911_CONFIG_CHKSUM - GT911_CONFIG_START] = checksum;
}
void GT911::reflashConfig() {
calculateChecksum();
write(GT911_X_OUTPUT_MAX_LOW, configBuf[GT911_X_OUTPUT_MAX_LOW - GT911_CONFIG_START]);
write(GT911_X_OUTPUT_MAX_HIGH, configBuf[GT911_X_OUTPUT_MAX_HIGH - GT911_CONFIG_START]);
write(GT911_Y_OUTPUT_MAX_LOW, configBuf[GT911_Y_OUTPUT_MAX_LOW - GT911_CONFIG_START]);
write(GT911_Y_OUTPUT_MAX_HIGH, configBuf[GT911_Y_OUTPUT_MAX_HIGH - GT911_CONFIG_START]);
write(GT911_CONFIG_CHKSUM, configBuf[GT911_CONFIG_CHKSUM - GT911_CONFIG_START]);
write(GT911_CONFIG_FRESH, 1);
}
void GT911::setResolution(uint16_t _width, uint16_t _height) {
configBuf[GT911_X_OUTPUT_MAX_LOW - GT911_CONFIG_START] = lowByte(_width);
configBuf[GT911_X_OUTPUT_MAX_HIGH - GT911_CONFIG_START] = highByte(_width);
configBuf[GT911_Y_OUTPUT_MAX_LOW - GT911_CONFIG_START] = lowByte(_height);
configBuf[GT911_Y_OUTPUT_MAX_HIGH - GT911_CONFIG_START] = highByte(_height);
reflashConfig();
}
bool GT911::avaliable()
{
if(gt911_irq_trigger == 1)
{
gt911_irq_trigger = 0;
return true;
}
return false;
}
void GT911::flush(void)
{
write(0x814E, 0x00);
gt911_irq_trigger = 0;
_num = 0;
_is_finger_up = 0;
}
void GT911::update()
{
uint8_t r814e = read(0x814E);
uint8_t num = r814e & 0x0F;
if(r814e & 0x80)
{
if(num != 0)
{
_is_finger_up = false;
_num = num;
uint8_t data[num * 8];
read(0x8150, data, num * 8);
for(int j = 0; j < num; j++)
{
uint8_t *buf = data + j * 8;
if(_rotate == ROTATE_0)
{
_fingers[j].x = (buf[3] << 8) | buf[2];
_fingers[j].y = 540 - ((buf[1] << 8) | buf[0]);
}
else if(_rotate == ROTATE_180)
{
_fingers[j].x = 960 - ((buf[3] << 8) | buf[2]);
_fingers[j].y = (buf[1] << 8) | buf[0];
}
else if(_rotate == ROTATE_270)
{
_fingers[j].x = 540 - ((buf[1] << 8) | buf[0]);
_fingers[j].y = 960 - ((buf[3] << 8) | buf[2]);
}
else
{
_fingers[j].x = (buf[1] << 8) | buf[0];
_fingers[j].y = (buf[3] << 8) | buf[2];
}
_fingers[j].size = (buf[5] << 8) | buf[4];
_fingers[j].id = buf[7];
}
}
else
{
_is_finger_up = true;
}
write(0x814E, 0x00);
}
else
{
_is_finger_up = 1;
}
}
bool GT911::isFingerUp(void)
{
if(_is_finger_up == 1)
{
_is_finger_up = 0;
return true;
}
return false;
}
void GT911::SetRotation(uint16_t rotate)
{
if(rotate < 4)
{
this->_rotate = rotate;
}
else if(rotate < 90)
{
this->_rotate = ROTATE_0;
}
else if(rotate < 180)
{
this->_rotate = ROTATE_90;
}
else if(rotate < 270)
{
this->_rotate = ROTATE_180;
}
else
{
this->_rotate = ROTATE_270;
}
}
tp_finger_t GT911::readFinger(uint8_t num)
{
if(num > 2)
{
num = 1;
}
return this->_fingers[num];
}
uint16_t GT911::readFingerID(uint8_t num)
{
return this->_fingers[num].id;
}
uint16_t GT911::readFingerSize(uint8_t num)
{
return this->_fingers[num].size;
}
uint16_t GT911::readFingerX(uint8_t num)
{
return this->_fingers[num].x;
}
uint16_t GT911::readFingerY(uint8_t num)
{
return this->_fingers[num].y;
}
uint8_t GT911::getFingerNum(void)
{
return _num;
}

179
lib/lib_display/GT911/GT911.h Executable file
View File

@ -0,0 +1,179 @@
#ifndef GT911_H
#define GT911_H
#include <stdint.h>
#include <Wire.h>
// Real-time command (Write only)
#define GT911_COMMAND (uint16_t)0x8040
#define GT911_ESD_CHECK (uint16_t)0x8041
#define GT911_COMMAND_CHECK (uint16_t)0x8046
// Configuration information (R/W)
#define GT911_CONFIG_START (uint16_t)0x8047
#define GT911_CONFIG_VERSION (uint16_t)0x8047
#define GT911_X_OUTPUT_MAX_LOW (uint16_t)0x8048
#define GT911_X_OUTPUT_MAX_HIGH (uint16_t)0x8049
#define GT911_Y_OUTPUT_MAX_LOW (uint16_t)0x804A
#define GT911_Y_OUTPUT_MAX_HIGH (uint16_t)0x804B
#define GT911_TOUCH_NUMBER (uint16_t)0x804C
#define GT911_MODULE_SWITCH_1 (uint16_t)0x804D
#define GT911_MODULE_SWITCH_2 (uint16_t)0x804E
#define GT911_SHAKE_COUNT (uint16_t)0x804F
#define GT911_FILTER (uint16_t)0x8050
#define GT911_LARGE_TOUCH (uint16_t)0x8051
#define GT911_NOISE_REDUCTION (uint16_t)0x8052
#define GT911_SCREEN_TOUCH_LEVEL (uint16_t)0x8053
#define GT911_SCREEN_RELEASE_LEVEL (uint16_t)0x8054
#define GT911_LOW_POWER_CONTROL (uint16_t)0x8055
#define GT911_REFRESH_RATE (uint16_t)0x8056
#define GT911_X_THRESHOLD (uint16_t)0x8057
#define GT911_Y_THRESHOLD (uint16_t)0x8058
#define GT911_X_SPEED_LIMIT (uint16_t)0x8059 //Reserve
#define GT911_Y_SPEED_LIMIT (uint16_t)0x805A //Reserve
#define GT911_SPACE_TOP_BOTTOM (uint16_t)0x805B
#define GT911_SPACE_LEFT_RIGHT (uint16_t)0x805C
#define GT911_MINI_FILTER (uint16_t)0x805D
#define GT911_STRETCH_R0 (uint16_t)0x805E
#define GT911_STRETCH_R1 (uint16_t)0x805F
#define GT911_STRETCH_R2 (uint16_t)0x8060
#define GT911_STRETCH_RM (uint16_t)0x8061
#define GT911_DRV_GROUPA_NUM (uint16_t)0x8062
#define GT911_DRV_GROUPB_NUM (uint16_t)0x8063
#define GT911_SENSOR_NUM (uint16_t)0x8064
#define GT911_FREQ_A_FACTOR (uint16_t)0x8065
#define GT911_FREQ_B_FACTOR (uint16_t)0x8066
#define GT911_PANEL_BIT_FREQ_L (uint16_t)0x8067
#define GT911_PANEL_BIT_FREQ_H (uint16_t)0x8068
#define GT911_PANEL_SENSOR_TIME_L (uint16_t)0x8069 //Reserve
#define GT911_PANEL_SENSOR_TIME_H (uint16_t)0x806A
#define GT911_PANEL_TX_GAIN (uint16_t)0x806B
#define GT911_PANEL_RX_GAIN (uint16_t)0x806C
#define GT911_PANEL_DUMP_SHIFT (uint16_t)0x806D
#define GT911_DRV_FRAME_CONTROL (uint16_t)0x806E
#define GT911_CHARGING_LEVEL_UP (uint16_t)0x806F
#define GT911_MODULE_SWITCH3 (uint16_t)0x8070
#define GT911_GESTURE_DIS (uint16_t)0X8071
#define GT911_GESTURE_LONG_PRESS_TIME (uint16_t)0x8072
#define GT911_X_Y_SLOPE_ADJUST (uint16_t)0X8073
#define GT911_GESTURE_CONTROL (uint16_t)0X8074
#define GT911_GESTURE_SWITCH1 (uint16_t)0X8075
#define GT911_GESTURE_SWITCH2 (uint16_t)0X8076
#define GT911_GESTURE_REFRESH_RATE (uint16_t)0x8077
#define GT911_GESTURE_TOUCH_LEVEL (uint16_t)0x8078
#define GT911_NEWGREENWAKEUPLEVEL (uint16_t)0x8079
#define GT911_FREQ_HOPPING_START (uint16_t)0x807A
#define GT911_FREQ_HOPPING_END (uint16_t)0X807B
#define GT911_NOISE_DETECT_TIMES (uint16_t)0x807C
#define GT911_HOPPING_FLAG (uint16_t)0X807D
#define GT911_HOPPING_THRESHOLD (uint16_t)0X807E
#define GT911_NOISE_THRESHOLD (uint16_t)0X807F //Reserve
#define GT911_NOISE_MIN_THRESHOLD (uint16_t)0X8080
#define GT911_HOPPING_SENSOR_GROUP (uint16_t)0X8082
#define GT911_HOPPING_SEG1_NORMALIZE (uint16_t)0X8083
#define GT911_HOPPING_SEG1_FACTOR (uint16_t)0X8084
#define GT911_MAIN_CLOCK_AJDUST (uint16_t)0X8085
#define GT911_HOPPING_SEG2_NORMALIZE (uint16_t)0X8086
#define GT911_HOPPING_SEG2_FACTOR (uint16_t)0X8087
#define GT911_HOPPING_SEG3_NORMALIZE (uint16_t)0X8089
#define GT911_HOPPING_SEG3_FACTOR (uint16_t)0X808A
#define GT911_HOPPING_SEG4_NORMALIZE (uint16_t)0X808C
#define GT911_HOPPING_SEG4_FACTOR (uint16_t)0X808D
#define GT911_HOPPING_SEG5_NORMALIZE (uint16_t)0X808F
#define GT911_HOPPING_SEG5_FACTOR (uint16_t)0X8090
#define GT911_HOPPING_SEG6_NORMALIZE (uint16_t)0X8092
#define GT911_KEY_1 (uint16_t)0X8093
#define GT911_KEY_2 (uint16_t)0X8094
#define GT911_KEY_3 (uint16_t)0X8095
#define GT911_KEY_4 (uint16_t)0X8096
#define GT911_KEY_AREA (uint16_t)0X8097
#define GT911_KEY_TOUCH_LEVEL (uint16_t)0X8098
#define GT911_KEY_LEAVE_LEVEL (uint16_t)0X8099
#define GT911_KEY_SENS_1_2 (uint16_t)0X809A
#define GT911_KEY_SENS_3_4 (uint16_t)0X809B
#define GT911_KEY_RESTRAIN (uint16_t)0X809C
#define GT911_KEY_RESTRAIN_TIME (uint16_t)0X809D
#define GT911_GESTURE_LARGE_TOUCH (uint16_t)0X809E
#define GT911_HOTKNOT_NOISE_MAP (uint16_t)0X80A1
#define GT911_LINK_THRESHOLD (uint16_t)0X80A2
#define GT911_PXY_THRESHOLD (uint16_t)0X80A3
#define GT911_GHOT_DUMP_SHIFT (uint16_t)0X80A4
#define GT911_GHOT_RX_GAIN (uint16_t)0X80A5
#define GT911_FREQ_GAIN0 (uint16_t)0X80A6
#define GT911_FREQ_GAIN1 (uint16_t)0X80A7
#define GT911_FREQ_GAIN2 (uint16_t)0X80A8
#define GT911_FREQ_GAIN3 (uint16_t)0X80A9
#define GT911_COMBINE_DIS (uint16_t)0X80B3
#define GT911_SPLIT_SET (uint16_t)0X80B4
#define GT911_SENSOR_CH0 (uint16_t)0X80B7
#define GT911_DRIVER_CH0 (uint16_t)0X80D5
#define GT911_CONFIG_CHKSUM (uint16_t)0X80FF
#define GT911_CONFIG_FRESH (uint16_t)0X8100
#define GT911_CONFIG_SIZE (uint16_t)0xFF-0x46
// Coordinate information
#define GT911_PRODUCT_ID (uint16_t)0X8140
#define GT911_FIRMWARE_VERSION (uint16_t)0X8140
#define GT911_RESOLUTION (uint16_t)0X8140
#define GT911_VENDOR_ID (uint16_t)0X8140
#define GT911_IMFORMATION (uint16_t)0X8140
#define GT911_POINT_INFO (uint16_t)0X814E
#define GT911_POINT_1 (uint16_t)0X814F
#define GT911_POINT_2 (uint16_t)0X8157
#define GT911_POINT_3 (uint16_t)0X815F
#define GT911_POINT_4 (uint16_t)0X8167
#define GT911_POINT_5 (uint16_t)0X816F
#define GT911_POINTS_REG {GT911_POINT_1, GT911_POINT_2, GT911_POINT_3, GT911_POINT_4, GT911_POINT_5}
typedef struct
{
uint16_t x;
uint16_t y;
uint16_t id;
uint16_t size;
}tp_finger_t;
class GT911
{
public:
static const uint8_t ROTATE_0 = 0;
static const uint8_t ROTATE_90 = 1;
static const uint8_t ROTATE_180 = 2;
static const uint8_t ROTATE_270 = 3;
public:
GT911();
int32_t begin(TwoWire *use_wire, int8_t pin_int, int8_t pin_res, uint16_t xs, uint16_t ys);
bool avaliable();
void update();
void SetRotation(uint16_t rotate);
tp_finger_t readFinger(uint8_t num);
uint16_t readFingerX(uint8_t num);
uint16_t readFingerY(uint8_t num);
uint16_t readFingerID(uint8_t num);
uint16_t readFingerSize(uint8_t num);
uint8_t getFingerNum(void);
bool isFingerUp(void);
void flush(void);
private:
void write(uint16_t addr, uint8_t data);
void write(uint16_t addr, const uint8_t *data, uint16_t len);
uint8_t read(uint16_t addr);
void read(uint16_t addr, uint8_t *buf, uint16_t len);
uint8_t calcChecksum(const uint8_t *buf, uint8_t len);
void reflashConfig();
void calculateChecksum();
void setResolution(uint16_t _width, uint16_t _height);
void readBlockData(uint8_t *buf, uint16_t reg, uint8_t size);
bool _is_finger_up = false;
uint8_t _num = 0;
uint8_t _rotate = ROTATE_90;
tp_finger_t _fingers[2];
uint8_t _iic_addr = 0x14;
TwoWire *wire;
uint8_t configBuf[GT911_CONFIG_SIZE];
};
#endif

File diff suppressed because it is too large Load Diff

View File

@ -31,16 +31,25 @@ static inline volatile uint32_t* get_gpio_lo_reg(int_fast8_t pin) { return (pin
static inline bool gpio_in(int_fast8_t pin) { return ((pin & 32) ? GPIO.in1.data : GPIO.in) & (1 << (pin & 31)); }
static inline void gpio_hi(int_fast8_t pin) { if (pin >= 0) *get_gpio_hi_reg(pin) = 1 << (pin & 31); } // ESP_LOGI("LGFX", "gpio_hi: %d", pin); }
static inline void gpio_lo(int_fast8_t pin) { if (pin >= 0) *get_gpio_lo_reg(pin) = 1 << (pin & 31); } // ESP_LOGI("LGFX", "gpio_lo: %d", pin); }
#endif
#include "esp_lcd_panel_interface.h"
#include "esp_lcd_panel_rgb.h"
#include "esp_pm.h"
#include "esp_lcd_panel_ops.h"
#include <hal/dma_types.h>
#include <rom/cache.h>
#endif // USE_ESP32_S3
#define _UDSP_I2C 1
#define _UDSP_SPI 2
#define _UDSP_PAR8 3
#define _UDSP_PAR16 4
#define _UDSP_RGB 5
#define UDISP1_WHITE 1
#define UDISP1_BLACK 0
#define MAX_LUTS 5
#define DISPLAY_INIT_MODE 0
#define DISPLAY_INIT_PARTIAL 1
#define DISPLAY_INIT_FULL 2
@ -100,7 +109,6 @@ enum uColorType { uCOLOR_BW, uCOLOR_COLOR };
#define SPI_DC_LOW if (spi_dc >= 0) GPIO_CLR_SLOW(spi_dc);
#define SPI_DC_HIGH if (spi_dc >= 0) GPIO_SET_SLOW(spi_dc);
#define LUTMAXSIZE 64
#ifdef USE_ESP32_S3
struct esp_lcd_i80_bus_t {
@ -115,6 +123,39 @@ struct esp_lcd_i80_bus_t {
size_t resolution_hz; // LCD_CLK resolution, determined by selected clock source
gdma_channel_handle_t dma_chan; // DMA channel handle
};
// extract from esp-idf esp_lcd_rgb_panel.c
struct esp_rgb_panel_t
{
esp_lcd_panel_t base; // Base class of generic lcd panel
int panel_id; // LCD panel ID
lcd_hal_context_t hal; // Hal layer object
size_t data_width; // Number of data lines (e.g. for RGB565, the data width is 16)
size_t sram_trans_align; // Alignment for framebuffer that allocated in SRAM
size_t psram_trans_align; // Alignment for framebuffer that allocated in PSRAM
int disp_gpio_num; // Display control GPIO, which is used to perform action like "disp_off"
intr_handle_t intr; // LCD peripheral interrupt handle
esp_pm_lock_handle_t pm_lock; // Power management lock
size_t num_dma_nodes; // Number of DMA descriptors that used to carry the frame buffer
uint8_t *fb; // Frame buffer
size_t fb_size; // Size of frame buffer
int data_gpio_nums[SOC_LCD_RGB_DATA_WIDTH]; // GPIOs used for data lines, we keep these GPIOs for action like "invert_color"
size_t resolution_hz; // Peripheral clock resolution
esp_lcd_rgb_timing_t timings; // RGB timing parameters (e.g. pclk, sync pulse, porch width)
gdma_channel_handle_t dma_chan; // DMA channel handle
esp_lcd_rgb_panel_frame_trans_done_cb_t on_frame_trans_done; // Callback, invoked after frame trans done
void *user_ctx; // Reserved user's data of callback functions
int x_gap; // Extra gap in x coordinate, it's used when calculate the flush window
int y_gap; // Extra gap in y coordinate, it's used when calculate the flush window
struct
{
unsigned int disp_en_level : 1; // The level which can turn on the screen by `disp_gpio_num`
unsigned int stream_mode : 1; // If set, the LCD transfers data continuously, otherwise, it stops refreshing the LCD when transaction done
unsigned int fb_in_psram : 1; // Whether the frame buffer is in PSRAM
} flags;
dma_descriptor_t dma_nodes[]; // DMA descriptor pool of size `num_dma_nodes`
};
#endif
@ -205,7 +246,7 @@ class uDisplay : public Renderer {
uint8_t i2c_page_start;
uint8_t i2c_page_end;
int8_t reset;
uint8_t dsp_cmds[128];
uint8_t dsp_cmds[256];
uint8_t dsp_ncmds;
uint8_t dsp_on;
uint8_t dsp_off;
@ -249,16 +290,28 @@ class uDisplay : public Renderer {
uint8_t dim_op;
uint8_t lutfsize;
uint8_t lutpsize;
uint16_t lutftime;
int16_t lutftime;
int8_t busy_pin;
uint16_t lutptime;
uint16_t lut3time;
uint16_t lut_num;
uint8_t ep_mode;
uint8_t lut_full[LUTMAXSIZE];
uint8_t lut_partial[LUTMAXSIZE];
uint8_t lut_array[LUTMAXSIZE][5];
uint8_t lut_cnt[5];
uint8_t lut_cmd[5];
uint8_t ep_update_mode;
uint8_t *lut_full;
uint8_t lut_siz_full;
uint8_t *lut_partial;
uint8_t lut_siz_partial;
uint8_t *frame_buffer;
uint8_t epcoffs_full;
uint8_t epc_full_cnt;
uint8_t epcoffs_part;
uint8_t epc_part_cnt;
uint8_t *lut_array[MAX_LUTS];
uint8_t lut_cnt[MAX_LUTS];
uint8_t lut_cmd[MAX_LUTS];
uint8_t lut_siz[MAX_LUTS];
uint16_t seta_xp1;
uint16_t seta_xp2;
uint16_t seta_yp1;
@ -268,6 +321,11 @@ class uDisplay : public Renderer {
int16_t rotmap_ymin;
int16_t rotmap_ymax;
void pushColorsMono(uint16_t *data, uint16_t len, bool rgb16_swap = false);
void delay_sync(int32_t time);
void reset_pin(int32_t delayl, int32_t delayh);
void delay_arg(uint32_t arg);
void Send_EP_Data(void);
void send_spi_cmds(uint16_t cmd_offset, uint16_t cmd_size);
#ifdef USE_ESP32_S3
int8_t par_cs;
@ -278,6 +336,26 @@ class uDisplay : public Renderer {
int8_t par_dbl[8];
int8_t par_dbh[8];
int8_t de;
int8_t vsync;
int8_t hsync;
int8_t pclk;
uint16_t hsync_polarity;
uint16_t hsync_front_porch;
uint16_t hsync_pulse_width;
uint16_t hsync_back_porch;
uint16_t vsync_polarity;
uint16_t vsync_front_porch;
uint16_t vsync_pulse_width;
uint16_t vsync_back_porch;
uint16_t pclk_active_neg;
esp_lcd_panel_handle_t _panel_handle = NULL;
esp_rgb_panel_t *_rgb_panel;
uint16_t *rgb_fb;
esp_lcd_i80_bus_handle_t _i80_bus = nullptr;
gdma_channel_handle_t _dma_chan;
lldesc_t *_dmadesc = nullptr;
@ -304,6 +382,7 @@ class uDisplay : public Renderer {
uint8_t _align_data;
void cs_control(bool level);
uint32_t get_sr_touch(uint32_t xp, uint32_t xm, uint32_t yp, uint32_t ym);
void drawPixel_RGB(int16_t x, int16_t y, uint16_t color);
#endif
#ifdef ESP32

25
lib/lib_div/ams/Cosem.cpp Normal file
View File

@ -0,0 +1,25 @@
#include "Cosem.h"
#include "lwip/def.h"
#include "TimeLib.h"
time_t decodeCosemDateTime(CosemDateTime timestamp) {
tmElements_t tm;
uint16_t year = ntohs(timestamp.year);
if(year < 1970) return 0;
tm.Year = year - 1970;
tm.Month = timestamp.month;
tm.Day = timestamp.dayOfMonth;
tm.Hour = timestamp.hour;
tm.Minute = timestamp.minute;
tm.Second = timestamp.second;
//Serial.printf("\nY: %d, M: %d, D: %d, h: %d, m: %d, s: %d, deviation: 0x%2X, status: 0x%1X\n", tm.Year, tm.Month, tm.Day, tm.Hour, tm.Minute, tm.Second, timestamp.deviation, timestamp.status);
time_t time = makeTime(tm);
int16_t deviation = ntohs(timestamp.deviation);
if(deviation >= -720 && deviation <= 720) {
time -= deviation * 60;
}
return time;
}

92
lib/lib_div/ams/Cosem.h Normal file
View File

@ -0,0 +1,92 @@
#ifndef _COSEM_H
#define _COSEM_H
#include "lwip/def.h"
// Blue book, Table 2
enum CosemType {
CosemTypeNull = 0x00,
CosemTypeArray = 0x01,
CosemTypeStructure = 0x02,
CosemTypeOctetString = 0x09,
CosemTypeString = 0x0A,
CosemTypeDLongSigned = 0x05,
CosemTypeDLongUnsigned = 0x06,
CosemTypeLongSigned = 0x10,
CosemTypeLongUnsigned = 0x12,
CosemTypeLong64Signed = 0x14,
CosemTypeLong64Unsigned = 0x15,
CosemTypeDateTime = 0x19
};
struct CosemBasic {
uint8_t type;
uint8_t length;
} __attribute__((packed));
struct CosemString {
uint8_t type;
uint8_t length;
uint8_t data[];
} __attribute__((packed));
struct CosemLongSigned {
uint8_t type;
int16_t data;
} __attribute__((packed));
struct CosemLongUnsigned {
uint8_t type;
uint16_t data;
} __attribute__((packed));
struct CosemDLongSigned {
uint8_t type;
int32_t data;
} __attribute__((packed));
struct CosemDLongUnsigned {
uint8_t type;
uint32_t data;
} __attribute__((packed));
struct CosemLong64Signed {
uint8_t type;
int64_t data;
} __attribute__((packed));
struct CosemLong64Unsigned {
uint8_t type;
uint64_t data;
} __attribute__((packed));
struct CosemDateTime {
uint8_t type;
uint16_t year;
uint8_t month;
uint8_t dayOfMonth;
uint8_t dayOfWeek;
uint8_t hour;
uint8_t minute;
uint8_t second;
uint8_t hundredths;
int16_t deviation;
uint8_t status;
} __attribute__((packed));
typedef union {
struct CosemBasic base;
struct CosemString str;
struct CosemString oct;
struct CosemLongSigned ls;
struct CosemLongUnsigned lu;
struct CosemDLongSigned dls;
struct CosemDLongUnsigned dlu;
struct CosemLong64Signed l64s;
struct CosemLong64Unsigned l64u;
struct CosemDateTime dt;
} CosemData;
time_t decodeCosemDateTime(CosemDateTime timestamp);
#endif

View File

@ -0,0 +1,31 @@
#ifndef _DATAPASERSER_H
#define _DATAPASERSER_H
#define DATA_TAG_NONE 0x00
#define DATA_TAG_AUTO 0x01
#define DATA_TAG_HDLC 0x7E
#define DATA_TAG_LLC 0xE6
#define DATA_TAG_DLMS 0x0F
#define DATA_TAG_DSMR 0x2F
#define DATA_TAG_MBUS 0x68
#define DATA_TAG_GBT 0xE0
#define DATA_TAG_GCM 0xDB
#define DATA_PARSE_OK 0
#define DATA_PARSE_FAIL -1
#define DATA_PARSE_INCOMPLETE -2
#define DATA_PARSE_BOUNDRY_FLAG_MISSING -3
#define DATA_PARSE_HEADER_CHECKSUM_ERROR -4
#define DATA_PARSE_FOOTER_CHECKSUM_ERROR -5
#define DATA_PARSE_INTERMEDIATE_SEGMENT -6
#define DATA_PARSE_FINAL_SEGMENT -7
#define DATA_PARSE_UNKNOWN_DATA -9
struct DataParserContext {
uint8_t type;
uint16_t length;
time_t timestamp;
uint8_t system_title[8];
};
#endif

View File

@ -0,0 +1,13 @@
#ifndef _DATAPASERSERS_H
#define _DATAPASERSERS_H
#include "HdlcParser.h"
#include "DlmsParser.h"
#include "DsmrParser.h"
#include "MbusParser.h"
#include "GbtParser.h"
#include "GcmParser.h"
#include "LlcParser.h"
#endif

View File

@ -0,0 +1,38 @@
#include "DlmsParser.h"
#include "Cosem.h"
int8_t DLMSParser::parse(uint8_t *buf, DataParserContext &ctx) {
if(ctx.length < 6) return DATA_PARSE_INCOMPLETE;
uint8_t* ptr = buf+1;
ptr += 4; // Skip invoke ID and priority
CosemData* item = (CosemData*) ptr;
if(item->base.type == CosemTypeOctetString) {
if(item->base.length == 0x0C) {
CosemDateTime* dateTime = (CosemDateTime*) (ptr+1);
ctx.timestamp = decodeCosemDateTime(*dateTime);
}
uint8_t len = 5+14;
ctx.length -= len;
return len;
} else if(item->base.type == CosemTypeNull) {
ctx.timestamp = 0;
uint8_t len = 5+1;
ctx.length -= len;
return len;
} else if(item->base.type == CosemTypeDateTime) {
CosemDateTime* dateTime = (CosemDateTime*) (ptr);
ctx.timestamp = decodeCosemDateTime(*dateTime);
uint8_t len = 5+13;
ctx.length -= len;
return len;
} else if(item->base.type == 0x0C) { // Kamstrup bug...
CosemDateTime* dateTime = (CosemDateTime*) (ptr);
ctx.timestamp = decodeCosemDateTime(*dateTime);
uint8_t len = 5+13;
ctx.length -= len;
return len;
}
return DATA_PARSE_UNKNOWN_DATA;
}

View File

@ -0,0 +1,12 @@
#ifndef _DLMSPARSER_H
#define _DLMSPARSER_H
#include "Arduino.h"
#include "DataParser.h"
class DLMSParser {
public:
int8_t parse(uint8_t *buf, DataParserContext &ctx);
};
#endif

View File

@ -0,0 +1,29 @@
#include "DsmrParser.h"
#include "crc.h"
#include "hexutils.h"
#include "lwip/def.h"
int8_t DSMRParser::parse(uint8_t *buf, DataParserContext &ctx, bool verified) {
uint16_t crcPos = 0;
bool reachedEnd = verified;
uint8_t lastByte = 0x00;
for(int pos = 0; pos < ctx.length; pos++) {
uint8_t b = *(buf+pos);
if(pos == 0 && b != '/') return DATA_PARSE_BOUNDRY_FLAG_MISSING;
if(pos > 0 && b == '!' && lastByte == '\n') crcPos = pos+1;
if(crcPos > 0 && b == '\n') reachedEnd = true;
lastByte = b;
}
if(!reachedEnd) return DATA_PARSE_INCOMPLETE;
buf[ctx.length+1] = '\0';
if(crcPos > 0) {
uint16_t crc_calc = AMS_crc16(buf, crcPos);
uint16_t crc = 0x0000;
AMS_fromHex((uint8_t*) &crc, String((char*) buf+crcPos), 2);
crc = ntohs(crc);
if(crc != crc_calc)
return DATA_PARSE_FOOTER_CHECKSUM_ERROR;
}
return DATA_PARSE_OK;
}

View File

@ -0,0 +1,13 @@
#ifndef _DSMRPARSER_H
#define _DSMRPARSER_H
#include "Arduino.h"
#include "DataParser.h"
class DSMRParser {
public:
int8_t parse(uint8_t *buf, DataParserContext &ctx, bool verified);
private:
};
#endif

View File

@ -0,0 +1,36 @@
#include "GbtParser.h"
#include "lwip/def.h"
GBTParser::~GBTParser(void) {
if (buf) free(buf);
}
int8_t GBTParser::parse(uint8_t *d, DataParserContext &ctx) {
GBTHeader* h = (GBTHeader*) (d);
uint16_t sequence = ntohs(h->sequence);
if(h->flag != GBT_TAG) return DATA_PARSE_BOUNDRY_FLAG_MISSING;
if(sequence == 1) {
if(buf == NULL) buf = (uint8_t *)malloc((size_t)1024); // TODO find out from first package ?
pos = 0;
} else if(lastSequenceNumber != sequence-1) {
return DATA_PARSE_FAIL;
}
if(buf == NULL) return DATA_PARSE_FAIL;
uint8_t* ptr = (uint8_t*) &h[1];
memcpy(buf + pos, ptr, h->size);
pos += h->size;
lastSequenceNumber = sequence;
if((h->control & 0x80) == 0x00) {
return DATA_PARSE_INTERMEDIATE_SEGMENT;
} else {
memcpy((uint8_t *) d, buf, pos);
}
ctx.length = pos;
return DATA_PARSE_OK;
}

View File

@ -0,0 +1,27 @@
#ifndef _GBTPARSER_H
#define _GBTPARSER_H
#include "Arduino.h"
#include "DataParser.h"
#define GBT_TAG 0xE0
typedef struct GBTHeader {
uint8_t flag;
uint8_t control;
uint16_t sequence;
uint16_t sequenceAck;
uint8_t size;
} __attribute__((packed)) GBTHeader;
class GBTParser {
public:
int8_t parse(uint8_t *buf, DataParserContext &ctx);
~GBTParser(void);
private:
uint8_t lastSequenceNumber = 0;
uint16_t pos = 0;
uint8_t *buf = NULL;
};
#endif

View File

@ -0,0 +1,104 @@
#include "GcmParser.h"
#include "tasmota_options.h"
#ifdef USE_TLS
#include "lwip/def.h"
#include <t_bearssl.h>
GCMParser::GCMParser(uint8_t *encryption_key, uint8_t *authentication_key) {
memcpy(this->encryption_key, encryption_key, 16);
memcpy(this->authentication_key, authentication_key, 16);
use_auth = 0;
for (uint16_t cnt = 0; cnt < 16; cnt++) {
if (authentication_key[cnt]) {
use_auth |= 1;
}
}
}
int8_t GCMParser::parse(uint8_t *d, DataParserContext &ctx) {
if(ctx.length < 12) return DATA_PARSE_INCOMPLETE;
uint8_t* ptr = (uint8_t*) d;
if(*ptr != GCM_TAG) return DATA_PARSE_BOUNDRY_FLAG_MISSING;
ptr++;
// Encrypted APDU
// http://www.weigu.lu/tutorials/sensors2bus/04_encryption/index.html
uint8_t systemTitleLength = *ptr;
ptr++;
uint8_t initialization_vector[12];
memcpy(ctx.system_title, ptr, systemTitleLength);
memcpy(initialization_vector, ctx.system_title, systemTitleLength);
int len = 0;
int headersize = 2 + systemTitleLength;
ptr += systemTitleLength;
if(((*ptr) & 0xFF) == 0x81) {
ptr++;
len = *ptr;
// 1-byte payload length
ptr++;
headersize += 2;
} else if(((*ptr) & 0xFF) == 0x82) {
GCMSizeDef* h = (GCMSizeDef*) ptr;
// 2-byte payload length
len = (ntohs(h->format) & 0xFFFF);
ptr += 3;
headersize += 3;
} else if(((*ptr) & 0xFF) == 0x4f) {
// ???????? single frame did only decode with this compare
ptr++;
headersize++;
}
if(len + headersize > ctx.length)
return DATA_PARSE_INCOMPLETE;
uint8_t additional_authenticated_data[17];
memcpy(additional_authenticated_data, ptr, 1);
// Security tag
uint8_t sec = *ptr;
ptr++;
headersize++;
// Frame counter
memcpy(initialization_vector + 8, ptr, 4);
ptr += 4;
headersize += 4;
int footersize = 0;
// Authentication enabled
uint8_t authentication_tag[12];
uint8_t authkeylen = 0, aadlen = 0;
if((sec & 0x10) == 0x10) {
authkeylen = 12;
aadlen = 17;
footersize += authkeylen;
memcpy(additional_authenticated_data + 1, authentication_key, 16);
memcpy(authentication_tag, ptr + len - footersize - 5, authkeylen);
}
br_gcm_context gcm_ctx;
br_aes_small_ctr_keys ctr_ctx;
br_aes_small_ctr_init(&ctr_ctx, encryption_key, 16);
br_gcm_init(&gcm_ctx, &ctr_ctx.vtable, &br_ghash_ctmul32);
br_gcm_reset(&gcm_ctx, initialization_vector, 12);
if (use_auth && authkeylen > 0) {
br_gcm_aad_inject(&gcm_ctx, additional_authenticated_data, aadlen);
}
br_gcm_flip(&gcm_ctx);
br_gcm_run(&gcm_ctx, 0, ptr , ctx.length - headersize);
if (use_auth && authkeylen > 0 && br_gcm_check_tag_trunc(&gcm_ctx, authentication_tag, authkeylen) != 1) {
return GCM_AUTH_FAILED;
}
ctx.length -= footersize + headersize;
return ptr - d;
}
#endif // USE_TLS

View File

@ -0,0 +1,28 @@
#ifndef _GCMPARSER_H
#define _GCMPARSER_H
#include "Arduino.h"
#include "DataParser.h"
#define GCM_TAG 0xDB
#define GCM_AUTH_FAILED -51
#define GCM_DECRYPT_FAILED -52
#define GCM_ENCRYPTION_KEY_FAILED -53
typedef struct GCMSizeDef {
uint8_t flag;
uint16_t format;
} __attribute__((packed)) GCMSizeDef;
class GCMParser {
public:
GCMParser(uint8_t *encryption_key, uint8_t *authentication_key);
int8_t parse(uint8_t *buf, DataParserContext &ctx);
private:
uint8_t encryption_key[16];
uint8_t authentication_key[16];
uint8_t use_auth = 0;
};
#endif

View File

@ -0,0 +1,56 @@
#include "HdlcParser.h"
#include "lwip/def.h"
#include "crc.h"
int8_t HDLCParser::parse(uint8_t *d, DataParserContext &ctx) {
int len;
uint8_t* ptr;
if(ctx.length < 3)
return DATA_PARSE_INCOMPLETE;
HDLCHeader* h = (HDLCHeader*) d;
ptr = (uint8_t*) &h[1];
// Frame format type 3
if((h->format & 0xF0) == 0xA0) {
// Length field (11 lsb of format)
len = (ntohs(h->format) & 0x7FF) + 2;
if(len > ctx.length)
return DATA_PARSE_INCOMPLETE;
HDLCFooter* f = (HDLCFooter*) (d + len - sizeof *f);
// First and last byte should be HDLC_FLAG
if(h->flag != HDLC_FLAG || f->flag != HDLC_FLAG)
return DATA_PARSE_BOUNDRY_FLAG_MISSING;
// Verify FCS
if(ntohs(f->fcs) != AMS_crc16_x25(d + 1, len - sizeof *f - 1))
return DATA_PARSE_FOOTER_CHECKSUM_ERROR;
// Skip destination address, LSB marks last byte
while(((*ptr) & 0x01) == 0x00) {
ptr++;
}
ptr++;
// Skip source address, LSB marks last byte
while(((*ptr) & 0x01) == 0x00) {
ptr++;
}
ptr++;
HDLC3CtrlHcs* t3 = (HDLC3CtrlHcs*) (ptr);
// Verify HCS
if(ntohs(t3->hcs) != AMS_crc16_x25(d + 1, ptr-d))
return DATA_PARSE_HEADER_CHECKSUM_ERROR;
ptr += 3;
// Exclude all of header and 3 byte footer
ctx.length -= ptr-d+3;
return ptr-d;
}
return DATA_PARSE_UNKNOWN_DATA;
}

View File

@ -0,0 +1,29 @@
#ifndef _HDLCPARSER_H
#define _HDLCPARSER_H
#include "Arduino.h"
#include "DataParser.h"
#define HDLC_FLAG 0x7E
typedef struct HDLCHeader {
uint8_t flag;
uint16_t format;
} __attribute__((packed)) HDLCHeader;
typedef struct HDLCFooter {
uint16_t fcs;
uint8_t flag;
} __attribute__((packed)) HDLCFooter;
typedef struct HDLC3CtrlHcs {
uint8_t control;
uint16_t hcs;
} __attribute__((packed)) HDLC3CtrlHcs;
class HDLCParser {
public:
int8_t parse(uint8_t *buf, DataParserContext &ctx);
};
#endif

View File

@ -0,0 +1,6 @@
#include "LlcParser.h"
int8_t LLCParser::parse(uint8_t *buf, DataParserContext &ctx) {
ctx.length -= 3;
return 3;
}

View File

@ -0,0 +1,18 @@
#ifndef _LLCPARSER_H
#define _LLCPARSER_H
#include "Arduino.h"
#include "DataParser.h"
typedef struct LLCHeader {
uint8_t dst;
uint8_t src;
uint8_t control;
} __attribute__((packed)) LLCHeader;
class LLCParser {
public:
int8_t parse(uint8_t *buf, DataParserContext &ctx);
};
#endif

View File

@ -0,0 +1,91 @@
#include "MbusParser.h"
MBUSParser::~MBUSParser(void) {
if (buf) free(buf);
}
int8_t MBUSParser::parse(uint8_t *d, DataParserContext &ctx) {
int len;
int headersize = 3;
int footersize = 1;
uint8_t* ptr;
// https://m-bus.com/documentation-wired/06-application-layer
if(ctx.length < 4)
return DATA_PARSE_INCOMPLETE;
MbusHeader* mh = (MbusHeader*) d;
if(mh->flag1 != MBUS_START || mh->flag2 != MBUS_START)
return DATA_PARSE_BOUNDRY_FLAG_MISSING;
// First two bytes is 1-byte length value repeated. Only used for last segment
if(mh->len1 != mh->len2)
return MBUS_FRAME_LENGTH_NOT_EQUAL;
len = mh->len1;
ptr = (uint8_t*) &mh[1];
headersize = 4;
footersize = 2;
if(len == 0x00)
len = ctx.length - headersize - footersize;
// Payload can max be 255 bytes, so I think the following case is only valid for austrian meters
if(len < headersize)
len += 256;
if((headersize + footersize + len) > ctx.length)
return DATA_PARSE_INCOMPLETE;
MbusFooter* mf = (MbusFooter*) (d + len + headersize);
if(mf->flag != MBUS_END)
return DATA_PARSE_BOUNDRY_FLAG_MISSING;
if(checksum(d + headersize, len) != mf->fcs)
return DATA_PARSE_FOOTER_CHECKSUM_ERROR;
ptr += 2; len -= 2;
// Control information field
uint8_t ci = *ptr;
// Skip CI, STSAP and DTSAP
ptr += 3; len -= 3;
// Bits 7 6 5 4 3 2 1 0
// 0 0 0 Finished Sequence number
uint8_t sequenceNumber = (ci & 0x0F);
if((ci & 0x10) == 0x00) { // Not finished yet
if(sequenceNumber == 0) {
if(buf == NULL) buf = (uint8_t *)malloc((size_t)1024); // TODO find out from first package ?
pos = 0;
} else if(buf == NULL || pos + len > 1024 || sequenceNumber != (lastSequenceNumber + 1)) {
return DATA_PARSE_FAIL;
}
memcpy(buf+pos, ptr, len);
pos += len;
lastSequenceNumber = sequenceNumber;
return DATA_PARSE_INTERMEDIATE_SEGMENT;
} else if(sequenceNumber > 0) { // This is the last frame of multiple, assembly needed
if(buf == NULL || pos + len > 1024 || sequenceNumber != (lastSequenceNumber + 1)) {
return DATA_PARSE_FAIL;
}
memcpy(buf+pos, ptr, len);
pos += len;
return DATA_PARSE_FINAL_SEGMENT;
}
return ptr-d;
}
uint16_t MBUSParser::write(const uint8_t* d, DataParserContext &ctx) {
if(buf != NULL) {
memcpy((uint8_t *) d, buf, pos);
ctx.length = pos;
}
return 0;
}
uint8_t MBUSParser::checksum(const uint8_t* p, int len) {
uint8_t ret = 0;
while(len--)
ret += *p++;
return ret;
}

View File

@ -0,0 +1,35 @@
#ifndef _MBUSPARSER_H
#define _MBUSPARSER_H
#include "Arduino.h"
#include "DataParser.h"
#define MBUS_START 0x68
#define MBUS_END 0x16
#define MBUS_FRAME_LENGTH_NOT_EQUAL -41
typedef struct MbusHeader {
uint8_t flag1;
uint8_t len1;
uint8_t len2;
uint8_t flag2;
} __attribute__((packed)) MbusHeader;
typedef struct MbusFooter {
uint8_t fcs;
uint8_t flag;
} __attribute__((packed)) MbusFooter;
class MBUSParser {
public:
int8_t parse(uint8_t *buf, DataParserContext &ctx);
~MBUSParser(void);
uint16_t write(const uint8_t* d, DataParserContext &ctx);
private:
uint8_t lastSequenceNumber = 0;
uint16_t pos = 0;
uint8_t *buf = NULL;
uint8_t checksum(const uint8_t* p, int len);
};
#endif

321
lib/lib_div/ams/Time.cpp Normal file
View File

@ -0,0 +1,321 @@
/*
time.c - low level time and date functions
Copyright (c) Michael Margolis 2009-2014
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
1.0 6 Jan 2010 - initial release
1.1 12 Feb 2010 - fixed leap year calculation error
1.2 1 Nov 2010 - fixed setTime bug (thanks to Korman for this)
1.3 24 Mar 2012 - many edits by Paul Stoffregen: fixed timeStatus() to update
status, updated examples for Arduino 1.0, fixed ARM
compatibility issues, added TimeArduinoDue and TimeTeensy3
examples, add error checking and messages to RTC examples,
add examples to DS1307RTC library.
1.4 5 Sep 2014 - compatibility with Arduino 1.5.7
*/
#if ARDUINO >= 100
#include <Arduino.h>
#else
#include <WProgram.h>
#endif
#include "TimeLib.h"
static tmElements_t tm; // a cache of time elements
static time_t cacheTime; // the time the cache was updated
static uint32_t syncInterval = 300; // time sync will be attempted after this many seconds
void refreshCache(time_t t) {
if (t != cacheTime) {
breakTime(t, tm);
cacheTime = t;
}
}
int hour() { // the hour now
return hour(now());
}
int hour(time_t t) { // the hour for the given time
refreshCache(t);
return tm.Hour;
}
int hourFormat12() { // the hour now in 12 hour format
return hourFormat12(now());
}
int hourFormat12(time_t t) { // the hour for the given time in 12 hour format
refreshCache(t);
if( tm.Hour == 0 )
return 12; // 12 midnight
else if( tm.Hour > 12)
return tm.Hour - 12 ;
else
return tm.Hour ;
}
uint8_t isAM() { // returns true if time now is AM
return !isPM(now());
}
uint8_t isAM(time_t t) { // returns true if given time is AM
return !isPM(t);
}
uint8_t isPM() { // returns true if PM
return isPM(now());
}
uint8_t isPM(time_t t) { // returns true if PM
return (hour(t) >= 12);
}
int minute() {
return minute(now());
}
int minute(time_t t) { // the minute for the given time
refreshCache(t);
return tm.Minute;
}
int second() {
return second(now());
}
int second(time_t t) { // the second for the given time
refreshCache(t);
return tm.Second;
}
int day(){
return(day(now()));
}
int day(time_t t) { // the day for the given time (0-6)
refreshCache(t);
return tm.Day;
}
int weekday() { // Sunday is day 1
return weekday(now());
}
int weekday(time_t t) {
refreshCache(t);
return tm.Wday;
}
int month(){
return month(now());
}
int month(time_t t) { // the month for the given time
refreshCache(t);
return tm.Month;
}
int year() { // as in Processing, the full four digit year: (2009, 2010 etc)
return year(now());
}
int year(time_t t) { // the year for the given time
refreshCache(t);
return tmYearToCalendar(tm.Year);
}
/*============================================================================*/
/* functions to convert to and from system time */
/* These are for interfacing with time services and are not normally needed in a sketch */
// leap year calculator expects year argument as years offset from 1970
#define LEAP_YEAR(Y) ( ((1970+(Y))>0) && !((1970+(Y))%4) && ( ((1970+(Y))%100) || !((1970+(Y))%400) ) )
static const uint8_t monthDays[]={31,28,31,30,31,30,31,31,30,31,30,31}; // API starts months from 1, this array starts from 0
void breakTime(time_t timeInput, tmElements_t &tm){
// break the given time_t into time components
// this is a more compact version of the C library localtime function
// note that year is offset from 1970 !!!
uint8_t year;
uint8_t month, monthLength;
uint32_t time;
unsigned long days;
time = (uint32_t)timeInput;
tm.Second = time % 60;
time /= 60; // now it is minutes
tm.Minute = time % 60;
time /= 60; // now it is hours
tm.Hour = time % 24;
time /= 24; // now it is days
tm.Wday = ((time + 4) % 7) + 1; // Sunday is day 1
year = 0;
days = 0;
while((unsigned)(days += (LEAP_YEAR(year) ? 366 : 365)) <= time) {
year++;
}
tm.Year = year; // year is offset from 1970
days -= LEAP_YEAR(year) ? 366 : 365;
time -= days; // now it is days in this year, starting at 0
days=0;
month=0;
monthLength=0;
for (month=0; month<12; month++) {
if (month==1) { // february
if (LEAP_YEAR(year)) {
monthLength=29;
} else {
monthLength=28;
}
} else {
monthLength = monthDays[month];
}
if (time >= monthLength) {
time -= monthLength;
} else {
break;
}
}
tm.Month = month + 1; // jan is month 1
tm.Day = time + 1; // day of month
}
time_t makeTime(const tmElements_t &tm){
// assemble time elements into time_t
// note year argument is offset from 1970 (see macros in time.h to convert to other formats)
// previous version used full four digit year (or digits since 2000),i.e. 2009 was 2009 or 9
int i;
uint32_t seconds;
// seconds from 1970 till 1 jan 00:00:00 of the given year
seconds= tm.Year*(SECS_PER_DAY * 365);
for (i = 0; i < tm.Year; i++) {
if (LEAP_YEAR(i)) {
seconds += SECS_PER_DAY; // add extra days for leap years
}
}
// add days for this year, months start from 1
for (i = 1; i < tm.Month; i++) {
if ( (i == 2) && LEAP_YEAR(tm.Year)) {
seconds += SECS_PER_DAY * 29;
} else {
seconds += SECS_PER_DAY * monthDays[i-1]; //monthDay array starts from 0
}
}
seconds+= (tm.Day-1) * SECS_PER_DAY;
seconds+= tm.Hour * SECS_PER_HOUR;
seconds+= tm.Minute * SECS_PER_MIN;
seconds+= tm.Second;
return (time_t)seconds;
}
/*=====================================================*/
/* Low level system time functions */
static uint32_t sysTime = 0;
static uint32_t prevMillis = 0;
static uint32_t nextSyncTime = 0;
static timeStatus_t Status = timeNotSet;
getExternalTime getTimePtr; // pointer to external sync function
//setExternalTime setTimePtr; // not used in this version
#ifdef TIME_DRIFT_INFO // define this to get drift data
time_t sysUnsyncedTime = 0; // the time sysTime unadjusted by sync
#endif
time_t now() {
// calculate number of seconds passed since last call to now()
while (millis() - prevMillis >= 1000) {
// millis() and prevMillis are both unsigned ints thus the subtraction will always be the absolute value of the difference
sysTime++;
prevMillis += 1000;
#ifdef TIME_DRIFT_INFO
sysUnsyncedTime++; // this can be compared to the synced time to measure long term drift
#endif
}
if (nextSyncTime <= sysTime) {
if (getTimePtr != 0) {
time_t t = getTimePtr();
if (t != 0) {
setTime(t);
} else {
nextSyncTime = sysTime + syncInterval;
Status = (Status == timeNotSet) ? timeNotSet : timeNeedsSync;
}
}
}
return (time_t)sysTime;
}
void setTime(time_t t) {
#ifdef TIME_DRIFT_INFO
if(sysUnsyncedTime == 0)
sysUnsyncedTime = t; // store the time of the first call to set a valid Time
#endif
sysTime = (uint32_t)t;
nextSyncTime = (uint32_t)t + syncInterval;
Status = timeSet;
prevMillis = millis(); // restart counting from now (thanks to Korman for this fix)
}
void setTime(int hr,int min,int sec,int dy, int mnth, int yr){
// year can be given as full four digit year or two digts (2010 or 10 for 2010);
//it is converted to years since 1970
if( yr > 99)
yr = yr - 1970;
else
yr += 30;
tm.Year = yr;
tm.Month = mnth;
tm.Day = dy;
tm.Hour = hr;
tm.Minute = min;
tm.Second = sec;
setTime(makeTime(tm));
}
void adjustTime(long adjustment) {
sysTime += adjustment;
}
// indicates if time has been set and recently synchronized
timeStatus_t timeStatus() {
now(); // required to actually update the status
return Status;
}
void setSyncProvider( getExternalTime getTimeFunction){
getTimePtr = getTimeFunction;
nextSyncTime = sysTime;
now(); // this will sync the clock
}
void setSyncInterval(time_t interval){ // set the number of seconds between re-sync
syncInterval = (uint32_t)interval;
nextSyncTime = sysTime + syncInterval;
}

144
lib/lib_div/ams/TimeLib.h Normal file
View File

@ -0,0 +1,144 @@
/*
time.h - low level time and date functions
*/
/*
July 3 2011 - fixed elapsedSecsThisWeek macro (thanks Vincent Valdy for this)
- fixed daysToTime_t macro (thanks maniacbug)
*/
#ifndef _Time_h
#ifdef __cplusplus
#define _Time_h
#include <inttypes.h>
#ifndef __AVR__
#include <sys/types.h> // for __time_t_defined, but avr libc lacks sys/types.h
#endif
#if !defined(__time_t_defined) // avoid conflict with newlib or other posix libc
typedef unsigned long time_t;
#endif
// This ugly hack allows us to define C++ overloaded functions, when included
// from within an extern "C", as newlib's sys/stat.h does. Actually it is
// intended to include "time.h" from the C library (on ARM, but AVR does not
// have that file at all). On Mac and Windows, the compiler will find this
// "Time.h" instead of the C library "time.h", so we may cause other weird
// and unpredictable effects by conflicting with the C library header "time.h",
// but at least this hack lets us define C++ functions as intended. Hopefully
// nothing too terrible will result from overriding the C library header?!
extern "C++" {
typedef enum {timeNotSet, timeNeedsSync, timeSet
} timeStatus_t ;
typedef enum {
dowInvalid, dowSunday, dowMonday, dowTuesday, dowWednesday, dowThursday, dowFriday, dowSaturday
} timeDayOfWeek_t;
typedef enum {
tmSecond, tmMinute, tmHour, tmWday, tmDay,tmMonth, tmYear, tmNbrFields
} tmByteFields;
typedef struct {
uint8_t Second;
uint8_t Minute;
uint8_t Hour;
uint8_t Wday; // day of week, sunday is day 1
uint8_t Day;
uint8_t Month;
uint8_t Year; // offset from 1970;
} tmElements_t, TimeElements, *tmElementsPtr_t;
//convenience macros to convert to and from tm years
#define tmYearToCalendar(Y) ((Y) + 1970) // full four digit year
#define CalendarYrToTm(Y) ((Y) - 1970)
#define tmYearToY2k(Y) ((Y) - 30) // offset is from 2000
#define y2kYearToTm(Y) ((Y) + 30)
typedef time_t(*getExternalTime)();
//typedef void (*setExternalTime)(const time_t); // not used in this version
/*==============================================================================*/
/* Useful Constants */
#define SECS_PER_MIN ((time_t)(60UL))
#define SECS_PER_HOUR ((time_t)(3600UL))
#define SECS_PER_DAY ((time_t)(SECS_PER_HOUR * 24UL))
#define DAYS_PER_WEEK ((time_t)(7UL))
#define SECS_PER_WEEK ((time_t)(SECS_PER_DAY * DAYS_PER_WEEK))
#define SECS_PER_YEAR ((time_t)(SECS_PER_DAY * 365UL)) // TODO: ought to handle leap years
#define SECS_YR_2000 ((time_t)(946684800UL)) // the time at the start of y2k
/* Useful Macros for getting elapsed time */
#define numberOfSeconds(_time_) ((_time_) % SECS_PER_MIN)
#define numberOfMinutes(_time_) (((_time_) / SECS_PER_MIN) % SECS_PER_MIN)
#define numberOfHours(_time_) (((_time_) % SECS_PER_DAY) / SECS_PER_HOUR)
#define dayOfWeek(_time_) ((((_time_) / SECS_PER_DAY + 4) % DAYS_PER_WEEK)+1) // 1 = Sunday
#define elapsedDays(_time_) ((_time_) / SECS_PER_DAY) // this is number of days since Jan 1 1970
#define elapsedSecsToday(_time_) ((_time_) % SECS_PER_DAY) // the number of seconds since last midnight
// The following macros are used in calculating alarms and assume the clock is set to a date later than Jan 1 1971
// Always set the correct time before setting alarms
#define previousMidnight(_time_) (((_time_) / SECS_PER_DAY) * SECS_PER_DAY) // time at the start of the given day
#define nextMidnight(_time_) (previousMidnight(_time_) + SECS_PER_DAY) // time at the end of the given day
#define elapsedSecsThisWeek(_time_) (elapsedSecsToday(_time_) + ((dayOfWeek(_time_)-1) * SECS_PER_DAY)) // note that week starts on day 1
#define previousSunday(_time_) ((_time_) - elapsedSecsThisWeek(_time_)) // time at the start of the week for the given time
#define nextSunday(_time_) (previousSunday(_time_)+SECS_PER_WEEK) // time at the end of the week for the given time
/* Useful Macros for converting elapsed time to a time_t */
#define minutesToTime_t ((M)) ( (M) * SECS_PER_MIN)
#define hoursToTime_t ((H)) ( (H) * SECS_PER_HOUR)
#define daysToTime_t ((D)) ( (D) * SECS_PER_DAY) // fixed on Jul 22 2011
#define weeksToTime_t ((W)) ( (W) * SECS_PER_WEEK)
/*============================================================================*/
/* time and date functions */
int hour(); // the hour now
int hour(time_t t); // the hour for the given time
int hourFormat12(); // the hour now in 12 hour format
int hourFormat12(time_t t); // the hour for the given time in 12 hour format
uint8_t isAM(); // returns true if time now is AM
uint8_t isAM(time_t t); // returns true the given time is AM
uint8_t isPM(); // returns true if time now is PM
uint8_t isPM(time_t t); // returns true the given time is PM
int minute(); // the minute now
int minute(time_t t); // the minute for the given time
int second(); // the second now
int second(time_t t); // the second for the given time
int day(); // the day now
int day(time_t t); // the day for the given time
int weekday(); // the weekday now (Sunday is day 1)
int weekday(time_t t); // the weekday for the given time
int month(); // the month now (Jan is month 1)
int month(time_t t); // the month for the given time
int year(); // the full four digit year: (2009, 2010 etc)
int year(time_t t); // the year for the given time
time_t now(); // return the current time as seconds since Jan 1 1970
void setTime(time_t t);
void setTime(int hr,int min,int sec,int day, int month, int yr);
void adjustTime(long adjustment);
/* date strings */
#define dt_MAX_STRING_LEN 9 // length of longest date string (excluding terminating null)
char* monthStr(uint8_t month);
char* dayStr(uint8_t day);
char* monthShortStr(uint8_t month);
char* dayShortStr(uint8_t day);
/* time sync functions */
timeStatus_t timeStatus(); // indicates if time has been set and recently synchronized
void setSyncProvider( getExternalTime getTimeFunction); // identify the external time provider
void setSyncInterval(time_t interval); // set the number of seconds between re-sync
/* low level functions to convert to and from system time */
void breakTime(time_t time, tmElements_t &tm); // break time_t into elements
time_t makeTime(const tmElements_t &tm); // convert time elements into time_t
} // extern "C++"
#endif // __cplusplus
#endif /* _Time_h */

29
lib/lib_div/ams/crc.cpp Normal file
View File

@ -0,0 +1,29 @@
#include "crc.h"
uint16_t AMS_crc16_x25(const uint8_t* p, int len)
{
uint16_t crc = UINT16_MAX;
while(len--)
for (uint16_t i = 0, d = 0xff & *p++; i < 8; i++, d >>= 1)
crc = ((crc & 1) ^ (d & 1)) ? (crc >> 1) ^ 0x8408 : (crc >> 1);
return (~crc << 8) | (~crc >> 8 & 0xff);
}
uint16_t AMS_crc16 (const uint8_t *p, int len) {
uint16_t crc = 0;
while (len--) {
int i;
crc ^= *p++;
for (i = 0 ; i < 8 ; ++i) {
if (crc & 1)
crc = (crc >> 1) ^ 0xa001;
else
crc = (crc >> 1);
}
}
return crc;
}

10
lib/lib_div/ams/crc.h Normal file
View File

@ -0,0 +1,10 @@
#ifndef _CRC_H
#define _CRC_H
#include "Arduino.h"
#include <stdint.h>
uint16_t AMS_crc16(const uint8_t* p, int len);
uint16_t AMS_crc16_x25(const uint8_t* p, int len);
#endif

View File

@ -0,0 +1,273 @@
#include "han_Parser.h"
#include "Cosem.h"
extern int SML_print(const char *, ...);
#define han_debug SML_print
Han_Parser::Han_Parser(uint16_t (dp)(uint8_t, uint8_t), uint8_t m, uint8_t *key, uint8_t *auth) {
dispatch = dp;
meter = m;
memmove(encryptionKey, key, 16);
if (auth) {
memmove(authenticationKey, auth, 16);
} else {
memset(authenticationKey, 0, 16);
}
}
Han_Parser::~Han_Parser(void) {
if (hdlcParser) delete hdlcParser;
if (mbusParser) delete mbusParser;
if (gbtParser) delete gbtParser;
if (gcmParser) delete gcmParser;
if (llcParser) delete llcParser;
if (dlmsParser) delete dlmsParser;
if (dsmrParser) delete dsmrParser;
}
int Han_Parser::serial_available(void) {
return dispatch(meter, 0);
}
int Han_Parser::serial_read(void) {
return dispatch(meter, 1);
}
int16_t Han_Parser::serial_readBytes(uint8_t *buf, uint16_t size) {
if (size > serial_available()) {
size = serial_available();
}
for (uint16_t cnt = 0; cnt < size; cnt++) {
buf[cnt] = serial_read();
}
return size;
}
bool Han_Parser::readHanPort(uint8_t **out, uint16_t *size) {
if (!serial_available()) return false;
// Before reading, empty serial buffer to increase chance of getting first byte of a data transfer
if (!serialInit) {
serial_readBytes(hanBuffer, BUF_SIZE_HAN);
serialInit = true;
return false;
}
DataParserContext ctx = {0};
int pos = DATA_PARSE_INCOMPLETE;
// For each byte received, check if we have a complete frame we can handle
while (serial_available() && pos == DATA_PARSE_INCOMPLETE) {
yield();
// If buffer was overflowed, reset
if (len >= BUF_SIZE_HAN) {
serial_readBytes(hanBuffer, BUF_SIZE_HAN);
len = 0;
han_debug(PSTR("Buffer overflow, resetting"));
return false;
}
hanBuffer[len++] = serial_read();
ctx.length = len;
pos = unwrapData((uint8_t *) hanBuffer, ctx);
if(ctx.type > 0 && pos >= 0) {
if(ctx.type == DATA_TAG_DLMS) {
han_debug(PSTR("Received valid DLMS at %d"), pos);
} else if(ctx.type == DATA_TAG_DSMR) {
han_debug(PSTR("Received valid DSMR at %d"), pos);
} else {
// TODO: Move this so that payload is sent to MQTT
han_debug(PSTR("Unknown tag %02X at pos %d"), ctx.type, pos);
len = 0;
return false;
}
}
}
if (pos == DATA_PARSE_INCOMPLETE) {
return false;
} else if(pos == DATA_PARSE_UNKNOWN_DATA) {
han_debug(PSTR("Unknown data payload:"));
len = len + serial_readBytes(hanBuffer + len, BUF_SIZE_HAN - len);
//debugPrint(hanBuffer, 0, len);
len = 0;
return false;
}
if (pos == DATA_PARSE_INTERMEDIATE_SEGMENT) {
len = 0;
return false;
} else if (pos < 0) {
printHanReadError(pos);
len += serial_readBytes(hanBuffer + len, BUF_SIZE_HAN - len);
while (serial_available()) serial_read(); // Make sure it is all empty, in case we overflowed buffer above
len = 0;
return false;
}
// Data is valid, clear the rest of the buffer to avoid tainted parsing
for (int i = pos + ctx.length; i < BUF_SIZE_HAN; i++) {
hanBuffer[i] = 0x00;
}
//AmsData data;
char* payload = ((char *) (hanBuffer)) + pos;
if (ctx.type == DATA_TAG_DLMS) {
han_debug(PSTR("Using application data:"));
//if (Debug.isActive(RemoteDebug::VERBOSE)) debugPrint((byte*) payload, 0, ctx.length);
// Rudimentary detector for L&G proprietary format
if (payload[0] == CosemTypeStructure && payload[2] == CosemTypeArray && payload[1] == payload[3]) {
//data = LNG(payload, meterState.getMeterType(), &meterConfig, ctx, &Debug);
} else {
// TODO: Split IEC6205675 into DataParserKaifa and DataParserObis. This way we can add other means of parsing, for those other proprietary formats
//data = IEC6205675(payload, meterState.getMeterType(), &meterConfig, ctx);
}
} else if(ctx.type == DATA_TAG_DSMR) {
//data = IEC6205621(payload);
}
*out = hanBuffer + pos;
*size = ctx.length;
len = 0;
return true;
}
int16_t Han_Parser::unwrapData(uint8_t *buf, DataParserContext &context) {
int16_t ret = 0;
bool doRet = false;
uint16_t end = BUF_SIZE_HAN;
uint8_t tag = (*buf);
uint8_t lastTag = DATA_TAG_NONE;
while (tag != DATA_TAG_NONE) {
int16_t curLen = context.length;
int8_t res = 0;
switch(tag) {
case DATA_TAG_HDLC:
if (hdlcParser == NULL) hdlcParser = new HDLCParser();
res = hdlcParser->parse(buf, context);
break;
case DATA_TAG_MBUS:
if (mbusParser == NULL) mbusParser = new MBUSParser();
res = mbusParser->parse(buf, context);
break;
case DATA_TAG_GBT:
if (gbtParser == NULL) gbtParser = new GBTParser();
res = gbtParser->parse(buf, context);
break;
case DATA_TAG_GCM:
if (gcmParser == NULL) gcmParser = new GCMParser(encryptionKey, authenticationKey);
res = gcmParser->parse(buf, context);
break;
case DATA_TAG_LLC:
if (llcParser == NULL) llcParser = new LLCParser();
res = llcParser->parse(buf, context);
break;
case DATA_TAG_DLMS:
if (dlmsParser == NULL) dlmsParser = new DLMSParser();
res = dlmsParser->parse(buf, context);
if (res >= 0) doRet = true;
break;
case DATA_TAG_DSMR:
if (dsmrParser == NULL) dsmrParser = new DSMRParser();
res = dsmrParser->parse(buf, context, lastTag != DATA_TAG_NONE);
if (res >= 0) doRet = true;
break;
default:
han_debug(PSTR("Ended up in default case while unwrapping...(tag %02X)"), tag);
return DATA_PARSE_UNKNOWN_DATA;
}
lastTag = tag;
if (res == DATA_PARSE_INCOMPLETE) {
return res;
}
if (context.length > end) return false;
if (Debug) {
switch(tag) {
case DATA_TAG_HDLC:
han_debug(PSTR("HDLC frame:"));
break;
case DATA_TAG_MBUS:
han_debug(PSTR("MBUS frame:"));
break;
case DATA_TAG_GBT:
han_debug(PSTR("GBT frame:"));
break;
case DATA_TAG_GCM:
han_debug(PSTR("GCM frame:"));
break;
case DATA_TAG_LLC:
han_debug(PSTR("LLC frame:"));
break;
case DATA_TAG_DLMS:
han_debug(PSTR("DLMS frame:"));
break;
case DATA_TAG_DSMR:
han_debug(PSTR("DSMR frame:"));
break;
}
}
if (res == DATA_PARSE_FINAL_SEGMENT) {
if (tag == DATA_TAG_MBUS) {
res = mbusParser->write(buf, context);
}
}
if (res < 0) {
return res;
}
buf += res;
end -= res;
ret += res;
// If we are ready to return, do that
if (doRet) {
context.type = tag;
return ret;
}
// Use start byte of new buffer position as tag for next round in loop
tag = (*buf);
}
han_debug(PSTR("Got to end of unwrap method..."));
return DATA_PARSE_UNKNOWN_DATA;
}
void Han_Parser::printHanReadError(int16_t pos) {
switch(pos) {
case DATA_PARSE_BOUNDRY_FLAG_MISSING:
han_debug(PSTR("Boundry flag missing"));
break;
case DATA_PARSE_HEADER_CHECKSUM_ERROR:
han_debug(PSTR("Header checksum error"));
break;
case DATA_PARSE_FOOTER_CHECKSUM_ERROR:
han_debug(PSTR("Frame checksum error"));
break;
case DATA_PARSE_INCOMPLETE:
han_debug(PSTR("Received frame is incomplete"));
break;
case GCM_AUTH_FAILED:
han_debug(PSTR("Decrypt authentication failed"));
break;
case GCM_ENCRYPTION_KEY_FAILED:
han_debug(PSTR("Setting decryption key failed"));
break;
case GCM_DECRYPT_FAILED:
han_debug(PSTR("Decryption failed"));
break;
case MBUS_FRAME_LENGTH_NOT_EQUAL:
han_debug(PSTR("Frame length mismatch"));
break;
case DATA_PARSE_INTERMEDIATE_SEGMENT:
han_debug(PSTR("Intermediate segment received"));
break;
case DATA_PARSE_UNKNOWN_DATA:
han_debug(PSTR("Unknown data format %02X"), hanBuffer[0]);
break;
default:
han_debug(PSTR("Unspecified error while reading data: %d"), pos);
break;
}
}

View File

@ -0,0 +1,47 @@
#ifndef _HAN_PARSER_H
#define _HAN_PARSER_H
#if defined __cplusplus
#include "Arduino.h"
#include "DataParsers.h"
#include "DataParser.h"
#include "Cosem.h"
#include "ntohll.h"
#define BUF_SIZE_HAN (1280)
int16_t serial_available(void);
uint8_t serial_read(void);
class Han_Parser
{
public:
Han_Parser(uint16_t (*)(uint8_t, uint8_t), uint8_t, uint8_t *, uint8_t *);
~Han_Parser(void);
bool readHanPort(uint8_t **out, uint16_t *size);
int16_t unwrapData(uint8_t *buf, DataParserContext &context);
void printHanReadError(int16_t pos);
uint8_t encryptionKey[16];
uint8_t authenticationKey[16];
uint8_t hanBuffer[BUF_SIZE_HAN];
int len = 0;
private:
uint16_t (*dispatch)(uint8_t, uint8_t);
int serial_available(void);
int serial_read(void);
int16_t serial_readBytes(uint8_t *, uint16_t);
HDLCParser *hdlcParser = NULL;
MBUSParser *mbusParser = NULL;
GBTParser *gbtParser = NULL;
GCMParser *gcmParser = NULL;
LLCParser *llcParser = NULL;
DLMSParser *dlmsParser = NULL;
DSMRParser *dsmrParser = NULL;
uint8_t encryption_key[16];
uint8_t authentication_key[16];
uint8_t meter;
bool serialInit = true;
bool Debug = true;
};
#endif
#endif

View File

@ -0,0 +1,23 @@
#include "hexutils.h"
String AMS_toHex(uint8_t* in) {
return AMS_toHex(in, sizeof(in)*2);
}
String AMS_toHex(uint8_t* in, uint16_t size) {
String hex;
for(int i = 0; i < size; i++) {
if(in[i] < 0x10) {
hex += '0';
}
hex += String(in[i], HEX);
}
hex.toUpperCase();
return hex;
}
void AMS_fromHex(uint8_t *out, String in, uint16_t size) {
for(int i = 0; i < size*2; i += 2) {
out[i/2] = strtol(in.substring(i, i+2).c_str(), 0, 16);
}
}

View File

@ -0,0 +1,11 @@
#ifndef _HEXUTILS_H
#define _HEXUTILS_H
#include <stdint.h>
#include "Arduino.h"
String AMS_toHex(uint8_t* in);
String AMS_toHex(uint8_t* in, uint16_t size);
void AMS_fromHex(uint8_t *out, String in, uint16_t size);
#endif

View File

@ -0,0 +1,17 @@
{
"name": "ams",
"version": "1.0",
"description": "ESP8266 ESP32 library for Advanced utility meters",
"license": "GPL",
"homepage": "https://github.com/arendst/Tasmota",
"frameworks": "*",
"platforms": "*",
"authors":
{
"name": "Gerhard Mutz",
"maintainer": true
},
"build": {
"flags": [ "-I$PROJECT_DIR/include" ]
}
}

View File

@ -0,0 +1,17 @@
name=AMS Parser
version=1.2.0
author=Gunnar Skjold
maintainer=https://github.com/gskjold
sentence=Meter parsing Library for Espressif ESP32 and ESP8266 devices.
paragraph=This library allows the ESP32 and ESP8266 devices to parse several meter protocolls
category=Communication
url=https://github.com/UtilitechAS/amsreader-firmware
architectures=esp32,esp8266

View File

@ -0,0 +1,5 @@
#include "ntohll.h"
uint64_t ntohll(uint64_t x) {
return (((uint64_t)ntohl((uint32_t)x)) << 32) + ntohl(x >> 32);
}

8
lib/lib_div/ams/ntohll.h Normal file
View File

@ -0,0 +1,8 @@
#ifndef _NTOHLL_H
#define _NTOHLL_H
#include "lwip/def.h"
uint64_t ntohll(uint64_t x);
#endif

View File

@ -0,0 +1,14 @@
---
Language: Cpp
BasedOnStyle: LLVM
IndentWidth: 4
AlignAfterOpenBracket: Align
AllowShortBlocksOnASingleLine: false
AllowShortCaseLabelsOnASingleLine: false
AllowShortFunctionsOnASingleLine: false
IndentCaseLabels: true
SpacesBeforeTrailingComments: 2
PointerAlignment: Left
AlignEscapedNewlines: Left
ForEachMacros: ['TEST_GROUP', 'TEST']
...

View File

@ -0,0 +1,161 @@
Changelog
=========
All notable changes to this project will be documented in this file.
The format is based on `Keep a Changelog <https://keepachangelog.com/en/1.0.0/>`_
and this project adheres to `Semantic Versioning <https://semver.org/spec/v2.0.0.html>`_.
`Unreleased`_
-------------
`0.6.0`_ 2022-06-22
-------------------
- Fix compiler warnings in SensirionErrors.cpp
- Allow drivers to choose CRC function
`0.5.3`_ 2021-10-19
-------------------
- Add support for sensor specific errors
- Update keywords.txt
`0.5.2`_ 2021-08-03
-------------------
Fixed
.....
- Fix CRC insertion in ``SensirionI2CTxFrame`` when more then one parameter
is sent to the sensor.
`0.5.1`_ 2021-07-08
-------------------
Changed
.......
- Adjusted deprecation warnings
`0.5.0`_ 2021-07-07
-------------------
Added
.....
- Enable SensirionTxFrame to incorporate Uint8 and Uint16 commands
`0.4.3`_ 2021-02-12
-------------------
Added
.....
- Added ``const`` modifier to functions which process MOSI array data.
`0.4.2`_ 2021-01-29
-------------------
Changed
.......
- Renamed the library header from ``SensirionCoreArduinoLibrary.h`` to ``SensirionCore.h``.
We keep the old header for legacy support.
`0.4.1`_ 2021-01-28
-------------------
Fixed
.....
- Properly handle I2C write errors
`0.4.0`_ 2021-01-20
-------------------
Added
.....
- Documentation for all functions.
Breaking
........
- Change interface of ``errorToString()`` function to include length of the
provided buffer.
Removed
.......
- Removed ``reset()`` function from ``SensirionI2CTxFrame`` since the
functionality is not needed.
`0.3.0`_ 2021-01-13
-------------------
Added
.....
- Core implementation for I2C communication. This includes a RX and TX frame
and a I2C communication class.
Changed
.......
- SHDLC and I2C RX frame inherit from a RX frame base class.
- ESP8266 test board from esp8266:esp8266:arduino to esp8266:esp8266:generic.
- Sorted errors into general, SHDLC and I2C errors.
- Replace C style casts with ``static_cast``.
`0.2.0`_ 2021-01-11
-------------------
Added
.....
- Explanation what SHDLC is in README.
- ``SensirionErrors.h`` to ``SensirionCoreArduinoLibrary.h``.
- ``sendAndReceiveFrame()`` function to ``SensirionShdlcCommunication``. This
function combines ``sendFrame()`` and ``receiveFrame()`` into one function and
adds additional error checking.
Changed
.......
- Rename DeviceError to ExecutionError.
- Move check for execution error after the whole frame is read and checksum is
checked. This prevents that a wrong checksum can't be displayed as an
execution error.
Removed
.......
- ``reset()`` function from ``SensirionShdlcTxFrame`` and ``SensirionShdlcRxFrame``,
since one can just create a new frame object which has the same effect.
`0.1.0`_ 2021-01-07
-------------------
- Initial release
.. _Unreleased: https://github.com/Sensirion/arduino-core/compare/0.6.0...main
.. _0.6.0: https://github.com/Sensirion/arduino-core/compare/0.6.0...0.5.3
.. _0.5.3: https://github.com/Sensirion/arduino-core/compare/0.5.2...0.5.3
.. _0.5.2: https://github.com/Sensirion/arduino-core/compare/0.5.1...0.5.2
.. _0.5.1: https://github.com/Sensirion/arduino-core/compare/0.5.0...0.5.1
.. _0.5.0: https://github.com/Sensirion/arduino-core/compare/0.4.3...0.5.0
.. _0.4.3: https://github.com/Sensirion/arduino-core/compare/0.4.2...0.4.3
.. _0.4.2: https://github.com/Sensirion/arduino-core/compare/0.4.1...0.4.2
.. _0.4.1: https://github.com/Sensirion/arduino-core/compare/0.4.0...0.4.1
.. _0.4.0: https://github.com/Sensirion/arduino-core/compare/0.3.0...0.4.0
.. _0.3.0: https://github.com/Sensirion/arduino-core/compare/0.2.0...0.3.0
.. _0.2.0: https://github.com/Sensirion/arduino-core/compare/0.1.0...0.2.0
.. _0.1.0: https://github.com/Sensirion/arduino-core/releases/tag/0.1.0

View File

@ -0,0 +1,29 @@
BSD 3-Clause License
Copyright (c) 2020, Sensirion AG
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
3. Neither the name of the copyright holder nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

View File

@ -0,0 +1,139 @@
<!-- Downloaded from https://github.com/Sensirion/arduino-core
on 10/01/2023 at commit bd2d3ce9355a3a1997beeb820e59072ef9430d5e -->
# Sensirion Arduino Core Library
This library provides SHDLC and I2C protocol implementations for Sensirion
sensors. There shouldn't be a reason to use it directly, but is required by the
sensor driver libraries provided here:
- [SCD4x](https://github.com/Sensirion/arduino-i2c-scd4x)
- [SVM40-I2C](https://github.com/Sensirion/arduino-i2c-svm40)
- [SVM40-UART](https://github.com/Sensirion/arduino-uart-svm40)
- [SFA3x-I2C](https://github.com/Sensirion/arduino-i2c-sfa3x)
- [SFA3x-UART](https://github.com/Sensirion/arduino-uart-sfa3x)
# More Drivers
Not looking for Arduino drivers? Check out our other drivers here:
- [Embedded](https://github.com/Sensirion/info#repositories)
- [Python](https://github.com/Sensirion/info#python-drivers)
# Usage
## SHDLC
SHDLC (Sensirion High-Level Data Link Control) is a byte-oriented master-slave
communication protocol based on [ISO
HDLC](https://en.wikipedia.org/wiki/High-Level_Data_Link_Control). It is used
to control some of Sensirions devices (for example mass flow controllers). The
detailed protocol documentation is not publicly available (yet). If you need
it, please contact our [customer
support](https://www.sensirion.com/en/about-us/contact/).
This library provides the following classes for communication with Sensirion
Sensors using the SHDLC protocol.
- `SensirionShdlcTxFrame`
- `SensirionShdlcRxFrame`
- `SensirionShdlcCommunication`
### Example Usage
First initialize an instance of `SensirionShdlcTxFrame` and
`SensirionShdlcRxFrame` with a properly sized buffer. A good worst case
estimation for the buffer size is `2 * (n+6)` where `n` is the number of bytes
you want to send. After that you can build your frame by first calling
`begin()`. Information about the correct COMMAND and ADDRESS can be found on
the data sheet of your sensor. Then you can add data to the frame by using
different add member functions. See the code below for examples. After adding
your data finish the frame by calling `finish()`.
To send this frame to the sensor you first need to initialize the correct
Stream object (Serial,UART,...) to talk to your sensor. Don't forget to also
call the `.begin()` function with the right configuration. Then call the static
function `sendAndReceiveFrame()` from `SensirionShdlcCommunication` as shown
below. You need to replace `STREAMOBJECT` with the initialized Stream object of
your choice. Additionally you need to provide a timeout for to receive data
back, consult the data sheet of your sensor for information on the best timeout
value.
You can decode the frame by using the different get members to convert the
received data to desired data types.
All functions return a error code if an error occurs during execution and zero
otherwise.
```cpp
uint8_t txBuffer[256];
uint8_t rxBuffer[256];
SensirionShdlcTxFrame txFrame(txBuffer, 256);
SensirionShdlcRxFrame rxFrame(rxBuffer, 256);
txFrame.begin(COMMAND, ADDRESS, DATALENGTH);
txFrame.addUInt8(UINT8);
txFrame.addUInt32(UINT32);
txFrame.finish();
SensirionShdlcCommunication::sendAndReceiveFrame(STREAMOBJECT, txFrame, rxFrame, TIMEOUT);
rxFrame.getUInt16(UINT16);
rxFrame.getFloat(FLOAT);
```
## I2C
This library provides the following classes for communication with Sensirion
Sensors using the I2C protocol.
- `SensirionI2cTxFrame`
- `SensirionI2cRxFrame`
- `SensirionI2cCommunication`
### Example Usage
First initialize an instance of `SensirionI2CTxFrame` and `SensirionI2CRxFrame`
with a buffer sized the amount of data to read times 1.5. This is needed to
account for the CRC which is added after every second byte. After that you can
build your frame by first calling `addCommand()` to add the command at the
beginning of the frame. Information about the different COMMANDs can be found
on the data sheet of your sensor. Then you can add data to the frame by using
different add member functions. See the code below for examples.
To send this frame to the sensor you first need to initialize a Wire object.
Don't forget to also call the `.begin()` function with the right configuration.
Then call the static function `sendFrame()` form `SensirionI2CCommunication` as
shown below. You can find the ADDRESS on the data sheet of the sensor. You also
need to replace `WIREOBJECT` with the initialized Wire object. Then wait the in
the data sheet documented `READ_DELAY` before receiving the reply from the
sensor by calling `receiveFrame()` with the same Wire object.
You then can decode the frame by using the different get members to convert the
received data to desired data types.
All functions return a error code if an error occurs during execution and zero
otherwise.
```cpp
uint8_t txBuffer[256];
uint8_t rxBuffer[256];
SensirionShdlcTxFrame txFrame(txBuffer, 256);
SensirionShdlcRxFrame rxFrame(rxBuffer, 256);
txFrame.addCommand(COMMAND);
txFrame.addUInt8(UINT8);
txFrame.addUInt32(UINT32);
SensirionShdlcCommunication::sendFrame(ADDRESS, txFrame, WIREOBJECT);
delay(READ_DELAY);
SensirionShdlcCommunication::receiveFrame(ADDRESS, rxFrame, WIREOBJECT);
rxFrame.getUInt16(UINT16);
rxFrame.getFloat(FLOAT);
```

View File

@ -0,0 +1,62 @@
#include <SensirionCore.h>
#include <Wire.h>
#include <stdint.h>
uint8_t txBuffer[256];
uint8_t rxBuffer[256];
SensirionI2CTxFrame txFrame(txBuffer, 256);
SensirionI2CRxFrame rxFrame(rxBuffer, 256);
void setup() {
Wire.begin();
}
void loop() {
uint16_t mockCommand = 42;
uint16_t error = txFrame.addCommand(mockCommand);
uint32_t mockUInt32 = 42;
error |= txFrame.addUInt32(mockUInt32);
int32_t mockInt32 = 42;
error |= txFrame.addInt32(mockInt32);
uint16_t mockUInt16 = 42;
error |= txFrame.addUInt16(mockUInt16);
int16_t mockInt16 = 42;
error |= txFrame.addInt16(mockInt16);
uint8_t mockUInt8 = 42;
error |= txFrame.addUInt8(mockUInt8);
int8_t mockInt8 = 42;
error |= txFrame.addInt8(mockInt8);
float mockFloat = 42.0f;
error |= txFrame.addFloat(mockFloat);
bool mockBool = true;
error |= txFrame.addBool(mockBool);
uint8_t mockBytes[] = {42, 42, 42, 42};
error |= txFrame.addBytes(mockBytes, 4);
uint8_t mockAddress = 42;
error |= SensirionI2CCommunication::sendFrame(mockAddress, txFrame, Wire);
size_t mockNumBytes = 42;
error |= SensirionI2CCommunication::receiveFrame(mockAddress, mockNumBytes,
rxFrame, Wire);
error |= rxFrame.getUInt32(mockUInt32);
error |= rxFrame.getInt32(mockInt32);
error |= rxFrame.getUInt16(mockUInt16);
error |= rxFrame.getInt16(mockInt16);
error |= rxFrame.getUInt8(mockUInt8);
error |= rxFrame.getInt8(mockInt8);
error |= rxFrame.getFloat(mockFloat);
error |= rxFrame.getBytes(mockBytes, 4);
}

View File

@ -0,0 +1,73 @@
#include <SensirionCore.h>
#include <stdint.h>
uint8_t txBuffer[256];
uint8_t rxBuffer[256];
SensirionShdlcTxFrame txFrame(txBuffer, 256);
SensirionShdlcRxFrame rxFrame(rxBuffer, 256);
void setup() {
Serial.begin(115200);
}
void loop() {
uint8_t mockCommand = 42;
uint8_t mockAddress = 42;
uint8_t mockDataLength = 42;
uint16_t error = txFrame.begin(mockCommand, mockAddress, mockDataLength);
uint32_t mockUInt32 = 42;
error |= txFrame.addUInt32(mockUInt32);
int32_t mockInt32 = 42;
error |= txFrame.addInt32(mockInt32);
uint16_t mockUInt16 = 42;
error |= txFrame.addUInt16(mockUInt16);
int16_t mockInt16 = 42;
error |= txFrame.addInt16(mockInt16);
uint8_t mockUInt8 = 42;
error |= txFrame.addUInt8(mockUInt8);
int8_t mockInt8 = 42;
error |= txFrame.addInt8(mockInt8);
float mockFloat = 42.0f;
error |= txFrame.addFloat(mockFloat);
bool mockBool = true;
error |= txFrame.addBool(mockBool);
uint8_t mockBytes[] = {42, 42, 42, 42};
error |= txFrame.addBytes(mockBytes, 4);
error |= txFrame.finish();
error |= SensirionShdlcCommunication::sendFrame(txFrame, Serial);
error |= SensirionShdlcCommunication::sendAndReceiveFrame(
Serial, txFrame, rxFrame, 10000000);
error |=
SensirionShdlcCommunication::receiveFrame(rxFrame, Serial, 1000000);
error |= rxFrame.getUInt32(mockUInt32);
error |= rxFrame.getInt32(mockInt32);
error |= rxFrame.getUInt16(mockUInt16);
error |= rxFrame.getInt16(mockInt16);
error |= rxFrame.getUInt8(mockUInt8);
error |= rxFrame.getInt8(mockInt8);
error |= rxFrame.getFloat(mockFloat);
error |= rxFrame.getBytes(mockBytes, 4);
mockCommand = rxFrame.getCommand();
mockAddress = rxFrame.getAddress();
mockDataLength = rxFrame.getDataLength();
uint8_t mockState = rxFrame.getState();
if (mockState) {
// There is an error in the device.
}
}

View File

@ -0,0 +1,56 @@
#######################################
# Syntax Coloring Map
#######################################
#######################################
# Datatypes (KEYWORD1)
#######################################
SensirionShdlcCommunication KEYWORD1
SensirionShdlcRxFrame KEYWORD1
SensirionShdlcTxFrame KEYWORD1
SensirionI2CTxFrame KEYWORD1
SensirionI2CRxFrame KEYWORD1
SensirionI2CCommunication KEYWORD1
# SensirionErrors.h
HighLevelError KEYWORD1
LowLevelError KEYWORD1
#######################################
# Methods and Functions (KEYWORD2)
#######################################
sendFrame KEYWORD2
receiveFrame KEYWORD2
sendAndReceiveFrame KEYWORD2
addUInt32 KEYWORD2
addInt32 KEYWORD2
addUInt16 KEYWORD2
addInt16 KEYWORD2
addUInt8 KEYWORD2
addInt8 KEYWORD2
addFloat KEYWORD2
addBytes KEYWORD2
addBool KEYWORD2
addCommand KEYWORD2
begin KEYWORD2
finish KEYWORD2
reset KEYWORD2
getUInt32 KEYWORD2
getInt32 KEYWORD2
getUInt16 KEYWORD2
getInt16 KEYWORD2
getUInt8 KEYWORD2
getInt8 KEYWORD2
getFloat KEYWORD2
getBytes KEYWORD2
getCommand KEYWORD2
getAddress KEYWORD2
getDataLength KEYWORD2
getState KEYWORD2
# SensirionErrors.h
errorToString KEYWORD2
#######################################
# Constants (LITERAL1)
#######################################

View File

@ -0,0 +1,9 @@
name=Sensirion Core
version=0.6.0
author=Sensirion
maintainer=Sensirion
sentence=Library containing code base for Sensirion Sensor Libraries.
paragraph=All Libraries for Sensirion Sensors use this library as a code base. In this library the Sensirion specific parts for I2C and UART communication are implemented. It provides dynamic frame construction, checksum calculation and buffer handling.
category=Communication
url=https://github.com/Sensirion/arduino-core/
includes=SensirionCore.h

View File

@ -0,0 +1,46 @@
/*
* Copyright (c) 2021, Sensirion AG
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* * Neither the name of Sensirion AG nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef _SENSIRION_CORE_H_
#define _SENSIRION_CORE_H_
#include "SensirionCrc.h"
#include "SensirionErrors.h"
#include "SensirionRxFrame.h"
#include "SensirionShdlcCommunication.h"
#include "SensirionShdlcRxFrame.h"
#include "SensirionShdlcTxFrame.h"
#include "SensirionI2CCommunication.h"
#include "SensirionI2CRxFrame.h"
#include "SensirionI2CTxFrame.h"
#endif /* _SENSIRION_CORE_H_ */

View File

@ -0,0 +1,51 @@
/*
*
* THIS IS A LEGACY FILE AND WILL BE REMOVED SOON.
*
* Copyright (c) 2020, Sensirion AG
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* * Neither the name of Sensirion AG nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef _SENSIRION_CORE_ARDUINO_LIBRARY_H_
#define _SENSIRION_CORE_ARDUINO_LIBRARY_H_
#pragma GCC warning \
"Legacy file SensirionCoreArdunioLibrary.h included. Please include SensirionCore.h instead."
#include "SensirionErrors.h"
#include "SensirionRxFrame.h"
#include "SensirionShdlcCommunication.h"
#include "SensirionShdlcRxFrame.h"
#include "SensirionShdlcTxFrame.h"
#include "SensirionI2CCommunication.h"
#include "SensirionI2CRxFrame.h"
#include "SensirionI2CTxFrame.h"
#endif /* _SENSIRION_CORE_ARDUION_LIBRARY_H_ */

View File

@ -0,0 +1,64 @@
/*
* Copyright (c) 2022, Sensirion AG
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* * Neither the name of Sensirion AG nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
#include "SensirionCrc.h"
uint8_t generateCRCGeneric(const uint8_t* data, size_t count, uint8_t init,
uint8_t polynomial) {
uint8_t crc = init;
/* calculates 8-Bit checksum with given polynomial */
for (size_t current_byte = 0; current_byte < count; ++current_byte) {
crc ^= (data[current_byte]);
for (uint8_t crc_bit = 8; crc_bit > 0; --crc_bit) {
if (crc & 0x80)
crc = (crc << 1) ^ polynomial;
else
crc = (crc << 1);
}
}
return crc;
}
uint8_t generateCRC31_ff(const uint8_t* data, size_t count) {
return generateCRCGeneric(data, count, 0xff, 0x31);
}
uint8_t generateCRC31_00(const uint8_t* data, size_t count) {
return generateCRCGeneric(data, count, 0x00, 0x31);
}
uint8_t generateCRC(const uint8_t* data, size_t count, CrcPolynomial type) {
if (CRC31_00 == type) {
return generateCRC31_00(data, count);
}
return generateCRC31_ff(data, count);
}

View File

@ -0,0 +1,59 @@
/*
* Copyright (c) 2022, Sensirion AG
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* * Neither the name of Sensirion AG nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef _SENSIRION_CRC_H_
#define _SENSIRION_CRC_H_
#include <stdint.h>
#include <stdlib.h>
enum CrcPolynomial : uint8_t {
CRC31_00 = 0x0,
CRC31_ff = 0x1,
};
uint8_t generateCRCGeneric(const uint8_t* data, size_t count, uint8_t init,
uint8_t polynomial);
uint8_t generateCRC31_ff(const uint8_t* data, size_t count);
uint8_t generateCRC31_00(const uint8_t* data, size_t count);
/**
* @brief Generate a crc for data given a polynomial type
*
* @param data data to calculate CRC for
* @param count the array size of data
* @param poly CRC polynomal to use
*/
uint8_t generateCRC(const uint8_t* data, size_t count, CrcPolynomial type);
#endif /* _SENSIRION_CRC_H_ */

View File

@ -0,0 +1,148 @@
/*
* Copyright (c) 2020, Sensirion AG
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* * Neither the name of Sensirion AG nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
#include "SensirionErrors.h"
#include <stdint.h>
#include <stdio.h>
#include <string.h>
void errorToString(uint16_t error, char errorMessage[],
size_t errorMessageSize) {
uint16_t highLevelError = error & 0xFF00;
uint16_t lowLevelError = error & 0x00FF;
if (error & HighLevelError::SensorSpecificError) {
snprintf(errorMessage, errorMessageSize, "Sensor specific error: 0x%2x",
lowLevelError);
return;
}
switch (highLevelError) {
case HighLevelError::NoError:
if (!error) {
strncpy(errorMessage, "No error", errorMessageSize);
return;
}
break;
case HighLevelError::WriteError:
switch (lowLevelError) {
case LowLevelError::SerialWriteError:
strncpy(errorMessage, "Error writing to serial",
errorMessageSize);
return;
case LowLevelError::InternalBufferSizeError:
strncpy(errorMessage,
"Data too long to fit in transmit buffer",
errorMessageSize);
return;
case LowLevelError::I2cAddressNack:
strncpy(errorMessage,
"Received NACK on transmit of address",
errorMessageSize);
return;
case LowLevelError::I2cDataNack:
strncpy(errorMessage, "Received NACK on transmit of data",
errorMessageSize);
return;
case LowLevelError::I2cOtherError:
strncpy(errorMessage, "Error writing to I2C bus",
errorMessageSize);
return;
}
break;
case HighLevelError::ReadError:
switch (lowLevelError) {
case LowLevelError::NonemptyFrameError:
strncpy(errorMessage, "Frame already contains data",
errorMessageSize);
return;
case LowLevelError::TimeoutError:
strncpy(errorMessage, "Timeout while reading data",
errorMessageSize);
return;
case LowLevelError::ChecksumError:
strncpy(errorMessage, "Checksum is wrong",
errorMessageSize);
return;
case LowLevelError::CRCError:
strncpy(errorMessage, "Wrong CRC found", errorMessageSize);
return;
case LowLevelError::WrongNumberBytesError:
strncpy(errorMessage, "Number of bytes not a multiple of 3",
errorMessageSize);
return;
case LowLevelError::NotEnoughDataError:
strncpy(errorMessage, "Not enough data received",
errorMessageSize);
return;
case LowLevelError::InternalBufferSizeError:
strncpy(errorMessage, "Internal I2C buffer too small",
errorMessageSize);
return;
}
break;
case HighLevelError::ExecutionError: {
char format[] = "Execution error, status register: 0x%x";
snprintf(errorMessage, errorMessageSize, format, lowLevelError);
return;
}
case HighLevelError::TxFrameError:
switch (lowLevelError) {
case LowLevelError::BufferSizeError:
strncpy(errorMessage, "Not enough space in buffer",
errorMessageSize);
return;
}
break;
case HighLevelError::RxFrameError:
switch (lowLevelError) {
case LowLevelError::BufferSizeError:
strncpy(errorMessage, "Not enough space in buffer",
errorMessageSize);
return;
case LowLevelError::NoDataError:
strncpy(errorMessage, "No more data in frame",
errorMessageSize);
return;
case LowLevelError::RxAddressError:
strncpy(errorMessage, "Wrong address in return frame",
errorMessageSize);
return;
case LowLevelError::RxCommandError:
strncpy(errorMessage, "Wrong command in return frame",
errorMessageSize);
return;
}
}
strncpy(errorMessage, "Error processing error", errorMessageSize);
return;
}

View File

@ -0,0 +1,87 @@
/*
* Copyright (c) 2020, Sensirion AG
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* * Neither the name of Sensirion AG nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef _SENSIRION_ERRORS_H_
#define _SENSIRION_ERRORS_H_
#include <stdint.h>
#include <stdlib.h>
enum HighLevelError : uint16_t {
// general errors
NoError = 0,
WriteError = 0x0100,
ReadError = 0x0200,
TxFrameError = 0x0300,
RxFrameError = 0x0400,
// shdlc errors
ExecutionError = 0x0500,
// i2c errors
// Sensor specific errors. All errors higher than that are depending on the
// sensor used.
SensorSpecificError = 0x8000,
};
enum LowLevelError : uint16_t {
// general errors
NonemptyFrameError,
NoDataError,
BufferSizeError,
// shdlc errors
StopByteError,
ChecksumError,
TimeoutError,
RxCommandError,
RxAddressError,
SerialWriteError,
// i2c errors
WrongNumberBytesError,
CRCError,
I2cAddressNack,
I2cDataNack,
I2cOtherError,
NotEnoughDataError,
InternalBufferSizeError,
};
/**
* errorToString() - Convert error code to a human readable error message
*
* @param error Error code to be converted.
* @param errorMessage String where the error text can be
* stored.
* @param errorMessageSize Size in bytes of the string buffer for the error
* message.
*/
void errorToString(uint16_t error, char errorMessage[],
size_t errorMessageSize);
#endif /* _SENSIRION_ERRORS_H_ */

View File

@ -0,0 +1,119 @@
/*
* Copyright (c) 2020, Sensirion AG
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* * Neither the name of Sensirion AG nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
#include "SensirionI2CCommunication.h"
#include <stdint.h>
#include <stdlib.h>
#include "Arduino.h"
#include "SensirionCrc.h"
#include "SensirionErrors.h"
#include "SensirionI2CRxFrame.h"
#include "SensirionI2CTxFrame.h"
static void clearRxBuffer(TwoWire& i2cBus) {
while (i2cBus.available()) {
(void)i2cBus.read();
}
}
uint16_t SensirionI2CCommunication::sendFrame(uint8_t address,
SensirionI2CTxFrame& frame,
TwoWire& i2cBus) {
i2cBus.beginTransmission(address);
size_t writtenBytes = i2cBus.write(frame._buffer, frame._index);
uint8_t i2c_error = i2cBus.endTransmission();
if (writtenBytes != frame._index) {
return WriteError | I2cOtherError;
}
// translate Arduino errors, see
// https://www.arduino.cc/en/Reference/WireEndTransmission
switch (i2c_error) {
case 0:
return NoError;
case 1:
return WriteError | InternalBufferSizeError;
case 2:
return WriteError | I2cAddressNack;
case 3:
return WriteError | I2cDataNack;
default:
return WriteError | I2cOtherError;
}
}
uint16_t SensirionI2CCommunication::receiveFrame(uint8_t address,
size_t numBytes,
SensirionI2CRxFrame& frame,
TwoWire& i2cBus,
CrcPolynomial poly) {
size_t readAmount;
size_t i = 0;
#ifdef I2C_BUFFER_LENGTH
const uint8_t sizeBuffer =
(static_cast<uint8_t>(I2C_BUFFER_LENGTH) / static_cast<uint8_t>(3)) * 3;
#elif defined(BUFFER_LENGTH)
const uint8_t sizeBuffer =
(static_cast<uint8_t>(BUFFER_LENGTH) / static_cast<uint8_t>(3)) * 3;
#else
const uint8_t sizeBuffer = 30;
#endif
if (numBytes % 3) {
return ReadError | WrongNumberBytesError;
}
if ((numBytes / 3) * 2 > frame._bufferSize) {
return ReadError | BufferSizeError;
}
if (numBytes > sizeBuffer) {
return ReadError | InternalBufferSizeError;
}
readAmount = i2cBus.requestFrom(address, static_cast<uint8_t>(numBytes),
static_cast<uint8_t>(true));
if (numBytes != readAmount) {
return ReadError | NotEnoughDataError;
}
do {
frame._buffer[i++] = i2cBus.read();
frame._buffer[i++] = i2cBus.read();
uint8_t actualCRC = i2cBus.read();
uint8_t expectedCRC = generateCRC(&frame._buffer[i - 2], 2, poly);
if (actualCRC != expectedCRC) {
clearRxBuffer(i2cBus);
return ReadError | CRCError;
}
readAmount -= 3;
} while (readAmount > 0);
frame._numBytes = i;
return NoError;
}

View File

@ -0,0 +1,83 @@
/*
* Copyright (c) 2020, Sensirion AG
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* * Neither the name of Sensirion AG nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef SENSIRION_I2C_COMMUNICATION_H_
#define SENSIRION_I2C_COMMUNICATION_H_
#include <stdint.h>
#include <stdlib.h>
#include "Arduino.h"
#include "Wire.h"
#include "SensirionI2CRxFrame.h"
#include "SensirionI2CTxFrame.h"
class SensirionI2CTxFrame;
class SensirionI2CRxFrame;
/*
* SensirionI2CCommunication - Class which is responsible for the communication
* via a I2C bus. It provides functionality to send and receive frames from a
* Sensirion sensor. The data is sent and received in a SensirionI2cTxFrame or
* SensirionI2cRxFrame respectively.
*/
class SensirionI2CCommunication {
public:
/**
* sendFrame() - Sends frame to sensor
*
* @param address I2C address of the sensor.
* @param frame Tx frame object containing a finished frame to send to
* the sensor.
* @param i2cBus TwoWire object to communicate with the sensor.
*
* @return NoError on success, an error code otherwise
*/
static uint16_t sendFrame(uint8_t address, SensirionI2CTxFrame& frame,
TwoWire& i2cBus);
/**
* receiveFrame() - Receive Frame from sensor
*
* @param address I2C address of the sensor.
* @param numBytes Number of bytes to receive.
* @param frame Rx frame to store the received data in.
* @param i2cBus TwoWire object to communicate with the sensor.
* @param poly CRC polynomal to use. Defaults to 0x31 with init 0xFF
*
* @return NoError on success, an error code otherwise
*/
static uint16_t receiveFrame(uint8_t address, size_t numBytes,
SensirionI2CRxFrame& frame, TwoWire& i2cBus,
CrcPolynomial poly = CRC31_ff);
};
#endif /* SENSIRION_I2C_COMMUNICATION_H_ */

View File

@ -0,0 +1,64 @@
/*
* Copyright (c) 2020, Sensirion AG
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* * Neither the name of Sensirion AG nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef SENSIRION_I2C_RX_FRAME_H_
#define SENSIRION_I2C_RX_FRAME_H_
#include <stdint.h>
#include <stdlib.h>
#include "SensirionI2CCommunication.h"
#include "SensirionRxFrame.h"
/**
* SenirionI2CRxFrame - Class which decodes the through I2C received data into
* common data types. It contains a buffer which is filled by the
* SensirionI2CCommunication class. By calling the different decode function
* inherited from the SensirionRxFrame base class the raw data can be decoded
* into different data types.
*/
class SensirionI2CRxFrame : public SensirionRxFrame {
friend class SensirionI2CCommunication;
public:
/**
* Constructor
*
* @param buffer Buffer in which the receive frame will be
* stored.
* @param bufferSize Number of bytes in the buffer for the receive frame.
*
*/
SensirionI2CRxFrame(uint8_t buffer[], size_t bufferSize)
: SensirionRxFrame(buffer, bufferSize){};
};
#endif /* SENSIRION_I2C_RX_FRAME_H_ */

View File

@ -0,0 +1,144 @@
/*
* Copyright (c) 2020, Sensirion AG
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright notice,
* list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* * Neither the name of Sensirion AG nor the names of its
* contributors may be used to endorse or promote products derived from
* software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
#include "SensirionI2CTxFrame.h"
#include <stdint.h>
#include <stdlib.h>
#include "SensirionCrc.h"
#include "SensirionErrors.h"
SensirionI2CTxFrame::SensirionI2CTxFrame(uint8_t buffer[], size_t bufferSize,
size_t numCommandBytes,
CrcPolynomial poly)
: _buffer(buffer), _bufferSize(bufferSize), _index(numCommandBytes),
_numCommandBytes(numCommandBytes), _polynomial_type(poly) {
}
SensirionI2CTxFrame::SensirionI2CTxFrame(uint8_t buffer[], size_t bufferSize,
CrcPolynomial poly)
: SensirionI2CTxFrame(buffer, bufferSize, 2, poly) {
}
SensirionI2CTxFrame SensirionI2CTxFrame::createWithUInt8Command(
uint8_t command, uint8_t buffer[], size_t bufferSize, CrcPolynomial poly) {
SensirionI2CTxFrame instance =
SensirionI2CTxFrame(buffer, bufferSize, 1, poly);
instance._buffer[0] = command;
return instance;
}
SensirionI2CTxFrame SensirionI2CTxFrame::createWithUInt16Command(
uint16_t command, uint8_t buffer[], size_t bufferSize, CrcPolynomial poly) {
SensirionI2CTxFrame instance =
SensirionI2CTxFrame(buffer, bufferSize, 2, poly);
instance._buffer[0] = static_cast<uint8_t>((command & 0xFF00) >> 8);
instance._buffer[1] = static_cast<uint8_t>((command & 0x00FF) >> 0);
return instance;
}
uint16_t SensirionI2CTxFrame::addCommand(uint16_t command) {
if (_bufferSize < 2) {
return TxFrameError | BufferSizeError;
}
_buffer[0] = static_cast<uint8_t>((command & 0xFF00) >> 8);
_buffer[1] = static_cast<uint8_t>((command & 0x00FF) >> 0);
return NoError;
}
uint16_t SensirionI2CTxFrame::addUInt32(uint32_t data) {
uint16_t error = _addByte(static_cast<uint8_t>((data & 0xFF000000) >> 24));
error |= _addByte(static_cast<uint8_t>((data & 0x00FF0000) >> 16));
error |= _addByte(static_cast<uint8_t>((data & 0x0000FF00) >> 8));
error |= _addByte(static_cast<uint8_t>((data & 0x000000FF) >> 0));
return error;
}
uint16_t SensirionI2CTxFrame::addInt32(int32_t data) {
return addUInt32(static_cast<uint32_t>(data));
}
uint16_t SensirionI2CTxFrame::addUInt16(uint16_t data) {
uint16_t error = _addByte(static_cast<uint8_t>((data & 0xFF00) >> 8));
error |= _addByte(static_cast<uint8_t>((data & 0x00FF) >> 0));
return error;
}
uint16_t SensirionI2CTxFrame::addInt16(int16_t data) {
return addUInt16(static_cast<uint16_t>(data));
}
uint16_t SensirionI2CTxFrame::addUInt8(uint8_t data) {
return _addByte(data);
}
uint16_t SensirionI2CTxFrame::addInt8(int8_t data) {
return _addByte(static_cast<uint8_t>(data));
}
uint16_t SensirionI2CTxFrame::addBool(bool data) {
return _addByte(static_cast<uint8_t>(data));
}
uint16_t SensirionI2CTxFrame::addFloat(float data) {
union {
uint32_t uInt32Data;
float floatData;
} convert;
convert.floatData = data;
return addUInt32(convert.uInt32Data);
}
uint16_t SensirionI2CTxFrame::addBytes(const uint8_t data[],
size_t dataLength) {
uint16_t error = 0;
for (size_t i = 0; i < dataLength; i++) {
error |= _addByte(data[i]);
}
return error;
}
uint16_t SensirionI2CTxFrame::_addByte(uint8_t data) {
if (_bufferSize <= _index) {
return TxFrameError | BufferSizeError;
}
_buffer[_index++] = data;
if ((_index - _numCommandBytes) % 3 == 2) {
if (_bufferSize <= _index) {
return TxFrameError | BufferSizeError;
}
uint8_t crc = generateCRC(&_buffer[_index - 2], 2, _polynomial_type);
_buffer[_index++] = crc;
}
return NoError;
}

View File

@ -0,0 +1,197 @@
/*
* Copyright (c) 2020, Sensirion AG
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* * Neither the name of Sensirion AG nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef SENSIRION_I2C_TX_FRAME_H_
#define SENSIRION_I2C_TX_FRAME_H_
#include <stdint.h>
#include <stdlib.h>
#include "SensirionCrc.h"
#include "SensirionI2CCommunication.h"
/*
* SensirionI2CTxFrame - Class which helps to build a correct I2C frame for
* Sensirion Sensors. The different addDatatype() functions add the frame data
* and the addCommand() function writes the command at the beginning. Using
* these functions one can easily construct a I2C frame for Sensirion sensors.
*/
class SensirionI2CTxFrame {
friend class SensirionI2CCommunication;
public:
/**
* Factory to create a SensirionI2CTxFrame using a UInt8 command.
*
* @param command Command to add to the send frame.
* @param buffer Buffer in which the send frame will be stored.
* @param bufferSize Number of bytes in the buffer for the send frame.
* @param poly CRC polynomal to use. Defaults to 0x31 with init 0xFF
*
* @return the constructed SensirionI2CTxFrame.
*/
static SensirionI2CTxFrame
createWithUInt8Command(uint8_t command, uint8_t buffer[], size_t bufferSize,
CrcPolynomial poly = CRC31_ff);
/**
* Factory to create a SensirionI2CTxFrame using a UInt16 command.
*
* @param command Command to add to the send frame.
* @param buffer Buffer in which the send frame will be stored.
* @param bufferSize Number of bytes in the buffer for the send frame.
* @param poly CRC polynomal to use. Defaults to 0x31 with init 0xFF
*
* @return the constructed SensirionI2CTxFrame.
*/
static SensirionI2CTxFrame
createWithUInt16Command(uint16_t command, uint8_t buffer[],
size_t bufferSize, CrcPolynomial poly = CRC31_ff);
/**
* Constructor
*
* @param buffer Buffer in which the send frame will be stored.
* @param bufferSize Number of bytes in the buffer for the send frame.
* @param poly CRC polynomal to use. Defaults to 0x31 with init 0xFF
*
* @deprecated Use createWithUInt16Command() instead
*/
SensirionI2CTxFrame(uint8_t buffer[], size_t bufferSize,
CrcPolynomial poly = CRC31_ff);
/**
* addCommand() - Add command to the send frame.
*
* @param command Command to add to the send frame.
*
* @return NoError on success, an error code otherwise
*
* @deprecated Use createWithUInt16Command() instead
*/
uint16_t addCommand(uint16_t command);
/**
* addUInt32() - Add unsigned 32bit integer to the send frame.
*
* @param data Unsigned 32bit integer to add to the send frame.
*
* @return NoError on success, an error code otherwise
*/
uint16_t addUInt32(uint32_t data);
/**
* addInt32() - Add signed 32bit integer to the send frame.
*
* @param data Signed 32bit integer to add to the send frame.
*
* @return NoError on success, an error code otherwise
*/
uint16_t addInt32(int32_t data);
/**
* addUInt16() - Add unsigned 16bit integer to the send frame.
*
* @param data Unsigned 16bit integer to add to the send frame.
*
* @return NoError on success, an error code otherwise
*/
uint16_t addUInt16(uint16_t data);
/**
* addInt16() - Add signed 16bit integer to the send frame.
*
* @param data Signed 16bit integer to add to the send frame.
*
* @return NoError on success, an error code otherwise
*/
uint16_t addInt16(int16_t data);
/**
* addUInt8() - Add unsigned 8bit integer to the send frame.
*
* @param data Unsigned 8bit integer to add to the send frame.
*
* @return NoError on success, an error code otherwise
*/
uint16_t addUInt8(uint8_t data);
/**
* addInt8() - Add signed 8bit integer to the send frame.
*
* @param data Signed 8bit integer to add to the send frame.
*
* @return NoError on success, an error code otherwise
*/
uint16_t addInt8(int8_t data);
/**
* addBool() - Add boolean to the send frame.
*
* @param data Boolean to add to the send frame.
*
* @return NoError on success, an error code otherwise
*/
uint16_t addBool(bool data);
/**
* addFloat() - Add float to the send frame.
*
* @param data Float to add to the send frame.
*
* @return NoError on success, an error code otherwise
*/
uint16_t addFloat(float data);
/**
* addBytes() - Add byte array to the send frame.
*
* @param data Byte array to add to the send frame.
* @param dataLength Number of bytes to add to the send frame.
*
* @return NoError on success, an error code otherwise
*/
uint16_t addBytes(const uint8_t data[], size_t dataLength);
private:
SensirionI2CTxFrame(uint8_t buffer[], size_t bufferSize,
size_t numCommandBytes, CrcPolynomial poly = CRC31_ff);
uint16_t _addByte(uint8_t data);
uint8_t* _buffer;
size_t _bufferSize;
size_t _index;
size_t _numCommandBytes;
CrcPolynomial _polynomial_type;
};
#endif /* SENSIRION_I2C_TX_FRAME_H_ */

View File

@ -0,0 +1,129 @@
/*
* Copyright (c) 2020, Sensirion AG
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* * Neither the name of Sensirion AG nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
#include "SensirionRxFrame.h"
#include <stdint.h>
#include <stdlib.h>
#include "SensirionErrors.h"
SensirionRxFrame::SensirionRxFrame(uint8_t buffer[], size_t bufferSize)
: _buffer(buffer), _bufferSize(bufferSize), _index(0), _numBytes(0) {
}
uint16_t SensirionRxFrame::getUInt32(uint32_t& data) {
if (_numBytes < 4) {
return RxFrameError | NoDataError;
}
data = static_cast<uint32_t>(_buffer[_index++]) << 24;
data |= static_cast<uint32_t>(_buffer[_index++]) << 16;
data |= static_cast<uint32_t>(_buffer[_index++]) << 8;
data |= static_cast<uint32_t>(_buffer[_index++]);
_numBytes -= 4;
return NoError;
}
uint16_t SensirionRxFrame::getInt32(int32_t& data) {
uint32_t ret;
uint16_t error = getUInt32(ret);
data = static_cast<int32_t>(ret);
return error;
}
uint16_t SensirionRxFrame::getUInt16(uint16_t& data) {
if (_numBytes < 2) {
return RxFrameError | NoDataError;
}
data = static_cast<uint16_t>(_buffer[_index++]) << 8;
data |= static_cast<uint16_t>(_buffer[_index++]);
_numBytes -= 2;
return NoError;
}
uint16_t SensirionRxFrame::getInt16(int16_t& data) {
uint16_t ret;
uint16_t error = getUInt16(ret);
data = static_cast<int16_t>(ret);
return error;
}
uint16_t SensirionRxFrame::getUInt8(uint8_t& data) {
if (_numBytes < 1) {
return RxFrameError | NoDataError;
}
data = _buffer[_index++];
_numBytes -= 1;
return NoError;
}
uint16_t SensirionRxFrame::getInt8(int8_t& data) {
if (_numBytes < 1) {
return RxFrameError | NoDataError;
}
data = static_cast<int8_t>(_buffer[_index++]);
_numBytes -= 1;
return NoError;
}
uint16_t SensirionRxFrame::getBool(bool& data) {
if (_numBytes < 1) {
return RxFrameError | NoDataError;
}
data = static_cast<bool>(_buffer[_index++]);
_numBytes -= 1;
return NoError;
}
uint16_t SensirionRxFrame::getFloat(float& data) {
union {
uint32_t uInt32Data;
float floatData;
} convert;
uint16_t error = getUInt32(convert.uInt32Data);
data = convert.floatData;
return error;
}
uint16_t SensirionRxFrame::getBytes(uint8_t data[], size_t maxBytes) {
if (_numBytes < 1) {
return RxFrameError | NoDataError;
}
size_t readAmount = maxBytes;
if (_numBytes < maxBytes) {
readAmount = _numBytes;
}
for (size_t i = 0; i < readAmount; i++) {
data[i] = _buffer[_index++];
}
_numBytes -= readAmount;
return NoError;
}

View File

@ -0,0 +1,147 @@
/*
* Copyright (c) 2020, Sensirion AG
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* * Neither the name of Sensirion AG nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef SENSIRION_RX_FRAME_H_
#define SENSIRION_RX_FRAME_H_
#include <stdint.h>
#include <stdlib.h>
/**
* SenirionRxFrame - Base class for SensirionShdlcRxFrame and
* SensirionI2cRxFrame. It decodes received data into common data types. The
* data is contained in a buffer which is filled by the one of the two
* communication classes. By calling the different decode function the raw data
* can be decoded into different data types.
*/
class SensirionRxFrame {
friend class SensirionI2CCommunication;
friend class SensirionShdlcCommunication;
public:
/**
* Constructor
*
* @param buffer Buffer in which the receive frame will be stored.
* @param bufferSize Number of bytes in the buffer for the receive frame.
*/
SensirionRxFrame(uint8_t buffer[], size_t bufferSize);
/**
* getUInt32() - Get unsigned 32bit integer from the received data.
*
* @param data Memory to store unsigned 32bit integer in.
*
* @return NoError on success, an error code otherwise
*/
uint16_t getUInt32(uint32_t& data);
/**
* getInt32() - Get signed 32bit integer from the received data.
*
* @param data Memory to store signed 32bit integer in.
*
* @return NoError on success, an error code otherwise
*/
uint16_t getInt32(int32_t& data);
/**
* getUInt16() - Get unsigned 16bit integer from the received data.
*
* @param data Memory to store unsigned 16bit integer in.
*
* @return NoError on success, an error code otherwise
*/
uint16_t getUInt16(uint16_t& data);
/**
* getInt16() - Get signed 16bit integer from the received data.
*
* @param data Memory to store signed 16bit integer in.
*
* @return NoError on success, an error code otherwise
*/
uint16_t getInt16(int16_t& data);
/**
* getUInt8() - Get unsigned 8bit integer from the received data.
*
* @param data Memory to store unsigned 8bit integer in.
*
* @return NoError on success, an error code otherwise
*/
uint16_t getUInt8(uint8_t& data);
/**
* getInt8() - Get signed 8bit integer from the received data.
*
* @param data Memory to store signed 8bit integer in.
*
* @return NoError on success, an error code otherwise
*/
uint16_t getInt8(int8_t& data);
/**
* getBool() - Get Boolean from the received data.
*
* @param data Memory to store boolean in.
*
* @return NoError on success, an error code otherwise
*/
uint16_t getBool(bool& data);
/**
* getFloat() - Get float from the received data.
*
* @param data Memory to store float in.
*
* @return NoError on success, an error code otherwise
*/
uint16_t getFloat(float& data);
/**
* getBytes() - Get an array of bytes from the received data.
*
* @param data Buffer to store the bytes in.
* @param maxBytes Maximal amount of bytes to read from the received data.
*
* @return NoError on success, an error code otherwise
*/
uint16_t getBytes(uint8_t data[], size_t maxBytes);
private:
uint8_t* _buffer = 0;
size_t _bufferSize = 0;
size_t _index = 0;
size_t _numBytes = 0;
};
#endif /* SENSIRION_RX_FRAME_H_ */

View File

@ -0,0 +1,184 @@
/*
* Copyright (c) 2020, Sensirion AG
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* * Neither the name of Sensirion AG nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
#include "SensirionShdlcCommunication.h"
#include <stdint.h>
#include <stdlib.h>
#include "Arduino.h"
#include "SensirionErrors.h"
#include "SensirionShdlcRxFrame.h"
#include "SensirionShdlcTxFrame.h"
static uint16_t readByte(uint8_t& data, Stream& serial, unsigned long startTime,
unsigned long timeoutMicros) {
do {
if (micros() - startTime > timeoutMicros) {
return ReadError | TimeoutError;
}
} while (!serial.available());
data = serial.read();
return NoError;
}
static uint16_t unstuffByte(uint8_t& data, Stream& serial,
unsigned long startTime,
unsigned long timeoutMicros) {
uint16_t error = readByte(data, serial, startTime, timeoutMicros);
if (error) {
return error;
}
if (data == 0x7d) {
error = readByte(data, serial, startTime, timeoutMicros);
if (error) {
return error;
}
data = data ^ (1 << 5);
}
return NoError;
}
uint16_t SensirionShdlcCommunication::sendFrame(SensirionShdlcTxFrame& frame,
Stream& serial) {
size_t writtenBytes = serial.write(&frame._buffer[0], frame._index);
if (writtenBytes != frame._index) {
return WriteError | SerialWriteError;
}
return NoError;
}
uint16_t SensirionShdlcCommunication::receiveFrame(
SensirionShdlcRxFrame& frame, Stream& serial, unsigned long timeoutMicros) {
unsigned long startTime = micros();
uint16_t error;
uint8_t dataLength;
uint8_t current = 0;
if (frame._numBytes) {
return ReadError | NonemptyFrameError;
}
// Wait for start byte and ignore all other bytes in case a partial frame
// is still in the receive buffer due to a previous error.
do {
error = readByte(current, serial, startTime, timeoutMicros);
if (error) {
return error;
}
} while (current != 0x7e);
// Handle a repeated start byte which may happen
do {
error = unstuffByte(current, serial, startTime, timeoutMicros);
if (error) {
return error;
}
} while (current == 0x7e);
frame._address = current;
error = unstuffByte(frame._command, serial, startTime, timeoutMicros);
if (error) {
return error;
}
error = unstuffByte(frame._state, serial, startTime, timeoutMicros);
if (error) {
return error;
}
error = unstuffByte(dataLength, serial, startTime, timeoutMicros);
if (error) {
return error;
}
uint8_t checksum =
frame._address + frame._command + frame._state + dataLength;
if (dataLength > frame._bufferSize) {
return RxFrameError | BufferSizeError;
}
size_t i = 0;
while (i < dataLength) {
error = unstuffByte(current, serial, startTime, timeoutMicros);
if (error) {
return error;
}
frame._buffer[i] = current;
checksum += current;
i++;
}
uint8_t expectedChecksum = ~checksum;
uint8_t actualChecksum;
error = unstuffByte(actualChecksum, serial, startTime, timeoutMicros);
if (error) {
return error;
}
if (expectedChecksum != actualChecksum) {
return ReadError | ChecksumError;
}
uint8_t stop;
error = readByte(stop, serial, startTime, timeoutMicros);
if (error) {
return error;
}
if (stop != 0x7e) {
return ReadError | StopByteError;
}
if (frame._state & 0x7F) {
return ExecutionError | frame._state;
}
frame._dataLength = dataLength;
frame._numBytes = dataLength;
return NoError;
}
uint16_t SensirionShdlcCommunication::sendAndReceiveFrame(
Stream& serial, SensirionShdlcTxFrame& txFrame,
SensirionShdlcRxFrame& rxFrame, unsigned long rxTimeoutMicros) {
uint16_t error;
error = SensirionShdlcCommunication::sendFrame(txFrame, serial);
if (error) {
return error;
}
error = SensirionShdlcCommunication::receiveFrame(rxFrame, serial,
rxTimeoutMicros);
if (error) {
return error;
}
if (rxFrame.getCommand() != txFrame.getCommand()) {
return RxFrameError | RxCommandError;
}
if (rxFrame.getAddress() != txFrame.getAddress()) {
return RxFrameError | RxAddressError;
}
return NoError;
}

View File

@ -0,0 +1,95 @@
/*
* Copyright (c) 2020, Sensirion AG
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* * Neither the name of Sensirion AG nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef SENSIRION_SHDLC_COMMUNICATION_H_
#define SENSIRION_SHDLC_COMMUNICATION_H_
#include <stdint.h>
#include <stdlib.h>
#include "Arduino.h"
#include "SensirionShdlcRxFrame.h"
#include "SensirionShdlcTxFrame.h"
class SensirionShdlcTxFrame;
class SensirionShdlcRxFrame;
/*
* SensirionShdlcCommunication - Class which is responsible for the
* communication via a UART (Serial) interface. It provides functionality to
* send and receive frames from a Sensirion sensor. The data is sent and
* received in a SensirionShdlcTxFrame or SensirionShdlcRxFrame respectively.
*/
class SensirionShdlcCommunication {
public:
/**
* sendFrame() - Sends frame to sensor
*
* @param frame Tx frame object containing a finished frame to send to the
* sensor.
* @param serial Stream object to communicate with the sensor.
*
* @return NoError on success, an error code otherwise
*/
static uint16_t sendFrame(SensirionShdlcTxFrame& frame, Stream& serial);
/**
* receiveFrame() - Receive Frame from sensor
*
* @param frame Rx frame to store the received data in.
* @param serial Stream object to communicate with the sensor.
* @param timeoutMicros Timeout in micro seconds for the receive operation.
*
* @return NoError on success, an error code otherwise
*/
static uint16_t receiveFrame(SensirionShdlcRxFrame& frame, Stream& serial,
unsigned long timeoutMicros);
/**
* sendAndReceiveFrame() - Send and receive a frame from sensor.
*
* @param serial Stream object to communicate with the sensor.
* @param txFrame Tx frame object containing a finished frame to
* send to the sensor.
* @param rxFrame Rx frame to store the received data in.
* @param rxTimeoutMicros Timeout in micro seconds for the receive
* operation.
*
* @return NoError on success, an error code otherwise
*/
static uint16_t sendAndReceiveFrame(Stream& serial,
SensirionShdlcTxFrame& txFrame,
SensirionShdlcRxFrame& rxFrame,
unsigned long rxTimeoutMicros);
};
#endif /* SENSIRION_SHDLC_COMMUNICATION_H_ */

View File

@ -0,0 +1,86 @@
/*
* Copyright (c) 2020, Sensirion AG
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* * Neither the name of Sensirion AG nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef SENSIRION_SHDLC_RX_FRAME_H_
#define SENSIRION_SHDLC_RX_FRAME_H_
#include <stdint.h>
#include <stdlib.h>
#include "SensirionRxFrame.h"
#include "SensirionShdlcCommunication.h"
/**
* SenirionShdlcRxFrame - Class which decodes the through UART received data
* into common data types. It contains a buffer which is filled by the
* SensirionShdlcCommunication class. By calling the different decode function
* inherited from the SensirionRxFrame base class the raw data can be decoded
* into different data types. In addition to that it also stores the four
* header bytes defined by the SHDLC protocol state, command, address,
* datalength. These bytes can be read out by the corresponding getter method.
*/
class SensirionShdlcRxFrame : public SensirionRxFrame {
friend class SensirionShdlcCommunication;
public:
/**
* Constructor
*
* @param buffer Buffer in which the receive frame will be stored.
* @param bufferSize Number of bytes in the buffer for the receive frame.
*/
SensirionShdlcRxFrame(uint8_t buffer[], size_t bufferSize)
: SensirionRxFrame(buffer, bufferSize){};
uint8_t getAddress(void) const {
return _address;
};
uint8_t getCommand(void) const {
return _command;
};
uint8_t getState(void) const {
return _state;
};
uint8_t getDataLength(void) const {
return _dataLength;
};
private:
uint8_t _address = 0;
uint8_t _command = 0;
uint8_t _state = 0;
uint8_t _dataLength = 0;
};
#endif /* SENSIRION_SHDLC_RX_FRAME_H_ */

View File

@ -0,0 +1,130 @@
/*
* Copyright (c) 2020, Sensirion AG
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright notice,
* list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* * Neither the name of Sensirion AG nor the names of its
* contributors may be used to endorse or promote products derived from
* software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
#include "SensirionShdlcTxFrame.h"
#include <stdint.h>
#include <stdlib.h>
#include "SensirionErrors.h"
uint16_t SensirionShdlcTxFrame::begin(uint8_t command, uint8_t address,
uint8_t dataLength) {
_buffer[_index++] = 0x7e;
uint16_t error = addUInt8(address);
error |= addUInt8(command);
error |= addUInt8(dataLength);
_command = command;
_address = address;
return error;
}
uint16_t SensirionShdlcTxFrame::finish(void) {
uint16_t error = addUInt8(~_checksum);
if (error) {
return error;
}
if (_index + 1 > _bufferSize) {
return TxFrameError | BufferSizeError;
}
_buffer[_index++] = 0x7e;
_isFinished = true;
return NoError;
}
uint16_t SensirionShdlcTxFrame::addUInt32(uint32_t data) {
uint16_t error = addUInt8(static_cast<uint8_t>((data & 0xFF000000) >> 24));
error |= addUInt8(static_cast<uint8_t>((data & 0x00FF0000) >> 16));
error |= addUInt8(static_cast<uint8_t>((data & 0x0000FF00) >> 8));
error |= addUInt8(static_cast<uint8_t>((data & 0x000000FF) >> 0));
return error;
}
uint16_t SensirionShdlcTxFrame::addInt32(int32_t data) {
return addUInt32(static_cast<uint32_t>(data));
}
uint16_t SensirionShdlcTxFrame::addUInt16(uint16_t data) {
uint16_t error = addUInt8(static_cast<uint8_t>((data & 0xFF00) >> 8));
error |= addUInt8(static_cast<uint8_t>((data & 0x00FF) >> 0));
return error;
}
uint16_t SensirionShdlcTxFrame::addInt16(int16_t data) {
return addUInt16(static_cast<uint16_t>(data));
}
uint16_t SensirionShdlcTxFrame::addUInt8(uint8_t data) {
if (_index + 2 > _bufferSize) {
return TxFrameError | BufferSizeError;
}
switch (data) {
case 0x11:
case 0x13:
case 0x7d:
case 0x7e:
// byte stuffing is done by inserting 0x7d and inverting bit 5
_buffer[_index++] = 0x7d;
_buffer[_index++] = data ^ (1 << 5);
break;
default:
_buffer[_index++] = data;
}
_checksum += data;
return NoError;
}
uint16_t SensirionShdlcTxFrame::addInt8(int8_t data) {
return addUInt8(static_cast<uint8_t>(data));
}
uint16_t SensirionShdlcTxFrame::addBool(bool data) {
return addUInt8(static_cast<uint8_t>(data));
}
uint16_t SensirionShdlcTxFrame::addFloat(float data) {
union {
uint32_t uInt32Data;
float floatData;
} convert;
convert.floatData = data;
return addUInt32(convert.uInt32Data);
}
uint16_t SensirionShdlcTxFrame::addBytes(const uint8_t data[],
size_t dataLength) {
uint16_t error = 0;
for (size_t i = 0; i < dataLength; i++) {
error |= addUInt8(data[i]);
}
return error;
}

View File

@ -0,0 +1,184 @@
/*
* Copyright (c) 2020, Sensirion AG
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* * Neither the name of Sensirion AG nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef SENSIRION_SHDLC_TX_FRAME_H_
#define SENSIRION_SHDLC_TX_FRAME_H_
#include <stdint.h>
#include <stdlib.h>
#include "SensirionShdlcCommunication.h"
/*
* SensirionShdlcTxFrame - Class which helps to build a correct SHDLC frame.
* The begin() functions writes the header. The different addDatatype()
* functions add the frame data and the finish() function writes the tail.
* Using these functions one can easily construct a SHDLC frame.
*/
class SensirionShdlcTxFrame {
friend class SensirionShdlcCommunication;
public:
/**
* Constructor
*
* @param buffer Buffer in which the send frame will be stored.
* @param bufferSize Number of bytes in the buffer for the send frame.
*/
SensirionShdlcTxFrame(uint8_t buffer[], size_t bufferSize)
: _buffer(buffer), _bufferSize(bufferSize) {
}
/**
* begin() - Begin frame and write header.
*
* @note This function needs to be called before calling other
* data add functions to write the header at the beginning.
*
* @param command Command byte to add to the send frame.
* @param address Address byte to add to the send frame.
* @param dataLength Data length byte to add to the send frame.
*
* @return NoError on success, an error code otherwise
*/
uint16_t begin(uint8_t command, uint8_t address, uint8_t dataLength);
/**
* finish() - Finish frame and write tail.
*
* @note This function needs to be called last, after adding all
* data to frame and before sending the frame to the sensor.
*
* @return NoError on success, an error code otherwise
*/
uint16_t finish(void);
/**
* addUInt32() - Add unsigned 32bit integer to the send frame.
*
* @param data Unsigned 32bit integer to add to the send frame.
*
* @return NoError on success, an error code otherwise
*/
uint16_t addUInt32(uint32_t data);
/**
* addInt32() - Add signed 32bit integer to the send frame.
*
* @param data Signed 32bit integer to add to the send frame.
*
* @return NoError on success, an error code otherwise
*/
uint16_t addInt32(int32_t data);
/**
* addUInt16() - Add unsigned 16bit integer to the send frame.
*
* @param data Unsigned 16bit integer to add to the send frame.
*
* @return NoError on success, an error code otherwise
*/
uint16_t addUInt16(uint16_t data);
/**
* addInt16() - Add signed 16bit integer to the send frame.
*
* @param data Signed 16bit integer to add to the send frame.
*
* @return NoError on success, an error code otherwise
*/
uint16_t addInt16(int16_t data);
/**
* addUInt8() - Add unsigned 8bit integer to the send frame.
*
* @param data Unsigned 8bit integer to add to the send frame.
*
* @return NoError on success, an error code otherwise
*/
uint16_t addUInt8(uint8_t data);
/**
* addInt8() - Add signed 8bit integer to the send frame.
*
* @param data Signed 8bit integer to add to the send frame.
*
* @return NoError on success, an error code otherwise
*/
uint16_t addInt8(int8_t data);
/**
* addBool() - Add boolean to the send frame.
*
* @param data Boolean to add to the send frame.
*
* @return NoError on success, an error code otherwise
*/
uint16_t addBool(bool data);
/**
* addFloat() - Add float to the send frame.
*
* @param data Float to add to the send frame.
*
* @return NoError on success, an error code otherwise
*/
uint16_t addFloat(float data);
/**
* addBytes() - Add byte array to the send frame.
*
* @param data Byte array to add to the send frame.
* @param dataLength Number of bytes to add to the send frame.
*
* @return NoError on success, an error code otherwise
*/
uint16_t addBytes(const uint8_t data[], size_t dataLength);
uint8_t getCommand(void) const {
return _command;
};
uint8_t getAddress(void) const {
return _address;
}
private:
uint8_t* _buffer;
size_t _bufferSize;
size_t _index = 0;
uint8_t _checksum = 0;
bool _isFinished = false;
uint8_t _command = 0;
uint8_t _address = 0;
};
#endif /* SENSIRION_SHDLC_TX_FRAME_H_ */

View File

@ -0,0 +1,45 @@
#! /usr/bin/env python3
import subprocess
import json
import argparse
import sys
import logging
def main():
parser = argparse.ArgumentParser()
parser.description = u'Compile test a sketch for all available boards'
parser.add_argument(u'-s', u'--sketch', dest=u'sketch',
required=True, help=u'Path to sketch')
args = parser.parse_args()
test_all_boards(args.sketch)
def test_all_boards(sketch):
logging.basicConfig(level=logging.INFO,
format='%(asctime)s [%(levelname)s] %(message)s')
log = logging.getLogger('arduino-compile-test')
process = subprocess.run("arduino-cli board listall --format json".split(),
stdout=subprocess.PIPE)
board_list_json = process.stdout.decode('utf-8')
board_list = json.loads(board_list_json)
test_list = ["arduino:samd:mkrzero", "arduino:avr:mega",
"arduino:avr:nano", "arduino:avr:uno",
"esp32:esp32:esp32", "esp8266:esp8266:generic"]
for board in test_list:
if board in (b['fqbn'] for b in board_list['boards']):
log.info('Test compilation for board {}'.format(board))
command = 'arduino-cli compile --libraries="." --warnings all'\
' --fqbn {board} {sketch}'.format(board=board,
sketch=sketch)
process = subprocess.run(command.split(), stdout=subprocess.PIPE)
if process.returncode:
log.error(process.stdout.decode('utf-8'))
sys.exit(process.returncode)
else:
log.error('Board not installed: {}'.format(board))
sys.exit(-1)
if __name__ == '__main__':
main()

View File

@ -0,0 +1,3 @@
#!/bin/bash
set -euxo pipefail
cppcheck --std=c++11 --language=c++ --error-exitcode=1 --enable=warning,style,performance,portability src/*

View File

@ -0,0 +1,5 @@
#! /bin/bash
set -euxo pipefail
editorconfig-checker
flake8
find . -type f -iregex ".*\.\(c\|h\|cpp\|ino\)" -exec clang-format-6.0 -i -style=file {} \; && git diff --exit-code

View File

@ -0,0 +1,20 @@
# Changelog
All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## [Unreleased]
## [0.2.0] - 2022-03-30
Add support for SEN50
## [0.1.0] - 2022-01-05
Initial release
[0.2.0]: https://github.com/Sensirion/embedded-i2c-sen5x/compare/0.1.0...0.2.0
[0.1.0]: https://github.com/Sensirion/arduino-i2c-sen5x/releases/tag/0.1.0

View File

@ -0,0 +1,29 @@
BSD 3-Clause License
Copyright (c) 2021, Sensirion AG
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
3. Neither the name of the copyright holder nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

View File

@ -0,0 +1,97 @@
<!-- Downloaded from https://github.com/Sensirion/arduino-i2c-sen5x
on 10/01/2023 at commit d7a73c86073cca34b84cde83814f5c17b87aad6d -->
# Sensirion I2C SEN5X Arduino Library
This is the Sensirion SEN5X library for Arduino using the
modules I2C interface.
<center><img src="images/SEN5x.png" width="500px"></center>
## Supported sensors
- SEN50 (only particulate matter signals available)
- SEN54 (no NOx signal available)
- SEN55 (full feature set)
# Installation
To install, download the latest release as .zip file and add it to your
[Arduino IDE](http://www.arduino.cc/en/main/software) via
Sketch => Include Library => Add .ZIP Library...
Don't forget to **install the dependencies** listed below the same way via `Add
.ZIP Library`
Note: Installation via the Arduino Library Manager is coming soon.
# Dependencies
* [Sensirion Core](https://github.com/Sensirion/arduino-core)
# Quick Start
1. Connect the SEN5X Sensor to your Arduino board's standard
I2C bus. Check the pinout of your Arduino board to find the correct pins.
The pinout of the SEN5X Sensor board can be found in the
data sheet.
| *SEN5X* | *Arduino* | *Jumper Wire* |
| ------- | ----------- | ------------- |
| VCC | 5V | Red |
| GND | GND | Black |
| SDA | SDA | Green |
| SCL | SCL | Yellow |
| SEL | GND for I2C | Blue |
<center><img src="images/SEN5X_pinout.png" width="300px"></center>
| *Pin* | *Name* | *Description* | *Comments* |
| ----- | ------ | ------------------------------- | -------------------------------- |
| 1 | VCC | Supply Voltage | 5V ±10% |
| 2 | GND | Ground |
| 3 | SDA | I2C: Serial data input / output | TTL 5V and LVTTL 3.3V compatible |
| 4 | SCL | I2C: Serial clock input | TTL 5V and LVTTL 3.3V compatible |
| 5 | SEL | Interface select | Pull to GND to select I2C |
| 6 | NC | Do not connect |
2. Open the `exampleUsage` sample project within the Arduino IDE
File => Examples => Sensirion I2C SEN5X => exampleUsage
3. Click the `Upload` button in the Arduino IDE or
Sketch => Upload
4. When the upload process has finished, open the `Serial Monitor` or `Serial
Plotter` via the `Tools` menu to observe the measurement values. Note that
the `Baud Rate` in the corresponding window has to be set to `115200 baud`.
# Contributing
**Contributions are welcome!**
We develop and test this driver using our company internal tools (version
control, continuous integration, code review etc.) and automatically
synchronize the master branch with GitHub. But this doesn't mean that we don't
respond to issues or don't accept pull requests on GitHub. In fact, you're very
welcome to open issues or create pull requests :)
This Sensirion library uses
[`clang-format`](https://releases.llvm.org/download.html) to standardize the
formatting of all our `.cpp` and `.h` files. Make sure your contributions are
formatted accordingly:
The `-i` flag will apply the format changes to the files listed.
```bash
clang-format -i src/*.cpp src/*.h
```
Note that differences from this formatting will result in a failed build until
they are fixed.
# License
See [LICENSE](LICENSE).

View File

@ -0,0 +1,249 @@
/*
* I2C-Generator: 0.3.0
* Yaml Version: 2.1.3
* Template Version: 0.7.0-112-g190ecaa
*/
/*
* Copyright (c) 2021, Sensirion AG
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* * Neither the name of Sensirion AG nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
#include <Arduino.h>
#include <SensirionI2CSen5x.h>
#include <Wire.h>
// The used commands use up to 48 bytes. On some Arduino's the default buffer
// space is not large enough
#define MAXBUF_REQUIREMENT 48
#if (defined(I2C_BUFFER_LENGTH) && \
(I2C_BUFFER_LENGTH >= MAXBUF_REQUIREMENT)) || \
(defined(BUFFER_LENGTH) && BUFFER_LENGTH >= MAXBUF_REQUIREMENT)
#define USE_PRODUCT_INFO
#endif
SensirionI2CSen5x sen5x;
void printModuleVersions() {
uint16_t error;
char errorMessage[256];
unsigned char productName[32];
uint8_t productNameSize = 32;
error = sen5x.getProductName(productName, productNameSize);
if (error) {
Serial.print("Error trying to execute getProductName(): ");
errorToString(error, errorMessage, 256);
Serial.println(errorMessage);
} else {
Serial.print("ProductName:");
Serial.println((char*)productName);
}
uint8_t firmwareMajor;
uint8_t firmwareMinor;
bool firmwareDebug;
uint8_t hardwareMajor;
uint8_t hardwareMinor;
uint8_t protocolMajor;
uint8_t protocolMinor;
error = sen5x.getVersion(firmwareMajor, firmwareMinor, firmwareDebug,
hardwareMajor, hardwareMinor, protocolMajor,
protocolMinor);
if (error) {
Serial.print("Error trying to execute getVersion(): ");
errorToString(error, errorMessage, 256);
Serial.println(errorMessage);
} else {
Serial.print("Firmware: ");
Serial.print(firmwareMajor);
Serial.print(".");
Serial.print(firmwareMinor);
Serial.print(", ");
Serial.print("Hardware: ");
Serial.print(hardwareMajor);
Serial.print(".");
Serial.println(hardwareMinor);
}
}
void printSerialNumber() {
uint16_t error;
char errorMessage[256];
unsigned char serialNumber[32];
uint8_t serialNumberSize = 32;
error = sen5x.getSerialNumber(serialNumber, serialNumberSize);
if (error) {
Serial.print("Error trying to execute getSerialNumber(): ");
errorToString(error, errorMessage, 256);
Serial.println(errorMessage);
} else {
Serial.print("SerialNumber:");
Serial.println((char*)serialNumber);
}
}
void setup() {
Serial.begin(115200);
while (!Serial) {
delay(100);
}
Wire.begin();
sen5x.begin(Wire);
uint16_t error;
char errorMessage[256];
error = sen5x.deviceReset();
if (error) {
Serial.print("Error trying to execute deviceReset(): ");
errorToString(error, errorMessage, 256);
Serial.println(errorMessage);
}
// Print SEN55 module information if i2c buffers are large enough
#ifdef USE_PRODUCT_INFO
printSerialNumber();
printModuleVersions();
#endif
// set a temperature offset in degrees celsius
// Note: supported by SEN54 and SEN55 sensors
// By default, the temperature and humidity outputs from the sensor
// are compensated for the modules self-heating. If the module is
// designed into a device, the temperature compensation might need
// to be adapted to incorporate the change in thermal coupling and
// self-heating of other device components.
//
// A guide to achieve optimal performance, including references
// to mechanical design-in examples can be found in the app note
// “SEN5x Temperature Compensation Instruction” at www.sensirion.com.
// Please refer to those application notes for further information
// on the advanced compensation settings used
// in `setTemperatureOffsetParameters`, `setWarmStartParameter` and
// `setRhtAccelerationMode`.
//
// Adjust tempOffset to account for additional temperature offsets
// exceeding the SEN module's self heating.
float tempOffset = 0.0;
error = sen5x.setTemperatureOffsetSimple(tempOffset);
if (error) {
Serial.print("Error trying to execute setTemperatureOffsetSimple(): ");
errorToString(error, errorMessage, 256);
Serial.println(errorMessage);
} else {
Serial.print("Temperature Offset set to ");
Serial.print(tempOffset);
Serial.println(" deg. Celsius (SEN54/SEN55 only");
}
// Start Measurement
error = sen5x.startMeasurement();
if (error) {
Serial.print("Error trying to execute startMeasurement(): ");
errorToString(error, errorMessage, 256);
Serial.println(errorMessage);
}
}
void loop() {
uint16_t error;
char errorMessage[256];
delay(1000);
// Read Measurement
float massConcentrationPm1p0;
float massConcentrationPm2p5;
float massConcentrationPm4p0;
float massConcentrationPm10p0;
float ambientHumidity;
float ambientTemperature;
float vocIndex;
float noxIndex;
error = sen5x.readMeasuredValues(
massConcentrationPm1p0, massConcentrationPm2p5, massConcentrationPm4p0,
massConcentrationPm10p0, ambientHumidity, ambientTemperature, vocIndex,
noxIndex);
if (error) {
Serial.print("Error trying to execute readMeasuredValues(): ");
errorToString(error, errorMessage, 256);
Serial.println(errorMessage);
} else {
Serial.print("MassConcentrationPm1p0:");
Serial.print(massConcentrationPm1p0);
Serial.print("\t");
Serial.print("MassConcentrationPm2p5:");
Serial.print(massConcentrationPm2p5);
Serial.print("\t");
Serial.print("MassConcentrationPm4p0:");
Serial.print(massConcentrationPm4p0);
Serial.print("\t");
Serial.print("MassConcentrationPm10p0:");
Serial.print(massConcentrationPm10p0);
Serial.print("\t");
Serial.print("AmbientHumidity:");
if (isnan(ambientHumidity)) {
Serial.print("n/a");
} else {
Serial.print(ambientHumidity);
}
Serial.print("\t");
Serial.print("AmbientTemperature:");
if (isnan(ambientTemperature)) {
Serial.print("n/a");
} else {
Serial.print(ambientTemperature);
}
Serial.print("\t");
Serial.print("VocIndex:");
if (isnan(vocIndex)) {
Serial.print("n/a");
} else {
Serial.print(vocIndex);
}
Serial.print("\t");
Serial.print("NoxIndex:");
if (isnan(noxIndex)) {
Serial.println("n/a");
} else {
Serial.println(noxIndex);
}
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 37 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 440 KiB

View File

@ -0,0 +1,55 @@
#######################################
# Syntax Coloring Map
#######################################
#######################################
# Datatypes (KEYWORD1)
#######################################
SensirionI2CSen5x KEYWORD1
#######################################
# Methods and Functions (KEYWORD2)
#######################################
startMeasurement KEYWORD2
startMeasurementWithoutPm KEYWORD2
stopMeasurement KEYWORD2
readDataReady KEYWORD2
readMeasuredValues KEYWORD2
readMeasuredValuesAsIntegers KEYWORD2
readMeasuredValuesSen50 KEYWORD2
readMeasuredRawValues KEYWORD2
readMeasuredPmValues KEYWORD2
readMeasuredPmValuesAsIntegers KEYWORD2
startFanCleaning KEYWORD2
setTemperatureOffsetSimple KEYWORD2
getTemperatureOffsetSimple KEYWORD2
setTemperatureOffsetParameters KEYWORD2
getTemperatureOffsetParameters KEYWORD2
setWarmStartParameter KEYWORD2
getWarmStartParameter KEYWORD2
setVocAlgorithmTuningParameters KEYWORD2
getVocAlgorithmTuningParameters KEYWORD2
setNoxAlgorithmTuningParameters KEYWORD2
getNoxAlgorithmTuningParameters KEYWORD2
setRhtAccelerationMode KEYWORD2
getRhtAccelerationMode KEYWORD2
setVocAlgorithmState KEYWORD2
getVocAlgorithmState KEYWORD2
setFanAutoCleaningInterval KEYWORD2
getFanAutoCleaningInterval KEYWORD2
getProductName KEYWORD2
getSerialNumber KEYWORD2
getVersion KEYWORD2
readDeviceStatus KEYWORD2
readAndClearDeviceStatus KEYWORD2
deviceReset KEYWORD2
#######################################
# Instances (KEYWORD2)
#######################################
sen5x KEYWORD2
#######################################
# Constants (LITERAL1)
#######################################

View File

@ -0,0 +1,10 @@
name=Sensirion I2C SEN5X
version=0.2.0
author=Sensirion
maintainer=Sensirion
sentence=Library for the SEN5X sensor family by Sensirion
paragraph=Enables you to use the SEN50, SEN54 and SEN55 via I2C.
url=https://github.com/Sensirion/arduino-i2c-sen5x
category=Sensors
depends=Sensirion Core
includes=SensirionI2CSen5x.h

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