mirror of
https://github.com/arendst/Tasmota.git
synced 2025-07-28 21:26:33 +00:00
Merge branch 'development' into HP303B
This commit is contained in:
commit
344d544e43
@ -120,6 +120,9 @@
|
||||
| USE_WEMOS_MOTOR_V1 | - | - | - | - | x | - | - |
|
||||
| USE_IAQ | - | - | - | - | x | - | - |
|
||||
| USE_AS3935 | - | - | - | - | x | - | - |
|
||||
| USE_VEML6075 | - | - | - | - | - | - | - |
|
||||
| USE_VEML7700 | - | - | - | - | - | - | - |
|
||||
| USE_MCP9808 | - | - | - | - | - | - | - |
|
||||
| | | | | | | | |
|
||||
| Feature or Sensor | minimal | lite | tasmota | knx | sensors | ir | display | Remarks
|
||||
| USE_SPI | - | - | - | - | - | - | x |
|
||||
|
@ -71,4 +71,5 @@ Index | Define | Driver | Device | Address(es) | Description
|
||||
47 | USE_DISPLAY_SEVENSEG| xdsp_11 | HT16K33 | 0x70 - 0x77 | Seven segment LED
|
||||
48 | USE_AS3935 | xsns_67 | AS3935 | 0x03 | Franklin Lightning Sensor
|
||||
49 | USE_VEML6075 | xsns_70 | VEML6075 | 0x10 | UVA/UVB/UVINDEX Sensor
|
||||
50 | USE_VEML7700 | xsns_71 | VEML7700 | 0x10 | Ambient light intensity sensor
|
||||
50 | USE_VEML7700 | xsns_71 | VEML7700 | 0x10 | Ambient light intensity sensor
|
||||
51 | USE_MCP9808 | xsns_72 | MCP9808 | 0x18 - 0x1F | Temperature sensor
|
@ -61,6 +61,7 @@ The following binary downloads have been compiled with ESP8266/Arduino library c
|
||||
- Fix escape of non-JSON received serial data (#8329)
|
||||
- Add command ``Rule0`` to change global rule parameters
|
||||
- Add command ``Time 4`` to display timestamp using milliseconds (#8537)
|
||||
- Add command ``SetOption94 0/1`` to select MAX31855 or MAX6675 thermocouple support (#8616)
|
||||
- Add commands ``LedPwmOn 0..255``, ``LedPwmOff 0..255`` and ``LedPwmMode1 0/1`` to control led brightness by George (#8491)
|
||||
- Add support for unique MQTTClient (and inherited fallback topic) by full Mac address using ``mqttclient DVES_%12X`` (#8300)
|
||||
- Add more functionality to ``Switchmode`` 11 and 12 (#8450)
|
||||
@ -68,3 +69,9 @@ The following binary downloads have been compiled with ESP8266/Arduino library c
|
||||
- Add support for VEML6075 UVA/UVB/UVINDEX Sensor by device111 (#8432)
|
||||
- Add support for VEML7700 Ambient light intensity Sensor by device111 (#8432)
|
||||
- Add Three Phase Export Active Energy to SDM630 driver
|
||||
- Add Zigbee options to ``ZbSend`` to write and report attributes
|
||||
- Add Zigbee auto-responder for common attributes
|
||||
- Add ``CpuFrequency`` to ``status 2``
|
||||
- Add ``FlashFrequency`` to ``status 4``
|
||||
- Add support for up to two BH1750 sensors controlled by commands ``BH1750Resolution`` and ``BH1750MTime`` (#8139)
|
||||
- Add support for up to eight MCP9808 temperature sensors by device111 (#8594)
|
||||
|
46
lib/Adafruit_MCP9808_Tasmota/.github/ISSUE_TEMPLATE.md
vendored
Normal file
46
lib/Adafruit_MCP9808_Tasmota/.github/ISSUE_TEMPLATE.md
vendored
Normal file
@ -0,0 +1,46 @@
|
||||
Thank you for opening an issue on an Adafruit Arduino library repository. To
|
||||
improve the speed of resolution please review the following guidelines and
|
||||
common troubleshooting steps below before creating the issue:
|
||||
|
||||
- **Do not use GitHub issues for troubleshooting projects and issues.** Instead use
|
||||
the forums at http://forums.adafruit.com to ask questions and troubleshoot why
|
||||
something isn't working as expected. In many cases the problem is a common issue
|
||||
that you will more quickly receive help from the forum community. GitHub issues
|
||||
are meant for known defects in the code. If you don't know if there is a defect
|
||||
in the code then start with troubleshooting on the forum first.
|
||||
|
||||
- **If following a tutorial or guide be sure you didn't miss a step.** Carefully
|
||||
check all of the steps and commands to run have been followed. Consult the
|
||||
forum if you're unsure or have questions about steps in a guide/tutorial.
|
||||
|
||||
- **For Arduino projects check these very common issues to ensure they don't apply**:
|
||||
|
||||
- For uploading sketches or communicating with the board make sure you're using
|
||||
a **USB data cable** and **not** a **USB charge-only cable**. It is sometimes
|
||||
very hard to tell the difference between a data and charge cable! Try using the
|
||||
cable with other devices or swapping to another cable to confirm it is not
|
||||
the problem.
|
||||
|
||||
- **Be sure you are supplying adequate power to the board.** Check the specs of
|
||||
your board and plug in an external power supply. In many cases just
|
||||
plugging a board into your computer is not enough to power it and other
|
||||
peripherals.
|
||||
|
||||
- **Double check all soldering joints and connections.** Flakey connections
|
||||
cause many mysterious problems. See the [guide to excellent soldering](https://learn.adafruit.com/adafruit-guide-excellent-soldering/tools) for examples of good solder joints.
|
||||
|
||||
- **Ensure you are using an official Arduino or Adafruit board.** We can't
|
||||
guarantee a clone board will have the same functionality and work as expected
|
||||
with this code and don't support them.
|
||||
|
||||
If you're sure this issue is a defect in the code and checked the steps above
|
||||
please fill in the following fields to provide enough troubleshooting information.
|
||||
You may delete the guideline and text above to just leave the following details:
|
||||
|
||||
- Arduino board: **INSERT ARDUINO BOARD NAME/TYPE HERE**
|
||||
|
||||
- Arduino IDE version (found in Arduino -> About Arduino menu): **INSERT ARDUINO
|
||||
VERSION HERE**
|
||||
|
||||
- List the steps to reproduce the problem below (if possible attach a sketch or
|
||||
copy the sketch code in too): **LIST REPRO STEPS BELOW**
|
26
lib/Adafruit_MCP9808_Tasmota/.github/PULL_REQUEST_TEMPLATE.md
vendored
Normal file
26
lib/Adafruit_MCP9808_Tasmota/.github/PULL_REQUEST_TEMPLATE.md
vendored
Normal file
@ -0,0 +1,26 @@
|
||||
Thank you for creating a pull request to contribute to Adafruit's GitHub code!
|
||||
Before you open the request please review the following guidelines and tips to
|
||||
help it be more easily integrated:
|
||||
|
||||
- **Describe the scope of your change--i.e. what the change does and what parts
|
||||
of the code were modified.** This will help us understand any risks of integrating
|
||||
the code.
|
||||
|
||||
- **Describe any known limitations with your change.** For example if the change
|
||||
doesn't apply to a supported platform of the library please mention it.
|
||||
|
||||
- **Please run any tests or examples that can exercise your modified code.** We
|
||||
strive to not break users of the code and running tests/examples helps with this
|
||||
process.
|
||||
|
||||
Thank you again for contributing! We will try to test and integrate the change
|
||||
as soon as we can, but be aware we have many GitHub repositories to manage and
|
||||
can't immediately respond to every request. There is no need to bump or check in
|
||||
on a pull request (it will clutter the discussion of the request).
|
||||
|
||||
Also don't be worried if the request is closed or not integrated--sometimes the
|
||||
priorities of Adafruit's GitHub code (education, ease of use) might not match the
|
||||
priorities of the pull request. Don't fret, the open source community thrives on
|
||||
forks and GitHub makes it easy to keep your changes in a forked repo.
|
||||
|
||||
After reviewing the guidelines above you can delete this text from the pull request.
|
32
lib/Adafruit_MCP9808_Tasmota/.github/workflows/githubci.yml
vendored
Normal file
32
lib/Adafruit_MCP9808_Tasmota/.github/workflows/githubci.yml
vendored
Normal file
@ -0,0 +1,32 @@
|
||||
name: Arduino Library CI
|
||||
|
||||
on: [pull_request, push, repository_dispatch]
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/setup-python@v1
|
||||
with:
|
||||
python-version: '3.x'
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/checkout@v2
|
||||
with:
|
||||
repository: adafruit/ci-arduino
|
||||
path: ci
|
||||
|
||||
- name: pre-install
|
||||
run: bash ci/actions_install.sh
|
||||
|
||||
- name: test platforms
|
||||
run: python3 ci/build_platform.py main_platforms
|
||||
|
||||
- name: clang
|
||||
run: python3 ci/run-clang-format.py -e "ci/*" -e "bin/*" -r .
|
||||
|
||||
- name: doxygen
|
||||
env:
|
||||
GH_REPO_TOKEN: ${{ secrets.GH_REPO_TOKEN }}
|
||||
PRETTYNAME : "Adafruit MCP9808 Arduino Library"
|
||||
run: bash ci/doxy_gen_and_deploy.sh
|
8
lib/Adafruit_MCP9808_Tasmota/.gitignore
vendored
Normal file
8
lib/Adafruit_MCP9808_Tasmota/.gitignore
vendored
Normal file
@ -0,0 +1,8 @@
|
||||
# osx
|
||||
.DS_Store
|
||||
|
||||
# doxygen
|
||||
Doxyfile*
|
||||
doxygen_sqlite3.db
|
||||
html
|
||||
*.tmp
|
273
lib/Adafruit_MCP9808_Tasmota/Adafruit_MCP9808.cpp
Normal file
273
lib/Adafruit_MCP9808_Tasmota/Adafruit_MCP9808.cpp
Normal file
@ -0,0 +1,273 @@
|
||||
/*!
|
||||
* @file Adafruit_MCP9808.cpp
|
||||
*
|
||||
* @mainpage Adafruit MCP9808 I2C Temp Sensor
|
||||
*
|
||||
* @section intro_sec Introduction
|
||||
*
|
||||
* I2C Driver for Microchip's MCP9808 I2C Temp sensor
|
||||
*
|
||||
* This is a library for the Adafruit MCP9808 breakout:
|
||||
* http://www.adafruit.com/products/1782
|
||||
*
|
||||
* Adafruit invests time and resources providing this open source code,
|
||||
* please support Adafruit and open-source hardware by purchasing products from
|
||||
* Adafruit!
|
||||
*
|
||||
* @section author Author
|
||||
*
|
||||
* K.Townsend (Adafruit Industries)
|
||||
*
|
||||
* @section license License
|
||||
*
|
||||
* BSD (see license.txt)
|
||||
*
|
||||
* @section HISTORY
|
||||
*
|
||||
* v1.0 - First release
|
||||
*
|
||||
* changes by Martin Wagner for tasmota project:
|
||||
*
|
||||
* - the libary supports variabel I2C address
|
||||
*
|
||||
*/
|
||||
|
||||
#if ARDUINO >= 100
|
||||
#include "Arduino.h"
|
||||
#else
|
||||
#include "WProgram.h"
|
||||
#endif
|
||||
|
||||
#ifdef __AVR_ATtiny85__
|
||||
#include "TinyWireM.h"
|
||||
#define Wire TinyWireM
|
||||
#else
|
||||
#include <Wire.h>
|
||||
#endif
|
||||
|
||||
#include "Adafruit_MCP9808.h"
|
||||
|
||||
/*!
|
||||
* @brief Instantiates a new MCP9808 class
|
||||
*/
|
||||
Adafruit_MCP9808::Adafruit_MCP9808() {}
|
||||
|
||||
/*!
|
||||
* @brief Setups the HW
|
||||
* @param *theWire
|
||||
* @return True if initialization was successful, otherwise false.
|
||||
*/
|
||||
bool Adafruit_MCP9808::begin(TwoWire *theWire) {
|
||||
_wire = theWire;
|
||||
_i2caddr = MCP9808_I2CADDR_DEFAULT;
|
||||
return init();
|
||||
}
|
||||
|
||||
/*!
|
||||
* @brief Setups the HW
|
||||
* @param addr
|
||||
* @return True if initialization was successful, otherwise false.
|
||||
*/
|
||||
bool Adafruit_MCP9808::begin(uint8_t addr) {
|
||||
_i2caddr = addr;
|
||||
_wire = &Wire;
|
||||
return init();
|
||||
}
|
||||
|
||||
/*!
|
||||
* @brief Setups the HW
|
||||
* @param addr
|
||||
* @param *theWire
|
||||
* @return True if initialization was successful, otherwise false.
|
||||
*/
|
||||
bool Adafruit_MCP9808::begin(uint8_t addr, TwoWire *theWire) {
|
||||
_i2caddr = addr;
|
||||
_wire = theWire;
|
||||
return init();
|
||||
}
|
||||
|
||||
/*!
|
||||
* @brief Setups the HW with default address
|
||||
* @return True if initialization was successful, otherwise false.
|
||||
*/
|
||||
bool Adafruit_MCP9808::begin() {
|
||||
_i2caddr = MCP9808_I2CADDR_DEFAULT;
|
||||
_wire = &Wire;
|
||||
return init();
|
||||
}
|
||||
|
||||
/*!
|
||||
* @brief init function
|
||||
* @return True if initialization was successful, otherwise false.
|
||||
*/
|
||||
bool Adafruit_MCP9808::init() {
|
||||
_wire->begin();
|
||||
|
||||
if (read16(MCP9808_REG_MANUF_ID) != 0x0054)
|
||||
return false;
|
||||
if (read16(MCP9808_REG_DEVICE_ID) != 0x0400)
|
||||
return false;
|
||||
|
||||
write16(MCP9808_REG_CONFIG, 0x0);
|
||||
return true;
|
||||
}
|
||||
|
||||
/*!
|
||||
* @brief Reads the 16-bit temperature register and returns the Centigrade
|
||||
* temperature as a float.
|
||||
* @return Temperature in Centigrade.
|
||||
*/
|
||||
float Adafruit_MCP9808::readTempC(uint8_t addr) {
|
||||
_i2caddr = addr;
|
||||
float temp = NAN;
|
||||
uint16_t t = read16(MCP9808_REG_AMBIENT_TEMP);
|
||||
|
||||
if (t != 0xFFFF) {
|
||||
temp = t & 0x0FFF;
|
||||
temp /= 16.0;
|
||||
if (t & 0x1000)
|
||||
temp -= 256;
|
||||
}
|
||||
|
||||
return temp;
|
||||
}
|
||||
|
||||
/*!
|
||||
* @brief Reads the 16-bit temperature register and returns the Fahrenheit
|
||||
* temperature as a float.
|
||||
* @return Temperature in Fahrenheit.
|
||||
*/
|
||||
float Adafruit_MCP9808::readTempF(uint8_t addr) {
|
||||
_i2caddr = addr;
|
||||
float temp = NAN;
|
||||
uint16_t t = read16(MCP9808_REG_AMBIENT_TEMP);
|
||||
|
||||
if (t != 0xFFFF) {
|
||||
temp = t & 0x0FFF;
|
||||
temp /= 16.0;
|
||||
if (t & 0x1000)
|
||||
temp -= 256;
|
||||
|
||||
temp = temp * 9.0 / 5.0 + 32;
|
||||
}
|
||||
|
||||
return temp;
|
||||
}
|
||||
|
||||
/*!
|
||||
* @brief Set Sensor to Shutdown-State or wake up (Conf_Register BIT8)
|
||||
* @param sw true = shutdown / false = wakeup
|
||||
*/
|
||||
void Adafruit_MCP9808::shutdown_wake(uint8_t addr, boolean sw) {
|
||||
_i2caddr = addr;
|
||||
uint16_t conf_shutdown;
|
||||
uint16_t conf_register = read16(MCP9808_REG_CONFIG);
|
||||
if (sw == true) {
|
||||
conf_shutdown = conf_register | MCP9808_REG_CONFIG_SHUTDOWN;
|
||||
write16(MCP9808_REG_CONFIG, conf_shutdown);
|
||||
}
|
||||
if (sw == false) {
|
||||
conf_shutdown = conf_register & ~MCP9808_REG_CONFIG_SHUTDOWN;
|
||||
write16(MCP9808_REG_CONFIG, conf_shutdown);
|
||||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
* @brief Shutdown MCP9808
|
||||
*/
|
||||
void Adafruit_MCP9808::shutdown(uint8_t addr) { shutdown_wake(addr, true); }
|
||||
|
||||
/*!
|
||||
* @brief Wake up MCP9808
|
||||
*/
|
||||
void Adafruit_MCP9808::wake(uint8_t addr) {
|
||||
shutdown_wake(addr, false);
|
||||
delay(250);
|
||||
}
|
||||
|
||||
/*!
|
||||
* @brief Get Resolution Value
|
||||
* @return Resolution value
|
||||
*/
|
||||
uint8_t Adafruit_MCP9808::getResolution(uint8_t addr) {
|
||||
_i2caddr = addr;
|
||||
return read8(MCP9808_REG_RESOLUTION);
|
||||
}
|
||||
|
||||
/*!
|
||||
* @brief Set Resolution Value
|
||||
* @param value
|
||||
*/
|
||||
void Adafruit_MCP9808::setResolution(uint8_t addr, uint8_t value) {
|
||||
_i2caddr = addr;
|
||||
write8(MCP9808_REG_RESOLUTION, value & 0x03);
|
||||
}
|
||||
|
||||
/*!
|
||||
* @brief Low level 16 bit write procedures
|
||||
* @param reg
|
||||
* @param value
|
||||
*/
|
||||
void Adafruit_MCP9808::write16(uint8_t reg, uint16_t value) {
|
||||
_wire->beginTransmission(_i2caddr);
|
||||
_wire->write((uint8_t)reg);
|
||||
_wire->write(value >> 8);
|
||||
_wire->write(value & 0xFF);
|
||||
_wire->endTransmission();
|
||||
}
|
||||
|
||||
/*!
|
||||
* @brief Low level 16 bit read procedure
|
||||
* @param reg
|
||||
* @return value
|
||||
*/
|
||||
uint16_t Adafruit_MCP9808::read16(uint8_t reg) {
|
||||
uint16_t val = 0xFFFF;
|
||||
uint8_t state;
|
||||
|
||||
_wire->beginTransmission(_i2caddr);
|
||||
_wire->write((uint8_t)reg);
|
||||
state = _wire->endTransmission();
|
||||
|
||||
if (state == 0) {
|
||||
_wire->requestFrom((uint8_t)_i2caddr, (uint8_t)2);
|
||||
val = _wire->read();
|
||||
val <<= 8;
|
||||
val |= _wire->read();
|
||||
}
|
||||
|
||||
return val;
|
||||
}
|
||||
|
||||
/*!
|
||||
* @brief Low level 8 bit write procedure
|
||||
* @param reg
|
||||
* @param value
|
||||
*/
|
||||
void Adafruit_MCP9808::write8(uint8_t reg, uint8_t value) {
|
||||
_wire->beginTransmission(_i2caddr);
|
||||
_wire->write((uint8_t)reg);
|
||||
_wire->write(value);
|
||||
_wire->endTransmission();
|
||||
}
|
||||
|
||||
/*!
|
||||
* @brief Low level 8 bit read procedure
|
||||
* @param reg
|
||||
* @return value
|
||||
*/
|
||||
uint8_t Adafruit_MCP9808::read8(uint8_t reg) {
|
||||
uint8_t val = 0xFF;
|
||||
uint8_t state;
|
||||
|
||||
_wire->beginTransmission(_i2caddr);
|
||||
_wire->write((uint8_t)reg);
|
||||
state = _wire->endTransmission();
|
||||
|
||||
if (state == 0) {
|
||||
_wire->requestFrom((uint8_t)_i2caddr, (uint8_t)1);
|
||||
val = _wire->read();
|
||||
}
|
||||
|
||||
return val;
|
||||
}
|
87
lib/Adafruit_MCP9808_Tasmota/Adafruit_MCP9808.h
Normal file
87
lib/Adafruit_MCP9808_Tasmota/Adafruit_MCP9808.h
Normal file
@ -0,0 +1,87 @@
|
||||
/*!
|
||||
* @file Adafruit_MCP9808.h
|
||||
*
|
||||
* I2C Driver for Microchip's MCP9808 I2C Temp sensor
|
||||
*
|
||||
* This is a library for the Adafruit MCP9808 breakout:
|
||||
* http://www.adafruit.com/products/1782
|
||||
*
|
||||
* Adafruit invests time and resources providing this open source code,
|
||||
*please support Adafruit and open-source hardware by purchasing products from
|
||||
* Adafruit!
|
||||
*
|
||||
*
|
||||
* BSD license (see license.txt)
|
||||
*
|
||||
* changes by Martin Wagner for tasmota project:
|
||||
*
|
||||
* - the libary supports variabel I2C address
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef _ADAFRUIT_MCP9808_H
|
||||
#define _ADAFRUIT_MCP9808_H
|
||||
|
||||
#if ARDUINO >= 100
|
||||
#include "Arduino.h"
|
||||
#else
|
||||
#include "WProgram.h"
|
||||
#endif
|
||||
|
||||
#include <Wire.h>
|
||||
|
||||
#define MCP9808_I2CADDR_DEFAULT 0x18 ///< I2C address
|
||||
#define MCP9808_REG_CONFIG 0x01 ///< MCP9808 config register
|
||||
|
||||
#define MCP9808_REG_CONFIG_SHUTDOWN 0x0100 ///< shutdown config
|
||||
#define MCP9808_REG_CONFIG_CRITLOCKED 0x0080 ///< critical trip lock
|
||||
#define MCP9808_REG_CONFIG_WINLOCKED 0x0040 ///< alarm window lock
|
||||
#define MCP9808_REG_CONFIG_INTCLR 0x0020 ///< interrupt clear
|
||||
#define MCP9808_REG_CONFIG_ALERTSTAT 0x0010 ///< alert output status
|
||||
#define MCP9808_REG_CONFIG_ALERTCTRL 0x0008 ///< alert output control
|
||||
#define MCP9808_REG_CONFIG_ALERTSEL 0x0004 ///< alert output select
|
||||
#define MCP9808_REG_CONFIG_ALERTPOL 0x0002 ///< alert output polarity
|
||||
#define MCP9808_REG_CONFIG_ALERTMODE 0x0001 ///< alert output mode
|
||||
|
||||
#define MCP9808_REG_UPPER_TEMP 0x02 ///< upper alert boundary
|
||||
#define MCP9808_REG_LOWER_TEMP 0x03 ///< lower alert boundery
|
||||
#define MCP9808_REG_CRIT_TEMP 0x04 ///< critical temperature
|
||||
#define MCP9808_REG_AMBIENT_TEMP 0x05 ///< ambient temperature
|
||||
#define MCP9808_REG_MANUF_ID 0x06 ///< manufacture ID
|
||||
#define MCP9808_REG_DEVICE_ID 0x07 ///< device ID
|
||||
#define MCP9808_REG_RESOLUTION 0x08 ///< resolutin
|
||||
|
||||
/*!
|
||||
* @brief Class that stores state and functions for interacting with
|
||||
* MCP9808 Temp Sensor
|
||||
*/
|
||||
class Adafruit_MCP9808 {
|
||||
public:
|
||||
Adafruit_MCP9808();
|
||||
bool begin();
|
||||
bool begin(TwoWire *theWire);
|
||||
bool begin(uint8_t addr);
|
||||
bool begin(uint8_t addr, TwoWire *theWire);
|
||||
|
||||
bool init();
|
||||
float readTempC(uint8_t addr);
|
||||
float readTempF(uint8_t addr);
|
||||
uint8_t getResolution(uint8_t addr);
|
||||
void setResolution(uint8_t addr, uint8_t value);
|
||||
|
||||
void shutdown_wake(uint8_t addr, boolean sw);
|
||||
void shutdown(uint8_t addr);
|
||||
void wake(uint8_t addr);
|
||||
|
||||
void write16(uint8_t reg, uint16_t val);
|
||||
uint16_t read16(uint8_t reg);
|
||||
|
||||
void write8(uint8_t reg, uint8_t val);
|
||||
uint8_t read8(uint8_t reg);
|
||||
|
||||
private:
|
||||
TwoWire *_wire;
|
||||
uint8_t _i2caddr;
|
||||
};
|
||||
|
||||
#endif
|
18
lib/Adafruit_MCP9808_Tasmota/README.md
Normal file
18
lib/Adafruit_MCP9808_Tasmota/README.md
Normal file
@ -0,0 +1,18 @@
|
||||
# Adafruit MCP9808 Library [](https://github.com/adafruit/Adafruit_MCP9808_Library/actions)[](http://adafruit.github.io/Adafruit_MCP9808_Library/html/index.html)
|
||||
|
||||
<a href="https://www.adafruit.com/product/1782"><img src="assets/board.jpg?raw=true" width="500px"></a>
|
||||
|
||||
This is the Adafruit MCP9808 Precision I2C Temperature sensor library
|
||||
|
||||
Tested and works great with the Adafruit MCP9808 Breakout Board
|
||||
* http://www.adafruit.com/products/1782
|
||||
|
||||
This chip uses I2C to communicate, 2 pins are required to interface
|
||||
|
||||
Adafruit invests time and resources providing this open source code, please support Adafruit and open-source hardware by purchasing products from Adafruit!
|
||||
|
||||
Written by Kevin Townsend/Limor Fried for Adafruit Industries.
|
||||
BSD license, check license.txt for more information
|
||||
All text above must be included in any redistribution
|
||||
|
||||
To install, use the Arduino Library Manager and search for "Adafruit MCP9808" and install the library.
|
BIN
lib/Adafruit_MCP9808_Tasmota/assets/board.jpg
Normal file
BIN
lib/Adafruit_MCP9808_Tasmota/assets/board.jpg
Normal file
Binary file not shown.
After Width: | Height: | Size: 132 KiB |
127
lib/Adafruit_MCP9808_Tasmota/code-of-conduct.md
Normal file
127
lib/Adafruit_MCP9808_Tasmota/code-of-conduct.md
Normal file
@ -0,0 +1,127 @@
|
||||
# Adafruit Community Code of Conduct
|
||||
|
||||
## Our Pledge
|
||||
|
||||
In the interest of fostering an open and welcoming environment, we as
|
||||
contributors and leaders pledge to making participation in our project and
|
||||
our community a harassment-free experience for everyone, regardless of age, body
|
||||
size, disability, ethnicity, gender identity and expression, level or type of
|
||||
experience, education, socio-economic status, nationality, personal appearance,
|
||||
race, religion, or sexual identity and orientation.
|
||||
|
||||
## Our Standards
|
||||
|
||||
We are committed to providing a friendly, safe and welcoming environment for
|
||||
all.
|
||||
|
||||
Examples of behavior that contributes to creating a positive environment
|
||||
include:
|
||||
|
||||
* Be kind and courteous to others
|
||||
* Using welcoming and inclusive language
|
||||
* Being respectful of differing viewpoints and experiences
|
||||
* Collaborating with other community members
|
||||
* Gracefully accepting constructive criticism
|
||||
* Focusing on what is best for the community
|
||||
* Showing empathy towards other community members
|
||||
|
||||
Examples of unacceptable behavior by participants include:
|
||||
|
||||
* The use of sexualized language or imagery and sexual attention or advances
|
||||
* The use of inappropriate images, including in a community member's avatar
|
||||
* The use of inappropriate language, including in a community member's nickname
|
||||
* Any spamming, flaming, baiting or other attention-stealing behavior
|
||||
* Excessive or unwelcome helping; answering outside the scope of the question
|
||||
asked
|
||||
* Trolling, insulting/derogatory comments, and personal or political attacks
|
||||
* Public or private harassment
|
||||
* Publishing others' private information, such as a physical or electronic
|
||||
address, without explicit permission
|
||||
* Other conduct which could reasonably be considered inappropriate
|
||||
|
||||
The goal of the standards and moderation guidelines outlined here is to build
|
||||
and maintain a respectful community. We ask that you don’t just aim to be
|
||||
"technically unimpeachable", but rather try to be your best self.
|
||||
|
||||
We value many things beyond technical expertise, including collaboration and
|
||||
supporting others within our community. Providing a positive experience for
|
||||
other community members can have a much more significant impact than simply
|
||||
providing the correct answer.
|
||||
|
||||
## Our Responsibilities
|
||||
|
||||
Project leaders are responsible for clarifying the standards of acceptable
|
||||
behavior and are expected to take appropriate and fair corrective action in
|
||||
response to any instances of unacceptable behavior.
|
||||
|
||||
Project leaders have the right and responsibility to remove, edit, or
|
||||
reject messages, comments, commits, code, issues, and other contributions
|
||||
that are not aligned to this Code of Conduct, or to ban temporarily or
|
||||
permanently any community member for other behaviors that they deem
|
||||
inappropriate, threatening, offensive, or harmful.
|
||||
|
||||
## Moderation
|
||||
|
||||
Instances of behaviors that violate the Adafruit Community Code of Conduct
|
||||
may be reported by any member of the community. Community members are
|
||||
encouraged to report these situations, including situations they witness
|
||||
involving other community members.
|
||||
|
||||
You may report in the following ways:
|
||||
|
||||
In any situation, you may send an email to <support@adafruit.com>.
|
||||
|
||||
On the Adafruit Discord, you may send an open message from any channel
|
||||
to all Community Helpers by tagging @community helpers. You may also send an
|
||||
open message from any channel, or a direct message to @kattni#1507,
|
||||
@tannewt#4653, @Dan Halbert#1614, @cater#2442, @sommersoft#0222, or
|
||||
@Andon#8175.
|
||||
|
||||
Email and direct message reports will be kept confidential.
|
||||
|
||||
In situations on Discord where the issue is particularly egregious, possibly
|
||||
illegal, requires immediate action, or violates the Discord terms of service,
|
||||
you should also report the message directly to Discord.
|
||||
|
||||
These are the steps for upholding our community’s standards of conduct.
|
||||
|
||||
1. Any member of the community may report any situation that violates the
|
||||
Adafruit Community Code of Conduct. All reports will be reviewed and
|
||||
investigated.
|
||||
2. If the behavior is an egregious violation, the community member who
|
||||
committed the violation may be banned immediately, without warning.
|
||||
3. Otherwise, moderators will first respond to such behavior with a warning.
|
||||
4. Moderators follow a soft "three strikes" policy - the community member may
|
||||
be given another chance, if they are receptive to the warning and change their
|
||||
behavior.
|
||||
5. If the community member is unreceptive or unreasonable when warned by a
|
||||
moderator, or the warning goes unheeded, they may be banned for a first or
|
||||
second offense. Repeated offenses will result in the community member being
|
||||
banned.
|
||||
|
||||
## Scope
|
||||
|
||||
This Code of Conduct and the enforcement policies listed above apply to all
|
||||
Adafruit Community venues. This includes but is not limited to any community
|
||||
spaces (both public and private), the entire Adafruit Discord server, and
|
||||
Adafruit GitHub repositories. Examples of Adafruit Community spaces include
|
||||
but are not limited to meet-ups, audio chats on the Adafruit Discord, or
|
||||
interaction at a conference.
|
||||
|
||||
This Code of Conduct applies both within project spaces and in public spaces
|
||||
when an individual is representing the project or its community. As a community
|
||||
member, you are representing our community, and are expected to behave
|
||||
accordingly.
|
||||
|
||||
## Attribution
|
||||
|
||||
This Code of Conduct is adapted from the [Contributor Covenant][homepage],
|
||||
version 1.4, available at
|
||||
<https://www.contributor-covenant.org/version/1/4/code-of-conduct.html>,
|
||||
and the [Rust Code of Conduct](https://www.rust-lang.org/en-US/conduct.html).
|
||||
|
||||
For other projects adopting the Adafruit Community Code of
|
||||
Conduct, please contact the maintainers of those projects for enforcement.
|
||||
If you wish to use this code of conduct for your own project, consider
|
||||
explicitly mentioning your moderation policy or making a copy with your
|
||||
own moderation policy so as to avoid confusion.
|
@ -0,0 +1,69 @@
|
||||
|
||||
/**************************************************************************/
|
||||
/*!
|
||||
This is a demo for the Adafruit MCP9808 breakout
|
||||
----> http://www.adafruit.com/products/1782
|
||||
Adafruit invests time and resources providing this open source code,
|
||||
please support Adafruit and open-source hardware by purchasing
|
||||
products from Adafruit!
|
||||
*/
|
||||
/**************************************************************************/
|
||||
|
||||
#include <Wire.h>
|
||||
#include "Adafruit_MCP9808.h"
|
||||
|
||||
// Create the MCP9808 temperature sensor object
|
||||
Adafruit_MCP9808 tempsensor = Adafruit_MCP9808();
|
||||
|
||||
void setup() {
|
||||
Serial.begin(9600);
|
||||
while (!Serial); //waits for serial terminal to be open, necessary in newer arduino boards.
|
||||
Serial.println("MCP9808 demo");
|
||||
|
||||
// Make sure the sensor is found, you can also pass in a different i2c
|
||||
// address with tempsensor.begin(0x19) for example, also can be left in blank for default address use
|
||||
// Also there is a table with all addres possible for this sensor, you can connect multiple sensors
|
||||
// to the same i2c bus, just configure each sensor with a different address and define multiple objects for that
|
||||
// A2 A1 A0 address
|
||||
// 0 0 0 0x18 this is the default address
|
||||
// 0 0 1 0x19
|
||||
// 0 1 0 0x1A
|
||||
// 0 1 1 0x1B
|
||||
// 1 0 0 0x1C
|
||||
// 1 0 1 0x1D
|
||||
// 1 1 0 0x1E
|
||||
// 1 1 1 0x1F
|
||||
if (!tempsensor.begin(0x18)) {
|
||||
Serial.println("Couldn't find MCP9808! Check your connections and verify the address is correct.");
|
||||
while (1);
|
||||
}
|
||||
|
||||
Serial.println("Found MCP9808!");
|
||||
|
||||
tempsensor.setResolution(3); // sets the resolution mode of reading, the modes are defined in the table bellow:
|
||||
// Mode Resolution SampleTime
|
||||
// 0 0.5°C 30 ms
|
||||
// 1 0.25°C 65 ms
|
||||
// 2 0.125°C 130 ms
|
||||
// 3 0.0625°C 250 ms
|
||||
}
|
||||
|
||||
void loop() {
|
||||
Serial.println("wake up MCP9808.... "); // wake up MCP9808 - power consumption ~200 mikro Ampere
|
||||
tempsensor.wake(); // wake up, ready to read!
|
||||
|
||||
// Read and print out the temperature, also shows the resolution mode used for reading.
|
||||
Serial.print("Resolution in mode: ");
|
||||
Serial.println (tempsensor.getResolution());
|
||||
float c = tempsensor.readTempC();
|
||||
float f = tempsensor.readTempF();
|
||||
Serial.print("Temp: ");
|
||||
Serial.print(c, 4); Serial.print("*C\t and ");
|
||||
Serial.print(f, 4); Serial.println("*F.");
|
||||
|
||||
delay(2000);
|
||||
Serial.println("Shutdown MCP9808.... ");
|
||||
tempsensor.shutdown_wake(1); // shutdown MSP9808 - power consumption ~0.1 mikro Ampere, stops temperature sampling
|
||||
Serial.println("");
|
||||
delay(200);
|
||||
}
|
10
lib/Adafruit_MCP9808_Tasmota/library.properties
Normal file
10
lib/Adafruit_MCP9808_Tasmota/library.properties
Normal file
@ -0,0 +1,10 @@
|
||||
name=Adafruit MCP9808 Library
|
||||
version=1.1.2
|
||||
author=Adafruit
|
||||
maintainer=Adafruit <info@adafruit.com>
|
||||
sentence=Arduino library for the MCP9808 sensors in the Adafruit shop
|
||||
paragraph=Arduino library for the MCP9808 sensors in the Adafruit shop
|
||||
category=Sensors
|
||||
url=https://github.com/adafruit/Adafruit_MCP9808_Library
|
||||
architectures=*
|
||||
depends=Adafruit Unified Sensor
|
26
lib/Adafruit_MCP9808_Tasmota/license.txt
Normal file
26
lib/Adafruit_MCP9808_Tasmota/license.txt
Normal file
@ -0,0 +1,26 @@
|
||||
Software License Agreement (BSD License)
|
||||
|
||||
Copyright (c) 2012, Adafruit Industries
|
||||
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 holders 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 ''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 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.
|
@ -19,7 +19,7 @@
|
||||
#include <limits.h>
|
||||
|
||||
// if using software spi this optimizes the code
|
||||
#define SWSPI_OPTMODE
|
||||
|
||||
|
||||
#define ILI9488_START start();
|
||||
#define ILI9488_STOP stop();
|
||||
@ -37,9 +37,6 @@ ILI9488::ILI9488(int8_t cs,int8_t mosi,int8_t sclk,int8_t bp) : Renderer(ILI9488
|
||||
_hwspi = 0;
|
||||
}
|
||||
|
||||
|
||||
#include "spi_register.h"
|
||||
|
||||
/*
|
||||
|
||||
CPU Clock = 80 Mhz
|
||||
@ -73,6 +70,13 @@ GPIO15: PERIPHS_IO_MUX_MTDO_U
|
||||
|
||||
uint8_t ili9488_start;
|
||||
|
||||
#ifndef ESP32
|
||||
// ESP8266
|
||||
#include "spi_register.h"
|
||||
#define SWSPI_OPTMODE
|
||||
// this enables the 27 bit packed mode
|
||||
#define RGB_PACK_MODE
|
||||
|
||||
uint32_t ili9488_clock;
|
||||
uint32_t ili9488_usr;
|
||||
uint32_t ili9488_usr1;
|
||||
@ -192,32 +196,6 @@ void ILI9488::stop(void) {
|
||||
ili9488_start=0;
|
||||
}
|
||||
|
||||
|
||||
#if 0
|
||||
// code from espressif SDK
|
||||
/******************************************************************************
|
||||
* FunctionName : spi_lcd_9bit_write
|
||||
* Description : SPI 9bits transmission function for driving LCD TM035PDZV36
|
||||
* Parameters : uint8 spi_no - SPI module number, Only "SPI" and "HSPI" are valid
|
||||
* uint8 high_bit - first high bit of the data, 0 is for "0",the other value 1-255 is for "1"
|
||||
* uint8 low_8bit- the rest 8bits of the data.
|
||||
*******************************************************************************/
|
||||
void spi_lcd_9bit_write(uint8_t high_bit,uint8_t low_8bit)
|
||||
{
|
||||
uint32_t regvalue;
|
||||
uint8_t bytetemp;
|
||||
|
||||
if(high_bit) bytetemp=(low_8bit>>1)|0x80;
|
||||
else bytetemp=(low_8bit>>1)&0x7f;
|
||||
|
||||
regvalue= ((8&SPI_USR_COMMAND_BITLEN)<<SPI_USR_COMMAND_BITLEN_S)|((uint32)bytetemp); //configure transmission variable,9bit transmission length and first 8 command bit
|
||||
if(low_8bit&0x01) regvalue|=BIT15; //write the 9th bit
|
||||
while(READ_PERI_REG(SPI_CMD(1))&SPI_USR); //waiting for spi module available
|
||||
WRITE_PERI_REG(SPI_USER2(1), regvalue); //write command and command length into spi reg
|
||||
SET_PERI_REG_MASK(SPI_CMD(1), SPI_USR); //transmission start
|
||||
}
|
||||
#endif
|
||||
|
||||
// dc = 0
|
||||
void ILI9488::writecommand(uint8_t c) {
|
||||
if (_hwspi) {
|
||||
@ -227,9 +205,6 @@ void ILI9488::writecommand(uint8_t c) {
|
||||
|
||||
ILI9488_START
|
||||
|
||||
//#define SPI_USR_COMMAND_BITLEN 0x0000000F
|
||||
//#define SPI_USR_COMMAND_BITLEN_S 28
|
||||
|
||||
regvalue= ((8&SPI_USR_COMMAND_BITLEN)<<SPI_USR_COMMAND_BITLEN_S)|((uint32)bytetemp); //configure transmission variable,9bit transmission length and first 8 command bit
|
||||
if(c&0x01) regvalue|=BIT15; //write the 9th bit
|
||||
while(READ_PERI_REG(SPI_CMD(1))&SPI_USR); //waiting for spi module available
|
||||
@ -255,39 +230,74 @@ void ILI9488::writedata(uint8_t d) {
|
||||
} else fastSPIwrite(d,1);
|
||||
}
|
||||
|
||||
// Rather than a bazillion writecommand() and writedata() calls, screen
|
||||
// initialization commands and arguments are organized in these tables
|
||||
// stored in PROGMEM. The table may look bulky, but that's mostly the
|
||||
// formatting -- storage-wise this is hundreds of bytes more compact
|
||||
// than the equivalent code. Companion function follows.
|
||||
void ICACHE_RAM_ATTR ILI9488::fastSPIwrite(uint8_t d,uint8_t dc) {
|
||||
|
||||
WRITE_PERI_REG( PIN_OUT_CLEAR, 1<<_cs);
|
||||
WRITE_PERI_REG( PIN_OUT_CLEAR, 1<<_sclk);
|
||||
if(dc) WRITE_PERI_REG( PIN_OUT_SET, 1<<_mosi);
|
||||
else WRITE_PERI_REG( PIN_OUT_CLEAR, 1<<_mosi);
|
||||
WRITE_PERI_REG( PIN_OUT_SET, 1<<_sclk);
|
||||
|
||||
/*
|
||||
// Companion code to the above tables. Reads and issues
|
||||
// a series of LCD commands stored in PROGMEM byte array.
|
||||
void ILI9488::commandList(uint8_t *addr) {
|
||||
|
||||
uint8_t numCommands, numArgs;
|
||||
uint16_t ms;
|
||||
|
||||
numCommands = pgm_read_byte(addr++); // Number of commands to follow
|
||||
while(numCommands--) { // For each command...
|
||||
writecommand(pgm_read_byte(addr++)); // Read, issue command
|
||||
numArgs = pgm_read_byte(addr++); // Number of args to follow
|
||||
ms = numArgs & DELAY; // If hibit set, delay follows args
|
||||
numArgs &= ~DELAY; // Mask out delay bit
|
||||
while(numArgs--) { // For each argument...
|
||||
writedata(pgm_read_byte(addr++)); // Read, issue argument
|
||||
}
|
||||
|
||||
if(ms) {
|
||||
ms = pgm_read_byte(addr++); // Read post-command delay time (ms)
|
||||
if(ms == 255) ms = 500; // If 255, delay for 500 ms
|
||||
delay(ms);
|
||||
}
|
||||
for(uint8_t bit = 0x80; bit; bit >>= 1) {
|
||||
WRITE_PERI_REG( PIN_OUT_CLEAR, 1<<_sclk);
|
||||
if(d&bit) WRITE_PERI_REG( PIN_OUT_SET, 1<<_mosi);
|
||||
else WRITE_PERI_REG( PIN_OUT_CLEAR, 1<<_mosi);
|
||||
WRITE_PERI_REG( PIN_OUT_SET, 1<<_sclk);
|
||||
}
|
||||
WRITE_PERI_REG( PIN_OUT_SET, 1<<_cs);
|
||||
}
|
||||
*/
|
||||
#else
|
||||
// ESP32 section
|
||||
void ILI9488::writedata(uint8_t d) {
|
||||
fastSPIwrite(d,1);
|
||||
}
|
||||
|
||||
void ILI9488::writecommand(uint8_t c) {
|
||||
fastSPIwrite(c,0);
|
||||
}
|
||||
|
||||
#include "soc/spi_reg.h"
|
||||
#include "soc/spi_struct.h"
|
||||
#include "esp32-hal-spi.h"
|
||||
#include "esp32-hal.h"
|
||||
#include "soc/spi_struct.h"
|
||||
|
||||
#define RGB_PACK_MODE
|
||||
|
||||
// since ardunio transferBits ia completely disfunctional
|
||||
// we use our own hardware driver for 9 bit spi
|
||||
void ILI9488::fastSPIwrite(uint8_t d,uint8_t dc) {
|
||||
digitalWrite( _cs, LOW);
|
||||
|
||||
uint32_t regvalue=d>>1;
|
||||
if (dc) regvalue|=0x80;
|
||||
else regvalue&=0x7f;
|
||||
if (d&1) regvalue|=0x8000;
|
||||
|
||||
REG_SET_BIT(SPI_USER_REG(3), SPI_USR_MOSI);
|
||||
REG_WRITE(SPI_MOSI_DLEN_REG(3), 9 - 1);
|
||||
uint32_t *dp=(uint32_t*)SPI_W0_REG(3);
|
||||
*dp=regvalue;
|
||||
REG_SET_BIT(SPI_CMD_REG(3), SPI_USR);
|
||||
while (REG_GET_FIELD(SPI_CMD_REG(3), SPI_USR));
|
||||
|
||||
digitalWrite( _cs, HIGH);
|
||||
}
|
||||
|
||||
SPISettings ili9488_spiSettings;
|
||||
|
||||
void ILI9488::start(void) {
|
||||
if (ili9488_start) return;
|
||||
SPI.beginTransaction(ili9488_spiSettings);
|
||||
ili9488_start=1;
|
||||
}
|
||||
void ILI9488::stop(void) {
|
||||
if (!ili9488_start) return;
|
||||
SPI.endTransaction();
|
||||
ili9488_start=0;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
uint16_t ILI9488::GetColorFromIndex(uint8_t index) {
|
||||
if (index>=sizeof(ili9488_colors)/2) index=0;
|
||||
@ -339,14 +349,23 @@ void ILI9488::begin(void) {
|
||||
pinMode(_bp, OUTPUT);
|
||||
digitalWrite(_bp,HIGH);
|
||||
}
|
||||
|
||||
#ifndef ESP32
|
||||
if ((_sclk==14) && (_mosi==13) && (_cs==15)) {
|
||||
// we use hardware spi
|
||||
SPI.begin();
|
||||
_hwspi=1;
|
||||
spi_lcd_mode_init();
|
||||
} else {
|
||||
// we must use software spi
|
||||
_hwspi=0;
|
||||
}
|
||||
#else
|
||||
SPI.begin(_sclk,-1,_mosi, -1);
|
||||
ili9488_spiSettings = SPISettings(10000000, MSBFIRST, SPI_MODE3);
|
||||
_hwspi=1;
|
||||
#endif
|
||||
|
||||
ILI9488_START
|
||||
delay(1);
|
||||
|
||||
@ -817,8 +836,7 @@ void ILI9488::fillScreen(uint16_t color) {
|
||||
|
||||
//#define WRITE_SPI_REG
|
||||
|
||||
// this enables the 27 bit packed mode
|
||||
#define RGB_PACK_MODE
|
||||
|
||||
|
||||
// extremely strange => if this code is merged into pack_rgb() the software crashes
|
||||
// swap bytes
|
||||
@ -844,9 +862,9 @@ uint32_t pack_rgb(uint32_t r, uint32_t g, uint32_t b) {
|
||||
return ulswap(data);
|
||||
}
|
||||
|
||||
#ifndef ESP32
|
||||
// fill a rectangle
|
||||
void ILI9488::fillRect(int16_t x, int16_t y, int16_t w, int16_t h,
|
||||
uint16_t color) {
|
||||
void ILI9488::fillRect(int16_t x, int16_t y, int16_t w, int16_t h, uint16_t color) {
|
||||
|
||||
ILI9488_START
|
||||
// rudimentary clipping (drawChar w/big text requires this)
|
||||
@ -990,6 +1008,56 @@ void ILI9488::fillRect(int16_t x, int16_t y, int16_t w, int16_t h,
|
||||
#endif
|
||||
|
||||
}
|
||||
#else
|
||||
// ESP32
|
||||
void ILI9488::fillRect(int16_t x, int16_t y, int16_t w, int16_t h, uint16_t color) {
|
||||
|
||||
// rudimentary clipping (drawChar w/big text requires this)
|
||||
if((x >= _width) || (y >= _height)) return;
|
||||
if((x + w - 1) >= _width) w = _width - x;
|
||||
if((y + h - 1) >= _height) h = _height - y;
|
||||
|
||||
setAddrWindow(x, y, x+w-1, y+h-1);
|
||||
|
||||
uint8_t r = (color & 0xF800) >> 11;
|
||||
uint8_t g = (color & 0x07E0) >> 5;
|
||||
uint8_t b = color & 0x001F;
|
||||
|
||||
r = (r * 255) / 31;
|
||||
g = (g * 255) / 63;
|
||||
b = (b * 255) / 31;
|
||||
|
||||
#ifdef RGB_PACK_MODE
|
||||
// init 27 bit mode
|
||||
uint32_t data=pack_rgb(r,g,b);
|
||||
REG_SET_BIT(SPI_USER_REG(3), SPI_USR_MOSI);
|
||||
REG_WRITE(SPI_MOSI_DLEN_REG(3), 27 - 1);
|
||||
uint32_t *dp=(uint32_t*)SPI_W0_REG(3);
|
||||
digitalWrite( _cs, LOW);
|
||||
#endif
|
||||
|
||||
for(y=h; y>0; y--) {
|
||||
for(x=w; x>0; x--) {
|
||||
#ifndef RGB_PACK_MODE
|
||||
writedata(r);
|
||||
writedata(g);
|
||||
writedata(b);
|
||||
#else
|
||||
while (REG_GET_FIELD(SPI_CMD_REG(3), SPI_USR));
|
||||
*dp=data;
|
||||
REG_SET_BIT(SPI_CMD_REG(3), SPI_USR);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef RGB_PACK_MODE
|
||||
while (REG_GET_FIELD(SPI_CMD_REG(3), SPI_USR));
|
||||
digitalWrite( _cs, HIGH);
|
||||
#endif
|
||||
|
||||
ILI9488_STOP
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
// Pass 8-bit (each) R,G,B, get back 16-bit packed color
|
||||
@ -1040,65 +1108,3 @@ void ILI9488::invertDisplay(boolean i) {
|
||||
writecommand(i ? ILI9488_INVON : ILI9488_INVOFF);
|
||||
ILI9488_STOP
|
||||
}
|
||||
|
||||
void ICACHE_RAM_ATTR ILI9488::fastSPIwrite(uint8_t d,uint8_t dc) {
|
||||
|
||||
WRITE_PERI_REG( PIN_OUT_CLEAR, 1<<_cs);
|
||||
WRITE_PERI_REG( PIN_OUT_CLEAR, 1<<_sclk);
|
||||
if(dc) WRITE_PERI_REG( PIN_OUT_SET, 1<<_mosi);
|
||||
else WRITE_PERI_REG( PIN_OUT_CLEAR, 1<<_mosi);
|
||||
WRITE_PERI_REG( PIN_OUT_SET, 1<<_sclk);
|
||||
|
||||
for(uint8_t bit = 0x80; bit; bit >>= 1) {
|
||||
WRITE_PERI_REG( PIN_OUT_CLEAR, 1<<_sclk);
|
||||
if(d&bit) WRITE_PERI_REG( PIN_OUT_SET, 1<<_mosi);
|
||||
else WRITE_PERI_REG( PIN_OUT_CLEAR, 1<<_mosi);
|
||||
WRITE_PERI_REG( PIN_OUT_SET, 1<<_sclk);
|
||||
}
|
||||
WRITE_PERI_REG( PIN_OUT_SET, 1<<_cs);
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
uint16_t ILI9488::readcommand16(uint8_t c) {
|
||||
digitalWrite(_dc, LOW);
|
||||
if (_cs)
|
||||
digitalWrite(_cs, LOW);
|
||||
|
||||
spiwrite(c);
|
||||
pinMode(_sid, INPUT); // input!
|
||||
uint16_t r = spiread();
|
||||
r <<= 8;
|
||||
r |= spiread();
|
||||
if (_cs)
|
||||
digitalWrite(_cs, HIGH);
|
||||
|
||||
pinMode(_sid, OUTPUT); // back to output
|
||||
return r;
|
||||
}
|
||||
|
||||
uint32_t ILI9488::readcommand32(uint8_t c) {
|
||||
digitalWrite(_dc, LOW);
|
||||
if (_cs)
|
||||
digitalWrite(_cs, LOW);
|
||||
spiwrite(c);
|
||||
pinMode(_sid, INPUT); // input!
|
||||
|
||||
dummyclock();
|
||||
dummyclock();
|
||||
|
||||
uint32_t r = spiread();
|
||||
r <<= 8;
|
||||
r |= spiread();
|
||||
r <<= 8;
|
||||
r |= spiread();
|
||||
r <<= 8;
|
||||
r |= spiread();
|
||||
if (_cs)
|
||||
digitalWrite(_cs, HIGH);
|
||||
|
||||
pinMode(_sid, OUTPUT); // back to output
|
||||
return r;
|
||||
}
|
||||
|
||||
*/
|
||||
|
@ -174,6 +174,9 @@ bool TasmotaSerial::begin(long speed, int stop_bits) {
|
||||
m_uart = tasmota_serial_index;
|
||||
tasmota_serial_index--;
|
||||
TSerial = new HardwareSerial(m_uart);
|
||||
if (serial_buffer_size > 256) {
|
||||
TSerial->setRxBufferSize(serial_buffer_size);
|
||||
}
|
||||
if (2 == m_stop_bits) {
|
||||
TSerial->begin(speed, SERIAL_8N2, m_rx_pin, m_tx_pin);
|
||||
} else {
|
||||
|
7
lib/UdpListener/library.properties
Normal file
7
lib/UdpListener/library.properties
Normal file
@ -0,0 +1,7 @@
|
||||
name=UdpListener
|
||||
version=1.0
|
||||
author=Ivan Grokhotkov, Stephan Hadinger
|
||||
maintainer=Stephan <stephan.hadinger@gmail.com>
|
||||
sentence=UdpListener optimized for static and limite memory allocation, to reduce memory footprint of receiving SSDP request, as a replacement for WifiUdp.
|
||||
paragraph=This class only handles receiving UDP Multicast packets. For sending packets, use WifiUdp.
|
||||
architectures=esp8266
|
209
lib/UdpListener/src/UdpListener.h
Normal file
209
lib/UdpListener/src/UdpListener.h
Normal file
@ -0,0 +1,209 @@
|
||||
/*
|
||||
UdpListener.h - webserver for Tasmota
|
||||
|
||||
Copyright (C) 2020 Theo Arends & Stephan Hadinger
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.@
|
||||
*/
|
||||
|
||||
// adapted from:
|
||||
/*
|
||||
UdpContext.h - UDP connection handling on top of lwIP
|
||||
|
||||
Copyright (c) 2014 Ivan Grokhotkov. All rights reserved.
|
||||
This file is part of the esp8266 core for Arduino environment.
|
||||
|
||||
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
|
||||
*/
|
||||
|
||||
/*
|
||||
* This is a stripped down version of Udp handler to avoid overflowing
|
||||
* memory when lots of multicast SSDP packets arrive.
|
||||
* The pbuf is freed immediately upon arrival of the packet.
|
||||
*
|
||||
* Packet data are kept in a statically area in RAM and keeps
|
||||
* only the <n> first bytes (200 by default) of each packet.
|
||||
* The number of packets treated is limited (3 by default), any
|
||||
* new packet arriving is dropped.
|
||||
*
|
||||
* This class does only receiving multicast packets for LWIP2
|
||||
*/
|
||||
|
||||
#ifndef UDPMULTICASTLISTENER_H
|
||||
#define UDPMULTICASTLISTENER_H
|
||||
|
||||
#ifdef ESP8266
|
||||
// #include <Arduino.h>
|
||||
|
||||
extern "C" {
|
||||
#include <lwip/udp.h>
|
||||
#include <lwip/igmp.h>
|
||||
}
|
||||
|
||||
template <size_t PACKET_SIZE>
|
||||
struct UdpPacket {
|
||||
IPAddress srcaddr;
|
||||
IPAddress dstaddr;
|
||||
int16_t srcport;
|
||||
netif* input_netif;
|
||||
size_t len;
|
||||
uint8_t buf[PACKET_SIZE];
|
||||
};
|
||||
|
||||
template <size_t PACKET_SIZE>
|
||||
class UdpListener
|
||||
{
|
||||
public:
|
||||
|
||||
typedef std::function<void(void)> rxhandler_t;
|
||||
|
||||
UdpListener(size_t packet_number)
|
||||
: _pcb(0)
|
||||
, _packet_number(packet_number)
|
||||
, _buffers(nullptr)
|
||||
, _udp_packets(0)
|
||||
, _udp_ready(false)
|
||||
, _udp_index(0)
|
||||
{
|
||||
_packet_number = packet_number;
|
||||
_buffers = new UdpPacket<PACKET_SIZE>[_packet_number];
|
||||
_pcb = udp_new();
|
||||
}
|
||||
|
||||
~UdpListener()
|
||||
{
|
||||
udp_remove(_pcb);
|
||||
_pcb = 0;
|
||||
delete[] _buffers;
|
||||
_buffers = nullptr;
|
||||
}
|
||||
|
||||
void reset(void)
|
||||
{
|
||||
_udp_packets = 0;
|
||||
_udp_index = 0;
|
||||
}
|
||||
|
||||
bool listen(const IPAddress& addr, uint16_t port)
|
||||
{
|
||||
if (!_buffers) { return false; }
|
||||
udp_recv(_pcb, &_s_recv, (void *) this);
|
||||
err_t err = udp_bind(_pcb, addr, port);
|
||||
return err == ERR_OK;
|
||||
}
|
||||
|
||||
void disconnect()
|
||||
{
|
||||
udp_disconnect(_pcb);
|
||||
}
|
||||
|
||||
bool next()
|
||||
{
|
||||
if (!_buffers) { return false; }
|
||||
if (_udp_packets > 0) {
|
||||
if (!_udp_ready) {
|
||||
// we just consume the first packet
|
||||
_udp_ready = true;
|
||||
} else {
|
||||
_udp_packets--;
|
||||
_udp_index = (_udp_index + 1) % _packet_number; // advance to next buffer index in ring
|
||||
if (_udp_packets == 0) {
|
||||
_udp_ready = false;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
_udp_ready = false;
|
||||
}
|
||||
return _udp_ready;
|
||||
}
|
||||
|
||||
UdpPacket<PACKET_SIZE> * read(void)
|
||||
{
|
||||
if (!_buffers) { return nullptr; }
|
||||
if (_udp_ready) { // we have a packet ready to consume
|
||||
return &_buffers[_udp_index];
|
||||
} else {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
void _recv(udp_pcb *upcb, pbuf *pb,
|
||||
const ip_addr_t *srcaddr, u16_t srcport)
|
||||
{
|
||||
if (!_buffers) { pbuf_free(pb); return; }
|
||||
// Serial.printf(">>> _recv: _udp_packets = %d, _udp_index = %d, tot_len = %d\n", _udp_packets, _udp_index, pb->tot_len);
|
||||
if (_udp_packets >= _packet_number) {
|
||||
// we don't have slots anymore, drop packet
|
||||
pbuf_free(pb);
|
||||
return;
|
||||
}
|
||||
|
||||
uint8_t next_slot = (_udp_index + _udp_packets) % _packet_number;
|
||||
|
||||
size_t packet_len = pb->tot_len;
|
||||
if (packet_len > PACKET_SIZE) { packet_len = PACKET_SIZE; }
|
||||
|
||||
uint8_t * dst = &_buffers[next_slot].buf[0];
|
||||
void* buf = pbuf_get_contiguous(pb, dst, PACKET_SIZE, packet_len, 0);
|
||||
if (buf) {
|
||||
|
||||
if (buf != dst)
|
||||
memcpy(dst, buf, packet_len);
|
||||
_buffers[next_slot].len = packet_len;
|
||||
|
||||
_buffers[next_slot].srcaddr = srcaddr;
|
||||
_buffers[next_slot].dstaddr = ip_current_dest_addr();
|
||||
_buffers[next_slot].srcport = srcport;
|
||||
_buffers[next_slot].input_netif = ip_current_input_netif();
|
||||
_udp_packets++; // we have one packet ready
|
||||
}
|
||||
pbuf_free(pb); // free memory immediately
|
||||
}
|
||||
|
||||
static void _s_recv(void *arg,
|
||||
udp_pcb *upcb, pbuf *p,
|
||||
CONST ip_addr_t *srcaddr, u16_t srcport)
|
||||
{
|
||||
reinterpret_cast<UdpListener*>(arg)->_recv(upcb, p, srcaddr, srcport);
|
||||
}
|
||||
|
||||
private:
|
||||
udp_pcb* _pcb;
|
||||
uint8_t _packet_number;
|
||||
|
||||
UdpPacket<PACKET_SIZE> * _buffers;
|
||||
|
||||
// how many packets are ready.
|
||||
int8_t _udp_packets; // number of udp packets ready to consume
|
||||
bool _udp_ready; // is a packet currenlty consumed after a call to next()
|
||||
// ring buffer ranges from 0..(_packet_number-1)
|
||||
int8_t _udp_index; // current index in the ring buffer
|
||||
};
|
||||
|
||||
#endif // ESP8266
|
||||
#endif //UDPMULTICASTLISTENER_H
|
@ -40,7 +40,7 @@ static const char* LOG_TAG = "NimBLEDevice";
|
||||
/**
|
||||
* Singletons for the NimBLEDevice.
|
||||
*/
|
||||
bool initialized = false;
|
||||
static bool initialized = false;
|
||||
#if defined(CONFIG_BT_NIMBLE_ROLE_OBSERVER)
|
||||
NimBLEScan* NimBLEDevice::m_pScan = nullptr;
|
||||
#endif
|
||||
|
@ -74,6 +74,7 @@ build_flags = ${core_active.build_flags}
|
||||
build_unflags = -Wall
|
||||
|
||||
board_build.f_cpu = 80000000L
|
||||
board_build.f_flash = 40000000L
|
||||
monitor_speed = 115200
|
||||
upload_speed = 115200
|
||||
; *** Upload Serial reset method for Wemos and NodeMCU
|
||||
|
@ -52,6 +52,11 @@ build_flags = ${core_active.build_flags}
|
||||
; set CPU frequency to 80MHz (default) or 160MHz
|
||||
;board_build.f_cpu = 160000000L
|
||||
|
||||
; set Flash chip frequency to 40MHz (default), 20MHz, 26Mhz, 80Mhz
|
||||
;board_build.f_flash = 20000000L
|
||||
;board_build.f_flash = 26000000L
|
||||
;board_build.f_flash = 80000000L
|
||||
|
||||
; *** Upload Serial reset method for Wemos and NodeMCU
|
||||
upload_port = COM5
|
||||
|
||||
@ -165,6 +170,7 @@ board = esp32dev
|
||||
board_build.ldscript = esp32_out.ld
|
||||
board_build.partitions = esp32_partition_app1984k_spiffs64k.csv
|
||||
board_build.flash_mode = ${common.board_build.flash_mode}
|
||||
board_build.f_flash = ${common.board_build.f_flash}
|
||||
board_build.f_cpu = ${common.board_build.f_cpu}
|
||||
build_unflags = ${common.build_unflags}
|
||||
-Wpointer-arith
|
||||
@ -192,6 +198,6 @@ lib_extra_dirs =
|
||||
libesp32
|
||||
|
||||
lib_ignore =
|
||||
ILI9488
|
||||
; ILI9488
|
||||
; SSD3115
|
||||
cc1101
|
||||
|
@ -5,6 +5,7 @@ framework = ${common.framework}
|
||||
board = ${common.board}
|
||||
board_build.ldscript = ${common.board_build.ldscript}
|
||||
board_build.flash_mode = ${common.board_build.flash_mode}
|
||||
board_build.f_flash = ${common.board_build.f_flash}
|
||||
board_build.f_cpu = ${common.board_build.f_cpu}
|
||||
build_unflags = ${common.build_unflags}
|
||||
build_flags = ${common.build_flags}
|
||||
|
@ -6,6 +6,7 @@ board = ${common32.board}
|
||||
board_build.ldscript = ${common32.board_build.ldscript}
|
||||
board_build.partitions = ${common32.board_build.partitions}
|
||||
board_build.flash_mode = ${common32.board_build.flash_mode}
|
||||
board_build.f_flash = ${common32.board_build.f_flash}
|
||||
board_build.f_cpu = ${common32.board_build.f_cpu}
|
||||
monitor_speed = ${common32.monitor_speed}
|
||||
upload_port = ${common32.upload_port}
|
||||
|
@ -7,10 +7,16 @@
|
||||
- Change Adafruit_SGP30 library from v1.0.3 to v1.2.0 (#8519)
|
||||
- Fix escape of non-JSON received serial data (#8329)
|
||||
- Add command ``Time 4`` to display timestamp using milliseconds (#8537)
|
||||
- Add command ``SetOption94 0/1`` to select MAX31855 or MAX6675 thermocouple support (#8616)
|
||||
- Add commands ``LedPwmOn 0..255``, ``LedPwmOff 0..255`` and ``LedPwmMode1 0/1`` to control led brightness by George (#8491)
|
||||
- Add Three Phase Export Active Energy to SDM630 driver
|
||||
- Add wildcard pattern ``?`` for JSON matching in rules
|
||||
- Add support for unique MQTTClient (and inherited fallback topic) by full Mac address using ``mqttclient DVES_%12X`` (#8300)
|
||||
- Add Zigbee options to ``ZbSend`` to write and report attributes
|
||||
- Add ``CpuFrequency`` to ``status 2``
|
||||
- Add ``FlashFrequency`` to ``status 4``
|
||||
- Add support for up to two BH1750 sensors controlled by commands ``BH1750Resolution`` and ``BH1750MTime`` (#8139)
|
||||
- Add Zigbee auto-responder for common attributes
|
||||
|
||||
### 8.3.1.1 20200518
|
||||
|
||||
|
@ -1,627 +0,0 @@
|
||||
/*
|
||||
Parsing.cpp - HTTP request parsing.
|
||||
|
||||
Copyright (c) 2015 Ivan Grokhotkov. All rights reserved.
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
Modified 8 May 2015 by Hristo Gochkov (proper post and file upload handling)
|
||||
*/
|
||||
|
||||
#ifdef ESP8266
|
||||
|
||||
// Use patched Parsing.cpp to fix ALEXA parsing issue in v2.4.2
|
||||
#include <core_version.h>
|
||||
#if defined(ARDUINO_ESP8266_RELEASE_2_4_2)
|
||||
#warning **** Tasmota is using v2.4.2 patched Parsing.cpp as planned ****
|
||||
|
||||
#include <Arduino.h>
|
||||
#include "WiFiServer.h"
|
||||
#include "WiFiClient.h"
|
||||
#include "ESP8266WebServer.h"
|
||||
#include "detail/mimetable.h"
|
||||
|
||||
//#define DEBUG_ESP_HTTP_SERVER
|
||||
#ifdef DEBUG_ESP_PORT
|
||||
#define DEBUG_OUTPUT DEBUG_ESP_PORT
|
||||
#else
|
||||
#define DEBUG_OUTPUT Serial
|
||||
#endif
|
||||
|
||||
static const char Content_Type[] PROGMEM = "Content-Type";
|
||||
static const char filename[] PROGMEM = "filename";
|
||||
|
||||
static char* readBytesWithTimeout(WiFiClient& client, size_t maxLength, size_t& dataLength, int timeout_ms)
|
||||
{
|
||||
char *buf = nullptr;
|
||||
dataLength = 0;
|
||||
while (dataLength < maxLength) {
|
||||
int tries = timeout_ms;
|
||||
size_t newLength;
|
||||
while (!(newLength = client.available()) && tries--) delay(1);
|
||||
if (!newLength) {
|
||||
break;
|
||||
}
|
||||
if (!buf) {
|
||||
buf = (char *) malloc(newLength + 1);
|
||||
if (!buf) {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
else {
|
||||
char* newBuf = (char *) realloc(buf, dataLength + newLength + 1);
|
||||
if (!newBuf) {
|
||||
free(buf);
|
||||
return nullptr;
|
||||
}
|
||||
buf = newBuf;
|
||||
}
|
||||
client.readBytes(buf + dataLength, newLength);
|
||||
dataLength += newLength;
|
||||
buf[dataLength] = '\0';
|
||||
}
|
||||
return buf;
|
||||
}
|
||||
|
||||
bool ESP8266WebServer::_parseRequest(WiFiClient& client) {
|
||||
// Read the first line of HTTP request
|
||||
String req = client.readStringUntil('\r');
|
||||
client.readStringUntil('\n');
|
||||
//reset header value
|
||||
for (int i = 0; i < _headerKeysCount; ++i) {
|
||||
_currentHeaders[i].value =String();
|
||||
}
|
||||
|
||||
// First line of HTTP request looks like "GET /path HTTP/1.1"
|
||||
// Retrieve the "/path" part by finding the spaces
|
||||
int addr_start = req.indexOf(' ');
|
||||
int addr_end = req.indexOf(' ', addr_start + 1);
|
||||
if (addr_start == -1 || addr_end == -1) {
|
||||
#ifdef DEBUG_ESP_HTTP_SERVER
|
||||
DEBUG_OUTPUT.print("Invalid request: ");
|
||||
DEBUG_OUTPUT.println(req);
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
|
||||
String methodStr = req.substring(0, addr_start);
|
||||
String url = req.substring(addr_start + 1, addr_end);
|
||||
String versionEnd = req.substring(addr_end + 8);
|
||||
_currentVersion = atoi(versionEnd.c_str());
|
||||
String searchStr = "";
|
||||
int hasSearch = url.indexOf('?');
|
||||
if (hasSearch != -1){
|
||||
searchStr = url.substring(hasSearch + 1);
|
||||
url = url.substring(0, hasSearch);
|
||||
}
|
||||
_currentUri = url;
|
||||
_chunked = false;
|
||||
|
||||
HTTPMethod method = HTTP_GET;
|
||||
if (methodStr == F("POST")) {
|
||||
method = HTTP_POST;
|
||||
} else if (methodStr == F("DELETE")) {
|
||||
method = HTTP_DELETE;
|
||||
} else if (methodStr == F("OPTIONS")) {
|
||||
method = HTTP_OPTIONS;
|
||||
} else if (methodStr == F("PUT")) {
|
||||
method = HTTP_PUT;
|
||||
} else if (methodStr == F("PATCH")) {
|
||||
method = HTTP_PATCH;
|
||||
}
|
||||
_currentMethod = method;
|
||||
|
||||
#ifdef DEBUG_ESP_HTTP_SERVER
|
||||
DEBUG_OUTPUT.print("method: ");
|
||||
DEBUG_OUTPUT.print(methodStr);
|
||||
DEBUG_OUTPUT.print(" url: ");
|
||||
DEBUG_OUTPUT.print(url);
|
||||
DEBUG_OUTPUT.print(" search: ");
|
||||
DEBUG_OUTPUT.println(searchStr);
|
||||
#endif
|
||||
|
||||
//attach handler
|
||||
RequestHandler* handler;
|
||||
for (handler = _firstHandler; handler; handler = handler->next()) {
|
||||
if (handler->canHandle(_currentMethod, _currentUri))
|
||||
break;
|
||||
}
|
||||
_currentHandler = handler;
|
||||
|
||||
String formData;
|
||||
// below is needed only when POST type request
|
||||
if (method == HTTP_POST || method == HTTP_PUT || method == HTTP_PATCH || method == HTTP_DELETE){
|
||||
String boundaryStr;
|
||||
String headerName;
|
||||
String headerValue;
|
||||
bool isForm = false;
|
||||
bool isEncoded = false;
|
||||
uint32_t contentLength = 0;
|
||||
//parse headers
|
||||
while(1){
|
||||
req = client.readStringUntil('\r');
|
||||
client.readStringUntil('\n');
|
||||
if (req == "") break;//no moar headers
|
||||
int headerDiv = req.indexOf(':');
|
||||
if (headerDiv == -1){
|
||||
break;
|
||||
}
|
||||
headerName = req.substring(0, headerDiv);
|
||||
headerValue = req.substring(headerDiv + 1);
|
||||
headerValue.trim();
|
||||
_collectHeader(headerName.c_str(),headerValue.c_str());
|
||||
|
||||
#ifdef DEBUG_ESP_HTTP_SERVER
|
||||
DEBUG_OUTPUT.print("headerName: ");
|
||||
DEBUG_OUTPUT.println(headerName);
|
||||
DEBUG_OUTPUT.print("headerValue: ");
|
||||
DEBUG_OUTPUT.println(headerValue);
|
||||
#endif
|
||||
|
||||
if (headerName.equalsIgnoreCase(FPSTR(Content_Type))){
|
||||
using namespace mime;
|
||||
if (headerValue.startsWith(FPSTR(mimeTable[txt].mimeType))){
|
||||
isForm = false;
|
||||
} else if (headerValue.startsWith(F("application/x-www-form-urlencoded"))){
|
||||
isForm = false;
|
||||
isEncoded = true;
|
||||
} else if (headerValue.startsWith(F("multipart/"))){
|
||||
boundaryStr = headerValue.substring(headerValue.indexOf('=') + 1);
|
||||
boundaryStr.replace("\"","");
|
||||
isForm = true;
|
||||
}
|
||||
} else if (headerName.equalsIgnoreCase(F("Content-Length"))){
|
||||
contentLength = headerValue.toInt();
|
||||
} else if (headerName.equalsIgnoreCase(F("Host"))){
|
||||
_hostHeader = headerValue;
|
||||
}
|
||||
}
|
||||
|
||||
if (!isForm){
|
||||
size_t plainLength;
|
||||
char* plainBuf = readBytesWithTimeout(client, contentLength, plainLength, HTTP_MAX_POST_WAIT);
|
||||
if (plainLength < contentLength) {
|
||||
free(plainBuf);
|
||||
return false;
|
||||
}
|
||||
if (contentLength > 0) {
|
||||
if(isEncoded){
|
||||
//url encoded form
|
||||
if (searchStr != "") searchStr += '&';
|
||||
searchStr += plainBuf;
|
||||
}
|
||||
_parseArguments(searchStr);
|
||||
if(!isEncoded||(0==_currentArgCount)){ // @20180124OF01: Workarround for Alexa Bug
|
||||
//plain post json or other data
|
||||
RequestArgument& arg = _currentArgs[_currentArgCount++];
|
||||
arg.key = F("plain");
|
||||
arg.value = String(plainBuf);
|
||||
}
|
||||
|
||||
#ifdef DEBUG_ESP_HTTP_SERVER
|
||||
DEBUG_OUTPUT.print("Plain: ");
|
||||
DEBUG_OUTPUT.println(plainBuf);
|
||||
#endif
|
||||
free(plainBuf);
|
||||
} else {
|
||||
// No content - but we can still have arguments in the URL.
|
||||
_parseArguments(searchStr);
|
||||
}
|
||||
}
|
||||
|
||||
if (isForm){
|
||||
_parseArguments(searchStr);
|
||||
if (!_parseForm(client, boundaryStr, contentLength)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
String headerName;
|
||||
String headerValue;
|
||||
//parse headers
|
||||
while(1){
|
||||
req = client.readStringUntil('\r');
|
||||
client.readStringUntil('\n');
|
||||
if (req == "") break;//no moar headers
|
||||
int headerDiv = req.indexOf(':');
|
||||
if (headerDiv == -1){
|
||||
break;
|
||||
}
|
||||
headerName = req.substring(0, headerDiv);
|
||||
headerValue = req.substring(headerDiv + 2);
|
||||
_collectHeader(headerName.c_str(),headerValue.c_str());
|
||||
|
||||
#ifdef DEBUG_ESP_HTTP_SERVER
|
||||
DEBUG_OUTPUT.print("headerName: ");
|
||||
DEBUG_OUTPUT.println(headerName);
|
||||
DEBUG_OUTPUT.print("headerValue: ");
|
||||
DEBUG_OUTPUT.println(headerValue);
|
||||
#endif
|
||||
|
||||
if (headerName.equalsIgnoreCase("Host")){
|
||||
_hostHeader = headerValue;
|
||||
}
|
||||
}
|
||||
_parseArguments(searchStr);
|
||||
}
|
||||
client.flush();
|
||||
|
||||
#ifdef DEBUG_ESP_HTTP_SERVER
|
||||
DEBUG_OUTPUT.print("Request: ");
|
||||
DEBUG_OUTPUT.println(url);
|
||||
DEBUG_OUTPUT.print(" Arguments: ");
|
||||
DEBUG_OUTPUT.println(searchStr);
|
||||
#endif
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ESP8266WebServer::_collectHeader(const char* headerName, const char* headerValue) {
|
||||
for (int i = 0; i < _headerKeysCount; i++) {
|
||||
if (_currentHeaders[i].key.equalsIgnoreCase(headerName)) {
|
||||
_currentHeaders[i].value=headerValue;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void ESP8266WebServer::_parseArguments(String data) {
|
||||
#ifdef DEBUG_ESP_HTTP_SERVER
|
||||
DEBUG_OUTPUT.print("args: ");
|
||||
DEBUG_OUTPUT.println(data);
|
||||
#endif
|
||||
if (_currentArgs)
|
||||
delete[] _currentArgs;
|
||||
_currentArgs = 0;
|
||||
if (data.length() == 0) {
|
||||
_currentArgCount = 0;
|
||||
_currentArgs = new RequestArgument[1];
|
||||
return;
|
||||
}
|
||||
_currentArgCount = 1;
|
||||
|
||||
for (int i = 0; i < (int)data.length(); ) {
|
||||
i = data.indexOf('&', i);
|
||||
if (i == -1)
|
||||
break;
|
||||
++i;
|
||||
++_currentArgCount;
|
||||
}
|
||||
#ifdef DEBUG_ESP_HTTP_SERVER
|
||||
DEBUG_OUTPUT.print("args count: ");
|
||||
DEBUG_OUTPUT.println(_currentArgCount);
|
||||
#endif
|
||||
|
||||
_currentArgs = new RequestArgument[_currentArgCount+1];
|
||||
int pos = 0;
|
||||
int iarg;
|
||||
for (iarg = 0; iarg < _currentArgCount;) {
|
||||
int equal_sign_index = data.indexOf('=', pos);
|
||||
int next_arg_index = data.indexOf('&', pos);
|
||||
#ifdef DEBUG_ESP_HTTP_SERVER
|
||||
DEBUG_OUTPUT.print("pos ");
|
||||
DEBUG_OUTPUT.print(pos);
|
||||
DEBUG_OUTPUT.print("=@ ");
|
||||
DEBUG_OUTPUT.print(equal_sign_index);
|
||||
DEBUG_OUTPUT.print(" &@ ");
|
||||
DEBUG_OUTPUT.println(next_arg_index);
|
||||
#endif
|
||||
if ((equal_sign_index == -1) || ((equal_sign_index > next_arg_index) && (next_arg_index != -1))) {
|
||||
#ifdef DEBUG_ESP_HTTP_SERVER
|
||||
DEBUG_OUTPUT.print("arg missing value: ");
|
||||
DEBUG_OUTPUT.println(iarg);
|
||||
#endif
|
||||
if (next_arg_index == -1)
|
||||
break;
|
||||
pos = next_arg_index + 1;
|
||||
continue;
|
||||
}
|
||||
RequestArgument& arg = _currentArgs[iarg];
|
||||
arg.key = urlDecode(data.substring(pos, equal_sign_index));
|
||||
arg.value = urlDecode(data.substring(equal_sign_index + 1, next_arg_index));
|
||||
#ifdef DEBUG_ESP_HTTP_SERVER
|
||||
DEBUG_OUTPUT.print("arg ");
|
||||
DEBUG_OUTPUT.print(iarg);
|
||||
DEBUG_OUTPUT.print(" key: ");
|
||||
DEBUG_OUTPUT.print(arg.key);
|
||||
DEBUG_OUTPUT.print(" value: ");
|
||||
DEBUG_OUTPUT.println(arg.value);
|
||||
#endif
|
||||
++iarg;
|
||||
if (next_arg_index == -1)
|
||||
break;
|
||||
pos = next_arg_index + 1;
|
||||
}
|
||||
_currentArgCount = iarg;
|
||||
#ifdef DEBUG_ESP_HTTP_SERVER
|
||||
DEBUG_OUTPUT.print("args count: ");
|
||||
DEBUG_OUTPUT.println(_currentArgCount);
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
void ESP8266WebServer::_uploadWriteByte(uint8_t b){
|
||||
if (_currentUpload->currentSize == HTTP_UPLOAD_BUFLEN){
|
||||
if(_currentHandler && _currentHandler->canUpload(_currentUri))
|
||||
_currentHandler->upload(*this, _currentUri, *_currentUpload);
|
||||
_currentUpload->totalSize += _currentUpload->currentSize;
|
||||
_currentUpload->currentSize = 0;
|
||||
}
|
||||
_currentUpload->buf[_currentUpload->currentSize++] = b;
|
||||
}
|
||||
|
||||
uint8_t ESP8266WebServer::_uploadReadByte(WiFiClient& client){
|
||||
int res = client.read();
|
||||
if(res == -1){
|
||||
while(!client.available() && client.connected())
|
||||
yield();
|
||||
res = client.read();
|
||||
}
|
||||
return (uint8_t)res;
|
||||
}
|
||||
|
||||
bool ESP8266WebServer::_parseForm(WiFiClient& client, String boundary, uint32_t len){
|
||||
(void) len;
|
||||
#ifdef DEBUG_ESP_HTTP_SERVER
|
||||
DEBUG_OUTPUT.print("Parse Form: Boundary: ");
|
||||
DEBUG_OUTPUT.print(boundary);
|
||||
DEBUG_OUTPUT.print(" Length: ");
|
||||
DEBUG_OUTPUT.println(len);
|
||||
#endif
|
||||
String line;
|
||||
int retry = 0;
|
||||
do {
|
||||
line = client.readStringUntil('\r');
|
||||
++retry;
|
||||
} while (line.length() == 0 && retry < 3);
|
||||
|
||||
client.readStringUntil('\n');
|
||||
//start reading the form
|
||||
if (line == ("--"+boundary)){
|
||||
RequestArgument* postArgs = new RequestArgument[32];
|
||||
int postArgsLen = 0;
|
||||
while(1){
|
||||
String argName;
|
||||
String argValue;
|
||||
String argType;
|
||||
String argFilename;
|
||||
bool argIsFile = false;
|
||||
|
||||
line = client.readStringUntil('\r');
|
||||
client.readStringUntil('\n');
|
||||
if (line.length() > 19 && line.substring(0, 19).equalsIgnoreCase(F("Content-Disposition"))){
|
||||
int nameStart = line.indexOf('=');
|
||||
if (nameStart != -1){
|
||||
argName = line.substring(nameStart+2);
|
||||
nameStart = argName.indexOf('=');
|
||||
if (nameStart == -1){
|
||||
argName = argName.substring(0, argName.length() - 1);
|
||||
} else {
|
||||
argFilename = argName.substring(nameStart+2, argName.length() - 1);
|
||||
argName = argName.substring(0, argName.indexOf('"'));
|
||||
argIsFile = true;
|
||||
#ifdef DEBUG_ESP_HTTP_SERVER
|
||||
DEBUG_OUTPUT.print("PostArg FileName: ");
|
||||
DEBUG_OUTPUT.println(argFilename);
|
||||
#endif
|
||||
//use GET to set the filename if uploading using blob
|
||||
if (argFilename == F("blob") && hasArg(FPSTR(filename)))
|
||||
argFilename = arg(FPSTR(filename));
|
||||
}
|
||||
#ifdef DEBUG_ESP_HTTP_SERVER
|
||||
DEBUG_OUTPUT.print("PostArg Name: ");
|
||||
DEBUG_OUTPUT.println(argName);
|
||||
#endif
|
||||
using namespace mime;
|
||||
argType = FPSTR(mimeTable[txt].mimeType);
|
||||
line = client.readStringUntil('\r');
|
||||
client.readStringUntil('\n');
|
||||
if (line.length() > 12 && line.substring(0, 12).equalsIgnoreCase(FPSTR(Content_Type))){
|
||||
argType = line.substring(line.indexOf(':')+2);
|
||||
//skip next line
|
||||
client.readStringUntil('\r');
|
||||
client.readStringUntil('\n');
|
||||
}
|
||||
#ifdef DEBUG_ESP_HTTP_SERVER
|
||||
DEBUG_OUTPUT.print("PostArg Type: ");
|
||||
DEBUG_OUTPUT.println(argType);
|
||||
#endif
|
||||
if (!argIsFile){
|
||||
while(1){
|
||||
line = client.readStringUntil('\r');
|
||||
client.readStringUntil('\n');
|
||||
if (line.startsWith("--"+boundary)) break;
|
||||
if (argValue.length() > 0) argValue += "\n";
|
||||
argValue += line;
|
||||
}
|
||||
#ifdef DEBUG_ESP_HTTP_SERVER
|
||||
DEBUG_OUTPUT.print("PostArg Value: ");
|
||||
DEBUG_OUTPUT.println(argValue);
|
||||
DEBUG_OUTPUT.println();
|
||||
#endif
|
||||
|
||||
RequestArgument& arg = postArgs[postArgsLen++];
|
||||
arg.key = argName;
|
||||
arg.value = argValue;
|
||||
|
||||
if (line == ("--"+boundary+"--")){
|
||||
#ifdef DEBUG_ESP_HTTP_SERVER
|
||||
DEBUG_OUTPUT.println("Done Parsing POST");
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
_currentUpload.reset(new HTTPUpload());
|
||||
_currentUpload->status = UPLOAD_FILE_START;
|
||||
_currentUpload->name = argName;
|
||||
_currentUpload->filename = argFilename;
|
||||
_currentUpload->type = argType;
|
||||
_currentUpload->totalSize = 0;
|
||||
_currentUpload->currentSize = 0;
|
||||
#ifdef DEBUG_ESP_HTTP_SERVER
|
||||
DEBUG_OUTPUT.print("Start File: ");
|
||||
DEBUG_OUTPUT.print(_currentUpload->filename);
|
||||
DEBUG_OUTPUT.print(" Type: ");
|
||||
DEBUG_OUTPUT.println(_currentUpload->type);
|
||||
#endif
|
||||
if(_currentHandler && _currentHandler->canUpload(_currentUri))
|
||||
_currentHandler->upload(*this, _currentUri, *_currentUpload);
|
||||
_currentUpload->status = UPLOAD_FILE_WRITE;
|
||||
uint8_t argByte = _uploadReadByte(client);
|
||||
readfile:
|
||||
while(argByte != 0x0D){
|
||||
if (!client.connected()) return _parseFormUploadAborted();
|
||||
_uploadWriteByte(argByte);
|
||||
argByte = _uploadReadByte(client);
|
||||
}
|
||||
|
||||
argByte = _uploadReadByte(client);
|
||||
if (!client.connected()) return _parseFormUploadAborted();
|
||||
if (argByte == 0x0A){
|
||||
argByte = _uploadReadByte(client);
|
||||
if (!client.connected()) return _parseFormUploadAborted();
|
||||
if ((char)argByte != '-'){
|
||||
//continue reading the file
|
||||
_uploadWriteByte(0x0D);
|
||||
_uploadWriteByte(0x0A);
|
||||
goto readfile;
|
||||
} else {
|
||||
argByte = _uploadReadByte(client);
|
||||
if (!client.connected()) return _parseFormUploadAborted();
|
||||
if ((char)argByte != '-'){
|
||||
//continue reading the file
|
||||
_uploadWriteByte(0x0D);
|
||||
_uploadWriteByte(0x0A);
|
||||
_uploadWriteByte((uint8_t)('-'));
|
||||
goto readfile;
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t endBuf[boundary.length()];
|
||||
client.readBytes(endBuf, boundary.length());
|
||||
|
||||
if (strstr((const char*)endBuf, boundary.c_str()) != nullptr){
|
||||
if(_currentHandler && _currentHandler->canUpload(_currentUri))
|
||||
_currentHandler->upload(*this, _currentUri, *_currentUpload);
|
||||
_currentUpload->totalSize += _currentUpload->currentSize;
|
||||
_currentUpload->status = UPLOAD_FILE_END;
|
||||
if(_currentHandler && _currentHandler->canUpload(_currentUri))
|
||||
_currentHandler->upload(*this, _currentUri, *_currentUpload);
|
||||
#ifdef DEBUG_ESP_HTTP_SERVER
|
||||
DEBUG_OUTPUT.print("End File: ");
|
||||
DEBUG_OUTPUT.print(_currentUpload->filename);
|
||||
DEBUG_OUTPUT.print(" Type: ");
|
||||
DEBUG_OUTPUT.print(_currentUpload->type);
|
||||
DEBUG_OUTPUT.print(" Size: ");
|
||||
DEBUG_OUTPUT.println(_currentUpload->totalSize);
|
||||
#endif
|
||||
line = client.readStringUntil(0x0D);
|
||||
client.readStringUntil(0x0A);
|
||||
if (line == "--"){
|
||||
#ifdef DEBUG_ESP_HTTP_SERVER
|
||||
DEBUG_OUTPUT.println("Done Parsing POST");
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
continue;
|
||||
} else {
|
||||
_uploadWriteByte(0x0D);
|
||||
_uploadWriteByte(0x0A);
|
||||
_uploadWriteByte((uint8_t)('-'));
|
||||
_uploadWriteByte((uint8_t)('-'));
|
||||
uint32_t i = 0;
|
||||
while(i < boundary.length()){
|
||||
_uploadWriteByte(endBuf[i++]);
|
||||
}
|
||||
argByte = _uploadReadByte(client);
|
||||
goto readfile;
|
||||
}
|
||||
} else {
|
||||
_uploadWriteByte(0x0D);
|
||||
goto readfile;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int iarg;
|
||||
int totalArgs = ((32 - postArgsLen) < _currentArgCount)?(32 - postArgsLen):_currentArgCount;
|
||||
for (iarg = 0; iarg < totalArgs; iarg++){
|
||||
RequestArgument& arg = postArgs[postArgsLen++];
|
||||
arg.key = _currentArgs[iarg].key;
|
||||
arg.value = _currentArgs[iarg].value;
|
||||
}
|
||||
if (_currentArgs) delete[] _currentArgs;
|
||||
_currentArgs = new RequestArgument[postArgsLen];
|
||||
for (iarg = 0; iarg < postArgsLen; iarg++){
|
||||
RequestArgument& arg = _currentArgs[iarg];
|
||||
arg.key = postArgs[iarg].key;
|
||||
arg.value = postArgs[iarg].value;
|
||||
}
|
||||
_currentArgCount = iarg;
|
||||
if (postArgs)
|
||||
delete[] postArgs;
|
||||
return true;
|
||||
}
|
||||
#ifdef DEBUG_ESP_HTTP_SERVER
|
||||
DEBUG_OUTPUT.print("Error: line: ");
|
||||
DEBUG_OUTPUT.println(line);
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
|
||||
String ESP8266WebServer::urlDecode(const String& text)
|
||||
{
|
||||
String decoded = "";
|
||||
char temp[] = "0x00";
|
||||
unsigned int len = text.length();
|
||||
unsigned int i = 0;
|
||||
while (i < len)
|
||||
{
|
||||
char decodedChar;
|
||||
char encodedChar = text.charAt(i++);
|
||||
if ((encodedChar == '%') && (i + 1 < len))
|
||||
{
|
||||
temp[2] = text.charAt(i++);
|
||||
temp[3] = text.charAt(i++);
|
||||
|
||||
decodedChar = strtol(temp, NULL, 16);
|
||||
}
|
||||
else {
|
||||
if (encodedChar == '+')
|
||||
{
|
||||
decodedChar = ' ';
|
||||
}
|
||||
else {
|
||||
decodedChar = encodedChar; // normal ascii char
|
||||
}
|
||||
}
|
||||
decoded += decodedChar;
|
||||
}
|
||||
return decoded;
|
||||
}
|
||||
|
||||
bool ESP8266WebServer::_parseFormUploadAborted(){
|
||||
_currentUpload->status = UPLOAD_FILE_ABORTED;
|
||||
if(_currentHandler && _currentHandler->canUpload(_currentUri))
|
||||
_currentHandler->upload(*this, _currentUri, *_currentUpload);
|
||||
return false;
|
||||
}
|
||||
|
||||
#endif // ARDUINO_ESP8266_RELEASE
|
||||
|
||||
#endif // ESP8266
|
@ -147,6 +147,7 @@
|
||||
#define D_JSON_SPEED "Speed"
|
||||
#define D_JSON_SPEED_UNIT "SpeedUnit"
|
||||
#define D_JSON_SSID "SSId"
|
||||
#define D_JSON_STAGE "Stage"
|
||||
#define D_JSON_STARTDST "StartDST" // Start Daylight Savings Time
|
||||
#define D_JSON_STARTED "Started"
|
||||
#define D_JSON_STARTUPUTC "StartupUTC"
|
||||
@ -319,6 +320,11 @@
|
||||
#define D_CMND_HUMOFFSET "HumOffset"
|
||||
#define D_CMND_GLOBAL_TEMP "GlobalTemp"
|
||||
#define D_CMND_GLOBAL_HUM "GlobalHum"
|
||||
#ifdef ESP32
|
||||
#define D_CMND_TOUCH_CAL "TouchCal"
|
||||
#define D_CMND_TOUCH_THRES "TouchThres"
|
||||
#define D_CMND_TOUCH_NUM "TouchNum"
|
||||
#endif //ESP32
|
||||
|
||||
// Commands xdrv_01_mqtt.ino
|
||||
#define D_CMND_MQTTLOG "MqttLog"
|
||||
@ -513,10 +519,16 @@
|
||||
#define D_CMND_ZIGBEE_FORGET "Forget"
|
||||
#define D_CMND_ZIGBEE_SAVE "Save"
|
||||
#define D_CMND_ZIGBEE_LINKQUALITY "LinkQuality"
|
||||
#define D_CMND_ZIGBEE_CLUSTER "Cluster"
|
||||
#define D_CMND_ZIGBEE_ENDPOINT "Endpoint"
|
||||
#define D_CMND_ZIGBEE_GROUP "Group"
|
||||
#define D_CMND_ZIGBEE_MANUF "Manuf"
|
||||
#define D_CMND_ZIGBEE_DEVICE "Device"
|
||||
#define D_CMND_ZIGBEE_READ "Read"
|
||||
#define D_CMND_ZIGBEE_SEND "Send"
|
||||
#define D_CMND_ZIGBEE_WRITE "Write"
|
||||
#define D_CMND_ZIGBEE_REPORT "Report"
|
||||
#define D_CMND_ZIGBEE_RESPONSE "Response"
|
||||
#define D_JSON_ZIGBEE_ZCL_SENT "ZbZCLSent"
|
||||
#define D_JSON_ZIGBEE_RECEIVED "ZbReceived"
|
||||
#define D_CMND_ZIGBEE_BIND "Bind"
|
||||
|
@ -107,7 +107,7 @@
|
||||
#define D_HOST "Host"
|
||||
#define D_HOSTNAME "Hostname"
|
||||
#define D_HUMIDITY "Feuchtigkeit"
|
||||
#define D_ILLUMINANCE "Beleuchtungsintensität"
|
||||
#define D_ILLUMINANCE "Beleuchtungsstärke"
|
||||
#define D_IMMEDIATE "direkt" // Button immediate
|
||||
#define D_INDEX "Index"
|
||||
#define D_INFO "Info"
|
||||
|
@ -518,6 +518,7 @@
|
||||
// #define USE_AS3935 // [I2cDriver48] Enable AS3935 Franklin Lightning Sensor (I2C address 0x03) (+5k4 code)
|
||||
// #define USE_VEML6075 // [I2cDriver49] Enable VEML6075 UVA/UVB/UVINDEX Sensor (I2C address 0x10) (+2k1 code)
|
||||
// #define USE_VEML7700 // [I2cDriver50] Enable VEML7700 Ambient Light sensor (I2C addresses 0x10) (+4k5 code)
|
||||
// #define USE_MCP9808 // [I2cDriver51] Enable MCP9808 temperature sensor (I2C addresses 0x18 - 0x1F) (+0k9 code)
|
||||
|
||||
// #define USE_DISPLAY // Add I2C Display Support (+2k code)
|
||||
#define USE_DISPLAY_MODES1TO5 // Enable display mode 1 to 5 in addition to mode 0
|
||||
@ -611,12 +612,12 @@
|
||||
// -- Low level interface devices -----------------
|
||||
#define USE_DHT // Add support for DHT11, AM2301 (DHT21, DHT22, AM2302, AM2321) and SI7021 Temperature and Humidity sensor (1k6 code)
|
||||
|
||||
//#define USE_MAX31855 // Add support for MAX31855 K-Type thermocouple sensor using softSPI
|
||||
//#define USE_MAX31855 // Add support for MAX31855/MAX6675 K-Type thermocouple sensor using softSPI
|
||||
//#define USE_MAX31865 // Add support for MAX31865 RTD sensors using softSPI
|
||||
#define MAX31865_PTD_WIRES 2 // PTDs come in several flavors. Pick yours
|
||||
#define MAX31865_PTD_RES 100 // Nominal PTD resistance at 0°C (100Ω for a PT100, 1000Ω for a PT1000, YMMV!)
|
||||
#define MAX31865_REF_RES 430 // Reference resistor (Usually 430Ω for a PT100, 4300Ω for a PT1000)
|
||||
#define MAX31865_PTD_BIAS 0 // To calibrate your not-so-good PTD
|
||||
#define MAX31865_PTD_WIRES 2 // PTDs come in several flavors. Pick yours
|
||||
#define MAX31865_PTD_RES 100 // Nominal PTD resistance at 0°C (100Ω for a PT100, 1000Ω for a PT1000, YMMV!)
|
||||
#define MAX31865_REF_RES 430 // Reference resistor (Usually 430Ω for a PT100, 4300Ω for a PT1000)
|
||||
#define MAX31865_PTD_BIAS 0 // To calibrate your not-so-good PTD
|
||||
|
||||
// -- IR Remote features - all protocols from IRremoteESP8266 --------------------------
|
||||
// IR Full Protocols mode is activated through platform.io only.
|
||||
|
@ -113,7 +113,7 @@ typedef union { // Restricted by MISRA-C Rule 18.4 bu
|
||||
uint32_t fade_at_startup : 1; // bit 9 (v8.2.0.3) - SetOption91 - Enable light fading at start/power on
|
||||
uint32_t pwm_ct_mode : 1; // bit 10 (v8.2.0.4) - SetOption92 - Set PWM Mode from regular PWM to ColorTemp control (Xiaomi Philips ...)
|
||||
uint32_t compress_rules_cpu : 1; // bit 11 (v8.2.0.6) - SetOption93 - Keep uncompressed rules in memory to avoid CPU load of uncompressing at each tick
|
||||
uint32_t spare12 : 1;
|
||||
uint32_t max6675 : 1; // bit 12 (v8.3.1.2) - SetOption94 - Implement simpler MAX6675 protocol instead of MAX31855
|
||||
uint32_t spare13 : 1;
|
||||
uint32_t spare14 : 1;
|
||||
uint32_t spare15 : 1;
|
||||
@ -232,9 +232,8 @@ typedef union {
|
||||
struct {
|
||||
uint8_t spare0 : 1;
|
||||
uint8_t spare1 : 1;
|
||||
uint8_t spare2 : 1;
|
||||
uint8_t spare3 : 1;
|
||||
uint8_t bh1750_resolution : 2; // Sensor10 1,2,3
|
||||
uint8_t bh1750_2_resolution : 2;
|
||||
uint8_t bh1750_1_resolution : 2; // Sensor10 1,2,3
|
||||
uint8_t hx711_json_weight_change : 1; // Sensor34 8,x - Enable JSON message on weight change
|
||||
uint8_t mhz19b_abc_disable : 1; // Disable ABC (Automatic Baseline Correction for MHZ19(B) (0 = Enabled (default), 1 = Disabled with Sensor15 command)
|
||||
};
|
||||
|
@ -1459,6 +1459,7 @@ bool I2cValidRead(uint8_t addr, uint8_t reg, uint8_t size)
|
||||
}
|
||||
retry--;
|
||||
}
|
||||
if (!retry) Wire.endTransmission();
|
||||
return status;
|
||||
}
|
||||
|
||||
|
@ -44,7 +44,7 @@ struct BUTTON {
|
||||
uint8_t dual_receive_count = 0; // Sonoff dual input flag
|
||||
uint8_t no_pullup_mask = 0; // key no pullup flag (1 = no pullup)
|
||||
uint8_t inverted_mask = 0; // Key inverted flag (1 = inverted)
|
||||
#ifdef ESP32
|
||||
#ifdef ESP32
|
||||
uint8_t touch_mask = 0; // Touch flag (1 = inverted)
|
||||
uint8_t touch_hits[MAX_KEYS] = { 0 }; // Hits in a row to filter out noise
|
||||
#endif // ESP32
|
||||
@ -52,6 +52,14 @@ struct BUTTON {
|
||||
uint8_t adc = 99; // ADC0 button number
|
||||
} Button;
|
||||
|
||||
#ifdef ESP32
|
||||
struct TOUCH_BUTTON {
|
||||
uint8_t pin_threshold = TOUCH_PIN_THRESHOLD;
|
||||
uint8_t hit_threshold = TOUCH_HIT_THRESHOLD;
|
||||
uint8_t calibration = 0; // Bitfield
|
||||
} TOUCH_BUTTON;
|
||||
#endif // ESP32
|
||||
|
||||
/********************************************************************************************/
|
||||
|
||||
void ButtonPullupFlag(uint8 button_bit)
|
||||
@ -72,6 +80,11 @@ void ButtonTouchFlag(uint8 button_bit)
|
||||
void ButtonInit(void)
|
||||
{
|
||||
Button.present = 0;
|
||||
#ifdef ESP8266
|
||||
if ((SONOFF_DUAL == my_module_type) || (CH4 == my_module_type)) {
|
||||
Button.present++;
|
||||
}
|
||||
#endif // ESP8266
|
||||
for (uint32_t i = 0; i < MAX_KEYS; i++) {
|
||||
if (PinUsed(GPIO_KEY1, i)) {
|
||||
Button.present++;
|
||||
@ -143,29 +156,35 @@ void ButtonHandler(void)
|
||||
Button.dual_code = 0;
|
||||
}
|
||||
}
|
||||
else
|
||||
if (PinUsed(GPIO_KEY1, button_index)) {
|
||||
button_present = 1;
|
||||
button = (digitalRead(Pin(GPIO_KEY1, button_index)) != bitRead(Button.inverted_mask, button_index));
|
||||
else {
|
||||
if (PinUsed(GPIO_KEY1, button_index)) {
|
||||
button_present = 1;
|
||||
button = (digitalRead(Pin(GPIO_KEY1, button_index)) != bitRead(Button.inverted_mask, button_index));
|
||||
}
|
||||
}
|
||||
#else
|
||||
if (PinUsed(GPIO_KEY1, button_index)) {
|
||||
button_present = 1;
|
||||
if (bitRead(Button.touch_mask, button_index)){ // Touch
|
||||
if (bitRead(Button.touch_mask, button_index)) { // Touch
|
||||
uint32_t _value = touchRead(Pin(GPIO_KEY1, button_index));
|
||||
button = NOT_PRESSED;
|
||||
if (_value != 0){ // probably read-error
|
||||
if(_value < TOUCH_PIN_THRESHOLD){
|
||||
if(++Button.touch_hits[button_index]>TOUCH_HIT_THRESHOLD){
|
||||
button = PRESSED;
|
||||
AddLog_P2(LOG_LEVEL_DEBUG, PSTR("Touch value: %u hits: %u"), _value, Button.touch_hits[button_index]);
|
||||
if (_value != 0) { // Probably read-error
|
||||
if (_value < TOUCH_BUTTON.pin_threshold) {
|
||||
if (++Button.touch_hits[button_index] > TOUCH_BUTTON.hit_threshold) {
|
||||
if (!bitRead(TOUCH_BUTTON.calibration, button_index+1)) {
|
||||
button = PRESSED;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
Button.touch_hits[button_index] = 0;
|
||||
}
|
||||
else Button.touch_hits[button_index] = 0;
|
||||
} else {
|
||||
Button.touch_hits[button_index] = 0;
|
||||
}
|
||||
else Button.touch_hits[button_index] = 0;
|
||||
}
|
||||
else{ // Normal button
|
||||
if (bitRead(TOUCH_BUTTON.calibration, button_index+1)) {
|
||||
AddLog_P2(LOG_LEVEL_INFO, PSTR("PLOT: %u, %u, %u,"), button_index+1, _value, Button.touch_hits[button_index]); // Button number (1..4), value, continuous hits under threshold
|
||||
}
|
||||
} else { // Normal button
|
||||
button = (digitalRead(Pin(GPIO_KEY1, button_index)) != bitRead(Button.inverted_mask, button_index));
|
||||
}
|
||||
}
|
||||
@ -202,12 +221,12 @@ void ButtonHandler(void)
|
||||
if (!Button.hold_timer[button_index]) { button_pressed = true; } // Do not allow within 1 second
|
||||
}
|
||||
if (button_pressed) {
|
||||
if (!Settings.flag3.mqtt_buttons) {
|
||||
if (!Settings.flag3.mqtt_buttons) { // SetOption73 (0) - Decouple button from relay and send just mqtt topic
|
||||
if (!SendKey(KEY_BUTTON, button_index +1, POWER_TOGGLE)) { // Execute Toggle command via MQTT if ButtonTopic is set
|
||||
ExecuteCommandPower(button_index +1, POWER_TOGGLE, SRC_BUTTON); // Execute Toggle command internally
|
||||
}
|
||||
} else {
|
||||
MqttButtonTopic(button_index +1, 1, 0); // SetOption73 (0) - Decouple button from relay and send just mqtt topic
|
||||
MqttButtonTopic(button_index +1, 1, 0); // SetOption73 (0) - Decouple button from relay and send just mqtt topic
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -236,7 +255,7 @@ void ButtonHandler(void)
|
||||
Button.hold_timer[button_index] = 0;
|
||||
} else {
|
||||
Button.hold_timer[button_index]++;
|
||||
if (Settings.flag.button_single) { // SetOption13 (0) - Allow only single button press for immediate action
|
||||
if (Settings.flag.button_single) { // SetOption13 (0) - Allow only single button press for immediate action
|
||||
if (Button.hold_timer[button_index] == loops_per_second * hold_time_extent * Settings.param[P_HOLD_TIME] / 10) { // SetOption32 (40) - Button held for factor times longer
|
||||
snprintf_P(scmnd, sizeof(scmnd), PSTR(D_CMND_SETOPTION "13 0")); // Disable single press only
|
||||
ExecuteCommand(scmnd, SRC_BUTTON);
|
||||
@ -244,13 +263,13 @@ void ButtonHandler(void)
|
||||
} else {
|
||||
if (Button.hold_timer[button_index] == loops_per_second * Settings.param[P_HOLD_TIME] / 10) { // SetOption32 (40) - Button hold
|
||||
Button.press_counter[button_index] = 0;
|
||||
if (Settings.flag3.mqtt_buttons) { // SetOption73 (0) - Decouple button from relay and send just mqtt topic
|
||||
if (Settings.flag3.mqtt_buttons) { // SetOption73 (0) - Decouple button from relay and send just mqtt topic
|
||||
MqttButtonTopic(button_index +1, 3, 1);
|
||||
} else {
|
||||
SendKey(KEY_BUTTON, button_index +1, POWER_HOLD); // Execute Hold command via MQTT if ButtonTopic is set
|
||||
}
|
||||
} else {
|
||||
if (!Settings.flag.button_restrict) {
|
||||
if (!Settings.flag.button_restrict) { // SetOption1 - Control button multipress
|
||||
if ((Button.hold_timer[button_index] == loops_per_second * hold_time_extent * Settings.param[P_HOLD_TIME] / 10)) { // SetOption32 (40) - Button held for factor times longer
|
||||
Button.press_counter[button_index] = 0;
|
||||
snprintf_P(scmnd, sizeof(scmnd), PSTR(D_CMND_RESET " 1"));
|
||||
@ -261,14 +280,14 @@ void ButtonHandler(void)
|
||||
}
|
||||
}
|
||||
|
||||
if (!Settings.flag.button_single) { // SetOption13 (0) - Allow multi-press
|
||||
if (!Settings.flag.button_single) { // SetOption13 (0) - Allow multi-press
|
||||
if (Button.window_timer[button_index]) {
|
||||
Button.window_timer[button_index]--;
|
||||
} else {
|
||||
if (!restart_flag && !Button.hold_timer[button_index] && (Button.press_counter[button_index] > 0) && (Button.press_counter[button_index] < 7)) {
|
||||
|
||||
bool single_press = false;
|
||||
if (Button.press_counter[button_index] < 3) { // Single or Double press
|
||||
if (Button.press_counter[button_index] < 3) { // Single or Double press
|
||||
#ifdef ESP8266
|
||||
if ((SONOFF_DUAL_R2 == my_module_type) || (SONOFF_DUAL == my_module_type) || (CH4 == my_module_type)) {
|
||||
single_press = true;
|
||||
@ -293,15 +312,21 @@ void ButtonHandler(void)
|
||||
if (WifiState() > WIFI_RESTART) { // Wifimanager active
|
||||
restart_flag = 1;
|
||||
}
|
||||
if (!Settings.flag3.mqtt_buttons) {
|
||||
if (Button.press_counter[button_index] == 1) { // By default first press always send a TOGGLE (2)
|
||||
if (!Settings.flag3.mqtt_buttons) { // SetOption73 - Detach buttons from relays and enable MQTT action state for multipress
|
||||
if (Button.press_counter[button_index] == 1) { // By default first press always send a TOGGLE (2)
|
||||
ExecuteCommandPower(button_index + Button.press_counter[button_index], POWER_TOGGLE, SRC_BUTTON);
|
||||
} else {
|
||||
SendKey(KEY_BUTTON, button_index +1, Button.press_counter[button_index] +9); // 2,3,4 and 5 press send just the key value (11,12,13 and 14) for rules
|
||||
if (0 == button_index) { // BUTTON1 can toggle up to 5 relays if present. If a relay is not present will send out the key value (2,11,12,13 and 14) for rules
|
||||
if ((Button.press_counter[button_index] > 1 && PinUsed(GPIO_REL1, Button.press_counter[button_index]-1)) && Button.press_counter[button_index] <= MAX_RELAY_BUTTON1) {
|
||||
if (0 == button_index) { // BUTTON1 can toggle up to 5 relays if present. If a relay is not present will send out the key value (2,11,12,13 and 14) for rules
|
||||
bool valid_relay = PinUsed(GPIO_REL1, Button.press_counter[button_index]-1);
|
||||
#ifdef ESP8266
|
||||
if ((SONOFF_DUAL == my_module_type) || (CH4 == my_module_type)) {
|
||||
valid_relay = (Button.press_counter[button_index] <= devices_present);
|
||||
}
|
||||
#endif // ESP8266
|
||||
if ((Button.press_counter[button_index] > 1) && valid_relay && (Button.press_counter[button_index] <= MAX_RELAY_BUTTON1)) {
|
||||
ExecuteCommandPower(button_index + Button.press_counter[button_index], POWER_TOGGLE, SRC_BUTTON); // Execute Toggle command internally
|
||||
AddLog_P2(LOG_LEVEL_DEBUG, PSTR("DBG: Relay%d found on GPIO%d"), Button.press_counter[button_index], Pin(GPIO_REL1, Button.press_counter[button_index]-1));
|
||||
// AddLog_P2(LOG_LEVEL_DEBUG, PSTR("DBG: Relay%d found on GPIO%d"), Button.press_counter[button_index], Pin(GPIO_REL1, Button.press_counter[button_index]-1));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -38,7 +38,11 @@ const char kTasmotaCommands[] PROGMEM = "|" // No prefix
|
||||
#endif // USE_DEVICE_GROUPS_SEND
|
||||
D_CMND_DEVGROUP_SHARE "|" D_CMND_DEVGROUPSTATUS "|"
|
||||
#endif // USE_DEVICE_GROUPS
|
||||
D_CMND_SENSOR "|" D_CMND_DRIVER;
|
||||
D_CMND_SENSOR "|" D_CMND_DRIVER
|
||||
#ifdef ESP32
|
||||
"|" D_CMND_TOUCH_CAL "|" D_CMND_TOUCH_THRES "|" D_CMND_TOUCH_NUM
|
||||
#endif //ESP32
|
||||
;
|
||||
|
||||
void (* const TasmotaCommand[])(void) PROGMEM = {
|
||||
&CmndBacklog, &CmndDelay, &CmndPower, &CmndStatus, &CmndState, &CmndSleep, &CmndUpgrade, &CmndUpgrade, &CmndOtaUrl,
|
||||
@ -61,7 +65,11 @@ void (* const TasmotaCommand[])(void) PROGMEM = {
|
||||
#endif // USE_DEVICE_GROUPS_SEND
|
||||
&CmndDevGroupShare, &CmndDevGroupStatus,
|
||||
#endif // USE_DEVICE_GROUPS
|
||||
&CmndSensor, &CmndDriver };
|
||||
&CmndSensor, &CmndDriver
|
||||
#ifdef ESP32
|
||||
,&CmndTouchCal, &CmndTouchThres, &CmndTouchNum
|
||||
#endif //ESP32
|
||||
};
|
||||
|
||||
const char kWifiConfig[] PROGMEM =
|
||||
D_WCFG_0_RESTART "||" D_WCFG_2_WIFIMANAGER "||" D_WCFG_4_RETRY "|" D_WCFG_5_WAIT "|" D_WCFG_6_SERIAL "|" D_WCFG_7_WIFIMANAGER_RESET_ONLY;
|
||||
@ -436,14 +444,14 @@ void CmndStatus(void)
|
||||
",\"" D_JSON_BOOTVERSION "\":%d"
|
||||
#endif
|
||||
",\"" D_JSON_COREVERSION "\":\"" ARDUINO_CORE_RELEASE "\",\"" D_JSON_SDKVERSION "\":\"%s\","
|
||||
"\"Hardware\":\"%s\""
|
||||
"\"CpuFrequency\":%d,\"Hardware\":\"%s\""
|
||||
"%s}}"),
|
||||
my_version, my_image, GetBuildDateAndTime().c_str()
|
||||
#ifdef ESP8266
|
||||
, ESP.getBootVersion()
|
||||
#endif
|
||||
, ESP.getSdkVersion(),
|
||||
GetDeviceHardware().c_str(),
|
||||
ESP.getCpuFreqMHz(), GetDeviceHardware().c_str(),
|
||||
GetStatistics().c_str());
|
||||
MqttPublishPrefixTopic_P(option, PSTR(D_CMND_STATUS "2"));
|
||||
}
|
||||
@ -468,7 +476,7 @@ void CmndStatus(void)
|
||||
#ifdef ESP8266
|
||||
",\"" D_JSON_FLASHCHIPID "\":\"%06X\""
|
||||
#endif
|
||||
",\"" D_JSON_FLASHMODE "\":%d,\""
|
||||
",\"FlashFrequency\":%d,\"" D_JSON_FLASHMODE "\":%d,\""
|
||||
D_JSON_FEATURES "\":[\"%08X\",\"%08X\",\"%08X\",\"%08X\",\"%08X\",\"%08X\",\"%08X\"]"),
|
||||
ESP_getSketchSize()/1024, ESP.getFreeSketchSpace()/1024, ESP_getFreeHeap()/1024,
|
||||
#ifdef ESP32
|
||||
@ -478,7 +486,7 @@ void CmndStatus(void)
|
||||
#ifdef ESP8266
|
||||
, ESP.getFlashChipId()
|
||||
#endif
|
||||
, ESP.getFlashChipMode(),
|
||||
, ESP.getFlashChipSpeed()/1000000, ESP.getFlashChipMode(),
|
||||
LANGUAGE_LCID, feature_drv1, feature_drv2, feature_sns1, feature_sns2, feature5, feature6);
|
||||
XsnsDriverState();
|
||||
ResponseAppend_P(PSTR(",\"Sensors\":"));
|
||||
@ -1946,3 +1954,41 @@ void CmndDriver(void)
|
||||
{
|
||||
XdrvCall(FUNC_COMMAND_DRIVER);
|
||||
}
|
||||
|
||||
#ifdef ESP32
|
||||
void CmndTouchCal(void)
|
||||
{
|
||||
if (XdrvMailbox.payload >= 0) {
|
||||
if (XdrvMailbox.payload < MAX_KEYS + 1) TOUCH_BUTTON.calibration = bitSet(TOUCH_BUTTON.calibration, XdrvMailbox.payload);
|
||||
if (XdrvMailbox.payload == 0) TOUCH_BUTTON.calibration = 0;
|
||||
if (XdrvMailbox.payload == 255) TOUCH_BUTTON.calibration = 255; // all pinss
|
||||
}
|
||||
Response_P(PSTR("{\"" D_CMND_TOUCH_CAL "\": %u"), TOUCH_BUTTON.calibration);
|
||||
ResponseJsonEnd();
|
||||
AddLog_P2(LOG_LEVEL_INFO, PSTR("Button Touchvalue Hits,"));
|
||||
}
|
||||
|
||||
void CmndTouchThres(void)
|
||||
{
|
||||
if (XdrvMailbox.payload >= 0) {
|
||||
if (XdrvMailbox.payload<256){
|
||||
TOUCH_BUTTON.pin_threshold = XdrvMailbox.payload;
|
||||
}
|
||||
}
|
||||
Response_P(PSTR("{\"" D_CMND_TOUCH_THRES "\": %u"), TOUCH_BUTTON.pin_threshold);
|
||||
ResponseJsonEnd();
|
||||
}
|
||||
|
||||
void CmndTouchNum(void)
|
||||
{
|
||||
if (XdrvMailbox.payload >= 0) {
|
||||
if (XdrvMailbox.payload<32){
|
||||
TOUCH_BUTTON.hit_threshold = XdrvMailbox.payload;
|
||||
}
|
||||
}
|
||||
Response_P(PSTR("{\"" D_CMND_TOUCH_NUM "\": %u"), TOUCH_BUTTON.hit_threshold);
|
||||
ResponseJsonEnd();
|
||||
|
||||
}
|
||||
|
||||
#endif //ESP32
|
@ -387,7 +387,7 @@ void SendReceiveDeviceGroupMessage(struct device_group * device_group, struct de
|
||||
case DGR_ITEM_POWER:
|
||||
if (Settings.flag4.remote_device_mode) { // SetOption88 - Enable relays in separate device groups
|
||||
bool on = (value & 1);
|
||||
if (on != (power & 1)) ExecuteCommandPower(device_group_index + 1, (on ? POWER_ON : POWER_OFF), SRC_REMOTE);
|
||||
if (on != (power & (1 << device_group_index))) ExecuteCommandPower(device_group_index + 1, (on ? POWER_ON : POWER_OFF), SRC_REMOTE);
|
||||
}
|
||||
else if (device_group->local) {
|
||||
uint8_t mask_devices = value >> 24;
|
||||
@ -396,7 +396,6 @@ void SendReceiveDeviceGroupMessage(struct device_group * device_group, struct de
|
||||
uint32_t mask = 1 << i;
|
||||
bool on = (value & mask);
|
||||
if (on != (power & mask)) ExecuteCommandPower(i + 1, (on ? POWER_ON : POWER_OFF), SRC_REMOTE);
|
||||
if (Settings.flag4.remote_device_mode) break; // SetOption88 - Enable relays in separate device groups
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
@ -569,8 +569,10 @@ void GetFeatures(void)
|
||||
#ifdef USE_VEML7700
|
||||
feature6 |= 0x00001000; // xsns_71_veml7700.ino
|
||||
#endif
|
||||
#ifdef USE_MCP9808
|
||||
feature6 |= 0x00002000; // xsns_72_mcp9808.ino
|
||||
#endif
|
||||
|
||||
// feature6 |= 0x00002000;
|
||||
// feature6 |= 0x00004000;
|
||||
// feature6 |= 0x00008000;
|
||||
|
||||
|
@ -89,7 +89,7 @@ const JsonVariant &GetCaseInsensitive(const JsonObject &json, const char *needle
|
||||
// key can be in PROGMEM
|
||||
// if needle == "?" then we return the first valid key
|
||||
bool wildcard = strcmp_P("?", needle) == 0;
|
||||
if ((nullptr == &json) || (nullptr == needle) || (0 == pgm_read_byte(needle))) {
|
||||
if ((nullptr == &json) || (nullptr == needle) || (0 == pgm_read_byte(needle)) || (!json.success())) {
|
||||
return *(JsonVariant*)nullptr;
|
||||
}
|
||||
|
||||
@ -104,3 +104,9 @@ const JsonVariant &GetCaseInsensitive(const JsonObject &json, const char *needle
|
||||
// if not found
|
||||
return *(JsonVariant*)nullptr;
|
||||
}
|
||||
|
||||
// This function returns true if the JsonObject contains the specified key
|
||||
// It's just a wrapper to the previous function but it can be tricky to test nullptr on an object ref
|
||||
bool HasKeyCaseInsensitive(const JsonObject &json, const char *needle) {
|
||||
return &GetCaseInsensitive(json, needle) != nullptr;
|
||||
}
|
||||
|
@ -117,7 +117,7 @@ void RotaryHandler(void)
|
||||
Rotary.changed = 1;
|
||||
// button1 is pressed: set color temperature
|
||||
int16_t t = LightGetColorTemp();
|
||||
t = t + (Rotary.position - Rotary.last_position);
|
||||
t = t + ((Rotary.position - Rotary.last_position) * 4);
|
||||
if (t < 153) {
|
||||
t = 153;
|
||||
}
|
||||
|
@ -26,6 +26,8 @@
|
||||
\*********************************************************************************************/
|
||||
|
||||
const uint8_t SWITCH_PROBE_INTERVAL = 10; // Time in milliseconds between switch input probe
|
||||
const uint8_t SWITCH_FAST_PROBE_INTERVAL =2;// Time in milliseconds between switch input probe for AC detection
|
||||
const uint8_t AC_PERIOD = (20 + SWITCH_FAST_PROBE_INTERVAL - 1) / SWITCH_FAST_PROBE_INTERVAL; // Duration of an AC wave in probe intervals
|
||||
|
||||
#include <Ticker.h>
|
||||
|
||||
@ -38,6 +40,7 @@ struct SWITCH {
|
||||
uint8_t last_state[MAX_SWITCHES]; // Last wall switch states
|
||||
uint8_t hold_timer[MAX_SWITCHES] = { 0 }; // Timer for wallswitch push button hold
|
||||
uint8_t virtual_state[MAX_SWITCHES]; // Virtual switch states
|
||||
uint8_t first_change = 0;
|
||||
uint8_t present = 0;
|
||||
} Switch;
|
||||
|
||||
@ -81,60 +84,136 @@ void SwitchProbe(void)
|
||||
{
|
||||
if (uptime < 4) { return; } // Block GPIO for 4 seconds after poweron to workaround Wemos D1 / Obi RTS circuit
|
||||
|
||||
uint8_t state_filter = Settings.switch_debounce / SWITCH_PROBE_INTERVAL; // 5, 10, 15
|
||||
uint8_t force_high = (Settings.switch_debounce % 50) &1; // 51, 101, 151 etc
|
||||
uint8_t force_low = (Settings.switch_debounce % 50) &2; // 52, 102, 152 etc
|
||||
uint8_t state_filter;
|
||||
uint8_t debounce_flags = Settings.switch_debounce % 10;
|
||||
uint8_t force_high = debounce_flags &1; // 51, 101, 151 etc
|
||||
uint8_t force_low = debounce_flags &2; // 52, 102, 152 etc
|
||||
uint8_t ac_detect = debounce_flags == 9;
|
||||
uint8_t switch_probe_interval;
|
||||
uint8_t first_change = Switch.first_change;
|
||||
|
||||
if (ac_detect) {
|
||||
switch_probe_interval = SWITCH_FAST_PROBE_INTERVAL;
|
||||
if (Settings.switch_debounce < 2 * AC_PERIOD * SWITCH_FAST_PROBE_INTERVAL + 9) {
|
||||
state_filter = 2 * AC_PERIOD;
|
||||
} else if (Settings.switch_debounce > (0x7f - 2 * AC_PERIOD) * SWITCH_FAST_PROBE_INTERVAL) {
|
||||
state_filter = 0x7f;
|
||||
} else {
|
||||
state_filter = (Settings.switch_debounce - 9) / SWITCH_FAST_PROBE_INTERVAL;
|
||||
}
|
||||
} else {
|
||||
switch_probe_interval = SWITCH_PROBE_INTERVAL;
|
||||
state_filter = Settings.switch_debounce / SWITCH_PROBE_INTERVAL; // 5, 10, 15
|
||||
}
|
||||
|
||||
for (uint32_t i = 0; i < MAX_SWITCHES; i++) {
|
||||
if (PinUsed(GPIO_SWT1, i)) {
|
||||
// Olimex user_switch2.c code to fix 50Hz induced pulses
|
||||
if (1 == digitalRead(Pin(GPIO_SWT1, i))) {
|
||||
|
||||
if (force_high) { // Enabled with SwitchDebounce x1
|
||||
if (1 == Switch.virtual_state[i]) {
|
||||
Switch.state[i] = state_filter; // With noisy input keep current state 1 unless constant 0
|
||||
if (ac_detect) { // Enabled with SwitchDebounce x9
|
||||
Switch.state[i] |= 0x80;
|
||||
if (Switch.state[i] > 0x80) {
|
||||
Switch.state[i]--;
|
||||
if (0x80 == Switch.state[i]) {
|
||||
Switch.virtual_state[i] = 0;
|
||||
Switch.first_change = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
||||
if (Switch.state[i] < state_filter) {
|
||||
Switch.state[i]++;
|
||||
if (state_filter == Switch.state[i]) {
|
||||
Switch.virtual_state[i] = 1;
|
||||
if (force_high) { // Enabled with SwitchDebounce x1
|
||||
if (1 == Switch.virtual_state[i]) {
|
||||
Switch.state[i] = state_filter; // With noisy input keep current state 1 unless constant 0
|
||||
}
|
||||
}
|
||||
|
||||
if (Switch.state[i] < state_filter) {
|
||||
Switch.state[i]++;
|
||||
if (state_filter == Switch.state[i]) {
|
||||
Switch.virtual_state[i] = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
||||
if (force_low) { // Enabled with SwitchDebounce x2
|
||||
if (0 == Switch.virtual_state[i]) {
|
||||
Switch.state[i] = 0; // With noisy input keep current state 0 unless constant 1
|
||||
if (ac_detect) { // Enabled with SwitchDebounce x9
|
||||
/*
|
||||
* Moes MS-104B and similar devices using an AC detection circuitry
|
||||
* on their switch inputs generating an ~4 ms long low pulse every
|
||||
* AC wave. We start the time measurement on the falling edge.
|
||||
*
|
||||
* state: bit7: previous state, bit6..0: counter
|
||||
*/
|
||||
if (Switch.state[i] & 0x80) {
|
||||
Switch.state[i] &= 0x7f;
|
||||
if (Switch.state[i] < state_filter - 2 * AC_PERIOD) {
|
||||
Switch.state[i] += 2 * AC_PERIOD;
|
||||
} else {
|
||||
Switch.state[i] = state_filter;
|
||||
Switch.virtual_state[i] = 1;
|
||||
if (first_change) {
|
||||
Switch.last_state[i] = 1;
|
||||
Switch.first_change = false;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (Switch.state[i] > 0x00) {
|
||||
Switch.state[i]--;
|
||||
if (0x00 == Switch.state[i]) {
|
||||
Switch.virtual_state[i] = 0;
|
||||
Switch.first_change = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
||||
if (Switch.state[i] > 0) {
|
||||
Switch.state[i]--;
|
||||
if (0 == Switch.state[i]) {
|
||||
Switch.virtual_state[i] = 0;
|
||||
if (force_low) { // Enabled with SwitchDebounce x2
|
||||
if (0 == Switch.virtual_state[i]) {
|
||||
Switch.state[i] = 0; // With noisy input keep current state 0 unless constant 1
|
||||
}
|
||||
}
|
||||
|
||||
if (Switch.state[i] > 0) {
|
||||
Switch.state[i]--;
|
||||
if (0 == Switch.state[i]) {
|
||||
Switch.virtual_state[i] = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
TickerSwitch.attach_ms(SWITCH_PROBE_INTERVAL, SwitchProbe); // Re-arm as core 2.3.0 does only support ONCE mode
|
||||
TickerSwitch.attach_ms(switch_probe_interval, SwitchProbe); // Re-arm as core 2.3.0 does only support ONCE mode
|
||||
}
|
||||
|
||||
void SwitchInit(void)
|
||||
{
|
||||
uint8_t ac_detect = Settings.switch_debounce % 10 == 9;
|
||||
|
||||
Switch.present = 0;
|
||||
for (uint32_t i = 0; i < MAX_SWITCHES; i++) {
|
||||
Switch.last_state[i] = 1; // Init global to virtual switch state;
|
||||
if (PinUsed(GPIO_SWT1, i)) {
|
||||
Switch.present++;
|
||||
pinMode(Pin(GPIO_SWT1, i), bitRead(Switch.no_pullup_mask, i) ? INPUT : ((16 == Pin(GPIO_SWT1, i)) ? INPUT_PULLDOWN_16 : INPUT_PULLUP));
|
||||
Switch.last_state[i] = digitalRead(Pin(GPIO_SWT1, i)); // Set global now so doesn't change the saved power state on first switch check
|
||||
if (ac_detect) {
|
||||
Switch.state[i] = 0x80 + 2 * AC_PERIOD;
|
||||
Switch.last_state[i] = 0; // Will set later in the debouncing code
|
||||
} else {
|
||||
Switch.last_state[i] = digitalRead(Pin(GPIO_SWT1, i)); // Set global now so doesn't change the saved power state on first switch check
|
||||
}
|
||||
}
|
||||
Switch.virtual_state[i] = Switch.last_state[i];
|
||||
}
|
||||
if (Switch.present) { TickerSwitch.attach_ms(SWITCH_PROBE_INTERVAL, SwitchProbe); }
|
||||
if (Switch.present) {
|
||||
if (ac_detect) {
|
||||
TickerSwitch.attach_ms(SWITCH_FAST_PROBE_INTERVAL, SwitchProbe);
|
||||
Switch.first_change = true;
|
||||
} else {
|
||||
TickerSwitch.attach_ms(SWITCH_PROBE_INTERVAL, SwitchProbe);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*********************************************************************************************\
|
||||
|
@ -573,7 +573,7 @@ void ExecuteCommandPower(uint32_t device, uint32_t state, uint32_t source)
|
||||
#ifdef USE_DEVICE_GROUPS
|
||||
if (SRC_REMOTE != source && SRC_RETRY != source) {
|
||||
if (Settings.flag4.remote_device_mode) // SetOption88 - Enable relays in separate device groups
|
||||
SendDeviceGroupMessage(device - 1, DGR_MSGTYP_UPDATE, DGR_ITEM_POWER, (power >> device - 1) & 1 | 0x01000000); // Explicitly set number of relays to one
|
||||
SendDeviceGroupMessage(device - 1, DGR_MSGTYP_UPDATE, DGR_ITEM_POWER, (power >> (device - 1)) & 1 | 0x01000000); // Explicitly set number of relays to one
|
||||
else
|
||||
SendLocalDeviceGroupMessage(DGR_MSGTYP_UPDATE, DGR_ITEM_POWER, power);
|
||||
}
|
||||
|
@ -19,7 +19,9 @@
|
||||
|
||||
#ifdef USE_EMULATION
|
||||
|
||||
#define UDP_BUFFER_SIZE 200 // Max UDP buffer size needed for M-SEARCH message
|
||||
#ifndef UDP_BUFFER_SIZE
|
||||
#define UDP_BUFFER_SIZE 120 // Max UDP buffer size needed for M-SEARCH message
|
||||
#endif
|
||||
#define UDP_MSEARCH_SEND_DELAY 1500 // Delay in ms before M-Search response is send
|
||||
|
||||
#include <Ticker.h>
|
||||
@ -31,6 +33,15 @@ uint16_t udp_remote_port; // M-Search remote port
|
||||
bool udp_connected = false;
|
||||
bool udp_response_mutex = false; // M-Search response mutex to control re-entry
|
||||
|
||||
#ifdef ESP8266
|
||||
#ifndef UDP_MAX_PACKETS
|
||||
#define UDP_MAX_PACKETS 3 // we support x more packets than the current one
|
||||
#endif
|
||||
|
||||
#include "UdpListener.h"
|
||||
UdpListener<UDP_BUFFER_SIZE> UdpCtx(UDP_MAX_PACKETS);
|
||||
#endif
|
||||
|
||||
/*********************************************************************************************\
|
||||
* UPNP/SSDP search targets
|
||||
\*********************************************************************************************/
|
||||
@ -48,10 +59,16 @@ const char SSDP_ALL[] PROGMEM = "ssdp:all";
|
||||
bool UdpDisconnect(void)
|
||||
{
|
||||
if (udp_connected) {
|
||||
// flush any outgoing packet
|
||||
PortUdp.flush();
|
||||
#ifdef ESP8266
|
||||
UdpCtx.disconnect();
|
||||
#endif
|
||||
#ifdef USE_DEVICE_GROUPS
|
||||
// stop
|
||||
PortUdp.stop();
|
||||
#else // USE_DEVICE_GROUPS
|
||||
// stop all
|
||||
WiFiUDP::stopAll();
|
||||
#endif // !USE_DEVICE_GROUPS
|
||||
AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_UPNP D_MULTICAST_DISABLED));
|
||||
@ -64,13 +81,25 @@ bool UdpConnect(void)
|
||||
{
|
||||
if (!udp_connected && !restart_flag) {
|
||||
// Simple Service Discovery Protocol (SSDP)
|
||||
#ifdef ESP8266
|
||||
UdpCtx.reset();
|
||||
if (igmp_joingroup(WiFi.localIP(), IPAddress(239,255,255,250)) == ERR_OK) { // addr 239.255.255.250
|
||||
ip_addr_t addr = IPADDR4_INIT(INADDR_ANY);
|
||||
if (UdpCtx.listen(&addr, 1900)) { // port 1900
|
||||
// OK
|
||||
AddLog_P(LOG_LEVEL_INFO, PSTR(D_LOG_UPNP D_MULTICAST_REJOINED));
|
||||
udp_response_mutex = false;
|
||||
udp_connected = true;
|
||||
}
|
||||
#else // ESP32
|
||||
if (PortUdp.beginMulticast(WiFi.localIP(), IPAddress(239,255,255,250), 1900)) {
|
||||
AddLog_P(LOG_LEVEL_INFO, PSTR(D_LOG_UPNP D_MULTICAST_REJOINED));
|
||||
udp_response_mutex = false;
|
||||
udp_connected = true;
|
||||
} else {
|
||||
#endif
|
||||
}
|
||||
if (!udp_connected) { // if connection failed
|
||||
AddLog_P(LOG_LEVEL_INFO, PSTR(D_LOG_UPNP D_MULTICAST_JOIN_FAILED));
|
||||
udp_connected = false;
|
||||
}
|
||||
}
|
||||
return udp_connected;
|
||||
@ -79,14 +108,25 @@ bool UdpConnect(void)
|
||||
void PollUdp(void)
|
||||
{
|
||||
if (udp_connected) {
|
||||
#ifdef ESP8266
|
||||
while (UdpCtx.next()) {
|
||||
UdpPacket<UDP_BUFFER_SIZE> *packet;
|
||||
packet = UdpCtx.read();
|
||||
if (packet->len >= UDP_BUFFER_SIZE) {
|
||||
packet->len--; // leave space for NULL terminator
|
||||
}
|
||||
packet->buf[packet->len] = 0; // add NULL at the end of the packer
|
||||
char * packet_buffer = (char*) &packet->buf;
|
||||
int32_t len = packet->len;
|
||||
#else // ESP32
|
||||
while (PortUdp.parsePacket()) {
|
||||
char packet_buffer[UDP_BUFFER_SIZE]; // buffer to hold incoming UDP/SSDP packet
|
||||
|
||||
int len = PortUdp.read(packet_buffer, UDP_BUFFER_SIZE -1);
|
||||
int32_t len = PortUdp.read(packet_buffer, UDP_BUFFER_SIZE -1);
|
||||
packet_buffer[len] = 0;
|
||||
|
||||
#endif
|
||||
AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("UDP: Packet (%d)"), len);
|
||||
// AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("\n%s"), packet_buffer);
|
||||
// AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("\n%s"), packet_buffer);
|
||||
|
||||
// Simple Service Discovery Protocol (SSDP)
|
||||
if (Settings.flag2.emulation) {
|
||||
@ -97,11 +137,16 @@ void PollUdp(void)
|
||||
#endif
|
||||
udp_response_mutex = true;
|
||||
|
||||
#ifdef ESP8266
|
||||
udp_remote_ip = packet->srcaddr;
|
||||
udp_remote_port = packet->srcport;
|
||||
#else
|
||||
udp_remote_ip = PortUdp.remoteIP();
|
||||
udp_remote_port = PortUdp.remotePort();
|
||||
#endif
|
||||
|
||||
// AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("UDP: M-SEARCH Packet from %s:%d\n%s"),
|
||||
// udp_remote_ip.toString().c_str(), udp_remote_port, packet_buffer);
|
||||
// AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("UDP: M-SEARCH Packet from %s:%d\n%s"),
|
||||
// udp_remote_ip.toString().c_str(), udp_remote_port, packet_buffer);
|
||||
|
||||
uint32_t response_delay = UDP_MSEARCH_SEND_DELAY + ((millis() &0x7) * 100); // 1500 - 2200 msec
|
||||
|
||||
|
@ -154,6 +154,7 @@
|
||||
#define USE_HRXL // Add support for MaxBotix HRXL-MaxSonar ultrasonic range finders (+0k7)
|
||||
//#define USE_TASMOTA_SLAVE // Add support for Arduino Uno/Pro Mini via serial interface including flashing (+2k3 code, 44 mem)
|
||||
//#define USE_OPENTHERM // Add support for OpenTherm (+15k code)
|
||||
//#define USE_MCP9808 // Add support for MCP9808 temperature sensor (+0k9 code)
|
||||
|
||||
#define USE_ENERGY_SENSOR // Add energy sensors (-14k code)
|
||||
#define USE_PZEM004T // Add support for PZEM004T Energy monitor (+2k code)
|
||||
|
@ -300,7 +300,7 @@ const char kWebColors[] PROGMEM =
|
||||
|
||||
#undef USE_HM10 // Disable support for HM-10 as a BLE-bridge as an alternative is using the internal ESP32 BLE
|
||||
#undef USE_KEELOQ // Disable support for Jarolift rollers by Keeloq algorithm as it's library cc1101 is not compatible with ESP32
|
||||
#undef USE_DISPLAY_ILI9488 // Disable as it's library JaretBurkett_ILI9488-gemu-1.0 is not compatible with ESP32
|
||||
//#undef USE_DISPLAY_ILI9488 // Disable as it's library JaretBurkett_ILI9488-gemu-1.0 is not compatible with ESP32
|
||||
//#undef USE_DISPLAY_SSD1351 // Disable as it's library Adafruit_SSD1351_gemu-1.0 is not compatible with ESP32
|
||||
|
||||
#endif // ESP32
|
||||
@ -335,7 +335,7 @@ const char kWebColors[] PROGMEM =
|
||||
#ifdef USE_DEVICE_GROUPS
|
||||
#define SendDeviceGroupMessage(DEVICE_INDEX, REQUEST_TYPE, ...) _SendDeviceGroupMessage(DEVICE_INDEX, REQUEST_TYPE, __VA_ARGS__, 0)
|
||||
#define SendLocalDeviceGroupMessage(REQUEST_TYPE, ...) _SendDeviceGroupMessage(0, REQUEST_TYPE, __VA_ARGS__, 0)
|
||||
uint8_t device_group_count = 0;
|
||||
uint8_t device_group_count = 1;
|
||||
#endif // USE_DEVICE_GROUPS
|
||||
|
||||
#ifdef DEBUG_TASMOTA_CORE
|
||||
|
@ -34,8 +34,8 @@
|
||||
const uint16_t CHUNKED_BUFFER_SIZE = (MESSZ / 2) - 100; // Chunk buffer size (should be smaller than half mqtt_data size = MESSZ)
|
||||
|
||||
const uint16_t HTTP_REFRESH_TIME = 2345; // milliseconds
|
||||
#define HTTP_RESTART_RECONNECT_TIME 9000 // milliseconds
|
||||
#define HTTP_OTA_RESTART_RECONNECT_TIME 20000 // milliseconds
|
||||
const uint16_t HTTP_RESTART_RECONNECT_TIME = 9000; // milliseconds - Allow time for restart and wifi reconnect
|
||||
const uint16_t HTTP_OTA_RESTART_RECONNECT_TIME = 28000; // milliseconds - Allow time for uploading binary, unzip/write to final destination and wifi reconnect
|
||||
|
||||
#include <ESP8266WebServer.h>
|
||||
#include <DNSServer.h>
|
||||
@ -170,12 +170,8 @@ const char HTTP_SCRIPT_WIFI[] PROGMEM =
|
||||
"eb('p1').focus();"
|
||||
"}";
|
||||
|
||||
const char HTTP_SCRIPT_RELOAD[] PROGMEM =
|
||||
"setTimeout(function(){location.href='.';}," STR(HTTP_RESTART_RECONNECT_TIME) ");";
|
||||
|
||||
// Local OTA upgrade requires more time to complete cp: before web ui should be reloaded
|
||||
const char HTTP_SCRIPT_RELOAD_OTA[] PROGMEM =
|
||||
"setTimeout(function(){location.href='.';}," STR(HTTP_OTA_RESTART_RECONNECT_TIME) ");";
|
||||
const char HTTP_SCRIPT_RELOAD_TIME[] PROGMEM =
|
||||
"setTimeout(function(){location.href='.';},%d);";
|
||||
|
||||
const char HTTP_SCRIPT_CONSOL[] PROGMEM =
|
||||
"var sn=0,id=0;" // Scroll position, Get most of weblog initially
|
||||
@ -980,7 +976,7 @@ void WebRestart(uint32_t type)
|
||||
bool reset_only = (HTTP_MANAGER_RESET_ONLY == Web.state);
|
||||
|
||||
WSContentStart_P((type) ? S_SAVE_CONFIGURATION : S_RESTART, !reset_only);
|
||||
WSContentSend_P(HTTP_SCRIPT_RELOAD);
|
||||
WSContentSend_P(HTTP_SCRIPT_RELOAD_TIME, HTTP_RESTART_RECONNECT_TIME);
|
||||
WSContentSendStyle();
|
||||
if (type) {
|
||||
WSContentSend_P(PSTR("<div style='text-align:center;'><b>" D_CONFIGURATION_SAVED "</b><br>"));
|
||||
@ -2346,7 +2342,7 @@ void HandleUpgradeFirmwareStart(void)
|
||||
}
|
||||
|
||||
WSContentStart_P(S_INFORMATION);
|
||||
WSContentSend_P(HTTP_SCRIPT_RELOAD_OTA);
|
||||
WSContentSend_P(HTTP_SCRIPT_RELOAD_TIME, HTTP_OTA_RESTART_RECONNECT_TIME);
|
||||
WSContentSendStyle();
|
||||
WSContentSend_P(PSTR("<div style='text-align:center;'><b>" D_UPGRADE_STARTED " ...</b></div>"));
|
||||
WSContentSend_P(HTTP_MSG_RSTRT);
|
||||
@ -2371,7 +2367,7 @@ void HandleUploadDone(void)
|
||||
|
||||
WSContentStart_P(S_INFORMATION);
|
||||
if (!Web.upload_error) {
|
||||
WSContentSend_P(HTTP_SCRIPT_RELOAD_OTA); // Refesh main web ui after OTA upgrade
|
||||
WSContentSend_P(HTTP_SCRIPT_RELOAD_TIME, HTTP_OTA_RESTART_RECONNECT_TIME); // Refesh main web ui after OTA upgrade
|
||||
}
|
||||
WSContentSendStyle();
|
||||
WSContentSend_P(PSTR("<div style='text-align:center;'><b>" D_UPLOAD " <font color='#"));
|
||||
|
@ -1429,6 +1429,10 @@ void LightGetHSB(uint16_t *hue, uint8_t *sat, uint8_t *bri) {
|
||||
light_state.getHSB(hue, sat, bri);
|
||||
}
|
||||
|
||||
void LightGetXY(float *X, float *Y) {
|
||||
light_state.getXY(X, Y);
|
||||
}
|
||||
|
||||
void LightHsToRgb(uint16_t hue, uint8_t sat, uint8_t *r_r, uint8_t *r_g, uint8_t *r_b) {
|
||||
light_state.HsToRgb(hue, sat, r_r, r_g, r_b);
|
||||
}
|
||||
|
@ -458,6 +458,21 @@ bool RulesRuleMatch(uint8_t rule_set, String &event, String &rule)
|
||||
rule_param = String(SunMinutes(1));
|
||||
}
|
||||
#endif // USE_TIMERS and USE_SUNRISE
|
||||
// #ifdef USE_ZIGBEE
|
||||
// if (rule_param.startsWith(F("%ZBDEVICE%"))) {
|
||||
// snprintf_P(stemp, sizeof(stemp), PSTR("0x%04X"), Z_GetLastDevice());
|
||||
// rule_param = String(stemp);
|
||||
// }
|
||||
// if (rule_param.startsWith(F("%ZBGROUP%"))) {
|
||||
// rule_param = String(Z_GetLastGroup());
|
||||
// }
|
||||
// if (rule_param.startsWith(F("%ZBCLUSTER%"))) {
|
||||
// rule_param = String(Z_GetLastCluster());
|
||||
// }
|
||||
// if (rule_param.startsWith(F("%ZBENDPOINT%"))) {
|
||||
// rule_param = String(Z_GetLastEndpoint());
|
||||
// }
|
||||
// #endif
|
||||
rule_param.toUpperCase();
|
||||
strlcpy(rule_svalue, rule_param.c_str(), sizeof(rule_svalue));
|
||||
|
||||
@ -701,6 +716,13 @@ bool RuleSetProcess(uint8_t rule_set, String &event_saved)
|
||||
RulesVarReplace(commands, F("%SUNRISE%"), String(SunMinutes(0)));
|
||||
RulesVarReplace(commands, F("%SUNSET%"), String(SunMinutes(1)));
|
||||
#endif // USE_TIMERS and USE_SUNRISE
|
||||
#ifdef USE_ZIGBEE
|
||||
snprintf_P(stemp, sizeof(stemp), PSTR("0x%04X"), Z_GetLastDevice());
|
||||
RulesVarReplace(commands, F("%ZBDEVICE%"), String(stemp));
|
||||
RulesVarReplace(commands, F("%ZBGROUP%"), String(Z_GetLastGroup()));
|
||||
RulesVarReplace(commands, F("%ZBCLUSTER%"), String(Z_GetLastCluster()));
|
||||
RulesVarReplace(commands, F("%ZBENDPOINT%"), String(Z_GetLastEndpoint()));
|
||||
#endif
|
||||
|
||||
char command[commands.length() +1];
|
||||
strlcpy(command, commands.c_str(), sizeof(command));
|
||||
@ -1261,6 +1283,16 @@ bool findNextVariableValue(char * &pVarname, float &value)
|
||||
} else if (sVarName.equals(F("SUNSET"))) {
|
||||
value = SunMinutes(1);
|
||||
#endif
|
||||
// #ifdef USE_ZIGBEE
|
||||
// // } else if (sVarName.equals(F("ZBDEVICE"))) {
|
||||
// // value = Z_GetLastDevice();
|
||||
// } else if (sVarName.equals(F("ZBGROUP"))) {
|
||||
// value = Z_GetLastGroup();
|
||||
// } else if (sVarName.equals(F("ZBCLUSTER"))) {
|
||||
// value = Z_GetLastCluster();
|
||||
// } else if (sVarName.equals(F("ZBENDPOINT"))) {
|
||||
// value = Z_GetLastEndpoint();
|
||||
// #endif
|
||||
} else {
|
||||
succeed = false;
|
||||
}
|
||||
|
@ -64,7 +64,6 @@ keywords if then else endif, or, and are better readable for beginners (others m
|
||||
#define MAX_SCRIPT_SIZE MAX_RULE_SIZE*MAX_RULE_SETS
|
||||
|
||||
|
||||
|
||||
uint32_t EncodeLightId(uint8_t relay_id);
|
||||
uint32_t DecodeLightId(uint32_t hue_id);
|
||||
|
||||
@ -79,30 +78,40 @@ uint32_t DecodeLightId(uint32_t hue_id);
|
||||
#endif
|
||||
#endif // USE_SCRIPT_COMPRESSION
|
||||
|
||||
#if defined(ESP32) && defined(ESP32_SCRIPT_SIZE) && !defined(USE_24C256) && !defined(USE_SCRIPT_FATFS)
|
||||
#if (defined(LITTLEFS_SCRIPT_SIZE) && !defined(USE_24C256) && !defined(USE_SCRIPT_FATFS)) || (USE_SCRIPT_FATFS==-1)
|
||||
|
||||
#ifdef ESP32
|
||||
#include "FS.h"
|
||||
#include "SPIFFS.h"
|
||||
#else
|
||||
#include <LittleFS.h>
|
||||
#endif
|
||||
|
||||
FS *fsp;
|
||||
|
||||
void SaveFile(const char *name,const uint8_t *buf,uint32_t len) {
|
||||
File file = SPIFFS.open(name, FILE_WRITE);
|
||||
File file = fsp->open(name, "w");
|
||||
if (!file) return;
|
||||
file.write(buf, len);
|
||||
file.close();
|
||||
}
|
||||
|
||||
#define FORMAT_SPIFFS_IF_FAILED true
|
||||
uint8_t spiffs_mounted=0;
|
||||
uint8_t fs_mounted=0;
|
||||
|
||||
void LoadFile(const char *name,uint8_t *buf,uint32_t len) {
|
||||
if (!spiffs_mounted) {
|
||||
if (!fs_mounted) {
|
||||
#ifdef ESP32
|
||||
if(!SPIFFS.begin(FORMAT_SPIFFS_IF_FAILED)){
|
||||
#else
|
||||
if(!fsp->begin()){
|
||||
#endif
|
||||
//Serial.println("SPIFFS Mount Failed");
|
||||
return;
|
||||
}
|
||||
spiffs_mounted=1;
|
||||
fs_mounted=1;
|
||||
}
|
||||
File file = SPIFFS.open(name);
|
||||
File file = fsp->open(name, "r");
|
||||
if (!file) return;
|
||||
file.read(buf, len);
|
||||
file.close();
|
||||
@ -116,29 +125,43 @@ enum {OPER_EQU=1,OPER_PLS,OPER_MIN,OPER_MUL,OPER_DIV,OPER_PLSEQU,OPER_MINEQU,OPE
|
||||
enum {SCRIPT_LOGLEVEL=1,SCRIPT_TELEPERIOD};
|
||||
|
||||
#ifdef USE_SCRIPT_FATFS
|
||||
|
||||
#if USE_SCRIPT_FATFS>=0
|
||||
#include <SPI.h>
|
||||
|
||||
//#define USE_MMC
|
||||
|
||||
#ifdef USE_MMC
|
||||
#include <SD_MMC.h>
|
||||
#undef FS_USED
|
||||
#define FS_USED SD_MMC
|
||||
#else
|
||||
#include <SD.h>
|
||||
#undef FS_USED
|
||||
#define FS_USED SD
|
||||
#ifdef ESP32
|
||||
FS *fsp;
|
||||
#else
|
||||
SDClass *fsp;
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifndef ESP32
|
||||
// esp8266
|
||||
|
||||
#if USE_SCRIPT_FATFS>=0
|
||||
// old fs
|
||||
#undef FILE_WRITE
|
||||
#define FILE_WRITE (sdfat::O_READ | sdfat::O_WRITE | sdfat::O_CREAT)
|
||||
#define FILE_APPEND (sdfat::O_READ | sdfat::O_WRITE | sdfat::O_CREAT | sdfat::O_APPEND)
|
||||
|
||||
#else
|
||||
// new fs
|
||||
#undef FILE_WRITE
|
||||
#define FILE_WRITE "w"
|
||||
#undef FILE_READ
|
||||
#define FILE_READ "r"
|
||||
#undef FILE_APPEND
|
||||
#define FILE_APPEND "a"
|
||||
#endif
|
||||
|
||||
#endif // USE_SCRIPT_FATFS>=0
|
||||
|
||||
|
||||
#ifndef FAT_SCRIPT_SIZE
|
||||
#define FAT_SCRIPT_SIZE 4096
|
||||
#endif
|
||||
|
||||
#ifdef ESP32
|
||||
#undef FAT_SCRIPT_NAME
|
||||
#define FAT_SCRIPT_NAME "/script.txt"
|
||||
@ -150,7 +173,8 @@ enum {SCRIPT_LOGLEVEL=1,SCRIPT_TELEPERIOD};
|
||||
#if USE_STANDARD_SPI_LIBRARY==0
|
||||
#warning ("FATFS standard spi should be used");
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#endif // USE_SCRIPT_FATFS
|
||||
|
||||
#ifdef SUPPORT_MQTT_EVENT
|
||||
#include <LinkedList.h> // Import LinkedList library
|
||||
@ -624,10 +648,18 @@ char *script;
|
||||
|
||||
#ifdef USE_SCRIPT_FATFS
|
||||
if (!glob_script_mem.script_sd_found) {
|
||||
|
||||
#if USE_SCRIPT_FATFS>=0
|
||||
fsp=&SD;
|
||||
|
||||
#ifdef USE_MMC
|
||||
if (FS_USED.begin()) {
|
||||
if (fsp->begin()) {
|
||||
#else
|
||||
if (FS_USED.begin(USE_SCRIPT_FATFS)) {
|
||||
if (SD.begin(USE_SCRIPT_FATFS)) {
|
||||
#endif
|
||||
|
||||
#else
|
||||
if (fsp->begin()) {
|
||||
#endif
|
||||
glob_script_mem.script_sd_found=1;
|
||||
} else {
|
||||
@ -1276,6 +1308,7 @@ chknext:
|
||||
#endif //USE_ENERGY_SENSOR
|
||||
break;
|
||||
case 'f':
|
||||
//#define DEBUG_FS
|
||||
#ifdef USE_SCRIPT_FATFS
|
||||
if (!strncmp(vname,"fo(",3)) {
|
||||
lp+=3;
|
||||
@ -1304,7 +1337,10 @@ chknext:
|
||||
for (uint8_t cnt=0;cnt<SFS_MAX;cnt++) {
|
||||
if (!glob_script_mem.file_flags[cnt].is_open) {
|
||||
if (mode==0) {
|
||||
glob_script_mem.files[cnt]=FS_USED.open(str,FILE_READ);
|
||||
#ifdef DEBUG_FS
|
||||
AddLog_P2(LOG_LEVEL_INFO,PSTR("open file for read %d"),cnt);
|
||||
#endif
|
||||
glob_script_mem.files[cnt]=fsp->open(str,FILE_READ);
|
||||
if (glob_script_mem.files[cnt].isDirectory()) {
|
||||
glob_script_mem.files[cnt].rewindDirectory();
|
||||
glob_script_mem.file_flags[cnt].is_dir=1;
|
||||
@ -1314,9 +1350,15 @@ chknext:
|
||||
}
|
||||
else {
|
||||
if (mode==1) {
|
||||
glob_script_mem.files[cnt]=FS_USED.open(str,FILE_WRITE);
|
||||
glob_script_mem.files[cnt]=fsp->open(str,FILE_WRITE);
|
||||
#ifdef DEBUG_FS
|
||||
AddLog_P2(LOG_LEVEL_INFO,PSTR("open file for write %d"),cnt);
|
||||
#endif
|
||||
} else {
|
||||
glob_script_mem.files[cnt]=FS_USED.open(str,FILE_APPEND);
|
||||
glob_script_mem.files[cnt]=fsp->open(str,FILE_APPEND);
|
||||
#ifdef DEBUG_FS
|
||||
AddLog_P2(LOG_LEVEL_INFO,PSTR("open file for append %d"),cnt);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
if (glob_script_mem.files[cnt]) {
|
||||
@ -1335,10 +1377,15 @@ chknext:
|
||||
if (!strncmp(vname,"fc(",3)) {
|
||||
lp+=3;
|
||||
lp=GetNumericResult(lp,OPER_EQU,&fvar,0);
|
||||
uint8_t ind=fvar;
|
||||
if (ind>=SFS_MAX) ind=SFS_MAX-1;
|
||||
glob_script_mem.files[ind].close();
|
||||
glob_script_mem.file_flags[ind].is_open=0;
|
||||
if (fvar>=0) {
|
||||
uint8_t ind=fvar;
|
||||
if (ind>=SFS_MAX) ind=SFS_MAX-1;
|
||||
#ifdef DEBUG_FS
|
||||
AddLog_P2(LOG_LEVEL_INFO,PSTR("closing file %d"),ind);
|
||||
#endif
|
||||
glob_script_mem.files[ind].close();
|
||||
glob_script_mem.file_flags[ind].is_open=0;
|
||||
}
|
||||
fvar=0;
|
||||
lp++;
|
||||
len=0;
|
||||
@ -1450,7 +1497,7 @@ chknext:
|
||||
lp+=3;
|
||||
char str[glob_script_mem.max_ssize+1];
|
||||
lp=GetStringResult(lp,OPER_EQU,str,0);
|
||||
FS_USED.remove(str);
|
||||
fsp->remove(str);
|
||||
lp++;
|
||||
len=0;
|
||||
goto exit;
|
||||
@ -1490,7 +1537,7 @@ chknext:
|
||||
char str[glob_script_mem.max_ssize+1];
|
||||
lp=GetStringResult(lp,OPER_EQU,str,0);
|
||||
// execute script
|
||||
File ef=FS_USED.open(str);
|
||||
File ef=fsp->open(str,FILE_READ);
|
||||
if (ef) {
|
||||
uint16_t fsiz=ef.size();
|
||||
if (fsiz<2048) {
|
||||
@ -1512,7 +1559,7 @@ chknext:
|
||||
lp+=4;
|
||||
char str[glob_script_mem.max_ssize+1];
|
||||
lp=GetStringResult(lp,OPER_EQU,str,0);
|
||||
fvar=FS_USED.mkdir(str);
|
||||
fvar=fsp->mkdir(str);
|
||||
lp++;
|
||||
len=0;
|
||||
goto exit;
|
||||
@ -1521,7 +1568,7 @@ chknext:
|
||||
lp+=4;
|
||||
char str[glob_script_mem.max_ssize+1];
|
||||
lp=GetStringResult(lp,OPER_EQU,str,0);
|
||||
fvar=FS_USED.rmdir(str);
|
||||
fvar=fsp->rmdir(str);
|
||||
lp++;
|
||||
len=0;
|
||||
goto exit;
|
||||
@ -1530,7 +1577,7 @@ chknext:
|
||||
lp+=3;
|
||||
char str[glob_script_mem.max_ssize+1];
|
||||
lp=GetStringResult(lp,OPER_EQU,str,0);
|
||||
if (FS_USED.exists(str)) fvar=1;
|
||||
if (fsp->exists(str)) fvar=1;
|
||||
else fvar=0;
|
||||
lp++;
|
||||
len=0;
|
||||
@ -2951,7 +2998,6 @@ int16_t Run_Scripter(const char *type, int8_t tlen, char *js) {
|
||||
toLogEOL("for error",lp);
|
||||
}
|
||||
} else if (!strncmp(lp,"next",4)) {
|
||||
lp+=4;
|
||||
lp_next=lp;
|
||||
if (floop>0) {
|
||||
// for next loop
|
||||
@ -3700,7 +3746,7 @@ void ListDir(char *path, uint8_t depth) {
|
||||
char format[12];
|
||||
sprintf(format,"%%-%ds",24-depth);
|
||||
|
||||
File dir=FS_USED.open(path);
|
||||
File dir=fsp->open(path, FILE_READ);
|
||||
if (dir) {
|
||||
dir.rewindDirectory();
|
||||
if (strlen(path)>1) {
|
||||
@ -3822,8 +3868,8 @@ void script_upload(void) {
|
||||
if (upload.status == UPLOAD_FILE_START) {
|
||||
char npath[48];
|
||||
sprintf(npath,"%s/%s",path,upload.filename.c_str());
|
||||
FS_USED.remove(npath);
|
||||
upload_file=FS_USED.open(npath,FILE_WRITE);
|
||||
fsp->remove(npath);
|
||||
upload_file=fsp->open(npath,FILE_WRITE);
|
||||
if (!upload_file) Web.upload_error=1;
|
||||
} else if(upload.status == UPLOAD_FILE_WRITE) {
|
||||
if (upload_file) upload_file.write(upload.buf,upload.currentSize);
|
||||
@ -3842,12 +3888,12 @@ uint8_t DownloadFile(char *file) {
|
||||
File download_file;
|
||||
WiFiClient download_Client;
|
||||
|
||||
if (!FS_USED.exists(file)) {
|
||||
if (!fsp->exists(file)) {
|
||||
AddLog_P(LOG_LEVEL_INFO,PSTR("file not found"));
|
||||
return 0;
|
||||
}
|
||||
|
||||
download_file=FS_USED.open(file,FILE_READ);
|
||||
download_file=fsp->open(file,FILE_READ);
|
||||
if (!download_file) {
|
||||
AddLog_P(LOG_LEVEL_INFO,PSTR("could not open file"));
|
||||
return 0;
|
||||
@ -4027,16 +4073,16 @@ void ScriptSaveSettings(void) {
|
||||
|
||||
#if !defined(USE_24C256) && defined(USE_SCRIPT_FATFS)
|
||||
if (glob_script_mem.flags&1) {
|
||||
FS_USED.remove(FAT_SCRIPT_NAME);
|
||||
File file=FS_USED.open(FAT_SCRIPT_NAME,FILE_WRITE);
|
||||
fsp->remove(FAT_SCRIPT_NAME);
|
||||
File file=fsp->open(FAT_SCRIPT_NAME,FILE_WRITE);
|
||||
file.write((const uint8_t*)glob_script_mem.script_ram,FAT_SCRIPT_SIZE);
|
||||
file.close();
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(ESP32) && defined(ESP32_SCRIPT_SIZE) && !defined(USE_24C256) && !defined(USE_SCRIPT_FATFS)
|
||||
#if defined(LITTLEFS_SCRIPT_SIZE) && !defined(USE_24C256) && !defined(USE_SCRIPT_FATFS)
|
||||
if (glob_script_mem.flags&1) {
|
||||
SaveFile("/script.txt",(uint8_t*)glob_script_mem.script_ram,ESP32_SCRIPT_SIZE);
|
||||
SaveFile("/script.txt",(uint8_t*)glob_script_mem.script_ram,LITTLEFS_SCRIPT_SIZE);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
@ -4051,7 +4097,7 @@ void ScriptSaveSettings(void) {
|
||||
#ifdef USE_SCRIPT_COMPRESSION
|
||||
#ifndef USE_24C256
|
||||
#ifndef USE_SCRIPT_FATFS
|
||||
#ifndef ESP32_SCRIPT_SIZE
|
||||
#ifndef LITTLEFS_SCRIPT_SIZE
|
||||
|
||||
//AddLog_P2(LOG_LEVEL_INFO,PSTR("in string: %s len = %d"),glob_script_mem.script_ram,strlen(glob_script_mem.script_ram));
|
||||
uint32_t len_compressed = SCRIPT_COMPRESS(glob_script_mem.script_ram, strlen(glob_script_mem.script_ram), Settings.rules[0], MAX_SCRIPT_SIZE-1);
|
||||
@ -4300,8 +4346,7 @@ void Script_Check_Hue(String *response) {
|
||||
uint8_t hue_script_found=Run_Scripter(">H",-2,0);
|
||||
if (hue_script_found!=99) return;
|
||||
|
||||
char line[128];
|
||||
char tmp[128];
|
||||
char tmp[256];
|
||||
uint8_t hue_devs=0;
|
||||
uint8_t vindex=0;
|
||||
char *cp;
|
||||
@ -4316,17 +4361,7 @@ void Script_Check_Hue(String *response) {
|
||||
}
|
||||
if (*lp!=';') {
|
||||
// check this line
|
||||
memcpy(line,lp,sizeof(line));
|
||||
line[sizeof(line)-1]=0;
|
||||
cp=line;
|
||||
for (uint32_t i=0; i<sizeof(line); i++) {
|
||||
if (!*cp || *cp=='\n' || *cp=='\r') {
|
||||
*cp=0;
|
||||
break;
|
||||
}
|
||||
cp++;
|
||||
}
|
||||
Replace_Cmd_Vars(line,0,tmp,sizeof(tmp));
|
||||
Replace_Cmd_Vars(lp,1,tmp,sizeof(tmp));
|
||||
// check for hue defintions
|
||||
// NAME, TYPE , vars
|
||||
cp=tmp;
|
||||
@ -5518,8 +5553,7 @@ nextwebline:
|
||||
void script_send_email_body(void(*func)(char *)) {
|
||||
uint8_t msect=Run_Scripter(">m",-2,0);
|
||||
if (msect==99) {
|
||||
char line[128];
|
||||
char tmp[128];
|
||||
char tmp[256];
|
||||
char *lp=glob_script_mem.section_ptr+2;
|
||||
while (lp) {
|
||||
while (*lp==SCRIPT_EOL) {
|
||||
@ -5530,17 +5564,7 @@ uint8_t msect=Run_Scripter(">m",-2,0);
|
||||
}
|
||||
if (*lp!=';') {
|
||||
// send this line to smtp
|
||||
memcpy(line,lp,sizeof(line));
|
||||
line[sizeof(line)-1]=0;
|
||||
char *cp=line;
|
||||
for (uint32_t i=0; i<sizeof(line); i++) {
|
||||
if (!*cp || *cp=='\n' || *cp=='\r') {
|
||||
*cp=0;
|
||||
break;
|
||||
}
|
||||
cp++;
|
||||
}
|
||||
Replace_Cmd_Vars(line,0,tmp,sizeof(tmp));
|
||||
Replace_Cmd_Vars(lp,1,tmp,sizeof(tmp));
|
||||
//client->println(tmp);
|
||||
func(tmp);
|
||||
}
|
||||
@ -5563,8 +5587,7 @@ uint8_t msect=Run_Scripter(">m",-2,0);
|
||||
void ScriptJsonAppend(void) {
|
||||
uint8_t web_script=Run_Scripter(">J",-2,0);
|
||||
if (web_script==99) {
|
||||
char line[128];
|
||||
char tmp[128];
|
||||
char tmp[256];
|
||||
char *lp=glob_script_mem.section_ptr+2;
|
||||
while (lp) {
|
||||
while (*lp==SCRIPT_EOL) {
|
||||
@ -5575,17 +5598,7 @@ void ScriptJsonAppend(void) {
|
||||
}
|
||||
if (*lp!=';') {
|
||||
// send this line to mqtt
|
||||
memcpy(line,lp,sizeof(line));
|
||||
line[sizeof(line)-1]=0;
|
||||
char *cp=line;
|
||||
for (uint32_t i=0; i<sizeof(line); i++) {
|
||||
if (!*cp || *cp=='\n' || *cp=='\r') {
|
||||
*cp=0;
|
||||
break;
|
||||
}
|
||||
cp++;
|
||||
}
|
||||
Replace_Cmd_Vars(line,0,tmp,sizeof(tmp));
|
||||
Replace_Cmd_Vars(lp,1,tmp,sizeof(tmp));
|
||||
ResponseAppend_P(PSTR("%s"),tmp);
|
||||
}
|
||||
if (*lp==SCRIPT_EOL) {
|
||||
@ -5607,6 +5620,67 @@ bool RulesProcessEvent(char *json_event) {
|
||||
|
||||
#ifdef ESP32
|
||||
#ifdef USE_SCRIPT_TASK
|
||||
|
||||
#ifndef STASK_STACK
|
||||
#define STASK_STACK 8192
|
||||
#endif
|
||||
|
||||
#ifndef STASK_PRIO
|
||||
#define STASK_PRIO 1
|
||||
#endif
|
||||
|
||||
#if 1
|
||||
|
||||
struct ESP32_Task {
|
||||
uint16_t task_timer;
|
||||
TaskHandle_t task_t;
|
||||
} esp32_tasks[2];
|
||||
|
||||
|
||||
void script_task1(void *arg) {
|
||||
//uint32_t lastms=millis();
|
||||
//uint32_t time;
|
||||
while (1) {
|
||||
//time=millis()-lastms;
|
||||
//lastms=millis();
|
||||
//time=esp32_tasks[0].task_timer-time;
|
||||
//if (time<esp32_tasks[1].task_timer) {delay(time); }
|
||||
//if (time<=esp32_tasks[0].task_timer) {vTaskDelay( pdMS_TO_TICKS( time ) ); }
|
||||
delay(esp32_tasks[0].task_timer);
|
||||
Run_Scripter(">t1",3,0);
|
||||
}
|
||||
}
|
||||
|
||||
void script_task2(void *arg) {
|
||||
//uint32_t lastms=millis();
|
||||
//uint32_t time;
|
||||
while (1) {
|
||||
//time=millis()-lastms;
|
||||
//lastms=millis();
|
||||
//time=esp32_tasks[1].task_timer-time;
|
||||
//if (time<esp32_tasks[1].task_timer) {delay(time); }
|
||||
//if (time<=esp32_tasks[1].task_timer) {vTaskDelay( pdMS_TO_TICKS( time ) ); }
|
||||
delay(esp32_tasks[1].task_timer);
|
||||
Run_Scripter(">t2",3,0);
|
||||
}
|
||||
}
|
||||
uint32_t scripter_create_task(uint32_t num, uint32_t time, uint32_t core) {
|
||||
//return 0;
|
||||
BaseType_t res = 0;
|
||||
if (core > 1) { core = 1; }
|
||||
if (num == 1) {
|
||||
if (esp32_tasks[0].task_t) { vTaskDelete(esp32_tasks[0].task_t); }
|
||||
res = xTaskCreatePinnedToCore(script_task1, "T1", STASK_STACK, NULL, STASK_PRIO, &esp32_tasks[0].task_t, core);
|
||||
esp32_tasks[0].task_timer = time;
|
||||
} else {
|
||||
if (esp32_tasks[1].task_t) { vTaskDelete(esp32_tasks[1].task_t); }
|
||||
res = xTaskCreatePinnedToCore(script_task2, "T2", STASK_STACK, NULL, STASK_PRIO, &esp32_tasks[1].task_t, core);
|
||||
esp32_tasks[1].task_timer = time;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
#else
|
||||
|
||||
uint16_t task_timer1;
|
||||
uint16_t task_timer2;
|
||||
TaskHandle_t task_t1;
|
||||
@ -5625,13 +5699,6 @@ void script_task2(void *arg) {
|
||||
Run_Scripter(">t2",3,0);
|
||||
}
|
||||
}
|
||||
#ifndef STASK_STACK
|
||||
#define STASK_STACK 4096
|
||||
#endif
|
||||
|
||||
#ifndef STASK_PRIO
|
||||
#define STASK_PRIO 5
|
||||
#endif
|
||||
|
||||
uint32_t scripter_create_task(uint32_t num, uint32_t time, uint32_t core) {
|
||||
//return 0;
|
||||
@ -5648,6 +5715,8 @@ uint32_t scripter_create_task(uint32_t num, uint32_t time, uint32_t core) {
|
||||
}
|
||||
return res;
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif // USE_SCRIPT_TASK
|
||||
#endif // ESP32
|
||||
/*********************************************************************************************\
|
||||
@ -5673,7 +5742,7 @@ bool Xdrv10(uint8_t function)
|
||||
#ifdef USE_SCRIPT_COMPRESSION
|
||||
#ifndef USE_24C256
|
||||
#ifndef USE_SCRIPT_FATFS
|
||||
#ifndef ESP32_SCRIPT_SIZE
|
||||
#ifndef LITTLEFS_SCRIPT_SIZE
|
||||
int32_t len_decompressed;
|
||||
sprt=(char*)calloc(UNISHOXRSIZE+8,1);
|
||||
if (!sprt) { break; }
|
||||
@ -5719,10 +5788,14 @@ bool Xdrv10(uint8_t function)
|
||||
#endif
|
||||
#endif
|
||||
|
||||
|
||||
#ifdef USE_SCRIPT_FATFS
|
||||
|
||||
#if USE_SCRIPT_FATFS>=0
|
||||
fsp = &SD;
|
||||
|
||||
#ifdef USE_MMC
|
||||
if (FS_USED.begin()) {
|
||||
if (fsp->begin()) {
|
||||
#else
|
||||
|
||||
#ifdef ESP32
|
||||
@ -5730,10 +5803,15 @@ bool Xdrv10(uint8_t function)
|
||||
SPI.begin(Pin(GPIO_SPI_CLK),Pin(GPIO_SPI_MISO),Pin(GPIO_SPI_MOSI), -1);
|
||||
}
|
||||
#endif
|
||||
if (FS_USED.begin(USE_SCRIPT_FATFS)) {
|
||||
if (SD.begin(USE_SCRIPT_FATFS)) {
|
||||
#endif
|
||||
|
||||
//FS_USED.dateTimeCallback(dateTime);
|
||||
#else
|
||||
fsp = &LittleFS;
|
||||
if (fsp->begin()) {
|
||||
#endif
|
||||
|
||||
//fsp->dateTimeCallback(dateTime);
|
||||
|
||||
glob_script_mem.script_sd_found=1;
|
||||
char *script;
|
||||
@ -5741,8 +5819,8 @@ bool Xdrv10(uint8_t function)
|
||||
if (!script) break;
|
||||
glob_script_mem.script_ram=script;
|
||||
glob_script_mem.script_size=FAT_SCRIPT_SIZE;
|
||||
if (FS_USED.exists(FAT_SCRIPT_NAME)) {
|
||||
File file=FS_USED.open(FAT_SCRIPT_NAME,FILE_READ);
|
||||
if (fsp->exists(FAT_SCRIPT_NAME)) {
|
||||
File file=fsp->open(FAT_SCRIPT_NAME,FILE_READ);
|
||||
file.read((uint8_t*)script,FAT_SCRIPT_SIZE);
|
||||
file.close();
|
||||
}
|
||||
@ -5759,15 +5837,21 @@ bool Xdrv10(uint8_t function)
|
||||
#endif
|
||||
|
||||
|
||||
#if defined(ESP32) && defined(ESP32_SCRIPT_SIZE) && !defined(USE_24C256) && !defined(USE_SCRIPT_FATFS)
|
||||
#if defined(LITTLEFS_SCRIPT_SIZE) && !defined(USE_24C256) && !defined(USE_SCRIPT_FATFS)
|
||||
|
||||
#ifdef ESP32
|
||||
fsp = &SPIFFS;
|
||||
#else
|
||||
fsp = &LittleFS;
|
||||
#endif
|
||||
char *script;
|
||||
script=(char*)calloc(ESP32_SCRIPT_SIZE+4,1);
|
||||
script=(char*)calloc(LITTLEFS_SCRIPT_SIZE+4,1);
|
||||
if (!script) break;
|
||||
LoadFile("/script.txt",(uint8_t*)script,ESP32_SCRIPT_SIZE);
|
||||
LoadFile("/script.txt",(uint8_t*)script,LITTLEFS_SCRIPT_SIZE);
|
||||
|
||||
glob_script_mem.script_ram=script;
|
||||
glob_script_mem.script_size=ESP32_SCRIPT_SIZE;
|
||||
script[ESP32_SCRIPT_SIZE-1]=0;
|
||||
glob_script_mem.script_size=LITTLEFS_SCRIPT_SIZE;
|
||||
script[LITTLEFS_SCRIPT_SIZE-1]=0;
|
||||
// use rules storage for permanent vars
|
||||
glob_script_mem.script_pram=(uint8_t*)Settings.rules[0];
|
||||
glob_script_mem.script_pram_size=MAX_SCRIPT_SIZE;
|
||||
|
@ -505,7 +505,7 @@ void DisplayText(void)
|
||||
cp += var;
|
||||
linebuf[fill] = 0;
|
||||
break;
|
||||
#if defined(USE_SCRIPT_FATFS) && defined(USE_SCRIPT)
|
||||
#if defined(USE_SCRIPT_FATFS) && defined(USE_SCRIPT) && USE_SCRIPT_FATFS>=0
|
||||
case 'P':
|
||||
{ char *ep=strchr(cp,':');
|
||||
if (ep) {
|
||||
@ -1510,8 +1510,13 @@ void rgb888_to_565(uint8_t *in, uint16_t *out, uint32_t len);
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if defined(USE_SCRIPT_FATFS) && defined(USE_SCRIPT) && USE_SCRIPT_FATFS>=0
|
||||
|
||||
#if defined(USE_SCRIPT_FATFS) && defined(USE_SCRIPT)
|
||||
#ifdef ESP32
|
||||
extern FS *fsp;
|
||||
#else
|
||||
extern SDClass *fsp;
|
||||
#endif
|
||||
#define XBUFF_LEN 128
|
||||
void Draw_RGB_Bitmap(char *file,uint16_t xp, uint16_t yp) {
|
||||
if (!renderer) return;
|
||||
@ -1527,7 +1532,7 @@ void Draw_RGB_Bitmap(char *file,uint16_t xp, uint16_t yp) {
|
||||
|
||||
if (!strcmp(estr,"rgb")) {
|
||||
// special rgb format
|
||||
fp=SD.open(file,FILE_READ);
|
||||
fp=fsp->open(file,FILE_READ);
|
||||
if (!fp) return;
|
||||
uint16_t xsize;
|
||||
fp.read((uint8_t*)&xsize,2);
|
||||
@ -1564,7 +1569,7 @@ void Draw_RGB_Bitmap(char *file,uint16_t xp, uint16_t yp) {
|
||||
#ifdef ESP32
|
||||
#ifdef JPEG_PICTS
|
||||
if (psramFound()) {
|
||||
fp=SD.open(file,FILE_READ);
|
||||
fp=fsp->open(file,FILE_READ);
|
||||
if (!fp) return;
|
||||
uint32_t size = fp.size();
|
||||
uint8_t *mem = (uint8_t *)heap_caps_malloc(size+4, MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT);
|
||||
@ -1850,7 +1855,9 @@ void DisplayCheckGraph() {
|
||||
|
||||
|
||||
#if defined(USE_SCRIPT_FATFS) && defined(USE_SCRIPT)
|
||||
#ifdef ESP32
|
||||
#include <SD.h>
|
||||
#endif
|
||||
|
||||
void Save_graph(uint8_t num, char *path) {
|
||||
if (!renderer) return;
|
||||
@ -1858,8 +1865,8 @@ void Save_graph(uint8_t num, char *path) {
|
||||
struct GRAPH *gp=graph[index];
|
||||
if (!gp) return;
|
||||
File fp;
|
||||
SD.remove(path);
|
||||
fp=SD.open(path,FILE_WRITE);
|
||||
fsp->remove(path);
|
||||
fp=fsp->open(path,FILE_WRITE);
|
||||
if (!fp) return;
|
||||
char str[32];
|
||||
sprintf_P(str,PSTR("%d\t%d\t%d\t"),gp->xcnt,gp->xs,gp->ys);
|
||||
@ -1884,7 +1891,7 @@ void Restore_graph(uint8_t num, char *path) {
|
||||
struct GRAPH *gp=graph[index];
|
||||
if (!gp) return;
|
||||
File fp;
|
||||
fp=SD.open(path,FILE_READ);
|
||||
fp=fsp->open(path,FILE_READ);
|
||||
if (!fp) return;
|
||||
char vbuff[32];
|
||||
char *cp=vbuff;
|
||||
|
@ -384,7 +384,6 @@ enum ZCL_Global_Commands {
|
||||
ZCL_DEFAULT_RESPONSE = 0x0b,
|
||||
ZCL_DISCOVER_ATTRIBUTES = 0x0c,
|
||||
ZCL_DISCOVER_ATTRIBUTES_RESPONSE = 0x0d
|
||||
|
||||
};
|
||||
|
||||
#define ZF(s) static const char ZS_ ## s[] PROGMEM = #s;
|
||||
|
@ -26,6 +26,24 @@
|
||||
#endif
|
||||
const uint16_t kZigbeeSaveDelaySeconds = ZIGBEE_SAVE_DELAY_SECONDS; // wait for x seconds
|
||||
|
||||
/*********************************************************************************************\
|
||||
* Structures for Rules variables related to the last received message
|
||||
\*********************************************************************************************/
|
||||
|
||||
typedef struct Z_LastMessageVars {
|
||||
uint16_t device; // device short address
|
||||
uint16_t groupaddr; // group address
|
||||
uint16_t cluster; // cluster id
|
||||
uint8_t endpoint; // source endpoint
|
||||
} Z_LastMessageVars;
|
||||
|
||||
Z_LastMessageVars gZbLastMessage;
|
||||
|
||||
uint16_t Z_GetLastDevice(void) { return gZbLastMessage.device; }
|
||||
uint16_t Z_GetLastGroup(void) { return gZbLastMessage.groupaddr; }
|
||||
uint16_t Z_GetLastCluster(void) { return gZbLastMessage.cluster; }
|
||||
uint8_t Z_GetLastEndpoint(void) { return gZbLastMessage.endpoint; }
|
||||
|
||||
/*********************************************************************************************\
|
||||
* Structures for device configuration
|
||||
\*********************************************************************************************/
|
||||
@ -256,7 +274,7 @@ int32_t Z_Devices::findEndpointInVector(const std::vector<T> & vecOfElements, u
|
||||
// entry with same shortaddr or longaddr exists.
|
||||
//
|
||||
Z_Device & Z_Devices::createDeviceEntry(uint16_t shortaddr, uint64_t longaddr) {
|
||||
if (!shortaddr && !longaddr) { return *(Z_Device*) nullptr; } // it is not legal to create an enrty with both short/long addr null
|
||||
if ((BAD_SHORTADDR == shortaddr) && !longaddr) { return *(Z_Device*) nullptr; } // it is not legal to create this entry
|
||||
//Z_Device* device_alloc = (Z_Device*) malloc(sizeof(Z_Device));
|
||||
Z_Device* device_alloc = new Z_Device{
|
||||
longaddr,
|
||||
@ -340,7 +358,7 @@ int32_t Z_Devices::findFriendlyName(const char * name) const {
|
||||
if (name_len) {
|
||||
for (auto &elem : _devices) {
|
||||
if (elem->friendlyName) {
|
||||
if (strcmp(elem->friendlyName, name) == 0) { return found; }
|
||||
if (strcasecmp(elem->friendlyName, name) == 0) { return found; }
|
||||
}
|
||||
found++;
|
||||
}
|
||||
@ -508,6 +526,8 @@ void Z_Devices::addEndpoint(uint16_t shortaddr, uint8_t endpoint) {
|
||||
|
||||
// Find the first endpoint of the device
|
||||
uint8_t Z_Devices::findFirstEndpoint(uint16_t shortaddr) const {
|
||||
// When in router of end-device mode, the coordinator was not probed, in this case always talk to endpoint 1
|
||||
if (0x0000 == shortaddr) { return 1; }
|
||||
int32_t found = findShortAddr(shortaddr);
|
||||
if (found < 0) return 0; // avoid creating an entry if the device was never seen
|
||||
const Z_Device &device = devicesAt(found);
|
||||
@ -860,21 +880,21 @@ const JsonObject *Z_Devices::jsonGet(uint16_t shortaddr) {
|
||||
void Z_Devices::jsonPublishFlush(uint16_t shortaddr) {
|
||||
Z_Device & device = getShortAddr(shortaddr);
|
||||
if (&device == nullptr) { return; } // don't crash if not found
|
||||
JsonObject * json = device.json;
|
||||
if (json == nullptr) { return; } // abort if nothing in buffer
|
||||
JsonObject & json = *device.json;
|
||||
if (&json == nullptr) { return; } // abort if nothing in buffer
|
||||
|
||||
const char * fname = zigbee_devices.getFriendlyName(shortaddr);
|
||||
bool use_fname = (Settings.flag4.zigbee_use_names) && (fname); // should we replace shortaddr with friendlyname?
|
||||
|
||||
// Remove redundant "Name" or "Device"
|
||||
if (use_fname) {
|
||||
json->remove(F(D_JSON_ZIGBEE_NAME));
|
||||
} else {
|
||||
json->remove(F(D_JSON_ZIGBEE_DEVICE));
|
||||
}
|
||||
// save parameters is global variables to be used by Rules
|
||||
gZbLastMessage.device = shortaddr; // %zbdevice%
|
||||
gZbLastMessage.groupaddr = json[F(D_CMND_ZIGBEE_GROUP)]; // %zbgroup%
|
||||
gZbLastMessage.cluster = json[F(D_CMND_ZIGBEE_CLUSTER)]; // %zbcluster%
|
||||
gZbLastMessage.endpoint = json[F(D_CMND_ZIGBEE_ENDPOINT)]; // %zbendpoint%
|
||||
|
||||
// dump json in string
|
||||
String msg = "";
|
||||
json->printTo(msg);
|
||||
json.printTo(msg);
|
||||
zigbee_devices.jsonClear(shortaddr);
|
||||
|
||||
if (use_fname) {
|
||||
@ -889,7 +909,7 @@ void Z_Devices::jsonPublishFlush(uint16_t shortaddr) {
|
||||
} else {
|
||||
MqttPublishPrefixTopic_P(TELE, PSTR(D_RSLT_SENSOR), Settings.flag.mqtt_sensor_retain);
|
||||
}
|
||||
XdrvRulesProcess();
|
||||
XdrvRulesProcess(); // apply rules
|
||||
}
|
||||
|
||||
void Z_Devices::jsonPublishNow(uint16_t shortaddr, JsonObject & values) {
|
||||
@ -923,7 +943,7 @@ uint16_t Z_Devices::parseDeviceParam(const char * param, bool short_must_be_know
|
||||
if ((XdrvMailbox.payload > 0) && (XdrvMailbox.payload <= 99)) {
|
||||
shortaddr = zigbee_devices.isKnownIndex(XdrvMailbox.payload - 1);
|
||||
}
|
||||
} else if ((dataBuf[0] == '0') && (dataBuf[1] == 'x')) {
|
||||
} else if ((dataBuf[0] == '0') && ((dataBuf[1] == 'x') || (dataBuf[1] == 'X'))) {
|
||||
// starts with 0x
|
||||
if (strlen(dataBuf) < 18) {
|
||||
// expect a short address
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -106,10 +106,11 @@ enum Zigbee_StateMachine_Instruction_Set {
|
||||
// Labels used in the State Machine -- internal only
|
||||
const uint8_t ZIGBEE_LABEL_INIT_COORD = 10; // Start ZNP as coordinator
|
||||
const uint8_t ZIGBEE_LABEL_START_COORD = 11; // Start ZNP as coordinator
|
||||
const uint8_t ZIGBEE_LABEL_INIT_ROUTER = 12; // Start ZNP as router
|
||||
const uint8_t ZIGBEE_LABEL_INIT_ROUTER = 12; // Init ZNP as router
|
||||
const uint8_t ZIGBEE_LABEL_START_ROUTER = 13; // Start ZNP as router
|
||||
const uint8_t ZIGBEE_LABEL_INIT_DEVICE = 14; // Start ZNP as end-device
|
||||
// const uint8_t ZIGBEE_LABEL_START_DEVICE = 15; // Start ZNP as end-device - same as ZIGBEE_LABEL_START_ROUTER
|
||||
const uint8_t ZIGBEE_LABEL_INIT_DEVICE = 14; // Init ZNP as end-device
|
||||
const uint8_t ZIGBEE_LABEL_START_DEVICE = 15; // Start ZNP as end-device
|
||||
const uint8_t ZIGBEE_LABEL_START_ROUTER_DEVICE = 16; // Start common to router and device
|
||||
const uint8_t ZIGBEE_LABEL_FACT_RESET_ROUTER_DEVICE_POST = 19; // common post configuration for router and device
|
||||
const uint8_t ZIGBEE_LABEL_READY = 20; // goto label 20 for main loop
|
||||
const uint8_t ZIGBEE_LABEL_MAIN_LOOP = 21; // main loop
|
||||
@ -400,7 +401,7 @@ void Z_UpdateConfig(uint8_t zb_channel, uint16_t zb_pan_id, uint64_t zb_ext_pani
|
||||
const char kCheckingDeviceConfiguration[] PROGMEM = D_LOG_ZIGBEE "checking device configuration";
|
||||
const char kConfiguredCoord[] PROGMEM = "Configured, starting coordinator";
|
||||
const char kConfiguredRouter[] PROGMEM = "Configured, starting router";
|
||||
const char kConfiguredDevice[] PROGMEM = "Configured, starting end-device";
|
||||
const char kConfiguredDevice[] PROGMEM = "Configured, starting device";
|
||||
const char kStarted[] PROGMEM = "Started";
|
||||
const char kZigbeeStarted[] PROGMEM = D_LOG_ZIGBEE "Zigbee started";
|
||||
const char kResetting[] PROGMEM = "Resetting configuration";
|
||||
@ -426,7 +427,7 @@ static const Zigbee_Instruction zb_prog[] PROGMEM = {
|
||||
ZI_WAIT_RECV_FUNC(2000, ZBR_VERSION, &Z_ReceiveCheckVersion) // Check if version is valid
|
||||
|
||||
// Dispatching whether coordinator, router or end-device
|
||||
ZI_CALL(&Z_SwitchDeviceType, 0) // goto ZIGBEE_LABEL_START_ROUTER, ZIGBEE_LABEL_START_DEVICE or continue if coordinator
|
||||
ZI_CALL(&Z_SwitchDeviceType, 0) // goto ZIGBEE_LABEL_INIT_ROUTER, ZIGBEE_LABEL_INIT_DEVICE or continue if coordinator
|
||||
|
||||
// ======================================================================
|
||||
// Start as Zigbee Coordinator
|
||||
@ -537,8 +538,9 @@ static const Zigbee_Instruction zb_prog[] PROGMEM = {
|
||||
ZI_SEND(ZBS_LOGTYPE) // check the logical type
|
||||
ZI_WAIT_RECV(1000, ZBS_LOGTYPE_ROUTER) // it should be coordinator
|
||||
|
||||
ZI_LABEL(ZIGBEE_LABEL_START_ROUTER) // Init as a router
|
||||
// ZI_LABEL(ZIGBEE_LABEL_START_ROUTER) // Init as a router
|
||||
ZI_MQTT_STATE(ZIGBEE_STATUS_STARTING, kConfiguredRouter)
|
||||
ZI_LABEL(ZIGBEE_LABEL_START_ROUTER_DEVICE)
|
||||
ZI_ON_ERROR_GOTO(ZIGBEE_LABEL_ABORT)
|
||||
ZI_SEND(ZBS_AF_REGISTER_ALL) // Z_AF register for endpoint 01, profile 0x0104 Home Automation
|
||||
ZI_WAIT_RECV(1000, ZBR_AF_REGISTER)
|
||||
@ -570,7 +572,7 @@ static const Zigbee_Instruction zb_prog[] PROGMEM = {
|
||||
ZI_SEND(ZBS_WNV_ZNPHC) // Write NV ZNP Has Configured
|
||||
ZI_WAIT_RECV(1000, ZBR_WNV_OK)
|
||||
|
||||
ZI_GOTO(ZIGBEE_LABEL_START_ROUTER)
|
||||
ZI_GOTO(ZIGBEE_LABEL_START_ROUTER_DEVICE)
|
||||
|
||||
// ======================================================================
|
||||
// Start as Zigbee Device
|
||||
@ -583,7 +585,8 @@ static const Zigbee_Instruction zb_prog[] PROGMEM = {
|
||||
ZI_SEND(ZBS_LOGTYPE) // check the logical type
|
||||
ZI_WAIT_RECV(1000, ZBS_LOGTYPE_DEVICE) // it should be coordinator
|
||||
|
||||
ZI_GOTO(ZIGBEE_LABEL_START_ROUTER)
|
||||
ZI_MQTT_STATE(ZIGBEE_STATUS_STARTING, kConfiguredDevice)
|
||||
ZI_GOTO(ZIGBEE_LABEL_START_ROUTER_DEVICE)
|
||||
|
||||
ZI_LABEL(ZIGBEE_LABEL_FACT_RESET_DEVICE) // Factory reset for router
|
||||
ZI_MQTT_STATE(ZIGBEE_STATUS_RESET_CONF, kResetting)
|
||||
|
@ -653,22 +653,28 @@ int32_t Z_ReceiveAfIncomingMessage(int32_t res, const class SBuffer &buf) {
|
||||
JsonObject& json = jsonBuffer.createObject();
|
||||
|
||||
if ( (!zcl_received.isClusterSpecificCommand()) && (ZCL_DEFAULT_RESPONSE == zcl_received.getCmdId())) {
|
||||
zcl_received.parseResponse();
|
||||
zcl_received.parseResponse(); // Zigbee general "Degault Response", publish ZbResponse message
|
||||
} else {
|
||||
// Build the ZbReceive json
|
||||
if ( (!zcl_received.isClusterSpecificCommand()) && (ZCL_REPORT_ATTRIBUTES == zcl_received.getCmdId())) {
|
||||
zcl_received.parseRawAttributes(json);
|
||||
zcl_received.parseReportAttributes(json); // Zigbee report attributes from sensors
|
||||
if (clusterid) { defer_attributes = true; } // don't defer system Cluster=0 messages
|
||||
} else if ( (!zcl_received.isClusterSpecificCommand()) && (ZCL_READ_ATTRIBUTES_RESPONSE == zcl_received.getCmdId())) {
|
||||
zcl_received.parseReadAttributes(json);
|
||||
zcl_received.parseReadAttributesResponse(json);
|
||||
if (clusterid) { defer_attributes = true; } // don't defer system Cluster=0 messages
|
||||
} else if ( (!zcl_received.isClusterSpecificCommand()) && (ZCL_READ_ATTRIBUTES == zcl_received.getCmdId())) {
|
||||
zcl_received.parseReadAttributes(json);
|
||||
// never defer read_attributes, so the auto-responder can send response back on a per cluster basis
|
||||
} else if (zcl_received.isClusterSpecificCommand()) {
|
||||
zcl_received.parseClusterSpecificCommand(json);
|
||||
}
|
||||
String msg("");
|
||||
msg.reserve(100);
|
||||
json.printTo(msg);
|
||||
AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_ZIGBEE D_JSON_ZIGBEEZCL_RAW_RECEIVED ": {\"0x%04X\":%s}"), srcaddr, msg.c_str());
|
||||
|
||||
{ // fence to force early de-allocation of msg
|
||||
String msg("");
|
||||
msg.reserve(100);
|
||||
json.printTo(msg);
|
||||
AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_ZIGBEE D_JSON_ZIGBEEZCL_RAW_RECEIVED ": {\"0x%04X\":%s}"), srcaddr, msg.c_str());
|
||||
}
|
||||
|
||||
zcl_received.postProcessAttributes(srcaddr, json);
|
||||
// Add Endpoint
|
||||
@ -698,6 +704,9 @@ int32_t Z_ReceiveAfIncomingMessage(int32_t res, const class SBuffer &buf) {
|
||||
} else {
|
||||
// Publish immediately
|
||||
zigbee_devices.jsonPublishNow(srcaddr, json);
|
||||
|
||||
// Add auto-responder here
|
||||
Z_AutoResponder(srcaddr, clusterid, srcendpoint, json[F("ReadNames")]);
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
@ -814,4 +823,74 @@ int32_t Z_State_Ready(uint8_t value) {
|
||||
return 0; // continue
|
||||
}
|
||||
|
||||
//
|
||||
// Auto-responder for Read request from extenal devices.
|
||||
//
|
||||
// Mostly used for routers/end-devices
|
||||
// json: holds the attributes in JSON format
|
||||
void Z_AutoResponder(uint16_t srcaddr, uint16_t cluster, uint8_t endpoint, const JsonObject &json) {
|
||||
DynamicJsonBuffer jsonBuffer;
|
||||
JsonObject& json_out = jsonBuffer.createObject();
|
||||
|
||||
// responder
|
||||
switch (cluster) {
|
||||
case 0x0000:
|
||||
if (HasKeyCaseInsensitive(json, PSTR("ModelId"))) { json_out[F("ModelId")] = F("Tasmota Z2T"); }
|
||||
if (HasKeyCaseInsensitive(json, PSTR("Manufacturer"))) { json_out[F("Manufacturer")] = F("Tasmota"); }
|
||||
break;
|
||||
#ifdef USE_LIGHT
|
||||
case 0x0006:
|
||||
if (HasKeyCaseInsensitive(json, PSTR("Power"))) { json_out[F("Power")] = Light.power ? 1 : 0; }
|
||||
break;
|
||||
case 0x0008:
|
||||
if (HasKeyCaseInsensitive(json, PSTR("Dimmer"))) { json_out[F("Dimmer")] = LightGetDimmer(0); }
|
||||
break;
|
||||
case 0x0300:
|
||||
{
|
||||
uint16_t hue;
|
||||
uint8_t sat;
|
||||
float XY[2];
|
||||
LightGetHSB(&hue, &sat, nullptr);
|
||||
LightGetXY(&XY[0], &XY[1]);
|
||||
uint16_t uxy[2];
|
||||
for (uint32_t i = 0; i < ARRAY_SIZE(XY); i++) {
|
||||
uxy[i] = XY[i] * 65536.0f;
|
||||
uxy[i] = (uxy[i] > 0xFEFF) ? uxy[i] : 0xFEFF;
|
||||
}
|
||||
if (HasKeyCaseInsensitive(json, PSTR("Hue"))) { json_out[F("Hue")] = changeUIntScale(hue, 0, 360, 0, 254); }
|
||||
if (HasKeyCaseInsensitive(json, PSTR("Sat"))) { json_out[F("Sat")] = changeUIntScale(sat, 0, 255, 0, 254); }
|
||||
if (HasKeyCaseInsensitive(json, PSTR("CT"))) { json_out[F("CT")] = LightGetColorTemp(); }
|
||||
if (HasKeyCaseInsensitive(json, PSTR("X"))) { json_out[F("X")] = uxy[0]; }
|
||||
if (HasKeyCaseInsensitive(json, PSTR("Y"))) { json_out[F("Y")] = uxy[1]; }
|
||||
}
|
||||
break;
|
||||
#endif
|
||||
case 0x000A: // Time
|
||||
if (HasKeyCaseInsensitive(json, PSTR("Time"))) { json_out[F("Time")] = Rtc.utc_time; }
|
||||
if (HasKeyCaseInsensitive(json, PSTR("TimeStatus"))) { json_out[F("TimeStatus")] = (Rtc.utc_time > (60 * 60 * 24 * 365 * 10)) ? 0x02 : 0x00; } // if time is beyond 2010 then we are synchronized
|
||||
if (HasKeyCaseInsensitive(json, PSTR("TimeZone"))) { json_out[F("TimeZone")] = Settings.toffset[0] * 60; } // seconds
|
||||
break;
|
||||
}
|
||||
|
||||
if (json_out.size() > 0) {
|
||||
// we have a non-empty output
|
||||
|
||||
// log first
|
||||
String msg("");
|
||||
msg.reserve(100);
|
||||
json_out.printTo(msg);
|
||||
AddLog_P2(LOG_LEVEL_INFO, PSTR("ZIG: Auto-responder: ZbSend {\"Device\":\"0x%04X\""
|
||||
",\"Cluster\":\"0x%04X\""
|
||||
",\"Endpoint\":%d"
|
||||
",\"Response\":%s}"
|
||||
),
|
||||
srcaddr, cluster, endpoint,
|
||||
msg.c_str());
|
||||
|
||||
// send
|
||||
const JsonVariant &json_out_v = json_out;
|
||||
ZbSendReportWrite(json_out_v, srcaddr, 0 /* group */,cluster, endpoint, 0 /* manuf */, ZCL_READ_ATTRIBUTES_RESPONSE);
|
||||
}
|
||||
}
|
||||
|
||||
#endif // USE_ZIGBEE
|
||||
|
@ -32,7 +32,7 @@ TasmotaSerial *ZigbeeSerial = nullptr;
|
||||
const char kZbCommands[] PROGMEM = D_PRFX_ZB "|" // prefix
|
||||
D_CMND_ZIGBEEZNPSEND "|" D_CMND_ZIGBEE_PERMITJOIN "|"
|
||||
D_CMND_ZIGBEE_STATUS "|" D_CMND_ZIGBEE_RESET "|" D_CMND_ZIGBEE_SEND "|"
|
||||
D_CMND_ZIGBEE_PROBE "|" D_CMND_ZIGBEE_READ "|" D_CMND_ZIGBEEZNPRECEIVE "|"
|
||||
D_CMND_ZIGBEE_PROBE "|" D_CMND_ZIGBEEZNPRECEIVE "|"
|
||||
D_CMND_ZIGBEE_FORGET "|" D_CMND_ZIGBEE_SAVE "|" D_CMND_ZIGBEE_NAME "|"
|
||||
D_CMND_ZIGBEE_BIND "|" D_CMND_ZIGBEE_UNBIND "|" D_CMND_ZIGBEE_PING "|" D_CMND_ZIGBEE_MODELID "|"
|
||||
D_CMND_ZIGBEE_LIGHT "|" D_CMND_ZIGBEE_RESTORE "|" D_CMND_ZIGBEE_BIND_STATE "|"
|
||||
@ -42,7 +42,7 @@ const char kZbCommands[] PROGMEM = D_PRFX_ZB "|" // prefix
|
||||
void (* const ZigbeeCommand[])(void) PROGMEM = {
|
||||
&CmndZbZNPSend, &CmndZbPermitJoin,
|
||||
&CmndZbStatus, &CmndZbReset, &CmndZbSend,
|
||||
&CmndZbProbe, &CmndZbRead, &CmndZbZNPReceive,
|
||||
&CmndZbProbe, &CmndZbZNPReceive,
|
||||
&CmndZbForget, &CmndZbSave, &CmndZbName,
|
||||
&CmndZbBind, &CmndZbUnbind, &CmndZbPing, &CmndZbModelId,
|
||||
&CmndZbLight, &CmndZbRestore, &CmndZbBindState,
|
||||
@ -393,9 +393,345 @@ void zigbeeZCLSendStr(uint16_t shortaddr, uint16_t groupaddr, uint8_t endpoint,
|
||||
}
|
||||
}
|
||||
|
||||
// Parse "Report", "Write" or "Response" attribute
|
||||
// Operation is one of: ZCL_REPORT_ATTRIBUTES (0x0A), ZCL_WRITE_ATTRIBUTES (0x02) or ZCL_READ_ATTRIBUTES_RESPONSE (0x01)
|
||||
void ZbSendReportWrite(const JsonObject &val_pubwrite, uint16_t device, uint16_t groupaddr, uint16_t cluster, uint8_t endpoint, uint16_t manuf, uint32_t operation) {
|
||||
SBuffer buf(200); // buffer to store the binary output of attibutes
|
||||
|
||||
if (nullptr == XdrvMailbox.command) {
|
||||
XdrvMailbox.command = (char*) ""; // prevent a crash when calling ReponseCmndChar and there was no previous command
|
||||
}
|
||||
|
||||
// iterate on keys
|
||||
for (JsonObject::const_iterator it=val_pubwrite.begin(); it!=val_pubwrite.end(); ++it) {
|
||||
const char *key = it->key;
|
||||
const JsonVariant &value = it->value;
|
||||
|
||||
uint16_t attr_id = 0xFFFF;
|
||||
uint16_t cluster_id = 0xFFFF;
|
||||
uint8_t type_id = Znodata;
|
||||
int16_t multiplier = 1; // multiplier to adjust the key value
|
||||
float val_f = 0.0f; // alternative value if multiplier is used
|
||||
|
||||
// check if the name has the format "XXXX/YYYY" where XXXX is the cluster, YYYY the attribute id
|
||||
// alternative "XXXX/YYYY%ZZ" where ZZ is the type (for unregistered attributes)
|
||||
char * delimiter = strchr(key, '/');
|
||||
char * delimiter2 = strchr(key, '%');
|
||||
if (delimiter) {
|
||||
cluster_id = strtoul(key, &delimiter, 16);
|
||||
if (!delimiter2) {
|
||||
attr_id = strtoul(delimiter+1, nullptr, 16);
|
||||
} else {
|
||||
attr_id = strtoul(delimiter+1, &delimiter2, 16);
|
||||
type_id = strtoul(delimiter2+1, nullptr, 16);
|
||||
}
|
||||
}
|
||||
// AddLog_P2(LOG_LEVEL_DEBUG, PSTR("cluster_id = 0x%04X, attr_id = 0x%04X"), cluster_id, attr_id);
|
||||
|
||||
// do we already know the type, i.e. attribute and cluster are also known
|
||||
if (Znodata == type_id) {
|
||||
// scan attributes to find by name, and retrieve type
|
||||
for (uint32_t i = 0; i < ARRAY_SIZE(Z_PostProcess); i++) {
|
||||
const Z_AttributeConverter *converter = &Z_PostProcess[i];
|
||||
bool match = false;
|
||||
uint16_t local_attr_id = pgm_read_word(&converter->attribute);
|
||||
uint16_t local_cluster_id = CxToCluster(pgm_read_byte(&converter->cluster_short));
|
||||
uint8_t local_type_id = pgm_read_byte(&converter->type);
|
||||
int16_t local_multiplier = pgm_read_word(&converter->multiplier);
|
||||
// AddLog_P2(LOG_LEVEL_DEBUG, PSTR("Try cluster = 0x%04X, attr = 0x%04X, type_id = 0x%02X"), local_cluster_id, local_attr_id, local_type_id);
|
||||
|
||||
if (delimiter) {
|
||||
if ((cluster_id == local_cluster_id) && (attr_id == local_attr_id)) {
|
||||
type_id = local_type_id;
|
||||
break;
|
||||
}
|
||||
} else if (converter->name) {
|
||||
// AddLog_P2(LOG_LEVEL_DEBUG, PSTR("Comparing '%s' with '%s'"), attr_name, converter->name);
|
||||
if (0 == strcasecmp_P(key, converter->name)) {
|
||||
// match
|
||||
cluster_id = local_cluster_id;
|
||||
attr_id = local_attr_id;
|
||||
type_id = local_type_id;
|
||||
multiplier = local_multiplier;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Buffer ready, do some sanity checks
|
||||
// AddLog_P2(LOG_LEVEL_DEBUG, PSTR("cluster_id = 0x%04X, attr_id = 0x%04X, type_id = 0x%02X"), cluster_id, attr_id, type_id);
|
||||
if ((0xFFFF == attr_id) || (0xFFFF == cluster_id)) {
|
||||
Response_P(PSTR("{\"%s\":\"%s'%s'\"}"), XdrvMailbox.command, PSTR("Unknown attribute "), key);
|
||||
return;
|
||||
}
|
||||
if (Znodata == type_id) {
|
||||
Response_P(PSTR("{\"%s\":\"%s'%s'\"}"), XdrvMailbox.command, PSTR("Unknown attribute type for attribute "), key);
|
||||
return;
|
||||
}
|
||||
|
||||
if (0xFFFF == cluster) {
|
||||
cluster = cluster_id; // set the cluster for this packet
|
||||
} else if (cluster != cluster_id) {
|
||||
ResponseCmndChar_P(PSTR("No more than one cluster id per command"));
|
||||
return;
|
||||
}
|
||||
// apply multiplier if needed
|
||||
bool use_val = true;
|
||||
if ((0 != multiplier) && (1 != multiplier)) {
|
||||
val_f = value;
|
||||
if (multiplier > 0) { // inverse of decoding
|
||||
val_f = val_f / multiplier;
|
||||
} else {
|
||||
val_f = val_f * multiplier;
|
||||
}
|
||||
use_val = false;
|
||||
}
|
||||
// push the value in the buffer
|
||||
int32_t res = encodeSingleAttribute(buf, use_val ? value : *(const JsonVariant*)nullptr, val_f, attr_id, type_id, operation == ZCL_READ_ATTRIBUTES_RESPONSE); // force status if Reponse
|
||||
if (res < 0) {
|
||||
Response_P(PSTR("{\"%s\":\"%s'%s' 0x%02X\"}"), XdrvMailbox.command, PSTR("Unsupported attribute type "), key, type_id);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// did we have any attribute?
|
||||
if (0 == buf.len()) {
|
||||
ResponseCmndChar_P(PSTR("No attribute in list"));
|
||||
return;
|
||||
}
|
||||
|
||||
// all good, send the packet
|
||||
ZigbeeZCLSend_Raw(device, groupaddr, cluster, endpoint, operation, false /* not cluster specific */, manuf, buf.getBuffer(), buf.len(), false /* noresponse */, zigbee_devices.getNextSeqNumber(device));
|
||||
ResponseCmndDone();
|
||||
}
|
||||
|
||||
// Parse the "Send" attribute and send the command
|
||||
void ZbSendSend(const JsonVariant &val_cmd, uint16_t device, uint16_t groupaddr, uint16_t cluster, uint8_t endpoint, uint16_t manuf) {
|
||||
uint8_t cmd = 0;
|
||||
String cmd_str = ""; // the actual low-level command, either specified or computed
|
||||
const char *cmd_s; // pointer to payload string
|
||||
bool clusterSpecific = true;
|
||||
|
||||
static char delim[] = ", "; // delimiters for parameters
|
||||
// probe the type of the argument
|
||||
// If JSON object, it's high level commands
|
||||
// If String, it's a low level command
|
||||
if (val_cmd.is<JsonObject>()) {
|
||||
// we have a high-level command
|
||||
const JsonObject &cmd_obj = val_cmd.as<const JsonObject&>();
|
||||
int32_t cmd_size = cmd_obj.size();
|
||||
if (cmd_size > 1) {
|
||||
Response_P(PSTR("Only 1 command allowed (%d)"), cmd_size);
|
||||
return;
|
||||
} else if (1 == cmd_size) {
|
||||
// We have exactly 1 command, parse it
|
||||
JsonObject::const_iterator it = cmd_obj.begin(); // just get the first key/value
|
||||
String key = it->key;
|
||||
const JsonVariant& value = it->value;
|
||||
uint32_t x = 0, y = 0, z = 0;
|
||||
uint16_t cmd_var;
|
||||
uint16_t local_cluster_id;
|
||||
|
||||
const __FlashStringHelper* tasmota_cmd = zigbeeFindCommand(key.c_str(), &local_cluster_id, &cmd_var);
|
||||
if (tasmota_cmd) {
|
||||
cmd_str = tasmota_cmd;
|
||||
} else {
|
||||
Response_P(PSTR("Unrecognized zigbee command: %s"), key.c_str());
|
||||
return;
|
||||
}
|
||||
// check cluster
|
||||
if (0xFFFF == cluster) {
|
||||
cluster = local_cluster_id;
|
||||
} else if (cluster != local_cluster_id) {
|
||||
ResponseCmndChar_P(PSTR("No more than one cluster id per command"));
|
||||
return;
|
||||
}
|
||||
|
||||
// parse the JSON value, depending on its type fill in x,y,z
|
||||
if (value.is<bool>()) {
|
||||
x = value.as<bool>() ? 1 : 0;
|
||||
} else if (value.is<unsigned int>()) {
|
||||
x = value.as<unsigned int>();
|
||||
} else {
|
||||
// if non-bool or non-int, trying char*
|
||||
const char *s_const = value.as<const char*>();
|
||||
if (s_const != nullptr) {
|
||||
char s[strlen(s_const)+1];
|
||||
strcpy(s, s_const);
|
||||
if ((nullptr != s) && (0x00 != *s)) { // ignore any null or empty string, could represent 'null' json value
|
||||
char *sval = strtok(s, delim);
|
||||
if (sval) {
|
||||
x = ZigbeeAliasOrNumber(sval);
|
||||
sval = strtok(nullptr, delim);
|
||||
if (sval) {
|
||||
y = ZigbeeAliasOrNumber(sval);
|
||||
sval = strtok(nullptr, delim);
|
||||
if (sval) {
|
||||
z = ZigbeeAliasOrNumber(sval);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//AddLog_P2(LOG_LEVEL_DEBUG, PSTR("ZbSend: command_template = %s"), cmd_str.c_str());
|
||||
if (0xFF == cmd_var) { // if command number is a variable, replace it with x
|
||||
cmd = x;
|
||||
x = y; // and shift other variables
|
||||
y = z;
|
||||
} else {
|
||||
cmd = cmd_var; // or simply copy the cmd number
|
||||
}
|
||||
cmd_str = zigbeeCmdAddParams(cmd_str.c_str(), x, y, z); // fill in parameters
|
||||
//AddLog_P2(LOG_LEVEL_DEBUG, PSTR("ZbSend: command_final = %s"), cmd_str.c_str());
|
||||
cmd_s = cmd_str.c_str();
|
||||
} else {
|
||||
// we have zero command, pass through until last error for missing command
|
||||
}
|
||||
} else if (val_cmd.is<const char*>()) {
|
||||
// low-level command
|
||||
cmd_str = val_cmd.as<String>();
|
||||
// Now parse the string to extract cluster, command, and payload
|
||||
// Parse 'cmd' in the form "AAAA_BB/CCCCCCCC" or "AAAA!BB/CCCCCCCC"
|
||||
// where AA is the cluster number, BBBB the command number, CCCC... the payload
|
||||
// First delimiter is '_' for a global command, or '!' for a cluster specific command
|
||||
const char * data = cmd_str.c_str();
|
||||
uint16_t local_cluster_id = parseHex(&data, 4);
|
||||
|
||||
// check cluster
|
||||
if (0xFFFF == cluster) {
|
||||
cluster = local_cluster_id;
|
||||
} else if (cluster != local_cluster_id) {
|
||||
ResponseCmndChar_P(PSTR("No more than one cluster id per command"));
|
||||
return;
|
||||
}
|
||||
|
||||
// delimiter
|
||||
if (('_' == *data) || ('!' == *data)) {
|
||||
if ('_' == *data) { clusterSpecific = false; }
|
||||
data++;
|
||||
} else {
|
||||
ResponseCmndChar_P(PSTR("Wrong delimiter for payload"));
|
||||
return;
|
||||
}
|
||||
// parse cmd number
|
||||
cmd = parseHex(&data, 2);
|
||||
|
||||
// move to end of payload
|
||||
// delimiter is optional
|
||||
if ('/' == *data) { data++; } // skip delimiter
|
||||
|
||||
cmd_s = data;
|
||||
} else {
|
||||
// we have an unsupported command type, just ignore it and fallback to missing command
|
||||
}
|
||||
|
||||
AddLog_P2(LOG_LEVEL_DEBUG, PSTR("ZigbeeZCLSend device: 0x%04X, group: 0x%04X, endpoint:%d, cluster:0x%04X, cmd:0x%02X, send:\"%s\""),
|
||||
device, groupaddr, endpoint, cluster, cmd, cmd_s);
|
||||
zigbeeZCLSendStr(device, groupaddr, endpoint, clusterSpecific, manuf, cluster, cmd, cmd_s);
|
||||
ResponseCmndDone();
|
||||
}
|
||||
|
||||
|
||||
// Parse the "Send" attribute and send the command
|
||||
void ZbSendRead(const JsonVariant &val_attr, uint16_t device, uint16_t groupaddr, uint16_t cluster, uint8_t endpoint, uint16_t manuf) {
|
||||
// ZbSend {"Device":"0xF289","Cluster":0,"Endpoint":3,"Read":5}
|
||||
// ZbSend {"Device":"0xF289","Cluster":"0x0000","Endpoint":"0x0003","Read":"0x0005"}
|
||||
// ZbSend {"Device":"0xF289","Cluster":0,"Endpoint":3,"Read":[5,6,7,4]}
|
||||
// ZbSend {"Device":"0xF289","Endpoint":3,"Read":{"ModelId":true}}
|
||||
// ZbSend {"Device":"0xF289","Read":{"ModelId":true}}
|
||||
|
||||
// params
|
||||
size_t attrs_len = 0;
|
||||
uint8_t* attrs = nullptr; // empty string is valid
|
||||
|
||||
uint16_t val = strToUInt(val_attr);
|
||||
if (val_attr.is<JsonArray>()) {
|
||||
const JsonArray& attr_arr = val_attr.as<const JsonArray&>();
|
||||
attrs_len = attr_arr.size() * 2;
|
||||
attrs = new uint8_t[attrs_len];
|
||||
|
||||
uint32_t i = 0;
|
||||
for (auto value : attr_arr) {
|
||||
uint16_t val = strToUInt(value);
|
||||
attrs[i++] = val & 0xFF;
|
||||
attrs[i++] = val >> 8;
|
||||
}
|
||||
} else if (val_attr.is<JsonObject>()) {
|
||||
const JsonObject& attr_obj = val_attr.as<const JsonObject&>();
|
||||
attrs_len = attr_obj.size() * 2;
|
||||
attrs = new uint8_t[attrs_len];
|
||||
uint32_t actual_attr_len = 0;
|
||||
|
||||
// iterate on keys
|
||||
for (JsonObject::const_iterator it=attr_obj.begin(); it!=attr_obj.end(); ++it) {
|
||||
const char *key = it->key;
|
||||
// const JsonVariant &value = it->value; // we don't need the value here, only keys are relevant
|
||||
|
||||
bool found = false;
|
||||
// scan attributes to find by name, and retrieve type
|
||||
for (uint32_t i = 0; i < ARRAY_SIZE(Z_PostProcess); i++) {
|
||||
const Z_AttributeConverter *converter = &Z_PostProcess[i];
|
||||
bool match = false;
|
||||
uint16_t local_attr_id = pgm_read_word(&converter->attribute);
|
||||
uint16_t local_cluster_id = CxToCluster(pgm_read_byte(&converter->cluster_short));
|
||||
// uint8_t local_type_id = pgm_read_byte(&converter->type);
|
||||
|
||||
if ((converter->name) && (0 == strcasecmp_P(key, converter->name))) {
|
||||
// match name
|
||||
// check if there is a conflict with cluster
|
||||
// TODO
|
||||
attrs[actual_attr_len++] = local_attr_id & 0xFF;
|
||||
attrs[actual_attr_len++] = local_attr_id >> 8;
|
||||
found = true;
|
||||
// check cluster
|
||||
if (0xFFFF == cluster) {
|
||||
cluster = local_cluster_id;
|
||||
} else if (cluster != local_cluster_id) {
|
||||
ResponseCmndChar_P(PSTR("No more than one cluster id per command"));
|
||||
if (attrs) { delete[] attrs; }
|
||||
return;
|
||||
}
|
||||
break; // found, exit loop
|
||||
}
|
||||
}
|
||||
if (!found) {
|
||||
AddLog_P2(LOG_LEVEL_INFO, PSTR("ZIG: Unknown attribute name (ignored): %s"), key);
|
||||
}
|
||||
}
|
||||
|
||||
attrs_len = actual_attr_len;
|
||||
} else {
|
||||
attrs_len = 2;
|
||||
attrs = new uint8_t[attrs_len];
|
||||
attrs[0] = val & 0xFF; // little endian
|
||||
attrs[1] = val >> 8;
|
||||
}
|
||||
|
||||
if (attrs_len > 0) {
|
||||
ZigbeeZCLSend_Raw(device, groupaddr, cluster, endpoint, ZCL_READ_ATTRIBUTES, false, manuf, attrs, attrs_len, true /* we do want a response */, zigbee_devices.getNextSeqNumber(device));
|
||||
ResponseCmndDone();
|
||||
} else {
|
||||
ResponseCmndChar_P(PSTR("Missing parameters"));
|
||||
}
|
||||
|
||||
if (attrs) { delete[] attrs; }
|
||||
}
|
||||
|
||||
//
|
||||
// Command `ZbSend`
|
||||
//
|
||||
// Examples:
|
||||
// ZbSend {"Device":"0x0000","Endpoint":1,"Write":{"0006/0000":0}}
|
||||
// ZbSend {"Device":"0x0000","Endpoint":1,"Write":{"Power":0}}
|
||||
// ZbSend {"Device":"0x0000","Endpoint":1,"Write":{"AqaraRotate":0}}
|
||||
// ZbSend {"Device":"0x0000","Endpoint":1,"Write":{"AqaraRotate":12.5}}
|
||||
// ZbSend {"Device":"0x0000","Endpoint":1,"Write":{"006/0000%39":12.5}}
|
||||
// ZbSend {"Device":"0x0000","Endpoint":1,"Write":{"AnalogInApplicationType":1000000}}
|
||||
// ZbSend {"Device":"0x0000","Endpoint":1,"Write":{"TimeZone":-1000000}}
|
||||
// ZbSend {"Device":"0x0000","Endpoint":1,"Write":{"Manufacturer":"Tasmota","ModelId":"Tasmota Z2T Router"}}
|
||||
void CmndZbSend(void) {
|
||||
// ZbSend { "device":"0x1234", "endpoint":"0x03", "send":{"Power":1} }
|
||||
// ZbSend { "device":"0x1234", "endpoint":"0x03", "send":{"Power":"3"} }
|
||||
@ -405,7 +741,7 @@ void CmndZbSend(void) {
|
||||
// ZbSend { "device":"0x1234", "endpoint":"0x03", "send":{"Power":true} }
|
||||
// ZbSend { "device":"0x1234", "endpoint":"0x03", "send":{"Power":"true"} }
|
||||
// ZbSend { "device":"0x1234", "endpoint":"0x03", "send":{"ShutterClose":null} }
|
||||
// ZbSend { "devicse":"0x1234", "endpoint":"0x03", "send":{"Power":1} }
|
||||
// ZbSend { "device":"0x1234", "endpoint":"0x03", "send":{"Power":1} }
|
||||
// ZbSend { "device":"0x1234", "endpoint":"0x03", "send":{"Color":"1,2"} }
|
||||
// ZbSend { "device":"0x1234", "endpoint":"0x03", "send":{"Color":"0x1122,0xFFEE"} }
|
||||
if (zigbee.init_phase) { ResponseCmndChar_P(PSTR(D_ZIGBEE_NOT_STARTED)); return; }
|
||||
@ -414,26 +750,21 @@ void CmndZbSend(void) {
|
||||
if (!json.success()) { ResponseCmndChar_P(PSTR(D_JSON_INVALID_JSON)); return; }
|
||||
|
||||
// params
|
||||
static char delim[] = ", "; // delimiters for parameters
|
||||
uint16_t device = BAD_SHORTADDR; // 0x0000 is local, so considered invalid
|
||||
uint16_t groupaddr = 0x0000; // group address
|
||||
uint8_t endpoint = 0x00; // 0x00 is invalid for the dst endpoint
|
||||
uint16_t manuf = 0x0000; // Manuf Id in ZCL frame
|
||||
// Command elements
|
||||
uint16_t cluster = 0;
|
||||
uint8_t cmd = 0;
|
||||
String cmd_str = ""; // the actual low-level command, either specified or computed
|
||||
const char *cmd_s; // pointer to payload string
|
||||
bool clusterSpecific = true;
|
||||
uint16_t device = BAD_SHORTADDR; // BAD_SHORTADDR is broadcast, so considered invalid
|
||||
uint16_t groupaddr = 0x0000; // group address valid only if device == BAD_SHORTADDR
|
||||
uint16_t cluster = 0xFFFF; // no default
|
||||
uint8_t endpoint = 0x00; // 0x00 is invalid for the dst endpoint
|
||||
uint16_t manuf = 0x0000; // Manuf Id in ZCL frame
|
||||
|
||||
// parse JSON
|
||||
const JsonVariant &val_device = GetCaseInsensitive(json, PSTR("Device"));
|
||||
|
||||
// parse "Device" and "Group"
|
||||
const JsonVariant &val_device = GetCaseInsensitive(json, PSTR(D_CMND_ZIGBEE_DEVICE));
|
||||
if (nullptr != &val_device) {
|
||||
device = zigbee_devices.parseDeviceParam(val_device.as<char*>());
|
||||
if (BAD_SHORTADDR == device) { ResponseCmndChar_P(PSTR("Invalid parameter")); return; }
|
||||
}
|
||||
if (BAD_SHORTADDR == device) { // if not found, check if we have a group
|
||||
const JsonVariant &val_group = GetCaseInsensitive(json, PSTR("Group"));
|
||||
const JsonVariant &val_group = GetCaseInsensitive(json, PSTR(D_CMND_ZIGBEE_GROUP));
|
||||
if (nullptr != &val_group) {
|
||||
groupaddr = strToUInt(val_group);
|
||||
} else { // no device nor group
|
||||
@ -441,117 +772,77 @@ void CmndZbSend(void) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
// from here, either device has a device shortaddr, or if BAD_SHORTADDR then use group address
|
||||
// Note: groupaddr == 0 is valid
|
||||
|
||||
const JsonVariant &val_endpoint = GetCaseInsensitive(json, PSTR("Endpoint"));
|
||||
// read other parameters
|
||||
const JsonVariant &val_cluster = GetCaseInsensitive(json, PSTR(D_CMND_ZIGBEE_CLUSTER));
|
||||
if (nullptr != &val_cluster) { cluster = strToUInt(val_cluster); }
|
||||
const JsonVariant &val_endpoint = GetCaseInsensitive(json, PSTR(D_CMND_ZIGBEE_ENDPOINT));
|
||||
if (nullptr != &val_endpoint) { endpoint = strToUInt(val_endpoint); }
|
||||
const JsonVariant &val_manuf = GetCaseInsensitive(json, PSTR("Manuf"));
|
||||
const JsonVariant &val_manuf = GetCaseInsensitive(json, PSTR(D_CMND_ZIGBEE_MANUF));
|
||||
if (nullptr != &val_manuf) { manuf = strToUInt(val_manuf); }
|
||||
const JsonVariant &val_cmd = GetCaseInsensitive(json, PSTR("Send"));
|
||||
|
||||
// infer endpoint
|
||||
if (BAD_SHORTADDR == device) {
|
||||
endpoint = 0xFF; // endpoint not used for group addresses, so use a dummy broadcast endpoint
|
||||
} else if (0 == endpoint) { // if it was not already specified, try to guess it
|
||||
endpoint = zigbee_devices.findFirstEndpoint(device);
|
||||
AddLog_P2(LOG_LEVEL_DEBUG, PSTR("ZIG: guessing endpoint %d"), endpoint);
|
||||
}
|
||||
if (0 == endpoint) { // after this, if it is still zero, then it's an error
|
||||
ResponseCmndChar_P(PSTR("Missing endpoint"));
|
||||
return;
|
||||
}
|
||||
// from here endpoint is valid and non-zero
|
||||
// cluster may be already specified or 0xFFFF
|
||||
|
||||
const JsonVariant &val_cmd = GetCaseInsensitive(json, PSTR(D_CMND_ZIGBEE_SEND));
|
||||
const JsonVariant &val_read = GetCaseInsensitive(json, PSTR(D_CMND_ZIGBEE_READ));
|
||||
const JsonVariant &val_write = GetCaseInsensitive(json, PSTR(D_CMND_ZIGBEE_WRITE));
|
||||
const JsonVariant &val_publish = GetCaseInsensitive(json, PSTR(D_CMND_ZIGBEE_REPORT));
|
||||
const JsonVariant &val_response = GetCaseInsensitive(json, PSTR(D_CMND_ZIGBEE_RESPONSE));
|
||||
uint32_t multi_cmd = (nullptr != &val_cmd) + (nullptr != &val_read) + (nullptr != &val_write) + (nullptr != &val_publish)+ (nullptr != &val_response);
|
||||
if (multi_cmd > 1) {
|
||||
ResponseCmndChar_P(PSTR("Can only have one of: 'Send', 'Read', 'Write', 'Report' or 'Reponse'"));
|
||||
return;
|
||||
}
|
||||
// from here we have one and only one command
|
||||
|
||||
if (nullptr != &val_cmd) {
|
||||
// probe the type of the argument
|
||||
// If JSON object, it's high level commands
|
||||
// If String, it's a low level command
|
||||
if (val_cmd.is<JsonObject>()) {
|
||||
// we have a high-level command
|
||||
const JsonObject &cmd_obj = val_cmd.as<const JsonObject&>();
|
||||
int32_t cmd_size = cmd_obj.size();
|
||||
if (cmd_size > 1) {
|
||||
Response_P(PSTR("Only 1 command allowed (%d)"), cmd_size);
|
||||
return;
|
||||
} else if (1 == cmd_size) {
|
||||
// We have exactly 1 command, parse it
|
||||
JsonObject::const_iterator it = cmd_obj.begin(); // just get the first key/value
|
||||
String key = it->key;
|
||||
const JsonVariant& value = it->value;
|
||||
uint32_t x = 0, y = 0, z = 0;
|
||||
uint16_t cmd_var;
|
||||
|
||||
const __FlashStringHelper* tasmota_cmd = zigbeeFindCommand(key.c_str(), &cluster, &cmd_var);
|
||||
if (tasmota_cmd) {
|
||||
cmd_str = tasmota_cmd;
|
||||
} else {
|
||||
Response_P(PSTR("Unrecognized zigbee command: %s"), key.c_str());
|
||||
return;
|
||||
}
|
||||
|
||||
// parse the JSON value, depending on its type fill in x,y,z
|
||||
if (value.is<bool>()) {
|
||||
x = value.as<bool>() ? 1 : 0;
|
||||
} else if (value.is<unsigned int>()) {
|
||||
x = value.as<unsigned int>();
|
||||
} else {
|
||||
// if non-bool or non-int, trying char*
|
||||
const char *s_const = value.as<const char*>();
|
||||
if (s_const != nullptr) {
|
||||
char s[strlen(s_const)+1];
|
||||
strcpy(s, s_const);
|
||||
if ((nullptr != s) && (0x00 != *s)) { // ignore any null or empty string, could represent 'null' json value
|
||||
char *sval = strtok(s, delim);
|
||||
if (sval) {
|
||||
x = ZigbeeAliasOrNumber(sval);
|
||||
sval = strtok(nullptr, delim);
|
||||
if (sval) {
|
||||
y = ZigbeeAliasOrNumber(sval);
|
||||
sval = strtok(nullptr, delim);
|
||||
if (sval) {
|
||||
z = ZigbeeAliasOrNumber(sval);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//AddLog_P2(LOG_LEVEL_DEBUG, PSTR("ZbSend: command_template = %s"), cmd_str.c_str());
|
||||
if (0xFF == cmd_var) { // if command number is a variable, replace it with x
|
||||
cmd = x;
|
||||
x = y; // and shift other variables
|
||||
y = z;
|
||||
} else {
|
||||
cmd = cmd_var; // or simply copy the cmd number
|
||||
}
|
||||
cmd_str = zigbeeCmdAddParams(cmd_str.c_str(), x, y, z); // fill in parameters
|
||||
//AddLog_P2(LOG_LEVEL_DEBUG, PSTR("ZbSend: command_final = %s"), cmd_str.c_str());
|
||||
cmd_s = cmd_str.c_str();
|
||||
} else {
|
||||
// we have zero command, pass through until last error for missing command
|
||||
}
|
||||
} else if (val_cmd.is<const char*>()) {
|
||||
// low-level command
|
||||
cmd_str = val_cmd.as<String>();
|
||||
// Now parse the string to extract cluster, command, and payload
|
||||
// Parse 'cmd' in the form "AAAA_BB/CCCCCCCC" or "AAAA!BB/CCCCCCCC"
|
||||
// where AA is the cluster number, BBBB the command number, CCCC... the payload
|
||||
// First delimiter is '_' for a global command, or '!' for a cluster specific command
|
||||
const char * data = cmd_str.c_str();
|
||||
cluster = parseHex(&data, 4);
|
||||
|
||||
// delimiter
|
||||
if (('_' == *data) || ('!' == *data)) {
|
||||
if ('_' == *data) { clusterSpecific = false; }
|
||||
data++;
|
||||
} else {
|
||||
ResponseCmndChar_P(PSTR("Wrong delimiter for payload"));
|
||||
return;
|
||||
}
|
||||
// parse cmd number
|
||||
cmd = parseHex(&data, 2);
|
||||
|
||||
// move to end of payload
|
||||
// delimiter is optional
|
||||
if ('/' == *data) { data++; } // skip delimiter
|
||||
|
||||
cmd_s = data;
|
||||
} else {
|
||||
// we have an unsupported command type, just ignore it and fallback to missing command
|
||||
// "Send":{...commands...}
|
||||
// we accept either a string or a JSON object
|
||||
ZbSendSend(val_cmd, device, groupaddr, cluster, endpoint, manuf);
|
||||
} else if (nullptr != &val_read) {
|
||||
// "Read":{...attributes...}, "Read":attribute or "Read":[...attributes...]
|
||||
// we accept eitehr a number, a string, an array of numbers/strings, or a JSON object
|
||||
ZbSendRead(val_read, device, groupaddr, cluster, endpoint, manuf);
|
||||
} else if (nullptr != &val_write) {
|
||||
// only KSON object
|
||||
if (!val_write.is<JsonObject>()) {
|
||||
ResponseCmndChar_P(PSTR("Missing parameters"));
|
||||
return;
|
||||
}
|
||||
|
||||
AddLog_P2(LOG_LEVEL_DEBUG, PSTR("ZigbeeZCLSend device: 0x%04X, group: 0x%04X, endpoint:%d, cluster:0x%04X, cmd:0x%02X, send:\"%s\""),
|
||||
device, groupaddr, endpoint, cluster, cmd, cmd_s);
|
||||
zigbeeZCLSendStr(device, groupaddr, endpoint, clusterSpecific, manuf, cluster, cmd, cmd_s);
|
||||
ResponseCmndDone();
|
||||
// "Write":{...attributes...}
|
||||
ZbSendReportWrite(val_write, device, groupaddr, cluster, endpoint, manuf, ZCL_WRITE_ATTRIBUTES);
|
||||
} else if (nullptr != &val_publish) {
|
||||
// "Report":{...attributes...}
|
||||
// only KSON object
|
||||
if (!val_publish.is<JsonObject>()) {
|
||||
ResponseCmndChar_P(PSTR("Missing parameters"));
|
||||
return;
|
||||
}
|
||||
ZbSendReportWrite(val_publish, device, groupaddr, cluster, endpoint, manuf, ZCL_REPORT_ATTRIBUTES);
|
||||
} else if (nullptr != &val_response) {
|
||||
// "Report":{...attributes...}
|
||||
// only KSON object
|
||||
if (!val_response.is<JsonObject>()) {
|
||||
ResponseCmndChar_P(PSTR("Missing parameters"));
|
||||
return;
|
||||
}
|
||||
ZbSendReportWrite(val_response, device, groupaddr, cluster, endpoint, manuf, ZCL_READ_ATTRIBUTES_RESPONSE);
|
||||
} else {
|
||||
Response_P(PSTR("Missing zigbee 'Send'"));
|
||||
Response_P(PSTR("Missing zigbee 'Send', 'Write', 'Report' or 'Response'"));
|
||||
return;
|
||||
}
|
||||
}
|
||||
@ -570,7 +861,6 @@ void ZbBindUnbind(bool unbind) { // false = bind, true = unbind
|
||||
if (!json.success()) { ResponseCmndChar_P(PSTR(D_JSON_INVALID_JSON)); return; }
|
||||
|
||||
// params
|
||||
// static char delim[] = ", "; // delimiters for parameters
|
||||
uint16_t srcDevice = BAD_SHORTADDR; // BAD_SHORTADDR is broadcast, so considered invalid
|
||||
uint16_t dstDevice = BAD_SHORTADDR; // BAD_SHORTADDR is broadcast, so considered invalid
|
||||
uint64_t dstLongAddr = 0;
|
||||
@ -582,7 +872,7 @@ void ZbBindUnbind(bool unbind) { // false = bind, true = unbind
|
||||
|
||||
// Information about source device: "Device", "Endpoint", "Cluster"
|
||||
// - the source endpoint must have a known IEEE address
|
||||
const JsonVariant &val_device = GetCaseInsensitive(json, PSTR("Device"));
|
||||
const JsonVariant &val_device = GetCaseInsensitive(json, PSTR(D_CMND_ZIGBEE_DEVICE));
|
||||
if (nullptr != &val_device) {
|
||||
srcDevice = zigbee_devices.parseDeviceParam(val_device.as<char*>());
|
||||
}
|
||||
@ -591,10 +881,10 @@ void ZbBindUnbind(bool unbind) { // false = bind, true = unbind
|
||||
uint64_t srcLongAddr = zigbee_devices.getDeviceLongAddr(srcDevice);
|
||||
if (0 == srcLongAddr) { ResponseCmndChar_P(PSTR("Unknown source IEEE address")); return; }
|
||||
// look for source endpoint
|
||||
const JsonVariant &val_endpoint = GetCaseInsensitive(json, PSTR("Endpoint"));
|
||||
const JsonVariant &val_endpoint = GetCaseInsensitive(json, PSTR(D_CMND_ZIGBEE_ENDPOINT));
|
||||
if (nullptr != &val_endpoint) { endpoint = strToUInt(val_endpoint); }
|
||||
// look for source cluster
|
||||
const JsonVariant &val_cluster = GetCaseInsensitive(json, PSTR("Cluster"));
|
||||
const JsonVariant &val_cluster = GetCaseInsensitive(json, PSTR(D_CMND_ZIGBEE_CLUSTER));
|
||||
if (nullptr != &val_cluster) { cluster = strToUInt(val_cluster); }
|
||||
|
||||
// Either Device address
|
||||
@ -885,90 +1175,6 @@ void CmndZbRestore(void) {
|
||||
ResponseCmndDone();
|
||||
}
|
||||
|
||||
//
|
||||
// Command `ZbRead`
|
||||
// Send an attribute read command to a device, specifying cluster and list of attributes
|
||||
//
|
||||
void CmndZbRead(void) {
|
||||
// ZbRead {"Device":"0xF289","Cluster":0,"Endpoint":3,"Attr":5}
|
||||
// ZbRead {"Device":"0xF289","Cluster":"0x0000","Endpoint":"0x0003","Attr":"0x0005"}
|
||||
// ZbRead {"Device":"0xF289","Cluster":0,"Endpoint":3,"Attr":[5,6,7,4]}
|
||||
if (zigbee.init_phase) { ResponseCmndChar_P(PSTR(D_ZIGBEE_NOT_STARTED)); return; }
|
||||
DynamicJsonBuffer jsonBuf;
|
||||
JsonObject &json = jsonBuf.parseObject((const char*) XdrvMailbox.data);
|
||||
if (!json.success()) { ResponseCmndChar_P(PSTR(D_JSON_INVALID_JSON)); return; }
|
||||
|
||||
// params
|
||||
uint16_t device = BAD_SHORTADDR; // BAD_SHORTADDR is broadcast, so considered invalid
|
||||
uint16_t groupaddr = 0x0000; // if 0x0000 ignore group adress
|
||||
uint16_t cluster = 0x0000; // default to general cluster
|
||||
uint8_t endpoint = 0x00; // 0x00 is invalid for the dst endpoint
|
||||
uint16_t manuf = 0x0000; // Manuf Id in ZCL frame
|
||||
size_t attrs_len = 0;
|
||||
uint8_t* attrs = nullptr; // empty string is valid
|
||||
|
||||
const JsonVariant &val_device = GetCaseInsensitive(json, PSTR("Device"));
|
||||
if (nullptr != &val_device) {
|
||||
device = zigbee_devices.parseDeviceParam(val_device.as<char*>());
|
||||
if (BAD_SHORTADDR == device) { ResponseCmndChar_P(PSTR("Invalid parameter")); return; }
|
||||
}
|
||||
if (BAD_SHORTADDR == device) { // if not found, check if we have a group
|
||||
const JsonVariant &val_group = GetCaseInsensitive(json, PSTR("Group"));
|
||||
if (nullptr != &val_group) {
|
||||
groupaddr = strToUInt(val_group);
|
||||
} else { // no device nor group
|
||||
ResponseCmndChar_P(PSTR("Unknown device"));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
const JsonVariant &val_cluster = GetCaseInsensitive(json, PSTR("Cluster"));
|
||||
if (nullptr != &val_cluster) { cluster = strToUInt(val_cluster); }
|
||||
const JsonVariant &val_endpoint = GetCaseInsensitive(json, PSTR("Endpoint"));
|
||||
if (nullptr != &val_endpoint) { endpoint = strToUInt(val_endpoint); }
|
||||
const JsonVariant &val_manuf = GetCaseInsensitive(json, PSTR("Manuf"));
|
||||
if (nullptr != &val_manuf) { manuf = strToUInt(val_manuf); }
|
||||
|
||||
const JsonVariant &val_attr = GetCaseInsensitive(json, PSTR("Read"));
|
||||
if (nullptr != &val_attr) {
|
||||
uint16_t val = strToUInt(val_attr);
|
||||
if (val_attr.is<JsonArray>()) {
|
||||
const JsonArray& attr_arr = val_attr.as<const JsonArray&>();
|
||||
attrs_len = attr_arr.size() * 2;
|
||||
attrs = new uint8_t[attrs_len];
|
||||
|
||||
uint32_t i = 0;
|
||||
for (auto value : attr_arr) {
|
||||
uint16_t val = strToUInt(value);
|
||||
attrs[i++] = val & 0xFF;
|
||||
attrs[i++] = val >> 8;
|
||||
}
|
||||
} else {
|
||||
attrs_len = 2;
|
||||
attrs = new uint8_t[attrs_len];
|
||||
attrs[0] = val & 0xFF; // little endian
|
||||
attrs[1] = val >> 8;
|
||||
}
|
||||
}
|
||||
|
||||
if ((0 == endpoint) && (device)) { // try to compute the endpoint
|
||||
endpoint = zigbee_devices.findFirstEndpoint(device);
|
||||
AddLog_P2(LOG_LEVEL_DEBUG, PSTR("ZbRead: guessing endpoint 0x%02X"), endpoint);
|
||||
}
|
||||
if (BAD_SHORTADDR == device) {
|
||||
endpoint = 0xFF; // endpoint not used for group addresses
|
||||
}
|
||||
|
||||
if ((0 != endpoint) && (attrs_len > 0)) {
|
||||
ZigbeeZCLSend_Raw(device, groupaddr, cluster, endpoint, ZCL_READ_ATTRIBUTES, false, manuf, attrs, attrs_len, true /* we do want a response */, zigbee_devices.getNextSeqNumber(device));
|
||||
ResponseCmndDone();
|
||||
} else {
|
||||
ResponseCmndChar_P(PSTR("Missing parameters"));
|
||||
}
|
||||
|
||||
if (attrs) { delete[] attrs; }
|
||||
}
|
||||
|
||||
//
|
||||
// Command `ZbPermitJoin`
|
||||
// Allow or Deny pairing of new Zigbee devices
|
||||
|
@ -84,18 +84,33 @@ void ILI9488_InitDriver()
|
||||
bppin=Pin(GPIO_BACKLIGHT);
|
||||
}
|
||||
|
||||
// init renderer
|
||||
if (PinUsed(GPIO_SSPI_CS) && PinUsed(GPIO_SSPI_MOSI) && PinUsed(GPIO_SSPI_SCLK)) {
|
||||
ili9488 = new ILI9488(Pin(GPIO_SSPI_CS),Pin(GPIO_SSPI_MOSI),Pin(GPIO_SSPI_SCLK),bppin);
|
||||
#ifdef ESP32
|
||||
#undef HW_SPI_MOSI
|
||||
#define HW_SPI_MOSI 23
|
||||
#undef HW_SPI_MISO
|
||||
#define HW_SPI_MISO 19
|
||||
#undef HW_SPI_CLK
|
||||
#define HW_SPI_CLK 18
|
||||
#else
|
||||
#undef HW_SPI_MOSI
|
||||
#define HW_SPI_MOSI 13
|
||||
#undef HW_SPI_MISO
|
||||
#define HW_SPI_MISO 12
|
||||
#undef HW_SPI_CLK
|
||||
#define HW_SPI_CLK 14
|
||||
#endif
|
||||
|
||||
// init renderer, must use hardware spi
|
||||
if (PinUsed(GPIO_SSPI_CS) && (Pin(GPIO_SSPI_MOSI)==HW_SPI_MOSI) && (Pin(GPIO_SSPI_SCLK)==HW_SPI_CLK)) {
|
||||
ili9488 = new ILI9488(Pin(GPIO_SSPI_CS),Pin(GPIO_SSPI_MOSI),Pin(GPIO_SSPI_SCLK),bppin);
|
||||
} else {
|
||||
if (PinUsed(GPIO_SPI_CS) && PinUsed(GPIO_SPI_MOSI) && PinUsed(GPIO_SPI_CLK)) {
|
||||
if (PinUsed(GPIO_SPI_CS) && (Pin(GPIO_SPI_MOSI)==HW_SPI_MOSI) && (Pin(GPIO_SPI_CLK)==HW_SPI_CLK)) {
|
||||
ili9488 = new ILI9488(Pin(GPIO_SPI_CS),Pin(GPIO_SPI_MOSI),Pin(GPIO_SPI_CLK),bppin);
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
SPI.begin();
|
||||
ili9488->begin();
|
||||
renderer = ili9488;
|
||||
renderer->DisplayInit(DISPLAY_INIT_MODE,Settings.display_size,Settings.display_rotate,Settings.display_font);
|
||||
|
@ -72,8 +72,11 @@ void RA8876_InitDriver()
|
||||
bg_color = RA8876_BLACK;
|
||||
|
||||
#ifdef ESP32
|
||||
#undef HW_SPI_MOSI
|
||||
#define HW_SPI_MOSI 23
|
||||
#undef HW_SPI_MISO
|
||||
#define HW_SPI_MISO 19
|
||||
#undef HW_SPI_CLK
|
||||
#define HW_SPI_CLK 18
|
||||
#else
|
||||
#undef HW_SPI_MOSI
|
||||
|
@ -22,6 +22,11 @@
|
||||
/*********************************************************************************************\
|
||||
* BH1750 - Ambient Light Intensity
|
||||
*
|
||||
* Bh1750Resolution1 0..2 - Set BH1750 1 resolution mode
|
||||
* Bh1750Resolution2 0..2 - Set BH1750 2 resolution mode
|
||||
* Bh1750MTime1 30..255 - Set BH1750 1 MT register
|
||||
* Bh1750MTime2 30..255 - Set BH1750 2 MT register
|
||||
*
|
||||
* I2C Address: 0x23 or 0x5C
|
||||
\*********************************************************************************************/
|
||||
|
||||
@ -38,119 +43,148 @@
|
||||
#define BH1750_MEASUREMENT_TIME_HIGH 0x40 // Measurement Time register high 3 bits
|
||||
#define BH1750_MEASUREMENT_TIME_LOW 0x60 // Measurement Time register low 5 bits
|
||||
|
||||
struct BH1750DATA {
|
||||
uint8_t address;
|
||||
#define D_PRFX_BH1750 "Bh1750"
|
||||
#define D_CMND_RESOLUTION "Resolution"
|
||||
#define D_CMND_MTREG "MTime"
|
||||
|
||||
const char kBh1750Commands[] PROGMEM = D_PRFX_BH1750 "|" // Prefix
|
||||
D_CMND_RESOLUTION "|" D_CMND_MTREG ;
|
||||
|
||||
void (* const Bh1750Command[])(void) PROGMEM = {
|
||||
&CmndBh1750Resolution, &CmndBh1750MTime };
|
||||
|
||||
struct {
|
||||
uint8_t addresses[2] = { BH1750_ADDR1, BH1750_ADDR2 };
|
||||
uint8_t resolution[3] = { BH1750_CONTINUOUS_HIGH_RES_MODE, BH1750_CONTINUOUS_HIGH_RES_MODE2, BH1750_CONTINUOUS_LOW_RES_MODE };
|
||||
uint8_t type = 0;
|
||||
uint8_t valid = 0;
|
||||
uint8_t mtreg = 69; // Default Measurement Time
|
||||
uint16_t illuminance = 0;
|
||||
uint8_t count = 0;
|
||||
char types[7] = "BH1750";
|
||||
} Bh1750;
|
||||
|
||||
struct {
|
||||
uint8_t address;
|
||||
uint8_t valid = 0;
|
||||
uint8_t mtreg = 69; // Default Measurement Time
|
||||
uint16_t illuminance = 0;
|
||||
} Bh1750_sensors[2];
|
||||
|
||||
/*********************************************************************************************/
|
||||
|
||||
bool Bh1750SetResolution(void)
|
||||
{
|
||||
Wire.beginTransmission(Bh1750.address);
|
||||
Wire.write(Bh1750.resolution[Settings.SensorBits1.bh1750_resolution]);
|
||||
uint8_t Bh1750Resolution(uint32_t sensor_index) {
|
||||
uint8_t settings_resolution = Settings.SensorBits1.bh1750_1_resolution;
|
||||
if (1 == sensor_index) {
|
||||
settings_resolution = Settings.SensorBits1.bh1750_2_resolution;
|
||||
}
|
||||
return settings_resolution;
|
||||
}
|
||||
|
||||
bool Bh1750SetResolution(uint32_t sensor_index) {
|
||||
Wire.beginTransmission(Bh1750_sensors[sensor_index].address);
|
||||
Wire.write(Bh1750.resolution[Bh1750Resolution(sensor_index)]);
|
||||
return (!Wire.endTransmission());
|
||||
}
|
||||
|
||||
bool Bh1750SetMTreg(void)
|
||||
{
|
||||
Wire.beginTransmission(Bh1750.address);
|
||||
uint8_t data = BH1750_MEASUREMENT_TIME_HIGH | ((Bh1750.mtreg >> 5) & 0x07);
|
||||
bool Bh1750SetMTreg(uint32_t sensor_index) {
|
||||
Wire.beginTransmission(Bh1750_sensors[sensor_index].address);
|
||||
uint8_t data = BH1750_MEASUREMENT_TIME_HIGH | ((Bh1750_sensors[sensor_index].mtreg >> 5) & 0x07);
|
||||
Wire.write(data);
|
||||
if (Wire.endTransmission()) { return false; }
|
||||
Wire.beginTransmission(Bh1750.address);
|
||||
data = BH1750_MEASUREMENT_TIME_LOW | (Bh1750.mtreg & 0x1F);
|
||||
Wire.beginTransmission(Bh1750_sensors[sensor_index].address);
|
||||
data = BH1750_MEASUREMENT_TIME_LOW | (Bh1750_sensors[sensor_index].mtreg & 0x1F);
|
||||
Wire.write(data);
|
||||
if (Wire.endTransmission()) { return false; }
|
||||
return Bh1750SetResolution();
|
||||
return Bh1750SetResolution(sensor_index);
|
||||
}
|
||||
|
||||
bool Bh1750Read(void)
|
||||
{
|
||||
if (Bh1750.valid) { Bh1750.valid--; }
|
||||
bool Bh1750Read(uint32_t sensor_index) {
|
||||
if (Bh1750_sensors[sensor_index].valid) { Bh1750_sensors[sensor_index].valid--; }
|
||||
|
||||
if (2 != Wire.requestFrom(Bh1750_sensors[sensor_index].address, (uint8_t)2)) { return false; }
|
||||
|
||||
if (2 != Wire.requestFrom(Bh1750.address, (uint8_t)2)) { return false; }
|
||||
float illuminance = (Wire.read() << 8) | Wire.read();
|
||||
illuminance /= (1.2 * (69 / (float)Bh1750.mtreg));
|
||||
if (1 == Settings.SensorBits1.bh1750_resolution) {
|
||||
illuminance /= (1.2 * (69 / (float)Bh1750_sensors[sensor_index].mtreg));
|
||||
if (1 == Bh1750Resolution(sensor_index)) {
|
||||
illuminance /= 2;
|
||||
}
|
||||
Bh1750.illuminance = illuminance;
|
||||
Bh1750_sensors[sensor_index].illuminance = illuminance;
|
||||
|
||||
Bh1750.valid = SENSOR_MAX_MISS;
|
||||
Bh1750_sensors[sensor_index].valid = SENSOR_MAX_MISS;
|
||||
return true;
|
||||
}
|
||||
|
||||
/********************************************************************************************/
|
||||
|
||||
void Bh1750Detect(void)
|
||||
{
|
||||
void Bh1750Detect(void) {
|
||||
for (uint32_t i = 0; i < sizeof(Bh1750.addresses); i++) {
|
||||
Bh1750.address = Bh1750.addresses[i];
|
||||
if (I2cActive(Bh1750.address)) { continue; }
|
||||
if (I2cActive(Bh1750.addresses[i])) { continue; }
|
||||
|
||||
if (Bh1750SetMTreg()) {
|
||||
I2cSetActiveFound(Bh1750.address, Bh1750.types);
|
||||
Bh1750.type = 1;
|
||||
break;
|
||||
Bh1750_sensors[Bh1750.count].address = Bh1750.addresses[i];
|
||||
if (Bh1750SetMTreg(Bh1750.count)) {
|
||||
I2cSetActiveFound(Bh1750_sensors[Bh1750.count].address, Bh1750.types);
|
||||
Bh1750.count++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Bh1750EverySecond(void)
|
||||
{
|
||||
// 1mS
|
||||
if (!Bh1750Read()) {
|
||||
AddLogMissed(Bh1750.types, Bh1750.valid);
|
||||
void Bh1750EverySecond(void) {
|
||||
for (uint32_t i = 0; i < Bh1750.count; i++) {
|
||||
// 1mS
|
||||
if (!Bh1750Read(i)) {
|
||||
// AddLogMissed(Bh1750.types, Bh1750.valid);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*********************************************************************************************\
|
||||
* Command Sensor10
|
||||
*
|
||||
* 0 - High resolution mode (default)
|
||||
* 1 - High resolution mode 2
|
||||
* 2 - Low resolution mode
|
||||
* 31..254 - Measurement Time value (not persistent, default is 69)
|
||||
* Commands
|
||||
\*********************************************************************************************/
|
||||
|
||||
bool Bh1750CommandSensor(void)
|
||||
{
|
||||
if (XdrvMailbox.data_len) {
|
||||
void CmndBh1750Resolution(void) {
|
||||
if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= Bh1750.count)) {
|
||||
if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= 2)) {
|
||||
Settings.SensorBits1.bh1750_resolution = XdrvMailbox.payload;
|
||||
Bh1750SetResolution();
|
||||
}
|
||||
else if ((XdrvMailbox.payload > 30) && (XdrvMailbox.payload < 255)) {
|
||||
Bh1750.mtreg = XdrvMailbox.payload;
|
||||
Bh1750SetMTreg();
|
||||
if (1 == XdrvMailbox.index) {
|
||||
Settings.SensorBits1.bh1750_1_resolution = XdrvMailbox.payload;
|
||||
} else {
|
||||
Settings.SensorBits1.bh1750_2_resolution = XdrvMailbox.payload;
|
||||
}
|
||||
Bh1750SetResolution(XdrvMailbox.index -1);
|
||||
}
|
||||
ResponseCmndIdxNumber(Bh1750Resolution(XdrvMailbox.index -1));
|
||||
}
|
||||
Response_P(PSTR("{\"" D_CMND_SENSOR "10\":{\"Resolution\":%d,\"MTime\":%d}}"), Settings.SensorBits1.bh1750_resolution, Bh1750.mtreg);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void Bh1750Show(bool json)
|
||||
{
|
||||
if (Bh1750.valid) {
|
||||
if (json) {
|
||||
ResponseAppend_P(JSON_SNS_ILLUMINANCE, Bh1750.types, Bh1750.illuminance);
|
||||
#ifdef USE_DOMOTICZ
|
||||
if (0 == tele_period) {
|
||||
DomoticzSensor(DZ_ILLUMINANCE, Bh1750.illuminance);
|
||||
void CmndBh1750MTime(void) {
|
||||
if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= Bh1750.count)) {
|
||||
if ((XdrvMailbox.payload > 30) && (XdrvMailbox.payload < 255)) {
|
||||
Bh1750_sensors[XdrvMailbox.index -1].mtreg = XdrvMailbox.payload;
|
||||
Bh1750SetMTreg(XdrvMailbox.index -1);
|
||||
}
|
||||
ResponseCmndIdxNumber(Bh1750_sensors[XdrvMailbox.index -1].mtreg);
|
||||
}
|
||||
}
|
||||
|
||||
/********************************************************************************************/
|
||||
|
||||
void Bh1750Show(bool json) {
|
||||
for (uint32_t sensor_index = 0; sensor_index < Bh1750.count; sensor_index++) {
|
||||
if (Bh1750_sensors[sensor_index].valid) {
|
||||
char sensor_name[10];
|
||||
strlcpy(sensor_name, Bh1750.types, sizeof(sensor_name));
|
||||
if (Bh1750.count > 1) {
|
||||
snprintf_P(sensor_name, sizeof(sensor_name), PSTR("%s%c%02X"), sensor_name, IndexSeparator(), Bh1750_sensors[sensor_index].address); // BH1750-23
|
||||
}
|
||||
|
||||
if (json) {
|
||||
ResponseAppend_P(JSON_SNS_ILLUMINANCE, sensor_name, Bh1750_sensors[sensor_index].illuminance);
|
||||
#ifdef USE_DOMOTICZ
|
||||
if ((0 == tele_period) && (0 == sensor_index)) {
|
||||
DomoticzSensor(DZ_ILLUMINANCE, Bh1750_sensors[sensor_index].illuminance);
|
||||
}
|
||||
#endif // USE_DOMOTICZ
|
||||
#ifdef USE_WEBSERVER
|
||||
} else {
|
||||
WSContentSend_PD(HTTP_SNS_ILLUMINANCE, Bh1750.types, Bh1750.illuminance);
|
||||
} else {
|
||||
WSContentSend_PD(HTTP_SNS_ILLUMINANCE, sensor_name, Bh1750_sensors[sensor_index].illuminance);
|
||||
#endif // USE_WEBSERVER
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -159,8 +193,7 @@ void Bh1750Show(bool json)
|
||||
* Interface
|
||||
\*********************************************************************************************/
|
||||
|
||||
bool Xsns10(uint8_t function)
|
||||
{
|
||||
bool Xsns10(uint8_t function) {
|
||||
if (!I2cEnabled(XI2C_11)) { return false; }
|
||||
|
||||
bool result = false;
|
||||
@ -168,15 +201,13 @@ bool Xsns10(uint8_t function)
|
||||
if (FUNC_INIT == function) {
|
||||
Bh1750Detect();
|
||||
}
|
||||
else if (Bh1750.type) {
|
||||
else if (Bh1750.count) {
|
||||
switch (function) {
|
||||
case FUNC_EVERY_SECOND:
|
||||
Bh1750EverySecond();
|
||||
break;
|
||||
case FUNC_COMMAND_SENSOR:
|
||||
if (XSNS_10 == XdrvMailbox.index) {
|
||||
result = Bh1750CommandSensor();
|
||||
}
|
||||
case FUNC_COMMAND:
|
||||
result = DecodeCommand(kBh1750Commands, Bh1750Command);
|
||||
break;
|
||||
case FUNC_JSON_APPEND:
|
||||
Bh1750Show(1);
|
||||
|
@ -89,7 +89,7 @@ void LM75ADShow(bool json)
|
||||
dtostrfd(t, Settings.flag2.temperature_resolution, temperature);
|
||||
|
||||
if (json) {
|
||||
ResponseAppend_P(PSTR(",\"LM75AD\":{\"" D_JSON_TEMPERATURE "\":%s}"), temperature);
|
||||
ResponseAppend_P(JSON_SNS_TEMP, "LM75AD", temperature);
|
||||
#ifdef USE_DOMOTICZ
|
||||
if (0 == tele_period) DomoticzSensor(DZ_TEMP, temperature);
|
||||
#endif // USE_DOMOTICZ
|
||||
|
@ -18,20 +18,27 @@
|
||||
*/
|
||||
|
||||
#ifdef USE_MAX31855
|
||||
/*********************************************************************************************\
|
||||
* MAX31855 and MAX6675 - Thermocouple
|
||||
*
|
||||
* SetOption94 0 - MAX31855
|
||||
* SetOption94 1 - MAX6675
|
||||
\*********************************************************************************************/
|
||||
|
||||
#define XSNS_39 39
|
||||
|
||||
bool initialized = false;
|
||||
const char kMax31855Types[] PROGMEM = "MAX31855|MAX6675";
|
||||
|
||||
struct MAX31855_ResultStruct{
|
||||
uint8_t ErrorCode; // Error Codes: 0 = No Error / 1 = TC open circuit / 2 = TC short to GND / 4 = TC short to VCC
|
||||
float ProbeTemperature; // Measured temperature of the 'hot' TC junction (probe temp)
|
||||
float ReferenceTemperature; // Measured temperature of the 'cold' TC junction (reference temp)
|
||||
bool max31855_initialized = false;
|
||||
|
||||
struct MAX31855_ResultStruct {
|
||||
uint8_t ErrorCode; // Error Codes: 0 = No Error / 1 = TC open circuit / 2 = TC short to GND / 4 = TC short to VCC
|
||||
float ProbeTemperature; // Measured temperature of the 'hot' TC junction (probe temp)
|
||||
float ReferenceTemperature; // Measured temperature of the 'cold' TC junction (reference temp)
|
||||
} MAX31855_Result;
|
||||
|
||||
void MAX31855_Init(void){
|
||||
if(initialized)
|
||||
return;
|
||||
void MAX31855_Init(void) {
|
||||
if (PinUsed(GPIO_MAX31855CS) && PinUsed(GPIO_MAX31855CLK) && PinUsed(GPIO_MAX31855DO)) {
|
||||
|
||||
// Set GPIO modes for SW-SPI
|
||||
pinMode(Pin(GPIO_MAX31855CS), OUTPUT);
|
||||
@ -42,106 +49,122 @@ void MAX31855_Init(void){
|
||||
digitalWrite(Pin(GPIO_MAX31855CS), HIGH);
|
||||
digitalWrite(Pin(GPIO_MAX31855CLK), LOW);
|
||||
|
||||
initialized = true;
|
||||
}
|
||||
|
||||
/*
|
||||
* MAX31855_GetResult(void)
|
||||
* Acquires the raw data via SPI, checks for MAX31855 errors and fills result structure
|
||||
*/
|
||||
void MAX31855_GetResult(void){
|
||||
int32_t RawData = MAX31855_ShiftIn(32);
|
||||
uint8_t probeerror = RawData & 0x7;
|
||||
|
||||
MAX31855_Result.ErrorCode = probeerror;
|
||||
MAX31855_Result.ReferenceTemperature = MAX31855_GetReferenceTemperature(RawData);
|
||||
if(probeerror)
|
||||
MAX31855_Result.ProbeTemperature = NAN; // Return NaN if MAX31855 reports an error
|
||||
else
|
||||
MAX31855_Result.ProbeTemperature = MAX31855_GetProbeTemperature(RawData);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* MAX31855_GetProbeTemperature(int32_t RawData)
|
||||
* Decodes and returns the temperature of TCs 'hot' junction from RawData
|
||||
*/
|
||||
float MAX31855_GetProbeTemperature(int32_t RawData){
|
||||
if(RawData & 0x80000000)
|
||||
RawData = (RawData >> 18) | 0xFFFFC000; // Negative value - Drop lower 18 bits and extend to negative number
|
||||
else
|
||||
RawData >>= 18; // Positiv value - Drop lower 18 bits
|
||||
|
||||
float result = (RawData * 0.25); // MAX31855 LSB resolution is 0.25°C for probe temperature
|
||||
|
||||
return ConvertTemp(result); // Check if we have to convert to Fahrenheit
|
||||
}
|
||||
|
||||
/*
|
||||
* MAX31855_GetReferenceTemperature(int32_t RawData)
|
||||
* Decodes and returns the temperature of TCs 'cold' junction from RawData
|
||||
*/
|
||||
float MAX31855_GetReferenceTemperature(int32_t RawData){
|
||||
if(RawData & 0x8000)
|
||||
RawData = (RawData >> 4) | 0xFFFFF000; // Negative value - Drop lower 4 bits and extend to negative number
|
||||
else
|
||||
RawData = (RawData >> 4) & 0x00000FFF; // Positiv value - Drop lower 4 bits and mask out remaining bits (probe temp, error bit, etc.)
|
||||
|
||||
float result = (RawData * 0.0625); // MAX31855 LSB resolution is 0.0625°C for reference temperature
|
||||
|
||||
return ConvertTemp(result); // Check if we have to convert to Fahrenheit
|
||||
max31855_initialized = true;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* MAX31855_ShiftIn(uint8_t Length)
|
||||
* Communicates with MAX31855 via SW-SPI and returns the raw data read from the chip
|
||||
*/
|
||||
int32_t MAX31855_ShiftIn(uint8_t Length){
|
||||
int32_t dataIn = 0;
|
||||
int32_t MAX31855_ShiftIn(uint8_t Length) {
|
||||
int32_t dataIn = 0;
|
||||
|
||||
digitalWrite(Pin(GPIO_MAX31855CS), LOW); // CS = LOW -> Start SPI communication
|
||||
delayMicroseconds(1); // CS fall to output enable = max. 100ns
|
||||
digitalWrite(Pin(GPIO_MAX31855CS), LOW); // CS = LOW -> Start SPI communication
|
||||
delayMicroseconds(1); // CS fall to output enable = max. 100ns
|
||||
|
||||
for (uint32_t i = 0; i < Length; i++)
|
||||
{
|
||||
digitalWrite(Pin(GPIO_MAX31855CLK), LOW);
|
||||
delayMicroseconds(1); // CLK pulse width low = min. 100ns / CLK fall to output valid = max. 40ns
|
||||
dataIn <<= 1;
|
||||
if(digitalRead(Pin(GPIO_MAX31855DO)))
|
||||
dataIn |= 1;
|
||||
digitalWrite(Pin(GPIO_MAX31855CLK), HIGH);
|
||||
delayMicroseconds(1); // CLK pulse width high = min. 100ns
|
||||
}
|
||||
|
||||
digitalWrite(Pin(GPIO_MAX31855CS), HIGH); // CS = HIGH -> End SPI communication
|
||||
for (uint32_t i = 0; i < Length; i++) {
|
||||
digitalWrite(Pin(GPIO_MAX31855CLK), LOW);
|
||||
return dataIn;
|
||||
delayMicroseconds(1); // CLK pulse width low = min. 100ns / CLK fall to output valid = max. 40ns
|
||||
dataIn <<= 1;
|
||||
if (digitalRead(Pin(GPIO_MAX31855DO))) {
|
||||
dataIn |= 1;
|
||||
}
|
||||
digitalWrite(Pin(GPIO_MAX31855CLK), HIGH);
|
||||
delayMicroseconds(1); // CLK pulse width high = min. 100ns
|
||||
}
|
||||
|
||||
digitalWrite(Pin(GPIO_MAX31855CS), HIGH); // CS = HIGH -> End SPI communication
|
||||
digitalWrite(Pin(GPIO_MAX31855CLK), LOW);
|
||||
return dataIn;
|
||||
}
|
||||
|
||||
void MAX31855_Show(bool Json){
|
||||
char probetemp[33];
|
||||
char referencetemp[33];
|
||||
dtostrfd(MAX31855_Result.ProbeTemperature, Settings.flag2.temperature_resolution, probetemp);
|
||||
dtostrfd(MAX31855_Result.ReferenceTemperature, Settings.flag2.temperature_resolution, referencetemp);
|
||||
/*
|
||||
* MAX31855_GetProbeTemperature(int32_t RawData)
|
||||
* Decodes and returns the temperature of TCs 'hot' junction from RawData
|
||||
*/
|
||||
float MAX31855_GetProbeTemperature(int32_t RawData) {
|
||||
if (RawData & 0x80000000) {
|
||||
RawData = (RawData >> 18) | 0xFFFFC000; // Negative value - Drop lower 18 bits and extend to negative number
|
||||
} else {
|
||||
RawData >>= 18; // Positiv value - Drop lower 18 bits
|
||||
}
|
||||
float result = (RawData * 0.25); // MAX31855 LSB resolution is 0.25°C for probe temperature
|
||||
|
||||
if(Json){
|
||||
ResponseAppend_P(PSTR(",\"MAX31855\":{\"" D_JSON_PROBETEMPERATURE "\":%s,\"" D_JSON_REFERENCETEMPERATURE "\":%s,\"" D_JSON_ERROR "\":%d}"), \
|
||||
probetemp, referencetemp, MAX31855_Result.ErrorCode);
|
||||
return ConvertTemp(result); // Check if we have to convert to Fahrenheit
|
||||
}
|
||||
|
||||
/*
|
||||
* MAX31855_GetReferenceTemperature(int32_t RawData)
|
||||
* Decodes and returns the temperature of TCs 'cold' junction from RawData
|
||||
*/
|
||||
float MAX31855_GetReferenceTemperature(int32_t RawData) {
|
||||
if (RawData & 0x8000) {
|
||||
RawData = (RawData >> 4) | 0xFFFFF000; // Negative value - Drop lower 4 bits and extend to negative number
|
||||
} else {
|
||||
RawData = (RawData >> 4) & 0x00000FFF; // Positiv value - Drop lower 4 bits and mask out remaining bits (probe temp, error bit, etc.)
|
||||
}
|
||||
float result = (RawData * 0.0625); // MAX31855 LSB resolution is 0.0625°C for reference temperature
|
||||
|
||||
return ConvertTemp(result); // Check if we have to convert to Fahrenheit
|
||||
}
|
||||
|
||||
/*
|
||||
* MAX31855_GetResult(void)
|
||||
* Acquires the raw data via SPI, checks for MAX31855 errors and fills result structure
|
||||
*/
|
||||
void MAX31855_GetResult(void) {
|
||||
if (Settings.flag4.max6675) { // SetOption94 - Implement simpler MAX6675 protocol instead of MAX31855
|
||||
int32_t RawData = MAX31855_ShiftIn(16);
|
||||
int32_t temp = (RawData >> 3) & ((1 << 12) - 1);
|
||||
|
||||
/* Occasionally the sensor returns 0xfff, consider it an error */
|
||||
if (temp == ((1 << 12) - 1)) { return; }
|
||||
|
||||
MAX31855_Result.ErrorCode = 0;
|
||||
MAX31855_Result.ReferenceTemperature = NAN;
|
||||
MAX31855_Result.ProbeTemperature = ConvertTemp(0.25 * temp);
|
||||
} else {
|
||||
int32_t RawData = MAX31855_ShiftIn(32);
|
||||
uint8_t probeerror = RawData & 0x7;
|
||||
|
||||
MAX31855_Result.ErrorCode = probeerror;
|
||||
MAX31855_Result.ReferenceTemperature = MAX31855_GetReferenceTemperature(RawData);
|
||||
if (probeerror) {
|
||||
MAX31855_Result.ProbeTemperature = NAN; // Return NaN if MAX31855 reports an error
|
||||
} else {
|
||||
MAX31855_Result.ProbeTemperature = MAX31855_GetProbeTemperature(RawData);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void MAX31855_Show(bool Json) {
|
||||
char probetemp[33];
|
||||
char referencetemp[33];
|
||||
dtostrfd(MAX31855_Result.ProbeTemperature, Settings.flag2.temperature_resolution, probetemp);
|
||||
dtostrfd(MAX31855_Result.ReferenceTemperature, Settings.flag2.temperature_resolution, referencetemp);
|
||||
|
||||
char sensor_name[10];
|
||||
GetTextIndexed(sensor_name, sizeof(sensor_name), Settings.flag4.max6675, kMax31855Types);
|
||||
|
||||
if (Json) {
|
||||
ResponseAppend_P(PSTR(",\"%s\":{\"" D_JSON_PROBETEMPERATURE "\":%s,\"" D_JSON_REFERENCETEMPERATURE "\":%s,\"" D_JSON_ERROR "\":%d}"), \
|
||||
sensor_name, probetemp, referencetemp, MAX31855_Result.ErrorCode);
|
||||
#ifdef USE_DOMOTICZ
|
||||
if (0 == tele_period) {
|
||||
DomoticzSensor(DZ_TEMP, probetemp);
|
||||
}
|
||||
if (0 == tele_period) {
|
||||
DomoticzSensor(DZ_TEMP, probetemp);
|
||||
}
|
||||
#endif // USE_DOMOTICZ
|
||||
#ifdef USE_KNX
|
||||
if (0 == tele_period) {
|
||||
KnxSensor(KNX_TEMPERATURE, MAX31855_Result.ProbeTemperature);
|
||||
}
|
||||
#endif // USE_KNX
|
||||
} else {
|
||||
#ifdef USE_WEBSERVER
|
||||
WSContentSend_PD(HTTP_SNS_TEMP, "MAX31855", probetemp, TempUnit());
|
||||
#endif // USE_WEBSERVER
|
||||
if (0 == tele_period) {
|
||||
KnxSensor(KNX_TEMPERATURE, MAX31855_Result.ProbeTemperature);
|
||||
}
|
||||
#endif // USE_KNX
|
||||
#ifdef USE_WEBSERVER
|
||||
} else {
|
||||
WSContentSend_PD(HTTP_SNS_TEMP, sensor_name, probetemp, TempUnit());
|
||||
#endif // USE_WEBSERVER
|
||||
}
|
||||
}
|
||||
|
||||
/*********************************************************************************************\
|
||||
@ -151,12 +174,12 @@ void MAX31855_Show(bool Json){
|
||||
bool Xsns39(uint8_t function)
|
||||
{
|
||||
bool result = false;
|
||||
if(PinUsed(GPIO_MAX31855CS) && PinUsed(GPIO_MAX31855CLK) && PinUsed(GPIO_MAX31855DO)){
|
||||
|
||||
if (FUNC_INIT == function) {
|
||||
MAX31855_Init();
|
||||
}
|
||||
else if (max31855_initialized) {
|
||||
switch (function) {
|
||||
case FUNC_INIT:
|
||||
MAX31855_Init();
|
||||
break;
|
||||
case FUNC_EVERY_SECOND:
|
||||
MAX31855_GetResult();
|
||||
break;
|
||||
|
10
tasmota/xsns_52_ibeacon.ino
Normal file → Executable file
10
tasmota/xsns_52_ibeacon.ino
Normal file → Executable file
@ -25,6 +25,8 @@
|
||||
|
||||
#include <TasmotaSerial.h>
|
||||
|
||||
#define TMSBSIZ 256
|
||||
|
||||
#define HM17_BAUDRATE 9600
|
||||
|
||||
#define IBEACON_DEBUG
|
||||
@ -96,7 +98,7 @@ void IBEACON_Init() {
|
||||
|
||||
// actually doesnt work reliably with software serial
|
||||
if (PinUsed(GPIO_IBEACON_RX) && PinUsed(GPIO_IBEACON_TX)) {
|
||||
IBEACON_Serial = new TasmotaSerial(Pin(GPIO_IBEACON_RX), Pin(GPIO_IBEACON_TX),1);
|
||||
IBEACON_Serial = new TasmotaSerial(Pin(GPIO_IBEACON_RX), Pin(GPIO_IBEACON_TX),1,0,TMSBSIZ);
|
||||
if (IBEACON_Serial->begin(HM17_BAUDRATE)) {
|
||||
if (IBEACON_Serial->hardwareSerial()) {
|
||||
ClaimSerial();
|
||||
@ -144,7 +146,7 @@ void hm17_every_second(void) {
|
||||
void hm17_sbclr(void) {
|
||||
memset(hm17_sbuffer,0,HM17_BSIZ);
|
||||
hm17_sindex=0;
|
||||
IBEACON_Serial->flush();
|
||||
//IBEACON_Serial->flush();
|
||||
}
|
||||
|
||||
void hm17_sendcmd(uint8_t cmd) {
|
||||
@ -405,7 +407,7 @@ hm17_v110:
|
||||
}
|
||||
} else {
|
||||
#ifdef IBEACON_DEBUG
|
||||
if (hm17_debug) AddLog_P2(LOG_LEVEL_INFO, PSTR(">>%s"),&hm17_sbuffer[8]);
|
||||
if (hm17_debug) AddLog_P2(LOG_LEVEL_INFO, PSTR(">->%s"),&hm17_sbuffer[8]);
|
||||
#endif
|
||||
}
|
||||
break;
|
||||
@ -517,7 +519,7 @@ bool xsns52_cmd(void) {
|
||||
#ifdef IBEACON_DEBUG
|
||||
else if (*cp=='d') {
|
||||
cp++;
|
||||
if (*cp) hm17_debug=atoi(cp);
|
||||
hm17_debug=atoi(cp);
|
||||
Response_P(S_JSON_IBEACON, XSNS_52,"debug",hm17_debug);
|
||||
}
|
||||
#endif
|
||||
|
@ -49,6 +49,8 @@
|
||||
#define SPECIAL_SS
|
||||
#endif
|
||||
|
||||
#define TMSBSIZ 256
|
||||
|
||||
// addresses a bug in meter DWS74
|
||||
//#define DWS74_BUG
|
||||
|
||||
@ -2144,9 +2146,9 @@ init10:
|
||||
// serial input, init
|
||||
#ifdef SPECIAL_SS
|
||||
if (meter_desc_p[meters].type=='m' || meter_desc_p[meters].type=='M' || meter_desc_p[meters].type=='p') {
|
||||
meter_ss[meters] = new TasmotaSerial(meter_desc_p[meters].srcpin,meter_desc_p[meters].trxpin,1);
|
||||
meter_ss[meters] = new TasmotaSerial(meter_desc_p[meters].srcpin,meter_desc_p[meters].trxpin,1,0,TMSBSIZ);
|
||||
} else {
|
||||
meter_ss[meters] = new TasmotaSerial(meter_desc_p[meters].srcpin,meter_desc_p[meters].trxpin,1,1);
|
||||
meter_ss[meters] = new TasmotaSerial(meter_desc_p[meters].srcpin,meter_desc_p[meters].trxpin,1,1,TMSBSIZ);
|
||||
}
|
||||
#else
|
||||
#ifdef ESP32
|
||||
@ -2154,8 +2156,9 @@ init10:
|
||||
if (uart_index==0) { ClaimSerial(); }
|
||||
uart_index--;
|
||||
if (uart_index<0) uart_index=0;
|
||||
meter_ss[meters]->setRxBufferSize(TMSBSIZ);
|
||||
#else
|
||||
meter_ss[meters] = new TasmotaSerial(meter_desc_p[meters].srcpin,meter_desc_p[meters].trxpin,1);
|
||||
meter_ss[meters] = new TasmotaSerial(meter_desc_p[meters].srcpin,meter_desc_p[meters].trxpin,1,0,TMSBSIZ);
|
||||
#endif
|
||||
#endif
|
||||
|
||||
@ -2188,6 +2191,15 @@ uint32_t SML_SetBaud(uint32_t meter, uint32_t br) {
|
||||
if (meter<1 || meter>meters_used) return 0;
|
||||
meter--;
|
||||
if (!meter_ss[meter]) return 0;
|
||||
|
||||
#ifdef ESP32
|
||||
meter_ss[meter]->flush();
|
||||
if (meter_desc_p[meter].type=='M') {
|
||||
meter_ss[meter]->begin(br,SERIAL_8E1,meter_desc_p[meter].srcpin,meter_desc_p[meter].trxpin);
|
||||
} else {
|
||||
meter_ss[meter]->begin(br,SERIAL_8N1,meter_desc_p[meter].srcpin,meter_desc_p[meter].trxpin);
|
||||
}
|
||||
#else
|
||||
if (meter_ss[meter]->begin(br)) {
|
||||
meter_ss[meter]->flush();
|
||||
}
|
||||
@ -2196,6 +2208,7 @@ uint32_t SML_SetBaud(uint32_t meter, uint32_t br) {
|
||||
Serial.begin(br, SERIAL_8E1);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
@ -184,7 +184,7 @@ void DS1624Show(bool json)
|
||||
|
||||
dtostrfd(ds1624_sns[i].value, Settings.flag2.temperature_resolution, temperature);
|
||||
if (json) {
|
||||
ResponseAppend_P(PSTR(",\"%s\":{\"" D_JSON_TEMPERATURE "\":%s}"), ds1624_sns[i].name, temperature);
|
||||
ResponseAppend_P(JSON_SNS_TEMP, ds1624_sns[i].name, temperature);
|
||||
if ((0 == tele_period) && once) {
|
||||
#ifdef USE_DOMOTICZ
|
||||
DomoticzSensor(DZ_TEMP, temperature);
|
||||
|
@ -53,6 +53,8 @@ struct {
|
||||
uint32_t willSetTime:1;
|
||||
uint32_t shallReadBatt:1;
|
||||
uint32_t willReadBatt:1;
|
||||
uint32_t shallSetUnit:1;
|
||||
uint32_t willSetUnit:1;
|
||||
} mode;
|
||||
struct {
|
||||
uint8_t sensor; // points to to the number 0...255
|
||||
@ -152,7 +154,7 @@ BLEScanResults MI32foundDevices;
|
||||
|
||||
const char S_JSON_MI32_COMMAND_NVALUE[] PROGMEM = "{\"" D_CMND_MI32 "%s\":%d}";
|
||||
const char S_JSON_MI32_COMMAND[] PROGMEM = "{\"" D_CMND_MI32 "%s%s\"}";
|
||||
const char kMI32_Commands[] PROGMEM = "Period|Time|Page|Battery";
|
||||
const char kMI32_Commands[] PROGMEM = "Period|Time|Page|Battery|Unit";
|
||||
|
||||
#define FLORA 1
|
||||
#define MJ_HT_V1 2
|
||||
@ -185,7 +187,8 @@ enum MI32_Commands { // commands useable in console or rules
|
||||
CMND_MI32_PERIOD, // set period like TELE-period in seconds between read-cycles
|
||||
CMND_MI32_TIME, // set LYWSD02-Time from ESP8266-time
|
||||
CMND_MI32_PAGE, // sensor entries per web page, which will be shown alternated
|
||||
CMND_MI32_BATTERY // read all battery levels
|
||||
CMND_MI32_BATTERY, // read all battery levels
|
||||
CMND_MI32_UNIT // toggles the displayed unit between C/F (LYWSD02)
|
||||
};
|
||||
|
||||
enum MI32_TASK {
|
||||
@ -193,6 +196,7 @@ enum MI32_TASK {
|
||||
MI32_TASK_CONN = 1,
|
||||
MI32_TASK_TIME = 2,
|
||||
MI32_TASK_BATT = 3,
|
||||
MI32_TASK_UNIT = 4,
|
||||
};
|
||||
|
||||
/*********************************************************************************************\
|
||||
@ -394,6 +398,10 @@ void MI32StartTask(uint32_t task){
|
||||
if (MI32.mode.willReadBatt == 1) return;
|
||||
MI32StartBatteryTask();
|
||||
break;
|
||||
case MI32_TASK_UNIT:
|
||||
if (MI32.mode.shallSetUnit == 0) return;
|
||||
MI32StartUnitTask();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@ -616,6 +624,72 @@ void MI32TimeTask(void *pvParameters){
|
||||
vTaskDelete( NULL );
|
||||
}
|
||||
|
||||
void MI32StartUnitTask(){
|
||||
MI32.mode.willConnect = 1;
|
||||
xTaskCreatePinnedToCore(
|
||||
MI32UnitTask, /* Function to implement the task */
|
||||
"MI32UnitTask", /* Name of the task */
|
||||
8912, /* Stack size in words */
|
||||
NULL, /* Task input parameter */
|
||||
15, /* Priority of the task */
|
||||
NULL, /* Task handle. */
|
||||
0); /* Core where the task should run */
|
||||
// AddLog_P2(LOG_LEVEL_DEBUG,PSTR("%s: Start unit set"),D_CMND_MI32);
|
||||
// AddLog_P2(LOG_LEVEL_DEBUG,PSTR("%s: with sensor: %u"),D_CMND_MI32, MI32.state.sensor);
|
||||
}
|
||||
|
||||
void MI32UnitTask(void *pvParameters){
|
||||
if (MIBLEsensors[MI32.state.sensor].type != LYWSD02) {
|
||||
MI32.mode.shallSetUnit = 0;
|
||||
vTaskDelete( NULL );
|
||||
}
|
||||
|
||||
if(MI32ConnectActiveSensor()){
|
||||
uint32_t timer = 0;
|
||||
while (MI32.mode.connected == 0){
|
||||
if (timer>1000){
|
||||
break;
|
||||
}
|
||||
timer++;
|
||||
vTaskDelay(10/ portTICK_PERIOD_MS);
|
||||
}
|
||||
|
||||
NimBLERemoteService* pSvc = nullptr;
|
||||
NimBLERemoteCharacteristic* pChr = nullptr;
|
||||
static BLEUUID serviceUUID("EBE0CCB0-7A0A-4B0C-8A1A-6FF2997DA3A6");
|
||||
static BLEUUID charUUID("EBE0CCBE-7A0A-4B0C-8A1A-6FF2997DA3A6");
|
||||
pSvc = MI32Client->getService(serviceUUID);
|
||||
if(pSvc) {
|
||||
pChr = pSvc->getCharacteristic(charUUID);
|
||||
}
|
||||
|
||||
if(pChr->canRead()){
|
||||
uint8_t curUnit;
|
||||
const char *buf = pChr->readValue().c_str();
|
||||
if( buf[0] != 0 && buf[0]<101 ){
|
||||
curUnit = buf[0];
|
||||
}
|
||||
|
||||
if(pChr->canWrite()) {
|
||||
curUnit = curUnit == 0x01?0xFF:0x01; // C/F
|
||||
|
||||
if(!pChr->writeValue(&curUnit,sizeof(curUnit),true)) { // true is important !
|
||||
MI32.mode.willConnect = 0;
|
||||
MI32Client->disconnect();
|
||||
}
|
||||
else {
|
||||
MI32.mode.shallSetUnit = 0;
|
||||
MI32.mode.willSetUnit = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
MI32Client->disconnect();
|
||||
}
|
||||
vTaskDelay(500/ portTICK_PERIOD_MS);
|
||||
MI32.mode.connected = 0;
|
||||
vTaskDelete( NULL );
|
||||
}
|
||||
|
||||
void MI32StartBatteryTask(){
|
||||
if (MI32.mode.connected) return;
|
||||
MI32.mode.willReadBatt = 1;
|
||||
@ -964,6 +1038,15 @@ void MI32EverySecond(bool restart){
|
||||
}
|
||||
}
|
||||
|
||||
if (MI32.mode.shallSetUnit) {
|
||||
MI32.mode.canScan = 0;
|
||||
MI32.mode.canConnect = 0;
|
||||
if (MI32.mode.willSetUnit == 0){
|
||||
MI32.mode.willSetUnit = 1;
|
||||
MI32StartTask(MI32_TASK_UNIT);
|
||||
}
|
||||
}
|
||||
|
||||
if (MI32.mode.willReadBatt) return;
|
||||
|
||||
if (_counter>MI32.period) {
|
||||
@ -1057,6 +1140,21 @@ bool MI32Cmd(void) {
|
||||
}
|
||||
Response_P(S_JSON_MI32_COMMAND_NVALUE, command, XdrvMailbox.payload);
|
||||
break;
|
||||
case CMND_MI32_UNIT:
|
||||
if (XdrvMailbox.data_len > 0) {
|
||||
if(MIBLEsensors.size()>XdrvMailbox.payload){
|
||||
if(MIBLEsensors[XdrvMailbox.payload].type == LYWSD02){
|
||||
AddLog_P2(LOG_LEVEL_DEBUG,PSTR("%s: will set Unit"),D_CMND_MI32);
|
||||
MI32.state.sensor = XdrvMailbox.payload;
|
||||
MI32.mode.canScan = 0;
|
||||
MI32.mode.canConnect = 0;
|
||||
MI32.mode.shallSetUnit = 1;
|
||||
MI32.mode.willSetUnit = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
Response_P(S_JSON_MI32_COMMAND_NVALUE, command, XdrvMailbox.payload);
|
||||
break;
|
||||
case CMND_MI32_PAGE:
|
||||
if (XdrvMailbox.data_len > 0) {
|
||||
if (XdrvMailbox.payload == 0) XdrvMailbox.payload = MI32.perPage; // ignore 0
|
||||
|
@ -51,8 +51,6 @@
|
||||
#define INDOORS 0x24
|
||||
#define OUTDOORS 0x1C
|
||||
|
||||
|
||||
|
||||
// Global
|
||||
const char HTTP_SNS_UNIT_KILOMETER[] PROGMEM = D_UNIT_KILOMETER;
|
||||
// Http
|
||||
@ -78,7 +76,7 @@ const char HTTP_SNS_AS3935_INTNOEV[] PROGMEM = "{s}%s: " D_AS3935_INTNOEV "{e}";
|
||||
const char HTTP_SNS_AS3935_MSG[] PROGMEM = "{s}%s: " D_AS3935_LIGHT " " D_AS3935_APRX " %d " D_UNIT_KILOMETER " " D_AS3935_AWAY "{e}";
|
||||
const char* const HTTP_SNS_AS3935_TABLE_1[] PROGMEM = { HTTP_SNS_AS3935_EMPTY, HTTP_SNS_AS3935_MSG, HTTP_SNS_AS3935_OUT, HTTP_SNS_AS3935_NOT, HTTP_SNS_AS3935_ABOVE, HTTP_SNS_AS3935_NOISE, HTTP_SNS_AS3935_DISTURB, HTTP_SNS_AS3935_INTNOEV };
|
||||
// Json
|
||||
const char JSON_SNS_AS3935_EVENTS[] PROGMEM = ",\"%s\":{\"" D_JSON_EVENT "\":%d,\"" D_JSON_DISTANCE "\":%d,\"" D_JSON_ENERGY "\":%u}";
|
||||
const char JSON_SNS_AS3935_EVENTS[] PROGMEM = ",\"%s\":{\"" D_JSON_EVENT "\":%d,\"" D_JSON_DISTANCE "\":%d,\"" D_JSON_ENERGY "\":%u,\"" D_JSON_STAGE "\":%d}";
|
||||
// Json Command
|
||||
const char* const S_JSON_AS3935_COMMAND_ONOFF[] PROGMEM = {"\"" D_AS3935_OFF "\"","\"" D_AS3935_ON"\""};
|
||||
const char* const S_JSON_AS3935_COMMAND_GAIN[] PROGMEM = {"\"" D_AS3935_INDOORS "\"", "\"" D_AS3935_OUTDOORS "\""};
|
||||
@ -465,8 +463,15 @@ bool AS3935SetDefault() {
|
||||
|
||||
void AS3935InitSettings() {
|
||||
if(Settings.as3935_functions.nf_autotune){
|
||||
AS3935SetGain(INDOORS);
|
||||
AS3935SetNoiseFloor(0);
|
||||
if(Settings.as3935_parameter.nf_autotune_min) {
|
||||
if (Settings.as3935_parameter.nf_autotune_min > 7) {
|
||||
AS3935SetGain(OUTDOORS);
|
||||
AS3935SetNoiseFloor(Settings.as3935_parameter.nf_autotune_min - 8);
|
||||
} else {
|
||||
AS3935SetGain(INDOORS);
|
||||
AS3935SetNoiseFloor(Settings.as3935_parameter.nf_autotune_min);
|
||||
}
|
||||
}
|
||||
}
|
||||
I2cWrite8(AS3935_ADDR, 0x00, Settings.as3935_sensor_cfg[0]);
|
||||
I2cWrite8(AS3935_ADDR, 0x01, Settings.as3935_sensor_cfg[1]);
|
||||
@ -746,8 +751,10 @@ bool AS3935Cmd(void) {
|
||||
void AH3935Show(bool json)
|
||||
{
|
||||
if (json) {
|
||||
ResponseAppend_P(JSON_SNS_AS3935_EVENTS, D_SENSOR_AS3935, as3935_sensor.mqtt_irq, as3935_sensor.distance, as3935_sensor.intensity );
|
||||
|
||||
uint16_t vrms;
|
||||
uint8_t stage;
|
||||
AS3935CalcVrmsLevel(vrms, stage);
|
||||
ResponseAppend_P(JSON_SNS_AS3935_EVENTS, D_SENSOR_AS3935, as3935_sensor.mqtt_irq, as3935_sensor.distance, as3935_sensor.intensity, stage);
|
||||
#ifdef USE_WEBSERVER
|
||||
} else {
|
||||
uint8_t gain = AS3935GetGainInt();
|
||||
|
@ -83,7 +83,7 @@ void WindMeterUpdateSpeed(void)
|
||||
if (time_diff > Settings.windmeter_pulse_debounce * 1000) {
|
||||
WindMeter.counter_time = time;
|
||||
WindMeter.counter++;
|
||||
AddLog_P2(LOG_LEVEL_DEBUG, PSTR("WMET: Counter %d"), WindMeter.counter);
|
||||
// AddLog_P2(LOG_LEVEL_DEBUG, PSTR("WMET: Counter %d"), WindMeter.counter);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -37,12 +37,27 @@ Adafruit_VEML7700 veml7700 = Adafruit_VEML7700(); //create object copy
|
||||
const char HTTP_SNS_WHITE[] PROGMEM = "{s}%s " D_WHITE_CONTENT "{m}%d {e}";
|
||||
const char JSON_SNS_VEML7700[] PROGMEM = ",\"%s\":{\"" D_JSON_ILLUMINANCE "\":%d,\"" D_JSON_WHITE_CONTENT "\":%d}";
|
||||
|
||||
#define D_CMND_VEML7700_PWR "power"
|
||||
#define D_CMND_VEML7700_GAIN "gain"
|
||||
#define D_CMND_VEML7700_INTTIME "inttime"
|
||||
|
||||
const char S_JSON_VEML7700_COMMAND_NVALUE[] PROGMEM = "{\"" D_NAME_VEML7700 "\":{\"%s\":%d}}";
|
||||
const char kVEML7700_Commands[] PROGMEM = D_CMND_VEML7700_PWR "|" D_CMND_VEML7700_GAIN "|" D_CMND_VEML7700_INTTIME;
|
||||
|
||||
enum VEML7700_Commands { // commands for Console
|
||||
CMND_VEML7700_PWR,
|
||||
CMND_VEML7700_GAIN,
|
||||
CMND_VEML7700_SET_IT,
|
||||
};
|
||||
|
||||
struct VEML7700STRUCT
|
||||
{
|
||||
char types[9] = D_NAME_VEML7700;
|
||||
uint8_t address = VEML7700_I2CADDR_DEFAULT;
|
||||
uint16_t lux = 0;
|
||||
uint16_t white = 0;
|
||||
//uint16_t lux = 0;
|
||||
//uint16_t white = 0;
|
||||
uint16_t lux_normalized = 0;
|
||||
uint16_t white_normalized = 0;
|
||||
} veml7700_sensor;
|
||||
|
||||
uint8_t veml7700_active = 0;
|
||||
@ -50,34 +65,102 @@ uint8_t veml7700_active = 0;
|
||||
/********************************************************************************************/
|
||||
|
||||
void VEML7700Detect(void) {
|
||||
if (I2cActive(veml7700_sensor.address)) return;
|
||||
if (!I2cSetDevice(veml7700_sensor.address)) return;
|
||||
if (veml7700.begin()) {
|
||||
I2cSetActiveFound(veml7700_sensor.address, veml7700_sensor.types);
|
||||
veml7700_active = 1;
|
||||
}
|
||||
}
|
||||
|
||||
uint16_t VEML7700TranslateItMs (uint8_t ittime){
|
||||
switch (ittime) {
|
||||
case 0: return 100;
|
||||
case 1: return 200;
|
||||
case 2: return 400;
|
||||
case 3: return 800;
|
||||
case 8: return 50;
|
||||
case 12: return 25;
|
||||
default: return 0xFFFF;
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t VEML7700TranslateItInt (uint16_t ittimems){
|
||||
switch (ittimems) {
|
||||
case 100: return 0;
|
||||
case 200: return 1;
|
||||
case 400: return 2;
|
||||
case 800: return 3;
|
||||
case 50: return 8;
|
||||
case 25: return 12;
|
||||
default: return 0xFF;
|
||||
}
|
||||
}
|
||||
|
||||
void VEML7700EverySecond(void) {
|
||||
veml7700_sensor.lux = (uint16_t) veml7700.readLux();
|
||||
veml7700_sensor.white = (uint16_t) veml7700.readWhite();
|
||||
veml7700_sensor.lux_normalized = (uint16_t) veml7700.readLuxNormalized();
|
||||
veml7700_sensor.white_normalized = (uint16_t) veml7700.readWhiteNormalized();
|
||||
//veml7700_sensor.lux = (uint16_t) veml7700.readLux();
|
||||
//veml7700_sensor.white = (uint16_t) veml7700.readWhite();
|
||||
}
|
||||
|
||||
void VEML7700Show(bool json)
|
||||
{
|
||||
if (json) {
|
||||
ResponseAppend_P(JSON_SNS_VEML7700, D_NAME_VEML7700, veml7700_sensor.lux, veml7700_sensor.white);
|
||||
ResponseAppend_P(JSON_SNS_VEML7700, D_NAME_VEML7700, veml7700_sensor.lux_normalized, veml7700_sensor.white_normalized);
|
||||
|
||||
#ifdef USE_DOMOTICZ
|
||||
if (0 == tele_period) DomoticzSensor(DZ_ILLUMINANCE, veml7700_sensor.lux);
|
||||
if (0 == tele_period) DomoticzSensor(DZ_ILLUMINANCE, veml7700_sensor.lux_normalized);
|
||||
#endif // USE_DOMOTICZ
|
||||
#ifdef USE_WEBSERVER
|
||||
} else {
|
||||
WSContentSend_PD(HTTP_SNS_ILLUMINANCE, D_NAME_VEML7700, veml7700_sensor.lux);
|
||||
WSContentSend_PD(HTTP_SNS_WHITE, D_NAME_VEML7700, veml7700_sensor.white);
|
||||
WSContentSend_PD(HTTP_SNS_ILLUMINANCE, D_NAME_VEML7700, veml7700_sensor.lux_normalized);
|
||||
WSContentSend_PD(HTTP_SNS_WHITE, D_NAME_VEML7700, veml7700_sensor.white_normalized);
|
||||
#endif // USE_WEBSERVER
|
||||
}
|
||||
}
|
||||
|
||||
bool VEML7700Cmd(void) {
|
||||
char command[CMDSZ];
|
||||
uint8_t name_len = strlen(D_NAME_VEML7700);
|
||||
if (!strncasecmp_P(XdrvMailbox.topic, PSTR(D_NAME_VEML7700), name_len)) {
|
||||
uint32_t command_code = GetCommandCode(command, sizeof(command), XdrvMailbox.topic + name_len, kVEML7700_Commands);
|
||||
switch (command_code) {
|
||||
case CMND_VEML7700_PWR:
|
||||
if (XdrvMailbox.data_len) {
|
||||
if (2 >= XdrvMailbox.payload) {
|
||||
veml7700.enable(XdrvMailbox.payload);
|
||||
}
|
||||
}
|
||||
Response_P(S_JSON_VEML7700_COMMAND_NVALUE, command, veml7700.enabled());
|
||||
break;
|
||||
case CMND_VEML7700_GAIN:
|
||||
if (XdrvMailbox.data_len) {
|
||||
if (4 >= XdrvMailbox.payload) {
|
||||
veml7700.setGain(XdrvMailbox.payload);
|
||||
}
|
||||
}
|
||||
Response_P(S_JSON_VEML7700_COMMAND_NVALUE, command, veml7700.getGain());
|
||||
break;
|
||||
case CMND_VEML7700_SET_IT: {
|
||||
if (XdrvMailbox.data_len) {
|
||||
uint8_t data = VEML7700TranslateItInt(XdrvMailbox.payload);
|
||||
if (0xFF != data) {
|
||||
veml7700.setIntegrationTime(data);
|
||||
}
|
||||
}
|
||||
uint16_t dataret = VEML7700TranslateItMs(veml7700.getIntegrationTime());
|
||||
Response_P(S_JSON_VEML7700_COMMAND_NVALUE, command, dataret);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
/*********************************************************************************************\
|
||||
* Interface
|
||||
\*********************************************************************************************/
|
||||
@ -97,7 +180,7 @@ bool Xsns71(uint8_t function)
|
||||
VEML7700EverySecond();
|
||||
break;
|
||||
case FUNC_COMMAND:
|
||||
//result = VEML7700Cmd();
|
||||
result = VEML7700Cmd();
|
||||
break;
|
||||
case FUNC_JSON_APPEND:
|
||||
VEML7700Show(1);
|
||||
|
136
tasmota/xsns_72_mcp9808.ino
Normal file
136
tasmota/xsns_72_mcp9808.ino
Normal file
@ -0,0 +1,136 @@
|
||||
/*
|
||||
xsns_72_mcp9808 - MCP9808 I2C temperature sensor support for Tasmota
|
||||
|
||||
Copyright (C) 2020 Martin Wagner and Theo Arends
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
*/
|
||||
|
||||
#ifdef USE_I2C
|
||||
#ifdef USE_MCP9808
|
||||
/*********************************************************************************************\
|
||||
* MCP9808 - Temperature Sensor
|
||||
*
|
||||
* I2C Address: 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F
|
||||
*
|
||||
\*********************************************************************************************/
|
||||
|
||||
#define XSNS_72 72
|
||||
#define XI2C_51 51 // See I2CDEVICES.md
|
||||
|
||||
#include "Adafruit_MCP9808.h"
|
||||
Adafruit_MCP9808 mcp9808 = Adafruit_MCP9808(); // create object copy
|
||||
|
||||
#define MCP9808_MAX_SENSORS 8
|
||||
#define MCP9808_START_ADDRESS 0x18
|
||||
|
||||
struct {
|
||||
char types[9] = "MCP9808";
|
||||
uint8_t count = 0;
|
||||
} mcp9808_cfg;
|
||||
|
||||
struct {
|
||||
float temperature = NAN;
|
||||
uint8_t address;
|
||||
} mcp9808_sensors[MCP9808_MAX_SENSORS];
|
||||
|
||||
/********************************************************************************************/
|
||||
|
||||
float MCP9808Read(uint8_t addr) {
|
||||
float t = mcp9808.readTempC(addr);
|
||||
return t;
|
||||
}
|
||||
|
||||
void MCP9808Detect(void) {
|
||||
for (uint8_t i = 0; i < MCP9808_MAX_SENSORS; i++) {
|
||||
if (!I2cSetDevice(MCP9808_START_ADDRESS + i)) { continue; }
|
||||
|
||||
if (mcp9808.begin(MCP9808_START_ADDRESS + i)) {
|
||||
mcp9808_sensors[mcp9808_cfg.count].address = MCP9808_START_ADDRESS + i;
|
||||
I2cSetActiveFound(mcp9808_sensors[mcp9808_cfg.count].address, mcp9808_cfg.types);
|
||||
mcp9808.setResolution (mcp9808_sensors[mcp9808_cfg.count].address, 2); // Set Resolution to 0.125°C
|
||||
mcp9808_cfg.count++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void MCP9808EverySecond(void) {
|
||||
for (uint32_t i = 0; i < mcp9808_cfg.count; i++) {
|
||||
float t = MCP9808Read(mcp9808_sensors[i].address);
|
||||
mcp9808_sensors[i].temperature = ConvertTemp(t);
|
||||
}
|
||||
}
|
||||
|
||||
void MCP9808Show(bool json) {
|
||||
for (uint32_t i = 0; i < mcp9808_cfg.count; i++) {
|
||||
char temperature[33];
|
||||
dtostrfd(mcp9808_sensors[i].temperature, Settings.flag2.temperature_resolution, temperature);
|
||||
|
||||
char sensor_name[11];
|
||||
strlcpy(sensor_name, mcp9808_cfg.types, sizeof(sensor_name));
|
||||
if (mcp9808_cfg.count > 1) {
|
||||
snprintf_P(sensor_name, sizeof(sensor_name), PSTR("%s%c%02X"), sensor_name, IndexSeparator(), mcp9808_sensors[i].address); // MCP9808-18, MCP9808-1A etc.
|
||||
}
|
||||
|
||||
if (json) {
|
||||
ResponseAppend_P(JSON_SNS_TEMP, sensor_name, temperature);
|
||||
if ((0 == tele_period) && (0 == i)) {
|
||||
#ifdef USE_DOMOTICZ
|
||||
DomoticzSensor(DZ_TEMP, temperature);
|
||||
#endif // USE_DOMOTICZ
|
||||
#ifdef USE_KNX
|
||||
KnxSensor(KNX_TEMPERATURE, mcp9808_sensors[i].temperature);
|
||||
#endif // USE_KNX
|
||||
}
|
||||
#ifdef USE_WEBSERVER
|
||||
} else {
|
||||
WSContentSend_PD(HTTP_SNS_TEMP, sensor_name, temperature, TempUnit());
|
||||
#endif // USE_WEBSERVER
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*********************************************************************************************\
|
||||
* Interface
|
||||
\*********************************************************************************************/
|
||||
|
||||
bool Xsns72(uint8_t function)
|
||||
{
|
||||
if (!I2cEnabled(XI2C_51)) { return false; }
|
||||
bool result = false;
|
||||
|
||||
if (FUNC_INIT == function) {
|
||||
MCP9808Detect();
|
||||
}
|
||||
else if (mcp9808_cfg.count){
|
||||
switch (function) {
|
||||
case FUNC_EVERY_SECOND:
|
||||
MCP9808EverySecond();
|
||||
break;
|
||||
case FUNC_JSON_APPEND:
|
||||
MCP9808Show(1);
|
||||
break;
|
||||
#ifdef USE_WEBSERVER
|
||||
case FUNC_WEB_SENSOR:
|
||||
MCP9808Show(0);
|
||||
break;
|
||||
#endif // USE_WEBSERVER
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
#endif // USE_MCP9808
|
||||
#endif // USE_I2C
|
@ -1 +1,5 @@
|
||||
This files are needed for flashing Tasmota with esptool.py to a ESP32
|
||||
These files are needed for flashing Tasmota32 with esptool.py to an ESP32.
|
||||
|
||||
Command syntax for flashing Tasmota32 firmware on ESP32 via Esptool (replace COM Port Number!):
|
||||
|
||||
esptool.py --chip esp32 --port COM5 --baud 921600 --before default_reset --after hard_reset write_flash -z --flash_mode dout --flash_freq 40m --flash_size detect 0x1000 bootloader_dout_40m.bin 0x8000 partitions.bin 0xe000 boot_app0.bin 0x10000 tasmota32.bin
|
||||
|
@ -204,7 +204,7 @@ a_features = [[
|
||||
"USE_KEELOQ","USE_HRXL","USE_SONOFF_D1","USE_HDC1080",
|
||||
"USE_IAQ","USE_DISPLAY_SEVENSEG","USE_AS3935","USE_PING",
|
||||
"USE_WINDMETER","USE_OPENTHERM","USE_THERMOSTAT","USE_VEML6075",
|
||||
"USE_VEML7700","","","",
|
||||
"USE_VEML7700","USE_MCP9808","","",
|
||||
"","","","",
|
||||
"","","","",
|
||||
"","","","",
|
||||
|
192
tools/serial-plotter.py
Normal file
192
tools/serial-plotter.py
Normal file
@ -0,0 +1,192 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
"""
|
||||
serial-plotter.py - for Tasmota
|
||||
|
||||
Copyright (C) 2020 Christian Baars
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
Requirements:
|
||||
- Python
|
||||
- pip3 install matplotlib pyserial
|
||||
- for Windows: Full python install including tkinter
|
||||
- a Tasmotadriver that plots
|
||||
|
||||
Instructions:
|
||||
expects serial data in the format:
|
||||
'PLOT: graphnumber value'
|
||||
graph (1-4)
|
||||
integer value
|
||||
Code snippet example: (last value will be ignored)
|
||||
AddLog_P2(LOG_LEVEL_INFO, PSTR("PLOT: %u, %u, %u,"),button_index+1, _value, Button.touch_hits[button_index]);
|
||||
|
||||
Usage:
|
||||
./serial-plotter.py --port /dev/PORT --baud BAUD (or change defaults in the script)
|
||||
set output in tasmota, e.g.; TouchCal 1..4 (via Textbox)
|
||||
|
||||
"""
|
||||
import numpy as np
|
||||
import matplotlib.pyplot as plt
|
||||
import matplotlib.animation as animation
|
||||
from matplotlib.widgets import TextBox
|
||||
import time
|
||||
import serial
|
||||
import argparse
|
||||
import sys
|
||||
|
||||
print("Python version")
|
||||
print (sys.version)
|
||||
|
||||
#default values
|
||||
port = '/dev/cu.SLAB_USBtoUART'
|
||||
baud = 115200
|
||||
|
||||
#command line input
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument("--port", "-p", help="change serial port, default: " + port)
|
||||
parser.add_argument("--baud", "-b", help="change baud rate, default: " + str(baud))
|
||||
args = parser.parse_args()
|
||||
if args.port:
|
||||
print("change serial port to %s" % args.port)
|
||||
port = args.port
|
||||
if args.baud:
|
||||
print("change baud rate to %s" % args.baud)
|
||||
baud = args.baud
|
||||
|
||||
|
||||
#time range
|
||||
dt = 0.01
|
||||
t = np.arange(0.0, 100, dt)
|
||||
|
||||
#lists for the data
|
||||
xs = [0] #counting up x
|
||||
ys = [[0],[0],[0],[0]] #4 fixed graphs for now
|
||||
max_y = 1
|
||||
# min_y = 0
|
||||
|
||||
fig = plt.figure('Tasmota Serial Plotter')
|
||||
ax = fig.add_subplot(111, autoscale_on=True, xlim=(0, 200), ylim=(0, 20)) #fixed x scale for now, y will adapt
|
||||
ax.grid()
|
||||
|
||||
line1, = ax.plot([], [], color = "r", label='G 1')
|
||||
line2, = ax.plot([], [], color = "g", label='G 2')
|
||||
line3, = ax.plot([], [], color = "b", label='G 3')
|
||||
line4, = ax.plot([], [], color = "y", label='G 4')
|
||||
|
||||
time_template = 'time = %.1fs'
|
||||
time_text = ax.text(0.05, 0.9, '', transform=ax.transAxes)
|
||||
|
||||
ser = serial.Serial()
|
||||
ser.port = port
|
||||
ser.baudrate = baud
|
||||
ser.timeout = 0 #return immediately
|
||||
try:
|
||||
ser.open()
|
||||
except:
|
||||
print("Could not connect to serial with settings: " + str(ser.port) + ' at ' + str(ser.baudrate) + 'baud')
|
||||
print("port available?")
|
||||
exit()
|
||||
|
||||
if ser.is_open==True:
|
||||
print("Serial Plotter started ...:")
|
||||
plt.title('connected to ' + str(ser.port) + ' at ' + str(ser.baudrate) + 'baud')
|
||||
else:
|
||||
print("Could not connect to serial: " + str(ser.port) + ' at ' + str(ser.baudrate) + 'baud')
|
||||
plt.title('NOT connected to ' + str(ser.port) + ' at ' + str(ser.baudrate) + 'baud')
|
||||
|
||||
def init():
|
||||
line1.set_data([], [])
|
||||
line2.set_data([], [])
|
||||
line3.set_data([], [])
|
||||
line4.set_data([], [])
|
||||
time_text.set_text('')
|
||||
return [line1,line2,line3,line4,time_text ] #was line
|
||||
|
||||
|
||||
def parse_line(data_line):
|
||||
pos = data_line.find("PLOT:", 10)
|
||||
if pos<0:
|
||||
# print("wrong format")
|
||||
return 0,0
|
||||
|
||||
raw_data = data_line[pos+6:]
|
||||
val_list = raw_data.split(',')
|
||||
try:
|
||||
g = int(val_list[0])
|
||||
v = int(val_list[1])
|
||||
return g, v
|
||||
except:
|
||||
return 0,0
|
||||
|
||||
def update(num, line1, line2):
|
||||
global xs, ys, max_y
|
||||
|
||||
time_text.set_text(time_template % (num*dt) )
|
||||
|
||||
receive_data = str(ser.readline()) #string
|
||||
|
||||
g, v = parse_line(receive_data)
|
||||
if (g in range(1,5)):
|
||||
# print(v,g)
|
||||
if v>max_y:
|
||||
max_y = v
|
||||
print(max_y)
|
||||
ax.set_ylim([0, max_y * 1.2])
|
||||
|
||||
idx = 0
|
||||
for y in ys:
|
||||
y.append(y[-1])
|
||||
if idx == g-1:
|
||||
y[-1] = v
|
||||
idx = idx +1
|
||||
xs.append(xs[-1]+1)
|
||||
|
||||
if len(ys[0])>200:
|
||||
xs.pop()
|
||||
for y in ys:
|
||||
y.pop(0)
|
||||
line1.set_data(xs, ys[0])
|
||||
line2.set_data(xs, ys[1])
|
||||
line3.set_data(xs, ys[2])
|
||||
line4.set_data(xs, ys[3])
|
||||
return [line1,line2,line3,line4, time_text]
|
||||
|
||||
def handle_close(evt):
|
||||
print('Closing serial connection')
|
||||
ser.close()
|
||||
print('Closed serial plotter')
|
||||
|
||||
def submit(text):
|
||||
print (text)
|
||||
ser.write(text.encode() + "\n".encode())
|
||||
|
||||
|
||||
ani = animation.FuncAnimation(fig, update, None, fargs=[line1, line2],
|
||||
interval=10, blit=True, init_func=init)
|
||||
|
||||
ax.set_xlabel('Last 200 Samples')
|
||||
ax.set_ylabel('Values')
|
||||
plt.subplots_adjust(bottom=0.25)
|
||||
ax.legend(loc='lower right', ncol=2)
|
||||
|
||||
fig.canvas.mpl_connect('close_event', handle_close)
|
||||
|
||||
axbox = plt.axes([0.15, 0.05, 0.7, 0.075])
|
||||
text_box = TextBox(axbox, 'Send:', initial='')
|
||||
text_box.on_submit(submit)
|
||||
|
||||
if ser.is_open==True:
|
||||
plt.show()
|
||||
|
Loading…
x
Reference in New Issue
Block a user