diff --git a/BUILDS.md b/BUILDS.md index f510ee046..7c8618e39 100644 --- a/BUILDS.md +++ b/BUILDS.md @@ -18,9 +18,9 @@ | USE_WEBSEND_RESPONSE | - | - | - | - | - | - | - | | USE_EMULATION_HUE | - | x | x | - | x | - | - | | USE_EMULATION_WEMO | - | x | x | - | x | - | - | -| USE_DISCOVERY | - | - | x | x | - | - | x | +| USE_DISCOVERY | - | - | - | - | - | - | - | | WEBSERVER_ADVERTISE | - | - | x | x | - | - | x | -| MQTT_HOST_DISCOVERY | - | - | x | x | - | - | x | +| MQTT_HOST_DISCOVERY | - | - | - | - | - | - | - | | USE_TIMERS | - | x | x | x | x | x | x | | USE_TIMERS_WEB | - | x | x | x | x | x | x | | USE_SUNRISE | - | x | x | x | x | x | x | diff --git a/CHANGELOG.md b/CHANGELOG.md index d840a1395..dbc59b21b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,7 @@ All notable changes to this project will be documented in this file. ## [9.3.1.2] ### Added - Commands ``MqttKeepAlive 1..100`` to set Mqtt Keep Alive timer (default 30) and ``MqttTimeout 1..100`` to set Mqtt Socket Timeout (default 4) (#5341) +- Commands ``DisplayType`` to select sub-modules where implemented and ``DisplayInvert`` to select inverted display where implemented - Support for TM1638 seven segment display by Ajith Vasudevan (#11031) ### Changed diff --git a/RELEASENOTES.md b/RELEASENOTES.md index 19e7f50c2..b94f603c8 100644 --- a/RELEASENOTES.md +++ b/RELEASENOTES.md @@ -82,16 +82,20 @@ The attached binaries can also be downloaded from http://ota.tasmota.com/tasmota ### Added - Commands ``MqttKeepAlive 1..100`` to set Mqtt Keep Alive timer (default 30) and ``MqttTimeout 1..100`` to set Mqtt Socket Timeout (default 4) [#5341](https://github.com/arendst/Tasmota/issues/5341) - Command ``Sensor80 1 <0..7>`` to control MFRC522 RFID antenna gain from 18dB (0) to 48dB (7) [#11073](https://github.com/arendst/Tasmota/issues/11073) +- Commands ``DisplayType`` to select sub-modules where implemented and ``DisplayInvert`` to select inverted display where implemented - Support for SML VBUS [#11125](https://github.com/arendst/Tasmota/issues/11125) - Support for NEC and OPTOMA LCD/DLP Projector serial power control by Jan Bubík [#11145](https://github.com/arendst/Tasmota/issues/11145) - Support for XPT2046 touch screen digitizer on ILI9341 display by nonix [#11159](https://github.com/arendst/Tasmota/issues/11159) - Support for zigbee lumi.sensor_wleak [#11200](https://github.com/arendst/Tasmota/issues/11200) - Support for CSE7761 energy monitor as used in ESP32 based Sonoff Dual R3 Pow [#10793](https://github.com/arendst/Tasmota/issues/10793) - Support for TM1638 seven segment display by Ajith Vasudevan [#11031](https://github.com/arendst/Tasmota/issues/11031) +- Support for MPU6886 on primary or secondary I2C bus - Allow MCP230xx pinmode from output to input [#11104](https://github.com/arendst/Tasmota/issues/11104) - Berry improvements [#11163](https://github.com/arendst/Tasmota/issues/11163) - Extent compile time SetOptions support [#11204](https://github.com/arendst/Tasmota/issues/11204) - ESP32 Extent BLE [#11212](https://github.com/arendst/Tasmota/issues/11212) +- ESP32 support for WS2812 hardware driver via RMT or I2S +- ESP32 support for secondary I2C controller ### Changed - TasmotaSerial library from v3.2.0 to v3.3.0 diff --git a/lib/lib_display/LedControl/LICENSE b/lib/lib_display/LedControl/LICENSE new file mode 100644 index 000000000..8d59812aa --- /dev/null +++ b/lib/lib_display/LedControl/LICENSE @@ -0,0 +1,23 @@ +LedControl.h - A library for controling Leds with a MAX7219/MAX7221 +Copyright (c) 2007-2015 Eberhard Fahle + +Permission is hereby granted, free of charge, to any person +obtaining a copy of this software and associated documentation +files (the "Software"), to deal in the Software without +restriction, including without limitation the rights to use, +copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the +Software is furnished to do so, subject to the following +conditions: + +This permission notice shall be included in all copies or +substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +OTHER DEALINGS IN THE SOFTWARE. diff --git a/lib/lib_display/LedControl/README.md b/lib/lib_display/LedControl/README.md new file mode 100644 index 000000000..8c832d654 --- /dev/null +++ b/lib/lib_display/LedControl/README.md @@ -0,0 +1,25 @@ +LedControl +========== +LedControl is an [Arduino](http://arduino.cc) library for MAX7219 and MAX7221 Led display drivers. +The code also works with the [Teensy (3.1)](https://www.pjrc.com/teensy/) + +Documentation +------------- +Documentation for the library is on the [Github Project Pages](http://wayoda.github.io/LedControl/) + +Download +-------- +The lastest binary version of the Library is always available from the +[LedControl Release Page](https://github.com/wayoda/LedControl/releases) + + +Install +------- +The library can be installed using the [standard Arduino library install procedure](http://arduino.cc/en/Guide/Libraries#.UwxndHX5PtY) + + + + + + + diff --git a/lib/lib_display/LedControl/keywords.txt b/lib/lib_display/LedControl/keywords.txt new file mode 100644 index 000000000..9d2a94f1f --- /dev/null +++ b/lib/lib_display/LedControl/keywords.txt @@ -0,0 +1,28 @@ +####################################### +# Syntax Coloring Map For LedControl +####################################### + +####################################### +# Datatypes (KEYWORD1) +####################################### + +LedControl KEYWORD1 + +####################################### +# Methods and Functions (KEYWORD2) +####################################### + +shutdown KEYWORD2 +setScanLimit KEYWORD2 +setIntensity KEYWORD2 +clearDisplay KEYWORD2 +setLed KEYWORD2 +setRow KEYWORD2 +setColumn KEYWORD2 +setDigit KEYWORD2 +setChar KEYWORD2 + +####################################### +# Constants (LITERAL1) +####################################### + diff --git a/lib/lib_display/LedControl/library.properties b/lib/lib_display/LedControl/library.properties new file mode 100644 index 000000000..66baced18 --- /dev/null +++ b/lib/lib_display/LedControl/library.properties @@ -0,0 +1,10 @@ +name=LedControl +version=1.0.6 +author=Eberhard Fahle +maintainer=Eberhard Fahle +sentence=A library for the MAX7219 and the MAX7221 Led display drivers. +paragraph=The library supports multiple daisychained drivers and supports Led-Matrix displays as well as 7-Segment displays. +category=Display +url=http://wayoda.github.io/LedControl/ +architectures=* + diff --git a/lib/lib_display/LedControl/src/LedControl.cpp b/lib/lib_display/LedControl/src/LedControl.cpp new file mode 100644 index 000000000..e43211fd8 --- /dev/null +++ b/lib/lib_display/LedControl/src/LedControl.cpp @@ -0,0 +1,211 @@ +/* + * LedControl.cpp - A library for controling Leds with a MAX7219/MAX7221 + * Copyright (c) 2007 Eberhard Fahle + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * This permission notice shall be included in all copies or + * substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + + +#include "LedControl.h" + +//the opcodes for the MAX7221 and MAX7219 +#define OP_NOOP 0 +#define OP_DIGIT0 1 +#define OP_DIGIT1 2 +#define OP_DIGIT2 3 +#define OP_DIGIT3 4 +#define OP_DIGIT4 5 +#define OP_DIGIT5 6 +#define OP_DIGIT6 7 +#define OP_DIGIT7 8 +#define OP_DECODEMODE 9 +#define OP_INTENSITY 10 +#define OP_SCANLIMIT 11 +#define OP_SHUTDOWN 12 +#define OP_DISPLAYTEST 15 + +LedControl::LedControl(int dataPin, int clkPin, int csPin, int numDevices) { + SPI_MOSI=dataPin; + SPI_CLK=clkPin; + SPI_CS=csPin; + if(numDevices<=0 || numDevices>8 ) + numDevices=8; + maxDevices=numDevices; + pinMode(SPI_MOSI,OUTPUT); + pinMode(SPI_CLK,OUTPUT); + pinMode(SPI_CS,OUTPUT); + digitalWrite(SPI_CS,HIGH); + SPI_MOSI=dataPin; + for(int i=0;i<64;i++) + status[i]=0x00; + for(int i=0;i=maxDevices) + return; + if(b) + spiTransfer(addr, OP_SHUTDOWN,0); + else + spiTransfer(addr, OP_SHUTDOWN,1); +} + +void LedControl::setScanLimit(int addr, int limit) { + if(addr<0 || addr>=maxDevices) + return; + if(limit>=0 && limit<8) + spiTransfer(addr, OP_SCANLIMIT,limit); +} + +void LedControl::setIntensity(int addr, int intensity) { + if(addr<0 || addr>=maxDevices) + return; + if(intensity>=0 && intensity<16) + spiTransfer(addr, OP_INTENSITY,intensity); +} + +void LedControl::clearDisplay(int addr) { + int offset; + + if(addr<0 || addr>=maxDevices) + return; + offset=addr*8; + for(int i=0;i<8;i++) { + status[offset+i]=0; + spiTransfer(addr, i+1,status[offset+i]); + } +} + +void LedControl::setLed(int addr, int row, int column, boolean state) { + int offset; + byte val=0x00; + + if(addr<0 || addr>=maxDevices) + return; + if(row<0 || row>7 || column<0 || column>7) + return; + offset=addr*8; + val=B10000000 >> column; + if(state) + status[offset+row]=status[offset+row]|val; + else { + val=~val; + status[offset+row]=status[offset+row]&val; + } + spiTransfer(addr, row+1,status[offset+row]); +} + +void LedControl::setRow(int addr, int row, byte value) { + int offset; + if(addr<0 || addr>=maxDevices) + return; + if(row<0 || row>7) + return; + offset=addr*8; + status[offset+row]=value; + spiTransfer(addr, row+1,status[offset+row]); +} + +void LedControl::setColumn(int addr, int col, byte value) { + byte val; + + if(addr<0 || addr>=maxDevices) + return; + if(col<0 || col>7) + return; + for(int row=0;row<8;row++) { + val=value >> (7-row); + val=val & 0x01; + setLed(addr,row,col,val); + } +} + +void LedControl::setDigit(int addr, int digit, byte value, boolean dp) { + int offset; + byte v; + + if(addr<0 || addr>=maxDevices) + return; + if(digit<0 || digit>7 || value>15) + return; + offset=addr*8; + v=pgm_read_byte_near(charTable + value); + if(dp) + v|=B10000000; + status[offset+digit]=v; + spiTransfer(addr, digit+1,v); +} + +void LedControl::setChar(int addr, int digit, char value, boolean dp) { + int offset; + byte index,v; + + if(addr<0 || addr>=maxDevices) + return; + if(digit<0 || digit>7) + return; + offset=addr*8; + index=(byte)value; + if(index >127) { + //no defined beyond index 127, so we use the space char + index=32; + } + v=pgm_read_byte_near(charTable + index); + if(dp) + v|=B10000000; + status[offset+digit]=v; + spiTransfer(addr, digit+1,v); +} + +void LedControl::spiTransfer(int addr, volatile byte opcode, volatile byte data) { + //Create an array with the data to shift out + int offset=addr*2; + int maxbytes=maxDevices*2; + + for(int i=0;i0;i--) + shiftOut(SPI_MOSI,SPI_CLK,MSBFIRST,spidata[i-1]); + //latch the data onto the display + digitalWrite(SPI_CS,HIGH); +} + + diff --git a/lib/lib_display/LedControl/src/LedControl.h b/lib/lib_display/LedControl/src/LedControl.h new file mode 100644 index 000000000..f8180d07d --- /dev/null +++ b/lib/lib_display/LedControl/src/LedControl.h @@ -0,0 +1,190 @@ +/* + * LedControl.h - A library for controling Leds with a MAX7219/MAX7221 + * Copyright (c) 2007 Eberhard Fahle + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * This permission notice shall be included in all copies or + * substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#ifndef LedControl_h +#define LedControl_h + +#include + +#if (ARDUINO >= 100) +#include +#else +#include +#endif + +/* + * Segments to be switched on for characters and digits on + * 7-Segment Displays + */ +const static byte charTable [] PROGMEM = { + B01111110,B00110000,B01101101,B01111001,B00110011,B01011011,B01011111,B01110000, + B01111111,B01111011,B01110111,B00011111,B00001101,B00111101,B01001111,B01000111, + B00000000,B00000000,B00000000,B00000000,B00000000,B00000000,B00000000,B00000000, + B00000000,B00000000,B00000000,B00000000,B00000000,B00000000,B00000000,B00000000, + B00000000,B00000000,B00000000,B00000000,B00000000,B00000000,B00000000,B00000000, + B00000000,B00000000,B00000000,B00000000,B10000000,B00000001,B10000000,B00000000, + B01111110,B00110000,B01101101,B01111001,B00110011,B01011011,B01011111,B01110000, + B01111111,B01111011,B00000000,B00000000,B00000000,B00000000,B00000000,B00000000, + B00000000,B01110111,B00011111,B00001101,B00111101,B01001111,B01000111,B00000000, + B00110111,B00000000,B00000000,B00000000,B00001110,B00000000,B00000000,B00000000, + B01100111,B00000000,B00000000,B00000000,B00000000,B00000000,B00000000,B00000000, + B00000000,B00000000,B00000000,B00000000,B00000000,B00000000,B00000000,B00001000, + B00000000,B01110111,B00011111,B00001101,B00111101,B01001111,B01000111,B00000000, + B00110111,B00000000,B00000000,B00000000,B00001110,B00000000,B00010101,B00011101, + B01100111,B00000000,B00000000,B00000000,B00000000,B00000000,B00000000,B00000000, + B00000000,B00000000,B00000000,B00000000,B00000000,B00000000,B00000000,B00000000 +}; + +class LedControl { + private : + /* The array for shifting the data to the devices */ + byte spidata[16]; + /* Send out a single command to the device */ + void spiTransfer(int addr, byte opcode, byte data); + + /* We keep track of the led-status for all 8 devices in this array */ + byte status[64]; + /* Data is shifted out of this pin*/ + int SPI_MOSI; + /* The clock is signaled on this pin */ + int SPI_CLK; + /* This one is driven LOW for chip selectzion */ + int SPI_CS; + /* The maximum number of devices we use */ + int maxDevices; + + public: + /* + * Create a new controler + * Params : + * dataPin pin on the Arduino where data gets shifted out + * clockPin pin for the clock + * csPin pin for selecting the device + * numDevices maximum number of devices that can be controled + */ + LedControl(int dataPin, int clkPin, int csPin, int numDevices=1); + + /* + * Gets the number of devices attached to this LedControl. + * Returns : + * int the number of devices on this LedControl + */ + int getDeviceCount(); + + /* + * Set the shutdown (power saving) mode for the device + * Params : + * addr The address of the display to control + * status If true the device goes into power-down mode. Set to false + * for normal operation. + */ + void shutdown(int addr, bool status); + + /* + * Set the number of digits (or rows) to be displayed. + * See datasheet for sideeffects of the scanlimit on the brightness + * of the display. + * Params : + * addr address of the display to control + * limit number of digits to be displayed (1..8) + */ + void setScanLimit(int addr, int limit); + + /* + * Set the brightness of the display. + * Params: + * addr the address of the display to control + * intensity the brightness of the display. (0..15) + */ + void setIntensity(int addr, int intensity); + + /* + * Switch all Leds on the display off. + * Params: + * addr address of the display to control + */ + void clearDisplay(int addr); + + /* + * Set the status of a single Led. + * Params : + * addr address of the display + * row the row of the Led (0..7) + * col the column of the Led (0..7) + * state If true the led is switched on, + * if false it is switched off + */ + void setLed(int addr, int row, int col, boolean state); + + /* + * Set all 8 Led's in a row to a new state + * Params: + * addr address of the display + * row row which is to be set (0..7) + * value each bit set to 1 will light up the + * corresponding Led. + */ + void setRow(int addr, int row, byte value); + + /* + * Set all 8 Led's in a column to a new state + * Params: + * addr address of the display + * col column which is to be set (0..7) + * value each bit set to 1 will light up the + * corresponding Led. + */ + void setColumn(int addr, int col, byte value); + + /* + * Display a hexadecimal digit on a 7-Segment Display + * Params: + * addr address of the display + * digit the position of the digit on the display (0..7) + * value the value to be displayed. (0x00..0x0F) + * dp sets the decimal point. + */ + void setDigit(int addr, int digit, byte value, boolean dp); + + /* + * Display a character on a 7-Segment display. + * There are only a few characters that make sense here : + * '0','1','2','3','4','5','6','7','8','9','0', + * 'A','b','c','d','E','F','H','L','P', + * '.','-','_',' ' + * Params: + * addr address of the display + * digit the position of the character on the display (0..7) + * value the character to be displayed. + * dp sets the decimal point. + */ + void setChar(int addr, int digit, char value, boolean dp); +}; + +#endif //LedControl.h + + + diff --git a/lib/lib_i2c/MPU6886/src/MPU6886.cpp b/lib/lib_i2c/MPU6886/src/MPU6886.cpp index 9ae45461d..8774d09f7 100755 --- a/lib/lib_i2c/MPU6886/src/MPU6886.cpp +++ b/lib/lib_i2c/MPU6886/src/MPU6886.cpp @@ -4,24 +4,24 @@ void MPU6886::I2C_Read_NBytes(uint8_t driver_Addr, uint8_t start_Addr, uint8_t number_Bytes, uint8_t *read_Buffer){ - myWire.beginTransmission(driver_Addr); - myWire.write(start_Addr); - myWire.endTransmission(false); + myWire->beginTransmission(driver_Addr); + myWire->write(start_Addr); + myWire->endTransmission(false); uint8_t i = 0; - myWire.requestFrom(driver_Addr,number_Bytes); + myWire->requestFrom(driver_Addr,number_Bytes); //! Put read results in the Rx buffer - while (myWire.available()) { - read_Buffer[i++] = myWire.read(); + while (myWire->available()) { + read_Buffer[i++] = myWire->read(); } } void MPU6886::I2C_Write_NBytes(uint8_t driver_Addr, uint8_t start_Addr, uint8_t number_Bytes, uint8_t *write_Buffer){ - myWire.beginTransmission(driver_Addr); - myWire.write(start_Addr); - myWire.write(*write_Buffer); - myWire.endTransmission(); + myWire->beginTransmission(driver_Addr); + myWire->write(start_Addr); + myWire->write(*write_Buffer); + myWire->endTransmission(); } diff --git a/lib/lib_i2c/MPU6886/src/MPU6886.h b/lib/lib_i2c/MPU6886/src/MPU6886.h index 025f71587..b4a5541e0 100755 --- a/lib/lib_i2c/MPU6886/src/MPU6886.h +++ b/lib/lib_i2c/MPU6886/src/MPU6886.h @@ -71,9 +71,9 @@ class MPU6886 { public: MPU6886(void) {}; #ifdef ESP32 - void setBus(uint32_t _bus) { myWire = _bus ? Wire1 : Wire; }; + void setBus(uint32_t _bus) { myWire = _bus ? &Wire1 : &Wire; }; #else - void setBus(uint32_t _bus) { myWire = Wire; }; + void setBus(uint32_t _bus) { myWire = &Wire; }; #endif int Init(void); void getAccelAdc(int16_t* ax, int16_t* ay, int16_t* az); @@ -93,7 +93,7 @@ class MPU6886 { // void getAhrsData(float *pitch,float *roll,float *yaw); public: - TwoWire & myWire = Wire; // default to Wire (bus 0) + TwoWire * myWire = &Wire; // default to Wire (bus 0) float aRes, gRes; private: diff --git a/lib/libesp32/Berry-0.1.10/src/port/be_energylib.c b/lib/libesp32/Berry-0.1.10/src/port/be_energylib.c index 444133852..020766460 100644 --- a/lib/libesp32/Berry-0.1.10/src/port/be_energylib.c +++ b/lib/libesp32/Berry-0.1.10/src/port/be_energylib.c @@ -19,7 +19,7 @@ be_define_native_module(energy, NULL); #else /* @const_object_info_begin module tasmota (scope: global, depend: 1) { - getfreeheap, func(l_getFreeHeap) + get_free_heap, func(l_getFreeHeap) } @const_object_info_end */ #include "../generate/be_fixed_tasmota.h" diff --git a/lib/libesp32/Berry-0.1.10/src/port/be_tasmotalib.c b/lib/libesp32/Berry-0.1.10/src/port/be_tasmotalib.c index 17b09a219..713effefd 100644 --- a/lib/libesp32/Berry-0.1.10/src/port/be_tasmotalib.c +++ b/lib/libesp32/Berry-0.1.10/src/port/be_tasmotalib.c @@ -27,6 +27,8 @@ extern int l_getpower(bvm *vm); extern int l_setlight(bvm *vm); extern int l_setpower(bvm *vm); +extern int l_i2cenabled(bvm *vm); + // #if !BE_USE_PRECOMPILED_OBJECT #if 1 // TODO we will do pre-compiled later // Class definition @@ -39,27 +41,30 @@ void be_load_tasmota_ntvlib(bvm *vm) { "_rules", NULL }, { "_timers", NULL }, { "_cmd", NULL }, - { "getfreeheap", l_getFreeHeap }, + { "_drivers", NULL }, + { "get_free_heap", l_getFreeHeap }, { "publish", l_publish }, { "cmd", l_cmd }, - { "getoption", l_getoption }, + { "get_option", l_getoption }, { "millis", l_millis }, - { "timereached", l_timereached }, + { "time_reached", l_timereached }, { "yield", l_yield }, { "delay", l_delay }, - { "scaleuint", l_scaleuint }, + { "scale_uint", l_scaleuint }, - { "respcmnd", l_respCmnd }, - { "respcmndstr", l_respCmndStr }, - { "respcmnd_done", l_respCmndDone }, - { "respcmnd_error", l_respCmndError }, - { "respcmnd_failed", l_respCmndFailed }, + { "resp_cmnd", l_respCmnd }, + { "resp_cmnd_str", l_respCmndStr }, + { "resp_cmnd_done", l_respCmndDone }, + { "resp_cmnd_error", l_respCmndError }, + { "resp_cmnd_failed", l_respCmndFailed }, { "resolvecmnd", l_resolveCmnd }, - { "getlight", l_getlight }, - { "getpower", l_getpower }, - { "setlight", l_setlight }, - { "setpower", l_setpower }, + { "get_light", l_getlight }, + { "get_power", l_getpower }, + { "set_light", l_setlight }, + { "set_power", l_setpower }, + + { "i2c_enabled", l_i2cenabled }, { NULL, NULL } }; @@ -69,7 +74,7 @@ void be_load_tasmota_ntvlib(bvm *vm) #else /* @const_object_info_begin module tasmota (scope: global, depend: 1) { - getfreeheap, func(l_getFreeHeap) + get_free_heap, func(l_getFreeHeap) } @const_object_info_end */ #include "../generate/be_fixed_tasmota.h" diff --git a/lib/libesp32/Berry-0.1.10/src/port/be_wirelib.c b/lib/libesp32/Berry-0.1.10/src/port/be_wirelib.c index 07581b2a4..fc785c488 100644 --- a/lib/libesp32/Berry-0.1.10/src/port/be_wirelib.c +++ b/lib/libesp32/Berry-0.1.10/src/port/be_wirelib.c @@ -20,6 +20,7 @@ extern int b_wire_scan(bvm *vm); extern int b_wire_validwrite(bvm *vm); extern int b_wire_validread(bvm *vm); +extern int b_wire_detect(bvm *vm); // #if !BE_USE_PRECOMPILED_OBJECT #if 1 // TODO we will do pre-compiled later @@ -28,24 +29,25 @@ void be_load_wirelib(bvm *vm) static const bnfuncinfo members[] = { { "_bus", NULL }, // bus number { "init", b_wire_init }, - { "_begintransmission", b_wire_begintransmission }, - { "_endtransmission", b_wire_endtransmission }, - { "_requestfrom", b_wire_requestfrom }, + { "_begin_transmission", b_wire_begintransmission }, + { "_end_transmission", b_wire_endtransmission }, + { "_request_from", b_wire_requestfrom }, { "_available", b_wire_available }, { "_write", b_wire_write }, { "_read", b_wire_read }, { "scan", b_wire_scan }, { "write", b_wire_validwrite }, { "read", b_wire_validread }, + { "detect", b_wire_detect }, { NULL, NULL } }; - be_regclass(vm, "Wire", members); + be_regclass(vm, "Wire_ntv", members); } #else /* @const_object_info_begin module tasmota (scope: global, depend: 1) { - getfreeheap, func(l_getFreeHeap) + get_free_heap, func(l_getFreeHeap) } @const_object_info_end */ #include "../generate/be_fixed_tasmota.h" diff --git a/lib/libesp32/Berry-0.1.10/src/port/berry_conf.h b/lib/libesp32/Berry-0.1.10/src/port/berry_conf.h index e3720b4d1..74fc7f505 100644 --- a/lib/libesp32/Berry-0.1.10/src/port/berry_conf.h +++ b/lib/libesp32/Berry-0.1.10/src/port/berry_conf.h @@ -162,11 +162,27 @@ * are not required. * The default is to use the functions in the standard library. **/ +#ifdef USE_BERRY_PSRAM +#ifdef __cplusplus +extern "C" { +#endif + extern void *berry_malloc(uint32_t size); + extern void *berry_realloc(void *ptr, size_t size); +#ifdef __cplusplus +} +#endif + #define BE_EXPLICIT_MALLOC special_malloc + #define BE_EXPLICIT_REALLOC special_realloc +#else + #define BE_EXPLICIT_MALLOC malloc + #define BE_EXPLICIT_REALLOC realloc +#endif // USE_BERRY_PSRAM + #define BE_EXPLICIT_ABORT abort #define BE_EXPLICIT_EXIT exit -#define BE_EXPLICIT_MALLOC malloc +// #define BE_EXPLICIT_MALLOC malloc #define BE_EXPLICIT_FREE free -#define BE_EXPLICIT_REALLOC realloc +// #define BE_EXPLICIT_REALLOC realloc /* Macro: be_assert * Berry debug assertion. Only enabled when BE_DEBUG is active. diff --git a/tasmota/berry/denky.be b/tasmota/berry/denky.be index bcec51407..416e26078 100644 --- a/tasmota/berry/denky.be +++ b/tasmota/berry/denky.be @@ -10,11 +10,11 @@ runcolor = nil def runcolor() var pwr = energy.read().find('activepower',0) print(pwr) - var red = tasmota.scaleuint(int(pwr), 0, 2500, 0, 255) + var red = tasmota.scale_uint(int(pwr), 0, 2500, 0, 255) var green = 255 - red var channels = [red, green, 0] - tasmota.setlight({"channels":channels, "bri":64, "power":true}) - tasmota.settimer(2000, runcolor) + tasmota.set_light({"channels":channels, "bri":64, "power":true}) + tasmota.set_timer(2000, runcolor) end #- run animation -# diff --git a/tasmota/berry/tasmota.be b/tasmota/berry/tasmota.be deleted file mode 100644 index 213eb0be0..000000000 --- a/tasmota/berry/tasmota.be +++ /dev/null @@ -1,211 +0,0 @@ -import json import string -tasmota = module("tasmota") -def log(m) print(m) end -def save() end - -####### -import string -import json -import gc -import tasmota -#// import alias -import tasmota as t - -def charsinstring(s,c) - for i:0..size(s)-1 - for j:0..size(c)-1 - if s[i] == c[j] return i end - end - end - return -1 -end - -### -class Tasmota - var _op, _operators, _rules - def init() - self._operators = "=<>!|" - self._op = [ - ['==', /s1,s2-> str(s1) == str(s2)], - ['!==',/s1,s2-> str(s1) != str(s2)], - ['=', /f1,f2-> real(f1) == real(f2)], - ['!=', /f1,f2-> real(f1) != real(f2)], - ['>=', /f1,f2-> real(f1) >= real(f2)], - ['<=', /f1,f2-> real(f1) <= real(f2)], - ['>', /f1,f2-> real(f1) > real(f2)], - ['<', /f1,f2-> real(f1) < real(f2)], - ] - self._rules = {} - end -end -### - -tasmota._eqstr=/s1,s2-> str(s1) == str(s2) -tasmota._neqstr=/s1,s2-> str(s1) != str(s2) -tasmota._eq=/f1,f2-> real(f1) == real(f2) -tasmota._neq=/f1,f2-> real(f1) != real(f2) -tasmota._gt=/f1,f2-> real(f1) > real(f2) -tasmota._lt=/f1,f2-> real(f1) < real(f2) -tasmota._ge=/f1,f2-> real(f1) >= real(f2) -tasmota._le=/f1,f2-> real(f1) <= real(f2) -tasmota._op=[ - ['==',tasmota._eqstr], - ['!==',tasmota._neqstr], - ['=',tasmota._eq], - ['!=',tasmota._neq], - ['>=',tasmota._ge], - ['<=',tasmota._le], - ['>',tasmota._gt], - ['<',tasmota._lt], -] -tasmota._operators="=<>!|" - -# split the item when there is an operator, returns a list of (left,op,right) -# ex: "Dimmer>50" -> ["Dimmer",tasmota_gt,"50"] -tasmota.find_op = def (item) - var pos = charsinstring(item, tasmota._operators) - if pos>=0 - var op_split = string.split(item,pos) - #print(op_split) - var op_left = op_split[0] - var op_rest = op_split[1] - # iterate through operators - for op:tasmota._op - if string.find(op_rest,op[0]) == 0 - var op_func = op[1] - var op_right = string.split(op_rest,size(op[0]))[1] - return [op_left,op_func,op_right] - end - end - end - return [item, nil, nil] -end - - -def findkeyi(m,keyi) - var keyu = string.toupper(keyi) - if classof(m) == map - for k:m.keys() - if string.toupper(k)==keyu || keyi=='?' - return k - end - end - end -end - - -tasmota.try_rule = def (ev, rule, f) - var rl_list = tasmota.find_op(rule) - var e=ev - var rl=string.split(rl_list[0],'#') - for it:rl - found=findkeyi(e,it) - if found == nil - return false - end - e=e[found] - end - # check if condition is true - if rl_list[1] - # did we find a function - if !rl_list[1](e,rl_list[2]) - # condition is not met - return false - end - end - f(e,ev) - return true -end -tasmota_rules={} -tasmota.rule = def(pat,f) tasmota_rules[pat] = f end - -tasmota.exec_rules = def (ev_json) - var ev = json.load(ev_json) - var ret = false - if ev == nil - log('BRY: ERROR, bad json: '+ev_json, 3) - end - for r:tasmota_rules.keys() - ret = tasmota.try_rule(ev,r,tasmota_rules[r]) || ret - end - return ret -end - -tasmota.delay = def(ms) - tend = tasmota.millis(ms) - while !tasmota.timereached(tend) - tasmota.yield() - end -end - -def load(f) - try - if f[0] != '/' f = '/' + f end - compile(f,'file')() - except .. as e - log(string.format("BRY: could not load file '%s' - %s",f,e)) - end -end - -#- Test -################################################################# - -def log(m) print(m) end -def my_rule(e,ev) log("e1="+str(e)+" e2="+str(ev)) end - -tasmota.rule("ZBRECEIVED#?#LINKQUALITY", my_rule) -tasmota.rule("ZBRECEIVED#0x1234", my_rule) - -tasmota.rule("ZBRECEIVED#?#LINKQUALITY<10", my_rule) - -tasmota.rule("Dimmer>50", my_rule) -tasmota.rule("Dimmer=01", my_rule) - - -tasmota.rule("Color==022600", my_rule) - -tasmota.exec_rules('{"Color":"022600"}') - -tasmota.exec_rules('{"ZbReceived":{"0x1234":{"Device":"0x1234","LinkQuality":50}}}') - -tasmota.exec_rules('{"Dimmer":10}') - - - -# tasmota.rule("DIMMER", my_rule) -# tasmota.rule("DIMMER#DATA#DATA", my_rule) -# tasmota.exec_rules('{"Dimmer":{"Data":50}}') - - --# - -#- -tasmota.find_op("aaa") -tasmota.find_op("aaa>50") --# - -#- -# Example of backlog equivalent - -def backlog(cmd_list) - delay_backlog = tasmota.getoption(34) # in milliseconds - delay = 0 - for cmd:cmd_list - tasmota.timer(delay, /-> tasmota.cmd(cmd)) - delay = delay + delay_backlog - end -end - - -br def backlog(cmd_list) delay_backlog = tasmota.getoption(34) delay = 0 for cmd:cmd_list tasmota.timer(delay, /-> tasmota.cmd(cmd)) delay = delay + delay_backlog end end - -br backlog( [ "Power 0", "Status 4", "Power 1" ] ) - --# - -#- - -tasmota.delay = def(ms) tend = tasmota.millis(ms) log(str(tasmota.millis())) while !tasmota.timereached(tend) end log(str(tasmota.millis())) end -tasmota.delay = def(ms) a=0 tend = tasmota.millis(ms) log(str(tasmota.millis())) while !tasmota.timereached(tend) a=a+1 end log(str(tasmota.millis())) log(str(a)) end - --# \ No newline at end of file diff --git a/tasmota/homekit.c b/tasmota/homekit.c index 5bd539ddb..92e19543c 100755 --- a/tasmota/homekit.c +++ b/tasmota/homekit.c @@ -56,6 +56,7 @@ uint8_t hk_services; extern void Ext_Replace_Cmd_Vars(char *srcbuf, uint32_t srcsize, char *dstbuf, uint32_t dstsize); extern uint32_t Ext_UpdVar(char *vname, float *fvar, uint32_t mode); +extern void Ext_toLog(char *str); #define MAX_HAP_DEFS 16 struct HAP_DESC { @@ -64,6 +65,7 @@ struct HAP_DESC { char var2_name[12]; char var3_name[12]; char var4_name[12]; + char var5_name[12]; uint8_t hap_cid; uint8_t type; hap_acc_t *accessory; @@ -157,8 +159,8 @@ const struct HAP_CHAR_TABLE { {HAP_CHAR_UUID_CURRENT_RELATIVE_HUMIDITY,'f',0}, {HAP_CHAR_UUID_CURRENT_AMBIENT_LIGHT_LEVEL,'f',0}, {HAP_CHAR_UUID_BATTERY_LEVEL,'u',0}, - {HAP_CHAR_UUID_STATUS_LOW_BATTERY,'b',1}, - {HAP_CHAR_UUID_CHARGING_STATE,'b',2}, + {HAP_CHAR_UUID_STATUS_LOW_BATTERY,'u',1}, + {HAP_CHAR_UUID_CHARGING_STATE,'u',2}, {HAP_CHAR_UUID_ON,'b',0}, {HAP_CHAR_UUID_HUE,'f',1}, {HAP_CHAR_UUID_SATURATION,'f',2}, @@ -282,7 +284,7 @@ void hap_update_from_vars(void) { new_val.u = fvar; hap_char_update_val(hc, &new_val); } - hc = hap_serv_get_char_by_uuid(hap_devs[cnt].service, HAP_CHAR_UUID_STATUS_LOW_BATTERY); + hc = hap_serv_get_char_by_uuid(hap_devs[cnt].service, HAP_CHAR_UUID_CHARGING_STATE); if (Ext_UpdVar(hap_devs[cnt].var3_name, &fvar, 0)) { new_val.u = fvar; hap_char_update_val(hc, &new_val); @@ -332,6 +334,13 @@ void hap_update_from_vars(void) { new_val.u = fvar; hap_char_update_val(hc, &new_val); } + if (hap_devs[cnt].var5_name[0]) { + hc = hap_serv_get_char_by_uuid(hap_devs[cnt].service, HAP_CHAR_UUID_COLOR_TEMPERATURE); + if (Ext_UpdVar(hap_devs[cnt].var5_name, &fvar, 0)) { + new_val.u = fvar; + hap_char_update_val(hc, &new_val); + } + } break; } } @@ -454,6 +463,7 @@ uint32_t str2c(char **sp, char *vp, uint32_t len) { } else { if (strlen(*sp)) { strlcpy(vp, *sp, len); + *sp = lp + strlen(*sp); return 0; } } @@ -520,19 +530,16 @@ static void smart_outlet_thread_entry(void *p) { if (str2c(&lp1, hap_devs[index].var_name, sizeof(hap_devs[index].var_name))) { goto nextline; } - if (hap_devs[index].hap_cid == HAP_CID_LIGHTING) { - // get 3 add vars - if (str2c(&lp1, hap_devs[index].var2_name, sizeof(hap_devs[index].var2_name))) { - goto nextline; - } - if (str2c(&lp1, hap_devs[index].var3_name, sizeof(hap_devs[index].var3_name))) { - goto nextline; - } - if (str2c(&lp1, hap_devs[index].var4_name, sizeof(hap_devs[index].var4_name))) { - goto nextline; - } - } + hap_devs[index].var2_name[0] = 0; + hap_devs[index].var3_name[0] = 0; + hap_devs[index].var4_name[0] = 0; + hap_devs[index].var5_name[0] = 0; + + str2c(&lp1, hap_devs[index].var2_name, sizeof(hap_devs[index].var2_name)); + str2c(&lp1, hap_devs[index].var3_name, sizeof(hap_devs[index].var3_name)); + str2c(&lp1, hap_devs[index].var4_name, sizeof(hap_devs[index].var4_name)); + str2c(&lp1, hap_devs[index].var5_name, sizeof(hap_devs[index].var5_name)); hap_acc_cfg_t hap_cfg; hap_cfg.name = hap_devs[index].hap_name; @@ -557,12 +564,20 @@ static void smart_outlet_thread_entry(void *p) { { float fvar = 0; Ext_UpdVar(hap_devs[index].var_name, &fvar, 0); hap_devs[index].service = hap_serv_lightbulb_create(fvar); - Ext_UpdVar(hap_devs[index].var2_name, &fvar, 0); - ret |= hap_serv_add_char(hap_devs[index].service, hap_char_hue_create(fvar)); - Ext_UpdVar(hap_devs[index].var3_name, &fvar, 0); - ret |= hap_serv_add_char(hap_devs[index].service, hap_char_saturation_create(fvar)); + if (hap_devs[index].var2_name[0]) { + Ext_UpdVar(hap_devs[index].var2_name, &fvar, 0); + ret |= hap_serv_add_char(hap_devs[index].service, hap_char_hue_create(fvar)); + } + if (hap_devs[index].var3_name[0]) { + Ext_UpdVar(hap_devs[index].var3_name, &fvar, 0); + ret |= hap_serv_add_char(hap_devs[index].service, hap_char_saturation_create(fvar)); + } Ext_UpdVar(hap_devs[index].var4_name, &fvar, 0); ret |= hap_serv_add_char(hap_devs[index].service, hap_char_brightness_create(fvar)); + if (hap_devs[index].var5_name[0]) { + Ext_UpdVar(hap_devs[index].var5_name, &fvar, 0); + ret |= hap_serv_add_char(hap_devs[index].service, hap_char_color_temperature_create(fvar)); + } } break; case HAP_CID_OUTLET: @@ -681,14 +696,14 @@ nextline: // vTaskDelete(NULL); while (1) { delay(500); - // hap_update_from_vars(); + hap_update_from_vars(); } } } #define HK_PASSCODE "111-11-111" int hap_loop_stop(void); -extern void Ext_toLog(char *str); + void homekit_main(char *desc, uint32_t flag ) { if (desc) { diff --git a/tasmota/language/af_AF.h b/tasmota/language/af_AF.h index 2954219f1..ada14a2d1 100644 --- a/tasmota/language/af_AF.h +++ b/tasmota/language/af_AF.h @@ -647,6 +647,9 @@ #define D_SENSOR_TM1638_CLK "TM1638 CLK" #define D_SENSOR_TM1638_DIO "TM1638 DIO" #define D_SENSOR_TM1638_STB "TM1638 STB" +#define D_SENSOR_MAX7219_DIN "MAX7219 DIN" +#define D_SENSOR_MAX7219_CS "MAX7219 CS" +#define D_SENSOR_MAX7219_CLK "MAX7219 CLK" #define D_SENSOR_HX711_SCK "HX711 SCK" #define D_SENSOR_HX711_DAT "HX711 DAT" #define D_SENSOR_FTC532 "FTC532" diff --git a/tasmota/language/bg_BG.h b/tasmota/language/bg_BG.h index 45fd69563..649e00e24 100644 --- a/tasmota/language/bg_BG.h +++ b/tasmota/language/bg_BG.h @@ -646,6 +646,9 @@ #define D_SENSOR_TM1638_CLK "TM1638 CLK" #define D_SENSOR_TM1638_DIO "TM1638 DIO" #define D_SENSOR_TM1638_STB "TM1638 STB" +#define D_SENSOR_MAX7219_DIN "MAX7219 DIN" +#define D_SENSOR_MAX7219_CS "MAX7219 CS" +#define D_SENSOR_MAX7219_CLK "MAX7219 CLK" #define D_SENSOR_HX711_SCK "HX711 SCK" #define D_SENSOR_HX711_DAT "HX711 DAT" #define D_SENSOR_FTC532 "FTC532" diff --git a/tasmota/language/cs_CZ.h b/tasmota/language/cs_CZ.h index 837cc91ef..acd29ff2d 100644 --- a/tasmota/language/cs_CZ.h +++ b/tasmota/language/cs_CZ.h @@ -647,6 +647,9 @@ #define D_SENSOR_TM1638_CLK "TM1638 CLK" #define D_SENSOR_TM1638_DIO "TM1638 DIO" #define D_SENSOR_TM1638_STB "TM1638 STB" +#define D_SENSOR_MAX7219_DIN "MAX7219 DIN" +#define D_SENSOR_MAX7219_CS "MAX7219 CS" +#define D_SENSOR_MAX7219_CLK "MAX7219 CLK" #define D_SENSOR_HX711_SCK "HX711 SCK" #define D_SENSOR_HX711_DAT "HX711 DAT" #define D_SENSOR_FTC532 "FTC532" diff --git a/tasmota/language/de_DE.h b/tasmota/language/de_DE.h index ef753b639..40afbd61d 100644 --- a/tasmota/language/de_DE.h +++ b/tasmota/language/de_DE.h @@ -647,6 +647,9 @@ #define D_SENSOR_TM1638_CLK "TM1638 CLK" #define D_SENSOR_TM1638_DIO "TM1638 DIO" #define D_SENSOR_TM1638_STB "TM1638 STB" +#define D_SENSOR_MAX7219_DIN "MAX7219 DIN" +#define D_SENSOR_MAX7219_CS "MAX7219 CS" +#define D_SENSOR_MAX7219_CLK "MAX7219 CLK" #define D_SENSOR_HX711_SCK "HX711 SCK" #define D_SENSOR_HX711_DAT "HX711 DAT" #define D_SENSOR_FTC532 "FTC532" diff --git a/tasmota/language/el_GR.h b/tasmota/language/el_GR.h index 6bfb44891..d4e8b1bc5 100644 --- a/tasmota/language/el_GR.h +++ b/tasmota/language/el_GR.h @@ -647,6 +647,9 @@ #define D_SENSOR_TM1638_CLK "TM1638 CLK" #define D_SENSOR_TM1638_DIO "TM1638 DIO" #define D_SENSOR_TM1638_STB "TM1638 STB" +#define D_SENSOR_MAX7219_DIN "MAX7219 DIN" +#define D_SENSOR_MAX7219_CS "MAX7219 CS" +#define D_SENSOR_MAX7219_CLK "MAX7219 CLK" #define D_SENSOR_HX711_SCK "HX711 SCK" #define D_SENSOR_HX711_DAT "HX711 DAT" #define D_SENSOR_FTC532 "FTC532" diff --git a/tasmota/language/en_GB.h b/tasmota/language/en_GB.h index 5d4641c22..3dab7a2ba 100644 --- a/tasmota/language/en_GB.h +++ b/tasmota/language/en_GB.h @@ -647,6 +647,9 @@ #define D_SENSOR_TM1638_CLK "TM1638 CLK" #define D_SENSOR_TM1638_DIO "TM1638 DIO" #define D_SENSOR_TM1638_STB "TM1638 STB" +#define D_SENSOR_MAX7219_DIN "MAX7219 DIN" +#define D_SENSOR_MAX7219_CS "MAX7219 CS" +#define D_SENSOR_MAX7219_CLK "MAX7219 CLK" #define D_SENSOR_HX711_SCK "HX711 SCK" #define D_SENSOR_HX711_DAT "HX711 DAT" #define D_SENSOR_FTC532 "FTC532" diff --git a/tasmota/language/es_ES.h b/tasmota/language/es_ES.h index c868b06e4..c8b3be4c0 100644 --- a/tasmota/language/es_ES.h +++ b/tasmota/language/es_ES.h @@ -647,6 +647,9 @@ #define D_SENSOR_TM1638_CLK "TM1638 CLK" #define D_SENSOR_TM1638_DIO "TM1638 DIO" #define D_SENSOR_TM1638_STB "TM1638 STB" +#define D_SENSOR_MAX7219_DIN "MAX7219 DIN" +#define D_SENSOR_MAX7219_CS "MAX7219 CS" +#define D_SENSOR_MAX7219_CLK "MAX7219 CLK" #define D_SENSOR_HX711_SCK "HX711 SCK" #define D_SENSOR_HX711_DAT "HX711 DAT" #define D_SENSOR_FTC532 "FTC532" diff --git a/tasmota/language/fr_FR.h b/tasmota/language/fr_FR.h index 584c22724..b39f2a2de 100644 --- a/tasmota/language/fr_FR.h +++ b/tasmota/language/fr_FR.h @@ -362,7 +362,7 @@ #define D_TRANSFER_STARTED "Transfert lancé" #define D_UPLOAD_ERR_1 "Aucun fichier sélectionné" #define D_UPLOAD_ERR_2 "Espace insuffisant" -#define D_UPLOAD_ERR_3 "Invalid file signature" +#define D_UPLOAD_ERR_3 "Signature de fichier invalide" #define D_UPLOAD_ERR_4 "La taille du programme à flasher est plus grande que la taille réelle de la mémoire flash" #define D_UPLOAD_ERR_5 "Erreur de comparaison du buffer de téléchargement" #define D_UPLOAD_ERR_6 "Téléchargement échoué. Activer WebLog 3" @@ -602,7 +602,7 @@ #define D_SENSOR_WS2812 "WS2812" #define D_SENSOR_DFR562 "MP3 Player" #define D_SENSOR_IRSEND "IR TX" -#define D_SENSOR_SWITCH "Inter." // Suffix "1" +#define D_SENSOR_SWITCH "Inter" // Suffix "1" #define D_SENSOR_BUTTON "Bouton" // Suffix "1" #define D_SENSOR_RELAY "Relais" // Suffix "1i" #define D_SENSOR_LED "LED" // Suffix "1i" @@ -624,8 +624,8 @@ #define D_SENSOR_SPI_MOSI "SPI MOSI" #define D_SENSOR_SPI_CLK "SPI CLK" #define D_SENSOR_BACKLIGHT "RétroÉcl" -#define D_SENSOR_PMS5003_TX "PMS5003 Tx" -#define D_SENSOR_PMS5003_RX "PMS5003 Rx" +#define D_SENSOR_PMS5003_TX "PMS5003 TX" +#define D_SENSOR_PMS5003_RX "PMS5003 RX" #define D_SENSOR_SDS0X1_RX "SDS0X1 RX" #define D_SENSOR_SDS0X1_TX "SDS0X1 TX" #define D_SENSOR_HPMA_RX "HPMA RX" @@ -634,19 +634,22 @@ #define D_SENSOR_SBR_TX "SerBr TX" #define D_SENSOR_SR04_TRIG "SR04 Tri/TX" #define D_SENSOR_SR04_ECHO "SR04 Ech/RX" -#define D_SENSOR_SDM72_TX "SDM72 Tx" -#define D_SENSOR_SDM72_RX "SDM72 Rx" +#define D_SENSOR_SDM72_TX "SDM72 TX" +#define D_SENSOR_SDM72_RX "SDM72 RX" #define D_SENSOR_SDM120_TX "SDMx20 TX" #define D_SENSOR_SDM120_RX "SDMx20 RX" #define D_SENSOR_SDM630_TX "SDM630 TX" #define D_SENSOR_SDM630_RX "SDM630 RX" -#define D_SENSOR_WE517_TX "WE517 Tx" -#define D_SENSOR_WE517_RX "WE517 Rx" +#define D_SENSOR_WE517_TX "WE517 TX" +#define D_SENSOR_WE517_RX "WE517 RX" #define D_SENSOR_TM1637_CLK "TM1637 CLK" #define D_SENSOR_TM1637_DIO "TM1637 DIO" #define D_SENSOR_TM1638_CLK "TM1638 CLK" #define D_SENSOR_TM1638_DIO "TM1638 DIO" #define D_SENSOR_TM1638_STB "TM1638 STB" +#define D_SENSOR_MAX7219_DIN "MAX7219 DIN" +#define D_SENSOR_MAX7219_CS "MAX7219 CS" +#define D_SENSOR_MAX7219_CLK "MAX7219 CLK" #define D_SENSOR_HX711_SCK "HX711 SCK" #define D_SENSOR_HX711_DAT "HX711 DAT" #define D_SENSOR_FTC532 "FTC532" @@ -657,8 +660,8 @@ #define D_SENSOR_RFRECV "RF RX" #define D_SENSOR_TUYA_TX "Tuya TX" #define D_SENSOR_TUYA_RX "Tuya RX" -#define D_SENSOR_MGC3130_XFER "MGC3130 Xfr" -#define D_SENSOR_MGC3130_RESET "MGC3130 Rst" +#define D_SENSOR_MGC3130_XFER "MGC3130 XFR" +#define D_SENSOR_MGC3130_RESET "MGC3130 RST" #define D_SENSOR_SSPI_MISO "SSPI MISO" #define D_SENSOR_SSPI_MOSI "SSPI MOSI" #define D_SENSOR_SSPI_SCLK "SSPI SCLK" @@ -677,9 +680,9 @@ #define D_SENSOR_HJL_CF "BL0937 CF" #define D_SENSOR_MCP39F5_TX "MCP39F5 TX" #define D_SENSOR_MCP39F5_RX "MCP39F5 RX" -#define D_SENSOR_MCP39F5_RST "MCP39F5 Rst" -#define D_SENSOR_CSE7761_TX "CSE7761 Tx" -#define D_SENSOR_CSE7761_RX "CSE7761 Rx" +#define D_SENSOR_MCP39F5_RST "MCP39F5 RST" +#define D_SENSOR_CSE7761_TX "CSE7761 TX" +#define D_SENSOR_CSE7761_RX "CSE7761 RX" #define D_SENSOR_CSE7766_TX "CSE7766 TX" #define D_SENSOR_CSE7766_RX "CSE7766 RX" #define D_SENSOR_PN532_TX "PN532 TX" @@ -687,8 +690,8 @@ #define D_SENSOR_SM16716_CLK "SM16716 CLK" #define D_SENSOR_SM16716_DAT "SM16716 DAT" #define D_SENSOR_SM16716_POWER "SM16716 PWR" -#define D_SENSOR_P9813_CLK "P9813 Clk" -#define D_SENSOR_P9813_DAT "P9813 Dat" +#define D_SENSOR_P9813_CLK "P9813 CLK" +#define D_SENSOR_P9813_DAT "P9813 DAT" #define D_SENSOR_MY92X1_DI "MY92x1 DI" #define D_SENSOR_MY92X1_DCKI "MY92x1 DCKI" #define D_SENSOR_ARIRFRCV "ALux IrRcv" @@ -696,14 +699,14 @@ #define D_SENSOR_TXD "Série TX" #define D_SENSOR_RXD "Série RX" #define D_SENSOR_ROTARY "Rotary" // Suffix "1A" -#define D_SENSOR_HRE_CLOCK "HRE Clock" -#define D_SENSOR_HRE_DATA "HRE Data" +#define D_SENSOR_HRE_CLOCK "HRE CLK" +#define D_SENSOR_HRE_DATA "HRE DAT" #define D_SENSOR_ADE7953_IRQ "ADE7953 IRQ" #define D_SENSOR_BUZZER "Buzzer" -#define D_SENSOR_OLED_RESET "OLED Reset" +#define D_SENSOR_OLED_RESET "OLED RST" #define D_SENSOR_ZIGBEE_TXD "ZigBee TX" #define D_SENSOR_ZIGBEE_RXD "ZigBee RX" -#define D_SENSOR_ZIGBEE_RST "ZigBee Rst" +#define D_SENSOR_ZIGBEE_RST "ZigBee RST" #define D_SENSOR_SOLAXX1_TX "SolaxX1 TX" #define D_SENSOR_SOLAXX1_RX "SolaxX1 RX" #define D_SENSOR_IBEACON_TX "iBeacon TX" @@ -714,36 +717,36 @@ #define D_SENSOR_A4988_STP "A4988 STP" #define D_SENSOR_A4988_ENA "A4988 ENA" #define D_SENSOR_A4988_MS1 "A4988 MS1" -#define D_SENSOR_OUTPUT_HI "Output Hi" -#define D_SENSOR_OUTPUT_LO "Output Lo" -#define D_SENSOR_AS608_TX "AS608 Tx" -#define D_SENSOR_AS608_RX "AS608 Rx" +#define D_SENSOR_OUTPUT_HI "Sortie Hi" +#define D_SENSOR_OUTPUT_LO "Sortie Lo" +#define D_SENSOR_AS608_TX "AS608 TX" +#define D_SENSOR_AS608_RX "AS608 RX" #define D_SENSOR_DDS2382_TX "DDS238-2 TX" #define D_SENSOR_DDS2382_RX "DDS238-2 RX" #define D_SENSOR_DDSU666_TX "DDSU666 TX" #define D_SENSOR_DDSU666_RX "DDSU666 RX" -#define D_SENSOR_SM2135_CLK "SM2135 Clk" -#define D_SENSOR_SM2135_DAT "SM2135 Dat" +#define D_SENSOR_SM2135_CLK "SM2135 CLK" +#define D_SENSOR_SM2135_DAT "SM2135 DAT" #define D_SENSOR_DEEPSLEEP "Hibernation" #define D_SENSOR_EXS_ENABLE "EXS Enable" #define D_SENSOR_CLIENT_TX "Esclave TX" #define D_SENSOR_CLIENT_RX "Esclave RX" -#define D_SENSOR_CLIENT_RESET "Esclave Rst" +#define D_SENSOR_CLIENT_RESET "Esclave RST" #define D_SENSOR_GPS_RX "GPS RX" #define D_SENSOR_GPS_TX "GPS TX" #define D_SENSOR_HM10_RX "HM10 RX" #define D_SENSOR_HM10_TX "HM10 TX" -#define D_SENSOR_LE01MR_RX "LE-01MR Rx" -#define D_SENSOR_LE01MR_TX "LE-01MR Tx" -#define D_SENSOR_BL0940_RX "BL0940 Rx" +#define D_SENSOR_LE01MR_RX "LE-01MR RX" +#define D_SENSOR_LE01MR_TX "LE-01MR TX" +#define D_SENSOR_BL0940_RX "BL0940 RX" #define D_SENSOR_CC1101_GDO0 "CC1101 GDO0" #define D_SENSOR_CC1101_GDO2 "CC1101 GDO2" -#define D_SENSOR_HRXL_RX "HRXL Rx" -#define D_SENSOR_DYP_RX "DYP Rx" -#define D_SENSOR_ELECTRIQ_MOODL "MOODL Tx" +#define D_SENSOR_HRXL_RX "HRXL RX" +#define D_SENSOR_DYP_RX "DYP RX" +#define D_SENSOR_ELECTRIQ_MOODL "MOODL TX" #define D_SENSOR_AS3935 "AS3935" #define D_SENSOR_WINDMETER_SPEED "Anémomètre" -#define D_SENSOR_TELEINFO_RX "TInfo Rx" +#define D_SENSOR_TELEINFO_RX "TInfo RX" #define D_SENSOR_TELEINFO_ENABLE "TInfo En" #define D_SENSOR_LMT01_PULSE "LMT01 Impulsion" #define D_SENSOR_ADC_INPUT "ADC Entrée" @@ -769,14 +772,14 @@ #define D_SENSOR_ETH_PHY_POWER "ETH POWER" #define D_SENSOR_ETH_PHY_MDC "ETH MDC" #define D_SENSOR_ETH_PHY_MDIO "ETH MDIO" -#define D_SENSOR_TCP_TXD "TCP Tx" -#define D_SENSOR_TCP_RXD "TCP Rx" +#define D_SENSOR_TCP_TXD "TCP TX" +#define D_SENSOR_TCP_RXD "TCP RX" #define D_SENSOR_IEM3000_TX "iEM3000 TX" #define D_SENSOR_IEM3000_RX "iEM3000 RX" -#define D_SENSOR_MIEL_HVAC_TX "MiEl HVAC Tx" -#define D_SENSOR_MIEL_HVAC_RX "MiEl HVAC Rx" -#define D_SENSOR_PROJECTOR_CTRL_TX "DLP Tx" -#define D_SENSOR_PROJECTOR_CTRL_RX "DLP Rx" +#define D_SENSOR_MIEL_HVAC_TX "MiEl HVAC TX" +#define D_SENSOR_MIEL_HVAC_RX "MiEl HVAC RX" +#define D_SENSOR_PROJECTOR_CTRL_TX "DLP TX" +#define D_SENSOR_PROJECTOR_CTRL_RX "DLP RX" #define D_SENSOR_SHELLY_DIMMER_BOOT0 "SHD Boot 0" #define D_SENSOR_SHELLY_DIMMER_RST_INV "SHD Reset" #define D_SENSOR_RC522_RST "RC522 Rst" @@ -799,8 +802,8 @@ #define D_SENSOR_SDCARD_CS "CarteSD CS" #define D_SENSOR_WIEGAND_D0 "Wiegand D0" #define D_SENSOR_WIEGAND_D1 "Wiegand D1" -#define D_SENSOR_NEOPOOL_TX "NeoPool Tx" -#define D_SENSOR_NEOPOOL_RX "NeoPool Rx" +#define D_SENSOR_NEOPOOL_TX "NeoPool TX" +#define D_SENSOR_NEOPOOL_RX "NeoPool RX" #define D_SENSOR_VL53L0X_XSHUT "VL53L0X XSHUT" #define D_NEW_ADDRESS "Setting address to" #define D_OUT_OF_RANGE "Out of Range" @@ -808,6 +811,9 @@ #define D_SENSOR_TFMINIPLUS_TX "TFmini+ TX" #define D_SENSOR_TFMINIPLUS_RX "TFmini+ RX" +#define D_NEW_ADDRESS "Positionner l'adresse à" +#define D_OUT_OF_RANGE "Hors limites" +#define D_SENSOR_DETECTED "détecté" // Units #define D_UNIT_AMPERE "A" @@ -855,8 +861,8 @@ #define D_UNIT_WATTHOUR "Wh" #define D_UNIT_WATT_METER_QUADRAT "W/m²" //SDM220, SDM120, SDM72, LE01MR -#define D_EXPORT_POWER "Export Power" -#define D_IMPORT_POWER "Import Power" +#define D_EXPORT_POWER "Puissance fournie" +#define D_IMPORT_POWER "Puissance consommée" #define D_PHASE_ANGLE "Angle de phase" #define D_IMPORT_ACTIVE "Énergie act conso" #define D_EXPORT_ACTIVE "Énergie act fournie" @@ -938,7 +944,7 @@ #define D_SENSOR_BOILER_OT_TX "OpenTherm TX" // xnrg_15_teleinfo Denky (Teleinfo) -#define D_CONTRACT "Type contrat" +#define D_CONTRACT "Type de contrat" #define D_POWER_LOAD "Charge actuelle" #define D_CURRENT_TARIFF "Tarif en cours" #define D_TARIFF "Tarif" @@ -964,7 +970,7 @@ #define D_FP_FEATUREFAIL "Empreinte trop petite" // 0x07 Failed to generate character file due to the lack of character point or small fingerprint image #define D_FP_NOMATCH "Le doigt ne correspond pas" // 0x08 Finger doesn't match #define D_FP_NOTFOUND "Pas de doigt correspondant" // 0x09 Failed to find matching finger -#define D_FP_ENROLLMISMATCH "Echec de la comparaison" // 0x0A Failed to combine the character files +#define D_FP_ENROLLMISMATCH "Échec de la comparaison" // 0x0A Failed to combine the character files #define D_FP_BADLOCATION "Erreur d'indexation" // 0x0B Addressed PageID is beyond the finger library #define D_FP_DBRANGEFAIL "Modèle invalide" // 0x0C Error when reading template from library or invalid template #define D_FP_UPLOADFEATUREFAIL "Erreur de transfert" // 0x0D Error when uploading template @@ -993,43 +999,43 @@ #define D_NEOPOOL_MACH_GENERIC "Generic" #define D_NEOPOOL_MACH_BAYROL "Bayrol" #define D_NEOPOOL_MACH_HAY "Hay" -#define D_NEOPOOL_FILTRATION_MANUAL "Manual" // Filtration modes +#define D_NEOPOOL_FILTRATION_MANUAL "Manuel" // Filtration modes #define D_NEOPOOL_FILTRATION_AUTO "Auto" -#define D_NEOPOOL_FILTRATION_HEATING "Heating" -#define D_NEOPOOL_FILTRATION_SMART "Smart" +#define D_NEOPOOL_FILTRATION_HEATING "Chauffage" +#define D_NEOPOOL_FILTRATION_SMART "Malin" #define D_NEOPOOL_FILTRATION_INTELLIGENT "Intelligent" -#define D_NEOPOOL_FILTRATION_BACKWASH "Backwash" +#define D_NEOPOOL_FILTRATION_BACKWASH "Rétro-lavage" #define D_NEOPOOL_FILTRATION_NONE "" // Filtration speed level -#define D_NEOPOOL_FILTRATION_SLOW "slow" -#define D_NEOPOOL_FILTRATION_MEDIUM "medium" -#define D_NEOPOOL_FILTRATION_FAST "fast" +#define D_NEOPOOL_FILTRATION_SLOW "lent" +#define D_NEOPOOL_FILTRATION_MEDIUM "moyen" +#define D_NEOPOOL_FILTRATION_FAST "rapide" #define D_NEOPOOL_TYPE "Type" // Sensor & relais names #define D_NEOPOOL_REDOX "Redox" -#define D_NEOPOOL_CHLORINE "Chlorine" -#define D_NEOPOOL_CONDUCTIVITY "Conductivity" -#define D_NEOPOOL_IONIZATION "Ionization" -#define D_NEOPOOL_HYDROLYSIS "Hydrolysis" -#define D_NEOPOOL_RELAY "Relay" +#define D_NEOPOOL_CHLORINE "Chlore" +#define D_NEOPOOL_CONDUCTIVITY "Conductivité" +#define D_NEOPOOL_IONIZATION "Ionisation" +#define D_NEOPOOL_HYDROLYSIS "Hydrolyse" +#define D_NEOPOOL_RELAY "Relais" #define D_NEOPOOL_RELAY_FILTRATION "Filtration" -#define D_NEOPOOL_RELAY_LIGHT "Light" -#define D_NEOPOOL_RELAY_PH_ACID "Acid pump" -#define D_NEOPOOL_RELAY_PH_BASE "Base pump" -#define D_NEOPOOL_RELAY_RX "Redox level" -#define D_NEOPOOL_RELAY_CL "Chlorine pump" -#define D_NEOPOOL_RELAY_CD "Brine pump" -#define D_NEOPOOL_TIME "Time" +#define D_NEOPOOL_RELAY_LIGHT "Lumière" +#define D_NEOPOOL_RELAY_PH_ACID "Pompe acide" +#define D_NEOPOOL_RELAY_PH_BASE "Pompe base" +#define D_NEOPOOL_RELAY_RX "Pompe RedOx" +#define D_NEOPOOL_RELAY_CL "Pompe Chlore" +#define D_NEOPOOL_RELAY_CD "Pompe Brome" +#define D_NEOPOOL_TIME "Durée" #define D_NEOPOOL_FILT_MODE "Filtration" #define D_NEOPOOL_POLARIZATION "Pol" // Sensor status #define D_NEOPOOL_PR_OFF "PrOff" -#define D_NEOPOOL_SETPOINT_OK "Ok" -#define D_NEOPOOL_COVER "Cover" -#define D_NEOPOOL_SHOCK "Shock" +#define D_NEOPOOL_SETPOINT_OK "OK" +#define D_NEOPOOL_COVER "Couverture" +#define D_NEOPOOL_SHOCK "Choc chlore" #define D_NEOPOOL_ALARM "! " -#define D_NEOPOOL_LOW "Low" +#define D_NEOPOOL_LOW "Bas" #define D_NEOPOOL_FLOW1 "FL1" #define D_NEOPOOL_FLOW2 "FL2" -#define D_NEOPOOL_PH_HIGH "too high" // ph Alarms -#define D_NEOPOOL_PH_LOW "too low" -#define D_NEOPOOL_PUMP_TIME_EXCEEDED "pump time exceeded" +#define D_NEOPOOL_PH_HIGH "Trop haut" // ph Alarms +#define D_NEOPOOL_PH_LOW "Trop bas" +#define D_NEOPOOL_PUMP_TIME_EXCEEDED "durée pompage expirée" #endif // _LANGUAGE_FR_FR_H_ diff --git a/tasmota/language/fy_NL.h b/tasmota/language/fy_NL.h index d607220ce..923664689 100644 --- a/tasmota/language/fy_NL.h +++ b/tasmota/language/fy_NL.h @@ -647,6 +647,9 @@ #define D_SENSOR_TM1638_CLK "TM1638 CLK" #define D_SENSOR_TM1638_DIO "TM1638 DIO" #define D_SENSOR_TM1638_STB "TM1638 STB" +#define D_SENSOR_MAX7219_DIN "MAX7219 DIN" +#define D_SENSOR_MAX7219_CS "MAX7219 CS" +#define D_SENSOR_MAX7219_CLK "MAX7219 CLK" #define D_SENSOR_HX711_SCK "HX711 SCK" #define D_SENSOR_HX711_DAT "HX711 DAT" #define D_SENSOR_FTC532 "FTC532" diff --git a/tasmota/language/he_HE.h b/tasmota/language/he_HE.h index 91f49739c..d3a7f7fe9 100644 --- a/tasmota/language/he_HE.h +++ b/tasmota/language/he_HE.h @@ -647,6 +647,9 @@ #define D_SENSOR_TM1638_CLK "TM1638 CLK" #define D_SENSOR_TM1638_DIO "TM1638 DIO" #define D_SENSOR_TM1638_STB "TM1638 STB" +#define D_SENSOR_MAX7219_DIN "MAX7219 DIN" +#define D_SENSOR_MAX7219_CS "MAX7219 CS" +#define D_SENSOR_MAX7219_CLK "MAX7219 CLK" #define D_SENSOR_HX711_SCK "HX711 SCK" #define D_SENSOR_HX711_DAT "HX711 DAT" #define D_SENSOR_FTC532 "FTC532" diff --git a/tasmota/language/hu_HU.h b/tasmota/language/hu_HU.h index fd2b3e075..68185612f 100644 --- a/tasmota/language/hu_HU.h +++ b/tasmota/language/hu_HU.h @@ -647,6 +647,9 @@ #define D_SENSOR_TM1638_CLK "TM1638 CLK" #define D_SENSOR_TM1638_DIO "TM1638 DIO" #define D_SENSOR_TM1638_STB "TM1638 STB" +#define D_SENSOR_MAX7219_DIN "MAX7219 DIN" +#define D_SENSOR_MAX7219_CS "MAX7219 CS" +#define D_SENSOR_MAX7219_CLK "MAX7219 CLK" #define D_SENSOR_HX711_SCK "HX711 SCK" #define D_SENSOR_HX711_DAT "HX711 DAT" #define D_SENSOR_FTC532 "FTC532" diff --git a/tasmota/language/it_IT.h b/tasmota/language/it_IT.h index 5e43527a4..540f5e007 100644 --- a/tasmota/language/it_IT.h +++ b/tasmota/language/it_IT.h @@ -647,6 +647,9 @@ #define D_SENSOR_TM1638_CLK "TM1638 - CLK" #define D_SENSOR_TM1638_DIO "TM1638 - DIO" #define D_SENSOR_TM1638_STB "TM1638 - STB" +#define D_SENSOR_MAX7219_DIN "MAX7219 - DIN" +#define D_SENSOR_MAX7219_CS "MAX7219 - CS" +#define D_SENSOR_MAX7219_CLK "MAX7219 - CLK" #define D_SENSOR_HX711_SCK "HX711 - SCK" #define D_SENSOR_HX711_DAT "HX711 - DAT" #define D_SENSOR_FTC532 "FTC532" diff --git a/tasmota/language/ko_KO.h b/tasmota/language/ko_KO.h index 061a93bca..0ed01d5b4 100644 --- a/tasmota/language/ko_KO.h +++ b/tasmota/language/ko_KO.h @@ -647,6 +647,9 @@ #define D_SENSOR_TM1638_CLK "TM1638 CLK" #define D_SENSOR_TM1638_DIO "TM1638 DIO" #define D_SENSOR_TM1638_STB "TM1638 STB" +#define D_SENSOR_MAX7219_DIN "MAX7219 DIN" +#define D_SENSOR_MAX7219_CS "MAX7219 CS" +#define D_SENSOR_MAX7219_CLK "MAX7219 CLK" #define D_SENSOR_HX711_SCK "HX711 SCK" #define D_SENSOR_HX711_DAT "HX711 DAT" #define D_SENSOR_FTC532 "FTC532" diff --git a/tasmota/language/nl_NL.h b/tasmota/language/nl_NL.h index 2be3f71b2..1c46cd6d6 100644 --- a/tasmota/language/nl_NL.h +++ b/tasmota/language/nl_NL.h @@ -647,6 +647,9 @@ #define D_SENSOR_TM1638_CLK "TM1638 CLK" #define D_SENSOR_TM1638_DIO "TM1638 DIO" #define D_SENSOR_TM1638_STB "TM1638 STB" +#define D_SENSOR_MAX7219_DIN "MAX7219 DIN" +#define D_SENSOR_MAX7219_CS "MAX7219 CS" +#define D_SENSOR_MAX7219_CLK "MAX7219 CLK" #define D_SENSOR_HX711_SCK "HX711 SCK" #define D_SENSOR_HX711_DAT "HX711 DAT" #define D_SENSOR_FTC532 "FTC532" diff --git a/tasmota/language/pl_PL.h b/tasmota/language/pl_PL.h index 1648bf058..a0d24bd77 100644 --- a/tasmota/language/pl_PL.h +++ b/tasmota/language/pl_PL.h @@ -647,6 +647,9 @@ #define D_SENSOR_TM1638_CLK "TM1638 CLK" #define D_SENSOR_TM1638_DIO "TM1638 DIO" #define D_SENSOR_TM1638_STB "TM1638 STB" +#define D_SENSOR_MAX7219_DIN "MAX7219 DIN" +#define D_SENSOR_MAX7219_CS "MAX7219 CS" +#define D_SENSOR_MAX7219_CLK "MAX7219 CLK" #define D_SENSOR_HX711_SCK "HX711 SCK" #define D_SENSOR_HX711_DAT "HX711 DAT" #define D_SENSOR_FTC532 "FTC532" diff --git a/tasmota/language/pt_BR.h b/tasmota/language/pt_BR.h index 9eff10649..c9ebccf8b 100644 --- a/tasmota/language/pt_BR.h +++ b/tasmota/language/pt_BR.h @@ -647,6 +647,9 @@ #define D_SENSOR_TM1638_CLK "TM1638 CLK" #define D_SENSOR_TM1638_DIO "TM1638 DIO" #define D_SENSOR_TM1638_STB "TM1638 STB" +#define D_SENSOR_MAX7219_DIN "MAX7219 DIN" +#define D_SENSOR_MAX7219_CS "MAX7219 CS" +#define D_SENSOR_MAX7219_CLK "MAX7219 CLK" #define D_SENSOR_HX711_SCK "HX711 SCK" #define D_SENSOR_HX711_DAT "HX711 DAT" #define D_SENSOR_FTC532 "FTC532" diff --git a/tasmota/language/pt_PT.h b/tasmota/language/pt_PT.h index 1d3a0bdf3..c897c9208 100644 --- a/tasmota/language/pt_PT.h +++ b/tasmota/language/pt_PT.h @@ -647,6 +647,9 @@ #define D_SENSOR_TM1638_CLK "TM1638 CLK" #define D_SENSOR_TM1638_DIO "TM1638 DIO" #define D_SENSOR_TM1638_STB "TM1638 STB" +#define D_SENSOR_MAX7219_DIN "MAX7219 DIN" +#define D_SENSOR_MAX7219_CS "MAX7219 CS" +#define D_SENSOR_MAX7219_CLK "MAX7219 CLK" #define D_SENSOR_HX711_SCK "HX711 SCK" #define D_SENSOR_HX711_DAT "HX711 DAT" #define D_SENSOR_FTC532 "FTC532" diff --git a/tasmota/language/ro_RO.h b/tasmota/language/ro_RO.h index 8098f9fdb..742665707 100644 --- a/tasmota/language/ro_RO.h +++ b/tasmota/language/ro_RO.h @@ -647,6 +647,9 @@ #define D_SENSOR_TM1638_CLK "TM1638 CLK" #define D_SENSOR_TM1638_DIO "TM1638 DIO" #define D_SENSOR_TM1638_STB "TM1638 STB" +#define D_SENSOR_MAX7219_DIN "MAX7219 DIN" +#define D_SENSOR_MAX7219_CS "MAX7219 CS" +#define D_SENSOR_MAX7219_CLK "MAX7219 CLK" #define D_SENSOR_HX711_SCK "HX711 SCK" #define D_SENSOR_HX711_DAT "HX711 DAT" #define D_SENSOR_FTC532 "FTC532" diff --git a/tasmota/language/ru_RU.h b/tasmota/language/ru_RU.h index 3c56f13a9..678bfa859 100644 --- a/tasmota/language/ru_RU.h +++ b/tasmota/language/ru_RU.h @@ -647,6 +647,9 @@ #define D_SENSOR_TM1638_CLK "TM1638 CLK" #define D_SENSOR_TM1638_DIO "TM1638 DIO" #define D_SENSOR_TM1638_STB "TM1638 STB" +#define D_SENSOR_MAX7219_DIN "MAX7219 DIN" +#define D_SENSOR_MAX7219_CS "MAX7219 CS" +#define D_SENSOR_MAX7219_CLK "MAX7219 CLK" #define D_SENSOR_HX711_SCK "HX711 SCK" #define D_SENSOR_HX711_DAT "HX711 DAT" #define D_SENSOR_FTC532 "FTC532" diff --git a/tasmota/language/sk_SK.h b/tasmota/language/sk_SK.h index 8705a7e9c..6f09c0444 100644 --- a/tasmota/language/sk_SK.h +++ b/tasmota/language/sk_SK.h @@ -647,6 +647,9 @@ #define D_SENSOR_TM1638_CLK "TM1638 CLK" #define D_SENSOR_TM1638_DIO "TM1638 DIO" #define D_SENSOR_TM1638_STB "TM1638 STB" +#define D_SENSOR_MAX7219_DIN "MAX7219 DIN" +#define D_SENSOR_MAX7219_CS "MAX7219 CS" +#define D_SENSOR_MAX7219_CLK "MAX7219 CLK" #define D_SENSOR_HX711_SCK "HX711 SCK" #define D_SENSOR_HX711_DAT "HX711 DAT" #define D_SENSOR_FTC532 "FTC532" diff --git a/tasmota/language/sv_SE.h b/tasmota/language/sv_SE.h index 13079ae46..df5ed9f0f 100644 --- a/tasmota/language/sv_SE.h +++ b/tasmota/language/sv_SE.h @@ -647,6 +647,9 @@ #define D_SENSOR_TM1638_CLK "TM1638 CLK" #define D_SENSOR_TM1638_DIO "TM1638 DIO" #define D_SENSOR_TM1638_STB "TM1638 STB" +#define D_SENSOR_MAX7219_DIN "MAX7219 DIN" +#define D_SENSOR_MAX7219_CS "MAX7219 CS" +#define D_SENSOR_MAX7219_CLK "MAX7219 CLK" #define D_SENSOR_HX711_SCK "HX711 SCK" #define D_SENSOR_HX711_DAT "HX711 DAT" #define D_SENSOR_FTC532 "FTC532" diff --git a/tasmota/language/tr_TR.h b/tasmota/language/tr_TR.h index 17b521ba5..4d83cc49e 100644 --- a/tasmota/language/tr_TR.h +++ b/tasmota/language/tr_TR.h @@ -647,6 +647,9 @@ #define D_SENSOR_TM1638_CLK "TM1638 CLK" #define D_SENSOR_TM1638_DIO "TM1638 DIO" #define D_SENSOR_TM1638_STB "TM1638 STB" +#define D_SENSOR_MAX7219_DIN "MAX7219 DIN" +#define D_SENSOR_MAX7219_CS "MAX7219 CS" +#define D_SENSOR_MAX7219_CLK "MAX7219 CLK" #define D_SENSOR_HX711_SCK "HX711 SCK" #define D_SENSOR_HX711_DAT "HX711 DAT" #define D_SENSOR_FTC532 "FTC532" diff --git a/tasmota/language/uk_UA.h b/tasmota/language/uk_UA.h index 9a5ebf98d..b49d5e6b5 100644 --- a/tasmota/language/uk_UA.h +++ b/tasmota/language/uk_UA.h @@ -647,6 +647,9 @@ #define D_SENSOR_TM1638_CLK "TM1638 CLK" #define D_SENSOR_TM1638_DIO "TM1638 DIO" #define D_SENSOR_TM1638_STB "TM1638 STB" +#define D_SENSOR_MAX7219_DIN "MAX7219 DIN" +#define D_SENSOR_MAX7219_CS "MAX7219 CS" +#define D_SENSOR_MAX7219_CLK "MAX7219 CLK" #define D_SENSOR_HX711_SCK "HX711 SCK" #define D_SENSOR_HX711_DAT "HX711 DAT" #define D_SENSOR_FTC532 "FTC532" diff --git a/tasmota/language/vi_VN.h b/tasmota/language/vi_VN.h index b1be0f94c..d309431f1 100644 --- a/tasmota/language/vi_VN.h +++ b/tasmota/language/vi_VN.h @@ -647,6 +647,9 @@ #define D_SENSOR_TM1638_CLK "TM1638 CLK" #define D_SENSOR_TM1638_DIO "TM1638 DIO" #define D_SENSOR_TM1638_STB "TM1638 STB" +#define D_SENSOR_MAX7219_DIN "MAX7219 DIN" +#define D_SENSOR_MAX7219_CS "MAX7219 CS" +#define D_SENSOR_MAX7219_CLK "MAX7219 CLK" #define D_SENSOR_HX711_SCK "HX711 SCK" #define D_SENSOR_HX711_DAT "HX711 DAT" #define D_SENSOR_FTC532 "FTC532" diff --git a/tasmota/language/zh_CN.h b/tasmota/language/zh_CN.h index 1d8bba996..4b0554dda 100644 --- a/tasmota/language/zh_CN.h +++ b/tasmota/language/zh_CN.h @@ -647,6 +647,9 @@ #define D_SENSOR_TM1638_CLK "TM1638 CLK" #define D_SENSOR_TM1638_DIO "TM1638 DIO" #define D_SENSOR_TM1638_STB "TM1638 STB" +#define D_SENSOR_MAX7219_DIN "MAX7219 DIN" +#define D_SENSOR_MAX7219_CS "MAX7219 CS" +#define D_SENSOR_MAX7219_CLK "MAX7219 CLK" #define D_SENSOR_HX711_SCK "HX711 SCK" #define D_SENSOR_HX711_DAT "HX711 DAT" #define D_SENSOR_FTC532 "FTC532" diff --git a/tasmota/language/zh_TW.h b/tasmota/language/zh_TW.h index 41a364d3b..4ec8a94dd 100644 --- a/tasmota/language/zh_TW.h +++ b/tasmota/language/zh_TW.h @@ -647,6 +647,9 @@ #define D_SENSOR_TM1638_CLK "TM1638 CLK" #define D_SENSOR_TM1638_DIO "TM1638 DIO" #define D_SENSOR_TM1638_STB "TM1638 STB" +#define D_SENSOR_MAX7219_DIN "MAX7219 DIN" +#define D_SENSOR_MAX7219_CS "MAX7219 CS" +#define D_SENSOR_MAX7219_CLK "MAX7219 CLK" #define D_SENSOR_HX711_SCK "HX711 SCK" #define D_SENSOR_HX711_DAT "HX711 DAT" #define D_SENSOR_FTC532 "FTC532" diff --git a/tasmota/my_user_config.h b/tasmota/my_user_config.h index 10de5d9fc..56315acd5 100644 --- a/tasmota/my_user_config.h +++ b/tasmota/my_user_config.h @@ -463,6 +463,11 @@ // #define SUPPORT_MQTT_EVENT // Support trigger event with MQTT subscriptions (+3k5 code) +// -- Berry Scripting Language - ESP32 only ---------------------------- +// #define USE_BERRY // Enable Berry scripting language + #define USE_BERRY_PSRAM // Allocate Berry memory in PSRAM if PSRAM is connected - this might be slightly slower but leaves main memory intact + + // -- Optional modules ---------------------------- #define ROTARY_V1 // Add support for Rotary Encoder as used in MI Desk Lamp (+0k8 code) #define ROTARY_MAX_STEPS 10 // Rotary step boundary diff --git a/tasmota/settings.h b/tasmota/settings.h index 05ab83922..804f47835 100644 --- a/tasmota/settings.h +++ b/tasmota/settings.h @@ -149,7 +149,7 @@ typedef union { // Restricted by MISRA-C Rule 18.4 bu uint32_t mqtt_state_retain : 1; // bit 7 (v9.3.0.1) - CMND_STATERETAIN uint32_t mqtt_info_retain : 1; // bit 8 (v9.3.0.1) - CMND_INFORETAIN uint32_t wiegand_hex_output : 1; // bit 9 (v9.3.1.1) - SetOption123 - (Wiegand) switch tag number output to hex format (1) - uint32_t wiegand_keypad_to_tag : 1; // bit 10 (v9.3.1.1) - SetOption124 - (Wiegand) send key pad stroke as single char (0) or one tag (ending char #) (1) + uint32_t wiegand_keypad_to_tag : 1; // bit 10 (v9.3.1.1) - SetOption124 - (Wiegand) send key pad stroke as single char (0) or one tag (ending char #) (1) uint32_t zigbee_hide_bridge_topic : 1; // bit 11 (v9.3.1.1) - SetOption125 - (Zigbee) Hide bridge topic from zigbee topic (use with SetOption89) (1) uint32_t spare12 : 1; // bit 12 uint32_t spare13 : 1; // bit 13 @@ -330,12 +330,12 @@ typedef struct { typedef union { uint8_t data; struct { - uint8_t ilimode : 3; - uint8_t Invert : 1; - uint8_t spare2 : 1; - uint8_t spare3 : 1; + uint8_t type : 3; + uint8_t invert : 1; uint8_t spare4 : 1; uint8_t spare5 : 1; + uint8_t spare6 : 1; + uint8_t spare7 : 1; }; } DisplayOptions; diff --git a/tasmota/support_esp.ino b/tasmota/support_esp.ino index 1f929f400..65dd3871c 100644 --- a/tasmota/support_esp.ino +++ b/tasmota/support_esp.ino @@ -74,6 +74,10 @@ void *special_malloc(uint32_t size) { return malloc(size); } +void *special_realloc(void *ptr, size_t size) { + return realloc(ptr, size); +} + #endif /*********************************************************************************************\ @@ -461,6 +465,13 @@ void *special_malloc(uint32_t size) { return malloc(size); } } +void *special_realloc(void *ptr, size_t size) { + if (psramFound()) { + return heap_caps_realloc(ptr, size, MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT); + } else { + return realloc(ptr, size); + } +} #endif // ESP32 diff --git a/tasmota/support_tasmota.ino b/tasmota/support_tasmota.ino index 9ad44d32e..94d18f9dc 100644 --- a/tasmota/support_tasmota.ino +++ b/tasmota/support_tasmota.ino @@ -556,18 +556,22 @@ void ExecuteCommandPower(uint32_t device, uint32_t state, uint32_t source) ((POWER_ON == state) || ((POWER_TOGGLE == state) && !(TasmotaGlobal.power & mask))) ) { interlock_mutex = true; // Clear all but masked relay in interlock group if new set requested + bool perform_interlock_delay = false; for (uint32_t i = 0; i < MAX_INTERLOCKS; i++) { if (Settings.interlock[i] & mask) { // Find interlock group for (uint32_t j = 0; j < TasmotaGlobal.devices_present; j++) { power_t imask = 1 << j; if ((Settings.interlock[i] & imask) && (TasmotaGlobal.power & imask) && (mask != imask)) { ExecuteCommandPower(j +1, POWER_OFF, SRC_IGNORE); - delay(50); // Add some delay to make sure never have more than one relay on + perform_interlock_delay = true; } } break; // An interlocked relay is only present in one group so quit } } + if (perform_interlock_delay) { + delay(50); // Add some delay to make sure never have more than one relay on + } interlock_mutex = false; } diff --git a/tasmota/tasmota_configurations.h b/tasmota/tasmota_configurations.h index 7a964b191..59257154a 100644 --- a/tasmota/tasmota_configurations.h +++ b/tasmota/tasmota_configurations.h @@ -307,6 +307,7 @@ #define USE_DISPLAY // Add Display Support (+2k code) #define USE_DISPLAY_TM1637 // [DisplayModel 15] Enable TM1637 module + #define USE_DISPLAY_MAX7219 // [DisplayModel 16] Enable MAX7219 7-segment module #define USE_I2C // I2C using library wire (+10k code, 0k2 mem, 124 iram) #define USE_DISPLAY_MODES1TO5 // Enable display mode 1 to 5 in addition to mode 0 diff --git a/tasmota/tasmota_configurations_ESP32.h b/tasmota/tasmota_configurations_ESP32.h index b00c65738..f5d161b38 100644 --- a/tasmota/tasmota_configurations_ESP32.h +++ b/tasmota/tasmota_configurations_ESP32.h @@ -55,6 +55,10 @@ #define USE_UFILESYS #define USE_SDCARD #define GUI_TRASH_FILE + +#define USE_BERRY // Enable Berry scripting language + #define USE_BERRY_PSRAM // Allocate Berry memory in PSRAM if PSRAM is connected - this might be slightly slower but leaves main memory intact + #define USE_ADC #define USE_SPI #define USE_DISPLAY // Add SPI Display Support (+2k code) @@ -84,6 +88,10 @@ #define USE_UFILESYS #define USE_SDCARD #define GUI_TRASH_FILE + +#define USE_BERRY // Enable Berry scripting language + #define USE_BERRY_PSRAM // Allocate Berry memory in PSRAM if PSRAM is connected - this might be slightly slower but leaves main memory intact + #define USE_I2C #define USE_BMA423 #define USE_MPU6886 diff --git a/tasmota/tasmota_template.h b/tasmota/tasmota_template.h index 2fa3882d6..8971e7482 100644 --- a/tasmota/tasmota_template.h +++ b/tasmota/tasmota_template.h @@ -156,6 +156,7 @@ enum UserSelectablePins { GPIO_CSE7761_TX, GPIO_CSE7761_RX, // CSE7761 Serial interface (Dual R3) GPIO_VL53L0X_XSHUT1, // VL53L0X_XSHUT (the max number of sensors is VL53L0X_MAX_SENSORS)- Used when connecting multiple VL53L0X GPIO_TFMINIPLUS_TX, GPIO_TFMINIPLUS_RX, // TFmini Plus ToF sensor + GPIO_MAX7219CLK, GPIO_MAX7219DIN, GPIO_MAX7219CS, // MAX7219 interface GPIO_SENSOR_END }; enum ProgramSelectablePins { @@ -332,9 +333,13 @@ const char kSensorNames[] PROGMEM = D_SENSOR_CSE7761_TX "|" D_SENSOR_CSE7761_RX "|" <<<<<<< HEAD D_SENSOR_VL53L0X_XSHUT "|" +<<<<<<< HEAD ======= >>>>>>> TFmini+ D_SENSOR_TFMINIPLUS_TX "|" D_SENSOR_TFMINIPLUS_RX "|" +======= + D_SENSOR_MAX7219_CLK "|" D_SENSOR_MAX7219_DIN "|" D_SENSOR_MAX7219_CS "|" +>>>>>>> upstream/development ; const char kSensorNamesFixed[] PROGMEM = @@ -798,8 +803,13 @@ const uint16_t kGpioNiceList[] PROGMEM = { #endif #ifdef USE_VL53L0X AGPIO(GPIO_VL53L0X_XSHUT1) + VL53L0X_MAX_SENSORS, // When using multiple VL53L0X. -#endif +#endif +#ifdef USE_DISPLAY_MAX7219 + AGPIO(GPIO_MAX7219CLK), + AGPIO(GPIO_MAX7219DIN), + AGPIO(GPIO_MAX7219CS), +#endif // USE_DISPLAY_MAX7219 /*-------------------------------------------------------------------------------------------*\ * ESP32 specifics \*-------------------------------------------------------------------------------------------*/ diff --git a/tasmota/xdrv_01_webserver.ino b/tasmota/xdrv_01_webserver.ino index 4595f50a2..7ecf647b4 100644 --- a/tasmota/xdrv_01_webserver.ino +++ b/tasmota/xdrv_01_webserver.ino @@ -214,12 +214,15 @@ const char HTTP_HEAD_STYLE3[] PROGMEM = "

" D_MINIMAL_FIRMWARE_PLEASE_UPGRADE "

" // COLOR_TEXT_WARNING #endif "
" // COLOR_TITLE +/* #ifdef LANGUAGE_MODULE_NAME "

" D_MODULE " %s

" #else "

%s " D_MODULE "

" #endif - "

%s

"; +*/ + "

%s

" // Module name + "

%s

"; // Device name const char HTTP_MSG_SLIDER_GRADIENT[] PROGMEM = "
" diff --git a/tasmota/xdrv_03_energy.ino b/tasmota/xdrv_03_energy.ino index 702267f2c..c8d326bec 100644 --- a/tasmota/xdrv_03_energy.ino +++ b/tasmota/xdrv_03_energy.ino @@ -36,15 +36,16 @@ #define D_CMND_POWERCAL "PowerCal" #define D_CMND_VOLTAGECAL "VoltageCal" #define D_CMND_CURRENTCAL "CurrentCal" +#define D_CMND_FREQUENCYCAL "FrequencyCal" #define D_CMND_TARIFF "Tariff" #define D_CMND_MODULEADDRESS "ModuleAddress" enum EnergyCommands { - CMND_POWERCAL, CMND_VOLTAGECAL, CMND_CURRENTCAL, + CMND_POWERCAL, CMND_VOLTAGECAL, CMND_CURRENTCAL, CMND_FREQUENCYCAL, CMND_POWERSET, CMND_VOLTAGESET, CMND_CURRENTSET, CMND_FREQUENCYSET, CMND_MODULEADDRESS }; const char kEnergyCommands[] PROGMEM = "|" // No prefix - D_CMND_POWERCAL "|" D_CMND_VOLTAGECAL "|" D_CMND_CURRENTCAL "|" + D_CMND_POWERCAL "|" D_CMND_VOLTAGECAL "|" D_CMND_CURRENTCAL "|" D_CMND_FREQUENCYCAL "|" D_CMND_POWERSET "|" D_CMND_VOLTAGESET "|" D_CMND_CURRENTSET "|" D_CMND_FREQUENCYSET "|" D_CMND_MODULEADDRESS "|" #ifdef USE_ENERGY_MARGIN_DETECTION D_CMND_POWERDELTA "|" D_CMND_POWERLOW "|" D_CMND_POWERHIGH "|" D_CMND_VOLTAGELOW "|" D_CMND_VOLTAGEHIGH "|" D_CMND_CURRENTLOW "|" D_CMND_CURRENTHIGH "|" @@ -57,7 +58,7 @@ const char kEnergyCommands[] PROGMEM = "|" // No prefix D_CMND_ENERGYRESET "|" D_CMND_TARIFF ; void (* const EnergyCommand[])(void) PROGMEM = { - &CmndPowerCal, &CmndVoltageCal, &CmndCurrentCal, + &CmndPowerCal, &CmndVoltageCal, &CmndCurrentCal, &CmndFrequencyCal, &CmndPowerSet, &CmndVoltageSet, &CmndCurrentSet, &CmndFrequencySet, &CmndModuleAddress, #ifdef USE_ENERGY_MARGIN_DETECTION &CmndPowerDelta, &CmndPowerLow, &CmndPowerHigh, &CmndVoltageLow, &CmndVoltageHigh, &CmndCurrentLow, &CmndCurrentHigh, @@ -547,14 +548,12 @@ void EnergyEverySecond(void) * Commands \*********************************************************************************************/ -void EnergyCommandCalResponse(uint32_t nvalue) -{ +void EnergyCommandCalResponse(uint32_t nvalue) { snprintf_P(XdrvMailbox.command, CMDSZ, PSTR("%sCal"), XdrvMailbox.command); ResponseCmndNumber(nvalue); } -void CmndEnergyReset(void) -{ +void CmndEnergyReset(void) { uint32_t values[2] = { 0 }; uint32_t params = ParseParameters(2, values); values[0] *= 100; @@ -641,8 +640,7 @@ void CmndEnergyReset(void) Settings.flag2.energy_resolution, &return2_kWhtotal); } -void CmndTariff(void) -{ +void CmndTariff(void) { // Tariff1 22:00,23:00 - Tariff1 start hour for Standard Time and Daylight Savings Time // Tariff2 6:00,7:00 - Tariff2 start hour for Standard Time and Daylight Savings Time // Tariffx 1320, 1380 = minutes and also 22:00, 23:00 @@ -686,11 +684,9 @@ void CmndTariff(void) GetStateText(Settings.flag3.energy_weekend)); // CMND_TARIFF } -void CmndPowerCal(void) -{ +void CmndPowerCal(void) { Energy.command_code = CMND_POWERCAL; if (XnrgCall(FUNC_COMMAND)) { // microseconds -// if ((XdrvMailbox.payload > 999) && (XdrvMailbox.payload < 32001)) { if (XdrvMailbox.payload > 999) { Settings.energy_power_calibration = XdrvMailbox.payload; } @@ -698,11 +694,9 @@ void CmndPowerCal(void) } } -void CmndVoltageCal(void) -{ +void CmndVoltageCal(void) { Energy.command_code = CMND_VOLTAGECAL; if (XnrgCall(FUNC_COMMAND)) { // microseconds -// if ((XdrvMailbox.payload > 999) && (XdrvMailbox.payload < 32001)) { if (XdrvMailbox.payload > 999) { Settings.energy_voltage_calibration = XdrvMailbox.payload; } @@ -710,11 +704,9 @@ void CmndVoltageCal(void) } } -void CmndCurrentCal(void) -{ +void CmndCurrentCal(void) { Energy.command_code = CMND_CURRENTCAL; if (XnrgCall(FUNC_COMMAND)) { // microseconds -// if ((XdrvMailbox.payload > 999) && (XdrvMailbox.payload < 32001)) { if (XdrvMailbox.payload > 999) { Settings.energy_current_calibration = XdrvMailbox.payload; } @@ -722,40 +714,45 @@ void CmndCurrentCal(void) } } -void CmndPowerSet(void) -{ +void CmndFrequencyCal(void) { + Energy.command_code = CMND_FREQUENCYCAL; + if (XnrgCall(FUNC_COMMAND)) { // microseconds + if (XdrvMailbox.payload > 999) { + Settings.energy_frequency_calibration = XdrvMailbox.payload; + } + ResponseCmndNumber(Settings.energy_frequency_calibration); + } +} + +void CmndPowerSet(void) { Energy.command_code = CMND_POWERSET; if (XnrgCall(FUNC_COMMAND)) { // Watt EnergyCommandCalResponse(Settings.energy_power_calibration); } } -void CmndVoltageSet(void) -{ +void CmndVoltageSet(void) { Energy.command_code = CMND_VOLTAGESET; if (XnrgCall(FUNC_COMMAND)) { // Volt EnergyCommandCalResponse(Settings.energy_voltage_calibration); } } -void CmndCurrentSet(void) -{ +void CmndCurrentSet(void) { Energy.command_code = CMND_CURRENTSET; if (XnrgCall(FUNC_COMMAND)) { // milliAmpere EnergyCommandCalResponse(Settings.energy_current_calibration); } } -void CmndFrequencySet(void) -{ +void CmndFrequencySet(void) { Energy.command_code = CMND_FREQUENCYSET; if (XnrgCall(FUNC_COMMAND)) { // Hz EnergyCommandCalResponse(Settings.energy_frequency_calibration); } } -void CmndModuleAddress(void) -{ +void CmndModuleAddress(void) { if ((XdrvMailbox.payload > 0) && (XdrvMailbox.payload < 4) && (1 == Energy.phase_count)) { Energy.command_code = CMND_MODULEADDRESS; if (XnrgCall(FUNC_COMMAND)) { // Module address @@ -765,8 +762,7 @@ void CmndModuleAddress(void) } #ifdef USE_ENERGY_MARGIN_DETECTION -void CmndPowerDelta(void) -{ +void CmndPowerDelta(void) { if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= 3)) { if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload < 32000)) { Settings.energy_power_delta[XdrvMailbox.index -1] = XdrvMailbox.payload; @@ -775,48 +771,42 @@ void CmndPowerDelta(void) } } -void CmndPowerLow(void) -{ +void CmndPowerLow(void) { if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload < 3601)) { Settings.energy_min_power = XdrvMailbox.payload; } ResponseCmndNumber(Settings.energy_min_power); } -void CmndPowerHigh(void) -{ +void CmndPowerHigh(void) { if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload < 3601)) { Settings.energy_max_power = XdrvMailbox.payload; } ResponseCmndNumber(Settings.energy_max_power); } -void CmndVoltageLow(void) -{ +void CmndVoltageLow(void) { if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload < 501)) { Settings.energy_min_voltage = XdrvMailbox.payload; } ResponseCmndNumber(Settings.energy_min_voltage); } -void CmndVoltageHigh(void) -{ +void CmndVoltageHigh(void) { if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload < 501)) { Settings.energy_max_voltage = XdrvMailbox.payload; } ResponseCmndNumber(Settings.energy_max_voltage); } -void CmndCurrentLow(void) -{ +void CmndCurrentLow(void) { if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload < 16001)) { Settings.energy_min_current = XdrvMailbox.payload; } ResponseCmndNumber(Settings.energy_min_current); } -void CmndCurrentHigh(void) -{ +void CmndCurrentHigh(void) { if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload < 16001)) { Settings.energy_max_current = XdrvMailbox.payload; } @@ -824,56 +814,49 @@ void CmndCurrentHigh(void) } #ifdef USE_ENERGY_POWER_LIMIT -void CmndMaxPower(void) -{ +void CmndMaxPower(void) { if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload < 3601)) { Settings.energy_max_power_limit = XdrvMailbox.payload; } ResponseCmndNumber(Settings.energy_max_power_limit); } -void CmndMaxPowerHold(void) -{ +void CmndMaxPowerHold(void) { if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload < 3601)) { Settings.energy_max_power_limit_hold = (1 == XdrvMailbox.payload) ? MAX_POWER_HOLD : XdrvMailbox.payload; } ResponseCmndNumber(Settings.energy_max_power_limit_hold); } -void CmndMaxPowerWindow(void) -{ +void CmndMaxPowerWindow(void) { if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload < 3601)) { Settings.energy_max_power_limit_window = (1 == XdrvMailbox.payload) ? MAX_POWER_WINDOW : XdrvMailbox.payload; } ResponseCmndNumber(Settings.energy_max_power_limit_window); } -void CmndSafePower(void) -{ +void CmndSafePower(void) { if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload < 3601)) { Settings.energy_max_power_safe_limit = XdrvMailbox.payload; } ResponseCmndNumber(Settings.energy_max_power_safe_limit); } -void CmndSafePowerHold(void) -{ +void CmndSafePowerHold(void) { if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload < 3601)) { Settings.energy_max_power_safe_limit_hold = (1 == XdrvMailbox.payload) ? SAFE_POWER_HOLD : XdrvMailbox.payload; } ResponseCmndNumber(Settings.energy_max_power_safe_limit_hold); } -void CmndSafePowerWindow(void) -{ +void CmndSafePowerWindow(void) { if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload < 1440)) { Settings.energy_max_power_safe_limit_window = (1 == XdrvMailbox.payload) ? SAFE_POWER_WINDOW : XdrvMailbox.payload; } ResponseCmndNumber(Settings.energy_max_power_safe_limit_window); } -void CmndMaxEnergy(void) -{ +void CmndMaxEnergy(void) { if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload < 3601)) { Settings.energy_max_energy = XdrvMailbox.payload; Energy.max_energy_state = 3; @@ -881,8 +864,7 @@ void CmndMaxEnergy(void) ResponseCmndNumber(Settings.energy_max_energy); } -void CmndMaxEnergyStart(void) -{ +void CmndMaxEnergyStart(void) { if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload < 24)) { Settings.energy_max_energy_start = XdrvMailbox.payload; } diff --git a/tasmota/xdrv_05_irremote.ino b/tasmota/xdrv_05_irremote.ino index 0cacb2161..fa4831746 100644 --- a/tasmota/xdrv_05_irremote.ino +++ b/tasmota/xdrv_05_irremote.ino @@ -176,7 +176,10 @@ void IrSendInit(void) \*********************************************************************************************/ const bool IR_RCV_SAVE_BUFFER = false; // false = do not use buffer, true = use buffer for decoding -const uint32_t IR_TIME_AVOID_DUPLICATE = 500; // Milliseconds + +#ifndef IR_TIME_AVOID_DUPLICATE +#define IR_TIME_AVOID_DUPLICATE 50 // Milliseconds +#endif // IR_TIME_AVOID_DUPLICATE #include diff --git a/tasmota/xdrv_05_irremote_full.ino b/tasmota/xdrv_05_irremote_full.ino index 66d0e17d4..00b6cf7bd 100644 --- a/tasmota/xdrv_05_irremote_full.ino +++ b/tasmota/xdrv_05_irremote_full.ino @@ -167,7 +167,10 @@ uint64_t reverseBitsInBytes64(uint64_t b) { \*********************************************************************************************/ const bool IR_FULL_RCV_SAVE_BUFFER = false; // false = do not use buffer, true = use buffer for decoding -const uint32_t IR_TIME_AVOID_DUPLICATE = 50; // Milliseconds + +#ifndef IR_TIME_AVOID_DUPLICATE +#define IR_TIME_AVOID_DUPLICATE 50 // Milliseconds +#endif // IR_TIME_AVOID_DUPLICATE // Below is from IRrecvDumpV2.ino // As this program is a special purpose capture/decoder, let us use a larger diff --git a/tasmota/xdrv_10_scripter.ino b/tasmota/xdrv_10_scripter.ino index 97ebfad30..326c0df47 100755 --- a/tasmota/xdrv_10_scripter.ino +++ b/tasmota/xdrv_10_scripter.ino @@ -67,8 +67,8 @@ keywords if then else endif, or, and are better readable for beginners (others m #define MAX_SARRAY_NUM 32 -//uint32_t EncodeLightId(uint8_t relay_id); -//uint32_t DecodeLightId(uint32_t hue_id); +uint32_t EncodeLightId(uint8_t relay_id); +uint32_t DecodeLightId(uint32_t hue_id); #define SPECIAL_EEPMODE_SIZE 6200 @@ -776,7 +776,7 @@ char *script; script_mem_size += 16; uint8_t *script_mem; - script_mem = (uint8_t*)calloc(script_mem_size, 1); + script_mem = (uint8_t*)special_malloc(script_mem_size); if (!script_mem) { if (imemptr) free(imemptr); return -4; @@ -2175,7 +2175,7 @@ chknext: if (ef) { uint16_t fsiz = ef.size(); if (fsiz<2048) { - char *script = (char*)calloc(fsiz + 16, 1); + char *script = (char*)special_malloc(fsiz + 16); if (script) { ef.read((uint8_t*)script,fsiz); execute_script(script); @@ -3643,15 +3643,22 @@ int32_t UpdVar(char *vname, float *fvar, uint32_t mode) { if (vtype == NUM_RES || (vtype & STYPE) == 0) { if (mode) { // set var + //AddLog(LOG_LEVEL_DEBUG, PSTR("write from homekit: %s - %d"), vname, (uint32_t)res); index = glob_script_mem.type[ind.index].index; glob_script_mem.fvars[index] = res; glob_script_mem.type[ind.index].bits.changed = 1; +#ifdef USE_SCRIPT_GLOBVARS + if (glob_script_mem.type[ind.index].bits.global) { + script_udp_sendvar(vname, &res, 0); + } +#endif //USE_SCRIPT_GLOBVARS return 0; } else { // get var //index = glob_script_mem.type[ind.index].index; int32_t ret = glob_script_mem.type[ind.index].bits.hchanged; glob_script_mem.type[ind.index].bits.hchanged = 0; + //AddLog(LOG_LEVEL_DEBUG, PSTR("read from homekit: %s - %d - %d"), vname, (uint32_t)*fvar, ret); return ret; } } else { @@ -6357,7 +6364,8 @@ void ScriptGetSDCard(void) { if (!HttpCheckPriviledgedAccess()) { return; } String stmp = Webserver->uri(); - char *cp = strstr_P(stmp.c_str(), PSTR("/sdc/")); + + char *cp = strstr_P(stmp.c_str(), PSTR("/ufs/")); // if (cp) Serial.printf(">>>%s\n",cp); if (cp) { #ifdef ESP32 @@ -6365,13 +6373,15 @@ void ScriptGetSDCard(void) { #else cp += 5; #endif - if (strstr_P(cp, PSTR("scrdmp.bmp"))) { - SendFile(cp); - return; - } else { - if (ufsp->exists(cp)) { + if (ufsp) { + if (strstr_P(cp, PSTR("scrdmp.bmp"))) { SendFile(cp); return; + } else { + if (ufsp->exists(cp)) { + SendFile(cp); + return; + } } } } @@ -6392,7 +6402,6 @@ char buff[512]; #ifdef USE_DISPLAY_DUMP char *sbmp = strstr_P(fname, PSTR("scrdmp.bmp")); if (sbmp) { - mime = "image/bmp"; sflg = 1; } #endif // USE_DISPLAY_DUMP @@ -6419,7 +6428,7 @@ char buff[512]; #define infoHeaderSize 40 if (buffer) { uint8_t *bp = buffer; - uint8_t *lbuf = (uint8_t*)calloc(Settings.display_width + 2, 3); + uint8_t *lbuf = (uint8_t*)special_malloc(Settings.display_width + 2); uint8_t *lbp; uint8_t fileHeader[fileHeaderSize]; createBitmapFileHeader(Settings.display_height , Settings.display_width , fileHeader); @@ -7679,7 +7688,7 @@ bool Xdrv10(uint8_t function) // we have a file system AddLog(LOG_LEVEL_INFO,PSTR("UFILESYSTEM OK!")); char *script; - script = (char*)calloc(UFSYS_SIZE + 4, 1); + script = (char*)special_malloc(UFSYS_SIZE + 4); if (!script) break; glob_script_mem.script_ram = script; glob_script_mem.script_size = UFSYS_SIZE; @@ -7850,6 +7859,10 @@ bool Xdrv10(uint8_t function) Webserver->on("/sfd", ScriptFullWebpage); } #endif // SCRIPT_FULL_WEBPAGE + +#ifdef USE_UFILESYS + Webserver->onNotFound(ScriptGetSDCard); +#endif // USE_UFILESYS } break; #endif // USE_SCRIPT_WEB_DISPLAY @@ -7858,7 +7871,6 @@ bool Xdrv10(uint8_t function) Webserver->on("/ta",HTTP_POST, HandleScriptTextareaConfiguration); Webserver->on("/exs", HTTP_POST,[]() { Webserver->sendHeader("Location","/exs");Webserver->send(303);}, script_upload_start); Webserver->on("/exs", HTTP_GET, ScriptExecuteUploadSuccess); - break; #endif // USE_WEBSERVER case FUNC_SAVE_BEFORE_RESTART: if (bitRead(Settings.rule_enabled, 0)) { diff --git a/tasmota/xdrv_13_display.ino b/tasmota/xdrv_13_display.ino index cf5e384eb..ee9777ca6 100755 --- a/tasmota/xdrv_13_display.ino +++ b/tasmota/xdrv_13_display.ino @@ -54,22 +54,24 @@ const uint8_t DISPLAY_LOG_ROWS = 32; // Number of lines in display log #define D_CMND_DISP_DIMMER "Dimmer" #define D_CMND_DISP_MODE "Mode" #define D_CMND_DISP_MODEL "Model" +#define D_CMND_DISP_TYPE "Type" #define D_CMND_DISP_REFRESH "Refresh" #define D_CMND_DISP_ROWS "Rows" #define D_CMND_DISP_SIZE "Size" #define D_CMND_DISP_FONT "Font" #define D_CMND_DISP_ROTATE "Rotate" -#define D_CMND_DISP_TEXT "Text" +#define D_CMND_DISP_INVERT "Invert" #define D_CMND_DISP_WIDTH "Width" #define D_CMND_DISP_HEIGHT "Height" #define D_CMND_DISP_BLINKRATE "Blinkrate" #define D_CMND_DISP_BATCH "Batch" +#define D_CMND_DISP_TEXT "Text" + #define D_CMND_DISP_CLEAR "Clear" #define D_CMND_DISP_NUMBER "Number" #define D_CMND_DISP_FLOAT "Float" -#define D_CMND_DISP_NUMBERNC "NumberNC" // NC - "No Clear" -#define D_CMND_DISP_FLOATNC "FloatNC" // NC - "No Clear" -#define D_CMND_DISP_BRIGHTNESS "Brightness" +#define D_CMND_DISP_NUMBERNC "NumberNC" // NC - "No Clear" +#define D_CMND_DISP_FLOATNC "FloatNC" // NC - "No Clear" #define D_CMND_DISP_RAW "Raw" #define D_CMND_DISP_LEVEL "Level" #define D_CMND_DISP_SEVENSEG_TEXT "SevensegText" @@ -78,9 +80,6 @@ const uint8_t DISPLAY_LOG_ROWS = 32; // Number of lines in display log #define D_CMND_DISP_CLOCK "Clock" #define D_CMND_DISP_TEXTNC "TextNC" // NC - "No Clear" #define D_CMND_DISP_SCROLLTEXT "ScrollText" -#define D_CMND_DISP_ILIMODE "ILIMode" -#define D_CMND_DISP_ILIINVERT "Invert" - enum XdspFunctions { FUNC_DISPLAY_INIT_DRIVER, FUNC_DISPLAY_INIT, FUNC_DISPLAY_EVERY_50_MSECOND, FUNC_DISPLAY_EVERY_SECOND, FUNC_DISPLAY_MODEL, FUNC_DISPLAY_MODE, FUNC_DISPLAY_POWER, @@ -101,31 +100,71 @@ enum XdspFunctions { FUNC_DISPLAY_INIT_DRIVER, FUNC_DISPLAY_INIT, FUNC_DISPLAY_E enum DisplayInitModes { DISPLAY_INIT_MODE, DISPLAY_INIT_PARTIAL, DISPLAY_INIT_FULL }; const char kDisplayCommands[] PROGMEM = D_PRFX_DISPLAY "|" // Prefix - "|" D_CMND_DISP_MODEL "|" D_CMND_DISP_WIDTH "|" D_CMND_DISP_HEIGHT "|" D_CMND_DISP_MODE "|" D_CMND_DISP_REFRESH "|" - D_CMND_DISP_DIMMER "|" D_CMND_DISP_COLS "|" D_CMND_DISP_ROWS "|" D_CMND_DISP_SIZE "|" D_CMND_DISP_FONT "|" - D_CMND_DISP_ROTATE "|" D_CMND_DISP_TEXT "|" D_CMND_DISP_ADDRESS "|" D_CMND_DISP_BLINKRATE "|" + "|" D_CMND_DISP_MODEL "|" D_CMND_DISP_TYPE "|" D_CMND_DISP_WIDTH "|" D_CMND_DISP_HEIGHT "|" D_CMND_DISP_MODE "|" + D_CMND_DISP_INVERT "|" D_CMND_DISP_REFRESH "|" D_CMND_DISP_DIMMER "|" D_CMND_DISP_COLS "|" D_CMND_DISP_ROWS "|" + D_CMND_DISP_SIZE "|" D_CMND_DISP_FONT "|" D_CMND_DISP_ROTATE "|" D_CMND_DISP_TEXT "|" D_CMND_DISP_ADDRESS "|" D_CMND_DISP_BLINKRATE "|" #ifdef USE_UFILESYS D_CMND_DISP_BATCH "|" #endif D_CMND_DISP_CLEAR "|" D_CMND_DISP_NUMBER "|" D_CMND_DISP_FLOAT "|" D_CMND_DISP_NUMBERNC "|" D_CMND_DISP_FLOATNC "|" D_CMND_DISP_RAW "|" D_CMND_DISP_LEVEL "|" D_CMND_DISP_SEVENSEG_TEXT "|" D_CMND_DISP_SEVENSEG_TEXTNC "|" - D_CMND_DISP_SCROLLDELAY "|" D_CMND_DISP_CLOCK "|" D_CMND_DISP_TEXTNC "|" - D_CMND_DISP_SCROLLTEXT "|" D_CMND_DISP_ILIMODE "|" D_CMND_DISP_ILIINVERT + D_CMND_DISP_SCROLLDELAY "|" D_CMND_DISP_CLOCK "|" D_CMND_DISP_TEXTNC "|" D_CMND_DISP_SCROLLTEXT ; void (* const DisplayCommand[])(void) PROGMEM = { - &CmndDisplay, &CmndDisplayModel, &CmndDisplayWidth, &CmndDisplayHeight, &CmndDisplayMode, &CmndDisplayRefresh, - &CmndDisplayDimmer, &CmndDisplayColumns, &CmndDisplayRows, &CmndDisplaySize, &CmndDisplayFont, - &CmndDisplayRotate, &CmndDisplayText, &CmndDisplayAddress, &CmndDisplayBlinkrate, + &CmndDisplay, &CmndDisplayModel, &CmndDisplayType, &CmndDisplayWidth, &CmndDisplayHeight, &CmndDisplayMode, + &CmndDisplayInvert, &CmndDisplayRefresh, &CmndDisplayDimmer, &CmndDisplayColumns, &CmndDisplayRows, + &CmndDisplaySize, &CmndDisplayFont, &CmndDisplayRotate, &CmndDisplayText, &CmndDisplayAddress, &CmndDisplayBlinkrate, #ifdef USE_UFILESYS &CmndDisplayBatch, #endif &CmndDisplayClear, &CmndDisplayNumber, &CmndDisplayFloat, &CmndDisplayNumberNC, &CmndDisplayFloatNC, &CmndDisplayRaw, &CmndDisplayLevel, &CmndDisplaySevensegText, &CmndDisplaySevensegTextNC, - &CmndDisplayScrollDelay, &CmndDisplayClock, &CmndDisplayTextNC, - &CmndDisplayScrollText, &CmndDisplayILIMOde , &CmndDisplayILIInvert + &CmndDisplayScrollDelay, &CmndDisplayClock, &CmndDisplayTextNC, &CmndDisplayScrollText }; +#ifdef USE_GRAPH + +typedef union { + uint8_t data; + struct { + uint8_t overlay : 1; + uint8_t draw : 1; + uint8_t nu3 : 1; + uint8_t nu4 : 1; + uint8_t nu5 : 1; + uint8_t nu6 : 1; + uint8_t nu7 : 1; + uint8_t nu8 : 1; + }; +} GFLAGS; + +struct GRAPH { + uint16_t xp; + uint16_t yp; + uint16_t xs; + uint16_t ys; + float ymin; + float ymax; + float range; + uint32_t x_time; // time per x slice in milliseconds + uint32_t last_ms; + uint32_t last_ms_redrawn; + int16_t decimation; // decimation or graph duration in minutes + uint16_t dcnt; + uint32_t summ; + uint16_t xcnt; + uint8_t *values; + uint8_t xticks; + uint8_t yticks; + uint8_t last_val; + uint8_t color_index; + GFLAGS flags; +}; + +struct GRAPH *graph[NUM_GRAPHS]; +#endif // USE_GRAPH + char *dsp_str; uint16_t dsp_x; @@ -1637,18 +1676,16 @@ void DisplaySetPower(void) * Commands \*********************************************************************************************/ -void CmndDisplay(void) -{ - Response_P(PSTR("{\"" D_PRFX_DISPLAY "\":{\"" D_CMND_DISP_MODEL "\":%d,\"" D_CMND_DISP_WIDTH "\":%d,\"" D_CMND_DISP_HEIGHT "\":%d,\"" +void CmndDisplay(void) { + Response_P(PSTR("{\"" D_PRFX_DISPLAY "\":{\"" D_CMND_DISP_MODEL "\":%d,\"" D_CMND_DISP_TYPE "\":%d,\"" D_CMND_DISP_WIDTH "\":%d,\"" D_CMND_DISP_HEIGHT "\":%d,\"" D_CMND_DISP_MODE "\":%d,\"" D_CMND_DISP_DIMMER "\":%d,\"" D_CMND_DISP_SIZE "\":%d,\"" D_CMND_DISP_FONT "\":%d,\"" - D_CMND_DISP_ROTATE "\":%d,\"" D_CMND_DISP_REFRESH "\":%d,\"" D_CMND_DISP_COLS "\":[%d,%d],\"" D_CMND_DISP_ROWS "\":%d}}"), - Settings.display_model, Settings.display_width, Settings.display_height, - Settings.display_mode, ((Settings.display_dimmer * 666) / 100) +1, Settings.display_size, Settings.display_font, - Settings.display_rotate, Settings.display_refresh, Settings.display_cols[0], Settings.display_cols[1], Settings.display_rows); + D_CMND_DISP_ROTATE "\":%d,\"" D_CMND_DISP_INVERT "\":%d,\"" D_CMND_DISP_REFRESH "\":%d,\"" D_CMND_DISP_COLS "\":[%d,%d],\"" D_CMND_DISP_ROWS "\":%d}}"), + Settings.display_model, Settings.display_options.type, Settings.display_width, Settings.display_height, + Settings.display_mode, changeUIntScale(Settings.display_dimmer, 0, 15, 0, 100), Settings.display_size, Settings.display_font, + Settings.display_rotate, Settings.display_options.invert, Settings.display_refresh, Settings.display_cols[0], Settings.display_cols[1], Settings.display_rows); } -void CmndDisplayModel(void) -{ +void CmndDisplayModel(void) { if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload < DISPLAY_MAX_DRIVERS)) { uint32_t last_display_model = Settings.display_model; Settings.display_model = XdrvMailbox.payload; @@ -1661,8 +1698,15 @@ void CmndDisplayModel(void) ResponseCmndNumber(Settings.display_model); } -void CmndDisplayWidth(void) -{ +void CmndDisplayType(void) { + if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= 7)) { + Settings.display_options.type = XdrvMailbox.payload; + TasmotaGlobal.restart_flag = 2; + } + ResponseCmndNumber(Settings.display_options.type); +} + +void CmndDisplayWidth(void) { if (XdrvMailbox.payload > 0) { if (XdrvMailbox.payload != Settings.display_width) { Settings.display_width = XdrvMailbox.payload; @@ -1672,8 +1716,7 @@ void CmndDisplayWidth(void) ResponseCmndNumber(Settings.display_width); } -void CmndDisplayHeight(void) -{ +void CmndDisplayHeight(void) { if (XdrvMailbox.payload > 0) { if (XdrvMailbox.payload != Settings.display_height) { Settings.display_height = XdrvMailbox.payload; @@ -1683,8 +1726,7 @@ void CmndDisplayHeight(void) ResponseCmndNumber(Settings.display_height); } -void CmndDisplayMode(void) -{ +void CmndDisplayMode(void) { #ifdef USE_DISPLAY_MODES1TO5 /* Matrix / 7-segment LCD / Oled TFT * 1 = Text up and time Time @@ -1716,7 +1758,7 @@ void CmndDisplayMode(void) void CmndDisplayDimmer(void) { if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= 100)) { - Settings.display_dimmer = ((XdrvMailbox.payload +1) * 100) / 666; // Correction for Domoticz (0 - 15) + Settings.display_dimmer = changeUIntScale(XdrvMailbox.payload, 0, 100, 0, 15); // Correction for Domoticz (0 - 15) if (Settings.display_dimmer && !(disp_power)) { ExecuteCommandPower(disp_device, POWER_ON, SRC_DISPLAY); } @@ -1729,146 +1771,11 @@ void CmndDisplayDimmer(void) { XdspCall(FUNC_DISPLAY_DIM); } } - ResponseCmndNumber(((Settings.display_dimmer * 666) / 100) +1); + ResponseCmndNumber(changeUIntScale(Settings.display_dimmer, 0, 15, 0, 100)); } -void CmndDisplayBlinkrate(void) -{ - if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= 3)) { - - if (!renderer) - XdspCall(FUNC_DISPLAY_BLINKRATE); - } - ResponseCmndNumber(XdrvMailbox.payload); -} - - -#ifdef USE_UFILESYS -void CmndDisplayBatch(void) { - if (XdrvMailbox.data_len > 0) { - if (!Settings.display_mode) { - Display_Text_From_File(XdrvMailbox.data); - } - ResponseCmndChar(XdrvMailbox.data); - } -} -#endif - - -void CmndDisplayClear(void) -{ - if (!renderer) - XdspCall(FUNC_DISPLAY_CLEAR); - ResponseCmndChar(XdrvMailbox.data); -} - -void CmndDisplayNumber(void) -{ - if (!renderer) { - XdspCall(FUNC_DISPLAY_NUMBER); - } - ResponseCmndChar(XdrvMailbox.data); -} - -void CmndDisplayFloat(void) -{ - if (!renderer) { - XdspCall(FUNC_DISPLAY_FLOAT); - } - ResponseCmndChar(XdrvMailbox.data); -} - -void CmndDisplayNumberNC(void) -{ - if (!renderer) { - XdspCall(FUNC_DISPLAY_NUMBERNC); - } - ResponseCmndChar(XdrvMailbox.data); -} - -void CmndDisplayFloatNC(void) -{ - if (!renderer) { - XdspCall(FUNC_DISPLAY_FLOATNC); - } - ResponseCmndChar(XdrvMailbox.data); -} - -void CmndDisplayRaw(void) -{ - if (!renderer) { - XdspCall(FUNC_DISPLAY_RAW); - } - ResponseCmndChar(XdrvMailbox.data); -} - -void CmndDisplayLevel(void) -{ - bool result = false; - if (!renderer) { - result = XdspCall(FUNC_DISPLAY_LEVEL); - } - if(result) ResponseCmndNumber(XdrvMailbox.payload); -} - -void CmndDisplaySevensegText(void) -{ - if (!renderer) { - XdspCall(FUNC_DISPLAY_SEVENSEG_TEXT); - } - ResponseCmndChar(XdrvMailbox.data); -} - -void CmndDisplayTextNC(void) -{ - if (!renderer) { - XdspCall(FUNC_DISPLAY_SEVENSEG_TEXTNC); - } - ResponseCmndChar(XdrvMailbox.data); -} - -void CmndDisplaySevensegTextNC(void) -{ - if (!renderer) { - XdspCall(FUNC_DISPLAY_SEVENSEG_TEXTNC); - } - ResponseCmndChar(XdrvMailbox.data); -} - -void CmndDisplayScrollDelay(void) -{ - if (!renderer) { - XdspCall(FUNC_DISPLAY_SCROLLDELAY); - } - ResponseCmndNumber(XdrvMailbox.payload); -} - -void CmndDisplayClock(void) -{ - if (!renderer) { - XdspCall(FUNC_DISPLAY_CLOCK); - } - ResponseCmndNumber(XdrvMailbox.payload); -} - - -void CmndDisplayScrollText(void) -{ - bool result = false; - if (!renderer) { - result = XdspCall(FUNC_DISPLAY_SCROLLTEXT); - } - if(result) ResponseCmndChar(XdrvMailbox.data); -} - - -void CmndDisplaySize(void) -{ -#ifdef USE_DISPLAY_TM1637 - if ((XdrvMailbox.payload > 0) && (XdrvMailbox.payload <= 6)) { -#else +void CmndDisplaySize(void) { if ((XdrvMailbox.payload > 0) && (XdrvMailbox.payload <= 4)) { -#endif Settings.display_size = XdrvMailbox.payload; if (renderer) renderer->setTextSize(Settings.display_size); //else DisplaySetSize(Settings.display_size); @@ -1876,8 +1783,7 @@ void CmndDisplaySize(void) ResponseCmndNumber(Settings.display_size); } -void CmndDisplayFont(void) -{ +void CmndDisplayFont(void) { if ((XdrvMailbox.payload >=0) && (XdrvMailbox.payload <= 4)) { Settings.display_font = XdrvMailbox.payload; if (renderer) renderer->setTextFont(Settings.display_font); @@ -1886,27 +1792,7 @@ void CmndDisplayFont(void) ResponseCmndNumber(Settings.display_font); } - -void CmndDisplayILIMOde(void) -{ - if ((XdrvMailbox.payload >= 1) && (XdrvMailbox.payload <= 7)) { - Settings.display_options.ilimode = XdrvMailbox.payload; - TasmotaGlobal.restart_flag = 2; - } - ResponseCmndNumber(Settings.display_options.ilimode); -} - -void CmndDisplayILIInvert(void) -{ - if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= 1)) { - Settings.display_options.Invert = XdrvMailbox.payload; - if (renderer) renderer->invertDisplay(Settings.display_options.Invert); - } - ResponseCmndNumber(Settings.display_options.Invert); -} - -void CmndDisplayRotate(void) -{ +void CmndDisplayRotate(void) { if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload < 4)) { if ((Settings.display_rotate) != XdrvMailbox.payload) { /* @@ -1930,8 +1816,77 @@ void CmndDisplayRotate(void) ResponseCmndNumber(Settings.display_rotate); } -void CmndDisplayText(void) -{ +void CmndDisplayInvert(void) { + if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= 1)) { + Settings.display_options.invert = XdrvMailbox.payload; + if (renderer) renderer->invertDisplay(Settings.display_options.invert); + } + ResponseCmndNumber(Settings.display_options.invert); +} + +void CmndDisplayRefresh(void) { + if ((XdrvMailbox.payload >= 1) && (XdrvMailbox.payload <= 7)) { + Settings.display_refresh = XdrvMailbox.payload; + } + ResponseCmndNumber(Settings.display_refresh); +} + +void CmndDisplayColumns(void) { + if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= 2)) { + if ((XdrvMailbox.payload > 0) && (XdrvMailbox.payload <= DISPLAY_MAX_COLS)) { + Settings.display_cols[XdrvMailbox.index -1] = XdrvMailbox.payload; +#ifdef USE_DISPLAY_MODES1TO5 + if (1 == XdrvMailbox.index) { + DisplayLogBufferInit(); + DisplayReAllocScreenBuffer(); + } +#endif // USE_DISPLAY_MODES1TO5 + } + ResponseCmndIdxNumber(Settings.display_cols[XdrvMailbox.index -1]); + } +} + +void CmndDisplayRows(void) { + if ((XdrvMailbox.payload > 0) && (XdrvMailbox.payload <= DISPLAY_MAX_ROWS)) { + Settings.display_rows = XdrvMailbox.payload; +#ifdef USE_DISPLAY_MODES1TO5 + DisplayLogBufferInit(); + DisplayReAllocScreenBuffer(); +#endif // USE_DISPLAY_MODES1TO5 + } + ResponseCmndNumber(Settings.display_rows); +} + +void CmndDisplayAddress(void) { + if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= 8)) { + if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= 255)) { + Settings.display_address[XdrvMailbox.index -1] = XdrvMailbox.payload; + } + ResponseCmndIdxNumber(Settings.display_address[XdrvMailbox.index -1]); + } +} + +void CmndDisplayBlinkrate(void) { + if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= 3)) { + if (!renderer) { + XdspCall(FUNC_DISPLAY_BLINKRATE); + } + } + ResponseCmndNumber(XdrvMailbox.payload); +} + +#ifdef USE_UFILESYS +void CmndDisplayBatch(void) { + if (XdrvMailbox.data_len > 0) { + if (!Settings.display_mode) { + Display_Text_From_File(XdrvMailbox.data); + } + ResponseCmndChar(XdrvMailbox.data); + } +} +#endif + +void CmndDisplayText(void) { if (disp_device && XdrvMailbox.data_len > 0) { #ifndef USE_DISPLAY_MODES1TO5 DisplayText(); @@ -1948,50 +1903,100 @@ void CmndDisplayText(void) } } -void CmndDisplayAddress(void) -{ - if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= 8)) { - if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= 255)) { - Settings.display_address[XdrvMailbox.index -1] = XdrvMailbox.payload; - } - ResponseCmndIdxNumber(Settings.display_address[XdrvMailbox.index -1]); - } +/*********************************************************************************************\ + * Currently 7-segement specific - should have been handled by (extended) DisplayText command +\*********************************************************************************************/ + +void CmndDisplayClear(void) { + if (!renderer) + XdspCall(FUNC_DISPLAY_CLEAR); + ResponseCmndChar(XdrvMailbox.data); } -void CmndDisplayRefresh(void) -{ - if ((XdrvMailbox.payload >= 1) && (XdrvMailbox.payload <= 7)) { - Settings.display_refresh = XdrvMailbox.payload; +void CmndDisplayNumber(void) { + if (!renderer) { + XdspCall(FUNC_DISPLAY_NUMBER); } - ResponseCmndNumber(Settings.display_refresh); + ResponseCmndChar(XdrvMailbox.data); } -void CmndDisplayColumns(void) -{ - if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= 2)) { - if ((XdrvMailbox.payload > 0) && (XdrvMailbox.payload <= DISPLAY_MAX_COLS)) { - Settings.display_cols[XdrvMailbox.index -1] = XdrvMailbox.payload; -#ifdef USE_DISPLAY_MODES1TO5 - if (1 == XdrvMailbox.index) { - DisplayLogBufferInit(); - DisplayReAllocScreenBuffer(); - } -#endif // USE_DISPLAY_MODES1TO5 - } - ResponseCmndIdxNumber(Settings.display_cols[XdrvMailbox.index -1]); +void CmndDisplayFloat(void) { + if (!renderer) { + XdspCall(FUNC_DISPLAY_FLOAT); } + ResponseCmndChar(XdrvMailbox.data); } -void CmndDisplayRows(void) -{ - if ((XdrvMailbox.payload > 0) && (XdrvMailbox.payload <= DISPLAY_MAX_ROWS)) { - Settings.display_rows = XdrvMailbox.payload; -#ifdef USE_DISPLAY_MODES1TO5 - DisplayLogBufferInit(); - DisplayReAllocScreenBuffer(); -#endif // USE_DISPLAY_MODES1TO5 +void CmndDisplayNumberNC(void) { + if (!renderer) { + XdspCall(FUNC_DISPLAY_NUMBERNC); } - ResponseCmndNumber(Settings.display_rows); + ResponseCmndChar(XdrvMailbox.data); +} + +void CmndDisplayFloatNC(void) { + if (!renderer) { + XdspCall(FUNC_DISPLAY_FLOATNC); + } + ResponseCmndChar(XdrvMailbox.data); +} + +void CmndDisplayRaw(void) { + if (!renderer) { + XdspCall(FUNC_DISPLAY_RAW); + } + ResponseCmndChar(XdrvMailbox.data); +} + +void CmndDisplayLevel(void) { + bool result = false; + if (!renderer) { + result = XdspCall(FUNC_DISPLAY_LEVEL); + } + if(result) ResponseCmndNumber(XdrvMailbox.payload); +} + +void CmndDisplaySevensegText(void) { + if (!renderer) { + XdspCall(FUNC_DISPLAY_SEVENSEG_TEXT); + } + ResponseCmndChar(XdrvMailbox.data); +} + +void CmndDisplayTextNC(void) { + if (!renderer) { + XdspCall(FUNC_DISPLAY_SEVENSEG_TEXTNC); + } + ResponseCmndChar(XdrvMailbox.data); +} + +void CmndDisplaySevensegTextNC(void) { + if (!renderer) { + XdspCall(FUNC_DISPLAY_SEVENSEG_TEXTNC); + } + ResponseCmndChar(XdrvMailbox.data); +} + +void CmndDisplayScrollDelay(void) { + if (!renderer) { + XdspCall(FUNC_DISPLAY_SCROLLDELAY); + } + ResponseCmndNumber(XdrvMailbox.payload); +} + +void CmndDisplayClock(void) { + if (!renderer) { + XdspCall(FUNC_DISPLAY_CLOCK); + } + ResponseCmndNumber(XdrvMailbox.payload); +} + +void CmndDisplayScrollText(void) { + bool result = false; + if (!renderer) { + result = XdspCall(FUNC_DISPLAY_SCROLLTEXT); + } + if(result) ResponseCmndChar(XdrvMailbox.data); } /*********************************************************************************************\ @@ -2177,46 +2182,9 @@ void DrawAClock(uint16_t rad) { * Graphics \*********************************************************************************************/ + #ifdef USE_GRAPH -typedef union { - uint8_t data; - struct { - uint8_t overlay : 1; - uint8_t draw : 1; - uint8_t nu3 : 1; - uint8_t nu4 : 1; - uint8_t nu5 : 1; - uint8_t nu6 : 1; - uint8_t nu7 : 1; - uint8_t nu8 : 1; - }; -} GFLAGS; - -struct GRAPH { - uint16_t xp; - uint16_t yp; - uint16_t xs; - uint16_t ys; - float ymin; - float ymax; - float range; - uint32_t x_time; // time per x slice in milliseconds - uint32_t last_ms; - uint32_t last_ms_redrawn; - int16_t decimation; // decimation or graph duration in minutes - uint16_t dcnt; - uint32_t summ; - uint16_t xcnt; - uint8_t *values; - uint8_t xticks; - uint8_t yticks; - uint8_t last_val; - uint8_t color_index; - GFLAGS flags; -}; - -struct GRAPH *graph[NUM_GRAPHS]; #define TICKLEN 4 void ClrGraph(uint16_t num) { diff --git a/tasmota/xdrv_20_hue.ino b/tasmota/xdrv_20_hue.ino index 37c3308d0..5bb442bbe 100644 --- a/tasmota/xdrv_20_hue.ino +++ b/tasmota/xdrv_20_hue.ino @@ -411,9 +411,10 @@ String GetHueDeviceId(uint16_t id) { String deviceid = WiFi.macAddress(); deviceid += F(":00:11-"); - deviceid += String(id); + if(id<0x10) deviceid += F("0"); + deviceid += String(id,HEX); deviceid.toLowerCase(); - return deviceid; // 5c:cf:7f:13:9f:3d:00:11-1 + return deviceid; // 5c:cf:7f:13:9f:3d:00:11-01 } String GetHueUserId(void) diff --git a/tasmota/xdrv_52_0_berry_struct.ino b/tasmota/xdrv_52_0_berry_struct.ino new file mode 100644 index 000000000..22f062ba5 --- /dev/null +++ b/tasmota/xdrv_52_0_berry_struct.ino @@ -0,0 +1,62 @@ +/* + xdrv_52_0_berry_struct.ino - Berry scripting language, native fucnctions + + Copyright (C) 2021 Stephan Hadinger, Berry language by Guan Wenliang https://github.com/Skiars/berry + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + + +#ifdef USE_BERRY + +#include + +typedef LList_elt log_elt; // store the string after the header to avoid double allocation if we had used char* + +class BerryLog { +public: + // typedef LList_elt log_elt; // store the string after the header to avoid double allocation if we had used char* + inline static size_t size(size_t chars) { return sizeof(log_elt) + chars; } + inline bool isEmpty(void) const { return log.isEmpty(); } + log_elt * addString(const char * s, const char * prefix = nullptr, const char * suffix = nullptr) { + if (suffix == nullptr) { suffix = ""; } + if (prefix == nullptr) { prefix = ""; } + if (s == nullptr) { s = ""; } + size_t s_len = strlen_P(s) + strlen_P(prefix) + strlen_P(suffix); + if (0 == s_len) { return nullptr; } // do nothing + log_elt * elt = (log_elt*) ::operator new(sizeof(log_elt) + s_len + 1); // use low-level new to specify the bytes size + snprintf_P((char*) &elt->val(), s_len+1, PSTR("%s%s%s"), prefix, s, suffix); + log.addToLast(elt); + return elt; + } + void reset(void) { + log.reset(); + } + LList log; +}; + +class BerrySupport { +public: + bvm *vm = nullptr; // berry vm + bool rules_busy = false; // are we already processing rules, avoid infinite loop + bool autoexec_done = false; // do we still need to load 'autoexec.be' + bool repl_active = false; // is REPL running (activates log recording) + // output log is stored as a LinkedList of buffers + // and active only when a REPL command is running + BerryLog log; +}; +BerrySupport berry; + + +#endif // USE_BERRY diff --git a/tasmota/xdrv_52_3_berry_tasmota.ino b/tasmota/xdrv_52_3_berry_tasmota.ino index f39f3cc1c..e29382d18 100644 --- a/tasmota/xdrv_52_3_berry_tasmota.ino +++ b/tasmota/xdrv_52_3_berry_tasmota.ino @@ -23,6 +23,8 @@ #include #include +const uint32_t BERRY_MAX_LOGS = 16; // max number of print output recorded when outside of REPL, used to avoid infinite grow of logs + /*********************************************************************************************\ * Native functions mapped to Berry functions * @@ -30,18 +32,18 @@ * * import tasmota * - * tasmota.getfreeheap() -> int + * tasmota.get_free_heap() -> int * tasmota.publish(topic:string, payload:string[, retain:bool]) -> nil * tasmota.cmd(command:string) -> string - * tasmota.getoption(index:int) -> int + * tasmota.get_option(index:int) -> int * tasmota.millis([delay:int]) -> int - * tasmota.timereached(timer:int) -> bool + * tasmota.time_reached(timer:int) -> bool * tasmota.yield() -> nil * - * tasmota.getlight([index:int = 0]) -> map - * tasmota.getpower([index:int = 0]) -> bool - * tasmota.setpower(idx:int, power:bool) -> bool or nil - * tasmota.setlight(idx:int, values:map) -> map + * tasmota.get_light([index:int = 0]) -> map + * tasmota.get_power([index:int = 0]) -> bool + * tasmota.set_power(idx:int, power:bool) -> bool or nil + * tasmota.set_light(idx:int, values:map) -> map * \*********************************************************************************************/ extern "C" { @@ -97,7 +99,7 @@ extern "C" { be_raise(vm, kTypeError, nullptr); } - // Berry: tasmota.getoption(index:int) -> int + // Berry: tasmota.get_option(index:int) -> int // int32_t l_getoption(struct bvm *vm); int32_t l_getoption(struct bvm *vm) { @@ -110,7 +112,7 @@ extern "C" { be_raise(vm, kTypeError, nullptr); } - // Berry: tasmota.timereached(timer:int) -> bool + // Berry: tasmota.time_reached(timer:int) -> bool // int32_t l_timereached(struct bvm *vm); int32_t l_timereached(struct bvm *vm) { @@ -145,7 +147,7 @@ extern "C" { be_return_nil(vm); } - // Berry: tasmota.scaleuint(int * 5) -> int + // Berry: tasmota.scale_uint(int * 5) -> int // int32_t l_scaleuint(struct bvm *vm); int32_t l_scaleuint(struct bvm *vm) { @@ -315,7 +317,7 @@ extern "C" { int32_t top = be_top(vm); // Get the number of arguments if (top == 1 || (top == 2 && be_isint(vm, 2))) { int32_t light_num = 0; - if (top > 0) { + if (top > 1) { light_num = be_toint(vm, 2); } push_getlight(vm, light_num); @@ -470,6 +472,24 @@ extern "C" { be_raise(vm, kTypeError, nullptr); } +#ifdef USE_I2C + // I2C specific + // Berry: `i2c_enabled(index:int) -> bool` is I2C device enabled + int32_t l_i2cenabled(struct bvm *vm); + int32_t l_i2cenabled(struct bvm *vm) { + int32_t top = be_top(vm); // Get the number of arguments + if (top == 2 && be_isint(vm, 2)) { + int32_t index = be_toint(vm, 2); + bool enabled = I2cEnabled(index); + be_pushbool(vm, enabled); + be_return(vm); // Return + } + be_raise(vm, kTypeError, nullptr); + } +#else // USE_I2C + int32_t l_i2cenabled(struct bvm *vm) __attribute__ ((weak, alias ("b_wire_i2cmissing"))); +#endif // USE_I2C + } /*********************************************************************************************\ @@ -522,6 +542,13 @@ extern "C" { // called as a replacement to Berry `print()` void berry_log(const char * berry_buf); void berry_log(const char * berry_buf) { + if (berry.repl_active) { + if (berry.log.log.length() >= BERRY_MAX_LOGS) { + berry.log.log.remove(berry.log.log.head()); + } + } + // AddLog(LOG_LEVEL_INFO, PSTR("[Add to log] %s"), berry_buf); + berry.log.addString(berry_buf, nullptr, "\n"); AddLog(LOG_LEVEL_INFO, PSTR("%s"), berry_buf); } diff --git a/tasmota/xdrv_52_3_berry_wire.ino b/tasmota/xdrv_52_3_berry_wire.ino index 5959b98b9..1c5c56473 100644 --- a/tasmota/xdrv_52_3_berry_wire.ino +++ b/tasmota/xdrv_52_3_berry_wire.ino @@ -48,7 +48,7 @@ int32_t getBus(bvm *vm) { * * import wire * - * wire.getfreeheap() -> int + * wire.get_free_heap() -> int * \*********************************************************************************************/ extern "C" { @@ -135,14 +135,18 @@ extern "C" { int32_t b_wire_write(struct bvm *vm); int32_t b_wire_write(struct bvm *vm) { int32_t top = be_top(vm); // Get the number of arguments + const void * buf; + size_t len; TwoWire & myWire = getWire(vm); - if (top == 2 && (be_isint(vm, 2) || be_isstring(vm, 2))) { + if (top == 2 && (be_isint(vm, 2) || be_isstring(vm, 2) || be_isinstance(vm, 2))) { if (be_isint(vm, 2)) { int32_t value = be_toint(vm, 2); myWire.write(value); } else if (be_isstring(vm, 2)) { const char * s = be_tostring(vm, 1); myWire.write(s); + } else if ((buf = be_tobytes(vm, 2, &len)) != nullptr) { + myWire.write((uint8_t*) buf, len); } else { be_return_nil(vm); } @@ -211,7 +215,7 @@ extern "C" { uint8_t addr = be_toint(vm, 2); uint8_t reg = be_toint(vm, 3); uint8_t size = be_toint(vm, 4); - bool ok = I2cValidRead(addr, reg, size); // TODO + bool ok = I2cValidRead(addr, reg, size, bus); // TODO if (ok) { be_pushint(vm, i2c_buffer); } else { @@ -221,6 +225,22 @@ extern "C" { } be_raise(vm, kTypeError, nullptr); } + + // Berry: `find(address:int) -> bool` true if device responds + int32_t b_wire_detect(struct bvm *vm); + int32_t b_wire_detect(struct bvm *vm) { + int32_t top = be_top(vm); // Get the number of arguments + TwoWire & myWire = getWire(vm); + if (top == 2 && be_isint(vm, 2)) { + uint8_t addr = be_toint(vm, 2); + // check the presence of the device + myWire.beginTransmission((uint8_t)addr); + bool found = (0 == myWire.endTransmission()); + be_pushbool(vm, found); + be_return(vm); // Return + } + be_raise(vm, kTypeError, nullptr); + } #else // USE_I2C // int32_t b_wire_i2cmissing(struct bvm *vm); @@ -239,6 +259,9 @@ extern "C" { int32_t b_wire_scan(struct bvm *vm) __attribute__ ((weak, alias ("b_wire_i2cmissing"))); int32_t b_wire_validwrite(struct bvm *vm) __attribute__ ((weak, alias ("b_wire_i2cmissing"))); int32_t b_wire_validread(struct bvm *vm) __attribute__ ((weak, alias ("b_wire_i2cmissing"))); + int32_t b_wire_readbytes(struct bvm *vm) __attribute__ ((weak, alias ("b_wire_i2cmissing"))); + int32_t b_wire_writebytes(struct bvm *vm) __attribute__ ((weak, alias ("b_wire_i2cmissing"))); + int32_t b_wire_detect(struct bvm *vm) __attribute__ ((weak, alias ("b_wire_i2cmissing"))); #endif // USE_I2C } diff --git a/tasmota/xdrv_52_7_berry_embedded.ino b/tasmota/xdrv_52_7_berry_embedded.ino index 83a3895e7..3c7834149 100644 --- a/tasmota/xdrv_52_7_berry_embedded.ino +++ b/tasmota/xdrv_52_7_berry_embedded.ino @@ -55,14 +55,11 @@ const char berry_prog[] = "/f1,f2-> real(f1) < real(f2)," "] " "self._operators = \"=<>!|\" " - "self._rules = {} " - "self._timers = [] " - "self._cmd = {} " "end " - // add `charsinstring(s:string,c:string) -> int`` + // add `chars_in_string(s:string,c:string) -> int`` // looks for any char in c, and return the position of the first chat // or -1 if not found - "def charsinstring(s,c) " + "def chars_in_string(s,c) " "for i:0..size(s)-1 " "for j:0..size(c)-1 " "if s[i] == c[j] return i end " @@ -72,7 +69,7 @@ const char berry_prog[] = "end " // find a key in map, case insensitive, return actual key or nil if not found - "def findkeyi(m,keyi) " + "def find_key_i(m,keyi) " "import string " "var keyu = string.toupper(keyi) " "if classof(m) == map " @@ -84,14 +81,11 @@ const char berry_prog[] = "end " "end " - // Rules - "def addrule(pat,f) self._rules[pat] = f end " - // # split the item when there is an operator, returns a list of (left,op,right) // # ex: "Dimmer>50" -> ["Dimmer",tasmota_gt,"50"] "def find_op(item) " "import string " - "var pos = self.charsinstring(item, self._operators) " + "var pos = self.chars_in_string(item, self._operators) " "if pos>=0 " "var op_split = string.split(item,pos) " // #print(op_split) @@ -109,6 +103,14 @@ const char berry_prog[] = "end " "return [item, nil, nil] " "end " + + // Rules + "def add_rule(pat,f) " + "if !self._rules " + "self._rules={} " + "end " + "self._rules[pat] = f " + "end " // Rules trigger if match. return true if match, false if not "def try_rule(ev, rule, f) " @@ -117,7 +119,7 @@ const char berry_prog[] = "var e=ev " "var rl=string.split(rl_list[0],'#') " "for it:rl " - "found=self.findkeyi(e,it) " + "found=self.find_key_i(e,it) " "if found == nil " "return false " "end " @@ -138,55 +140,69 @@ const char berry_prog[] = // Run rules, i.e. check each individual rule // Returns true if at least one rule matched, false if none "def exec_rules(ev_json) " - "import json " - "var ev = json.load(ev_json) " - "var ret = false " - "if ev == nil " - "print('BRY: ERROR, bad json: '+ev_json, 3) " - "else " - "for r: self._rules.keys() " - "ret = self.try_rule(ev,r,self._rules[r]) || ret " + "if self._rules " + "import json " + "var ev = json.load(ev_json) " + "var ret = false " + "if ev == nil " + "print('BRY: ERROR, bad json: '+ev_json, 3) " + "else " + "for r: self._rules.keys() " + "ret = self.try_rule(ev,r,self._rules[r]) || ret " + "end " "end " + "return ret " "end " - "return ret " + "return false " "end " - "def settimer(delay,f) self._timers.push([self.millis(delay),f]) end " + "def set_timer(delay,f) " + "if !self._timers self._timers=[] end " + "self._timers.push([self.millis(delay),f]) " + "end " + // run every 50ms tick "def run_deferred() " - "var i=0 " - "while i +#define BERRY_CONSOLE_CMD_DELIMITER "\x01" + const char kBrCommands[] PROGMEM = D_PRFX_BR "|" // prefix D_CMND_BR_RUN "|" D_CMND_BR_RESET ; @@ -32,14 +34,6 @@ void (* const BerryCommand[])(void) PROGMEM = { CmndBrRun, CmndBrReset, }; -class BerrySupport { -public: - bvm *vm = nullptr; // berry vm - bool rules_busy = false; // are we already processing rules, avoid infinite loop - bool autoexec_done = false; // do we still need to load 'autoexec.be' -}; -BerrySupport berry; - // // Sanity Check for be_top() // @@ -53,6 +47,32 @@ void checkBeTop(void) { } } +/*********************************************************************************************\ + * Memory handler + * Use PSRAM if available +\*********************************************************************************************/ +extern "C" { + void *berry_malloc(uint32_t size); + void *berry_realloc(void *ptr, size_t size); +#ifdef USE_BERRY_PSRAM + void *berry_malloc(uint32_t size) { + return special_malloc(size); + } + void *berry_realloc(void *ptr, size_t size) { + return special_realloc(ptr, size); + } +#else + void *berry_malloc(uint32_t size) { + return malloc(size); + } + void *berry_realloc(void *ptr, size_t size) { + return realloc(ptr, size); + } +#endif // USE_BERRY_PSRAM + +} + + /*********************************************************************************************\ * Handlers for Berry calls and async * @@ -64,79 +84,13 @@ bool callBerryRule(void) { berry.rules_busy = true; char * json_event = TasmotaGlobal.mqtt_data; bool serviced = false; - - checkBeTop(); - be_getglobal(berry.vm, "_exec_rules"); - if (!be_isnil(berry.vm, -1)) { - - // { - // String event_saved = TasmotaGlobal.mqtt_data; - // // json_event = {"INA219":{"Voltage":4.494,"Current":0.020,"Power":0.089}} - // // json_event = {"System":{"Boot":1}} - // // json_event = {"SerialReceived":"on"} - invalid but will be expanded to {"SerialReceived":{"Data":"on"}} - // char *p = strchr(json_event, ':'); - // if ((p != NULL) && !(strchr(++p, ':'))) { // Find second colon - // event_saved.replace(F(":"), F(":{\"Data\":")); - // event_saved += F("}"); - // // event_saved = {"SerialReceived":{"Data":"on"}} - // } - // be_pushstring(berry.vm, event_saved.c_str()); - // } - be_pushstring(berry.vm, TasmotaGlobal.mqtt_data); - int ret = be_pcall(berry.vm, 1); - serviced = be_tobool(berry.vm, 1); - AddLog(LOG_LEVEL_DEBUG, PSTR(D_LOG_BERRY "Event (%s) serviced=%d"), TasmotaGlobal.mqtt_data, serviced); - be_pop(berry.vm, 2); // remove function object - } else { - be_pop(berry.vm, 1); // remove nil object - } - checkBeTop(); + serviced = callBerryEventDispatcher(PSTR("rule"), nullptr, 0, TasmotaGlobal.mqtt_data); berry.rules_busy = false; - - return serviced; // TODO event not handled -} - -bool callBerryCommand(void) { - bool serviced = false; - - checkBeTop(); - be_getglobal(berry.vm, "_exec_cmd"); - if (!be_isnil(berry.vm, -1)) { - be_pushstring(berry.vm, XdrvMailbox.topic); - be_pushint(berry.vm, XdrvMailbox.index); - be_pushstring(berry.vm, XdrvMailbox.data); - int ret = be_pcall(berry.vm, 3); - // AddLog(LOG_LEVEL_INFO, "callBerryCommand: top=%d", be_top(berry.vm)); - // AddLog(LOG_LEVEL_INFO, "callBerryCommand: type(1)=%s", be_typename(berry.vm, 1)); - // AddLog(LOG_LEVEL_INFO, "callBerryCommand: type(2)=%s", be_typename(berry.vm, 2)); - // AddLog(LOG_LEVEL_INFO, "callBerryCommand: type(3)=%s", be_typename(berry.vm, 3)); - // AddLog(LOG_LEVEL_INFO, "callBerryCommand: type(4)=%s", be_typename(berry.vm, 4)); - // AddLog(LOG_LEVEL_INFO, "callBerryCommand: type(5)=%s", be_typename(berry.vm, 5)); - serviced = be_tobool(berry.vm, 1); // return value is in slot 1 - // AddLog(LOG_LEVEL_INFO, "callBerryCommand: serviced=%d", serviced); - be_pop(berry.vm, 4); // remove function object - } else { - be_pop(berry.vm, 1); // remove nil object - } - checkBeTop(); - return serviced; // TODO event not handled } size_t callBerryGC(void) { - size_t ram_used = 0; - checkBeTop(); - be_getglobal(berry.vm, "_gc"); - if (!be_isnil(berry.vm, -1)) { - int ret = be_pcall(berry.vm, 0); - ram_used = be_toint(berry.vm, 1); - be_pop(berry.vm, 1); // remove function object - } else { - be_pop(berry.vm, 1); // remove nil object - } - checkBeTop(); - - return ram_used; + return callBerryEventDispatcher(PSTR("gc"), nullptr, 0, nullptr); } // void callBerryMqttData(void) { @@ -161,16 +115,79 @@ size_t callBerryGC(void) { // checkBeTop(); // } -// call a function (if exists) of type void -> void -void callBerryFunctionVoid(const char * fname) { - if (nullptr == berry.vm) { return; } - checkBeTop(); - be_getglobal(berry.vm, fname); +/* +// Call a method of a global object, with n args +// Before: stack must containt n args +// After: stack contains return value or nil if something wrong (args removes) +// returns true is successful, false if object or method not found +bool callMethodObjectWithArgs(const char * objname, const char * method, size_t argc) { + if (nullptr == berry.vm) { return false; } + int32_t top = be_top(berry.vm); + // stacks contains n x arg + be_getglobal(berry.vm, objname); + // stacks contains n x arg + object if (!be_isnil(berry.vm, -1)) { - be_pcall(berry.vm, 0); + be_getmethod(berry.vm, -1, method); + // stacks contains n x arg + object + method + if (!be_isnil(berry.vm, -1)) { + // reshuffle the entire stack since we want: method + object + n x arg + be_pushvalue(berry.vm, -1); // add instance as first arg + // stacks contains n x arg + object + method + method + be_pushvalue(berry.vm, -3); // add instance as first arg + // stacks contains n x arg + object + method + method + object + // now move args 2 slots up to make room for method and object + for (uint32_t i = 1; i <= argc; i++) { + be_moveto(berry.vm, -4 - i, -2 - i); + } + // stacks contains free + free + n x arg + method + object + be_moveto(berry.vm, -2, -4 - argc); + be_moveto(berry.vm, -1, -3 - argc); + // stacks contains method + object + n x arg + method + object + be_pop(berry.vm, 2); + // stacks contains method + object + n x arg + be_pcall(berry.vm, argc + 1); + // stacks contains return_val + object + n x arg + be_pop(berry.vm, argc + 1); + // stacks contains return_val + return true; + } + be_pop(berry.vm, 1); // remove method + // stacks contains n x arg + object } - be_pop(berry.vm, 1); // remove function or nil object + // stacks contains n x arg + object + be_pop(berry.vm, argc + 1); // clear stack + be_pushnil(berry.vm); // put nil object + return false; +} +*/ + + +// call the event dispatcher from Tasmota object +int32_t callBerryEventDispatcher(const char *type, const char *cmd, int32_t idx, const char *payload) { + int32_t ret = 0; + + if (nullptr == berry.vm) { return ret; } checkBeTop(); + be_getglobal(berry.vm, PSTR("tasmota")); + if (!be_isnil(berry.vm, -1)) { + be_getmethod(berry.vm, -1, PSTR("event")); + if (!be_isnil(berry.vm, -1)) { + be_pushvalue(berry.vm, -2); // add instance as first arg + be_pushstring(berry.vm, type != nullptr ? type : ""); + be_pushstring(berry.vm, cmd != nullptr ? cmd : ""); + be_pushint(berry.vm, idx); + be_pushstring(berry.vm, payload != nullptr ? payload : "{}"); // empty json + be_pcall(berry.vm, 5); // 5 arguments + be_pop(berry.vm, 5); + if (be_isint(berry.vm, -1)) { + ret = be_toint(berry.vm, -1); + } + } + be_pop(berry.vm, 1); // remove method + } + be_pop(berry.vm, 1); // remove instance object + checkBeTop(); + return ret; } /*********************************************************************************************\ @@ -349,6 +366,313 @@ void CmndBrReset(void) { BrReset(); } +/*********************************************************************************************\ + * Berry console +\*********************************************************************************************/ +#ifdef USE_WEBSERVER + +void BrREPLRun(char * cmd) { + if (berry.vm == nullptr) { return; } + + size_t cmd_len = strlen(cmd); + size_t cmd2_len = cmd_len + 12; + char * cmd2 = (char*) malloc(cmd2_len); + do { + int32_t ret_code; + + snprintf_P(cmd2, cmd2_len, PSTR("return (%s)"), cmd); + ret_code = be_loadbuffer(berry.vm, PSTR("input"), cmd2, strlen(cmd2)); + // AddLog(LOG_LEVEL_INFO, PSTR(">>>> be_loadbuffer cmd2 '%s', ret=%i"), cmd2, ret_code); + if (be_getexcept(berry.vm, ret_code) == BE_SYNTAX_ERROR) { + be_pop(berry.vm, 2); // remove exception values + // if fails, try the direct command + ret_code = be_loadbuffer(berry.vm, PSTR("input"), cmd, cmd_len); + // AddLog(LOG_LEVEL_INFO, PSTR(">>>> be_loadbuffer cmd1 '%s', ret=%i"), cmd, ret_code); + } + if (0 == ret_code) { // code is ready to run + ret_code = be_pcall(berry.vm, 0); // execute code + // AddLog(LOG_LEVEL_INFO, PSTR(">>>> be_pcall ret=%i"), ret_code); + if (0 == ret_code) { + if (!be_isnil(berry.vm, 1)) { + const char * ret_val = be_tostring(berry.vm, 1); + berry.log.addString(ret_val, nullptr, "\n"); + // AddLog_P(LOG_LEVEL_INFO, PSTR(">>> %s"), ret_val); + } + be_pop(berry.vm, 1); + } + } + if (BE_EXCEPTION == ret_code) { + be_dumpstack(berry.vm); + char exception_s[120]; + ext_snprintf_P(exception_s, sizeof(exception_s), PSTR("%s: %s"), be_tostring(berry.vm, -2), be_tostring(berry.vm, -1)); + berry.log.addString(exception_s, nullptr, "\n"); + // AddLog_P(LOG_LEVEL_INFO, PSTR(">>> %s"), exception_s); + be_pop(berry.vm, 2); + } + } while(0); + + if (cmd2 != nullptr) { + free(cmd2); + cmd2 = nullptr; + } + checkBeTop(); +} + +const char HTTP_SCRIPT_BERRY_CONSOLE[] PROGMEM = + "var sn=0,id=0,ft,ltm=%d;" // Scroll position, Get most of weblog initially + // Console command history + "var hc=[],cn=0;" // hc = History commands, cn = Number of history being shown + + "function l(p){" // Console log and command service + "var c,cc,o='';" + "clearTimeout(lt);" + "clearTimeout(ft);" + "t=eb('t1');" + "if(p==1){" + "c=eb('c1');" // Console command id + "cc=c.value.trim();" + "if(cc){" + "o='&c1='+encodeURIComponent(cc);" + "hc.length>19&&hc.pop();" + "hc.unshift(cc);" + "cn=0;" + "}" + "c.value='';" + "t.scrollTop=99999;" + "sn=t.scrollTop;" + "}" + "if(t.scrollTop>=sn){" // User scrolled back so no updates + "if(x!=null){x.abort();}" // Abort if no response within 2 seconds (happens on restart 1) + "x=new XMLHttpRequest();" + "x.onreadystatechange=function(){" + "if(x.readyState==4&&x.status==200){" + "var d,t1;" + "d=x.responseText.split(/" BERRY_CONSOLE_CMD_DELIMITER "/);" // Field separator + "var d1=d.shift();" + "if(d1){" + "t1=document.createElement('div');" + "t1.classList.add('br1');" + "t1.innerText=d1;" + "t.appendChild(t1);" + "}" + "d1=d.shift();" + "if(d1){" + "t1=document.createElement('div');" + "t1.classList.add('br2');" + "t1.innerText=d1;" + "t.appendChild(t1);" + "}" + "t.scrollTop=99999;" + "sn=t.scrollTop;" + "clearTimeout(ft);" + "lt=setTimeout(l,ltm);" // webrefresh timer.... + "}" + "};" + "x.open('GET','bs?c2='+id+o,true);" // Related to Webserver->hasArg("c2") and WebGetArg("c2", stmp, sizeof(stmp)) + "x.send();" + "ft=setTimeout(l,20000);" // fail timeout, triggered 20s after asking for XHR + "}else{" + "lt=setTimeout(l,ltm);" // webrefresh timer.... + "}" + "c1.focus();" + "return false;" + "}" + "wl(l);" // Load initial console text +; // Add console command key eventlistener after name has been synced with id (= wl(jd)) + +const char HTTP_SCRIPT_BERRY_CONSOLE2[] PROGMEM = + // // Console command history + // "var hc=[],cn=0;" // hc = History commands, cn = Number of history being shown + "var pc=0;" // pc = previous char + "function h(){" +// "if(!(navigator.maxTouchPoints||'ontouchstart'in document.documentElement)){eb('c1').autocomplete='off';}" // No touch so stop browser autocomplete + "eb('c1').addEventListener('keydown',function(e){" + "var b=eb('c1'),c=e.keyCode;" // c1 = Console command id + "if((38==c||40==c)&&0==this.selectionStart&&0==this.selectionEnd){" + "b.autocomplete='off';" + "e.preventDefault();" + "38==c?(++cn>hc.length&&(cn=hc.length),b.value=hc[cn-1]||''):" // ArrowUp + "40==c?(0>--cn&&(cn=0),b.value=hc[cn-1]||''):" // ArrowDown + "0;" + "this.selectionStart=this.selectionEnd=0;" + "}" // ArrowUp or ArrowDown must be a keyboard so stop browser autocomplete + "if(c==13&&pc==13){" + "e.preventDefault();" // prevent 'enter' from being inserted + "l(1);" + "}" + "if(c==9){" + "e.preventDefault();" + "var start=this.selectionStart;" + "var end=this.selectionEnd;" + // set textarea value to: text before caret + tab + text after caret + "this.value=this.value.substring(0, start)+\" \"+this.value.substring(end);" + // put caret at right position again + "this.selectionStart=this.selectionEnd=start + 1;" + "}" + "pc=c;" // record previous key + // "13==c&&(hc.length>19&&hc.pop(),hc.unshift(b.value),cn=0)" // Enter, 19 = Max number -1 of commands in history + "});" + "}" + "wl(h);"; // Add console command key eventlistener after name has been synced with id (= wl(jd)) + +const char HTTP_BERRY_STYLE_CMND[] PROGMEM = + "" + ; + +const char HTTP_BERRY_FORM_CMND[] PROGMEM = + "
" + "
" + "
Welcome to the Berry Scripting console. " + "Check the documentation." + "
" + "
" + // "" + // "

" + "
" + "" + // "
" + // "" + "" + "
"; + +const char HTTP_BTN_BERRY_CONSOLE[] PROGMEM = + "

"; + + +void HandleBerryConsoleRefresh(void) +{ + String svalue = Webserver->arg(F("c1")); + + svalue.trim(); + if (svalue.length()) { + berry.log.reset(); // clear all previous logs + berry.repl_active = true; // start recording + // AddLog_P(LOG_LEVEL_INFO, PSTR("BRY: received command %s"), svalue.c_str()); + berry.log.addString(svalue.c_str(), nullptr, BERRY_CONSOLE_CMD_DELIMITER); + + // Call berry + BrREPLRun((char*)svalue.c_str()); + berry.repl_active = false; // don't record further + } + + WSContentBegin(200, CT_PLAIN); + + if (!berry.log.isEmpty()) { + + WSContentFlush(); + + for (auto & l: berry.log.log) { + _WSContentSend((char*) l); + } + + berry.log.reset(); + } + WSContentEnd(); +} + +void HandleBerryConsole(void) +{ + if (!HttpCheckPriviledgedAccess()) { return; } + // int i=16; + // // AddLog(LOG_LEVEL_INFO, PSTR("Size = %d %d"), sizeof(LList_elt), sizeof(LList_elt)+12); + // LList_elt * elt = (LList_elt*) ::operator new(sizeof(LList_elt) + 12); + + if (Webserver->hasArg(F("c2"))) { // Console refresh requested + HandleBerryConsoleRefresh(); + return; + } + + AddLog(LOG_LEVEL_DEBUG, PSTR(D_LOG_HTTP "Berry " D_CONSOLE)); + + WSContentStart_P(PSTR("Berry " D_CONSOLE)); + WSContentSend_P(HTTP_SCRIPT_BERRY_CONSOLE, Settings.web_refresh); + WSContentSend_P(HTTP_SCRIPT_BERRY_CONSOLE2); + WSContentSendStyle(); + WSContentFlush(); + _WSContentSend(HTTP_BERRY_STYLE_CMND); + _WSContentSend(HTTP_BERRY_FORM_CMND); + WSContentSpaceButton(BUTTON_MAIN); + WSContentStop(); +} + +// void HandleBerryConsoleRefresh(void) +// { +// String svalue = Webserver->arg(F("c1")); +// if (svalue.length() && (svalue.length() < MQTT_MAX_PACKET_SIZE)) { +// // TODO run command and store result +// // AddLog_P(LOG_LEVEL_INFO, PSTR(D_LOG_COMMAND "%s"), svalue.c_str()); +// // ExecuteWebCommand((char*)svalue.c_str(), SRC_WEBCONSOLE); +// } + +// char stmp[8]; +// WebGetArg(PSTR("c2"), stmp, sizeof(stmp)); +// uint32_t index = 0; // Initial start, dump all +// if (strlen(stmp)) { index = atoi(stmp); } + +// WSContentBegin(200, CT_PLAIN); +// WSContentSend_P(PSTR("%d}1%d}1"), TasmotaGlobal.log_buffer_pointer, Web.reset_web_log_flag); +// if (!Web.reset_web_log_flag) { +// index = 0; +// Web.reset_web_log_flag = true; +// } +// bool cflg = (index); +// char* line; +// size_t len; +// while (GetLog(Settings.weblog_level, &index, &line, &len)) { +// if (len > sizeof(TasmotaGlobal.mqtt_data) -2) { len = sizeof(TasmotaGlobal.mqtt_data); } +// char stemp[len +1]; +// strlcpy(stemp, line, len); +// WSContentSend_P(PSTR("%s%s"), (cflg) ? PSTR("\n") : "", stemp); +// cflg = true; +// } +// WSContentSend_P(PSTR("}1")); +// WSContentEnd(); +// } +#endif // USE_WEBSERVER + /*********************************************************************************************\ * Interface \*********************************************************************************************/ @@ -363,51 +687,63 @@ bool Xdrv52(uint8_t function) break; case FUNC_LOOP: if (!berry.autoexec_done) { - BrAutoexec(); + BrAutoexec(); // run autoexec.be at first tick, so we know all modules are initialized berry.autoexec_done = true; } break; + + // Berry wide commands and events + case FUNC_RULES_PROCESS: + result = callBerryRule(); + break; + case FUNC_MQTT_DATA: + result = callBerryEventDispatcher(PSTR("mqtt_data"), XdrvMailbox.topic, 0, XdrvMailbox.data); + break; case FUNC_EVERY_50_MSECOND: - callBerryFunctionVoid(PSTR("_run_deferred")); - break; - case FUNC_EVERY_100_MSECOND: - callBerryFunctionVoid(PSTR("every_100ms")); - break; - case FUNC_EVERY_SECOND: - callBerryFunctionVoid(PSTR("every_second")); + callBerryEventDispatcher(PSTR("every_50ms"), nullptr, 0, nullptr); break; case FUNC_COMMAND: result = DecodeCommand(kBrCommands, BerryCommand); if (!result) { - result = callBerryCommand(); + result = callBerryEventDispatcher(PSTR("cmd"), XdrvMailbox.topic, XdrvMailbox.index, XdrvMailbox.data); } break; + + // Module specific events + case FUNC_EVERY_100_MSECOND: + callBerryEventDispatcher(PSTR("every_100ms"), nullptr, 0, nullptr); + break; + case FUNC_EVERY_SECOND: + callBerryEventDispatcher(PSTR("every_second"), nullptr, 0, nullptr); + break; // case FUNC_SET_POWER: // break; - case FUNC_RULES_PROCESS: - result = callBerryRule(); - break; #ifdef USE_WEBSERVER case FUNC_WEB_ADD_BUTTON: + WSContentSend_P(HTTP_BTN_BERRY_CONSOLE); + callBerryEventDispatcher(PSTR("web_add_button"), nullptr, 0, nullptr); break; case FUNC_WEB_ADD_MAIN_BUTTON: - + callBerryEventDispatcher(PSTR("web_add_main_button"), nullptr, 0, nullptr); break; case FUNC_WEB_ADD_HANDLER: + callBerryEventDispatcher(PSTR("web_add_handler"), nullptr, 0, nullptr); + WebServer_on(PSTR("/bs"), HandleBerryConsole); break; #endif // USE_WEBSERVER case FUNC_SAVE_BEFORE_RESTART: - break; - case FUNC_MQTT_DATA: - // callBerryMqttData(); + callBerryEventDispatcher(PSTR("save_before_restart"), nullptr, 0, nullptr); break; case FUNC_WEB_SENSOR: + callBerryEventDispatcher(PSTR("web_sensor"), nullptr, 0, nullptr); break; case FUNC_JSON_APPEND: + callBerryEventDispatcher(PSTR("json_aooend"), nullptr, 0, nullptr); break; case FUNC_BUTTON_PRESSED: + callBerryEventDispatcher(PSTR("button_pressed"), nullptr, 0, nullptr); break; diff --git a/tasmota/xdsp_04_ili9341.ino b/tasmota/xdsp_04_ili9341.ino index 737b9376f..60b2dd646 100644 --- a/tasmota/xdsp_04_ili9341.ino +++ b/tasmota/xdsp_04_ili9341.ino @@ -42,7 +42,7 @@ uint8_t ili9342_ctouch_counter = 0; bool tft_init_done = false; -//Settings.display_options.ilimode = ILIMODE_9341; +//Settings.display_options.type = ILIMODE_9341; /*********************************************************************************************/ @@ -65,8 +65,8 @@ void ILI9341_InitDriver() // disable screen buffer buffer = NULL; - if (!Settings.display_options.ilimode || (Settings.display_options.ilimode >= ILIMODE_MAX)) { - Settings.display_options.ilimode = ILIMODE_9341; + if (!Settings.display_options.type || (Settings.display_options.type >= ILIMODE_MAX)) { + Settings.display_options.type = ILIMODE_9341; } // default colors @@ -77,11 +77,11 @@ void ILI9341_InitDriver() if (TasmotaGlobal.soft_spi_enabled) { // Init renderer, may use hardware spi, however we use SSPI defintion because SD card uses SPI definition (2 spi busses) if (PinUsed(GPIO_SSPI_MOSI) && PinUsed(GPIO_SSPI_MISO) && PinUsed(GPIO_SSPI_SCLK)) { - ili9341_2 = new ILI9341_2(Pin(GPIO_ILI9341_CS), Pin(GPIO_SSPI_MOSI), Pin(GPIO_SSPI_MISO), Pin(GPIO_SSPI_SCLK), Pin(GPIO_OLED_RESET), Pin(GPIO_ILI9341_DC), Pin(GPIO_BACKLIGHT), 2, Settings.display_options.ilimode & 3); + ili9341_2 = new ILI9341_2(Pin(GPIO_ILI9341_CS), Pin(GPIO_SSPI_MOSI), Pin(GPIO_SSPI_MISO), Pin(GPIO_SSPI_SCLK), Pin(GPIO_OLED_RESET), Pin(GPIO_ILI9341_DC), Pin(GPIO_BACKLIGHT), 2, Settings.display_options.type & 3); } } else if (TasmotaGlobal.spi_enabled) { if (PinUsed(GPIO_ILI9341_DC)) { - ili9341_2 = new ILI9341_2(Pin(GPIO_ILI9341_CS), Pin(GPIO_SPI_MOSI), Pin(GPIO_SPI_MISO), Pin(GPIO_SPI_CLK), Pin(GPIO_OLED_RESET), Pin(GPIO_ILI9341_DC), Pin(GPIO_BACKLIGHT), 1, Settings.display_options.ilimode & 3); + ili9341_2 = new ILI9341_2(Pin(GPIO_ILI9341_CS), Pin(GPIO_SPI_MOSI), Pin(GPIO_SPI_MISO), Pin(GPIO_SPI_CLK), Pin(GPIO_OLED_RESET), Pin(GPIO_ILI9341_DC), Pin(GPIO_BACKLIGHT), 1, Settings.display_options.type & 3); } } @@ -101,7 +101,7 @@ void ILI9341_InitDriver() renderer->setTextFont(2); renderer->setTextSize(1); renderer->setTextColor(ILI9341_WHITE, ILI9341_BLACK); - renderer->DrawStringAt(50, (Settings.display_height/2)-12, (Settings.display_options.ilimode & 3)==ILIMODE_9341?"ILI9341 TFT!":"ILI9342 TFT!", ILI9341_WHITE, 0); + renderer->DrawStringAt(50, (Settings.display_height/2)-12, (Settings.display_options.type & 3)==ILIMODE_9341?"ILI9341 TFT!":"ILI9342 TFT!", ILI9341_WHITE, 0); delay(1000); #endif // SHOW_SPLASH diff --git a/tasmota/xdsp_15_tm1637.ino b/tasmota/xdsp_15_tm1637.ino index 0b507fce5..1d55ec26f 100644 --- a/tasmota/xdsp_15_tm1637.ino +++ b/tasmota/xdsp_15_tm1637.ino @@ -21,7 +21,7 @@ #ifdef USE_DISPLAY_TM1637 /*********************************************************************************************\ This driver enables the display of numbers (both integers and floats) and basic text - on the inexpensive TM1637- and TM1638-based seven-segment modules. + on the inexpensive TM1637-, TM1638- and MAX7219-based seven-segment modules. Raw segments can also be displayed. @@ -45,14 +45,24 @@ CLK hardware pin --> "TM1638 CLK" STB hardware pin --> "TM1638 STB" + For MAX7219: + Connect the MAX7219 display module's pins to any free GPIOs of the ESP8266 module + and assign the pins as follows from Tasmota's GUI: - Once the GPIO configuration is saved and the ESP8266/ESP32 module restarts, set the Display Model to 15 - using the command "DisplayModel 15" + DIN hardware pin --> "MAX7219 DIN" + CS hardware pin --> "MAX7219 CS" + CLK hardware pin --> "MAX7219 CLK" - If your display is a TM1637 with 6 digits, set Display Columns to the number of digits your - display has, using the command "DisplayCols 6" and restart the ESP module. + Once the GPIO configuration is saved and the ESP8266/ESP32 module restarts, + set the Display Model to 15 and Display Mode to 0 + using the command "Backlog DisplayModel 15 ; DisplayMode 0" - After the ESP8266/ESP32 module restarts again, the following "Display" commands can be used: + If your display is a TM1637 with 6 digits, set Display Width to the number of digits your + display has, using the command "DisplayWidth 6". + + After the ESP8266/ESP32 module restarts again, turn ON the display with the command "Power 1" + + Now, the following "Display" commands can be used: DisplayClear @@ -136,63 +146,86 @@ "DisplayClock 0" // turn off clock +In addition, setting DisplayMode to 1 shows the time, setting it to 2 shows the date +and setting it to 3 alternates between time and date. + \*********************************************************************************************/ -#define XDSP_15 15 +#define XDSP_15 15 + +#define CMD_MAX_LEN 55 +#define LEVEL_MIN 0 +#define LEVEL_MAX 100 +#define SCROLL_MAX_LEN 50 +#define POSITION_MIN 0 +#define POSITION_MAX 8 +#define LED_MIN 0 +#define LED_MAX 255 +#define MAX7219_ADDR 0 -#define CMD_MAX_LEN 55 -#define LEVEL_MIN 0 -#define LEVEL_MAX 100 -#define SCROLL_MAX_LEN 50 -#define POSITION_MIN 0 -#define POSITION_MAX 8 -#define LED_MIN 0 -#define LED_MAX 255 #include "SevenSegmentTM1637.h" #include +#include SevenSegmentTM1637 *tm1637display; TM1638plus *tm1638display; +LedControl *max7219display; -enum display_types { TM1637, TM1638 }; +enum display_types +{ + TM1637, + TM1638, + MAX7219 +}; -struct { +struct +{ char scroll_text[CMD_MAX_LEN]; char msg[60]; char model_name[8]; uint8_t scroll_delay = 4; uint8_t scroll_index = 0; uint8_t iteration = 0; - uint8_t buttons; uint8_t display_type = TM1637; - uint8_t prev_buttons; + uint8_t digit_order[6] = { 0, 1, 2, 3, 4, 5 }; bool init_done = false; bool scroll = false; bool show_clock = false; bool clock_24 = false; - bool LED[8] = {false, false, false, false, false, false, false, false}; } TM1637Data; /*********************************************************************************************\ * Init function \*********************************************************************************************/ -void TM1637Init(void) { - if (PinUsed(GPIO_TM1638CLK) && PinUsed(GPIO_TM1638DIO) && PinUsed(GPIO_TM1638STB)) { +void TM1637Init(void) +{ + if (PinUsed(GPIO_TM1638CLK) && PinUsed(GPIO_TM1638DIO) && PinUsed(GPIO_TM1638STB)) + { TM1637Data.display_type = TM1638; Settings.display_width = 8; } - else if (PinUsed(GPIO_TM1637CLK) && PinUsed(GPIO_TM1637DIO)) { + else if (PinUsed(GPIO_TM1637CLK) && PinUsed(GPIO_TM1637DIO)) + { TM1637Data.display_type = TM1637; - if ((!Settings.display_width || Settings.display_width > 6)) { + if ((!Settings.display_width || Settings.display_width > 6)) + { Settings.display_width = 4; + Settings.display_options.type = 0; } + TM1637SetDigitOrder(); } - else { + else if (PinUsed(GPIO_MAX7219DIN) && PinUsed(GPIO_MAX7219CLK) && PinUsed(GPIO_MAX7219CS)) + { + TM1637Data.display_type = MAX7219; + Settings.display_width = 8; + } + else + { return; } @@ -200,21 +233,80 @@ void TM1637Init(void) { Settings.display_cols[0] = Settings.display_width; Settings.display_height = 1; Settings.display_rows = Settings.display_height; + if(!Settings.display_dimmer || Settings.display_dimmer < 2 || Settings.display_dimmer > 15) Settings.display_dimmer = 8; - if (TM1637 == TM1637Data.display_type) { + if (TM1637 == TM1637Data.display_type) + { strcpy_P(TM1637Data.model_name, PSTR("TM1637")); tm1637display = new SevenSegmentTM1637(Pin(GPIO_TM1637CLK), Pin(GPIO_TM1637DIO)); tm1637display->begin(Settings.display_width, 1); } - else if (TM1638 == TM1637Data.display_type) { + else if (TM1638 == TM1637Data.display_type) + { strcpy_P(TM1637Data.model_name, PSTR("TM1638")); - tm1638display = new TM1638plus(Pin(GPIO_TM1638STB), Pin(GPIO_TM1638CLK), Pin(GPIO_TM1638DIO), true ); + tm1638display = new TM1638plus(Pin(GPIO_TM1638STB), Pin(GPIO_TM1638CLK), Pin(GPIO_TM1638DIO), true); tm1638display->displayBegin(); } + else if (MAX7219 == TM1637Data.display_type) + { + strcpy_P(TM1637Data.model_name, PSTR("MAX7219")); + max7219display = new LedControl(Pin(GPIO_MAX7219DIN), Pin(GPIO_MAX7219CLK), Pin(GPIO_MAX7219CS), 1); + max7219display->shutdown(MAX7219_ADDR, false); + } TM1637ClearDisplay(); TM1637Dim(); TM1637Data.init_done = true; - AddLog(LOG_LEVEL_INFO, PSTR("DSP: %s with %d digits"), TM1637Data.model_name, Settings.display_width); + AddLog(LOG_LEVEL_INFO, PSTR("DSP: %s with %d digits (type %d)"), TM1637Data.model_name, Settings.display_width, Settings.display_options.type); +} + +// Function to display specified ascii char at specified position for MAX7219 +void displayMAX7219ASCII(uint8_t pos, char c) +{ + pos = 7 - pos; + max7219display->setChar(MAX7219_ADDR, pos, c, false); +} + +// Function to display specified ascii char with dot at specified position for MAX7219 +void displayMAX7219ASCIIwDot(uint8_t pos, char c) +{ + pos = 7 - pos; + max7219display->setChar(MAX7219_ADDR, pos, c, true); +} + +// Function to display raw segments at specified position for MAX7219 +void displayMAX72197Seg(uint8_t pos, uint8_t seg) +{ + bool dec_bit = seg & 128; + seg = seg << 1; + seg = seg | dec_bit; + uint8_t NO_OF_BITS = 8; + uint8_t reverse_num = 0; + for (uint8_t i = 0; i < NO_OF_BITS; i++) + { + if ((seg & (1 << i))) + reverse_num |= 1 << ((NO_OF_BITS - 1) - i); + } + seg = reverse_num; + + pos = 7 - pos; + max7219display->setRow(MAX7219_ADDR, pos, seg); +} + +// Function to fix order of hardware digits for different TM1637 variants +void TM1637SetDigitOrder(void) { + if (0 == Settings.display_options.type) { + for (uint32_t i = 0; i < 6; i++) { + TM1637Data.digit_order[i] = i; + } + } + else if (1 == Settings.display_options.type) { + TM1637Data.digit_order[0] = 2; + TM1637Data.digit_order[1] = 1; + TM1637Data.digit_order[2] = 0; + TM1637Data.digit_order[3] = 5; + TM1637Data.digit_order[4] = 4; + TM1637Data.digit_order[5] = 3; + } } /*********************************************************************************************\ @@ -223,7 +315,8 @@ void TM1637Init(void) { * commands: DisplayNumber num [,position {0-(Settings.display_width-1)} [,leading_zeros {0|1} [,length {1 to Settings.display_width}]]] * DisplayNumberNC num [,position {0-(Settings.display_width-1)} [,leading_zeros {0|1} [,length {1 to Settings.display_width}]]] // "NC" --> "No Clear" \*********************************************************************************************/ -bool CmndTM1637Number(bool clear) { +bool CmndTM1637Number(bool clear) +{ char sNum[CMD_MAX_LEN]; char sLeadingzeros[CMD_MAX_LEN]; char sPosition[CMD_MAX_LEN]; @@ -236,47 +329,79 @@ bool CmndTM1637Number(bool clear) { switch (ArgC()) { - case 4 : - subStr(sLength, XdrvMailbox.data, ",", 4); - length = atoi(sLength); - case 3 : - subStr(sLeadingzeros, XdrvMailbox.data, ",", 3); - leadingzeros = atoi(sLeadingzeros); - case 2 : - subStr(sPosition, XdrvMailbox.data, ",", 2); - position = atoi(sPosition); - case 1 : - subStr(sNum, XdrvMailbox.data, ",", 1); - num = atof(sNum); + case 4: + subStr(sLength, XdrvMailbox.data, ",", 4); + length = atoi(sLength); + case 3: + subStr(sLeadingzeros, XdrvMailbox.data, ",", 3); + leadingzeros = atoi(sLeadingzeros); + case 2: + subStr(sPosition, XdrvMailbox.data, ",", 2); + position = atoi(sPosition); + case 1: + subStr(sNum, XdrvMailbox.data, ",", 1); + num = atof(sNum); } - - if((position < 0) || (position > (Settings.display_width-1))) position = 0; + if ((position < 0) || (position > (Settings.display_width - 1))) + position = 0; AddLog(LOG_LEVEL_DEBUG, PSTR("TM7: num %d, pos %d, lead %d, len %d"), num, position, leadingzeros, length); - if(clear) TM1637ClearDisplay(); + if (clear) + TM1637ClearDisplay(); char txt[30]; snprintf_P(txt, sizeof(txt), PSTR("%d"), num); - if(!length) length = strlen(txt); - if((length < 0) || (length > Settings.display_width)) length = Settings.display_width; + if (!length) + length = strlen(txt); + if ((length < 0) || (length > Settings.display_width)) + length = Settings.display_width; - char pad = (leadingzeros ? '0': ' '); + char pad = (leadingzeros ? '0' : ' '); uint32_t i = position; uint8_t rawBytes[1]; - for(; iSettings.display_width) break; - if(TM1637Data.display_type == TM1637) { rawBytes[0] = tm1637display->encode(pad); tm1637display->printRaw(rawBytes, 1, i); } - else if(TM1637Data.display_type == TM1638) tm1638display->displayASCII(i, pad); + for (; i < position + (length - strlen(txt)); i++) + { + if (i > Settings.display_width) + break; + if (TM1637 == TM1637Data.display_type) + { + rawBytes[0] = tm1637display->encode(pad); + tm1637display->printRaw(rawBytes, 1, TM1637Data.digit_order[i]); + } + else if (TM1638 == TM1637Data.display_type) + tm1638display->displayASCII(i, pad); + else if (MAX7219 == TM1637Data.display_type) + { + if (i > 7) + break; + displayMAX7219ASCII(i, pad); + } } - for(uint32_t j = 0; i< position + length; i++, j++) { - if(i>Settings.display_width) break; - if(txt[j] == 0) break; - if(TM1637Data.display_type == TM1637) { rawBytes[0] = tm1637display->encode(txt[j]); tm1637display->printRaw(rawBytes, 1, i); } - else if(TM1637Data.display_type == TM1638) tm1638display->displayASCII(i, txt[j]); + for (uint32_t j = 0; i < position + length; i++, j++) + { + if (i > Settings.display_width) + break; + if (txt[j] == 0) + break; + if (TM1637 == TM1637Data.display_type) + { + rawBytes[0] = tm1637display->encode(txt[j]); + tm1637display->printRaw(rawBytes, 1, TM1637Data.digit_order[i]); + } + else if (TM1638 == TM1637Data.display_type) + tm1638display->displayASCII(i, txt[j]); + else if (MAX7219 == TM1637Data.display_type) + { + if (i > 7) + break; + if (txt[j] == 0) + break; + displayMAX7219ASCII(i, txt[j]); + } } return true; @@ -288,7 +413,8 @@ bool CmndTM1637Number(bool clear) { * commands: DisplayFloat num [,position {0-(Settings.display_width-1)} [,precision {0-Settings.display_width} [,length {1 to Settings.display_width}]]] * DisplayFloatNC num [,position {0-(Settings.display_width-1)} [,precision {0-Settings.display_width} [,length {1 to Settings.display_width}]]] // "NC" --> "No Clear" \*********************************************************************************************/ -bool CmndTM1637Float(bool clear) { +bool CmndTM1637Float(bool clear) +{ char sNum[CMD_MAX_LEN]; char sPrecision[CMD_MAX_LEN]; @@ -302,102 +428,148 @@ bool CmndTM1637Float(bool clear) { switch (ArgC()) { - case 4 : - subStr(sLength, XdrvMailbox.data, ",", 4); - length = atoi(sLength); - case 3 : - subStr(sPrecision, XdrvMailbox.data, ",", 3); - precision = atoi(sPrecision); - case 2 : - subStr(sPosition, XdrvMailbox.data, ",", 2); - position = atoi(sPosition); - case 1 : - subStr(sNum, XdrvMailbox.data, ",", 1); - fnum = atof(sNum); + case 4: + subStr(sLength, XdrvMailbox.data, ",", 4); + length = atoi(sLength); + case 3: + subStr(sPrecision, XdrvMailbox.data, ",", 3); + precision = atoi(sPrecision); + case 2: + subStr(sPosition, XdrvMailbox.data, ",", 2); + position = atoi(sPosition); + case 1: + subStr(sNum, XdrvMailbox.data, ",", 1); + fnum = atof(sNum); } + if ((position < 0) || (position > (Settings.display_width - 1))) + position = 0; + if ((precision < 0) || (precision > Settings.display_width)) + precision = Settings.display_width; - if((position < 0) || (position > (Settings.display_width-1))) position = 0; - if((precision < 0) || (precision > Settings.display_width)) precision = Settings.display_width; - - if(clear) TM1637ClearDisplay(); + if (clear) + TM1637ClearDisplay(); char txt[30]; ext_snprintf_P(txt, sizeof(txt), PSTR("%*_f"), precision, &fnum); - if(!length) length = strlen(txt); - if((length <= 0) || (length > Settings.display_width)) length = Settings.display_width; + if (!length) + length = strlen(txt); + if ((length <= 0) || (length > Settings.display_width)) + length = Settings.display_width; AddLog(LOG_LEVEL_DEBUG, PSTR("TM7: num %4_f, prec %d, len %d"), &fnum, precision, length); - if(TM1637Data.display_type == TM1637) { + if (TM1637 == TM1637Data.display_type) + { uint8_t rawBytes[1]; - for(uint32_t i=0, j=0; iencode(txt[i]); - if(txt[i+1] == '.') { + if (txt[i + 1] == '.') + { rawBytes[0] = rawBytes[0] | 128; i++; length++; } - if((j+position) > Settings.display_width) break; - tm1637display->printRaw(rawBytes, 1, j+position); + if ((j + position) > Settings.display_width) + break; + tm1637display->printRaw(rawBytes, 1, TM1637Data.digit_order[j + position]); } - } else if(TM1637Data.display_type == TM1638) { - for(uint32_t i=0, j=0; i 7) break; - if(txt[i] == 0) break; - if(txt[i+1] == '.') { - tm1638display->displayASCIIwDot(j+position, txt[i]); + } + else if (TM1638 == TM1637Data.display_type) + { + for (uint32_t i = 0, j = 0; i < length; i++, j++) + { + if ((j + position) > 7) + break; + if (txt[i] == 0) + break; + if (txt[i + 1] == '.') + { + tm1638display->displayASCIIwDot(j + position, txt[i]); i++; length++; } - else tm1638display->displayASCII(j+position, txt[i]); + else + tm1638display->displayASCII(j + position, txt[i]); + } + } + else if (MAX7219 == TM1637Data.display_type) + { + for (uint32_t i = 0, j = 0; i < length; i++, j++) + { + if ((j + position) > 7) + break; + if (txt[i] == 0) + break; + if (txt[i + 1] == '.') + { + displayMAX7219ASCIIwDot(j + position, txt[i]); + i++; + length++; + } + else + displayMAX7219ASCII(j + position, txt[i]); } } return true; } - // /*********************************************************************************************\ // * Clears the display // * Command: DisplayClear // \*********************************************************************************************/ -bool CmndTM1637Clear(void) { +bool CmndTM1637Clear(void) +{ TM1637ClearDisplay(); sprintf(TM1637Data.msg, PSTR("Cleared")); XdrvMailbox.data = TM1637Data.msg; return true; } - // /*********************************************************************************************\ // * Clears the display // \*********************************************************************************************/ -void TM1637ClearDisplay (void) { - if(TM1637Data.display_type == TM1637) { - unsigned char arr[] = {0}; - for(int i=0; iprintRaw(arr, 1, i); - } else if(TM1637Data.display_type == TM1638) { - for(int i=0; idisplay7Seg(i, 0); +void TM1637ClearDisplay(void) +{ + if (TM1637 == TM1637Data.display_type) + { + unsigned char arr[] = {0}; + for (int i = 0; i < Settings.display_width; i++) + tm1637display->printRaw(arr, 1, i); + } + else if (TM1638 == TM1637Data.display_type) + { + for (int i = 0; i < Settings.display_width; i++) + tm1638display->display7Seg(i, 0); + } + else if (MAX7219 == TM1637Data.display_type) + { + max7219display->clearDisplay(MAX7219_ADDR); } } - /*********************************************************************************************\ * Display scrolling text * Command: DisplayTM1637Data.scroll_text text \*********************************************************************************************/ -bool CmndTM1637ScrollText(void) { +bool CmndTM1637ScrollText(void) +{ AddLog(LOG_LEVEL_DEBUG, PSTR("TM7: Text %s"), XdrvMailbox.data); - if(XdrvMailbox.data_len > SCROLL_MAX_LEN) { + if (XdrvMailbox.data_len > SCROLL_MAX_LEN) + { snprintf(TM1637Data.msg, sizeof(TM1637Data.msg), PSTR("Text too long. Length should be less than %d"), SCROLL_MAX_LEN); XdrvMailbox.data = TM1637Data.msg; return false; - } else { + } + else + { snprintf(TM1637Data.scroll_text, sizeof(TM1637Data.scroll_text), PSTR(" ")); snprintf(TM1637Data.scroll_text, sizeof(TM1637Data.scroll_text), PSTR("%s"), XdrvMailbox.data); TM1637Data.scroll_text[XdrvMailbox.data_len] = 0; @@ -405,65 +577,85 @@ bool CmndTM1637ScrollText(void) { TM1637Data.scroll = true; return true; } - } - - /*********************************************************************************************\ * Sets the scroll delay for scrolling text. * Command: DisplayTM1637Data.scroll_delay delay {0-15} // default = 4 \*********************************************************************************************/ -bool CmndTM1637ScrollDelay(void) { - if(ArgC() == 0) { +bool CmndTM1637ScrollDelay(void) +{ + if (ArgC() == 0) + { XdrvMailbox.payload = TM1637Data.scroll_delay; return true; } - if(TM1637Data.scroll_delay<0) TM1637Data.scroll_delay=0; + if (TM1637Data.scroll_delay < 0) + TM1637Data.scroll_delay = 0; TM1637Data.scroll_delay = XdrvMailbox.payload; return true; } - - /*********************************************************************************************\ * Scrolls a given string. Called every 50ms \*********************************************************************************************/ -void TM1637ScrollText(void) { +void TM1637ScrollText(void) +{ TM1637Data.iteration++; - if(TM1637Data.scroll_delay) TM1637Data.iteration = TM1637Data.iteration % TM1637Data.scroll_delay; - else TM1637Data.iteration = 0; - if(TM1637Data.iteration) return; + if (TM1637Data.scroll_delay) + TM1637Data.iteration = TM1637Data.iteration % TM1637Data.scroll_delay; + else + TM1637Data.iteration = 0; + if (TM1637Data.iteration) + return; - if(TM1637Data.scroll_index > strlen(TM1637Data.scroll_text)) { - TM1637Data.scroll= false; + if (TM1637Data.scroll_index > strlen(TM1637Data.scroll_text)) + { + TM1637Data.scroll = false; TM1637Data.scroll_index = 0; return; } uint8_t rawBytes[1]; - for(uint32_t i=0, j=TM1637Data.scroll_index; i< 1 + strlen(TM1637Data.scroll_text); i++, j++) { - if(i > (Settings.display_width-1)) { break; } + for (uint32_t i = 0, j = TM1637Data.scroll_index; i < 1 + strlen(TM1637Data.scroll_text); i++, j++) + { + if (i > (Settings.display_width - 1)) + { + break; + } rawBytes[0] = tm1637display->encode(TM1637Data.scroll_text[j]); bool dotSkipped = false; - if(TM1637Data.scroll_text[j+1] == '.') { + if (TM1637Data.scroll_text[j + 1] == '.') + { dotSkipped = true; rawBytes[0] = rawBytes[0] | 128; j++; - } else if(TM1637Data.scroll_text[j] == '^') { + } + else if (TM1637Data.scroll_text[j] == '^') + { rawBytes[0] = 1 | 2 | 32 | 64; } - if(!dotSkipped && TM1637Data.scroll_text[j] == '.') { + if (!dotSkipped && TM1637Data.scroll_text[j] == '.') + { j++; TM1637Data.scroll_index++; rawBytes[0] = tm1637display->encode(TM1637Data.scroll_text[j]); } - if(TM1637Data.scroll_text[j+1] == '.') { rawBytes[0] = rawBytes[0] | 128; } - if(TM1637Data.display_type == TM1637) { - tm1637display->printRaw(rawBytes, 1, i); - } else if(TM1637Data.display_type == TM1638) { + if (TM1637Data.scroll_text[j + 1] == '.') + { + rawBytes[0] = rawBytes[0] | 128; + } + if (TM1637 == TM1637Data.display_type) + { + tm1637display->printRaw(rawBytes, 1, TM1637Data.digit_order[i]); + } + else if (TM1638 == TM1637Data.display_type) + { tm1638display->display7Seg(i, rawBytes[0]); } - + else if (MAX7219 == TM1637Data.display_type) + { + displayMAX72197Seg(i, rawBytes[0]); + } } TM1637Data.scroll_index++; } @@ -472,14 +664,16 @@ void TM1637ScrollText(void) { * Displays a horizontal bar graph. Takes a percentage number (0-100) as input * Command: DisplayLevel level {0-100} \*********************************************************************************************/ -bool CmndTM1637Level(void) { +bool CmndTM1637Level(void) +{ uint16_t val = XdrvMailbox.payload; - if((val < LEVEL_MIN) || (val > LEVEL_MAX)) { + if ((val < LEVEL_MIN) || (val > LEVEL_MAX)) + { Response_P(PSTR("{\"Error\":\"Level should be a number in the range [%d, %d]\"}"), LEVEL_MIN, LEVEL_MAX); return false; } - uint8_t totalBars = 2*Settings.display_width; + uint8_t totalBars = 2 * Settings.display_width; AddLog(LOG_LEVEL_DEBUG, PSTR("TM7: TM1637Data.model_name %s CmndTM1637Level totalBars=%d"), TM1637Data.model_name, totalBars); float barsToDisplay = totalBars * val / 100.0f; char txt[5]; @@ -492,16 +686,23 @@ bool CmndTM1637Level(void) { TM1637ClearDisplay(); uint8_t rawBytes[1]; - for(int i=1; i<=numBars; i++) { - uint8_t digit = (i-1) / 2; - uint8_t value = (((i%2) == 0) ? 54 : 48); - if(TM1637Data.display_type == TM1637) { + for (int i = 1; i <= numBars; i++) + { + uint8_t digit = (i - 1) / 2; + uint8_t value = (((i % 2) == 0) ? 54 : 48); + if (TM1637 == TM1637Data.display_type) + { rawBytes[0] = value; - tm1637display->printRaw(rawBytes, 1, digit); - } else if(TM1637Data.display_type == TM1638) { + tm1637display->printRaw(rawBytes, 1, TM1637Data.digit_order[digit]); + } + else if (TM1638 == TM1637Data.display_type) + { tm1638display->display7Seg(digit, value); } - + else if (MAX7219 == TM1637Data.display_type) + { + displayMAX72197Seg(digit, value); + } } return true; } @@ -514,8 +715,9 @@ bool CmndTM1637Level(void) { * bit 1 is segment B etc. The function may either set the entire display * or any desired part using the length and position parameters. \*********************************************************************************************/ -bool CmndTM1637Raw(void) { - uint8_t DATA[6] = { 0, 0, 0, 0, 0, 0 }; +bool CmndTM1637Raw(void) +{ + uint8_t DATA[6] = {0, 0, 0, 0, 0, 0}; char as[CMD_MAX_LEN]; char bs[CMD_MAX_LEN]; @@ -527,60 +729,76 @@ bool CmndTM1637Raw(void) { char sLength[CMD_MAX_LEN]; char sPos[CMD_MAX_LEN]; - uint32_t position = 0; uint32_t length = 0; switch (ArgC()) { - case 8 : - subStr(fs, XdrvMailbox.data, ",", 8); - DATA[5] = atoi(fs); - case 7 : - subStr(es, XdrvMailbox.data, ",", 7); - DATA[4] = atoi(es); - case 6 : - subStr(ds, XdrvMailbox.data, ",", 6); - DATA[3] = atoi(ds); - case 5 : - subStr(cs, XdrvMailbox.data, ",", 5); - DATA[2] = atoi(cs); - case 4 : - subStr(bs, XdrvMailbox.data, ",", 4); - DATA[1] = atoi(bs); - case 3 : - subStr(as, XdrvMailbox.data, ",", 3); - DATA[0] = atoi(as); - case 2 : - subStr(sLength, XdrvMailbox.data, ",", 2); - length = atoi(sLength); - case 1 : - subStr(sPos, XdrvMailbox.data, ",", 1); - position = atoi(sPos); + case 8: + subStr(fs, XdrvMailbox.data, ",", 8); + DATA[5] = atoi(fs); + case 7: + subStr(es, XdrvMailbox.data, ",", 7); + DATA[4] = atoi(es); + case 6: + subStr(ds, XdrvMailbox.data, ",", 6); + DATA[3] = atoi(ds); + case 5: + subStr(cs, XdrvMailbox.data, ",", 5); + DATA[2] = atoi(cs); + case 4: + subStr(bs, XdrvMailbox.data, ",", 4); + DATA[1] = atoi(bs); + case 3: + subStr(as, XdrvMailbox.data, ",", 3); + DATA[0] = atoi(as); + case 2: + subStr(sLength, XdrvMailbox.data, ",", 2); + length = atoi(sLength); + case 1: + subStr(sPos, XdrvMailbox.data, ",", 1); + position = atoi(sPos); } - if(!length) length = ArgC() - 2; - if(length < 0 || length > Settings.display_width) length = Settings.display_width; - if(position < 0 || position > (Settings.display_width-1)) position = 0; + if (!length) + length = ArgC() - 2; + if (length < 0 || length > Settings.display_width) + length = Settings.display_width; + if (position < 0 || position > (Settings.display_width - 1)) + position = 0; AddLog(LOG_LEVEL_DEBUG, PSTR("TM7: a %d, b %d, c %d, d %d, e %d, f %d, len %d, pos %d"), - DATA[0], DATA[1], DATA[2], DATA[3], DATA[4], DATA[5], length, position); + DATA[0], DATA[1], DATA[2], DATA[3], DATA[4], DATA[5], length, position); - if(TM1637Data.display_type == TM1637) { + if (TM1637 == TM1637Data.display_type) + { uint8_t rawBytes[1]; - for(uint32_t i=position; i(Settings.display_width-1)) break; - rawBytes[0] = DATA[i-position]; - tm1637display->printRaw(rawBytes, 1, i); - } - } else if(TM1637Data.display_type == TM1638) { - for(uint32_t i=position; i7) break; - tm1638display->display7Seg(i, DATA[i-position]); + for (uint32_t i = position; i < position + length; i++) + { + if (i > (Settings.display_width - 1)) + break; + rawBytes[0] = DATA[i - position]; + tm1637display->printRaw(rawBytes, 1, TM1637Data.digit_order[i]); + } + } + else if (TM1638 == TM1637Data.display_type) + { + for (uint32_t i = position; i < position + length; i++) + { + if (i > 7) + break; + tm1638display->display7Seg(i, DATA[i - position]); + } + } + else if (MAX7219 == TM1637Data.display_type) + { + for (uint32_t i = position; i < position + length; i++) + { + if (i > 7) + break; + displayMAX72197Seg(i, DATA[i - position]); } } - - return true; } @@ -590,7 +808,8 @@ bool CmndTM1637Raw(void) { * position parameters without affecting the rest of the display. * Command: DisplayText text [, position {0-(Settings.display_width-1)} [,length {1 to Settings.display_width}]] \*********************************************************************************************/ -bool CmndTM1637Text(bool clear) { +bool CmndTM1637Text(bool clear) +{ char sString[CMD_MAX_LEN + 1]; char sPosition[CMD_MAX_LEN]; char sLength[CMD_MAX_LEN]; @@ -599,74 +818,126 @@ bool CmndTM1637Text(bool clear) { switch (ArgC()) { - case 3 : - subStr(sLength, XdrvMailbox.data, ",", 3); - length = atoi(sLength); - case 2 : - subStr(sPosition, XdrvMailbox.data, ",", 2); - position = atoi(sPosition); - case 1 : - subStr(sString, XdrvMailbox.data, ",", 1); + case 3: + subStr(sLength, XdrvMailbox.data, ",", 3); + length = atoi(sLength); + case 2: + subStr(sPosition, XdrvMailbox.data, ",", 2); + position = atoi(sPosition); + case 1: + subStr(sString, XdrvMailbox.data, ",", 1); } - - if((position < 0) || (position > (Settings.display_width-1))) position = 0; + if ((position < 0) || (position > (Settings.display_width - 1))) + position = 0; AddLog(LOG_LEVEL_DEBUG, PSTR("TM7: sString %s, pos %d, len %d"), sString, position, length); - if(clear) TM1637ClearDisplay(); + if (clear) + TM1637ClearDisplay(); - if(!length) length = strlen(sString); - if((length < 0) || (length > Settings.display_width)) length = Settings.display_width; + if (!length) + length = strlen(sString); + if ((length < 0) || (length > Settings.display_width)) + length = Settings.display_width; uint32_t i = position; - if(TM1637Data.display_type == TM1637) { + if (TM1637 == TM1637Data.display_type) + { uint8_t rawBytes[1]; - for(uint32_t j = 0; i< position + length; i++, j++) { - if(i > (Settings.display_width-1)) break; - if(sString[j] == 0) break; + for (uint32_t j = 0; i < position + length; i++, j++) + { + if (i > (Settings.display_width - 1)) + break; + if (sString[j] == 0) + break; rawBytes[0] = tm1637display->encode(sString[j]); bool dotSkipped = false; - if(sString[j+1] == '.') { + if (sString[j + 1] == '.') + { dotSkipped = true; rawBytes[0] = rawBytes[0] | 128; j++; - } else if(sString[j] == '^') { + } + else if (sString[j] == '^') + { rawBytes[0] = 1 | 2 | 32 | 64; } - if(!dotSkipped && sString[j] == '.') rawBytes[0] = 128; - tm1637display->printRaw(rawBytes, 1, i); - } - } else if(TM1637Data.display_type == TM1638) { - for(uint32_t j = 0; i< position + length; i++, j++) { - if(i > 7) break; - if(sString[j] == 0) break; - if(sString[j+1] == '.') { - tm1638display->displayASCIIwDot(i, sString[j]); - j++; - } else if(sString[j] == '^') { - tm1638display->display7Seg(i, (1 | 2 | 32 | 64)); - } else tm1638display->displayASCII(i, sString[j]); + if (!dotSkipped && sString[j] == '.') + rawBytes[0] = 128; + tm1637display->printRaw(rawBytes, 1, TM1637Data.digit_order[i]); + } + } + else if (TM1638 == TM1637Data.display_type) + { + for (uint32_t j = 0; i < position + length; i++, j++) + { + if (i > 7) + break; + if (sString[j] == 0) + break; + if (sString[j + 1] == '.') + { + tm1638display->displayASCIIwDot(i, sString[j]); + j++; + } + else if (sString[j] == '^') + { + tm1638display->display7Seg(i, (1 | 2 | 32 | 64)); + } + else + tm1638display->displayASCII(i, sString[j]); + } + } + else if (MAX7219 == TM1637Data.display_type) + { + uint8_t rawBytes[1]; + for (uint32_t j = 0; i < position + length; i++, j++) + { + if (i > 7) + break; + if (sString[j] == 0) + break; + rawBytes[0] = tm1637display->encode(sString[j]); + bool dotSkipped = false; + if (sString[j + 1] == '.') + { + dotSkipped = true; + rawBytes[0] = rawBytes[0] | 128; + j++; + } + else if (sString[j] == '^') + { + rawBytes[0] = 1 | 2 | 32 | 64; + } + if (!dotSkipped && sString[j] == '.') + rawBytes[0] = 128; + displayMAX72197Seg(i, rawBytes[0]); } } - return true; } - /*********************************************************************************************\ * Displays a clock. * Command: DisplayClock 1 // 12-hour format * DisplayClock 2 // 24-hour format * DisplayClock 0 // turn off clock and clear \*********************************************************************************************/ -bool CmndTM1637Clock(void) { +bool CmndTM1637Clock(void) +{ TM1637Data.show_clock = XdrvMailbox.payload; - if(ArgC() == 0) XdrvMailbox.payload = 1; - if(XdrvMailbox.payload > 1) TM1637Data.clock_24 = true; - else if(XdrvMailbox.payload == 1) TM1637Data.clock_24 = false; + if (ArgC() == 0) + XdrvMailbox.payload = 1; + if (XdrvMailbox.payload > 1) { + TM1637Data.clock_24 = true; + XdrvMailbox.payload = 2; + } else { + TM1637Data.clock_24 = false; + XdrvMailbox.payload = 1; + } AddLog(LOG_LEVEL_DEBUG, PSTR("TM7: TM1637Data.show_clock %d, TM1637Data.clock_24 %d"), TM1637Data.show_clock, TM1637Data.clock_24); @@ -674,110 +945,149 @@ bool CmndTM1637Clock(void) { return true; } - /*********************************************************************************************\ * refreshes the time if clock is displayed \*********************************************************************************************/ -void TM1637ShowTime() { +void TM1637ShowTime() +{ uint8_t hr = RtcTime.hour; uint8_t mn = RtcTime.minute; // uint8_t hr = 1; // uint8_t mn = 0; char z = ' '; - if(TM1637Data.clock_24) { + if (TM1637Data.clock_24) + { z = '0'; - } else { - if(hr > 12) hr -= 12; - if(hr == 0) hr = 12; + } + else + { + if (hr > 12) + hr -= 12; + if (hr == 0) + hr = 12; } char tm[5]; - if(hr < 10) { - if(mn < 10) snprintf(tm, sizeof(tm), PSTR("%c%d0%d"), z, hr, mn); - else snprintf(tm, sizeof(tm), PSTR("%c%d%d"), z, hr, mn); - } else { - if(mn < 10) snprintf(tm, sizeof(tm), PSTR("%d0%d"), hr, mn); - else snprintf(tm, sizeof(tm), PSTR("%d%d"), hr, mn); + if (hr < 10) + { + if (mn < 10) + snprintf(tm, sizeof(tm), PSTR("%c%d0%d"), z, hr, mn); + else + snprintf(tm, sizeof(tm), PSTR("%c%d%d"), z, hr, mn); + } + else + { + if (mn < 10) + snprintf(tm, sizeof(tm), PSTR("%d0%d"), hr, mn); + else + snprintf(tm, sizeof(tm), PSTR("%d%d"), hr, mn); } - if(TM1637Data.display_type == TM1637) { + if (TM1637 == TM1637Data.display_type) + { uint8_t rawBytes[1]; - for(uint32_t i = 0; i< 4; i++) { + for (uint32_t i = 0; i < 4; i++) + { rawBytes[0] = tm1637display->encode(tm[i]); - if((millis() % 1000) > 500 && (i == 1)) rawBytes[0] = rawBytes[0] | 128; - tm1637display->printRaw(rawBytes, 1, i); - } - } else if(TM1637Data.display_type == TM1638) { - for(uint32_t i = 0; i< 4; i++) { - if((millis() % 1000) > 500 && (i == 1)) tm1638display->displayASCIIwDot(i, tm[i]); - else tm1638display->displayASCII(i, tm[i]); + if ((millis() % 1000) > 500 && (i == 1)) + rawBytes[0] = rawBytes[0] | 128; + tm1637display->printRaw(rawBytes, 1, TM1637Data.digit_order[i]); + } + } + else if (TM1638 == TM1637Data.display_type) + { + for (uint32_t i = 0; i < 4; i++) + { + if ((millis() % 1000) > 500 && (i == 1)) + tm1638display->displayASCIIwDot(i, tm[i]); + else + tm1638display->displayASCII(i, tm[i]); + } + } + else if (MAX7219 == TM1637Data.display_type) + { + for (uint32_t i = 0; i < 4; i++) + { + if ((millis() % 1000) > 500 && (i == 1)) + displayMAX7219ASCIIwDot(i, tm[i]); + else + displayMAX7219ASCII(i, tm[i]); } } - } /*********************************************************************************************\ * This function is called for all Display functions. \*********************************************************************************************/ -bool TM1637MainFunc(uint8_t fn) { +bool TM1637MainFunc(uint8_t fn) +{ bool result = false; - if(XdrvMailbox.data_len > CMD_MAX_LEN) { + if (XdrvMailbox.data_len > CMD_MAX_LEN) + { Response_P(PSTR("{\"Error\":\"Command text too long. Please limit it to %d characters\"}"), CMD_MAX_LEN); return false; } - switch (fn) { - case FUNC_DISPLAY_CLEAR: - result = CmndTM1637Clear(); - break; - case FUNC_DISPLAY_NUMBER : - result = CmndTM1637Number(true); - break; - case FUNC_DISPLAY_NUMBERNC : - result = CmndTM1637Number(false); - break; - case FUNC_DISPLAY_FLOAT : - result = CmndTM1637Float(true); - break; - case FUNC_DISPLAY_FLOATNC : - result = CmndTM1637Float(false); - break; - case FUNC_DISPLAY_RAW: - result = CmndTM1637Raw(); - break; - case FUNC_DISPLAY_SEVENSEG_TEXT: - result = CmndTM1637Text(true); - break; - case FUNC_DISPLAY_SEVENSEG_TEXTNC: - result = CmndTM1637Text(false); - break; - case FUNC_DISPLAY_LEVEL: - result = CmndTM1637Level(); - break; - case FUNC_DISPLAY_SCROLLTEXT: - result = CmndTM1637ScrollText(); - break; - case FUNC_DISPLAY_SCROLLDELAY: - result = CmndTM1637ScrollDelay(); - break; - case FUNC_DISPLAY_CLOCK: - result = CmndTM1637Clock(); - break; + switch (fn) + { + case FUNC_DISPLAY_CLEAR: + result = CmndTM1637Clear(); + break; + case FUNC_DISPLAY_NUMBER: + result = CmndTM1637Number(true); + break; + case FUNC_DISPLAY_NUMBERNC: + result = CmndTM1637Number(false); + break; + case FUNC_DISPLAY_FLOAT: + result = CmndTM1637Float(true); + break; + case FUNC_DISPLAY_FLOATNC: + result = CmndTM1637Float(false); + break; + case FUNC_DISPLAY_RAW: + result = CmndTM1637Raw(); + break; + case FUNC_DISPLAY_SEVENSEG_TEXT: + result = CmndTM1637Text(true); + break; + case FUNC_DISPLAY_SEVENSEG_TEXTNC: + result = CmndTM1637Text(false); + break; + case FUNC_DISPLAY_LEVEL: + result = CmndTM1637Level(); + break; + case FUNC_DISPLAY_SCROLLTEXT: + result = CmndTM1637ScrollText(); + break; + case FUNC_DISPLAY_SCROLLDELAY: + result = CmndTM1637ScrollDelay(); + break; + case FUNC_DISPLAY_CLOCK: + result = CmndTM1637Clock(); + break; } return result; } -void TM1637Dim(void) { +void TM1637Dim(void) +{ // Settings.display_dimmer = 0 - 15 - uint8_t brightness = Settings.display_dimmer >> 1; // 0 - 7 + uint8_t brightness = Settings.display_dimmer >> 1; // 0 - 7 - if (TM1637 == TM1637Data.display_type) { - tm1637display->setBacklight(brightness * 12); // 0 - 84 + if (TM1637 == TM1637Data.display_type) + { + tm1637display->setBacklight(brightness * 12); // 0 - 84 } - else if (TM1637Data.display_type == TM1638) { - tm1638display->brightness(brightness); // 0 - 7 + else if (TM1638 == TM1637Data.display_type) + { + tm1638display->brightness(brightness); // 0 - 7 + } + else if (MAX7219 == TM1637Data.display_type) + { + max7219display->setIntensity(MAX7219_ADDR, brightness); // 0 - 7 } } @@ -785,35 +1095,50 @@ void TM1637Dim(void) { #ifdef USE_DISPLAY_MODES1TO5 -void TM1637Print(char* txt) { - for (uint32_t i = 0; i < Settings.display_cols[0]; i++) { - if (TM1637 == TM1637Data.display_type) { +void TM1637Print(char *txt) +{ + for (uint32_t i = 0; i < Settings.display_cols[0]; i++) + { + if (TM1637 == TM1637Data.display_type) + { uint8_t rawBytes[1]; rawBytes[0] = tm1637display->encode(txt[i]); -// if ((millis() % 1000) > 500 && (i == 1)) { rawBytes[0] = rawBytes[0] | 128; } - tm1637display->printRaw(rawBytes, 1, i); + // if ((millis() % 1000) > 500 && (i == 1)) { rawBytes[0] = rawBytes[0] | 128; } + tm1637display->printRaw(rawBytes, 1, TM1637Data.digit_order[i]); } - else if (TM1638 == TM1637Data.display_type) { -// if ((millis() % 1000) > 500 && (i == 1)) { tm1638display->displayASCIIwDot(i, txt[i]); } + else if (TM1638 == TM1637Data.display_type) + { + // if ((millis() % 1000) > 500 && (i == 1)) { tm1638display->displayASCIIwDot(i, txt[i]); } tm1638display->displayASCII(i, txt[i]); } + else if (MAX7219 == TM1637Data.display_type) + { + // if ((millis() % 1000) > 500 && (i == 1)) { tm1638display->displayASCIIwDot(i, txt[i]); } + displayMAX7219ASCII(i, txt[i]); + } + } } -void TM1637Center(char* txt) { - char line[Settings.display_cols[0] +2]; +void TM1637Center(char *txt) +{ + char line[Settings.display_cols[0] + 2]; int len = strlen(txt); int offset = 0; - if (len >= Settings.display_cols[0]) { + if (len >= Settings.display_cols[0]) + { len = Settings.display_cols[0]; - } else { + } + else + { offset = (Settings.display_cols[0] - len) / 2; } memset(line, 0x20, Settings.display_cols[0]); line[Settings.display_cols[0]] = 0; - for (uint32_t i = 0; i < len; i++) { - line[offset +i] = txt[i]; + for (uint32_t i = 0; i < len; i++) + { + line[offset + i] = txt[i]; } TM1637Print(line); } @@ -845,54 +1170,70 @@ bool TM1637PrintLog(void) { } */ -void TM1637Time(void) { - char line[Settings.display_cols[0] +1]; +void TM1637Time(void) +{ + char line[Settings.display_cols[0] + 1]; - if (Settings.display_cols[0] >= 8) { + if (Settings.display_cols[0] >= 8) + { snprintf_P(line, sizeof(line), PSTR("%02d %02d %02d"), RtcTime.hour, RtcTime.minute, RtcTime.second); } - else if (Settings.display_cols[0] >= 6) { + else if (Settings.display_cols[0] >= 6) + { snprintf_P(line, sizeof(line), PSTR("%02d%02d%02d"), RtcTime.hour, RtcTime.minute, RtcTime.second); } - else { + else + { snprintf_P(line, sizeof(line), PSTR("%02d%02d"), RtcTime.hour, RtcTime.minute); } TM1637Center(line); } -void TM1637Date(void) { - char line[Settings.display_cols[0] +1]; +void TM1637Date(void) +{ + char line[Settings.display_cols[0] + 1]; - if (Settings.display_cols[0] >= 8) { - snprintf_P(line, sizeof(line), PSTR("%02d-%02d-%02d"), RtcTime.day_of_month, RtcTime.month, RtcTime.year -2000); + if (Settings.display_cols[0] >= 8) + { + snprintf_P(line, sizeof(line), PSTR("%02d-%02d-%02d"), RtcTime.day_of_month, RtcTime.month, RtcTime.year - 2000); } - else if (Settings.display_cols[0] >= 6) { - snprintf_P(line, sizeof(line), PSTR("%02d%02d%02d"), RtcTime.day_of_month, RtcTime.month, RtcTime.year -2000); + else if (Settings.display_cols[0] >= 6) + { + snprintf_P(line, sizeof(line), PSTR("%02d%02d%02d"), RtcTime.day_of_month, RtcTime.month, RtcTime.year - 2000); } - else { + else + { snprintf_P(line, sizeof(line), PSTR("%02d%02d"), RtcTime.day_of_month, RtcTime.month); } TM1637Center(line); } -void TM1637Refresh(void) { // Every second - if (!disp_power || !Settings.display_mode) { return; } // Mode 0 is User text +void TM1637Refresh(void) +{ // Every second + if (!disp_power || !Settings.display_mode) + { + return; + } // Mode 0 is User text - switch (Settings.display_mode) { - case 1: // Time + switch (Settings.display_mode) + { + case 1: // Time + TM1637Time(); + break; + case 2: // Date + TM1637Date(); + break; + case 3: // Time + if (TasmotaGlobal.uptime % Settings.display_refresh) + { TM1637Time(); - break; - case 2: // Date + } + else + { TM1637Date(); - break; - case 3: // Time - if (TasmotaGlobal.uptime % Settings.display_refresh) { - TM1637Time(); - } else { - TM1637Date(); - } - break; -/* + } + break; + /* case 4: // Mqtt TM1637PrintLog(); break; @@ -904,61 +1245,76 @@ void TM1637Refresh(void) { // Every second } } -#endif // USE_DISPLAY_MODES1TO5 +#endif // USE_DISPLAY_MODES1TO5 /*********************************************************************************************\ * Interface \*********************************************************************************************/ -bool Xdsp15(uint8_t function) { +bool Xdsp15(uint8_t function) +{ bool result = false; - if (FUNC_DISPLAY_INIT_DRIVER == function) { + if (FUNC_DISPLAY_INIT_DRIVER == function) + { TM1637Init(); } - else if (TM1637Data.init_done && (XDSP_15 == Settings.display_model)) { - switch (function) { - case FUNC_DISPLAY_EVERY_50_MSECOND: - if (disp_power && !Settings.display_mode) { - if (TM1637Data.scroll) { TM1637ScrollText(); } - if (TM1637Data.show_clock) { TM1637ShowTime(); } + else if (TM1637Data.init_done && (XDSP_15 == Settings.display_model)) + { + switch (function) + { + case FUNC_DISPLAY_EVERY_50_MSECOND: + if (disp_power && !Settings.display_mode) + { + if (TM1637Data.scroll) + { + TM1637ScrollText(); } - break; + if (TM1637Data.show_clock) + { + TM1637ShowTime(); + } + } + break; #ifdef USE_DISPLAY_MODES1TO5 - case FUNC_DISPLAY_EVERY_SECOND: - TM1637Refresh(); - break; -#endif // USE_DISPLAY_MODES1TO5 - case FUNC_DISPLAY_MODEL: - result = true; - break; - case FUNC_DISPLAY_SEVENSEG_TEXT: - case FUNC_DISPLAY_CLEAR: - case FUNC_DISPLAY_NUMBER: - case FUNC_DISPLAY_FLOAT: - case FUNC_DISPLAY_NUMBERNC: - case FUNC_DISPLAY_FLOATNC: - case FUNC_DISPLAY_RAW: - case FUNC_DISPLAY_LEVEL: - case FUNC_DISPLAY_SEVENSEG_TEXTNC: - case FUNC_DISPLAY_SCROLLTEXT: - case FUNC_DISPLAY_SCROLLDELAY: - case FUNC_DISPLAY_CLOCK: - if (disp_power && !Settings.display_mode) { - TM1637Data.show_clock = false; - result = TM1637MainFunc(function); - } - break; - case FUNC_DISPLAY_DIM: - TM1637Dim(); - break; - case FUNC_DISPLAY_POWER: - if (!disp_power) { TM1637ClearDisplay(); } - break; + case FUNC_DISPLAY_EVERY_SECOND: + TM1637Refresh(); + break; +#endif // USE_DISPLAY_MODES1TO5 + case FUNC_DISPLAY_MODEL: + result = true; + break; + case FUNC_DISPLAY_SEVENSEG_TEXT: + case FUNC_DISPLAY_CLEAR: + case FUNC_DISPLAY_NUMBER: + case FUNC_DISPLAY_FLOAT: + case FUNC_DISPLAY_NUMBERNC: + case FUNC_DISPLAY_FLOATNC: + case FUNC_DISPLAY_RAW: + case FUNC_DISPLAY_LEVEL: + case FUNC_DISPLAY_SEVENSEG_TEXTNC: + case FUNC_DISPLAY_SCROLLTEXT: + case FUNC_DISPLAY_SCROLLDELAY: + case FUNC_DISPLAY_CLOCK: + if (disp_power && !Settings.display_mode) + { + TM1637Data.show_clock = false; + result = TM1637MainFunc(function); + } + break; + case FUNC_DISPLAY_DIM: + TM1637Dim(); + break; + case FUNC_DISPLAY_POWER: + if (!disp_power) + { + TM1637ClearDisplay(); + } + break; } } return result; } -#endif // USE_DISPLAY_TM1637 -#endif // USE_DISPLAY +#endif // USE_DISPLAY_TM1637 +#endif // USE_DISPLAY diff --git a/tasmota/xnrg_19_cse7761.ino b/tasmota/xnrg_19_cse7761.ino index 98ad863bb..77237dda7 100644 --- a/tasmota/xnrg_19_cse7761.ino +++ b/tasmota/xnrg_19_cse7761.ino @@ -21,7 +21,7 @@ #ifdef USE_CSE7761 /*********************************************************************************************\ * CSE7761 - Energy (Sonoff Dual R3 Pow) - * {"NAME":"Sonoff Dual R3","GPIO":[0,0,1,0,0,0,3232,3200,0,0,225,0,0,0,0,0,0,0,0,0,1,7296,7328,224,0,0,0,0,160,161,0,0,0,0,0,0],"FLAG":0,"BASE":1} + * {"NAME":"Sonoff Dual R3","GPIO":[32,0,0,0,0,0,0,0,0,576,225,0,0,0,0,0,0,0,0,0,0,7296,7328,224,0,0,0,0,160,161,0,0,0,0,0,0],"FLAG":0,"BASE":1} * * Based on datasheet from ChipSea and analysing serial data * See https://github.com/arendst/Tasmota/discussions/10793 @@ -34,29 +34,31 @@ #define CSE7761_UREF 42563 // RmsUc #define CSE7761_IREF 52241 // RmsIAC #define CSE7761_PREF 44513 // PowerPAC +#define CSE7761_FREF 3579545 // System clock (3.579545MHz) as used in frequency calculation -#define CSE7761_REG_SYSCON 0x00 // System Control Register -#define CSE7761_REG_EMUCON 0x01 // Metering control register -#define CSE7761_REG_EMUCON2 0x13 // Metering control register 2 +#define CSE7761_REG_SYSCON 0x00 // (2) System Control Register (0x0A04) +#define CSE7761_REG_EMUCON 0x01 // (2) Metering control register (0x0000) +#define CSE7761_REG_EMUCON2 0x13 // (2) Metering control register 2 (0x0001) -#define CSE7761_REG_UFREQ 0x23 // Voltage Frequency Register -#define CSE7761_REG_RMSIA 0x24 // The effective value of channel A current -#define CSE7761_REG_RMSIB 0x25 // The effective value of channel B current -#define CSE7761_REG_RMSU 0x26 // Voltage RMS -#define CSE7761_REG_POWERPA 0x2C // Channel A active power, update rate 27.2Hz -#define CSE7761_REG_POWERPB 0x2D // Channel B active power, update rate 27.2Hz -#define CSE7761_REG_SYSSTATUS 0x43 // System status register +#define CSE7761_REG_UFREQ 0x23 // (2) Voltage Frequency (0x0000) +#define CSE7761_REG_RMSIA 0x24 // (3) The effective value of channel A current (0x000000) +#define CSE7761_REG_RMSIB 0x25 // (3) The effective value of channel B current (0x000000) +#define CSE7761_REG_RMSU 0x26 // (3) Voltage RMS (0x000000) +#define CSE7761_REG_POWERFACTOR 0x27 // (3) Power factor register, select by command: channel A Power factor or channel B power factor (0x7FFFFF) +#define CSE7761_REG_POWERPA 0x2C // (4) Channel A active power, update rate 27.2Hz (0x00000000) +#define CSE7761_REG_POWERPB 0x2D // (4) Channel B active power, update rate 27.2Hz (0x00000000) +#define CSE7761_REG_SYSSTATUS 0x43 // (1) System status register -#define CSE7761_REG_COEFFOFFSET 0x6E // Coefficient checksum offset (0xFFFF) -#define CSE7761_REG_COEFFCHKSUM 0x6F // Coefficient checksum -#define CSE7761_REG_RMSIAC 0x70 // Channel A effective current conversion coefficient -#define CSE7761_REG_RMSIBC 0x71 // Channel B effective current conversion coefficient -#define CSE7761_REG_RMSUC 0x72 // Effective voltage conversion coefficient -#define CSE7761_REG_POWERPAC 0x73 // Channel A active power conversion coefficient -#define CSE7761_REG_POWERPBC 0x74 // Channel B active power conversion coefficient -#define CSE7761_REG_POWERSC 0x75 // Apparent power conversion coefficient -#define CSE7761_REG_ENERGYAC 0x76 // Channel A energy conversion coefficient -#define CSE7761_REG_ENERGYBC 0x77 // Channel B energy conversion coefficient +#define CSE7761_REG_COEFFOFFSET 0x6E // (2) Coefficient checksum offset (0xFFFF) +#define CSE7761_REG_COEFFCHKSUM 0x6F // (2) Coefficient checksum +#define CSE7761_REG_RMSIAC 0x70 // (2) Channel A effective current conversion coefficient +#define CSE7761_REG_RMSIBC 0x71 // (2) Channel B effective current conversion coefficient +#define CSE7761_REG_RMSUC 0x72 // (2) Effective voltage conversion coefficient +#define CSE7761_REG_POWERPAC 0x73 // (2) Channel A active power conversion coefficient +#define CSE7761_REG_POWERPBC 0x74 // (2) Channel B active power conversion coefficient +#define CSE7761_REG_POWERSC 0x75 // (2) Apparent power conversion coefficient +#define CSE7761_REG_ENERGYAC 0x76 // (2) Channel A energy conversion coefficient +#define CSE7761_REG_ENERGYBC 0x77 // (2) Channel B energy conversion coefficient #define CSE7761_SPECIAL_COMMAND 0xEA // Start special command #define CSE7761_CMD_RESET 0x96 // Reset command, after receiving the command, the chip resets @@ -76,6 +78,7 @@ enum CSE7761 { RmsIAC, RmsIBC, RmsUC, PowerPAC, PowerPBC, PowerSC, EnergyAC, Ene TasmotaSerial *Cse7761Serial = nullptr; struct { + uint32_t frequency = 0; uint32_t voltage_rms = 0; uint32_t current_rms[2] = { 0 }; uint32_t energy[2] = { 0 }; @@ -114,7 +117,7 @@ void Cse7761Write(uint32_t reg, uint32_t data) { AddLog(LOG_LEVEL_DEBUG_MORE, PSTR("C61: Tx %*_H"), len, buffer); } -uint32_t Cse7761Read(uint32_t reg, uint32_t size) { +bool Cse7761ReadOnce(uint32_t log_level, uint32_t reg, uint32_t size, uint32_t* value) { while (Cse7761Serial->available()) { Cse7761Serial->read(); } Cse7761Write(reg, 0); @@ -123,8 +126,8 @@ uint32_t Cse7761Read(uint32_t reg, uint32_t size) { uint32_t rcvd = 0; uint32_t timeout = millis() + 3; -// while (!TimeReached(timeout) && (rcvd <= size)) { - while (!TimeReached(timeout)) { + while (!TimeReached(timeout) && (rcvd <= size)) { +// while (!TimeReached(timeout)) { int value = Cse7761Serial->read(); if ((value > -1) && (rcvd < sizeof(buffer) -1)) { buffer[rcvd++] = value; @@ -133,12 +136,12 @@ uint32_t Cse7761Read(uint32_t reg, uint32_t size) { if (!rcvd) { AddLog(LOG_LEVEL_DEBUG_MORE, PSTR("C61: Rx none")); - return 0; + return false; } AddLog(LOG_LEVEL_DEBUG_MORE, PSTR("C61: Rx %*_H"), rcvd, buffer); if (rcvd > 5) { AddLog(LOG_LEVEL_DEBUG_MORE, PSTR("C61: Rx overflow")); - return 0; + return false; } rcvd--; @@ -150,16 +153,28 @@ uint32_t Cse7761Read(uint32_t reg, uint32_t size) { } crc = ~crc; if (crc != buffer[rcvd]) { - AddLog(LOG_LEVEL_DEBUG, PSTR("C61: Rx %*_H, CRC error %02X"), rcvd +1, buffer, crc); - return 1; + AddLog(log_level, PSTR("C61: Rx %*_H, CRC error %02X"), rcvd +1, buffer, crc); + return false; } - return result; + *value = result; + return true; +} + +uint32_t Cse7761Read(uint32_t reg, uint32_t size) { + bool result = false; // Start loop + uint32_t retry = 3; // Retry up to three times + uint32_t value = 0; // Default no value + while (!result && retry) { + retry--; + result = Cse7761ReadOnce((retry) ? LOG_LEVEL_DEBUG_MORE : LOG_LEVEL_DEBUG, reg, size, &value); + } + return value; } uint32_t Cse7761ReadFallback(uint32_t reg, uint32_t prev, uint32_t size) { uint32_t value = Cse7761Read(reg, size); - if (1 == value) { // CRC Error so use previous value read + if (!value) { // Error so use previous value read value = prev; } return value; @@ -192,16 +207,21 @@ bool Cse7761ChipInit(void) { // CSE7761Data.coefficient[PowerPBC] = 0xADD7; } if (HLW_PREF_PULSE == Settings.energy_power_calibration) { + Settings.energy_frequency_calibration = CSE7761_FREF; Settings.energy_voltage_calibration = Cse7761Ref(RmsUC); Settings.energy_current_calibration = Cse7761Ref(RmsIAC); Settings.energy_power_calibration = Cse7761Ref(PowerPAC); } + // Just to fix intermediate users + if (Settings.energy_frequency_calibration < CSE7761_FREF / 2) { + Settings.energy_frequency_calibration = CSE7761_FREF; + } Cse7761Write(CSE7761_SPECIAL_COMMAND, CSE7761_CMD_ENABLE_WRITE); // delay(8); // Exception on ESP8266 - uint32_t timeout = millis() + 8; - while (!TimeReached(timeout)) { } +// uint32_t timeout = millis() + 8; +// while (!TimeReached(timeout)) { } uint8_t sys_status = Cse7761Read(CSE7761_REG_SYSSTATUS, 1); #ifdef CSE7761_SIMULATE @@ -310,7 +330,7 @@ bool Cse7761ChipInit(void) { =1, turn on the power factor output function (Sonoff Dual R3 Pow) =0, turn off the power factor output function 5 WaveEN Waveform data, instantaneous data output enable signal - =1, turn on the waveform data output function + =1, turn on the waveform data output function (Tasmota add frequency) =0, turn off the waveform data output function (Sonoff Dual R3 Pow) 4 SAGEN Voltage drop detection enable signal, WaveEN=1 must be configured first =1, turn on the voltage drop detection function @@ -319,14 +339,15 @@ bool Cse7761ChipInit(void) { =1, turn on the overvoltage, overcurrent, and overload detection functions =0, turn off the overvoltage, overcurrent, and overload detection functions (Sonoff Dual R3 Pow) 2 ZxEN Zero-crossing detection, phase angle, voltage frequency measurement enable signal - =1, turn on the zero-crossing detection, phase angle, and voltage frequency measurement functions + =1, turn on the zero-crossing detection, phase angle, and voltage frequency measurement functions (Tasmota add frequency) =0, disable zero-crossing detection, phase angle, voltage frequency measurement functions (Sonoff Dual R3 Pow) 1 PeakEN Peak detect enable signal =1, turn on the peak detection function =0, turn off the peak detection function (Sonoff Dual R3 Pow) 0 NC Default is 1 */ - Cse7761Write(CSE7761_REG_EMUCON2 | 0x80, 0x0FC1); +// Cse7761Write(CSE7761_REG_EMUCON2 | 0x80, 0x0FC1); // Sonoff Dual R3 Pow + Cse7761Write(CSE7761_REG_EMUCON2 | 0x80, 0x0FE5); // Tasmota add Frequency } else { AddLog(LOG_LEVEL_DEBUG, PSTR("C61: Write failed")); return false; @@ -344,6 +365,12 @@ void Cse7761GetData(void) { #endif CSE7761Data.voltage_rms = (value >= 0x800000) ? 0 : value; + value = Cse7761ReadFallback(CSE7761_REG_UFREQ, CSE7761Data.frequency, 2); +#ifdef CSE7761_SIMULATE + value = 8948; // 49.99Hz +#endif + CSE7761Data.frequency = (value >= 0x8000) ? 0 : value; + value = Cse7761ReadFallback(CSE7761_REG_RMSIA, CSE7761Data.current_rms[0], 3); #ifdef CSE7761_SIMULATE value = 455; @@ -366,8 +393,8 @@ void Cse7761GetData(void) { #endif CSE7761Data.active_power[1] = (0 == CSE7761Data.current_rms[1]) ? 0 : (value & 0x80000000) ? (~value) + 1 : value; - AddLog(LOG_LEVEL_DEBUG_MORE, PSTR("C61: U%d, I%d/%d, P%d/%d"), - CSE7761Data.voltage_rms, + AddLog(LOG_LEVEL_DEBUG_MORE, PSTR("C61: F%d, U%d, I%d/%d, P%d/%d"), + CSE7761Data.frequency, CSE7761Data.voltage_rms, CSE7761Data.current_rms[0], CSE7761Data.current_rms[1], CSE7761Data.active_power[0], CSE7761Data.active_power[1]); @@ -375,6 +402,7 @@ void Cse7761GetData(void) { // Voltage = RmsU * RmsUC * 10 / 0x400000 // Energy.voltage[0] = (float)(((uint64_t)CSE7761Data.voltage_rms * CSE7761Data.coefficient[RmsUC] * 10) >> 22) / 1000; // V Energy.voltage[0] = ((float)CSE7761Data.voltage_rms / Settings.energy_voltage_calibration); // V + Energy.frequency[0] = (CSE7761Data.frequency) ? ((float)Settings.energy_frequency_calibration / 8 / CSE7761Data.frequency) : 0; // Hz for (uint32_t channel = 0; channel < 2; channel++) { Energy.data_valid[channel] = 0; @@ -460,6 +488,7 @@ void Cse7761DrvInit(void) { CSE7761Data.init = 4; // Init setup steps Energy.phase_count = 2; // Handle two channels as two phases Energy.voltage_common = true; // Use common voltage + Energy.frequency_common = true; // Use common frequency TasmotaGlobal.energy_driver = XNRG_19; } } @@ -482,6 +511,10 @@ bool Cse7761Command(void) { if (1 == XdrvMailbox.payload) { XdrvMailbox.payload = Cse7761Ref(RmsIAC); } // Service in xdrv_03_energy.ino } + else if (CMND_FREQUENCYCAL == Energy.command_code) { + if (1 == XdrvMailbox.payload) { XdrvMailbox.payload = CSE7761_FREF; } + // Service in xdrv_03_energy.ino + } else if (CMND_POWERSET == Energy.command_code) { if (XdrvMailbox.data_len && CSE7761Data.active_power[channel]) { if ((value > 100) && (value < 200000)) { // Between 1W and 2000W @@ -503,6 +536,13 @@ bool Cse7761Command(void) { } } } + else if (CMND_FREQUENCYSET == Energy.command_code) { + if (XdrvMailbox.data_len && CSE7761Data.frequency) { + if ((value > 4500) && (value < 6500)) { // Between 45.00Hz and 65.00Hz + Settings.energy_frequency_calibration = (CSE7761Data.frequency * 8 * value) / 100; + } + } + } else serviced = false; // Unknown command return serviced; diff --git a/tasmota/xsns_29_mcp230xx.ino b/tasmota/xsns_29_mcp230xx.ino index e5cf3de09..32ec7c769 100644 --- a/tasmota/xsns_29_mcp230xx.ino +++ b/tasmota/xsns_29_mcp230xx.ino @@ -113,7 +113,6 @@ const char* ConvertNumTxt(uint8_t statu, uint8_t pinmod=0) { #endif // USE_MCP230xx_OUTPUT #ifdef USE_MCP230xx_OUTPUT if ((6 == pinmod) && (statu < 2)) { statu = 1-statu; } - AddLog(LOG_LEVEL_DEBUG, PSTR("MCP: ConvertNumTxt config=%d save_state=%d"),config, Settings.flag.save_state); if ((config) && (Settings.flag.save_state)) { return "SAVED"; } @@ -205,7 +204,6 @@ void MCP230xx_ApplySettings(void) reg_portpins[mcp230xx_port] |= (Settings.mcp230xx_config[idx+(mcp230xx_port*8)].saved_state << idx); } else { if (Settings.mcp230xx_config[idx+(mcp230xx_port*8)].keep_output) { // Read the value to use from the MCP230xx - AddLog(LOG_LEVEL_DEBUG, PSTR("MCP: readpins=%d or_val=%d"),reg_readpins, reg_readpins & (1 << idx)); reg_portpins[mcp230xx_port] |= reg_readpins & (1 << idx); } else if (Settings.mcp230xx_config[idx+(mcp230xx_port*8)].pullup) { diff --git a/tasmota/xsns_82_wiegand.ino b/tasmota/xsns_82_wiegand.ino index 70f87b9c8..0a8edb76a 100644 --- a/tasmota/xsns_82_wiegand.ino +++ b/tasmota/xsns_82_wiegand.ino @@ -43,6 +43,7 @@ * - added SetOption123 0-Wiegand UID decimal (default) 1-Wiegand UID hexadecimal * - added SetOption124 0-all keys up to ending char (# or *) send as one tag by MQTT (default) 1-Keypad every key a single tag * - added a new realtime testing option emulating a Wiegang reader output on same GPIOs where normally reader is attached. Details below + * - fix timing issue when fast glitches are detected on one on the datalines. The interbitgab was too short in that case \*********************************************************************************************/ #pragma message("**** Wiegand interface enabled ****") @@ -106,10 +107,12 @@ class Wiegand { bool WiegandConversion (uint64_t , uint16_t ); void setOutputFormat(void); // fix output HEX format void HandleKeyPad(void); //handle one tag for multi key strokes + static void handleD0Interrupt(void); static void handleD1Interrupt(void); static void handleDxInterrupt(int in); // fix #11047 + static void ClearRFIDBuffer(int); uint64_t rfid; uint32_t tagSize; @@ -142,6 +145,58 @@ volatile bool Wiegand::CodeComplete; volatile RFID_store Wiegand::rfid_found[WIEGAND_RFID_ARRAY_SIZE]; volatile int Wiegand::currentFoundRFIDcount; + + +void ICACHE_RAM_ATTR Wiegand::ClearRFIDBuffer(int endIndex = WIEGAND_RFID_ARRAY_SIZE) { + currentFoundRFIDcount=WIEGAND_RFID_ARRAY_SIZE-endIndex; // clear all buffers + for (int i= 0; i < endIndex; i++) { + rfid_found[i].RFID=0; + rfid_found[i].bitCount=0; + } +} +void ICACHE_RAM_ATTR Wiegand::handleD1Interrupt() { // Receive a 1 bit. (D0=high & D1=low) + handleDxInterrupt(1); +} + +void ICACHE_RAM_ATTR Wiegand::handleD0Interrupt() { // Receive a 0 bit. (D0=low & D1=high) + handleDxInterrupt(0); +} + +void ICACHE_RAM_ATTR Wiegand::handleDxInterrupt(int in) { + unsigned long curTime = micros(); // to be sure I will use micros() instead of millis() overflow is handle by using the minus operator to compare + unsigned long diffTime= curTime - lastFoundTime; + if ( (diffTime > CodeGapTime) && (bitCount > 0)) { + // previous RFID tag (key pad numer)is complete. Will be detected by the code ending gap + // one bit will take the time of impulse_time + impulse_gap_time. it (bitTime) will be recalculated each time an impulse is detected + // the devices will add some inter_code_gap_time to separate codes this will be much longer than the bit_time. (WIEGAND_CODE_GAP_FACTOR) + // unfortunately there's no timing defined for Wiegand. On my test reader the impulse time = 125 µs impulse gap time = 950 µs. + if (currentFoundRFIDcount < WIEGAND_RFID_ARRAY_SIZE) { // when reaching the end of rfid buffer we will overwrite the last one. + currentFoundRFIDcount++; + } + // start a new tag + rfidBuffer = 0; + bitCount = 0; + FirstBitTimeStamp = 0; + } + + if (in == 0) { rfidBuffer = rfidBuffer << 1; } // Receive a 0 bit. (D0=low & D1=high): Leftshift the 0 bit is now at the end of rfidBuffer + else if (in == 1) {rfidBuffer = (rfidBuffer << 1) | 1; } // Receive a 1 bit. (D0=high & D1=low): Leftshift + 1 bit + else { return; } // (in==3) called by ScanForTag to get the last tag, because the interrupt handler is no longer called after receiving the last bit + + bitCount++; + if (bitCount == 1) { // first bit was detected + FirstBitTimeStamp = (curTime != 0) ? curTime : 1; // accept 1µs differenct to avoid a miss the first timestamp if curTime is 0. + } + else if (bitCount == 2) { // only calculate once per RFID tag, but restrict to values, which are in within a plausible range + bitTime = ((diffTime > (WIEGAND_BIT_TIME_DEFAULT/4)) && (diffTime < (4*WIEGAND_BIT_TIME_DEFAULT))) ? diffTime : WIEGAND_BIT_TIME_DEFAULT; + CodeGapTime = WIEGAND_CODE_GAP_FACTOR * bitTime; + } + //save current rfid in array otherwise we will never see the last found tag + rfid_found[currentFoundRFIDcount].RFID=rfidBuffer; + rfid_found[currentFoundRFIDcount].bitCount= bitCount; + lastFoundTime = curTime; // Last time a bit was detected +} + Wiegand::Wiegand() { rfid = 0; lastFoundTime = 0; @@ -154,67 +209,12 @@ Wiegand::Wiegand() { FirstBitTimeStamp = 0; CodeGapTime = WIEGAND_CODE_GAP_FACTOR * bitTime; CodeComplete = false; - currentFoundRFIDcount=0; - for (int i=0; i < WIEGAND_RFID_ARRAY_SIZE; i++ ) - { - rfid_found[i].RFID=0; - rfid_found[i].bitCount=0; - } + ClearRFIDBuffer(); outFormat="u"; // standard output format decimal mqttRFIDKeypadBuffer = 0; webRFIDKeypadBuffer = 0; } -void ICACHE_RAM_ATTR Wiegand::handleD1Interrupt() { // Receive a 1 bit. (D0=high & D1=low) - handleDxInterrupt(1); -} - -void ICACHE_RAM_ATTR Wiegand::handleD0Interrupt() { // Receive a 0 bit. (D0=low & D1=high) - handleDxInterrupt(0); -} - -void ICACHE_RAM_ATTR Wiegand::handleDxInterrupt(int in) { - - unsigned long curTime = micros(); // to be sure I will use micros() instead of millis() overflow is handle by using the minus operator to compare - unsigned long diffTime= curTime - lastFoundTime; - if (diffTime > 3000000 ) { //cancel noisy bits in buffer and start a new tag - rfidBuffer = 0; - bitCount = 0; - FirstBitTimeStamp = 0; - } - if ( (diffTime > CodeGapTime) && (bitCount > 0)) { - // previous RFID tag (key pad numer)is complete. Will be detected by the code ending gap - // one bit will take the time of impulse_time + impulse_gap_time. it (bitTime) will be recalculated each time an impulse is detected - // the devices will add some inter_code_gap_time to separate codes this will be much longer than the bit_time. (WIEGAND_CODE_GAP_FACTOR) - // unfortunately there's no timing defined for Wiegang. On my test reader the impulse time = 125 µs impulse gap time = 950 µs. - if (currentFoundRFIDcount < WIEGAND_RFID_ARRAY_SIZE) { // when reaching the end of rfid buffer we will overwrite the last one. - currentFoundRFIDcount++; - } - // start a new tag - rfidBuffer = 0; - bitCount = 0; - FirstBitTimeStamp = 0; - } - if (in ==3) {// called by ScanForTag to get the last tag, because the interrupt handler is no longer called after receiving the last bit - return; - } - if (in == 0) { rfidBuffer = rfidBuffer << 1; } // Receive a 0 bit. (D0=low & D1=high): Leftshift the 0 bit is now at the end of rfidBuffer - else {rfidBuffer = (rfidBuffer << 1) | 1; } // Receive a 1 bit. (D0=high & D1=low): Leftshift + 1 bit - - bitCount++; - if (bitCount == 1) { // first bit was detected - FirstBitTimeStamp = (curTime != 0) ? curTime : 1; // accept 1µs differenct to avoid a miss the first timestamp if curTime is 0. - } - else if (bitCount == 2) { // only calculate once per RFID tag - bitTime = diffTime; //calc maximum current length of one bit - CodeGapTime = WIEGAND_CODE_GAP_FACTOR * bitTime; - } - //save current rfid in array otherwise we will never see the last found tag - rfid_found[currentFoundRFIDcount].RFID=rfidBuffer; - rfid_found[currentFoundRFIDcount].bitCount= bitCount; - lastFoundTime = curTime; // Last time a bit was detected -} - void Wiegand::Init() { isInit = false; if (PinUsed(GPIO_WIEGAND_D0) && PinUsed(GPIO_WIEGAND_D1)) { // Only start, if the Wiegang pins are selected @@ -400,7 +400,7 @@ void Wiegand::ScanForTag() { uint64_t oldTag = rfid; bool validKey = WiegandConversion(rfid_found[i].RFID, rfid_found[i].bitCount); #if (DEV_WIEGAND_TEST_MODE)>0 - AddLog(LOG_LEVEL_INFO, PSTR("WIE: Previous tag %llu"), oldTag); + AddLog(LOG_LEVEL_INFO, PSTR("WIE: ValidKey: %d Previous tag %llu"), validKey, oldTag); #endif // DEV_WIEGAND_TEST_MODE>0 if (validKey) { // Only in case of valid key do action. Issue#10585 HandleKeyPad(); //support one tag for multi key input @@ -412,15 +412,14 @@ void Wiegand::ScanForTag() { MqttPublishTeleSensor(); } } - rfid_found[i].RFID=0; - rfid_found[i].bitCount=0; } } if (currentFoundRFIDcount > lastFoundRFIDcount) { // if that happens: we need to move the id found during the loop to top of the array // and correct the currentFoundRFIDcount + AddLog(LOG_LEVEL_INFO, PSTR("WIE: ScanForTag() %lu tags added while working on buffer"), (currentFoundRFIDcount-lastFoundRFIDcount)); } - currentFoundRFIDcount=0; //reset array + ClearRFIDBuffer(); //reset array #if (DEV_WIEGAND_TEST_MODE)>0 AddLog(LOG_LEVEL_INFO, PSTR("WIE: ScanForTag() time elapsed %lu"), (micros() - startTime)); #endif