From 4beb589c18400f4cb9fc7bb444df920d9eb6a518 Mon Sep 17 00:00:00 2001 From: Ajith Vasudevan Date: Thu, 18 Feb 2021 22:44:15 +0530 Subject: [PATCH 01/87] Added support for TM1638 Seven-Segment Display, Buttons and LED --- lib/lib_display/TM1638plus/README.md | 276 ++++++++ lib/lib_display/TM1638plus/keywords.txt | 35 + lib/lib_display/TM1638plus/library.properties | 9 + lib/lib_display/TM1638plus/src/TM1638plus.cpp | 191 ++++++ lib/lib_display/TM1638plus/src/TM1638plus.h | 104 +++ .../TM1638plus/src/TM1638plus_common.cpp | 52 ++ .../TM1638plus/src/TM1638plus_common.h | 152 +++++ tasmota/settings.h | 3 +- tasmota/tasmota_configurations.h | 1 + tasmota/xdrv_13_display.ino | 61 +- tasmota/xdsp_15_tm1637.ino | 602 +++++++++++++----- 11 files changed, 1300 insertions(+), 186 deletions(-) create mode 100644 lib/lib_display/TM1638plus/README.md create mode 100644 lib/lib_display/TM1638plus/keywords.txt create mode 100644 lib/lib_display/TM1638plus/library.properties create mode 100644 lib/lib_display/TM1638plus/src/TM1638plus.cpp create mode 100644 lib/lib_display/TM1638plus/src/TM1638plus.h create mode 100644 lib/lib_display/TM1638plus/src/TM1638plus_common.cpp create mode 100644 lib/lib_display/TM1638plus/src/TM1638plus_common.h diff --git a/lib/lib_display/TM1638plus/README.md b/lib/lib_display/TM1638plus/README.md new file mode 100644 index 000000000..f23624b11 --- /dev/null +++ b/lib/lib_display/TM1638plus/README.md @@ -0,0 +1,276 @@ +![ module pics ](https://github.com/gavinlyonsrepo/TM1638plus/blob/master/extra/images/tm16383.jpg) + +Table of contents +--------------------------- + + * [Overview](#overview) + * [Installation](#installation) + * [Features](#features) + * [Model One](#model-one) + * [Model Two](#model-two) + * [Model Three](#model-three) + * [Notes](#notes) + * [Memory](#memory) + * [Pic Ports](#pic-port) + + +Overview +-------------------------------------------- +* Name: TM1638plus +* Description: +An Arduino library to display data on a 8-digit TM1638 seven segment module. +This library supports 3 different models, pictured above from left to right. + +1. Model 1, The (8 KEY & 8 LED) variant which has 8 LED's and 8 Push buttons. +2. Model 2, The (QYF 16 KEY) variant which has 16 pushbuttons. +3. Model 3, The (LKM1638) variant which has 8 bi-colour LED's and 8 Push buttons. + + +* Main Author: Gavin Lyons. + +* Tested on Development platforms: + +1. Arduino UNO & NANO v3 . +2. ATtiny85 . +3. ESP32. +4. STM32 STM32F103C8T6 "the blue pill". +5. ESP8266. +6. Teensy 4.0. (may not work at highest frequency see notes section) + +* History: see CHANGELOG.md in extra folder +* Contributors: [gabormay](https://github.com/gabormay) [centic9](https://github.com/centic9) [wunderbaum](https://github.com/wunderbaum) + + +Installation +------------------------------ + +The library is included in the official Arduino library manger and the optimum way to install it +is using the library manager which can be opened by the *manage libraries* option in Arduino IDE. +Search "tm1638" in search bar of library manager to find it. + +See link below for instruction for this and for the other methods too. + +[Installing Additional Arduino Libraries guide](https://www.arduino.cc/en/Guide/Libraries) + + +Features +---------------------- + +Connections to MCU: + +1. GPIO = STB = Strobe +2. GPIO = CLK = Clock +3. GPIO = DIO = Data input / output +4. GND +5. VCC 5V. + +This device is 5V if using 3.3V MCU, level shift. + +This library supports three variants of the TM1638, which for purposes of this documentation, +will be named Model 1 ,Model 2 and Model 3. + +| Model No | Module Name | LEDS | Push buttons | +| ------ | ------ | ------ | ------ | +| 1 | TM1638 LED & KEY | 8 red only | 8 | +| 2 | TM1638 KEYS, QYF | 0 | 16 | +| 3 | TM1638 V1.3 or LKM1638 | 8 bi color, red and green | 8 | + +There are two sets of files to support model 1 & 2 . I kept them separate as the models are wired quite different, Model 1 address by digit, while Model 2 address by segment. So the code is quite different for most functions. Model 3 uses same code as Model 1, just different example file and different use of LED functions. Common settings, data and functions are in the TM1638plus_common.x files. + +| Model | Header | Code file | TEST files | +| ------ | ------ | ------ | ------ | +| 1 | TM1638plus.h | TM1638plus.cpp | TM1638plus_HELLOWORLD_Model1.ino TM1638plus_TEST_Model1.ino | +| 3 | Same as model 1 | Same as model 1 | TM1638plus_TEST_Model3.ino | +| 2 | TM1638plus_Model2.h | TM1638plus_Model2.cpp | TM1638plus_HELLOWORLD_Model2 TM1638plus_TEST_Model2.ino | + +Model One +-------------------------------------- + +TM1638 Module 8 Push buttons 8 LEDS (LED & KEY) + +![ module ](https://github.com/gavinlyonsrepo/pic_16F18446_projects/blob/master/images/TM1638.jpg) + +![ sch ](https://github.com/gavinlyonsrepo/pic_16F18446_projects/blob/master/images/TM1638_2.jpg) + +This variant consist of an 8-digit seven segment display with decimal points, +8 Leds and 8 Push buttons. Two 4 digit 3461AS-1 (.34 inch, 4 digit ,common Cathode, decimal point, RED) are used in this module giving a total of 8 digits. A TM1638 controller chip drives the unit. +The unit is marked (LED & KEY). + +**Model 1 Library Functions** + +The commented functions can be found in library header file TM1638plus.h. +The library support ASCII ,text ,Hex and allows for setting individual segments, +and the decimal point of segment. +The TM1638plus_TEST.ino contains a set of tests demonstrating library functions. +For more information see the commented headers in header file. + +1. Print an ASCII character. +2. Print an ASCII character with a dot/decimal point. +3. Print a Hexadecimal digit(0-15). +4. Print a long integer number with or without leading zeros. +5. Print two integer numbers (0-9999) to each nibble with or without leading zeros. +6. Print a text string(dots are replaced and dot is turned on preceding digit), +"abc.def" becomes "abcdef" with c decimal point segment switched on. +7. Read buttons status. User may have to debounce buttons depending on application. +debouncing left out to minimise library size. +See [URL LINK](https://github.com/gavinlyonsrepo/Arduino_Clock_3) +for a practical real world example of using this library, +including a example of debouncing the key presses. It is also possible to read multiple key presses. +8. Reset and init module functions. +9. Adjust brightness of module. Support 8 degree brightness adjustment. +If user wishes to change the default brightness at start-up change, +The DEFAULT_BRIGHTNESS define in header file. +10. Manually set segments to create custom patterns. +11. Switch the 8 LEDS on and off, both a set one LED and set all LEDS methods available. + + +Model Two +----------------------------------------- + +TM1638 Module 16 Push buttons (16 KEY) (QYF). + +![ module ](https://github.com/gavinlyonsrepo/TM1638plus/blob/master/extra/images/tm16381.jpg) + +![ sch ](https://github.com/gavinlyonsrepo/TM1638plus/blob/master/extra/images/tm16382.jpg) + +They consist of an 8-digit seven segment display with decimal points, +and 16 Push buttons.Two 4 digit 3461BS-1 (.34 inch, 4 digit ,common Anode, decimal point, RED)are used in this module giving a total of 8 digits. A TM1638 controller chip drives the unit. +NB : If your display shows "56781234" for "12345678" see Notes section. Note A. + +**Model 2 Library Functions** + +The commented functions can be found in library header file TM1638plus_Model2.h. +The library support Strings,decimal ,Hex ,raw ASCII data, setting individual segments, +and the decimal point. For more detailed information on functions see commented headers in header file(.h). + +1. Print a Hexadecimal number with or without leading zeros +2. Print a decimal number with or without leading zeros +3. Manually set segments to create custom patterns. +4. Print two 4 digit decimal number(0-9999) to each nibble with or without leading zeros. +5. Print a text string, dot function supported. +6. Read buttons status. User may want to debounce buttons depending on application. +See TM1638plus_ADC_TEST_Model2.ino for debounce button example. +Two different functions to read buttons. +7. Reset and init module functions. +8. Adjust brightness of module. Support 8 degree brightness adjustment. +If user wishes to change the default brightness at start-up change, +The "DEFAULT_BRIGHTNESS" define in header file. +9. Print raw ASCII data without reference to font file. + +Model Three +----------------------------------------- + +There are different PCB's of these modules on market, +This library was tested on no 3 below. I think this software will work for all of them +and the differences in PCB are related to connectors, layout and component placement. +This module is a variant of Model 1. The differences are the LEDs are bigger and bi-color +both red and green, The seven segment display is larger and extra connectors are added for Daisy chaining. +Two 4 digit KYX-5461AS-7.3 (.54 inch, 4 digit ,common cathode, decimal point, RED)are used in this module +giving a total of 8 digits. + +1. LKM1638 v1.1 +2. LKM1638 v1.2 +3. TM1638 V1.3 + +![ module ](https://github.com/gavinlyonsrepo/TM1638plus/blob/master/extra/images/tm16384.jpg) + +**Model 3 Library Functions** + +The code is the same as model 1 and there is one unique model 3 example file. +setLED and setLEDs functions behaviour is the only difference in code base between 1 and 3. +SetLED: The difference is when you call the setLED function you pass the following to get LEDs to change colour. For more detailed information on functions see commented headers in header file(.h). + +| Model | setLED Value | result | +| ---- | ---- | ---- | +| 1 & 3 | 0 | LED off | +| 3 | 1 | Led green on | +| 3 | 2 | LED red on | +| 1 | 1 | LED on | + +SetLEDs: When you pass call the setLEDs function you can pass a word pattern where upper byte is turns LEDs green on and lower byte turns LEDs red on . Model one ignores lower byte, Set to 0x00 always. + +1. Model 3 setLEDs(word) = 0xGGRR +3. Model 1 setLEDs(word) = 0xRR00 + +Notes +-------------------------- + +1. Swapped data on Display issue on some Model 2 modules +2. Anomaly's on High frequency micro-controllers. +3. Driving multiple displays. +4. Detecting multiple buttons pressed together. + +*Note A* : Swapped display Issue: Model 2 only + +For Some users using this library the nibbles in information display byte +where swapped around. This is because there are different versions of modules on market with different wiring. See issue #3 on github called Swapped display :: "12345678" becomes "56781234". +If you test library and you see this issue, in order to fix this when you declare the +Object, set the fourth parameter "swap_nibbles" to True, The default is false. + +| PCB Model noted Label | Operation | Object constructor 4th parameter | +| ------ | ------ | ------ | +| QYF-TM1638 | default operation | false | +| QYF-TM1638 -Ver 1.0 | Swapped display Fix | true | + +*Note B* : High frequency micro-controllers. + +This library uses a software SPI-like protocol and may not work fully on +micro-controllers running at a very high frequency, without some adjustments to timing. +Its a SPI-like interface with a single bidirectional data wire DIO. +The TM1638 is basically a slow SPI device (< 500kHz) in DIO mode. The clock uses the equivalent of SPI mode 3 (normally high, clocks data on the rising edge). The problem is that the native Arduino shiftIn()/shiftOut() wire functions are simply too fast for this device (technically the clock signalling for the TM1638 is inverted but as it triggers on a rising edge still it is tolerant of that). +To make this work with fast devices, the shift clocking is slowed with a small delay (on the order of a microsecond). As of version 1.6 a new parameter *(_HIGH_FREQ)* has been introduced to constructor it is false by default. Set to true for high frequency MCU ~> 100Mhz. This will fix the issue of HF MCU not reading buttons correctly(ESP-Xs). The High_Freq parameter causes a custom shift-in function to be used. +The Teensy results have been sent to me, I don't have these MCU's them at time of writing. + +| IC | frequency | Status | +| ------ | ------ | ------ | +| ATtiny85 | 1Mhz internal | Working | +| Arduino UNO | 16 MHz | Working | +| Arduino Nano | 16 MHz | Working | +| STM32 "blue pill" STM32F103C8T6 | 72Mhz | Working | +| ESP8266 | 160Mhz | Working | +| ESP 32 | 240 MHz | Working, with high_freq set to true | +| Teensy 4.0| 150Mhz | Working model 1, no Data rest of models | +| Teensy 4.0| 396Mhz | Not working on model1 , no Data rest of models | + +*Note C* : Driving multiple displays. + +It is possible to drive multiple modules. Share the DIO and CLK lines and use a unique +STB line for each device. see issue number 10 at github for example code. + +*Note D* : Detecting multiple buttons pressed together. + +Model 1 and Model 3 CAN detect multiple buttons pressed. + +Model 3 has two different functions: + +1. ReadKey16 returns a byte with decimal value 1-16 this function cannot +detect multiple buttons pressed. + +2. ReadKey16Two returns a 16 bit integer where each bit corresponds to the 16 switch's. +However due to the wiring of the module, see SG-X lines on schematic, +Pressing Certain combinations of buttons will cause the data on Seven Segments to +change. So the simultaneous use of multiple key presses and the seven segments display +is problematic. See issue 12 on github for more details. + +Memory +------------------------------- + +Version 1.4. + +1. Model 1 memory usage NANO, basic hello world sketch. + +Sketch uses 1488 bytes (4%) of program storage space. +Global variables use 22 bytes (1%) of dynamic memory. + +2. Model 2 memory usage NANO, basic hello world sketch. + +Sketch uses 1536 bytes (5%) of program storage space. +Global variables use 23 bytes (1%) of dynamic memory. + + +Pic Port +------------------- + +MicroChip PIC XC8 port. +I have ported this library to the PIC for the XC8 compiler: +[ Link ](https://github.com/gavinlyonsrepo/pic_16F18446_projects) diff --git a/lib/lib_display/TM1638plus/keywords.txt b/lib/lib_display/TM1638plus/keywords.txt new file mode 100644 index 000000000..ae7e9bb67 --- /dev/null +++ b/lib/lib_display/TM1638plus/keywords.txt @@ -0,0 +1,35 @@ +# ----------------------------------------- +# Syntax coloring for TM1638plus library +# ----------------------------------------- + +# Datatypes (such as objects) +TM1638plus KEYWORD1 +TM1638plus_Model2 KEYWORD1 + +# Methods / functions +displayBegin KEYWORD2 +reset KEYWORD2 +brightness KEYWORD2 +DisplayDecNumNibble KEYWORD2 + +readButtons KEYWORD2 +setLED KEYWORD2 +setLEDs KEYWORD2 +displayText KEYWORD2 +displayASCIIwDot KEYWORD2 +displayASCII KEYWORD2 +display7Seg KEYWORD2 +displayHex KEYWORD2 +displayIntNum KEYWORD2 + +DisplaySegments KEYWORD2 +DisplayHexNum KEYWORD2 +DisplayDecNum KEYWORD2 +DisplayStr KEYWORD2 +ASCIItoSegment KEYWORD2 +ReadKey16 KEYWORD2 +ReadKey16Two KEYWORD2 + +# Constants + + diff --git a/lib/lib_display/TM1638plus/library.properties b/lib/lib_display/TM1638plus/library.properties new file mode 100644 index 000000000..8635f3eaf --- /dev/null +++ b/lib/lib_display/TM1638plus/library.properties @@ -0,0 +1,9 @@ +name=TM1638plus +version=1.7.0 +author=Gavin Lyons +maintainer=Gavin Lyons +sentence=TM1638plus is an Arduino library to control TM1638 seven segment modules. +paragraph=It supports Push Buttons, LEDs, ASCII, Decimal, Hexadecimal,text strings and the decimal point. Small Memory footprint. +category=Display +url=https://github.com/gavinlyonsrepo/TM1638plus +architectures=* diff --git a/lib/lib_display/TM1638plus/src/TM1638plus.cpp b/lib/lib_display/TM1638plus/src/TM1638plus.cpp new file mode 100644 index 000000000..c4076ab3a --- /dev/null +++ b/lib/lib_display/TM1638plus/src/TM1638plus.cpp @@ -0,0 +1,191 @@ +/* +* Project Name: TM1638 +* File: TM1638plus.cpp +* Description: source file arduino library for TM1638 module(LED & KEY). Model 1 & Model 3 +* Author: Gavin Lyons. +* Created May 2019 +* URL: https://github.com/gavinlyonsrepo/TM1638plus +*/ + +#include "TM1638plus.h" + + +TM1638plus::TM1638plus(uint8_t strobe, uint8_t clock, uint8_t data, bool highfreq) { + _STROBE_IO = strobe; + _DATA_IO = data; + _CLOCK_IO = clock; + _HIGH_FREQ = highfreq; +} + +void TM1638plus::displayBegin() { + pinMode(_STROBE_IO , OUTPUT); + pinMode(_DATA_IO, OUTPUT); + pinMode(_CLOCK_IO , OUTPUT); + sendCommand(TM_ACTIVATE); + brightness(TM_DEFAULT_BRIGHTNESS); + reset(); +} + + +void TM1638plus::sendCommand(uint8_t value) +{ + digitalWrite(_STROBE_IO, LOW); + sendData(value); + digitalWrite(_STROBE_IO, HIGH); +} + +void TM1638plus::sendData(uint8_t data) +{ + if (_HIGH_FREQ == false) + shiftOut(_DATA_IO, _CLOCK_IO, LSBFIRST, data); + else + TM_common.HighFreqshiftOut(_DATA_IO, _CLOCK_IO, LSBFIRST, data); +} + +void TM1638plus::reset() { + sendCommand(TM_WRITE_INC); // set auto increment mode + digitalWrite(_STROBE_IO, LOW); + sendData(TM_SEG_ADR); // set starting address to + for (uint8_t i = 0; i < 16; i++) + { + sendData(0x00); + } + digitalWrite(_STROBE_IO, HIGH); +} + +void TM1638plus::setLED(uint8_t position, uint8_t value) +{ + pinMode(_DATA_IO, OUTPUT); + sendCommand(TM_WRITE_LOC); + digitalWrite(_STROBE_IO, LOW); + sendData(TM_LEDS_ADR + (position << 1)); + sendData(value); + digitalWrite(_STROBE_IO, HIGH); +} + +void TM1638plus::setLEDs(uint16_t ledvalues) +{ + for (uint8_t LEDposition = 0; LEDposition < 8; LEDposition++) { + uint8_t colour = 0; + + if ((ledvalues & (1 << LEDposition)) != 0) { + colour |= TM_RED_LED; //scan lower byte, set Red if one + } + + if ((ledvalues & (1 << (LEDposition + 8))) != 0) { + colour |= TM_GREEN_LED; //scan upper byte, set green if one + } + + setLED(LEDposition, colour); + } +} + + +void TM1638plus::displayIntNum(unsigned long number, boolean leadingZeros) +{ + char values[TM_DISPLAY_SIZE + 1]; + snprintf(values, TM_DISPLAY_SIZE + 1, leadingZeros ? "%08ld" : "%ld", number); + displayText(values); +} + + +void TM1638plus::DisplayDecNumNibble(uint16_t numberUpper, uint16_t numberLower, boolean leadingZeros) +{ + char valuesUpper[TM_DISPLAY_SIZE + 1]; + char valuesLower[TM_DISPLAY_SIZE/2 + 1]; + snprintf(valuesUpper, TM_DISPLAY_SIZE/2 + 1, leadingZeros ? "%04d" : "%d", numberUpper); + snprintf(valuesLower, TM_DISPLAY_SIZE/2 + 1, leadingZeros ? "%04d" : "%d", numberLower); + strcat(valuesUpper ,valuesLower); + displayText(valuesUpper); +} + +void TM1638plus::displayText(const char *text) { + char c, pos; + pos = 0; + while ((c = (*text++)) && pos < TM_DISPLAY_SIZE) { + if (*text == '.' && c != '.') { + displayASCIIwDot(pos++, c); + + text++; + } else { + displayASCII(pos++, c); + } + } +} + + +void TM1638plus::displayASCIIwDot(uint8_t position, uint8_t ascii) { + // add 128 or 0x080 0b1000000 to turn on decimal point/dot in seven seg + display7Seg(position, pgm_read_byte(&SevenSeg[ascii- TM_ASCII_OFFSET]) + TM_DOT_MASK_DEC); +} + +void TM1638plus::display7Seg(uint8_t position, uint8_t value) { // call 7-segment + sendCommand(TM_WRITE_LOC); + digitalWrite(_STROBE_IO, LOW); + sendData(TM_SEG_ADR + (position << 1)); + sendData(value); + digitalWrite(_STROBE_IO, HIGH); +} + + +void TM1638plus::displayASCII(uint8_t position, uint8_t ascii) { + display7Seg(position, pgm_read_byte(&SevenSeg[ascii - TM_ASCII_OFFSET])); +} + +void TM1638plus::displayHex(uint8_t position, uint8_t hex) +{ + uint8_t offset = 0; + if (hex <= 9) + { + display7Seg(position, pgm_read_byte(&SevenSeg[hex + TM_HEX_OFFSET])); + // 16 is offset in reduced ASCII table for 0 + }else if ((hex >= 10) && (hex <=15)) + { + // Calculate offset in reduced ASCII table for AbCDeF + switch(hex) + { + case 10: offset = 'A'; break; + case 11: offset = 'b'; break; + case 12: offset = 'C'; break; + case 13: offset = 'd'; break; + case 14: offset = 'E'; break; + case 15: offset = 'F'; break; + } + display7Seg(position, pgm_read_byte(&SevenSeg[offset-TM_ASCII_OFFSET])); + } + +} + + +uint8_t TM1638plus::readButtons() +{ + uint8_t buttons = 0; + uint8_t v =0; + + digitalWrite(_STROBE_IO, LOW); + sendData(TM_BUTTONS_MODE); + pinMode(_DATA_IO, INPUT); + + for (uint8_t i = 0; i < 4; i++) + { + + if (_HIGH_FREQ == false) + v = shiftIn(_DATA_IO, _CLOCK_IO, LSBFIRST) << i; + else + v = TM_common.HighFreqshiftin(_DATA_IO, _CLOCK_IO, LSBFIRST) << i; + + buttons |= v; + } + + pinMode(_DATA_IO, OUTPUT); + digitalWrite(_STROBE_IO, HIGH); + return buttons; +} + + +void TM1638plus::brightness(uint8_t brightness) +{ + uint8_t value = 0; + value = TM_BRIGHT_ADR + (TM_BRIGHT_MASK & brightness); + sendCommand(value); +} diff --git a/lib/lib_display/TM1638plus/src/TM1638plus.h b/lib/lib_display/TM1638plus/src/TM1638plus.h new file mode 100644 index 000000000..6809a117b --- /dev/null +++ b/lib/lib_display/TM1638plus/src/TM1638plus.h @@ -0,0 +1,104 @@ +/* +* Project Name: TM1638plus +* File: TM1638plus.h +* Description: TM1638plus.h header file arduino library for TM1638 module(LED & KEY). Model 1 & Model 3 +* Author: Gavin Lyons. +* Created May 2019 +* URL: https://github.com/gavinlyonsrepo/TM1638plus +*/ + + +#ifndef TM1638PLUS_H +#define TM1638PLUS_H + +#if (ARDUINO >=100) + #include "Arduino.h" +#else + #include "WProgram.h" +#endif + +#include "TM1638plus_common.h" + +class TM1638plus { + +public: + // Constructor + //Parameters + // 1. strobe = GPIO STB pin + // 2. clock = GPIO CLK pin + // 3. data = GPIO DIO pin + // 4. higfreq Changes the value of parameter _HIGH_FREQ which is default false + // This is used when running high freq MCU CPU (~>100Mhz) because of issues with button function. + // Pass true when running high freq MCU CPU (~>100Mhz). + TM1638plus(uint8_t strobe, uint8_t clock, uint8_t data, bool highfreq = false); + + // Methods + + void displayBegin(); // Begin method , sets pinmodes , Call in setup + + void reset(void); // Reset / Clear module + + //Sets the brightness level on a scale of brightness = 0 to 7. + //0 is not turned off, it's just the lowest brightness. + //If user wishes to change the default brightness at start-up change. + //The DEFAULT_BRIGHTNESS define in header file. + void brightness(uint8_t brightness); + + //Read buttons returns a byte with value of buttons 1-8 b7b6b5b4b3b2b1b0 + // 1 pressed, zero not pressed. + //User may have to debounce buttons depending on application. + //See [URL LINK](https://github.com/gavinlyonsrepo/Arduino_Clock_3) + // for de-bonce example. + uint8_t readButtons(void); + + // Send Text to Seven segments, passed char array pointer + // dots are removed from string and dot on preceding digit switched on + // "abc.def" will be shown as "abcdef" with c decimal point turned on. + void displayText(const char *text); + + // Send ASCII value to seven segment, pass position 0-7 and ASCII value byte + void displayASCII(uint8_t position, uint8_t ascii); + + // Same as displayASCII function but turns on dot/decimal point as well + void displayASCIIwDot(uint8_t position, uint8_t ascii) ; + + // Send HEX value to seven segment, pass position 0-7 and hex value(DEC) 0-15 + void displayHex(uint8_t position, uint8_t hex); + + // Send seven segment value to seven segment + // pass position 0-7 byte of data corresponding to segments (dp)gfedcba + // i.e 0b01000001 will set g and a on. + void display7Seg(uint8_t position, uint8_t value); + + //Display an integer and leading zeros optional + void displayIntNum(unsigned long number, boolean leadingZeros = true); + + //Divides the display into two nibbles and displays a Decimal number in each. + //takes in two numbers 0-9999 for each nibble , and byte for decimal point display, + //and leading zeros optional + void DisplayDecNumNibble(uint16_t numberUpper, uint16_t numberLower, boolean leadingZeros = true); + + // Set the LEDs. passed one 16bit integer. + // MODEL 3: + //MSB byte for the green LEDs, LS byte for the red LEDs (0xgreenred) + //ie. 0xE007 1110 0000 0000 0111 results in L8-L0 GGGX XRRR, NOTE L8 is RHS on display + // MODEL 1: + // MSB byte 1 for red LED , LSB byte n/a set to 0x00 (0xleds, 0xXX) + //i.e 0xF100 1111 0000 L8-L0 RRRRXXX0 NOTE L8 is RHS on display + void setLEDs(uint16_t greenred); + + // Set an LED, pass it LED position 0-7 and value 0 or 1 , L1-L8 + void setLED(uint8_t position, uint8_t value); + +private: + uint8_t _STROBE_IO; + uint8_t _DATA_IO; + uint8_t _CLOCK_IO; + void sendCommand(uint8_t value); + void sendData(uint8_t data); + //This is used when running high freq CPU + bool _HIGH_FREQ = false; + TM1638plus_common TM_common; +}; + +#endif diff --git a/lib/lib_display/TM1638plus/src/TM1638plus_common.cpp b/lib/lib_display/TM1638plus/src/TM1638plus_common.cpp new file mode 100644 index 000000000..f0accc1fc --- /dev/null +++ b/lib/lib_display/TM1638plus/src/TM1638plus_common.cpp @@ -0,0 +1,52 @@ +/* +* Project Name: TM1638plus +* File: TM1638plus_common +* Description: cpp file for common data and functions between model 1 and 2 classes +* Arduino library TM1638plus +* Author: Gavin Lyons. +* URL: https://github.com/gavinlyonsrepo/TM1638plus +*/ + +#include "TM1638plus_common.h" + + +TM1638plus_common::TM1638plus_common() +{ + // Blank constructor +} + +uint8_t TM1638plus_common::HighFreqshiftin(uint8_t dataPin, uint8_t clockPin, uint8_t bitOrder) +{ + uint8_t value = 0; + uint8_t i = 0; + + for(i = 0; i < 8; ++i) { + if(bitOrder == LSBFIRST) + value |= digitalRead(dataPin) << i; + else + value |= digitalRead(dataPin) << (7 - i); + + digitalWrite(clockPin, HIGH); + delayMicroseconds(1); + digitalWrite(clockPin, LOW); + delayMicroseconds(1); + } + return value; +} + +void TM1638plus_common::HighFreqshiftOut(uint8_t dataPin, uint8_t clockPin, uint8_t bitOrder, uint8_t val) +{ + uint8_t i; + + for (i = 0; i < 8; i++) { + if (bitOrder == LSBFIRST) + digitalWrite(dataPin, !!(val & (1 << i))); + else + digitalWrite(dataPin, !!(val & (1 << (7 - i)))); + + digitalWrite(clockPin, HIGH); + delayMicroseconds(1); + digitalWrite(clockPin, LOW); + delayMicroseconds(1); + } +} diff --git a/lib/lib_display/TM1638plus/src/TM1638plus_common.h b/lib/lib_display/TM1638plus/src/TM1638plus_common.h new file mode 100644 index 000000000..36a7d5408 --- /dev/null +++ b/lib/lib_display/TM1638plus/src/TM1638plus_common.h @@ -0,0 +1,152 @@ +/* +* Project Name: TM1638plus +* File: TM1638plus_common.h +* Description: header file for common data and functions between model 1 and 2 classes +* Arduino library TM1638plus +* Author: Gavin Lyons. +* URL: https://github.com/gavinlyonsrepo/TM1638plus +*/ + +#ifndef TM1638PLUS_COMMON_H +#define TM1638PLUS_COMMON_H + +#if (ARDUINO >=100) + #include "Arduino.h" +#else + #include "WProgram.h" +#endif + +#define TM_ACTIVATE 0x8F // Start up +#define TM_BUTTONS_MODE 0x42 // Buttons mode +#define TM_WRITE_LOC 0x44 // Write to a location +#define TM_WRITE_INC 0x40 // Incremental write +#define TM_SEG_ADR 0xC0 // leftmost segment Address C0 C2 C4 C6 C8 CA CC CE +#define TM_LEDS_ADR 0xC1 // Leftmost LED address C1 C3 C5 C7 C9 CB CD CF +#define TM_BRIGHT_ADR 0x88 // Brightness address +#define TM_BRIGHT_MASK 0x07 // Brightness mask +#define TM_DEFAULT_BRIGHTNESS 0x02 //can be 0x00 to 0x07 +#define TM_DISPLAY_SIZE 8 //size of display + +#define TM_ASCII_OFFSET 32 // Ascii table offset to jump over first missing 32 chars +#define TM_HEX_OFFSET 16 // Ascii table offset to reach number position +#define TM_DOT_MASK_DEC 128 // 0x80 Mask to switch on decimal point in seven seg. + +#define TM_RED_LED 0x02 // Model 3 +#define TM_GREEN_LED 0x01 // Model 3 +#define TM_OFF_LED 0x00 + +// font , map of ASCII values/table to 7-segment, offset to position 32. +const PROGMEM unsigned char SevenSeg[] = { + 0x00, /* (space) */ + 0x86, /* ! */ + 0x22, /* " */ + 0x7E, /* # */ + 0x6D, /* $ */ + 0xD2, /* % */ + 0x46, /* & */ + 0x20, /* ' */ + 0x29, /* ( */ + 0x0B, /* ) */ + 0x21, /* * */ + 0x70, /* + */ + 0x10, /* , */ + 0x40, /* - */ + 0x80, /* . */ + 0x52, /* / */ + 0x3F, /* 0 */ + 0x06, /* 1 */ + 0x5B, /* 2 */ + 0x4F, /* 3 */ + 0x66, /* 4 */ + 0x6D, /* 5 */ + 0x7D, /* 6 */ + 0x07, /* 7 */ + 0x7F, /* 8 */ + 0x6F, /* 9 */ + 0x09, /* : */ + 0x0D, /* ; */ + 0x61, /* < */ + 0x48, /* = */ + 0x43, /* > */ + 0xD3, /* ? */ + 0x5F, /* @ */ + 0x77, /* A */ + 0x7C, /* B */ + 0x39, /* C */ + 0x5E, /* D */ + 0x79, /* E */ + 0x71, /* F */ + 0x3D, /* G */ + 0x76, /* H */ + 0x30, /* I */ + 0x1E, /* J */ + 0x75, /* K */ + 0x38, /* L */ + 0x15, /* M */ + 0x37, /* N */ + 0x3F, /* O */ + 0x73, /* P */ + 0x6B, /* Q */ + 0x33, /* R */ + 0x6D, /* S */ + 0x78, /* T */ + 0x3E, /* U */ + 0x3E, /* V */ + 0x2A, /* W */ + 0x76, /* X */ + 0x6E, /* Y */ + 0x5B, /* Z */ + 0x39, /* [ */ + 0x64, /* \ */ + 0x0F, /* ] */ + 0x23, /* ^ */ + 0x08, /* _ */ + 0x02, /* ` */ + 0x5F, /* a */ + 0x7C, /* b */ + 0x58, /* c */ + 0x5E, /* d */ + 0x7B, /* e */ + 0x71, /* f */ + 0x6F, /* g */ + 0x74, /* h */ + 0x10, /* i */ + 0x0C, /* j */ + 0x75, /* k */ + 0x30, /* l */ + 0x14, /* m */ + 0x54, /* n */ + 0x5C, /* o */ + 0x73, /* p */ + 0x67, /* q */ + 0x50, /* r */ + 0x6D, /* s */ + 0x78, /* t */ + 0x1C, /* u */ + 0x1C, /* v */ + 0x14, /* w */ + 0x76, /* x */ + 0x6E, /* y */ + 0x5B, /* z */ + // Note : Removed last 4 characters to reduce program size as of v 1.3.0 +// 0x46, /* { */ +// 0x30, /* | */ +// 0x70, /* } */ +// 0x01, /* ~ */ +}; + +// Class for some common functions +class TM1638plus_common{ + +public: + // Constructor + TM1638plus_common(); + + // Used instead of arduino function "shiftin" when _HIGH_FREQ is set to true + uint8_t HighFreqshiftin(uint8_t dataPin, uint8_t clockPin, uint8_t bitOrder) ; + // Used instead of arduino function "shiftOut" when _HIGH_FREQ is set to true + void HighFreqshiftOut(uint8_t dataPin, uint8_t clockPin, uint8_t bitOrder, uint8_t val); + +}; + +#endif diff --git a/tasmota/settings.h b/tasmota/settings.h index 5f8484ea0..6a9e60795 100644 --- a/tasmota/settings.h +++ b/tasmota/settings.h @@ -375,8 +375,9 @@ struct { uint8_t param[PARAM8_SIZE]; // 2FC SetOption32 .. SetOption49 int16_t toffset[2]; // 30E uint8_t display_font; // 312 + uint8_t display_type; // 313 - uint8_t free_313[44]; // 313 + uint8_t free_314[43]; // 314 uint8_t tuyamcu_topic; // 33F Manage tuyaSend topic. ex_energy_power_delta on 6.6.0.20, replaced on 8.5.0.1 uint16_t domoticz_update_timer; // 340 diff --git a/tasmota/tasmota_configurations.h b/tasmota/tasmota_configurations.h index e0bf0100e..c469f264a 100644 --- a/tasmota/tasmota_configurations.h +++ b/tasmota/tasmota_configurations.h @@ -321,6 +321,7 @@ #define USE_DISPLAY_RA8876 // [DisplayModel 10] #define USE_DISPLAY_ST7789 // [DisplayModel 12] Enable ST7789 module #define USE_DISPLAY_SSD1331 // [DisplayModel 14] Enable SSD1331 module + #define USE_DISPLAY_TM1637 // [DisplayModel 15] Enable TM1637, TM1638 modules #undef DEBUG_THEO // Disable debug code #undef USE_DEBUG_DRIVER // Disable debug code diff --git a/tasmota/xdrv_13_display.ino b/tasmota/xdrv_13_display.ino index c3ecd407b..fcecc087c 100755 --- a/tasmota/xdrv_13_display.ino +++ b/tasmota/xdrv_13_display.ino @@ -82,6 +82,7 @@ const uint8_t DISPLAY_LOG_ROWS = 32; // Number of lines in display log #define D_CMND_DISP_SETLED "SetLED" #define D_CMND_DISP_BUTTONS "Buttons" #define D_CMND_DISP_SCROLLTEXT "ScrollText" +#define D_CMND_DISP_TYPE "Type" @@ -99,7 +100,7 @@ enum XdspFunctions { FUNC_DISPLAY_INIT_DRIVER, FUNC_DISPLAY_INIT, FUNC_DISPLAY_E , FUNC_DISPLAY_NUMBER, FUNC_DISPLAY_FLOAT, FUNC_DISPLAY_NUMBERNC, FUNC_DISPLAY_FLOATNC, FUNC_DISPLAY_BRIGHTNESS, FUNC_DISPLAY_RAW, FUNC_DISPLAY_LEVEL, FUNC_DISPLAY_SEVENSEG_TEXT, FUNC_DISPLAY_SEVENSEG_TEXTNC, FUNC_DISPLAY_SCROLLDELAY, FUNC_DISPLAY_CLOCK, FUNC_DISPLAY_SETLEDS, FUNC_DISPLAY_SETLED, - FUNC_DISPLAY_BUTTONS, FUNC_DISPLAY_SCROLLTEXT + FUNC_DISPLAY_BUTTONS, FUNC_DISPLAY_SCROLLTEXT, FUNC_DISPLAY_TYPE }; enum DisplayInitModes { DISPLAY_INIT_MODE, DISPLAY_INIT_PARTIAL, DISPLAY_INIT_FULL }; @@ -114,7 +115,7 @@ const char kDisplayCommands[] PROGMEM = D_PRFX_DISPLAY "|" // Prefix "|" D_CMND_DISP_CLEAR "|" D_CMND_DISP_NUMBER "|" D_CMND_DISP_FLOAT "|" D_CMND_DISP_NUMBERNC "|" D_CMND_DISP_FLOATNC "|" D_CMND_DISP_BRIGHTNESS "|" 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_SETLEDS "|" D_CMND_DISP_SETLED "|" - D_CMND_DISP_BUTTONS "|" D_CMND_DISP_SCROLLTEXT + D_CMND_DISP_BUTTONS "|" D_CMND_DISP_SCROLLTEXT "|" D_CMND_DISP_TYPE ; void (* const DisplayCommand[])(void) PROGMEM = { @@ -127,7 +128,7 @@ void (* const DisplayCommand[])(void) PROGMEM = { , &CmndDisplayClear, &CmndDisplayNumber, &CmndDisplayFloat, &CmndDisplayNumberNC, &CmndDisplayFloatNC, &CmndDisplayBrightness, &CmndDisplayRaw, &CmndDisplayLevel, &CmndDisplaySevensegText, &CmndDisplaySevensegTextNC, &CmndDisplayScrollDelay, &CmndDisplayClock, &CmndDisplayTextNC, &CmndDisplaySetLEDs, &CmndDisplaySetLED, - &CmndDisplayButtons, &CmndDisplayScrollText + &CmndDisplayButtons, &CmndDisplayScrollText, &CmndDisplayType }; char *dsp_str; @@ -1875,32 +1876,50 @@ void CmndDisplaySetLEDs(void) void CmndDisplaySetLED(void) -{ - if (!renderer) { - XdspCall(FUNC_DISPLAY_SETLED); - } - ResponseCmndChar(XdrvMailbox.data); -} - -void CmndDisplayButtons(void) -{ - if (!renderer) { - XdspCall(FUNC_DISPLAY_BUTTONS); - } - ResponseCmndNumber(XdrvMailbox.payload); -} - - -void CmndDisplayScrollText(void) { bool result = false; if (!renderer) { - result = XdspCall(FUNC_DISPLAY_SCROLLTEXT); + result = XdspCall(FUNC_DISPLAY_SETLED); } if(result) ResponseCmndNumber(XdrvMailbox.payload); else ResponseCmndChar(XdrvMailbox.data); } +void CmndDisplayButtons(void) +{ + bool result = false; + if (!renderer) { + result = XdspCall(FUNC_DISPLAY_BUTTONS); + } + if(result) ResponseCmndNumber(XdrvMailbox.payload); + else ResponseCmndChar(XdrvMailbox.data); +} + + +void CmndDisplayScrollText(void) +{ + if (!renderer) { + XdspCall(FUNC_DISPLAY_SCROLLTEXT); + } + ResponseCmndChar(XdrvMailbox.data); +} + + +void CmndDisplayType(void) +{ + if(ArgC() == 0) { + XdrvMailbox.payload = Settings.display_type; + } else { + uint8_t prev_type = Settings.display_type; + if(prev_type != XdrvMailbox.payload) { + if (!renderer) { + XdspCall(FUNC_DISPLAY_TYPE); + Settings.display_type = XdrvMailbox.payload; + } + } + } + ResponseCmndNumber(XdrvMailbox.payload); +} void CmndDisplaySize(void) { diff --git a/tasmota/xdsp_15_tm1637.ino b/tasmota/xdsp_15_tm1637.ino index 79bf3ab93..13260f4a7 100644 --- a/tasmota/xdsp_15_tm1637.ino +++ b/tasmota/xdsp_15_tm1637.ino @@ -1,5 +1,5 @@ /* - xdsp_15_tm1637.ino - Support for TM1637 seven-segment display (upto 6 digits) for Tasmota + xdsp_15_tm1637.ino - Support for TM1637- and TM1638-based seven-segment displays for Tasmota Copyright (C) 2021 Ajith Vasudevan @@ -21,29 +21,36 @@ #ifdef USE_DISPLAY_TM1637 /*********************************************************************************************\ This driver enables the display of numbers (both integers and floats) and basic text - on the inexpensive TM1637-based seven-segment modules (tested on both 4- and 6-digit variants). + on the inexpensive TM1637- and TM1638-based seven-segment modules. + Raw segments can also be displayed. - In addition, it is also possible to set brightness (8 levels), clear the display, scroll text, display - a rudimentary bar graph, and a Clock (12 hr and 24 hr). + + In addition, it is also possible to set brightness (8 levels), clear the display, scroll text, + display a rudimentary bar graph, and a Clock (12 hr and 24 hr). To use, compile Tasmota with USE_DISPLAY and USE_DISPLAY_TM1637, or build the tasmota-display env. - The pins to use are "SSPI MOSI" and "SSPI SCLK". - - Connect the TM1637 display module's DIO and CLK pins to any free GPIOs of the ESP8266 module + Connect the TM1637 or TM1638 display module's pins to any free GPIOs of the ESP8266 module and assign the pins as follows from Tasmota's GUI: DIO hardware pin --> "SSPI MOSI" CLK hardware pin --> "SSPI SCLK" - - Once the device restarts the following "Display" commands become available: + STB hardware pin --> "SSPI MISO" (Only for TM1638) + Once the device restarts the following "Display" commands should become available: + + In case you get a stat//RESULT = {"Command":"Unknown"} result for any of these commands, + please ensure that your DisplayModel is set to 15 using the command "DisplayModel 15" - DisplaySize size {1-6} - Sets the number of digits to use. This is typically set to the actual number of digits available - in the display module. command e.g., "DisplaySize 6" + DisplayType type {0|1|2} + + Sets the display type. 0 => TM1637, 4 digit + 1 => TM1637, 6 digit + 2 => TM1638, 8 digit + Command e.g., "DisplayType 1" // to enable TM1637 6-digit variant + DisplayClear @@ -51,6 +58,7 @@ Clears the display, command: "DisplayClear" + DisplayNumber num [,position {0-(NUM_DIGITS-1))} [,leading_zeros {0|1} [,length {1 to NUM_DIGITS}]]] Clears and then displays number without decimal. command e.g., "DisplayNumber 1234" @@ -58,6 +66,8 @@ 'leading zeros' can be 1 or 0 (default), 'length' can be 1 to NUM_DIGITS, 'position' can be 0 (left-most) to NUM_DIGITS (right-most). See function description below for more details. + + DisplayNumberNC num [,position {0-(NUM_DIGITS-1))} [,leading_zeros {0|1} [,length {1 to NUM_DIGITS}]]] Display integer number as above, but without clearing first. e.g., "DisplayNumberNC 1234". Usage is same as above. @@ -97,10 +107,12 @@ Clears and then displays basic text. command e.g., "DisplayText ajith vasudevan" Control 'length' and 'position' with "DisplayText , , " 'length' can be 1 to NUM_DIGITS, 'position' can be 0 (left-most) to NUM_DIGITS-1 (right-most) + A caret(^) symbol in the text input is dispayed as the degrees(°) symbol. This is useful for displaying Temperature! For example, the command "DisplayText 22.5^" will display "22.5°". + DisplayTextNC text [, position {0-NUM_DIGITS-1} [,length {1 to NUM_DIGITS}]] Clears first, then displays text. Usage is same as above. @@ -132,16 +144,63 @@ "DisplayClock 2" // 24 hr format "DisplayClock 0" // turn off clock + + +Commands specific to TM1638 +==================================== + + DisplaySetLEDs bit_array {0-255} + + Sets the 8 LEDs (not the digits!) on the TM1638 module to the binary number represented by the input bit_array. + For example, "DisplaySetLEDs 3" would light up the first and second LED (from left), because 3 => 00000011 + + + + DisplaySetLED position {0-7}, value {0|1} + + Sets a specified LED to either ON or OFF. e.g., "DisplaySetLED 2, 1" would light up the 3rd LED (2 => 3rd position) + + + + DisplayButtons + + Causes the current state of the buttons to be returned as a "STAT" message of the form stat/TM1638/RESULT = {"DisplayButtons": } + The button value is the decimal representation of the bit-array that constitutes the button states. For example, if the 5th button is pressed and the + DisplayButtons command is issued, the response will be stat/TM1638/RESULT = {"DisplayButtons": 16} because 16 => 2^(5-1) + if the 2nd and 3rd buttons are pressed together and the DisplayButtons command is issued, the response will be + stat/TM1638/RESULT = {"DisplayButtons": 6} because 6 => 2^(2-1) + 2^(3-1) + + + + Button Functionality (TM1638 only): +====================================== + When this driver is initialized with "DisplayType 2" (TM1638), the buttons on the TM1638 module can be used + to toggle the corresponding LEDs. + + In addition, if SwitchTopic is set to some value, then for each button press, a TOGGLE for that switch number is sent. + + For example, if Topic = "TM1638" and SwitchTopic = "TEST_TM1638", and if S3 is pressed, then, + + 1) a STAT message is sent : stat/TM1638/RESULT = {"TM1638_BUTTONS":4} // (4 = 2^(3-1)) + and 2) a TOGGLE command is sent: cmnd/TEST/POWER3 = TOGGLE + + \*********************************************************************************************/ #define XDSP_15 15 -#include "SevenSegmentTM1637.h" -SevenSegmentTM1637 *display; +#include "SevenSegmentTM1637.h" +#include + +SevenSegmentTM1637 *disp37; +TM1638plus *disp38; +bool driverinited = false; bool showClock = false; bool clock24 = false; char tm[5]; char msg[60]; +uint8_t buttons; +uint8_t prevbuttons; uint32_t NUM_DIGITS = 4; uint32_t prev_num_digits = 4; bool scroll = false; @@ -149,7 +208,10 @@ uint32_t scrolldelay = 4; uint32_t scrollindex = 0; uint32_t iteration = 0; uint32_t brightness = 5; - +char modelname[8]; +enum displaytypes { TM1637, TM1638 }; +uint8_t displaytype = TM1637; +bool modechanged = false; #define BRIGHTNESS_MIN 0 // Display OFF #define BRIGHTNESS_MAX 8 @@ -157,21 +219,42 @@ uint32_t brightness = 5; #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 char scrolltext[CMD_MAX_LEN]; /*********************************************************************************************\ * Init function \*********************************************************************************************/ -bool TM1637Init(void) { - display = new SevenSegmentTM1637(Pin(GPIO_SSPI_SCLK), Pin(GPIO_SSPI_MOSI) ); - NUM_DIGITS = Settings.display_size > 3 ? Settings.display_size : 4; - Settings.display_size = NUM_DIGITS; - display->begin(NUM_DIGITS, 1); - display->setBacklight(brightness * 10); - clearDisplay(); - Settings.display_model = XDSP_15; - AddLog(LOG_LEVEL_INFO, PSTR("DSP: TM1637 display driver initialized")); +bool DriverInit(void) { + if(Settings.display_model == XDSP_15) { + if(driverinited) return true; + + if(Settings.display_type == 2) { NUM_DIGITS = 8; displaytype = TM1638; } + else if(Settings.display_type == 1) { NUM_DIGITS = 6; displaytype = TM1637; } + else { Settings.display_type = 0; NUM_DIGITS = 4; displaytype = TM1637; } + + if(displaytype == TM1637) { + strcpy(modelname, "TM1637"); + disp37 = new SevenSegmentTM1637(Pin(GPIO_SSPI_SCLK), Pin(GPIO_SSPI_MOSI) ); + disp37->begin(NUM_DIGITS, 1); + } else if(displaytype == TM1638) { + strcpy(modelname, "TM1638"); + disp38 = new TM1638plus(Pin(GPIO_SSPI_MISO), Pin(GPIO_SSPI_SCLK), Pin(GPIO_SSPI_MOSI), true ); + NUM_DIGITS = 8; + disp38->displayBegin(); + } + Settings.display_size = NUM_DIGITS; // Can use to check current display size + clearDisplay(); + brightness = (Settings.display_dimmer ? Settings.display_dimmer : brightness); + setBrightness(brightness); + driverinited = true; + modechanged = false; + AddLog(LOG_LEVEL_INFO, PSTR("DSP: %s display driver initialized with %d digits (DisplayType %d)"), modelname, NUM_DIGITS, Settings.display_type); + } + return true; } @@ -182,7 +265,7 @@ bool TM1637Init(void) { * commands: DisplayNumber num [,position {0-(NUM_DIGITS-1)} [,leading_zeros {0|1} [,length {1 to NUM_DIGITS}]]] * DisplayNumberNC num [,position {0-(NUM_DIGITS-1)} [,leading_zeros {0|1} [,length {1 to NUM_DIGITS}]]] // "NC" --> "No Clear" \*********************************************************************************************/ -bool CmndTM1637Number(bool clear) { +bool CmndNumber(bool clear) { char sNum[CMD_MAX_LEN]; char sLeadingzeros[CMD_MAX_LEN]; char sPosition[CMD_MAX_LEN]; @@ -212,10 +295,10 @@ bool CmndTM1637Number(bool clear) { if((position < 0) || (position > (NUM_DIGITS-1))) position = 0; - AddLog(LOG_LEVEL_DEBUG, PSTR("LOG: TM1637: num=%d"), num); - AddLog(LOG_LEVEL_DEBUG, PSTR("LOG: TM1637: position=%d"), position); - AddLog(LOG_LEVEL_DEBUG, PSTR("LOG: TM1637: leadingzeros=%d"), leadingzeros); - AddLog(LOG_LEVEL_DEBUG, PSTR("LOG: TM1637: length=%d"), length); + AddLog(LOG_LEVEL_DEBUG, PSTR("LOG: %s: num=%d"), modelname, num); + AddLog(LOG_LEVEL_DEBUG, PSTR("LOG: %s: position=%d"), modelname, position); + AddLog(LOG_LEVEL_DEBUG, PSTR("LOG: %s: leadingzeros=%d"), modelname, leadingzeros); + AddLog(LOG_LEVEL_DEBUG, PSTR("LOG: %s: length=%d"), modelname, length); if(clear) clearDisplay(); @@ -227,17 +310,18 @@ bool CmndTM1637Number(bool clear) { char pad = (leadingzeros ? '0': ' '); uint32_t i = position; uint8_t rawBytes[1]; - rawBytes[0] = display->encode(pad); + for(; iNUM_DIGITS) break; - display->printRaw(rawBytes, 1, i); + if(displaytype == TM1637) { rawBytes[0] = disp37->encode(pad); disp37->printRaw(rawBytes, 1, i); } + else if(displaytype == TM1638) disp38->displayASCII(i, pad); } for(uint32_t j = 0; i< position + length; i++, j++) { - if(txt[j] == 0) break; - rawBytes[0] = display->encode(txt[j]); if(i>NUM_DIGITS) break; - display->printRaw(rawBytes, 1, i); + if(txt[j] == 0) break; + if(displaytype == TM1637) { rawBytes[0] = disp37->encode(txt[j]); disp37->printRaw(rawBytes, 1, i); } + else if(displaytype == TM1638) disp38->displayASCII(i, txt[j]); } return true; @@ -251,7 +335,7 @@ bool CmndTM1637Number(bool clear) { * commands: DisplayFloat num [,position {0-(NUM_DIGITS-1)} [,precision {0-NUM_DIGITS} [,length {1 to NUM_DIGITS}]]] * DisplayFloatNC num [,position {0-(NUM_DIGITS-1)} [,precision {0-NUM_DIGITS} [,length {1 to NUM_DIGITS}]]] // "NC" --> "No Clear" \*********************************************************************************************/ -bool CmndTM1637Float(bool clear) { +bool CmndFloat(bool clear) { char sNum[CMD_MAX_LEN]; char sPrecision[CMD_MAX_LEN]; @@ -292,22 +376,36 @@ bool CmndTM1637Float(bool clear) { if((length <= 0) || (length > NUM_DIGITS)) length = NUM_DIGITS; char s[30]; - ext_snprintf_P(s, sizeof(s), PSTR("LOG: TM1637: num=%*_f"), 4, &fnum); + ext_snprintf_P(s, sizeof(s), PSTR("LOG: %s: num=%4_f"), modelname, &fnum); AddLog(LOG_LEVEL_DEBUG, PSTR("%s"), s); - AddLog(LOG_LEVEL_DEBUG, PSTR("LOG: TM1637: precision=%d"), precision); - AddLog(LOG_LEVEL_DEBUG, PSTR("LOG: TM1637: length=%d"), length); - uint8_t rawBytes[1]; - for(uint32_t i=0, j=0; iencode(txt[i]); - if(txt[i+1] == '.') { - rawBytes[0] = rawBytes[0] | 128; - i++; - length++; + AddLog(LOG_LEVEL_DEBUG, PSTR("LOG: %s: precision=%d"), modelname, precision); + AddLog(LOG_LEVEL_DEBUG, PSTR("LOG: %s: length=%d"), modelname, length); + + if(displaytype == TM1637) { + uint8_t rawBytes[1]; + for(uint32_t i=0, j=0; iencode(txt[i]); + if(txt[i+1] == '.') { + rawBytes[0] = rawBytes[0] | 128; + i++; + length++; + } + if((j+position) > NUM_DIGITS) break; + disp37->printRaw(rawBytes, 1, j+position); } - if((j+position) > NUM_DIGITS) break; - display->printRaw(rawBytes, 1, j+position); - } + } else if(displaytype == TM1638) { + for(uint32_t i=0, j=0; i 7) break; + if(txt[i] == 0) break; + if(txt[i+1] == '.') { + disp38->displayASCIIwDot(j+position, txt[i]); + i++; + length++; + } + else disp38->displayASCII(j+position, txt[i]); + } + } return true; } @@ -317,7 +415,7 @@ bool CmndTM1637Float(bool clear) { // * Clears the display // * Command: DisplayClear // \*********************************************************************************************/ -bool CmndTM1637Clear(void) { +bool CmndClear(void) { clearDisplay(); sprintf(msg, PSTR("Cleared")); XdrvMailbox.data = msg; @@ -325,10 +423,16 @@ bool CmndTM1637Clear(void) { } +// /*********************************************************************************************\ +// * Clears the display +// \*********************************************************************************************/ void clearDisplay (void) { - unsigned char arr[] = {0}; - AddLog(LOG_LEVEL_DEBUG, PSTR("Clearing digit %d"), NUM_DIGITS); - for(int i=0; iprintRaw(arr, 1, i); + if(displaytype == TM1637) { + unsigned char arr[] = {0}; + for(int i=0; iprintRaw(arr, 1, i); + } else if(displaytype == TM1638) { + for(int i=0; idisplay7Seg(i, 0); + } } @@ -336,9 +440,9 @@ void clearDisplay (void) { * Display scrolling text * Command: DisplayScrollText text \*********************************************************************************************/ -bool CmndTM1637ScrollText(void) { +bool CmndScrollText(void) { - AddLog(LOG_LEVEL_DEBUG, PSTR("LOG: TM1637: text=%s"), XdrvMailbox.data); + AddLog(LOG_LEVEL_DEBUG, PSTR("LOG: %s: text=%s"), modelname, XdrvMailbox.data); if(XdrvMailbox.data_len > SCROLL_MAX_LEN) { snprintf(msg, sizeof(msg), PSTR("Text too long. Length should be less than %d"), SCROLL_MAX_LEN); @@ -360,7 +464,7 @@ bool CmndTM1637ScrollText(void) { * Sets the scroll delay for scrolling text. * Command: DisplayScrollDelay delay {0-15} // default = 4 \*********************************************************************************************/ -bool CmndTM1637ScrollDelay(void) { +bool CmndScrollDelay(void) { if(scrolldelay<0) scrolldelay=0; scrolldelay = XdrvMailbox.payload; return true; @@ -372,28 +476,31 @@ bool CmndTM1637ScrollDelay(void) { * Scrolls a given string. Called every 50ms \*********************************************************************************************/ void scrollText(void) { - if(scroll) { - iteration++; - if(scrolldelay) iteration = iteration % scrolldelay; - else iteration = 0; - if(iteration) return; + iteration++; + if(scrolldelay) iteration = iteration % scrolldelay; + else iteration = 0; + if(iteration) return; - if(scrollindex > strlen(scrolltext)) { - scroll = false; - scrollindex = 0; - return; - } - bool clr = false; - uint8_t rawBytes[1]; - for(uint32_t i=0, j=scrollindex; i< strlen(scrolltext); i++, j++) { - if(i > (NUM_DIGITS-1)) break; - if(scrolltext[j] == 0) {clr = true;}; - char charToDisp = (clr ? ' ' : scrolltext[j]); - rawBytes[0] = display->encode(charToDisp); - display->printRaw(rawBytes, 1, i); - } - scrollindex++; + if(scrollindex > strlen(scrolltext)) { + scroll = false; + scrollindex = 0; + return; } + bool clr = false; + uint8_t rawBytes[1]; + for(uint32_t i=0, j=scrollindex; i< strlen(scrolltext); i++, j++) { + if(i > (NUM_DIGITS-1)) break; + if(scrolltext[j] == 0) {clr = true;}; + if(displaytype == TM1637) { + char charToDisp = (clr ? ' ' : scrolltext[j]); + rawBytes[0] = disp37->encode(charToDisp); + disp37->printRaw(rawBytes, 1, i); + } else if(displaytype == TM1638) { + disp38->displayASCII(i, (clr ? ' ' : scrolltext[j])); + } + + } + scrollindex++; } @@ -405,7 +512,7 @@ void scrollText(void) { * Displays a horizontal bar graph. Takes a percentage number (0-100) as input * Command: DisplayLevel level {0-100} \*********************************************************************************************/ -bool CmndTM1637Level(void) { +bool CmndLevel(void) { uint16_t val = XdrvMailbox.payload; if((val < LEVEL_MIN) || (val > LEVEL_MAX)) { sprintf(msg, PSTR("Level should be a number in the range [%d, %d]"), LEVEL_MIN, LEVEL_MAX); @@ -414,25 +521,31 @@ bool CmndTM1637Level(void) { } uint8_t totalBars = 2*NUM_DIGITS; - AddLog(LOG_LEVEL_DEBUG, PSTR("LOG: TM1637: CmndTM1637Level totalBars=%d"), totalBars); + AddLog(LOG_LEVEL_DEBUG, PSTR("LOG: %s: CmndLevel totalBars=%d"), modelname, totalBars); float barsToDisplay = totalBars * val / 100.0f; char txt[5]; ext_snprintf_P(txt, sizeof(txt), PSTR("%*_f"), 1, &barsToDisplay); - AddLog(LOG_LEVEL_DEBUG, PSTR("LOG: TM1637: CmndTM1637Level barsToDisplay=%s"), txt); + AddLog(LOG_LEVEL_DEBUG, PSTR("LOG: %s: CmndLevel barsToDisplay=%s"), modelname, txt); char s[4]; ext_snprintf_P(s, sizeof(s), PSTR("%*_f"), 0, &barsToDisplay); uint8_t numBars = atoi(s); - AddLog(LOG_LEVEL_DEBUG, PSTR("LOG: TM1637: CmndTM1637Level numBars=%d"), numBars); + AddLog(LOG_LEVEL_DEBUG, PSTR("LOG: %s: CmndLevel numBars=%d"), modelname, numBars); clearDisplay(); uint8_t rawBytes[1]; for(int i=1; i<=numBars; i++) { uint8_t digit = (i-1) / 2; - AddLog(LOG_LEVEL_DEBUG, PSTR("LOG: TM1637: CmndTM1637Level digit=%d"), digit); + AddLog(LOG_LEVEL_DEBUG, PSTR("LOG: %s: CmndLevel digit=%d"), modelname, digit); uint8_t value = (((i%2) == 0) ? 54 : 48); - AddLog(LOG_LEVEL_DEBUG, PSTR("LOG: TM1637: CmndTM1637Level value=%d"), value); - rawBytes[0] = value; - display->printRaw(rawBytes, 1, digit); + AddLog(LOG_LEVEL_DEBUG, PSTR("LOG: %s: CmndLevel value=%d"), modelname, value); + + if(displaytype == TM1637) { + rawBytes[0] = value; + disp37->printRaw(rawBytes, 1, digit); + } else if(displaytype == TM1638) { + disp38->display7Seg(digit, value); + } + } return true; } @@ -446,7 +559,7 @@ 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) { +bool CmndRaw(void) { uint8_t DATA[6] = { 0, 0, 0, 0, 0, 0 }; char as[CMD_MAX_LEN]; @@ -496,23 +609,32 @@ bool CmndTM1637Raw(void) { if(position < 0 || position > (NUM_DIGITS-1)) position = 0; - AddLog(LOG_LEVEL_DEBUG, PSTR("LOG: TM1637: a=%d"), DATA[0]); - AddLog(LOG_LEVEL_DEBUG, PSTR("LOG: TM1637: b=%d"), DATA[1]); - AddLog(LOG_LEVEL_DEBUG, PSTR("LOG: TM1637: c=%d"), DATA[2]); - AddLog(LOG_LEVEL_DEBUG, PSTR("LOG: TM1637: d=%d"), DATA[3]); - AddLog(LOG_LEVEL_DEBUG, PSTR("LOG: TM1637: e=%d"), DATA[4]); - AddLog(LOG_LEVEL_DEBUG, PSTR("LOG: TM1637: f=%d"), DATA[5]); + AddLog(LOG_LEVEL_DEBUG, PSTR("LOG: %s: a=%d"), modelname, DATA[0]); + AddLog(LOG_LEVEL_DEBUG, PSTR("LOG: %s: b=%d"), modelname, DATA[1]); + AddLog(LOG_LEVEL_DEBUG, PSTR("LOG: %s: c=%d"), modelname, DATA[2]); + AddLog(LOG_LEVEL_DEBUG, PSTR("LOG: %s: d=%d"), modelname, DATA[3]); + AddLog(LOG_LEVEL_DEBUG, PSTR("LOG: %s: e=%d"), modelname, DATA[4]); + AddLog(LOG_LEVEL_DEBUG, PSTR("LOG: %s: f=%d"), modelname, DATA[5]); - AddLog(LOG_LEVEL_DEBUG, PSTR("LOG: TM1637: length=%d"), length); - AddLog(LOG_LEVEL_DEBUG, PSTR("LOG: TM1637: position=%d"), position); + AddLog(LOG_LEVEL_DEBUG, PSTR("LOG: %s: length=%d"), modelname, length); + AddLog(LOG_LEVEL_DEBUG, PSTR("LOG: %s: position=%d"), modelname, position); - uint8_t rawBytes[1]; - for(uint32_t i=position; i(NUM_DIGITS-1)) break; - rawBytes[0] = DATA[i-position]; - display->printRaw(rawBytes, 1, i); + + if(displaytype == TM1637) { + uint8_t rawBytes[1]; + for(uint32_t i=position; i(NUM_DIGITS-1)) break; + rawBytes[0] = DATA[i-position]; + disp37->printRaw(rawBytes, 1, i); + } + } else if(displaytype == TM1638) { + for(uint32_t i=position; i7) break; + disp38->display7Seg(i, DATA[i-position]); + } } + return true; } @@ -524,7 +646,7 @@ bool CmndTM1637Raw(void) { * position parameters without affecting the rest of the display. * Command: DisplayText text [, position {0-(NUM_DIGITS-1)} [,length {1 to NUM_DIGITS}]] \*********************************************************************************************/ -bool CmndTM1637Text(bool clear) { +bool CmndText(bool clear) { char sString[CMD_MAX_LEN + 1]; char sPosition[CMD_MAX_LEN]; char sLength[CMD_MAX_LEN]; @@ -546,9 +668,9 @@ bool CmndTM1637Text(bool clear) { if((position < 0) || (position > (NUM_DIGITS-1))) position = 0; - AddLog(LOG_LEVEL_DEBUG, PSTR("LOG: TM1637: sString=%s"), sString); - AddLog(LOG_LEVEL_DEBUG, PSTR("LOG: TM1637: position=%d"), position); - AddLog(LOG_LEVEL_DEBUG, PSTR("LOG: TM1637: length=%d"), length); + AddLog(LOG_LEVEL_DEBUG, PSTR("LOG: %s: sString=%s"), modelname, sString); + AddLog(LOG_LEVEL_DEBUG, PSTR("LOG: %s: position=%d"), modelname, position); + AddLog(LOG_LEVEL_DEBUG, PSTR("LOG: %s: length=%d"), modelname, length); if(clear) clearDisplay(); @@ -556,18 +678,31 @@ bool CmndTM1637Text(bool clear) { if((length < 0) || (length > NUM_DIGITS)) length = NUM_DIGITS; uint32_t i = position; - uint8_t rawBytes[1]; - for(uint32_t j = 0; i< position + length; i++, j++) { - if(i > (NUM_DIGITS-1)) break; - if(sString[j] == 0) break; - rawBytes[0] = display->encode(sString[j]); - if(sString[j+1] == '.') { - rawBytes[0] = rawBytes[0] | 128; - j++; - } else if(sString[j] == '^') { - rawBytes[0] = 1 | 2 | 32 | 64; + if(displaytype == TM1637) { + uint8_t rawBytes[1]; + for(uint32_t j = 0; i< position + length; i++, j++) { + if(i > (NUM_DIGITS-1)) break; + if(sString[j] == 0) break; + rawBytes[0] = disp37->encode(sString[j]); + if(sString[j+1] == '.') { + rawBytes[0] = rawBytes[0] | 128; + j++; + } else if(sString[j] == '^') { + rawBytes[0] = 1 | 2 | 32 | 64; + } + disp37->printRaw(rawBytes, 1, i); } - display->printRaw(rawBytes, 1, i); + } else if(displaytype == TM1638) { + for(uint32_t j = 0; i< position + length; i++, j++) { + if(i > 7) break; + if(sString[j] == 0) break; + if(sString[j+1] == '.') { + disp38->displayASCIIwDot(i, sString[j]); + j++; + } else if(sString[j] == '^') { + disp38->display7Seg(i, (1 | 2 | 32 | 64)); + } else disp38->displayASCII(i, sString[j]); + } } return true; @@ -578,7 +713,7 @@ bool CmndTM1637Text(bool clear) { * Sets brightness of the display. * Command: DisplayBrightness {0-8} // 0 => off \*********************************************************************************************/ -bool CmndTM1637Brightness(void) { +bool CmndBrightness(void) { uint16_t val = XdrvMailbox.payload; if(ArgC() == 0) { @@ -592,20 +727,30 @@ bool CmndTM1637Brightness(void) { return false; } brightness = val; - - display->setBacklight(brightness*10); + setBrightness(brightness); return true; } +void setBrightness(uint8_t val) { + if((val < BRIGHTNESS_MIN) || (val > BRIGHTNESS_MAX)) val = 5; + Settings.display_dimmer = val; + if(displaytype == TM1637) disp37->setBacklight(val*10); + else if(displaytype == TM1638) disp38->brightness(val); +} + + + + + /*********************************************************************************************\ * 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 CmndClock(void) { showClock = XdrvMailbox.payload; @@ -613,8 +758,8 @@ bool CmndTM1637Clock(void) { if(XdrvMailbox.payload > 1) clock24 = true; else if(XdrvMailbox.payload == 1) clock24 = false; - AddLog(LOG_LEVEL_DEBUG, PSTR("LOG: TM1637: showClock=%d"), showClock); - AddLog(LOG_LEVEL_DEBUG, PSTR("LOG: TM1637: clock24=%d"), clock24); + AddLog(LOG_LEVEL_DEBUG, PSTR("LOG: %s: showClock=%d"), modelname, showClock); + AddLog(LOG_LEVEL_DEBUG, PSTR("LOG: %s: clock24=%d"), modelname, clock24); if(!showClock) { clearDisplay(); @@ -646,29 +791,144 @@ void showTime() { if(mn < 10) snprintf(tm, sizeof(tm), PSTR("%d0%d"), hr, mn); else snprintf(tm, sizeof(tm), PSTR("%d%d"), hr, mn); } - uint8_t rawBytes[1]; - for(uint32_t i = 0; i< 4; i++) { - rawBytes[0] = display->encode(tm[i]); - if((millis() % 1000) > 500 && (i == 1)) rawBytes[0] = rawBytes[0] | 128; - display->printRaw(rawBytes, 1, i); + + if(displaytype == TM1637) { + uint8_t rawBytes[1]; + for(uint32_t i = 0; i< 4; i++) { + rawBytes[0] = disp37->encode(tm[i]); + if((millis() % 1000) > 500 && (i == 1)) rawBytes[0] = rawBytes[0] | 128; + disp37->printRaw(rawBytes, 1, i); + } + } else if(displaytype == TM1638) { + for(uint32_t i = 0; i< 4; i++) { + if((millis() % 1000) > 500 && (i == 1)) disp38->displayASCIIwDot(i, tm[i]); + else disp38->displayASCII(i, tm[i]); + } } + +} + + + + +/*********************************************************************************************\ +* Sets all LEDs of the display. +* Command: DisplaySetLEDs {0-255} +\*********************************************************************************************/ +bool CmndSetLEDs(void) { + if(displaytype != TM1638) { + sprintf(msg, PSTR("Command not valid for DisplayType %d"), displaytype); + XdrvMailbox.data = msg; + return false; + } + if(ArgC() == 0) XdrvMailbox.payload = 0; + uint16_t val = XdrvMailbox.payload; + if((val < LED_MIN) || (val > LED_MAX)) { + sprintf(msg, PSTR("Set LEDs value should be a number in the range [%d, %d]"), LED_MIN, LED_MAX); + XdrvMailbox.data = msg; + return false; + } + disp38->setLEDs(val << 8); + return true; +} + + +/*********************************************************************************************\ +* Sets an LED at specified position. +* Command: DisplaySetLED position {0-7}, value {0|1} +\*********************************************************************************************/ +bool CmndSetLED(void) { + if(displaytype != TM1638) { + sprintf(msg, PSTR("Command not valid for DisplayType %d"), displaytype); + XdrvMailbox.data = msg; + return false; + } + if(ArgC() < 2) { + sprintf(msg, PSTR("Set LED requires two comma-separated numbers as arguments")); + XdrvMailbox.data = msg; + return false; + } + + char sVal[CMD_MAX_LEN]; + char sPos[CMD_MAX_LEN]; + uint32_t position = 0; + uint32_t value = 0; + + switch (ArgC()) + { + case 2 : + subStr(sVal, XdrvMailbox.data, ",", 2); + value = atoi(sVal); + case 1 : + subStr(sPos, XdrvMailbox.data, ",", 1); + position = atoi(sPos); + } + + + if((position < POSITION_MIN) || (position > POSITION_MAX)) { + sprintf(msg, PSTR("First argument, position should be in the range [%d, %d]"), POSITION_MIN, POSITION_MAX); + XdrvMailbox.data = msg; + return false; + } + + AddLog(LOG_LEVEL_DEBUG, PSTR("LOG: TM1638: position=%d"), position); + AddLog(LOG_LEVEL_DEBUG, PSTR("LOG: TM1638: value=%d"), value); + + disp38->setLED(position, value); + return true; +} + + +/*********************************************************************************************\ +* Reads the button states. Called every 50ms +\*********************************************************************************************/ +bool readButtons(void) { + buttons = disp38->readButtons(); + if(prevbuttons != buttons) { + prevbuttons = buttons; + if(!buttons) return true; + if(buttons) { + Response_P(PSTR("{\"TM1638_BUTTONS\":%d}"), buttons); + MqttPublishPrefixTopic_P(RESULT_OR_STAT, PSTR("BUTTONS")); + AddLog(LOG_LEVEL_DEBUG, PSTR("LOG: TM1638: buttons changed: %d"), buttons); + for(int i=0; i<8; i++) { + if(buttons & (1<setLED(i, (1 + SwitchGetVirtual(i)) % 2 ); + } + } + + SwitchHandler(1); + } + } + return true; } /*********************************************************************************************\ -* This function is called for all TM1637 Display functions. +* Returns the current state of the buttons as a decimal representation of the button states +* Command: DisplayButtons \*********************************************************************************************/ -bool TM1637Cmd(uint8_t fn) { +bool CmndButtons(void) { + if(displaytype != TM1638) { + sprintf(msg, PSTR("Command not valid for DisplayType %d"), displaytype); + XdrvMailbox.data = msg; + return false; + } + AddLog(LOG_LEVEL_DEBUG, PSTR("LOG: TM1638: buttons=%d"), buttons); + XdrvMailbox.payload = buttons; + return true; +} + + + + + +/*********************************************************************************************\ +* This function is called for all Display functions. +\*********************************************************************************************/ +bool MainFunc(uint8_t fn) { bool result = false; - NUM_DIGITS = Settings.display_size; - if(prev_num_digits != NUM_DIGITS) { // Cleck for change of display size, and re-init the library - AddLog(LOG_LEVEL_DEBUG, PSTR("LOG: TM1637: Size changed. Re-initializing library...")); - display = new SevenSegmentTM1637(Pin(GPIO_SSPI_SCLK), Pin(GPIO_SSPI_MOSI) ); - display->begin(NUM_DIGITS, 1); - display->setBacklight(40); - clearDisplay(); - prev_num_digits = NUM_DIGITS; - AddLog(LOG_LEVEL_DEBUG, PSTR("LOG: TM1637: Re-initialized library")); - } if(XdrvMailbox.data_len > CMD_MAX_LEN) { sprintf(msg, PSTR("Command text too long. Please limit it to %d characters"), CMD_MAX_LEN); @@ -678,43 +938,52 @@ bool TM1637Cmd(uint8_t fn) { switch (fn) { case FUNC_DISPLAY_CLEAR: - result = CmndTM1637Clear(); + result = CmndClear(); break; case FUNC_DISPLAY_NUMBER : - result = CmndTM1637Number(true); + result = CmndNumber(true); break; case FUNC_DISPLAY_NUMBERNC : - result = CmndTM1637Number(false); + result = CmndNumber(false); break; case FUNC_DISPLAY_FLOAT : - result = CmndTM1637Float(true); + result = CmndFloat(true); break; case FUNC_DISPLAY_FLOATNC : - result = CmndTM1637Float(false); + result = CmndFloat(false); break; case FUNC_DISPLAY_BRIGHTNESS: - result = CmndTM1637Brightness(); + result = CmndBrightness(); break; case FUNC_DISPLAY_RAW: - result = CmndTM1637Raw(); + result = CmndRaw(); break; case FUNC_DISPLAY_SEVENSEG_TEXT: - result = CmndTM1637Text(true); + result = CmndText(true); break; case FUNC_DISPLAY_SEVENSEG_TEXTNC: - result = CmndTM1637Text(false); + result = CmndText(false); break; case FUNC_DISPLAY_LEVEL: - result = CmndTM1637Level(); + result = CmndLevel(); break; case FUNC_DISPLAY_SCROLLTEXT: - result = CmndTM1637ScrollText(); + result = CmndScrollText(); break; case FUNC_DISPLAY_SCROLLDELAY: - result = CmndTM1637ScrollDelay(); + result = CmndScrollDelay(); break; + case FUNC_DISPLAY_SETLEDS: + result = CmndSetLEDs(); + break; + case FUNC_DISPLAY_SETLED: + result = CmndSetLED(); + break; + case FUNC_DISPLAY_BUTTONS: + result = CmndButtons(); + break; case FUNC_DISPLAY_CLOCK: - result = CmndTM1637Clock(); + result = CmndClock(); break; } @@ -725,23 +994,26 @@ bool TM1637Cmd(uint8_t fn) { /*********************************************************************************************\ * Interface \*********************************************************************************************/ - bool Xdsp15(uint8_t function) { bool result = false; - if (FUNC_DISPLAY_INIT_DRIVER == function) { - result = TM1637Init(); // init + if(function == FUNC_DISPLAY_MODEL) { + return true; } - else if (XDSP_15 == Settings.display_model) { + + if (Settings.display_model == XDSP_15) { switch (function) { - case FUNC_DISPLAY_MODEL: - AddLog(LOG_LEVEL_DEBUG, PSTR("LOG: TM1637: FUNC_DISPLAY_MODEL")); - result = true; + case FUNC_DISPLAY_INIT_DRIVER: + result = DriverInit(); // init break; case FUNC_DISPLAY_INIT: - CmndTM1637Clear(); - AddLog(LOG_LEVEL_DEBUG, PSTR("LOG: TM1637: FUNC_DISPLAY_INIT")); + AddLog(LOG_LEVEL_DEBUG, PSTR("LOG: %s: FUNC_DISPLAY_INIT: Display depends on DisplayType, currently %d"), modelname, Settings.display_type); + result = true; + break; + case FUNC_DISPLAY_TYPE: + AddLog(LOG_LEVEL_DEBUG, PSTR("LOG: %s: FUNC_DISPLAY_TYPE: DisplayType set to %d, restarting to take effect ..."), modelname, Settings.display_type); + TasmotaGlobal.restart_flag = 2; break; case FUNC_DISPLAY_SEVENSEG_TEXT: case FUNC_DISPLAY_CLEAR: @@ -755,14 +1027,16 @@ bool Xdsp15(uint8_t function) case FUNC_DISPLAY_SEVENSEG_TEXTNC: case FUNC_DISPLAY_SCROLLTEXT: case FUNC_DISPLAY_SCROLLDELAY: + case FUNC_DISPLAY_SETLEDS: + case FUNC_DISPLAY_SETLED: + case FUNC_DISPLAY_BUTTONS: case FUNC_DISPLAY_CLOCK: - result = TM1637Cmd(function); + result = MainFunc(function); break; case FUNC_DISPLAY_EVERY_50_MSECOND: - scrollText(); - if(showClock) { - showTime(); - } + if(scroll) scrollText(); + if(showClock) showTime(); + if(displaytype == TM1638) readButtons(); break; } } From ac14dab680903851abb7917693cb5757f2eecca9 Mon Sep 17 00:00:00 2001 From: Ajith Vasudevan Date: Fri, 19 Feb 2021 15:28:16 +0530 Subject: [PATCH 02/87] Better input validation error messages --- tasmota/xdrv_13_display.ino | 13 ++--- tasmota/xdsp_15_tm1637.ino | 97 +++++++++++++++++++------------------ 2 files changed, 54 insertions(+), 56 deletions(-) diff --git a/tasmota/xdrv_13_display.ino b/tasmota/xdrv_13_display.ino index fcecc087c..54aaca31c 100755 --- a/tasmota/xdrv_13_display.ino +++ b/tasmota/xdrv_13_display.ino @@ -1803,7 +1803,6 @@ void CmndDisplayBrightness(void) result = XdspCall(FUNC_DISPLAY_BRIGHTNESS); } if(result) ResponseCmndNumber(XdrvMailbox.payload); - else ResponseCmndChar(XdrvMailbox.data); } void CmndDisplayRaw(void) @@ -1821,7 +1820,6 @@ void CmndDisplayLevel(void) result = XdspCall(FUNC_DISPLAY_LEVEL); } if(result) ResponseCmndNumber(XdrvMailbox.payload); - else ResponseCmndChar(XdrvMailbox.data); } void CmndDisplaySevensegText(void) @@ -1871,7 +1869,6 @@ void CmndDisplaySetLEDs(void) result = XdspCall(FUNC_DISPLAY_SETLEDS); } if(result) ResponseCmndNumber(XdrvMailbox.payload); - else ResponseCmndChar(XdrvMailbox.data); } @@ -1882,26 +1879,24 @@ void CmndDisplaySetLED(void) result = XdspCall(FUNC_DISPLAY_SETLED); } if(result) ResponseCmndNumber(XdrvMailbox.payload); - else ResponseCmndChar(XdrvMailbox.data); } void CmndDisplayButtons(void) { bool result = false; if (!renderer) { - result = XdspCall(FUNC_DISPLAY_BUTTONS); + XdspCall(FUNC_DISPLAY_BUTTONS); } - if(result) ResponseCmndNumber(XdrvMailbox.payload); - else ResponseCmndChar(XdrvMailbox.data); } void CmndDisplayScrollText(void) { + bool result = false; if (!renderer) { - XdspCall(FUNC_DISPLAY_SCROLLTEXT); + result = XdspCall(FUNC_DISPLAY_SCROLLTEXT); } - ResponseCmndChar(XdrvMailbox.data); + if(result) ResponseCmndChar(XdrvMailbox.data); } diff --git a/tasmota/xdsp_15_tm1637.ino b/tasmota/xdsp_15_tm1637.ino index 13260f4a7..a97aa2d4c 100644 --- a/tasmota/xdsp_15_tm1637.ino +++ b/tasmota/xdsp_15_tm1637.ino @@ -21,7 +21,9 @@ #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- and TM1638-based seven-segment modules. Additionally, for the TM1638, + it allows the TM1638 LEDs to be toggled using its buttons. + Useful STAT messages are also sent when the TM1638 buttons are pressed. Raw segments can also be displayed. @@ -164,11 +166,15 @@ Commands specific to TM1638 DisplayButtons - Causes the current state of the buttons to be returned as a "STAT" message of the form stat/TM1638/RESULT = {"DisplayButtons": } - The button value is the decimal representation of the bit-array that constitutes the button states. For example, if the 5th button is pressed and the - DisplayButtons command is issued, the response will be stat/TM1638/RESULT = {"DisplayButtons": 16} because 16 => 2^(5-1) - if the 2nd and 3rd buttons are pressed together and the DisplayButtons command is issued, the response will be - stat/TM1638/RESULT = {"DisplayButtons": 6} because 6 => 2^(2-1) + 2^(3-1) + Causes the current state of the buttons/LEDs to be returned as a "STAT" message of the following form: + stat/TM1638/RESULT = {"DisplayButtons": {"S1":, "S2":, "S3":, "S4":, "S5":, "S6":, "S7":, "S8":, "Array": [,,,,,,,], "Byte": }} + where , , ... are the states of each of the buttons/LEDs, and is the decimal representation + of the bit-array that constitutes these states. + + For example, if the 5th button is pressed to turn ON LED5, and the "DisplayButtons" command is issued, + the response STAT message will be of the form: + stat/TM1638/RESULT = {"DisplayButtons": {"S1":0, "S2":0, "S3":0, "S4":0, "S5":1, "S6":0, "S7":0, "S8":0, "Array": [0,0,0,0,1,0,0,0], "Byte": 16}} + because 16 => 2^(5-1) @@ -177,12 +183,20 @@ Commands specific to TM1638 When this driver is initialized with "DisplayType 2" (TM1638), the buttons on the TM1638 module can be used to toggle the corresponding LEDs. - In addition, if SwitchTopic is set to some value, then for each button press, a TOGGLE for that switch number is sent. + In addition, for each button press, a STAT message of the following form is sent: - For example, if Topic = "TM1638" and SwitchTopic = "TEST_TM1638", and if S3 is pressed, then, + stat//RESULT = {"TM1638_BUTTON":} - 1) a STAT message is sent : stat/TM1638/RESULT = {"TM1638_BUTTONS":4} // (4 = 2^(3-1)) - and 2) a TOGGLE command is sent: cmnd/TEST/POWER3 = TOGGLE + where = button index (starting at 1) + = state of the corresponding LED. It can be "ON" or "OFF" + + For example, if Button3 is pressed, LED3 would light up and a STAT message is sent as follows: + + stat/TM1638/RESULT = {"TM1638_BUTTON3":"ON"} + + Pressing Button3 again would turn OFF LED3 and a STAT message is sent as follows: + + stat/TM1638/RESULT = {"TM1638_BUTTON3":"OFF"} \*********************************************************************************************/ @@ -200,6 +214,7 @@ bool clock24 = false; char tm[5]; char msg[60]; uint8_t buttons; +bool LED[8] = {false, false, false, false, false, false, false, false}; uint8_t prevbuttons; uint32_t NUM_DIGITS = 4; uint32_t prev_num_digits = 4; @@ -445,8 +460,7 @@ bool CmndScrollText(void) { AddLog(LOG_LEVEL_DEBUG, PSTR("LOG: %s: text=%s"), modelname, XdrvMailbox.data); if(XdrvMailbox.data_len > SCROLL_MAX_LEN) { - snprintf(msg, sizeof(msg), PSTR("Text too long. Length should be less than %d"), SCROLL_MAX_LEN); - XdrvMailbox.data = msg; + Response_P(PSTR("{\"Error\":\"Text too long. Length should be less than %d\"}"), SCROLL_MAX_LEN); return false; } else { snprintf(scrolltext, sizeof(scrolltext), PSTR("%s"), XdrvMailbox.data); @@ -515,8 +529,7 @@ void scrollText(void) { bool CmndLevel(void) { uint16_t val = XdrvMailbox.payload; if((val < LEVEL_MIN) || (val > LEVEL_MAX)) { - sprintf(msg, PSTR("Level should be a number in the range [%d, %d]"), LEVEL_MIN, LEVEL_MAX); - XdrvMailbox.data = msg; + Response_P(PSTR("{\"Error\":\"Level should be a number in the range [%d, %d]\"}"), LEVEL_MIN, LEVEL_MAX); return false; } @@ -535,10 +548,7 @@ bool CmndLevel(void) { uint8_t rawBytes[1]; for(int i=1; i<=numBars; i++) { uint8_t digit = (i-1) / 2; - AddLog(LOG_LEVEL_DEBUG, PSTR("LOG: %s: CmndLevel digit=%d"), modelname, digit); uint8_t value = (((i%2) == 0) ? 54 : 48); - AddLog(LOG_LEVEL_DEBUG, PSTR("LOG: %s: CmndLevel value=%d"), modelname, value); - if(displaytype == TM1637) { rawBytes[0] = value; disp37->printRaw(rawBytes, 1, digit); @@ -722,8 +732,7 @@ bool CmndBrightness(void) { } if((val < BRIGHTNESS_MIN) || (val > BRIGHTNESS_MAX)) { - sprintf(msg, PSTR("Brightness should be a number in the range [%d, %d]"), BRIGHTNESS_MIN, BRIGHTNESS_MAX); - XdrvMailbox.data = msg; + Response_P(PSTR("{\"Error\":\"Brightness should be a number in the range [%d, %d]\"}"), BRIGHTNESS_MIN, BRIGHTNESS_MAX); return false; } brightness = val; @@ -817,17 +826,16 @@ void showTime() { \*********************************************************************************************/ bool CmndSetLEDs(void) { if(displaytype != TM1638) { - sprintf(msg, PSTR("Command not valid for DisplayType %d"), displaytype); - XdrvMailbox.data = msg; + Response_P(PSTR("{\"Error\":\"Command not valid for DisplayType %d\"}"), displaytype); return false; } if(ArgC() == 0) XdrvMailbox.payload = 0; uint16_t val = XdrvMailbox.payload; if((val < LED_MIN) || (val > LED_MAX)) { - sprintf(msg, PSTR("Set LEDs value should be a number in the range [%d, %d]"), LED_MIN, LED_MAX); - XdrvMailbox.data = msg; - return false; - } + Response_P(PSTR("{\"Error\":\"Set LEDs value should be a number in the range [%d, %d]\"}"), LED_MIN, LED_MAX); + return false; + } + for(uint8_t i=0; i<8; i++) LED[i] = ((val & 2^i) >> i); disp38->setLEDs(val << 8); return true; } @@ -839,13 +847,11 @@ bool CmndSetLEDs(void) { \*********************************************************************************************/ bool CmndSetLED(void) { if(displaytype != TM1638) { - sprintf(msg, PSTR("Command not valid for DisplayType %d"), displaytype); - XdrvMailbox.data = msg; + Response_P(PSTR("{\"Error\":\"Command not valid for DisplayType %d\"}"), displaytype); return false; } if(ArgC() < 2) { - sprintf(msg, PSTR("Set LED requires two comma-separated numbers as arguments")); - XdrvMailbox.data = msg; + Response_P(PSTR("{\"Error\":\"Set LED requires two comma-separated numbers as arguments\"}")); return false; } @@ -866,14 +872,14 @@ bool CmndSetLED(void) { if((position < POSITION_MIN) || (position > POSITION_MAX)) { - sprintf(msg, PSTR("First argument, position should be in the range [%d, %d]"), POSITION_MIN, POSITION_MAX); - XdrvMailbox.data = msg; - return false; + Response_P(PSTR("{\"Error\":\"First argument, position should be in the range [%d, %d]\"}"), POSITION_MIN, POSITION_MAX); + return false; } AddLog(LOG_LEVEL_DEBUG, PSTR("LOG: TM1638: position=%d"), position); AddLog(LOG_LEVEL_DEBUG, PSTR("LOG: TM1638: value=%d"), value); + LED[position] = value; disp38->setLED(position, value); return true; } @@ -888,18 +894,15 @@ bool readButtons(void) { prevbuttons = buttons; if(!buttons) return true; if(buttons) { - Response_P(PSTR("{\"TM1638_BUTTONS\":%d}"), buttons); - MqttPublishPrefixTopic_P(RESULT_OR_STAT, PSTR("BUTTONS")); - AddLog(LOG_LEVEL_DEBUG, PSTR("LOG: TM1638: buttons changed: %d"), buttons); for(int i=0; i<8; i++) { - if(buttons & (1<setLED(i, (1 + SwitchGetVirtual(i)) % 2 ); + if(buttons & (1<setLED(i, LED[i]); + Response_P(PSTR("{\"TM1638_BUTTON%d\":\"%s\"}"), i+1, (LED[i]?PSTR("ON"):PSTR("OFF"))); + MqttPublishPrefixTopic_P(RESULT_OR_STAT, PSTR("BUTTONS")); } } - - SwitchHandler(1); } } return true; @@ -911,12 +914,13 @@ bool readButtons(void) { \*********************************************************************************************/ bool CmndButtons(void) { if(displaytype != TM1638) { - sprintf(msg, PSTR("Command not valid for DisplayType %d"), displaytype); - XdrvMailbox.data = msg; + Response_P(PSTR("{\"Error\":\"Command not valid for DisplayType %d\"}"), displaytype); return false; } AddLog(LOG_LEVEL_DEBUG, PSTR("LOG: TM1638: buttons=%d"), buttons); - XdrvMailbox.payload = buttons; + uint8_t byte = LED[0]<<0 | LED[1]<<1 | LED[2]<<2 | LED[3]<<3 | LED[4]<<4 | LED[5]<<5 | LED[6]<<6 | LED[7]<<7; + Response_P(PSTR("{\"DisplayButtons\": {\"S1\":%d, \"S2\":%d, \"S3\":%d, \"S4\":%d, \"S5\":%d, \"S6\":%d, \"S7\":%d, \"S8\":%d, \"Array\": [%d,%d,%d,%d,%d,%d,%d,%d], \"Byte\": %d}}"), + LED[0], LED[1], LED[2], LED[3], LED[4], LED[5], LED[6], LED[7], LED[0], LED[1], LED[2], LED[3], LED[4], LED[5], LED[6], LED[7], byte); return true; } @@ -931,9 +935,8 @@ bool MainFunc(uint8_t fn) { bool result = false; if(XdrvMailbox.data_len > CMD_MAX_LEN) { - sprintf(msg, PSTR("Command text too long. Please limit it to %d characters"), CMD_MAX_LEN); - XdrvMailbox.data = msg; - return result; + Response_P(PSTR("{\"Error\":\"Command text too long. Please limit it to %d characters\"}"), CMD_MAX_LEN); + return false; } switch (fn) { From 2f6cacc69bb2c0f2347f1a4d7e8f348822ee4df3 Mon Sep 17 00:00:00 2001 From: Ajith Vasudevan Date: Fri, 19 Feb 2021 18:39:42 +0530 Subject: [PATCH 03/87] Disabled Clock display automatically when a new command is issued --- tasmota/xdsp_15_tm1637.ino | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/tasmota/xdsp_15_tm1637.ino b/tasmota/xdsp_15_tm1637.ino index a97aa2d4c..11d52640f 100644 --- a/tasmota/xdsp_15_tm1637.ino +++ b/tasmota/xdsp_15_tm1637.ino @@ -39,12 +39,17 @@ CLK hardware pin --> "SSPI SCLK" STB hardware pin --> "SSPI MISO" (Only for TM1638) - - Once the device restarts the following "Display" commands should become available: - In case you get a stat//RESULT = {"Command":"Unknown"} result for any of these commands, - please ensure that your DisplayModel is set to 15 using the command "DisplayModel 15" - + Once the GPIO configuration is saved and the ESP8266/ESP32 module restarts, set the Display Model to 15 + using the command "DisplayModel 15" + + After this, depending upon your display variant, set your Display Type to 0, 1 or 2 using the command + "DisplayType 0" // for the 4-digit TM1637 + or "DisplayType 1" // for the 6-digit TM1637 + or "DisplayType 2" // for the 8-digit TM1638 + + After the ESP8266/ESP32 module restarts again, the following "Display" commands can be used: + DisplayType type {0|1|2} @@ -1032,8 +1037,9 @@ bool Xdsp15(uint8_t function) case FUNC_DISPLAY_SCROLLDELAY: case FUNC_DISPLAY_SETLEDS: case FUNC_DISPLAY_SETLED: - case FUNC_DISPLAY_BUTTONS: + case FUNC_DISPLAY_BUTTONS: case FUNC_DISPLAY_CLOCK: + showClock = false; result = MainFunc(function); break; case FUNC_DISPLAY_EVERY_50_MSECOND: From e2ea80ad8dc9b1b227387e24813c28a58a5ee8a7 Mon Sep 17 00:00:00 2001 From: Ajith Vasudevan Date: Sun, 21 Feb 2021 10:34:40 +0530 Subject: [PATCH 04/87] made brightness range consistent across display types --- tasmota/xdsp_15_tm1637.ino | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/tasmota/xdsp_15_tm1637.ino b/tasmota/xdsp_15_tm1637.ino index 11d52640f..57e087935 100644 --- a/tasmota/xdsp_15_tm1637.ino +++ b/tasmota/xdsp_15_tm1637.ino @@ -95,9 +95,9 @@ - DisplayBrightness num {0-8} + DisplayBrightness num {1-8} - Set brightness (0 (off) to 8) command e.g., "DisplayBrightness 2" + Set brightness (1 to 8) command e.g., "DisplayBrightness 2" @@ -231,9 +231,8 @@ uint32_t brightness = 5; char modelname[8]; enum displaytypes { TM1637, TM1638 }; uint8_t displaytype = TM1637; -bool modechanged = false; -#define BRIGHTNESS_MIN 0 // Display OFF +#define BRIGHTNESS_MIN 1 #define BRIGHTNESS_MAX 8 #define CMD_MAX_LEN 55 #define LEVEL_MIN 0 @@ -271,7 +270,6 @@ bool DriverInit(void) { brightness = (Settings.display_dimmer ? Settings.display_dimmer : brightness); setBrightness(brightness); driverinited = true; - modechanged = false; AddLog(LOG_LEVEL_INFO, PSTR("DSP: %s display driver initialized with %d digits (DisplayType %d)"), modelname, NUM_DIGITS, Settings.display_type); } @@ -726,7 +724,7 @@ bool CmndText(bool clear) { /*********************************************************************************************\ * Sets brightness of the display. -* Command: DisplayBrightness {0-8} // 0 => off +* Command: DisplayBrightness {1-8} \*********************************************************************************************/ bool CmndBrightness(void) { @@ -751,7 +749,7 @@ void setBrightness(uint8_t val) { if((val < BRIGHTNESS_MIN) || (val > BRIGHTNESS_MAX)) val = 5; Settings.display_dimmer = val; if(displaytype == TM1637) disp37->setBacklight(val*10); - else if(displaytype == TM1638) disp38->brightness(val); + else if(displaytype == TM1638) disp38->brightness(val-1); } From 5009a3fae9e23cb9e9935d91b78e97d9c8d5659b Mon Sep 17 00:00:00 2001 From: Ajith Vasudevan Date: Sun, 21 Feb 2021 11:07:12 +0530 Subject: [PATCH 05/87] Fixed bug where clock is disabled after a brightness change --- tasmota/xdsp_15_tm1637.ino | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/tasmota/xdsp_15_tm1637.ino b/tasmota/xdsp_15_tm1637.ino index 57e087935..da64edb1a 100644 --- a/tasmota/xdsp_15_tm1637.ino +++ b/tasmota/xdsp_15_tm1637.ino @@ -784,6 +784,7 @@ bool CmndClock(void) { * refreshes the time if clock is displayed \*********************************************************************************************/ void showTime() { + AddLog(LOG_LEVEL_DEBUG, PSTR("LOG: %s: showTime()"), modelname); uint8_t hr = RtcTime.hour; uint8_t mn = RtcTime.minute; // uint8_t hr = 1; @@ -1027,17 +1028,17 @@ bool Xdsp15(uint8_t function) case FUNC_DISPLAY_FLOAT: case FUNC_DISPLAY_NUMBERNC: case FUNC_DISPLAY_FLOATNC: - case FUNC_DISPLAY_BRIGHTNESS: 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: + showClock = false; + case FUNC_DISPLAY_BRIGHTNESS: case FUNC_DISPLAY_SETLEDS: case FUNC_DISPLAY_SETLED: case FUNC_DISPLAY_BUTTONS: - case FUNC_DISPLAY_CLOCK: - showClock = false; result = MainFunc(function); break; case FUNC_DISPLAY_EVERY_50_MSECOND: From db17863828de0f47b99bca825210bb33cb2a6149 Mon Sep 17 00:00:00 2001 From: Ajith Vasudevan Date: Sun, 21 Feb 2021 14:42:12 +0530 Subject: [PATCH 06/87] Removed unwanted log --- tasmota/xdsp_15_tm1637.ino | 1 - 1 file changed, 1 deletion(-) diff --git a/tasmota/xdsp_15_tm1637.ino b/tasmota/xdsp_15_tm1637.ino index da64edb1a..eec946083 100644 --- a/tasmota/xdsp_15_tm1637.ino +++ b/tasmota/xdsp_15_tm1637.ino @@ -784,7 +784,6 @@ bool CmndClock(void) { * refreshes the time if clock is displayed \*********************************************************************************************/ void showTime() { - AddLog(LOG_LEVEL_DEBUG, PSTR("LOG: %s: showTime()"), modelname); uint8_t hr = RtcTime.hour; uint8_t mn = RtcTime.minute; // uint8_t hr = 1; From c5f79aa532bcfeacdef07569cb44febc98bc2afb Mon Sep 17 00:00:00 2001 From: Ajith Vasudevan Date: Mon, 22 Feb 2021 12:04:30 +0530 Subject: [PATCH 07/87] Fixed some logs --- tasmota/xdsp_15_tm1637.ino | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/tasmota/xdsp_15_tm1637.ino b/tasmota/xdsp_15_tm1637.ino index 8f417ce1a..58b376271 100644 --- a/tasmota/xdsp_15_tm1637.ino +++ b/tasmota/xdsp_15_tm1637.ino @@ -751,9 +751,7 @@ bool CmndClock(void) { AddLog(LOG_LEVEL_DEBUG, PSTR("TM7: TM1637Data.show_clock %d, TM1637Data.clock_24 %d"), TM1637Data.show_clock, TM1637Data.clock_24); - if(!TM1637Data.show_clock) { - clearDisplay(); - } + clearDisplay(); return true; } @@ -877,7 +875,7 @@ bool readButtons(void) { if(TM1637Data.buttons) { for(int i=0; i<8; i++) { if(TM1637Data.buttons & (1<setLED(i, TM1637Data.LED[i]); Response_P(PSTR("{\"TM1638_BUTTON%d\":\"%s\"}"), i+1, (TM1637Data.LED[i]?PSTR("ON"):PSTR("OFF"))); @@ -898,7 +896,7 @@ bool CmndButtons(void) { Response_P(PSTR("{\"Error\":\"Command not valid for TM1637Data.display_type %d\"}"), TM1637Data.display_type); return false; } - AddLog(LOG_LEVEL_DEBUG, PSTR("LOG: TM1638: buttons=%d"), TM1637Data.buttons); + AddLog(LOG_LEVEL_DEBUG, PSTR("TM7: TM1638: buttons=%d"), TM1637Data.buttons); uint8_t byte = TM1637Data.LED[0]<<0 | TM1637Data.LED[1]<<1 | TM1637Data.LED[2]<<2 | TM1637Data.LED[3]<<3 | TM1637Data.LED[4]<<4 | TM1637Data.LED[5]<<5 | TM1637Data.LED[6]<<6 | TM1637Data.LED[7]<<7; Response_P(PSTR("{\"DisplayButtons\": {\"S1\":%d, \"S2\":%d, \"S3\":%d, \"S4\":%d, \"S5\":%d, \"S6\":%d, \"S7\":%d, \"S8\":%d, \"Array\": [%d,%d,%d,%d,%d,%d,%d,%d], \"Byte\": %d}}"), TM1637Data.LED[0], TM1637Data.LED[1], TM1637Data.LED[2], TM1637Data.LED[3], TM1637Data.LED[4], TM1637Data.LED[5], TM1637Data.LED[6], TM1637Data.LED[7], TM1637Data.LED[0], @@ -993,11 +991,11 @@ bool Xdsp15(uint8_t function) result = DriverInit(); // init break; case FUNC_DISPLAY_INIT: - AddLog(LOG_LEVEL_DEBUG, PSTR("LOG: %s: FUNC_DISPLAY_INIT: Display depends on TM1637Data.display_type, currently %d"), TM1637Data.model_name, Settings.display_type); + AddLog(LOG_LEVEL_DEBUG, PSTR("TM7: %s: FUNC_DISPLAY_INIT: Display depends on TM1637Data.display_type, currently %d"), TM1637Data.model_name, Settings.display_type); result = true; break; case FUNC_DISPLAY_TYPE: - AddLog(LOG_LEVEL_DEBUG, PSTR("LOG: %s: FUNC_DISPLAY_TYPE: TM1637Data.display_type set to %d, restarting to take effect ..."), TM1637Data.model_name, Settings.display_type); + AddLog(LOG_LEVEL_DEBUG, PSTR("TM7: %s: FUNC_DISPLAY_TYPE: TM1637Data.display_type set to %d, restarting to take effect ..."), TM1637Data.model_name, Settings.display_type); TasmotaGlobal.restart_flag = 2; break; case FUNC_DISPLAY_SEVENSEG_TEXT: From 75699fe203202a2832fbc81d73862828a8cbbc2b Mon Sep 17 00:00:00 2001 From: Ajith Vasudevan Date: Mon, 22 Feb 2021 15:12:48 +0530 Subject: [PATCH 08/87] Removed the need for SPI for TM1637 to work --- tasmota/support_tasmota.ino | 4 +--- tasmota/tasmota.ino | 5 ++++- tasmota/xdrv_13_display.ino | 2 +- tasmota/xdsp_interface.ino | 15 ++++++++++++++- 4 files changed, 20 insertions(+), 6 deletions(-) diff --git a/tasmota/support_tasmota.ino b/tasmota/support_tasmota.ino index 4742537a2..af9aca1e2 100644 --- a/tasmota/support_tasmota.ino +++ b/tasmota/support_tasmota.ino @@ -1678,9 +1678,7 @@ void GpioInit(void) ValidSpiPinUsed(GPIO_ST7789_DC) || // ST7789 CS may be omitted so chk DC too ValidSpiPinUsed(GPIO_ST7789_CS) || (ValidSpiPinUsed(GPIO_SSD1331_CS) && ValidSpiPinUsed(GPIO_SSD1331_DC)) || - ValidSpiPinUsed(GPIO_SDCARD_CS) || - (ValidSpiPinUsed(GPIO_TM1637CLK) && ValidSpiPinUsed(GPIO_TM1637DIO)) || - (ValidSpiPinUsed(GPIO_TM1638CLK) && ValidSpiPinUsed(GPIO_TM1638DIO) && ValidSpiPinUsed(GPIO_TM1638STB)) + ValidSpiPinUsed(GPIO_SDCARD_CS) ); // If SPI_CS and/or SPI_DC is used they must be valid TasmotaGlobal.spi_enabled = (valid_cs) ? SPI_MOSI_MISO : SPI_NONE; diff --git a/tasmota/tasmota.ino b/tasmota/tasmota.ino index c0b7b4669..7d52e5a44 100644 --- a/tasmota/tasmota.ino +++ b/tasmota/tasmota.ino @@ -147,6 +147,7 @@ struct { bool module_changed; // Indicate module changed since last restart bool wifi_stay_asleep; // Allow sleep only incase of ESP32 BLE bool no_autoexec; // Disable autoexec + bool tm1637_enabled; // TM1637 driver enabled StateBitfield global_state; // Global states (currently Wifi and Mqtt) (8 bits) uint8_t spi_enabled; // SPI configured @@ -227,7 +228,9 @@ void setup(void) { TasmotaGlobal.tele_period = 9999; TasmotaGlobal.active_device = 1; TasmotaGlobal.global_state.data = 0xF; // Init global state (wifi_down, mqtt_down) to solve possible network issues - +#if defined(USE_DISPLAY) && defined(USE_DISPLAY_TM1637) + TasmotaGlobal.tm1637_enabled = true; +#endif RtcRebootLoad(); if (!RtcRebootValid()) { RtcReboot.fast_reboot_count = 0; diff --git a/tasmota/xdrv_13_display.ino b/tasmota/xdrv_13_display.ino index 54aaca31c..fafeb0452 100755 --- a/tasmota/xdrv_13_display.ino +++ b/tasmota/xdrv_13_display.ino @@ -2779,7 +2779,7 @@ bool Xdrv13(uint8_t function) { bool result = false; - if ((TasmotaGlobal.i2c_enabled || TasmotaGlobal.spi_enabled || TasmotaGlobal.soft_spi_enabled) && XdspPresent()) { + if ((TasmotaGlobal.i2c_enabled || TasmotaGlobal.spi_enabled || TasmotaGlobal.soft_spi_enabled || TasmotaGlobal.tm1637_enabled) && XdspPresent()) { switch (function) { case FUNC_PRE_INIT: DisplayInitDriver(); diff --git a/tasmota/xdsp_interface.ino b/tasmota/xdsp_interface.ino index 3ca49f3d6..42907ae15 100644 --- a/tasmota/xdsp_interface.ino +++ b/tasmota/xdsp_interface.ino @@ -17,7 +17,7 @@ along with this program. If not, see . */ -#if defined(USE_I2C) || defined(USE_SPI) +#if defined(USE_I2C) || defined(USE_SPI) || defined(USE_DISPLAY_TM1637) #ifdef USE_DISPLAY #ifdef XFUNC_PTR_IN_ROM @@ -26,6 +26,8 @@ bool (* const xdsp_func_ptr[])(uint8_t) PROGMEM = { // Display Function Pointe bool (* const xdsp_func_ptr[])(uint8_t) = { // Display Function Pointers #endif +#if defined(USE_I2C) || defined(USE_SPI) + #ifdef XDSP_01 &Xdsp01, #endif @@ -82,10 +84,18 @@ bool (* const xdsp_func_ptr[])(uint8_t) = { // Display Function Pointers &Xdsp14, #endif +#endif // #if defined(USE_I2C) || defined(USE_SPI) + +#ifdef USE_DISPLAY_TM1637 + #ifdef XDSP_15 &Xdsp15, #endif +#endif // USE_DISPLAY_TM1637 + +#if defined(USE_I2C) || defined(USE_SPI) + #ifdef XDSP_16 &Xdsp16, #endif @@ -153,6 +163,9 @@ bool (* const xdsp_func_ptr[])(uint8_t) = { // Display Function Pointers #ifdef XDSP_32 &Xdsp32 #endif + +#endif // #if defined(USE_I2C) || defined(USE_SPI) + }; const uint8_t xdsp_present = sizeof(xdsp_func_ptr) / sizeof(xdsp_func_ptr[0]); // Number of drivers found From f16f2c187936f2f0fa5dcbcfa6c44256a1397726 Mon Sep 17 00:00:00 2001 From: Ajith Vasudevan Date: Tue, 23 Feb 2021 12:52:05 +0530 Subject: [PATCH 09/87] Fixed minor bug in DisplayScrollText --- tasmota/xdsp_15_tm1637.ino | 33 ++++++++++++++++++++++++++------- 1 file changed, 26 insertions(+), 7 deletions(-) diff --git a/tasmota/xdsp_15_tm1637.ino b/tasmota/xdsp_15_tm1637.ino index 58b376271..bba883711 100644 --- a/tasmota/xdsp_15_tm1637.ino +++ b/tasmota/xdsp_15_tm1637.ino @@ -464,6 +464,7 @@ bool CmndScrollText(void) { XdrvMailbox.data = TM1637Data.msg; return false; } 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; TM1637Data.scroll_index = 0; @@ -480,6 +481,10 @@ bool CmndScrollText(void) { * Command: DisplayTM1637Data.scroll_delay delay {0-15} // default = 4 \*********************************************************************************************/ bool CmndScrollDelay(void) { + if(ArgC() == 0) { + XdrvMailbox.payload = TM1637Data.scroll_delay; + return true; + } if(TM1637Data.scroll_delay<0) TM1637Data.scroll_delay=0; TM1637Data.scroll_delay = XdrvMailbox.payload; return true; @@ -501,17 +506,28 @@ void scrollText(void) { TM1637Data.scroll_index = 0; return; } - bool clr = false; uint8_t rawBytes[1]; - for(uint32_t i=0, j=TM1637Data.scroll_index; i< strlen(TM1637Data.scroll_text); i++, j++) { - if(i > (TM1637Data.num_digits-1)) break; - if(TM1637Data.scroll_text[j] == 0) {clr = true;}; + for(uint32_t i=0, j=TM1637Data.scroll_index; i< 1 + strlen(TM1637Data.scroll_text); i++, j++) { + if(i > (TM1637Data.num_digits-1)) { break; } + rawBytes[0] = tm1637display->encode(TM1637Data.scroll_text[j]); + bool dotSkipped = false; + if(TM1637Data.scroll_text[j+1] == '.') { + dotSkipped = true; + rawBytes[0] = rawBytes[0] | 128; + j++; + } else if(TM1637Data.scroll_text[j] == '^') { + rawBytes[0] = 1 | 2 | 32 | 64; + } + 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) { - char charToDisp = (clr ? ' ' : TM1637Data.scroll_text[j]); - rawBytes[0] = tm1637display->encode(charToDisp); tm1637display->printRaw(rawBytes, 1, i); } else if(TM1637Data.display_type == TM1638) { - tm1638display->displayASCII(i, (clr ? ' ' : TM1637Data.scroll_text[j])); + tm1638display->display7Seg(i, rawBytes[0]); } } @@ -676,12 +692,15 @@ bool CmndText(bool clear) { if(i > (TM1637Data.num_digits-1)) 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; tm1637display->printRaw(rawBytes, 1, i); } } else if(TM1637Data.display_type == TM1638) { From 5e3274af8f5c72dceed231e8fc27a98cd9b48d4c Mon Sep 17 00:00:00 2001 From: shaap Date: Tue, 2 Mar 2021 02:31:06 +0100 Subject: [PATCH 10/87] Add eMylo EAI-90 support Add support for eMylo EAI-90. WB3S Module has to be replaced by ESP-12. Please see eMyloEAI90.txt. --- eMyloEAI90.txt | 5 +++++ tasmota/tasmota.h | 2 +- tasmota/xdrv_16_tuyamcu.ino | 43 +++++++++++++++++++++++++++++++------ 3 files changed, 43 insertions(+), 7 deletions(-) create mode 100644 eMyloEAI90.txt diff --git a/eMyloEAI90.txt b/eMyloEAI90.txt new file mode 100644 index 000000000..aaa208053 --- /dev/null +++ b/eMyloEAI90.txt @@ -0,0 +1,5 @@ +Template TuyaMCU(54) + +Console: +Backlog SetOption66 1; TuyaMCU 11,16; TuyaMCU 36,6; TuyaMCU 37,1; SetOption59 1; SetOption72 1 +Rule1 on System#Boot do RuleTimer1 10 endon on Rules#Timer=1 do backlog TuyaSend8; RuleTimer1 10 endon \ No newline at end of file diff --git a/tasmota/tasmota.h b/tasmota/tasmota.h index 5f2f73c0b..8aa7ddd5f 100644 --- a/tasmota/tasmota.h +++ b/tasmota/tasmota.h @@ -417,7 +417,7 @@ enum TuyaSupportedFunctions { TUYA_MCU_FUNC_NONE, TUYA_MCU_FUNC_REL6, TUYA_MCU_FUNC_REL7, TUYA_MCU_FUNC_REL8, TUYA_MCU_FUNC_DIMMER = 21, TUYA_MCU_FUNC_DIMMER2, TUYA_MCU_FUNC_CT, TUYA_MCU_FUNC_RGB, TUYA_MCU_FUNC_WHITE, TUYA_MCU_FUNC_MODESET, TUYA_MCU_FUNC_REPORT1, TUYA_MCU_FUNC_REPORT2, - TUYA_MCU_FUNC_POWER = 31, TUYA_MCU_FUNC_CURRENT, TUYA_MCU_FUNC_VOLTAGE, TUYA_MCU_FUNC_BATTERY_STATE, TUYA_MCU_FUNC_BATTERY_PERCENTAGE, + TUYA_MCU_FUNC_POWER = 31, TUYA_MCU_FUNC_CURRENT, TUYA_MCU_FUNC_VOLTAGE, TUYA_MCU_FUNC_BATTERY_STATE, TUYA_MCU_FUNC_BATTERY_PERCENTAGE, TUYA_MCU_FUNC_POWER_COMBINED, TUYA_MCU_FUNC_POWER_TOTAL, TUYA_MCU_FUNC_REL1_INV = 41, TUYA_MCU_FUNC_REL2_INV, TUYA_MCU_FUNC_REL3_INV, TUYA_MCU_FUNC_REL4_INV, TUYA_MCU_FUNC_REL5_INV, TUYA_MCU_FUNC_REL6_INV, TUYA_MCU_FUNC_REL7_INV, TUYA_MCU_FUNC_REL8_INV, TUYA_MCU_FUNC_LOWPOWER_MODE = 51, diff --git a/tasmota/xdrv_16_tuyamcu.ino b/tasmota/xdrv_16_tuyamcu.ino index 22eba70f2..476328ef1 100644 --- a/tasmota/xdrv_16_tuyamcu.ino +++ b/tasmota/xdrv_16_tuyamcu.ino @@ -386,7 +386,7 @@ inline bool TuyaFuncIdValid(uint8_t fnId) { return (fnId >= TUYA_MCU_FUNC_SWT1 && fnId <= TUYA_MCU_FUNC_SWT4) || (fnId >= TUYA_MCU_FUNC_REL1 && fnId <= TUYA_MCU_FUNC_REL8) || (fnId >= TUYA_MCU_FUNC_DIMMER && fnId <= TUYA_MCU_FUNC_REPORT2) || - (fnId >= TUYA_MCU_FUNC_POWER && fnId <= TUYA_MCU_FUNC_BATTERY_PERCENTAGE) || + (fnId >= TUYA_MCU_FUNC_POWER && fnId <= TUYA_MCU_FUNC_POWER_TOTAL) || (fnId >= TUYA_MCU_FUNC_REL1_INV && fnId <= TUYA_MCU_FUNC_REL8_INV) || (fnId >= TUYA_MCU_FUNC_ENUM1 && fnId <= TUYA_MCU_FUNC_ENUM4) || (fnId >= TUYA_MCU_FUNC_MOTOR_DIR && fnId <= TUYA_MCU_FUNC_DUMMY) || @@ -697,13 +697,41 @@ void TuyaProcessStatePacket(void) { uint8_t fnId; uint16_t dpDataLen; bool PowerOff = false; + bool tuya_energy_enabled = (XNRG_32 == TasmotaGlobal.energy_driver); while (dpidStart + 4 < Tuya.byte_counter) { dpDataLen = Tuya.buffer[dpidStart + 2] << 8 | Tuya.buffer[dpidStart + 3]; fnId = TuyaGetFuncId(Tuya.buffer[dpidStart]); AddLog(LOG_LEVEL_DEBUG, PSTR("TYA: fnId=%d is set for dpId=%d"), fnId, Tuya.buffer[dpidStart]); - if (Tuya.buffer[dpidStart + 1] == 1) { // Data Type 1 + if (Tuya.buffer[dpidStart + 1] == 0) { +#ifdef USE_ENERGY_SENSOR + if (tuya_energy_enabled && fnId == TUYA_MCU_FUNC_POWER_COMBINED) { + if (dpDataLen == 8) { + uint16_t tmpVol = Tuya.buffer[dpidStart + 4] << 8 | Tuya.buffer[dpidStart + 5]; + uint16_t tmpCur = Tuya.buffer[dpidStart + 7] << 8 | Tuya.buffer[dpidStart + 8]; + uint16_t tmpPow = Tuya.buffer[dpidStart + 10] << 8 | Tuya.buffer[dpidStart + 11]; + Energy.voltage[0] = (float)tmpVol / 10; + Energy.current[0] = (float)tmpCur / 1000; + Energy.active_power[0] = (float)tmpPow; + AddLog(LOG_LEVEL_DEBUG, PSTR("TYA: Rx ID=%d Voltage=%d"), Tuya.buffer[dpidStart], tmpVol); + AddLog(LOG_LEVEL_DEBUG, PSTR("TYA: Rx ID=%d Current=%d"), Tuya.buffer[dpidStart], tmpCur); + AddLog(LOG_LEVEL_DEBUG, PSTR("TYA: Rx ID=%d Active_Power=%d"), Tuya.buffer[dpidStart], tmpPow); + + if (RtcTime.valid) { + if (Tuya.lastPowerCheckTime != 0 && Energy.active_power[0] > 0) { + Energy.kWhtoday += (float)Energy.active_power[0] * (Rtc.utc_time - Tuya.lastPowerCheckTime) / 36; + EnergyUpdateToday(); + } + Tuya.lastPowerCheckTime = Rtc.utc_time; + } + } else { + AddLog(LOG_LEVEL_DEBUG, PSTR("TYA: Rx ID=%d INV_LEN=%d"), Tuya.buffer[dpidStart], dpDataLen); + } + } + #endif // USE_ENERGY_SENSOR + } + else if (Tuya.buffer[dpidStart + 1] == 1) { // Data Type 1 if (fnId >= TUYA_MCU_FUNC_REL1 && fnId <= TUYA_MCU_FUNC_REL8) { AddLog(LOG_LEVEL_DEBUG, PSTR("TYA: RX Relay-%d --> MCU State: %s Current State:%s"), fnId - TUYA_MCU_FUNC_REL1 + 1, Tuya.buffer[dpidStart + 4]?"On":"Off",bitRead(TasmotaGlobal.power, fnId - TUYA_MCU_FUNC_REL1)?"On":"Off"); @@ -728,7 +756,6 @@ void TuyaProcessStatePacket(void) { if (PowerOff) { Tuya.ignore_dimmer_cmd_timeout = millis() + 250; } } else if (Tuya.buffer[dpidStart + 1] == 2) { // Data Type 2 - bool tuya_energy_enabled = (XNRG_32 == TasmotaGlobal.energy_driver); uint16_t packetValue = Tuya.buffer[dpidStart + 6] << 8 | Tuya.buffer[dpidStart + 7]; uint8_t dimIndex; bool SnsUpdate = false; @@ -824,6 +851,9 @@ void TuyaProcessStatePacket(void) { } Tuya.lastPowerCheckTime = Rtc.utc_time; } + } else if (tuya_energy_enabled && fnId == TUYA_MCU_FUNC_POWER_TOTAL) { + EnergyUpdateTotal((float)packetValue / 100,true); + AddLog(LOG_LEVEL_DEBUG, PSTR("TYA: Rx ID=%d Total_Power=%d"), Tuya.buffer[dpidStart], packetValue); } #endif // USE_ENERGY_SENSOR } @@ -862,6 +892,7 @@ void TuyaProcessStatePacket(void) { ExecuteCommand(scmnd, SRC_SWITCH); } } + } else if (Tuya.buffer[dpidStart + 1] == 4) { // Data Type 4 const unsigned char *dpData = (unsigned char*)&Tuya.buffer[dpidStart + 4]; @@ -1273,11 +1304,11 @@ bool Xnrg32(uint8_t function) if (TUYA_DIMMER == TasmotaGlobal.module_type) { if (FUNC_PRE_INIT == function) { - if (TuyaGetDpId(TUYA_MCU_FUNC_POWER) != 0) { - if (TuyaGetDpId(TUYA_MCU_FUNC_CURRENT) == 0) { + if (TuyaGetDpId(TUYA_MCU_FUNC_POWER) != 0 || TuyaGetDpId(TUYA_MCU_FUNC_POWER_COMBINED) != 0) { + if (TuyaGetDpId(TUYA_MCU_FUNC_CURRENT) == 0 && TuyaGetDpId(TUYA_MCU_FUNC_POWER_COMBINED) == 0) { Energy.current_available = false; } - if (TuyaGetDpId(TUYA_MCU_FUNC_VOLTAGE) == 0) { + if (TuyaGetDpId(TUYA_MCU_FUNC_VOLTAGE) == 0 && TuyaGetDpId(TUYA_MCU_FUNC_POWER_COMBINED) == 0) { Energy.voltage_available = false; } TasmotaGlobal.energy_driver = XNRG_32; From 7a85a0e88e52f3a76d779322a36dcfb37c156622 Mon Sep 17 00:00:00 2001 From: Ajith Vasudevan Date: Wed, 3 Mar 2021 18:15:27 +0530 Subject: [PATCH 11/87] Now using Settings.display_options instead of display_type --- tasmota/xdsp_15_tm1637.ino | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/tasmota/xdsp_15_tm1637.ino b/tasmota/xdsp_15_tm1637.ino index bba883711..614bc8c96 100644 --- a/tasmota/xdsp_15_tm1637.ino +++ b/tasmota/xdsp_15_tm1637.ino @@ -258,9 +258,9 @@ bool DriverInit(void) { if(Settings.display_model == XDSP_15) { if(TM1637Data.driver_inited) return true; - if(Settings.display_type == 2) { TM1637Data.num_digits = 8; TM1637Data.display_type = TM1638; } - else if(Settings.display_type == 1) { TM1637Data.num_digits = 6; TM1637Data.display_type = TM1637; } - else { Settings.display_type = 0; TM1637Data.num_digits = 4; TM1637Data.display_type = TM1637; } + if(Settings.display_options.data == 2) { TM1637Data.num_digits = 8; TM1637Data.display_type = TM1638; } + else if(Settings.display_options.data == 1) { TM1637Data.num_digits = 6; TM1637Data.display_type = TM1637; } + else { Settings.display_options.data = 0; TM1637Data.num_digits = 4; TM1637Data.display_type = TM1637; } if(TM1637Data.display_type == TM1637) { strcpy(TM1637Data.model_name, "TM1637"); @@ -277,7 +277,7 @@ bool DriverInit(void) { TM1637Data.brightness = (Settings.display_dimmer ? Settings.display_dimmer : TM1637Data.brightness); setBrightness(TM1637Data.brightness); TM1637Data.driver_inited = true; - AddLog(LOG_LEVEL_INFO, PSTR("DSP: %s display driver initialized with %d digits (DisplayType %d)"), TM1637Data.model_name, TM1637Data.num_digits, Settings.display_type); + AddLog(LOG_LEVEL_INFO, PSTR("DSP: %s display driver initialized with %d digits (DisplayType %d)"), TM1637Data.model_name, TM1637Data.num_digits, Settings.display_options.data); } return true; @@ -1010,11 +1010,11 @@ bool Xdsp15(uint8_t function) result = DriverInit(); // init break; case FUNC_DISPLAY_INIT: - AddLog(LOG_LEVEL_DEBUG, PSTR("TM7: %s: FUNC_DISPLAY_INIT: Display depends on TM1637Data.display_type, currently %d"), TM1637Data.model_name, Settings.display_type); + AddLog(LOG_LEVEL_DEBUG, PSTR("TM7: %s: FUNC_DISPLAY_INIT: Display depends on TM1637Data.display_type, currently %d"), TM1637Data.model_name, Settings.display_options.data); result = true; break; case FUNC_DISPLAY_TYPE: - AddLog(LOG_LEVEL_DEBUG, PSTR("TM7: %s: FUNC_DISPLAY_TYPE: TM1637Data.display_type set to %d, restarting to take effect ..."), TM1637Data.model_name, Settings.display_type); + AddLog(LOG_LEVEL_DEBUG, PSTR("TM7: %s: FUNC_DISPLAY_TYPE: TM1637Data.display_type set to %d, restarting to take effect ..."), TM1637Data.model_name, Settings.display_options.data); TasmotaGlobal.restart_flag = 2; break; case FUNC_DISPLAY_SEVENSEG_TEXT: From 7fe8f117a5505c4c61a1f3f5c943b0be17330d7b Mon Sep 17 00:00:00 2001 From: Ajith Vasudevan Date: Tue, 9 Mar 2021 00:47:19 +0530 Subject: [PATCH 12/87] Reverted changes to xdsp_interface and related changes done in 75699fe --- tasmota/tasmota.ino | 5 +---- tasmota/tasmota_template.h | 8 +++----- tasmota/xdrv_13_display.ino | 2 +- tasmota/xdsp_15_tm1637.ino | 2 +- tasmota/xdsp_interface.ino | 15 +-------------- 5 files changed, 7 insertions(+), 25 deletions(-) diff --git a/tasmota/tasmota.ino b/tasmota/tasmota.ino index 94d459d35..637d2b84b 100644 --- a/tasmota/tasmota.ino +++ b/tasmota/tasmota.ino @@ -147,7 +147,6 @@ struct { bool module_changed; // Indicate module changed since last restart bool wifi_stay_asleep; // Allow sleep only incase of ESP32 BLE bool no_autoexec; // Disable autoexec - bool tm1637_enabled; // TM1637 driver enabled StateBitfield global_state; // Global states (currently Wifi and Mqtt) (8 bits) uint8_t spi_enabled; // SPI configured @@ -228,9 +227,7 @@ void setup(void) { TasmotaGlobal.tele_period = 9999; TasmotaGlobal.active_device = 1; TasmotaGlobal.global_state.data = 0xF; // Init global state (wifi_down, mqtt_down) to solve possible network issues -#if defined(USE_DISPLAY) && defined(USE_DISPLAY_TM1637) - TasmotaGlobal.tm1637_enabled = true; -#endif + RtcRebootLoad(); if (!RtcRebootValid()) { RtcReboot.fast_reboot_count = 0; diff --git a/tasmota/tasmota_template.h b/tasmota/tasmota_template.h index 3afbf1794..a96d15830 100644 --- a/tasmota/tasmota_template.h +++ b/tasmota/tasmota_template.h @@ -150,7 +150,6 @@ enum UserSelectablePins { GPIO_NEOPOOL_TX, GPIO_NEOPOOL_RX, // Sugar Valley RS485 interface GPIO_SDM72_TX, GPIO_SDM72_RX, // SDM72 Serial interface GPIO_TM1637CLK, GPIO_TM1637DIO, // TM1637 interface - GPIO_TM1638CLK, GPIO_TM1638DIO, GPIO_TM1638STB, // TM1638 interface GPIO_PROJECTOR_CTRL_TX, GPIO_PROJECTOR_CTRL_RX, // LCD/DLP Projector Serial Control GPIO_SSD1351_DC, GPIO_XPT2046_CS, // XPT2046 SPI Chip Select @@ -325,7 +324,6 @@ const char kSensorNames[] PROGMEM = D_SENSOR_NEOPOOL_TX "|" D_SENSOR_NEOPOOL_RX "|" D_SENSOR_SDM72_TX "|" D_SENSOR_SDM72_RX "|" D_SENSOR_TM1637_CLK "|" D_SENSOR_TM1637_DIO "|" - D_SENSOR_TM1638_CLK "|" D_SENSOR_TM1638_DIO "|" D_SENSOR_TM1638_STB "|" D_SENSOR_PROJECTOR_CTRL_TX "|" D_SENSOR_PROJECTOR_CTRL_RX "|" D_SENSOR_SSD1351_DC "|" D_SENSOR_XPT2046_CS "|" @@ -451,9 +449,9 @@ const uint16_t kGpioNiceList[] PROGMEM = { #ifdef USE_DISPLAY_TM1637 AGPIO(GPIO_TM1637CLK), AGPIO(GPIO_TM1637DIO), - AGPIO(GPIO_TM1638CLK), - AGPIO(GPIO_TM1638DIO), - AGPIO(GPIO_TM1638STB), + AGPIO(GPIO_TM16CLK), + AGPIO(GPIO_TM16DIO), + AGPIO(GPIO_TM16STB), #endif // USE_DISPLAY_TM1637 AGPIO(GPIO_BACKLIGHT), // Display backlight control AGPIO(GPIO_OLED_RESET), // OLED Display Reset diff --git a/tasmota/xdrv_13_display.ino b/tasmota/xdrv_13_display.ino index bd1955a7b..2bb77a03a 100755 --- a/tasmota/xdrv_13_display.ino +++ b/tasmota/xdrv_13_display.ino @@ -2842,7 +2842,7 @@ bool Xdrv13(uint8_t function) { bool result = false; - if ((TasmotaGlobal.i2c_enabled || TasmotaGlobal.spi_enabled || TasmotaGlobal.soft_spi_enabled || TasmotaGlobal.tm1637_enabled) && XdspPresent()) { + if ((TasmotaGlobal.i2c_enabled || TasmotaGlobal.spi_enabled || TasmotaGlobal.soft_spi_enabled) && XdspPresent()) { switch (function) { case FUNC_PRE_INIT: DisplayInitDriver(); diff --git a/tasmota/xdsp_15_tm1637.ino b/tasmota/xdsp_15_tm1637.ino index 614bc8c96..d551582b0 100644 --- a/tasmota/xdsp_15_tm1637.ino +++ b/tasmota/xdsp_15_tm1637.ino @@ -268,7 +268,7 @@ bool DriverInit(void) { tm1637display->begin(TM1637Data.num_digits, 1); } else if(TM1637Data.display_type == TM1638) { strcpy(TM1637Data.model_name, "TM1638"); - tm1638display = new TM1638plus(Pin(GPIO_TM1638STB), Pin(GPIO_TM1638CLK), Pin(GPIO_TM1638DIO), true ); + tm1638display = new TM1638plus(Pin(GPIO_TM16STB), Pin(GPIO_TM16CLK), Pin(GPIO_TM16DIO), true ); TM1637Data.num_digits = 8; tm1638display->displayBegin(); } diff --git a/tasmota/xdsp_interface.ino b/tasmota/xdsp_interface.ino index 42907ae15..3ca49f3d6 100644 --- a/tasmota/xdsp_interface.ino +++ b/tasmota/xdsp_interface.ino @@ -17,7 +17,7 @@ along with this program. If not, see . */ -#if defined(USE_I2C) || defined(USE_SPI) || defined(USE_DISPLAY_TM1637) +#if defined(USE_I2C) || defined(USE_SPI) #ifdef USE_DISPLAY #ifdef XFUNC_PTR_IN_ROM @@ -26,8 +26,6 @@ bool (* const xdsp_func_ptr[])(uint8_t) PROGMEM = { // Display Function Pointe bool (* const xdsp_func_ptr[])(uint8_t) = { // Display Function Pointers #endif -#if defined(USE_I2C) || defined(USE_SPI) - #ifdef XDSP_01 &Xdsp01, #endif @@ -84,18 +82,10 @@ bool (* const xdsp_func_ptr[])(uint8_t) = { // Display Function Pointers &Xdsp14, #endif -#endif // #if defined(USE_I2C) || defined(USE_SPI) - -#ifdef USE_DISPLAY_TM1637 - #ifdef XDSP_15 &Xdsp15, #endif -#endif // USE_DISPLAY_TM1637 - -#if defined(USE_I2C) || defined(USE_SPI) - #ifdef XDSP_16 &Xdsp16, #endif @@ -163,9 +153,6 @@ bool (* const xdsp_func_ptr[])(uint8_t) = { // Display Function Pointers #ifdef XDSP_32 &Xdsp32 #endif - -#endif // #if defined(USE_I2C) || defined(USE_SPI) - }; const uint8_t xdsp_present = sizeof(xdsp_func_ptr) / sizeof(xdsp_func_ptr[0]); // Number of drivers found From acf65d7a68926a0a1176f5f1e1a7738c4091d8a4 Mon Sep 17 00:00:00 2001 From: Ajith Vasudevan Date: Tue, 9 Mar 2021 01:12:42 +0530 Subject: [PATCH 13/87] Removed unwanted lines --- tasmota/tasmota_configurations.h | 1 - tasmota/xdrv_13_display.ino | 1 - 2 files changed, 2 deletions(-) diff --git a/tasmota/tasmota_configurations.h b/tasmota/tasmota_configurations.h index 73b55c9f2..c4956bdbd 100644 --- a/tasmota/tasmota_configurations.h +++ b/tasmota/tasmota_configurations.h @@ -324,7 +324,6 @@ #define USE_DISPLAY_RA8876 // [DisplayModel 10] #define USE_DISPLAY_ST7789 // [DisplayModel 12] Enable ST7789 module #define USE_DISPLAY_SSD1331 // [DisplayModel 14] Enable SSD1331 module - #define USE_DISPLAY_TM1637 // [DisplayModel 15] Enable TM1637, TM1638 modules #undef DEBUG_THEO // Disable debug code #undef USE_DEBUG_DRIVER // Disable debug code diff --git a/tasmota/xdrv_13_display.ino b/tasmota/xdrv_13_display.ino index 2bb77a03a..ee53bdef8 100755 --- a/tasmota/xdrv_13_display.ino +++ b/tasmota/xdrv_13_display.ino @@ -83,7 +83,6 @@ const uint8_t DISPLAY_LOG_ROWS = 32; // Number of lines in display log #define D_CMND_DISP_BUTTONS "Buttons" #define D_CMND_DISP_SCROLLTEXT "ScrollText" #define D_CMND_DISP_TYPE "Type" - #define D_CMND_DISP_ILIMODE "ILIMode" #define D_CMND_DISP_ILIINVERT "Invert" From 189e612419c5174d89cb103ffb29078f608b40eb Mon Sep 17 00:00:00 2001 From: gemu2015 Date: Sat, 13 Mar 2021 12:50:28 +0100 Subject: [PATCH 14/87] nvs to littfs wrapper --- ...m_keystore.c => hap_platform_keystore.cpp} | 155 +++++++++++++++++- 1 file changed, 151 insertions(+), 4 deletions(-) rename lib/libesp32_div/ESP32-HomeKit/src/{hap_platform_keystore.c => hap_platform_keystore.cpp} (65%) mode change 100644 => 100755 diff --git a/lib/libesp32_div/ESP32-HomeKit/src/hap_platform_keystore.c b/lib/libesp32_div/ESP32-HomeKit/src/hap_platform_keystore.cpp old mode 100644 new mode 100755 similarity index 65% rename from lib/libesp32_div/ESP32-HomeKit/src/hap_platform_keystore.c rename to lib/libesp32_div/ESP32-HomeKit/src/hap_platform_keystore.cpp index b82457ca5..db6c3e905 --- a/lib/libesp32_div/ESP32-HomeKit/src/hap_platform_keystore.c +++ b/lib/libesp32_div/ESP32-HomeKit/src/hap_platform_keystore.cpp @@ -21,25 +21,169 @@ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * */ + + #include #include #include +#include #define HAP_PLATFORM_DEF_NVS_PARTITION "nvs" #define HAP_PLATFORM_DEF_FACTORY_NVS_PARTITION "factory_nvs" +extern "C" { + static const char *TAG = "hap_platform_keystore"; -char * hap_platform_keystore_get_nvs_partition_name() -{ +const char * hap_platform_keystore_get_nvs_partition_name() { return HAP_PLATFORM_DEF_NVS_PARTITION; } -char * hap_platform_keystore_get_factory_nvs_partition_name() -{ +const char * hap_platform_keystore_get_factory_nvs_partition_name() { return HAP_PLATFORM_DEF_FACTORY_NVS_PARTITION; } +#define HAP_USE_LITTLEFS + +#ifdef HAP_USE_LITTLEFS + +#include + +extern FS *ffsp; + +int hap_platform_keystore_init_partition(const char *part_name, bool read_only) { + return 0; +} + +int hap_platform_keystore_get(const char *part_name, const char *name_space, const char *key, uint8_t *val, size_t *val_size) { + char path[48]; + strcpy(path, "/"); + strcat(path, part_name); + + File fp = ffsp->open(path, "r"); + if (!fp) { + ffsp->mkdir(path); + return -1; + } + fp.close(); + + strcat(path, "/"); + strcat(path, name_space); + fp = ffsp->open(path, "r"); + if (!fp) { + ffsp->mkdir(path); + return -1; + } + fp.close(); + + strcat(path, "/"); + strcat(path, key); + fp = ffsp->open(path, "r"); + if (fp) { + fp.read(val, *val_size); + fp.close(); + } else { + *val_size = 0; + return -1; + } + return 0; +} + +int hap_platform_keystore_set(const char *part_name, const char *name_space, const char *key, const uint8_t *val, const size_t val_len) { + char path[48]; + strcpy(path, "/"); + strcat(path, part_name); + + File fp = ffsp->open(path, "r"); + if (!fp) { + ffsp->mkdir(path); + } + fp.close(); + + strcat(path, "/"); + strcat(path, name_space); + fp = ffsp->open(path, "r"); + if (!fp) { + ffsp->mkdir(path); + } + fp.close(); + + strcat(path, "/"); + strcat(path, key); + fp = ffsp->open(path, "w"); + if (fp) { + fp.write(val, val_len); + fp.close(); + } else { + return -1; + } + return 0; +} + +int hap_platform_keystore_delete(const char *part_name, const char *name_space, const char *key) { + char path[48]; + strcpy(path, "/"); + strcat(path, part_name); + strcat(path, "/"); + strcat(path, name_space); + strcat(path, "/"); + strcat(path, key); + ffsp->remove(path); + return 0; +} + +// should +int hap_platform_keystore_delete_namespace(const char *part_name, const char *name_space) { + char path[48]; + strcpy(path, "/"); + strcat(path, part_name); + strcat(path, "/"); + strcat(path, name_space); + File fp = ffsp->open(path, "r"); + if (fp.isDirectory()) { + while (true) { + File entry = fp.openNextFile(); + if (!entry) break; + char fp[48]; + strcpy(fp,path); + strcat(fp, "/"); + strcat(fp, entry.name()); + ffsp->remove(fp); + entry.close(); + } + } + return 0; +} + +// last resort only +int hap_platfrom_keystore_erase_partition(const char *part_name) { +// LITTLEFS.format(); +char path[48]; +strcpy(path, "/"); +strcat(path, part_name); +File fp = ffsp->open(path, "r"); +if (fp.isDirectory()) { + while (true) { + File entry = fp.openNextFile(); + if (!entry) break; + char fp[48]; + strcpy(fp,path); + strcat(fp, "/"); + strcat(fp, entry.name()); + if (entry.isDirectory()) { + hap_platform_keystore_delete_namespace(part_name, entry.name()); + ffsp->rmdir(fp); + } else { + ffsp->remove(fp); + } + entry.close(); + } +} + return 0; +} + +#else + #ifdef CONFIG_NVS_ENCRYPTION int hap_platform_keystore_init_partition(const char *part_name, bool read_only) { @@ -185,3 +329,6 @@ int hap_platfrom_keystore_erase_partition(const char *part_name) } return -1; } +#endif // USE_LITTLEFS + +} From 699ec7b3948ebedadbb3d26a4734887d034653fe Mon Sep 17 00:00:00 2001 From: gemu2015 Date: Sat, 13 Mar 2021 12:51:09 +0100 Subject: [PATCH 15/87] homekit update --- tasmota/homekit.c | 151 +++++++++++++++++++++++++++++++---- tasmota/xdrv_10_scripter.ino | 71 +++++++++++----- 2 files changed, 186 insertions(+), 36 deletions(-) diff --git a/tasmota/homekit.c b/tasmota/homekit.c index f8bb77e22..015c3d67d 100755 --- a/tasmota/homekit.c +++ b/tasmota/homekit.c @@ -24,7 +24,6 @@ /* HomeKit Smart Outlet Example */ -#ifdef USE_HOMEKIT #ifdef ESP32 #include @@ -49,6 +48,7 @@ static const char *TAG = "HAP outlet"; char *hk_desc; char hk_code[12]; +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); @@ -194,6 +194,8 @@ static int sensor_read(hap_char_t *hc, hap_status_t *status_code, void *serv_pri const char *hcp = hap_char_get_type_uuid(hc); + printf("read values %s\n", hcp ); + if (!strcmp(hcp, HAP_CHAR_UUID_CURRENT_TEMPERATURE) || !strcmp(hcp, HAP_CHAR_UUID_CURRENT_RELATIVE_HUMIDITY) || !strcmp(hcp, HAP_CHAR_UUID_CURRENT_AMBIENT_LIGHT_LEVEL) @@ -228,6 +230,24 @@ static int sensor_read(hap_char_t *hc, hap_status_t *status_code, void *serv_pri hap_char_update_val(hc, &new_val); *status_code = HAP_STATUS_SUCCESS; } + if (!strcmp(hcp, HAP_CHAR_UUID_BATTERY_LEVEL)) { + Ext_UpdVar(hap_devs[index].var_name, &fvar, 0); + new_val.u = fvar; + hap_char_update_val(hc, &new_val); + *status_code = HAP_STATUS_SUCCESS; + } + if (!strcmp(hcp, HAP_CHAR_UUID_STATUS_LOW_BATTERY)) { + Ext_UpdVar(hap_devs[index].var2_name, &fvar, 0); + new_val.u = fvar; + hap_char_update_val(hc, &new_val); + *status_code = HAP_STATUS_SUCCESS; + } + if (!strcmp(hcp, HAP_CHAR_UUID_CHARGING_STATE)) { + Ext_UpdVar(hap_devs[index].var3_name, &fvar, 0); + new_val.u = fvar; + hap_char_update_val(hc, &new_val); + *status_code = HAP_STATUS_SUCCESS; + } return HAP_SUCCESS; } @@ -324,6 +344,7 @@ uint32_t cnt; return cnt; } +float tsim = 20; uint32_t str2c(char **sp, char *vp, uint32_t len) { char *lp = *sp; @@ -440,7 +461,8 @@ static void smart_outlet_thread_entry(void *p) { uint8_t product_data[] = {'E','S','P','3','2','H','A','P'}; hap_acc_add_product_data(hap_devs[index].accessory, product_data, sizeof(product_data)); - int ret = hap_serv_add_char(hap_devs[index].service, hap_char_name_create(hap_devs[index].hap_name)); + int ret; + switch (hap_cfg.cid) { case HAP_CID_LIGHTING: { float fvar = 0; @@ -467,13 +489,22 @@ static void smart_outlet_thread_entry(void *p) { case 0: hap_devs[index].service = hap_serv_temperature_sensor_create(fvar); break; case 1: hap_devs[index].service = hap_serv_humidity_sensor_create(fvar); break; case 2: hap_devs[index].service = hap_serv_light_sensor_create(fvar); break; + case 3: + { float fvar1 = 0, fvar2 = 0; + Ext_UpdVar(hap_devs[index].var2_name, &fvar1, 0); + Ext_UpdVar(hap_devs[index].var3_name, &fvar2, 0); + hap_devs[index].service = hap_serv_battery_service_create(fvar, fvar1, fvar2); + } + break; } } break; + + default: hap_devs[index].service = hap_serv_outlet_create(true, true); } - + hap_serv_add_char(hap_devs[index].service, hap_char_name_create(hap_devs[index].hap_name)); hap_set_read(hap_devs[index].service, index); hap_set_write(hap_devs[index].service, index); @@ -497,7 +528,7 @@ nextline: lp++; } } - + hk_services = index; /* Initialize the appliance specific hardware. This enables out-in-use detection */ smart_outlet_hardware_init(OUTLET_IN_USE_GPIO); @@ -538,7 +569,7 @@ nextline: /* Start Wi-Fi */ //app_wifi_start(portMAX_DELAY); - uint32_t io_num = OUTLET_IN_USE_GPIO; + int32_t io_num = OUTLET_IN_USE_GPIO; if (io_num >= 0) { hap_val_t appliance_value = { .b = true, @@ -561,25 +592,112 @@ nextline: } } } else { - while (1) { + // vTaskDelete(NULL); + // update values every 100 ms + while (1) { + delay(100); + float fvar; + hap_char_t *hc; + hap_val_t new_val; + for (uint32_t cnt = 0; cnt < hk_services; cnt++) { + switch (hap_devs[cnt].hap_cid) { + case HAP_CID_SENSOR: + switch (hap_devs[cnt].type) { + case 0: + hc = hap_serv_get_char_by_uuid(hap_devs[cnt].service, HAP_CHAR_UUID_CURRENT_TEMPERATURE); + if (Ext_UpdVar(hap_devs[cnt].var_name, &fvar, 0)) { + new_val.f = fvar; + hap_char_update_val(hc, &new_val); + } + break; + case 1: + hc = hap_serv_get_char_by_uuid(hap_devs[cnt].service, HAP_CHAR_UUID_CURRENT_RELATIVE_HUMIDITY); + if (Ext_UpdVar(hap_devs[cnt].var_name, &fvar, 0)) { + new_val.f = fvar; + hap_char_update_val(hc, &new_val); + } + break; + case 2: + hc = hap_serv_get_char_by_uuid(hap_devs[cnt].service, HAP_CHAR_UUID_CURRENT_AMBIENT_LIGHT_LEVEL); + if (Ext_UpdVar(hap_devs[cnt].var_name, &fvar, 0)) { + new_val.f = fvar; + hap_char_update_val(hc, &new_val); + } + break; + case 3: + hc = hap_serv_get_char_by_uuid(hap_devs[cnt].service, HAP_CHAR_UUID_BATTERY_LEVEL); + if (Ext_UpdVar(hap_devs[cnt].var_name, &fvar, 0)) { + 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); + if (Ext_UpdVar(hap_devs[cnt].var2_name, &fvar, 0)) { + 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); + if (Ext_UpdVar(hap_devs[cnt].var3_name, &fvar, 0)) { + new_val.u = fvar; + hap_char_update_val(hc, &new_val); + } + break; + } + break; + case HAP_CID_OUTLET: + hc = hap_serv_get_char_by_uuid(hap_devs[cnt].service, HAP_CHAR_UUID_ON); + if (Ext_UpdVar(hap_devs[cnt].var_name, &fvar, 0)) { + new_val.b = fvar; + hap_char_update_val(hc, &new_val); + } + break; + case HAP_CID_LIGHTING: + hc = hap_serv_get_char_by_uuid(hap_devs[cnt].service, HAP_CHAR_UUID_ON); + if (Ext_UpdVar(hap_devs[cnt].var_name, &fvar, 0)) { + new_val.b = fvar; + hap_char_update_val(hc, &new_val); + } + hc = hap_serv_get_char_by_uuid(hap_devs[cnt].service, HAP_CHAR_UUID_HUE); + if (Ext_UpdVar(hap_devs[cnt].var2_name, &fvar, 0)) { + new_val.f = fvar; + hap_char_update_val(hc, &new_val); + } + hc = hap_serv_get_char_by_uuid(hap_devs[cnt].service, HAP_CHAR_UUID_SATURATION); + if (Ext_UpdVar(hap_devs[cnt].var3_name, &fvar, 0)) { + new_val.f = fvar; + hap_char_update_val(hc, &new_val); + } + hc = hap_serv_get_char_by_uuid(hap_devs[cnt].service, HAP_CHAR_UUID_BRIGHTNESS); + if (Ext_UpdVar(hap_devs[cnt].var4_name, &fvar, 0)) { + new_val.u = fvar; + hap_char_update_val(hc, &new_val); + } + break; + } } + } } } -#define HK_MAXSIZE 1024 +#define HK_PASSCODE "111-11-111" -void homekit_main(char *desc) { +void homekit_main(char *desc, uint32_t flag ) { if (desc) { char *cp = desc; cp += 2; while (*cp == ' ') cp++; // "111-11-111" - uint32_t cnt; - for (cnt = 0; cnt < 10; cnt++) { - hk_code[cnt] = *cp++; + + if (*cp == '*') { + strlcpy(hk_code, HK_PASSCODE, 10); + cp++; + } else { + uint32_t cnt; + for (cnt = 0; cnt < 10; cnt++) { + hk_code[cnt] = *cp++; + } + hk_code[cnt] = 0; } - hk_code[cnt] = 0; if (*cp != '\n') { printf("init error\n"); return; @@ -587,7 +705,12 @@ void homekit_main(char *desc) { cp++; hk_desc = cp; } else { - hap_platfrom_keystore_erase_partition("nvs"); + if (flag == 99) { + hap_reset_to_factory(); + } else { + // not yet implemented + hap_stop(); + } return; } @@ -595,7 +718,7 @@ void homekit_main(char *desc) { /* Create the application thread */ xTaskCreate(smart_outlet_thread_entry, SMART_OUTLET_TASK_NAME, SMART_OUTLET_TASK_STACKSIZE, NULL, SMART_OUTLET_TASK_PRIORITY, NULL); + } #endif // ESP32 -#endif // USE_HOMEKIT \ No newline at end of file diff --git a/tasmota/xdrv_10_scripter.ino b/tasmota/xdrv_10_scripter.ino index 3c230c8b1..7e34f8102 100755 --- a/tasmota/xdrv_10_scripter.ino +++ b/tasmota/xdrv_10_scripter.ino @@ -56,7 +56,9 @@ keywords if then else endif, or, and are better readable for beginners (others m #define MAXFILT 5 #endif #define SCRIPT_SVARSIZE 20 +#ifndef SCRIPT_MAXSSIZE #define SCRIPT_MAXSSIZE 48 +#endif #define SCRIPT_EOL '\n' #define SCRIPT_FLOAT_PRECISION 2 #define PMEM_SIZE sizeof(Settings.script_pram) @@ -221,7 +223,7 @@ extern FS *ufsp; #endif // USE_UFILESYS -extern "C" void homekit_main(char *); +extern "C" void homekit_main(char *, uint32_t); #ifdef SUPPORT_MQTT_EVENT #include // Import LinkedList library @@ -244,7 +246,7 @@ extern VButton *buttons[MAX_TOUCH_BUTTONS]; #endif typedef union { -#ifdef USE_SCRIPT_GLOBVARS +#if defined(USE_SCRIPT_GLOBVARS) || defined(USE_HOMEKIT) uint16_t data; #else uint8_t data; @@ -260,6 +262,9 @@ typedef union { uint8_t constant : 1; #ifdef USE_SCRIPT_GLOBVARS uint8_t global : 1; +#endif +#ifdef USE_SCRIPT_GLOBVARS + uint8_t hchanged : 1; #endif }; } SCRIPT_TYPE; @@ -490,7 +495,7 @@ void ScriptEverySecond(void) { uint8_t homekit_found = Run_Scripter(">h", -2, 0); if (homekit_found == 99) { if (!TasmotaGlobal.global_state.wifi_down) { - homekit_main(glob_script_mem.section_ptr); + homekit_main(glob_script_mem.section_ptr, 0); glob_script_mem.homekit_running = true; } } @@ -504,6 +509,15 @@ void RulesTeleperiod(void) { if (bitRead(Settings.rule_enabled, 0) && TasmotaGlobal.mqtt_data[0]) Run_Scripter(">T", 2, TasmotaGlobal.mqtt_data); } +void SetChanged(uint32_t index) { + glob_script_mem.type[index].bits.changed = 1; +#ifdef USE_HOMEKIT + glob_script_mem.type[index].bits.hchanged = 1; +#endif +//AddLog(LOG_LEVEL_INFO, PSTR("Change: %d"), index); +} + + #define SCRIPT_SKIP_SPACES while (*lp==' ' || *lp=='\t') lp++; #define SCRIPT_SKIP_EOL while (*lp==SCRIPT_EOL) lp++; @@ -1015,7 +1029,7 @@ void Script_PollUdp(void) { if (res) { // mark changed glob_script_mem.last_udp_ip = glob_script_mem.Script_PortUdp.remoteIP(); - glob_script_mem.type[index].bits.changed = 1; + SetChanged(index); if (glob_script_mem.glob_script == 99) { Run_Scripter(">G", 2, 0); } @@ -2497,14 +2511,16 @@ chknext: #endif //USE_LIGHT #ifdef USE_HOMEKIT - if (!strncmp(vname, "hki", 3)) { + if (!strncmp(vname, "hki(", 4)) { if (!TasmotaGlobal.global_state.wifi_down) { // erase nvs - homekit_main(0); + lp = GetNumericArgument(lp + 4, OPER_EQU, &fvar, gv); + homekit_main(0, fvar); // restart homekit - glob_script_mem.homekit_running = false; + TasmotaGlobal.restart_flag = 2; } - fvar = 0; + lp++; + len = 0; goto exit; } #endif @@ -3568,32 +3584,40 @@ char *ForceStringVar(char *lp, char *dstr) { return lp; } +#ifdef USE_HOMEKIT extern "C" { uint32_t Ext_UpdVar(char *vname, float *fvar, uint32_t mode) { return UpdVar(vname, fvar, mode); } } -uint32_t UpdVar(char *vname, float *fvar, uint32_t mode) { +int32_t UpdVar(char *vname, float *fvar, uint32_t mode) { struct T_INDEX ind; uint8_t vtype; float res = *fvar; + uint8_t index; isvar(vname, &vtype, &ind, fvar, 0, 0); if (vtype != VAR_NV) { // found variable as result if (vtype == NUM_RES || (vtype & STYPE) == 0) { if (mode) { // set var - uint8_t index = glob_script_mem.type[ind.index].index; + index = glob_script_mem.type[ind.index].index; glob_script_mem.fvars[index] = res; - glob_script_mem.type[index].bits.changed = 1; + SetChanged(ind.index); + 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; + return ret; } - return 0; } else { // break; } } - return 1; + return -1; } @@ -3602,6 +3626,9 @@ extern "C" { Replace_Cmd_Vars(srcbuf, srcsize, dstbuf, dstsize); } } + +#endif // USE_HOMEKIT + // replace vars in cmd %var% void Replace_Cmd_Vars(char *srcbuf, uint32_t srcsize, char *dstbuf, uint32_t dstsize) { char *cp; @@ -4575,7 +4602,7 @@ int16_t Run_script_sub(const char *type, int8_t tlen, struct GVARS *gv) { break; } // var was changed - glob_script_mem.type[globvindex].bits.changed = 1; + SetChanged(globvindex); #ifdef USE_SCRIPT_GLOBVARS if (glob_script_mem.type[globvindex].bits.global) { script_udp_sendvar(varname, dfvar, 0); @@ -4633,7 +4660,7 @@ int16_t Run_script_sub(const char *type, int8_t tlen, struct GVARS *gv) { if (!glob_script_mem.var_not_found) { // var was changed - glob_script_mem.type[globvindex].bits.changed = 1; + SetChanged(globvindex); #ifdef USE_SCRIPT_GLOBVARS if (glob_script_mem.type[globvindex].bits.global) { script_udp_sendvar(varname, 0, str); @@ -5631,7 +5658,7 @@ void Script_Handle_Hue(String *path) { glob_script_mem.fvars[hue_script[index].index[0] - 1] = 1; response.replace("{re", "true"); } - glob_script_mem.type[hue_script[index].vindex[0]].bits.changed = 1; + SetChanged(hue_script[index].vindex[0]); resp = true; } @@ -5647,7 +5674,7 @@ void Script_Handle_Hue(String *path) { response.replace("{cm", "bri"); response.replace("{re", String(tmp)); glob_script_mem.fvars[hue_script[index].index[1] - 1] = bri; - glob_script_mem.type[hue_script[index].vindex[1]].bits.changed = 1; + SetChanged(hue_script[index].vindex[1]); resp = true; } @@ -5670,9 +5697,9 @@ void Script_Handle_Hue(String *path) { response.replace("{cm", "xy"); response.replace("{re", "[" + x_str + "," + y_str + "]"); glob_script_mem.fvars[hue_script[index].index[2]-1] = hue; - glob_script_mem.type[hue_script[index].vindex[2]].bits.changed = 1; + SetChanged(hue_script[index].vindex[2]); glob_script_mem.fvars[hue_script[index].index[3]-1] = sat; - glob_script_mem.type[hue_script[index].vindex[3]].bits.changed = 1; + SetChanged(hue_script[index].vindex[3]); resp = true; } @@ -5688,7 +5715,7 @@ void Script_Handle_Hue(String *path) { response.replace("{cm", "hue"); response.replace("{re", String(tmp)); glob_script_mem.fvars[hue_script[index].index[2] - 1] = hue; - glob_script_mem.type[hue_script[index].vindex[2]].bits.changed = 1; + SetChanged(hue_script[index].vindex[2]); resp = true; } @@ -5703,7 +5730,7 @@ void Script_Handle_Hue(String *path) { response.replace("{cm", "sat"); response.replace("{re", String(tmp)); glob_script_mem.fvars[hue_script[index].index[3] - 1] = sat; - glob_script_mem.type[hue_script[index].vindex[3]].bits.changed = 1; + SetChanged(hue_script[index].vindex[3]); resp = true; } @@ -5716,7 +5743,7 @@ void Script_Handle_Hue(String *path) { response.replace("{cm", "ct"); response.replace("{re", String(ct)); glob_script_mem.fvars[hue_script[index].index[4] - 1] = ct; - glob_script_mem.type[hue_script[index].vindex[4]].bits.changed = 1; + SetChanged(hue_script[index].vindex[4]); resp = true; } response += "]"; From e265f3884bc7d28d8cc4d94a34c85048e70b8651 Mon Sep 17 00:00:00 2001 From: gemu2015 Date: Sat, 13 Mar 2021 13:28:33 +0100 Subject: [PATCH 16/87] Update homekit.c --- tasmota/homekit.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/tasmota/homekit.c b/tasmota/homekit.c index 015c3d67d..44f1dc536 100755 --- a/tasmota/homekit.c +++ b/tasmota/homekit.c @@ -24,6 +24,10 @@ /* HomeKit Smart Outlet Example */ +//#define USE_HOMEKIT + + +#ifdef USE_HOMEKIT #ifdef ESP32 #include @@ -722,3 +726,4 @@ void homekit_main(char *desc, uint32_t flag ) { } #endif // ESP32 +#endif // USE_HOMEKIT From 75f1d2a26dbe3a2f0c71b24fcb3ea3a4be3e0e92 Mon Sep 17 00:00:00 2001 From: Jason2866 <24528715+Jason2866@users.noreply.github.com> Date: Sat, 13 Mar 2021 15:48:38 +0100 Subject: [PATCH 17/87] Phase 2 of new Tasmota32 build variants --- .github/workflows/CI_github_ESP32.yml | 40 ----- .github/workflows/Tasmota_build.yml | 42 ----- .github/workflows/Tasmota_build_master.yml | 42 ----- platformio_override_sample.ini | 2 - platformio_tasmota32.ini | 2 - platformio_tasmota_env32.ini | 10 -- tasmota/tasmota.h | 1 + tasmota/tasmota_configurations.h | 9 + tasmota/tasmota_configurations_ESP32.h | 188 ++++++++++++++++++++- tasmota/user_config_override_sample.h | 3 + 10 files changed, 200 insertions(+), 139 deletions(-) diff --git a/.github/workflows/CI_github_ESP32.yml b/.github/workflows/CI_github_ESP32.yml index 849316beb..2a576e40d 100644 --- a/.github/workflows/CI_github_ESP32.yml +++ b/.github/workflows/CI_github_ESP32.yml @@ -104,46 +104,6 @@ jobs: name: firmware path: ./build_output - tasmota32-knx: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v1 - - name: Set up Python - uses: actions/setup-python@v1 - - name: Install dependencies - run: | - python -m pip install --upgrade pip - pip install -U platformio - platformio upgrade --dev - platformio update - - name: Run PlatformIO - run: | - platformio run -e tasmota32-knx - - uses: actions/upload-artifact@v2 - with: - name: firmware - path: ./build_output - - tasmota32-sensors: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v1 - - name: Set up Python - uses: actions/setup-python@v1 - - name: Install dependencies - run: | - python -m pip install --upgrade pip - pip install -U platformio - platformio upgrade --dev - platformio update - - name: Run PlatformIO - run: | - platformio run -e tasmota32-sensors - - uses: actions/upload-artifact@v2 - with: - name: firmware - path: ./build_output - tasmota32-display: runs-on: ubuntu-latest steps: diff --git a/.github/workflows/Tasmota_build.yml b/.github/workflows/Tasmota_build.yml index dc595d978..40fd060ba 100644 --- a/.github/workflows/Tasmota_build.yml +++ b/.github/workflows/Tasmota_build.yml @@ -804,46 +804,6 @@ jobs: path: ./build_output - tasmota32-knx: - needs: tasmota_pull - runs-on: ubuntu-latest - continue-on-error: true - steps: - - uses: actions/checkout@v1 - - name: Set up Python - uses: actions/setup-python@v1 - - name: Install dependencies - run: | - pip install -U platformio - - name: Run PlatformIO - run: | - platformio run -e tasmota32-knx - - uses: actions/upload-artifact@v2 - with: - name: firmware - path: ./build_output - - - tasmota32-sensors: - needs: tasmota_pull - runs-on: ubuntu-latest - continue-on-error: true - steps: - - uses: actions/checkout@v1 - - name: Set up Python - uses: actions/setup-python@v1 - - name: Install dependencies - run: | - pip install -U platformio - - name: Run PlatformIO - run: | - platformio run -e tasmota32-sensors - - uses: actions/upload-artifact@v2 - with: - name: firmware - path: ./build_output - - tasmota32-display: needs: tasmota_pull runs-on: ubuntu-latest @@ -1434,14 +1394,12 @@ jobs: [ ! -f ./mv_firmware/firmware/tasmota-knx.* ] || mv ./mv_firmware/firmware/tasmota-knx.* ./firmware/tasmota/ [ ! -f ./mv_firmware/firmware/tasmota-zbbridge.* ] || mv ./mv_firmware/firmware/tasmota-zbbridge.* ./firmware/tasmota/ [ ! -f ./mv_firmware/firmware/tasmota32.* ] || mv ./mv_firmware/firmware/tasmota32.* ./firmware/tasmota32/ - [ ! -f ./mv_firmware/firmware/tasmota32-sensors.* ] || mv ./mv_firmware/firmware/tasmota32-sensors.* ./firmware/tasmota32/ [ ! -f ./mv_firmware/firmware/tasmota32-ir*.* ] || mv ./mv_firmware/firmware/tasmota32-ir*.* ./firmware/tasmota32/ [ ! -f ./mv_firmware/firmware/tasmota32-display.* ] || mv ./mv_firmware/firmware/tasmota32-display.* ./firmware/tasmota32/ [ ! -f ./mv_firmware/firmware/tasmota32-web*.* ] || mv ./mv_firmware/firmware/tasmota32-web*.* ./firmware/tasmota32/ [ ! -f ./mv_firmware/firmware/tasmota32-odroidgo.* ] || mv ./mv_firmware/firmware/tasmota32-odroidgo.* ./firmware/tasmota32/ [ ! -f ./mv_firmware/firmware/tasmota32-core2.* ] || mv ./mv_firmware/firmware/tasmota32-core2.* ./firmware/tasmota32/ [ ! -f ./mv_firmware/firmware/tasmota32-bluetooth.* ] || mv ./mv_firmware/firmware/tasmota32-bluetooth.* ./firmware/tasmota32/ - [ ! -f ./mv_firmware/firmware/tasmota32-knx.* ] || mv ./mv_firmware/firmware/tasmota32-knx.* ./firmware/tasmota32/ [ ! -f ./mv_firmware/firmware/tasmota32* ] || mv ./mv_firmware/firmware/tasmota32* ./firmware/tasmota32/languages/ [ ! -f ./mv_firmware/firmware/* ] || mv ./mv_firmware/firmware/* ./firmware/tasmota/languages/ [ ! -f ./tools/Esptool/ESP32/*.* ] || mv ./tools/Esptool/ESP32/*.* ./firmware/tasmota32/ESP32_needed_files/ diff --git a/.github/workflows/Tasmota_build_master.yml b/.github/workflows/Tasmota_build_master.yml index 4735d702e..4bef7a371 100644 --- a/.github/workflows/Tasmota_build_master.yml +++ b/.github/workflows/Tasmota_build_master.yml @@ -804,46 +804,6 @@ jobs: path: ./build_output - tasmota32-knx: - needs: tasmota_pull - runs-on: ubuntu-latest - continue-on-error: true - steps: - - uses: actions/checkout@v1 - - name: Set up Python - uses: actions/setup-python@v1 - - name: Install dependencies - run: | - pip install -U platformio - - name: Run PlatformIO - run: | - platformio run -e tasmota32-knx - - uses: actions/upload-artifact@v2 - with: - name: firmware - path: ./build_output - - - tasmota32-sensors: - needs: tasmota_pull - runs-on: ubuntu-latest - continue-on-error: true - steps: - - uses: actions/checkout@v1 - - name: Set up Python - uses: actions/setup-python@v1 - - name: Install dependencies - run: | - pip install -U platformio - - name: Run PlatformIO - run: | - platformio run -e tasmota32-sensors - - uses: actions/upload-artifact@v2 - with: - name: firmware - path: ./build_output - - tasmota32-display: needs: tasmota_pull runs-on: ubuntu-latest @@ -1434,14 +1394,12 @@ jobs: [ ! -f ./mv_firmware/firmware/tasmota-knx.* ] || mv ./mv_firmware/firmware/tasmota-knx.* ./firmware/tasmota/ [ ! -f ./mv_firmware/firmware/tasmota-zbbridge.* ] || mv ./mv_firmware/firmware/tasmota-zbbridge.* ./firmware/tasmota/ [ ! -f ./mv_firmware/firmware/tasmota32.* ] || mv ./mv_firmware/firmware/tasmota32.* ./firmware/tasmota32/ - [ ! -f ./mv_firmware/firmware/tasmota32-sensors.* ] || mv ./mv_firmware/firmware/tasmota32-sensors.* ./firmware/tasmota32/ [ ! -f ./mv_firmware/firmware/tasmota32-ir*.* ] || mv ./mv_firmware/firmware/tasmota32-ir*.* ./firmware/tasmota32/ [ ! -f ./mv_firmware/firmware/tasmota32-display.* ] || mv ./mv_firmware/firmware/tasmota32-display.* ./firmware/tasmota32/ [ ! -f ./mv_firmware/firmware/tasmota32-web*.* ] || mv ./mv_firmware/firmware/tasmota32-web*.* ./firmware/tasmota32/ [ ! -f ./mv_firmware/firmware/tasmota32-odroidgo.* ] || mv ./mv_firmware/firmware/tasmota32-odroidgo.* ./firmware/tasmota32/ [ ! -f ./mv_firmware/firmware/tasmota32-core2.* ] || mv ./mv_firmware/firmware/tasmota32-core2.* ./firmware/tasmota32/ [ ! -f ./mv_firmware/firmware/tasmota32-bluetooth.* ] || mv ./mv_firmware/firmware/tasmota32-bluetooth.* ./firmware/tasmota32/ - [ ! -f ./mv_firmware/firmware/tasmota32-knx.* ] || mv ./mv_firmware/firmware/tasmota32-knx.* ./firmware/tasmota32/ [ ! -f ./mv_firmware/firmware/tasmota32* ] || mv ./mv_firmware/firmware/tasmota32* ./firmware/tasmota32/languages/ [ ! -f ./mv_firmware/firmware/* ] || mv ./mv_firmware/firmware/* ./firmware/tasmota/languages/ [ ! -f ./tools/Esptool/ESP32/*.* ] || mv ./tools/Esptool/ESP32/*.* ./firmware/tasmota32/ESP32_needed_files/ diff --git a/platformio_override_sample.ini b/platformio_override_sample.ini index 380145cd6..da565a651 100644 --- a/platformio_override_sample.ini +++ b/platformio_override_sample.ini @@ -29,8 +29,6 @@ default_envs = ; tasmota32 ; tasmota32-bluetooth ; tasmota32-webcam -; tasmota32-knx -; tasmota32-sensors ; tasmota32-display ; tasmota32-ir ; tasmota32-ircustom diff --git a/platformio_tasmota32.ini b/platformio_tasmota32.ini index 3eb8047da..1a69eec5d 100644 --- a/platformio_tasmota32.ini +++ b/platformio_tasmota32.ini @@ -11,8 +11,6 @@ default_envs = ${build_envs.default_envs} ; tasmota32-webcam ; tasmota32-odroidgo ; tasmota32-core2 -; tasmota32-knx -; tasmota32-sensors ; tasmota32-display ; tasmota32-ir ; tasmota32-ircustom diff --git a/platformio_tasmota_env32.ini b/platformio_tasmota_env32.ini index bcd391b62..29e8d69a5 100644 --- a/platformio_tasmota_env32.ini +++ b/platformio_tasmota_env32.ini @@ -66,16 +66,6 @@ extends = env:tasmota32 build_flags = ${common32.build_flags} -DFIRMWARE_BLUETOOTH lib_extra_dirs = lib/libesp32, lib/libesp32_div, lib/lib_basic, lib/lib_i2c, lib/lib_ssl -[env:tasmota32-knx] -extends = env:tasmota32 -build_flags = ${common32.build_flags} -DFIRMWARE_KNX_NO_EMULATION -lib_extra_dirs = lib/libesp32, lib/lib_basic, lib/lib_div - -[env:tasmota32-sensors] -extends = env:tasmota32 -build_flags = ${common32.build_flags} -DFIRMWARE_SENSORS -lib_extra_dirs = lib/libesp32, lib/lib_basic, lib/lib_i2c, lib/lib_rf, lib/lib_div, lib/lib_ssl - [env:tasmota32-display] extends = env:tasmota32 build_flags = ${common32.build_flags} -DFIRMWARE_DISPLAYS diff --git a/tasmota/tasmota.h b/tasmota/tasmota.h index 87e5f01b3..6c3f2c7db 100644 --- a/tasmota/tasmota.h +++ b/tasmota/tasmota.h @@ -31,6 +31,7 @@ \*********************************************************************************************/ #define CODE_IMAGE_STR "tasmota" +#define FLAG_VARIANT_TASMOTA32 #define USE_LIGHT // Enable light control #define USE_ENERGY_SENSOR // Use energy sensors (+14k code) diff --git a/tasmota/tasmota_configurations.h b/tasmota/tasmota_configurations.h index c4956bdbd..beefe4d8b 100644 --- a/tasmota/tasmota_configurations.h +++ b/tasmota/tasmota_configurations.h @@ -29,6 +29,8 @@ #undef CODE_IMAGE_STR #define CODE_IMAGE_STR "sensors" +#undef FLAG_VARIANT_TASMOTA32 + #undef USE_DISCOVERY // Disable mDNS (+8k code or +23.5k code with core 2_5_x, +0.3k mem) @@ -231,6 +233,7 @@ #undef CODE_IMAGE_STR #define CODE_IMAGE_STR "knx" +#undef FLAG_VARIANT_TASMOTA32 #ifndef USE_KNX #define USE_KNX // Enable KNX IP Protocol Support (+23k code, +3k3 mem) @@ -257,6 +260,7 @@ #undef CODE_IMAGE_STR #define CODE_IMAGE_STR "display" +#undef FLAG_VARIANT_TASMOTA32 #undef USE_EMULATION // Disable Belkin WeMo and Hue Bridge emulation for Alexa (-16k code, -2k mem) #undef USE_EMULATION_HUE // Disable Hue Bridge emulation for Alexa (+14k code, +2k mem common) @@ -338,6 +342,7 @@ #undef CODE_IMAGE_STR #define CODE_IMAGE_STR "ir" +#undef FLAG_VARIANT_TASMOTA32 #undef USE_EMULATION #undef USE_EMULATION_HUE // Disable Hue emulation - only for lights and relays @@ -463,6 +468,7 @@ #undef CODE_IMAGE_STR #define CODE_IMAGE_STR "zbbridge" +#undef FLAG_VARIANT_TASMOTA32 #undef MODULE #define MODULE SONOFF_ZB_BRIDGE // [Module] Select default module from tasmota_template.h @@ -620,6 +626,7 @@ #undef CODE_IMAGE_STR #define CODE_IMAGE_STR "lite" +#undef FLAG_VARIANT_TASMOTA32 #undef APP_SLEEP #define APP_SLEEP 1 // Default to sleep = 1 for FIRMWARE_LITE @@ -752,6 +759,7 @@ #undef CODE_IMAGE_STR #define CODE_IMAGE_STR "minimal" +#undef FLAG_VARIANT_TASMOTA32 #undef FIRMWARE_LITE // Disable tasmota-lite with no sensors #undef FIRMWARE_SENSORS // Disable tasmota-sensors with useful sensors enabled @@ -899,6 +907,7 @@ #undef CODE_IMAGE_STR #define CODE_IMAGE_STR "mini-custom" +#undef FLAG_VARIANT_TASMOTA32 #undef FIRMWARE_LITE // Disable tasmota-lite with no sensors #undef FIRMWARE_SENSORS // Disable tasmota-sensors with useful sensors enabled diff --git a/tasmota/tasmota_configurations_ESP32.h b/tasmota/tasmota_configurations_ESP32.h index 951d63e41..990d95edc 100644 --- a/tasmota/tasmota_configurations_ESP32.h +++ b/tasmota/tasmota_configurations_ESP32.h @@ -31,6 +31,7 @@ #undef CODE_IMAGE_STR #define CODE_IMAGE_STR "webcam" +#undef FLAG_VARIANT_TASMOTA32 #define USE_WEBCAM #undef USE_MI_ESP32 // (ESP32 only) Disable support for ESP32 as a BLE-bridge (+9k2 mem, +292k flash) @@ -45,6 +46,7 @@ #undef CODE_IMAGE_STR #define CODE_IMAGE_STR "odroid-go" +#undef FLAG_VARIANT_TASMOTA32 #undef MODULE #define MODULE ODROID_GO // [Module] Select default module from tasmota_template.h @@ -72,6 +74,7 @@ #undef CODE_IMAGE_STR #define CODE_IMAGE_STR "core2" +#undef FLAG_VARIANT_TASMOTA32 #undef MODULE #define MODULE M5STACK_CORE2 // [Module] Select default module from tasmota_template.h @@ -126,6 +129,7 @@ #undef CODE_IMAGE_STR #define CODE_IMAGE_STR "bluetooth" +#undef FLAG_VARIANT_TASMOTA32 #undef MODULE #define MODULE WEMOS // [Module] Select default module from tasmota_template.h @@ -140,6 +144,188 @@ #define USE_MI_ESP32 // (ESP32 only) Add support for ESP32 as a BLE-bridge (+9k2 mem, +292k flash) #endif // FIRMWARE_BLUETOOTH -#endif // ESP32 +/*********************************************************************************************\ + * [tasmota32.bin] + * Provide an image which includes KNX and Sensors +\*********************************************************************************************/ +// FLAG_VARIANT_TASMOTA32 is set for variant "Tasmota32" -> tasmota.h +// for all other Tasmota build variants this flag needs to be undefined!! + +#ifdef FLAG_VARIANT_TASMOTA32 + +#define USE_ENHANCED_GUI_WIFI_SCAN + +#define ROTARY_V1 // Add support for Rotary Encoder as used in MI Desk Lamp + +#define USE_TUYA_MCU // Add support for Tuya Serial MCU +#ifndef TUYA_DIMMER_ID + #define TUYA_DIMMER_ID 0 // Default dimmer Id +#endif +#undef USE_ARMTRONIX_DIMMERS // Disable support for Armtronix Dimmers (+1k4 code) +//#undef USE_PS_16_DZ // Disable support for PS-16-DZ Dimmer (+2k code) +#undef USE_SONOFF_IFAN // Disable support for Sonoff iFan02 and iFan03 (+2k code) +#define USE_BUZZER // Add support for a buzzer (+0k6 code) +//#undef USE_ARILUX_RF // Disable support for Arilux RF remote controller (+0k8 code, 252 iram (non 2.3.0)) +#define USE_DEEPSLEEP // Add support for deepsleep (+1k code) +#undef USE_EXS_DIMMER // Disable support for EX-Store WiFi Dimmer +//#define USE_HOTPLUG // Add support for sensor HotPlug +//#undef USE_DEVICE_GROUPS // Disable support for device groups (+5k6 code) +#undef USE_PWM_DIMMER // Disable support for MJ-SD01/acenx/NTONPOWER PWM dimmers (+4k5 code) +#undef USE_KEELOQ // Disable support for Jarolift rollers by Keeloq algorithm (+4k5 code) +#undef USE_SONOFF_D1 // Disable support for Sonoff D1 Dimmer (+0k7 code) +#undef USE_SHELLY_DIMMER // Disable support for Shelly Dimmer (+3k code) + +#define USE_LIGHT_PALETTE // Add support for color palette (+0k9 code) + +#define USE_DS18x20 // Add support for DS18x20 sensors with id sort, single scan and read retry (+1k3 code) + +#define USE_I2C // I2C using library wire (+10k code, 0k2 mem, 124 iram) +#define USE_SHT // [I2cDriver8] Enable SHT1X sensor (+1k4 code) +#define USE_HTU // [I2cDriver9] Enable HTU21/SI7013/SI7020/SI7021 sensor (I2C address 0x40) (+1k5 code) +#define USE_BMP // [I2cDriver10] Enable BMP085/BMP180/BMP280/BME280 sensors (I2C addresses 0x76 and 0x77) (+4k4 code) + #define USE_BME680 // Enable support for BME680 sensor using Bosch BME680 library (+4k code) +#define USE_BH1750 // [I2cDriver11] Enable BH1750 sensor (I2C address 0x23 or 0x5C) (+0k5 code) +#define USE_VEML6070 // [I2cDriver12] Enable VEML6070 sensor (I2C addresses 0x38 and 0x39) (+1k5 code) +//#define USE_VEML6075 // [I2cDriver49] Enable VEML6075 UVA/UVB/UVINDEX Sensor (I2C address 0x10) (+2k1 code) +//#define USE_VEML7700 // [I2cDriver50] Enable VEML7700 Ambient Light sensor (I2C addresses 0x10) (+4k5 code) +#define USE_ADS1115 // [I2cDriver13] Enable ADS1115 16 bit A/D converter (I2C address 0x48, 0x49, 0x4A or 0x4B) based on Adafruit ADS1x15 library (no library needed) (+0k7 code) +#define USE_INA219 // [I2cDriver14] Enable INA219 (I2C address 0x40, 0x41 0x44 or 0x45) Low voltage and current sensor (+1k code) +//#define USE_INA226 // [I2cDriver35] Enable INA226 (I2C address 0x40, 0x41 0x44 or 0x45) Low voltage and current sensor (+2k3 code) +#define USE_SHT3X // [I2cDriver15] Enable SHT3x (I2C address 0x44 or 0x45) or SHTC3 (I2C address 0x70) sensor (+0k7 code) +#define USE_TSL2561 // [I2cDriver16] Enable TSL2561 sensor (I2C address 0x29, 0x39 or 0x49) using library Joba_Tsl2561 (+2k3 code) +//#define USE_TSL2591 // [I2cDriver40] Enable TSL2591 sensor (I2C address 0x29) using library Adafruit_TSL2591 (+1k6 code) +#define USE_MGS // [I2cDriver17] Enable Xadow and Grove Mutichannel Gas sensor using library Multichannel_Gas_Sensor (+10k code) +#define USE_SGP30 // [I2cDriver18] Enable SGP30 sensor (I2C address 0x58) (+1k1 code) +//#define USE_SI1145 // [I2cDriver19] Enable SI1145/46/47 sensor (I2C address 0x60) (+1k code) +#define USE_LM75AD // [I2cDriver20] Enable LM75AD sensor (I2C addresses 0x48 - 0x4F) (+0k5 code) +//#define USE_APDS9960 // [I2cDriver21] Enable APDS9960 Proximity Sensor (I2C address 0x39). Disables SHT and VEML6070 (+4k7 code) +//#define USE_MCP230xx // [I2cDriver22] Enable MCP23008/MCP23017 - Must define I2C Address in #define USE_MCP230xx_ADDR below - range 0x20 - 0x27 (+4k7 code) +//#define USE_PCA9685 // [I2cDriver1] Enable PCA9685 I2C HW PWM Driver - Must define I2C Address in #define USE_PCA9685_ADDR below - range 0x40 - 0x47 (+1k4 code) +//#define USE_MPR121 // [I2cDriver23] Enable MPR121 controller (I2C addresses 0x5A, 0x5B, 0x5C and 0x5D) in input mode for touch buttons (+1k3 code) +#define USE_CCS811 // [I2cDriver24] Enable CCS811 sensor (I2C address 0x5A) (+2k2 code) +//#define USE_MPU6050 // [I2cDriver25] Enable MPU6050 sensor (I2C address 0x68 AD0 low or 0x69 AD0 high) (+3K3 of code and 188 Bytes of RAM) +//#define USE_DS3231 // [I2cDriver26] Enable DS3231 external RTC in case no Wifi is avaliable. See docs in the source file (+1k2 code) +//#define USE_MGC3130 // [I2cDriver27] Enable MGC3130 Electric Field Effect Sensor (I2C address 0x42) (+2k7 code, 0k3 mem) +//#define USE_MAX44009 // [I2cDriver28] Enable MAX44009 Ambient Light sensor (I2C addresses 0x4A and 0x4B) (+0k8 code) +#define USE_SCD30 // [I2cDriver29] Enable Sensiron SCd30 CO2 sensor (I2C address 0x61) (+3k3 code) +//#define USE_SPS30 // [I2cDriver30] Enable Sensiron SPS30 particle sensor (I2C address 0x69) (+1.7 code) +#define USE_ADE7953 // [I2cDriver7] Enable ADE7953 Energy monitor as used on Shelly 2.5 (I2C address 0x38) (+1k5) +//#define USE_VL53L0X // [I2cDriver31] Enable VL53L0x time of flight sensor (I2C address 0x29) (+4k code) +//#define USE_VL53L1X // [I2cDriver54] Enable VL53L1X time of flight sensor (I2C address 0x29) using Pololu VL53L1X library (+2k9 code) +//#define USE_TOF10120 // [I2cDriver57] Enable TOF10120 time of flight sensor (I2C address 0x52) (+0k6 code) +//#define USE_MLX90614 // [I2cDriver32] Enable MLX90614 ir temp sensor (I2C address 0x5a) (+0.6k code) +//#define USE_CHIRP // [I2cDriver33] Enable CHIRP soil moisture sensor (variable I2C address, default 0x20) +//#define USE_PAJ7620 // [I2cDriver34] Enable PAJ7620 gesture sensor (I2C address 0x73) (+2.5k code) +//#define USE_PCF8574 // [I2cDriver2] Enable PCF8574 I/O Expander (I2C addresses 0x20 - 0x26 and 0x39 - 0x3F) (+1k9 code) +#define USE_HIH6 // [I2cDriver36] Enable Honeywell HIH Humidity and Temperature sensor (I2C address 0x27) (+0k6) +#define USE_DHT12 // [I2cDriver41] Enable DHT12 humidity and temperature sensor (I2C address 0x5C) (+0k7 code) +#define USE_DS1624 // [I2cDriver42] Enable DS1624, DS1621 temperature sensor (I2C addresses 0x48 - 0x4F) (+1k2 code) +//#define USE_AHT1x // [I2cDriver43] Enable AHT10/15 humidity and temperature sensor (I2C address 0x38, 0x39) (+0k8 code) +// #define USE_AHT2x // [I2cDriver43] Enable AHT20 instead of AHT1x humidity and temperature sensor (I2C address 0x38) (+0k8 code) +#define USE_WEMOS_MOTOR_V1 // [I2cDriver44] Enable Wemos motor driver V1 (I2C addresses 0x2D - 0x30) (+0k7 code) + #define WEMOS_MOTOR_V1_ADDR 0x30 // Default I2C address 0x30 + #define WEMOS_MOTOR_V1_FREQ 1000 // Default frequency +//#define USE_HDC1080 // [I2cDriver45] Enable HDC1080 temperature/humidity sensor (I2C address 0x40) (+1k5 code) +#define USE_IAQ // [I2cDriver46] Enable iAQ-core air quality sensor (I2C address 0x5a) (+0k6 code) +#define USE_AS3935 // [I2cDriver48] Enable AS3935 Franklin Lightning Sensor (I2C address 0x03) (+5k4 code) +//#define USE_MCP9808 // [I2cDriver51] Enable MCP9808 temperature sensor (I2C addresses 0x18 - 0x1F) (+0k9 code) +//#define USE_HP303B // [I2cDriver52] Enable HP303B temperature and pressure sensor (I2C address 0x76 or 0x77) (+6k2 code) +//#define USE_MLX90640 // [I2cDriver53] Enable MLX90640 IR array temperature sensor (I2C address 0x33) (+20k code) +//#define USE_EZOPH // [I2cDriver55] Enable support for EZO's pH sensor (+0k3 code) - Shared EZO code required for any EZO device (+1k2 code) +//#define USE_EZOORP // [I2cDriver55] Enable support for EZO's ORP sensor (+0k3 code) - Shared EZO code required for any EZO device (+1k2 code) +//#define USE_EZORTD // [I2cDriver55] Enable support for EZO's RTD sensor (+0k2 code) - Shared EZO code required for any EZO device (+1k2 code) +//#define USE_EZOHUM // [I2cDriver55] Enable support for EZO's HUM sensor (+0k3 code) - Shared EZO code required for any EZO device (+1k2 code) +//#define USE_EZOEC // [I2cDriver55] Enable support for EZO's EC sensor (+0k3 code) - Shared EZO code required for any EZO device (+1k2 code) +//#define USE_EZOCO2 // [I2cDriver55] Enable support for EZO's CO2 sensor (+0k2 code) - Shared EZO code required for any EZO device (+1k2 code) +//#define USE_EZOO2 // [I2cDriver55] Enable support for EZO's O2 sensor (+0k3 code) - Shared EZO code required for any EZO device (+1k2 code) +//#define USE_EZOPRS // [I2cDriver55] Enable support for EZO's PRS sensor (+0k7 code) - Shared EZO code required for any EZO device (+1k2 code) +//#define USE_EZOFLO // [I2cDriver55] Enable support for EZO's FLO sensor (+0k4 code) - Shared EZO code required for any EZO device (+1k2 code) +//#define USE_EZODO // [I2cDriver55] Enable support for EZO's DO sensor (+0k3 code) - Shared EZO code required for any EZO device (+1k2 code) +//#define USE_EZORGB // [I2cDriver55] Enable support for EZO's RGB sensor (+0k5 code) - Shared EZO code required for any EZO device (+1k2 code) +//#define USE_EZOPMP // [I2cDriver55] Enable support for EZO's PMP sensor (+0k3 code) - Shared EZO code required for any EZO device (+1k2 code) +//#define USE_SEESAW_SOIL // [I2cDriver56] Enable Capacitice Soil Moisture & Temperature Sensor (I2C addresses 0x36 - 0x39) (+1k3 code) + +//#define USE_SPI // Hardware SPI using GPIO12(MISO), GPIO13(MOSI) and GPIO14(CLK) in addition to two user selectable GPIOs(CS and DC) +//#define USE_RC522 // Add support for MFRC522 13.56Mhz Rfid reader (+6k code) + +#define USE_MHZ19 // Add support for MH-Z19 CO2 sensor (+2k code) +#define USE_SENSEAIR // Add support for SenseAir K30, K70 and S8 CO2 sensor (+2k3 code) +#ifndef CO2_LOW + #define CO2_LOW 800 // Below this CO2 value show green light (needs PWM or WS2812 RG(B) led and enable with SetOption18 1) +#endif +#ifndef CO2_HIGH + #define CO2_HIGH 1200 // Above this CO2 value show red light (needs PWM or WS2812 RG(B) led and enable with SetOption18 1) +#endif +#define USE_PMS5003 // Add support for PMS5003 and PMS7003 particle concentration sensor (+1k3 code) + //#define PMS_MODEL_PMS3003 // Enable support of PMS3003 instead of PMS5003/PMS7003 (needs the USE_PMS5003 above) +#define USE_NOVA_SDS // Add support for SDS011 and SDS021 particle concentration sensor (+0k7 code) +#define USE_HPMA // Add support for Honeywell HPMA115S0 particle concentration sensor +#define USE_SR04 // Add support for HC-SR04 ultrasonic devices (+1k code) +//#define USE_DYP // Add support for DYP ME-007 ultrasonic distance sensor, serial port version (+0k5 code) +#define USE_SERIAL_BRIDGE // Add support for software Serial Bridge (+0k8 code) +#define USE_MP3_PLAYER // Use of the DFPlayer Mini MP3 Player RB-DFR-562 commands: play, volume and stop + #define MP3_VOLUME 10 // Set the startup volume on init, the range can be 0..30(max) +//#define USE_AZ7798 // Add support for AZ-Instrument 7798 CO2 datalogger +#define USE_PN532_HSU // Add support for PN532 using HSU (Serial) interface (+1k8 code, 140 bytes mem) +//#define USE_ZIGBEE // Enable serial communication with Zigbee CC2530 flashed with ZNP +#define USE_RDM6300 // Add support for RDM6300 125kHz RFID Reader (+0k8) +#define USE_IBEACON // Add support for bluetooth LE passive scan of ibeacon devices (uses HM17 module) +//#define USE_GPS // Add support for GPS and NTP Server for becoming Stratus 1 Time Source (+ 3.1kb flash, +132 bytes RAM) + +//#define USE_BLE_ESP32 // (ESP32 only) Add support for native BLE on ESP32 - use new driver +//#define USE_MI_ESP32 // (ESP32 only) Add support for ESP32 as a BLE-bridge (+9k2 mem, +292k flash) + +#define USE_HRXL // Add support for MaxBotix HRXL-MaxSonar ultrasonic range finders (+0k7) +//#define USE_TASMOTA_CLIENT // Add support for Arduino Uno/Pro Mini via serial interface including flashing (+2k3 code, 44 mem) +//#define USE_OPENTHERM // Add support for OpenTherm (+15k code) +//#define USE_MIEL_HVAC // Add support for Mitsubishi Electric HVAC serial interface (+5k code) +//#define USE_PROJECTOR_CTRL // Add support for LCD/DLP Projector serial control interface (+2k code) +// #define USE_PROJECTOR_CTRL_NEC // Use codes for NEC +// #define USE_PROJECTOR_CTRL_OPTOMA // Use codes for OPTOMA +//#define USE_AS608 // Add support for AS608 optical and R503 capacitive fingerprint sensor (+3k4 code) + +#define USE_ENERGY_SENSOR // Add energy sensors (-14k code) +#define USE_PZEM004T // Add support for PZEM004T Energy monitor (+2k code) +#define USE_PZEM_AC // Add support for PZEM014,016 Energy monitor (+1k1 code) +#define USE_PZEM_DC // Add support for PZEM003,017 Energy monitor (+1k1 code) +#define USE_MCP39F501 // Add support for MCP39F501 Energy monitor as used in Shelly 2 (+3k1 code) +#define USE_SDM72 // Add support for Eastron SDM72-Modbus energy monitor (+0k3 code) +#define USE_SDM120 // Add support for Eastron SDM120-Modbus energy monitor (+1k1 code) +#define USE_SDM630 // Add support for Eastron SDM630-Modbus energy monitor (+0k6 code) +#define USE_DDS2382 // Add support for Hiking DDS2382 Modbus energy monitor (+0k6 code) +#define USE_DDSU666 // Add support for Chint DDSU666 Modbus energy monitor (+0k6 code) +//#define USE_SOLAX_X1 // Add support for Solax X1 series Modbus log info (+3k1 code) +//#define USE_LE01MR // Add support for F&F LE-01MR modbus energy meter (+2k code) +//#define USE_TELEINFO // Add support for French Energy Provider metering telemetry (+5k2 code, +168 RAM + SmartMeter LinkedList Values RAM) +//#define USE_WE517 // Add support for Orno WE517-Modbus energy monitor (+1k code) + +#define USE_DHT // Add support for DHT11, AM2301 (DHT21, DHT22, AM2302, AM2321) and SI7021 Temperature and Humidity sensor +#define USE_MAX31855 // Add support for MAX31855 K-Type thermocouple sensor using softSPI +//#define USE_MAX31865 // Add support for MAX31865 RTD sensors using softSPI +#define USE_IR_REMOTE // Send IR remote commands using library IRremoteESP8266 and ArduinoJson (+4k code, 0k3 mem, 48 iram) + #define USE_IR_RECEIVE // Support for IR receiver (+5k5 code, 264 iram) +#define USE_LMT01 // Add support for TI LMT01 temperature sensor, count pulses on single GPIO (+0k5 code) +//#define USE_WIEGAND // Add support for 24/26/32/34 bit RFID Wiegand interface (D0/D1) (+1k7 code) +#define USE_TM1638 // Add support for TM1638 switches copying Switch1 .. Switch8 (+1k code) +#define USE_HX711 // Add support for HX711 load cell (+1k5 code) +//#define USE_HX711_GUI // Add optional web GUI to HX711 as scale (+1k8 code) +//#define USE_TX20_WIND_SENSOR // Add support for La Crosse TX20 anemometer (+2k6/0k8 code) +//#define USE_TX23_WIND_SENSOR // Add support for La Crosse TX23 anemometer (+2k7/1k code) +//#define USE_WINDMETER // Add support for analog anemometer (+2k2 code) +#define USE_RC_SWITCH // Add support for RF transceiver using library RcSwitch (+2k7 code, 460 iram) +#define USE_RF_SENSOR // Add support for RF sensor receiver (434MHz or 868MHz) (+0k8 code) +// #define USE_THEO_V2 // Add support for decoding Theo V2 sensors as documented on https://sidweb.nl using 434MHz RF sensor receiver (+1k4 code) + #define USE_ALECTO_V2 // Add support for decoding Alecto V2 sensors like ACH2010, WS3000 and DKW2012 using 868MHz RF sensor receiver (+1k7 code) +#define USE_HRE // Add support for Badger HR-E Water Meter (+1k4 code) +//#define USE_A4988_STEPPER // Add support for A4988/DRV8825 stepper-motor-driver-circuit (+10k5 code) +//#define USE_THERMOSTAT // Add support for Thermostat + +#ifndef USE_KNX +#define USE_KNX // Enable KNX IP Protocol Support (+23k code, +3k3 mem) +#endif + +#endif // FLAG_VARIANT_TASMOTA32 + +#endif // ESP32 #endif // _TASMOTA_CONFIGURATIONS_ESP32_H_ diff --git a/tasmota/user_config_override_sample.h b/tasmota/user_config_override_sample.h index ae434bb9c..369585717 100644 --- a/tasmota/user_config_override_sample.h +++ b/tasmota/user_config_override_sample.h @@ -20,6 +20,9 @@ #ifndef _USER_CONFIG_OVERRIDE_H_ #define _USER_CONFIG_OVERRIDE_H_ +// this flag needs to be unset otherwise settings from build variant Tasmota32 are included +#undef FLAG_VARIANT_TASMOTA32 + // force the compiler to show a warning to confirm that this file is included #warning **** user_config_override.h: Using Settings from this File **** From eed2c67744bc83f66795d6808e46349c1745a725 Mon Sep 17 00:00:00 2001 From: Paul C Diem Date: Sat, 13 Mar 2021 14:10:32 -0600 Subject: [PATCH 18/87] Range check item before storing in value array --- tasmota/support_device_groups.ino | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tasmota/support_device_groups.ino b/tasmota/support_device_groups.ino index f85b801a5..19112567a 100644 --- a/tasmota/support_device_groups.ino +++ b/tasmota/support_device_groups.ino @@ -344,18 +344,18 @@ void SendReceiveDeviceGroupMessage(struct device_group * device_group, struct de value |= *message_ptr++ << 16; value |= *message_ptr++ << 24; #ifdef USE_DEVICE_GROUPS_SEND - device_group->values_32bit[item - DGR_ITEM_MAX_16BIT - 1] = (item == DGR_ITEM_POWER ? value & 0xffffff : value); + if (item < DGR_ITEM_LAST_32BIT) device_group->values_32bit[item - DGR_ITEM_MAX_16BIT - 1] = (item == DGR_ITEM_POWER ? value & 0xffffff : value); #endif // USE_DEVICE_GROUPS_SEND } #ifdef USE_DEVICE_GROUPS_SEND else { - device_group->values_16bit[item - DGR_ITEM_MAX_8BIT - 1] = value; + if (item < DGR_ITEM_LAST_16BIT) device_group->values_16bit[item - DGR_ITEM_MAX_8BIT - 1] = value; } #endif // USE_DEVICE_GROUPS_SEND } #ifdef USE_DEVICE_GROUPS_SEND else { - device_group->values_8bit[item] = value; + if (item < DGR_ITEM_LAST_8BIT) device_group->values_8bit[item] = value; } #endif // USE_DEVICE_GROUPS_SEND log_length = snprintf(log_ptr, log_remaining, PSTR("%u"), value); From ecd78bfd96c6693f0358f0a0636a3a3c2f1cb713 Mon Sep 17 00:00:00 2001 From: Paul C Diem Date: Sat, 13 Mar 2021 14:13:44 -0600 Subject: [PATCH 19/87] Use SO32 for power button hold time --- tasmota/xdrv_35_pwm_dimmer.ino | 27 +++++++++++---------------- 1 file changed, 11 insertions(+), 16 deletions(-) diff --git a/tasmota/xdrv_35_pwm_dimmer.ino b/tasmota/xdrv_35_pwm_dimmer.ino index 555ec1a67..d30e7cba1 100644 --- a/tasmota/xdrv_35_pwm_dimmer.ino +++ b/tasmota/xdrv_35_pwm_dimmer.ino @@ -755,21 +755,8 @@ bool Xdrv35(uint8_t function) // If the button is pressed, ... if (!XdrvMailbox.payload) { - // If the button was just pressed, flag the button as pressed, set the hold time and - // increment the buttons pressed count. + // If the button was just pressed, ... if (!button_pressed[button_index]) { - button_pressed[button_index] = true; - uint32_t hold_delay = 250; - if (button_index == power_button_index) { -#ifdef USE_PWM_DIMMER_REMOTE - if (!(active_remote_pwm_dimmer ? active_remote_pwm_dimmer->power_on : TasmotaGlobal.power)) hold_delay = 500; -#else // USE_PWM_DIMMER_REMOTE - if (!TasmotaGlobal.power) hold_delay = 500; -#endif // USE_PWM_DIMMER_REMOTE - } - button_hold_time[button_index] = now + hold_delay; - buttons_pressed++; - if (buttons_pressed > 1) multibutton_in_progress = true; #ifdef USE_PWM_DIMMER_REMOTE // If there are no other buttons pressed right now and remote mode is enabled, make the @@ -785,7 +772,7 @@ bool Xdrv35(uint8_t function) // Top 0 1 1 0 // Middle 1 2 15 0 // Bottom 15 3 15 1 - if (buttons_pressed == 1 && Settings.flag4.multiple_device_groups) { + if (!buttons_pressed && Settings.flag4.multiple_device_groups) { power_button_index = button_index; down_button_index = (Pin(GPIO_KEY1, power_button_index) == 15 ? TasmotaGlobal.gpio_pin[1] : TasmotaGlobal.gpio_pin[15]) - 32; active_remote_pwm_dimmer = nullptr; @@ -793,9 +780,17 @@ bool Xdrv35(uint8_t function) active_remote_pwm_dimmer = &remote_pwm_dimmers[power_button_index]; } #endif // USE_PWM_DIMMER_REMOTE + + // Flag the button as pressed, increment the buttons pressed count and set the hold time. + button_pressed[button_index] = true; + buttons_pressed++; + if (buttons_pressed > 1) multibutton_in_progress = true; + uint32_t hold_delay = 250; + if (button_index == power_button_index) hold_delay = Settings.param[P_HOLD_TIME] * 10; + button_hold_time[button_index] = now + hold_delay; } - // If hold time has arrived and a rule is enabled that handles the button hold, handle it. + // If hold time has arrived and no rule is enabled that handles the button hold, handle it. else if (button_hold_time[button_index] <= now) { #ifdef USE_RULES sprintf(TasmotaGlobal.mqtt_data, PSTR("{\"Button%u\":{\"State\":3}}"), button_index + 1); From 44517380ff2e575e52353ef315ca42901484613d Mon Sep 17 00:00:00 2001 From: Stephan Hadinger Date: Sat, 13 Mar 2021 22:42:24 +0100 Subject: [PATCH 20/87] Berry upgrade --- CHANGELOG.md | 2 - .../Berry-0.1.10/generate/be_const_strtab.h | 213 +++--- .../generate/be_const_strtab_def.h | 301 ++++---- .../generate/be_fixed_be_class_bytes.h | 33 + .../generate/be_fixed_be_class_list.h | 39 +- .../generate/be_fixed_m_builtin.h | 46 +- .../Berry-0.1.10/generate/be_fixed_os_path.h | 20 - lib/libesp32/Berry-0.1.10/src/be_baselib.c | 2 + lib/libesp32/Berry-0.1.10/src/be_byteslib.c | 668 ++++++++++++++++++ lib/libesp32/Berry-0.1.10/src/be_libs.c | 6 + lib/libesp32/Berry-0.1.10/src/be_listlib.c | 21 +- lib/libesp32/Berry-0.1.10/src/be_vm.c | 2 +- lib/libesp32/Berry-0.1.10/src/berry.h | 4 + .../Berry-0.1.10/src/port/be_energylib.c | 26 + .../Berry-0.1.10/src/port/be_modtab.c | 6 +- .../Berry-0.1.10/src/port/be_tasmotalib.c | 59 +- .../Berry-0.1.10/src/port/be_wirelib.c | 34 +- tasmota/berry/autoexec.be | 1 + tasmota/berry/denky.be | 21 + tasmota/support.ino | 43 +- tasmota/xdrv_52_2_berry_native.ino | 119 ++++ tasmota/xdrv_52_3_berry_energy.ino | 70 ++ tasmota/xdrv_52_3_berry_native.ino | 534 -------------- tasmota/xdrv_52_3_berry_tasmota.ino | 529 ++++++++++++++ tasmota/xdrv_52_3_berry_wire.ino | 245 +++++++ tasmota/xdrv_52_7_berry_embedded.ino | 92 +-- tasmota/xdrv_52_9_berry.ino | 51 +- 27 files changed, 2222 insertions(+), 965 deletions(-) create mode 100644 lib/libesp32/Berry-0.1.10/generate/be_fixed_be_class_bytes.h delete mode 100644 lib/libesp32/Berry-0.1.10/generate/be_fixed_os_path.h create mode 100644 lib/libesp32/Berry-0.1.10/src/be_byteslib.c create mode 100644 lib/libesp32/Berry-0.1.10/src/port/be_energylib.c create mode 100644 tasmota/berry/autoexec.be create mode 100644 tasmota/berry/denky.be create mode 100644 tasmota/xdrv_52_2_berry_native.ino create mode 100644 tasmota/xdrv_52_3_berry_energy.ino delete mode 100644 tasmota/xdrv_52_3_berry_native.ino create mode 100644 tasmota/xdrv_52_3_berry_tasmota.ino create mode 100644 tasmota/xdrv_52_3_berry_wire.ino diff --git a/CHANGELOG.md b/CHANGELOG.md index f39aea56c..a2e722eac 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,9 +16,7 @@ All notable changes to this project will be documented in this file. - Crash protection in ext_vnsprintf_P (#11202) - Extent compile time SetOptions support (#11204) - ESP32 Extent BLE (#11212) - - ESP32 support for WS2812 hardware driver via RMT or I2S - - ESP32 support for secondary I2C controller diff --git a/lib/libesp32/Berry-0.1.10/generate/be_const_strtab.h b/lib/libesp32/Berry-0.1.10/generate/be_const_strtab.h index d3ad809d7..2d1b7e9e0 100644 --- a/lib/libesp32/Berry-0.1.10/generate/be_const_strtab.h +++ b/lib/libesp32/Berry-0.1.10/generate/be_const_strtab.h @@ -1,111 +1,116 @@ +extern const bcstring be_const_str_remove; +extern const bcstring be_const_str_false; +extern const bcstring be_const_str_raise; +extern const bcstring be_const_str_opt_connect; +extern const bcstring be_const_str_dot_p; +extern const bcstring be_const_str_calldepth; +extern const bcstring be_const_str_toupper; +extern const bcstring be_const_str_break; +extern const bcstring be_const_str_map; +extern const bcstring be_const_str_bytes; +extern const bcstring be_const_str_tanh; +extern const bcstring be_const_str_real; +extern const bcstring be_const_str_count; +extern const bcstring be_const_str_setitem; +extern const bcstring be_const_str_split; +extern const bcstring be_const_str_if; +extern const bcstring be_const_str_format; +extern const bcstring be_const_str_pow; +extern const bcstring be_const_str_tostring; +extern const bcstring be_const_str_load; +extern const bcstring be_const_str_setrange; +extern const bcstring be_const_str_char; extern const bcstring be_const_str_acos; +extern const bcstring be_const_str_deinit; +extern const bcstring be_const_str_dump; +extern const bcstring be_const_str_sqrt; +extern const bcstring be_const_str_fromstring; +extern const bcstring be_const_str_opt_eq; extern const bcstring be_const_str_asin; +extern const bcstring be_const_str_byte; +extern const bcstring be_const_str_copy; +extern const bcstring be_const_str_floor; +extern const bcstring be_const_str_find; +extern const bcstring be_const_str_var; +extern const bcstring be_const_str_opt_add; +extern const bcstring be_const_str___lower__; +extern const bcstring be_const_str_rand; +extern const bcstring be_const_str_sin; +extern const bcstring be_const_str_deg; +extern const bcstring be_const_str_imin; +extern const bcstring be_const_str_class; +extern const bcstring be_const_str_try; +extern const bcstring be_const_str_allocated; +extern const bcstring be_const_str_asstring; +extern const bcstring be_const_str_classname; +extern const bcstring be_const_str_reverse; +extern const bcstring be_const_str_assert; +extern const bcstring be_const_str_str; +extern const bcstring be_const_str_imax; +extern const bcstring be_const_str_init; +extern const bcstring be_const_str_except; +extern const bcstring be_const_str_return; +extern const bcstring be_const_str_opt_neq; +extern const bcstring be_const_str_hex; +extern const bcstring be_const_str_resize; +extern const bcstring be_const_str_as; +extern const bcstring be_const_str___upper__; +extern const bcstring be_const_str_codedump; +extern const bcstring be_const_str_cosh; +extern const bcstring be_const_str_abs; +extern const bcstring be_const_str_collect; +extern const bcstring be_const_str_atan; +extern const bcstring be_const_str_attrdump; +extern const bcstring be_const_str_input; +extern const bcstring be_const_str_; +extern const bcstring be_const_str_compile; +extern const bcstring be_const_str_iter; +extern const bcstring be_const_str_lower; +extern const bcstring be_const_str_number; +extern const bcstring be_const_str_print; +extern const bcstring be_const_str_exp; +extern const bcstring be_const_str_pi; +extern const bcstring be_const_str___iterator__; +extern const bcstring be_const_str_log; +extern const bcstring be_const_str_log10; +extern const bcstring be_const_str_ceil; +extern const bcstring be_const_str_get; +extern const bcstring be_const_str_tolower; +extern const bcstring be_const_str_while; +extern const bcstring be_const_str_import; +extern const bcstring be_const_str_size; extern const bcstring be_const_str_list; +extern const bcstring be_const_str_rad; +extern const bcstring be_const_str_tan; +extern const bcstring be_const_str_type; +extern const bcstring be_const_str_insert; extern const bcstring be_const_str_module; +extern const bcstring be_const_str_range; +extern const bcstring be_const_str_super; +extern const bcstring be_const_str_issubclass; +extern const bcstring be_const_str_def; +extern const bcstring be_const_str_classof; +extern const bcstring be_const_str_sinh; +extern const bcstring be_const_str_srand; +extern const bcstring be_const_str_end; +extern const bcstring be_const_str_clear; +extern const bcstring be_const_str_top; +extern const bcstring be_const_str_nil; +extern const bcstring be_const_str_open; +extern const bcstring be_const_str_do; +extern const bcstring be_const_str_add; +extern const bcstring be_const_str_isinstance; extern const bcstring be_const_str_pop; extern const bcstring be_const_str_int; -extern const bcstring be_const_str_push; -extern const bcstring be_const_str_setrange; -extern const bcstring be_const_str_sinh; -extern const bcstring be_const_str_elif; -extern const bcstring be_const_str_iter; -extern const bcstring be_const_str_load; -extern const bcstring be_const_str_class; -extern const bcstring be_const_str_if; -extern const bcstring be_const_str_opt_eq; -extern const bcstring be_const_str_ceil; -extern const bcstring be_const_str_floor; -extern const bcstring be_const_str_map; -extern const bcstring be_const_str_print; -extern const bcstring be_const_str_else; -extern const bcstring be_const_str_find; -extern const bcstring be_const_str_str; -extern const bcstring be_const_str___upper__; -extern const bcstring be_const_str_dump; -extern const bcstring be_const_str_atan; -extern const bcstring be_const_str_size; -extern const bcstring be_const_str_tolower; -extern const bcstring be_const_str_opt_add; -extern const bcstring be_const_str_abs; -extern const bcstring be_const_str_lower; -extern const bcstring be_const_str_end; -extern const bcstring be_const_str_import; -extern const bcstring be_const_str_classof; -extern const bcstring be_const_str_concat; -extern const bcstring be_const_str_byte; -extern const bcstring be_const_str_top; -extern const bcstring be_const_str_clear; -extern const bcstring be_const_str_opt_connect; -extern const bcstring be_const_str_collect; -extern const bcstring be_const_str_init; -extern const bcstring be_const_str_log10; -extern const bcstring be_const_str_nil; -extern const bcstring be_const_str_; -extern const bcstring be_const_str_real; -extern const bcstring be_const_str_calldepth; -extern const bcstring be_const_str_format; -extern const bcstring be_const_str_pi; -extern const bcstring be_const_str_do; -extern const bcstring be_const_str___iterator__; -extern const bcstring be_const_str_number; -extern const bcstring be_const_str_type; -extern const bcstring be_const_str_dot_p; -extern const bcstring be_const_str_traceback; -extern const bcstring be_const_str_as; -extern const bcstring be_const_str___lower__; -extern const bcstring be_const_str_exp; -extern const bcstring be_const_str_hex; -extern const bcstring be_const_str_char; -extern const bcstring be_const_str_split; -extern const bcstring be_const_str_toupper; -extern const bcstring be_const_str_deinit; -extern const bcstring be_const_str_tan; -extern const bcstring be_const_str_srand; -extern const bcstring be_const_str_imin; -extern const bcstring be_const_str_input; -extern const bcstring be_const_str_issubclass; -extern const bcstring be_const_str_tostring; -extern const bcstring be_const_str_break; -extern const bcstring be_const_str_insert; -extern const bcstring be_const_str_var; -extern const bcstring be_const_str_open; -extern const bcstring be_const_str_tanh; -extern const bcstring be_const_str_upper; -extern const bcstring be_const_str_allocated; -extern const bcstring be_const_str_rad; -extern const bcstring be_const_str_attrdump; -extern const bcstring be_const_str_copy; -extern const bcstring be_const_str_sqrt; -extern const bcstring be_const_str_for; -extern const bcstring be_const_str_raise; -extern const bcstring be_const_str_opt_neq; -extern const bcstring be_const_str_assert; -extern const bcstring be_const_str_item; -extern const bcstring be_const_str_reverse; -extern const bcstring be_const_str_sin; -extern const bcstring be_const_str_super; -extern const bcstring be_const_str_try; -extern const bcstring be_const_str_range; -extern const bcstring be_const_str_return; -extern const bcstring be_const_str_compile; -extern const bcstring be_const_str_false; -extern const bcstring be_const_str_resize; -extern const bcstring be_const_str_continue; -extern const bcstring be_const_str_log; -extern const bcstring be_const_str_true; -extern const bcstring be_const_str_while; -extern const bcstring be_const_str_pow; -extern const bcstring be_const_str_cos; -extern const bcstring be_const_str_count; -extern const bcstring be_const_str_remove; -extern const bcstring be_const_str_imax; -extern const bcstring be_const_str_rand; -extern const bcstring be_const_str_codedump; -extern const bcstring be_const_str_deg; extern const bcstring be_const_str_keys; -extern const bcstring be_const_str_setitem; -extern const bcstring be_const_str_def; -extern const bcstring be_const_str_except; -extern const bcstring be_const_str_classname; -extern const bcstring be_const_str_isinstance; -extern const bcstring be_const_str_cosh; +extern const bcstring be_const_str_elif; +extern const bcstring be_const_str_item; +extern const bcstring be_const_str_push; +extern const bcstring be_const_str_concat; +extern const bcstring be_const_str_traceback; +extern const bcstring be_const_str_upper; +extern const bcstring be_const_str_true; +extern const bcstring be_const_str_continue; +extern const bcstring be_const_str_else; +extern const bcstring be_const_str_cos; +extern const bcstring be_const_str_for; diff --git a/lib/libesp32/Berry-0.1.10/generate/be_const_strtab_def.h b/lib/libesp32/Berry-0.1.10/generate/be_const_strtab_def.h index b967bc818..bce08392c 100644 --- a/lib/libesp32/Berry-0.1.10/generate/be_const_strtab_def.h +++ b/lib/libesp32/Berry-0.1.10/generate/be_const_strtab_def.h @@ -1,165 +1,172 @@ -be_define_const_str(acos, "acos", 1006755615u, 0, 4, &be_const_str_asin); -be_define_const_str(asin, "asin", 4272848550u, 0, 4, &be_const_str_list); -be_define_const_str(list, "list", 217798785u, 0, 4, &be_const_str_module); -be_define_const_str(module, "module", 3617558685u, 0, 6, &be_const_str_pop); -be_define_const_str(pop, "pop", 1362321360u, 0, 3, NULL); -be_define_const_str(int, "int", 2515107422u, 0, 3, &be_const_str_push); -be_define_const_str(push, "push", 2272264157u, 0, 4, &be_const_str_setrange); -be_define_const_str(setrange, "setrange", 3794019032u, 0, 8, &be_const_str_sinh); -be_define_const_str(sinh, "sinh", 282220607u, 0, 4, &be_const_str_elif); -be_define_const_str(elif, "elif", 3232090307u, 51, 4, NULL); -be_define_const_str(iter, "iter", 3124256359u, 0, 4, &be_const_str_load); -be_define_const_str(load, "load", 3859241449u, 0, 4, &be_const_str_class); -be_define_const_str(class, "class", 2872970239u, 57, 5, &be_const_str_if); -be_define_const_str(if, "if", 959999494u, 50, 2, NULL); -be_define_const_str(opt_eq, "==", 2431966415u, 0, 2, &be_const_str_ceil); -be_define_const_str(ceil, "ceil", 1659167240u, 0, 4, NULL); -be_define_const_str(floor, "floor", 3102149661u, 0, 5, &be_const_str_map); -be_define_const_str(map, "map", 3751997361u, 0, 3, &be_const_str_print); -be_define_const_str(print, "print", 372738696u, 0, 5, &be_const_str_else); -be_define_const_str(else, "else", 3183434736u, 52, 4, NULL); -be_define_const_str(find, "find", 3186656602u, 0, 4, &be_const_str_str); -be_define_const_str(str, "str", 3259748752u, 0, 3, NULL); -be_define_const_str(__upper__, "__upper__", 3612202883u, 0, 9, &be_const_str_dump); -be_define_const_str(dump, "dump", 3663001223u, 0, 4, NULL); -be_define_const_str(atan, "atan", 108579519u, 0, 4, &be_const_str_size); -be_define_const_str(size, "size", 597743964u, 0, 4, &be_const_str_tolower); -be_define_const_str(tolower, "tolower", 1042520049u, 0, 7, NULL); -be_define_const_str(opt_add, "+", 772578730u, 0, 1, &be_const_str_abs); -be_define_const_str(abs, "abs", 709362235u, 0, 3, &be_const_str_lower); -be_define_const_str(lower, "lower", 3038577850u, 0, 5, &be_const_str_end); -be_define_const_str(end, "end", 1787721130u, 56, 3, &be_const_str_import); -be_define_const_str(import, "import", 288002260u, 66, 6, NULL); -be_define_const_str(classof, "classof", 1796577762u, 0, 7, &be_const_str_concat); -be_define_const_str(concat, "concat", 4124019837u, 0, 6, NULL); -be_define_const_str(byte, "byte", 1683620383u, 0, 4, &be_const_str_top); -be_define_const_str(top, "top", 2802900028u, 0, 3, NULL); -be_define_const_str(clear, "clear", 1550717474u, 0, 5, NULL); -be_define_const_str(opt_connect, "..", 2748622605u, 0, 2, &be_const_str_collect); -be_define_const_str(collect, "collect", 2399039025u, 0, 7, &be_const_str_init); -be_define_const_str(init, "init", 380752755u, 0, 4, &be_const_str_log10); -be_define_const_str(log10, "log10", 2346846000u, 0, 5, &be_const_str_nil); -be_define_const_str(nil, "nil", 228849900u, 63, 3, NULL); -be_define_const_str(, "", 2166136261u, 0, 0, &be_const_str_real); -be_define_const_str(real, "real", 3604983901u, 0, 4, NULL); -be_define_const_str(calldepth, "calldepth", 3122364302u, 0, 9, &be_const_str_format); -be_define_const_str(format, "format", 3114108242u, 0, 6, &be_const_str_pi); -be_define_const_str(pi, "pi", 1213090802u, 0, 2, &be_const_str_do); -be_define_const_str(do, "do", 1646057492u, 65, 2, NULL); -be_define_const_str(__iterator__, "__iterator__", 3884039703u, 0, 12, &be_const_str_number); -be_define_const_str(number, "number", 467038368u, 0, 6, &be_const_str_type); -be_define_const_str(type, "type", 1361572173u, 0, 4, NULL); -be_define_const_str(dot_p, ".p", 1171526419u, 0, 2, &be_const_str_traceback); -be_define_const_str(traceback, "traceback", 3385188109u, 0, 9, &be_const_str_as); -be_define_const_str(as, "as", 1579491469u, 67, 2, NULL); -be_define_const_str(__lower__, "__lower__", 123855590u, 0, 9, &be_const_str_exp); -be_define_const_str(exp, "exp", 1923516200u, 0, 3, &be_const_str_hex); -be_define_const_str(hex, "hex", 4273249610u, 0, 3, NULL); -be_define_const_str(char, "char", 2823553821u, 0, 4, &be_const_str_split); -be_define_const_str(split, "split", 2276994531u, 0, 5, &be_const_str_toupper); -be_define_const_str(toupper, "toupper", 3691983576u, 0, 7, NULL); -be_define_const_str(deinit, "deinit", 2345559592u, 0, 6, &be_const_str_tan); -be_define_const_str(tan, "tan", 2633446552u, 0, 3, NULL); -be_define_const_str(srand, "srand", 465518633u, 0, 5, NULL); -be_define_const_str(imin, "imin", 2714127864u, 0, 4, &be_const_str_input); -be_define_const_str(input, "input", 4191711099u, 0, 5, &be_const_str_issubclass); -be_define_const_str(issubclass, "issubclass", 4078395519u, 0, 10, NULL); -be_define_const_str(tostring, "tostring", 2299708645u, 0, 8, &be_const_str_break); -be_define_const_str(break, "break", 3378807160u, 58, 5, NULL); -be_define_const_str(insert, "insert", 3332609576u, 0, 6, &be_const_str_var); -be_define_const_str(var, "var", 2317739966u, 64, 3, NULL); -be_define_const_str(open, "open", 3546203337u, 0, 4, &be_const_str_tanh); -be_define_const_str(tanh, "tanh", 153638352u, 0, 4, &be_const_str_upper); -be_define_const_str(upper, "upper", 176974407u, 0, 5, NULL); -be_define_const_str(allocated, "allocated", 429986098u, 0, 9, &be_const_str_rad); -be_define_const_str(rad, "rad", 1358899048u, 0, 3, NULL); -be_define_const_str(attrdump, "attrdump", 1521571304u, 0, 8, &be_const_str_copy); -be_define_const_str(copy, "copy", 3848464964u, 0, 4, &be_const_str_sqrt); -be_define_const_str(sqrt, "sqrt", 2112764879u, 0, 4, NULL); -be_define_const_str(for, "for", 2901640080u, 54, 3, &be_const_str_raise); +be_define_const_str(remove, "remove", 3683784189u, 0, 6, &be_const_str_false); +be_define_const_str(false, "false", 184981848u, 62, 5, &be_const_str_raise); be_define_const_str(raise, "raise", 1593437475u, 70, 5, NULL); -be_define_const_str(opt_neq, "!=", 2428715011u, 0, 2, &be_const_str_assert); -be_define_const_str(assert, "assert", 2774883451u, 0, 6, &be_const_str_item); -be_define_const_str(item, "item", 2671260646u, 0, 4, &be_const_str_reverse); -be_define_const_str(reverse, "reverse", 558918661u, 0, 7, &be_const_str_sin); -be_define_const_str(sin, "sin", 3761252941u, 0, 3, &be_const_str_super); -be_define_const_str(super, "super", 4152230356u, 0, 5, &be_const_str_try); +be_define_const_str(opt_connect, "..", 2748622605u, 0, 2, &be_const_str_dot_p); +be_define_const_str(dot_p, ".p", 1171526419u, 0, 2, &be_const_str_calldepth); +be_define_const_str(calldepth, "calldepth", 3122364302u, 0, 9, NULL); +be_define_const_str(toupper, "toupper", 3691983576u, 0, 7, &be_const_str_break); +be_define_const_str(break, "break", 3378807160u, 58, 5, NULL); +be_define_const_str(map, "map", 3751997361u, 0, 3, NULL); +be_define_const_str(bytes, "bytes", 1706151940u, 0, 5, &be_const_str_tanh); +be_define_const_str(tanh, "tanh", 153638352u, 0, 4, NULL); +be_define_const_str(real, "real", 3604983901u, 0, 4, NULL); +be_define_const_str(count, "count", 967958004u, 0, 5, &be_const_str_setitem); +be_define_const_str(setitem, "setitem", 1554834596u, 0, 7, &be_const_str_split); +be_define_const_str(split, "split", 2276994531u, 0, 5, &be_const_str_if); +be_define_const_str(if, "if", 959999494u, 50, 2, NULL); +be_define_const_str(format, "format", 3114108242u, 0, 6, &be_const_str_pow); +be_define_const_str(pow, "pow", 1479764693u, 0, 3, &be_const_str_tostring); +be_define_const_str(tostring, "tostring", 2299708645u, 0, 8, NULL); +be_define_const_str(load, "load", 3859241449u, 0, 4, &be_const_str_setrange); +be_define_const_str(setrange, "setrange", 3794019032u, 0, 8, NULL); +be_define_const_str(char, "char", 2823553821u, 0, 4, NULL); +be_define_const_str(acos, "acos", 1006755615u, 0, 4, &be_const_str_deinit); +be_define_const_str(deinit, "deinit", 2345559592u, 0, 6, &be_const_str_dump); +be_define_const_str(dump, "dump", 3663001223u, 0, 4, &be_const_str_sqrt); +be_define_const_str(sqrt, "sqrt", 2112764879u, 0, 4, NULL); +be_define_const_str(fromstring, "fromstring", 610302344u, 0, 10, NULL); +be_define_const_str(opt_eq, "==", 2431966415u, 0, 2, &be_const_str_asin); +be_define_const_str(asin, "asin", 4272848550u, 0, 4, &be_const_str_byte); +be_define_const_str(byte, "byte", 1683620383u, 0, 4, &be_const_str_copy); +be_define_const_str(copy, "copy", 3848464964u, 0, 4, &be_const_str_floor); +be_define_const_str(floor, "floor", 3102149661u, 0, 5, NULL); +be_define_const_str(find, "find", 3186656602u, 0, 4, &be_const_str_var); +be_define_const_str(var, "var", 2317739966u, 64, 3, NULL); +be_define_const_str(opt_add, "+", 772578730u, 0, 1, &be_const_str___lower__); +be_define_const_str(__lower__, "__lower__", 123855590u, 0, 9, &be_const_str_rand); +be_define_const_str(rand, "rand", 2711325910u, 0, 4, &be_const_str_sin); +be_define_const_str(sin, "sin", 3761252941u, 0, 3, NULL); +be_define_const_str(deg, "deg", 3327754271u, 0, 3, &be_const_str_imin); +be_define_const_str(imin, "imin", 2714127864u, 0, 4, &be_const_str_class); +be_define_const_str(class, "class", 2872970239u, 57, 5, &be_const_str_try); be_define_const_str(try, "try", 2887626766u, 68, 3, NULL); -be_define_const_str(range, "range", 4208725202u, 0, 5, &be_const_str_return); +be_define_const_str(allocated, "allocated", 429986098u, 0, 9, &be_const_str_asstring); +be_define_const_str(asstring, "asstring", 1298225088u, 0, 8, &be_const_str_classname); +be_define_const_str(classname, "classname", 1998589948u, 0, 9, NULL); +be_define_const_str(reverse, "reverse", 558918661u, 0, 7, NULL); +be_define_const_str(assert, "assert", 2774883451u, 0, 6, &be_const_str_str); +be_define_const_str(str, "str", 3259748752u, 0, 3, NULL); +be_define_const_str(imax, "imax", 3084515410u, 0, 4, &be_const_str_init); +be_define_const_str(init, "init", 380752755u, 0, 4, &be_const_str_except); +be_define_const_str(except, "except", 950914032u, 69, 6, &be_const_str_return); be_define_const_str(return, "return", 2246981567u, 60, 6, NULL); -be_define_const_str(compile, "compile", 1000265118u, 0, 7, &be_const_str_false); -be_define_const_str(false, "false", 184981848u, 62, 5, NULL); -be_define_const_str(resize, "resize", 3514612129u, 0, 6, NULL); -be_define_const_str(continue, "continue", 2977070660u, 59, 8, NULL); -be_define_const_str(log, "log", 1062293841u, 0, 3, &be_const_str_true); -be_define_const_str(true, "true", 1303515621u, 61, 4, NULL); -be_define_const_str(while, "while", 231090382u, 53, 5, NULL); -be_define_const_str(pow, "pow", 1479764693u, 0, 3, NULL); -be_define_const_str(cos, "cos", 4220379804u, 0, 3, &be_const_str_count); -be_define_const_str(count, "count", 967958004u, 0, 5, &be_const_str_remove); -be_define_const_str(remove, "remove", 3683784189u, 0, 6, NULL); -be_define_const_str(imax, "imax", 3084515410u, 0, 4, &be_const_str_rand); -be_define_const_str(rand, "rand", 2711325910u, 0, 4, NULL); -be_define_const_str(codedump, "codedump", 1786337906u, 0, 8, &be_const_str_deg); -be_define_const_str(deg, "deg", 3327754271u, 0, 3, &be_const_str_keys); -be_define_const_str(keys, "keys", 4182378701u, 0, 4, &be_const_str_setitem); -be_define_const_str(setitem, "setitem", 1554834596u, 0, 7, NULL); -be_define_const_str(def, "def", 3310976652u, 55, 3, &be_const_str_except); -be_define_const_str(except, "except", 950914032u, 69, 6, NULL); -be_define_const_str(classname, "classname", 1998589948u, 0, 9, &be_const_str_isinstance); -be_define_const_str(isinstance, "isinstance", 3669352738u, 0, 10, NULL); +be_define_const_str(opt_neq, "!=", 2428715011u, 0, 2, &be_const_str_hex); +be_define_const_str(hex, "hex", 4273249610u, 0, 3, &be_const_str_resize); +be_define_const_str(resize, "resize", 3514612129u, 0, 6, &be_const_str_as); +be_define_const_str(as, "as", 1579491469u, 67, 2, NULL); +be_define_const_str(__upper__, "__upper__", 3612202883u, 0, 9, &be_const_str_codedump); +be_define_const_str(codedump, "codedump", 1786337906u, 0, 8, &be_const_str_cosh); be_define_const_str(cosh, "cosh", 4099687964u, 0, 4, NULL); +be_define_const_str(abs, "abs", 709362235u, 0, 3, &be_const_str_collect); +be_define_const_str(collect, "collect", 2399039025u, 0, 7, NULL); +be_define_const_str(atan, "atan", 108579519u, 0, 4, &be_const_str_attrdump); +be_define_const_str(attrdump, "attrdump", 1521571304u, 0, 8, &be_const_str_input); +be_define_const_str(input, "input", 4191711099u, 0, 5, NULL); +be_define_const_str(, "", 2166136261u, 0, 0, &be_const_str_compile); +be_define_const_str(compile, "compile", 1000265118u, 0, 7, &be_const_str_iter); +be_define_const_str(iter, "iter", 3124256359u, 0, 4, &be_const_str_lower); +be_define_const_str(lower, "lower", 3038577850u, 0, 5, &be_const_str_number); +be_define_const_str(number, "number", 467038368u, 0, 6, &be_const_str_print); +be_define_const_str(print, "print", 372738696u, 0, 5, NULL); +be_define_const_str(exp, "exp", 1923516200u, 0, 3, &be_const_str_pi); +be_define_const_str(pi, "pi", 1213090802u, 0, 2, NULL); +be_define_const_str(__iterator__, "__iterator__", 3884039703u, 0, 12, &be_const_str_log); +be_define_const_str(log, "log", 1062293841u, 0, 3, &be_const_str_log10); +be_define_const_str(log10, "log10", 2346846000u, 0, 5, NULL); +be_define_const_str(ceil, "ceil", 1659167240u, 0, 4, &be_const_str_get); +be_define_const_str(get, "get", 1410115415u, 0, 3, &be_const_str_tolower); +be_define_const_str(tolower, "tolower", 1042520049u, 0, 7, &be_const_str_while); +be_define_const_str(while, "while", 231090382u, 53, 5, NULL); +be_define_const_str(import, "import", 288002260u, 66, 6, NULL); +be_define_const_str(size, "size", 597743964u, 0, 4, NULL); +be_define_const_str(list, "list", 217798785u, 0, 4, &be_const_str_rad); +be_define_const_str(rad, "rad", 1358899048u, 0, 3, &be_const_str_tan); +be_define_const_str(tan, "tan", 2633446552u, 0, 3, &be_const_str_type); +be_define_const_str(type, "type", 1361572173u, 0, 4, NULL); +be_define_const_str(insert, "insert", 3332609576u, 0, 6, &be_const_str_module); +be_define_const_str(module, "module", 3617558685u, 0, 6, &be_const_str_range); +be_define_const_str(range, "range", 4208725202u, 0, 5, &be_const_str_super); +be_define_const_str(super, "super", 4152230356u, 0, 5, NULL); +be_define_const_str(issubclass, "issubclass", 4078395519u, 0, 10, &be_const_str_def); +be_define_const_str(def, "def", 3310976652u, 55, 3, NULL); +be_define_const_str(classof, "classof", 1796577762u, 0, 7, &be_const_str_sinh); +be_define_const_str(sinh, "sinh", 282220607u, 0, 4, &be_const_str_srand); +be_define_const_str(srand, "srand", 465518633u, 0, 5, NULL); +be_define_const_str(end, "end", 1787721130u, 56, 3, NULL); +be_define_const_str(clear, "clear", 1550717474u, 0, 5, &be_const_str_top); +be_define_const_str(top, "top", 2802900028u, 0, 3, &be_const_str_nil); +be_define_const_str(nil, "nil", 228849900u, 63, 3, NULL); +be_define_const_str(open, "open", 3546203337u, 0, 4, &be_const_str_do); +be_define_const_str(do, "do", 1646057492u, 65, 2, NULL); +be_define_const_str(add, "add", 993596020u, 0, 3, &be_const_str_isinstance); +be_define_const_str(isinstance, "isinstance", 3669352738u, 0, 10, &be_const_str_pop); +be_define_const_str(pop, "pop", 1362321360u, 0, 3, NULL); +be_define_const_str(int, "int", 2515107422u, 0, 3, &be_const_str_keys); +be_define_const_str(keys, "keys", 4182378701u, 0, 4, &be_const_str_elif); +be_define_const_str(elif, "elif", 3232090307u, 51, 4, NULL); +be_define_const_str(item, "item", 2671260646u, 0, 4, &be_const_str_push); +be_define_const_str(push, "push", 2272264157u, 0, 4, NULL); +be_define_const_str(concat, "concat", 4124019837u, 0, 6, &be_const_str_traceback); +be_define_const_str(traceback, "traceback", 3385188109u, 0, 9, &be_const_str_upper); +be_define_const_str(upper, "upper", 176974407u, 0, 5, &be_const_str_true); +be_define_const_str(true, "true", 1303515621u, 61, 4, NULL); +be_define_const_str(continue, "continue", 2977070660u, 59, 8, NULL); +be_define_const_str(else, "else", 3183434736u, 52, 4, NULL); +be_define_const_str(cos, "cos", 4220379804u, 0, 3, &be_const_str_for); +be_define_const_str(for, "for", 2901640080u, 54, 3, NULL); static const bstring* const m_string_table[] = { + (const bstring *)&be_const_str_remove, + (const bstring *)&be_const_str_opt_connect, + (const bstring *)&be_const_str_toupper, + NULL, + (const bstring *)&be_const_str_map, + (const bstring *)&be_const_str_bytes, + (const bstring *)&be_const_str_real, + (const bstring *)&be_const_str_count, + (const bstring *)&be_const_str_format, + (const bstring *)&be_const_str_load, + (const bstring *)&be_const_str_char, (const bstring *)&be_const_str_acos, - NULL, - (const bstring *)&be_const_str_int, - NULL, - (const bstring *)&be_const_str_iter, + (const bstring *)&be_const_str_fromstring, (const bstring *)&be_const_str_opt_eq, - (const bstring *)&be_const_str_floor, (const bstring *)&be_const_str_find, - (const bstring *)&be_const_str___upper__, - (const bstring *)&be_const_str_atan, (const bstring *)&be_const_str_opt_add, NULL, - (const bstring *)&be_const_str_classof, - (const bstring *)&be_const_str_byte, - (const bstring *)&be_const_str_clear, - (const bstring *)&be_const_str_opt_connect, - (const bstring *)&be_const_str_, - (const bstring *)&be_const_str_calldepth, - (const bstring *)&be_const_str___iterator__, - (const bstring *)&be_const_str_dot_p, - (const bstring *)&be_const_str___lower__, - (const bstring *)&be_const_str_char, - (const bstring *)&be_const_str_deinit, - (const bstring *)&be_const_str_srand, - (const bstring *)&be_const_str_imin, - (const bstring *)&be_const_str_tostring, - (const bstring *)&be_const_str_insert, - (const bstring *)&be_const_str_open, + (const bstring *)&be_const_str_deg, (const bstring *)&be_const_str_allocated, - (const bstring *)&be_const_str_attrdump, - (const bstring *)&be_const_str_for, - (const bstring *)&be_const_str_opt_neq, - (const bstring *)&be_const_str_range, - (const bstring *)&be_const_str_compile, - (const bstring *)&be_const_str_resize, - (const bstring *)&be_const_str_continue, - (const bstring *)&be_const_str_log, - (const bstring *)&be_const_str_while, - (const bstring *)&be_const_str_pow, - (const bstring *)&be_const_str_cos, + (const bstring *)&be_const_str_reverse, + (const bstring *)&be_const_str_assert, (const bstring *)&be_const_str_imax, - (const bstring *)&be_const_str_codedump, - (const bstring *)&be_const_str_def, - (const bstring *)&be_const_str_classname, - (const bstring *)&be_const_str_cosh + (const bstring *)&be_const_str_opt_neq, + (const bstring *)&be_const_str___upper__, + (const bstring *)&be_const_str_abs, + (const bstring *)&be_const_str_atan, + (const bstring *)&be_const_str_, + NULL, + (const bstring *)&be_const_str_exp, + (const bstring *)&be_const_str___iterator__, + (const bstring *)&be_const_str_ceil, + (const bstring *)&be_const_str_import, + (const bstring *)&be_const_str_size, + (const bstring *)&be_const_str_list, + (const bstring *)&be_const_str_insert, + (const bstring *)&be_const_str_issubclass, + (const bstring *)&be_const_str_classof, + (const bstring *)&be_const_str_end, + (const bstring *)&be_const_str_clear, + (const bstring *)&be_const_str_open, + (const bstring *)&be_const_str_add, + (const bstring *)&be_const_str_int, + (const bstring *)&be_const_str_item, + (const bstring *)&be_const_str_concat, + (const bstring *)&be_const_str_continue, + (const bstring *)&be_const_str_else, + (const bstring *)&be_const_str_cos }; static const struct bconststrtab m_const_string_table = { - .size = 45, - .count = 90, + .size = 47, + .count = 95, .table = m_string_table }; diff --git a/lib/libesp32/Berry-0.1.10/generate/be_fixed_be_class_bytes.h b/lib/libesp32/Berry-0.1.10/generate/be_fixed_be_class_bytes.h new file mode 100644 index 000000000..44f8eff3e --- /dev/null +++ b/lib/libesp32/Berry-0.1.10/generate/be_fixed_be_class_bytes.h @@ -0,0 +1,33 @@ +#include "be_constobj.h" + +static be_define_const_map_slots(be_class_bytes_map) { + { be_const_key(copy, -1), be_const_func(m_copy) }, + { be_const_key(setitem, -1), be_const_func(m_setitem) }, + { be_const_key(tostring, -1), be_const_func(m_tostring) }, + { be_const_key(item, -1), be_const_func(m_item) }, + { be_const_key(init, 8), be_const_func(m_init) }, + { be_const_key(size, 6), be_const_func(m_size) }, + { be_const_key(opt_connect, 7), be_const_func(m_connect) }, + { be_const_key(opt_add, -1), be_const_func(m_merge) }, + { be_const_key(fromstring, -1), be_const_func(m_fromstring) }, + { be_const_key(opt_eq, 14), be_const_func(m_equal) }, + { be_const_key(get, 1), be_const_func(m_get) }, + { be_const_key(asstring, 5), be_const_func(m_asstring) }, + { be_const_key(add, -1), be_const_func(m_add) }, + { be_const_key(dot_p, -1), be_const_int(0) }, + { be_const_key(clear, -1), be_const_func(m_clear) }, + { be_const_key(opt_neq, 4), be_const_func(m_nequal) }, + { be_const_key(resize, 9), be_const_func(m_resize) }, +}; + +static be_define_const_map( + be_class_bytes_map, + 17 +); + +BE_EXPORT_VARIABLE be_define_const_class( + be_class_bytes, + 1, + NULL, + bytes +); diff --git a/lib/libesp32/Berry-0.1.10/generate/be_fixed_be_class_list.h b/lib/libesp32/Berry-0.1.10/generate/be_fixed_be_class_list.h index 64eaac2ac..98a52b494 100644 --- a/lib/libesp32/Berry-0.1.10/generate/be_fixed_be_class_list.h +++ b/lib/libesp32/Berry-0.1.10/generate/be_fixed_be_class_list.h @@ -1,31 +1,32 @@ #include "be_constobj.h" static be_define_const_map_slots(be_class_list_map) { - { be_const_key(pop, -1), be_const_func(m_pop) }, - { be_const_key(reverse, -1), be_const_func(m_reverse) }, + { be_const_key(concat, -1), be_const_func(m_concat) }, { be_const_key(push, -1), be_const_func(m_push) }, - { be_const_key(init, -1), be_const_func(m_init) }, - { be_const_key(copy, 8), be_const_func(m_copy) }, - { be_const_key(opt_connect, 13), be_const_func(m_connect) }, - { be_const_key(item, -1), be_const_func(m_item) }, - { be_const_key(remove, -1), be_const_func(m_remove) }, - { be_const_key(size, -1), be_const_func(m_size) }, - { be_const_key(resize, 7), be_const_func(m_resize) }, - { be_const_key(opt_add, -1), be_const_func(m_merge) }, - { be_const_key(opt_neq, -1), be_const_func(m_nequal) }, - { be_const_key(setitem, -1), be_const_func(m_setitem) }, - { be_const_key(tostring, -1), be_const_func(m_tostring) }, + { be_const_key(insert, 1), be_const_func(m_insert) }, + { be_const_key(find, -1), be_const_func(m_find) }, + { be_const_key(remove, 9), be_const_func(m_remove) }, { be_const_key(clear, -1), be_const_func(m_clear) }, - { be_const_key(opt_eq, 3), be_const_func(m_equal) }, - { be_const_key(insert, 12), be_const_func(m_insert) }, - { be_const_key(concat, 2), be_const_func(m_concat) }, - { be_const_key(dot_p, -1), be_const_int(0) }, - { be_const_key(iter, 18), be_const_func(m_iter) }, + { be_const_key(size, -1), be_const_func(m_size) }, + { be_const_key(resize, 13), be_const_func(m_resize) }, + { be_const_key(copy, -1), be_const_func(m_copy) }, + { be_const_key(pop, -1), be_const_func(m_pop) }, + { be_const_key(tostring, 3), be_const_func(m_tostring) }, + { be_const_key(opt_eq, -1), be_const_func(m_equal) }, + { be_const_key(init, -1), be_const_func(m_init) }, + { be_const_key(dot_p, 17), be_const_int(0) }, + { be_const_key(setitem, -1), be_const_func(m_setitem) }, + { be_const_key(opt_connect, 4), be_const_func(m_connect) }, + { be_const_key(opt_neq, -1), be_const_func(m_nequal) }, + { be_const_key(opt_add, 18), be_const_func(m_merge) }, + { be_const_key(iter, 20), be_const_func(m_iter) }, + { be_const_key(item, -1), be_const_func(m_item) }, + { be_const_key(reverse, -1), be_const_func(m_reverse) }, }; static be_define_const_map( be_class_list_map, - 20 + 21 ); BE_EXPORT_VARIABLE be_define_const_class( diff --git a/lib/libesp32/Berry-0.1.10/generate/be_fixed_m_builtin.h b/lib/libesp32/Berry-0.1.10/generate/be_fixed_m_builtin.h index 4b6d13344..423be6100 100644 --- a/lib/libesp32/Berry-0.1.10/generate/be_fixed_m_builtin.h +++ b/lib/libesp32/Berry-0.1.10/generate/be_fixed_m_builtin.h @@ -1,37 +1,39 @@ #include "be_constobj.h" static be_define_const_map_slots(m_builtin_map) { - { be_const_key(map, 11), be_const_int(10) }, - { be_const_key(str, 14), be_const_int(18) }, - { be_const_key(int, -1), be_const_int(6) }, - { be_const_key(type, -1), be_const_int(20) }, - { be_const_key(assert, -1), be_const_int(1) }, - { be_const_key(issubclass, -1), be_const_int(8) }, - { be_const_key(number, 7), be_const_int(12) }, - { be_const_key(compile, 8), be_const_int(4) }, - { be_const_key(module, 18), be_const_int(11) }, - { be_const_key(input, 5), be_const_int(5) }, - { be_const_key(super, -1), be_const_int(19) }, - { be_const_key(classof, -1), be_const_int(3) }, - { be_const_key(open, 13), be_const_int(13) }, + { be_const_key(print, 19), be_const_int(15) }, + { be_const_key(isinstance, -1), be_const_int(8) }, + { be_const_key(classname, -1), be_const_int(3) }, + { be_const_key(module, -1), be_const_int(12) }, + { be_const_key(size, -1), be_const_int(18) }, + { be_const_key(type, 9), be_const_int(21) }, + { be_const_key(compile, -1), be_const_int(5) }, + { be_const_key(open, -1), be_const_int(14) }, + { be_const_key(real, -1), be_const_int(17) }, { be_const_key(__iterator__, -1), be_const_int(0) }, - { be_const_key(real, 10), be_const_int(16) }, - { be_const_key(list, 20), be_const_int(9) }, - { be_const_key(isinstance, -1), be_const_int(7) }, - { be_const_key(range, -1), be_const_int(15) }, - { be_const_key(size, -1), be_const_int(17) }, - { be_const_key(classname, -1), be_const_int(2) }, - { be_const_key(print, -1), be_const_int(14) }, + { be_const_key(super, -1), be_const_int(20) }, + { be_const_key(issubclass, -1), be_const_int(9) }, + { be_const_key(classof, -1), be_const_int(4) }, + { be_const_key(map, 8), be_const_int(11) }, + { be_const_key(int, 2), be_const_int(7) }, + { be_const_key(input, 3), be_const_int(6) }, + { be_const_key(number, -1), be_const_int(13) }, + { be_const_key(list, 7), be_const_int(10) }, + { be_const_key(str, 1), be_const_int(19) }, + { be_const_key(range, -1), be_const_int(16) }, + { be_const_key(bytes, -1), be_const_int(2) }, + { be_const_key(assert, -1), be_const_int(1) }, }; static be_define_const_map( m_builtin_map, - 21 + 22 ); static const bvalue __vlist_array[] = { be_const_func(l_iterator), be_const_func(l_assert), + be_const_class(be_class_bytes), be_const_func(l_classname), be_const_func(l_classof), be_const_func(l_compile), @@ -56,5 +58,5 @@ static const bvalue __vlist_array[] = { static be_define_const_vector( m_builtin_vector, __vlist_array, - 21 + 22 ); diff --git a/lib/libesp32/Berry-0.1.10/generate/be_fixed_os_path.h b/lib/libesp32/Berry-0.1.10/generate/be_fixed_os_path.h deleted file mode 100644 index fc4f5edbc..000000000 --- a/lib/libesp32/Berry-0.1.10/generate/be_fixed_os_path.h +++ /dev/null @@ -1,20 +0,0 @@ -#include "be_constobj.h" - -static be_define_const_map_slots(m_libpath_map) { - { be_const_key(isdir, -1), be_const_func(m_path_isdir) }, - { be_const_key(join, 2), be_const_func(m_path_join) }, - { be_const_key(exists, -1), be_const_func(m_path_exists) }, - { be_const_key(split, -1), be_const_func(m_path_split) }, - { be_const_key(splitext, -1), be_const_func(m_path_splitext) }, - { be_const_key(isfile, -1), be_const_func(m_path_isfile) }, -}; - -static be_define_const_map( - m_libpath_map, - 6 -); - -static be_define_const_module( - m_libpath, - "path" -); diff --git a/lib/libesp32/Berry-0.1.10/src/be_baselib.c b/lib/libesp32/Berry-0.1.10/src/be_baselib.c index f0d7e0147..d1bfd83b1 100644 --- a/lib/libesp32/Berry-0.1.10/src/be_baselib.c +++ b/lib/libesp32/Berry-0.1.10/src/be_baselib.c @@ -313,6 +313,7 @@ void be_load_baselib(bvm *vm) extern const bclass be_class_list; extern const bclass be_class_map; extern const bclass be_class_range; +extern const bclass be_class_bytes; extern int be_nfunc_open(bvm *vm); /* @const_object_info_begin vartab m_builtin (scope: local) { @@ -337,6 +338,7 @@ vartab m_builtin (scope: local) { list, class(be_class_list) map, class(be_class_map) range, class(be_class_range) + bytes, class(be_class_bytes) } @const_object_info_end */ #include "../generate/be_fixed_m_builtin.h" diff --git a/lib/libesp32/Berry-0.1.10/src/be_byteslib.c b/lib/libesp32/Berry-0.1.10/src/be_byteslib.c new file mode 100644 index 000000000..7217c7d75 --- /dev/null +++ b/lib/libesp32/Berry-0.1.10/src/be_byteslib.c @@ -0,0 +1,668 @@ +/******************************************************************** +** Copyright (c) 2018-2020 Guan Wenliang - Stephan Hadinger +** This file is part of the Berry default interpreter. +** skiars@qq.com, https://github.com/Skiars/berry +** See Copyright Notice in the LICENSE file or at +** https://github.com/Skiars/berry/blob/master/LICENSE +********************************************************************/ +#include "be_object.h" +#include "be_string.h" +#include "be_strlib.h" +#include "be_list.h" +#include "be_func.h" +#include "be_exec.h" +#include "be_vm.h" +#include "be_mem.h" +#include +#include + +#define BYTES_DEFAULT_SIZE 28 // default pre-reserved size for buffer (keep 4 bytes for len/size) +#define BYTES_MAX_SIZE (32*1024) // max 32Kb +#define BYTES_OVERHEAD 4 // bytes overhead to be added when allocating (used to store len and size) +#define BYTES_HEADROOM 8 // keep a natural headroom of 8 bytes when resizing + +typedef struct buf_impl { + uint16_t size; // size in bytes of the buffer + uint16_t len; // current size of the data in buffer. Invariant: len <= size + uint8_t buf[]; // the actual data +} buf_impl; + +/******************************************************************** +** Buffer low-level implementation +** +** Extracted from Tasmota SBuffer lib +********************************************************************/ +static inline uint8_t* buf_get_buf(buf_impl* buf) { + return &buf->buf[0]; +} + +// shrink or increase. If increase, fill with zeores. Cannot go beyond `size` +static void buf_set_len(buf_impl* buf, const size_t len) { + uint16_t old_len = buf->len; + buf->len = (len <= buf->size) ? len : buf->size; + if (old_len < buf->len) { + memset((void*) &buf->buf[old_len], 0, buf->len - old_len); + } +} + + +static void buf_set1(buf_impl* buf, const size_t offset, const uint8_t data) { + if (offset < buf->len) { + buf->buf[offset] = data; + } +} +static size_t buf_add1(buf_impl* buf, const uint8_t data) { // append 8 bits value + if (buf->len < buf->size) { // do we have room for 1 byte + buf->buf[buf->len++] = data; + } + return buf->len; +} +static size_t buf_add2_le(buf_impl* buf, const uint16_t data) { // append 16 bits value + if (buf->len < buf->size - 1) { // do we have room for 2 bytes + buf->buf[buf->len++] = data; + buf->buf[buf->len++] = data >> 8; + } + return buf->len; +} +static size_t buf_add2_be(buf_impl* buf, const uint16_t data) { // append 16 bits value + if (buf->len < buf->size - 1) { // do we have room for 2 bytes + buf->buf[buf->len++] = data >> 8; + buf->buf[buf->len++] = data; + } + return buf->len; +} +static size_t buf_add4_le(buf_impl* buf, const uint32_t data) { // append 32 bits value + if (buf->len < buf->size - 3) { // do we have room for 4 bytes + buf->buf[buf->len++] = data; + buf->buf[buf->len++] = data >> 8; + buf->buf[buf->len++] = data >> 16; + buf->buf[buf->len++] = data >> 24; + } + return buf->len; +} +size_t buf_add4_be(buf_impl* buf, const uint32_t data) { // append 32 bits value + if (buf->len < buf->size - 3) { // do we have room for 4 bytes + buf->buf[buf->len++] = data >> 24; + buf->buf[buf->len++] = data >> 16; + buf->buf[buf->len++] = data >> 8; + buf->buf[buf->len++] = data; + } + return buf->len; +} + +static size_t buf_add_buf(buf_impl* buf, buf_impl* buf2) { + if (buf->len + buf2->len <= buf->size) { + for (uint32_t i = 0; i < buf2->len; i++) { + buf->buf[buf->len++] = buf2->buf[i]; + } + } + return buf->len; +} + +static uint8_t buf_get1(buf_impl* buf, int offset) { + if ((offset >= 0) && (offset < buf->len)) { + return buf->buf[offset]; + } + return 0; +} +static uint16_t buf_get2_le(buf_impl* buf, int offset) { + if ((offset >= 0) && (offset < buf->len - 1)) { + return buf->buf[offset] | (buf->buf[offset+1] << 8); + } + return 0; +} +static uint16_t buf_get2_be(buf_impl* buf, int offset) { + if (offset < buf->len - 1) { + return buf->buf[offset+1] | (buf->buf[offset] << 8); + } + return 0; +} +static uint32_t buf_get4_le(buf_impl* buf, int offset) { + if ((offset >= 0) && (offset < buf->len - 3)) { + return buf->buf[offset] | (buf->buf[offset+1] << 8) | + (buf->buf[offset+2] << 16) | (buf->buf[offset+3] << 24); + } + return 0; +} +static uint32_t buf_get4_be(buf_impl* buf, int offset) { + if (offset < buf->len - 3) { + return buf->buf[offset+3] | (buf->buf[offset+2] << 8) | + (buf->buf[offset+1] << 16) | (buf->buf[offset] << 24); + } + return 0; +} + +// nullptr accepted +static bbool buf_equals(buf_impl* buf1, buf_impl* buf2) { + if (buf1 == buf2) { return btrue; } + if (!buf1 || !buf2) { return bfalse; } // at least one buf is not empty + // we know that both buf1 and buf2 are non-null + if (buf1->len != buf2->len) { return bfalse; } + size_t len = buf1->len; + for (uint32_t i=0; i= 'A' && chr <= 'F') { rVal = chr + 10 - 'A'; } + else if (chr >= 'a' && chr <= 'f') { rVal = chr + 10 - 'a'; } + return rVal; +} +// does not check if there is enough room before hand, truncated if buffer too small +static void buf_add_hex(buf_impl* buf, const char *hex, size_t len) { + uint8_t val; + for (; len > 1; len -= 2) { + val = asc2byte(*hex++) << 4; + val |= asc2byte(*hex++); + buf_add1(buf, val); + } +} + +/******************************************************************** +** Wrapping into lib +********************************************************************/ +// typedef int (*bntvfunc)(bvm*); /* native function pointer */ +int free_bytes_buf(bvm* vm) { + int argc = be_top(vm); + if (argc > 0) { + buf_impl * buf = (buf_impl*) be_tocomptr(vm, 1); + if (buf != NULL) { + be_os_free(buf); + } + } + be_return_nil(vm); +} + +buf_impl * bytes_alloc(int32_t size) +{ + if (size < 4) { size = 4; } + if (size > BYTES_MAX_SIZE) { size = BYTES_MAX_SIZE; } + buf_impl * next = (buf_impl*) be_os_malloc(size + BYTES_OVERHEAD); + next->size = size; + next->len = 0; + return next; +} + +/* allocate a new `bytes` object with pre-allocated size */ +static void bytes_new_object(bvm *vm, size_t size) +{ + be_getglobal(vm, "bytes"); /* eventually change with be_getbuiltin */ + be_call(vm, 0); /* stack has only instance */ + be_getmember(vm, -1, "init"); + be_pushvalue(vm, -2); + be_pushint(vm, size); /* stack: instance, init func, instance, size */ + be_call(vm, 2); /* stack: instance, ret, instance, size */ + be_pop(vm, 3); /* remove ret, instance, size */ +} + +static int m_init(bvm *vm) +{ + int argc = be_top(vm); + int size = BYTES_DEFAULT_SIZE; + const char * hex_in = NULL; + if (argc > 1 && be_isint(vm, 2)) { + int new_size = be_toint(vm, 2) + BYTES_HEADROOM; + if (new_size > size) { + size = new_size; + } + } else if (argc > 1 && be_isstring(vm, 2)) { + hex_in = be_tostring(vm, 2); + if (hex_in) { + size = strlen(hex_in) / 2 + BYTES_HEADROOM; // allocate headroom + } + } + buf_impl * buf = bytes_alloc(size); + if (!buf) { + be_throw(vm, BE_MALLOC_FAIL); + } + + if (hex_in) { + buf_add_hex(buf, hex_in, strlen(hex_in)); + } + be_newcomobj(vm, buf, &free_bytes_buf); + be_setmember(vm, 1, ".p"); + be_return_nil(vm); +} + +/* grow or shrink to the exact value */ +/* stack item 1 must contain the instance */ +static buf_impl * _bytes_resize(bvm *vm, buf_impl * buf, size_t new_size) { + buf_impl * new_buf = bytes_alloc(new_size); + if (!new_buf) { + be_throw(vm, BE_MALLOC_FAIL); + } + memmove(buf_get_buf(new_buf), buf_get_buf(buf), buf->len); + new_buf->len = buf->len; + /* replace the .p attribute */ + be_newcomobj(vm, new_buf, &free_bytes_buf); + be_setmember(vm, 1, ".p"); + be_pop(vm, 1); /* remove comobj from stack */ + /* the old buffer will be garbage collected later */ + return new_buf; +} + +/* grow if needed but don't shrink */ +/* if grow, then add some headroom */ +/* stack item 1 must contain the instance */ +static buf_impl * bytes_resize(bvm *vm, buf_impl * buf, size_t new_size) { + if (buf->size >= new_size) { return buf; } /* no resize needed */ + return _bytes_resize(vm, buf, new_size + BYTES_HEADROOM); +} + +static buf_impl * bytes_check_data(bvm *vm, size_t add_size) { + be_getmember(vm, 1, ".p"); + buf_impl * buf = be_tocomptr(vm, -1); + be_pop(vm, 1); /* remove member from stack */ + /* check if the `size` is big enough */ + if (buf->len + add_size > buf->size) { + /* it does not fit so we need to realocate the buffer */ + buf = bytes_resize(vm, buf, buf->len + add_size); + } + return buf; +} + +static size_t tohex(char * out, size_t outsz, const uint8_t * in, size_t insz) { + static const char * hex = "0123456789ABCDEF"; + const uint8_t * pin = in; + char * pout = out; + for (; pin < in + insz; pout += 2, pin++) { + pout[0] = hex[((*pin)>>4) & 0xF]; + pout[1] = hex[ (*pin) & 0xF]; + if (pout + 3 > out + outsz) { break; } /* check overflow */ + } + pout[0] = 0; /* terminating Nul char */ + return pout - out; +} + +static int m_tostring(bvm *vm) +{ + buf_impl * buf = bytes_check_data(vm, 0); + size_t len = buf->len; + size_t hex_len = len * 2 + 5 + 2 + 2 + 1; /* reserve size for `bytes("")\0` - 9 chars */ + + char * hex_out = be_pushbuffer(vm, hex_len); + size_t l = strlcpy(hex_out, "bytes('", hex_len); + l += tohex(&hex_out[l], hex_len - l, buf_get_buf(buf), buf->len); + l += strlcpy(&hex_out[l], "')", hex_len - l); + + be_pushnstring(vm, hex_out, l); /* make escape string from buffer */ + be_remove(vm, -2); /* remove buffer */ + be_return(vm); +} + +/* + * Copy the buffer into a string without any changes + */ +static int m_asstring(bvm *vm) +{ + buf_impl * buf = bytes_check_data(vm, 0); + be_pushnstring(vm, (const char*) buf_get_buf(buf), buf->len); + be_return(vm); +} +static int m_fromstring(bvm *vm) +{ + int argc = be_top(vm); + if (argc >= 2 && be_isstring(vm, 2)) { + const char *s = be_tostring(vm, 2); + size_t len = strlen(s); + buf_impl * buf = bytes_check_data(vm, 0); + buf = bytes_resize(vm, buf, len); /* resize if needed */ + if (len > buf->size) { len = buf->size; } /* avoid overflow */ + memmove(buf_get_buf(buf), s, len); + buf->len = len; + be_pop(vm, 1); /* remove arg to leave instance */ + be_return(vm); + } + be_raise(vm, "type_error", "operand must be a string"); + be_return_nil(vm); +} + +/* + * Add an int made of 1, 2 or 4 bytes, in little or big endian + * `add(value:int[, size:int = 1]) -> instance` + * + * size: may be 1, 2, 4 (little endian), or -1, -2, -4 (big endian) + * obvisouly -1 is idntical to 1 + * size==0 does nothing + */ +static int m_add(bvm *vm) +{ + int argc = be_top(vm); + buf_impl * buf = bytes_check_data(vm, 4); /* we reserve 4 bytes anyways */ + if (argc >= 2 && be_isint(vm, 2)) { + int32_t v = be_toint(vm, 2); + int vsize = 1; + if (argc >= 3 && be_isint(vm, 3)) { + vsize = be_toint(vm, 3); + } + switch (vsize) { + case 0: break; + case -1: /* fallback below */ + case 1: buf_add1(buf, v); break; + case 2: buf_add2_le(buf, v); break; + case 4: buf_add4_le(buf, v); break; + case -2: buf_add2_be(buf, v); break; + case -4: buf_add4_be(buf, v); break; + default: be_raise(vm, "type_error", "size must be -4, -2, -1, 0, 1, 2 or 4."); + } + be_pop(vm, argc - 1); + be_return(vm); + } + be_return_nil(vm); +} + +/* + * Get an int made of 1, 2 or 4 bytes, in little or big endian + * `get(index:int[, size:int = 1]) -> instance` + * + * size: may be 1, 2, 4 (little endian), or -1, -2, -4 (big endian) + * obvisouly -1 is idntical to 1 + * 0 returns nil + */ +static int m_get(bvm *vm) +{ + int argc = be_top(vm); + buf_impl * buf = bytes_check_data(vm, 0); /* we reserve 4 bytes anyways */ + if (argc >=2 && be_isint(vm, 2)) { + int32_t idx = be_toint(vm, 2); + int vsize = 1; + if (argc >= 3 && be_isint(vm, 3)) { + vsize = be_toint(vm, 3); + } + int ret = 0; + switch (vsize) { + case 0: break; + case -1: /* fallback below */ + case 1: ret = buf_get1(buf, idx); break; + case 2: ret = buf_get2_le(buf, idx); break; + case 4: ret = buf_get4_le(buf, idx); break; + case -2: ret = buf_get2_be(buf, idx); break; + case -4: ret = buf_get4_be(buf, idx); break; + default: be_raise(vm, "type_error", "size must be -4, -2, -1, 0, 1, 2 or 4."); + } + be_pop(vm, argc - 1); + if (vsize != 0) { + be_pushint(vm, ret); + } else { + be_pushnil(vm); + } + be_return(vm); + } + be_return_nil(vm); +} + + +static int m_setitem(bvm *vm) +{ + int argc = be_top(vm); + buf_impl * buf = bytes_check_data(vm, 0); /* we reserve 4 bytes anyways */ + if (argc >=3 && be_isint(vm, 2) && be_isint(vm, 3)) { + int index = be_toint(vm, 2); + int val = be_toint(vm, 3); + if (index >= 0 && index < buf->len) { + buf_set1(buf, index, val); + be_return_nil(vm); + } + } + be_raise(vm, "index_error", "bytes index out of range or value non int"); + be_return_nil(vm); +} + +static int m_item(bvm *vm) +{ + int argc = be_top(vm); + buf_impl * buf = bytes_check_data(vm, 0); /* we reserve 4 bytes anyways */ + if (argc >=2 && be_isint(vm, 2)) { + int index = be_toint(vm,2); + if (index >= 0 && index < buf->len) { + be_pushint(vm, buf_get1(buf, index)); + be_return(vm); + } + } + if (argc >= 2 && be_isinstance(vm, 2)) { + const char *cname = be_classname(vm, 2); + if (!strcmp(cname, "range")) { + bint lower, upper; + bint size = buf->len; + /* get index range */ + be_getmember(vm, 2, "__lower__"); + lower = be_toint(vm, -1); + be_pop(vm, 1); + be_getmember(vm, 2, "__upper__"); + upper = be_toint(vm, -1); + be_pop(vm, 1); + /* protection scope */ + upper = upper < size ? upper : size - 1; + lower = lower < 0 ? 0 : lower; + /* construction result list instance */ + bytes_new_object(vm, upper > lower ? upper-lower : 0); + be_getmember(vm, -1, ".p"); + buf_impl * buf2 = be_tocomptr(vm, -1); + be_pop(vm, 1); /* remove .p and leave bytes instance */ + for (; lower <= upper; ++lower) { + buf_add1(buf2, buf->buf[lower]); + } + be_return(vm); + } + } + be_raise(vm, "index_error", "bytes index out of range"); + be_return_nil(vm); +} + +static int m_size(bvm *vm) +{ + buf_impl * buf = bytes_check_data(vm, 0); + be_pushint(vm, buf->len); + be_return(vm); +} + +static int m_resize(bvm *vm) +{ + int argc = be_top(vm); + be_getmember(vm, 1, ".p"); + buf_impl * buf = be_tocomptr(vm, -1); + be_pop(vm, 1); + + if (argc <= 1 || !be_isint(vm, 2)) { + be_raise(vm, "type_error", "size must be of type 'int'"); + } + int new_len = be_toint(vm, 2); + if (new_len < 0) { + new_len = 0; + } + + buf = bytes_resize(vm, buf, new_len); + buf_set_len(buf, new_len); + be_pop(vm, 1); + be_return(vm); +} + +static int m_clear(bvm *vm) +{ + buf_impl * buf = bytes_check_data(vm, 0); + buf->len = 0; + be_return_nil(vm); +} + +static int m_merge(bvm *vm) +{ + int argc = be_top(vm); + buf_impl * buf1 = bytes_check_data(vm, 0); /* no resize yet */ + if (argc >= 2 && be_isinstance(vm, 2)) { + be_getglobal(vm, "bytes"); /* get the bytes class */ /* TODO eventually replace with be_getbuiltin */ + if (be_isderived(vm, 2)) { + be_getmember(vm, 2, ".p"); + buf_impl * buf2 = be_tocomptr(vm, -1); + be_pop(vm, 4); /* remove class, member, and 2 operands */ + + /* allocate new object */ + bytes_new_object(vm, buf1->len + buf2->len); + be_getmember(vm, -1, ".p"); + /* .p is on top of stack, then instance */ + buf_impl * buf3 = be_tocomptr(vm, -1); + be_pop(vm, 1); + buf_add_buf(buf3, buf1); + buf_add_buf(buf3, buf2); + + be_return(vm); /* return self */ + } + } + be_raise(vm, "type_error", "operand must be bytes"); + be_return_nil(vm); /* return self */ +} + +static int m_copy(bvm *vm) +{ + buf_impl * buf1 = bytes_check_data(vm, 0); /* no resize */ + bytes_new_object(vm, buf1->len); + be_getmember(vm, -1, ".p"); + buf_impl * buf2 = be_tocomptr(vm, -1); + be_pop(vm, 1); + buf_add_buf(buf2, buf1); + be_return(vm); /* return self */ +} + +/* accept bytes or int as operand */ +static int m_connect(bvm *vm) +{ + int argc = be_top(vm); + buf_impl * buf1 = bytes_check_data(vm, 0); /* don't resize yet */ + if (argc >= 2 && (be_isinstance(vm, 2) || be_isint(vm, 2))) { + if (be_isint(vm, 2)) { + buf1 = bytes_resize(vm, buf1, buf1->len + 1); /* resize */ + buf_add1(buf1, be_toint(vm, 2)); + be_pop(vm, 1); /* remove operand */ + be_return(vm); /* return self */ + } else { + be_getglobal(vm, "bytes"); /* get the bytes class */ /* TODO eventually replace with be_getbuiltin */ + if (be_isderived(vm, 2)) { + be_getmember(vm, 2, ".p"); + buf_impl * buf2 = be_tocomptr(vm, -1); + buf1 = bytes_resize(vm, buf1, buf1->len + buf2->len); /* resize buf1 for total size */ + buf_add_buf(buf1, buf2); + be_pop(vm, 3); /* remove class, member, and last operand */ + be_return(vm); /* return self */ + } + } + } + be_raise(vm, "type_error", "operand must be bytes or int"); + be_return_nil(vm); /* return self */ +} + +static int bytes_equal(bvm *vm, bbool iseq) +{ + be_getmember(vm, 1, ".p"); + buf_impl * buf1 = be_tocomptr(vm, -1); + be_pop(vm, 1); + + be_getmember(vm, 2, ".p"); + buf_impl * buf2 = be_tocomptr(vm, -1); + be_pop(vm, 1); + + bbool ret; + if (buf_equals(buf1, buf2)) { + ret = iseq; + } else { + ret = !iseq; + } + be_pushbool(vm, ret); + be_return(vm); +} + +static int m_equal(bvm *vm) +{ + return bytes_equal(vm, btrue); +} + +static int m_nequal(bvm *vm) +{ + return bytes_equal(vm, bfalse); +} + +/* + * External API + */ +BERRY_API void be_pushbytes(bvm *vm, const void * bytes, size_t len) +{ + bytes_new_object(vm, len); + be_getmember(vm, -1, ".p"); + buf_impl * buf = be_tocomptr(vm, -1); + be_pop(vm, 1); /* remove .p1 and leave instance */ + if (len > buf->size) { len = buf->size; } /* double check if the buffer allocated was smaller */ + memmove((void*)buf_get_buf(buf), bytes, len); + buf->len = len; + /* bytes instance is on top of stack */ +} + +BERRY_API const void *be_tobytes(bvm *vm, int rel_index, size_t *len) +{ + int index = be_absindex(vm, rel_index); + if (be_isinstance(vm, index)) { + be_getglobal(vm, "bytes"); /* get the bytes class */ /* TODO eventually replace with be_getbuiltin */ + if (be_isderived(vm, index)) { + be_getmember(vm, index, ".p"); + buf_impl * buf = be_tocomptr(vm, -1); + be_pop(vm, 2); /* class and .p */ + if (len) { *len = buf->len; } + return (void*) buf_get_buf(buf); + } else { + be_pop(vm, 1); /* remove class */ + } + } + if (len) { *len = 0; } + return NULL; +} + +#if !BE_USE_PRECOMPILED_OBJECT +void be_load_byteslib(bvm *vm) +{ + static const bnfuncinfo members[] = { + { ".p", NULL }, + { "init", m_init }, + { "tostring", m_tostring }, + { "asstring", m_asstring }, + { "fromstring", m_fromstring }, + { "add", m_add }, + { "get", m_get }, + { "item", m_item }, + { "setitem", m_setitem }, + { "size", m_size }, + { "resize", m_resize }, + { "clear", m_clear }, + { "copy", m_copy }, + { "+", m_merge }, + { "..", m_connect }, + { "==", m_equal }, + { "!=", m_nequal }, + { NULL, NULL } + }; + be_regclass(vm, "bytes", members); +} +#else +/* @const_object_info_begin +class be_class_bytes (scope: global, name: bytes) { + .p, var + init, func(m_init) + tostring, func(m_tostring) + asstring, func(m_asstring) + fromstring, func(m_fromstring) + add, func(m_add) + get, func(m_get) + item, func(m_item) + setitem, func(m_setitem) + size, func(m_size) + resize, func(m_resize) + clear, func(m_clear) + copy, func(m_copy) + +, func(m_merge) + .., func(m_connect) + ==, func(m_equal) + !=, func(m_nequal) +} +@const_object_info_end */ +#include "../generate/be_fixed_be_class_bytes.h" +#endif diff --git a/lib/libesp32/Berry-0.1.10/src/be_libs.c b/lib/libesp32/Berry-0.1.10/src/be_libs.c index bf95f5dad..5dc77ea85 100644 --- a/lib/libesp32/Berry-0.1.10/src/be_libs.c +++ b/lib/libesp32/Berry-0.1.10/src/be_libs.c @@ -13,6 +13,9 @@ extern void be_load_maplib(bvm *vm); extern void be_load_rangelib(bvm *vm); extern void be_load_filelib(bvm *vm); +extern void be_load_tasmota_ntvlib(bvm *vm); +extern void be_load_wirelib(bvm *vm); + void be_loadlibs(bvm *vm) { be_load_baselib(vm); @@ -21,5 +24,8 @@ void be_loadlibs(bvm *vm) be_load_maplib(vm); be_load_rangelib(vm); be_load_filelib(vm); + be_load_byteslib(vm); #endif + be_load_tasmota_ntvlib(vm); + be_load_wirelib(vm); } diff --git a/lib/libesp32/Berry-0.1.10/src/be_listlib.c b/lib/libesp32/Berry-0.1.10/src/be_listlib.c index a46c89e21..3733c78c0 100644 --- a/lib/libesp32/Berry-0.1.10/src/be_listlib.c +++ b/lib/libesp32/Berry-0.1.10/src/be_listlib.c @@ -207,6 +207,19 @@ static int m_item(bvm *vm) be_return_nil(vm); } +static int m_find(bvm *vm) +{ + be_getmember(vm, 1, ".p"); + list_check_data(vm, 2); + if (be_isint(vm, 2)) { + be_pushvalue(vm, 2); + if (be_getindex(vm, -2)) { + be_return(vm); + } + } + be_return_nil(vm); +} + static int m_setitem(bvm *vm) { be_getmember(vm, 1, ".p"); @@ -291,13 +304,15 @@ static int m_merge(bvm *vm) { int argc = be_top(vm); if (argc >= 2) { + be_newobject(vm, "list"); /* stack contains instance and .p */ be_getmember(vm, 1, ".p"); + be_data_merge(vm, -2); be_getmember(vm, 2, ".p"); if (!be_islist(vm, -1)) { be_raise(vm, "type_error", "operand must be a list"); } - be_data_merge(vm, -2); - be_pop(vm, argc + 1); + be_data_merge(vm, -3); + be_pop(vm, 3); } be_return(vm); /* return self */ } @@ -423,6 +438,7 @@ void be_load_listlib(bvm *vm) { "insert", m_insert }, { "remove", m_remove }, { "item", m_item }, + { "find", m_find }, { "setitem", m_setitem }, { "size", m_size }, { "resize", m_resize }, @@ -450,6 +466,7 @@ class be_class_list (scope: global, name: list) { insert, func(m_insert) remove, func(m_remove) item, func(m_item) + find, func(m_find) setitem, func(m_setitem) size, func(m_size) resize, func(m_resize) diff --git a/lib/libesp32/Berry-0.1.10/src/be_vm.c b/lib/libesp32/Berry-0.1.10/src/be_vm.c index fd45cabfb..ec786aa09 100644 --- a/lib/libesp32/Berry-0.1.10/src/be_vm.c +++ b/lib/libesp32/Berry-0.1.10/src/be_vm.c @@ -1060,4 +1060,4 @@ BERRY_API void be_set_obs_hook(bvm *vm, beobshook hook) #if BE_USE_OBSERVABILITY_HOOK vm->obshook = hook; #endif -} \ No newline at end of file +} diff --git a/lib/libesp32/Berry-0.1.10/src/berry.h b/lib/libesp32/Berry-0.1.10/src/berry.h index 91bc652a4..6bcdbc556 100644 --- a/lib/libesp32/Berry-0.1.10/src/berry.h +++ b/lib/libesp32/Berry-0.1.10/src/berry.h @@ -429,6 +429,10 @@ BERRY_API int be_savecode(bvm *vm, const char *name); BERRY_API void be_module_path(bvm *vm); BERRY_API void be_module_path_set(bvm *vm, const char *path); +/* bytes operations */ +BERRY_API void be_pushbytes(bvm *vm, const void *buf, size_t len); +BERRY_API const void *be_tobytes(bvm *vm, int index, size_t *len); + /* registry operation */ BERRY_API int be_register(bvm *vm, int index); BERRY_API void be_unregister(bvm *vm, int id); 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 new file mode 100644 index 000000000..444133852 --- /dev/null +++ b/lib/libesp32/Berry-0.1.10/src/port/be_energylib.c @@ -0,0 +1,26 @@ +/******************************************************************** + * Tasmota lib + * + * To use: `import power` + * + * read power values + *******************************************************************/ +#include "be_object.h" + +extern int b_nrg_read(bvm *vm); + +// #if !BE_USE_PRECOMPILED_OBJECT +#if 1 // TODO we will do pre-compiled later +be_native_module_attr_table(energy) { + be_native_module_function("read", b_nrg_read), +}; + +be_define_native_module(energy, NULL); +#else +/* @const_object_info_begin +module tasmota (scope: global, depend: 1) { + getfreeheap, func(l_getFreeHeap) +} +@const_object_info_end */ +#include "../generate/be_fixed_tasmota.h" +#endif diff --git a/lib/libesp32/Berry-0.1.10/src/port/be_modtab.c b/lib/libesp32/Berry-0.1.10/src/port/be_modtab.c index b3e763fec..fa3f09479 100644 --- a/lib/libesp32/Berry-0.1.10/src/port/be_modtab.c +++ b/lib/libesp32/Berry-0.1.10/src/port/be_modtab.c @@ -20,8 +20,9 @@ be_extern_native_module(debug); be_extern_native_module(gc); /* Tasmota specific */ -be_extern_native_module(tasmota_ntv); +// be_extern_native_module(tasmota_ntv); be_extern_native_module(wire); +be_extern_native_module(energy); /* user-defined modules declare start */ @@ -56,8 +57,7 @@ BERRY_LOCAL const bntvmodule* const be_module_table[] = { #endif /* user-defined modules register start */ - &be_native_module(tasmota_ntv), - &be_native_module(wire), + &be_native_module(energy), /* user-defined modules register end */ NULL /* do not remove */ 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 bd484a9da..17b09a219 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 @@ -13,6 +13,7 @@ extern int l_millis(bvm *vm); extern int l_timereached(bvm *vm); extern int l_yield(bvm *vm); extern int l_delay(bvm *vm); +extern int l_scaleuint(bvm *vm); extern int l_respCmnd(bvm *vm); extern int l_respCmndStr(bvm *vm); @@ -22,33 +23,49 @@ extern int l_respCmndFailed(bvm *vm); extern int l_resolveCmnd(bvm *vm); extern int l_getlight(bvm *vm); +extern int l_getpower(bvm *vm); +extern int l_setlight(bvm *vm); +extern int l_setpower(bvm *vm); // #if !BE_USE_PRECOMPILED_OBJECT #if 1 // TODO we will do pre-compiled later +// Class definition +void be_load_tasmota_ntvlib(bvm *vm) +{ + static const bnfuncinfo members[] = { + { "_op", NULL }, + { "_opf", NULL }, + { "_operators", NULL }, + { "_rules", NULL }, + { "_timers", NULL }, + { "_cmd", NULL }, + { "getfreeheap", l_getFreeHeap }, + { "publish", l_publish }, + { "cmd", l_cmd }, + { "getoption", l_getoption }, + { "millis", l_millis }, + { "timereached", l_timereached }, + { "yield", l_yield }, + { "delay", l_delay }, + { "scaleuint", l_scaleuint }, -be_native_module_attr_table(tasmota_ntv) { - be_native_module_function("getfreeheap", l_getFreeHeap), - be_native_module_function("publish", l_publish), - be_native_module_function("cmd", l_cmd), - be_native_module_function("getoption", l_getoption), - be_native_module_function("millis", l_millis), - be_native_module_function("timereached", l_timereached), - be_native_module_function("yield", l_yield), - be_native_module_function("delay", l_delay), + { "respcmnd", l_respCmnd }, + { "respcmndstr", l_respCmndStr }, + { "respcmnd_done", l_respCmndDone }, + { "respcmnd_error", l_respCmndError }, + { "respcmnd_failed", l_respCmndFailed }, + { "resolvecmnd", l_resolveCmnd }, - be_native_module_function("respcmnd", l_respCmnd), - be_native_module_function("respcmndstr", l_respCmndStr), - be_native_module_function("respcmnd_done", l_respCmndDone), - be_native_module_function("respcmnd_error", l_respCmndError), - be_native_module_function("respcmnd_failed", l_respCmndFailed), - be_native_module_function("resolvecmnd", l_resolveCmnd), + { "getlight", l_getlight }, + { "getpower", l_getpower }, + { "setlight", l_setlight }, + { "setpower", l_setpower }, + + { NULL, NULL } + }; + be_regclass(vm, "Tasmota_ntv", members); +} - be_native_module_function("getlight", l_getlight), - - be_native_module_str("_operators", "=<>!|"), -}; - -be_define_native_module(tasmota_ntv, NULL); #else /* @const_object_info_begin module tasmota (scope: global, depend: 1) { 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 948ccddef..07581b2a4 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 @@ -7,6 +7,8 @@ *******************************************************************/ #include "be_object.h" +extern int b_wire_init(bvm *vm); + extern int b_wire_begintransmission(bvm *vm); extern int b_wire_endtransmission(bvm *vm); extern int b_wire_requestfrom(bvm *vm); @@ -21,19 +23,25 @@ extern int b_wire_validread(bvm *vm); // #if !BE_USE_PRECOMPILED_OBJECT #if 1 // TODO we will do pre-compiled later -be_native_module_attr_table(wire) { - be_native_module_function("_begintransmission", b_wire_begintransmission), - be_native_module_function("_endtransmission", b_wire_endtransmission), - be_native_module_function("_requestfrom", b_wire_requestfrom), - be_native_module_function("_available", b_wire_available), - be_native_module_function("_write", b_wire_write), - be_native_module_function("_read", b_wire_read), - be_native_module_function("scan", b_wire_scan), - be_native_module_function("write", b_wire_validwrite), - be_native_module_function("read", b_wire_validread), -}; - -be_define_native_module(wire, NULL); +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 }, + { "_available", b_wire_available }, + { "_write", b_wire_write }, + { "_read", b_wire_read }, + { "scan", b_wire_scan }, + { "write", b_wire_validwrite }, + { "read", b_wire_validread }, + + { NULL, NULL } + }; + be_regclass(vm, "Wire", members); +} #else /* @const_object_info_begin module tasmota (scope: global, depend: 1) { diff --git a/tasmota/berry/autoexec.be b/tasmota/berry/autoexec.be new file mode 100644 index 000000000..60f18e5fc --- /dev/null +++ b/tasmota/berry/autoexec.be @@ -0,0 +1 @@ +load('denky.be') diff --git a/tasmota/berry/denky.be b/tasmota/berry/denky.be new file mode 100644 index 000000000..bcec51407 --- /dev/null +++ b/tasmota/berry/denky.be @@ -0,0 +1,21 @@ +#- +# example of using Berry script to change the led color +# accordingly to power consumption +# using Denky (French Teleinfo reader) +-# + +#- define the global symbol for reference -# +runcolor = nil + +def runcolor() + var pwr = energy.read().find('activepower',0) + print(pwr) + var red = tasmota.scaleuint(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) +end + +#- run animation -# +runcolor() diff --git a/tasmota/support.ino b/tasmota/support.ino index 85e1a7341..f4d030def 100644 --- a/tasmota/support.ino +++ b/tasmota/support.ino @@ -1812,27 +1812,37 @@ const uint8_t I2C_RETRY_COUNTER = 3; uint32_t i2c_active[4] = { 0 }; uint32_t i2c_buffer = 0; +#ifdef ESP32 +bool I2cValidRead(uint8_t addr, uint8_t reg, uint8_t size, uint32_t bus = 0); +bool I2cValidRead(uint8_t addr, uint8_t reg, uint8_t size, uint32_t bus) +#else bool I2cValidRead(uint8_t addr, uint8_t reg, uint8_t size) +#endif { uint8_t retry = I2C_RETRY_COUNTER; bool status = false; +#ifdef ESP32 + TwoWire & myWire = (bus == 0) ? Wire : Wire1; +#else + TwoWire & myWire = Wire; +#endif i2c_buffer = 0; while (!status && retry) { - Wire.beginTransmission(addr); // start transmission to device - Wire.write(reg); // sends register address to read from - if (0 == Wire.endTransmission(false)) { // Try to become I2C Master, send data and collect bytes, keep master status for next request... - Wire.requestFrom((int)addr, (int)size); // send data n-bytes read - if (Wire.available() == size) { + myWire.beginTransmission(addr); // start transmission to device + myWire.write(reg); // sends register address to read from + if (0 == myWire.endTransmission(false)) { // Try to become I2C Master, send data and collect bytes, keep master status for next request... + myWire.requestFrom((int)addr, (int)size); // send data n-bytes read + if (myWire.available() == size) { for (uint32_t i = 0; i < size; i++) { - i2c_buffer = i2c_buffer << 8 | Wire.read(); // receive DATA + i2c_buffer = i2c_buffer << 8 | myWire.read(); // receive DATA } status = true; } } retry--; } - if (!retry) Wire.endTransmission(); + if (!retry) myWire.endTransmission(); return status; } @@ -1916,19 +1926,30 @@ int32_t I2cRead24(uint8_t addr, uint8_t reg) return i2c_buffer; } +#ifdef ESP32 +bool I2cWrite(uint8_t addr, uint8_t reg, uint32_t val, uint8_t size, uint32_t bus = 0); +bool I2cWrite(uint8_t addr, uint8_t reg, uint32_t val, uint8_t size, uint32_t bus) +#else bool I2cWrite(uint8_t addr, uint8_t reg, uint32_t val, uint8_t size) +#endif { uint8_t x = I2C_RETRY_COUNTER; +#ifdef ESP32 + TwoWire & myWire = (bus == 0) ? Wire : Wire1; +#else + TwoWire & myWire = Wire; +#endif + do { - Wire.beginTransmission((uint8_t)addr); // start transmission to device - Wire.write(reg); // sends register address to write to + myWire.beginTransmission((uint8_t)addr); // start transmission to device + myWire.write(reg); // sends register address to write to uint8_t bytes = size; while (bytes--) { - Wire.write((val >> (8 * bytes)) & 0xFF); // write data + myWire.write((val >> (8 * bytes)) & 0xFF); // write data } x--; - } while (Wire.endTransmission(true) != 0 && x != 0); // end transmission + } while (myWire.endTransmission(true) != 0 && x != 0); // end transmission return (x); } diff --git a/tasmota/xdrv_52_2_berry_native.ino b/tasmota/xdrv_52_2_berry_native.ino new file mode 100644 index 000000000..58a4f8ec3 --- /dev/null +++ b/tasmota/xdrv_52_2_berry_native.ino @@ -0,0 +1,119 @@ +/* + xdrv_52_3_berry_native.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 +#include + +const char kTypeError[] PROGMEM = "type_error"; + +extern "C" { + void be_dumpstack(bvm *vm) { + int32_t top = be_top(vm); + AddLog(LOG_LEVEL_INFO, "BRY: top=%d", top); + for (uint32_t i = 1; i <= top; i++) { + const char * tname = be_typename(vm, i); + const char * cname = be_classname(vm, i); + AddLog(LOG_LEVEL_INFO, "BRY: stack[%d] = type='%s' (%s)", i, (tname != nullptr) ? tname : "", (cname != nullptr) ? cname : ""); + } + } + + // convert to unsigned 8 bits + static uint8_t to_u8(int32_t v) { + if (v < 0) { return 0; } + if (v > 0xFF) { return 0xFF; } + return v; + } + static void map_insert_int(bvm *vm, const char *key, int value) + { + be_pushstring(vm, key); + be_pushint(vm, value); + be_data_insert(vm, -3); + be_pop(vm, 2); + } + static void map_insert_bool(bvm *vm, const char *key, bool value) + { + be_pushstring(vm, key); + be_pushbool(vm, value); + be_data_insert(vm, -3); + be_pop(vm, 2); + } + // if value == NAN, ignore + static void map_insert_float(bvm *vm, const char *key, float value) + { + if (!isnan(value)) { + be_pushstring(vm, key); + be_pushreal(vm, value); + be_data_insert(vm, -3); + be_pop(vm, 2); + } + } + static void map_insert_str(bvm *vm, const char *key, const char *value) + { + be_pushstring(vm, key); + be_pushstring(vm, value); + be_data_insert(vm, -3); + be_pop(vm, 2); + } + static void map_insert_list_uint8(bvm *vm, const char *key, const uint8_t *value, size_t size) + { + be_pushstring(vm, key); + + be_newobject(vm, "list"); + for (uint32_t i=0; i < size; i++) { + be_pushint(vm, value[i]); + be_data_push(vm, -2); + be_pop(vm, 1); + } + be_pop(vm, 1); // now list is on top + + be_data_insert(vm, -3); // insert into map, key/value + be_pop(vm, 2); // pop both key and value + } + static bool map_find(bvm *vm, const char *key) + { + be_getmethod(vm, -1, "find"); // look for "find" method of "Map" instance + be_pushvalue(vm, -2); // put back instance as first argument (implicit instance) + be_pushstring(vm, key); // push string as second argument + be_call(vm, 2); // call wirn 2 parameters (implicit instance and key) + be_pop(vm, 2); // pop 2 arguments, the function is replaced by result + return !be_isnil(vm, -1); // true if not 'nil' + } + static int32_t get_list_size(bvm *vm) { + be_getmethod(vm, -1, "size"); // look for "size" method of "list" instance + be_pushvalue(vm, -2); // put back instance as first argument (implicit instance) + be_call(vm, 1); // call wirn 2 parameters (implicit instance and key) + int32_t ret = be_toint(vm, -2); + be_pop(vm, 2); // pop 1 argument and return value + return ret; + } + // get item number `index` from list, index must be valid or raises an exception + static void get_list_item(bvm *vm, int32_t index) { + be_getmethod(vm, -1, "item"); // look for "size" method of "list" instance + be_pushvalue(vm, -2); // put back instance as first argument (implicit instance) + be_pushint(vm, index); + // be_dumpstack(vm); + be_call(vm, 2); // call wirn 2 parameters (implicit instance and key) + be_pop(vm, 2); // pop 2 arguments and return value + } +} + +#endif // USE_BERRY diff --git a/tasmota/xdrv_52_3_berry_energy.ino b/tasmota/xdrv_52_3_berry_energy.ino new file mode 100644 index 000000000..75ae27245 --- /dev/null +++ b/tasmota/xdrv_52_3_berry_energy.ino @@ -0,0 +1,70 @@ +/* + xdrv_52_3_berry_native.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 + + +/*********************************************************************************************\ + * Native functions mapped to Berry functions + * + * import power + * + * power.read() -> map + * +\*********************************************************************************************/ +extern "C" { +#ifdef USE_ENERGY_SENSOR + // Berry: `begintransmission(address:int) -> nil` + int32_t b_nrg_read(struct bvm *vm); + int32_t b_nrg_read(struct bvm *vm) { + be_newobject(vm, "map"); + map_insert_float(vm, "total", Energy.total); + // Energy.phase_count + map_insert_float(vm, "power", Energy.active_power[0]); + map_insert_float(vm, "yesterday", (float)Settings.energy_kWhyesterday / 100000); + map_insert_float(vm, "today", Energy.daily); + map_insert_float(vm, "activepower", Energy.active_power[0]); + map_insert_float(vm, "apparentpower", Energy.active_power[0]); + map_insert_float(vm, "reactivepower", Energy.reactive_power[0]); + // map_insert_float(vm, "powerfactor", ); + map_insert_float(vm, "frequency", Energy.frequency[0]); + map_insert_float(vm, "voltage", Energy.voltage[0]); + map_insert_float(vm, "current", Energy.current[0]); + + be_pop(vm, 1); + be_return(vm); // Return + } +#else // USE_ENERGY_SENSOR + // + int32_t b_wire_energymissing(struct bvm *vm); + int32_t b_wire_energymissing(struct bvm *vm) { + be_raise(vm, "feature_error", "Energy sensor is not enabled, use '#define USE_ENERGY_SENSOR'"); + } + + // define weak aliases + int32_t b_nrg_read(struct bvm *vm) __attribute__ ((weak, alias ("b_wire_energymissing"))); +#endif // USE_ENERGY_SENSOR +} + + + +#endif // USE_BERRY diff --git a/tasmota/xdrv_52_3_berry_native.ino b/tasmota/xdrv_52_3_berry_native.ino deleted file mode 100644 index 46b869544..000000000 --- a/tasmota/xdrv_52_3_berry_native.ino +++ /dev/null @@ -1,534 +0,0 @@ -/* - xdrv_52_3_berry_native.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 -#include - -const char kTypeError[] PROGMEM = "type_error"; -/*********************************************************************************************\ - * Native functions mapped to Berry functions - * - * log(msg:string [,log_level:int]) ->nil - * - * import tasmota - * - * tasmota.getfreeheap() -> int - * tasmota.publish(topic:string, payload:string[, retain:bool]) -> nil - * tasmota.cmd(command:string) -> string - * tasmota.getoption(index:int) -> int - * tasmota.millis([delay:int]) -> int - * tasmota.timereached(timer:int) -> bool - * tasmota.yield() -> nil - * -\*********************************************************************************************/ -extern "C" { - // Berry: `tasmota.publish(topic, payload [,retain]) -> nil`` - // - int32_t l_publish(struct bvm *vm); - int32_t l_publish(struct bvm *vm) { - int32_t top = be_top(vm); // Get the number of arguments - if (top >= 2 && be_isstring(vm, 1) && be_isstring(vm, 2)) { // 2 mandatory string arguments - if (top == 2 || (top == 3 && be_isbool(vm, 3))) { // 3rd optional argument must be bool - const char * topic = be_tostring(vm, 1); - const char * payload = be_tostring(vm, 2); - bool retain = false; - if (top == 3) { - retain = be_tobool(vm, 3); - } - strlcpy(TasmotaGlobal.mqtt_data, payload, sizeof(TasmotaGlobal.mqtt_data)); - MqttPublish(topic, retain); - be_return(vm); // Return - } - } - be_raise(vm, kTypeError, nullptr); - } - - // Berry: `tasmota.cmd(command:string) -> string` - // - int32_t l_cmd(struct bvm *vm); - int32_t l_cmd(struct bvm *vm) { - int32_t top = be_top(vm); // Get the number of arguments - if (top == 1 && be_isstring(vm, 1)) { // only 1 argument of type string accepted - const char * command = be_tostring(vm, 1); - ExecuteCommand(command, SRC_BERRY); - be_pushstring(vm, TasmotaGlobal.mqtt_data); - be_return(vm); // Return - } - be_raise(vm, kTypeError, nullptr); - } - - // Berry: tasmota.millis([delay:int]) -> int - // - int32_t l_millis(struct bvm *vm); - int32_t l_millis(struct bvm *vm) { - int32_t top = be_top(vm); // Get the number of arguments - if (top == 0 || (top == 1 && be_isint(vm, 1))) { // only 1 argument of type string accepted - uint32_t delay = 0; - if (top == 1) { - delay = be_toint(vm, 1); - } - uint32_t ret_millis = millis() + delay; - be_pushint(vm, ret_millis); - be_return(vm); // Return - } - be_raise(vm, kTypeError, nullptr); - } - - // Berry: tasmota.getoption(index:int) -> int - // - int32_t l_getoption(struct bvm *vm); - int32_t l_getoption(struct bvm *vm) { - int32_t top = be_top(vm); // Get the number of arguments - if (top == 1 && be_isint(vm, 1)) { - uint32_t opt = GetOption(be_toint(vm, 1)); - be_pushint(vm, opt); - be_return(vm); // Return - } - be_raise(vm, kTypeError, nullptr); - } - - // Berry: tasmota.timereached(timer:int) -> bool - // - int32_t l_timereached(struct bvm *vm); - int32_t l_timereached(struct bvm *vm) { - int32_t top = be_top(vm); // Get the number of arguments - if (top == 1 && be_isint(vm, 1)) { // only 1 argument of type string accepted - uint32_t timer = be_toint(vm, 1); - bool reached = TimeReached(timer); - be_pushbool(vm, reached); - be_return(vm); // Return - } - be_raise(vm, kTypeError, nullptr); - } - - // Berry: tasmota.delay(timer:int) -> nil - // - int32_t l_delay(struct bvm *vm); - int32_t l_delay(struct bvm *vm) { - int32_t top = be_top(vm); // Get the number of arguments - if (top == 1 && be_isint(vm, 1)) { // only 1 argument of type string accepted - uint32_t timer = be_toint(vm, 1); - delay(timer); - be_return_nil(vm); // Return - } - be_raise(vm, kTypeError, nullptr); - } - - // Berry: `yield() -> nil` - // ESP object - int32_t l_yield(bvm *vm); - int32_t l_yield(bvm *vm) { - optimistic_yield(10); - be_return(vm); - } - - // Berry: `save(file:string, f:closure) -> bool` - int32_t l_save(struct bvm *vm); - int32_t l_save(struct bvm *vm) { - int32_t top = be_top(vm); // Get the number of arguments - if (top ==2 && be_isstring(vm, 1) && be_isclosure(vm, 2)) { // only 1 argument of type string accepted - const char *fname = be_tostring(vm, 1); - int32_t ret = be_savecode(vm, fname); - be_pushint(vm, ret); - be_return(vm); // Return - } - be_raise(vm, kTypeError, nullptr); - } - - int32_t l_respCmnd(bvm *vm); - int32_t l_respCmnd(bvm *vm) { - int32_t top = be_top(vm); // Get the number of arguments - if (top == 1) { - const char *msg = be_tostring(vm, 1); - Response_P("%s", msg); - be_return_nil(vm); // Return nil when something goes wrong - } - be_raise(vm, kTypeError, nullptr); - } - - int32_t l_respCmndStr(bvm *vm); - int32_t l_respCmndStr(bvm *vm) { - int32_t top = be_top(vm); // Get the number of arguments - if (top == 1) { - const char *msg = be_tostring(vm, 1); - ResponseCmndChar(msg); - be_return_nil(vm); // Return nil when something goes wrong - } - be_raise(vm, kTypeError, nullptr); - } - - int32_t l_respCmndDone(bvm *vm); - int32_t l_respCmndDone(bvm *vm) { - ResponseCmndDone(); - be_return_nil(vm); - } - - int32_t l_respCmndError(bvm *vm); - int32_t l_respCmndError(bvm *vm) { - ResponseCmndError(); - be_return_nil(vm); - } - - int32_t l_respCmndFailed(bvm *vm); - int32_t l_respCmndFailed(bvm *vm) { - ResponseCmndFailed(); - be_return_nil(vm); - } - - // update XdrvMailbox.command with actual command - int32_t l_resolveCmnd(bvm *vm); - int32_t l_resolveCmnd(bvm *vm) { - int32_t top = be_top(vm); // Get the number of arguments - if (top == 1 && be_isstring(vm, 1)) { - const char *msg = be_tostring(vm, 1); - strlcpy(XdrvMailbox.command, msg, CMDSZ); - be_return_nil(vm); // Return nil when something goes wrong - } - be_raise(vm, kTypeError, nullptr); - } - - - static void map_insert_int(bvm *vm, const char *key, int value) - { - be_pushstring(vm, key); - be_pushint(vm, value); - be_data_insert(vm, -3); - be_pop(vm, 2); - } - static void map_insert_bool(bvm *vm, const char *key, bool value) - { - be_pushstring(vm, key); - be_pushbool(vm, value); - be_data_insert(vm, -3); - be_pop(vm, 2); - } - static void map_insert_str(bvm *vm, const char *key, const char *value) - { - be_pushstring(vm, key); - be_pushstring(vm, value); - be_data_insert(vm, -3); - be_pop(vm, 2); - } - - // get light - int32_t l_getlight(bvm *vm); - int32_t l_getlight(bvm *vm) { - int32_t top = be_top(vm); // Get the number of arguments - if (top == 0 || (top == 1 && be_isint(vm, 1))) { - int32_t light_num = 0; - if (top > 0) { - light_num = be_toint(vm, 1); - } - bool data_present = false; // do we have relevant data - be_newobject(vm, "map"); - // check if the light exist - // TasmotaGlobal.devices_present - // Light.device - // Light.subtype - // Light.pwm_multi_channels - // light_controller.isCTRGBLinked() - - if (Light.device > 0) { - // we have a light - - uint8_t channels[LST_MAX]; - uint8_t channelsb[LST_MAX]; - char rgbcw[12] = {0}; - char rgbcwb[12] = {0}; - light_state.getChannelsRaw(channels); - light_state.getChannels(channelsb); - - // map_insert_int(vm, "_devices_present", TasmotaGlobal.devices_present); - // map_insert_int(vm, "_light_device", Light.device); - // map_insert_int(vm, "_light_subtype", Light.subtype); - // map_insert_int(vm, "_light_multi", Light.pwm_multi_channels); - // map_insert_int(vm, "_light_linked", light_controller.isCTRGBLinked()); - - if (!Light.pwm_multi_channels) { - uint32_t subtype = Light.subtype; // virtual sub-type, for SO37 128 - uint32_t chanidx = 0; // channel offset, for SO37 128 - - - if (light_controller.isCTRGBLinked() && (light_num == 0)) { - data_present = true; // valid combination - } - if (!light_controller.isCTRGBLinked()) { - if (light_num == 0) { - data_present = true; // valid combination - if (subtype > LST_RGB) { subtype = LST_RGB; } // limit to RGB - } - if ((light_num == 1) && subtype > LST_RGB) { - data_present = true; // valid combination - subtype = subtype - LST_RGB; - chanidx = 3; // skip first 3 channels - } - } - - if (data_present) { - // see ResponseLightState() - map_insert_bool(vm, "power", (bool)(Light.power & 1)); - map_insert_int(vm, "bri", light_state.getBri()); - - - if (subtype >= LST_RGB) { - uint16_t hue; - uint8_t sat, bri; - light_state.getHSB(&hue, &sat, &bri); - map_insert_int(vm, "hue", hue); - map_insert_int(vm, "sat", sat); - } - if ((LST_COLDWARM == subtype) || (LST_RGBW <= subtype)) { - map_insert_int(vm, "ct", light_state.getCT()); - } - if (subtype > LST_NONE) { - for (uint32_t i=0; i < subtype; i++) { - snprintf_P(rgbcw, sizeof(rgbcw), PSTR("%s%02X"), rgbcw, channels[i+chanidx]); - snprintf_P(rgbcwb, sizeof(rgbcwb), PSTR("%s%02X"), rgbcwb, channelsb[i+chanidx]); - } - map_insert_str(vm, "channels", rgbcw); - map_insert_str(vm, "channelsb", rgbcwb); - // map_insert_bool(vm, "gamma", Settings.light_correction); - } - } - } else { // Light.pwm_multi_channels - if ((light_num >= 0) && (light_num < LST_MAX)) { - data_present = true; - map_insert_bool(vm, "power", Light.power & (1 << light_num)); - map_insert_int(vm, "bri", Light.current_color[light_num]); - snprintf_P(rgbcw, sizeof(rgbcw), PSTR("%02X"), channels[light_num]); - snprintf_P(rgbcwb, sizeof(rgbcwb), PSTR("%02X"), channelsb[light_num]); - map_insert_str(vm, "channels", rgbcw); - map_insert_str(vm, "channelsb", rgbcwb); - } - } - - be_pop(vm, 1); - if (data_present) { - be_return(vm); // Return - } else { - be_return_nil(vm); // no data, return nil instead of empty map - } - } else { - be_return_nil(vm); - } - } - be_raise(vm, kTypeError, nullptr); - } - -} - -/*********************************************************************************************\ - * Native functions mapped to Berry functions - * - * import wire - * - * wire.getfreeheap() -> int - * -\*********************************************************************************************/ -extern "C" { - // Berry: `begintransmission(address:int) -> nil` - int32_t b_wire_begintransmission(struct bvm *vm); - int32_t b_wire_begintransmission(struct bvm *vm) { - int32_t top = be_top(vm); // Get the number of arguments - if (top == 1 && be_isint(vm, 1)) { // only 1 argument of type string accepted - int32_t address = be_toint(vm, 1); - Wire.beginTransmission(address); - be_return(vm); // Return - } - be_raise(vm, kTypeError, nullptr); - } - - // Berry: `endtransmission([stop:bool]) -> nil` - int32_t b_wire_endtransmission(struct bvm *vm); - int32_t b_wire_endtransmission(struct bvm *vm) { - int32_t top = be_top(vm); // Get the number of arguments - if (top == 0 || (top == 1 && be_isbool(vm, 1))) { // only 1 argument of type string accepted - bool stop = true; - if (top == 1) { - stop = be_tobool(vm, 1); - } - uint32_t ret = Wire.endTransmission(stop); - be_pushint(vm, ret); - be_return(vm); // Return - } - be_raise(vm, kTypeError, nullptr); - } - - // Berry: `requestfrom(address:int, quantity:int [stop:bool = true]) -> nil` - int32_t b_wire_requestfrom(struct bvm *vm); - int32_t b_wire_requestfrom(struct bvm *vm) { - int32_t top = be_top(vm); // Get the number of arguments - if ( (top == 2 || (top == 3 && be_isbool(vm, 3))) - && be_isint(vm, 1) && be_isint(vm, 2) ) { - int32_t address = be_toint(vm, 1); - int32_t quantity = be_toint(vm, 2); - bool stop = true; - if (top == 3) { - stop = be_tobool(vm, 3); - } - Wire.requestFrom((uint16_t)address, (uint8_t)quantity, stop); - be_return(vm); // Return - } - be_raise(vm, kTypeError, nullptr); - } - - // Berry: `available() -> bool` - int32_t b_wire_available(struct bvm *vm); - int32_t b_wire_available(struct bvm *vm) { - int32_t top = be_top(vm); // Get the number of arguments - if (top == 0) { - size_t available = Wire.available(); - be_pushint(vm, available); - be_return(vm); // Return - } - be_raise(vm, kTypeError, nullptr); - } - - // Berry: `write(value:int | s:string) -> nil` - 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 - if (top == 1 && (be_isint(vm, 1) || be_isstring(vm, 1))) { - if (be_isint(vm, 1)) { - int32_t value = be_toint(vm, 1); - Wire.write(value); - } else if (be_isstring(vm, 1)) { - const char * s = be_tostring(vm, 1); - Wire.write(s); - } else { - be_return_nil(vm); - } - be_return(vm); // Return - } - be_raise(vm, kTypeError, nullptr); - } - - // Berry: `read() -> int` - int32_t b_wire_read(struct bvm *vm); - int32_t b_wire_read(struct bvm *vm) { - int32_t top = be_top(vm); // Get the number of arguments - if (top == 0) { - int32_t value = Wire.read(); - be_pushint(vm, value); - be_return(vm); // Return - } - be_raise(vm, kTypeError, nullptr); - } - - int32_t b_wire_scan(struct bvm *vm); - int32_t b_wire_scan(struct bvm *vm) { - int32_t top = be_top(vm); // Get the number of arguments - if (top == 0) { - be_newobject(vm, "list"); - for (uint8_t address = 1; address <= 127; address++) { - Wire.beginTransmission(address); - int32_t error = Wire.endTransmission(); - if (0 == error) { - be_pushint(vm, address); - be_data_push(vm, -2); - be_pop(vm, 1); - } - } - be_pop(vm, 1); - be_return(vm); // Return - } - be_raise(vm, kTypeError, nullptr); - } - - // Berry: `validwrite(address:int, reg:int, val:int, size:int) -> bool or nil` - int32_t b_wire_validwrite(struct bvm *vm); - int32_t b_wire_validwrite(struct bvm *vm) { - int32_t top = be_top(vm); // Get the number of arguments - if (top == 4 && be_isint(vm, 1) && be_isint(vm, 2) && be_isint(vm, 3) && be_isint(vm, 4)) { - uint8_t addr = be_toint(vm, 1); - uint8_t reg = be_toint(vm, 2); - uint8_t val = be_toint(vm, 3); - uint8_t size = be_toint(vm, 4); - bool ok = I2cWrite(addr, reg, val, size); - be_pushbool(vm, ok); - be_return(vm); // Return - } - be_raise(vm, kTypeError, nullptr); - } - - // Berry: `validread(address:int, reg:int, size:int) -> int or nil` - int32_t b_wire_validread(struct bvm *vm); - int32_t b_wire_validread(struct bvm *vm) { - int32_t top = be_top(vm); // Get the number of arguments - if (top == 3 && be_isint(vm, 1) && be_isint(vm, 2) && be_isint(vm, 3)) { - uint8_t addr = be_toint(vm, 1); - uint8_t reg = be_toint(vm, 2); - uint8_t size = be_toint(vm, 3); - bool ok = I2cValidRead(addr, reg, size); - if (ok) { - be_pushint(vm, i2c_buffer); - } else { - be_pushnil(vm); - } - be_return(vm); // Return - } - be_raise(vm, kTypeError, nullptr); - } -} - -/*********************************************************************************************\ - * Native functions mapped to Berry functions - * - * log(msg:string [,log_level:int]) ->nil - * -\*********************************************************************************************/ -extern "C" { - // Berry: `log(msg:string [,log_level:int]) ->nil` - // Logs the string at LOG_LEVEL_INFO (loglevel=2) - int32_t l_logInfo(struct bvm *vm); - int32_t l_logInfo(struct bvm *vm) { - int32_t top = be_top(vm); // Get the number of arguments - if (top >= 1 && be_isstring(vm, 1)) { // only 1 argument of type string accepted - const char * msg = be_tostring(vm, 1); - uint32_t log_level = LOG_LEVEL_INFO; - if (top >= 2 && be_isint(vm, 2)) { - log_level = be_toint(vm, 2); - if (log_level > LOG_LEVEL_DEBUG_MORE) { log_level = LOG_LEVEL_DEBUG_MORE; } - } - AddLog(log_level, PSTR("%s"), msg); - be_return(vm); // Return - } - be_return_nil(vm); // Return nil when something goes wrong - } - - // Berry: `getFreeHeap() -> int` - // ESP object - int32_t l_getFreeHeap(bvm *vm); - int32_t l_getFreeHeap(bvm *vm) { - be_pushint(vm, ESP.getFreeHeap()); - be_return(vm); - } -} - -// called as a replacement to Berry `print()` -void berry_log(const char * berry_buf); -void berry_log(const char * berry_buf) { - AddLog(LOG_LEVEL_INFO, PSTR("%s"), berry_buf); -} - - -#endif // USE_BERRY diff --git a/tasmota/xdrv_52_3_berry_tasmota.ino b/tasmota/xdrv_52_3_berry_tasmota.ino new file mode 100644 index 000000000..f39f3cc1c --- /dev/null +++ b/tasmota/xdrv_52_3_berry_tasmota.ino @@ -0,0 +1,529 @@ +/* + xdrv_52_3_berry_native.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 +#include + +/*********************************************************************************************\ + * Native functions mapped to Berry functions + * + * log(msg:string [,log_level:int]) ->nil + * + * import tasmota + * + * tasmota.getfreeheap() -> int + * tasmota.publish(topic:string, payload:string[, retain:bool]) -> nil + * tasmota.cmd(command:string) -> string + * tasmota.getoption(index:int) -> int + * tasmota.millis([delay:int]) -> int + * tasmota.timereached(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 + * +\*********************************************************************************************/ +extern "C" { + // Berry: `tasmota.publish(topic, payload [,retain]) -> nil`` + // + int32_t l_publish(struct bvm *vm); + int32_t l_publish(struct bvm *vm) { + int32_t top = be_top(vm); // Get the number of arguments + if (top >= 3 && be_isstring(vm, 2) && be_isstring(vm, 3)) { // 2 mandatory string arguments + if (top == 3 || (top == 4 && be_isbool(vm, 4))) { // 3rd optional argument must be bool + const char * topic = be_tostring(vm, 2); + const char * payload = be_tostring(vm, 3); + bool retain = false; + if (top == 4) { + retain = be_tobool(vm, 4); + } + strlcpy(TasmotaGlobal.mqtt_data, payload, sizeof(TasmotaGlobal.mqtt_data)); + MqttPublish(topic, retain); + be_return(vm); // Return + } + } + be_raise(vm, kTypeError, nullptr); + } + + // Berry: `tasmota.cmd(command:string) -> string` + // + int32_t l_cmd(struct bvm *vm); + int32_t l_cmd(struct bvm *vm) { + int32_t top = be_top(vm); // Get the number of arguments + if (top == 2 && be_isstring(vm, 2)) { // only 1 argument of type string accepted + const char * command = be_tostring(vm, 2); + ExecuteCommand(command, SRC_BERRY); + be_pushstring(vm, TasmotaGlobal.mqtt_data); + be_return(vm); // Return + } + be_raise(vm, kTypeError, nullptr); + } + + // Berry: tasmota.millis([delay:int]) -> int + // + int32_t l_millis(struct bvm *vm); + int32_t l_millis(struct bvm *vm) { + int32_t top = be_top(vm); // Get the number of arguments + if (top == 1 || (top == 2 && be_isint(vm, 2))) { // only 1 argument of type string accepted + uint32_t delay = 0; + if (top == 2) { + delay = be_toint(vm, 2); + } + uint32_t ret_millis = millis() + delay; + be_pushint(vm, ret_millis); + be_return(vm); // Return + } + be_raise(vm, kTypeError, nullptr); + } + + // Berry: tasmota.getoption(index:int) -> int + // + int32_t l_getoption(struct bvm *vm); + int32_t l_getoption(struct bvm *vm) { + int32_t top = be_top(vm); // Get the number of arguments + if (top == 2 && be_isint(vm, 2)) { + uint32_t opt = GetOption(be_toint(vm, 2)); + be_pushint(vm, opt); + be_return(vm); // Return + } + be_raise(vm, kTypeError, nullptr); + } + + // Berry: tasmota.timereached(timer:int) -> bool + // + int32_t l_timereached(struct bvm *vm); + int32_t l_timereached(struct bvm *vm) { + int32_t top = be_top(vm); // Get the number of arguments + if (top == 2 && be_isint(vm, 2)) { // only 1 argument of type string accepted + uint32_t timer = be_toint(vm, 2); + bool reached = TimeReached(timer); + be_pushbool(vm, reached); + be_return(vm); // Return + } + be_raise(vm, kTypeError, nullptr); + } + + // Berry: tasmota.delay(timer:int) -> nil + // + int32_t l_delay(struct bvm *vm); + int32_t l_delay(struct bvm *vm) { + int32_t top = be_top(vm); // Get the number of arguments + if (top == 2 && be_isint(vm, 2)) { // only 1 argument of type string accepted + uint32_t timer = be_toint(vm, 2); + delay(timer); + be_return_nil(vm); // Return + } + be_raise(vm, kTypeError, nullptr); + } + + // Berry: `yield() -> nil` + // ESP object + int32_t l_yield(bvm *vm); + int32_t l_yield(bvm *vm) { + optimistic_yield(10); + be_return_nil(vm); + } + + // Berry: tasmota.scaleuint(int * 5) -> int + // + int32_t l_scaleuint(struct bvm *vm); + int32_t l_scaleuint(struct bvm *vm) { + int32_t top = be_top(vm); // Get the number of arguments + if (top == 6 && be_isint(vm, 2) && be_isint(vm, 3) && be_isint(vm, 4) && be_isint(vm, 5) && be_isint(vm, 6)) { // only 1 argument of type string accepted + int32_t v = be_toint(vm, 2); + int32_t from1 = be_toint(vm, 3); + int32_t from2 = be_toint(vm, 4); + int32_t to1 = be_toint(vm, 5); + int32_t to2 = be_toint(vm, 6); + + int32_t ret = changeUIntScale(v, from1, from2, to1, to2); + be_pushint(vm, ret); + be_return(vm); + } + be_raise(vm, kTypeError, nullptr); + } + + int32_t l_respCmnd(bvm *vm); + int32_t l_respCmnd(bvm *vm) { + int32_t top = be_top(vm); // Get the number of arguments + if (top == 2) { + const char *msg = be_tostring(vm, 2); + Response_P("%s", msg); + be_return_nil(vm); // Return nil when something goes wrong + } + be_raise(vm, kTypeError, nullptr); + } + + int32_t l_respCmndStr(bvm *vm); + int32_t l_respCmndStr(bvm *vm) { + int32_t top = be_top(vm); // Get the number of arguments + if (top == 2) { + const char *msg = be_tostring(vm, 2); + ResponseCmndChar(msg); + be_return_nil(vm); // Return nil when something goes wrong + } + be_raise(vm, kTypeError, nullptr); + } + + int32_t l_respCmndDone(bvm *vm); + int32_t l_respCmndDone(bvm *vm) { + ResponseCmndDone(); + be_return_nil(vm); + } + + int32_t l_respCmndError(bvm *vm); + int32_t l_respCmndError(bvm *vm) { + ResponseCmndError(); + be_return_nil(vm); + } + + int32_t l_respCmndFailed(bvm *vm); + int32_t l_respCmndFailed(bvm *vm) { + ResponseCmndFailed(); + be_return_nil(vm); + } + + // update XdrvMailbox.command with actual command + int32_t l_resolveCmnd(bvm *vm); + int32_t l_resolveCmnd(bvm *vm) { + int32_t top = be_top(vm); // Get the number of arguments + if (top == 2 && be_isstring(vm, 2)) { + const char *msg = be_tostring(vm, 2); + strlcpy(XdrvMailbox.command, msg, CMDSZ); + be_return_nil(vm); // Return nil when something goes wrong + } + be_raise(vm, kTypeError, nullptr); + } + + // push the light status object on the vm stack + void push_getlight(bvm *vm, uint32_t light_num) { + bool data_present = false; // do we have relevant data + be_newobject(vm, "map"); + // check if the light exist + // TasmotaGlobal.devices_present + // Light.device + // Light.subtype + // Light.pwm_multi_channels + // light_controller.isCTRGBLinked() + + if (Light.device > 0) { + // we have a light + + uint8_t channels[LST_MAX]; + char s_rgb[8] = {0}; // RGB raw levels + light_controller.calcLevels(channels); + uint8_t bri = light_state.getBri(); + + // map_insert_int(vm, "_devices_present", TasmotaGlobal.devices_present); + // map_insert_int(vm, "_light_device", Light.device); + // map_insert_int(vm, "_light_subtype", Light.subtype); + // map_insert_int(vm, "_light_multi", Light.pwm_multi_channels); + // map_insert_int(vm, "_light_linked", light_controller.isCTRGBLinked()); + + if (!Light.pwm_multi_channels) { + uint32_t subtype = Light.subtype; // virtual sub-type, for SO37 128 + uint32_t chanidx = 0; // channel offset, for SO37 128 + + + if (light_controller.isCTRGBLinked() && (light_num == 0)) { + data_present = true; // valid combination + if (subtype >= LST_RGBW) { + map_insert_str(vm, "colormode", (light_state.getColorMode() & LCM_RGB ? "rgb" : "ct")); + } + } + if (!light_controller.isCTRGBLinked()) { + if (light_num == 0) { + data_present = true; // valid combination + if (subtype > LST_RGB) { subtype = LST_RGB; } // limit to RGB + bri = light_state.getBriRGB(); + } + if ((light_num == 1) && subtype > LST_RGB) { + data_present = true; // valid combination + subtype = subtype - LST_RGB; + chanidx = 3; // skip first 3 channels + bri = light_state.getBriCT(); + } + } + + if (data_present) { + // see ResponseLightState() + map_insert_bool(vm, "power", bitRead(TasmotaGlobal.power, light_num)); + map_insert_int(vm, "bri", bri); + + if (subtype >= LST_RGB) { + uint16_t hue; + uint8_t sat, bri; + light_state.getHSB(&hue, &sat, &bri); + map_insert_int(vm, "hue", hue); + map_insert_int(vm, "sat", sat); + } + if ((LST_COLDWARM == subtype) || (LST_RGBW <= subtype)) { + map_insert_int(vm, "ct", light_state.getCT()); + } + if (subtype >= LST_RGB) { + snprintf(s_rgb, sizeof(s_rgb), PSTR("%02X%02X%02X"), channels[0], channels[1], channels[2]); + map_insert_str(vm, "rgb", s_rgb); + } + if (subtype > LST_NONE) { + map_insert_list_uint8(vm, "channels", &channels[chanidx], subtype); + } + } + } else { // Light.pwm_multi_channels + if ((light_num >= 0) && (light_num < LST_MAX)) { + data_present = true; + map_insert_bool(vm, "power", Light.power & (1 << light_num)); + map_insert_int(vm, "bri", Light.current_color[light_num]); + map_insert_list_uint8(vm, "channels", &channels[light_num], 1); + } + } + + be_pop(vm, 1); + if (!data_present) { + be_pop(vm, 1); + be_pushnil(vm); + } + } else { + be_pop(vm, 1); + be_pushnil(vm); + } + } + + // get light + int32_t l_getlight(bvm *vm); + int32_t l_getlight(bvm *vm) { + 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) { + light_num = be_toint(vm, 2); + } + push_getlight(vm, light_num); + be_return(vm); // Return + } + be_raise(vm, kTypeError, nullptr); + } + + // set light + int32_t l_setlight(bvm *vm); + int32_t l_setlight(bvm *vm) { + int32_t top = be_top(vm); // Get the number of arguments + if (top >= 2 && be_isinstance(vm, 2) && (top != 3 || be_isint(vm, 3))) { + int32_t idx = 0; + if (top >= 3) { + idx = be_toint(vm, 3); + be_pop(vm, 1); // remove last argument to have the map at the top of stack + } + + // power + if (map_find(vm, "power")) { + bool power = be_tobool(vm, -1); + bool current_power = bitRead(TasmotaGlobal.power, idx); + if (power != current_power) { // only send command if needed + ExecuteCommandPower(Light.device + idx, (power) ? POWER_ON : POWER_OFF, SRC_BERRY); + } + } + be_pop(vm, 1); + + // ct + if (map_find(vm, "ct")) { + int32_t ct = be_toint(vm, -1); + light_controller.changeCTB(ct, light_state.getBriCT()); + } + be_pop(vm, 1); + + // hue + if (map_find(vm, "hue")) { + int32_t hue = be_toint(vm, -1); + uint8_t sat; + uint8_t bri; + light_state.getHSB(nullptr, &sat, &bri); + light_controller.changeHSB(hue, sat, bri); + } + be_pop(vm, 1); + + // sat + if (map_find(vm, "sat")) { + int32_t sat = be_toint(vm, -1); + uint16_t hue; + uint8_t bri; + light_state.getHSB(&hue, nullptr, &bri); + light_controller.changeHSB(hue, sat, bri); + } + be_pop(vm, 1); + + // rgb + if (map_find(vm, "rgb")) { + const char * rgb_s = be_tostring(vm, -1); + SBuffer buf = SBuffer::SBufferFromHex(rgb_s, strlen(rgb_s)); + uint8_t channels[LST_MAX] = {}; + memcpy(channels, buf.buf(), buf.len() > LST_MAX ? LST_MAX : buf.len()); + bool on = false; // if all are zero, then only set power off + for (uint32_t i = 0; i < LST_MAX; i++) { + if (channels[i] != 0) { on = true; } + } + if (on) { + light_controller.changeChannels(channels); + } else { + ExecuteCommandPower(idx + 1, POWER_OFF, SRC_BERRY); + } + } + be_pop(vm, 1); + + // channels + if (map_find(vm, "channels")) { + if (be_isinstance(vm, -1)) { + be_getbuiltin(vm, "list"); // add "list" class + if (be_isderived(vm, -2)) { + be_pop(vm, 1); // remove "list" class from top + int32_t list_size = get_list_size(vm); + // AddLog(LOG_LEVEL_INFO, "Instance is list size = %d", list_size); + + uint8_t channels[LST_MAX] = {}; // initialized with all zeroes + if (list_size > LST_MAX) { list_size = LST_MAX; } // no more than 5 channels, no need to test for positive, any negative will be discarded by loop + for (uint32_t i = 0; i < list_size; i++) { + // be_dumpstack(vm); + get_list_item(vm, i); + // be_dumpstack(vm); + int32_t val = be_toint(vm, -1); + be_pop(vm, 1); // remove result from stack + channels[i] = to_u8(val); + + bool on = false; // if all are zero, then only set power off + for (uint32_t i = 0; i < LST_MAX; i++) { + if (channels[i] != 0) { on = true; } + } + if (on) { + light_controller.changeChannels(channels); + } else { + ExecuteCommandPower(idx + 1, POWER_OFF, SRC_BERRY); + } + } + } else { + be_pop(vm, 1); // remove "list" class from top + } + } + } + be_pop(vm, 1); + + // bri is done after channels and rgb + // bri + if (map_find(vm, "bri")) { + int32_t bri = be_toint(vm, -1); + light_controller.changeBri(bri); + } + be_pop(vm, 1); + + push_getlight(vm, idx); + be_return(vm); // Return + } + be_raise(vm, kTypeError, nullptr); + } // TODO + + // get power + int32_t l_getpower(bvm *vm); + int32_t l_getpower(bvm *vm) { + be_newobject(vm, "list"); + for (uint32_t i = 0; i < TasmotaGlobal.devices_present; i++) { + be_pushbool(vm, bitRead(TasmotaGlobal.power, i)); + be_data_push(vm, -2); + be_pop(vm, 1); + } + be_pop(vm, 1); + be_return(vm); // Return + } + + int32_t l_setpower(bvm *vm); + int32_t l_setpower(bvm *vm) { + int32_t top = be_top(vm); // Get the number of arguments + if (top == 3 && be_isint(vm, 2) && be_isbool(vm, 3)) { + int32_t idx = be_toint(vm, 2); + bool power = be_tobool(vm, 3); + if ((idx >= 0) && (idx < TasmotaGlobal.devices_present)) { + ExecuteCommandPower(idx + 1, (power) ? POWER_ON : POWER_OFF, SRC_BERRY); + be_pushbool(vm, power); + be_return(vm); // Return + } else { + be_return_nil(vm); + } + } + be_raise(vm, kTypeError, nullptr); + } + +} + +/*********************************************************************************************\ + * Native functions mapped to Berry functions + * + * log(msg:string [,log_level:int]) ->nil + * +\*********************************************************************************************/ +extern "C" { + // Berry: `log(msg:string [,log_level:int]) ->nil` + // Logs the string at LOG_LEVEL_INFO (loglevel=2) + int32_t l_logInfo(struct bvm *vm); + int32_t l_logInfo(struct bvm *vm) { + int32_t top = be_top(vm); // Get the number of arguments + if (top >= 1 && be_isstring(vm, 1)) { // only 1 argument of type string accepted + const char * msg = be_tostring(vm, 1); + uint32_t log_level = LOG_LEVEL_INFO; + if (top >= 2 && be_isint(vm, 2)) { + log_level = be_toint(vm, 2); + if (log_level > LOG_LEVEL_DEBUG_MORE) { log_level = LOG_LEVEL_DEBUG_MORE; } + } + AddLog(log_level, PSTR("%s"), msg); + be_return(vm); // Return + } + be_return_nil(vm); // Return nil when something goes wrong + } + + // Berry: `getFreeHeap() -> int` + // ESP object + int32_t l_getFreeHeap(bvm *vm); + int32_t l_getFreeHeap(bvm *vm) { + be_pushint(vm, ESP.getFreeHeap()); + be_return(vm); + } + + // Berry: `save(file:string, f:closure) -> bool` + int32_t l_save(struct bvm *vm); + int32_t l_save(struct bvm *vm) { + int32_t top = be_top(vm); // Get the number of arguments + if (top == 2 && be_isstring(vm, 1) && be_isclosure(vm, 2)) { // only 1 argument of type string accepted + const char *fname = be_tostring(vm, 1); + int32_t ret = be_savecode(vm, fname); + be_pushint(vm, ret); + be_return(vm); // Return + } + be_raise(vm, kTypeError, nullptr); + } +} + +// called as a replacement to Berry `print()` +void berry_log(const char * berry_buf); +void berry_log(const char * berry_buf) { + AddLog(LOG_LEVEL_INFO, PSTR("%s"), berry_buf); +} + + +#endif // USE_BERRY diff --git a/tasmota/xdrv_52_3_berry_wire.ino b/tasmota/xdrv_52_3_berry_wire.ino new file mode 100644 index 000000000..a5fccb44e --- /dev/null +++ b/tasmota/xdrv_52_3_berry_wire.ino @@ -0,0 +1,245 @@ +/* + xdrv_52_3_berry_native.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 +#include + +// read the `_bus` attribute and return `Wire` or `Wire1` +TwoWire & getWire(bvm *vm); +TwoWire & getWire(bvm *vm) { + be_getmember(vm, 1, "_bus"); + int32_t bus = be_toint(vm, -1); + be_pop(vm, 1); + if (0 == bus) { + return Wire; + } else { + return Wire1; + } +} +int32_t getBus(bvm *vm); +int32_t getBus(bvm *vm) { + be_getmember(vm, 1, "_bus"); + int32_t bus = be_toint(vm, -1); + be_pop(vm, 1); + return bus; +} + +/*********************************************************************************************\ + * Native functions mapped to Berry functions + * + * import wire + * + * wire.getfreeheap() -> int + * +\*********************************************************************************************/ +extern "C" { +#ifdef USE_I2C + // Berry: `init([bus:int = 0]) -> nil + int32_t b_wire_init(struct bvm *vm); + int32_t b_wire_init(struct bvm *vm) { + int32_t top = be_top(vm); // Get the number of arguments + int32_t bus = 0; + if (top > 1 && be_isint(vm, 2)) { + bus = be_toint(vm, 2); + if (bus < 0) { bus = 0; } + if (bus > 1) { bus = 1; } + } + // store bus in instance + be_pushint(vm, bus); + be_setmember(vm, 1, "_bus"); + be_return_nil(vm); + } + + // Berry: `begintransmission(address:int) -> nil` + int32_t b_wire_begintransmission(struct bvm *vm); + int32_t b_wire_begintransmission(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)) { // only 1 argument of type string accepted + int32_t address = be_toint(vm, 2); + myWire.beginTransmission(address); + be_return(vm); // Return + } + be_raise(vm, kTypeError, nullptr); + } + + // Berry: `endtransmission([stop:bool]) -> nil` + int32_t b_wire_endtransmission(struct bvm *vm); + int32_t b_wire_endtransmission(struct bvm *vm) { + int32_t top = be_top(vm); // Get the number of arguments + TwoWire & myWire = getWire(vm); + if (top == 1 || (top == 2 && be_isbool(vm, 2))) { // only 1 argument of type string accepted + bool stop = true; + if (top == 1) { + stop = be_tobool(vm, 2); + } + uint32_t ret = myWire.endTransmission(stop); + be_pushint(vm, ret); + be_return(vm); // Return + } + be_raise(vm, kTypeError, nullptr); + } + + // Berry: `requestfrom(address:int, quantity:int [stop:bool = true]) -> nil` + int32_t b_wire_requestfrom(struct bvm *vm); + int32_t b_wire_requestfrom(struct bvm *vm) { + int32_t top = be_top(vm); // Get the number of arguments + TwoWire & myWire = getWire(vm); + if ( (top == 3 || (top == 4 && be_isbool(vm, 4))) + && be_isint(vm, 2) && be_isint(vm, 3) ) { + int32_t address = be_toint(vm, 2); + int32_t quantity = be_toint(vm, 3); + bool stop = true; + if (top == 4) { + stop = be_tobool(vm, 4); + } + myWire.requestFrom((uint16_t)address, (uint8_t)quantity, stop); + be_return(vm); // Return + } + be_raise(vm, kTypeError, nullptr); + } + + // Berry: `available() -> bool` + int32_t b_wire_available(struct bvm *vm); + int32_t b_wire_available(struct bvm *vm) { + int32_t top = be_top(vm); // Get the number of arguments + TwoWire & myWire = getWire(vm); + if (top == 1) { + size_t available = myWire.available(); + be_pushint(vm, available); + be_return(vm); // Return + } + be_raise(vm, kTypeError, nullptr); + } + + // Berry: `write(value:int | s:string) -> nil` + 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 + TwoWire & myWire = getWire(vm); + if (top == 2 && (be_isint(vm, 2) || be_isstring(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 { + be_return_nil(vm); + } + be_return(vm); // Return + } + be_raise(vm, kTypeError, nullptr); + } + + // Berry: `read() -> int` + int32_t b_wire_read(struct bvm *vm); + int32_t b_wire_read(struct bvm *vm) { + int32_t top = be_top(vm); // Get the number of arguments + TwoWire & myWire = getWire(vm); + if (top == 1) { + int32_t value = myWire.read(); + be_pushint(vm, value); + be_return(vm); // Return + } + be_raise(vm, kTypeError, nullptr); + } + + int32_t b_wire_scan(struct bvm *vm); + int32_t b_wire_scan(struct bvm *vm) { + int32_t top = be_top(vm); // Get the number of arguments + TwoWire & myWire = getWire(vm); + if (top == 1) { + be_newobject(vm, "list"); + for (uint8_t address = 1; address <= 127; address++) { + myWire.beginTransmission(address); + int32_t error = myWire.endTransmission(); + if (0 == error) { + be_pushint(vm, address); + be_data_push(vm, -2); + be_pop(vm, 1); + } + } + be_pop(vm, 1); + be_return(vm); // Return + } + be_raise(vm, kTypeError, nullptr); + } + + // Berry: `validwrite(address:int, reg:int, val:int, size:int) -> bool or nil` + int32_t b_wire_validwrite(struct bvm *vm); + int32_t b_wire_validwrite(struct bvm *vm) { + int32_t top = be_top(vm); // Get the number of arguments + int32_t bus = getBus(vm); + if (top == 5 && be_isint(vm, 2) && be_isint(vm, 3) && be_isint(vm, 4) && be_isint(vm, 5)) { + uint8_t addr = be_toint(vm, 2); + uint8_t reg = be_toint(vm, 3); + uint8_t val = be_toint(vm, 4); + uint8_t size = be_toint(vm, 5); + bool ok = I2cWrite(addr, reg, val, size, bus); + be_pushbool(vm, ok); + be_return(vm); // Return + } + be_raise(vm, kTypeError, nullptr); + } + + // Berry: `validread(address:int, reg:int, size:int) -> int or nil` + int32_t b_wire_validread(struct bvm *vm); + int32_t b_wire_validread(struct bvm *vm) { + int32_t top = be_top(vm); // Get the number of arguments + int32_t bus = getBus(vm); + if (top == 4 && be_isint(vm, 2) && be_isint(vm, 3) && be_isint(vm, 4)) { + 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 + if (ok) { + be_pushint(vm, i2c_buffer); + } else { + be_pushnil(vm); + } + be_return(vm); // Return + } + be_raise(vm, kTypeError, nullptr); + } +#else // USE_I2C + // + int32_t b_wire_i2cmissing(struct bvm *vm); + int32_t b_wire_i2cmissing(struct bvm *vm) { + be_raise(vm, "feature_error", "I2C is not enabled, use '#define USE_I2C'"); + } + + // define weak aliases + int32_t b_wire_init(struct bvm *vm) __attribute__ ((weak, alias ("b_wire_i2cmissing"))); + int32_t b_wire_begintransmission(struct bvm *vm) __attribute__ ((weak, alias ("b_wire_i2cmissing"))); + int32_t b_wire_endtransmission(struct bvm *vm) __attribute__ ((weak, alias ("b_wire_i2cmissing"))); + int32_t b_wire_requestfrom(struct bvm *vm) __attribute__ ((weak, alias ("b_wire_i2cmissing"))); + int32_t b_wire_available(struct bvm *vm) __attribute__ ((weak, alias ("b_wire_i2cmissing"))); + int32_t b_wire_write(struct bvm *vm) __attribute__ ((weak, alias ("b_wire_i2cmissing"))); + int32_t b_wire_read(struct bvm *vm) __attribute__ ((weak, alias ("b_wire_i2cmissing"))); + 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"))); +#endif // USE_I2C +} + +#endif // USE_BERRY diff --git a/tasmota/xdrv_52_7_berry_embedded.ino b/tasmota/xdrv_52_7_berry_embedded.ino index b9575da81..83a3895e7 100644 --- a/tasmota/xdrv_52_7_berry_embedded.ino +++ b/tasmota/xdrv_52_7_berry_embedded.ino @@ -32,60 +32,33 @@ const char berry_prog[] = //"def noop() log('noop before'); yield(); log('middle after'); yield(); log('noop after'); end " //"log(\"foobar\") " + // create a 'ntv' module to allow functions to be registered in a safe namespace + "ntv = module('ntv') " + // auto-import modules // // import alias - "import wire " + "import energy " // Phase 1 - // Prepare the super class that will be eventually in Flash - "class Tasmota_ntv " - "var _op, _operators, _rules, _timers, _cmd " - - // Map all native functions to methods - // Again, this will be eventually pre-compiled - "var getfreeheap, publish, cmd, getoption, millis, timereached, yield " - "var respcmnd, respcmndstr, respcmnd_done, respcmnd_error, respcmnd_failed, resolvecmnd " - "var getlight " - "def init_ntv() " - "import tasmota_ntv " - "self.getfreeheap = tasmota_ntv.getfreeheap " - "self.publish = tasmota_ntv.publish " - "self.cmd = tasmota_ntv.cmd " - "self.getoption = tasmota_ntv.getoption " - "self.millis = tasmota_ntv.millis " - "self.timereached = tasmota_ntv.timereached " - "self.yield = tasmota_ntv.yield " - "self._operators = tasmota_ntv._operators " - - "self.respcmnd = tasmota_ntv.respcmnd " - "self.respcmndstr = tasmota_ntv.respcmndstr " - "self.respcmnd_done = tasmota_ntv.respcmnd_done " - "self.respcmnd_error = tasmota_ntv.respcmnd_error " - "self.respcmnd_failed = tasmota_ntv.respcmnd_failed " - "self.resolvecmnd = tasmota_ntv.resolvecmnd " - - "self.getlight = tasmota_ntv.getlight " - "end " - + "class Tasmota: Tasmota_ntv " + // for now the variables are built, need to find a way to push in Flash "def init() " - "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._op = ['==', '!==', '=', '!=', '>=', '<=', '>', '<'] " + "self._opf = [ " + "/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._operators = \"=<>!|\" " "self._rules = {} " "self._timers = [] " "self._cmd = {} " - "self.init_ntv() " "end " - "end " - - "class Tasmota: Tasmota_ntv " // add `charsinstring(s:string,c:string) -> int`` // looks for any char in c, and return the position of the first chat // or -1 if not found @@ -125,10 +98,11 @@ const char berry_prog[] = "var op_left = op_split[0] " "var op_rest = op_split[1] " // # iterate through operators - "for op: self._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] " + "for i: 0..size(self._op)-1 " + "var op = self._op[i] " + "if string.find(op_rest,op) == 0 " + "var op_func = self._opf[i] " + "var op_right = string.split(op_rest,size(op))[1] " "return [op_left,op_func,op_right] " "end " "end " @@ -137,7 +111,6 @@ const char berry_prog[] = "end " // Rules trigger if match. return true if match, false if not - // Note: condition is not yet managed "def try_rule(ev, rule, f) " "import string " "var rl_list = self.find_op(rule) " @@ -229,6 +202,8 @@ const char berry_prog[] = // Instantiate tasmota object "tasmota = Tasmota() " + "wire = Wire(0) " + "wire1 = Wire(1) " // Not sure how to run call methods from C "def _exec_rules(e) return tasmota.exec_rules(e) end " @@ -253,19 +228,30 @@ const char berry_prog[] = "var c = compile(f,'file') " // save the compiled bytecode "if !native " - "save(f+'c', c) " + "try " + "save(f+'c', c) " + "except .. as e " + "log(string.format('BRY: could not save compiled file %s (%s)',f+'c',e)) " + "end " "end " // call the compiled code "c() " + "log(string.format(\"BRY: sucessfully loaded '%s'\",f)) " "except .. as e " - "log(string.format(\"BRY: could not load file '%s' - %s\",f,e)) " + "raise \"io_error\",string.format(\"Could not load file '%s'\",f) " "end " "end " // try to load "/autoexec.be" // "try compile('/autoexec.be','file')() except .. log('BRY: no /autoexec.bat file') end " - - // Wire ; +const char berry_autoexec[] = + // load "autoexec.be" using import, which loads either .be or .bec file + "try " + "load('autoexec.be') " + "except .. " + "log(\"BRY: No 'autoexec.be' file\") " + "end " + ; #endif // USE_BERRY diff --git a/tasmota/xdrv_52_9_berry.ino b/tasmota/xdrv_52_9_berry.ino index fbbda679e..976e9e601 100644 --- a/tasmota/xdrv_52_9_berry.ino +++ b/tasmota/xdrv_52_9_berry.ino @@ -36,8 +36,7 @@ class BerrySupport { public: bvm *vm = nullptr; // berry vm bool rules_busy = false; // are we already processing rules, avoid infinite loop - const char *fname = nullptr; // name of berry function to call - int32_t fret = 0; + bool autoexec_done = false; // do we still need to load 'autoexec.be' }; BerrySupport berry; @@ -59,10 +58,6 @@ void checkBeTop(void) { * \*********************************************************************************************/ // // call a function (if exists) of type void -> void -// void callBerryFunctionVoid_berry(const char * fname) { -// berry.fret = 0; -// callBerryFunctionVoid(berry.fname); -// } bool callBerryRule(void) { if (berry.rules_busy) { return false; } @@ -244,7 +239,7 @@ void BrReset(void) { ret_code2 = be_pcall(berry.vm, 0); if (ret_code1 != 0) { AddLog(LOG_LEVEL_ERROR, PSTR(D_LOG_BERRY "ERROR: be_pcall [%s] %s"), be_tostring(berry.vm, -2), be_tostring(berry.vm, -1)); - be_pop(berry.vm, 2); + be_pop(berry.vm, 1); break; } // AddLog(LOG_LEVEL_DEBUG, PSTR(D_LOG_BERRY "Berry code ran, RAM used=%u"), be_gc_memcount(berry.vm)); @@ -268,6 +263,32 @@ void BrReset(void) { } } + +void BrAutoexec(void) { + if (berry.vm == nullptr) { return; } + + int32_t ret_code1, ret_code2; + bool berry_init_ok = false; + + // load 'autoexec.be' or 'autoexec.bec' + ret_code1 = be_loadstring(berry.vm, berry_autoexec); + // be_dumpstack(berry.vm); + if (ret_code1 != 0) { + be_pop(berry.vm, 2); + return; + } + ret_code2 = be_pcall(berry.vm, 0); + // be_dumpstack(berry.vm); + if (ret_code1 != 0) { + // AddLog(LOG_LEVEL_ERROR, PSTR(D_LOG_BERRY "ERROR: be_pcall [%s] %s"), be_tostring(berry.vm, -2), be_tostring(berry.vm, -1)); + be_pop(berry.vm, 1); + return; + } + // AddLog(LOG_LEVEL_DEBUG, PSTR(D_LOG_BERRY "Berry code ran, RAM used=%u"), be_gc_memcount(berry.vm)); + be_pop(berry.vm, 1); + // be_dumpstack(berry.vm); +} + /*********************************************************************************************\ * Tasmota Commands \*********************************************************************************************/ @@ -304,11 +325,11 @@ void CmndBrRun(void) { // AddLog(LOG_LEVEL_INFO, "run: type(2)=%s", be_typename(berry.vm, 2)); // code taken from REPL, look first at top, and if nil, look at return value - if (!be_isnil(berry.vm, 1)) { + // if (!be_isnil(berry.vm, 1)) { ret_val = be_tostring(berry.vm, 1); - } else { - ret_val = be_tostring(berry.vm, 2); - } + // } else { + // ret_val = be_tostring(berry.vm, 2); + // } Response_P("{\"" D_PRFX_BR "\":\"%s\"}", EscapeJSONString(ret_val).c_str()); // can't use XdrvMailbox.command as it may have been overwritten by subcommand be_pop(berry.vm, 1); } else { @@ -340,6 +361,12 @@ bool Xdrv52(uint8_t function) case FUNC_INIT: BrReset(); break; + case FUNC_LOOP: + if (!berry.autoexec_done) { + BrAutoexec(); + berry.autoexec_done = true; + } + break; case FUNC_EVERY_50_MSECOND: callBerryFunctionVoid(PSTR("_run_deferred")); break; @@ -383,8 +410,6 @@ bool Xdrv52(uint8_t function) case FUNC_BUTTON_PRESSED: break; - case FUNC_LOOP: - break; } return result; From 3be135cb1c1ba1df98f9a0d5d92d712f2a7a609c Mon Sep 17 00:00:00 2001 From: Jason2866 <24528715+Jason2866@users.noreply.github.com> Date: Sun, 14 Mar 2021 11:21:55 +0100 Subject: [PATCH 21/87] Tasmota32 with env --- platformio_override_sample.ini | 2 + platformio_tasmota_env32.ini | 70 ++++++++++++++------------ tasmota/tasmota.h | 1 - tasmota/tasmota_configurations.h | 9 ---- tasmota/tasmota_configurations_ESP32.h | 11 +--- 5 files changed, 41 insertions(+), 52 deletions(-) diff --git a/platformio_override_sample.ini b/platformio_override_sample.ini index da565a651..380145cd6 100644 --- a/platformio_override_sample.ini +++ b/platformio_override_sample.ini @@ -29,6 +29,8 @@ default_envs = ; tasmota32 ; tasmota32-bluetooth ; tasmota32-webcam +; tasmota32-knx +; tasmota32-sensors ; tasmota32-display ; tasmota32-ir ; tasmota32-ircustom diff --git a/platformio_tasmota_env32.ini b/platformio_tasmota_env32.ini index 29e8d69a5..219776576 100644 --- a/platformio_tasmota_env32.ini +++ b/platformio_tasmota_env32.ini @@ -1,4 +1,4 @@ -[env:tasmota32] +[env:tasmota32_base] framework = ${common.framework} platform = ${common32.platform} platform_packages = ${common32.platform_packages} @@ -30,8 +30,12 @@ lib_ignore = ; Disable next if you want to use ArduinoOTA in Tasmota32 (default disabled) ArduinoOTA +[env:tasmota32] +extends = env:tasmota32_base +build_flags = ${common32.build_flags} -DFIRMWARE_TASMOTA32 + [env:tasmota32-webcam] -extends = env:tasmota32 +extends = env:tasmota32_base board = esp32cam board_build.f_cpu = 240000000L board_build.flash_mode = qio @@ -40,7 +44,7 @@ build_flags = ${common32.build_flags} -DBOARD_HAS_PSRAM -mfix-esp32- lib_extra_dirs = lib/libesp32, lib/lib_basic [env:tasmota32-odroidgo] -extends = env:tasmota32 +extends = env:tasmota32_base board = odroid_esp32 board_build.f_cpu = 240000000L board_build.flash_mode = qio @@ -51,7 +55,7 @@ build_flags = ${common32.build_flags} -DBOARD_HAS_PSRAM -mfix-esp32- lib_extra_dirs = lib/libesp32, lib/lib_basic, lib/lib_i2c, lib/lib_rf, lib/lib_div, lib/lib_ssl, lib/lib_display [env:tasmota32-core2] -extends = env:tasmota32 +extends = env:tasmota32_base board = odroid_esp32 board_build.f_cpu = 240000000L board_build.flash_mode = qio @@ -62,120 +66,120 @@ build_flags = ${common32.build_flags} -DBOARD_HAS_PSRAM -mfix-esp32- lib_extra_dirs = lib/libesp32, lib/lib_basic, lib/lib_i2c, lib/lib_rf, lib/lib_div, lib/lib_ssl, lib/lib_display, lib/lib_audio [env:tasmota32-bluetooth] -extends = env:tasmota32 +extends = env:tasmota32_base build_flags = ${common32.build_flags} -DFIRMWARE_BLUETOOTH lib_extra_dirs = lib/libesp32, lib/libesp32_div, lib/lib_basic, lib/lib_i2c, lib/lib_ssl [env:tasmota32-display] -extends = env:tasmota32 +extends = env:tasmota32_base build_flags = ${common32.build_flags} -DFIRMWARE_DISPLAYS lib_extra_dirs = lib/libesp32, lib/lib_basic, lib/lib_display [env:tasmota32-ir] -extends = env:tasmota32 +extends = env:tasmota32_base build_flags = ${common32.build_flags} ${irremoteesp_full.build_flags} -DFIRMWARE_IR lib_extra_dirs = lib/libesp32, lib/lib_basic [env:tasmota32-ircustom] -extends = env:tasmota32 +extends = env:tasmota32_base build_flags = ${common32.build_flags} ${irremoteesp_full.build_flags} -DFIRMWARE_IR_CUSTOM [env:tasmota32-AF] -extends = env:tasmota32 +extends = env:tasmota32_base build_flags = ${common32.build_flags} -DMY_LANGUAGE=af_AF [env:tasmota32-BG] -extends = env:tasmota32 +extends = env:tasmota32_base build_flags = ${common32.build_flags} -DMY_LANGUAGE=bg_BG [env:tasmota32-BR] -extends = env:tasmota32 +extends = env:tasmota32_base build_flags = ${common32.build_flags} -DMY_LANGUAGE=pt_BR [env:tasmota32-CN] -extends = env:tasmota32 +extends = env:tasmota32_base build_flags = ${common32.build_flags} -DMY_LANGUAGE=zh_CN [env:tasmota32-CZ] -extends = env:tasmota32 +extends = env:tasmota32_base build_flags = ${common32.build_flags} -DMY_LANGUAGE=cs_CZ [env:tasmota32-DE] -extends = env:tasmota32 +extends = env:tasmota32_base build_flags = ${common32.build_flags} -DMY_LANGUAGE=de_DE [env:tasmota32-ES] -extends = env:tasmota32 +extends = env:tasmota32_base build_flags = ${common32.build_flags} -DMY_LANGUAGE=es_ES [env:tasmota32-FR] -extends = env:tasmota32 +extends = env:tasmota32_base build_flags = ${common32.build_flags} -DMY_LANGUAGE=fr_FR [env:tasmota32-FY] -extends = env:tasmota32 +extends = env:tasmota32_base build_flags = ${common32.build_flags} -DMY_LANGUAGE=fy_NL [env:tasmota32-GR] -extends = env:tasmota32 +extends = env:tasmota32_base build_flags = ${common32.build_flags} -DMY_LANGUAGE=el_GR [env:tasmota32-HE] -extends = env:tasmota32 +extends = env:tasmota32_base build_flags = ${common32.build_flags} -DMY_LANGUAGE=he_HE [env:tasmota32-HU] -extends = env:tasmota32 +extends = env:tasmota32_base build_flags = ${common32.build_flags} -DMY_LANGUAGE=hu_HU [env:tasmota32-IT] -extends = env:tasmota32 +extends = env:tasmota32_base build_flags = ${common32.build_flags} -DMY_LANGUAGE=it_IT [env:tasmota32-KO] -extends = env:tasmota32 +extends = env:tasmota32_base build_flags = ${common32.build_flags} -DMY_LANGUAGE=ko_KO [env:tasmota32-NL] -extends = env:tasmota32 +extends = env:tasmota32_base build_flags = ${common32.build_flags} -DMY_LANGUAGE=nl_NL [env:tasmota32-PL] -extends = env:tasmota32 +extends = env:tasmota32_base build_flags = ${common32.build_flags} -DMY_LANGUAGE=pl_PL [env:tasmota32-PT] -extends = env:tasmota32 +extends = env:tasmota32_base build_flags = ${common32.build_flags} -DMY_LANGUAGE=pt_PT [env:tasmota32-RO] -extends = env:tasmota32 +extends = env:tasmota32_base build_flags = ${common32.build_flags} -DMY_LANGUAGE=ro_RO [env:tasmota32-RU] -extends = env:tasmota32 +extends = env:tasmota32_base build_flags = ${common32.build_flags} -DMY_LANGUAGE=ru_RU [env:tasmota32-SE] -extends = env:tasmota32 +extends = env:tasmota32_base build_flags = ${common32.build_flags} -DMY_LANGUAGE=sv_SE [env:tasmota32-SK] -extends = env:tasmota32 +extends = env:tasmota32_base build_flags = ${common32.build_flags} -DMY_LANGUAGE=sk_SK [env:tasmota32-TR] -extends = env:tasmota32 +extends = env:tasmota32_base build_flags = ${common32.build_flags} -DMY_LANGUAGE=tr_TR [env:tasmota32-TW] -extends = env:tasmota32 +extends = env:tasmota32_base build_flags = ${common32.build_flags} -DMY_LANGUAGE=zh_TW [env:tasmota32-UK] -extends = env:tasmota32 +extends = env:tasmota32_base build_flags = ${common32.build_flags} -DMY_LANGUAGE=uk_UA [env:tasmota32-VN] -extends = env:tasmota32 +extends = env:tasmota32_base build_flags = ${common32.build_flags} -DMY_LANGUAGE=vi_VN diff --git a/tasmota/tasmota.h b/tasmota/tasmota.h index 6c3f2c7db..87e5f01b3 100644 --- a/tasmota/tasmota.h +++ b/tasmota/tasmota.h @@ -31,7 +31,6 @@ \*********************************************************************************************/ #define CODE_IMAGE_STR "tasmota" -#define FLAG_VARIANT_TASMOTA32 #define USE_LIGHT // Enable light control #define USE_ENERGY_SENSOR // Use energy sensors (+14k code) diff --git a/tasmota/tasmota_configurations.h b/tasmota/tasmota_configurations.h index beefe4d8b..c4956bdbd 100644 --- a/tasmota/tasmota_configurations.h +++ b/tasmota/tasmota_configurations.h @@ -29,8 +29,6 @@ #undef CODE_IMAGE_STR #define CODE_IMAGE_STR "sensors" -#undef FLAG_VARIANT_TASMOTA32 - #undef USE_DISCOVERY // Disable mDNS (+8k code or +23.5k code with core 2_5_x, +0.3k mem) @@ -233,7 +231,6 @@ #undef CODE_IMAGE_STR #define CODE_IMAGE_STR "knx" -#undef FLAG_VARIANT_TASMOTA32 #ifndef USE_KNX #define USE_KNX // Enable KNX IP Protocol Support (+23k code, +3k3 mem) @@ -260,7 +257,6 @@ #undef CODE_IMAGE_STR #define CODE_IMAGE_STR "display" -#undef FLAG_VARIANT_TASMOTA32 #undef USE_EMULATION // Disable Belkin WeMo and Hue Bridge emulation for Alexa (-16k code, -2k mem) #undef USE_EMULATION_HUE // Disable Hue Bridge emulation for Alexa (+14k code, +2k mem common) @@ -342,7 +338,6 @@ #undef CODE_IMAGE_STR #define CODE_IMAGE_STR "ir" -#undef FLAG_VARIANT_TASMOTA32 #undef USE_EMULATION #undef USE_EMULATION_HUE // Disable Hue emulation - only for lights and relays @@ -468,7 +463,6 @@ #undef CODE_IMAGE_STR #define CODE_IMAGE_STR "zbbridge" -#undef FLAG_VARIANT_TASMOTA32 #undef MODULE #define MODULE SONOFF_ZB_BRIDGE // [Module] Select default module from tasmota_template.h @@ -626,7 +620,6 @@ #undef CODE_IMAGE_STR #define CODE_IMAGE_STR "lite" -#undef FLAG_VARIANT_TASMOTA32 #undef APP_SLEEP #define APP_SLEEP 1 // Default to sleep = 1 for FIRMWARE_LITE @@ -759,7 +752,6 @@ #undef CODE_IMAGE_STR #define CODE_IMAGE_STR "minimal" -#undef FLAG_VARIANT_TASMOTA32 #undef FIRMWARE_LITE // Disable tasmota-lite with no sensors #undef FIRMWARE_SENSORS // Disable tasmota-sensors with useful sensors enabled @@ -907,7 +899,6 @@ #undef CODE_IMAGE_STR #define CODE_IMAGE_STR "mini-custom" -#undef FLAG_VARIANT_TASMOTA32 #undef FIRMWARE_LITE // Disable tasmota-lite with no sensors #undef FIRMWARE_SENSORS // Disable tasmota-sensors with useful sensors enabled diff --git a/tasmota/tasmota_configurations_ESP32.h b/tasmota/tasmota_configurations_ESP32.h index 990d95edc..3b0f969a4 100644 --- a/tasmota/tasmota_configurations_ESP32.h +++ b/tasmota/tasmota_configurations_ESP32.h @@ -31,7 +31,6 @@ #undef CODE_IMAGE_STR #define CODE_IMAGE_STR "webcam" -#undef FLAG_VARIANT_TASMOTA32 #define USE_WEBCAM #undef USE_MI_ESP32 // (ESP32 only) Disable support for ESP32 as a BLE-bridge (+9k2 mem, +292k flash) @@ -46,7 +45,6 @@ #undef CODE_IMAGE_STR #define CODE_IMAGE_STR "odroid-go" -#undef FLAG_VARIANT_TASMOTA32 #undef MODULE #define MODULE ODROID_GO // [Module] Select default module from tasmota_template.h @@ -74,7 +72,6 @@ #undef CODE_IMAGE_STR #define CODE_IMAGE_STR "core2" -#undef FLAG_VARIANT_TASMOTA32 #undef MODULE #define MODULE M5STACK_CORE2 // [Module] Select default module from tasmota_template.h @@ -129,7 +126,6 @@ #undef CODE_IMAGE_STR #define CODE_IMAGE_STR "bluetooth" -#undef FLAG_VARIANT_TASMOTA32 #undef MODULE #define MODULE WEMOS // [Module] Select default module from tasmota_template.h @@ -149,10 +145,7 @@ * Provide an image which includes KNX and Sensors \*********************************************************************************************/ -// FLAG_VARIANT_TASMOTA32 is set for variant "Tasmota32" -> tasmota.h -// for all other Tasmota build variants this flag needs to be undefined!! - -#ifdef FLAG_VARIANT_TASMOTA32 +#ifdef FIRMWARE_TASMOTA32 #define USE_ENHANCED_GUI_WIFI_SCAN @@ -325,7 +318,7 @@ #define USE_KNX // Enable KNX IP Protocol Support (+23k code, +3k3 mem) #endif -#endif // FLAG_VARIANT_TASMOTA32 +#endif // FIRMWARE_TASMOTA32 #endif // ESP32 #endif // _TASMOTA_CONFIGURATIONS_ESP32_H_ From 328c8a3204e962cce35005b6a85a9803da5806c7 Mon Sep 17 00:00:00 2001 From: Jason2866 <24528715+Jason2866@users.noreply.github.com> Date: Sun, 14 Mar 2021 11:24:39 +0100 Subject: [PATCH 22/87] no change --- tasmota/user_config_override_sample.h | 3 --- 1 file changed, 3 deletions(-) diff --git a/tasmota/user_config_override_sample.h b/tasmota/user_config_override_sample.h index 369585717..ae434bb9c 100644 --- a/tasmota/user_config_override_sample.h +++ b/tasmota/user_config_override_sample.h @@ -20,9 +20,6 @@ #ifndef _USER_CONFIG_OVERRIDE_H_ #define _USER_CONFIG_OVERRIDE_H_ -// this flag needs to be unset otherwise settings from build variant Tasmota32 are included -#undef FLAG_VARIANT_TASMOTA32 - // force the compiler to show a warning to confirm that this file is included #warning **** user_config_override.h: Using Settings from this File **** From be4a1294409145f05dfda2d6dd0f868b1126da0c Mon Sep 17 00:00:00 2001 From: Jason2866 <24528715+Jason2866@users.noreply.github.com> Date: Sun, 14 Mar 2021 11:31:21 +0100 Subject: [PATCH 23/87] env:tasmota32_base --- platformio_override_sample.ini | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/platformio_override_sample.ini b/platformio_override_sample.ini index 380145cd6..283c4405f 100644 --- a/platformio_override_sample.ini +++ b/platformio_override_sample.ini @@ -161,7 +161,7 @@ lib_extra_dirs = ; *** EXPERIMENTAL Tasmota version for ESP32solo1 (used in some Xiaomi devices) [env:tasmota32solo1] -extends = env:tasmota32 +extends = env:tasmota32_base platform_packages = framework-arduinoespressif32 @ https://github.com/Jason2866/esp32-arduino-lib-builder/raw/framework-arduinoespressif32/framework-arduinoespressif32-release_v3.3-solo1-bd65eb8d1.tar.gz platformio/tool-mklittlefs @ ~1.203.200522 platformio/tool-esptoolpy @ ~1.30000.0 @@ -170,7 +170,7 @@ build_flags = ${common32.build_flags} ; *** EXPERIMENTAL Tasmota version for ESP32-S2 [env:tasmota32s2] -extends = env:tasmota32 +extends = env:tasmota32_base board = esp32s2 board_build.flash_mode = qio platform_packages = framework-arduinoespressif32 @ https://github.com/Jason2866/arduino-esp32/releases/download/s2-1.0.5-rc6/esp32-s2-1.0.5-rc6.zip @@ -191,7 +191,7 @@ build_flags = ${esp82xx_defaults.build_flags} ; -Wstack-usage=300 [env:tasmota32-debug] -extends = env:tasmota32 +extends = env:tasmota32_base build_type = debug build_unflags = ${esp32_defaults.build_unflags} build_flags = ${esp32_defaults.build_flags} From bd791db7159aad921acd3801a2638b3feebe2c40 Mon Sep 17 00:00:00 2001 From: Jason2866 <24528715+Jason2866@users.noreply.github.com> Date: Sun, 14 Mar 2021 11:37:54 +0100 Subject: [PATCH 24/87] missing -DFIRMWARE_TASMOTA32 --- platformio_tasmota_env32.ini | 50 ++++++++++++++++++------------------ 1 file changed, 25 insertions(+), 25 deletions(-) diff --git a/platformio_tasmota_env32.ini b/platformio_tasmota_env32.ini index 219776576..f0b85e60b 100644 --- a/platformio_tasmota_env32.ini +++ b/platformio_tasmota_env32.ini @@ -86,100 +86,100 @@ build_flags = ${common32.build_flags} ${irremoteesp_full.build_flags [env:tasmota32-AF] extends = env:tasmota32_base -build_flags = ${common32.build_flags} -DMY_LANGUAGE=af_AF +build_flags = ${common32.build_flags} -DMY_LANGUAGE=af_AF -DFIRMWARE_TASMOTA32 [env:tasmota32-BG] extends = env:tasmota32_base -build_flags = ${common32.build_flags} -DMY_LANGUAGE=bg_BG +build_flags = ${common32.build_flags} -DMY_LANGUAGE=bg_BG -DFIRMWARE_TASMOTA32 [env:tasmota32-BR] extends = env:tasmota32_base -build_flags = ${common32.build_flags} -DMY_LANGUAGE=pt_BR +build_flags = ${common32.build_flags} -DMY_LANGUAGE=pt_BR -DFIRMWARE_TASMOTA32 [env:tasmota32-CN] extends = env:tasmota32_base -build_flags = ${common32.build_flags} -DMY_LANGUAGE=zh_CN +build_flags = ${common32.build_flags} -DMY_LANGUAGE=zh_CN -DFIRMWARE_TASMOTA32 [env:tasmota32-CZ] extends = env:tasmota32_base -build_flags = ${common32.build_flags} -DMY_LANGUAGE=cs_CZ +build_flags = ${common32.build_flags} -DMY_LANGUAGE=cs_CZ -DFIRMWARE_TASMOTA32 [env:tasmota32-DE] extends = env:tasmota32_base -build_flags = ${common32.build_flags} -DMY_LANGUAGE=de_DE +build_flags = ${common32.build_flags} -DMY_LANGUAGE=de_DE -DFIRMWARE_TASMOTA32 [env:tasmota32-ES] extends = env:tasmota32_base -build_flags = ${common32.build_flags} -DMY_LANGUAGE=es_ES +build_flags = ${common32.build_flags} -DMY_LANGUAGE=es_ES -DFIRMWARE_TASMOTA32 [env:tasmota32-FR] extends = env:tasmota32_base -build_flags = ${common32.build_flags} -DMY_LANGUAGE=fr_FR +build_flags = ${common32.build_flags} -DMY_LANGUAGE=fr_FR -DFIRMWARE_TASMOTA32 [env:tasmota32-FY] extends = env:tasmota32_base -build_flags = ${common32.build_flags} -DMY_LANGUAGE=fy_NL +build_flags = ${common32.build_flags} -DMY_LANGUAGE=fy_NL -DFIRMWARE_TASMOTA32 [env:tasmota32-GR] extends = env:tasmota32_base -build_flags = ${common32.build_flags} -DMY_LANGUAGE=el_GR +build_flags = ${common32.build_flags} -DMY_LANGUAGE=el_GR -DFIRMWARE_TASMOTA32 [env:tasmota32-HE] extends = env:tasmota32_base -build_flags = ${common32.build_flags} -DMY_LANGUAGE=he_HE +build_flags = ${common32.build_flags} -DMY_LANGUAGE=he_HE -DFIRMWARE_TASMOTA32 [env:tasmota32-HU] extends = env:tasmota32_base -build_flags = ${common32.build_flags} -DMY_LANGUAGE=hu_HU +build_flags = ${common32.build_flags} -DMY_LANGUAGE=hu_HU -DFIRMWARE_TASMOTA32 [env:tasmota32-IT] extends = env:tasmota32_base -build_flags = ${common32.build_flags} -DMY_LANGUAGE=it_IT +build_flags = ${common32.build_flags} -DMY_LANGUAGE=it_IT -DFIRMWARE_TASMOTA32 [env:tasmota32-KO] extends = env:tasmota32_base -build_flags = ${common32.build_flags} -DMY_LANGUAGE=ko_KO +build_flags = ${common32.build_flags} -DMY_LANGUAGE=ko_KO -DFIRMWARE_TASMOTA32 [env:tasmota32-NL] extends = env:tasmota32_base -build_flags = ${common32.build_flags} -DMY_LANGUAGE=nl_NL +build_flags = ${common32.build_flags} -DMY_LANGUAGE=nl_NL -DFIRMWARE_TASMOTA32 [env:tasmota32-PL] extends = env:tasmota32_base -build_flags = ${common32.build_flags} -DMY_LANGUAGE=pl_PL +build_flags = ${common32.build_flags} -DMY_LANGUAGE=pl_PL -DFIRMWARE_TASMOTA32 [env:tasmota32-PT] extends = env:tasmota32_base -build_flags = ${common32.build_flags} -DMY_LANGUAGE=pt_PT +build_flags = ${common32.build_flags} -DMY_LANGUAGE=pt_PT -DFIRMWARE_TASMOTA32 [env:tasmota32-RO] extends = env:tasmota32_base -build_flags = ${common32.build_flags} -DMY_LANGUAGE=ro_RO +build_flags = ${common32.build_flags} -DMY_LANGUAGE=ro_RO -DFIRMWARE_TASMOTA32 [env:tasmota32-RU] extends = env:tasmota32_base -build_flags = ${common32.build_flags} -DMY_LANGUAGE=ru_RU +build_flags = ${common32.build_flags} -DMY_LANGUAGE=ru_RU -DFIRMWARE_TASMOTA32 [env:tasmota32-SE] extends = env:tasmota32_base -build_flags = ${common32.build_flags} -DMY_LANGUAGE=sv_SE +build_flags = ${common32.build_flags} -DMY_LANGUAGE=sv_SE -DFIRMWARE_TASMOTA32 [env:tasmota32-SK] extends = env:tasmota32_base -build_flags = ${common32.build_flags} -DMY_LANGUAGE=sk_SK +build_flags = ${common32.build_flags} -DMY_LANGUAGE=sk_SK -DFIRMWARE_TASMOTA32 [env:tasmota32-TR] extends = env:tasmota32_base -build_flags = ${common32.build_flags} -DMY_LANGUAGE=tr_TR +build_flags = ${common32.build_flags} -DMY_LANGUAGE=tr_TR -DFIRMWARE_TASMOTA32 [env:tasmota32-TW] extends = env:tasmota32_base -build_flags = ${common32.build_flags} -DMY_LANGUAGE=zh_TW +build_flags = ${common32.build_flags} -DMY_LANGUAGE=zh_TW -DFIRMWARE_TASMOTA32 [env:tasmota32-UK] extends = env:tasmota32_base -build_flags = ${common32.build_flags} -DMY_LANGUAGE=uk_UA +build_flags = ${common32.build_flags} -DMY_LANGUAGE=uk_UA -DFIRMWARE_TASMOTA32 [env:tasmota32-VN] extends = env:tasmota32_base -build_flags = ${common32.build_flags} -DMY_LANGUAGE=vi_VN +build_flags = ${common32.build_flags} -DMY_LANGUAGE=vi_VN -DFIRMWARE_TASMOTA32 From 9645f3008eef1e88c49dc95d2b1b2774673d9c92 Mon Sep 17 00:00:00 2001 From: Theo Arends <11044339+arendst@users.noreply.github.com> Date: Mon, 15 Mar 2021 14:45:09 +0100 Subject: [PATCH 25/87] Fix string length overflow Fix string length overflow (#11347) --- tasmota/xdrv_12_home_assistant.ino | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/tasmota/xdrv_12_home_assistant.ino b/tasmota/xdrv_12_home_assistant.ino index 3280c8cb0..617ec2e39 100644 --- a/tasmota/xdrv_12_home_assistant.ino +++ b/tasmota/xdrv_12_home_assistant.ino @@ -270,10 +270,10 @@ void NewHAssDiscovery(void) char stopic[TOPSZ]; char stemp1[TOPSZ]; char stemp2[200]; - char stemp3[TOPSZ]; - char stemp4[TOPSZ]; - char stemp5[TOPSZ]; - char stemp6[TOPSZ]; + char switch_mode[90]; + char switch_name[300]; + char stemp5[90]; + char stemp6[90]; char unique_id[30]; char relays[TOPSZ]; char *state_topic = stemp1; @@ -297,14 +297,14 @@ void NewHAssDiscovery(void) snprintf_P(stemp2, sizeof(stemp2), PSTR("%s%s%s"), stemp2, (i > 0 ? "," : ""), (i < maxfn) ? fname : PSTR("null")); } - stemp3[0] = '\0'; - stemp4[0] = '\0'; + switch_mode[0] = '\0'; + switch_name[0] = '\0'; // Enable Discovery for Switches only if SetOption114 is enabled for (uint32_t i = 0; i < MAX_SWITCHES; i++) { char sname[TOPSZ]; snprintf_P(sname, sizeof(sname), PSTR("\"%s\""), GetSwitchText(i).c_str()); - snprintf_P(stemp3, sizeof(stemp3), PSTR("%s%s%d"), stemp3, (i > 0 ? "," : ""), (PinUsed(GPIO_SWT1, i) & Settings.flag5.mqtt_switches) ? Settings.switchmode[i] : -1); - snprintf_P(stemp4, sizeof(stemp4), PSTR("%s%s%s"), stemp4, (i > 0 ? "," : ""), (PinUsed(GPIO_SWT1, i) & Settings.flag5.mqtt_switches) ? sname : PSTR("null")); + snprintf_P(switch_mode, sizeof(switch_mode), PSTR("%s%s%d"), switch_mode, (i > 0 ? "," : ""), (PinUsed(GPIO_SWT1, i) & Settings.flag5.mqtt_switches) ? Settings.switchmode[i] : -1); + snprintf_P(switch_name, sizeof(switch_name), PSTR("%s%s%s"), switch_name, (i > 0 ? "," : ""), (PinUsed(GPIO_SWT1, i) & Settings.flag5.mqtt_switches) ? sname : PSTR("null")); } stemp5[0] = '\0'; @@ -338,7 +338,7 @@ void NewHAssDiscovery(void) if (!Settings.flag.hass_discovery) { // HassDiscoveryRelays(relays) Response_P(HASS_DISCOVER_DEVICE, (uint32_t)WiFi.localIP(), SettingsText(SET_DEVICENAME), stemp2, TasmotaGlobal.hostname, unique_id, ModuleName().c_str(), TuyaMod, iFanMod, GetStateText(0), GetStateText(1), GetStateText(2), GetStateText(3), - TasmotaGlobal.version, TasmotaGlobal.mqtt_topic, SettingsText(SET_MQTT_FULLTOPIC), PSTR(SUB_PREFIX), PSTR(PUB_PREFIX), PSTR(PUB_PREFIX2), Hass.RelLst, stemp3, stemp4, + TasmotaGlobal.version, TasmotaGlobal.mqtt_topic, SettingsText(SET_MQTT_FULLTOPIC), PSTR(SUB_PREFIX), PSTR(PUB_PREFIX), PSTR(PUB_PREFIX2), Hass.RelLst, switch_mode, switch_name, stemp5, Settings.flag.mqtt_response, Settings.flag.button_swap, Settings.flag.button_single, Settings.flag.decimal_text, Settings.flag.not_power_linked, Settings.flag.hass_light, Settings.flag3.pwm_multi_channels, Settings.flag3.mqtt_buttons, Settings.flag4.alexa_ct_range, Settings.flag5.mqtt_switches, Settings.flag5.fade_fixed_duration, light_controller.isCTRGBLinked(), Light.subtype, stemp6); From 3555b9e5f9753ead0ab95389044ccde6c4f11ecb Mon Sep 17 00:00:00 2001 From: Theo Arends <11044339+arendst@users.noreply.github.com> Date: Mon, 15 Mar 2021 15:51:31 +0100 Subject: [PATCH 26/87] Init RTC sooner --- tasmota/tasmota.ino | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tasmota/tasmota.ino b/tasmota/tasmota.ino index 54db9ae17..c24b8733a 100644 --- a/tasmota/tasmota.ino +++ b/tasmota/tasmota.ino @@ -338,6 +338,8 @@ void setup(void) { snprintf_P(TasmotaGlobal.hostname, sizeof(TasmotaGlobal.hostname)-1, SettingsText(SET_HOSTNAME)); } + RtcInit(); + GpioInit(); WifiConnect(); @@ -350,7 +352,7 @@ void setup(void) { AddLog(LOG_LEVEL_INFO, PSTR(D_WARNING_MINIMAL_VERSION)); #endif // FIRMWARE_MINIMAL - RtcInit(); +// RtcInit(); #ifdef USE_ARDUINO_OTA ArduinoOTAInit(); From 95e696075e4ff93a492b67df3a4223aee2c96a7d Mon Sep 17 00:00:00 2001 From: Stephan Hadinger Date: Mon, 15 Mar 2021 21:06:50 +0100 Subject: [PATCH 27/87] Add support for MPU6686 on primary or secondary I2C bus --- CHANGELOG.md | 1 + I2CDEVICES.md | 1 + lib/lib_i2c/MFRC522_I2C/library.properties | 9 + lib/lib_i2c/MFRC522_I2C/src/MFRC522_I2C.cpp | 1752 +++++++++++++++++ lib/lib_i2c/MFRC522_I2C/src/MFRC522_I2C.h | 407 ++++ lib/lib_i2c/MPU6886/library.properties | 9 + .../MPU6886/src}/MPU6886.cpp | 98 +- .../MPU6886/src}/MPU6886.h | 16 +- lib/libesp32/CORE2_Library/MahonyAHRS.cpp | 254 --- lib/libesp32/CORE2_Library/MahonyAHRS.h | 33 - tasmota/i18n.h | 1 + tasmota/my_user_config.h | 1 + tasmota/support.ino | 25 +- tasmota/xdrv_52_3_berry_wire.ino | 4 +- tasmota/xdrv_84_esp32_core2.ino | 35 +- tasmota/xsns_85_mpu6886.ino | 128 ++ 16 files changed, 2411 insertions(+), 363 deletions(-) create mode 100644 lib/lib_i2c/MFRC522_I2C/library.properties create mode 100644 lib/lib_i2c/MFRC522_I2C/src/MFRC522_I2C.cpp create mode 100644 lib/lib_i2c/MFRC522_I2C/src/MFRC522_I2C.h create mode 100644 lib/lib_i2c/MPU6886/library.properties rename lib/{libesp32/CORE2_Library => lib_i2c/MPU6886/src}/MPU6886.cpp (68%) rename lib/{libesp32/CORE2_Library => lib_i2c/MPU6886/src}/MPU6886.h (82%) delete mode 100755 lib/libesp32/CORE2_Library/MahonyAHRS.cpp delete mode 100755 lib/libesp32/CORE2_Library/MahonyAHRS.h create mode 100644 tasmota/xsns_85_mpu6886.ino diff --git a/CHANGELOG.md b/CHANGELOG.md index a2e722eac..1c3c9ca0f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,6 +18,7 @@ All notable changes to this project will be documented in this file. - ESP32 Extent BLE (#11212) - ESP32 support for WS2812 hardware driver via RMT or I2S - ESP32 support for secondary I2C controller +- Add support for MPU6686 on primary or secondary I2C bus ### Changed diff --git a/I2CDEVICES.md b/I2CDEVICES.md index 60ee9608b..8d5e34459 100644 --- a/I2CDEVICES.md +++ b/I2CDEVICES.md @@ -91,3 +91,4 @@ Index | Define | Driver | Device | Address(es) | Description 55 | USE_EZOPMP | xsns_78 | EZOPMP | 0x61 - 0x70 | Peristaltic Pump 56 | USE_SEESAW_SOIL | xsns_81 | SEESOIL | 0x36 - 0x39 | Adafruit seesaw soil moisture sensor 57 | USE_TOF10120 | xsns_84 | TOF10120 | 0x52 | Time-of-flight (ToF) distance sensor + 58 | USE_MPU6886 | xsns_85 | MPU6886 | 0x68 | MPU6886 M5Stack \ No newline at end of file diff --git a/lib/lib_i2c/MFRC522_I2C/library.properties b/lib/lib_i2c/MFRC522_I2C/library.properties new file mode 100644 index 000000000..6253b9d93 --- /dev/null +++ b/lib/lib_i2c/MFRC522_I2C/library.properties @@ -0,0 +1,9 @@ +name=MFRC522_I2C +version= +author=AROZCAN, COOQROBOT +maintainer= +sentence=I2C port of MFRC522 SPI +paragraph=Library to use ARDUINO RFID MODULE KIT 13.56 MHZ WITH TAGS I2C, Based on ARDUINO RFID MODULE KIT 13.56 MHZ WITH TAGS SPI Library BY COOQROBOT. +category=Communication +url=https://github.com/m5stack/M5StickC/blob/master/examples/Unit/RFID/ +architectures=esp8266,esp32 diff --git a/lib/lib_i2c/MFRC522_I2C/src/MFRC522_I2C.cpp b/lib/lib_i2c/MFRC522_I2C/src/MFRC522_I2C.cpp new file mode 100644 index 000000000..d63ee8d3e --- /dev/null +++ b/lib/lib_i2c/MFRC522_I2C/src/MFRC522_I2C.cpp @@ -0,0 +1,1752 @@ +/* +* MFRC522.cpp - Library to use ARDUINO RFID MODULE KIT 13.56 MHZ WITH TAGS I2C BY AROZCAN +* MFRC522.cpp - Based on ARDUINO RFID MODULE KIT 13.56 MHZ WITH TAGS SPI Library BY COOQROBOT. +* NOTE: Please also check the comments in MFRC522.h - they provide useful hints and background information. +* Released into the public domain. +* Author: arozcan @ https://github.com/arozcan/MFRC522-I2C-Library +*/ + +#include +#include +#include "MFRC522_I2C.h" + + +///////////////////////////////////////////////////////////////////////////////////// +// Functions for setting up the Arduino +///////////////////////////////////////////////////////////////////////////////////// + +/** + * Constructor. + * Prepares the output pins. + */ +MFRC522::MFRC522( byte chipAddress + //byte resetPowerDownPin ///< Arduino pin connected to MFRC522's reset and power down input (Pin 6, NRSTPD, active low) + ) { + _chipAddress = chipAddress; + // _resetPowerDownPin = resetPowerDownPin; +} // End constructor + + +///////////////////////////////////////////////////////////////////////////////////// +// Basic interface functions for communicating with the MFRC522 +///////////////////////////////////////////////////////////////////////////////////// + +/** + * Writes a byte to the specified register in the MFRC522 chip. + * The interface is described in the datasheet section 8.1.2. + */ +void MFRC522::PCD_WriteRegister( byte reg, ///< The register to write to. One of the PCD_Register enums. + byte value ///< The value to write. + ) { + Wire.beginTransmission(_chipAddress); + Wire.write(reg); + Wire.write(value); + Wire.endTransmission(); +} // End PCD_WriteRegister() + +/** + * Writes a number of bytes to the specified register in the MFRC522 chip. + * The interface is described in the datasheet section 8.1.2. + */ +void MFRC522::PCD_WriteRegister( byte reg, ///< The register to write to. One of the PCD_Register enums. + byte count, ///< The number of bytes to write to the register + byte *values ///< The values to write. Byte array. + ) { + Wire.beginTransmission(_chipAddress); + Wire.write(reg); + for (byte index = 0; index < count; index++) { + Wire.write(values[index]); + } + Wire.endTransmission(); +} // End PCD_WriteRegister() + +/** + * Reads a byte from the specified register in the MFRC522 chip. + * The interface is described in the datasheet section 8.1.2. + */ +byte MFRC522::PCD_ReadRegister( byte reg ///< The register to read from. One of the PCD_Register enums. + ) { + byte value; + //digitalWrite(_chipSelectPin, LOW); // Select slave + Wire.beginTransmission(_chipAddress); + Wire.write(reg); + Wire.endTransmission(); + + Wire.requestFrom(_chipAddress, (byte)1); + value = Wire.read(); + return value; +} // End PCD_ReadRegister() + +/** + * Reads a number of bytes from the specified register in the MFRC522 chip. + * The interface is described in the datasheet section 8.1.2. + */ +void MFRC522::PCD_ReadRegister( byte reg, ///< The register to read from. One of the PCD_Register enums. + byte count, ///< The number of bytes to read + byte *values, ///< Byte array to store the values in. + byte rxAlign ///< Only bit positions rxAlign..7 in values[0] are updated. + ) { + if (count == 0) { + return; + } + byte address = reg; + byte index = 0; // Index in values array. + Wire.beginTransmission(_chipAddress); + Wire.write(address); + Wire.endTransmission(); + Wire.requestFrom(_chipAddress, count); + while (Wire.available()) { + if (index == 0 && rxAlign) { // Only update bit positions rxAlign..7 in values[0] + // Create bit mask for bit positions rxAlign..7 + byte mask = 0; + for (byte i = rxAlign; i <= 7; i++) { + mask |= (1 << i); + } + // Read value and tell that we want to read the same address again. + byte value = Wire.read(); + // Apply mask to both current value of values[0] and the new data in value. + values[0] = (values[index] & ~mask) | (value & mask); + } + else { // Normal case + values[index] = Wire.read(); + } + index++; + } +} // End PCD_ReadRegister() + +/** + * Sets the bits given in mask in register reg. + */ +void MFRC522::PCD_SetRegisterBitMask( byte reg, ///< The register to update. One of the PCD_Register enums. + byte mask ///< The bits to set. + ) { + byte tmp; + tmp = PCD_ReadRegister(reg); + PCD_WriteRegister(reg, tmp | mask); // set bit mask +} // End PCD_SetRegisterBitMask() + +/** + * Clears the bits given in mask from register reg. + */ +void MFRC522::PCD_ClearRegisterBitMask( byte reg, ///< The register to update. One of the PCD_Register enums. + byte mask ///< The bits to clear. + ) { + byte tmp; + tmp = PCD_ReadRegister(reg); + PCD_WriteRegister(reg, tmp & (~mask)); // clear bit mask +} // End PCD_ClearRegisterBitMask() + + +/** + * Use the CRC coprocessor in the MFRC522 to calculate a CRC_A. + * + * @return STATUS_OK on success, STATUS_??? otherwise. + */ +byte MFRC522::PCD_CalculateCRC( byte *data, ///< In: Pointer to the data to transfer to the FIFO for CRC calculation. + byte length, ///< In: The number of bytes to transfer. + byte *result ///< Out: Pointer to result buffer. Result is written to result[0..1], low byte first. + ) { + PCD_WriteRegister(CommandReg, PCD_Idle); // Stop any active command. + PCD_WriteRegister(DivIrqReg, 0x04); // Clear the CRCIRq interrupt request bit + PCD_SetRegisterBitMask(FIFOLevelReg, 0x80); // FlushBuffer = 1, FIFO initialization + PCD_WriteRegister(FIFODataReg, length, data); // Write data to the FIFO + PCD_WriteRegister(CommandReg, PCD_CalcCRC); // Start the calculation + + // Wait for the CRC calculation to complete. Each iteration of the while-loop takes 17.73�s. + word i = 5000; + byte n; + while (1) { + n = PCD_ReadRegister(DivIrqReg); // DivIrqReg[7..0] bits are: Set2 reserved reserved MfinActIRq reserved CRCIRq reserved reserved + if (n & 0x04) { // CRCIRq bit set - calculation done + break; + } + if (--i == 0) { // The emergency break. We will eventually terminate on this one after 89ms. Communication with the MFRC522 might be down. + return STATUS_TIMEOUT; + } + } + PCD_WriteRegister(CommandReg, PCD_Idle); // Stop calculating CRC for new content in the FIFO. + + // Transfer the result from the registers to the result buffer + result[0] = PCD_ReadRegister(CRCResultRegL); + result[1] = PCD_ReadRegister(CRCResultRegH); + return STATUS_OK; +} // End PCD_CalculateCRC() + + +///////////////////////////////////////////////////////////////////////////////////// +// Functions for manipulating the MFRC522 +///////////////////////////////////////////////////////////////////////////////////// + +/** + * Initializes the MFRC522 chip. + */ +void MFRC522::PCD_Init() { + // Set the chipSelectPin as digital output, do not select the slave yet + + // Set the resetPowerDownPin as digital output, do not reset or power down. + // pinMode(_resetPowerDownPin, OUTPUT); + + + // if (digitalRead(_resetPowerDownPin) == LOW) { //The MFRC522 chip is in power down mode. + // digitalWrite(_resetPowerDownPin, HIGH); // Exit power down mode. This triggers a hard reset. + // // Section 8.8.2 in the datasheet says the oscillator start-up time is the start up time of the crystal + 37,74�s. Let us be generous: 50ms. + // delay(50); + // } + // else { // Perform a soft reset + PCD_Reset(); + // } + + // When communicating with a PICC we need a timeout if something goes wrong. + // f_timer = 13.56 MHz / (2*TPreScaler+1) where TPreScaler = [TPrescaler_Hi:TPrescaler_Lo]. + // TPrescaler_Hi are the four low bits in TModeReg. TPrescaler_Lo is TPrescalerReg. + PCD_WriteRegister(TModeReg, 0x80); // TAuto=1; timer starts automatically at the end of the transmission in all communication modes at all speeds + PCD_WriteRegister(TPrescalerReg, 0xA9); // TPreScaler = TModeReg[3..0]:TPrescalerReg, ie 0x0A9 = 169 => f_timer=40kHz, ie a timer period of 25�s. + PCD_WriteRegister(TReloadRegH, 0x03); // Reload timer with 0x3E8 = 1000, ie 25ms before timeout. + PCD_WriteRegister(TReloadRegL, 0xE8); + + PCD_WriteRegister(TxASKReg, 0x40); // Default 0x00. Force a 100 % ASK modulation independent of the ModGsPReg register setting + PCD_WriteRegister(ModeReg, 0x3D); // Default 0x3F. Set the preset value for the CRC coprocessor for the CalcCRC command to 0x6363 (ISO 14443-3 part 6.2.4) + PCD_AntennaOn(); // Enable the antenna driver pins TX1 and TX2 (they were disabled by the reset) +} // End PCD_Init() + +/** + * Performs a soft reset on the MFRC522 chip and waits for it to be ready again. + */ +void MFRC522::PCD_Reset() { + PCD_WriteRegister(CommandReg, PCD_SoftReset); // Issue the SoftReset command. + // The datasheet does not mention how long the SoftRest command takes to complete. + // But the MFRC522 might have been in soft power-down mode (triggered by bit 4 of CommandReg) + // Section 8.8.2 in the datasheet says the oscillator start-up time is the start up time of the crystal + 37,74�s. Let us be generous: 50ms. + delay(50); + // Wait for the PowerDown bit in CommandReg to be cleared + while (PCD_ReadRegister(CommandReg) & (1<<4)) { + // PCD still restarting - unlikely after waiting 50ms, but better safe than sorry. + } +} // End PCD_Reset() + +/** + * Turns the antenna on by enabling pins TX1 and TX2. + * After a reset these pins are disabled. + */ +void MFRC522::PCD_AntennaOn() { + byte value = PCD_ReadRegister(TxControlReg); + if ((value & 0x03) != 0x03) { + PCD_WriteRegister(TxControlReg, value | 0x03); + } +} // End PCD_AntennaOn() + +/** + * Turns the antenna off by disabling pins TX1 and TX2. + */ +void MFRC522::PCD_AntennaOff() { + PCD_ClearRegisterBitMask(TxControlReg, 0x03); +} // End PCD_AntennaOff() + +/** + * Get the current MFRC522 Receiver Gain (RxGain[2:0]) value. + * See 9.3.3.6 / table 98 in http://www.nxp.com/documents/data_sheet/MFRC522.pdf + * NOTE: Return value scrubbed with (0x07<<4)=01110000b as RCFfgReg may use reserved bits. + * + * @return Value of the RxGain, scrubbed to the 3 bits used. + */ +byte MFRC522::PCD_GetAntennaGain() { + return PCD_ReadRegister(RFCfgReg) & (0x07<<4); +} // End PCD_GetAntennaGain() + +/** + * Set the MFRC522 Receiver Gain (RxGain) to value specified by given mask. + * See 9.3.3.6 / table 98 in http://www.nxp.com/documents/data_sheet/MFRC522.pdf + * NOTE: Given mask is scrubbed with (0x07<<4)=01110000b as RCFfgReg may use reserved bits. + */ +void MFRC522::PCD_SetAntennaGain(byte mask) { + if (PCD_GetAntennaGain() != mask) { // only bother if there is a change + PCD_ClearRegisterBitMask(RFCfgReg, (0x07<<4)); // clear needed to allow 000 pattern + PCD_SetRegisterBitMask(RFCfgReg, mask & (0x07<<4)); // only set RxGain[2:0] bits + } +} // End PCD_SetAntennaGain() + +/** + * Performs a self-test of the MFRC522 + * See 16.1.1 in http://www.nxp.com/documents/data_sheet/MFRC522.pdf + * + * @return Whether or not the test passed. + */ +bool MFRC522::PCD_PerformSelfTest() { + // This follows directly the steps outlined in 16.1.1 + // 1. Perform a soft reset. + PCD_Reset(); + + // 2. Clear the internal buffer by writing 25 bytes of 00h + byte ZEROES[25] = {0x00}; + PCD_SetRegisterBitMask(FIFOLevelReg, 0x80); // flush the FIFO buffer + PCD_WriteRegister(FIFODataReg, 25, ZEROES); // write 25 bytes of 00h to FIFO + PCD_WriteRegister(CommandReg, PCD_Mem); // transfer to internal buffer + + // 3. Enable self-test + PCD_WriteRegister(AutoTestReg, 0x09); + + // 4. Write 00h to FIFO buffer + PCD_WriteRegister(FIFODataReg, 0x00); + + // 5. Start self-test by issuing the CalcCRC command + PCD_WriteRegister(CommandReg, PCD_CalcCRC); + + // 6. Wait for self-test to complete + word i; + byte n; + for (i = 0; i < 0xFF; i++) { + n = PCD_ReadRegister(DivIrqReg); // DivIrqReg[7..0] bits are: Set2 reserved reserved MfinActIRq reserved CRCIRq reserved reserved + if (n & 0x04) { // CRCIRq bit set - calculation done + break; + } + } + PCD_WriteRegister(CommandReg, PCD_Idle); // Stop calculating CRC for new content in the FIFO. + + // 7. Read out resulting 64 bytes from the FIFO buffer. + byte result[64]; + PCD_ReadRegister(FIFODataReg, 64, result, 0); + + // Auto self-test done + // Reset AutoTestReg register to be 0 again. Required for normal operation. + PCD_WriteRegister(AutoTestReg, 0x00); + + // Determine firmware version (see section 9.3.4.8 in spec) + byte version = PCD_ReadRegister(VersionReg); + + // Pick the appropriate reference values + const byte *reference; + switch (version) { + case 0x88: // Fudan Semiconductor FM17522 clone + reference = FM17522_firmware_reference; + break; + case 0x90: // Version 0.0 + reference = MFRC522_firmware_referenceV0_0; + break; + case 0x91: // Version 1.0 + reference = MFRC522_firmware_referenceV1_0; + break; + case 0x92: // Version 2.0 + reference = MFRC522_firmware_referenceV2_0; + break; + default: // Unknown version + return false; + } + + // Verify that the results match up to our expectations + for (i = 0; i < 64; i++) { + if (result[i] != pgm_read_byte(&(reference[i]))) { + return false; + } + } + + // Test passed; all is good. + return true; +} // End PCD_PerformSelfTest() + +///////////////////////////////////////////////////////////////////////////////////// +// Functions for communicating with PICCs +///////////////////////////////////////////////////////////////////////////////////// + +/** + * Executes the Transceive command. + * CRC validation can only be done if backData and backLen are specified. + * + * @return STATUS_OK on success, STATUS_??? otherwise. + */ +byte MFRC522::PCD_TransceiveData( byte *sendData, ///< Pointer to the data to transfer to the FIFO. + byte sendLen, ///< Number of bytes to transfer to the FIFO. + byte *backData, ///< NULL or pointer to buffer if data should be read back after executing the command. + byte *backLen, ///< In: Max number of bytes to write to *backData. Out: The number of bytes returned. + byte *validBits, ///< In/Out: The number of valid bits in the last byte. 0 for 8 valid bits. Default NULL. + byte rxAlign, ///< In: Defines the bit position in backData[0] for the first bit received. Default 0. + bool checkCRC ///< In: True => The last two bytes of the response is assumed to be a CRC_A that must be validated. + ) { + byte waitIRq = 0x30; // RxIRq and IdleIRq + return PCD_CommunicateWithPICC(PCD_Transceive, waitIRq, sendData, sendLen, backData, backLen, validBits, rxAlign, checkCRC); +} // End PCD_TransceiveData() + +/** + * Transfers data to the MFRC522 FIFO, executes a command, waits for completion and transfers data back from the FIFO. + * CRC validation can only be done if backData and backLen are specified. + * + * @return STATUS_OK on success, STATUS_??? otherwise. + */ +byte MFRC522::PCD_CommunicateWithPICC( byte command, ///< The command to execute. One of the PCD_Command enums. + byte waitIRq, ///< The bits in the ComIrqReg register that signals successful completion of the command. + byte *sendData, ///< Pointer to the data to transfer to the FIFO. + byte sendLen, ///< Number of bytes to transfer to the FIFO. + byte *backData, ///< NULL or pointer to buffer if data should be read back after executing the command. + byte *backLen, ///< In: Max number of bytes to write to *backData. Out: The number of bytes returned. + byte *validBits, ///< In/Out: The number of valid bits in the last byte. 0 for 8 valid bits. + byte rxAlign, ///< In: Defines the bit position in backData[0] for the first bit received. Default 0. + bool checkCRC ///< In: True => The last two bytes of the response is assumed to be a CRC_A that must be validated. + ) { + byte n, _validBits = 0; + unsigned int i; + + // Prepare values for BitFramingReg + byte txLastBits = validBits ? *validBits : 0; + byte bitFraming = (rxAlign << 4) + txLastBits; // RxAlign = BitFramingReg[6..4]. TxLastBits = BitFramingReg[2..0] + + PCD_WriteRegister(CommandReg, PCD_Idle); // Stop any active command. + PCD_WriteRegister(ComIrqReg, 0x7F); // Clear all seven interrupt request bits + PCD_SetRegisterBitMask(FIFOLevelReg, 0x80); // FlushBuffer = 1, FIFO initialization + PCD_WriteRegister(FIFODataReg, sendLen, sendData); // Write sendData to the FIFO + PCD_WriteRegister(BitFramingReg, bitFraming); // Bit adjustments + PCD_WriteRegister(CommandReg, command); // Execute the command + if (command == PCD_Transceive) { + PCD_SetRegisterBitMask(BitFramingReg, 0x80); // StartSend=1, transmission of data starts + } + + // Wait for the command to complete. + // In PCD_Init() we set the TAuto flag in TModeReg. This means the timer automatically starts when the PCD stops transmitting. + // Each iteration of the do-while-loop takes 17.86�s. + i = 2000; + while (1) { + n = PCD_ReadRegister(ComIrqReg); // ComIrqReg[7..0] bits are: Set1 TxIRq RxIRq IdleIRq HiAlertIRq LoAlertIRq ErrIRq TimerIRq + if (n & waitIRq) { // One of the interrupts that signal success has been set. + break; + } + if (n & 0x01) { // Timer interrupt - nothing received in 25ms + return STATUS_TIMEOUT; + } + if (--i == 0) { // The emergency break. If all other condions fail we will eventually terminate on this one after 35.7ms. Communication with the MFRC522 might be down. + return STATUS_TIMEOUT; + } + } + + // Stop now if any errors except collisions were detected. + byte errorRegValue = PCD_ReadRegister(ErrorReg); // ErrorReg[7..0] bits are: WrErr TempErr reserved BufferOvfl CollErr CRCErr ParityErr ProtocolErr + if (errorRegValue & 0x13) { // BufferOvfl ParityErr ProtocolErr + return STATUS_ERROR; + } + + // If the caller wants data back, get it from the MFRC522. + if (backData && backLen) { + n = PCD_ReadRegister(FIFOLevelReg); // Number of bytes in the FIFO + if (n > *backLen) { + return STATUS_NO_ROOM; + } + *backLen = n; // Number of bytes returned + PCD_ReadRegister(FIFODataReg, n, backData, rxAlign); // Get received data from FIFO + _validBits = PCD_ReadRegister(ControlReg) & 0x07; // RxLastBits[2:0] indicates the number of valid bits in the last received byte. If this value is 000b, the whole byte is valid. + if (validBits) { + *validBits = _validBits; + } + } + + // Tell about collisions + if (errorRegValue & 0x08) { // CollErr + return STATUS_COLLISION; + } + + // Perform CRC_A validation if requested. + if (backData && backLen && checkCRC) { + // In this case a MIFARE Classic NAK is not OK. + if (*backLen == 1 && _validBits == 4) { + return STATUS_MIFARE_NACK; + } + // We need at least the CRC_A value and all 8 bits of the last byte must be received. + if (*backLen < 2 || _validBits != 0) { + return STATUS_CRC_WRONG; + } + // Verify CRC_A - do our own calculation and store the control in controlBuffer. + byte controlBuffer[2]; + n = PCD_CalculateCRC(&backData[0], *backLen - 2, &controlBuffer[0]); + if (n != STATUS_OK) { + return n; + } + if ((backData[*backLen - 2] != controlBuffer[0]) || (backData[*backLen - 1] != controlBuffer[1])) { + return STATUS_CRC_WRONG; + } + } + + return STATUS_OK; +} // End PCD_CommunicateWithPICC() + +/** + * Transmits a REQuest command, Type A. Invites PICCs in state IDLE to go to READY and prepare for anticollision or selection. 7 bit frame. + * Beware: When two PICCs are in the field at the same time I often get STATUS_TIMEOUT - probably due do bad antenna design. + * + * @return STATUS_OK on success, STATUS_??? otherwise. + */ +byte MFRC522::PICC_RequestA(byte *bufferATQA, ///< The buffer to store the ATQA (Answer to request) in + byte *bufferSize ///< Buffer size, at least two bytes. Also number of bytes returned if STATUS_OK. + ) { + return PICC_REQA_or_WUPA(PICC_CMD_REQA, bufferATQA, bufferSize); +} // End PICC_RequestA() + +/** + * Transmits a Wake-UP command, Type A. Invites PICCs in state IDLE and HALT to go to READY(*) and prepare for anticollision or selection. 7 bit frame. + * Beware: When two PICCs are in the field at the same time I often get STATUS_TIMEOUT - probably due do bad antenna design. + * + * @return STATUS_OK on success, STATUS_??? otherwise. + */ +byte MFRC522::PICC_WakeupA( byte *bufferATQA, ///< The buffer to store the ATQA (Answer to request) in + byte *bufferSize ///< Buffer size, at least two bytes. Also number of bytes returned if STATUS_OK. + ) { + return PICC_REQA_or_WUPA(PICC_CMD_WUPA, bufferATQA, bufferSize); +} // End PICC_WakeupA() + +/** + * Transmits REQA or WUPA commands. + * Beware: When two PICCs are in the field at the same time I often get STATUS_TIMEOUT - probably due do bad antenna design. + * + * @return STATUS_OK on success, STATUS_??? otherwise. + */ +byte MFRC522::PICC_REQA_or_WUPA( byte command, ///< The command to send - PICC_CMD_REQA or PICC_CMD_WUPA + byte *bufferATQA, ///< The buffer to store the ATQA (Answer to request) in + byte *bufferSize ///< Buffer size, at least two bytes. Also number of bytes returned if STATUS_OK. + ) { + byte validBits; + byte status; + + if (bufferATQA == NULL || *bufferSize < 2) { // The ATQA response is 2 bytes long. + return STATUS_NO_ROOM; + } + PCD_ClearRegisterBitMask(CollReg, 0x80); // ValuesAfterColl=1 => Bits received after collision are cleared. + validBits = 7; // For REQA and WUPA we need the short frame format - transmit only 7 bits of the last (and only) byte. TxLastBits = BitFramingReg[2..0] + status = PCD_TransceiveData(&command, 1, bufferATQA, bufferSize, &validBits); + if (status != STATUS_OK) { + return status; + } + if (*bufferSize != 2 || validBits != 0) { // ATQA must be exactly 16 bits. + return STATUS_ERROR; + } + return STATUS_OK; +} // End PICC_REQA_or_WUPA() + +/** + * Transmits SELECT/ANTICOLLISION commands to select a single PICC. + * Before calling this function the PICCs must be placed in the READY(*) state by calling PICC_RequestA() or PICC_WakeupA(). + * On success: + * - The chosen PICC is in state ACTIVE(*) and all other PICCs have returned to state IDLE/HALT. (Figure 7 of the ISO/IEC 14443-3 draft.) + * - The UID size and value of the chosen PICC is returned in *uid along with the SAK. + * + * A PICC UID consists of 4, 7 or 10 bytes. + * Only 4 bytes can be specified in a SELECT command, so for the longer UIDs two or three iterations are used: + * UID size Number of UID bytes Cascade levels Example of PICC + * ======== =================== ============== =============== + * single 4 1 MIFARE Classic + * double 7 2 MIFARE Ultralight + * triple 10 3 Not currently in use? + * + * @return STATUS_OK on success, STATUS_??? otherwise. + */ +byte MFRC522::PICC_Select( Uid *uid, ///< Pointer to Uid struct. Normally output, but can also be used to supply a known UID. + byte validBits ///< The number of known UID bits supplied in *uid. Normally 0. If set you must also supply uid->size. + ) { + bool uidComplete; + bool selectDone; + bool useCascadeTag; + byte cascadeLevel = 1; + byte result; + byte count; + byte index; + byte uidIndex; // The first index in uid->uidByte[] that is used in the current Cascade Level. + int8_t currentLevelKnownBits; // The number of known UID bits in the current Cascade Level. + byte buffer[9]; // The SELECT/ANTICOLLISION commands uses a 7 byte standard frame + 2 bytes CRC_A + byte bufferUsed; // The number of bytes used in the buffer, ie the number of bytes to transfer to the FIFO. + byte rxAlign; // Used in BitFramingReg. Defines the bit position for the first bit received. + byte txLastBits; // Used in BitFramingReg. The number of valid bits in the last transmitted byte. + byte *responseBuffer; + byte responseLength; + + // Description of buffer structure: + // Byte 0: SEL Indicates the Cascade Level: PICC_CMD_SEL_CL1, PICC_CMD_SEL_CL2 or PICC_CMD_SEL_CL3 + // Byte 1: NVB Number of Valid Bits (in complete command, not just the UID): High nibble: complete bytes, Low nibble: Extra bits. + // Byte 2: UID-data or CT See explanation below. CT means Cascade Tag. + // Byte 3: UID-data + // Byte 4: UID-data + // Byte 5: UID-data + // Byte 6: BCC Block Check Character - XOR of bytes 2-5 + // Byte 7: CRC_A + // Byte 8: CRC_A + // The BCC and CRC_A is only transmitted if we know all the UID bits of the current Cascade Level. + // + // Description of bytes 2-5: (Section 6.5.4 of the ISO/IEC 14443-3 draft: UID contents and cascade levels) + // UID size Cascade level Byte2 Byte3 Byte4 Byte5 + // ======== ============= ===== ===== ===== ===== + // 4 bytes 1 uid0 uid1 uid2 uid3 + // 7 bytes 1 CT uid0 uid1 uid2 + // 2 uid3 uid4 uid5 uid6 + // 10 bytes 1 CT uid0 uid1 uid2 + // 2 CT uid3 uid4 uid5 + // 3 uid6 uid7 uid8 uid9 + + // Sanity checks + if (validBits > 80) { + return STATUS_INVALID; + } + + // Prepare MFRC522 + PCD_ClearRegisterBitMask(CollReg, 0x80); // ValuesAfterColl=1 => Bits received after collision are cleared. + + // Repeat Cascade Level loop until we have a complete UID. + uidComplete = false; + while (!uidComplete) { + // Set the Cascade Level in the SEL byte, find out if we need to use the Cascade Tag in byte 2. + switch (cascadeLevel) { + case 1: + buffer[0] = PICC_CMD_SEL_CL1; + uidIndex = 0; + useCascadeTag = validBits && uid->size > 4; // When we know that the UID has more than 4 bytes + break; + + case 2: + buffer[0] = PICC_CMD_SEL_CL2; + uidIndex = 3; + useCascadeTag = validBits && uid->size > 7; // When we know that the UID has more than 7 bytes + break; + + case 3: + buffer[0] = PICC_CMD_SEL_CL3; + uidIndex = 6; + useCascadeTag = false; // Never used in CL3. + break; + + default: + return STATUS_INTERNAL_ERROR; + break; + } + + // How many UID bits are known in this Cascade Level? + currentLevelKnownBits = validBits - (8 * uidIndex); + if (currentLevelKnownBits < 0) { + currentLevelKnownBits = 0; + } + // Copy the known bits from uid->uidByte[] to buffer[] + index = 2; // destination index in buffer[] + if (useCascadeTag) { + buffer[index++] = PICC_CMD_CT; + } + byte bytesToCopy = currentLevelKnownBits / 8 + (currentLevelKnownBits % 8 ? 1 : 0); // The number of bytes needed to represent the known bits for this level. + if (bytesToCopy) { + byte maxBytes = useCascadeTag ? 3 : 4; // Max 4 bytes in each Cascade Level. Only 3 left if we use the Cascade Tag + if (bytesToCopy > maxBytes) { + bytesToCopy = maxBytes; + } + for (count = 0; count < bytesToCopy; count++) { + buffer[index++] = uid->uidByte[uidIndex + count]; + } + } + // Now that the data has been copied we need to include the 8 bits in CT in currentLevelKnownBits + if (useCascadeTag) { + currentLevelKnownBits += 8; + } + + // Repeat anti collision loop until we can transmit all UID bits + BCC and receive a SAK - max 32 iterations. + selectDone = false; + while (!selectDone) { + // Find out how many bits and bytes to send and receive. + if (currentLevelKnownBits >= 32) { // All UID bits in this Cascade Level are known. This is a SELECT. + //Serial.print(F("SELECT: currentLevelKnownBits=")); Serial.println(currentLevelKnownBits, DEC); + buffer[1] = 0x70; // NVB - Number of Valid Bits: Seven whole bytes + // Calculate BCC - Block Check Character + buffer[6] = buffer[2] ^ buffer[3] ^ buffer[4] ^ buffer[5]; + // Calculate CRC_A + result = PCD_CalculateCRC(buffer, 7, &buffer[7]); + if (result != STATUS_OK) { + return result; + } + txLastBits = 0; // 0 => All 8 bits are valid. + bufferUsed = 9; + // Store response in the last 3 bytes of buffer (BCC and CRC_A - not needed after tx) + responseBuffer = &buffer[6]; + responseLength = 3; + } + else { // This is an ANTICOLLISION. + //Serial.print(F("ANTICOLLISION: currentLevelKnownBits=")); Serial.println(currentLevelKnownBits, DEC); + txLastBits = currentLevelKnownBits % 8; + count = currentLevelKnownBits / 8; // Number of whole bytes in the UID part. + index = 2 + count; // Number of whole bytes: SEL + NVB + UIDs + buffer[1] = (index << 4) + txLastBits; // NVB - Number of Valid Bits + bufferUsed = index + (txLastBits ? 1 : 0); + // Store response in the unused part of buffer + responseBuffer = &buffer[index]; + responseLength = sizeof(buffer) - index; + } + + // Set bit adjustments + rxAlign = txLastBits; // Having a seperate variable is overkill. But it makes the next line easier to read. + PCD_WriteRegister(BitFramingReg, (rxAlign << 4) + txLastBits); // RxAlign = BitFramingReg[6..4]. TxLastBits = BitFramingReg[2..0] + + // Transmit the buffer and receive the response. + result = PCD_TransceiveData(buffer, bufferUsed, responseBuffer, &responseLength, &txLastBits, rxAlign); + if (result == STATUS_COLLISION) { // More than one PICC in the field => collision. + result = PCD_ReadRegister(CollReg); // CollReg[7..0] bits are: ValuesAfterColl reserved CollPosNotValid CollPos[4:0] + if (result & 0x20) { // CollPosNotValid + return STATUS_COLLISION; // Without a valid collision position we cannot continue + } + byte collisionPos = result & 0x1F; // Values 0-31, 0 means bit 32. + if (collisionPos == 0) { + collisionPos = 32; + } + if (collisionPos <= currentLevelKnownBits) { // No progress - should not happen + return STATUS_INTERNAL_ERROR; + } + // Choose the PICC with the bit set. + currentLevelKnownBits = collisionPos; + count = (currentLevelKnownBits - 1) % 8; // The bit to modify + index = 1 + (currentLevelKnownBits / 8) + (count ? 1 : 0); // First byte is index 0. + buffer[index] |= (1 << count); + } + else if (result != STATUS_OK) { + return result; + } + else { // STATUS_OK + if (currentLevelKnownBits >= 32) { // This was a SELECT. + selectDone = true; // No more anticollision + // We continue below outside the while. + } + else { // This was an ANTICOLLISION. + // We now have all 32 bits of the UID in this Cascade Level + currentLevelKnownBits = 32; + // Run loop again to do the SELECT. + } + } + } // End of while (!selectDone) + + // We do not check the CBB - it was constructed by us above. + + // Copy the found UID bytes from buffer[] to uid->uidByte[] + index = (buffer[2] == PICC_CMD_CT) ? 3 : 2; // source index in buffer[] + bytesToCopy = (buffer[2] == PICC_CMD_CT) ? 3 : 4; + for (count = 0; count < bytesToCopy; count++) { + uid->uidByte[uidIndex + count] = buffer[index++]; + } + + // Check response SAK (Select Acknowledge) + if (responseLength != 3 || txLastBits != 0) { // SAK must be exactly 24 bits (1 byte + CRC_A). + return STATUS_ERROR; + } + // Verify CRC_A - do our own calculation and store the control in buffer[2..3] - those bytes are not needed anymore. + result = PCD_CalculateCRC(responseBuffer, 1, &buffer[2]); + if (result != STATUS_OK) { + return result; + } + if ((buffer[2] != responseBuffer[1]) || (buffer[3] != responseBuffer[2])) { + return STATUS_CRC_WRONG; + } + if (responseBuffer[0] & 0x04) { // Cascade bit set - UID not complete yes + cascadeLevel++; + } + else { + uidComplete = true; + uid->sak = responseBuffer[0]; + } + } // End of while (!uidComplete) + + // Set correct uid->size + uid->size = 3 * cascadeLevel + 1; + + return STATUS_OK; +} // End PICC_Select() + +/** + * Instructs a PICC in state ACTIVE(*) to go to state HALT. + * + * @return STATUS_OK on success, STATUS_??? otherwise. + */ +byte MFRC522::PICC_HaltA() { + byte result; + byte buffer[4]; + + // Build command buffer + buffer[0] = PICC_CMD_HLTA; + buffer[1] = 0; + // Calculate CRC_A + result = PCD_CalculateCRC(buffer, 2, &buffer[2]); + if (result != STATUS_OK) { + return result; + } + + // Send the command. + // The standard says: + // If the PICC responds with any modulation during a period of 1 ms after the end of the frame containing the + // HLTA command, this response shall be interpreted as 'not acknowledge'. + // We interpret that this way: Only STATUS_TIMEOUT is an success. + result = PCD_TransceiveData(buffer, sizeof(buffer), NULL, 0); + if (result == STATUS_TIMEOUT) { + return STATUS_OK; + } + if (result == STATUS_OK) { // That is ironically NOT ok in this case ;-) + return STATUS_ERROR; + } + return result; +} // End PICC_HaltA() + + +///////////////////////////////////////////////////////////////////////////////////// +// Functions for communicating with MIFARE PICCs +///////////////////////////////////////////////////////////////////////////////////// + +/** + * Executes the MFRC522 MFAuthent command. + * This command manages MIFARE authentication to enable a secure communication to any MIFARE Mini, MIFARE 1K and MIFARE 4K card. + * The authentication is described in the MFRC522 datasheet section 10.3.1.9 and http://www.nxp.com/documents/data_sheet/MF1S503x.pdf section 10.1. + * For use with MIFARE Classic PICCs. + * The PICC must be selected - ie in state ACTIVE(*) - before calling this function. + * Remember to call PCD_StopCrypto1() after communicating with the authenticated PICC - otherwise no new communications can start. + * + * All keys are set to FFFFFFFFFFFFh at chip delivery. + * + * @return STATUS_OK on success, STATUS_??? otherwise. Probably STATUS_TIMEOUT if you supply the wrong key. + */ +byte MFRC522::PCD_Authenticate(byte command, ///< PICC_CMD_MF_AUTH_KEY_A or PICC_CMD_MF_AUTH_KEY_B + byte blockAddr, ///< The block number. See numbering in the comments in the .h file. + MIFARE_Key *key, ///< Pointer to the Crypteo1 key to use (6 bytes) + Uid *uid ///< Pointer to Uid struct. The first 4 bytes of the UID is used. + ) { + byte waitIRq = 0x10; // IdleIRq + + // Build command buffer + byte sendData[12]; + sendData[0] = command; + sendData[1] = blockAddr; + for (byte i = 0; i < MF_KEY_SIZE; i++) { // 6 key bytes + sendData[2+i] = key->keyByte[i]; + } + for (byte i = 0; i < 4; i++) { // The first 4 bytes of the UID + sendData[8+i] = uid->uidByte[i]; + } + + // Start the authentication. + return PCD_CommunicateWithPICC(PCD_MFAuthent, waitIRq, &sendData[0], sizeof(sendData)); +} // End PCD_Authenticate() + +/** + * Used to exit the PCD from its authenticated state. + * Remember to call this function after communicating with an authenticated PICC - otherwise no new communications can start. + */ +void MFRC522::PCD_StopCrypto1() { + // Clear MFCrypto1On bit + PCD_ClearRegisterBitMask(Status2Reg, 0x08); // Status2Reg[7..0] bits are: TempSensClear I2CForceHS reserved reserved MFCrypto1On ModemState[2:0] +} // End PCD_StopCrypto1() + +/** + * Reads 16 bytes (+ 2 bytes CRC_A) from the active PICC. + * + * For MIFARE Classic the sector containing the block must be authenticated before calling this function. + * + * For MIFARE Ultralight only addresses 00h to 0Fh are decoded. + * The MF0ICU1 returns a NAK for higher addresses. + * The MF0ICU1 responds to the READ command by sending 16 bytes starting from the page address defined by the command argument. + * For example; if blockAddr is 03h then pages 03h, 04h, 05h, 06h are returned. + * A roll-back is implemented: If blockAddr is 0Eh, then the contents of pages 0Eh, 0Fh, 00h and 01h are returned. + * + * The buffer must be at least 18 bytes because a CRC_A is also returned. + * Checks the CRC_A before returning STATUS_OK. + * + * @return STATUS_OK on success, STATUS_??? otherwise. + */ +byte MFRC522::MIFARE_Read( byte blockAddr, ///< MIFARE Classic: The block (0-0xff) number. MIFARE Ultralight: The first page to return data from. + byte *buffer, ///< The buffer to store the data in + byte *bufferSize ///< Buffer size, at least 18 bytes. Also number of bytes returned if STATUS_OK. + ) { + byte result; + + // Sanity check + if (buffer == NULL || *bufferSize < 18) { + return STATUS_NO_ROOM; + } + + // Build command buffer + buffer[0] = PICC_CMD_MF_READ; + buffer[1] = blockAddr; + // Calculate CRC_A + result = PCD_CalculateCRC(buffer, 2, &buffer[2]); + if (result != STATUS_OK) { + return result; + } + + // Transmit the buffer and receive the response, validate CRC_A. + return PCD_TransceiveData(buffer, 4, buffer, bufferSize, NULL, 0, true); +} // End MIFARE_Read() + +/** + * Writes 16 bytes to the active PICC. + * + * For MIFARE Classic the sector containing the block must be authenticated before calling this function. + * + * For MIFARE Ultralight the operation is called "COMPATIBILITY WRITE". + * Even though 16 bytes are transferred to the Ultralight PICC, only the least significant 4 bytes (bytes 0 to 3) + * are written to the specified address. It is recommended to set the remaining bytes 04h to 0Fh to all logic 0. + * * + * @return STATUS_OK on success, STATUS_??? otherwise. + */ +byte MFRC522::MIFARE_Write( byte blockAddr, ///< MIFARE Classic: The block (0-0xff) number. MIFARE Ultralight: The page (2-15) to write to. + byte *buffer, ///< The 16 bytes to write to the PICC + byte bufferSize ///< Buffer size, must be at least 16 bytes. Exactly 16 bytes are written. + ) { + byte result; + + // Sanity check + if (buffer == NULL || bufferSize < 16) { + return STATUS_INVALID; + } + + // Mifare Classic protocol requires two communications to perform a write. + // Step 1: Tell the PICC we want to write to block blockAddr. + byte cmdBuffer[2]; + cmdBuffer[0] = PICC_CMD_MF_WRITE; + cmdBuffer[1] = blockAddr; + result = PCD_MIFARE_Transceive(cmdBuffer, 2); // Adds CRC_A and checks that the response is MF_ACK. + if (result != STATUS_OK) { + return result; + } + + // Step 2: Transfer the data + result = PCD_MIFARE_Transceive(buffer, bufferSize); // Adds CRC_A and checks that the response is MF_ACK. + if (result != STATUS_OK) { + return result; + } + + return STATUS_OK; +} // End MIFARE_Write() + +/** + * Writes a 4 byte page to the active MIFARE Ultralight PICC. + * + * @return STATUS_OK on success, STATUS_??? otherwise. + */ +byte MFRC522::MIFARE_Ultralight_Write( byte page, ///< The page (2-15) to write to. + byte *buffer, ///< The 4 bytes to write to the PICC + byte bufferSize ///< Buffer size, must be at least 4 bytes. Exactly 4 bytes are written. + ) { + byte result; + + // Sanity check + if (buffer == NULL || bufferSize < 4) { + return STATUS_INVALID; + } + + // Build commmand buffer + byte cmdBuffer[6]; + cmdBuffer[0] = PICC_CMD_UL_WRITE; + cmdBuffer[1] = page; + memcpy(&cmdBuffer[2], buffer, 4); + + // Perform the write + result = PCD_MIFARE_Transceive(cmdBuffer, 6); // Adds CRC_A and checks that the response is MF_ACK. + if (result != STATUS_OK) { + return result; + } + return STATUS_OK; +} // End MIFARE_Ultralight_Write() + +/** + * MIFARE Decrement subtracts the delta from the value of the addressed block, and stores the result in a volatile memory. + * For MIFARE Classic only. The sector containing the block must be authenticated before calling this function. + * Only for blocks in "value block" mode, ie with access bits [C1 C2 C3] = [110] or [001]. + * Use MIFARE_Transfer() to store the result in a block. + * + * @return STATUS_OK on success, STATUS_??? otherwise. + */ +byte MFRC522::MIFARE_Decrement( byte blockAddr, ///< The block (0-0xff) number. + long delta ///< This number is subtracted from the value of block blockAddr. + ) { + return MIFARE_TwoStepHelper(PICC_CMD_MF_DECREMENT, blockAddr, delta); +} // End MIFARE_Decrement() + +/** + * MIFARE Increment adds the delta to the value of the addressed block, and stores the result in a volatile memory. + * For MIFARE Classic only. The sector containing the block must be authenticated before calling this function. + * Only for blocks in "value block" mode, ie with access bits [C1 C2 C3] = [110] or [001]. + * Use MIFARE_Transfer() to store the result in a block. + * + * @return STATUS_OK on success, STATUS_??? otherwise. + */ +byte MFRC522::MIFARE_Increment( byte blockAddr, ///< The block (0-0xff) number. + long delta ///< This number is added to the value of block blockAddr. + ) { + return MIFARE_TwoStepHelper(PICC_CMD_MF_INCREMENT, blockAddr, delta); +} // End MIFARE_Increment() + +/** + * MIFARE Restore copies the value of the addressed block into a volatile memory. + * For MIFARE Classic only. The sector containing the block must be authenticated before calling this function. + * Only for blocks in "value block" mode, ie with access bits [C1 C2 C3] = [110] or [001]. + * Use MIFARE_Transfer() to store the result in a block. + * + * @return STATUS_OK on success, STATUS_??? otherwise. + */ +byte MFRC522::MIFARE_Restore( byte blockAddr ///< The block (0-0xff) number. + ) { + // The datasheet describes Restore as a two step operation, but does not explain what data to transfer in step 2. + // Doing only a single step does not work, so I chose to transfer 0L in step two. + return MIFARE_TwoStepHelper(PICC_CMD_MF_RESTORE, blockAddr, 0L); +} // End MIFARE_Restore() + +/** + * Helper function for the two-step MIFARE Classic protocol operations Decrement, Increment and Restore. + * + * @return STATUS_OK on success, STATUS_??? otherwise. + */ +byte MFRC522::MIFARE_TwoStepHelper( byte command, ///< The command to use + byte blockAddr, ///< The block (0-0xff) number. + long data ///< The data to transfer in step 2 + ) { + byte result; + byte cmdBuffer[2]; // We only need room for 2 bytes. + + // Step 1: Tell the PICC the command and block address + cmdBuffer[0] = command; + cmdBuffer[1] = blockAddr; + result = PCD_MIFARE_Transceive( cmdBuffer, 2); // Adds CRC_A and checks that the response is MF_ACK. + if (result != STATUS_OK) { + return result; + } + + // Step 2: Transfer the data + result = PCD_MIFARE_Transceive( (byte *)&data, 4, true); // Adds CRC_A and accept timeout as success. + if (result != STATUS_OK) { + return result; + } + + return STATUS_OK; +} // End MIFARE_TwoStepHelper() + +/** + * MIFARE Transfer writes the value stored in the volatile memory into one MIFARE Classic block. + * For MIFARE Classic only. The sector containing the block must be authenticated before calling this function. + * Only for blocks in "value block" mode, ie with access bits [C1 C2 C3] = [110] or [001]. + * + * @return STATUS_OK on success, STATUS_??? otherwise. + */ +byte MFRC522::MIFARE_Transfer( byte blockAddr ///< The block (0-0xff) number. + ) { + byte result; + byte cmdBuffer[2]; // We only need room for 2 bytes. + + // Tell the PICC we want to transfer the result into block blockAddr. + cmdBuffer[0] = PICC_CMD_MF_TRANSFER; + cmdBuffer[1] = blockAddr; + result = PCD_MIFARE_Transceive( cmdBuffer, 2); // Adds CRC_A and checks that the response is MF_ACK. + if (result != STATUS_OK) { + return result; + } + return STATUS_OK; +} // End MIFARE_Transfer() + +/** + * Helper routine to read the current value from a Value Block. + * + * Only for MIFARE Classic and only for blocks in "value block" mode, that + * is: with access bits [C1 C2 C3] = [110] or [001]. The sector containing + * the block must be authenticated before calling this function. + * + * @param[in] blockAddr The block (0x00-0xff) number. + * @param[out] value Current value of the Value Block. + * @return STATUS_OK on success, STATUS_??? otherwise. + */ +byte MFRC522::MIFARE_GetValue(byte blockAddr, long *value) { + byte status; + byte buffer[18]; + byte size = sizeof(buffer); + + // Read the block + status = MIFARE_Read(blockAddr, buffer, &size); + if (status == STATUS_OK) { + // Extract the value + *value = (long(buffer[3])<<24) | (long(buffer[2])<<16) | (long(buffer[1])<<8) | long(buffer[0]); + } + return status; +} // End MIFARE_GetValue() + +/** + * Helper routine to write a specific value into a Value Block. + * + * Only for MIFARE Classic and only for blocks in "value block" mode, that + * is: with access bits [C1 C2 C3] = [110] or [001]. The sector containing + * the block must be authenticated before calling this function. + * + * @param[in] blockAddr The block (0x00-0xff) number. + * @param[in] value New value of the Value Block. + * @return STATUS_OK on success, STATUS_??? otherwise. + */ +byte MFRC522::MIFARE_SetValue(byte blockAddr, long value) { + byte buffer[18]; + + // Translate the long into 4 bytes; repeated 2x in value block + buffer[0] = buffer[ 8] = (value & 0xFF); + buffer[1] = buffer[ 9] = (value & 0xFF00) >> 8; + buffer[2] = buffer[10] = (value & 0xFF0000) >> 16; + buffer[3] = buffer[11] = (value & 0xFF000000) >> 24; + // Inverse 4 bytes also found in value block + buffer[4] = ~buffer[0]; + buffer[5] = ~buffer[1]; + buffer[6] = ~buffer[2]; + buffer[7] = ~buffer[3]; + // Address 2x with inverse address 2x + buffer[12] = buffer[14] = blockAddr; + buffer[13] = buffer[15] = ~blockAddr; + + // Write the whole data block + return MIFARE_Write(blockAddr, buffer, 16); +} // End MIFARE_SetValue() + +///////////////////////////////////////////////////////////////////////////////////// +// Support functions +///////////////////////////////////////////////////////////////////////////////////// + +/** + * Wrapper for MIFARE protocol communication. + * Adds CRC_A, executes the Transceive command and checks that the response is MF_ACK or a timeout. + * + * @return STATUS_OK on success, STATUS_??? otherwise. + */ +byte MFRC522::PCD_MIFARE_Transceive( byte *sendData, ///< Pointer to the data to transfer to the FIFO. Do NOT include the CRC_A. + byte sendLen, ///< Number of bytes in sendData. + bool acceptTimeout ///< True => A timeout is also success + ) { + byte result; + byte cmdBuffer[18]; // We need room for 16 bytes data and 2 bytes CRC_A. + + // Sanity check + if (sendData == NULL || sendLen > 16) { + return STATUS_INVALID; + } + + // Copy sendData[] to cmdBuffer[] and add CRC_A + memcpy(cmdBuffer, sendData, sendLen); + result = PCD_CalculateCRC(cmdBuffer, sendLen, &cmdBuffer[sendLen]); + if (result != STATUS_OK) { + return result; + } + sendLen += 2; + + // Transceive the data, store the reply in cmdBuffer[] + byte waitIRq = 0x30; // RxIRq and IdleIRq + byte cmdBufferSize = sizeof(cmdBuffer); + byte validBits = 0; + result = PCD_CommunicateWithPICC(PCD_Transceive, waitIRq, cmdBuffer, sendLen, cmdBuffer, &cmdBufferSize, &validBits); + if (acceptTimeout && result == STATUS_TIMEOUT) { + return STATUS_OK; + } + if (result != STATUS_OK) { + return result; + } + // The PICC must reply with a 4 bit ACK + if (cmdBufferSize != 1 || validBits != 4) { + return STATUS_ERROR; + } + if (cmdBuffer[0] != MF_ACK) { + return STATUS_MIFARE_NACK; + } + return STATUS_OK; +} // End PCD_MIFARE_Transceive() + +/** + * Returns a __FlashStringHelper pointer to a status code name. + * + * @return const __FlashStringHelper * + */ +const __FlashStringHelper *MFRC522::GetStatusCodeName(byte code ///< One of the StatusCode enums. + ) { + switch (code) { + case STATUS_OK: return F("Success."); break; + case STATUS_ERROR: return F("Error in communication."); break; + case STATUS_COLLISION: return F("Collission detected."); break; + case STATUS_TIMEOUT: return F("Timeout in communication."); break; + case STATUS_NO_ROOM: return F("A buffer is not big enough."); break; + case STATUS_INTERNAL_ERROR: return F("Internal error in the code. Should not happen."); break; + case STATUS_INVALID: return F("Invalid argument."); break; + case STATUS_CRC_WRONG: return F("The CRC_A does not match."); break; + case STATUS_MIFARE_NACK: return F("A MIFARE PICC responded with NAK."); break; + default: return F("Unknown error"); break; + } +} // End GetStatusCodeName() + +/** + * Translates the SAK (Select Acknowledge) to a PICC type. + * + * @return PICC_Type + */ +byte MFRC522::PICC_GetType(byte sak ///< The SAK byte returned from PICC_Select(). + ) { + if (sak & 0x04) { // UID not complete + return PICC_TYPE_NOT_COMPLETE; + } + + switch (sak) { + case 0x09: return PICC_TYPE_MIFARE_MINI; break; + case 0x08: return PICC_TYPE_MIFARE_1K; break; + case 0x18: return PICC_TYPE_MIFARE_4K; break; + case 0x00: return PICC_TYPE_MIFARE_UL; break; + case 0x10: + case 0x11: return PICC_TYPE_MIFARE_PLUS; break; + case 0x01: return PICC_TYPE_TNP3XXX; break; + default: break; + } + + if (sak & 0x20) { + return PICC_TYPE_ISO_14443_4; + } + + if (sak & 0x40) { + return PICC_TYPE_ISO_18092; + } + + return PICC_TYPE_UNKNOWN; +} // End PICC_GetType() + +/** + * Returns a __FlashStringHelper pointer to the PICC type name. + * + * @return const __FlashStringHelper * + */ +const __FlashStringHelper *MFRC522::PICC_GetTypeName(byte piccType ///< One of the PICC_Type enums. + ) { + switch (piccType) { + case PICC_TYPE_ISO_14443_4: return F("PICC compliant with ISO/IEC 14443-4"); break; + case PICC_TYPE_ISO_18092: return F("PICC compliant with ISO/IEC 18092 (NFC)");break; + case PICC_TYPE_MIFARE_MINI: return F("MIFARE Mini, 320 bytes"); break; + case PICC_TYPE_MIFARE_1K: return F("MIFARE 1KB"); break; + case PICC_TYPE_MIFARE_4K: return F("MIFARE 4KB"); break; + case PICC_TYPE_MIFARE_UL: return F("MIFARE Ultralight or Ultralight C"); break; + case PICC_TYPE_MIFARE_PLUS: return F("MIFARE Plus"); break; + case PICC_TYPE_TNP3XXX: return F("MIFARE TNP3XXX"); break; + case PICC_TYPE_NOT_COMPLETE: return F("SAK indicates UID is not complete."); break; + case PICC_TYPE_UNKNOWN: + default: return F("Unknown type"); break; + } +} // End PICC_GetTypeName() + +/** + * Dumps debug info about the selected PICC to Serial. + * On success the PICC is halted after dumping the data. + * For MIFARE Classic the factory default key of 0xFFFFFFFFFFFF is tried. + */ +void MFRC522::PICC_DumpToSerial(Uid *uid ///< Pointer to Uid struct returned from a successful PICC_Select(). + ) { + MIFARE_Key key; + + // UID + Serial.print(F("Card UID:")); + for (byte i = 0; i < uid->size; i++) { + if(uid->uidByte[i] < 0x10) + Serial.print(F(" 0")); + else + Serial.print(F(" ")); + Serial.print(uid->uidByte[i], HEX); + } + Serial.println(); + + // PICC type + byte piccType = PICC_GetType(uid->sak); + Serial.print(F("PICC type: ")); + Serial.println(PICC_GetTypeName(piccType)); + + // Dump contents + switch (piccType) { + case PICC_TYPE_MIFARE_MINI: + case PICC_TYPE_MIFARE_1K: + case PICC_TYPE_MIFARE_4K: + // All keys are set to FFFFFFFFFFFFh at chip delivery from the factory. + for (byte i = 0; i < 6; i++) { + key.keyByte[i] = 0xFF; + } + PICC_DumpMifareClassicToSerial(uid, piccType, &key); + break; + + case PICC_TYPE_MIFARE_UL: + PICC_DumpMifareUltralightToSerial(); + break; + + case PICC_TYPE_ISO_14443_4: + case PICC_TYPE_ISO_18092: + case PICC_TYPE_MIFARE_PLUS: + case PICC_TYPE_TNP3XXX: + Serial.println(F("Dumping memory contents not implemented for that PICC type.")); + break; + + case PICC_TYPE_UNKNOWN: + case PICC_TYPE_NOT_COMPLETE: + default: + break; // No memory dump here + } + + Serial.println(); + PICC_HaltA(); // Already done if it was a MIFARE Classic PICC. +} // End PICC_DumpToSerial() + +/** + * Dumps memory contents of a MIFARE Classic PICC. + * On success the PICC is halted after dumping the data. + */ +void MFRC522::PICC_DumpMifareClassicToSerial( Uid *uid, ///< Pointer to Uid struct returned from a successful PICC_Select(). + byte piccType, ///< One of the PICC_Type enums. + MIFARE_Key *key ///< Key A used for all sectors. + ) { + byte no_of_sectors = 0; + switch (piccType) { + case PICC_TYPE_MIFARE_MINI: + // Has 5 sectors * 4 blocks/sector * 16 bytes/block = 320 bytes. + no_of_sectors = 5; + break; + + case PICC_TYPE_MIFARE_1K: + // Has 16 sectors * 4 blocks/sector * 16 bytes/block = 1024 bytes. + no_of_sectors = 16; + break; + + case PICC_TYPE_MIFARE_4K: + // Has (32 sectors * 4 blocks/sector + 8 sectors * 16 blocks/sector) * 16 bytes/block = 4096 bytes. + no_of_sectors = 40; + break; + + default: // Should not happen. Ignore. + break; + } + + // Dump sectors, highest address first. + if (no_of_sectors) { + Serial.println(F("Sector Block 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 AccessBits")); + for (int8_t i = no_of_sectors - 1; i >= 0; i--) { + PICC_DumpMifareClassicSectorToSerial(uid, key, i); + } + } + PICC_HaltA(); // Halt the PICC before stopping the encrypted session. + PCD_StopCrypto1(); +} // End PICC_DumpMifareClassicToSerial() + +/** + * Dumps memory contents of a sector of a MIFARE Classic PICC. + * Uses PCD_Authenticate(), MIFARE_Read() and PCD_StopCrypto1. + * Always uses PICC_CMD_MF_AUTH_KEY_A because only Key A can always read the sector trailer access bits. + */ +void MFRC522::PICC_DumpMifareClassicSectorToSerial(Uid *uid, ///< Pointer to Uid struct returned from a successful PICC_Select(). + MIFARE_Key *key, ///< Key A for the sector. + byte sector ///< The sector to dump, 0..39. + ) { + byte status; + byte firstBlock; // Address of lowest address to dump actually last block dumped) + byte no_of_blocks; // Number of blocks in sector + bool isSectorTrailer; // Set to true while handling the "last" (ie highest address) in the sector. + + // The access bits are stored in a peculiar fashion. + // There are four groups: + // g[3] Access bits for the sector trailer, block 3 (for sectors 0-31) or block 15 (for sectors 32-39) + // g[2] Access bits for block 2 (for sectors 0-31) or blocks 10-14 (for sectors 32-39) + // g[1] Access bits for block 1 (for sectors 0-31) or blocks 5-9 (for sectors 32-39) + // g[0] Access bits for block 0 (for sectors 0-31) or blocks 0-4 (for sectors 32-39) + // Each group has access bits [C1 C2 C3]. In this code C1 is MSB and C3 is LSB. + // The four CX bits are stored together in a nible cx and an inverted nible cx_. + byte c1, c2, c3; // Nibbles + byte c1_, c2_, c3_; // Inverted nibbles + bool invertedError; // True if one of the inverted nibbles did not match + byte g[4]; // Access bits for each of the four groups. + byte group; // 0-3 - active group for access bits + bool firstInGroup; // True for the first block dumped in the group + + // Determine position and size of sector. + if (sector < 32) { // Sectors 0..31 has 4 blocks each + no_of_blocks = 4; + firstBlock = sector * no_of_blocks; + } + else if (sector < 40) { // Sectors 32-39 has 16 blocks each + no_of_blocks = 16; + firstBlock = 128 + (sector - 32) * no_of_blocks; + } + else { // Illegal input, no MIFARE Classic PICC has more than 40 sectors. + return; + } + + // Dump blocks, highest address first. + byte byteCount; + byte buffer[18]; + byte blockAddr; + isSectorTrailer = true; + for (int8_t blockOffset = no_of_blocks - 1; blockOffset >= 0; blockOffset--) { + blockAddr = firstBlock + blockOffset; + // Sector number - only on first line + if (isSectorTrailer) { + if(sector < 10) + Serial.print(F(" ")); // Pad with spaces + else + Serial.print(F(" ")); // Pad with spaces + Serial.print(sector); + Serial.print(F(" ")); + } + else { + Serial.print(F(" ")); + } + // Block number + if(blockAddr < 10) + Serial.print(F(" ")); // Pad with spaces + else { + if(blockAddr < 100) + Serial.print(F(" ")); // Pad with spaces + else + Serial.print(F(" ")); // Pad with spaces + } + Serial.print(blockAddr); + Serial.print(F(" ")); + // Establish encrypted communications before reading the first block + if (isSectorTrailer) { + status = PCD_Authenticate(PICC_CMD_MF_AUTH_KEY_A, firstBlock, key, uid); + if (status != STATUS_OK) { + Serial.print(F("PCD_Authenticate() failed: ")); + Serial.println(GetStatusCodeName(status)); + return; + } + } + // Read block + byteCount = sizeof(buffer); + status = MIFARE_Read(blockAddr, buffer, &byteCount); + if (status != STATUS_OK) { + Serial.print(F("MIFARE_Read() failed: ")); + Serial.println(GetStatusCodeName(status)); + continue; + } + // Dump data + for (byte index = 0; index < 16; index++) { + if(buffer[index] < 0x10) + Serial.print(F(" 0")); + else + Serial.print(F(" ")); + Serial.print(buffer[index], HEX); + if ((index % 4) == 3) { + Serial.print(F(" ")); + } + } + // Parse sector trailer data + if (isSectorTrailer) { + c1 = buffer[7] >> 4; + c2 = buffer[8] & 0xF; + c3 = buffer[8] >> 4; + c1_ = buffer[6] & 0xF; + c2_ = buffer[6] >> 4; + c3_ = buffer[7] & 0xF; + invertedError = (c1 != (~c1_ & 0xF)) || (c2 != (~c2_ & 0xF)) || (c3 != (~c3_ & 0xF)); + g[0] = ((c1 & 1) << 2) | ((c2 & 1) << 1) | ((c3 & 1) << 0); + g[1] = ((c1 & 2) << 1) | ((c2 & 2) << 0) | ((c3 & 2) >> 1); + g[2] = ((c1 & 4) << 0) | ((c2 & 4) >> 1) | ((c3 & 4) >> 2); + g[3] = ((c1 & 8) >> 1) | ((c2 & 8) >> 2) | ((c3 & 8) >> 3); + isSectorTrailer = false; + } + + // Which access group is this block in? + if (no_of_blocks == 4) { + group = blockOffset; + firstInGroup = true; + } + else { + group = blockOffset / 5; + firstInGroup = (group == 3) || (group != (blockOffset + 1) / 5); + } + + if (firstInGroup) { + // Print access bits + Serial.print(F(" [ ")); + Serial.print((g[group] >> 2) & 1, DEC); Serial.print(F(" ")); + Serial.print((g[group] >> 1) & 1, DEC); Serial.print(F(" ")); + Serial.print((g[group] >> 0) & 1, DEC); + Serial.print(F(" ] ")); + if (invertedError) { + Serial.print(F(" Inverted access bits did not match! ")); + } + } + + if (group != 3 && (g[group] == 1 || g[group] == 6)) { // Not a sector trailer, a value block + long value = (long(buffer[3])<<24) | (long(buffer[2])<<16) | (long(buffer[1])<<8) | long(buffer[0]); + Serial.print(F(" Value=0x")); Serial.print(value, HEX); + Serial.print(F(" Adr=0x")); Serial.print(buffer[12], HEX); + } + Serial.println(); + } + + return; +} // End PICC_DumpMifareClassicSectorToSerial() + +/** + * Dumps memory contents of a MIFARE Ultralight PICC. + */ +void MFRC522::PICC_DumpMifareUltralightToSerial() { + byte status; + byte byteCount; + byte buffer[18]; + byte i; + + Serial.println(F("Page 0 1 2 3")); + // Try the mpages of the original Ultralight. Ultralight C has more pages. + for (byte page = 0; page < 16; page +=4) { // Read returns data for 4 pages at a time. + // Read pages + byteCount = sizeof(buffer); + status = MIFARE_Read(page, buffer, &byteCount); + if (status != STATUS_OK) { + Serial.print(F("MIFARE_Read() failed: ")); + Serial.println(GetStatusCodeName(status)); + break; + } + // Dump data + for (byte offset = 0; offset < 4; offset++) { + i = page + offset; + if(i < 10) + Serial.print(F(" ")); // Pad with spaces + else + Serial.print(F(" ")); // Pad with spaces + Serial.print(i); + Serial.print(F(" ")); + for (byte index = 0; index < 4; index++) { + i = 4 * offset + index; + if(buffer[i] < 0x10) + Serial.print(F(" 0")); + else + Serial.print(F(" ")); + Serial.print(buffer[i], HEX); + } + Serial.println(); + } + } +} // End PICC_DumpMifareUltralightToSerial() + +/** + * Calculates the bit pattern needed for the specified access bits. In the [C1 C2 C3] tupples C1 is MSB (=4) and C3 is LSB (=1). + */ +void MFRC522::MIFARE_SetAccessBits( byte *accessBitBuffer, ///< Pointer to byte 6, 7 and 8 in the sector trailer. Bytes [0..2] will be set. + byte g0, ///< Access bits [C1 C2 C3] for block 0 (for sectors 0-31) or blocks 0-4 (for sectors 32-39) + byte g1, ///< Access bits C1 C2 C3] for block 1 (for sectors 0-31) or blocks 5-9 (for sectors 32-39) + byte g2, ///< Access bits C1 C2 C3] for block 2 (for sectors 0-31) or blocks 10-14 (for sectors 32-39) + byte g3 ///< Access bits C1 C2 C3] for the sector trailer, block 3 (for sectors 0-31) or block 15 (for sectors 32-39) + ) { + byte c1 = ((g3 & 4) << 1) | ((g2 & 4) << 0) | ((g1 & 4) >> 1) | ((g0 & 4) >> 2); + byte c2 = ((g3 & 2) << 2) | ((g2 & 2) << 1) | ((g1 & 2) << 0) | ((g0 & 2) >> 1); + byte c3 = ((g3 & 1) << 3) | ((g2 & 1) << 2) | ((g1 & 1) << 1) | ((g0 & 1) << 0); + + accessBitBuffer[0] = (~c2 & 0xF) << 4 | (~c1 & 0xF); + accessBitBuffer[1] = c1 << 4 | (~c3 & 0xF); + accessBitBuffer[2] = c3 << 4 | c2; +} // End MIFARE_SetAccessBits() + + +/** + * Performs the "magic sequence" needed to get Chinese UID changeable + * Mifare cards to allow writing to sector 0, where the card UID is stored. + * + * Note that you do not need to have selected the card through REQA or WUPA, + * this sequence works immediately when the card is in the reader vicinity. + * This means you can use this method even on "bricked" cards that your reader does + * not recognise anymore (see MFRC522::MIFARE_UnbrickUidSector). + * + * Of course with non-bricked devices, you're free to select them before calling this function. + */ +bool MFRC522::MIFARE_OpenUidBackdoor(bool logErrors) { + // Magic sequence: + // > 50 00 57 CD (HALT + CRC) + // > 40 (7 bits only) + // < A (4 bits only) + // > 43 + // < A (4 bits only) + // Then you can write to sector 0 without authenticating + + PICC_HaltA(); // 50 00 57 CD + + byte cmd = 0x40; + byte validBits = 7; /* Our command is only 7 bits. After receiving card response, + this will contain amount of valid response bits. */ + byte response[32]; // Card's response is written here + byte received; + byte status = PCD_TransceiveData(&cmd, (byte)1, response, &received, &validBits, (byte)0, false); // 40 + if(status != STATUS_OK) { + if(logErrors) { + Serial.println(F("Card did not respond to 0x40 after HALT command. Are you sure it is a UID changeable one?")); + Serial.print(F("Error name: ")); + Serial.println(GetStatusCodeName(status)); + } + return false; + } + if (received != 1 || response[0] != 0x0A) { + if (logErrors) { + Serial.print(F("Got bad response on backdoor 0x40 command: ")); + Serial.print(response[0], HEX); + Serial.print(F(" (")); + Serial.print(validBits); + Serial.print(F(" valid bits)\r\n")); + } + return false; + } + + cmd = 0x43; + validBits = 8; + status = PCD_TransceiveData(&cmd, (byte)1, response, &received, &validBits, (byte)0, false); // 43 + if(status != STATUS_OK) { + if(logErrors) { + Serial.println(F("Error in communication at command 0x43, after successfully executing 0x40")); + Serial.print(F("Error name: ")); + Serial.println(GetStatusCodeName(status)); + } + return false; + } + if (received != 1 || response[0] != 0x0A) { + if (logErrors) { + Serial.print(F("Got bad response on backdoor 0x43 command: ")); + Serial.print(response[0], HEX); + Serial.print(F(" (")); + Serial.print(validBits); + Serial.print(F(" valid bits)\r\n")); + } + return false; + } + + // You can now write to sector 0 without authenticating! + return true; +} // End MIFARE_OpenUidBackdoor() + +/** + * Reads entire block 0, including all manufacturer data, and overwrites + * that block with the new UID, a freshly calculated BCC, and the original + * manufacturer data. + * + * It assumes a default KEY A of 0xFFFFFFFFFFFF. + * Make sure to have selected the card before this function is called. + */ +bool MFRC522::MIFARE_SetUid(byte *newUid, byte uidSize, bool logErrors) { + + // UID + BCC byte can not be larger than 16 together + if (!newUid || !uidSize || uidSize > 15) { + if (logErrors) { + Serial.println(F("New UID buffer empty, size 0, or size > 15 given")); + } + return false; + } + + // Authenticate for reading + MIFARE_Key key = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}; + byte status = PCD_Authenticate(MFRC522::PICC_CMD_MF_AUTH_KEY_A, (byte)1, &key, &uid); + if (status != STATUS_OK) { + + if (status == STATUS_TIMEOUT) { + // We get a read timeout if no card is selected yet, so let's select one + + // Wake the card up again if sleeping +// byte atqa_answer[2]; +// byte atqa_size = 2; +// PICC_WakeupA(atqa_answer, &atqa_size); + + if (!PICC_IsNewCardPresent() || !PICC_ReadCardSerial()) { + Serial.println(F("No card was previously selected, and none are available. Failed to set UID.")); + return false; + } + + status = PCD_Authenticate(MFRC522::PICC_CMD_MF_AUTH_KEY_A, (byte)1, &key, &uid); + if (status != STATUS_OK) { + // We tried, time to give up + if (logErrors) { + Serial.println(F("Failed to authenticate to card for reading, could not set UID: ")); + Serial.println(GetStatusCodeName(status)); + } + return false; + } + } + else { + if (logErrors) { + Serial.print(F("PCD_Authenticate() failed: ")); + Serial.println(GetStatusCodeName(status)); + } + return false; + } + } + + // Read block 0 + byte block0_buffer[18]; + byte byteCount = sizeof(block0_buffer); + status = MIFARE_Read((byte)0, block0_buffer, &byteCount); + if (status != STATUS_OK) { + if (logErrors) { + Serial.print(F("MIFARE_Read() failed: ")); + Serial.println(GetStatusCodeName(status)); + Serial.println(F("Are you sure your KEY A for sector 0 is 0xFFFFFFFFFFFF?")); + } + return false; + } + + // Write new UID to the data we just read, and calculate BCC byte + byte bcc = 0; + for (int i = 0; i < uidSize; i++) { + block0_buffer[i] = newUid[i]; + bcc ^= newUid[i]; + } + + // Write BCC byte to buffer + block0_buffer[uidSize] = bcc; + + // Stop encrypted traffic so we can send raw bytes + PCD_StopCrypto1(); + + // Activate UID backdoor + if (!MIFARE_OpenUidBackdoor(logErrors)) { + if (logErrors) { + Serial.println(F("Activating the UID backdoor failed.")); + } + return false; + } + + // Write modified block 0 back to card + status = MIFARE_Write((byte)0, block0_buffer, (byte)16); + if (status != STATUS_OK) { + if (logErrors) { + Serial.print(F("MIFARE_Write() failed: ")); + Serial.println(GetStatusCodeName(status)); + } + return false; + } + + // Wake the card up again + byte atqa_answer[2]; + byte atqa_size = 2; + PICC_WakeupA(atqa_answer, &atqa_size); + + return true; +} + +/** + * Resets entire sector 0 to zeroes, so the card can be read again by readers. + */ +bool MFRC522::MIFARE_UnbrickUidSector(bool logErrors) { + MIFARE_OpenUidBackdoor(logErrors); + + byte block0_buffer[] = {0x01, 0x02, 0x03, 0x04, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; + + // Write modified block 0 back to card + byte status = MIFARE_Write((byte)0, block0_buffer, (byte)16); + if (status != STATUS_OK) { + if (logErrors) { + Serial.print(F("MIFARE_Write() failed: ")); + Serial.println(GetStatusCodeName(status)); + } + return false; + } + return true; +} + +///////////////////////////////////////////////////////////////////////////////////// +// Convenience functions - does not add extra functionality +///////////////////////////////////////////////////////////////////////////////////// + +/** + * Returns true if a PICC responds to PICC_CMD_REQA. + * Only "new" cards in state IDLE are invited. Sleeping cards in state HALT are ignored. + * + * @return bool + */ +bool MFRC522::PICC_IsNewCardPresent() { + byte bufferATQA[2]; + byte bufferSize = sizeof(bufferATQA); + byte result = PICC_RequestA(bufferATQA, &bufferSize); + return (result == STATUS_OK || result == STATUS_COLLISION); +} // End PICC_IsNewCardPresent() + +/** + * Simple wrapper around PICC_Select. + * Returns true if a UID could be read. + * Remember to call PICC_IsNewCardPresent(), PICC_RequestA() or PICC_WakeupA() first. + * The read UID is available in the class variable uid. + * + * @return bool + */ +bool MFRC522::PICC_ReadCardSerial() { + byte result = PICC_Select(&uid); + return (result == STATUS_OK); +} // End PICC_ReadCardSerial() diff --git a/lib/lib_i2c/MFRC522_I2C/src/MFRC522_I2C.h b/lib/lib_i2c/MFRC522_I2C/src/MFRC522_I2C.h new file mode 100644 index 000000000..a58a9df1a --- /dev/null +++ b/lib/lib_i2c/MFRC522_I2C/src/MFRC522_I2C.h @@ -0,0 +1,407 @@ +/** + * MFRC522_I2C.h - Library to use ARDUINO RFID MODULE KIT 13.56 MHZ WITH TAGS I2C BY AROZCAN + * MFRC522_I2C.h - Based on ARDUINO RFID MODULE KIT 13.56 MHZ WITH TAGS SPI Library BY COOQROBOT. + * Based on code Dr.Leong ( WWW.B2CQSHOP.COM ) + * Created by Miguel Balboa (circuitito.com), Jan, 2012. + * Rewritten by Søren Thing Andersen (access.thing.dk), fall of 2013 (Translation to English, refactored, comments, anti collision, cascade levels.) + * Extended by Tom Clement with functionality to write to sector 0 of UID changeable Mifare cards. + * Extended by Ahmet Remzi Ozcan with I2C functionality. + * Author: arozcan @ https://github.com/arozcan/MFRC522-I2C-Library + * Released into the public domain. + * + * Please read this file for an overview and then MFRC522.cpp for comments on the specific functions. + * Search for "mf-rc522" on ebay.com to purchase the MF-RC522 board. + * + * There are three hardware components involved: + * 1) The micro controller: An Arduino + * 2) The PCD (short for Proximity Coupling Device): NXP MFRC522 Contactless Reader IC + * 3) The PICC (short for Proximity Integrated Circuit Card): A card or tag using the ISO 14443A interface, eg Mifare or NTAG203. + * + * The microcontroller and card reader uses I2C for communication. + * The protocol is described in the MFRC522 datasheet: http://www.nxp.com/documents/data_sheet/MFRC522.pdf + * + * The card reader and the tags communicate using a 13.56MHz electromagnetic field. + * The protocol is defined in ISO/IEC 14443-3 Identification cards -- Contactless integrated circuit cards -- Proximity cards -- Part 3: Initialization and anticollision". + * A free version of the final draft can be found at http://wg8.de/wg8n1496_17n3613_Ballot_FCD14443-3.pdf + * Details are found in chapter 6, Type A – Initialization and anticollision. + * + * If only the PICC UID is wanted, the above documents has all the needed information. + * To read and write from MIFARE PICCs, the MIFARE protocol is used after the PICC has been selected. + * The MIFARE Classic chips and protocol is described in the datasheets: + * 1K: http://www.nxp.com/documents/data_sheet/MF1S503x.pdf + * 4K: http://www.nxp.com/documents/data_sheet/MF1S703x.pdf + * Mini: http://www.idcardmarket.com/download/mifare_S20_datasheet.pdf + * The MIFARE Ultralight chip and protocol is described in the datasheets: + * Ultralight: http://www.nxp.com/documents/data_sheet/MF0ICU1.pdf + * Ultralight C: http://www.nxp.com/documents/short_data_sheet/MF0ICU2_SDS.pdf + * + * MIFARE Classic 1K (MF1S503x): + * Has 16 sectors * 4 blocks/sector * 16 bytes/block = 1024 bytes. + * The blocks are numbered 0-63. + * Block 3 in each sector is the Sector Trailer. See http://www.nxp.com/documents/data_sheet/MF1S503x.pdf sections 8.6 and 8.7: + * Bytes 0-5: Key A + * Bytes 6-8: Access Bits + * Bytes 9: User data + * Bytes 10-15: Key B (or user data) + * Block 0 is read-only manufacturer data. + * To access a block, an authentication using a key from the block's sector must be performed first. + * Example: To read from block 10, first authenticate using a key from sector 3 (blocks 8-11). + * All keys are set to FFFFFFFFFFFFh at chip delivery. + * Warning: Please read section 8.7 "Memory Access". It includes this text: if the PICC detects a format violation the whole sector is irreversibly blocked. + * To use a block in "value block" mode (for Increment/Decrement operations) you need to change the sector trailer. Use PICC_SetAccessBits() to calculate the bit patterns. + * MIFARE Classic 4K (MF1S703x): + * Has (32 sectors * 4 blocks/sector + 8 sectors * 16 blocks/sector) * 16 bytes/block = 4096 bytes. + * The blocks are numbered 0-255. + * The last block in each sector is the Sector Trailer like above. + * MIFARE Classic Mini (MF1 IC S20): + * Has 5 sectors * 4 blocks/sector * 16 bytes/block = 320 bytes. + * The blocks are numbered 0-19. + * The last block in each sector is the Sector Trailer like above. + * + * MIFARE Ultralight (MF0ICU1): + * Has 16 pages of 4 bytes = 64 bytes. + * Pages 0 + 1 is used for the 7-byte UID. + * Page 2 contains the last check digit for the UID, one byte manufacturer internal data, and the lock bytes (see http://www.nxp.com/documents/data_sheet/MF0ICU1.pdf section 8.5.2) + * Page 3 is OTP, One Time Programmable bits. Once set to 1 they cannot revert to 0. + * Pages 4-15 are read/write unless blocked by the lock bytes in page 2. + * MIFARE Ultralight C (MF0ICU2): + * Has 48 pages of 4 bytes = 192 bytes. + * Pages 0 + 1 is used for the 7-byte UID. + * Page 2 contains the last check digit for the UID, one byte manufacturer internal data, and the lock bytes (see http://www.nxp.com/documents/data_sheet/MF0ICU1.pdf section 8.5.2) + * Page 3 is OTP, One Time Programmable bits. Once set to 1 they cannot revert to 0. + * Pages 4-39 are read/write unless blocked by the lock bytes in page 2. + * Page 40 Lock bytes + * Page 41 16 bit one way counter + * Pages 42-43 Authentication configuration + * Pages 44-47 Authentication key + */ +#ifndef MFRC522_h +#define MFRC522_h + +#include +#include + +// Firmware data for self-test +// Reference values based on firmware version +// Hint: if needed, you can remove unused self-test data to save flash memory +// +// Version 0.0 (0x90) +// Philips Semiconductors; Preliminary Specification Revision 2.0 - 01 August 2005; 16.1 Sefttest +const byte MFRC522_firmware_referenceV0_0[] PROGMEM = { + 0x00, 0x87, 0x98, 0x0f, 0x49, 0xFF, 0x07, 0x19, + 0xBF, 0x22, 0x30, 0x49, 0x59, 0x63, 0xAD, 0xCA, + 0x7F, 0xE3, 0x4E, 0x03, 0x5C, 0x4E, 0x49, 0x50, + 0x47, 0x9A, 0x37, 0x61, 0xE7, 0xE2, 0xC6, 0x2E, + 0x75, 0x5A, 0xED, 0x04, 0x3D, 0x02, 0x4B, 0x78, + 0x32, 0xFF, 0x58, 0x3B, 0x7C, 0xE9, 0x00, 0x94, + 0xB4, 0x4A, 0x59, 0x5B, 0xFD, 0xC9, 0x29, 0xDF, + 0x35, 0x96, 0x98, 0x9E, 0x4F, 0x30, 0x32, 0x8D +}; +// Version 1.0 (0x91) +// NXP Semiconductors; Rev. 3.8 - 17 September 2014; 16.1.1 Self test +const byte MFRC522_firmware_referenceV1_0[] PROGMEM = { + 0x00, 0xC6, 0x37, 0xD5, 0x32, 0xB7, 0x57, 0x5C, + 0xC2, 0xD8, 0x7C, 0x4D, 0xD9, 0x70, 0xC7, 0x73, + 0x10, 0xE6, 0xD2, 0xAA, 0x5E, 0xA1, 0x3E, 0x5A, + 0x14, 0xAF, 0x30, 0x61, 0xC9, 0x70, 0xDB, 0x2E, + 0x64, 0x22, 0x72, 0xB5, 0xBD, 0x65, 0xF4, 0xEC, + 0x22, 0xBC, 0xD3, 0x72, 0x35, 0xCD, 0xAA, 0x41, + 0x1F, 0xA7, 0xF3, 0x53, 0x14, 0xDE, 0x7E, 0x02, + 0xD9, 0x0F, 0xB5, 0x5E, 0x25, 0x1D, 0x29, 0x79 +}; +// Version 2.0 (0x92) +// NXP Semiconductors; Rev. 3.8 - 17 September 2014; 16.1.1 Self test +const byte MFRC522_firmware_referenceV2_0[] PROGMEM = { + 0x00, 0xEB, 0x66, 0xBA, 0x57, 0xBF, 0x23, 0x95, + 0xD0, 0xE3, 0x0D, 0x3D, 0x27, 0x89, 0x5C, 0xDE, + 0x9D, 0x3B, 0xA7, 0x00, 0x21, 0x5B, 0x89, 0x82, + 0x51, 0x3A, 0xEB, 0x02, 0x0C, 0xA5, 0x00, 0x49, + 0x7C, 0x84, 0x4D, 0xB3, 0xCC, 0xD2, 0x1B, 0x81, + 0x5D, 0x48, 0x76, 0xD5, 0x71, 0x61, 0x21, 0xA9, + 0x86, 0x96, 0x83, 0x38, 0xCF, 0x9D, 0x5B, 0x6D, + 0xDC, 0x15, 0xBA, 0x3E, 0x7D, 0x95, 0x3B, 0x2F +}; +// Clone +// Fudan Semiconductor FM17522 (0x88) +const byte FM17522_firmware_reference[] PROGMEM = { + 0x00, 0xD6, 0x78, 0x8C, 0xE2, 0xAA, 0x0C, 0x18, + 0x2A, 0xB8, 0x7A, 0x7F, 0xD3, 0x6A, 0xCF, 0x0B, + 0xB1, 0x37, 0x63, 0x4B, 0x69, 0xAE, 0x91, 0xC7, + 0xC3, 0x97, 0xAE, 0x77, 0xF4, 0x37, 0xD7, 0x9B, + 0x7C, 0xF5, 0x3C, 0x11, 0x8F, 0x15, 0xC3, 0xD7, + 0xC1, 0x5B, 0x00, 0x2A, 0xD0, 0x75, 0xDE, 0x9E, + 0x51, 0x64, 0xAB, 0x3E, 0xE9, 0x15, 0xB5, 0xAB, + 0x56, 0x9A, 0x98, 0x82, 0x26, 0xEA, 0x2A, 0x62 +}; + +class MFRC522 { +public: + // MFRC522 registers. Described in chapter 9 of the datasheet. + enum PCD_Register { + // Page 0: Command and status + // 0x00 // reserved for future use + CommandReg = 0x01 , // starts and stops command execution + ComIEnReg = 0x02 , // enable and disable interrupt request control bits + DivIEnReg = 0x03 , // enable and disable interrupt request control bits + ComIrqReg = 0x04 , // interrupt request bits + DivIrqReg = 0x05 , // interrupt request bits + ErrorReg = 0x06 , // error bits showing the error status of the last command executed + Status1Reg = 0x07 , // communication status bits + Status2Reg = 0x08 , // receiver and transmitter status bits + FIFODataReg = 0x09 , // input and output of 64 byte FIFO buffer + FIFOLevelReg = 0x0A , // number of bytes stored in the FIFO buffer + WaterLevelReg = 0x0B , // level for FIFO underflow and overflow warning + ControlReg = 0x0C , // miscellaneous control registers + BitFramingReg = 0x0D , // adjustments for bit-oriented frames + CollReg = 0x0E , // bit position of the first bit-collision detected on the RF interface + // 0x0F // reserved for future use + + // Page 1: Command + // 0x10 // reserved for future use + ModeReg = 0x11 , // defines general modes for transmitting and receiving + TxModeReg = 0x12 , // defines transmission data rate and framing + RxModeReg = 0x13 , // defines reception data rate and framing + TxControlReg = 0x14 , // controls the logical behavior of the antenna driver pins TX1 and TX2 + TxASKReg = 0x15 , // controls the setting of the transmission modulation + TxSelReg = 0x16 , // selects the internal sources for the antenna driver + RxSelReg = 0x17 , // selects internal receiver settings + RxThresholdReg = 0x18 , // selects thresholds for the bit decoder + DemodReg = 0x19 , // defines demodulator settings + // 0x1A // reserved for future use + // 0x1B // reserved for future use + MfTxReg = 0x1C , // controls some MIFARE communication transmit parameters + MfRxReg = 0x1D , // controls some MIFARE communication receive parameters + // 0x1E // reserved for future use + SerialSpeedReg = 0x1F , // selects the speed of the serial UART interface + + // Page 2: Configuration + // 0x20 // reserved for future use + CRCResultRegH = 0x21 , // shows the MSB and LSB values of the CRC calculation + CRCResultRegL = 0x22 , + // 0x23 // reserved for future use + ModWidthReg = 0x24 , // controls the ModWidth setting? + // 0x25 // reserved for future use + RFCfgReg = 0x26 , // configures the receiver gain + GsNReg = 0x27 , // selects the conductance of the antenna driver pins TX1 and TX2 for modulation + CWGsPReg = 0x28 , // defines the conductance of the p-driver output during periods of no modulation + ModGsPReg = 0x29 , // defines the conductance of the p-driver output during periods of modulation + TModeReg = 0x2A , // defines settings for the internal timer + TPrescalerReg = 0x2B , // the lower 8 bits of the TPrescaler value. The 4 high bits are in TModeReg. + TReloadRegH = 0x2C , // defines the 16-bit timer reload value + TReloadRegL = 0x2D , + TCounterValueRegH = 0x2E , // shows the 16-bit timer value + TCounterValueRegL = 0x2F , + + // Page 3: Test Registers + // 0x30 // reserved for future use + TestSel1Reg = 0x31 , // general test signal configuration + TestSel2Reg = 0x32 , // general test signal configuration + TestPinEnReg = 0x33 , // enables pin output driver on pins D1 to D7 + TestPinValueReg = 0x34 , // defines the values for D1 to D7 when it is used as an I/O bus + TestBusReg = 0x35 , // shows the status of the internal test bus + AutoTestReg = 0x36 , // controls the digital self test + VersionReg = 0x37 , // shows the software version + AnalogTestReg = 0x38 , // controls the pins AUX1 and AUX2 + TestDAC1Reg = 0x39 , // defines the test value for TestDAC1 + TestDAC2Reg = 0x3A , // defines the test value for TestDAC2 + TestADCReg = 0x3B // shows the value of ADC I and Q channels + // 0x3C // reserved for production tests + // 0x3D // reserved for production tests + // 0x3E // reserved for production tests + // 0x3F // reserved for production tests + }; + + // MFRC522 commands. Described in chapter 10 of the datasheet. + enum PCD_Command { + PCD_Idle = 0x00, // no action, cancels current command execution + PCD_Mem = 0x01, // stores 25 bytes into the internal buffer + PCD_GenerateRandomID = 0x02, // generates a 10-byte random ID number + PCD_CalcCRC = 0x03, // activates the CRC coprocessor or performs a self test + PCD_Transmit = 0x04, // transmits data from the FIFO buffer + PCD_NoCmdChange = 0x07, // no command change, can be used to modify the CommandReg register bits without affecting the command, for example, the PowerDown bit + PCD_Receive = 0x08, // activates the receiver circuits + PCD_Transceive = 0x0C, // transmits data from FIFO buffer to antenna and automatically activates the receiver after transmission + PCD_MFAuthent = 0x0E, // performs the MIFARE standard authentication as a reader + PCD_SoftReset = 0x0F // resets the MFRC522 + }; + + // MFRC522 RxGain[2:0] masks, defines the receiver's signal voltage gain factor (on the PCD). + // Described in 9.3.3.6 / table 98 of the datasheet at http://www.nxp.com/documents/data_sheet/MFRC522.pdf + enum PCD_RxGain { + RxGain_18dB = 0x00 << 4, // 000b - 18 dB, minimum + RxGain_23dB = 0x01 << 4, // 001b - 23 dB + RxGain_18dB_2 = 0x02 << 4, // 010b - 18 dB, it seems 010b is a duplicate for 000b + RxGain_23dB_2 = 0x03 << 4, // 011b - 23 dB, it seems 011b is a duplicate for 001b + RxGain_33dB = 0x04 << 4, // 100b - 33 dB, average, and typical default + RxGain_38dB = 0x05 << 4, // 101b - 38 dB + RxGain_43dB = 0x06 << 4, // 110b - 43 dB + RxGain_48dB = 0x07 << 4, // 111b - 48 dB, maximum + RxGain_min = 0x00 << 4, // 000b - 18 dB, minimum, convenience for RxGain_18dB + RxGain_avg = 0x04 << 4, // 100b - 33 dB, average, convenience for RxGain_33dB + RxGain_max = 0x07 << 4 // 111b - 48 dB, maximum, convenience for RxGain_48dB + }; + + // Commands sent to the PICC. + enum PICC_Command { + // The commands used by the PCD to manage communication with several PICCs (ISO 14443-3, Type A, section 6.4) + PICC_CMD_REQA = 0x26, // REQuest command, Type A. Invites PICCs in state IDLE to go to READY and prepare for anticollision or selection. 7 bit frame. + PICC_CMD_WUPA = 0x52, // Wake-UP command, Type A. Invites PICCs in state IDLE and HALT to go to READY(*) and prepare for anticollision or selection. 7 bit frame. + PICC_CMD_CT = 0x88, // Cascade Tag. Not really a command, but used during anti collision. + PICC_CMD_SEL_CL1 = 0x93, // Anti collision/Select, Cascade Level 1 + PICC_CMD_SEL_CL2 = 0x95, // Anti collision/Select, Cascade Level 2 + PICC_CMD_SEL_CL3 = 0x97, // Anti collision/Select, Cascade Level 3 + PICC_CMD_HLTA = 0x50, // HaLT command, Type A. Instructs an ACTIVE PICC to go to state HALT. + // The commands used for MIFARE Classic (from http://www.nxp.com/documents/data_sheet/MF1S503x.pdf, Section 9) + // Use PCD_MFAuthent to authenticate access to a sector, then use these commands to read/write/modify the blocks on the sector. + // The read/write commands can also be used for MIFARE Ultralight. + PICC_CMD_MF_AUTH_KEY_A = 0x60, // Perform authentication with Key A + PICC_CMD_MF_AUTH_KEY_B = 0x61, // Perform authentication with Key B + PICC_CMD_MF_READ = 0x30, // Reads one 16 byte block from the authenticated sector of the PICC. Also used for MIFARE Ultralight. + PICC_CMD_MF_WRITE = 0xA0, // Writes one 16 byte block to the authenticated sector of the PICC. Called "COMPATIBILITY WRITE" for MIFARE Ultralight. + PICC_CMD_MF_DECREMENT = 0xC0, // Decrements the contents of a block and stores the result in the internal data register. + PICC_CMD_MF_INCREMENT = 0xC1, // Increments the contents of a block and stores the result in the internal data register. + PICC_CMD_MF_RESTORE = 0xC2, // Reads the contents of a block into the internal data register. + PICC_CMD_MF_TRANSFER = 0xB0, // Writes the contents of the internal data register to a block. + // The commands used for MIFARE Ultralight (from http://www.nxp.com/documents/data_sheet/MF0ICU1.pdf, Section 8.6) + // The PICC_CMD_MF_READ and PICC_CMD_MF_WRITE can also be used for MIFARE Ultralight. + PICC_CMD_UL_WRITE = 0xA2 // Writes one 4 byte page to the PICC. + }; + + // MIFARE constants that does not fit anywhere else + enum MIFARE_Misc { + MF_ACK = 0xA, // The MIFARE Classic uses a 4 bit ACK/NAK. Any other value than 0xA is NAK. + MF_KEY_SIZE = 6 // A Mifare Crypto1 key is 6 bytes. + }; + + // PICC types we can detect. Remember to update PICC_GetTypeName() if you add more. + enum PICC_Type { + PICC_TYPE_UNKNOWN = 0, + PICC_TYPE_ISO_14443_4 = 1, // PICC compliant with ISO/IEC 14443-4 + PICC_TYPE_ISO_18092 = 2, // PICC compliant with ISO/IEC 18092 (NFC) + PICC_TYPE_MIFARE_MINI = 3, // MIFARE Classic protocol, 320 bytes + PICC_TYPE_MIFARE_1K = 4, // MIFARE Classic protocol, 1KB + PICC_TYPE_MIFARE_4K = 5, // MIFARE Classic protocol, 4KB + PICC_TYPE_MIFARE_UL = 6, // MIFARE Ultralight or Ultralight C + PICC_TYPE_MIFARE_PLUS = 7, // MIFARE Plus + PICC_TYPE_TNP3XXX = 8, // Only mentioned in NXP AN 10833 MIFARE Type Identification Procedure + PICC_TYPE_NOT_COMPLETE = 255 // SAK indicates UID is not complete. + }; + + // Return codes from the functions in this class. Remember to update GetStatusCodeName() if you add more. + enum StatusCode { + STATUS_OK = 1, // Success + STATUS_ERROR = 2, // Error in communication + STATUS_COLLISION = 3, // Collission detected + STATUS_TIMEOUT = 4, // Timeout in communication. + STATUS_NO_ROOM = 5, // A buffer is not big enough. + STATUS_INTERNAL_ERROR = 6, // Internal error in the code. Should not happen ;-) + STATUS_INVALID = 7, // Invalid argument. + STATUS_CRC_WRONG = 8, // The CRC_A does not match + STATUS_MIFARE_NACK = 9 // A MIFARE PICC responded with NAK. + }; + + // A struct used for passing the UID of a PICC. + typedef struct { + byte size; // Number of bytes in the UID. 4, 7 or 10. + byte uidByte[10]; + byte sak; // The SAK (Select acknowledge) byte returned from the PICC after successful selection. + } Uid; + + // A struct used for passing a MIFARE Crypto1 key + typedef struct { + byte keyByte[MF_KEY_SIZE]; + } MIFARE_Key; + + // Member variables + Uid uid; // Used by PICC_ReadCardSerial(). + + // Size of the MFRC522 FIFO + static const byte FIFO_SIZE = 64; // The FIFO is 64 bytes. + + ///////////////////////////////////////////////////////////////////////////////////// + // Functions for setting up the Arduino + ///////////////////////////////////////////////////////////////////////////////////// + MFRC522(byte chipAddress); + + ///////////////////////////////////////////////////////////////////////////////////// + // Basic interface functions for communicating with the MFRC522 + ///////////////////////////////////////////////////////////////////////////////////// + void PCD_WriteRegister(byte reg, byte value); + void PCD_WriteRegister(byte reg, byte count, byte *values); + byte PCD_ReadRegister(byte reg); + void PCD_ReadRegister(byte reg, byte count, byte *values, byte rxAlign = 0); + void setBitMask(unsigned char reg, unsigned char mask); + void PCD_SetRegisterBitMask(byte reg, byte mask); + void PCD_ClearRegisterBitMask(byte reg, byte mask); + byte PCD_CalculateCRC(byte *data, byte length, byte *result); + + ///////////////////////////////////////////////////////////////////////////////////// + // Functions for manipulating the MFRC522 + ///////////////////////////////////////////////////////////////////////////////////// + void PCD_Init(); + void PCD_Reset(); + void PCD_AntennaOn(); + void PCD_AntennaOff(); + byte PCD_GetAntennaGain(); + void PCD_SetAntennaGain(byte mask); + bool PCD_PerformSelfTest(); + + ///////////////////////////////////////////////////////////////////////////////////// + // Functions for communicating with PICCs + ///////////////////////////////////////////////////////////////////////////////////// + byte PCD_TransceiveData(byte *sendData, byte sendLen, byte *backData, byte *backLen, byte *validBits = NULL, byte rxAlign = 0, bool checkCRC = false); + byte PCD_CommunicateWithPICC(byte command, byte waitIRq, byte *sendData, byte sendLen, byte *backData = NULL, byte *backLen = NULL, byte *validBits = NULL, byte rxAlign = 0, bool checkCRC = false); + byte PICC_RequestA(byte *bufferATQA, byte *bufferSize); + byte PICC_WakeupA(byte *bufferATQA, byte *bufferSize); + byte PICC_REQA_or_WUPA(byte command, byte *bufferATQA, byte *bufferSize); + byte PICC_Select(Uid *uid, byte validBits = 0); + byte PICC_HaltA(); + + ///////////////////////////////////////////////////////////////////////////////////// + // Functions for communicating with MIFARE PICCs + ///////////////////////////////////////////////////////////////////////////////////// + byte PCD_Authenticate(byte command, byte blockAddr, MIFARE_Key *key, Uid *uid); + void PCD_StopCrypto1(); + byte MIFARE_Read(byte blockAddr, byte *buffer, byte *bufferSize); + byte MIFARE_Write(byte blockAddr, byte *buffer, byte bufferSize); + byte MIFARE_Decrement(byte blockAddr, long delta); + byte MIFARE_Increment(byte blockAddr, long delta); + byte MIFARE_Restore(byte blockAddr); + byte MIFARE_Transfer(byte blockAddr); + byte MIFARE_Ultralight_Write(byte page, byte *buffer, byte bufferSize); + byte MIFARE_GetValue(byte blockAddr, long *value); + byte MIFARE_SetValue(byte blockAddr, long value); + + ///////////////////////////////////////////////////////////////////////////////////// + // Support functions + ///////////////////////////////////////////////////////////////////////////////////// + byte PCD_MIFARE_Transceive(byte *sendData, byte sendLen, bool acceptTimeout = false); + // old function used too much memory, now name moved to flash; if you need char, copy from flash to memory + //const char *GetStatusCodeName(byte code); + const __FlashStringHelper *GetStatusCodeName(byte code); + byte PICC_GetType(byte sak); + // old function used too much memory, now name moved to flash; if you need char, copy from flash to memory + //const char *PICC_GetTypeName(byte type); + const __FlashStringHelper *PICC_GetTypeName(byte type); + void PICC_DumpToSerial(Uid *uid); + void PICC_DumpMifareClassicToSerial(Uid *uid, byte piccType, MIFARE_Key *key); + void PICC_DumpMifareClassicSectorToSerial(Uid *uid, MIFARE_Key *key, byte sector); + void PICC_DumpMifareUltralightToSerial(); + void MIFARE_SetAccessBits(byte *accessBitBuffer, byte g0, byte g1, byte g2, byte g3); + bool MIFARE_OpenUidBackdoor(bool logErrors); + bool MIFARE_SetUid(byte *newUid, byte uidSize, bool logErrors); + bool MIFARE_UnbrickUidSector(bool logErrors); + + ///////////////////////////////////////////////////////////////////////////////////// + // Convenience functions - does not add extra functionality + ///////////////////////////////////////////////////////////////////////////////////// + bool PICC_IsNewCardPresent(); + bool PICC_ReadCardSerial(); + +private: + byte _chipAddress; + byte _resetPowerDownPin; // Arduino pin connected to MFRC522's reset and power down input (Pin 6, NRSTPD, active low) + byte MIFARE_TwoStepHelper(byte command, byte blockAddr, long data); +}; + +#endif diff --git a/lib/lib_i2c/MPU6886/library.properties b/lib/lib_i2c/MPU6886/library.properties new file mode 100644 index 000000000..1a08c8516 --- /dev/null +++ b/lib/lib_i2c/MPU6886/library.properties @@ -0,0 +1,9 @@ +name=MPU6886 +version= +author=M5StickC +maintainer=Stephan Hadinger +sentence=Support for MPU6886 +paragraph=Support for MPU6886 +category= +url=https://github.com/m5stack/M5StickC/blob/master/src/utility/ +architectures=esp32,esp8266 diff --git a/lib/libesp32/CORE2_Library/MPU6886.cpp b/lib/lib_i2c/MPU6886/src/MPU6886.cpp similarity index 68% rename from lib/libesp32/CORE2_Library/MPU6886.cpp rename to lib/lib_i2c/MPU6886/src/MPU6886.cpp index 418c94b39..9ae45461d 100755 --- a/lib/libesp32/CORE2_Library/MPU6886.cpp +++ b/lib/lib_i2c/MPU6886/src/MPU6886.cpp @@ -2,39 +2,33 @@ #include #include -MPU6886::MPU6886(){ - -} - void MPU6886::I2C_Read_NBytes(uint8_t driver_Addr, uint8_t start_Addr, uint8_t number_Bytes, uint8_t *read_Buffer){ - Wire1.beginTransmission(driver_Addr); - Wire1.write(start_Addr); - Wire1.endTransmission(false); + myWire.beginTransmission(driver_Addr); + myWire.write(start_Addr); + myWire.endTransmission(false); uint8_t i = 0; - Wire1.requestFrom(driver_Addr,number_Bytes); + myWire.requestFrom(driver_Addr,number_Bytes); //! Put read results in the Rx buffer - while (Wire1.available()) { - read_Buffer[i++] = Wire1.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){ - Wire1.beginTransmission(driver_Addr); - Wire1.write(start_Addr); - Wire1.write(*write_Buffer); - Wire1.endTransmission(); + myWire.beginTransmission(driver_Addr); + myWire.write(start_Addr); + myWire.write(*write_Buffer); + myWire.endTransmission(); } int MPU6886::Init(void){ unsigned char tempdata[1]; unsigned char regdata; - - Wire1.begin(21,22); - + I2C_Read_NBytes(MPU6886_ADDRESS, MPU6886_WHOAMI, 1, tempdata); if(tempdata[0] != 0x19) return -1; @@ -52,11 +46,11 @@ int MPU6886::Init(void){ I2C_Write_NBytes(MPU6886_ADDRESS, MPU6886_PWR_MGMT_1, 1, ®data); delay(10); - regdata = 0x10; + regdata = 0x10; // AFS_8G I2C_Write_NBytes(MPU6886_ADDRESS, MPU6886_ACCEL_CONFIG, 1, ®data); delay(1); - regdata = 0x18; + regdata = 0x18; // GFS_2000DPS I2C_Write_NBytes(MPU6886_ADDRESS, MPU6886_GYRO_CONFIG, 1, ®data); delay(1); @@ -128,24 +122,24 @@ void MPU6886::getTempAdc(int16_t *t){ -//!俯仰,航向,横滚:pitch,yaw,roll,指三维空间中飞行器的旋转状态。 -void MPU6886::getAhrsData(float *pitch,float *roll,float *yaw){ +// //!俯仰,航向,横滚:pitch,yaw,roll,指三维空间中飞行器的旋转状态。 +// void MPU6886::getAhrsData(float *pitch,float *roll,float *yaw){ - float accX = 0; - float accY = 0; - float accZ = 0; +// float accX = 0; +// float accY = 0; +// float accZ = 0; - float gyroX = 0; - float gyroY = 0; - float gyroZ = 0; +// float gyroX = 0; +// float gyroY = 0; +// float gyroZ = 0; - getGyroData(&gyroX,&gyroY,&gyroZ); - getAccelData(&accX,&accY,&accZ); +// getGyroData(&gyroX,&gyroY,&gyroZ); +// getAccelData(&accX,&accY,&accZ); - MahonyAHRSupdateIMU(gyroX * DEG_TO_RAD, gyroY * DEG_TO_RAD, gyroZ * DEG_TO_RAD, accX, accY, accZ,pitch,roll,yaw); +// MahonyAHRSupdateIMU(gyroX * DEG_TO_RAD, gyroY * DEG_TO_RAD, gyroZ * DEG_TO_RAD, accX, accY, accZ,pitch,roll,yaw); -} +// } void MPU6886::getGres(){ @@ -153,16 +147,20 @@ void MPU6886::getGres(){ { // Possible gyro scales (and their register bit settings) are: case GFS_250DPS: - gRes = 250.0/32768.0; + gRes = 250.0f/32768.0f; + gyRange = 250; break; case GFS_500DPS: - gRes = 500.0/32768.0; + gRes = 500.0f/32768.0f; + gyRange = 500; break; case GFS_1000DPS: - gRes = 1000.0/32768.0; + gRes = 1000.0f/32768.0f; + gyRange = 1000; break; case GFS_2000DPS: - gRes = 2000.0/32768.0; + gRes = 2000.0f/32768.0f; + gyRange = 2000; break; } @@ -177,15 +175,19 @@ void MPU6886::getAres(){ // Here's a bit of an algorith to calculate DPS/(ADC tick) based on that 2-bit value: case AFS_2G: aRes = 2.0/32768.0; + acRange = 2000; break; case AFS_4G: aRes = 4.0/32768.0; + acRange = 4000; break; case AFS_8G: aRes = 8.0/32768.0; + acRange = 8000; break; case AFS_16G: aRes = 16.0/32768.0; + acRange = 16000; break; } @@ -215,7 +217,19 @@ void MPU6886::SetAccelFsr(Ascale scale) } +// x/y/z are in 1/1000 if g +// avoiding costly float calculations +void MPU6886::getAccelDataInt(int16_t* ax, int16_t* ay, int16_t* az) { + int16_t accX = 0; + int16_t accY = 0; + int16_t accZ = 0; + getAccelAdc(&accX, &accY, &accZ); + if (ax != nullptr) { *ax = ((int32_t)accX * acRange) / 0x7FFFL; } + if (ay != nullptr) { *ay = ((int32_t)accY * acRange) / 0x7FFFL; } + if (az != nullptr) { *az = ((int32_t)accZ * acRange) / 0x7FFFL; } + +} void MPU6886::getAccelData(float* ax, float* ay, float* az){ @@ -232,6 +246,20 @@ void MPU6886::getAccelData(float* ax, float* ay, float* az){ } +// x/y/z are in dps - degrees per second +// avoiding costly float calculations +void MPU6886::getGyroDataInt(int16_t* ax, int16_t* ay, int16_t* az) { + int16_t gyX = 0; + int16_t gyY = 0; + int16_t gyZ = 0; + getGyroAdc(&gyX, &gyY, &gyZ); + + if (ax != nullptr) { *ax = ((int32_t)gyX * gyRange) / 0x7FFFL; } + if (ay != nullptr) { *ay = ((int32_t)gyY * gyRange) / 0x7FFFL; } + if (az != nullptr) { *az = ((int32_t)gyZ * gyRange) / 0x7FFFL; } + +} + void MPU6886::getGyroData(float* gx, float* gy, float* gz){ int16_t gyroX = 0; int16_t gyroY = 0; diff --git a/lib/libesp32/CORE2_Library/MPU6886.h b/lib/lib_i2c/MPU6886/src/MPU6886.h similarity index 82% rename from lib/libesp32/CORE2_Library/MPU6886.h rename to lib/lib_i2c/MPU6886/src/MPU6886.h index b1a0fb16b..025f71587 100755 --- a/lib/libesp32/CORE2_Library/MPU6886.h +++ b/lib/lib_i2c/MPU6886/src/MPU6886.h @@ -10,7 +10,6 @@ #include #include -#include "MahonyAHRS.h" #define MPU6886_ADDRESS 0x68 #define MPU6886_WHOAMI 0x75 @@ -67,8 +66,15 @@ class MPU6886 { Gscale Gyscale = GFS_2000DPS; Ascale Acscale = AFS_8G; + int16_t acRange = 8000; // 1/1000 of g + int16_t gyRange = 2000; // dps - degree per second public: - MPU6886(); + MPU6886(void) {}; + #ifdef ESP32 + void setBus(uint32_t _bus) { myWire = _bus ? Wire1 : Wire; }; + #else + void setBus(uint32_t _bus) { myWire = Wire; }; + #endif int Init(void); void getAccelAdc(int16_t* ax, int16_t* ay, int16_t* az); void getGyroAdc(int16_t* gx, int16_t* gy, int16_t* gz); @@ -77,13 +83,17 @@ class MPU6886 { void getAccelData(float* ax, float* ay, float* az); void getGyroData(float* gx, float* gy, float* gz); void getTempData(float *t); + // int variants + void getAccelDataInt(int16_t* ax, int16_t* ay, int16_t* az); + void getGyroDataInt(int16_t* gx, int16_t* gy, int16_t* gz); void SetGyroFsr(Gscale scale); void SetAccelFsr(Ascale scale); - void getAhrsData(float *pitch,float *roll,float *yaw); + // void getAhrsData(float *pitch,float *roll,float *yaw); public: + TwoWire & myWire = Wire; // default to Wire (bus 0) float aRes, gRes; private: diff --git a/lib/libesp32/CORE2_Library/MahonyAHRS.cpp b/lib/libesp32/CORE2_Library/MahonyAHRS.cpp deleted file mode 100755 index cae005075..000000000 --- a/lib/libesp32/CORE2_Library/MahonyAHRS.cpp +++ /dev/null @@ -1,254 +0,0 @@ -//===================================================================================================== -// MahonyAHRS.c -//===================================================================================================== -// -// Madgwick's implementation of Mayhony's AHRS algorithm. -// See: http://www.x-io.co.uk/node/8#open_source_ahrs_and_imu_algorithms -// -// Date Author Notes -// 29/09/2011 SOH Madgwick Initial release -// 02/10/2011 SOH Madgwick Optimised for reduced CPU load -// -//===================================================================================================== - -//--------------------------------------------------------------------------------------------------- -// Header files - -#include "MahonyAHRS.h" -#include "Arduino.h" -#include -//--------------------------------------------------------------------------------------------------- -// Definitions - -#define sampleFreq 25.0f // sample frequency in Hz -#define twoKpDef (2.0f * 1.0f) // 2 * proportional gain -#define twoKiDef (2.0f * 0.0f) // 2 * integral gain - -//#define twoKiDef (0.0f * 0.0f) - -//--------------------------------------------------------------------------------------------------- -// Variable definitions - -volatile float twoKp = twoKpDef; // 2 * proportional gain (Kp) -volatile float twoKi = twoKiDef; // 2 * integral gain (Ki) -volatile float q0 = 1.0, q1 = 0.0, q2 = 0.0, q3 = 0.0; // quaternion of sensor frame relative to auxiliary frame -volatile float integralFBx = 0.0f, integralFBy = 0.0f, integralFBz = 0.0f; // integral error terms scaled by Ki - -//--------------------------------------------------------------------------------------------------- -// Function declarations - -//float invSqrt(float x); - -//==================================================================================================== -// Functions - -//--------------------------------------------------------------------------------------------------- -// AHRS algorithm update - -void MahonyAHRSupdate(float gx, float gy, float gz, float ax, float ay, float az, float mx, float my, float mz) { - float recipNorm; - float q0q0, q0q1, q0q2, q0q3, q1q1, q1q2, q1q3, q2q2, q2q3, q3q3; - float hx, hy, bx, bz; - float halfvx, halfvy, halfvz, halfwx, halfwy, halfwz; - float halfex, halfey, halfez; - float qa, qb, qc; - - // Use IMU algorithm if magnetometer measurement invalid (avoids NaN in magnetometer normalisation) - if((mx == 0.0f) && (my == 0.0f) && (mz == 0.0f)) { - //MahonyAHRSupdateIMU(gx, gy, gz, ax, ay, az); - return; - } - - // Compute feedback only if accelerometer measurement valid (avoids NaN in accelerometer normalisation) - if(!((ax == 0.0f) && (ay == 0.0f) && (az == 0.0f))) { - - // Normalise accelerometer measurement - recipNorm = sqrt(ax * ax + ay * ay + az * az); - ax *= recipNorm; - ay *= recipNorm; - az *= recipNorm; - - // Normalise magnetometer measurement - recipNorm = sqrt(mx * mx + my * my + mz * mz); - mx *= recipNorm; - my *= recipNorm; - mz *= recipNorm; - - // Auxiliary variables to avoid repeated arithmetic - q0q0 = q0 * q0; - q0q1 = q0 * q1; - q0q2 = q0 * q2; - q0q3 = q0 * q3; - q1q1 = q1 * q1; - q1q2 = q1 * q2; - q1q3 = q1 * q3; - q2q2 = q2 * q2; - q2q3 = q2 * q3; - q3q3 = q3 * q3; - - // Reference direction of Earth's magnetic field - hx = 2.0f * (mx * (0.5f - q2q2 - q3q3) + my * (q1q2 - q0q3) + mz * (q1q3 + q0q2)); - hy = 2.0f * (mx * (q1q2 + q0q3) + my * (0.5f - q1q1 - q3q3) + mz * (q2q3 - q0q1)); - bx = sqrt(hx * hx + hy * hy); - bz = 2.0f * (mx * (q1q3 - q0q2) + my * (q2q3 + q0q1) + mz * (0.5f - q1q1 - q2q2)); - - // Estimated direction of gravity and magnetic field - halfvx = q1q3 - q0q2; - halfvy = q0q1 + q2q3; - halfvz = q0q0 - 0.5f + q3q3; - halfwx = bx * (0.5f - q2q2 - q3q3) + bz * (q1q3 - q0q2); - halfwy = bx * (q1q2 - q0q3) + bz * (q0q1 + q2q3); - halfwz = bx * (q0q2 + q1q3) + bz * (0.5f - q1q1 - q2q2); - - // Error is sum of cross product between estimated direction and measured direction of field vectors - halfex = (ay * halfvz - az * halfvy) + (my * halfwz - mz * halfwy); - halfey = (az * halfvx - ax * halfvz) + (mz * halfwx - mx * halfwz); - halfez = (ax * halfvy - ay * halfvx) + (mx * halfwy - my * halfwx); - - // Compute and apply integral feedback if enabled - if(twoKi > 0.0f) { - integralFBx += twoKi * halfex * (1.0f / sampleFreq); // integral error scaled by Ki - integralFBy += twoKi * halfey * (1.0f / sampleFreq); - integralFBz += twoKi * halfez * (1.0f / sampleFreq); - gx += integralFBx; // apply integral feedback - gy += integralFBy; - gz += integralFBz; - } - else { - integralFBx = 0.0f; // prevent integral windup - integralFBy = 0.0f; - integralFBz = 0.0f; - } - - // Apply proportional feedback - gx += twoKp * halfex; - gy += twoKp * halfey; - gz += twoKp * halfez; - } - - // Integrate rate of change of quaternion - gx *= (0.5f * (1.0f / sampleFreq)); // pre-multiply common factors - gy *= (0.5f * (1.0f / sampleFreq)); - gz *= (0.5f * (1.0f / sampleFreq)); - qa = q0; - qb = q1; - qc = q2; - q0 += (-qb * gx - qc * gy - q3 * gz); - q1 += (qa * gx + qc * gz - q3 * gy); - q2 += (qa * gy - qb * gz + q3 * gx); - q3 += (qa * gz + qb * gy - qc * gx); - - // Normalise quaternion - recipNorm = sqrt(q0 * q0 + q1 * q1 + q2 * q2 + q3 * q3); - q0 *= recipNorm; - q1 *= recipNorm; - q2 *= recipNorm; - q3 *= recipNorm; -} - -//--------------------------------------------------------------------------------------------------- -// IMU algorithm update - -void MahonyAHRSupdateIMU(float gx, float gy, float gz, float ax, float ay, float az,float *pitch,float *roll,float *yaw) { - float recipNorm; - float halfvx, halfvy, halfvz; - float halfex, halfey, halfez; - float qa, qb, qc; - - - // Compute feedback only if accelerometer measurement valid (avoids NaN in accelerometer normalisation) - if(!((ax == 0.0f) && (ay == 0.0f) && (az == 0.0f))) { - - // Normalise accelerometer measurement - recipNorm = invSqrt(ax * ax + ay * ay + az * az); - ax *= recipNorm; - ay *= recipNorm; - az *= recipNorm; - - // Estimated direction of gravity and vector perpendicular to magnetic flux - halfvx = q1 * q3 - q0 * q2; - halfvy = q0 * q1 + q2 * q3; - halfvz = q0 * q0 - 0.5f + q3 * q3; - - - - // Error is sum of cross product between estimated and measured direction of gravity - halfex = (ay * halfvz - az * halfvy); - halfey = (az * halfvx - ax * halfvz); - halfez = (ax * halfvy - ay * halfvx); - - // Compute and apply integral feedback if enabled - if(twoKi > 0.0f) { - integralFBx += twoKi * halfex * (1.0f / sampleFreq); // integral error scaled by Ki - integralFBy += twoKi * halfey * (1.0f / sampleFreq); - integralFBz += twoKi * halfez * (1.0f / sampleFreq); - gx += integralFBx; // apply integral feedback - gy += integralFBy; - gz += integralFBz; - } - else { - integralFBx = 0.0f; // prevent integral windup - integralFBy = 0.0f; - integralFBz = 0.0f; - } - - // Apply proportional feedback - gx += twoKp * halfex; - gy += twoKp * halfey; - gz += twoKp * halfez; - } - - // Integrate rate of change of quaternion - gx *= (0.5f * (1.0f / sampleFreq)); // pre-multiply common factors - gy *= (0.5f * (1.0f / sampleFreq)); - gz *= (0.5f * (1.0f / sampleFreq)); - qa = q0; - qb = q1; - qc = q2; - q0 += (-qb * gx - qc * gy - q3 * gz); - q1 += (qa * gx + qc * gz - q3 * gy); - q2 += (qa * gy - qb * gz + q3 * gx); - q3 += (qa * gz + qb * gy - qc * gx); - - // Normalise quaternion - recipNorm = invSqrt(q0 * q0 + q1 * q1 + q2 * q2 + q3 * q3); - q0 *= recipNorm; - q1 *= recipNorm; - q2 *= recipNorm; - q3 *= recipNorm; - - - *pitch = asin(-2 * q1 * q3 + 2 * q0* q2); // pitch - *roll = atan2(2 * q2 * q3 + 2 * q0 * q1, -2 * q1 * q1 - 2 * q2* q2 + 1); // roll - *yaw = atan2(2*(q1*q2 + q0*q3),q0*q0+q1*q1-q2*q2-q3*q3); //yaw - - *pitch *= RAD_TO_DEG; - *yaw *= RAD_TO_DEG; - // Declination of SparkFun Electronics (40°05'26.6"N 105°11'05.9"W) is - // 8° 30' E ± 0° 21' (or 8.5°) on 2016-07-19 - // - http://www.ngdc.noaa.gov/geomag-web/#declination - *yaw -= 8.5; - *roll *= RAD_TO_DEG; - - ///Serial.printf("%f %f %f \r\n", pitch, roll, yaw); -} - -//--------------------------------------------------------------------------------------------------- -// Fast inverse square-root -// See: http://en.wikipedia.org/wiki/Fast_inverse_square_root - -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wstrict-aliasing" -float invSqrt(float x) { - float halfx = 0.5f * x; - float y = x; - long i = *(long*)&y; - i = 0x5f3759df - (i>>1); - y = *(float*)&i; - y = y * (1.5f - (halfx * y * y)); - return y; -} -#pragma GCC diagnostic pop -//==================================================================================================== -// END OF CODE -//==================================================================================================== diff --git a/lib/libesp32/CORE2_Library/MahonyAHRS.h b/lib/libesp32/CORE2_Library/MahonyAHRS.h deleted file mode 100755 index bae082d24..000000000 --- a/lib/libesp32/CORE2_Library/MahonyAHRS.h +++ /dev/null @@ -1,33 +0,0 @@ -//===================================================================================================== -// MahonyAHRS.h -//===================================================================================================== -// -// Madgwick's implementation of Mayhony's AHRS algorithm. -// See: http://www.x-io.co.uk/node/8#open_source_ahrs_and_imu_algorithms -// -// Date Author Notes -// 29/09/2011 SOH Madgwick Initial release -// 02/10/2011 SOH Madgwick Optimised for reduced CPU load -// -//===================================================================================================== -#ifndef MahonyAHRS_h -#define MahonyAHRS_h - -//---------------------------------------------------------------------------------------------------- -// Variable declaration - -extern volatile float twoKp; // 2 * proportional gain (Kp) -extern volatile float twoKi; // 2 * integral gain (Ki) -//volatile float q0, q1, q2, q3; // quaternion of sensor frame relative to auxiliary frame - -//--------------------------------------------------------------------------------------------------- -// Function declarations - -void MahonyAHRSupdate(float gx, float gy, float gz, float ax, float ay, float az, float mx, float my, float mz); -//void MahonyAHRSupdateIMU(float gx, float gy, float gz, float ax, float ay, float az); -void MahonyAHRSupdateIMU(float gx, float gy, float gz, float ax, float ay, float az,float *pitch,float *roll,float *yaw); -float invSqrt(float x); -#endif -//===================================================================================================== -// End of file -//===================================================================================================== diff --git a/tasmota/i18n.h b/tasmota/i18n.h index 061b240b4..da0893f89 100644 --- a/tasmota/i18n.h +++ b/tasmota/i18n.h @@ -782,6 +782,7 @@ const char JSON_SNS_RANGE[] PROGMEM = ",\"%s\":{\"" D_JSON_RANGE "\":%d}"; const char JSON_SNS_GNGPM[] PROGMEM = ",\"%s\":{\"" D_JSON_TOTAL_USAGE "\":%s,\"" D_JSON_FLOWRATE "\":%s}"; const char S_LOG_I2C_FOUND_AT[] PROGMEM = D_LOG_I2C "%s " D_FOUND_AT " 0x%x"; +const char S_LOG_I2C_FOUND_AT_PORT[] PROGMEM = D_LOG_I2C "%s " D_FOUND_AT " 0x%x (" D_PORT " %d)"; const char S_RSLT_POWER[] PROGMEM = D_RSLT_POWER; const char S_RSLT_RESULT[] PROGMEM = D_RSLT_RESULT; diff --git a/tasmota/my_user_config.h b/tasmota/my_user_config.h index b4bd5dfae..e33e5bf3c 100644 --- a/tasmota/my_user_config.h +++ b/tasmota/my_user_config.h @@ -607,6 +607,7 @@ // #define USE_EZORGB // [I2cDriver55] Enable support for EZO's RGB sensor (+0k5 code) - Shared EZO code required for any EZO device (+1k2 code) // #define USE_EZOPMP // [I2cDriver55] Enable support for EZO's PMP sensor (+0k3 code) - Shared EZO code required for any EZO device (+1k2 code) // #define USE_SEESAW_SOIL // [I2cDriver56] Enable Capacitice Soil Moisture & Temperature Sensor (I2C addresses 0x36 - 0x39) (+1k3 code) +// #define USE_MPU6886 // [I2cDriver58] Enable MPU6686 - found in M5Stack - support 2 I2C buses on ESP32 (I2C address 0x68) (+2k code) // #define USE_DISPLAY // Add I2C Display Support (+2k code) #define USE_DISPLAY_MODES1TO5 // Enable display mode 1 to 5 in addition to mode 0 diff --git a/tasmota/support.ino b/tasmota/support.ino index f4d030def..b1653ff2a 100644 --- a/tasmota/support.ino +++ b/tasmota/support.ino @@ -2053,10 +2053,19 @@ void I2cSetActive(uint32_t addr, uint32_t count = 1) // AddLog(LOG_LEVEL_DEBUG, PSTR("I2C: Active %08X,%08X,%08X,%08X"), i2c_active[0], i2c_active[1], i2c_active[2], i2c_active[3]); } -void I2cSetActiveFound(uint32_t addr, const char *types) +void I2cSetActiveFound(uint32_t addr, const char *types, uint32_t bus = 0); +void I2cSetActiveFound(uint32_t addr, const char *types, uint32_t bus) { I2cSetActive(addr); +#ifdef ESP32 + if (0 == bus) { + AddLog(LOG_LEVEL_INFO, S_LOG_I2C_FOUND_AT, types, addr); + } else { + AddLog(LOG_LEVEL_INFO, S_LOG_I2C_FOUND_AT_PORT, types, addr, bus); + } +#else AddLog(LOG_LEVEL_INFO, S_LOG_I2C_FOUND_AT, types, addr); +#endif // ESP32 } bool I2cActive(uint32_t addr) @@ -2068,14 +2077,24 @@ bool I2cActive(uint32_t addr) return false; } +#ifdef ESP32 +bool I2cSetDevice(uint32_t addr, uint32_t bus = 0); +bool I2cSetDevice(uint32_t addr, uint32_t bus) +#else bool I2cSetDevice(uint32_t addr) +#endif { +#ifdef ESP32 + TwoWire & myWire = (bus == 0) ? Wire : Wire1; +#else + TwoWire & myWire = Wire; +#endif addr &= 0x7F; // Max I2C address is 127 if (I2cActive(addr)) { return false; // If already active report as not present; } - Wire.beginTransmission((uint8_t)addr); - return (0 == Wire.endTransmission()); + myWire.beginTransmission((uint8_t)addr); + return (0 == myWire.endTransmission()); } #endif // USE_I2C diff --git a/tasmota/xdrv_52_3_berry_wire.ino b/tasmota/xdrv_52_3_berry_wire.ino index a5fccb44e..5959b98b9 100644 --- a/tasmota/xdrv_52_3_berry_wire.ino +++ b/tasmota/xdrv_52_3_berry_wire.ino @@ -185,7 +185,7 @@ extern "C" { be_raise(vm, kTypeError, nullptr); } - // Berry: `validwrite(address:int, reg:int, val:int, size:int) -> bool or nil` + // Berry: `write(address:int, reg:int, val:int, size:int) -> bool or nil` int32_t b_wire_validwrite(struct bvm *vm); int32_t b_wire_validwrite(struct bvm *vm) { int32_t top = be_top(vm); // Get the number of arguments @@ -202,7 +202,7 @@ extern "C" { be_raise(vm, kTypeError, nullptr); } - // Berry: `validread(address:int, reg:int, size:int) -> int or nil` + // Berry: `read(address:int, reg:int, size:int) -> int or nil` int32_t b_wire_validread(struct bvm *vm); int32_t b_wire_validread(struct bvm *vm) { int32_t top = be_top(vm); // Get the number of arguments diff --git a/tasmota/xdrv_84_esp32_core2.ino b/tasmota/xdrv_84_esp32_core2.ino index dc196536c..9f0d3cc60 100644 --- a/tasmota/xdrv_84_esp32_core2.ino +++ b/tasmota/xdrv_84_esp32_core2.ino @@ -32,15 +32,14 @@ rtc better sync #include #include -#include #include #include +#include #define XDRV_84 84 struct CORE2_globs { AXP192 Axp; - MPU6886 Mpu; BM8563_RTC Rtc; bool ready; bool tset; @@ -56,9 +55,6 @@ struct CORE2_ADC { float vbus_c; float batt_c; float temp; - int16_t x; - int16_t y; - int16_t z; } core2_adc; // cause SC card is needed by scripter @@ -75,9 +71,6 @@ void CORE2_Module_Init(void) { // motor voltage core2_globs.Axp.SetLDOVoltage(3,2000); - core2_globs.Mpu.Init(); - I2cSetActiveFound(MPU6886_ADDRESS, "MPU6886"); - core2_globs.Rtc.begin(); I2cSetActiveFound(RTC_ADRESS, "RTC"); @@ -119,12 +112,6 @@ const char HTTP_CORE2[] PROGMEM = "{s}BATT Voltage" "{m}%s V" "{e}" "{s}BATT Current" "{m}%s mA" "{e}" "{s}Chip Temperature" "{m}%s C" "{e}"; -#ifdef USE_MPU6886 -const char HTTP_CORE2_MPU[] PROGMEM = - "{s}MPU x" "{m}%d mg" "{e}" - "{s}MPU y" "{m}%d mg" "{e}" - "{s}MPU z" "{m}%d mg" "{e}"; -#endif // USE_MPU6886 #endif // USE_WEBSERVER @@ -146,18 +133,9 @@ void CORE2_WebShow(uint32_t json) { dtostrfd(core2_adc.temp, 2, tstring); if (json) { - ResponseAppend_P(PSTR(",\"CORE2\":{\"VBV\":%s,\"BV\":%s,\"VBC\":%s,\"BC\":%s,\"CT\":%s"), vstring, cstring, bvstring, bcstring, tstring); - -#ifdef USE_MPU6886 - ResponseAppend_P(PSTR(",\"MPUX\":%d,\"MPUY\":%d,\"MPUZ\":%d"), core2_adc.x, core2_adc.y, core2_adc.z); -#endif - ResponseJsonEnd(); + ResponseAppend_P(PSTR(",\"CORE2\":{\"VBV\":%s,\"BV\":%s,\"VBC\":%s,\"BC\":%s,\"CT\":%s}"), vstring, cstring, bvstring, bcstring, tstring); } else { WSContentSend_PD(HTTP_CORE2, vstring, cstring, bvstring, bcstring, tstring); - -#ifdef USE_MPU6886 - WSContentSend_PD(HTTP_CORE2_MPU, core2_adc.x, core2_adc.y, core2_adc.z); -#endif // USE_MPU6886 } } @@ -342,15 +320,6 @@ void CORE2_GetADC(void) { core2_adc.batt_c = core2_globs.Axp.GetBatCurrent(); core2_adc.temp = core2_globs.Axp.GetTempInAXP192(); -#ifdef USE_MPU6886 - float x; - float y; - float z; - core2_globs.Mpu.getAccelData(&x, &y, &z); - core2_adc.x=x*1000; - core2_adc.y=y*1000; - core2_adc.z=z*1000; -#endif // USE_MPU6886 } /*********************************************************************************************\ diff --git a/tasmota/xsns_85_mpu6886.ino b/tasmota/xsns_85_mpu6886.ino new file mode 100644 index 000000000..0cbe317f9 --- /dev/null +++ b/tasmota/xsns_85_mpu6886.ino @@ -0,0 +1,128 @@ +/* + xsns_84_tof10120.ino - TOF10120 sensor support for Tasmota + + Copyright (C) 2021 Stephan Hadinger and Theo Arends + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#ifdef USE_I2C +#ifdef USE_MPU6886 + +#include +/*********************************************************************************************\ + * MPU6886 + * Internal chip found in M5Stack devices, using `Wire1` internal I2C bus + * + * I2C Address: 0x68 + * +\*********************************************************************************************/ + +#define XSNS_85 85 +#define XI2C_58 58 // See I2CDEVICES.md + +#define MPU6886_ADDRESS 0x68 + +struct { + MPU6886 Mpu; + bool ready = false; + int16_t ax=0, ay=0, az=0; // accelerator data + int16_t gyx=0, gyy=0, gyz=0; // accelerator data +} mpu6886_sensor; + +/********************************************************************************************/ + +const char HTTP_MPU6686[] PROGMEM = + "{s}MPU6686 acc_x" "{m}%3_f G" "{e}" + "{s}MPU6686 acc_y" "{m}%3_f G" "{e}" + "{s}MPU6686 acc_z" "{m}%3_f G" "{e}" + "{s}MPU6686 gyr_x" "{m}%i dps" "{e}" + "{s}MPU6686 gyr_y" "{m}%i dps" "{e}" + "{s}MPU6686 gyr_z" "{m}%i dps" "{e}" + ; + +void MPU6686_Show(uint32_t json) { + if (json) { + ResponseAppend_P(PSTR(",\"MPU6686\":{\"AX\":%i,\"AY\":%i,\"AZ\":%i,\"GX\":%i,\"GY\":%i,\"GZ\":%i}"), + mpu6886_sensor.ax, mpu6886_sensor.ay, mpu6886_sensor.az, + mpu6886_sensor.gyx, mpu6886_sensor.gyy, mpu6886_sensor.gyz); + } else { + float ax = mpu6886_sensor.ax / 1000.0f; + float ay = mpu6886_sensor.ay / 1000.0f; + float az = mpu6886_sensor.az / 1000.0f; + WSContentSend_PD(HTTP_MPU6686, &ax, &ay, &az, + mpu6886_sensor.gyx, mpu6886_sensor.gyy, mpu6886_sensor.gyz); + + } +} + +void MPU6686Detect(void) { +#ifdef ESP32 + if (!I2cSetDevice(MPU6886_ADDRESS, 0)) { + if (!I2cSetDevice(MPU6886_ADDRESS, 1)) { return; } // check on bus 1 + mpu6886_sensor.Mpu.setBus(1); // switch to bus 1 + I2cSetActiveFound(MPU6886_ADDRESS, "MPU6886", 1); + } else { + I2cSetActiveFound(MPU6886_ADDRESS, "MPU6886", 0); + } +#else + if (!I2cSetDevice(MPU6886_ADDRESS)) { return; } + I2cSetActiveFound(MPU6886_ADDRESS, "MPU6886"); +#endif + + mpu6886_sensor.Mpu.Init(); + mpu6886_sensor.ready = true; +} + +void MPU6886Every_Second(void) { + mpu6886_sensor.Mpu.getAccelDataInt(&mpu6886_sensor.ax, &mpu6886_sensor.ay, &mpu6886_sensor.az); + mpu6886_sensor.Mpu.getGyroDataInt(&mpu6886_sensor.gyx, &mpu6886_sensor.gyy, &mpu6886_sensor.gyz); + + // AddLog(LOG_LEVEL_DEBUG, PSTR(">> Acc x=%i y=%i z=%i gx=%i gy=%i gz=%i"), mpu6886_sensor.ax, mpu6886_sensor.ay, mpu6886_sensor.az, + // mpu6886_sensor.gyx, mpu6886_sensor.gyy, mpu6886_sensor.gyz); + +} + +/*********************************************************************************************\ + * Interface +\*********************************************************************************************/ + +bool Xsns85(uint8_t function) { + if (!I2cEnabled(XI2C_58)) { return false; } + + bool result = false; + + if (FUNC_INIT == function) { + MPU6686Detect(); + } + else if (mpu6886_sensor.ready) { + switch (function) { + case FUNC_EVERY_SECOND: + MPU6886Every_Second(); + break; + case FUNC_JSON_APPEND: + MPU6686_Show(1); + break; +#ifdef USE_WEBSERVER + case FUNC_WEB_SENSOR: + MPU6686_Show(0); + break; +#endif // USE_WEBSERVER + } + } + return result; +} + +#endif // USE_MPU6886 +#endif // USE_I2C \ No newline at end of file From e559ee1c3043e6f7ec0c1344b5d37f845ac15785 Mon Sep 17 00:00:00 2001 From: Stephan Hadinger Date: Tue, 16 Mar 2021 16:30:34 +0100 Subject: [PATCH 28/87] Remove MFRC522 --- lib/lib_i2c/MFRC522_I2C/library.properties | 9 - lib/lib_i2c/MFRC522_I2C/src/MFRC522_I2C.cpp | 1752 ------------------- lib/lib_i2c/MFRC522_I2C/src/MFRC522_I2C.h | 407 ----- tasmota/tasmota_configurations_ESP32.h | 2 +- 4 files changed, 1 insertion(+), 2169 deletions(-) delete mode 100644 lib/lib_i2c/MFRC522_I2C/library.properties delete mode 100644 lib/lib_i2c/MFRC522_I2C/src/MFRC522_I2C.cpp delete mode 100644 lib/lib_i2c/MFRC522_I2C/src/MFRC522_I2C.h diff --git a/lib/lib_i2c/MFRC522_I2C/library.properties b/lib/lib_i2c/MFRC522_I2C/library.properties deleted file mode 100644 index 6253b9d93..000000000 --- a/lib/lib_i2c/MFRC522_I2C/library.properties +++ /dev/null @@ -1,9 +0,0 @@ -name=MFRC522_I2C -version= -author=AROZCAN, COOQROBOT -maintainer= -sentence=I2C port of MFRC522 SPI -paragraph=Library to use ARDUINO RFID MODULE KIT 13.56 MHZ WITH TAGS I2C, Based on ARDUINO RFID MODULE KIT 13.56 MHZ WITH TAGS SPI Library BY COOQROBOT. -category=Communication -url=https://github.com/m5stack/M5StickC/blob/master/examples/Unit/RFID/ -architectures=esp8266,esp32 diff --git a/lib/lib_i2c/MFRC522_I2C/src/MFRC522_I2C.cpp b/lib/lib_i2c/MFRC522_I2C/src/MFRC522_I2C.cpp deleted file mode 100644 index d63ee8d3e..000000000 --- a/lib/lib_i2c/MFRC522_I2C/src/MFRC522_I2C.cpp +++ /dev/null @@ -1,1752 +0,0 @@ -/* -* MFRC522.cpp - Library to use ARDUINO RFID MODULE KIT 13.56 MHZ WITH TAGS I2C BY AROZCAN -* MFRC522.cpp - Based on ARDUINO RFID MODULE KIT 13.56 MHZ WITH TAGS SPI Library BY COOQROBOT. -* NOTE: Please also check the comments in MFRC522.h - they provide useful hints and background information. -* Released into the public domain. -* Author: arozcan @ https://github.com/arozcan/MFRC522-I2C-Library -*/ - -#include -#include -#include "MFRC522_I2C.h" - - -///////////////////////////////////////////////////////////////////////////////////// -// Functions for setting up the Arduino -///////////////////////////////////////////////////////////////////////////////////// - -/** - * Constructor. - * Prepares the output pins. - */ -MFRC522::MFRC522( byte chipAddress - //byte resetPowerDownPin ///< Arduino pin connected to MFRC522's reset and power down input (Pin 6, NRSTPD, active low) - ) { - _chipAddress = chipAddress; - // _resetPowerDownPin = resetPowerDownPin; -} // End constructor - - -///////////////////////////////////////////////////////////////////////////////////// -// Basic interface functions for communicating with the MFRC522 -///////////////////////////////////////////////////////////////////////////////////// - -/** - * Writes a byte to the specified register in the MFRC522 chip. - * The interface is described in the datasheet section 8.1.2. - */ -void MFRC522::PCD_WriteRegister( byte reg, ///< The register to write to. One of the PCD_Register enums. - byte value ///< The value to write. - ) { - Wire.beginTransmission(_chipAddress); - Wire.write(reg); - Wire.write(value); - Wire.endTransmission(); -} // End PCD_WriteRegister() - -/** - * Writes a number of bytes to the specified register in the MFRC522 chip. - * The interface is described in the datasheet section 8.1.2. - */ -void MFRC522::PCD_WriteRegister( byte reg, ///< The register to write to. One of the PCD_Register enums. - byte count, ///< The number of bytes to write to the register - byte *values ///< The values to write. Byte array. - ) { - Wire.beginTransmission(_chipAddress); - Wire.write(reg); - for (byte index = 0; index < count; index++) { - Wire.write(values[index]); - } - Wire.endTransmission(); -} // End PCD_WriteRegister() - -/** - * Reads a byte from the specified register in the MFRC522 chip. - * The interface is described in the datasheet section 8.1.2. - */ -byte MFRC522::PCD_ReadRegister( byte reg ///< The register to read from. One of the PCD_Register enums. - ) { - byte value; - //digitalWrite(_chipSelectPin, LOW); // Select slave - Wire.beginTransmission(_chipAddress); - Wire.write(reg); - Wire.endTransmission(); - - Wire.requestFrom(_chipAddress, (byte)1); - value = Wire.read(); - return value; -} // End PCD_ReadRegister() - -/** - * Reads a number of bytes from the specified register in the MFRC522 chip. - * The interface is described in the datasheet section 8.1.2. - */ -void MFRC522::PCD_ReadRegister( byte reg, ///< The register to read from. One of the PCD_Register enums. - byte count, ///< The number of bytes to read - byte *values, ///< Byte array to store the values in. - byte rxAlign ///< Only bit positions rxAlign..7 in values[0] are updated. - ) { - if (count == 0) { - return; - } - byte address = reg; - byte index = 0; // Index in values array. - Wire.beginTransmission(_chipAddress); - Wire.write(address); - Wire.endTransmission(); - Wire.requestFrom(_chipAddress, count); - while (Wire.available()) { - if (index == 0 && rxAlign) { // Only update bit positions rxAlign..7 in values[0] - // Create bit mask for bit positions rxAlign..7 - byte mask = 0; - for (byte i = rxAlign; i <= 7; i++) { - mask |= (1 << i); - } - // Read value and tell that we want to read the same address again. - byte value = Wire.read(); - // Apply mask to both current value of values[0] and the new data in value. - values[0] = (values[index] & ~mask) | (value & mask); - } - else { // Normal case - values[index] = Wire.read(); - } - index++; - } -} // End PCD_ReadRegister() - -/** - * Sets the bits given in mask in register reg. - */ -void MFRC522::PCD_SetRegisterBitMask( byte reg, ///< The register to update. One of the PCD_Register enums. - byte mask ///< The bits to set. - ) { - byte tmp; - tmp = PCD_ReadRegister(reg); - PCD_WriteRegister(reg, tmp | mask); // set bit mask -} // End PCD_SetRegisterBitMask() - -/** - * Clears the bits given in mask from register reg. - */ -void MFRC522::PCD_ClearRegisterBitMask( byte reg, ///< The register to update. One of the PCD_Register enums. - byte mask ///< The bits to clear. - ) { - byte tmp; - tmp = PCD_ReadRegister(reg); - PCD_WriteRegister(reg, tmp & (~mask)); // clear bit mask -} // End PCD_ClearRegisterBitMask() - - -/** - * Use the CRC coprocessor in the MFRC522 to calculate a CRC_A. - * - * @return STATUS_OK on success, STATUS_??? otherwise. - */ -byte MFRC522::PCD_CalculateCRC( byte *data, ///< In: Pointer to the data to transfer to the FIFO for CRC calculation. - byte length, ///< In: The number of bytes to transfer. - byte *result ///< Out: Pointer to result buffer. Result is written to result[0..1], low byte first. - ) { - PCD_WriteRegister(CommandReg, PCD_Idle); // Stop any active command. - PCD_WriteRegister(DivIrqReg, 0x04); // Clear the CRCIRq interrupt request bit - PCD_SetRegisterBitMask(FIFOLevelReg, 0x80); // FlushBuffer = 1, FIFO initialization - PCD_WriteRegister(FIFODataReg, length, data); // Write data to the FIFO - PCD_WriteRegister(CommandReg, PCD_CalcCRC); // Start the calculation - - // Wait for the CRC calculation to complete. Each iteration of the while-loop takes 17.73�s. - word i = 5000; - byte n; - while (1) { - n = PCD_ReadRegister(DivIrqReg); // DivIrqReg[7..0] bits are: Set2 reserved reserved MfinActIRq reserved CRCIRq reserved reserved - if (n & 0x04) { // CRCIRq bit set - calculation done - break; - } - if (--i == 0) { // The emergency break. We will eventually terminate on this one after 89ms. Communication with the MFRC522 might be down. - return STATUS_TIMEOUT; - } - } - PCD_WriteRegister(CommandReg, PCD_Idle); // Stop calculating CRC for new content in the FIFO. - - // Transfer the result from the registers to the result buffer - result[0] = PCD_ReadRegister(CRCResultRegL); - result[1] = PCD_ReadRegister(CRCResultRegH); - return STATUS_OK; -} // End PCD_CalculateCRC() - - -///////////////////////////////////////////////////////////////////////////////////// -// Functions for manipulating the MFRC522 -///////////////////////////////////////////////////////////////////////////////////// - -/** - * Initializes the MFRC522 chip. - */ -void MFRC522::PCD_Init() { - // Set the chipSelectPin as digital output, do not select the slave yet - - // Set the resetPowerDownPin as digital output, do not reset or power down. - // pinMode(_resetPowerDownPin, OUTPUT); - - - // if (digitalRead(_resetPowerDownPin) == LOW) { //The MFRC522 chip is in power down mode. - // digitalWrite(_resetPowerDownPin, HIGH); // Exit power down mode. This triggers a hard reset. - // // Section 8.8.2 in the datasheet says the oscillator start-up time is the start up time of the crystal + 37,74�s. Let us be generous: 50ms. - // delay(50); - // } - // else { // Perform a soft reset - PCD_Reset(); - // } - - // When communicating with a PICC we need a timeout if something goes wrong. - // f_timer = 13.56 MHz / (2*TPreScaler+1) where TPreScaler = [TPrescaler_Hi:TPrescaler_Lo]. - // TPrescaler_Hi are the four low bits in TModeReg. TPrescaler_Lo is TPrescalerReg. - PCD_WriteRegister(TModeReg, 0x80); // TAuto=1; timer starts automatically at the end of the transmission in all communication modes at all speeds - PCD_WriteRegister(TPrescalerReg, 0xA9); // TPreScaler = TModeReg[3..0]:TPrescalerReg, ie 0x0A9 = 169 => f_timer=40kHz, ie a timer period of 25�s. - PCD_WriteRegister(TReloadRegH, 0x03); // Reload timer with 0x3E8 = 1000, ie 25ms before timeout. - PCD_WriteRegister(TReloadRegL, 0xE8); - - PCD_WriteRegister(TxASKReg, 0x40); // Default 0x00. Force a 100 % ASK modulation independent of the ModGsPReg register setting - PCD_WriteRegister(ModeReg, 0x3D); // Default 0x3F. Set the preset value for the CRC coprocessor for the CalcCRC command to 0x6363 (ISO 14443-3 part 6.2.4) - PCD_AntennaOn(); // Enable the antenna driver pins TX1 and TX2 (they were disabled by the reset) -} // End PCD_Init() - -/** - * Performs a soft reset on the MFRC522 chip and waits for it to be ready again. - */ -void MFRC522::PCD_Reset() { - PCD_WriteRegister(CommandReg, PCD_SoftReset); // Issue the SoftReset command. - // The datasheet does not mention how long the SoftRest command takes to complete. - // But the MFRC522 might have been in soft power-down mode (triggered by bit 4 of CommandReg) - // Section 8.8.2 in the datasheet says the oscillator start-up time is the start up time of the crystal + 37,74�s. Let us be generous: 50ms. - delay(50); - // Wait for the PowerDown bit in CommandReg to be cleared - while (PCD_ReadRegister(CommandReg) & (1<<4)) { - // PCD still restarting - unlikely after waiting 50ms, but better safe than sorry. - } -} // End PCD_Reset() - -/** - * Turns the antenna on by enabling pins TX1 and TX2. - * After a reset these pins are disabled. - */ -void MFRC522::PCD_AntennaOn() { - byte value = PCD_ReadRegister(TxControlReg); - if ((value & 0x03) != 0x03) { - PCD_WriteRegister(TxControlReg, value | 0x03); - } -} // End PCD_AntennaOn() - -/** - * Turns the antenna off by disabling pins TX1 and TX2. - */ -void MFRC522::PCD_AntennaOff() { - PCD_ClearRegisterBitMask(TxControlReg, 0x03); -} // End PCD_AntennaOff() - -/** - * Get the current MFRC522 Receiver Gain (RxGain[2:0]) value. - * See 9.3.3.6 / table 98 in http://www.nxp.com/documents/data_sheet/MFRC522.pdf - * NOTE: Return value scrubbed with (0x07<<4)=01110000b as RCFfgReg may use reserved bits. - * - * @return Value of the RxGain, scrubbed to the 3 bits used. - */ -byte MFRC522::PCD_GetAntennaGain() { - return PCD_ReadRegister(RFCfgReg) & (0x07<<4); -} // End PCD_GetAntennaGain() - -/** - * Set the MFRC522 Receiver Gain (RxGain) to value specified by given mask. - * See 9.3.3.6 / table 98 in http://www.nxp.com/documents/data_sheet/MFRC522.pdf - * NOTE: Given mask is scrubbed with (0x07<<4)=01110000b as RCFfgReg may use reserved bits. - */ -void MFRC522::PCD_SetAntennaGain(byte mask) { - if (PCD_GetAntennaGain() != mask) { // only bother if there is a change - PCD_ClearRegisterBitMask(RFCfgReg, (0x07<<4)); // clear needed to allow 000 pattern - PCD_SetRegisterBitMask(RFCfgReg, mask & (0x07<<4)); // only set RxGain[2:0] bits - } -} // End PCD_SetAntennaGain() - -/** - * Performs a self-test of the MFRC522 - * See 16.1.1 in http://www.nxp.com/documents/data_sheet/MFRC522.pdf - * - * @return Whether or not the test passed. - */ -bool MFRC522::PCD_PerformSelfTest() { - // This follows directly the steps outlined in 16.1.1 - // 1. Perform a soft reset. - PCD_Reset(); - - // 2. Clear the internal buffer by writing 25 bytes of 00h - byte ZEROES[25] = {0x00}; - PCD_SetRegisterBitMask(FIFOLevelReg, 0x80); // flush the FIFO buffer - PCD_WriteRegister(FIFODataReg, 25, ZEROES); // write 25 bytes of 00h to FIFO - PCD_WriteRegister(CommandReg, PCD_Mem); // transfer to internal buffer - - // 3. Enable self-test - PCD_WriteRegister(AutoTestReg, 0x09); - - // 4. Write 00h to FIFO buffer - PCD_WriteRegister(FIFODataReg, 0x00); - - // 5. Start self-test by issuing the CalcCRC command - PCD_WriteRegister(CommandReg, PCD_CalcCRC); - - // 6. Wait for self-test to complete - word i; - byte n; - for (i = 0; i < 0xFF; i++) { - n = PCD_ReadRegister(DivIrqReg); // DivIrqReg[7..0] bits are: Set2 reserved reserved MfinActIRq reserved CRCIRq reserved reserved - if (n & 0x04) { // CRCIRq bit set - calculation done - break; - } - } - PCD_WriteRegister(CommandReg, PCD_Idle); // Stop calculating CRC for new content in the FIFO. - - // 7. Read out resulting 64 bytes from the FIFO buffer. - byte result[64]; - PCD_ReadRegister(FIFODataReg, 64, result, 0); - - // Auto self-test done - // Reset AutoTestReg register to be 0 again. Required for normal operation. - PCD_WriteRegister(AutoTestReg, 0x00); - - // Determine firmware version (see section 9.3.4.8 in spec) - byte version = PCD_ReadRegister(VersionReg); - - // Pick the appropriate reference values - const byte *reference; - switch (version) { - case 0x88: // Fudan Semiconductor FM17522 clone - reference = FM17522_firmware_reference; - break; - case 0x90: // Version 0.0 - reference = MFRC522_firmware_referenceV0_0; - break; - case 0x91: // Version 1.0 - reference = MFRC522_firmware_referenceV1_0; - break; - case 0x92: // Version 2.0 - reference = MFRC522_firmware_referenceV2_0; - break; - default: // Unknown version - return false; - } - - // Verify that the results match up to our expectations - for (i = 0; i < 64; i++) { - if (result[i] != pgm_read_byte(&(reference[i]))) { - return false; - } - } - - // Test passed; all is good. - return true; -} // End PCD_PerformSelfTest() - -///////////////////////////////////////////////////////////////////////////////////// -// Functions for communicating with PICCs -///////////////////////////////////////////////////////////////////////////////////// - -/** - * Executes the Transceive command. - * CRC validation can only be done if backData and backLen are specified. - * - * @return STATUS_OK on success, STATUS_??? otherwise. - */ -byte MFRC522::PCD_TransceiveData( byte *sendData, ///< Pointer to the data to transfer to the FIFO. - byte sendLen, ///< Number of bytes to transfer to the FIFO. - byte *backData, ///< NULL or pointer to buffer if data should be read back after executing the command. - byte *backLen, ///< In: Max number of bytes to write to *backData. Out: The number of bytes returned. - byte *validBits, ///< In/Out: The number of valid bits in the last byte. 0 for 8 valid bits. Default NULL. - byte rxAlign, ///< In: Defines the bit position in backData[0] for the first bit received. Default 0. - bool checkCRC ///< In: True => The last two bytes of the response is assumed to be a CRC_A that must be validated. - ) { - byte waitIRq = 0x30; // RxIRq and IdleIRq - return PCD_CommunicateWithPICC(PCD_Transceive, waitIRq, sendData, sendLen, backData, backLen, validBits, rxAlign, checkCRC); -} // End PCD_TransceiveData() - -/** - * Transfers data to the MFRC522 FIFO, executes a command, waits for completion and transfers data back from the FIFO. - * CRC validation can only be done if backData and backLen are specified. - * - * @return STATUS_OK on success, STATUS_??? otherwise. - */ -byte MFRC522::PCD_CommunicateWithPICC( byte command, ///< The command to execute. One of the PCD_Command enums. - byte waitIRq, ///< The bits in the ComIrqReg register that signals successful completion of the command. - byte *sendData, ///< Pointer to the data to transfer to the FIFO. - byte sendLen, ///< Number of bytes to transfer to the FIFO. - byte *backData, ///< NULL or pointer to buffer if data should be read back after executing the command. - byte *backLen, ///< In: Max number of bytes to write to *backData. Out: The number of bytes returned. - byte *validBits, ///< In/Out: The number of valid bits in the last byte. 0 for 8 valid bits. - byte rxAlign, ///< In: Defines the bit position in backData[0] for the first bit received. Default 0. - bool checkCRC ///< In: True => The last two bytes of the response is assumed to be a CRC_A that must be validated. - ) { - byte n, _validBits = 0; - unsigned int i; - - // Prepare values for BitFramingReg - byte txLastBits = validBits ? *validBits : 0; - byte bitFraming = (rxAlign << 4) + txLastBits; // RxAlign = BitFramingReg[6..4]. TxLastBits = BitFramingReg[2..0] - - PCD_WriteRegister(CommandReg, PCD_Idle); // Stop any active command. - PCD_WriteRegister(ComIrqReg, 0x7F); // Clear all seven interrupt request bits - PCD_SetRegisterBitMask(FIFOLevelReg, 0x80); // FlushBuffer = 1, FIFO initialization - PCD_WriteRegister(FIFODataReg, sendLen, sendData); // Write sendData to the FIFO - PCD_WriteRegister(BitFramingReg, bitFraming); // Bit adjustments - PCD_WriteRegister(CommandReg, command); // Execute the command - if (command == PCD_Transceive) { - PCD_SetRegisterBitMask(BitFramingReg, 0x80); // StartSend=1, transmission of data starts - } - - // Wait for the command to complete. - // In PCD_Init() we set the TAuto flag in TModeReg. This means the timer automatically starts when the PCD stops transmitting. - // Each iteration of the do-while-loop takes 17.86�s. - i = 2000; - while (1) { - n = PCD_ReadRegister(ComIrqReg); // ComIrqReg[7..0] bits are: Set1 TxIRq RxIRq IdleIRq HiAlertIRq LoAlertIRq ErrIRq TimerIRq - if (n & waitIRq) { // One of the interrupts that signal success has been set. - break; - } - if (n & 0x01) { // Timer interrupt - nothing received in 25ms - return STATUS_TIMEOUT; - } - if (--i == 0) { // The emergency break. If all other condions fail we will eventually terminate on this one after 35.7ms. Communication with the MFRC522 might be down. - return STATUS_TIMEOUT; - } - } - - // Stop now if any errors except collisions were detected. - byte errorRegValue = PCD_ReadRegister(ErrorReg); // ErrorReg[7..0] bits are: WrErr TempErr reserved BufferOvfl CollErr CRCErr ParityErr ProtocolErr - if (errorRegValue & 0x13) { // BufferOvfl ParityErr ProtocolErr - return STATUS_ERROR; - } - - // If the caller wants data back, get it from the MFRC522. - if (backData && backLen) { - n = PCD_ReadRegister(FIFOLevelReg); // Number of bytes in the FIFO - if (n > *backLen) { - return STATUS_NO_ROOM; - } - *backLen = n; // Number of bytes returned - PCD_ReadRegister(FIFODataReg, n, backData, rxAlign); // Get received data from FIFO - _validBits = PCD_ReadRegister(ControlReg) & 0x07; // RxLastBits[2:0] indicates the number of valid bits in the last received byte. If this value is 000b, the whole byte is valid. - if (validBits) { - *validBits = _validBits; - } - } - - // Tell about collisions - if (errorRegValue & 0x08) { // CollErr - return STATUS_COLLISION; - } - - // Perform CRC_A validation if requested. - if (backData && backLen && checkCRC) { - // In this case a MIFARE Classic NAK is not OK. - if (*backLen == 1 && _validBits == 4) { - return STATUS_MIFARE_NACK; - } - // We need at least the CRC_A value and all 8 bits of the last byte must be received. - if (*backLen < 2 || _validBits != 0) { - return STATUS_CRC_WRONG; - } - // Verify CRC_A - do our own calculation and store the control in controlBuffer. - byte controlBuffer[2]; - n = PCD_CalculateCRC(&backData[0], *backLen - 2, &controlBuffer[0]); - if (n != STATUS_OK) { - return n; - } - if ((backData[*backLen - 2] != controlBuffer[0]) || (backData[*backLen - 1] != controlBuffer[1])) { - return STATUS_CRC_WRONG; - } - } - - return STATUS_OK; -} // End PCD_CommunicateWithPICC() - -/** - * Transmits a REQuest command, Type A. Invites PICCs in state IDLE to go to READY and prepare for anticollision or selection. 7 bit frame. - * Beware: When two PICCs are in the field at the same time I often get STATUS_TIMEOUT - probably due do bad antenna design. - * - * @return STATUS_OK on success, STATUS_??? otherwise. - */ -byte MFRC522::PICC_RequestA(byte *bufferATQA, ///< The buffer to store the ATQA (Answer to request) in - byte *bufferSize ///< Buffer size, at least two bytes. Also number of bytes returned if STATUS_OK. - ) { - return PICC_REQA_or_WUPA(PICC_CMD_REQA, bufferATQA, bufferSize); -} // End PICC_RequestA() - -/** - * Transmits a Wake-UP command, Type A. Invites PICCs in state IDLE and HALT to go to READY(*) and prepare for anticollision or selection. 7 bit frame. - * Beware: When two PICCs are in the field at the same time I often get STATUS_TIMEOUT - probably due do bad antenna design. - * - * @return STATUS_OK on success, STATUS_??? otherwise. - */ -byte MFRC522::PICC_WakeupA( byte *bufferATQA, ///< The buffer to store the ATQA (Answer to request) in - byte *bufferSize ///< Buffer size, at least two bytes. Also number of bytes returned if STATUS_OK. - ) { - return PICC_REQA_or_WUPA(PICC_CMD_WUPA, bufferATQA, bufferSize); -} // End PICC_WakeupA() - -/** - * Transmits REQA or WUPA commands. - * Beware: When two PICCs are in the field at the same time I often get STATUS_TIMEOUT - probably due do bad antenna design. - * - * @return STATUS_OK on success, STATUS_??? otherwise. - */ -byte MFRC522::PICC_REQA_or_WUPA( byte command, ///< The command to send - PICC_CMD_REQA or PICC_CMD_WUPA - byte *bufferATQA, ///< The buffer to store the ATQA (Answer to request) in - byte *bufferSize ///< Buffer size, at least two bytes. Also number of bytes returned if STATUS_OK. - ) { - byte validBits; - byte status; - - if (bufferATQA == NULL || *bufferSize < 2) { // The ATQA response is 2 bytes long. - return STATUS_NO_ROOM; - } - PCD_ClearRegisterBitMask(CollReg, 0x80); // ValuesAfterColl=1 => Bits received after collision are cleared. - validBits = 7; // For REQA and WUPA we need the short frame format - transmit only 7 bits of the last (and only) byte. TxLastBits = BitFramingReg[2..0] - status = PCD_TransceiveData(&command, 1, bufferATQA, bufferSize, &validBits); - if (status != STATUS_OK) { - return status; - } - if (*bufferSize != 2 || validBits != 0) { // ATQA must be exactly 16 bits. - return STATUS_ERROR; - } - return STATUS_OK; -} // End PICC_REQA_or_WUPA() - -/** - * Transmits SELECT/ANTICOLLISION commands to select a single PICC. - * Before calling this function the PICCs must be placed in the READY(*) state by calling PICC_RequestA() or PICC_WakeupA(). - * On success: - * - The chosen PICC is in state ACTIVE(*) and all other PICCs have returned to state IDLE/HALT. (Figure 7 of the ISO/IEC 14443-3 draft.) - * - The UID size and value of the chosen PICC is returned in *uid along with the SAK. - * - * A PICC UID consists of 4, 7 or 10 bytes. - * Only 4 bytes can be specified in a SELECT command, so for the longer UIDs two or three iterations are used: - * UID size Number of UID bytes Cascade levels Example of PICC - * ======== =================== ============== =============== - * single 4 1 MIFARE Classic - * double 7 2 MIFARE Ultralight - * triple 10 3 Not currently in use? - * - * @return STATUS_OK on success, STATUS_??? otherwise. - */ -byte MFRC522::PICC_Select( Uid *uid, ///< Pointer to Uid struct. Normally output, but can also be used to supply a known UID. - byte validBits ///< The number of known UID bits supplied in *uid. Normally 0. If set you must also supply uid->size. - ) { - bool uidComplete; - bool selectDone; - bool useCascadeTag; - byte cascadeLevel = 1; - byte result; - byte count; - byte index; - byte uidIndex; // The first index in uid->uidByte[] that is used in the current Cascade Level. - int8_t currentLevelKnownBits; // The number of known UID bits in the current Cascade Level. - byte buffer[9]; // The SELECT/ANTICOLLISION commands uses a 7 byte standard frame + 2 bytes CRC_A - byte bufferUsed; // The number of bytes used in the buffer, ie the number of bytes to transfer to the FIFO. - byte rxAlign; // Used in BitFramingReg. Defines the bit position for the first bit received. - byte txLastBits; // Used in BitFramingReg. The number of valid bits in the last transmitted byte. - byte *responseBuffer; - byte responseLength; - - // Description of buffer structure: - // Byte 0: SEL Indicates the Cascade Level: PICC_CMD_SEL_CL1, PICC_CMD_SEL_CL2 or PICC_CMD_SEL_CL3 - // Byte 1: NVB Number of Valid Bits (in complete command, not just the UID): High nibble: complete bytes, Low nibble: Extra bits. - // Byte 2: UID-data or CT See explanation below. CT means Cascade Tag. - // Byte 3: UID-data - // Byte 4: UID-data - // Byte 5: UID-data - // Byte 6: BCC Block Check Character - XOR of bytes 2-5 - // Byte 7: CRC_A - // Byte 8: CRC_A - // The BCC and CRC_A is only transmitted if we know all the UID bits of the current Cascade Level. - // - // Description of bytes 2-5: (Section 6.5.4 of the ISO/IEC 14443-3 draft: UID contents and cascade levels) - // UID size Cascade level Byte2 Byte3 Byte4 Byte5 - // ======== ============= ===== ===== ===== ===== - // 4 bytes 1 uid0 uid1 uid2 uid3 - // 7 bytes 1 CT uid0 uid1 uid2 - // 2 uid3 uid4 uid5 uid6 - // 10 bytes 1 CT uid0 uid1 uid2 - // 2 CT uid3 uid4 uid5 - // 3 uid6 uid7 uid8 uid9 - - // Sanity checks - if (validBits > 80) { - return STATUS_INVALID; - } - - // Prepare MFRC522 - PCD_ClearRegisterBitMask(CollReg, 0x80); // ValuesAfterColl=1 => Bits received after collision are cleared. - - // Repeat Cascade Level loop until we have a complete UID. - uidComplete = false; - while (!uidComplete) { - // Set the Cascade Level in the SEL byte, find out if we need to use the Cascade Tag in byte 2. - switch (cascadeLevel) { - case 1: - buffer[0] = PICC_CMD_SEL_CL1; - uidIndex = 0; - useCascadeTag = validBits && uid->size > 4; // When we know that the UID has more than 4 bytes - break; - - case 2: - buffer[0] = PICC_CMD_SEL_CL2; - uidIndex = 3; - useCascadeTag = validBits && uid->size > 7; // When we know that the UID has more than 7 bytes - break; - - case 3: - buffer[0] = PICC_CMD_SEL_CL3; - uidIndex = 6; - useCascadeTag = false; // Never used in CL3. - break; - - default: - return STATUS_INTERNAL_ERROR; - break; - } - - // How many UID bits are known in this Cascade Level? - currentLevelKnownBits = validBits - (8 * uidIndex); - if (currentLevelKnownBits < 0) { - currentLevelKnownBits = 0; - } - // Copy the known bits from uid->uidByte[] to buffer[] - index = 2; // destination index in buffer[] - if (useCascadeTag) { - buffer[index++] = PICC_CMD_CT; - } - byte bytesToCopy = currentLevelKnownBits / 8 + (currentLevelKnownBits % 8 ? 1 : 0); // The number of bytes needed to represent the known bits for this level. - if (bytesToCopy) { - byte maxBytes = useCascadeTag ? 3 : 4; // Max 4 bytes in each Cascade Level. Only 3 left if we use the Cascade Tag - if (bytesToCopy > maxBytes) { - bytesToCopy = maxBytes; - } - for (count = 0; count < bytesToCopy; count++) { - buffer[index++] = uid->uidByte[uidIndex + count]; - } - } - // Now that the data has been copied we need to include the 8 bits in CT in currentLevelKnownBits - if (useCascadeTag) { - currentLevelKnownBits += 8; - } - - // Repeat anti collision loop until we can transmit all UID bits + BCC and receive a SAK - max 32 iterations. - selectDone = false; - while (!selectDone) { - // Find out how many bits and bytes to send and receive. - if (currentLevelKnownBits >= 32) { // All UID bits in this Cascade Level are known. This is a SELECT. - //Serial.print(F("SELECT: currentLevelKnownBits=")); Serial.println(currentLevelKnownBits, DEC); - buffer[1] = 0x70; // NVB - Number of Valid Bits: Seven whole bytes - // Calculate BCC - Block Check Character - buffer[6] = buffer[2] ^ buffer[3] ^ buffer[4] ^ buffer[5]; - // Calculate CRC_A - result = PCD_CalculateCRC(buffer, 7, &buffer[7]); - if (result != STATUS_OK) { - return result; - } - txLastBits = 0; // 0 => All 8 bits are valid. - bufferUsed = 9; - // Store response in the last 3 bytes of buffer (BCC and CRC_A - not needed after tx) - responseBuffer = &buffer[6]; - responseLength = 3; - } - else { // This is an ANTICOLLISION. - //Serial.print(F("ANTICOLLISION: currentLevelKnownBits=")); Serial.println(currentLevelKnownBits, DEC); - txLastBits = currentLevelKnownBits % 8; - count = currentLevelKnownBits / 8; // Number of whole bytes in the UID part. - index = 2 + count; // Number of whole bytes: SEL + NVB + UIDs - buffer[1] = (index << 4) + txLastBits; // NVB - Number of Valid Bits - bufferUsed = index + (txLastBits ? 1 : 0); - // Store response in the unused part of buffer - responseBuffer = &buffer[index]; - responseLength = sizeof(buffer) - index; - } - - // Set bit adjustments - rxAlign = txLastBits; // Having a seperate variable is overkill. But it makes the next line easier to read. - PCD_WriteRegister(BitFramingReg, (rxAlign << 4) + txLastBits); // RxAlign = BitFramingReg[6..4]. TxLastBits = BitFramingReg[2..0] - - // Transmit the buffer and receive the response. - result = PCD_TransceiveData(buffer, bufferUsed, responseBuffer, &responseLength, &txLastBits, rxAlign); - if (result == STATUS_COLLISION) { // More than one PICC in the field => collision. - result = PCD_ReadRegister(CollReg); // CollReg[7..0] bits are: ValuesAfterColl reserved CollPosNotValid CollPos[4:0] - if (result & 0x20) { // CollPosNotValid - return STATUS_COLLISION; // Without a valid collision position we cannot continue - } - byte collisionPos = result & 0x1F; // Values 0-31, 0 means bit 32. - if (collisionPos == 0) { - collisionPos = 32; - } - if (collisionPos <= currentLevelKnownBits) { // No progress - should not happen - return STATUS_INTERNAL_ERROR; - } - // Choose the PICC with the bit set. - currentLevelKnownBits = collisionPos; - count = (currentLevelKnownBits - 1) % 8; // The bit to modify - index = 1 + (currentLevelKnownBits / 8) + (count ? 1 : 0); // First byte is index 0. - buffer[index] |= (1 << count); - } - else if (result != STATUS_OK) { - return result; - } - else { // STATUS_OK - if (currentLevelKnownBits >= 32) { // This was a SELECT. - selectDone = true; // No more anticollision - // We continue below outside the while. - } - else { // This was an ANTICOLLISION. - // We now have all 32 bits of the UID in this Cascade Level - currentLevelKnownBits = 32; - // Run loop again to do the SELECT. - } - } - } // End of while (!selectDone) - - // We do not check the CBB - it was constructed by us above. - - // Copy the found UID bytes from buffer[] to uid->uidByte[] - index = (buffer[2] == PICC_CMD_CT) ? 3 : 2; // source index in buffer[] - bytesToCopy = (buffer[2] == PICC_CMD_CT) ? 3 : 4; - for (count = 0; count < bytesToCopy; count++) { - uid->uidByte[uidIndex + count] = buffer[index++]; - } - - // Check response SAK (Select Acknowledge) - if (responseLength != 3 || txLastBits != 0) { // SAK must be exactly 24 bits (1 byte + CRC_A). - return STATUS_ERROR; - } - // Verify CRC_A - do our own calculation and store the control in buffer[2..3] - those bytes are not needed anymore. - result = PCD_CalculateCRC(responseBuffer, 1, &buffer[2]); - if (result != STATUS_OK) { - return result; - } - if ((buffer[2] != responseBuffer[1]) || (buffer[3] != responseBuffer[2])) { - return STATUS_CRC_WRONG; - } - if (responseBuffer[0] & 0x04) { // Cascade bit set - UID not complete yes - cascadeLevel++; - } - else { - uidComplete = true; - uid->sak = responseBuffer[0]; - } - } // End of while (!uidComplete) - - // Set correct uid->size - uid->size = 3 * cascadeLevel + 1; - - return STATUS_OK; -} // End PICC_Select() - -/** - * Instructs a PICC in state ACTIVE(*) to go to state HALT. - * - * @return STATUS_OK on success, STATUS_??? otherwise. - */ -byte MFRC522::PICC_HaltA() { - byte result; - byte buffer[4]; - - // Build command buffer - buffer[0] = PICC_CMD_HLTA; - buffer[1] = 0; - // Calculate CRC_A - result = PCD_CalculateCRC(buffer, 2, &buffer[2]); - if (result != STATUS_OK) { - return result; - } - - // Send the command. - // The standard says: - // If the PICC responds with any modulation during a period of 1 ms after the end of the frame containing the - // HLTA command, this response shall be interpreted as 'not acknowledge'. - // We interpret that this way: Only STATUS_TIMEOUT is an success. - result = PCD_TransceiveData(buffer, sizeof(buffer), NULL, 0); - if (result == STATUS_TIMEOUT) { - return STATUS_OK; - } - if (result == STATUS_OK) { // That is ironically NOT ok in this case ;-) - return STATUS_ERROR; - } - return result; -} // End PICC_HaltA() - - -///////////////////////////////////////////////////////////////////////////////////// -// Functions for communicating with MIFARE PICCs -///////////////////////////////////////////////////////////////////////////////////// - -/** - * Executes the MFRC522 MFAuthent command. - * This command manages MIFARE authentication to enable a secure communication to any MIFARE Mini, MIFARE 1K and MIFARE 4K card. - * The authentication is described in the MFRC522 datasheet section 10.3.1.9 and http://www.nxp.com/documents/data_sheet/MF1S503x.pdf section 10.1. - * For use with MIFARE Classic PICCs. - * The PICC must be selected - ie in state ACTIVE(*) - before calling this function. - * Remember to call PCD_StopCrypto1() after communicating with the authenticated PICC - otherwise no new communications can start. - * - * All keys are set to FFFFFFFFFFFFh at chip delivery. - * - * @return STATUS_OK on success, STATUS_??? otherwise. Probably STATUS_TIMEOUT if you supply the wrong key. - */ -byte MFRC522::PCD_Authenticate(byte command, ///< PICC_CMD_MF_AUTH_KEY_A or PICC_CMD_MF_AUTH_KEY_B - byte blockAddr, ///< The block number. See numbering in the comments in the .h file. - MIFARE_Key *key, ///< Pointer to the Crypteo1 key to use (6 bytes) - Uid *uid ///< Pointer to Uid struct. The first 4 bytes of the UID is used. - ) { - byte waitIRq = 0x10; // IdleIRq - - // Build command buffer - byte sendData[12]; - sendData[0] = command; - sendData[1] = blockAddr; - for (byte i = 0; i < MF_KEY_SIZE; i++) { // 6 key bytes - sendData[2+i] = key->keyByte[i]; - } - for (byte i = 0; i < 4; i++) { // The first 4 bytes of the UID - sendData[8+i] = uid->uidByte[i]; - } - - // Start the authentication. - return PCD_CommunicateWithPICC(PCD_MFAuthent, waitIRq, &sendData[0], sizeof(sendData)); -} // End PCD_Authenticate() - -/** - * Used to exit the PCD from its authenticated state. - * Remember to call this function after communicating with an authenticated PICC - otherwise no new communications can start. - */ -void MFRC522::PCD_StopCrypto1() { - // Clear MFCrypto1On bit - PCD_ClearRegisterBitMask(Status2Reg, 0x08); // Status2Reg[7..0] bits are: TempSensClear I2CForceHS reserved reserved MFCrypto1On ModemState[2:0] -} // End PCD_StopCrypto1() - -/** - * Reads 16 bytes (+ 2 bytes CRC_A) from the active PICC. - * - * For MIFARE Classic the sector containing the block must be authenticated before calling this function. - * - * For MIFARE Ultralight only addresses 00h to 0Fh are decoded. - * The MF0ICU1 returns a NAK for higher addresses. - * The MF0ICU1 responds to the READ command by sending 16 bytes starting from the page address defined by the command argument. - * For example; if blockAddr is 03h then pages 03h, 04h, 05h, 06h are returned. - * A roll-back is implemented: If blockAddr is 0Eh, then the contents of pages 0Eh, 0Fh, 00h and 01h are returned. - * - * The buffer must be at least 18 bytes because a CRC_A is also returned. - * Checks the CRC_A before returning STATUS_OK. - * - * @return STATUS_OK on success, STATUS_??? otherwise. - */ -byte MFRC522::MIFARE_Read( byte blockAddr, ///< MIFARE Classic: The block (0-0xff) number. MIFARE Ultralight: The first page to return data from. - byte *buffer, ///< The buffer to store the data in - byte *bufferSize ///< Buffer size, at least 18 bytes. Also number of bytes returned if STATUS_OK. - ) { - byte result; - - // Sanity check - if (buffer == NULL || *bufferSize < 18) { - return STATUS_NO_ROOM; - } - - // Build command buffer - buffer[0] = PICC_CMD_MF_READ; - buffer[1] = blockAddr; - // Calculate CRC_A - result = PCD_CalculateCRC(buffer, 2, &buffer[2]); - if (result != STATUS_OK) { - return result; - } - - // Transmit the buffer and receive the response, validate CRC_A. - return PCD_TransceiveData(buffer, 4, buffer, bufferSize, NULL, 0, true); -} // End MIFARE_Read() - -/** - * Writes 16 bytes to the active PICC. - * - * For MIFARE Classic the sector containing the block must be authenticated before calling this function. - * - * For MIFARE Ultralight the operation is called "COMPATIBILITY WRITE". - * Even though 16 bytes are transferred to the Ultralight PICC, only the least significant 4 bytes (bytes 0 to 3) - * are written to the specified address. It is recommended to set the remaining bytes 04h to 0Fh to all logic 0. - * * - * @return STATUS_OK on success, STATUS_??? otherwise. - */ -byte MFRC522::MIFARE_Write( byte blockAddr, ///< MIFARE Classic: The block (0-0xff) number. MIFARE Ultralight: The page (2-15) to write to. - byte *buffer, ///< The 16 bytes to write to the PICC - byte bufferSize ///< Buffer size, must be at least 16 bytes. Exactly 16 bytes are written. - ) { - byte result; - - // Sanity check - if (buffer == NULL || bufferSize < 16) { - return STATUS_INVALID; - } - - // Mifare Classic protocol requires two communications to perform a write. - // Step 1: Tell the PICC we want to write to block blockAddr. - byte cmdBuffer[2]; - cmdBuffer[0] = PICC_CMD_MF_WRITE; - cmdBuffer[1] = blockAddr; - result = PCD_MIFARE_Transceive(cmdBuffer, 2); // Adds CRC_A and checks that the response is MF_ACK. - if (result != STATUS_OK) { - return result; - } - - // Step 2: Transfer the data - result = PCD_MIFARE_Transceive(buffer, bufferSize); // Adds CRC_A and checks that the response is MF_ACK. - if (result != STATUS_OK) { - return result; - } - - return STATUS_OK; -} // End MIFARE_Write() - -/** - * Writes a 4 byte page to the active MIFARE Ultralight PICC. - * - * @return STATUS_OK on success, STATUS_??? otherwise. - */ -byte MFRC522::MIFARE_Ultralight_Write( byte page, ///< The page (2-15) to write to. - byte *buffer, ///< The 4 bytes to write to the PICC - byte bufferSize ///< Buffer size, must be at least 4 bytes. Exactly 4 bytes are written. - ) { - byte result; - - // Sanity check - if (buffer == NULL || bufferSize < 4) { - return STATUS_INVALID; - } - - // Build commmand buffer - byte cmdBuffer[6]; - cmdBuffer[0] = PICC_CMD_UL_WRITE; - cmdBuffer[1] = page; - memcpy(&cmdBuffer[2], buffer, 4); - - // Perform the write - result = PCD_MIFARE_Transceive(cmdBuffer, 6); // Adds CRC_A and checks that the response is MF_ACK. - if (result != STATUS_OK) { - return result; - } - return STATUS_OK; -} // End MIFARE_Ultralight_Write() - -/** - * MIFARE Decrement subtracts the delta from the value of the addressed block, and stores the result in a volatile memory. - * For MIFARE Classic only. The sector containing the block must be authenticated before calling this function. - * Only for blocks in "value block" mode, ie with access bits [C1 C2 C3] = [110] or [001]. - * Use MIFARE_Transfer() to store the result in a block. - * - * @return STATUS_OK on success, STATUS_??? otherwise. - */ -byte MFRC522::MIFARE_Decrement( byte blockAddr, ///< The block (0-0xff) number. - long delta ///< This number is subtracted from the value of block blockAddr. - ) { - return MIFARE_TwoStepHelper(PICC_CMD_MF_DECREMENT, blockAddr, delta); -} // End MIFARE_Decrement() - -/** - * MIFARE Increment adds the delta to the value of the addressed block, and stores the result in a volatile memory. - * For MIFARE Classic only. The sector containing the block must be authenticated before calling this function. - * Only for blocks in "value block" mode, ie with access bits [C1 C2 C3] = [110] or [001]. - * Use MIFARE_Transfer() to store the result in a block. - * - * @return STATUS_OK on success, STATUS_??? otherwise. - */ -byte MFRC522::MIFARE_Increment( byte blockAddr, ///< The block (0-0xff) number. - long delta ///< This number is added to the value of block blockAddr. - ) { - return MIFARE_TwoStepHelper(PICC_CMD_MF_INCREMENT, blockAddr, delta); -} // End MIFARE_Increment() - -/** - * MIFARE Restore copies the value of the addressed block into a volatile memory. - * For MIFARE Classic only. The sector containing the block must be authenticated before calling this function. - * Only for blocks in "value block" mode, ie with access bits [C1 C2 C3] = [110] or [001]. - * Use MIFARE_Transfer() to store the result in a block. - * - * @return STATUS_OK on success, STATUS_??? otherwise. - */ -byte MFRC522::MIFARE_Restore( byte blockAddr ///< The block (0-0xff) number. - ) { - // The datasheet describes Restore as a two step operation, but does not explain what data to transfer in step 2. - // Doing only a single step does not work, so I chose to transfer 0L in step two. - return MIFARE_TwoStepHelper(PICC_CMD_MF_RESTORE, blockAddr, 0L); -} // End MIFARE_Restore() - -/** - * Helper function for the two-step MIFARE Classic protocol operations Decrement, Increment and Restore. - * - * @return STATUS_OK on success, STATUS_??? otherwise. - */ -byte MFRC522::MIFARE_TwoStepHelper( byte command, ///< The command to use - byte blockAddr, ///< The block (0-0xff) number. - long data ///< The data to transfer in step 2 - ) { - byte result; - byte cmdBuffer[2]; // We only need room for 2 bytes. - - // Step 1: Tell the PICC the command and block address - cmdBuffer[0] = command; - cmdBuffer[1] = blockAddr; - result = PCD_MIFARE_Transceive( cmdBuffer, 2); // Adds CRC_A and checks that the response is MF_ACK. - if (result != STATUS_OK) { - return result; - } - - // Step 2: Transfer the data - result = PCD_MIFARE_Transceive( (byte *)&data, 4, true); // Adds CRC_A and accept timeout as success. - if (result != STATUS_OK) { - return result; - } - - return STATUS_OK; -} // End MIFARE_TwoStepHelper() - -/** - * MIFARE Transfer writes the value stored in the volatile memory into one MIFARE Classic block. - * For MIFARE Classic only. The sector containing the block must be authenticated before calling this function. - * Only for blocks in "value block" mode, ie with access bits [C1 C2 C3] = [110] or [001]. - * - * @return STATUS_OK on success, STATUS_??? otherwise. - */ -byte MFRC522::MIFARE_Transfer( byte blockAddr ///< The block (0-0xff) number. - ) { - byte result; - byte cmdBuffer[2]; // We only need room for 2 bytes. - - // Tell the PICC we want to transfer the result into block blockAddr. - cmdBuffer[0] = PICC_CMD_MF_TRANSFER; - cmdBuffer[1] = blockAddr; - result = PCD_MIFARE_Transceive( cmdBuffer, 2); // Adds CRC_A and checks that the response is MF_ACK. - if (result != STATUS_OK) { - return result; - } - return STATUS_OK; -} // End MIFARE_Transfer() - -/** - * Helper routine to read the current value from a Value Block. - * - * Only for MIFARE Classic and only for blocks in "value block" mode, that - * is: with access bits [C1 C2 C3] = [110] or [001]. The sector containing - * the block must be authenticated before calling this function. - * - * @param[in] blockAddr The block (0x00-0xff) number. - * @param[out] value Current value of the Value Block. - * @return STATUS_OK on success, STATUS_??? otherwise. - */ -byte MFRC522::MIFARE_GetValue(byte blockAddr, long *value) { - byte status; - byte buffer[18]; - byte size = sizeof(buffer); - - // Read the block - status = MIFARE_Read(blockAddr, buffer, &size); - if (status == STATUS_OK) { - // Extract the value - *value = (long(buffer[3])<<24) | (long(buffer[2])<<16) | (long(buffer[1])<<8) | long(buffer[0]); - } - return status; -} // End MIFARE_GetValue() - -/** - * Helper routine to write a specific value into a Value Block. - * - * Only for MIFARE Classic and only for blocks in "value block" mode, that - * is: with access bits [C1 C2 C3] = [110] or [001]. The sector containing - * the block must be authenticated before calling this function. - * - * @param[in] blockAddr The block (0x00-0xff) number. - * @param[in] value New value of the Value Block. - * @return STATUS_OK on success, STATUS_??? otherwise. - */ -byte MFRC522::MIFARE_SetValue(byte blockAddr, long value) { - byte buffer[18]; - - // Translate the long into 4 bytes; repeated 2x in value block - buffer[0] = buffer[ 8] = (value & 0xFF); - buffer[1] = buffer[ 9] = (value & 0xFF00) >> 8; - buffer[2] = buffer[10] = (value & 0xFF0000) >> 16; - buffer[3] = buffer[11] = (value & 0xFF000000) >> 24; - // Inverse 4 bytes also found in value block - buffer[4] = ~buffer[0]; - buffer[5] = ~buffer[1]; - buffer[6] = ~buffer[2]; - buffer[7] = ~buffer[3]; - // Address 2x with inverse address 2x - buffer[12] = buffer[14] = blockAddr; - buffer[13] = buffer[15] = ~blockAddr; - - // Write the whole data block - return MIFARE_Write(blockAddr, buffer, 16); -} // End MIFARE_SetValue() - -///////////////////////////////////////////////////////////////////////////////////// -// Support functions -///////////////////////////////////////////////////////////////////////////////////// - -/** - * Wrapper for MIFARE protocol communication. - * Adds CRC_A, executes the Transceive command and checks that the response is MF_ACK or a timeout. - * - * @return STATUS_OK on success, STATUS_??? otherwise. - */ -byte MFRC522::PCD_MIFARE_Transceive( byte *sendData, ///< Pointer to the data to transfer to the FIFO. Do NOT include the CRC_A. - byte sendLen, ///< Number of bytes in sendData. - bool acceptTimeout ///< True => A timeout is also success - ) { - byte result; - byte cmdBuffer[18]; // We need room for 16 bytes data and 2 bytes CRC_A. - - // Sanity check - if (sendData == NULL || sendLen > 16) { - return STATUS_INVALID; - } - - // Copy sendData[] to cmdBuffer[] and add CRC_A - memcpy(cmdBuffer, sendData, sendLen); - result = PCD_CalculateCRC(cmdBuffer, sendLen, &cmdBuffer[sendLen]); - if (result != STATUS_OK) { - return result; - } - sendLen += 2; - - // Transceive the data, store the reply in cmdBuffer[] - byte waitIRq = 0x30; // RxIRq and IdleIRq - byte cmdBufferSize = sizeof(cmdBuffer); - byte validBits = 0; - result = PCD_CommunicateWithPICC(PCD_Transceive, waitIRq, cmdBuffer, sendLen, cmdBuffer, &cmdBufferSize, &validBits); - if (acceptTimeout && result == STATUS_TIMEOUT) { - return STATUS_OK; - } - if (result != STATUS_OK) { - return result; - } - // The PICC must reply with a 4 bit ACK - if (cmdBufferSize != 1 || validBits != 4) { - return STATUS_ERROR; - } - if (cmdBuffer[0] != MF_ACK) { - return STATUS_MIFARE_NACK; - } - return STATUS_OK; -} // End PCD_MIFARE_Transceive() - -/** - * Returns a __FlashStringHelper pointer to a status code name. - * - * @return const __FlashStringHelper * - */ -const __FlashStringHelper *MFRC522::GetStatusCodeName(byte code ///< One of the StatusCode enums. - ) { - switch (code) { - case STATUS_OK: return F("Success."); break; - case STATUS_ERROR: return F("Error in communication."); break; - case STATUS_COLLISION: return F("Collission detected."); break; - case STATUS_TIMEOUT: return F("Timeout in communication."); break; - case STATUS_NO_ROOM: return F("A buffer is not big enough."); break; - case STATUS_INTERNAL_ERROR: return F("Internal error in the code. Should not happen."); break; - case STATUS_INVALID: return F("Invalid argument."); break; - case STATUS_CRC_WRONG: return F("The CRC_A does not match."); break; - case STATUS_MIFARE_NACK: return F("A MIFARE PICC responded with NAK."); break; - default: return F("Unknown error"); break; - } -} // End GetStatusCodeName() - -/** - * Translates the SAK (Select Acknowledge) to a PICC type. - * - * @return PICC_Type - */ -byte MFRC522::PICC_GetType(byte sak ///< The SAK byte returned from PICC_Select(). - ) { - if (sak & 0x04) { // UID not complete - return PICC_TYPE_NOT_COMPLETE; - } - - switch (sak) { - case 0x09: return PICC_TYPE_MIFARE_MINI; break; - case 0x08: return PICC_TYPE_MIFARE_1K; break; - case 0x18: return PICC_TYPE_MIFARE_4K; break; - case 0x00: return PICC_TYPE_MIFARE_UL; break; - case 0x10: - case 0x11: return PICC_TYPE_MIFARE_PLUS; break; - case 0x01: return PICC_TYPE_TNP3XXX; break; - default: break; - } - - if (sak & 0x20) { - return PICC_TYPE_ISO_14443_4; - } - - if (sak & 0x40) { - return PICC_TYPE_ISO_18092; - } - - return PICC_TYPE_UNKNOWN; -} // End PICC_GetType() - -/** - * Returns a __FlashStringHelper pointer to the PICC type name. - * - * @return const __FlashStringHelper * - */ -const __FlashStringHelper *MFRC522::PICC_GetTypeName(byte piccType ///< One of the PICC_Type enums. - ) { - switch (piccType) { - case PICC_TYPE_ISO_14443_4: return F("PICC compliant with ISO/IEC 14443-4"); break; - case PICC_TYPE_ISO_18092: return F("PICC compliant with ISO/IEC 18092 (NFC)");break; - case PICC_TYPE_MIFARE_MINI: return F("MIFARE Mini, 320 bytes"); break; - case PICC_TYPE_MIFARE_1K: return F("MIFARE 1KB"); break; - case PICC_TYPE_MIFARE_4K: return F("MIFARE 4KB"); break; - case PICC_TYPE_MIFARE_UL: return F("MIFARE Ultralight or Ultralight C"); break; - case PICC_TYPE_MIFARE_PLUS: return F("MIFARE Plus"); break; - case PICC_TYPE_TNP3XXX: return F("MIFARE TNP3XXX"); break; - case PICC_TYPE_NOT_COMPLETE: return F("SAK indicates UID is not complete."); break; - case PICC_TYPE_UNKNOWN: - default: return F("Unknown type"); break; - } -} // End PICC_GetTypeName() - -/** - * Dumps debug info about the selected PICC to Serial. - * On success the PICC is halted after dumping the data. - * For MIFARE Classic the factory default key of 0xFFFFFFFFFFFF is tried. - */ -void MFRC522::PICC_DumpToSerial(Uid *uid ///< Pointer to Uid struct returned from a successful PICC_Select(). - ) { - MIFARE_Key key; - - // UID - Serial.print(F("Card UID:")); - for (byte i = 0; i < uid->size; i++) { - if(uid->uidByte[i] < 0x10) - Serial.print(F(" 0")); - else - Serial.print(F(" ")); - Serial.print(uid->uidByte[i], HEX); - } - Serial.println(); - - // PICC type - byte piccType = PICC_GetType(uid->sak); - Serial.print(F("PICC type: ")); - Serial.println(PICC_GetTypeName(piccType)); - - // Dump contents - switch (piccType) { - case PICC_TYPE_MIFARE_MINI: - case PICC_TYPE_MIFARE_1K: - case PICC_TYPE_MIFARE_4K: - // All keys are set to FFFFFFFFFFFFh at chip delivery from the factory. - for (byte i = 0; i < 6; i++) { - key.keyByte[i] = 0xFF; - } - PICC_DumpMifareClassicToSerial(uid, piccType, &key); - break; - - case PICC_TYPE_MIFARE_UL: - PICC_DumpMifareUltralightToSerial(); - break; - - case PICC_TYPE_ISO_14443_4: - case PICC_TYPE_ISO_18092: - case PICC_TYPE_MIFARE_PLUS: - case PICC_TYPE_TNP3XXX: - Serial.println(F("Dumping memory contents not implemented for that PICC type.")); - break; - - case PICC_TYPE_UNKNOWN: - case PICC_TYPE_NOT_COMPLETE: - default: - break; // No memory dump here - } - - Serial.println(); - PICC_HaltA(); // Already done if it was a MIFARE Classic PICC. -} // End PICC_DumpToSerial() - -/** - * Dumps memory contents of a MIFARE Classic PICC. - * On success the PICC is halted after dumping the data. - */ -void MFRC522::PICC_DumpMifareClassicToSerial( Uid *uid, ///< Pointer to Uid struct returned from a successful PICC_Select(). - byte piccType, ///< One of the PICC_Type enums. - MIFARE_Key *key ///< Key A used for all sectors. - ) { - byte no_of_sectors = 0; - switch (piccType) { - case PICC_TYPE_MIFARE_MINI: - // Has 5 sectors * 4 blocks/sector * 16 bytes/block = 320 bytes. - no_of_sectors = 5; - break; - - case PICC_TYPE_MIFARE_1K: - // Has 16 sectors * 4 blocks/sector * 16 bytes/block = 1024 bytes. - no_of_sectors = 16; - break; - - case PICC_TYPE_MIFARE_4K: - // Has (32 sectors * 4 blocks/sector + 8 sectors * 16 blocks/sector) * 16 bytes/block = 4096 bytes. - no_of_sectors = 40; - break; - - default: // Should not happen. Ignore. - break; - } - - // Dump sectors, highest address first. - if (no_of_sectors) { - Serial.println(F("Sector Block 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 AccessBits")); - for (int8_t i = no_of_sectors - 1; i >= 0; i--) { - PICC_DumpMifareClassicSectorToSerial(uid, key, i); - } - } - PICC_HaltA(); // Halt the PICC before stopping the encrypted session. - PCD_StopCrypto1(); -} // End PICC_DumpMifareClassicToSerial() - -/** - * Dumps memory contents of a sector of a MIFARE Classic PICC. - * Uses PCD_Authenticate(), MIFARE_Read() and PCD_StopCrypto1. - * Always uses PICC_CMD_MF_AUTH_KEY_A because only Key A can always read the sector trailer access bits. - */ -void MFRC522::PICC_DumpMifareClassicSectorToSerial(Uid *uid, ///< Pointer to Uid struct returned from a successful PICC_Select(). - MIFARE_Key *key, ///< Key A for the sector. - byte sector ///< The sector to dump, 0..39. - ) { - byte status; - byte firstBlock; // Address of lowest address to dump actually last block dumped) - byte no_of_blocks; // Number of blocks in sector - bool isSectorTrailer; // Set to true while handling the "last" (ie highest address) in the sector. - - // The access bits are stored in a peculiar fashion. - // There are four groups: - // g[3] Access bits for the sector trailer, block 3 (for sectors 0-31) or block 15 (for sectors 32-39) - // g[2] Access bits for block 2 (for sectors 0-31) or blocks 10-14 (for sectors 32-39) - // g[1] Access bits for block 1 (for sectors 0-31) or blocks 5-9 (for sectors 32-39) - // g[0] Access bits for block 0 (for sectors 0-31) or blocks 0-4 (for sectors 32-39) - // Each group has access bits [C1 C2 C3]. In this code C1 is MSB and C3 is LSB. - // The four CX bits are stored together in a nible cx and an inverted nible cx_. - byte c1, c2, c3; // Nibbles - byte c1_, c2_, c3_; // Inverted nibbles - bool invertedError; // True if one of the inverted nibbles did not match - byte g[4]; // Access bits for each of the four groups. - byte group; // 0-3 - active group for access bits - bool firstInGroup; // True for the first block dumped in the group - - // Determine position and size of sector. - if (sector < 32) { // Sectors 0..31 has 4 blocks each - no_of_blocks = 4; - firstBlock = sector * no_of_blocks; - } - else if (sector < 40) { // Sectors 32-39 has 16 blocks each - no_of_blocks = 16; - firstBlock = 128 + (sector - 32) * no_of_blocks; - } - else { // Illegal input, no MIFARE Classic PICC has more than 40 sectors. - return; - } - - // Dump blocks, highest address first. - byte byteCount; - byte buffer[18]; - byte blockAddr; - isSectorTrailer = true; - for (int8_t blockOffset = no_of_blocks - 1; blockOffset >= 0; blockOffset--) { - blockAddr = firstBlock + blockOffset; - // Sector number - only on first line - if (isSectorTrailer) { - if(sector < 10) - Serial.print(F(" ")); // Pad with spaces - else - Serial.print(F(" ")); // Pad with spaces - Serial.print(sector); - Serial.print(F(" ")); - } - else { - Serial.print(F(" ")); - } - // Block number - if(blockAddr < 10) - Serial.print(F(" ")); // Pad with spaces - else { - if(blockAddr < 100) - Serial.print(F(" ")); // Pad with spaces - else - Serial.print(F(" ")); // Pad with spaces - } - Serial.print(blockAddr); - Serial.print(F(" ")); - // Establish encrypted communications before reading the first block - if (isSectorTrailer) { - status = PCD_Authenticate(PICC_CMD_MF_AUTH_KEY_A, firstBlock, key, uid); - if (status != STATUS_OK) { - Serial.print(F("PCD_Authenticate() failed: ")); - Serial.println(GetStatusCodeName(status)); - return; - } - } - // Read block - byteCount = sizeof(buffer); - status = MIFARE_Read(blockAddr, buffer, &byteCount); - if (status != STATUS_OK) { - Serial.print(F("MIFARE_Read() failed: ")); - Serial.println(GetStatusCodeName(status)); - continue; - } - // Dump data - for (byte index = 0; index < 16; index++) { - if(buffer[index] < 0x10) - Serial.print(F(" 0")); - else - Serial.print(F(" ")); - Serial.print(buffer[index], HEX); - if ((index % 4) == 3) { - Serial.print(F(" ")); - } - } - // Parse sector trailer data - if (isSectorTrailer) { - c1 = buffer[7] >> 4; - c2 = buffer[8] & 0xF; - c3 = buffer[8] >> 4; - c1_ = buffer[6] & 0xF; - c2_ = buffer[6] >> 4; - c3_ = buffer[7] & 0xF; - invertedError = (c1 != (~c1_ & 0xF)) || (c2 != (~c2_ & 0xF)) || (c3 != (~c3_ & 0xF)); - g[0] = ((c1 & 1) << 2) | ((c2 & 1) << 1) | ((c3 & 1) << 0); - g[1] = ((c1 & 2) << 1) | ((c2 & 2) << 0) | ((c3 & 2) >> 1); - g[2] = ((c1 & 4) << 0) | ((c2 & 4) >> 1) | ((c3 & 4) >> 2); - g[3] = ((c1 & 8) >> 1) | ((c2 & 8) >> 2) | ((c3 & 8) >> 3); - isSectorTrailer = false; - } - - // Which access group is this block in? - if (no_of_blocks == 4) { - group = blockOffset; - firstInGroup = true; - } - else { - group = blockOffset / 5; - firstInGroup = (group == 3) || (group != (blockOffset + 1) / 5); - } - - if (firstInGroup) { - // Print access bits - Serial.print(F(" [ ")); - Serial.print((g[group] >> 2) & 1, DEC); Serial.print(F(" ")); - Serial.print((g[group] >> 1) & 1, DEC); Serial.print(F(" ")); - Serial.print((g[group] >> 0) & 1, DEC); - Serial.print(F(" ] ")); - if (invertedError) { - Serial.print(F(" Inverted access bits did not match! ")); - } - } - - if (group != 3 && (g[group] == 1 || g[group] == 6)) { // Not a sector trailer, a value block - long value = (long(buffer[3])<<24) | (long(buffer[2])<<16) | (long(buffer[1])<<8) | long(buffer[0]); - Serial.print(F(" Value=0x")); Serial.print(value, HEX); - Serial.print(F(" Adr=0x")); Serial.print(buffer[12], HEX); - } - Serial.println(); - } - - return; -} // End PICC_DumpMifareClassicSectorToSerial() - -/** - * Dumps memory contents of a MIFARE Ultralight PICC. - */ -void MFRC522::PICC_DumpMifareUltralightToSerial() { - byte status; - byte byteCount; - byte buffer[18]; - byte i; - - Serial.println(F("Page 0 1 2 3")); - // Try the mpages of the original Ultralight. Ultralight C has more pages. - for (byte page = 0; page < 16; page +=4) { // Read returns data for 4 pages at a time. - // Read pages - byteCount = sizeof(buffer); - status = MIFARE_Read(page, buffer, &byteCount); - if (status != STATUS_OK) { - Serial.print(F("MIFARE_Read() failed: ")); - Serial.println(GetStatusCodeName(status)); - break; - } - // Dump data - for (byte offset = 0; offset < 4; offset++) { - i = page + offset; - if(i < 10) - Serial.print(F(" ")); // Pad with spaces - else - Serial.print(F(" ")); // Pad with spaces - Serial.print(i); - Serial.print(F(" ")); - for (byte index = 0; index < 4; index++) { - i = 4 * offset + index; - if(buffer[i] < 0x10) - Serial.print(F(" 0")); - else - Serial.print(F(" ")); - Serial.print(buffer[i], HEX); - } - Serial.println(); - } - } -} // End PICC_DumpMifareUltralightToSerial() - -/** - * Calculates the bit pattern needed for the specified access bits. In the [C1 C2 C3] tupples C1 is MSB (=4) and C3 is LSB (=1). - */ -void MFRC522::MIFARE_SetAccessBits( byte *accessBitBuffer, ///< Pointer to byte 6, 7 and 8 in the sector trailer. Bytes [0..2] will be set. - byte g0, ///< Access bits [C1 C2 C3] for block 0 (for sectors 0-31) or blocks 0-4 (for sectors 32-39) - byte g1, ///< Access bits C1 C2 C3] for block 1 (for sectors 0-31) or blocks 5-9 (for sectors 32-39) - byte g2, ///< Access bits C1 C2 C3] for block 2 (for sectors 0-31) or blocks 10-14 (for sectors 32-39) - byte g3 ///< Access bits C1 C2 C3] for the sector trailer, block 3 (for sectors 0-31) or block 15 (for sectors 32-39) - ) { - byte c1 = ((g3 & 4) << 1) | ((g2 & 4) << 0) | ((g1 & 4) >> 1) | ((g0 & 4) >> 2); - byte c2 = ((g3 & 2) << 2) | ((g2 & 2) << 1) | ((g1 & 2) << 0) | ((g0 & 2) >> 1); - byte c3 = ((g3 & 1) << 3) | ((g2 & 1) << 2) | ((g1 & 1) << 1) | ((g0 & 1) << 0); - - accessBitBuffer[0] = (~c2 & 0xF) << 4 | (~c1 & 0xF); - accessBitBuffer[1] = c1 << 4 | (~c3 & 0xF); - accessBitBuffer[2] = c3 << 4 | c2; -} // End MIFARE_SetAccessBits() - - -/** - * Performs the "magic sequence" needed to get Chinese UID changeable - * Mifare cards to allow writing to sector 0, where the card UID is stored. - * - * Note that you do not need to have selected the card through REQA or WUPA, - * this sequence works immediately when the card is in the reader vicinity. - * This means you can use this method even on "bricked" cards that your reader does - * not recognise anymore (see MFRC522::MIFARE_UnbrickUidSector). - * - * Of course with non-bricked devices, you're free to select them before calling this function. - */ -bool MFRC522::MIFARE_OpenUidBackdoor(bool logErrors) { - // Magic sequence: - // > 50 00 57 CD (HALT + CRC) - // > 40 (7 bits only) - // < A (4 bits only) - // > 43 - // < A (4 bits only) - // Then you can write to sector 0 without authenticating - - PICC_HaltA(); // 50 00 57 CD - - byte cmd = 0x40; - byte validBits = 7; /* Our command is only 7 bits. After receiving card response, - this will contain amount of valid response bits. */ - byte response[32]; // Card's response is written here - byte received; - byte status = PCD_TransceiveData(&cmd, (byte)1, response, &received, &validBits, (byte)0, false); // 40 - if(status != STATUS_OK) { - if(logErrors) { - Serial.println(F("Card did not respond to 0x40 after HALT command. Are you sure it is a UID changeable one?")); - Serial.print(F("Error name: ")); - Serial.println(GetStatusCodeName(status)); - } - return false; - } - if (received != 1 || response[0] != 0x0A) { - if (logErrors) { - Serial.print(F("Got bad response on backdoor 0x40 command: ")); - Serial.print(response[0], HEX); - Serial.print(F(" (")); - Serial.print(validBits); - Serial.print(F(" valid bits)\r\n")); - } - return false; - } - - cmd = 0x43; - validBits = 8; - status = PCD_TransceiveData(&cmd, (byte)1, response, &received, &validBits, (byte)0, false); // 43 - if(status != STATUS_OK) { - if(logErrors) { - Serial.println(F("Error in communication at command 0x43, after successfully executing 0x40")); - Serial.print(F("Error name: ")); - Serial.println(GetStatusCodeName(status)); - } - return false; - } - if (received != 1 || response[0] != 0x0A) { - if (logErrors) { - Serial.print(F("Got bad response on backdoor 0x43 command: ")); - Serial.print(response[0], HEX); - Serial.print(F(" (")); - Serial.print(validBits); - Serial.print(F(" valid bits)\r\n")); - } - return false; - } - - // You can now write to sector 0 without authenticating! - return true; -} // End MIFARE_OpenUidBackdoor() - -/** - * Reads entire block 0, including all manufacturer data, and overwrites - * that block with the new UID, a freshly calculated BCC, and the original - * manufacturer data. - * - * It assumes a default KEY A of 0xFFFFFFFFFFFF. - * Make sure to have selected the card before this function is called. - */ -bool MFRC522::MIFARE_SetUid(byte *newUid, byte uidSize, bool logErrors) { - - // UID + BCC byte can not be larger than 16 together - if (!newUid || !uidSize || uidSize > 15) { - if (logErrors) { - Serial.println(F("New UID buffer empty, size 0, or size > 15 given")); - } - return false; - } - - // Authenticate for reading - MIFARE_Key key = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}; - byte status = PCD_Authenticate(MFRC522::PICC_CMD_MF_AUTH_KEY_A, (byte)1, &key, &uid); - if (status != STATUS_OK) { - - if (status == STATUS_TIMEOUT) { - // We get a read timeout if no card is selected yet, so let's select one - - // Wake the card up again if sleeping -// byte atqa_answer[2]; -// byte atqa_size = 2; -// PICC_WakeupA(atqa_answer, &atqa_size); - - if (!PICC_IsNewCardPresent() || !PICC_ReadCardSerial()) { - Serial.println(F("No card was previously selected, and none are available. Failed to set UID.")); - return false; - } - - status = PCD_Authenticate(MFRC522::PICC_CMD_MF_AUTH_KEY_A, (byte)1, &key, &uid); - if (status != STATUS_OK) { - // We tried, time to give up - if (logErrors) { - Serial.println(F("Failed to authenticate to card for reading, could not set UID: ")); - Serial.println(GetStatusCodeName(status)); - } - return false; - } - } - else { - if (logErrors) { - Serial.print(F("PCD_Authenticate() failed: ")); - Serial.println(GetStatusCodeName(status)); - } - return false; - } - } - - // Read block 0 - byte block0_buffer[18]; - byte byteCount = sizeof(block0_buffer); - status = MIFARE_Read((byte)0, block0_buffer, &byteCount); - if (status != STATUS_OK) { - if (logErrors) { - Serial.print(F("MIFARE_Read() failed: ")); - Serial.println(GetStatusCodeName(status)); - Serial.println(F("Are you sure your KEY A for sector 0 is 0xFFFFFFFFFFFF?")); - } - return false; - } - - // Write new UID to the data we just read, and calculate BCC byte - byte bcc = 0; - for (int i = 0; i < uidSize; i++) { - block0_buffer[i] = newUid[i]; - bcc ^= newUid[i]; - } - - // Write BCC byte to buffer - block0_buffer[uidSize] = bcc; - - // Stop encrypted traffic so we can send raw bytes - PCD_StopCrypto1(); - - // Activate UID backdoor - if (!MIFARE_OpenUidBackdoor(logErrors)) { - if (logErrors) { - Serial.println(F("Activating the UID backdoor failed.")); - } - return false; - } - - // Write modified block 0 back to card - status = MIFARE_Write((byte)0, block0_buffer, (byte)16); - if (status != STATUS_OK) { - if (logErrors) { - Serial.print(F("MIFARE_Write() failed: ")); - Serial.println(GetStatusCodeName(status)); - } - return false; - } - - // Wake the card up again - byte atqa_answer[2]; - byte atqa_size = 2; - PICC_WakeupA(atqa_answer, &atqa_size); - - return true; -} - -/** - * Resets entire sector 0 to zeroes, so the card can be read again by readers. - */ -bool MFRC522::MIFARE_UnbrickUidSector(bool logErrors) { - MIFARE_OpenUidBackdoor(logErrors); - - byte block0_buffer[] = {0x01, 0x02, 0x03, 0x04, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; - - // Write modified block 0 back to card - byte status = MIFARE_Write((byte)0, block0_buffer, (byte)16); - if (status != STATUS_OK) { - if (logErrors) { - Serial.print(F("MIFARE_Write() failed: ")); - Serial.println(GetStatusCodeName(status)); - } - return false; - } - return true; -} - -///////////////////////////////////////////////////////////////////////////////////// -// Convenience functions - does not add extra functionality -///////////////////////////////////////////////////////////////////////////////////// - -/** - * Returns true if a PICC responds to PICC_CMD_REQA. - * Only "new" cards in state IDLE are invited. Sleeping cards in state HALT are ignored. - * - * @return bool - */ -bool MFRC522::PICC_IsNewCardPresent() { - byte bufferATQA[2]; - byte bufferSize = sizeof(bufferATQA); - byte result = PICC_RequestA(bufferATQA, &bufferSize); - return (result == STATUS_OK || result == STATUS_COLLISION); -} // End PICC_IsNewCardPresent() - -/** - * Simple wrapper around PICC_Select. - * Returns true if a UID could be read. - * Remember to call PICC_IsNewCardPresent(), PICC_RequestA() or PICC_WakeupA() first. - * The read UID is available in the class variable uid. - * - * @return bool - */ -bool MFRC522::PICC_ReadCardSerial() { - byte result = PICC_Select(&uid); - return (result == STATUS_OK); -} // End PICC_ReadCardSerial() diff --git a/lib/lib_i2c/MFRC522_I2C/src/MFRC522_I2C.h b/lib/lib_i2c/MFRC522_I2C/src/MFRC522_I2C.h deleted file mode 100644 index a58a9df1a..000000000 --- a/lib/lib_i2c/MFRC522_I2C/src/MFRC522_I2C.h +++ /dev/null @@ -1,407 +0,0 @@ -/** - * MFRC522_I2C.h - Library to use ARDUINO RFID MODULE KIT 13.56 MHZ WITH TAGS I2C BY AROZCAN - * MFRC522_I2C.h - Based on ARDUINO RFID MODULE KIT 13.56 MHZ WITH TAGS SPI Library BY COOQROBOT. - * Based on code Dr.Leong ( WWW.B2CQSHOP.COM ) - * Created by Miguel Balboa (circuitito.com), Jan, 2012. - * Rewritten by Søren Thing Andersen (access.thing.dk), fall of 2013 (Translation to English, refactored, comments, anti collision, cascade levels.) - * Extended by Tom Clement with functionality to write to sector 0 of UID changeable Mifare cards. - * Extended by Ahmet Remzi Ozcan with I2C functionality. - * Author: arozcan @ https://github.com/arozcan/MFRC522-I2C-Library - * Released into the public domain. - * - * Please read this file for an overview and then MFRC522.cpp for comments on the specific functions. - * Search for "mf-rc522" on ebay.com to purchase the MF-RC522 board. - * - * There are three hardware components involved: - * 1) The micro controller: An Arduino - * 2) The PCD (short for Proximity Coupling Device): NXP MFRC522 Contactless Reader IC - * 3) The PICC (short for Proximity Integrated Circuit Card): A card or tag using the ISO 14443A interface, eg Mifare or NTAG203. - * - * The microcontroller and card reader uses I2C for communication. - * The protocol is described in the MFRC522 datasheet: http://www.nxp.com/documents/data_sheet/MFRC522.pdf - * - * The card reader and the tags communicate using a 13.56MHz electromagnetic field. - * The protocol is defined in ISO/IEC 14443-3 Identification cards -- Contactless integrated circuit cards -- Proximity cards -- Part 3: Initialization and anticollision". - * A free version of the final draft can be found at http://wg8.de/wg8n1496_17n3613_Ballot_FCD14443-3.pdf - * Details are found in chapter 6, Type A – Initialization and anticollision. - * - * If only the PICC UID is wanted, the above documents has all the needed information. - * To read and write from MIFARE PICCs, the MIFARE protocol is used after the PICC has been selected. - * The MIFARE Classic chips and protocol is described in the datasheets: - * 1K: http://www.nxp.com/documents/data_sheet/MF1S503x.pdf - * 4K: http://www.nxp.com/documents/data_sheet/MF1S703x.pdf - * Mini: http://www.idcardmarket.com/download/mifare_S20_datasheet.pdf - * The MIFARE Ultralight chip and protocol is described in the datasheets: - * Ultralight: http://www.nxp.com/documents/data_sheet/MF0ICU1.pdf - * Ultralight C: http://www.nxp.com/documents/short_data_sheet/MF0ICU2_SDS.pdf - * - * MIFARE Classic 1K (MF1S503x): - * Has 16 sectors * 4 blocks/sector * 16 bytes/block = 1024 bytes. - * The blocks are numbered 0-63. - * Block 3 in each sector is the Sector Trailer. See http://www.nxp.com/documents/data_sheet/MF1S503x.pdf sections 8.6 and 8.7: - * Bytes 0-5: Key A - * Bytes 6-8: Access Bits - * Bytes 9: User data - * Bytes 10-15: Key B (or user data) - * Block 0 is read-only manufacturer data. - * To access a block, an authentication using a key from the block's sector must be performed first. - * Example: To read from block 10, first authenticate using a key from sector 3 (blocks 8-11). - * All keys are set to FFFFFFFFFFFFh at chip delivery. - * Warning: Please read section 8.7 "Memory Access". It includes this text: if the PICC detects a format violation the whole sector is irreversibly blocked. - * To use a block in "value block" mode (for Increment/Decrement operations) you need to change the sector trailer. Use PICC_SetAccessBits() to calculate the bit patterns. - * MIFARE Classic 4K (MF1S703x): - * Has (32 sectors * 4 blocks/sector + 8 sectors * 16 blocks/sector) * 16 bytes/block = 4096 bytes. - * The blocks are numbered 0-255. - * The last block in each sector is the Sector Trailer like above. - * MIFARE Classic Mini (MF1 IC S20): - * Has 5 sectors * 4 blocks/sector * 16 bytes/block = 320 bytes. - * The blocks are numbered 0-19. - * The last block in each sector is the Sector Trailer like above. - * - * MIFARE Ultralight (MF0ICU1): - * Has 16 pages of 4 bytes = 64 bytes. - * Pages 0 + 1 is used for the 7-byte UID. - * Page 2 contains the last check digit for the UID, one byte manufacturer internal data, and the lock bytes (see http://www.nxp.com/documents/data_sheet/MF0ICU1.pdf section 8.5.2) - * Page 3 is OTP, One Time Programmable bits. Once set to 1 they cannot revert to 0. - * Pages 4-15 are read/write unless blocked by the lock bytes in page 2. - * MIFARE Ultralight C (MF0ICU2): - * Has 48 pages of 4 bytes = 192 bytes. - * Pages 0 + 1 is used for the 7-byte UID. - * Page 2 contains the last check digit for the UID, one byte manufacturer internal data, and the lock bytes (see http://www.nxp.com/documents/data_sheet/MF0ICU1.pdf section 8.5.2) - * Page 3 is OTP, One Time Programmable bits. Once set to 1 they cannot revert to 0. - * Pages 4-39 are read/write unless blocked by the lock bytes in page 2. - * Page 40 Lock bytes - * Page 41 16 bit one way counter - * Pages 42-43 Authentication configuration - * Pages 44-47 Authentication key - */ -#ifndef MFRC522_h -#define MFRC522_h - -#include -#include - -// Firmware data for self-test -// Reference values based on firmware version -// Hint: if needed, you can remove unused self-test data to save flash memory -// -// Version 0.0 (0x90) -// Philips Semiconductors; Preliminary Specification Revision 2.0 - 01 August 2005; 16.1 Sefttest -const byte MFRC522_firmware_referenceV0_0[] PROGMEM = { - 0x00, 0x87, 0x98, 0x0f, 0x49, 0xFF, 0x07, 0x19, - 0xBF, 0x22, 0x30, 0x49, 0x59, 0x63, 0xAD, 0xCA, - 0x7F, 0xE3, 0x4E, 0x03, 0x5C, 0x4E, 0x49, 0x50, - 0x47, 0x9A, 0x37, 0x61, 0xE7, 0xE2, 0xC6, 0x2E, - 0x75, 0x5A, 0xED, 0x04, 0x3D, 0x02, 0x4B, 0x78, - 0x32, 0xFF, 0x58, 0x3B, 0x7C, 0xE9, 0x00, 0x94, - 0xB4, 0x4A, 0x59, 0x5B, 0xFD, 0xC9, 0x29, 0xDF, - 0x35, 0x96, 0x98, 0x9E, 0x4F, 0x30, 0x32, 0x8D -}; -// Version 1.0 (0x91) -// NXP Semiconductors; Rev. 3.8 - 17 September 2014; 16.1.1 Self test -const byte MFRC522_firmware_referenceV1_0[] PROGMEM = { - 0x00, 0xC6, 0x37, 0xD5, 0x32, 0xB7, 0x57, 0x5C, - 0xC2, 0xD8, 0x7C, 0x4D, 0xD9, 0x70, 0xC7, 0x73, - 0x10, 0xE6, 0xD2, 0xAA, 0x5E, 0xA1, 0x3E, 0x5A, - 0x14, 0xAF, 0x30, 0x61, 0xC9, 0x70, 0xDB, 0x2E, - 0x64, 0x22, 0x72, 0xB5, 0xBD, 0x65, 0xF4, 0xEC, - 0x22, 0xBC, 0xD3, 0x72, 0x35, 0xCD, 0xAA, 0x41, - 0x1F, 0xA7, 0xF3, 0x53, 0x14, 0xDE, 0x7E, 0x02, - 0xD9, 0x0F, 0xB5, 0x5E, 0x25, 0x1D, 0x29, 0x79 -}; -// Version 2.0 (0x92) -// NXP Semiconductors; Rev. 3.8 - 17 September 2014; 16.1.1 Self test -const byte MFRC522_firmware_referenceV2_0[] PROGMEM = { - 0x00, 0xEB, 0x66, 0xBA, 0x57, 0xBF, 0x23, 0x95, - 0xD0, 0xE3, 0x0D, 0x3D, 0x27, 0x89, 0x5C, 0xDE, - 0x9D, 0x3B, 0xA7, 0x00, 0x21, 0x5B, 0x89, 0x82, - 0x51, 0x3A, 0xEB, 0x02, 0x0C, 0xA5, 0x00, 0x49, - 0x7C, 0x84, 0x4D, 0xB3, 0xCC, 0xD2, 0x1B, 0x81, - 0x5D, 0x48, 0x76, 0xD5, 0x71, 0x61, 0x21, 0xA9, - 0x86, 0x96, 0x83, 0x38, 0xCF, 0x9D, 0x5B, 0x6D, - 0xDC, 0x15, 0xBA, 0x3E, 0x7D, 0x95, 0x3B, 0x2F -}; -// Clone -// Fudan Semiconductor FM17522 (0x88) -const byte FM17522_firmware_reference[] PROGMEM = { - 0x00, 0xD6, 0x78, 0x8C, 0xE2, 0xAA, 0x0C, 0x18, - 0x2A, 0xB8, 0x7A, 0x7F, 0xD3, 0x6A, 0xCF, 0x0B, - 0xB1, 0x37, 0x63, 0x4B, 0x69, 0xAE, 0x91, 0xC7, - 0xC3, 0x97, 0xAE, 0x77, 0xF4, 0x37, 0xD7, 0x9B, - 0x7C, 0xF5, 0x3C, 0x11, 0x8F, 0x15, 0xC3, 0xD7, - 0xC1, 0x5B, 0x00, 0x2A, 0xD0, 0x75, 0xDE, 0x9E, - 0x51, 0x64, 0xAB, 0x3E, 0xE9, 0x15, 0xB5, 0xAB, - 0x56, 0x9A, 0x98, 0x82, 0x26, 0xEA, 0x2A, 0x62 -}; - -class MFRC522 { -public: - // MFRC522 registers. Described in chapter 9 of the datasheet. - enum PCD_Register { - // Page 0: Command and status - // 0x00 // reserved for future use - CommandReg = 0x01 , // starts and stops command execution - ComIEnReg = 0x02 , // enable and disable interrupt request control bits - DivIEnReg = 0x03 , // enable and disable interrupt request control bits - ComIrqReg = 0x04 , // interrupt request bits - DivIrqReg = 0x05 , // interrupt request bits - ErrorReg = 0x06 , // error bits showing the error status of the last command executed - Status1Reg = 0x07 , // communication status bits - Status2Reg = 0x08 , // receiver and transmitter status bits - FIFODataReg = 0x09 , // input and output of 64 byte FIFO buffer - FIFOLevelReg = 0x0A , // number of bytes stored in the FIFO buffer - WaterLevelReg = 0x0B , // level for FIFO underflow and overflow warning - ControlReg = 0x0C , // miscellaneous control registers - BitFramingReg = 0x0D , // adjustments for bit-oriented frames - CollReg = 0x0E , // bit position of the first bit-collision detected on the RF interface - // 0x0F // reserved for future use - - // Page 1: Command - // 0x10 // reserved for future use - ModeReg = 0x11 , // defines general modes for transmitting and receiving - TxModeReg = 0x12 , // defines transmission data rate and framing - RxModeReg = 0x13 , // defines reception data rate and framing - TxControlReg = 0x14 , // controls the logical behavior of the antenna driver pins TX1 and TX2 - TxASKReg = 0x15 , // controls the setting of the transmission modulation - TxSelReg = 0x16 , // selects the internal sources for the antenna driver - RxSelReg = 0x17 , // selects internal receiver settings - RxThresholdReg = 0x18 , // selects thresholds for the bit decoder - DemodReg = 0x19 , // defines demodulator settings - // 0x1A // reserved for future use - // 0x1B // reserved for future use - MfTxReg = 0x1C , // controls some MIFARE communication transmit parameters - MfRxReg = 0x1D , // controls some MIFARE communication receive parameters - // 0x1E // reserved for future use - SerialSpeedReg = 0x1F , // selects the speed of the serial UART interface - - // Page 2: Configuration - // 0x20 // reserved for future use - CRCResultRegH = 0x21 , // shows the MSB and LSB values of the CRC calculation - CRCResultRegL = 0x22 , - // 0x23 // reserved for future use - ModWidthReg = 0x24 , // controls the ModWidth setting? - // 0x25 // reserved for future use - RFCfgReg = 0x26 , // configures the receiver gain - GsNReg = 0x27 , // selects the conductance of the antenna driver pins TX1 and TX2 for modulation - CWGsPReg = 0x28 , // defines the conductance of the p-driver output during periods of no modulation - ModGsPReg = 0x29 , // defines the conductance of the p-driver output during periods of modulation - TModeReg = 0x2A , // defines settings for the internal timer - TPrescalerReg = 0x2B , // the lower 8 bits of the TPrescaler value. The 4 high bits are in TModeReg. - TReloadRegH = 0x2C , // defines the 16-bit timer reload value - TReloadRegL = 0x2D , - TCounterValueRegH = 0x2E , // shows the 16-bit timer value - TCounterValueRegL = 0x2F , - - // Page 3: Test Registers - // 0x30 // reserved for future use - TestSel1Reg = 0x31 , // general test signal configuration - TestSel2Reg = 0x32 , // general test signal configuration - TestPinEnReg = 0x33 , // enables pin output driver on pins D1 to D7 - TestPinValueReg = 0x34 , // defines the values for D1 to D7 when it is used as an I/O bus - TestBusReg = 0x35 , // shows the status of the internal test bus - AutoTestReg = 0x36 , // controls the digital self test - VersionReg = 0x37 , // shows the software version - AnalogTestReg = 0x38 , // controls the pins AUX1 and AUX2 - TestDAC1Reg = 0x39 , // defines the test value for TestDAC1 - TestDAC2Reg = 0x3A , // defines the test value for TestDAC2 - TestADCReg = 0x3B // shows the value of ADC I and Q channels - // 0x3C // reserved for production tests - // 0x3D // reserved for production tests - // 0x3E // reserved for production tests - // 0x3F // reserved for production tests - }; - - // MFRC522 commands. Described in chapter 10 of the datasheet. - enum PCD_Command { - PCD_Idle = 0x00, // no action, cancels current command execution - PCD_Mem = 0x01, // stores 25 bytes into the internal buffer - PCD_GenerateRandomID = 0x02, // generates a 10-byte random ID number - PCD_CalcCRC = 0x03, // activates the CRC coprocessor or performs a self test - PCD_Transmit = 0x04, // transmits data from the FIFO buffer - PCD_NoCmdChange = 0x07, // no command change, can be used to modify the CommandReg register bits without affecting the command, for example, the PowerDown bit - PCD_Receive = 0x08, // activates the receiver circuits - PCD_Transceive = 0x0C, // transmits data from FIFO buffer to antenna and automatically activates the receiver after transmission - PCD_MFAuthent = 0x0E, // performs the MIFARE standard authentication as a reader - PCD_SoftReset = 0x0F // resets the MFRC522 - }; - - // MFRC522 RxGain[2:0] masks, defines the receiver's signal voltage gain factor (on the PCD). - // Described in 9.3.3.6 / table 98 of the datasheet at http://www.nxp.com/documents/data_sheet/MFRC522.pdf - enum PCD_RxGain { - RxGain_18dB = 0x00 << 4, // 000b - 18 dB, minimum - RxGain_23dB = 0x01 << 4, // 001b - 23 dB - RxGain_18dB_2 = 0x02 << 4, // 010b - 18 dB, it seems 010b is a duplicate for 000b - RxGain_23dB_2 = 0x03 << 4, // 011b - 23 dB, it seems 011b is a duplicate for 001b - RxGain_33dB = 0x04 << 4, // 100b - 33 dB, average, and typical default - RxGain_38dB = 0x05 << 4, // 101b - 38 dB - RxGain_43dB = 0x06 << 4, // 110b - 43 dB - RxGain_48dB = 0x07 << 4, // 111b - 48 dB, maximum - RxGain_min = 0x00 << 4, // 000b - 18 dB, minimum, convenience for RxGain_18dB - RxGain_avg = 0x04 << 4, // 100b - 33 dB, average, convenience for RxGain_33dB - RxGain_max = 0x07 << 4 // 111b - 48 dB, maximum, convenience for RxGain_48dB - }; - - // Commands sent to the PICC. - enum PICC_Command { - // The commands used by the PCD to manage communication with several PICCs (ISO 14443-3, Type A, section 6.4) - PICC_CMD_REQA = 0x26, // REQuest command, Type A. Invites PICCs in state IDLE to go to READY and prepare for anticollision or selection. 7 bit frame. - PICC_CMD_WUPA = 0x52, // Wake-UP command, Type A. Invites PICCs in state IDLE and HALT to go to READY(*) and prepare for anticollision or selection. 7 bit frame. - PICC_CMD_CT = 0x88, // Cascade Tag. Not really a command, but used during anti collision. - PICC_CMD_SEL_CL1 = 0x93, // Anti collision/Select, Cascade Level 1 - PICC_CMD_SEL_CL2 = 0x95, // Anti collision/Select, Cascade Level 2 - PICC_CMD_SEL_CL3 = 0x97, // Anti collision/Select, Cascade Level 3 - PICC_CMD_HLTA = 0x50, // HaLT command, Type A. Instructs an ACTIVE PICC to go to state HALT. - // The commands used for MIFARE Classic (from http://www.nxp.com/documents/data_sheet/MF1S503x.pdf, Section 9) - // Use PCD_MFAuthent to authenticate access to a sector, then use these commands to read/write/modify the blocks on the sector. - // The read/write commands can also be used for MIFARE Ultralight. - PICC_CMD_MF_AUTH_KEY_A = 0x60, // Perform authentication with Key A - PICC_CMD_MF_AUTH_KEY_B = 0x61, // Perform authentication with Key B - PICC_CMD_MF_READ = 0x30, // Reads one 16 byte block from the authenticated sector of the PICC. Also used for MIFARE Ultralight. - PICC_CMD_MF_WRITE = 0xA0, // Writes one 16 byte block to the authenticated sector of the PICC. Called "COMPATIBILITY WRITE" for MIFARE Ultralight. - PICC_CMD_MF_DECREMENT = 0xC0, // Decrements the contents of a block and stores the result in the internal data register. - PICC_CMD_MF_INCREMENT = 0xC1, // Increments the contents of a block and stores the result in the internal data register. - PICC_CMD_MF_RESTORE = 0xC2, // Reads the contents of a block into the internal data register. - PICC_CMD_MF_TRANSFER = 0xB0, // Writes the contents of the internal data register to a block. - // The commands used for MIFARE Ultralight (from http://www.nxp.com/documents/data_sheet/MF0ICU1.pdf, Section 8.6) - // The PICC_CMD_MF_READ and PICC_CMD_MF_WRITE can also be used for MIFARE Ultralight. - PICC_CMD_UL_WRITE = 0xA2 // Writes one 4 byte page to the PICC. - }; - - // MIFARE constants that does not fit anywhere else - enum MIFARE_Misc { - MF_ACK = 0xA, // The MIFARE Classic uses a 4 bit ACK/NAK. Any other value than 0xA is NAK. - MF_KEY_SIZE = 6 // A Mifare Crypto1 key is 6 bytes. - }; - - // PICC types we can detect. Remember to update PICC_GetTypeName() if you add more. - enum PICC_Type { - PICC_TYPE_UNKNOWN = 0, - PICC_TYPE_ISO_14443_4 = 1, // PICC compliant with ISO/IEC 14443-4 - PICC_TYPE_ISO_18092 = 2, // PICC compliant with ISO/IEC 18092 (NFC) - PICC_TYPE_MIFARE_MINI = 3, // MIFARE Classic protocol, 320 bytes - PICC_TYPE_MIFARE_1K = 4, // MIFARE Classic protocol, 1KB - PICC_TYPE_MIFARE_4K = 5, // MIFARE Classic protocol, 4KB - PICC_TYPE_MIFARE_UL = 6, // MIFARE Ultralight or Ultralight C - PICC_TYPE_MIFARE_PLUS = 7, // MIFARE Plus - PICC_TYPE_TNP3XXX = 8, // Only mentioned in NXP AN 10833 MIFARE Type Identification Procedure - PICC_TYPE_NOT_COMPLETE = 255 // SAK indicates UID is not complete. - }; - - // Return codes from the functions in this class. Remember to update GetStatusCodeName() if you add more. - enum StatusCode { - STATUS_OK = 1, // Success - STATUS_ERROR = 2, // Error in communication - STATUS_COLLISION = 3, // Collission detected - STATUS_TIMEOUT = 4, // Timeout in communication. - STATUS_NO_ROOM = 5, // A buffer is not big enough. - STATUS_INTERNAL_ERROR = 6, // Internal error in the code. Should not happen ;-) - STATUS_INVALID = 7, // Invalid argument. - STATUS_CRC_WRONG = 8, // The CRC_A does not match - STATUS_MIFARE_NACK = 9 // A MIFARE PICC responded with NAK. - }; - - // A struct used for passing the UID of a PICC. - typedef struct { - byte size; // Number of bytes in the UID. 4, 7 or 10. - byte uidByte[10]; - byte sak; // The SAK (Select acknowledge) byte returned from the PICC after successful selection. - } Uid; - - // A struct used for passing a MIFARE Crypto1 key - typedef struct { - byte keyByte[MF_KEY_SIZE]; - } MIFARE_Key; - - // Member variables - Uid uid; // Used by PICC_ReadCardSerial(). - - // Size of the MFRC522 FIFO - static const byte FIFO_SIZE = 64; // The FIFO is 64 bytes. - - ///////////////////////////////////////////////////////////////////////////////////// - // Functions for setting up the Arduino - ///////////////////////////////////////////////////////////////////////////////////// - MFRC522(byte chipAddress); - - ///////////////////////////////////////////////////////////////////////////////////// - // Basic interface functions for communicating with the MFRC522 - ///////////////////////////////////////////////////////////////////////////////////// - void PCD_WriteRegister(byte reg, byte value); - void PCD_WriteRegister(byte reg, byte count, byte *values); - byte PCD_ReadRegister(byte reg); - void PCD_ReadRegister(byte reg, byte count, byte *values, byte rxAlign = 0); - void setBitMask(unsigned char reg, unsigned char mask); - void PCD_SetRegisterBitMask(byte reg, byte mask); - void PCD_ClearRegisterBitMask(byte reg, byte mask); - byte PCD_CalculateCRC(byte *data, byte length, byte *result); - - ///////////////////////////////////////////////////////////////////////////////////// - // Functions for manipulating the MFRC522 - ///////////////////////////////////////////////////////////////////////////////////// - void PCD_Init(); - void PCD_Reset(); - void PCD_AntennaOn(); - void PCD_AntennaOff(); - byte PCD_GetAntennaGain(); - void PCD_SetAntennaGain(byte mask); - bool PCD_PerformSelfTest(); - - ///////////////////////////////////////////////////////////////////////////////////// - // Functions for communicating with PICCs - ///////////////////////////////////////////////////////////////////////////////////// - byte PCD_TransceiveData(byte *sendData, byte sendLen, byte *backData, byte *backLen, byte *validBits = NULL, byte rxAlign = 0, bool checkCRC = false); - byte PCD_CommunicateWithPICC(byte command, byte waitIRq, byte *sendData, byte sendLen, byte *backData = NULL, byte *backLen = NULL, byte *validBits = NULL, byte rxAlign = 0, bool checkCRC = false); - byte PICC_RequestA(byte *bufferATQA, byte *bufferSize); - byte PICC_WakeupA(byte *bufferATQA, byte *bufferSize); - byte PICC_REQA_or_WUPA(byte command, byte *bufferATQA, byte *bufferSize); - byte PICC_Select(Uid *uid, byte validBits = 0); - byte PICC_HaltA(); - - ///////////////////////////////////////////////////////////////////////////////////// - // Functions for communicating with MIFARE PICCs - ///////////////////////////////////////////////////////////////////////////////////// - byte PCD_Authenticate(byte command, byte blockAddr, MIFARE_Key *key, Uid *uid); - void PCD_StopCrypto1(); - byte MIFARE_Read(byte blockAddr, byte *buffer, byte *bufferSize); - byte MIFARE_Write(byte blockAddr, byte *buffer, byte bufferSize); - byte MIFARE_Decrement(byte blockAddr, long delta); - byte MIFARE_Increment(byte blockAddr, long delta); - byte MIFARE_Restore(byte blockAddr); - byte MIFARE_Transfer(byte blockAddr); - byte MIFARE_Ultralight_Write(byte page, byte *buffer, byte bufferSize); - byte MIFARE_GetValue(byte blockAddr, long *value); - byte MIFARE_SetValue(byte blockAddr, long value); - - ///////////////////////////////////////////////////////////////////////////////////// - // Support functions - ///////////////////////////////////////////////////////////////////////////////////// - byte PCD_MIFARE_Transceive(byte *sendData, byte sendLen, bool acceptTimeout = false); - // old function used too much memory, now name moved to flash; if you need char, copy from flash to memory - //const char *GetStatusCodeName(byte code); - const __FlashStringHelper *GetStatusCodeName(byte code); - byte PICC_GetType(byte sak); - // old function used too much memory, now name moved to flash; if you need char, copy from flash to memory - //const char *PICC_GetTypeName(byte type); - const __FlashStringHelper *PICC_GetTypeName(byte type); - void PICC_DumpToSerial(Uid *uid); - void PICC_DumpMifareClassicToSerial(Uid *uid, byte piccType, MIFARE_Key *key); - void PICC_DumpMifareClassicSectorToSerial(Uid *uid, MIFARE_Key *key, byte sector); - void PICC_DumpMifareUltralightToSerial(); - void MIFARE_SetAccessBits(byte *accessBitBuffer, byte g0, byte g1, byte g2, byte g3); - bool MIFARE_OpenUidBackdoor(bool logErrors); - bool MIFARE_SetUid(byte *newUid, byte uidSize, bool logErrors); - bool MIFARE_UnbrickUidSector(bool logErrors); - - ///////////////////////////////////////////////////////////////////////////////////// - // Convenience functions - does not add extra functionality - ///////////////////////////////////////////////////////////////////////////////////// - bool PICC_IsNewCardPresent(); - bool PICC_ReadCardSerial(); - -private: - byte _chipAddress; - byte _resetPowerDownPin; // Arduino pin connected to MFRC522's reset and power down input (Pin 6, NRSTPD, active low) - byte MIFARE_TwoStepHelper(byte command, byte blockAddr, long data); -}; - -#endif diff --git a/tasmota/tasmota_configurations_ESP32.h b/tasmota/tasmota_configurations_ESP32.h index 3b0f969a4..7f7a8d01b 100644 --- a/tasmota/tasmota_configurations_ESP32.h +++ b/tasmota/tasmota_configurations_ESP32.h @@ -81,12 +81,12 @@ #define USE_M5STACK_CORE2 // Add support for M5Stack Core2 #define USE_I2S_SAY_TIME #define USE_I2S_WEBRADIO - #define USE_MPU6886 #define USE_UFILESYS #define USE_SDCARD #define GUI_TRASH_FILE #define USE_I2C #define USE_BMA423 + #define USE_MPU6886 #define USE_SPI #define USE_DISPLAY #define USE_DISPLAY_ILI9342 From 1c1197ab53b8b24cc97e4b587f29fd011b7b927d Mon Sep 17 00:00:00 2001 From: Adrian Scillato <35405447+ascillato@users.noreply.github.com> Date: Tue, 16 Mar 2021 12:55:34 -0300 Subject: [PATCH 29/87] VL53L0X: Added support for multiple sensors --- tasmota/xsns_45_vl53l0x.ino | 225 +++++++++++++++++++++++++++--------- 1 file changed, 168 insertions(+), 57 deletions(-) diff --git a/tasmota/xsns_45_vl53l0x.ino b/tasmota/xsns_45_vl53l0x.ino index 1d377bd6d..3996f1c8e 100644 --- a/tasmota/xsns_45_vl53l0x.ino +++ b/tasmota/xsns_45_vl53l0x.ino @@ -1,7 +1,7 @@ /* - xsns_45_vl53l0x.ino - VL53L0X time of flight sensor support for Tasmota + xsns_45_vl53l0x.ino - VL53L0X time of flight multiple sensors support for Tasmota - Copyright (C) 2021 Theo Arends and Gerhard Mutz + Copyright (C) 2021 Theo Arends, Gerhard Mutz and Adrian Scillato 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 @@ -23,105 +23,216 @@ * VL53L0x time of flight sensor * * I2C Addres: 0x29 + ********************************************************************************************* + * + * Note: When using multiple VL53L0X, it is required to also wire the XSHUT pin of all those sensors + * in order to let Tasmota change by software the I2C address of those and give them an unique address + * for operation. The sensor don't save its address, so this procedure of changing its address is needed + * to be performed every restart. The Addresses used for this are 120 (0x78) to 127 (0x7F). In the I2c + * Standard (https://i2cdevices.org/addresses) those addresses are used by the PCA9685, so take into + * account they won't work together. + * + * The default value of VL53L0X_MAX_SENSORS is set in the file tasmota.h + * Changing that is backwards incompatible - Max supported devices by this driver are 8 + * + ********************************************************************************************** + * + * How to install this sensor: https://www.st.com/resource/en/datasheet/vl53l0x.pdf + * + * If you are going to use long I2C wires read this: + * https://hackaday.com/2017/02/08/taking-the-leap-off-board-an-introduction-to-i2c-over-long-wires/ + * \*********************************************************************************************/ #define XSNS_45 45 #define XI2C_31 31 // See I2CDEVICES.md +// Uncomment this line to use long range mode. This +// increases the sensitivity of the sensor and extends its +// potential range, but increases the likelihood of getting +// an inaccurate reading because of reflections from objects +// other than the intended target. It works best in dark +// conditions. + +//#define VL53L0X_LONG_RANGE + +// Uncomment ONE of these two lines to get +// - higher speed at the cost of lower accuracy OR +// - higher accuracy at the cost of lower speed + +//#define VL53L0X_HIGH_SPEED +//#define VL53L0X_HIGH_ACCURACY + #define USE_VL_MEDIAN #define USE_VL_MEDIAN_SIZE 5 // Odd number of samples median detection #include #include "VL53L0X.h" -VL53L0X sensor; +VL53L0X sensor[VL53L0X_MAX_SENSORS]; struct { uint16_t distance; uint16_t distance_prev; uint16_t buffer[5]; uint8_t ready = 0; uint8_t index; -} Vl53l0x; +} Vl53l0x[VL53L0X_MAX_SENSORS]; + +bool xshut = false; +bool VL53L0X_detected = false; /********************************************************************************************/ void Vl53l0Detect(void) { - if (!I2cSetDevice(0x29)) { return; } - if (!sensor.init()) { return; } - I2cSetActiveFound(sensor.getAddress(), "VL53L0X"); + for (uint32_t i = 0; i < VL53L0X_MAX_SENSORS; i++) { + if (PinUsed(GPIO_VL53L0X_XSHUT1, i)) { + pinMode(Pin(GPIO_VL53L0X_XSHUT1, i), OUTPUT); + digitalWrite(Pin(GPIO_VL53L0X_XSHUT1, i), i==0 ? 1 : 0); + xshut = true; + } + } - sensor.setTimeout(500); + for (uint32_t i = 0; i < VL53L0X_MAX_SENSORS; i++) { + if (PinUsed(GPIO_VL53L0X_XSHUT1, i) || (!xshut)) { + if (xshut) { pinMode(Pin(GPIO_VL53L0X_XSHUT1, i), INPUT); delay(1); } + if (!I2cSetDevice(0x29) && !I2cSetDevice((uint8_t)(120+i))) { return; } // Detection for unconfigured OR configured sensor + if (sensor[i].init()) { + if (xshut) { sensor[i].setAddress((uint8_t)(120+i)); } + uint8_t addr = sensor[i].getAddress(); + if (xshut) { + I2cSetActive(addr); + AddLog(LOG_LEVEL_DEBUG, PSTR(D_LOG_I2C D_SENSOR " VL53L0X %d " D_SENSOR_DETECTED " - " D_NEW_ADDRESS " 0x%02X"), i+1, addr); + } else { + I2cSetActiveFound(addr, "VL53L0X"); + } + sensor[i].setTimeout(500); - // Start continuous back-to-back mode (take readings as - // fast as possible). To use continuous timed mode - // instead, provide a desired inter-measurement period in - // ms (e.g. sensor.startContinuous(100)). - sensor.startContinuous(); - Vl53l0x.ready = 1; +#if defined VL53L0X_LONG_RANGE + // lower the return signal rate limit (default is 0.25 MCPS) + sensor[i].setSignalRateLimit(0.1); + // increase laser pulse periods (defaults are 14 and 10 PCLKs) + sensor[i].setVcselPulsePeriod(VL53L0X::VcselPeriodPreRange, 18); + sensor[i].setVcselPulsePeriod(VL53L0X::VcselPeriodFinalRange, 14); +#endif +#if defined VL53L0X_HIGH_SPEED + // reduce timing budget to 20 ms (default is about 33 ms) + sensor[i].setMeasurementTimingBudget(20000); +#elif defined VL53L0X_HIGH_ACCURACY + // increase timing budget to 200 ms + sensor[i].setMeasurementTimingBudget(200000); +#endif + // Start continuous back-to-back mode (take readings as + // fast as possible). To use continuous timed mode + // instead, provide a desired inter-measurement period in + // ms (e.g. sensor.startContinuous(100)). + sensor[i].startContinuous(); - Vl53l0x.index = 0; + Vl53l0x[i].ready = 1; + Vl53l0x[i].index = 0; + VL53L0X_detected = true; + if (!xshut) { break; } + } else { + AddLog(LOG_LEVEL_DEBUG, PSTR(D_LOG_I2C D_SENSOR " VL53L0X %d - " D_FAILED_TO_START), i+1); + } + } + } } void Vl53l0Every_250MSecond(void) { - uint16_t dist = sensor.readRangeContinuousMillimeters(); - if ((0 == dist) || (dist > 2000)) { - dist = 9999; - } + for (uint32_t i = 0; i < VL53L0X_MAX_SENSORS; i++) { + if (PinUsed(GPIO_VL53L0X_XSHUT1, i) || (!xshut)) { + uint16_t dist = sensor[i].readRangeContinuousMillimeters(); + if ((0 == dist) || (dist > 2200)) { + dist = 9999; + } #ifdef USE_VL_MEDIAN - // store in ring buffer - Vl53l0x.buffer[Vl53l0x.index] = dist; - Vl53l0x.index++; - if (Vl53l0x.index >= USE_VL_MEDIAN_SIZE) { - Vl53l0x.index = 0; - } + // store in ring buffer + Vl53l0x[i].buffer[Vl53l0x[i].index] = dist; + Vl53l0x[i].index++; + if (Vl53l0x[i].index >= USE_VL_MEDIAN_SIZE) { + Vl53l0x[i].index = 0; + } - // sort list and take median - uint16_t tbuff[USE_VL_MEDIAN_SIZE]; - memmove(tbuff, Vl53l0x.buffer, sizeof(tbuff)); - uint16_t tmp; - uint8_t flag; - for (uint32_t ocnt = 0; ocnt < USE_VL_MEDIAN_SIZE; ocnt++) { - flag = 0; - for (uint32_t count = 0; count < USE_VL_MEDIAN_SIZE -1; count++) { - if (tbuff[count] > tbuff[count +1]) { - tmp = tbuff[count]; - tbuff[count] = tbuff[count +1]; - tbuff[count +1] = tmp; - flag = 1; - } - } - if (!flag) { break; } - } - Vl53l0x.distance = tbuff[(USE_VL_MEDIAN_SIZE -1) / 2]; + // sort list and take median + uint16_t tbuff[USE_VL_MEDIAN_SIZE]; + memmove(tbuff, Vl53l0x[i].buffer, sizeof(tbuff)); + uint16_t tmp; + uint8_t flag; + for (uint32_t ocnt = 0; ocnt < USE_VL_MEDIAN_SIZE; ocnt++) { + flag = 0; + for (uint32_t count = 0; count < USE_VL_MEDIAN_SIZE -1; count++) { + if (tbuff[count] > tbuff[count +1]) { + tmp = tbuff[count]; + tbuff[count] = tbuff[count +1]; + tbuff[count +1] = tmp; + flag = 1; + } + } + if (!flag) { break; } + } + Vl53l0x[i].distance = tbuff[(USE_VL_MEDIAN_SIZE -1) / 2]; #else - Vl53l0x.distance = dist; + Vl53l0x[i].distance = dist; #endif + } + if (!xshut) { break; } + } } #ifdef USE_DOMOTICZ void Vl53l0Every_Second(void) { - if (abs(Vl53l0x.distance - Vl53l0x.distance_prev) > 8) { - Vl53l0x.distance_prev = Vl53l0x.distance; - DomoticzSensor(DZ_ILLUMINANCE, Vl53l0x.distance); + if (abs(Vl53l0x[0].distance - Vl53l0x[0].distance_prev) > 8) { + Vl53l0x[0].distance_prev = Vl53l0x[0].distance; + DomoticzSensor(DZ_ILLUMINANCE, Vl53l0x[0].distance); } } #endif // USE_DOMOTICZ void Vl53l0Show(boolean json) { - if (json) { - ResponseAppend_P(PSTR(",\"VL53L0X\":{\"" D_JSON_DISTANCE "\":%d}"), Vl53l0x.distance); + for (uint32_t i = 0; i < VL53L0X_MAX_SENSORS; i++) { + if (PinUsed(GPIO_VL53L0X_XSHUT1, i) || (!xshut)) { + if (json) { + if (Vl53l0x[i].distance == 9999) { + if (xshut) { + ResponseAppend_P(PSTR(",\"VL53L0X_%d\":{\"" D_JSON_DISTANCE "\":null}"), i+1); + } else { + ResponseAppend_P(PSTR(",\"VL53L0X\":{\"" D_JSON_DISTANCE "\":null}")); // For backwards compatibility when not using XSHUT GPIOs + } + } else { + if (xshut) { + ResponseAppend_P(PSTR(",\"VL53L0X_%d\":{\"" D_JSON_DISTANCE "\":%d}"), i+1, Vl53l0x[i].distance); + } else { + ResponseAppend_P(PSTR(",\"VL53L0X\":{\"" D_JSON_DISTANCE "\":%d}"), Vl53l0x[i].distance); // For backwards compatibility when not using XSHUT GPIOs + } + } +#ifdef USE_WEBSERVER + } else { + if (Vl53l0x[i].distance == 9999) { + if (xshut) { + WSContentSend_PD("{s}%s_%d " D_DISTANCE "{m}%s {e}", PSTR("VL53L0X"), i+1, PSTR(D_OUT_OF_RANGE)); + } else { + WSContentSend_PD("{s}%s " D_DISTANCE "{m}%s {e}", PSTR("VL53L0X"), PSTR(D_OUT_OF_RANGE)); // For backwards compatibility when not using XSHUT GPIOs + } + } else { + if (xshut) { + WSContentSend_PD("{s}%s_%d " D_DISTANCE "{m}%d " D_UNIT_MILLIMETER "{e}", PSTR("VL53L0X"), i+1, Vl53l0x[i].distance); + } else { + WSContentSend_PD(HTTP_SNS_DISTANCE, PSTR("VL53L0X"), Vl53l0x[i].distance); // For backwards compatibility when not using XSHUT GPIOs + } + } +#endif + } + } + if (sensor[i].timeoutOccurred()) { AddLog(LOG_LEVEL_DEBUG, PSTR(D_LOG_I2C D_TIMEOUT_WAITING_FOR D_SENSOR " VL53L0X %d"), i+1); } + if (!xshut) { break; } + } #ifdef USE_DOMOTICZ - if (0 == TasmotaGlobal.tele_period) { - DomoticzSensor(DZ_ILLUMINANCE, Vl53l0x.distance); + if ((json) && (0 == TasmotaGlobal.tele_period)){ + DomoticzSensor(DZ_ILLUMINANCE, Vl53l0x[0].distance); } #endif // USE_DOMOTICZ -#ifdef USE_WEBSERVER - } else { - WSContentSend_PD(HTTP_SNS_DISTANCE, PSTR("VL53L0X"), Vl53l0x.distance); -#endif - } } /*********************************************************************************************\ @@ -136,7 +247,7 @@ bool Xsns45(byte function) { if (FUNC_INIT == function) { Vl53l0Detect(); } - else if (Vl53l0x.ready) { + else if (VL53L0X_detected) { switch (function) { case FUNC_EVERY_250_MSECOND: Vl53l0Every_250MSecond(); From a3c5ac8204c58564424d03bb74b0ff0704dfb543 Mon Sep 17 00:00:00 2001 From: Adrian Scillato <35405447+ascillato@users.noreply.github.com> Date: Tue, 16 Mar 2021 12:58:24 -0300 Subject: [PATCH 30/87] VL53L0X: Set max number of sensors --- tasmota/tasmota.h | 1 + 1 file changed, 1 insertion(+) diff --git a/tasmota/tasmota.h b/tasmota/tasmota.h index 87e5f01b3..3191036e3 100644 --- a/tasmota/tasmota.h +++ b/tasmota/tasmota.h @@ -83,6 +83,7 @@ const uint8_t MAX_SHUTTER_KEYS = 4; // Max number of shutter keys or but const uint8_t MAX_PCF8574 = 4; // Max number of PCF8574 devices const uint8_t MAX_RULE_SETS = 3; // Max number of rule sets of size 512 characters const uint16_t MAX_RULE_SIZE = 512; // Max number of characters in rules +const uint16_t VL53L0X_MAX_SENSORS = 8; // Max number of VL53L0X sensors #ifdef ESP32 const uint8_t MAX_I2C = 2; // Max number of I2C controllers (ESP32 = 2) From 82bbc7d215e4a9a6907d88646fddafdb5b857245 Mon Sep 17 00:00:00 2001 From: Adrian Scillato <35405447+ascillato@users.noreply.github.com> Date: Tue, 16 Mar 2021 13:05:08 -0300 Subject: [PATCH 31/87] VL53L0X: Added new XSHUT GPIOs --- tasmota/tasmota_template.h | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/tasmota/tasmota_template.h b/tasmota/tasmota_template.h index 7b9bac855..bffb281bf 100644 --- a/tasmota/tasmota_template.h +++ b/tasmota/tasmota_template.h @@ -154,6 +154,7 @@ enum UserSelectablePins { GPIO_SSD1351_DC, GPIO_XPT2046_CS, // XPT2046 SPI Chip Select 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_SENSOR_END }; enum ProgramSelectablePins { @@ -328,6 +329,7 @@ const char kSensorNames[] PROGMEM = D_SENSOR_SSD1351_DC "|" D_SENSOR_XPT2046_CS "|" D_SENSOR_CSE7761_TX "|" D_SENSOR_CSE7761_RX "|" + D_SENSOR_VL53L0X_XSHUT "|" ; const char kSensorNamesFixed[] PROGMEM = @@ -781,6 +783,10 @@ const uint16_t kGpioNiceList[] PROGMEM = { AGPIO(GPIO_PROJECTOR_CTRL_TX), // LCD/DLP Projector Serial Control AGPIO(GPIO_PROJECTOR_CTRL_RX), // LCD/DLP Projector Serial Control #endif +#ifdef USE_VL53L0X + AGPIO(GPIO_VL53L0X_XSHUT1) + VL53L0X_MAX_SENSORS, // When using multiple VL53L0X. +#endif + /*-------------------------------------------------------------------------------------------*\ * ESP32 specifics \*-------------------------------------------------------------------------------------------*/ From 4f9cbaab076e4572f6a5a4287bfa7a1c4cda3b82 Mon Sep 17 00:00:00 2001 From: Adrian Scillato <35405447+ascillato@users.noreply.github.com> Date: Tue, 16 Mar 2021 13:09:12 -0300 Subject: [PATCH 32/87] VL53L0X: Translations --- tasmota/language/en_GB.h | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/tasmota/language/en_GB.h b/tasmota/language/en_GB.h index df99b6504..a3c12f55c 100644 --- a/tasmota/language/en_GB.h +++ b/tasmota/language/en_GB.h @@ -797,7 +797,10 @@ #define D_SENSOR_WIEGAND_D1 "Wiegand D1" #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" +#define D_SENSOR_DETECTED "detected" // Units #define D_UNIT_AMPERE "A" From ff4e2b9b3be7dc60be13b81a544ae48cec6affcb Mon Sep 17 00:00:00 2001 From: Adrian Scillato <35405447+ascillato@users.noreply.github.com> Date: Tue, 16 Mar 2021 13:19:37 -0300 Subject: [PATCH 33/87] Update af_AF.h --- tasmota/language/af_AF.h | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/tasmota/language/af_AF.h b/tasmota/language/af_AF.h index b445d8dcd..b61c82c6e 100644 --- a/tasmota/language/af_AF.h +++ b/tasmota/language/af_AF.h @@ -28,7 +28,7 @@ * Use online command StateText to translate ON, OFF, HOLD and TOGGLE. * Use online command Prefix to translate cmnd, stat and tele. * - * Updated until v9.2.0.4 + * Updated until v9.3.1.1 \*********************************************************************/ //#define LANGUAGE_MODULE_NAME // Enable to display "Module Generic" (ie Spanish), Disable to display "Generic Module" (ie English) @@ -797,7 +797,10 @@ #define D_SENSOR_WIEGAND_D1 "Wiegand D1" #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" +#define D_SENSOR_DETECTED "detected" // Units #define D_UNIT_AMPERE "A" From f0e95969adb3b14f7f3018a012dccdee2f2f90d6 Mon Sep 17 00:00:00 2001 From: Adrian Scillato <35405447+ascillato@users.noreply.github.com> Date: Tue, 16 Mar 2021 13:21:54 -0300 Subject: [PATCH 34/87] Update bg_BG.h --- tasmota/language/bg_BG.h | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/tasmota/language/bg_BG.h b/tasmota/language/bg_BG.h index c9ddf4cbf..2061eb460 100644 --- a/tasmota/language/bg_BG.h +++ b/tasmota/language/bg_BG.h @@ -28,7 +28,7 @@ * Use online command StateText to translate ON, OFF, HOLD and TOGGLE. * Use online command Prefix to translate cmnd, stat and tele. * - * Updated until v8.2.0.6 + * Updated until v9.3.1.1 \*********************************************************************/ //#define LANGUAGE_MODULE_NAME // Enable to display "Module Generic" (ie Spanish), Disable to display "Generic Module" (ie English) @@ -796,7 +796,10 @@ #define D_SENSOR_WIEGAND_D1 "Wiegand D1" #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" +#define D_SENSOR_DETECTED "detected" // Units #define D_UNIT_AMPERE "A" From dc744c1aac30828ed4f93734f2e3780ba92135dc Mon Sep 17 00:00:00 2001 From: Adrian Scillato <35405447+ascillato@users.noreply.github.com> Date: Tue, 16 Mar 2021 13:25:24 -0300 Subject: [PATCH 35/87] Update cs_CZ.h --- tasmota/language/cs_CZ.h | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/tasmota/language/cs_CZ.h b/tasmota/language/cs_CZ.h index 6bd44919c..be329c3e2 100644 --- a/tasmota/language/cs_CZ.h +++ b/tasmota/language/cs_CZ.h @@ -28,7 +28,7 @@ * Use online command StateText to translate ON, OFF, HOLD and TOGGLE. * Use online command Prefix to translate cmnd, stat and tele. * - * Updated until v6.5.0.9 + * Updated until v9.3.1.1 \*********************************************************************/ //#define LANGUAGE_MODULE_NAME // Enable to display "Module Generic" (ie Spanish), Disable to display "Generic Module" (ie English) @@ -797,7 +797,10 @@ #define D_SENSOR_WIEGAND_D1 "Wiegand D1" #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" +#define D_SENSOR_DETECTED "detected" // Units #define D_UNIT_AMPERE "A" From 9b69aa6d37e7fedb381024ea19695f26cb2e01f8 Mon Sep 17 00:00:00 2001 From: Adrian Scillato <35405447+ascillato@users.noreply.github.com> Date: Tue, 16 Mar 2021 13:27:19 -0300 Subject: [PATCH 36/87] Update de_DE.h --- tasmota/language/de_DE.h | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/tasmota/language/de_DE.h b/tasmota/language/de_DE.h index c7221243b..9eaff2441 100644 --- a/tasmota/language/de_DE.h +++ b/tasmota/language/de_DE.h @@ -28,7 +28,7 @@ * Use online command StateText to translate ON, OFF, HOLD and TOGGLE. * Use online command Prefix to translate cmnd, stat and tele. * - * Updated until v9.2.0.3 + * Updated until v9.3.1.1 \*********************************************************************/ //#define LANGUAGE_MODULE_NAME // Enable to display "Module Generic" (ie Spanish), Disable to display "Generic Module" (ie English) @@ -797,7 +797,10 @@ #define D_SENSOR_WIEGAND_D1 "Wiegand D1" #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" +#define D_SENSOR_DETECTED "detected" // Units #define D_UNIT_AMPERE "A" From 9d22bb0a2e5eb5181b672d39b53c7027a9886f9e Mon Sep 17 00:00:00 2001 From: Adrian Scillato <35405447+ascillato@users.noreply.github.com> Date: Tue, 16 Mar 2021 13:32:34 -0300 Subject: [PATCH 37/87] Update el_GR.h --- tasmota/language/el_GR.h | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/tasmota/language/el_GR.h b/tasmota/language/el_GR.h index e11ae5194..ca14da7c1 100644 --- a/tasmota/language/el_GR.h +++ b/tasmota/language/el_GR.h @@ -28,7 +28,7 @@ * Use online command StateText to translate ON, OFF, HOLD and TOGGLE. * Use online command Prefix to translate cmnd, stat and tele. * - * Updated until v6.5.0 + * Updated until v9.3.1.1 \*********************************************************************/ #define LANGUAGE_MODULE_NAME // Enable to display "Module Generic" (ie Spanish), Disable to display "Generic Module" (ie English) @@ -797,7 +797,10 @@ #define D_SENSOR_WIEGAND_D1 "Wiegand D1" #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" +#define D_SENSOR_DETECTED "detected" // Units #define D_UNIT_AMPERE "A" From 1fc3d662dc2f81d90c321cd7ce7c8282d27b6bdd Mon Sep 17 00:00:00 2001 From: Adrian Scillato <35405447+ascillato@users.noreply.github.com> Date: Tue, 16 Mar 2021 13:34:00 -0300 Subject: [PATCH 38/87] Update en_GB.h --- tasmota/language/en_GB.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tasmota/language/en_GB.h b/tasmota/language/en_GB.h index a3c12f55c..12a476a32 100644 --- a/tasmota/language/en_GB.h +++ b/tasmota/language/en_GB.h @@ -28,7 +28,7 @@ * Use online command StateText to translate ON, OFF, HOLD and TOGGLE. * Use online command Prefix to translate cmnd, stat and tele. * - * Updated until v8.0.0.0 + * Updated until v9.3.1.1 \*********************************************************************/ //#define LANGUAGE_MODULE_NAME // Enable to display "Module Generic" (ie Spanish), Disable to display "Generic Module" (ie English) From 13f388b3c2282a34bd8408f97545e8acbf463948 Mon Sep 17 00:00:00 2001 From: Adrian Scillato <35405447+ascillato@users.noreply.github.com> Date: Tue, 16 Mar 2021 13:43:18 -0300 Subject: [PATCH 39/87] Update es_ES.h --- tasmota/language/es_ES.h | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/tasmota/language/es_ES.h b/tasmota/language/es_ES.h index b05540083..93967a818 100644 --- a/tasmota/language/es_ES.h +++ b/tasmota/language/es_ES.h @@ -1,5 +1,5 @@ /* - es-ES.h - localization for Spanish - Spain for Tasmota + es-ES.h - localization for Spanish - Spain for Tasmota (translation also valid for all latinamerica) Copyright (C) 2021 Adrian Scillato @@ -28,7 +28,7 @@ * Use online command StateText to translate ON, OFF, HOLD and TOGGLE. * Use online command Prefix to translate cmnd, stat and tele. * - * Updated until v9.2.0.3 + * Updated until v9.3.1.1 \*********************************************************************/ #define LANGUAGE_MODULE_NAME // Enable to display "Module Generic" (ie Spanish), Disable to display "Generic Module" (ie English) @@ -797,7 +797,10 @@ #define D_SENSOR_WIEGAND_D1 "Wiegand D1" #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 "Cambiando dirección a" +#define D_OUT_OF_RANGE "Fuera de Rango" +#define D_SENSOR_DETECTED "detectado" // Units #define D_UNIT_AMPERE "A" From 496240d664c6a7e932147946e5b4b60daec64240 Mon Sep 17 00:00:00 2001 From: Adrian Scillato <35405447+ascillato@users.noreply.github.com> Date: Tue, 16 Mar 2021 13:45:20 -0300 Subject: [PATCH 40/87] Update fr_FR.h --- tasmota/language/fr_FR.h | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/tasmota/language/fr_FR.h b/tasmota/language/fr_FR.h index a5c8ab250..980b380f7 100644 --- a/tasmota/language/fr_FR.h +++ b/tasmota/language/fr_FR.h @@ -1,14 +1,18 @@ /* fr-FR.h - localization for French - France for Tasmota + Copyright (C) 2021 Olivier Francais + 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 . */ @@ -24,7 +28,7 @@ * Use online command StateText to translate ON, OFF, HOLD and TOGGLE. * Use online command Prefix to translate cmnd, stat and tele. * - * Updated until v9.2.0.3 + * Updated until v9.3.1.1 \*********************************************************************/ #define LANGUAGE_MODULE_NAME // Enable to display "Module Generic" (ie Spanish), Disable to display "Generic Module" (ie English) @@ -793,7 +797,10 @@ #define D_SENSOR_WIEGAND_D1 "Wiegand D1" #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" +#define D_SENSOR_DETECTED "detected" // Units #define D_UNIT_AMPERE "A" From d2c4876f6b8671cc70abfae19dec099d8808cc30 Mon Sep 17 00:00:00 2001 From: Adrian Scillato <35405447+ascillato@users.noreply.github.com> Date: Tue, 16 Mar 2021 13:47:14 -0300 Subject: [PATCH 41/87] Update fy_NL.h --- tasmota/language/fy_NL.h | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/tasmota/language/fy_NL.h b/tasmota/language/fy_NL.h index bb33ebc2d..8b166dd94 100644 --- a/tasmota/language/fy_NL.h +++ b/tasmota/language/fy_NL.h @@ -28,7 +28,7 @@ * Use online command StateText to translate ON, OFF, HOLD and TOGGLE. * Use online command Prefix to translate cmnd, stat and tele. * - * Updated until v9.2.0.4 + * Updated until v9.3.1.1 \*********************************************************************/ //#define LANGUAGE_MODULE_NAME // Enable to display "Module Generic" (ie Spanish), Disable to display "Generic Module" (ie English) @@ -797,7 +797,10 @@ #define D_SENSOR_WIEGAND_D1 "Wiegand D1" #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" +#define D_SENSOR_DETECTED "detected" // Units #define D_UNIT_AMPERE "A" From 6bbfbc086ea4b5be4ef1af8e7859871d8b62acab Mon Sep 17 00:00:00 2001 From: Adrian Scillato <35405447+ascillato@users.noreply.github.com> Date: Tue, 16 Mar 2021 13:49:13 -0300 Subject: [PATCH 42/87] Update hu_HU.h --- tasmota/language/hu_HU.h | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/tasmota/language/hu_HU.h b/tasmota/language/hu_HU.h index 6baf1960f..239239348 100644 --- a/tasmota/language/hu_HU.h +++ b/tasmota/language/hu_HU.h @@ -28,7 +28,7 @@ * Use online command StateText to translate ON, OFF, HOLD and TOGGLE. * Use online command Prefix to translate cmnd, stat and tele. * - * Updated until v5.12.0e + * Updated until v9.3.1.1 \*********************************************************************/ //#define LANGUAGE_MODULE_NAME // Enable to display "Module Generic" (ie Spanish), Disable to display "Generic Module" (ie English) @@ -797,7 +797,10 @@ #define D_SENSOR_WIEGAND_D1 "Wiegand D1" #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" +#define D_SENSOR_DETECTED "detected" // Units #define D_UNIT_AMPERE "A" From a527dc619ef335c800407309e4799db78cb7b2b5 Mon Sep 17 00:00:00 2001 From: Adrian Scillato <35405447+ascillato@users.noreply.github.com> Date: Tue, 16 Mar 2021 13:54:25 -0300 Subject: [PATCH 43/87] Update it_IT.h --- tasmota/language/it_IT.h | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/tasmota/language/it_IT.h b/tasmota/language/it_IT.h index 3540c0cb0..c2251af85 100644 --- a/tasmota/language/it_IT.h +++ b/tasmota/language/it_IT.h @@ -1,7 +1,7 @@ /* it-IT.h - localization for Italian - Italy for Tasmota - Copyright (C) 2021 Gennaro Tortone - some mods by Antonio Fragola - Updated by bovirus - rev. 03.03.2021 + Copyright (C) 2021 Gennaro Tortone, Antonio Fragola, Bovirus and Adrian Scillato 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 @@ -28,7 +28,7 @@ * Use online command StateText to translate ON, OFF, HOLD and TOGGLE. * Use online command Prefix to translate cmnd, stat and tele. * - * Updated until v6.0.0a + * Updated until v9.3.1.1 \*********************************************************************/ #define LANGUAGE_MODULE_NAME // Enable to display "Module Generic" (ie Spanish), Disable to display "Generic Module" (ie English) @@ -797,6 +797,10 @@ #define D_SENSOR_WIEGAND_D1 "Wiegand - D1" #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 "Cambio di indirizzo a" +#define D_OUT_OF_RANGE "Fuori dal limite" +#define D_SENSOR_DETECTED "rilevato" // Units #define D_UNIT_AMPERE "A" From 33ac2f8f1cc0e30c721ff75eef6d3af2703b8ebc Mon Sep 17 00:00:00 2001 From: Adrian Scillato <35405447+ascillato@users.noreply.github.com> Date: Tue, 16 Mar 2021 13:56:47 -0300 Subject: [PATCH 44/87] Update he_HE.h --- tasmota/language/he_HE.h | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/tasmota/language/he_HE.h b/tasmota/language/he_HE.h index 1d5785433..8bf5e5e97 100644 --- a/tasmota/language/he_HE.h +++ b/tasmota/language/he_HE.h @@ -28,7 +28,7 @@ * Use online command StateText to translate ON, OFF, HOLD and TOGGLE. * Use online command Prefix to translate cmnd, stat and tele. * - * Updated until v5.14.0b + * Updated until v9.3.1.1 \*********************************************************************/ //#define LANGUAGE_MODULE_NAME // Enable to display "Module Generic" (ie Spanish), Disable to display "Generic Module" (ie English) @@ -797,7 +797,10 @@ #define D_SENSOR_WIEGAND_D1 "Wiegand D1" #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" +#define D_SENSOR_DETECTED "detected" // Units #define D_UNIT_AMPERE "A" From 78face85de9ce6f805834902de3821ca7a32f252 Mon Sep 17 00:00:00 2001 From: Adrian Scillato <35405447+ascillato@users.noreply.github.com> Date: Tue, 16 Mar 2021 14:00:11 -0300 Subject: [PATCH 45/87] Update ko_KO.h --- tasmota/language/ko_KO.h | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/tasmota/language/ko_KO.h b/tasmota/language/ko_KO.h index 907f840e5..964ab46a7 100644 --- a/tasmota/language/ko_KO.h +++ b/tasmota/language/ko_KO.h @@ -1,7 +1,7 @@ /* ko-KO.h - localization for Korean - Korean for Tasmota - Copyright (C) 2021 Theo Arends (translated by NyaamZ) + Copyright (C) 2021 NyaamZ 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 @@ -28,7 +28,7 @@ * Use online command StateText to translate ON, OFF, HOLD and TOGGLE. * Use online command Prefix to translate cmnd, stat and tele. * - * Updated until v6.2.1.11 + * Updated until v9.3.1.1 \*********************************************************************/ //#define LANGUAGE_MODULE_NAME // Enable to display "Module Generic" (ie Spanish), Disable to display "Generic Module" (ie English) @@ -797,7 +797,10 @@ #define D_SENSOR_WIEGAND_D1 "Wiegand D1" #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" +#define D_SENSOR_DETECTED "detected" // Units #define D_UNIT_AMPERE "A" From d41e118a9d5c2b455fdc2baf177bac5da6a8cc37 Mon Sep 17 00:00:00 2001 From: Adrian Scillato <35405447+ascillato@users.noreply.github.com> Date: Tue, 16 Mar 2021 14:01:45 -0300 Subject: [PATCH 46/87] Update nl_NL.h --- tasmota/language/nl_NL.h | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/tasmota/language/nl_NL.h b/tasmota/language/nl_NL.h index 96991391f..bc855e4a8 100644 --- a/tasmota/language/nl_NL.h +++ b/tasmota/language/nl_NL.h @@ -28,7 +28,7 @@ * Use online command StateText to translate ON, OFF, HOLD and TOGGLE. * Use online command Prefix to translate cmnd, stat and tele. * - * Updated until v9.2.0.4 + * Updated until v9.3.1.1 \*********************************************************************/ //#define LANGUAGE_MODULE_NAME // Enable to display "Module Generic" (ie Spanish), Disable to display "Generic Module" (ie English) @@ -797,7 +797,10 @@ #define D_SENSOR_WIEGAND_D1 "Wiegand D1" #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" +#define D_SENSOR_DETECTED "detected" // Units #define D_UNIT_AMPERE "A" From 2a25e21d81f204a65d6142f3c95c86581411045e Mon Sep 17 00:00:00 2001 From: Adrian Scillato <35405447+ascillato@users.noreply.github.com> Date: Tue, 16 Mar 2021 14:03:59 -0300 Subject: [PATCH 47/87] Update pl_PL.h --- tasmota/language/pl_PL.h | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/tasmota/language/pl_PL.h b/tasmota/language/pl_PL.h index 818aba4cb..78710996f 100644 --- a/tasmota/language/pl_PL.h +++ b/tasmota/language/pl_PL.h @@ -1,7 +1,7 @@ /* pl-PL-d.h - localization for Polish with diacritics - Poland for Tasmota - Copyright (C) 2021 Theo Arends (translated by roblad - Robert L., upgraded by R. Turala) + Copyright (C) 2021 Roblad (Robert L.) and R. Turala 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 @@ -28,7 +28,7 @@ * Use online command StateText to translate ON, OFF, HOLD and TOGGLE. * Use online command Prefix to translate cmnd, stat and tele. * - * Updated until v5.12.0d + * Updated until v9.3.1.1 \*********************************************************************/ //#define LANGUAGE_MODULE_NAME // Enable to display "Module Generic" (ie Spanish), Disable to display "Generic Module" (ie English) @@ -797,7 +797,10 @@ #define D_SENSOR_WIEGAND_D1 "Wiegand D1" #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" +#define D_SENSOR_DETECTED "detected" // Units #define D_UNIT_AMPERE "A" From c9a5fcca44840b6f4b5c79bec7742075d622c417 Mon Sep 17 00:00:00 2001 From: Adrian Scillato <35405447+ascillato@users.noreply.github.com> Date: Tue, 16 Mar 2021 14:13:25 -0300 Subject: [PATCH 48/87] Update pt_PT.h --- tasmota/language/pt_PT.h | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/tasmota/language/pt_PT.h b/tasmota/language/pt_PT.h index 0687aedcd..c5b524875 100644 --- a/tasmota/language/pt_PT.h +++ b/tasmota/language/pt_PT.h @@ -1,7 +1,7 @@ /* pt-PT.h - localization for Portuguese - Portugal for Tasmota - Copyright (C) 2021 Paulo Paiva + Copyright (C) 2021 Paulo Paiva and Adrian Scillato 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 @@ -28,7 +28,7 @@ * Use online command StateText to translate ON, OFF, HOLD and TOGGLE. * Use online command Prefix to translate cmnd, stat and tele. * - * Updated until v7.0.0.1 + * Updated until v9.3.1.1 \*********************************************************************/ #define LANGUAGE_MODULE_NAME // Enable to display "Module Generic" (ie Spanish), Disable to display "Generic Module" (ie English) @@ -797,7 +797,10 @@ #define D_SENSOR_WIEGAND_D1 "Wiegand D1" #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 "Mudança de endereço para" +#define D_OUT_OF_RANGE "Fora de Alcance" +#define D_SENSOR_DETECTED "detectou" // Units #define D_UNIT_AMPERE "A" From a3e5fc1d5e86f5beb7166bff8a03da1928881cf2 Mon Sep 17 00:00:00 2001 From: Adrian Scillato <35405447+ascillato@users.noreply.github.com> Date: Tue, 16 Mar 2021 14:13:33 -0300 Subject: [PATCH 49/87] Update pt_BR.h --- tasmota/language/pt_BR.h | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/tasmota/language/pt_BR.h b/tasmota/language/pt_BR.h index c9ba30c50..e0fbd7250 100644 --- a/tasmota/language/pt_BR.h +++ b/tasmota/language/pt_BR.h @@ -1,7 +1,7 @@ /* pt-BR.h - localization for Portuguese - Brazil for Tasmota - Copyright (C) 2021 Fabiano Bovo + Copyright (C) 2021 Fabiano Bovo and Adrian Scillato 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 @@ -28,7 +28,7 @@ * Use online command StateText to translate ON, OFF, HOLD and TOGGLE. * Use online command Prefix to translate cmnd, stat and tele. * - * Updated until v7.0.0.1 + * Updated until v9.3.1.1 \*********************************************************************/ #define LANGUAGE_MODULE_NAME // Enable to display "Module Generic" (ie Spanish), Disable to display "Generic Module" (ie English) @@ -797,7 +797,9 @@ #define D_SENSOR_WIEGAND_D1 "Wiegand D1" #define D_SENSOR_NEOPOOL_TX "NeoPool Tx" #define D_SENSOR_NEOPOOL_RX "NeoPool Rx" - +#define D_NEW_ADDRESS "Mudança de endereço para" +#define D_OUT_OF_RANGE "Fora de Alcance" +#define D_SENSOR_DETECTED "detectou" // Units #define D_UNIT_AMPERE "A" From 239bc5c3a4f0d7b2ba74a97a9b485666e031f004 Mon Sep 17 00:00:00 2001 From: Adrian Scillato <35405447+ascillato@users.noreply.github.com> Date: Tue, 16 Mar 2021 14:15:21 -0300 Subject: [PATCH 50/87] Update ro_RO.h --- tasmota/language/ro_RO.h | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/tasmota/language/ro_RO.h b/tasmota/language/ro_RO.h index d9d779948..c8182eb75 100644 --- a/tasmota/language/ro_RO.h +++ b/tasmota/language/ro_RO.h @@ -28,7 +28,7 @@ * Use online command StateText to translate ON, OFF, HOLD and TOGGLE. * Use online command Prefix to translate cmnd, stat and tele. * - * Updated until v8.1.0.10 + * Updated until v9.3.1.1 \*********************************************************************/ //#define LANGUAGE_MODULE_NAME // Enable to display "Module Generic" (ie Spanish), Disable to display "Generic Module" (ie English) @@ -797,7 +797,10 @@ #define D_SENSOR_WIEGAND_D1 "Wiegand D1" #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" +#define D_SENSOR_DETECTED "detected" // Units #define D_UNIT_AMPERE "A" From 0965e13107f06a3ba4b93927875a8c00ddbeac30 Mon Sep 17 00:00:00 2001 From: Adrian Scillato <35405447+ascillato@users.noreply.github.com> Date: Tue, 16 Mar 2021 14:18:17 -0300 Subject: [PATCH 51/87] Update ru_RU.h --- tasmota/language/ru_RU.h | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/tasmota/language/ru_RU.h b/tasmota/language/ru_RU.h index ab546c0f1..6d8d0b932 100644 --- a/tasmota/language/ru_RU.h +++ b/tasmota/language/ru_RU.h @@ -1,7 +1,7 @@ /* - ru-RU.h - localization for Russian - Rissia for Tasmota + ru-RU.h - localization for Russian - Russia for Tasmota - Copyright (C) 2021 Theo Arends / roman-vn + Copyright (C) 2021 Roman-vn 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 @@ -28,7 +28,7 @@ * Use online command StateText to translate ON, OFF, HOLD and TOGGLE. * Use online command Prefix to translate cmnd, stat and tele. * - * Updated until v5.12.0b + * Updated until v9.3.1.1 \*********************************************************************/ //#define LANGUAGE_MODULE_NAME // Enable to display "Module Generic" (ie Spanish), Disable to display "Generic Module" (ie English) @@ -797,7 +797,10 @@ #define D_SENSOR_WIEGAND_D1 "Wiegand D1" #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" +#define D_SENSOR_DETECTED "detected" // Units #define D_UNIT_AMPERE "А" From 98c3da837a6a94284e39c214fe8dd6bdd2347b6c Mon Sep 17 00:00:00 2001 From: Adrian Scillato <35405447+ascillato@users.noreply.github.com> Date: Tue, 16 Mar 2021 14:24:16 -0300 Subject: [PATCH 52/87] Update sk_SK.h --- tasmota/language/sk_SK.h | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/tasmota/language/sk_SK.h b/tasmota/language/sk_SK.h index 9310a6d24..b0fbc5c3f 100644 --- a/tasmota/language/sk_SK.h +++ b/tasmota/language/sk_SK.h @@ -28,7 +28,7 @@ * Use online command StateText to translate ON, OFF, HOLD and TOGGLE. * Use online command Prefix to translate cmnd, stat and tele. * - * Updated until v6.2.1.14 + * Updated until v9.3.1.1 \*********************************************************************/ //#define LANGUAGE_MODULE_NAME // Enable to display "Module Generic" (ie Spanish), Disable to display "Generic Module" (ie English) @@ -797,7 +797,10 @@ #define D_SENSOR_WIEGAND_D1 "Wiegand D1" #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" +#define D_SENSOR_DETECTED "detected" // Units #define D_UNIT_AMPERE "A" From 7a25c9ccdd746e13559d005f546b754821c95d0e Mon Sep 17 00:00:00 2001 From: Adrian Scillato <35405447+ascillato@users.noreply.github.com> Date: Tue, 16 Mar 2021 14:25:37 -0300 Subject: [PATCH 53/87] Update sv_SE.h --- tasmota/language/sv_SE.h | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/tasmota/language/sv_SE.h b/tasmota/language/sv_SE.h index 67de1e2aa..5ccbc77fe 100644 --- a/tasmota/language/sv_SE.h +++ b/tasmota/language/sv_SE.h @@ -28,7 +28,7 @@ * Use online command StateText to translate ON, OFF, HOLD and TOGGLE. * Use online command Prefix to translate cmnd, stat and tele. * - * Updated until v6.2.1.11 + * Updated until v9.3.1.1 \*********************************************************************/ //#define LANGUAGE_MODULE_NAME // Enable to display "Module Generic" (ie Spanish), Disable to display "Generic Module" (ie English) @@ -797,7 +797,10 @@ #define D_SENSOR_WIEGAND_D1 "Wiegand D1" #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" +#define D_SENSOR_DETECTED "detected" // Units #define D_UNIT_AMPERE "A" From 30abe26a0a2317b3b0c1cc6338d6923337d45d1a Mon Sep 17 00:00:00 2001 From: Adrian Scillato <35405447+ascillato@users.noreply.github.com> Date: Tue, 16 Mar 2021 14:26:41 -0300 Subject: [PATCH 54/87] Update tr_TR.h --- tasmota/language/tr_TR.h | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/tasmota/language/tr_TR.h b/tasmota/language/tr_TR.h index b21b0574a..e6429fed1 100644 --- a/tasmota/language/tr_TR.h +++ b/tasmota/language/tr_TR.h @@ -28,7 +28,7 @@ * Use online command StateText to translate ON, OFF, HOLD and TOGGLE. * Use online command Prefix to translate cmnd, stat and tele. * - * Updated until v6.1.1 + * Updated until v9.3.1.1 \*********************************************************************/ //#define LANGUAGE_MODULE_NAME // Enable to display "Module Generic" (ie Spanish), Disable to display "Generic Module" (ie English) @@ -797,7 +797,10 @@ #define D_SENSOR_WIEGAND_D1 "Wiegand D1" #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" +#define D_SENSOR_DETECTED "detected" // Units #define D_UNIT_AMPERE "A" From a25b3aea269f209fd284b5cbb3f5fe1972776cb0 Mon Sep 17 00:00:00 2001 From: Adrian Scillato <35405447+ascillato@users.noreply.github.com> Date: Tue, 16 Mar 2021 14:28:14 -0300 Subject: [PATCH 55/87] Update uk_UA.h --- tasmota/language/uk_UA.h | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/tasmota/language/uk_UA.h b/tasmota/language/uk_UA.h index b1baca8dd..286b851dc 100644 --- a/tasmota/language/uk_UA.h +++ b/tasmota/language/uk_UA.h @@ -1,7 +1,7 @@ /* uk-UA.h - localization for Ukrainian - Ukraine for Tasmota - Copyright (C) 2021 Theo Arends / vadym-adik + Copyright (C) 2021 Vadym-adik 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 @@ -28,7 +28,7 @@ * Use online command StateText to translate ON, OFF, HOLD and TOGGLE. * Use online command Prefix to translate cmnd, stat and tele. * - * Updated until v5.14.0a + * Updated until v9.3.1.1 \*********************************************************************/ //#define LANGUAGE_MODULE_NAME // Enable to display "Module Generic" (ie Spanish), Disable to display "Generic Module" (ie English) @@ -797,7 +797,10 @@ #define D_SENSOR_WIEGAND_D1 "Wiegand D1" #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" +#define D_SENSOR_DETECTED "detected" // Units #define D_UNIT_AMPERE "А" From e6a79202236cf5b7061523b4a201c4078f7b1918 Mon Sep 17 00:00:00 2001 From: Adrian Scillato <35405447+ascillato@users.noreply.github.com> Date: Tue, 16 Mar 2021 14:29:18 -0300 Subject: [PATCH 56/87] Update vi_VN.h --- tasmota/language/vi_VN.h | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/tasmota/language/vi_VN.h b/tasmota/language/vi_VN.h index 40ea941d0..c04e43010 100644 --- a/tasmota/language/vi_VN.h +++ b/tasmota/language/vi_VN.h @@ -1,7 +1,7 @@ /* vi-VN.h - localization for Vietnam for Tasmota - Copyright (C) 2021 translateb by Tâm.NT + Copyright (C) 2021 Tâm.NT 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 @@ -28,7 +28,7 @@ * Use online command StateText to translate ON, OFF, HOLD and TOGGLE. * Use online command Prefix to translate cmnd, stat and tele. * - * Updated until v9.0.0.1 + * Updated until v9.3.1.1 \*********************************************************************/ //#define LANGUAGE_MODULE_NAME // Enable to display "Module Generic" (ie Spanish), Disable to display "Generic Module" (ie English) @@ -797,7 +797,10 @@ #define D_SENSOR_WIEGAND_D1 "Wiegand D1" #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" +#define D_SENSOR_DETECTED "detected" // Units #define D_UNIT_AMPERE "A" From 839d13adf653269dd26ffa99f4d9ffe602070d2d Mon Sep 17 00:00:00 2001 From: Adrian Scillato <35405447+ascillato@users.noreply.github.com> Date: Tue, 16 Mar 2021 14:31:23 -0300 Subject: [PATCH 57/87] Update zh_CN.h --- tasmota/language/zh_CN.h | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/tasmota/language/zh_CN.h b/tasmota/language/zh_CN.h index 73f9424ed..a7e1847ca 100644 --- a/tasmota/language/zh_CN.h +++ b/tasmota/language/zh_CN.h @@ -1,7 +1,7 @@ /* zh-CN.h - localization for Chinese (Simplified) - China for Tasmota - Copyright (C) 2021 Theo Arends (translated by killadm) + Copyright (C) 2021 Killadm 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 @@ -28,7 +28,7 @@ * Use online command StateText to translate ON, OFF, HOLD and TOGGLE. * Use online command Prefix to translate cmnd, stat and tele. * - * Updated until v5.14.0b + * Updated until v9.3.1.1 \*********************************************************************/ //#define LANGUAGE_MODULE_NAME // Enable to display "Module Generic" (ie Spanish), Disable to display "Generic Module" (ie English) @@ -797,7 +797,10 @@ #define D_SENSOR_WIEGAND_D1 "Wiegand D1" #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" +#define D_SENSOR_DETECTED "detected" // Units #define D_UNIT_AMPERE "安" From 17e096cb7ea4beab6c817a7ecec3ed5a394bf3fa Mon Sep 17 00:00:00 2001 From: Adrian Scillato <35405447+ascillato@users.noreply.github.com> Date: Tue, 16 Mar 2021 14:33:15 -0300 Subject: [PATCH 58/87] Update zh_TW.h --- tasmota/language/zh_TW.h | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/tasmota/language/zh_TW.h b/tasmota/language/zh_TW.h index 8a759f0eb..13cc6e858 100644 --- a/tasmota/language/zh_TW.h +++ b/tasmota/language/zh_TW.h @@ -1,7 +1,7 @@ /* zh-TW.h - localization for Chinese (Traditional) - Taiwan for Tasmota - Copyright (C) 2021 Theo Arends (translated by dannydu) + Copyright (C) 2021 Dannydu 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 @@ -28,7 +28,7 @@ * Use online command StateText to translate ON, OFF, HOLD and TOGGLE. * Use online command Prefix to translate cmnd, stat and tele. * - * Updated until v5.12.0d + * Updated until v9.3.1.1 \*********************************************************************/ //#define LANGUAGE_MODULE_NAME // Enable to display "Module Generic" (ie Spanish), Disable to display "Generic Module" (ie English) @@ -797,7 +797,10 @@ #define D_SENSOR_WIEGAND_D1 "Wiegand D1" #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" +#define D_SENSOR_DETECTED "detected" // Units #define D_UNIT_AMPERE "安培" From eb8d5b74392d792f0e6c1ee895ab0c7a03e958d7 Mon Sep 17 00:00:00 2001 From: Adrian Scillato <35405447+ascillato@users.noreply.github.com> Date: Tue, 16 Mar 2021 14:57:47 -0300 Subject: [PATCH 59/87] Update pt_BR.h --- tasmota/language/pt_BR.h | 1 + 1 file changed, 1 insertion(+) diff --git a/tasmota/language/pt_BR.h b/tasmota/language/pt_BR.h index e0fbd7250..a2929b529 100644 --- a/tasmota/language/pt_BR.h +++ b/tasmota/language/pt_BR.h @@ -797,6 +797,7 @@ #define D_SENSOR_WIEGAND_D1 "Wiegand D1" #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 "Mudança de endereço para" #define D_OUT_OF_RANGE "Fora de Alcance" #define D_SENSOR_DETECTED "detectou" From b2178bc3721a307131c3f4e9628e2ef3374ab0b4 Mon Sep 17 00:00:00 2001 From: sle Date: Tue, 16 Mar 2021 18:58:31 +0100 Subject: [PATCH 60/87] support now several key pad strokes as one tag switchable by SetOption124 and added a hardware emulation for Wiegand reader ( will be only compiled in a debug mode) --- tasmota/settings.h | 2 +- tasmota/xsns_82_wiegand.ino | 321 ++++++++++++++++++++++++++++++------ 2 files changed, 267 insertions(+), 56 deletions(-) diff --git a/tasmota/settings.h b/tasmota/settings.h index 25b0145da..7dd8dbc29 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 spare10 : 1; // bit 10 + 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 spare11 : 1; // bit 11 uint32_t spare12 : 1; // bit 12 uint32_t spare13 : 1; // bit 13 diff --git a/tasmota/xsns_82_wiegand.ino b/tasmota/xsns_82_wiegand.ino index 4fff9c5d1..70f87b9c8 100644 --- a/tasmota/xsns_82_wiegand.ino +++ b/tasmota/xsns_82_wiegand.ino @@ -41,29 +41,47 @@ * - fix for #11047 Wiegand 26/34 missed some key press if they are press at normal speed * - removed testing code for tests without attached hardware * - added SetOption123 0-Wiegand UID decimal (default) 1-Wiegand UID hexadecimal - * - added SetOption124 0-Keypad every key a single tag (default) 1-all keys up to ending char (#) send as one tag + * - 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 \*********************************************************************************************/ -#warning **** Wiegand interface enabled **** +#pragma message("**** Wiegand interface enabled ****") #define XSNS_82 82 #define WIEGAND_CODE_GAP_FACTOR 3 // Gap between 2 complete RFID codes send by the device. (WIEGAND_CODE_GAP_FACTOR * bitTime) to detect the end of a code -#define WIEGAND_BIT_TIME_DEFAULT 1250 // period time of one bit (impluse + impulse_gap time) 1250µs measured by oscilloscope on my RFID Reader -#define WIEGAND_RFID_ARRAY_SIZE 5 // storage of rfids found between 2 calls of FUNC_EVERY_100_MSECOND +#define WIEGAND_BIT_TIME_DEFAULT 1250 // period time (µs) of one bit (impluse + impulse_gap time) 1250µs measured by oscilloscope on my RFID Reader +#define WIEGAND_RFID_ARRAY_SIZE 11 // storage of rfids found between 2 calls of FUNC_EVERY_100_MSECOND #define WIEGAND_OPTION_HEX 123 // Index of option to switch output between hex (1) an decimal (0) (default) +#define WIEGAND_OPTION_HEX_POSTFIX "h" // will be added after UID output nothing = "" +#define WIEGAND_OPTION_KEYPAD_TO_TAG 124 //Index of option to switch output of key pad strokes between every single stroke one single char (0) (default) + // or all strokes until detecting ending char (WIEGAND_OPTION_KEYPAD_END_CHAR) as one tag (1) -// using #define will save some space in the final code -// DEV_WIEGAND_TEST_MODE 2 : testing with hardware correctly connected. #define DEV_WIEGAND_TEST_MODE 0 +// using #define will save some space in the final code +// DEV_WIEGAND_TEST_MODE 1 : Use only without Wiegand reader device attache. On a second ESP to simulate reader output! +// DEV_WIEGAND_TEST_MODE 2 : testing with hardware correctly connected. #ifdef DEV_WIEGAND_TEST_MODE #if (DEV_WIEGAND_TEST_MODE==0) #elif (DEV_WIEGAND_TEST_MODE==1) - #warning "(no longer available) Wiegand Interface compiled with 'DEV_WIEGAND_TEST_MODE' 1 (Random RFID)" + #pragma message("\nWiegand Interface code generator (testing purpose only!) compiled with 'DEV_WIEGAND_TEST_MODE' 1 \nUse only on esp WITHOUT Wiegand reader hardware attached! GPIOS will be configured as OUTPUT!" ) + // use on own risk for testing purpose only. + // please don't attach your reader to the ESP when you use this option. The GPIOS will be defined as OUTPUT + // the interrupts will be enabled and normally recognize the generated code, that's the idea behind for testing. + // Commands: + // WieBitTime [time] : get or set the bit impuls length + // WieInterBitTime [time]: get or set the length of the gap between 2 bits + // WieTagGap [tagGap]: get or set the current used gap time between 2 tags send in µs minimal WIEGAND_BIT_TIME_DEFAULT µs default WIEGAND_BIT_TIME_DEFAULT * WIEGAND_CODE_GAP_FACTOR + // WieTagSize [tagsize]: get or set the tagsize (4,8,24,26,32,34) default 26. + // WieTag [tag]: get or set the current used tag. For tagsize 4,8 only one char will be used. + // WieSend [tag[:tagsize];tag[:tagsize];...] : Generate the current Tag with current TagSize to GPIOs if the paramters are used + // tags and tagsize from commandline are used as current values. If tagsize is omitted always last value will be used + // WieSend 4:4;5:8; will send 4 in 4 bit mode and 5 in 8 bit mode with a pause of current TagGab between the chars + // WieSend will send the last used tag with last used tagsize #elif (DEV_WIEGAND_TEST_MODE==2) - #warning "Wiegand Interface compiled with 'DEV_WIEGAND_TEST_MODE' 2 (Hardware connected)" + #pragma message("\nWiegand Interface compiled with 'DEV_WIEGAND_TEST_MODE' 2 (Hardware connected)") #else - #warning "Wiegand Interface compiled with unknown mode" + #pragma message("\nWiegand Interface compiled with unknown mode") #endif #endif @@ -71,7 +89,6 @@ typedef struct rfid_store { uint64_t RFID; uint16_t bitCount; } RFID_store; class Wiegand { public: - Wiegand(void); void Init(void); void ScanForTag(void); @@ -81,21 +98,24 @@ class Wiegand { bool isInit = false; - private: - //uint64_t HexStringToDec(uint64_t); + #if (DEV_WIEGAND_TEST_MODE!=1) + private: + #endif //(DEV_WIEGAND_TEST_MODE==1) uint64_t CheckAndConvertRfid(uint64_t,uint16_t); - char translateEnterEscapeKeyPress(char); uint8_t CalculateParities(uint64_t, int); 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 uint64_t rfid; - uint8_t tagSize; - char outFormat; + uint32_t tagSize; + const char* outFormat; + uint64_t mqttRFIDKeypadBuffer; + uint64_t webRFIDKeypadBuffer; static volatile uint64_t rfidBuffer; static volatile uint16_t bitCount; @@ -107,7 +127,6 @@ class Wiegand { static volatile bool CodeComplete; static volatile RFID_store rfid_found[]; static volatile int currentFoundRFIDcount; - }; Wiegand* oWiegand = new Wiegand(); @@ -141,7 +160,9 @@ Wiegand::Wiegand() { rfid_found[i].RFID=0; rfid_found[i].bitCount=0; } - outFormat='u'; // standard output format decimal + outFormat="u"; // standard output format decimal + mqttRFIDKeypadBuffer = 0; + webRFIDKeypadBuffer = 0; } void ICACHE_RAM_ATTR Wiegand::handleD1Interrupt() { // Receive a 1 bit. (D0=high & D1=low) @@ -202,6 +223,10 @@ void Wiegand::Init() { #endif // DEV_WIEGAND_TEST_MODE>0 pinMode(Pin(GPIO_WIEGAND_D0), INPUT_PULLUP); pinMode(Pin(GPIO_WIEGAND_D1), INPUT_PULLUP); +#if (DEV_WIEGAND_TEST_MODE==1) // overwrite the setting + pinMode(Pin(GPIO_WIEGAND_D0), OUTPUT); + pinMode(Pin(GPIO_WIEGAND_D1), OUTPUT); +#endif //(DEV_WIEGAND_TEST_MODE==1) attachInterrupt(Pin(GPIO_WIEGAND_D0), handleD0Interrupt, FALLING); attachInterrupt(Pin(GPIO_WIEGAND_D1), handleD1Interrupt, FALLING); isInit = true; // Helps to run only if correctly setup @@ -286,27 +311,8 @@ uint8_t Wiegand::CalculateParities(uint64_t tagWithoutParities, int tag_size=26) return retValue; } -char Wiegand::translateEnterEscapeKeyPress(char oKeyPressed) { - switch(oKeyPressed) { - case 0x0b: // 11 or * key - return 0x0d; // 13 or ASCII ENTER - - case 0x0a: // 10 or # key - return 0x1b; // 27 or ASCII ESCAPE - - default: - return oKeyPressed; - } -} - bool Wiegand::WiegandConversion (uint64_t rfidBuffer, uint16_t bitCount) { bool bRet = false; - // unsigned long nowTick = micros(); - // Add a maximum wait time for new bits - // unsigned long diffTicks = nowTick - lastFoundTime; - // unsigned long inter_code_gap = WIEGAND_CODE_GAP_FACTOR * bitTime; - // if ((diffTicks > inter_code_gap) && (diffTicks >= 1000000 )) { // Max. 4-8 secs between 2 bits comming in. depends on micros() resolution - #if (DEV_WIEGAND_TEST_MODE)>0 AddLog(LOG_LEVEL_INFO, PSTR("WIE: Raw tag %llu, Bit count %u"), rfidBuffer, bitCount); #endif // DEV_WIEGAND_TEST_MODE>0 @@ -318,7 +324,7 @@ bool Wiegand::WiegandConversion (uint64_t rfidBuffer, uint16_t bitCount) { } else if (4 == bitCount) { // 4-bit Wiegand codes for keypads - rfid = (int)translateEnterEscapeKeyPress(rfidBuffer & 0x0000000F); + rfid = (int)(rfidBuffer & 0x0000000F); tagSize = bitCount; bRet = true; } @@ -329,16 +335,15 @@ bool Wiegand::WiegandConversion (uint64_t rfidBuffer, uint16_t bitCount) { char highNibble = (rfidBuffer & 0xf0) >>4; char lowNibble = (rfidBuffer & 0x0f); if (lowNibble == (~highNibble & 0x0f)) { // Check if low nibble matches the "NOT" of high nibble. - rfid = (int)translateEnterEscapeKeyPress(lowNibble); + rfid = (int)(lowNibble); bRet = true; } else { - // lastFoundTime = nowTick; bRet = false; } tagSize = bitCount; } else { // Time reached but unknown bitCount, clear and start again - // lastFoundTime = nowTick; + tagSize = 0; bRet = false; } #if (DEV_WIEGAND_TEST_MODE)>0 @@ -349,8 +354,31 @@ bool Wiegand::WiegandConversion (uint64_t rfidBuffer, uint16_t bitCount) { void Wiegand::setOutputFormat(void) { - if (GetOption(WIEGAND_OPTION_HEX) == 0) { outFormat = 'u'; } - else { outFormat = 'X'; } + if (GetOption(WIEGAND_OPTION_HEX) == 0) { outFormat = "u"; } + else { outFormat = "X" WIEGAND_OPTION_HEX_POSTFIX ; } +} + +void Wiegand::HandleKeyPad(void) { // will be called if a valid key pad input was recognized + if (GetOption(WIEGAND_OPTION_KEYPAD_TO_TAG) == 0) { // handle all key pad inputs as ONE Tag until # is recognized + if ( (tagSize == 4) || (tagSize == 8) ) { + //only handle Keypad strokes if it is requested + if (rfid >= 0x0a) { // # * as end of input detected -> all key values which are larger than 9 + rfid = mqttRFIDKeypadBuffer; // original tagsize of 4 or 8 will be kept. + webRFIDKeypadBuffer = 0; // can be resetted, because now rfid > 0 will be used at web interface + mqttRFIDKeypadBuffer = 0; + } + else { + mqttRFIDKeypadBuffer = (mqttRFIDKeypadBuffer*10)+rfid; //left shift + new key + webRFIDKeypadBuffer = mqttRFIDKeypadBuffer; // visualising the current typed keys + rfid = 0; + tagSize = 0; + } + } + else { //it's not a key pad entry, so another key come in, we will reset the buffer, if it is not finished yet + webRFIDKeypadBuffer = 0; + mqttRFIDKeypadBuffer = 0; + } + } } void Wiegand::ScanForTag() { @@ -364,7 +392,7 @@ void Wiegand::ScanForTag() { // format MQTT output setOutputFormat(); char sFormat[50]; - snprintf( sFormat, 50, PSTR(",\"Wiegand\":{\"UID\":%%0ll%c,\"" D_JSON_SIZE "\":%%%c}}"), outFormat, outFormat); + snprintf( sFormat, 50, PSTR(",\"Wiegand\":{\"UID\":%%0ll%s,\"" D_JSON_SIZE "\":%%%s}}"), outFormat, outFormat); for (int i= 0; i < WIEGAND_RFID_ARRAY_SIZE; i++) { @@ -375,11 +403,14 @@ void Wiegand::ScanForTag() { AddLog(LOG_LEVEL_INFO, PSTR("WIE: Previous tag %llu"), oldTag); #endif // DEV_WIEGAND_TEST_MODE>0 if (validKey) { // Only in case of valid key do action. Issue#10585 - if (oldTag == rfid) { - AddLog(LOG_LEVEL_DEBUG, PSTR("WIE: Old tag")); + HandleKeyPad(); //support one tag for multi key input + if (tagSize>0) { //do output only for rfids which are complete + if (oldTag == rfid) { + AddLog(LOG_LEVEL_DEBUG, PSTR("WIE: Old tag")); + } + ResponseTime_P(sFormat, rfid, tagSize); + MqttPublishTeleSensor(); } - ResponseTime_P(sFormat, rfid,tagSize); - MqttPublishTeleSensor(); } rfid_found[i].RFID=0; rfid_found[i].bitCount=0; @@ -399,17 +430,192 @@ void Wiegand::ScanForTag() { #ifdef USE_WEBSERVER void Wiegand::Show(void) { - setOutputFormat(); - char sFormat [30]; - snprintf( sFormat, 30,PSTR("{s}Wiegand UID{m}%%ll%c {e}"), outFormat); - WSContentSend_PD(sFormat, rfid); - //WSContentSend_PD(PSTR("{s}Wiegand UID{m}%llX {e}"), rfid); -#if (DEV_WIEGAND_TEST_MODE)>0 - AddLog(LOG_LEVEL_INFO, PSTR("WIE: Tag %llu, Bits %u"), rfid, bitCount); -#endif // DEV_WIEGAND_TEST_MODE>0 + setOutputFormat(); + char sFormat [30]; + snprintf( sFormat, 30,PSTR("{s}Wiegand UID{m}%%ll%s {e}"), outFormat); + if (tagSize>0) { WSContentSend_PD(sFormat, rfid); } + else { WSContentSend_PD(sFormat, webRFIDKeypadBuffer); } + + #if (DEV_WIEGAND_TEST_MODE)>0 + AddLog(LOG_LEVEL_INFO, PSTR("WIE: Tag %llu, Bits %u"), rfid, bitCount); + #endif // DEV_WIEGAND_TEST_MODE>0 + } #endif // USE_WEBSERVER +#if (DEV_WIEGAND_TEST_MODE==1) + void CmndTag(void); + void CmndTagSize(void); + void CmndTagGap(void); + void CmndTimeReset(void); + void CmndAllReset(void); + void CmndSend(void); + void CmndBitTime(void); + void CmndInterBitTime(void); + unsigned int setTagSize( char *); + unsigned int setTag ( char * ); + void sendBit(unsigned int b); + void sendTag(uint32_t Tag, uint32_t TagSize); + + uint32_t currTag = 0; + uint32_t currTagSize = 26; //default value 26 Wiegand + uint32_t currBitTime=(WIEGAND_BIT_TIME_DEFAULT/10); //length of the bit impluse in µs + uint32_t currInterBitTime = ((WIEGAND_BIT_TIME_DEFAULT/10)*9); //time to wait before next bit is send in µs + uint32_t currTagGabTime = (WIEGAND_BIT_TIME_DEFAULT * WIEGAND_CODE_GAP_FACTOR) ; //time to wait before next tag is send in µs + + + void CmndTag(void){ + if (XdrvMailbox.data_len > 0) { + currTag= strtoul(XdrvMailbox.data, nullptr, 0); + } + ResponseCmndNumber(currTag); + } + void CmndTagSize(void){ + if (XdrvMailbox.data_len > 0) { + currTagSize = setTagSize(XdrvMailbox.data); + } + ResponseCmndNumber(currTagSize); + } + void CmndTagGap(void){ + if (XdrvMailbox.data_len > 0) { + currTagGabTime = strtoul(XdrvMailbox.data, nullptr, 0); + if (currTagGabTime < (currBitTime+currInterBitTime) ) // doesn't make sense + { currTagGabTime = (currBitTime+currInterBitTime) * WIEGAND_CODE_GAP_FACTOR; } + } + ResponseCmndNumber(currTagGabTime); + } + void CmndBitTime(void){ + if (XdrvMailbox.data_len > 0) { + uint32_t newBitTime = strtoul(XdrvMailbox.data, nullptr, 0); + if ( (newBitTime >= 100) && (newBitTime <= 500000) ) // accept only values between 100µs and 5s + { currBitTime = newBitTime; } + } + ResponseCmndNumber(currBitTime); + } + void CmndInterBitTime(void){ + if (XdrvMailbox.data_len > 0) { + uint32_t newInterBitTime = strtoul(XdrvMailbox.data, nullptr, 0); + if ( (newInterBitTime >= currBitTime) && (newInterBitTime <= (100 * currBitTime)) ) // accept only values between 100µs and 5s + { currInterBitTime = newInterBitTime; } + } + ResponseCmndNumber(currInterBitTime); + } + void CmndTimeReset(void){ + currBitTime=(WIEGAND_BIT_TIME_DEFAULT/10); + currInterBitTime = ((WIEGAND_BIT_TIME_DEFAULT/10)*9); + currTagGabTime = (WIEGAND_BIT_TIME_DEFAULT * WIEGAND_CODE_GAP_FACTOR) ; + ResponseCmndChar_P(PSTR("All timings reset to default!")); + } + void CmndAllReset(void){ + CmndTimeReset(); + currTagSize = 26; + ResponseCmndChar_P(PSTR("All timings and tag size reset to default")); + } + void CmndSend(void){ + if (XdrvMailbox.data_len > 0) { // parameter [tag[:tagsize];tag[:tagsize];...] + char *parameter = strtok(XdrvMailbox.data, ";"); + while (parameter != nullptr) { + char* pTagSize = strchr(parameter,':'); // find optional tagsizes + if (pTagSize != 0) { // 2 parameters found tag:tagsize + *pTagSize = 0; //replace separator ':' by \0 string end + currTag = setTag(parameter); // is now ending before tagsize + pTagSize++; //set the starting char of tagsize correctly + currTagSize = setTagSize(pTagSize); + ResponseCmndChar(pTagSize); + } + else {//only one parameter (tag) found + currTag = setTag(parameter); + } + ResponseCmndChar(parameter); + sendTag(currTag, currTagSize); + ResponseCmndNumber(currTag); + parameter = strtok(nullptr, ";"); + } + } + else { // send last used values again + sendTag(currTag, currTagSize); + ResponseCmndNumber(currTag); + } + } + unsigned int setTag ( char * newTag) { + unsigned int retValue = strtoul(newTag, nullptr, 0); + if ( (currTagSize == 4) || (currTagSize == 8) ) //key pad input simulation requested + { retValue &= 0x0F; } + return retValue; + } + unsigned int setTagSize ( char * newTagSize) { + unsigned int retValue = strtoul(newTagSize, nullptr, 0); + // accept only supported TagSize + if ( retValue <= 4) { retValue = 4;} + else if ( retValue <= 8) { retValue = 8;} + else if ( retValue <= 24) { retValue = 24;} + else if ( retValue <= 26) { retValue = 26;} + else if ( retValue <= 32) { retValue = 32;} + else if ( retValue <= 34) { retValue = 34;} + else { retValue = 26;} //default value + return retValue; + } + void sendBit(unsigned int b) { + int sel = (b == 0) ? Pin(GPIO_WIEGAND_D0) : Pin(GPIO_WIEGAND_D1); + digitalWrite(sel, 0); + delayMicroseconds(currBitTime); // bit impuls time + digitalWrite(sel, 1); + delayMicroseconds(currBitTime+currInterBitTime); // bit + inter bit gap time + } + void sendPlainTag( uint32_t pTag, uint32_t pTagSize){ // send tag without parity + for (int i=1; i<=pTagSize; ++i) + { + sendBit((pTag >> (pTagSize-i)) & 1); + } + } + void sendTag(uint32_t Tag, uint32_t TagSize) { + // TagSize is the requested output tagSize. means b.e. 24 bit == 24 Tag without parity 26 bit = 24 bit with parity bits + // supported tag sizes 4/8 for key pad simulation 24/26 and 32/34 for RFID tags + switch (TagSize){ + case 24: + case 32: + case 4: + sendPlainTag( Tag, TagSize); + break; + case 26: + case 34: + uint8_t parity; + parity = oWiegand->CalculateParities(Tag, TagSize); + sendBit(parity & 0x01); //even parity (starting parity) + sendPlainTag( Tag, TagSize-2); + sendBit(parity & 0x80); //odd parity (ending parity) + break; + case 8: // high nibble is ~ low nibble + Tag = Tag & 0x0F; // low nibble in case of more the one char input it will be cut here + Tag = Tag | ((~Tag) << 4); + sendPlainTag ( Tag, TagSize); + break; + } + //delay to simulate end of tag + delayMicroseconds(currTagGabTime); // inter code gap + return; +} +const char kWiegandCommands[] PROGMEM = "Wie|" // No prefix + "Tag|" + "TagSize|" + "TagGap|" + "BitTime|" + "InterBitTime|" + "TimeReset|" + "AllReset|" + "Send"; + + void (* const WiegandCommand[])(void) PROGMEM = { + &CmndTag, + &CmndTagSize, + &CmndTagGap, + &CmndBitTime, + &CmndInterBitTime, + &CmndTimeReset, + &CmndAllReset, + &CmndSend + }; +#endif //(DEV_WIEGAND_TEST_MODE==1) /*********************************************************************************************\ * Interface \*********************************************************************************************/ @@ -430,6 +636,11 @@ bool Xsns82(byte function) { oWiegand->Show(); break; #endif // USE_WEBSERVER +#if (DEV_WIEGAND_TEST_MODE==1) + case FUNC_COMMAND: + result = DecodeCommand(kWiegandCommands, WiegandCommand); + break; +#endif //(DEV_WIEGAND_TEST_MODE==1) } } return result; From 2dfec2ec4d8c0bb4bd806cc2bf9a57f56b0faf60 Mon Sep 17 00:00:00 2001 From: Barbudor Date: Tue, 16 Mar 2021 22:28:39 +0100 Subject: [PATCH 61/87] don't keep 0xFFFFFFFF as a valid addres in case of DNS fail --- tasmota/support.ino | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/tasmota/support.ino b/tasmota/support.ino index b1653ff2a..ff6a0aecf 100644 --- a/tasmota/support.ino +++ b/tasmota/support.ino @@ -2137,8 +2137,11 @@ void SyslogAsync(bool refresh) { if (mxtime > 0) { uint32_t current_hash = GetHash(SettingsText(SET_SYSLOG_HOST), strlen(SettingsText(SET_SYSLOG_HOST))); if (syslog_host_hash != current_hash) { + IPAddress temp_syslog_host_addr; + int ret = WiFi.hostByName(SettingsText(SET_SYSLOG_HOST), temp_syslog_host_addr); // If sleep enabled this might result in exception so try to do it once using hash + if (!ret) return; syslog_host_hash = current_hash; - WiFi.hostByName(SettingsText(SET_SYSLOG_HOST), syslog_host_addr); // If sleep enabled this might result in exception so try to do it once using hash + syslog_host_addr = temp_syslog_host_addr; } if (!PortUdp.beginPacket(syslog_host_addr, Settings.syslog_port)) { TasmotaGlobal.syslog_level = 0; From 6a3d6c7e5e030088604338256731dfac513aa4c4 Mon Sep 17 00:00:00 2001 From: Simon Ratcliffe Date: Wed, 17 Mar 2021 14:28:04 +1100 Subject: [PATCH 62/87] Fix MCP2300x initialisation. --- tasmota/xsns_29_mcp230xx.ino | 44 ++++++++++++++++++++---------------- 1 file changed, 25 insertions(+), 19 deletions(-) diff --git a/tasmota/xsns_29_mcp230xx.ino b/tasmota/xsns_29_mcp230xx.ino index 3e4dd9dee..c1e7a08bb 100644 --- a/tasmota/xsns_29_mcp230xx.ino +++ b/tasmota/xsns_29_mcp230xx.ino @@ -45,7 +45,7 @@ uint8_t MCP230xx_GPIO = 0x09; uint8_t mcp230xx_type = 0; uint8_t mcp230xx_pincount = 0; -uint8_t mcp230xx_oldoutpincount = 0; +uint8_t mcp230xx_outpincount = 0; #ifdef USE_MCP230xx_OUTPUT uint8_t mcp230xx_outpinmapping[16]; #endif @@ -146,6 +146,18 @@ uint8_t MCP230xx_readGPIO(uint8_t port) { void MCP230xx_ApplySettings(void) { +#ifdef USE_MCP230xx_OUTPUT + TasmotaGlobal.devices_present -= mcp230xx_outpincount; + mcp230xx_outpincount = 0; + for (uint32_t idx = 0; idx < mcp230xx_pincount; idx++) { + if (Settings.mcp230xx_config[idx].pinmode >= 5) { + mcp230xx_outpinmapping[mcp230xx_outpincount] = idx; + mcp230xx_outpincount++; + } + int_millis[idx]=millis(); + } + TasmotaGlobal.devices_present += mcp230xx_outpincount; +#endif // USE_MCP230xx_OUTPUT uint8_t int_en = 0; for (uint32_t mcp230xx_port = 0; mcp230xx_port < mcp230xx_type; mcp230xx_port++) { uint8_t reg_gppu = 0; @@ -191,23 +203,17 @@ void MCP230xx_ApplySettings(void) } I2cWrite8(USE_MCP230xx_ADDR, MCP230xx_GPPU+mcp230xx_port, reg_gppu); I2cWrite8(USE_MCP230xx_ADDR, MCP230xx_GPINTEN+mcp230xx_port, reg_gpinten); +#ifdef USE_MCP230xx_OUTPUT I2cWrite8(USE_MCP230xx_ADDR, MCP230xx_IODIR+mcp230xx_port, reg_iodir); -#ifdef USE_MCP230xx_OUTPUT - I2cWrite8(USE_MCP230xx_ADDR, MCP230xx_GPIO+mcp230xx_port, reg_portpins); -#endif // USE_MCP230xx_OUTPUT - } -#ifdef USE_MCP230xx_OUTPUT - TasmotaGlobal.devices_present -= mcp230xx_oldoutpincount; - mcp230xx_oldoutpincount = 0; - for (uint32_t idx=0;idx= 5) { - mcp230xx_outpinmapping[mcp230xx_oldoutpincount] = idx; - mcp230xx_oldoutpincount++; + for (uint32_t idx = 0; idx < mcp230xx_outpincount; idx++) { + if (mcp230xx_port ? mcp230xx_outpinmapping[idx] > 7 : mcp230xx_outpinmapping[idx] < 8) { + uint8_t relay_no = TasmotaGlobal.devices_present - mcp230xx_outpincount + idx + 1; + ExecuteCommandPower(relay_no, (reg_portpins >> (mcp230xx_outpinmapping[idx] & 7)) & 1, SRC_IGNORE); + } } - int_millis[idx]=millis(); - } - TasmotaGlobal.devices_present += mcp230xx_oldoutpincount; + //I2cWrite8(USE_MCP230xx_ADDR, MCP230xx_GPIO+mcp230xx_port, reg_portpins); #endif // USE_MCP230xx_OUTPUT + } mcp230xx_int_en = int_en; MCP230xx_CheckForIntCounter(); // update register on whether or not we should be counting interrupts MCP230xx_CheckForIntRetainer(); // update register on whether or not we should be retaining interrupt events for teleperiod @@ -651,7 +657,7 @@ bool MCP230xx_Command(void) for (relay_no = 0; relay_no < mcp230xx_pincount ; relay_no ++) { if ( mcp230xx_outpinmapping[relay_no] == pin) break; } - relay_no = TasmotaGlobal.devices_present - mcp230xx_oldoutpincount + relay_no +1; + relay_no = TasmotaGlobal.devices_present - mcp230xx_outpincount + relay_no +1; if ((!strcmp(ArgV(argument, 2), "ON")) || (!strcmp(ArgV(argument, 2), "1"))) { ExecuteCommandPower(relay_no, 1, SRC_IGNORE); return serviced; @@ -799,8 +805,8 @@ void MCP230xx_Interrupt_Retain_Report(void) { #ifdef USE_MCP230xx_OUTPUT void MCP230xx_SwitchRelay() { - for (uint32_t i = TasmotaGlobal.devices_present - mcp230xx_oldoutpincount; i < TasmotaGlobal.devices_present; i++) { - uint8_t pin = mcp230xx_outpinmapping[i - (TasmotaGlobal.devices_present - mcp230xx_oldoutpincount)]; + for (uint32_t i = TasmotaGlobal.devices_present - mcp230xx_outpincount; i < TasmotaGlobal.devices_present; i++) { + uint8_t pin = mcp230xx_outpinmapping[i - (TasmotaGlobal.devices_present - mcp230xx_outpincount)]; uint8_t pincmd = Settings.mcp230xx_config[pin].pinmode - 5; uint8_t relay_state = bitRead(XdrvMailbox.index, i); AddLog(LOG_LEVEL_DEBUG, PSTR("MCP: relay %d pin_no %d state %d"), i,pin, relay_state); @@ -826,7 +832,7 @@ bool Xsns29(uint8_t function) bool result = false; - if (FUNC_PRE_INIT == function) { + if (FUNC_INIT == function) { MCP230xx_Detect(); } else if (mcp230xx_type) { From 8e904eaed6143f9e3bd8fb6a9e771600957fd4e8 Mon Sep 17 00:00:00 2001 From: bovirus <1262554+bovirus@users.noreply.github.com> Date: Wed, 17 Mar 2021 09:47:42 +0100 Subject: [PATCH 63/87] Update Italian language --- tasmota/language/it_IT.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tasmota/language/it_IT.h b/tasmota/language/it_IT.h index c2251af85..b8a7d828b 100644 --- a/tasmota/language/it_IT.h +++ b/tasmota/language/it_IT.h @@ -28,11 +28,11 @@ * Use online command StateText to translate ON, OFF, HOLD and TOGGLE. * Use online command Prefix to translate cmnd, stat and tele. * - * Updated until v9.3.1.1 + * Last update - 9.3.1.1 (17.03.2021) \*********************************************************************/ #define LANGUAGE_MODULE_NAME // Enable to display "Module Generic" (ie Spanish), Disable to display "Generic Module" (ie English) - +// https://www.science.co.il/language/Locale-codes.php #define LANGUAGE_LCID 1040 // HTML (ISO 639-1) Language Code #define D_HTML_LANGUAGE "it" @@ -798,8 +798,8 @@ #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 "Cambio di indirizzo a" -#define D_OUT_OF_RANGE "Fuori dal limite" +#define D_NEW_ADDRESS "Imposta indirizzo a" +#define D_OUT_OF_RANGE "Fuori limite" #define D_SENSOR_DETECTED "rilevato" // Units From dce555cf6b219e8b1ebe1bcca71626c3e6c97f5f Mon Sep 17 00:00:00 2001 From: gemu2015 Date: Wed, 17 Mar 2021 13:30:51 +0100 Subject: [PATCH 64/87] homekit update --- tasmota/homekit.c | 347 ++++++++++++++++++----------------- tasmota/xdrv_10_scripter.ino | 51 ++++- 2 files changed, 223 insertions(+), 175 deletions(-) diff --git a/tasmota/homekit.c b/tasmota/homekit.c index 44f1dc536..5bd539ddb 100755 --- a/tasmota/homekit.c +++ b/tasmota/homekit.c @@ -59,11 +59,11 @@ extern uint32_t Ext_UpdVar(char *vname, float *fvar, uint32_t mode); #define MAX_HAP_DEFS 16 struct HAP_DESC { - char hap_name[16]; - char var_name[16]; - char var2_name[16]; - char var3_name[16]; - char var4_name[16]; + char hap_name[24]; + char var_name[12]; + char var2_name[12]; + char var3_name[12]; + char var4_name[12]; uint8_t hap_cid; uint8_t type; hap_acc_t *accessory; @@ -148,45 +148,66 @@ static int accessory_identify(hap_acc_t *ha) return HAP_SUCCESS; } +const struct HAP_CHAR_TABLE { + char stype[4]; + char ntype; + int8_t index; +} hap_rtab[] = { + {HAP_CHAR_UUID_CURRENT_TEMPERATURE,'f',0}, + {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_ON,'b',0}, + {HAP_CHAR_UUID_HUE,'f',1}, + {HAP_CHAR_UUID_SATURATION,'f',2}, + {HAP_CHAR_UUID_BRIGHTNESS,'u',3}, + {HAP_CHAR_UUID_COLOR_TEMPERATURE,'u',0}, + {HAP_CHAR_UUID_CONTACT_SENSOR_STATE,'u',0}, + {HAP_CHAR_UUID_WATTAGE,'f',0} +}; + +#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0])) + /* A dummy callback for handling a write on the "On" characteristic of Outlet. * In an actual accessory, this should control the hardware */ static int sensor_write(hap_write_data_t write_data[], int count, void *serv_priv, void *write_priv, uint32_t index) { int i, ret = HAP_SUCCESS; + float fvar; hap_write_data_t *write; for (i = 0; i < count; i++) { write = &write_data[i]; + bool found = false; const char *hcp = hap_char_get_type_uuid(write->hc); - if (!strcmp(hcp, HAP_CHAR_UUID_ON)) { - //ESP_LOGI(TAG, "Received Write. Outlet %s", write->val.b ? "On" : "Off"); - ESP_LOG_LEVEL(ESP_LOG_INFO, TAG, "Received Write. Outlet %s", write->val.b ? "On" : "Off"); - hap_char_update_val(write->hc, &(write->val)); - float fvar = write->val.b; - Ext_UpdVar(hap_devs[index].var_name, &fvar, 1); - *(write->status) = HAP_STATUS_SUCCESS; - } else if (!strcmp(hcp, HAP_CHAR_UUID_HUE)) { - hap_char_update_val(write->hc, &(write->val)); - float fvar = write->val.f; - Ext_UpdVar(hap_devs[index].var2_name, &fvar, 1); - *(write->status) = HAP_STATUS_SUCCESS; - } else if (!strcmp(hcp, HAP_CHAR_UUID_SATURATION)) { - hap_char_update_val(write->hc, &(write->val)); - float fvar = write->val.f; - Ext_UpdVar(hap_devs[index].var3_name, &fvar, 1); - *(write->status) = HAP_STATUS_SUCCESS; - } else if (!strcmp(hcp, HAP_CHAR_UUID_BRIGHTNESS)) { - hap_char_update_val(write->hc, &(write->val)); - float fvar = write->val.u; - Ext_UpdVar(hap_devs[index].var4_name, &fvar, 1); - *(write->status) = HAP_STATUS_SUCCESS; - } else { + for (uint32_t cnt = 0; cnt < ARRAY_SIZE(hap_rtab); cnt++ ) { + if (!strcmp(hcp, hap_rtab[cnt].stype)) { + hap_char_update_val(write->hc, &(write->val)); + switch (hap_rtab[cnt].ntype) { + case 'f': fvar = write->val.f; break; + case 'u': fvar = write->val.u; break; + case 'b': fvar = write->val.b; break; + } + switch (hap_rtab[cnt].index) { + case 0: Ext_UpdVar(hap_devs[index].var_name, &fvar, 1);break; + case 1: Ext_UpdVar(hap_devs[index].var2_name, &fvar, 1);break; + case 2: Ext_UpdVar(hap_devs[index].var3_name, &fvar, 1);break; + case 3: Ext_UpdVar(hap_devs[index].var4_name, &fvar, 1);break; + } + *(write->status) = HAP_STATUS_SUCCESS; + found = true; + break; + } + } + if (!found) { *(write->status) = HAP_STATUS_RES_ABSENT; } + } return ret; } - // common read routine static int sensor_read(hap_char_t *hc, hap_status_t *status_code, void *serv_priv, void *read_priv, uint32_t index) { hap_val_t new_val; @@ -200,61 +221,123 @@ static int sensor_read(hap_char_t *hc, hap_status_t *status_code, void *serv_pri printf("read values %s\n", hcp ); - if (!strcmp(hcp, HAP_CHAR_UUID_CURRENT_TEMPERATURE) - || !strcmp(hcp, HAP_CHAR_UUID_CURRENT_RELATIVE_HUMIDITY) - || !strcmp(hcp, HAP_CHAR_UUID_CURRENT_AMBIENT_LIGHT_LEVEL) - ) { - - Ext_UpdVar(hap_devs[index].var_name, &fvar, 0); - new_val.f = fvar; + for (uint32_t cnt = 0; cnt < ARRAY_SIZE(hap_rtab); cnt++ ) { + if (!strcmp(hcp, hap_rtab[cnt].stype)) { + switch (hap_rtab[cnt].index) { + case 0: Ext_UpdVar(hap_devs[index].var_name, &fvar, 0);break; + case 1: Ext_UpdVar(hap_devs[index].var2_name, &fvar, 0);break; + case 2: Ext_UpdVar(hap_devs[index].var3_name, &fvar, 0);break; + case 3: Ext_UpdVar(hap_devs[index].var4_name, &fvar, 0);break; + } + switch (hap_rtab[cnt].ntype) { + case 'f': new_val.f = fvar; break; + case 'u': new_val.u = fvar; break; + case 'b': new_val.b = fvar; break; + } hap_char_update_val(hc, &new_val); *status_code = HAP_STATUS_SUCCESS; - } - if (!strcmp(hcp, HAP_CHAR_UUID_ON)) { - Ext_UpdVar(hap_devs[index].var_name, &fvar, 0); - new_val.b = fvar; - hap_char_update_val(hc, &new_val); - *status_code = HAP_STATUS_SUCCESS; - } - if (!strcmp(hcp, HAP_CHAR_UUID_HUE)) { - Ext_UpdVar(hap_devs[index].var2_name, &fvar, 0); - new_val.f = fvar; - hap_char_update_val(hc, &new_val); - *status_code = HAP_STATUS_SUCCESS; - } - if (!strcmp(hcp, HAP_CHAR_UUID_SATURATION)) { - Ext_UpdVar(hap_devs[index].var3_name, &fvar, 0); - new_val.f = fvar; - hap_char_update_val(hc, &new_val); - *status_code = HAP_STATUS_SUCCESS; - } - if (!strcmp(hcp, HAP_CHAR_UUID_BRIGHTNESS)) { - Ext_UpdVar(hap_devs[index].var4_name, &fvar, 0); - new_val.u = fvar; - hap_char_update_val(hc, &new_val); - *status_code = HAP_STATUS_SUCCESS; - } - if (!strcmp(hcp, HAP_CHAR_UUID_BATTERY_LEVEL)) { - Ext_UpdVar(hap_devs[index].var_name, &fvar, 0); - new_val.u = fvar; - hap_char_update_val(hc, &new_val); - *status_code = HAP_STATUS_SUCCESS; - } - if (!strcmp(hcp, HAP_CHAR_UUID_STATUS_LOW_BATTERY)) { - Ext_UpdVar(hap_devs[index].var2_name, &fvar, 0); - new_val.u = fvar; - hap_char_update_val(hc, &new_val); - *status_code = HAP_STATUS_SUCCESS; - } - if (!strcmp(hcp, HAP_CHAR_UUID_CHARGING_STATE)) { - Ext_UpdVar(hap_devs[index].var3_name, &fvar, 0); - new_val.u = fvar; - hap_char_update_val(hc, &new_val); - *status_code = HAP_STATUS_SUCCESS; + } } return HAP_SUCCESS; } +// update values every 500 ms +void hap_update_from_vars(void) { + float fvar; + hap_char_t *hc; + hap_val_t new_val; + for (uint32_t cnt = 0; cnt < hk_services; cnt++) { + switch (hap_devs[cnt].hap_cid) { + case HAP_CID_SENSOR: + switch (hap_devs[cnt].type) { + case 0: + hc = hap_serv_get_char_by_uuid(hap_devs[cnt].service, HAP_CHAR_UUID_CURRENT_TEMPERATURE); + if (Ext_UpdVar(hap_devs[cnt].var_name, &fvar, 0)) { + new_val.f = fvar; + hap_char_update_val(hc, &new_val); + } + break; + case 1: + hc = hap_serv_get_char_by_uuid(hap_devs[cnt].service, HAP_CHAR_UUID_CURRENT_RELATIVE_HUMIDITY); + if (Ext_UpdVar(hap_devs[cnt].var_name, &fvar, 0)) { + new_val.f = fvar; + hap_char_update_val(hc, &new_val); + } + break; + case 2: + hc = hap_serv_get_char_by_uuid(hap_devs[cnt].service, HAP_CHAR_UUID_CURRENT_AMBIENT_LIGHT_LEVEL); + if (Ext_UpdVar(hap_devs[cnt].var_name, &fvar, 0)) { + new_val.f = fvar; + hap_char_update_val(hc, &new_val); + } + break; + case 3: + hc = hap_serv_get_char_by_uuid(hap_devs[cnt].service, HAP_CHAR_UUID_BATTERY_LEVEL); + if (Ext_UpdVar(hap_devs[cnt].var_name, &fvar, 0)) { + 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); + if (Ext_UpdVar(hap_devs[cnt].var2_name, &fvar, 0)) { + 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); + if (Ext_UpdVar(hap_devs[cnt].var3_name, &fvar, 0)) { + new_val.u = fvar; + hap_char_update_val(hc, &new_val); + } + break; + case 4: + hc = hap_serv_get_char_by_uuid(hap_devs[cnt].service, HAP_CHAR_UUID_CURRENT_AMBIENT_LIGHT_LEVEL); + if (Ext_UpdVar(hap_devs[cnt].var_name, &fvar, 0)) { + new_val.f = fvar; + hap_char_update_val(hc, &new_val); + } + break; + case 5: + hc = hap_serv_get_char_by_uuid(hap_devs[cnt].service, HAP_CHAR_UUID_CONTACT_SENSOR_STATE); + if (Ext_UpdVar(hap_devs[cnt].var_name, &fvar, 0)) { + new_val.u = fvar; + hap_char_update_val(hc, &new_val); + } + break; + } + break; + case HAP_CID_OUTLET: + hc = hap_serv_get_char_by_uuid(hap_devs[cnt].service, HAP_CHAR_UUID_ON); + if (Ext_UpdVar(hap_devs[cnt].var_name, &fvar, 0)) { + new_val.b = fvar; + hap_char_update_val(hc, &new_val); + } + break; + case HAP_CID_LIGHTING: + hc = hap_serv_get_char_by_uuid(hap_devs[cnt].service, HAP_CHAR_UUID_ON); + if (Ext_UpdVar(hap_devs[cnt].var_name, &fvar, 0)) { + new_val.b = fvar; + hap_char_update_val(hc, &new_val); + } + hc = hap_serv_get_char_by_uuid(hap_devs[cnt].service, HAP_CHAR_UUID_HUE); + if (Ext_UpdVar(hap_devs[cnt].var2_name, &fvar, 0)) { + new_val.f = fvar; + hap_char_update_val(hc, &new_val); + } + hc = hap_serv_get_char_by_uuid(hap_devs[cnt].service, HAP_CHAR_UUID_SATURATION); + if (Ext_UpdVar(hap_devs[cnt].var3_name, &fvar, 0)) { + new_val.f = fvar; + hap_char_update_val(hc, &new_val); + } + hc = hap_serv_get_char_by_uuid(hap_devs[cnt].service, HAP_CHAR_UUID_BRIGHTNESS); + if (Ext_UpdVar(hap_devs[cnt].var4_name, &fvar, 0)) { + new_val.u = fvar; + hap_char_update_val(hc, &new_val); + } + break; + } + } + +} + #define HAP_READ hap_char_t *hc, hap_status_t *status_code, void *serv_priv, void *read_priv) { return sensor_read(hc, status_code, serv_priv, read_priv static int sensor_read1(HAP_READ, 0);} @@ -377,6 +460,8 @@ uint32_t str2c(char **sp, char *vp, uint32_t len) { return 1; } +extern char *GetFName(); + /*The main thread for handling the Smart Outlet Accessory */ static void smart_outlet_thread_entry(void *p) { /* Initialize the HAP core */ @@ -385,7 +470,8 @@ static void smart_outlet_thread_entry(void *p) { hap_acc_t *accessory; hap_acc_cfg_t cfg = { - .name = "Tasmota-Bridge", + //.name = "Tasmota-Bridge", + .name = GetFName(), .manufacturer = "Tasmota", .model = "Bridge", .serial_num = "001122334455", @@ -462,7 +548,6 @@ static void smart_outlet_thread_entry(void *p) { /* Create accessory object */ hap_devs[index].accessory = hap_acc_create(&hap_cfg); /* Add a dummy Product Data */ - uint8_t product_data[] = {'E','S','P','3','2','H','A','P'}; hap_acc_add_product_data(hap_devs[index].accessory, product_data, sizeof(product_data)); int ret; @@ -500,6 +585,8 @@ static void smart_outlet_thread_entry(void *p) { hap_devs[index].service = hap_serv_battery_service_create(fvar, fvar1, fvar2); } break; + case 4: hap_devs[index].service = hap_serv_wattage_create(fvar); break; + case 5: hap_devs[index].service = hap_serv_contact_sensor_create(fvar); break; } } break; @@ -513,7 +600,7 @@ static void smart_outlet_thread_entry(void *p) { hap_set_write(hap_devs[index].service, index); /* Get pointer to the outlet in use characteristic which we need to monitor for state changes */ - hap_char_t *outlet_in_use = hap_serv_get_char_by_uuid(hap_devs[index].service, HAP_CHAR_UUID_OUTLET_IN_USE); + outlet_in_use = hap_serv_get_char_by_uuid(hap_devs[index].service, HAP_CHAR_UUID_OUTLET_IN_USE); /* Add the Outlet Service to the Accessory Object */ hap_acc_add_serv(hap_devs[index].accessory, hap_devs[index].service); @@ -565,13 +652,8 @@ nextline: /* Enable Hardware MFi authentication (applicable only for MFi variant of SDK) */ hap_enable_mfi_auth(HAP_MFI_AUTH_HW); - /* Initialize Wi-Fi */ - //app_wifi_init(); - /* After all the initializations are done, start the HAP core */ hap_start(); - /* Start Wi-Fi */ - //app_wifi_start(portMAX_DELAY); int32_t io_num = OUTLET_IN_USE_GPIO; if (io_num >= 0) { @@ -597,93 +679,16 @@ nextline: } } else { // vTaskDelete(NULL); - // update values every 100 ms while (1) { - delay(100); - float fvar; - hap_char_t *hc; - hap_val_t new_val; - for (uint32_t cnt = 0; cnt < hk_services; cnt++) { - switch (hap_devs[cnt].hap_cid) { - case HAP_CID_SENSOR: - switch (hap_devs[cnt].type) { - case 0: - hc = hap_serv_get_char_by_uuid(hap_devs[cnt].service, HAP_CHAR_UUID_CURRENT_TEMPERATURE); - if (Ext_UpdVar(hap_devs[cnt].var_name, &fvar, 0)) { - new_val.f = fvar; - hap_char_update_val(hc, &new_val); - } - break; - case 1: - hc = hap_serv_get_char_by_uuid(hap_devs[cnt].service, HAP_CHAR_UUID_CURRENT_RELATIVE_HUMIDITY); - if (Ext_UpdVar(hap_devs[cnt].var_name, &fvar, 0)) { - new_val.f = fvar; - hap_char_update_val(hc, &new_val); - } - break; - case 2: - hc = hap_serv_get_char_by_uuid(hap_devs[cnt].service, HAP_CHAR_UUID_CURRENT_AMBIENT_LIGHT_LEVEL); - if (Ext_UpdVar(hap_devs[cnt].var_name, &fvar, 0)) { - new_val.f = fvar; - hap_char_update_val(hc, &new_val); - } - break; - case 3: - hc = hap_serv_get_char_by_uuid(hap_devs[cnt].service, HAP_CHAR_UUID_BATTERY_LEVEL); - if (Ext_UpdVar(hap_devs[cnt].var_name, &fvar, 0)) { - 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); - if (Ext_UpdVar(hap_devs[cnt].var2_name, &fvar, 0)) { - 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); - if (Ext_UpdVar(hap_devs[cnt].var3_name, &fvar, 0)) { - new_val.u = fvar; - hap_char_update_val(hc, &new_val); - } - break; - } - break; - case HAP_CID_OUTLET: - hc = hap_serv_get_char_by_uuid(hap_devs[cnt].service, HAP_CHAR_UUID_ON); - if (Ext_UpdVar(hap_devs[cnt].var_name, &fvar, 0)) { - new_val.b = fvar; - hap_char_update_val(hc, &new_val); - } - break; - case HAP_CID_LIGHTING: - hc = hap_serv_get_char_by_uuid(hap_devs[cnt].service, HAP_CHAR_UUID_ON); - if (Ext_UpdVar(hap_devs[cnt].var_name, &fvar, 0)) { - new_val.b = fvar; - hap_char_update_val(hc, &new_val); - } - hc = hap_serv_get_char_by_uuid(hap_devs[cnt].service, HAP_CHAR_UUID_HUE); - if (Ext_UpdVar(hap_devs[cnt].var2_name, &fvar, 0)) { - new_val.f = fvar; - hap_char_update_val(hc, &new_val); - } - hc = hap_serv_get_char_by_uuid(hap_devs[cnt].service, HAP_CHAR_UUID_SATURATION); - if (Ext_UpdVar(hap_devs[cnt].var3_name, &fvar, 0)) { - new_val.f = fvar; - hap_char_update_val(hc, &new_val); - } - hc = hap_serv_get_char_by_uuid(hap_devs[cnt].service, HAP_CHAR_UUID_BRIGHTNESS); - if (Ext_UpdVar(hap_devs[cnt].var4_name, &fvar, 0)) { - new_val.u = fvar; - hap_char_update_val(hc, &new_val); - } - break; - } - } + delay(500); + // 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) { @@ -710,10 +715,14 @@ void homekit_main(char *desc, uint32_t flag ) { hk_desc = cp; } else { if (flag == 99) { + hap_loop_stop(); hap_reset_to_factory(); + } else if (flag == 98) { + hap_loop_stop(); + // is just the folder in wrapper + hap_platfrom_keystore_erase_partition(hap_platform_keystore_get_nvs_partition_name()); } else { - // not yet implemented - hap_stop(); + hap_loop_stop(); } return; } diff --git a/tasmota/xdrv_10_scripter.ino b/tasmota/xdrv_10_scripter.ino index 7e34f8102..97ebfad30 100755 --- a/tasmota/xdrv_10_scripter.ino +++ b/tasmota/xdrv_10_scripter.ino @@ -2515,9 +2515,12 @@ chknext: if (!TasmotaGlobal.global_state.wifi_down) { // erase nvs lp = GetNumericArgument(lp + 4, OPER_EQU, &fvar, gv); + homekit_main(0, fvar); - // restart homekit - TasmotaGlobal.restart_flag = 2; + if (fvar >= 98) { + glob_script_mem.homekit_running == false; + } + } lp++; len = 0; @@ -3589,13 +3592,51 @@ extern "C" { uint32_t Ext_UpdVar(char *vname, float *fvar, uint32_t mode) { return UpdVar(vname, fvar, mode); } + void Ext_toLog(char *str) { + toLog(str); + } + + char *GetFName(void) { + return SettingsText(SET_FRIENDLYNAME1); + } } int32_t UpdVar(char *vname, float *fvar, uint32_t mode) { + uint8_t type; + uint8_t index; + if (*vname == '@') { + vname++; + type = *vname; + vname++; + index = (*vname & 0x0f); + if (index < 1) index = 1; + if (index > 9) index = 9; + switch (type) { + case 'p': + if (mode) { + // set power + ExecuteCommandPower(index, *fvar, SRC_BUTTON); + return 0; + } else { + // read power + *fvar = bitRead(TasmotaGlobal.power, index - 1); + return 1; + } + break; + case 's': + *fvar = SwitchLastState(index - 1); + return 1; + break; + case 'b': + *fvar = Button.last_state[index - 1]; + return 1; + break; + } + return 0; + } struct T_INDEX ind; uint8_t vtype; float res = *fvar; - uint8_t index; isvar(vname, &vtype, &ind, fvar, 0, 0); if (vtype != VAR_NV) { // found variable as result @@ -3604,7 +3645,7 @@ int32_t UpdVar(char *vname, float *fvar, uint32_t mode) { // set var index = glob_script_mem.type[ind.index].index; glob_script_mem.fvars[index] = res; - SetChanged(ind.index); + glob_script_mem.type[ind.index].bits.changed = 1; return 0; } else { // get var @@ -3724,13 +3765,11 @@ void Replace_Cmd_Vars(char *srcbuf, uint32_t srcsize, char *dstbuf, uint32_t dst dstbuf[count] = 0; } - void toLog(const char *str) { if (!str) return; AddLog(LOG_LEVEL_INFO, str); } - void toLogN(const char *cp, uint8_t len) { if (!cp) return; char str[32]; From 1d38a6465d10193ef1ed6d52e57fa270f86ae6a4 Mon Sep 17 00:00:00 2001 From: gemu2015 Date: Wed, 17 Mar 2021 13:33:55 +0100 Subject: [PATCH 65/87] update homekit --- .../ESP32-HomeKit/src/_esp_hap_config.h | 2 +- lib/libesp32_div/ESP32-HomeKit/src/hap.h | 0 .../ESP32-HomeKit/src/hap_apple_chars.c | 21 +++++++++-- .../ESP32-HomeKit/src/hap_apple_chars.h | 3 ++ .../ESP32-HomeKit/src/hap_apple_servs.c | 17 ++++++++- .../ESP32-HomeKit/src/hap_apple_servs.h | 4 +++ .../src/hap_platform_keystore.cpp | 35 ++++++++++--------- 7 files changed, 61 insertions(+), 21 deletions(-) mode change 100644 => 100755 lib/libesp32_div/ESP32-HomeKit/src/_esp_hap_config.h mode change 100644 => 100755 lib/libesp32_div/ESP32-HomeKit/src/hap.h mode change 100644 => 100755 lib/libesp32_div/ESP32-HomeKit/src/hap_apple_chars.c mode change 100644 => 100755 lib/libesp32_div/ESP32-HomeKit/src/hap_apple_chars.h mode change 100644 => 100755 lib/libesp32_div/ESP32-HomeKit/src/hap_apple_servs.c mode change 100644 => 100755 lib/libesp32_div/ESP32-HomeKit/src/hap_apple_servs.h diff --git a/lib/libesp32_div/ESP32-HomeKit/src/_esp_hap_config.h b/lib/libesp32_div/ESP32-HomeKit/src/_esp_hap_config.h old mode 100644 new mode 100755 index e848049b8..ea5421b62 --- a/lib/libesp32_div/ESP32-HomeKit/src/_esp_hap_config.h +++ b/lib/libesp32_div/ESP32-HomeKit/src/_esp_hap_config.h @@ -14,7 +14,7 @@ #define CONFIG_HAP_HTTP_STACK_SIZE 12288 #define CONFIG_HAP_HTTP_SERVER_PORT 5556 // 80 for normal webserver #define CONFIG_HAP_HTTP_CONTROL_PORT 32859 -#define CONFIG_HAP_HTTP_MAX_OPEN_SOCKETS 6 +#define CONFIG_HAP_HTTP_MAX_OPEN_SOCKETS 5 // 6 #define CONFIG_HAP_HTTP_MAX_URI_HANDLERS 16 #endif /* ESP_HAP_CONFIG_H_ */ diff --git a/lib/libesp32_div/ESP32-HomeKit/src/hap.h b/lib/libesp32_div/ESP32-HomeKit/src/hap.h old mode 100644 new mode 100755 diff --git a/lib/libesp32_div/ESP32-HomeKit/src/hap_apple_chars.c b/lib/libesp32_div/ESP32-HomeKit/src/hap_apple_chars.c old mode 100644 new mode 100755 index 773cf31fd..de079b4dd --- a/lib/libesp32_div/ESP32-HomeKit/src/hap_apple_chars.c +++ b/lib/libesp32_div/ESP32-HomeKit/src/hap_apple_chars.c @@ -39,6 +39,21 @@ hap_char_t *hap_char_brightness_create(int brightness) return hc; } +/* Char: Brightness */ +hap_char_t *hap_char_wattage_create(float watts) +{ + hap_char_t *hc = hap_char_float_create(HAP_CHAR_UUID_CURRENT_AMBIENT_LIGHT_LEVEL, + HAP_CHAR_PERM_PR | HAP_CHAR_PERM_PW | HAP_CHAR_PERM_EV, watts); + if (!hc) { + return NULL; + } + + hap_char_float_set_constraints(hc, -10000.0, 10000.0, 1); + hap_char_add_unit(hc, HAP_CHAR_UNIT_LUX); + + return hc; +} + /* Char: Cooling Threshold Temperature */ hap_char_t *hap_char_cooling_threshold_temperature_create(float cooling_threshold_temp) { @@ -1284,7 +1299,7 @@ hap_char_t *hap_char_relative_humidity_humidifier_threshold_create(float rel_hum } hap_char_float_set_constraints(hc, 0.0, 100.0, 1.0); - hap_char_add_unit(hc, HAP_CHAR_UNIT_PERCENTAGE); + hap_char_add_unit(hc, HAP_CHAR_UNIT_LUX); return hc; } @@ -1391,7 +1406,7 @@ hap_char_t *hap_char_status_jammed_create(uint8_t status_jammed) hap_char_t *hap_char_administrator_only_access_create(bool administrator_only_access) { hap_char_t *hc = hap_char_bool_create(HAP_CHAR_UUID_ADMINISTRATOR_ONLY_ACCESS, - HAP_CHAR_PERM_PR | HAP_CHAR_PERM_PW | HAP_CHAR_PERM_EV, + HAP_CHAR_PERM_PR | HAP_CHAR_PERM_PW | HAP_CHAR_PERM_EV, administrator_only_access); if (!hc) { return NULL; @@ -1430,7 +1445,7 @@ hap_char_t *hap_char_lock_last_known_action_create(uint8_t lock_last_known_actio hap_char_t *hap_char_lock_management_auto_security_timeout_create(uint32_t lock_management_auto_security_timeout) { hap_char_t *hc = hap_char_uint32_create(HAP_CHAR_UUID_LOCK_MANAGEMENT_AUTO_SECURITY_TIMEOUT, - HAP_CHAR_PERM_PR | HAP_CHAR_PERM_PW | HAP_CHAR_PERM_EV, + HAP_CHAR_PERM_PR | HAP_CHAR_PERM_PW | HAP_CHAR_PERM_EV, lock_management_auto_security_timeout); if (!hc) { return NULL; diff --git a/lib/libesp32_div/ESP32-HomeKit/src/hap_apple_chars.h b/lib/libesp32_div/ESP32-HomeKit/src/hap_apple_chars.h old mode 100644 new mode 100755 index e734a6e57..90aa12e08 --- a/lib/libesp32_div/ESP32-HomeKit/src/hap_apple_chars.h +++ b/lib/libesp32_div/ESP32-HomeKit/src/hap_apple_chars.h @@ -142,6 +142,7 @@ extern "C" { #define HAP_CHAR_UUID_REMAINING_DURATION "D4" #define HAP_CHAR_UUID_VALVE_TYPE "D5" #define HAP_CHAR_UUID_IS_CONFIGURED "D6" +#define HAP_CHAR_UUID_WATTAGE "DC" #define HAP_CHAR_UUID_PRODUCT_DATA "220" /** Create Brightness Characteristic @@ -1415,6 +1416,8 @@ hap_char_t *hap_char_air_particulate_density_create(float air_particulate_densit */ hap_char_t *hap_char_air_particulate_size_create(uint8_t air_particulate_size); +hap_char_t *hap_char_wattage_create(float watts); + #ifdef __cplusplus } #endif diff --git a/lib/libesp32_div/ESP32-HomeKit/src/hap_apple_servs.c b/lib/libesp32_div/ESP32-HomeKit/src/hap_apple_servs.c old mode 100644 new mode 100755 index b8783695f..fec046ffc --- a/lib/libesp32_div/ESP32-HomeKit/src/hap_apple_servs.c +++ b/lib/libesp32_div/ESP32-HomeKit/src/hap_apple_servs.c @@ -419,6 +419,22 @@ err: return NULL; } +hap_serv_t *hap_serv_wattage_create(float curr_watts) +{ + hap_serv_t *hs = hap_serv_create(HAP_SERV_UUID_LIGHT_SENSOR); + if (!hs) { + return NULL; + } + if (hap_serv_add_char(hs, hap_char_wattage_create(curr_watts)) != HAP_SUCCESS) { + goto err; + } + return hs; +err: + hap_serv_delete(hs); + return NULL; +} + + hap_serv_t *hap_serv_temperature_sensor_create(float curr_temp) { hap_serv_t *hs = hap_serv_create(HAP_SERV_UUID_TEMPERATURE_SENSOR); @@ -703,4 +719,3 @@ err: hap_serv_delete(hs); return NULL; } - diff --git a/lib/libesp32_div/ESP32-HomeKit/src/hap_apple_servs.h b/lib/libesp32_div/ESP32-HomeKit/src/hap_apple_servs.h old mode 100644 new mode 100755 index e3f6101f9..f1e65e441 --- a/lib/libesp32_div/ESP32-HomeKit/src/hap_apple_servs.h +++ b/lib/libesp32_div/ESP32-HomeKit/src/hap_apple_servs.h @@ -75,6 +75,7 @@ extern "C" { #define HAP_SERV_UUID_VALVE "D0" #define HAP_SERV_UUID_FAUCET "D7" + /** Create Accessory Information Service * * This API will create the Accessory Information Service with the mandatory @@ -553,6 +554,9 @@ hap_serv_t *hap_serv_valve_create(uint8_t active, uint8_t in_use, uint8_t valve_ */ hap_serv_t *hap_serv_faucet_create(uint8_t active); + +hap_serv_t *hap_serv_wattage_create(float curr_watts); + #ifdef __cplusplus } #endif diff --git a/lib/libesp32_div/ESP32-HomeKit/src/hap_platform_keystore.cpp b/lib/libesp32_div/ESP32-HomeKit/src/hap_platform_keystore.cpp index db6c3e905..ec27c90c5 100755 --- a/lib/libesp32_div/ESP32-HomeKit/src/hap_platform_keystore.cpp +++ b/lib/libesp32_div/ESP32-HomeKit/src/hap_platform_keystore.cpp @@ -144,12 +144,10 @@ int hap_platform_keystore_delete_namespace(const char *part_name, const char *na while (true) { File entry = fp.openNextFile(); if (!entry) break; - char fp[48]; - strcpy(fp,path); - strcat(fp, "/"); - strcat(fp, entry.name()); - ffsp->remove(fp); + char p[48]; + strcpy(p,entry.name()); entry.close(); + ffsp->remove(p); } } return 0; @@ -157,7 +155,6 @@ int hap_platform_keystore_delete_namespace(const char *part_name, const char *na // last resort only int hap_platfrom_keystore_erase_partition(const char *part_name) { -// LITTLEFS.format(); char path[48]; strcpy(path, "/"); strcat(path, part_name); @@ -166,17 +163,23 @@ if (fp.isDirectory()) { while (true) { File entry = fp.openNextFile(); if (!entry) break; - char fp[48]; - strcpy(fp,path); - strcat(fp, "/"); - strcat(fp, entry.name()); - if (entry.isDirectory()) { - hap_platform_keystore_delete_namespace(part_name, entry.name()); - ffsp->rmdir(fp); - } else { - ffsp->remove(fp); + const char *ep = entry.name(); + if (*ep=='/') ep++; + char *lcp = strrchr(ep,'/'); + if (lcp) { + ep = lcp + 1; } - entry.close(); + char p[48]; + strcpy(p,entry.name()); + if (entry.isDirectory()) { + hap_platform_keystore_delete_namespace(part_name, ep); + entry.close(); + ffsp->rmdir(p); + } else { + entry.close(); + ffsp->remove(p); + } + } } return 0; From b427267e2b831cbd972b4ff76ea8ca8669f178eb Mon Sep 17 00:00:00 2001 From: Theo Arends <11044339+arendst@users.noreply.github.com> Date: Wed, 17 Mar 2021 16:26:04 +0100 Subject: [PATCH 66/87] Fix TM1637 driver selection Fix TM1637 driver selection (#11358) --- tasmota/xdrv_13_display.ino | 54 +++++++++++++++++++++--------------- tasmota/xdsp_01_lcd.ino | 5 ++-- tasmota/xdsp_02_ssd1306.ino | 5 ++-- tasmota/xdsp_03_matrix.ino | 5 ++-- tasmota/xdsp_07_sh1106.ino | 5 ++-- tasmota/xdsp_11_sevenseg.ino | 5 ++-- 6 files changed, 46 insertions(+), 33 deletions(-) diff --git a/tasmota/xdrv_13_display.ino b/tasmota/xdrv_13_display.ino index 1d319750b..e73b611e4 100755 --- a/tasmota/xdrv_13_display.ino +++ b/tasmota/xdrv_13_display.ino @@ -17,7 +17,6 @@ along with this program. If not, see . */ -#if defined(USE_I2C) || defined(USE_SPI) #ifdef USE_DISPLAY #define XDRV_13 13 @@ -1600,7 +1599,9 @@ void DisplayInitDriver(void) Display_Text_From_File("/display.ini"); #endif - +#ifdef USE_GRAPH + for (uint8_t count = 0; count < NUM_GRAPHS; count++) { graph[count] = 0; } +#endif // AddLog(LOG_LEVEL_DEBUG, PSTR(D_LOG_DEBUG "Display model %d"), Settings.display_model); @@ -2037,8 +2038,9 @@ void CmndDisplayRows(void) } /*********************************************************************************************\ - * optional drivers + * Optional drivers \*********************************************************************************************/ + #ifdef USE_TOUCH_BUTTONS // very limited path size, so, add .jpg void draw_picture(char *path, uint32_t xp, uint32_t yp, uint32_t xs, uint32_t ys, uint32_t ocol, bool inverted) { @@ -2059,7 +2061,7 @@ char ppath[16]; } Draw_RGB_Bitmap(ppath, xp, yp, inverted); } -#endif +#endif // USE_TOUCH_BUTTONS #ifdef ESP32 @@ -2173,6 +2175,10 @@ void Draw_RGB_Bitmap(char *file,uint16_t xp, uint16_t yp, bool inverted ) { } #endif // USE_UFILESYS +/*********************************************************************************************\ + * AWatch +\*********************************************************************************************/ + #ifdef USE_AWATCH #define MINUTE_REDUCT 4 @@ -2210,6 +2216,9 @@ void DrawAClock(uint16_t rad) { } #endif // USE_AWATCH +/*********************************************************************************************\ + * Graphics +\*********************************************************************************************/ #ifdef USE_GRAPH @@ -2250,7 +2259,6 @@ struct GRAPH { GFLAGS flags; }; - struct GRAPH *graph[NUM_GRAPHS]; #define TICKLEN 4 @@ -2447,6 +2455,7 @@ void Save_graph(uint8_t num, char *path) { fp.print("\n"); fp.close(); } + void Restore_graph(uint8_t num, char *path) { if (!renderer) return; uint16_t index=num%NUM_GRAPHS; @@ -2558,7 +2567,6 @@ void AddGraph(uint8_t num,uint8_t val) { } } - // add next value void AddValue(uint8_t num,float fval) { // not yet defined ??? @@ -2593,7 +2601,12 @@ void AddValue(uint8_t num,float fval) { } #endif // USE_GRAPH +/*********************************************************************************************\ + * Touch panel control +\*********************************************************************************************/ + #if defined(USE_FT5206) || defined(USE_XPT2046) + #ifdef USE_FT5206 #include @@ -2630,7 +2643,7 @@ uint32_t Touch_Status(uint32_t sel) { return 0; } } -#endif +#endif // USE_FT5206 #if defined(USE_XPT2046) && defined(USE_DISPLAY_ILI9341) #include @@ -2664,7 +2677,7 @@ uint32_t Touch_Status(uint32_t sel) { } } -#endif +#endif // USE_XPT2046 && USE_DISPLAY_ILI9341 #ifdef USE_TOUCH_BUTTONS void Touch_MQTT(uint8_t index, const char *cp, uint32_t val) { @@ -2672,7 +2685,7 @@ void Touch_MQTT(uint8_t index, const char *cp, uint32_t val) { ResponseTime_P(PSTR(",\"FT5206\":{\"%s%d\":\"%d\"}}"), cp, index+1, val); #elif defined(USE_XPT2046) ResponseTime_P(PSTR(",\"XPT2046\":{\"%s%d\":\"%d\"}}"), cp, index+1, val); -#endif +#endif // USE_XPT2046 MqttPublishTeleSensor(); } @@ -2688,18 +2701,17 @@ uint8_t tbstate[3]; // check digitizer hit void Touch_Check(void(*rotconvert)(int16_t *x, int16_t *y)) { -uint16_t temp; -uint8_t rbutt=0; -uint8_t vbutt=0; + uint16_t temp; + uint8_t rbutt=0; + uint8_t vbutt=0; - - if (touchp->touched()) { + if (touchp->touched()) { // did find a hit #if defined(USE_FT5206) pLoc = touchp->getPoint(0); #elif defined(USE_XPT2046) pLoc = touchp->getPoint(); -#endif +#endif // USE_XPT2046 if (renderer) { #ifdef USE_M5STACK_CORE2 @@ -2720,7 +2732,7 @@ uint8_t vbutt=0; } xcenter += 100; } -#endif +#endif // USE_M5STACK_CORE2 rotconvert(&pLoc.x, &pLoc.y); @@ -2784,7 +2796,7 @@ uint8_t vbutt=0; //AddLog(LOG_LEVEL_INFO, PSTR("tbut: %d released"), tbut); } } -#endif +#endif // USE_M5STACK_CORE2 for (uint8_t count = 0; count < MAX_TOUCH_BUTTONS; count++) { if (buttons[count]) { if (!buttons[count]->vpower.slider) { @@ -2817,7 +2829,7 @@ uint8_t vbutt=0; } #endif // USE_TOUCH_BUTTONS -#endif // USE_FT5206 +#endif // USE_FT5206 || USE_XPT2046 /*********************************************************************************************\ * Interface @@ -2827,13 +2839,10 @@ bool Xdrv13(uint8_t function) { bool result = false; - if ((TasmotaGlobal.i2c_enabled || TasmotaGlobal.spi_enabled || TasmotaGlobal.soft_spi_enabled) && XdspPresent()) { + if (XdspPresent()) { switch (function) { case FUNC_PRE_INIT: DisplayInitDriver(); -#ifdef USE_GRAPH - for (uint8_t count = 0; count < NUM_GRAPHS; count++) { graph[count] = 0; } -#endif break; case FUNC_EVERY_50_MSECOND: if (Settings.display_model) { XdspCall(FUNC_DISPLAY_EVERY_50_MSECOND); } @@ -2875,4 +2884,3 @@ bool Xdrv13(uint8_t function) } #endif // USE_DISPLAY -#endif // USE_I2C or USE_SPI diff --git a/tasmota/xdsp_01_lcd.ino b/tasmota/xdsp_01_lcd.ino index 8f3a9a779..efb3b542c 100644 --- a/tasmota/xdsp_01_lcd.ino +++ b/tasmota/xdsp_01_lcd.ino @@ -55,8 +55,9 @@ void LcdInit(uint8_t mode) } } -void LcdInitDriver(void) -{ +void LcdInitDriver(void) { + if (!TasmotaGlobal.i2c_enabled) { return; } + if (!Settings.display_model) { if (I2cSetDevice(LCD_ADDRESS1)) { Settings.display_address[0] = LCD_ADDRESS1; diff --git a/tasmota/xdsp_02_ssd1306.ino b/tasmota/xdsp_02_ssd1306.ino index 1698f00f6..03bea26b6 100644 --- a/tasmota/xdsp_02_ssd1306.ino +++ b/tasmota/xdsp_02_ssd1306.ino @@ -47,8 +47,9 @@ extern uint8_t *buffer; /*********************************************************************************************/ -void SSD1306InitDriver(void) -{ +void SSD1306InitDriver(void) { + if (!TasmotaGlobal.i2c_enabled) { return; } + if (!Settings.display_model) { if (I2cSetDevice(OLED_ADDRESS1)) { Settings.display_address[0] = OLED_ADDRESS1; diff --git a/tasmota/xdsp_03_matrix.ino b/tasmota/xdsp_03_matrix.ino index 3b0155a8f..6d20efeec 100644 --- a/tasmota/xdsp_03_matrix.ino +++ b/tasmota/xdsp_03_matrix.ino @@ -194,8 +194,9 @@ void MatrixInit(uint8_t mode) } } -void MatrixInitDriver(void) -{ +void MatrixInitDriver(void) { + if (!TasmotaGlobal.i2c_enabled) { return; } + mtx_buffer = (char*)(malloc(MTX_MAX_SCREEN_BUFFER)); if (mtx_buffer != nullptr) { if (!Settings.display_model) { diff --git a/tasmota/xdsp_07_sh1106.ino b/tasmota/xdsp_07_sh1106.ino index 3ca925932..a4fa94509 100644 --- a/tasmota/xdsp_07_sh1106.ino +++ b/tasmota/xdsp_07_sh1106.ino @@ -48,8 +48,9 @@ Adafruit_SH1106 *oled1106; /*********************************************************************************************/ -void SH1106InitDriver() -{ +void SH1106InitDriver() { + if (!TasmotaGlobal.i2c_enabled) { return; } + if (!Settings.display_model) { if (I2cSetDevice(OLED_ADDRESS1)) { Settings.display_address[0] = OLED_ADDRESS1; diff --git a/tasmota/xdsp_11_sevenseg.ino b/tasmota/xdsp_11_sevenseg.ino index b3bde3149..69342cbbf 100644 --- a/tasmota/xdsp_11_sevenseg.ino +++ b/tasmota/xdsp_11_sevenseg.ino @@ -147,8 +147,9 @@ void SevensegInit(uint8_t mode) } } -void SevensegInitDriver(void) -{ +void SevensegInitDriver(void) { + if (!TasmotaGlobal.i2c_enabled) { return; } + if (!Settings.display_model) { if (I2cSetDevice(Settings.display_address[0])) { Settings.display_model = XDSP_11; From 2f7eefa8f48249b5ef3a957ad10473a5c7e0e7bd Mon Sep 17 00:00:00 2001 From: Theo Arends <11044339+arendst@users.noreply.github.com> Date: Wed, 17 Mar 2021 17:26:00 +0100 Subject: [PATCH 67/87] Fix some display issues Fix some display issues (#11358) --- tasmota/tasmota_template.h | 3 ++- tasmota/xdrv_13_display.ino | 2 +- tasmota/xdsp_04_ili9341.ino | 13 ++++++------- tasmota/xdsp_interface.ino | 2 -- 4 files changed, 9 insertions(+), 11 deletions(-) diff --git a/tasmota/tasmota_template.h b/tasmota/tasmota_template.h index bffb281bf..41df68ff8 100644 --- a/tasmota/tasmota_template.h +++ b/tasmota/tasmota_template.h @@ -410,11 +410,13 @@ const uint16_t kGpioNiceList[] PROGMEM = { AGPIO(GPIO_SDCARD_CS), #endif // USE_SDCARD #endif // USE_SPI + AGPIO(GPIO_SSPI_MISO), // Software SPI Master Input Client Output AGPIO(GPIO_SSPI_MOSI), // Software SPI Master Output Client Input AGPIO(GPIO_SSPI_SCLK), // Software SPI Serial Clock AGPIO(GPIO_SSPI_CS), // Software SPI Chip Select AGPIO(GPIO_SSPI_DC), // Software SPI Data or Command + #ifdef USE_DISPLAY #ifdef USE_DISPLAY_ILI9341 AGPIO(GPIO_ILI9341_CS), @@ -422,7 +424,6 @@ const uint16_t kGpioNiceList[] PROGMEM = { #ifdef USE_XPT2046 AGPIO(GPIO_XPT2046_CS), // XPT2046 SPI Chip Select #endif - #endif // USE_DISPLAY_ILI9341 #ifdef USE_DISPLAY_ILI9488 AGPIO(GPIO_ILI9488_CS), diff --git a/tasmota/xdrv_13_display.ino b/tasmota/xdrv_13_display.ino index e73b611e4..a3586c14d 100755 --- a/tasmota/xdrv_13_display.ino +++ b/tasmota/xdrv_13_display.ino @@ -1932,7 +1932,7 @@ void CmndDisplayFont(void) void CmndDisplayILIMOde(void) { - if ((XdrvMailbox.payload >= 1) && (XdrvMailbox.payload < 16)) { + if ((XdrvMailbox.payload >= 1) && (XdrvMailbox.payload <= 7)) { Settings.display_options.ilimode = XdrvMailbox.payload; TasmotaGlobal.restart_flag = 2; } diff --git a/tasmota/xdsp_04_ili9341.ino b/tasmota/xdsp_04_ili9341.ino index 599964ab9..737b9376f 100644 --- a/tasmota/xdsp_04_ili9341.ino +++ b/tasmota/xdsp_04_ili9341.ino @@ -23,6 +23,8 @@ #define XDSP_04 4 +enum IliModes { ILIMODE_9341 = 1, ILIMODE_9342, ILIMODE_MAX }; + #include extern uint8_t *buffer; @@ -37,13 +39,10 @@ uint8_t ili9342_ctouch_counter = 0; uint8_t ili9342_ctouch_counter = 0; #endif // USE_FT5206 - bool tft_init_done = false; -#define ILI9341_ID 1 -#define ILI9342_ID 2 -//Settings.display_options.ilimode = ILI9341_ID; +//Settings.display_options.ilimode = ILIMODE_9341; /*********************************************************************************************/ @@ -66,8 +65,8 @@ void ILI9341_InitDriver() // disable screen buffer buffer = NULL; - if (!Settings.display_options.ilimode) { - Settings.display_options.ilimode = ILI9341_ID; + if (!Settings.display_options.ilimode || (Settings.display_options.ilimode >= ILIMODE_MAX)) { + Settings.display_options.ilimode = ILIMODE_9341; } // default colors @@ -102,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)==ILI9341_ID?"ILI9341 TFT!":"ILI9342 TFT!", ILI9341_WHITE, 0); + renderer->DrawStringAt(50, (Settings.display_height/2)-12, (Settings.display_options.ilimode & 3)==ILIMODE_9341?"ILI9341 TFT!":"ILI9342 TFT!", ILI9341_WHITE, 0); delay(1000); #endif // SHOW_SPLASH diff --git a/tasmota/xdsp_interface.ino b/tasmota/xdsp_interface.ino index 3ca49f3d6..d404e169e 100644 --- a/tasmota/xdsp_interface.ino +++ b/tasmota/xdsp_interface.ino @@ -17,7 +17,6 @@ along with this program. If not, see . */ -#if defined(USE_I2C) || defined(USE_SPI) #ifdef USE_DISPLAY #ifdef XFUNC_PTR_IN_ROM @@ -201,4 +200,3 @@ bool XdspCall(uint8_t Function) } #endif // USE_DISPLAY -#endif // USE_I2C or USE_SPI From e5a566f0bb0704a5cafd0bb92042da52f791fb77 Mon Sep 17 00:00:00 2001 From: Theo Arends <11044339+arendst@users.noreply.github.com> Date: Wed, 17 Mar 2021 17:51:12 +0100 Subject: [PATCH 68/87] Hide I2C address for single sensors Hide I2C address for single sensors (#11323) --- tasmota/xsns_14_sht3x.ino | 5 ++++- tasmota/xsns_63_aht1x.ino | 5 ++++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/tasmota/xsns_14_sht3x.ino b/tasmota/xsns_14_sht3x.ino index 4e29742e5..0f791f78a 100644 --- a/tasmota/xsns_14_sht3x.ino +++ b/tasmota/xsns_14_sht3x.ino @@ -99,7 +99,10 @@ void Sht3xShow(bool json) float h; if (Sht3xRead(t, h, sht3x_sensors[i].address)) { char types[11]; - snprintf_P(types, sizeof(types), PSTR("%s%c0x%02X"), sht3x_sensors[i].types, IndexSeparator(), sht3x_sensors[i].address); // "SHT3X-0xXX" + strlcpy(types, sht3x_sensors[i].types, sizeof(types)); + if (sht3x_count > 1) { + snprintf_P(types, sizeof(types), PSTR("%s%c%02X"), sht3x_sensors[i].types, IndexSeparator(), sht3x_sensors[i].address); // "SHT3X-0xXX" + } TempHumDewShow(json, ((0 == TasmotaGlobal.tele_period) && (0 == i)), types, t, h); } } diff --git a/tasmota/xsns_63_aht1x.ino b/tasmota/xsns_63_aht1x.ino index 88da71890..bb47873eb 100644 --- a/tasmota/xsns_63_aht1x.ino +++ b/tasmota/xsns_63_aht1x.ino @@ -175,7 +175,10 @@ void AHT1XShow(bool json) { float tem = ConvertTemp(aht1x_sensors[i].temperature); float hum = ConvertHumidity(aht1x_sensors[i].humidity); char types[11]; // AHT1X-0x38 - snprintf_P(types, sizeof(types), PSTR("%s%c0x%02X"), aht1x_sensors[i].types, IndexSeparator(), aht1x_sensors[i].address); // "X-0xXX" + strlcpy(types, aht1x_sensors[i].types, sizeof(types)); + if (aht1x.count > 1) { + snprintf_P(types, sizeof(types), PSTR("%s%c%02X"), aht1x_sensors[i].types, IndexSeparator(), aht1x_sensors[i].address); // "X-0xXX" + } TempHumDewShow(json, ((0 == TasmotaGlobal.tele_period) && (0 == i)), types, tem, hum); } } From 74da6eb0571ba204cfa435f6be6b439e1af927b1 Mon Sep 17 00:00:00 2001 From: Barbudor Date: Wed, 17 Mar 2021 19:45:29 +0100 Subject: [PATCH 69/87] check on hostByName + disable syslog --- tasmota/support.ino | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/tasmota/support.ino b/tasmota/support.ino index ff6a0aecf..800c0a1ca 100644 --- a/tasmota/support.ino +++ b/tasmota/support.ino @@ -2138,8 +2138,13 @@ void SyslogAsync(bool refresh) { uint32_t current_hash = GetHash(SettingsText(SET_SYSLOG_HOST), strlen(SettingsText(SET_SYSLOG_HOST))); if (syslog_host_hash != current_hash) { IPAddress temp_syslog_host_addr; - int ret = WiFi.hostByName(SettingsText(SET_SYSLOG_HOST), temp_syslog_host_addr); // If sleep enabled this might result in exception so try to do it once using hash - if (!ret) return; + int ok = WiFi.hostByName(SettingsText(SET_SYSLOG_HOST), temp_syslog_host_addr); // If sleep enabled this might result in exception so try to do it once using hash + if (!ok || (0xFFFFFFFF == (uint32_t)temp_syslog_host_addr)) { // 255.255.255.255 is assumed a DNS problem + TasmotaGlobal.syslog_level = 0; + TasmotaGlobal.syslog_timer = SYSLOG_TIMER; + AddLog(LOG_LEVEL_INFO, PSTR(D_LOG_APPLICATION "Loghost DNS resolve failed (%s). " D_RETRY_IN " %d " D_UNIT_SECOND), SettingsText(SET_SYSLOG_HOST), SYSLOG_TIMER); + return; + } syslog_host_hash = current_hash; syslog_host_addr = temp_syslog_host_addr; } From f31bdfdfb34edb36bc7c1498d6fbc4f4001cde7b Mon Sep 17 00:00:00 2001 From: Alex Brown <100519+alexpbrown@users.noreply.github.com> Date: Wed, 17 Mar 2021 22:26:24 -0400 Subject: [PATCH 70/87] Add period char replacement for prometheus This was a problem for me when trying to get output from an SDS011 Nova PM sensor. When parsed by the pometheus code it returns labels / metrics like: ```# TYPE tasmota_sensors_pm2.5_ gauge tasmota_sensors_pm2.5_{sensor="sds0x1"} 2.2``` The error from prometheus was: ```"append failed" err="invalid metric type \\"5_ gauge\\"``` --- tasmota/xsns_75_prometheus.ino | 1 + 1 file changed, 1 insertion(+) diff --git a/tasmota/xsns_75_prometheus.ino b/tasmota/xsns_75_prometheus.ino index bbdb303c5..0b0251229 100644 --- a/tasmota/xsns_75_prometheus.ino +++ b/tasmota/xsns_75_prometheus.ino @@ -57,6 +57,7 @@ String FormatMetricName(const char *metric) { // cleanup spaces and uppercases String formatted = metric; formatted.toLowerCase(); formatted.replace(" ", "_"); + formatted.replace(".", "_"); return formatted; } From ff7a911e4572d56846a51a23fcce0836b8593757 Mon Sep 17 00:00:00 2001 From: Theo Arends <11044339+arendst@users.noreply.github.com> Date: Thu, 18 Mar 2021 09:27:46 +0100 Subject: [PATCH 71/87] Fix logging if network down (#11373) --- tasmota/support.ino | 2 +- tasmota/xdrv_02_mqtt.ino | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/tasmota/support.ino b/tasmota/support.ino index 800c0a1ca..63ebc371e 100644 --- a/tasmota/support.ino +++ b/tasmota/support.ino @@ -2125,7 +2125,7 @@ void SyslogAsync(bool refresh) { static uint32_t syslog_host_hash = 0; // Syslog host name hash static uint32_t index = 1; - if (!TasmotaGlobal.syslog_level) { return; } + if (!TasmotaGlobal.syslog_level || TasmotaGlobal.global_state.network_down) { return; } if (refresh && !NeedLogRefresh(TasmotaGlobal.syslog_level, index)) { return; } char* line; diff --git a/tasmota/xdrv_02_mqtt.ino b/tasmota/xdrv_02_mqtt.ino index 02fe422f1..3ef1ec84b 100644 --- a/tasmota/xdrv_02_mqtt.ino +++ b/tasmota/xdrv_02_mqtt.ino @@ -311,7 +311,7 @@ void MqttUnsubscribe(const char *topic) { void MqttPublishLoggingAsync(bool refresh) { static uint32_t index = 1; - if (!Settings.mqttlog_level || !Settings.flag.mqtt_enabled) { return; } // SetOption3 - Enable MQTT + if (!Settings.mqttlog_level || !Settings.flag.mqtt_enabled || !Mqtt.connected) { return; } // SetOption3 - Enable MQTT if (refresh && !NeedLogRefresh(Settings.mqttlog_level, index)) { return; } char* line; From ba77e6d1c0d5632133e80d14bf66c38e45912cad Mon Sep 17 00:00:00 2001 From: Ajith Vasudevan Date: Thu, 18 Mar 2021 19:30:50 +0530 Subject: [PATCH 72/87] Removed DisplayType and TM1638 Buttons and LEDs support --- tasmota/xdrv_13_display.ino | 58 +------ tasmota/xdsp_15_tm1637.ino | 330 ++++++++---------------------------- 2 files changed, 78 insertions(+), 310 deletions(-) diff --git a/tasmota/xdrv_13_display.ino b/tasmota/xdrv_13_display.ino index 4524858ac..35e4e05ee 100755 --- a/tasmota/xdrv_13_display.ino +++ b/tasmota/xdrv_13_display.ino @@ -77,11 +77,7 @@ const uint8_t DISPLAY_LOG_ROWS = 32; // Number of lines in display log #define D_CMND_DISP_SCROLLDELAY "ScrollDelay" #define D_CMND_DISP_CLOCK "Clock" #define D_CMND_DISP_TEXTNC "TextNC" // NC - "No Clear" -#define D_CMND_DISP_SETLEDS "SetLEDs" -#define D_CMND_DISP_SETLED "SetLED" -#define D_CMND_DISP_BUTTONS "Buttons" #define D_CMND_DISP_SCROLLTEXT "ScrollText" -#define D_CMND_DISP_TYPE "Type" #define D_CMND_DISP_ILIMODE "ILIMode" #define D_CMND_DISP_ILIINVERT "Invert" @@ -99,8 +95,7 @@ enum XdspFunctions { FUNC_DISPLAY_INIT_DRIVER, FUNC_DISPLAY_INIT, FUNC_DISPLAY_E #endif , FUNC_DISPLAY_NUMBER, FUNC_DISPLAY_FLOAT, FUNC_DISPLAY_NUMBERNC, FUNC_DISPLAY_FLOATNC, FUNC_DISPLAY_BRIGHTNESS, FUNC_DISPLAY_RAW, FUNC_DISPLAY_LEVEL, FUNC_DISPLAY_SEVENSEG_TEXT, FUNC_DISPLAY_SEVENSEG_TEXTNC, - FUNC_DISPLAY_SCROLLDELAY, FUNC_DISPLAY_CLOCK, FUNC_DISPLAY_SETLEDS, FUNC_DISPLAY_SETLED, - FUNC_DISPLAY_BUTTONS, FUNC_DISPLAY_SCROLLTEXT, FUNC_DISPLAY_TYPE + FUNC_DISPLAY_SCROLLDELAY, FUNC_DISPLAY_CLOCK, FUNC_DISPLAY_SCROLLTEXT }; enum DisplayInitModes { DISPLAY_INIT_MODE, DISPLAY_INIT_PARTIAL, DISPLAY_INIT_FULL }; @@ -114,8 +109,8 @@ const char kDisplayCommands[] PROGMEM = D_PRFX_DISPLAY "|" // Prefix #endif "|" D_CMND_DISP_CLEAR "|" D_CMND_DISP_NUMBER "|" D_CMND_DISP_FLOAT "|" D_CMND_DISP_NUMBERNC "|" D_CMND_DISP_FLOATNC "|" D_CMND_DISP_BRIGHTNESS "|" 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_SETLEDS "|" D_CMND_DISP_SETLED "|" - D_CMND_DISP_BUTTONS "|" D_CMND_DISP_SCROLLTEXT "|" D_CMND_DISP_TYPE "|" D_CMND_DISP_ILIMODE "|" D_CMND_DISP_ILIINVERT + D_CMND_DISP_SCROLLDELAY "|" D_CMND_DISP_CLOCK "|" D_CMND_DISP_TEXTNC "|" + D_CMND_DISP_SCROLLTEXT "|" D_CMND_DISP_ILIMODE "|" D_CMND_DISP_ILIINVERT ; void (* const DisplayCommand[])(void) PROGMEM = { @@ -127,8 +122,8 @@ void (* const DisplayCommand[])(void) PROGMEM = { #endif , &CmndDisplayClear, &CmndDisplayNumber, &CmndDisplayFloat, &CmndDisplayNumberNC, &CmndDisplayFloatNC, &CmndDisplayBrightness, &CmndDisplayRaw, &CmndDisplayLevel, &CmndDisplaySevensegText, &CmndDisplaySevensegTextNC, - &CmndDisplayScrollDelay, &CmndDisplayClock, &CmndDisplayTextNC, &CmndDisplaySetLEDs, &CmndDisplaySetLED, - &CmndDisplayButtons, &CmndDisplayScrollText, &CmndDisplayType, &CmndDisplayILIMOde , &CmndDisplayILIInvert + &CmndDisplayScrollDelay, &CmndDisplayClock, &CmndDisplayTextNC, + &CmndDisplayScrollText, &CmndDisplayILIMOde , &CmndDisplayILIInvert }; char *dsp_str; @@ -1865,33 +1860,6 @@ void CmndDisplayClock(void) ResponseCmndNumber(XdrvMailbox.payload); } -void CmndDisplaySetLEDs(void) -{ - bool result = false; - if (!renderer) { - result = XdspCall(FUNC_DISPLAY_SETLEDS); - } - if(result) ResponseCmndNumber(XdrvMailbox.payload); -} - - -void CmndDisplaySetLED(void) -{ - bool result = false; - if (!renderer) { - result = XdspCall(FUNC_DISPLAY_SETLED); - } - if(result) ResponseCmndNumber(XdrvMailbox.payload); -} - -void CmndDisplayButtons(void) -{ - bool result = false; - if (!renderer) { - XdspCall(FUNC_DISPLAY_BUTTONS); - } -} - void CmndDisplayScrollText(void) { @@ -1903,22 +1871,6 @@ void CmndDisplayScrollText(void) } -void CmndDisplayType(void) -{ - if(ArgC() == 0) { - XdrvMailbox.payload = Settings.display_options.data; - } else { - uint8_t prev_type = Settings.display_options.data; - if(prev_type != XdrvMailbox.payload) { - if (!renderer) { - XdspCall(FUNC_DISPLAY_TYPE); - Settings.display_options.data = XdrvMailbox.payload; - } - } - } - ResponseCmndNumber(XdrvMailbox.payload); -} - void CmndDisplaySize(void) { #ifdef USE_DISPLAY_TM1637 diff --git a/tasmota/xdsp_15_tm1637.ino b/tasmota/xdsp_15_tm1637.ino index d551582b0..2d2f17bba 100644 --- a/tasmota/xdsp_15_tm1637.ino +++ b/tasmota/xdsp_15_tm1637.ino @@ -21,9 +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. Additionally, for the TM1638, - it allows the TM1638 LEDs to be toggled using its buttons. - Useful STAT messages are also sent when the TM1638 buttons are pressed. + on the inexpensive TM1637- and TM1638-based seven-segment modules. Raw segments can also be displayed. @@ -51,23 +49,12 @@ Once the GPIO configuration is saved and the ESP8266/ESP32 module restarts, set the Display Model to 15 using the command "DisplayModel 15" - After this, depending upon your display variant, set your Display Type to 0, 1 or 2 using the command - "DisplayType 0" // for the 4-digit TM1637 - or "DisplayType 1" // for the 6-digit TM1637 - or "DisplayType 2" // for the 8-digit TM1638 + 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. After the ESP8266/ESP32 module restarts again, the following "Display" commands can be used: - DisplayType type {0|1|2} - - Sets the display type. 0 => TM1637, 4 digit - 1 => TM1637, 6 digit - 2 => TM1638, 8 digit - Command e.g., "TM1637Data.display_type 1" // to enable TM1637 6-digit variant - - - DisplayClear Clears the display, command: "DisplayClear" @@ -156,56 +143,6 @@ -Commands specific to TM1638 -==================================== - - DisplaySetLEDs bit_array {0-255} - - Sets the 8 LEDs (not the digits!) on the TM1638 module to the binary number represented by the input bit_array. - For example, "DisplaySetLEDs 3" would light up the first and second LED (from left), because 3 => 00000011 - - - - DisplaySetLED position {0-7}, value {0|1} - - Sets a specified LED to either ON or OFF. e.g., "DisplaySetLED 2, 1" would light up the 3rd LED (2 => 3rd position) - - - - DisplayButtons - - Causes the current state of the buttons/LEDs to be returned as a "STAT" message of the following form: - stat/TM1638/RESULT = {"DisplayButtons": {"S1":, "S2":, "S3":, "S4":, "S5":, "S6":, "S7":, "S8":, "Array": [,,,,,,,], "Byte": }} - where , , ... are the states of each of the buttons/LEDs, and is the decimal representation - of the bit-array that constitutes these states. - - For example, if the 5th button is pressed to turn ON LED5, and the "DisplayButtons" command is issued, - the response STAT message will be of the form: - stat/TM1638/RESULT = {"DisplayButtons": {"S1":0, "S2":0, "S3":0, "S4":0, "S5":1, "S6":0, "S7":0, "S8":0, "Array": [0,0,0,0,1,0,0,0], "Byte": 16}} - because 16 => 2^(5-1) - - - - Button Functionality (TM1638 only): -====================================== - When this driver is initialized with "DisplayType 2" (TM1638), the buttons on the TM1638 module can be used - to toggle the corresponding LEDs. - - In addition, for each button press, a STAT message of the following form is sent: - - stat//RESULT = {"TM1638_BUTTON":} - - where = button index (starting at 1) - = state of the corresponding LED. It can be "ON" or "OFF" - - For example, if Button3 is pressed, LED3 would light up and a STAT message is sent as follows: - - stat/TM1638/RESULT = {"TM1638_BUTTON3":"ON"} - - Pressing Button3 again would turn OFF LED3 and a STAT message is sent as follows: - - stat/TM1638/RESULT = {"TM1638_BUTTON3":"OFF"} - \*********************************************************************************************/ @@ -254,31 +191,37 @@ struct { /*********************************************************************************************\ * Init function \*********************************************************************************************/ -bool DriverInit(void) { - if(Settings.display_model == XDSP_15) { - if(TM1637Data.driver_inited) return true; +bool TM1637Init(void) { - if(Settings.display_options.data == 2) { TM1637Data.num_digits = 8; TM1637Data.display_type = TM1638; } - else if(Settings.display_options.data == 1) { TM1637Data.num_digits = 6; TM1637Data.display_type = TM1637; } - else { Settings.display_options.data = 0; TM1637Data.num_digits = 4; TM1637Data.display_type = TM1637; } + if(TM1637Data.driver_inited) return true; - if(TM1637Data.display_type == TM1637) { - strcpy(TM1637Data.model_name, "TM1637"); - tm1637display = new SevenSegmentTM1637(Pin(GPIO_TM1637CLK), Pin(GPIO_TM1637DIO)); - tm1637display->begin(TM1637Data.num_digits, 1); - } else if(TM1637Data.display_type == TM1638) { - strcpy(TM1637Data.model_name, "TM1638"); - tm1638display = new TM1638plus(Pin(GPIO_TM16STB), Pin(GPIO_TM16CLK), Pin(GPIO_TM16DIO), true ); - TM1637Data.num_digits = 8; - tm1638display->displayBegin(); - } - Settings.display_size = TM1637Data.num_digits; // Can use to check current display size - clearDisplay(); - TM1637Data.brightness = (Settings.display_dimmer ? Settings.display_dimmer : TM1637Data.brightness); - setBrightness(TM1637Data.brightness); - TM1637Data.driver_inited = true; - AddLog(LOG_LEVEL_INFO, PSTR("DSP: %s display driver initialized with %d digits (DisplayType %d)"), TM1637Data.model_name, TM1637Data.num_digits, Settings.display_options.data); + if(PinUsed(GPIO_TM16CLK) && PinUsed(GPIO_TM16DIO) && PinUsed(GPIO_TM16STB)) { + TM1637Data.display_type = TM1638; + TM1637Data.num_digits = 8; + } else if(PinUsed(GPIO_TM1637CLK) && PinUsed(GPIO_TM1637DIO)) { + TM1637Data.display_type = TM1637; + if(Settings.display_cols[0] <= 6) TM1637Data.num_digits = Settings.display_cols[0]; + else TM1637Data.num_digits = 4; } + else return false; + + Settings.display_model == XDSP_15; + + if(TM1637Data.display_type == TM1637) { + strcpy(TM1637Data.model_name, "TM1637"); + tm1637display = new SevenSegmentTM1637(Pin(GPIO_TM1637CLK), Pin(GPIO_TM1637DIO)); + tm1637display->begin(TM1637Data.num_digits, 1); + } else if(TM1637Data.display_type == TM1638) { + strcpy(TM1637Data.model_name, "TM1638"); + tm1638display = new TM1638plus(Pin(GPIO_TM16STB), Pin(GPIO_TM16CLK), Pin(GPIO_TM16DIO), true ); + TM1637Data.num_digits = 8; + tm1638display->displayBegin(); + } + TM1637ClearDisplay(); + TM1637Data.brightness = (Settings.display_dimmer ? Settings.display_dimmer : TM1637Data.brightness); + TM1637SetBrightness(TM1637Data.brightness); + TM1637Data.driver_inited = true; + AddLog(LOG_LEVEL_INFO, PSTR("DSP: %s display driver initialized with %d digits"), TM1637Data.model_name, TM1637Data.num_digits); return true; } @@ -289,7 +232,7 @@ bool DriverInit(void) { * commands: DisplayNumber num [,position {0-(TM1637Data.num_digits-1)} [,leading_zeros {0|1} [,length {1 to TM1637Data.num_digits}]]] * DisplayNumberNC num [,position {0-(TM1637Data.num_digits-1)} [,leading_zeros {0|1} [,length {1 to TM1637Data.num_digits}]]] // "NC" --> "No Clear" \*********************************************************************************************/ -bool CmndNumber(bool clear) { +bool CmndTM1637Number(bool clear) { char sNum[CMD_MAX_LEN]; char sLeadingzeros[CMD_MAX_LEN]; char sPosition[CMD_MAX_LEN]; @@ -321,7 +264,7 @@ bool CmndNumber(bool clear) { AddLog(LOG_LEVEL_DEBUG, PSTR("TM7: num %d, pos %d, lead %d, len %d"), num, position, leadingzeros, length); - if(clear) clearDisplay(); + if(clear) TM1637ClearDisplay(); char txt[30]; snprintf_P(txt, sizeof(txt), PSTR("%d"), num); @@ -354,7 +297,7 @@ bool CmndNumber(bool clear) { * commands: DisplayFloat num [,position {0-(TM1637Data.num_digits-1)} [,precision {0-TM1637Data.num_digits} [,length {1 to TM1637Data.num_digits}]]] * DisplayFloatNC num [,position {0-(TM1637Data.num_digits-1)} [,precision {0-TM1637Data.num_digits} [,length {1 to TM1637Data.num_digits}]]] // "NC" --> "No Clear" \*********************************************************************************************/ -bool CmndFloat(bool clear) { +bool CmndTM1637Float(bool clear) { char sNum[CMD_MAX_LEN]; char sPrecision[CMD_MAX_LEN]; @@ -386,7 +329,7 @@ bool CmndFloat(bool clear) { if((position < 0) || (position > (TM1637Data.num_digits-1))) position = 0; if((precision < 0) || (precision > TM1637Data.num_digits)) precision = TM1637Data.num_digits; - if(clear) clearDisplay(); + if(clear) TM1637ClearDisplay(); char txt[30]; ext_snprintf_P(txt, sizeof(txt), PSTR("%*_f"), precision, &fnum); @@ -430,8 +373,8 @@ bool CmndFloat(bool clear) { // * Clears the display // * Command: DisplayClear // \*********************************************************************************************/ -bool CmndClear(void) { - clearDisplay(); +bool CmndTM1637Clear(void) { + TM1637ClearDisplay(); sprintf(TM1637Data.msg, PSTR("Cleared")); XdrvMailbox.data = TM1637Data.msg; return true; @@ -441,7 +384,7 @@ bool CmndClear(void) { // /*********************************************************************************************\ // * Clears the display // \*********************************************************************************************/ -void clearDisplay (void) { +void TM1637ClearDisplay (void) { if(TM1637Data.display_type == TM1637) { unsigned char arr[] = {0}; for(int i=0; iprintRaw(arr, 1, i); @@ -455,7 +398,7 @@ void clearDisplay (void) { * Display scrolling text * Command: DisplayTM1637Data.scroll_text text \*********************************************************************************************/ -bool CmndScrollText(void) { +bool CmndTM1637ScrollText(void) { AddLog(LOG_LEVEL_DEBUG, PSTR("TM7: Text %s"), XdrvMailbox.data); @@ -480,7 +423,7 @@ bool CmndScrollText(void) { * Sets the scroll delay for scrolling text. * Command: DisplayTM1637Data.scroll_delay delay {0-15} // default = 4 \*********************************************************************************************/ -bool CmndScrollDelay(void) { +bool CmndTM1637ScrollDelay(void) { if(ArgC() == 0) { XdrvMailbox.payload = TM1637Data.scroll_delay; return true; @@ -495,7 +438,7 @@ bool CmndScrollDelay(void) { /*********************************************************************************************\ * Scrolls a given string. Called every 50ms \*********************************************************************************************/ -void scrollText(void) { +void TM1637ScrollText(void) { TM1637Data.iteration++; if(TM1637Data.scroll_delay) TM1637Data.iteration = TM1637Data.iteration % TM1637Data.scroll_delay; else TM1637Data.iteration = 0; @@ -538,7 +481,7 @@ void scrollText(void) { * Displays a horizontal bar graph. Takes a percentage number (0-100) as input * Command: DisplayLevel level {0-100} \*********************************************************************************************/ -bool CmndLevel(void) { +bool CmndTM1637Level(void) { uint16_t val = XdrvMailbox.payload; 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); @@ -546,17 +489,17 @@ bool CmndLevel(void) { } uint8_t totalBars = 2*TM1637Data.num_digits; - AddLog(LOG_LEVEL_DEBUG, PSTR("TM7: TM1637Data.model_name %s CmndLevel totalBars=%d"), TM1637Data.model_name, totalBars); + 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]; ext_snprintf_P(txt, sizeof(txt), PSTR("%*_f"), 1, &barsToDisplay); - AddLog(LOG_LEVEL_DEBUG, PSTR("TM7: TM1637Data.model_name %s CmndLevel barsToDisplay=%s"), TM1637Data.model_name, txt); + AddLog(LOG_LEVEL_DEBUG, PSTR("TM7: TM1637Data.model_name %s CmndTM1637Level barsToDisplay=%s"), TM1637Data.model_name, txt); char s[4]; ext_snprintf_P(s, sizeof(s), PSTR("%0_f"), &barsToDisplay); uint8_t numBars = atoi(s); AddLog(LOG_LEVEL_DEBUG, PSTR("TM7: CmndTM1637Level numBars %d"), numBars); - clearDisplay(); + TM1637ClearDisplay(); uint8_t rawBytes[1]; for(int i=1; i<=numBars; i++) { uint8_t digit = (i-1) / 2; @@ -580,7 +523,7 @@ bool CmndLevel(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 CmndRaw(void) { +bool CmndTM1637Raw(void) { uint8_t DATA[6] = { 0, 0, 0, 0, 0, 0 }; char as[CMD_MAX_LEN]; @@ -656,7 +599,7 @@ bool CmndRaw(void) { * position parameters without affecting the rest of the display. * Command: DisplayText text [, position {0-(TM1637Data.num_digits-1)} [,length {1 to TM1637Data.num_digits}]] \*********************************************************************************************/ -bool CmndText(bool clear) { +bool CmndTM1637Text(bool clear) { char sString[CMD_MAX_LEN + 1]; char sPosition[CMD_MAX_LEN]; char sLength[CMD_MAX_LEN]; @@ -680,7 +623,7 @@ bool CmndText(bool clear) { AddLog(LOG_LEVEL_DEBUG, PSTR("TM7: sString %s, pos %d, len %d"), sString, position, length); - if(clear) clearDisplay(); + if(clear) TM1637ClearDisplay(); if(!length) length = strlen(sString); if((length < 0) || (length > TM1637Data.num_digits)) length = TM1637Data.num_digits; @@ -724,7 +667,7 @@ bool CmndText(bool clear) { * Sets brightness of the display. * Command: DisplayBrightness {1-8} \*********************************************************************************************/ -bool CmndBrightness(void) { +bool CmndTM1637Brightness(void) { uint16_t val = XdrvMailbox.payload; if(ArgC() == 0) { @@ -737,13 +680,13 @@ bool CmndBrightness(void) { return false; } TM1637Data.brightness = val; - setBrightness(TM1637Data.brightness); + TM1637SetBrightness(TM1637Data.brightness); return true; } -void setBrightness(uint8_t val) { +void TM1637SetBrightness(uint8_t val) { if((val < BRIGHTNESS_MIN) || (val > BRIGHTNESS_MAX)) val = 5; Settings.display_dimmer = val; if(TM1637Data.display_type == TM1637) tm1637display->setBacklight(val*10); @@ -760,7 +703,7 @@ void setBrightness(uint8_t val) { * DisplayClock 2 // 24-hour format * DisplayClock 0 // turn off clock and clear \*********************************************************************************************/ -bool CmndClock(void) { +bool CmndTM1637Clock(void) { TM1637Data.show_clock = XdrvMailbox.payload; @@ -770,7 +713,7 @@ bool CmndClock(void) { AddLog(LOG_LEVEL_DEBUG, PSTR("TM7: TM1637Data.show_clock %d, TM1637Data.clock_24 %d"), TM1637Data.show_clock, TM1637Data.clock_24); - clearDisplay(); + TM1637ClearDisplay(); return true; } @@ -778,7 +721,7 @@ bool CmndClock(void) { /*********************************************************************************************\ * refreshes the time if clock is displayed \*********************************************************************************************/ -void showTime() { +void TM1637ShowTime() { uint8_t hr = RtcTime.hour; uint8_t mn = RtcTime.minute; // uint8_t hr = 1; @@ -817,120 +760,10 @@ void showTime() { } - - -/*********************************************************************************************\ -* Sets all LEDs of the display. -* Command: DisplaySetLEDs {0-255} -\*********************************************************************************************/ -bool CmndSetLEDs(void) { - if(TM1637Data.display_type != TM1638) { - Response_P(PSTR("{\"Error\":\"Command not valid for TM1637Data.display_type %d\"}"), TM1637Data.display_type); - return false; - } - if(ArgC() == 0) XdrvMailbox.payload = 0; - uint16_t val = XdrvMailbox.payload; - if((val < LED_MIN) || (val > LED_MAX)) { - Response_P(PSTR("{\"Error\":\"Set LEDs value should be a number in the range [%d, %d]\"}"), LED_MIN, LED_MAX); - return false; - } - for(uint8_t i=0; i<8; i++) TM1637Data.LED[i] = ((val & 2^i) >> i); - tm1638display->setLEDs(val << 8); - return true; -} - - -/*********************************************************************************************\ -* Sets an TM1637Data.LED at specified position. -* Command: DisplaySetLED position {0-7}, value {0|1} -\*********************************************************************************************/ -bool CmndSetLED(void) { - if(TM1637Data.display_type != TM1638) { - Response_P(PSTR("{\"Error\":\"Command not valid for TM1637Data.display_type %d\"}"), TM1637Data.display_type); - return false; - } - if(ArgC() < 2) { - Response_P(PSTR("{\"Error\":\"Set LED requires two comma-separated numbers as arguments\"}")); - return false; - } - - char sVal[CMD_MAX_LEN]; - char sPos[CMD_MAX_LEN]; - uint32_t position = 0; - uint32_t value = 0; - - switch (ArgC()) - { - case 2 : - subStr(sVal, XdrvMailbox.data, ",", 2); - value = atoi(sVal); - case 1 : - subStr(sPos, XdrvMailbox.data, ",", 1); - position = atoi(sPos); - } - - - if((position < POSITION_MIN) || (position > POSITION_MAX)) { - Response_P(PSTR("{\"Error\":\"First argument, position should be in the range [%d, %d]\"}"), POSITION_MIN, POSITION_MAX); - return false; - } - - AddLog(LOG_LEVEL_DEBUG, PSTR("TM7: TM1637Data.model_name TM1638, position=%d, value %d"), position, value); - - TM1637Data.LED[position] = value; - tm1638display->setLED(position, value); - return true; -} - - -/*********************************************************************************************\ -* Reads the button states. Called every 50ms -\*********************************************************************************************/ -bool readButtons(void) { - TM1637Data.buttons = tm1638display->readButtons(); - if(TM1637Data.prev_buttons != TM1637Data.buttons) { - TM1637Data.prev_buttons = TM1637Data.buttons; - if(!TM1637Data.buttons) return true; - if(TM1637Data.buttons) { - for(int i=0; i<8; i++) { - if(TM1637Data.buttons & (1<setLED(i, TM1637Data.LED[i]); - Response_P(PSTR("{\"TM1638_BUTTON%d\":\"%s\"}"), i+1, (TM1637Data.LED[i]?PSTR("ON"):PSTR("OFF"))); - MqttPublishPrefixTopic_P(RESULT_OR_STAT, PSTR("BUTTONS")); - } - } - } - } - return true; -} - -/*********************************************************************************************\ -* Returns the current state of the buttons as a decimal representation of the button states -* Command: DisplayButtons -\*********************************************************************************************/ -bool CmndButtons(void) { - if(TM1637Data.display_type != TM1638) { - Response_P(PSTR("{\"Error\":\"Command not valid for TM1637Data.display_type %d\"}"), TM1637Data.display_type); - return false; - } - AddLog(LOG_LEVEL_DEBUG, PSTR("TM7: TM1638: buttons=%d"), TM1637Data.buttons); - uint8_t byte = TM1637Data.LED[0]<<0 | TM1637Data.LED[1]<<1 | TM1637Data.LED[2]<<2 | TM1637Data.LED[3]<<3 | TM1637Data.LED[4]<<4 | TM1637Data.LED[5]<<5 | TM1637Data.LED[6]<<6 | TM1637Data.LED[7]<<7; - Response_P(PSTR("{\"DisplayButtons\": {\"S1\":%d, \"S2\":%d, \"S3\":%d, \"S4\":%d, \"S5\":%d, \"S6\":%d, \"S7\":%d, \"S8\":%d, \"Array\": [%d,%d,%d,%d,%d,%d,%d,%d], \"Byte\": %d}}"), - TM1637Data.LED[0], TM1637Data.LED[1], TM1637Data.LED[2], TM1637Data.LED[3], TM1637Data.LED[4], TM1637Data.LED[5], TM1637Data.LED[6], TM1637Data.LED[7], TM1637Data.LED[0], - TM1637Data.LED[1], TM1637Data.LED[2], TM1637Data.LED[3], TM1637Data.LED[4], TM1637Data.LED[5], TM1637Data.LED[6], TM1637Data.LED[7], byte); - return true; -} - - - - - /*********************************************************************************************\ * This function is called for all Display functions. \*********************************************************************************************/ -bool MainFunc(uint8_t fn) { +bool TM1637MainFunc(uint8_t fn) { bool result = false; if(XdrvMailbox.data_len > CMD_MAX_LEN) { @@ -940,52 +773,43 @@ bool MainFunc(uint8_t fn) { switch (fn) { case FUNC_DISPLAY_CLEAR: - result = CmndClear(); + result = CmndTM1637Clear(); break; case FUNC_DISPLAY_NUMBER : - result = CmndNumber(true); + result = CmndTM1637Number(true); break; case FUNC_DISPLAY_NUMBERNC : - result = CmndNumber(false); + result = CmndTM1637Number(false); break; case FUNC_DISPLAY_FLOAT : - result = CmndFloat(true); + result = CmndTM1637Float(true); break; case FUNC_DISPLAY_FLOATNC : - result = CmndFloat(false); + result = CmndTM1637Float(false); break; case FUNC_DISPLAY_BRIGHTNESS: - result = CmndBrightness(); + result = CmndTM1637Brightness(); break; case FUNC_DISPLAY_RAW: - result = CmndRaw(); + result = CmndTM1637Raw(); break; case FUNC_DISPLAY_SEVENSEG_TEXT: - result = CmndText(true); + result = CmndTM1637Text(true); break; case FUNC_DISPLAY_SEVENSEG_TEXTNC: - result = CmndText(false); + result = CmndTM1637Text(false); break; case FUNC_DISPLAY_LEVEL: - result = CmndLevel(); + result = CmndTM1637Level(); break; case FUNC_DISPLAY_SCROLLTEXT: - result = CmndScrollText(); + result = CmndTM1637ScrollText(); break; case FUNC_DISPLAY_SCROLLDELAY: - result = CmndScrollDelay(); + result = CmndTM1637ScrollDelay(); break; - case FUNC_DISPLAY_SETLEDS: - result = CmndSetLEDs(); - break; - case FUNC_DISPLAY_SETLED: - result = CmndSetLED(); - break; - case FUNC_DISPLAY_BUTTONS: - result = CmndButtons(); - break; - case FUNC_DISPLAY_CLOCK: - result = CmndClock(); + case FUNC_DISPLAY_CLOCK: + result = CmndTM1637Clock(); break; } @@ -1007,16 +831,12 @@ bool Xdsp15(uint8_t function) if (Settings.display_model == XDSP_15) { switch (function) { case FUNC_DISPLAY_INIT_DRIVER: - result = DriverInit(); // init + result = TM1637Init(); // init break; case FUNC_DISPLAY_INIT: AddLog(LOG_LEVEL_DEBUG, PSTR("TM7: %s: FUNC_DISPLAY_INIT: Display depends on TM1637Data.display_type, currently %d"), TM1637Data.model_name, Settings.display_options.data); result = true; break; - case FUNC_DISPLAY_TYPE: - AddLog(LOG_LEVEL_DEBUG, PSTR("TM7: %s: FUNC_DISPLAY_TYPE: TM1637Data.display_type set to %d, restarting to take effect ..."), TM1637Data.model_name, Settings.display_options.data); - TasmotaGlobal.restart_flag = 2; - break; case FUNC_DISPLAY_SEVENSEG_TEXT: case FUNC_DISPLAY_CLEAR: case FUNC_DISPLAY_NUMBER: @@ -1031,15 +851,11 @@ bool Xdsp15(uint8_t function) case FUNC_DISPLAY_CLOCK: TM1637Data.show_clock = false; case FUNC_DISPLAY_BRIGHTNESS: - case FUNC_DISPLAY_SETLEDS: - case FUNC_DISPLAY_SETLED: - case FUNC_DISPLAY_BUTTONS: - result = MainFunc(function); + result = TM1637MainFunc(function); break; case FUNC_DISPLAY_EVERY_50_MSECOND: - if(TM1637Data.scroll) scrollText(); - if(TM1637Data.show_clock) showTime(); - if(TM1637Data.display_type == TM1638) readButtons(); + if(TM1637Data.scroll) TM1637ScrollText(); + if(TM1637Data.show_clock) TM1637ShowTime(); break; } } From d5ca2994ee94532405b002e28b0aca194958790f Mon Sep 17 00:00:00 2001 From: Theo Arends <11044339+arendst@users.noreply.github.com> Date: Thu, 18 Mar 2021 17:44:10 +0100 Subject: [PATCH 73/87] Add commands MqttKeepAlive and MqttTimeout - Add commands ``MqttKeepAlive 1..100`` to set Mqtt Keep Alive timer (default 30) and ``MqttTimeout 1..100`` to set Mqtt Socket Timeout (default 4) (#5341) - Changed PubSubClient library from EspEasy v2.7.12 to Tasmota v2.8.12 - Bump version to 9.3.1.2 --- CHANGELOG.md | 10 +- RELEASENOTES.md | 4 +- .../PubSubClient-EspEasy-2.7.12/.gitignore | 1 - lib/default/pubsubclient-2.8.12/.gitignore | 5 + .../.travis.yml | 0 .../CHANGES.txt | 9 + .../LICENSE.txt | 2 +- .../README.md | 8 +- .../examples/mqtt_auth/mqtt_auth.ino | 6 +- .../examples/mqtt_basic/mqtt_basic.ino | 0 .../examples/mqtt_esp8266/mqtt_esp8266.ino | 15 +- .../mqtt_large_message/mqtt_large_message.ino | 0 .../mqtt_publish_in_callback.ino | 0 .../mqtt_reconnect_nonblocking.ino | 0 .../examples/mqtt_stream/mqtt_stream.ino | 0 .../keywords.txt | 3 + .../library.json | 8 +- .../library.properties | 2 +- .../src/PubSubClient.cpp | 410 +++++++++++++----- .../src/PubSubClient.h | 46 +- .../tests/.gitignore | 0 .../tests/Makefile | 0 .../tests/README.md | 0 .../tests/src/connect_spec.cpp | 34 ++ .../tests/src/keepalive_spec.cpp | 0 .../tests/src/lib/Arduino.h | 0 .../tests/src/lib/BDDTest.cpp | 0 .../tests/src/lib/BDDTest.h | 0 .../tests/src/lib/Buffer.cpp | 0 .../tests/src/lib/Buffer.h | 8 +- .../tests/src/lib/Client.h | 0 .../tests/src/lib/IPAddress.cpp | 0 .../tests/src/lib/IPAddress.h | 0 .../tests/src/lib/Print.h | 0 .../tests/src/lib/ShimClient.cpp | 0 .../tests/src/lib/ShimClient.h | 0 .../tests/src/lib/Stream.cpp | 0 .../tests/src/lib/Stream.h | 0 .../tests/src/lib/trace.h | 0 .../tests/src/publish_spec.cpp | 1 + .../tests/src/receive_spec.cpp | 71 ++- .../tests/src/subscribe_spec.cpp | 1 + .../tests/testcases/__init__.py | 0 .../tests/testcases/mqtt_basic.py | 0 .../testcases/mqtt_publish_in_callback.py | 0 .../tests/testcases/settings.py | 0 .../tests/testsuite.py | 0 tasmota/i18n.h | 2 + tasmota/my_user_config.h | 3 + tasmota/settings.h | 7 +- tasmota/settings.ino | 6 + tasmota/support_command.ino | 4 +- tasmota/tasmota_globals.h | 4 +- tasmota/tasmota_version.h | 2 +- tasmota/xdrv_02_mqtt.ino | 29 +- 55 files changed, 543 insertions(+), 158 deletions(-) delete mode 100644 lib/default/PubSubClient-EspEasy-2.7.12/.gitignore create mode 100644 lib/default/pubsubclient-2.8.12/.gitignore rename lib/default/{PubSubClient-EspEasy-2.7.12 => pubsubclient-2.8.12}/.travis.yml (100%) rename lib/default/{PubSubClient-EspEasy-2.7.12 => pubsubclient-2.8.12}/CHANGES.txt (86%) rename lib/default/{PubSubClient-EspEasy-2.7.12 => pubsubclient-2.8.12}/LICENSE.txt (96%) rename lib/default/{PubSubClient-EspEasy-2.7.12 => pubsubclient-2.8.12}/README.md (87%) rename lib/default/{PubSubClient-EspEasy-2.7.12 => pubsubclient-2.8.12}/examples/mqtt_auth/mqtt_auth.ino (91%) rename lib/default/{PubSubClient-EspEasy-2.7.12 => pubsubclient-2.8.12}/examples/mqtt_basic/mqtt_basic.ino (100%) rename lib/default/{PubSubClient-EspEasy-2.7.12 => pubsubclient-2.8.12}/examples/mqtt_esp8266/mqtt_esp8266.ino (94%) rename lib/default/{PubSubClient-EspEasy-2.7.12 => pubsubclient-2.8.12}/examples/mqtt_large_message/mqtt_large_message.ino (100%) rename lib/default/{PubSubClient-EspEasy-2.7.12 => pubsubclient-2.8.12}/examples/mqtt_publish_in_callback/mqtt_publish_in_callback.ino (100%) rename lib/default/{PubSubClient-EspEasy-2.7.12 => pubsubclient-2.8.12}/examples/mqtt_reconnect_nonblocking/mqtt_reconnect_nonblocking.ino (100%) rename lib/default/{PubSubClient-EspEasy-2.7.12 => pubsubclient-2.8.12}/examples/mqtt_stream/mqtt_stream.ino (100%) rename lib/default/{PubSubClient-EspEasy-2.7.12 => pubsubclient-2.8.12}/keywords.txt (91%) rename lib/default/{PubSubClient-EspEasy-2.7.12 => pubsubclient-2.8.12}/library.json (85%) rename lib/default/{PubSubClient-EspEasy-2.7.12 => pubsubclient-2.8.12}/library.properties (98%) rename lib/default/{PubSubClient-EspEasy-2.7.12 => pubsubclient-2.8.12}/src/PubSubClient.cpp (61%) rename lib/default/{PubSubClient-EspEasy-2.7.12 => pubsubclient-2.8.12}/src/PubSubClient.h (88%) rename lib/default/{PubSubClient-EspEasy-2.7.12 => pubsubclient-2.8.12}/tests/.gitignore (100%) rename lib/default/{PubSubClient-EspEasy-2.7.12 => pubsubclient-2.8.12}/tests/Makefile (100%) rename lib/default/{PubSubClient-EspEasy-2.7.12 => pubsubclient-2.8.12}/tests/README.md (100%) rename lib/default/{PubSubClient-EspEasy-2.7.12 => pubsubclient-2.8.12}/tests/src/connect_spec.cpp (90%) rename lib/default/{PubSubClient-EspEasy-2.7.12 => pubsubclient-2.8.12}/tests/src/keepalive_spec.cpp (100%) rename lib/default/{PubSubClient-EspEasy-2.7.12 => pubsubclient-2.8.12}/tests/src/lib/Arduino.h (100%) rename lib/default/{PubSubClient-EspEasy-2.7.12 => pubsubclient-2.8.12}/tests/src/lib/BDDTest.cpp (100%) rename lib/default/{PubSubClient-EspEasy-2.7.12 => pubsubclient-2.8.12}/tests/src/lib/BDDTest.h (100%) rename lib/default/{PubSubClient-EspEasy-2.7.12 => pubsubclient-2.8.12}/tests/src/lib/Buffer.cpp (100%) rename lib/default/{PubSubClient-EspEasy-2.7.12 => pubsubclient-2.8.12}/tests/src/lib/Buffer.h (88%) rename lib/default/{PubSubClient-EspEasy-2.7.12 => pubsubclient-2.8.12}/tests/src/lib/Client.h (100%) rename lib/default/{PubSubClient-EspEasy-2.7.12 => pubsubclient-2.8.12}/tests/src/lib/IPAddress.cpp (100%) rename lib/default/{PubSubClient-EspEasy-2.7.12 => pubsubclient-2.8.12}/tests/src/lib/IPAddress.h (100%) rename lib/default/{PubSubClient-EspEasy-2.7.12 => pubsubclient-2.8.12}/tests/src/lib/Print.h (100%) rename lib/default/{PubSubClient-EspEasy-2.7.12 => pubsubclient-2.8.12}/tests/src/lib/ShimClient.cpp (100%) rename lib/default/{PubSubClient-EspEasy-2.7.12 => pubsubclient-2.8.12}/tests/src/lib/ShimClient.h (100%) rename lib/default/{PubSubClient-EspEasy-2.7.12 => pubsubclient-2.8.12}/tests/src/lib/Stream.cpp (100%) rename lib/default/{PubSubClient-EspEasy-2.7.12 => pubsubclient-2.8.12}/tests/src/lib/Stream.h (100%) rename lib/default/{PubSubClient-EspEasy-2.7.12 => pubsubclient-2.8.12}/tests/src/lib/trace.h (100%) rename lib/default/{PubSubClient-EspEasy-2.7.12 => pubsubclient-2.8.12}/tests/src/publish_spec.cpp (99%) rename lib/default/{PubSubClient-EspEasy-2.7.12 => pubsubclient-2.8.12}/tests/src/receive_spec.cpp (75%) rename lib/default/{PubSubClient-EspEasy-2.7.12 => pubsubclient-2.8.12}/tests/src/subscribe_spec.cpp (99%) rename lib/default/{PubSubClient-EspEasy-2.7.12 => pubsubclient-2.8.12}/tests/testcases/__init__.py (100%) rename lib/default/{PubSubClient-EspEasy-2.7.12 => pubsubclient-2.8.12}/tests/testcases/mqtt_basic.py (100%) rename lib/default/{PubSubClient-EspEasy-2.7.12 => pubsubclient-2.8.12}/tests/testcases/mqtt_publish_in_callback.py (100%) rename lib/default/{PubSubClient-EspEasy-2.7.12 => pubsubclient-2.8.12}/tests/testcases/settings.py (100%) rename lib/default/{PubSubClient-EspEasy-2.7.12 => pubsubclient-2.8.12}/tests/testsuite.py (100%) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1c3c9ca0f..0d3c637bf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,13 @@ All notable changes to this project will be documented in this file. ## [Unreleased] - Development +## [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) + +### Changed +- PubSubClient library from EspEasy v2.7.12 to Tasmota v2.8.12 + ## [9.3.1.1] ### Added - Support for CSE7761 energy monitor as used in ESP32 based Sonoff Dual R3 Pow (#10793) @@ -18,8 +25,7 @@ All notable changes to this project will be documented in this file. - ESP32 Extent BLE (#11212) - ESP32 support for WS2812 hardware driver via RMT or I2S - ESP32 support for secondary I2C controller -- Add support for MPU6686 on primary or secondary I2C bus - +- Support for MPU6686 on primary or secondary I2C bus ### Changed - ESP32 core library from v1.0.5-rc6 to v1.0.5 diff --git a/RELEASENOTES.md b/RELEASENOTES.md index c5ad1ba9e..cdb71c52e 100644 --- a/RELEASENOTES.md +++ b/RELEASENOTES.md @@ -78,8 +78,9 @@ The attached binaries can also be downloaded from http://ota.tasmota.com/tasmota [Complete list](BUILDS.md) of available feature and sensors. -## Changelog v9.3.1.1 +## Changelog v9.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](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) - 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) @@ -93,6 +94,7 @@ The attached binaries can also be downloaded from http://ota.tasmota.com/tasmota ### Changed - TasmotaSerial library from v3.2.0 to v3.3.0 +- PubSubClient library from EspEasy v2.7.12 to Tasmota v2.8.12 - ESP32 core library from v1.0.5-rc6 to v1.0.5 - TuyaMcu dimmer timeout [#11121](https://github.com/arendst/Tasmota/issues/11121) - Rename epaper 42 commands [#11222](https://github.com/arendst/Tasmota/issues/11222) diff --git a/lib/default/PubSubClient-EspEasy-2.7.12/.gitignore b/lib/default/PubSubClient-EspEasy-2.7.12/.gitignore deleted file mode 100644 index 1c3ba189b..000000000 --- a/lib/default/PubSubClient-EspEasy-2.7.12/.gitignore +++ /dev/null @@ -1 +0,0 @@ -tests/bin diff --git a/lib/default/pubsubclient-2.8.12/.gitignore b/lib/default/pubsubclient-2.8.12/.gitignore new file mode 100644 index 000000000..a42cc406e --- /dev/null +++ b/lib/default/pubsubclient-2.8.12/.gitignore @@ -0,0 +1,5 @@ +tests/bin +.pioenvs +.piolibdeps +.clang_complete +.gcc-flags.json diff --git a/lib/default/PubSubClient-EspEasy-2.7.12/.travis.yml b/lib/default/pubsubclient-2.8.12/.travis.yml similarity index 100% rename from lib/default/PubSubClient-EspEasy-2.7.12/.travis.yml rename to lib/default/pubsubclient-2.8.12/.travis.yml diff --git a/lib/default/PubSubClient-EspEasy-2.7.12/CHANGES.txt b/lib/default/pubsubclient-2.8.12/CHANGES.txt similarity index 86% rename from lib/default/PubSubClient-EspEasy-2.7.12/CHANGES.txt rename to lib/default/pubsubclient-2.8.12/CHANGES.txt index ff4da62ab..e23d5315f 100644 --- a/lib/default/PubSubClient-EspEasy-2.7.12/CHANGES.txt +++ b/lib/default/pubsubclient-2.8.12/CHANGES.txt @@ -1,3 +1,12 @@ +2.8 + * Add setBufferSize() to override MQTT_MAX_PACKET_SIZE + * Add setKeepAlive() to override MQTT_KEEPALIVE + * Add setSocketTimeout() to overide MQTT_SOCKET_TIMEOUT + * Added check to prevent subscribe/unsubscribe to empty topics + * Declare wifi mode prior to connect in ESP example + * Use `strnlen` to avoid overruns + * Support pre-connected Client objects + 2.7 * Fix remaining-length handling to prevent buffer overrun * Add large-payload API - beginPublish/write/publish/endPublish diff --git a/lib/default/PubSubClient-EspEasy-2.7.12/LICENSE.txt b/lib/default/pubsubclient-2.8.12/LICENSE.txt similarity index 96% rename from lib/default/PubSubClient-EspEasy-2.7.12/LICENSE.txt rename to lib/default/pubsubclient-2.8.12/LICENSE.txt index 217df35cc..12c1689e6 100644 --- a/lib/default/PubSubClient-EspEasy-2.7.12/LICENSE.txt +++ b/lib/default/pubsubclient-2.8.12/LICENSE.txt @@ -1,4 +1,4 @@ -Copyright (c) 2008-2015 Nicholas O'Leary +Copyright (c) 2008-2020 Nicholas O'Leary Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the diff --git a/lib/default/PubSubClient-EspEasy-2.7.12/README.md b/lib/default/pubsubclient-2.8.12/README.md similarity index 87% rename from lib/default/PubSubClient-EspEasy-2.7.12/README.md rename to lib/default/pubsubclient-2.8.12/README.md index 69cbb8f0c..2e1317185 100644 --- a/lib/default/PubSubClient-EspEasy-2.7.12/README.md +++ b/lib/default/pubsubclient-2.8.12/README.md @@ -13,10 +13,12 @@ Full API documentation is available here: https://pubsubclient.knolleary.net ## Limitations - It can only publish QoS 0 messages. It can subscribe at QoS 0 or QoS 1. - - The maximum message size, including header, is **128 bytes** by default. This - is configurable via `MQTT_MAX_PACKET_SIZE` in `PubSubClient.h`. + - The maximum message size, including header, is **256 bytes** by default. This + is configurable via `MQTT_MAX_PACKET_SIZE` in `PubSubClient.h` or can be changed + by calling `PubSubClient::setBufferSize(size)`. - The keepalive interval is set to 15 seconds by default. This is configurable - via `MQTT_KEEPALIVE` in `PubSubClient.h`. + via `MQTT_KEEPALIVE` in `PubSubClient.h` or can be changed by calling + `PubSubClient::setKeepAlive(keepAlive)`. - The client uses MQTT 3.1.1 by default. It can be changed to use MQTT 3.1 by changing value of `MQTT_VERSION` in `PubSubClient.h`. diff --git a/lib/default/PubSubClient-EspEasy-2.7.12/examples/mqtt_auth/mqtt_auth.ino b/lib/default/pubsubclient-2.8.12/examples/mqtt_auth/mqtt_auth.ino similarity index 91% rename from lib/default/PubSubClient-EspEasy-2.7.12/examples/mqtt_auth/mqtt_auth.ino rename to lib/default/pubsubclient-2.8.12/examples/mqtt_auth/mqtt_auth.ino index e9f7b180f..04bd7bb29 100644 --- a/lib/default/PubSubClient-EspEasy-2.7.12/examples/mqtt_auth/mqtt_auth.ino +++ b/lib/default/pubsubclient-2.8.12/examples/mqtt_auth/mqtt_auth.ino @@ -27,9 +27,9 @@ void setup() { Ethernet.begin(mac, ip); // Note - the default maximum packet size is 128 bytes. If the - // combined length of clientId, username and password exceed this, - // you will need to increase the value of MQTT_MAX_PACKET_SIZE in - // PubSubClient.h + // combined length of clientId, username and password exceed this use the + // following to increase the buffer size: + // client.setBufferSize(255); if (client.connect("arduinoClient", "testuser", "testpass")) { client.publish("outTopic","hello world"); diff --git a/lib/default/PubSubClient-EspEasy-2.7.12/examples/mqtt_basic/mqtt_basic.ino b/lib/default/pubsubclient-2.8.12/examples/mqtt_basic/mqtt_basic.ino similarity index 100% rename from lib/default/PubSubClient-EspEasy-2.7.12/examples/mqtt_basic/mqtt_basic.ino rename to lib/default/pubsubclient-2.8.12/examples/mqtt_basic/mqtt_basic.ino diff --git a/lib/default/PubSubClient-EspEasy-2.7.12/examples/mqtt_esp8266/mqtt_esp8266.ino b/lib/default/pubsubclient-2.8.12/examples/mqtt_esp8266/mqtt_esp8266.ino similarity index 94% rename from lib/default/PubSubClient-EspEasy-2.7.12/examples/mqtt_esp8266/mqtt_esp8266.ino rename to lib/default/pubsubclient-2.8.12/examples/mqtt_esp8266/mqtt_esp8266.ino index e7357b507..7e6effd24 100644 --- a/lib/default/PubSubClient-EspEasy-2.7.12/examples/mqtt_esp8266/mqtt_esp8266.ino +++ b/lib/default/pubsubclient-2.8.12/examples/mqtt_esp8266/mqtt_esp8266.ino @@ -1,26 +1,21 @@ /* Basic ESP8266 MQTT example - This sketch demonstrates the capabilities of the pubsub library in combination with the ESP8266 board/library. - It connects to an MQTT server then: - publishes "hello world" to the topic "outTopic" every two seconds - subscribes to the topic "inTopic", printing out any messages it receives. NB - it assumes the received payloads are strings not binary - If the first character of the topic "inTopic" is an 1, switch ON the ESP Led, else switch it off - It will reconnect to the server if the connection is lost using a blocking reconnect function. See the 'mqtt_reconnect_nonblocking' example for how to achieve the same result without blocking the main loop. - To install the ESP8266 board, (using Arduino 1.6.4+): - Add the following 3rd party board manager under "File -> Preferences -> Additional Boards Manager URLs": http://arduino.esp8266.com/stable/package_esp8266com_index.json - Open the "Tools -> Board -> Board Manager" and click install for the ESP8266" - Select your ESP8266 in "Tools -> Board" - */ #include @@ -34,8 +29,9 @@ const char* mqtt_server = "broker.mqtt-dashboard.com"; WiFiClient espClient; PubSubClient client(espClient); -long lastMsg = 0; -char msg[50]; +unsigned long lastMsg = 0; +#define MSG_BUFFER_SIZE (50) +char msg[MSG_BUFFER_SIZE]; int value = 0; void setup_wifi() { @@ -46,6 +42,7 @@ void setup_wifi() { Serial.print("Connecting to "); Serial.println(ssid); + WiFi.mode(WIFI_STA); WiFi.begin(ssid, password); while (WiFi.status() != WL_CONNECTED) { @@ -120,11 +117,11 @@ void loop() { } client.loop(); - long now = millis(); + unsigned long now = millis(); if (now - lastMsg > 2000) { lastMsg = now; ++value; - snprintf (msg, 50, "hello world #%ld", value); + snprintf (msg, MSG_BUFFER_SIZE, "hello world #%ld", value); Serial.print("Publish message: "); Serial.println(msg); client.publish("outTopic", msg); diff --git a/lib/default/PubSubClient-EspEasy-2.7.12/examples/mqtt_large_message/mqtt_large_message.ino b/lib/default/pubsubclient-2.8.12/examples/mqtt_large_message/mqtt_large_message.ino similarity index 100% rename from lib/default/PubSubClient-EspEasy-2.7.12/examples/mqtt_large_message/mqtt_large_message.ino rename to lib/default/pubsubclient-2.8.12/examples/mqtt_large_message/mqtt_large_message.ino diff --git a/lib/default/PubSubClient-EspEasy-2.7.12/examples/mqtt_publish_in_callback/mqtt_publish_in_callback.ino b/lib/default/pubsubclient-2.8.12/examples/mqtt_publish_in_callback/mqtt_publish_in_callback.ino similarity index 100% rename from lib/default/PubSubClient-EspEasy-2.7.12/examples/mqtt_publish_in_callback/mqtt_publish_in_callback.ino rename to lib/default/pubsubclient-2.8.12/examples/mqtt_publish_in_callback/mqtt_publish_in_callback.ino diff --git a/lib/default/PubSubClient-EspEasy-2.7.12/examples/mqtt_reconnect_nonblocking/mqtt_reconnect_nonblocking.ino b/lib/default/pubsubclient-2.8.12/examples/mqtt_reconnect_nonblocking/mqtt_reconnect_nonblocking.ino similarity index 100% rename from lib/default/PubSubClient-EspEasy-2.7.12/examples/mqtt_reconnect_nonblocking/mqtt_reconnect_nonblocking.ino rename to lib/default/pubsubclient-2.8.12/examples/mqtt_reconnect_nonblocking/mqtt_reconnect_nonblocking.ino diff --git a/lib/default/PubSubClient-EspEasy-2.7.12/examples/mqtt_stream/mqtt_stream.ino b/lib/default/pubsubclient-2.8.12/examples/mqtt_stream/mqtt_stream.ino similarity index 100% rename from lib/default/PubSubClient-EspEasy-2.7.12/examples/mqtt_stream/mqtt_stream.ino rename to lib/default/pubsubclient-2.8.12/examples/mqtt_stream/mqtt_stream.ino diff --git a/lib/default/PubSubClient-EspEasy-2.7.12/keywords.txt b/lib/default/pubsubclient-2.8.12/keywords.txt similarity index 91% rename from lib/default/PubSubClient-EspEasy-2.7.12/keywords.txt rename to lib/default/pubsubclient-2.8.12/keywords.txt index 1ee23d0fa..960a033f9 100644 --- a/lib/default/PubSubClient-EspEasy-2.7.12/keywords.txt +++ b/lib/default/pubsubclient-2.8.12/keywords.txt @@ -27,6 +27,9 @@ setServer KEYWORD2 setCallback KEYWORD2 setClient KEYWORD2 setStream KEYWORD2 +setKeepAlive KEYWORD2 +setBufferSize KEYWORD2 +setSocketTimeout KEYWORD2 ####################################### # Constants (LITERAL1) diff --git a/lib/default/PubSubClient-EspEasy-2.7.12/library.json b/lib/default/pubsubclient-2.8.12/library.json similarity index 85% rename from lib/default/PubSubClient-EspEasy-2.7.12/library.json rename to lib/default/pubsubclient-2.8.12/library.json index e675ecff3..c0d7bae2d 100644 --- a/lib/default/PubSubClient-EspEasy-2.7.12/library.json +++ b/lib/default/pubsubclient-2.8.12/library.json @@ -6,9 +6,13 @@ "type": "git", "url": "https://github.com/knolleary/pubsubclient.git" }, - "version": "2.7", + "version": "2.8", "exclude": "tests", "examples": "examples/*/*.ino", "frameworks": "arduino", - "platforms": ["espressif8266", "espressif32"] + "platforms": [ + "atmelavr", + "espressif8266", + "espressif32" + ] } diff --git a/lib/default/PubSubClient-EspEasy-2.7.12/library.properties b/lib/default/pubsubclient-2.8.12/library.properties similarity index 98% rename from lib/default/PubSubClient-EspEasy-2.7.12/library.properties rename to lib/default/pubsubclient-2.8.12/library.properties index 7df4daf80..732331bf9 100644 --- a/lib/default/PubSubClient-EspEasy-2.7.12/library.properties +++ b/lib/default/pubsubclient-2.8.12/library.properties @@ -1,5 +1,5 @@ name=PubSubClient -version=2.7 +version=2.8 author=Nick O'Leary maintainer=Nick O'Leary sentence=A client library for MQTT messaging. diff --git a/lib/default/PubSubClient-EspEasy-2.7.12/src/PubSubClient.cpp b/lib/default/pubsubclient-2.8.12/src/PubSubClient.cpp similarity index 61% rename from lib/default/PubSubClient-EspEasy-2.7.12/src/PubSubClient.cpp rename to lib/default/pubsubclient-2.8.12/src/PubSubClient.cpp index 0748fdc4a..acd0fab9b 100644 --- a/lib/default/PubSubClient-EspEasy-2.7.12/src/PubSubClient.cpp +++ b/lib/default/pubsubclient-2.8.12/src/PubSubClient.cpp @@ -12,12 +12,20 @@ PubSubClient::PubSubClient() { this->_client = NULL; this->stream = NULL; setCallback(NULL); + this->bufferSize = 0; + setBufferSize(MQTT_MAX_PACKET_SIZE); + setKeepAlive(MQTT_KEEPALIVE); + setSocketTimeout(MQTT_SOCKET_TIMEOUT); } PubSubClient::PubSubClient(Client& client) { this->_state = MQTT_DISCONNECTED; setClient(client); this->stream = NULL; + this->bufferSize = 0; + setBufferSize(MQTT_MAX_PACKET_SIZE); + setKeepAlive(MQTT_KEEPALIVE); + setSocketTimeout(MQTT_SOCKET_TIMEOUT); } PubSubClient::PubSubClient(IPAddress addr, uint16_t port, Client& client) { @@ -25,12 +33,20 @@ PubSubClient::PubSubClient(IPAddress addr, uint16_t port, Client& client) { setServer(addr, port); setClient(client); this->stream = NULL; + this->bufferSize = 0; + setBufferSize(MQTT_MAX_PACKET_SIZE); + setKeepAlive(MQTT_KEEPALIVE); + setSocketTimeout(MQTT_SOCKET_TIMEOUT); } PubSubClient::PubSubClient(IPAddress addr, uint16_t port, Client& client, Stream& stream) { this->_state = MQTT_DISCONNECTED; setServer(addr,port); setClient(client); setStream(stream); + this->bufferSize = 0; + setBufferSize(MQTT_MAX_PACKET_SIZE); + setKeepAlive(MQTT_KEEPALIVE); + setSocketTimeout(MQTT_SOCKET_TIMEOUT); } PubSubClient::PubSubClient(IPAddress addr, uint16_t port, MQTT_CALLBACK_SIGNATURE, Client& client) { this->_state = MQTT_DISCONNECTED; @@ -38,6 +54,10 @@ PubSubClient::PubSubClient(IPAddress addr, uint16_t port, MQTT_CALLBACK_SIGNATUR setCallback(callback); setClient(client); this->stream = NULL; + this->bufferSize = 0; + setBufferSize(MQTT_MAX_PACKET_SIZE); + setKeepAlive(MQTT_KEEPALIVE); + setSocketTimeout(MQTT_SOCKET_TIMEOUT); } PubSubClient::PubSubClient(IPAddress addr, uint16_t port, MQTT_CALLBACK_SIGNATURE, Client& client, Stream& stream) { this->_state = MQTT_DISCONNECTED; @@ -45,6 +65,10 @@ PubSubClient::PubSubClient(IPAddress addr, uint16_t port, MQTT_CALLBACK_SIGNATUR setCallback(callback); setClient(client); setStream(stream); + this->bufferSize = 0; + setBufferSize(MQTT_MAX_PACKET_SIZE); + setKeepAlive(MQTT_KEEPALIVE); + setSocketTimeout(MQTT_SOCKET_TIMEOUT); } PubSubClient::PubSubClient(uint8_t *ip, uint16_t port, Client& client) { @@ -52,12 +76,20 @@ PubSubClient::PubSubClient(uint8_t *ip, uint16_t port, Client& client) { setServer(ip, port); setClient(client); this->stream = NULL; + this->bufferSize = 0; + setBufferSize(MQTT_MAX_PACKET_SIZE); + setKeepAlive(MQTT_KEEPALIVE); + setSocketTimeout(MQTT_SOCKET_TIMEOUT); } PubSubClient::PubSubClient(uint8_t *ip, uint16_t port, Client& client, Stream& stream) { this->_state = MQTT_DISCONNECTED; setServer(ip,port); setClient(client); setStream(stream); + this->bufferSize = 0; + setBufferSize(MQTT_MAX_PACKET_SIZE); + setKeepAlive(MQTT_KEEPALIVE); + setSocketTimeout(MQTT_SOCKET_TIMEOUT); } PubSubClient::PubSubClient(uint8_t *ip, uint16_t port, MQTT_CALLBACK_SIGNATURE, Client& client) { this->_state = MQTT_DISCONNECTED; @@ -65,6 +97,10 @@ PubSubClient::PubSubClient(uint8_t *ip, uint16_t port, MQTT_CALLBACK_SIGNATURE, setCallback(callback); setClient(client); this->stream = NULL; + this->bufferSize = 0; + setBufferSize(MQTT_MAX_PACKET_SIZE); + setKeepAlive(MQTT_KEEPALIVE); + setSocketTimeout(MQTT_SOCKET_TIMEOUT); } PubSubClient::PubSubClient(uint8_t *ip, uint16_t port, MQTT_CALLBACK_SIGNATURE, Client& client, Stream& stream) { this->_state = MQTT_DISCONNECTED; @@ -72,6 +108,10 @@ PubSubClient::PubSubClient(uint8_t *ip, uint16_t port, MQTT_CALLBACK_SIGNATURE, setCallback(callback); setClient(client); setStream(stream); + this->bufferSize = 0; + setBufferSize(MQTT_MAX_PACKET_SIZE); + setKeepAlive(MQTT_KEEPALIVE); + setSocketTimeout(MQTT_SOCKET_TIMEOUT); } PubSubClient::PubSubClient(const char* domain, uint16_t port, Client& client) { @@ -79,12 +119,20 @@ PubSubClient::PubSubClient(const char* domain, uint16_t port, Client& client) { setServer(domain,port); setClient(client); this->stream = NULL; + this->bufferSize = 0; + setBufferSize(MQTT_MAX_PACKET_SIZE); + setKeepAlive(MQTT_KEEPALIVE); + setSocketTimeout(MQTT_SOCKET_TIMEOUT); } PubSubClient::PubSubClient(const char* domain, uint16_t port, Client& client, Stream& stream) { this->_state = MQTT_DISCONNECTED; setServer(domain,port); setClient(client); setStream(stream); + this->bufferSize = 0; + setBufferSize(MQTT_MAX_PACKET_SIZE); + setKeepAlive(MQTT_KEEPALIVE); + setSocketTimeout(MQTT_SOCKET_TIMEOUT); } PubSubClient::PubSubClient(const char* domain, uint16_t port, MQTT_CALLBACK_SIGNATURE, Client& client) { this->_state = MQTT_DISCONNECTED; @@ -92,6 +140,10 @@ PubSubClient::PubSubClient(const char* domain, uint16_t port, MQTT_CALLBACK_SIGN setCallback(callback); setClient(client); this->stream = NULL; + this->bufferSize = 0; + setBufferSize(MQTT_MAX_PACKET_SIZE); + setKeepAlive(MQTT_KEEPALIVE); + setSocketTimeout(MQTT_SOCKET_TIMEOUT); } PubSubClient::PubSubClient(const char* domain, uint16_t port, MQTT_CALLBACK_SIGNATURE, Client& client, Stream& stream) { this->_state = MQTT_DISCONNECTED; @@ -99,6 +151,14 @@ PubSubClient::PubSubClient(const char* domain, uint16_t port, MQTT_CALLBACK_SIGN setCallback(callback); setClient(client); setStream(stream); + this->bufferSize = 0; + setBufferSize(MQTT_MAX_PACKET_SIZE); + setKeepAlive(MQTT_KEEPALIVE); + setSocketTimeout(MQTT_SOCKET_TIMEOUT); +} + +PubSubClient::~PubSubClient() { + free(this->buffer); } boolean PubSubClient::connect(const char *id) { @@ -121,18 +181,29 @@ boolean PubSubClient::connect(const char *id, const char *user, const char *pass if (!connected()) { int result = 0; +// Start Tasmota patch if (_client == nullptr) { return false; } - if (_client->connected()) { +// End Tasmota patch + + if(_client->connected()) { result = 1; } else { + +// Start Tasmota patch +// if (domain != NULL) { +// result = _client->connect(this->domain, this->port); + if (domain.length() != 0) { result = _client->connect(this->domain.c_str(), this->port); +// End Tasmota patch + } else { result = _client->connect(this->ip, this->port); } } + if (result == 1) { nextMsgId = 1; // Leave room in the buffer for header and variable length field @@ -147,7 +218,7 @@ boolean PubSubClient::connect(const char *id, const char *user, const char *pass #define MQTT_HEADER_VERSION_LENGTH 7 #endif for (j = 0;jbuffer[length++] = d[j]; } uint8_t v; @@ -167,45 +238,48 @@ boolean PubSubClient::connect(const char *id, const char *user, const char *pass v = v|(0x80>>1); } } + this->buffer[length++] = v; - buffer[length++] = v; - - buffer[length++] = ((MQTT_KEEPALIVE) >> 8); - buffer[length++] = ((MQTT_KEEPALIVE) & 0xFF); + this->buffer[length++] = ((this->keepAlive) >> 8); + this->buffer[length++] = ((this->keepAlive) & 0xFF); CHECK_STRING_LENGTH(length,id) - length = writeString(id,buffer,length); + length = writeString(id,this->buffer,length); if (willTopic) { CHECK_STRING_LENGTH(length,willTopic) - length = writeString(willTopic,buffer,length); + length = writeString(willTopic,this->buffer,length); CHECK_STRING_LENGTH(length,willMessage) - length = writeString(willMessage,buffer,length); + length = writeString(willMessage,this->buffer,length); } if(user != NULL) { CHECK_STRING_LENGTH(length,user) - length = writeString(user,buffer,length); + length = writeString(user,this->buffer,length); if(pass != NULL) { CHECK_STRING_LENGTH(length,pass) - length = writeString(pass,buffer,length); + length = writeString(pass,this->buffer,length); } } - write(MQTTCONNECT,buffer,length-MQTT_MAX_HEADER_SIZE); + write(MQTTCONNECT,this->buffer,length-MQTT_MAX_HEADER_SIZE); lastInActivity = lastOutActivity = millis(); while (!_client->available()) { + +// Start Tasmota patch delay(0); // Prevent watchdog crashes +// End Tasmota patch + unsigned long t = millis(); - if (t-lastInActivity >= ((int32_t) MQTT_SOCKET_TIMEOUT*1000UL)) { + if (t-lastInActivity >= ((int32_t) this->socketTimeout*1000UL)) { _state = MQTT_CONNECTION_TIMEOUT; _client->stop(); return false; } } uint8_t llen; - uint16_t len = readPacket(&llen); + uint32_t len = readPacket(&llen); if (len == 4) { if (buffer[3] == 0) { @@ -228,14 +302,24 @@ boolean PubSubClient::connect(const char *id, const char *user, const char *pass // reads a byte into result boolean PubSubClient::readByte(uint8_t * result) { + +// Start Tasmota patch if (_client == nullptr) { return false; } +// End Tasmota patch + uint32_t previousMillis = millis(); while(!_client->available()) { + +// Start Tasmota patch +// yield(); + delay(1); // Prevent watchdog crashes +// End Tasmota patch + uint32_t currentMillis = millis(); - if(currentMillis - previousMillis >= ((int32_t) MQTT_SOCKET_TIMEOUT * 1000)){ + if(currentMillis - previousMillis >= ((int32_t) this->socketTimeout * 1000)){ return false; } } @@ -254,15 +338,15 @@ boolean PubSubClient::readByte(uint8_t * result, uint16_t * index){ return false; } -uint16_t PubSubClient::readPacket(uint8_t* lengthLength) { +uint32_t PubSubClient::readPacket(uint8_t* lengthLength) { uint16_t len = 0; - if(!readByte(buffer, &len)) return 0; - bool isPublish = (buffer[0]&0xF0) == MQTTPUBLISH; + if(!readByte(this->buffer, &len)) return 0; + bool isPublish = (this->buffer[0]&0xF0) == MQTTPUBLISH; uint32_t multiplier = 1; - uint16_t length = 0; + uint32_t length = 0; uint8_t digit = 0; uint16_t skip = 0; - uint8_t start = 0; + uint32_t start = 0; do { if (len == 5) { @@ -272,59 +356,75 @@ uint16_t PubSubClient::readPacket(uint8_t* lengthLength) { return 0; } if(!readByte(&digit)) return 0; - buffer[len++] = digit; + this->buffer[len++] = digit; length += (digit & 127) * multiplier; - multiplier *= 128; - } while ((digit & 128) != 0 && len < (MQTT_MAX_PACKET_SIZE -2)); + multiplier <<=7; //multiplier *= 128 + +// Start Tasmota patch +// } while ((digit & 128) != 0); + + } while ((digit & 128) != 0 && len < (this->bufferSize -2)); +// End Tasmota patch + *lengthLength = len-1; if (isPublish) { // Read in topic length to calculate bytes to skip over for Stream writing - if(!readByte(buffer, &len)) return 0; - if(!readByte(buffer, &len)) return 0; - skip = (buffer[*lengthLength+1]<<8)+buffer[*lengthLength+2]; + if(!readByte(this->buffer, &len)) return 0; + if(!readByte(this->buffer, &len)) return 0; + skip = (this->buffer[*lengthLength+1]<<8)+this->buffer[*lengthLength+2]; start = 2; - if (buffer[0]&MQTTQOS1) { + if (this->buffer[0]&MQTTQOS1) { // skip message id skip += 2; } } + uint32_t idx = len; - for (uint16_t i = start;istream) { - if (isPublish && len-*lengthLength-2>skip) { + if (isPublish && idx-*lengthLength-2>skip) { this->stream->write(digit); } } - if (len < MQTT_MAX_PACKET_SIZE) { - buffer[len] = digit; + + if (len < this->bufferSize) { + this->buffer[len] = digit; + len++; } - len++; + idx++; } - if (!this->stream && len > MQTT_MAX_PACKET_SIZE) { + if (!this->stream && idx > this->bufferSize) { len = 0; // This will cause the packet to be ignored. } - return len; } boolean PubSubClient::loop() { if (connected()) { unsigned long t = millis(); - if ((t - lastInActivity > MQTT_KEEPALIVE*1000UL) || (t - lastOutActivity > MQTT_KEEPALIVE*1000UL)) { + if ((t - lastInActivity > this->keepAlive*1000UL) || (t - lastOutActivity > this->keepAlive*1000UL)) { if (pingOutstanding) { this->_state = MQTT_CONNECTION_TIMEOUT; _client->stop(); return false; } else { - buffer[0] = MQTTPINGREQ; - buffer[1] = 0; - if (_client->write(buffer,2) != 0) { + this->buffer[0] = MQTTPINGREQ; + this->buffer[1] = 0; + +// Start Tasmota patch +// _client->write(this->buffer,2); +// lastOutActivity = t; +// lastInActivity = t; + + if (_client->write(this->buffer,2) != 0) { lastOutActivity = t; lastInActivity = t; } +// End Tasmota patch + pingOutstanding = true; } } @@ -335,35 +435,42 @@ boolean PubSubClient::loop() { uint8_t *payload; if (len > 0) { lastInActivity = t; - uint8_t type = buffer[0]&0xF0; + uint8_t type = this->buffer[0]&0xF0; if (type == MQTTPUBLISH) { if (callback) { - uint16_t tl = (buffer[llen+1]<<8)+buffer[llen+2]; /* topic length in bytes */ - memmove(buffer+llen+2,buffer+llen+3,tl); /* move topic inside buffer 1 byte to front */ - buffer[llen+2+tl] = 0; /* end the topic as a 'C' string with \x00 */ - char *topic = (char*) buffer+llen+2; + uint16_t tl = (this->buffer[llen+1]<<8)+this->buffer[llen+2]; /* topic length in bytes */ + memmove(this->buffer+llen+2,this->buffer+llen+3,tl); /* move topic inside buffer 1 byte to front */ + this->buffer[llen+2+tl] = 0; /* end the topic as a 'C' string with \x00 */ + char *topic = (char*) this->buffer+llen+2; // msgId only present for QOS>0 - if ((buffer[0]&0x06) == MQTTQOS1) { - msgId = (buffer[llen+3+tl]<<8)+buffer[llen+3+tl+1]; - payload = buffer+llen+3+tl+2; + if ((this->buffer[0]&0x06) == MQTTQOS1) { + msgId = (this->buffer[llen+3+tl]<<8)+this->buffer[llen+3+tl+1]; + payload = this->buffer+llen+3+tl+2; callback(topic,payload,len-llen-3-tl-2); - buffer[0] = MQTTPUBACK; - buffer[1] = 2; - buffer[2] = (msgId >> 8); - buffer[3] = (msgId & 0xFF); - if (_client->write(buffer,4) != 0) { + this->buffer[0] = MQTTPUBACK; + this->buffer[1] = 2; + this->buffer[2] = (msgId >> 8); + this->buffer[3] = (msgId & 0xFF); + +// Start Tasmota patch +// _client->write(this->buffer,4); +// lastOutActivity = t; + + if (_client->write(this->buffer,4) != 0) { lastOutActivity = t; } +// End Tasmota patch + } else { - payload = buffer+llen+3+tl; + payload = this->buffer+llen+3+tl; callback(topic,payload,len-llen-3-tl); } } } else if (type == MQTTPINGREQ) { - buffer[0] = MQTTPINGRESP; - buffer[1] = 0; - _client->write(buffer,2); + this->buffer[0] = MQTTPINGRESP; + this->buffer[1] = 0; + _client->write(this->buffer,2); } else if (type == MQTTPINGRESP) { pingOutstanding = false; } @@ -378,13 +485,11 @@ boolean PubSubClient::loop() { } boolean PubSubClient::publish(const char* topic, const char* payload) { - size_t plength = (payload != nullptr) ? strlen(payload) : 0; - return publish(topic,(const uint8_t*)payload,plength,false); + return publish(topic,(const uint8_t*)payload, payload ? strnlen(payload, this->bufferSize) : 0,false); } boolean PubSubClient::publish(const char* topic, const char* payload, boolean retained) { - size_t plength = (payload != nullptr) ? strlen(payload) : 0; - return publish(topic,(const uint8_t*)payload,plength,retained); + return publish(topic,(const uint8_t*)payload, payload ? strnlen(payload, this->bufferSize) : 0,retained); } boolean PubSubClient::publish(const char* topic, const uint8_t* payload, unsigned int plength) { @@ -393,29 +498,32 @@ boolean PubSubClient::publish(const char* topic, const uint8_t* payload, unsigne boolean PubSubClient::publish(const char* topic, const uint8_t* payload, unsigned int plength, boolean retained) { if (connected()) { - if (MQTT_MAX_PACKET_SIZE < MQTT_MAX_HEADER_SIZE + 2+strlen(topic) + plength) { + if (this->bufferSize < MQTT_MAX_HEADER_SIZE + 2+strnlen(topic, this->bufferSize) + plength) { // Too long return false; } // Leave room in the buffer for header and variable length field uint16_t length = MQTT_MAX_HEADER_SIZE; - length = writeString(topic,buffer,length); + length = writeString(topic,this->buffer,length); + + // Add payload uint16_t i; for (i=0;ibuffer[length++] = payload[i]; } + + // Write the header uint8_t header = MQTTPUBLISH; if (retained) { header |= 1; } - return write(header,buffer,length-MQTT_MAX_HEADER_SIZE); + return write(header,this->buffer,length-MQTT_MAX_HEADER_SIZE); } return false; } boolean PubSubClient::publish_P(const char* topic, const char* payload, boolean retained) { - size_t plength = (payload != nullptr) ? strlen(payload) : 0; - return publish_P(topic, (const uint8_t*)payload, plength, retained); + return publish_P(topic, (const uint8_t*)payload, payload ? strnlen(payload, this->bufferSize) : 0, retained); } boolean PubSubClient::publish_P(const char* topic, const uint8_t* payload, unsigned int plength, boolean retained) { @@ -427,42 +535,48 @@ boolean PubSubClient::publish_P(const char* topic, const uint8_t* payload, unsig unsigned int i; uint8_t header; unsigned int len; + int expectedLength; if (!connected()) { return false; } - tlen = strlen(topic); + tlen = strnlen(topic, this->bufferSize); header = MQTTPUBLISH; if (retained) { header |= 1; } - buffer[pos++] = header; + this->buffer[pos++] = header; len = plength + 2 + tlen; do { - digit = len % 128; - len = len / 128; + digit = len & 127; //digit = len %128 + len >>= 7; //len = len / 128 if (len > 0) { digit |= 0x80; } - buffer[pos++] = digit; + this->buffer[pos++] = digit; llen++; } while(len>0); - pos = writeString(topic,buffer,pos); + pos = writeString(topic,this->buffer,pos); - rc += _client->write(buffer,pos); + rc += _client->write(this->buffer,pos); for (i=0;iwrite((char)pgm_read_byte_near(payload + i)); } + +// Start Tasmota patch +// lastOutActivity = millis(); + if (rc > 0) { lastOutActivity = millis(); } +// End Tasmota patch + + expectedLength = 1 + llen + 2 + tlen + plength; - // Header (1 byte) + llen + identifier (2 bytes) + topic len + payload len - const unsigned int expectedLength = 1 + llen + 2 + tlen + plength; return (rc == expectedLength); } @@ -470,16 +584,22 @@ boolean PubSubClient::beginPublish(const char* topic, unsigned int plength, bool if (connected()) { // Send the header and variable length field uint16_t length = MQTT_MAX_HEADER_SIZE; - length = writeString(topic,buffer,length); + length = writeString(topic,this->buffer,length); uint8_t header = MQTTPUBLISH; if (retained) { header |= 1; } - size_t hlen = buildHeader(header, buffer, plength+length-MQTT_MAX_HEADER_SIZE); - uint16_t rc = _client->write(buffer+(MQTT_MAX_HEADER_SIZE-hlen),length-(MQTT_MAX_HEADER_SIZE-hlen)); + size_t hlen = buildHeader(header, this->buffer, plength+length-MQTT_MAX_HEADER_SIZE); + uint16_t rc = _client->write(this->buffer+(MQTT_MAX_HEADER_SIZE-hlen),length-(MQTT_MAX_HEADER_SIZE-hlen)); + +// Start Tasmota patch +// lastOutActivity = millis(); + if (rc > 0) { lastOutActivity = millis(); } +// End Tasmota patch + return (rc == (length-(MQTT_MAX_HEADER_SIZE-hlen))); } return false; @@ -490,6 +610,11 @@ int PubSubClient::endPublish() { } size_t PubSubClient::write(uint8_t data) { + +// Start Tasmota patch +// lastOutActivity = millis(); +// return _client->write(data); + if (_client == nullptr) { lastOutActivity = millis(); return 0; @@ -499,9 +624,16 @@ size_t PubSubClient::write(uint8_t data) { lastOutActivity = millis(); } return rc; +// End Tasmota patch + } size_t PubSubClient::write(const uint8_t *buffer, size_t size) { + +// Start Tasmota patch +// lastOutActivity = millis(); +// return _client->write(buffer,size); + if (_client == nullptr) { lastOutActivity = millis(); return 0; @@ -511,6 +643,8 @@ size_t PubSubClient::write(const uint8_t *buffer, size_t size) { lastOutActivity = millis(); } return rc; +// End Tasmota patch + } size_t PubSubClient::buildHeader(uint8_t header, uint8_t* buf, uint16_t length) { @@ -520,8 +654,9 @@ size_t PubSubClient::buildHeader(uint8_t header, uint8_t* buf, uint16_t length) uint8_t pos = 0; uint16_t len = length; do { - digit = len % 128; - len = len / 128; + + digit = len & 127; //digit = len %128 + len >>= 7; //len = len / 128 if (len > 0) { digit |= 0x80; } @@ -546,7 +681,6 @@ boolean PubSubClient::write(uint8_t header, uint8_t* buf, uint16_t length) { uint8_t bytesToWrite; boolean result = true; while((bytesRemaining > 0) && result) { - delay(0); // Prevent watchdog crashes bytesToWrite = (bytesRemaining > MQTT_MAX_TRANSFER_SIZE)?MQTT_MAX_TRANSFER_SIZE:bytesRemaining; rc = _client->write(writeBuf,bytesToWrite); result = (rc == bytesToWrite); @@ -556,9 +690,15 @@ boolean PubSubClient::write(uint8_t header, uint8_t* buf, uint16_t length) { return result; #else rc = _client->write(buf+(MQTT_MAX_HEADER_SIZE-hlen),length+hlen); + +// Start Tasmota patch +// lastOutActivity = millis(); + if (rc != 0) { lastOutActivity = millis(); } +// End Tasmota patch + return (rc == hlen+length); #endif } @@ -568,10 +708,14 @@ boolean PubSubClient::subscribe(const char* topic) { } boolean PubSubClient::subscribe(const char* topic, uint8_t qos) { + size_t topicLength = strnlen(topic, this->bufferSize); + if (topic == 0) { + return false; + } if (qos > 1) { return false; } - if (MQTT_MAX_PACKET_SIZE < 9 + strlen(topic)) { + if (this->bufferSize < 9 + topicLength) { // Too long return false; } @@ -582,17 +726,21 @@ boolean PubSubClient::subscribe(const char* topic, uint8_t qos) { if (nextMsgId == 0) { nextMsgId = 1; } - buffer[length++] = (nextMsgId >> 8); - buffer[length++] = (nextMsgId & 0xFF); - length = writeString((char*)topic, buffer,length); - buffer[length++] = qos; - return write(MQTTSUBSCRIBE|MQTTQOS1,buffer,length-MQTT_MAX_HEADER_SIZE); + this->buffer[length++] = (nextMsgId >> 8); + this->buffer[length++] = (nextMsgId & 0xFF); + length = writeString((char*)topic, this->buffer,length); + this->buffer[length++] = qos; + return write(MQTTSUBSCRIBE|MQTTQOS1,this->buffer,length-MQTT_MAX_HEADER_SIZE); } return false; } boolean PubSubClient::unsubscribe(const char* topic) { - if (MQTT_MAX_PACKET_SIZE < 9 + strlen(topic)) { + size_t topicLength = strnlen(topic, this->bufferSize); + if (topic == 0) { + return false; + } + if (this->bufferSize < 9 + topicLength) { // Too long return false; } @@ -602,25 +750,34 @@ boolean PubSubClient::unsubscribe(const char* topic) { if (nextMsgId == 0) { nextMsgId = 1; } - buffer[length++] = (nextMsgId >> 8); - buffer[length++] = (nextMsgId & 0xFF); - length = writeString(topic, buffer,length); - return write(MQTTUNSUBSCRIBE|MQTTQOS1,buffer,length-MQTT_MAX_HEADER_SIZE); + this->buffer[length++] = (nextMsgId >> 8); + this->buffer[length++] = (nextMsgId & 0xFF); + length = writeString(topic, this->buffer,length); + return write(MQTTUNSUBSCRIBE|MQTTQOS1,this->buffer,length-MQTT_MAX_HEADER_SIZE); } return false; } void PubSubClient::disconnect(bool disconnect_package) { - buffer[0] = MQTTDISCONNECT; - buffer[1] = 0; + this->buffer[0] = MQTTDISCONNECT; + this->buffer[1] = 0; + +// Start Tasmota patch +// _client->write(this->buffer,2); +// _state = MQTT_DISCONNECTED; +// _client->flush(); +// _client->stop(); + if (_client != nullptr) { if (disconnect_package) { - _client->write(buffer,2); + _client->write(this->buffer,2); } _client->flush(); _client->stop(); } _state = MQTT_DISCONNECTED; +// End Tasmota patch + lastInActivity = lastOutActivity = millis(); } @@ -628,7 +785,7 @@ uint16_t PubSubClient::writeString(const char* string, uint8_t* buf, uint16_t po const char* idp = string; uint16_t i = 0; pos += 2; - while (*idp && pos < (MQTT_MAX_PACKET_SIZE - 2)) { + while (*idp) { buf[pos++] = *idp++; i++; } @@ -639,19 +796,27 @@ uint16_t PubSubClient::writeString(const char* string, uint8_t* buf, uint16_t po boolean PubSubClient::connected() { + boolean rc; if (_client == NULL ) { + +// Start Tasmota patch this->_state = MQTT_DISCONNECTED; - return false; - } - if (_client->connected() == 0) { - bool lastStateConnected = this->_state == MQTT_CONNECTED; - this->disconnect(); - if (lastStateConnected) { - this->_state = MQTT_CONNECTION_LOST; +// End Tasmota patch + + rc = false; + } else { + rc = (int)_client->connected(); + if (!rc) { + if (this->_state == MQTT_CONNECTED) { + this->_state = MQTT_CONNECTION_LOST; + _client->flush(); + _client->stop(); + } + } else { + return this->_state == MQTT_CONNECTED; } - return false; } - return this->_state == MQTT_CONNECTED; + return rc; } PubSubClient& PubSubClient::setServer(uint8_t * ip, uint16_t port) { @@ -662,7 +827,13 @@ PubSubClient& PubSubClient::setServer(uint8_t * ip, uint16_t port) { PubSubClient& PubSubClient::setServer(IPAddress ip, uint16_t port) { this->ip = ip; this->port = port; + +// Start Tasmota patch +// this->domain = NULL; + this->domain = ""; +// End Tasmota patch + return *this; } @@ -690,3 +861,34 @@ PubSubClient& PubSubClient::setStream(Stream& stream){ int PubSubClient::state() { return this->_state; } + +boolean PubSubClient::setBufferSize(uint16_t size) { + if (size == 0) { + // Cannot set it back to 0 + return false; + } + if (this->bufferSize == 0) { + this->buffer = (uint8_t*)malloc(size); + } else { + uint8_t* newBuffer = (uint8_t*)realloc(this->buffer, size); + if (newBuffer != NULL) { + this->buffer = newBuffer; + } else { + return false; + } + } + this->bufferSize = size; + return (this->buffer != NULL); +} + +uint16_t PubSubClient::getBufferSize() { + return this->bufferSize; +} +PubSubClient& PubSubClient::setKeepAlive(uint16_t keepAlive) { + this->keepAlive = keepAlive; + return *this; +} +PubSubClient& PubSubClient::setSocketTimeout(uint16_t timeout) { + this->socketTimeout = timeout; + return *this; +} diff --git a/lib/default/PubSubClient-EspEasy-2.7.12/src/PubSubClient.h b/lib/default/pubsubclient-2.8.12/src/PubSubClient.h similarity index 88% rename from lib/default/PubSubClient-EspEasy-2.7.12/src/PubSubClient.h rename to lib/default/pubsubclient-2.8.12/src/PubSubClient.h index 19b35160a..4edf5ec57 100644 --- a/lib/default/PubSubClient-EspEasy-2.7.12/src/PubSubClient.h +++ b/lib/default/pubsubclient-2.8.12/src/PubSubClient.h @@ -21,24 +21,20 @@ #define MQTT_VERSION MQTT_VERSION_3_1_1 #endif -// MQTT_MAX_PACKET_SIZE : Maximum packet size +// MQTT_MAX_PACKET_SIZE : Maximum packet size. Override with setBufferSize(). #ifndef MQTT_MAX_PACKET_SIZE -//#define MQTT_MAX_PACKET_SIZE 128 -//#define MQTT_MAX_PACKET_SIZE 1000 // Tasmota v5.11.1c +//#define MQTT_MAX_PACKET_SIZE 256 #define MQTT_MAX_PACKET_SIZE 1200 // Tasmota v8.1.0.8 #endif -// MQTT_KEEPALIVE : keepAlive interval in Seconds -// Keepalive timeout for default MQTT Broker is 10s +// MQTT_KEEPALIVE : keepAlive interval in Seconds. Override with setKeepAlive() #ifndef MQTT_KEEPALIVE -//#define MQTT_KEEPALIVE 10 -#define MQTT_KEEPALIVE 30 // Tasmota v6.5.0.14 enabling AWS-iot +#define MQTT_KEEPALIVE 15 #endif -// MQTT_SOCKET_TIMEOUT: socket timeout interval in Seconds +// MQTT_SOCKET_TIMEOUT: socket timeout interval in Seconds. Override with setSocketTimeout() #ifndef MQTT_SOCKET_TIMEOUT -//#define MQTT_SOCKET_TIMEOUT 15 -#define MQTT_SOCKET_TIMEOUT 4 // Tasmota 20210120 +#define MQTT_SOCKET_TIMEOUT 15 #endif // MQTT_MAX_TRANSFER_SIZE : limit how much data is passed to the network client @@ -88,18 +84,21 @@ #define MQTT_CALLBACK_SIGNATURE void (*callback)(char*, uint8_t*, unsigned int) #endif -#define CHECK_STRING_LENGTH(l,s) if (l+2+strlen(s) > MQTT_MAX_PACKET_SIZE) {_client->stop();return false;} +#define CHECK_STRING_LENGTH(l,s) if (l+2+strnlen(s, this->bufferSize) > this->bufferSize) {_client->stop();return false;} class PubSubClient : public Print { private: Client* _client; - uint8_t buffer[MQTT_MAX_PACKET_SIZE]; + uint8_t* buffer; + uint16_t bufferSize; + uint16_t keepAlive; + uint16_t socketTimeout; uint16_t nextMsgId; unsigned long lastOutActivity; unsigned long lastInActivity; bool pingOutstanding; MQTT_CALLBACK_SIGNATURE; - uint16_t readPacket(uint8_t*); + uint32_t readPacket(uint8_t*); boolean readByte(uint8_t * result); boolean readByte(uint8_t * result, uint16_t * index); boolean write(uint8_t header, uint8_t* buf, uint16_t length); @@ -110,7 +109,13 @@ private: // (MQTT_MAX_HEADER_SIZE - ) bytes into the buffer size_t buildHeader(uint8_t header, uint8_t* buf, uint16_t length); IPAddress ip; + +// Start Tasmota patch +// const char* domain; + String domain; +// End Tasmota patch + uint16_t port; Stream* stream; int _state; @@ -129,7 +134,8 @@ public: PubSubClient(const char*, uint16_t, Client& client, Stream&); PubSubClient(const char*, uint16_t, MQTT_CALLBACK_SIGNATURE,Client& client); PubSubClient(const char*, uint16_t, MQTT_CALLBACK_SIGNATURE,Client& client, Stream&); - virtual ~PubSubClient() {} + + ~PubSubClient(); PubSubClient& setServer(IPAddress ip, uint16_t port); PubSubClient& setServer(uint8_t * ip, uint16_t port); @@ -137,13 +143,24 @@ public: PubSubClient& setCallback(MQTT_CALLBACK_SIGNATURE); PubSubClient& setClient(Client& client); PubSubClient& setStream(Stream& stream); + PubSubClient& setKeepAlive(uint16_t keepAlive); + PubSubClient& setSocketTimeout(uint16_t timeout); + + boolean setBufferSize(uint16_t size); + uint16_t getBufferSize(); boolean connect(const char* id); boolean connect(const char* id, const char* user, const char* pass); boolean connect(const char* id, const char* willTopic, uint8_t willQos, boolean willRetain, const char* willMessage); boolean connect(const char* id, const char* user, const char* pass, const char* willTopic, uint8_t willQos, boolean willRetain, const char* willMessage); boolean connect(const char* id, const char* user, const char* pass, const char* willTopic, uint8_t willQos, boolean willRetain, const char* willMessage, boolean cleanSession); + +// Start Tasmota patch +// void disconnect(); + void disconnect(bool disconnect_package = false); +// End Tasmota patch + boolean publish(const char* topic, const char* payload); boolean publish(const char* topic, const char* payload, boolean retained); boolean publish(const char* topic, const uint8_t * payload, unsigned int plength); @@ -173,6 +190,7 @@ public: boolean loop(); boolean connected(); int state(); + }; diff --git a/lib/default/PubSubClient-EspEasy-2.7.12/tests/.gitignore b/lib/default/pubsubclient-2.8.12/tests/.gitignore similarity index 100% rename from lib/default/PubSubClient-EspEasy-2.7.12/tests/.gitignore rename to lib/default/pubsubclient-2.8.12/tests/.gitignore diff --git a/lib/default/PubSubClient-EspEasy-2.7.12/tests/Makefile b/lib/default/pubsubclient-2.8.12/tests/Makefile similarity index 100% rename from lib/default/PubSubClient-EspEasy-2.7.12/tests/Makefile rename to lib/default/pubsubclient-2.8.12/tests/Makefile diff --git a/lib/default/PubSubClient-EspEasy-2.7.12/tests/README.md b/lib/default/pubsubclient-2.8.12/tests/README.md similarity index 100% rename from lib/default/PubSubClient-EspEasy-2.7.12/tests/README.md rename to lib/default/pubsubclient-2.8.12/tests/README.md diff --git a/lib/default/PubSubClient-EspEasy-2.7.12/tests/src/connect_spec.cpp b/lib/default/pubsubclient-2.8.12/tests/src/connect_spec.cpp similarity index 90% rename from lib/default/PubSubClient-EspEasy-2.7.12/tests/src/connect_spec.cpp rename to lib/default/pubsubclient-2.8.12/tests/src/connect_spec.cpp index e27a1f59f..e8545c49d 100644 --- a/lib/default/PubSubClient-EspEasy-2.7.12/tests/src/connect_spec.cpp +++ b/lib/default/pubsubclient-2.8.12/tests/src/connect_spec.cpp @@ -280,6 +280,38 @@ int test_connect_disconnect_connect() { END_IT } +int test_connect_custom_keepalive() { + IT("sends a properly formatted connect packet with custom keepalive value"); + ShimClient shimClient; + + shimClient.setAllowConnect(true); + byte expectServer[] = { 172, 16, 0, 2 }; + shimClient.expectConnect(expectServer,1883); + + // Set keepalive to 300secs == 0x01 0x2c + byte connect[] = {0x10,0x18,0x0,0x4,0x4d,0x51,0x54,0x54,0x4,0x2,0x01,0x2c,0x0,0xc,0x63,0x6c,0x69,0x65,0x6e,0x74,0x5f,0x74,0x65,0x73,0x74,0x31}; + byte connack[] = { 0x20, 0x02, 0x00, 0x00 }; + + shimClient.expect(connect,26); + shimClient.respond(connack,4); + + PubSubClient client(server, 1883, callback, shimClient); + int state = client.state(); + IS_TRUE(state == MQTT_DISCONNECTED); + + client.setKeepAlive(300); + + int rc = client.connect((char*)"client_test1"); + IS_TRUE(rc); + IS_FALSE(shimClient.error()); + + state = client.state(); + IS_TRUE(state == MQTT_CONNECTED); + + END_IT +} + + int main() { SUITE("Connect"); @@ -298,5 +330,7 @@ int main() test_connect_with_will(); test_connect_with_will_username_password(); test_connect_disconnect_connect(); + + test_connect_custom_keepalive(); FINISH } diff --git a/lib/default/PubSubClient-EspEasy-2.7.12/tests/src/keepalive_spec.cpp b/lib/default/pubsubclient-2.8.12/tests/src/keepalive_spec.cpp similarity index 100% rename from lib/default/PubSubClient-EspEasy-2.7.12/tests/src/keepalive_spec.cpp rename to lib/default/pubsubclient-2.8.12/tests/src/keepalive_spec.cpp diff --git a/lib/default/PubSubClient-EspEasy-2.7.12/tests/src/lib/Arduino.h b/lib/default/pubsubclient-2.8.12/tests/src/lib/Arduino.h similarity index 100% rename from lib/default/PubSubClient-EspEasy-2.7.12/tests/src/lib/Arduino.h rename to lib/default/pubsubclient-2.8.12/tests/src/lib/Arduino.h diff --git a/lib/default/PubSubClient-EspEasy-2.7.12/tests/src/lib/BDDTest.cpp b/lib/default/pubsubclient-2.8.12/tests/src/lib/BDDTest.cpp similarity index 100% rename from lib/default/PubSubClient-EspEasy-2.7.12/tests/src/lib/BDDTest.cpp rename to lib/default/pubsubclient-2.8.12/tests/src/lib/BDDTest.cpp diff --git a/lib/default/PubSubClient-EspEasy-2.7.12/tests/src/lib/BDDTest.h b/lib/default/pubsubclient-2.8.12/tests/src/lib/BDDTest.h similarity index 100% rename from lib/default/PubSubClient-EspEasy-2.7.12/tests/src/lib/BDDTest.h rename to lib/default/pubsubclient-2.8.12/tests/src/lib/BDDTest.h diff --git a/lib/default/PubSubClient-EspEasy-2.7.12/tests/src/lib/Buffer.cpp b/lib/default/pubsubclient-2.8.12/tests/src/lib/Buffer.cpp similarity index 100% rename from lib/default/PubSubClient-EspEasy-2.7.12/tests/src/lib/Buffer.cpp rename to lib/default/pubsubclient-2.8.12/tests/src/lib/Buffer.cpp diff --git a/lib/default/PubSubClient-EspEasy-2.7.12/tests/src/lib/Buffer.h b/lib/default/pubsubclient-2.8.12/tests/src/lib/Buffer.h similarity index 88% rename from lib/default/PubSubClient-EspEasy-2.7.12/tests/src/lib/Buffer.h rename to lib/default/pubsubclient-2.8.12/tests/src/lib/Buffer.h index f448cade8..c6a2cb584 100644 --- a/lib/default/PubSubClient-EspEasy-2.7.12/tests/src/lib/Buffer.h +++ b/lib/default/pubsubclient-2.8.12/tests/src/lib/Buffer.h @@ -5,18 +5,18 @@ class Buffer { private: - uint8_t buffer[1024]; + uint8_t buffer[2048]; uint16_t pos; uint16_t length; - + public: Buffer(); Buffer(uint8_t* buf, size_t size); - + virtual bool available(); virtual uint8_t next(); virtual void reset(); - + virtual void add(uint8_t* buf, size_t size); }; diff --git a/lib/default/PubSubClient-EspEasy-2.7.12/tests/src/lib/Client.h b/lib/default/pubsubclient-2.8.12/tests/src/lib/Client.h similarity index 100% rename from lib/default/PubSubClient-EspEasy-2.7.12/tests/src/lib/Client.h rename to lib/default/pubsubclient-2.8.12/tests/src/lib/Client.h diff --git a/lib/default/PubSubClient-EspEasy-2.7.12/tests/src/lib/IPAddress.cpp b/lib/default/pubsubclient-2.8.12/tests/src/lib/IPAddress.cpp similarity index 100% rename from lib/default/PubSubClient-EspEasy-2.7.12/tests/src/lib/IPAddress.cpp rename to lib/default/pubsubclient-2.8.12/tests/src/lib/IPAddress.cpp diff --git a/lib/default/PubSubClient-EspEasy-2.7.12/tests/src/lib/IPAddress.h b/lib/default/pubsubclient-2.8.12/tests/src/lib/IPAddress.h similarity index 100% rename from lib/default/PubSubClient-EspEasy-2.7.12/tests/src/lib/IPAddress.h rename to lib/default/pubsubclient-2.8.12/tests/src/lib/IPAddress.h diff --git a/lib/default/PubSubClient-EspEasy-2.7.12/tests/src/lib/Print.h b/lib/default/pubsubclient-2.8.12/tests/src/lib/Print.h similarity index 100% rename from lib/default/PubSubClient-EspEasy-2.7.12/tests/src/lib/Print.h rename to lib/default/pubsubclient-2.8.12/tests/src/lib/Print.h diff --git a/lib/default/PubSubClient-EspEasy-2.7.12/tests/src/lib/ShimClient.cpp b/lib/default/pubsubclient-2.8.12/tests/src/lib/ShimClient.cpp similarity index 100% rename from lib/default/PubSubClient-EspEasy-2.7.12/tests/src/lib/ShimClient.cpp rename to lib/default/pubsubclient-2.8.12/tests/src/lib/ShimClient.cpp diff --git a/lib/default/PubSubClient-EspEasy-2.7.12/tests/src/lib/ShimClient.h b/lib/default/pubsubclient-2.8.12/tests/src/lib/ShimClient.h similarity index 100% rename from lib/default/PubSubClient-EspEasy-2.7.12/tests/src/lib/ShimClient.h rename to lib/default/pubsubclient-2.8.12/tests/src/lib/ShimClient.h diff --git a/lib/default/PubSubClient-EspEasy-2.7.12/tests/src/lib/Stream.cpp b/lib/default/pubsubclient-2.8.12/tests/src/lib/Stream.cpp similarity index 100% rename from lib/default/PubSubClient-EspEasy-2.7.12/tests/src/lib/Stream.cpp rename to lib/default/pubsubclient-2.8.12/tests/src/lib/Stream.cpp diff --git a/lib/default/PubSubClient-EspEasy-2.7.12/tests/src/lib/Stream.h b/lib/default/pubsubclient-2.8.12/tests/src/lib/Stream.h similarity index 100% rename from lib/default/PubSubClient-EspEasy-2.7.12/tests/src/lib/Stream.h rename to lib/default/pubsubclient-2.8.12/tests/src/lib/Stream.h diff --git a/lib/default/PubSubClient-EspEasy-2.7.12/tests/src/lib/trace.h b/lib/default/pubsubclient-2.8.12/tests/src/lib/trace.h similarity index 100% rename from lib/default/PubSubClient-EspEasy-2.7.12/tests/src/lib/trace.h rename to lib/default/pubsubclient-2.8.12/tests/src/lib/trace.h diff --git a/lib/default/PubSubClient-EspEasy-2.7.12/tests/src/publish_spec.cpp b/lib/default/pubsubclient-2.8.12/tests/src/publish_spec.cpp similarity index 99% rename from lib/default/PubSubClient-EspEasy-2.7.12/tests/src/publish_spec.cpp rename to lib/default/pubsubclient-2.8.12/tests/src/publish_spec.cpp index 232df0d37..ee3d3bedb 100644 --- a/lib/default/PubSubClient-EspEasy-2.7.12/tests/src/publish_spec.cpp +++ b/lib/default/pubsubclient-2.8.12/tests/src/publish_spec.cpp @@ -134,6 +134,7 @@ int test_publish_too_long() { shimClient.respond(connack,4); PubSubClient client(server, 1883, callback, shimClient); + client.setBufferSize(128); int rc = client.connect((char*)"client_test1"); IS_TRUE(rc); diff --git a/lib/default/PubSubClient-EspEasy-2.7.12/tests/src/receive_spec.cpp b/lib/default/pubsubclient-2.8.12/tests/src/receive_spec.cpp similarity index 75% rename from lib/default/PubSubClient-EspEasy-2.7.12/tests/src/receive_spec.cpp rename to lib/default/pubsubclient-2.8.12/tests/src/receive_spec.cpp index 9a18af042..93e909aeb 100644 --- a/lib/default/PubSubClient-EspEasy-2.7.12/tests/src/receive_spec.cpp +++ b/lib/default/pubsubclient-2.8.12/tests/src/receive_spec.cpp @@ -20,6 +20,7 @@ void reset_callback() { } void callback(char* topic, byte* payload, unsigned int length) { + TRACE("Callback received topic=[" << topic << "] length=" << length << "\n") callback_called = true; strcpy(lastTopic,topic); memcpy(lastPayload,payload,length); @@ -102,10 +103,15 @@ int test_receive_max_sized_message() { shimClient.respond(connack,4); PubSubClient client(server, 1883, callback, shimClient); + int length = 80; // If this is changed to > 128 then the publish packet below + // is no longer valid as it assumes the remaining length + // is a single-byte. Don't make that mistake like I just + // did and lose a whole evening tracking down the issue. + client.setBufferSize(length); int rc = client.connect((char*)"client_test1"); IS_TRUE(rc); - int length = MQTT_MAX_PACKET_SIZE; + byte publish[] = {0x30,length-2,0x0,0x5,0x74,0x6f,0x70,0x69,0x63,0x70,0x61,0x79,0x6c,0x6f,0x61,0x64}; byte bigPublish[length]; memset(bigPublish,'A',length); @@ -137,11 +143,13 @@ int test_receive_oversized_message() { byte connack[] = { 0x20, 0x02, 0x00, 0x00 }; shimClient.respond(connack,4); + int length = 80; // See comment in test_receive_max_sized_message before changing this value + PubSubClient client(server, 1883, callback, shimClient); + client.setBufferSize(length-1); int rc = client.connect((char*)"client_test1"); IS_TRUE(rc); - int length = MQTT_MAX_PACKET_SIZE+1; byte publish[] = {0x30,length-2,0x0,0x5,0x74,0x6f,0x70,0x69,0x63,0x70,0x61,0x79,0x6c,0x6f,0x61,0x64}; byte bigPublish[length]; memset(bigPublish,'A',length); @@ -188,9 +196,58 @@ int test_drop_invalid_remaining_length_message() { END_IT } +int test_resize_buffer() { + IT("receives a message larger than the default maximum"); + reset_callback(); + + ShimClient shimClient; + shimClient.setAllowConnect(true); + + byte connack[] = { 0x20, 0x02, 0x00, 0x00 }; + shimClient.respond(connack,4); + + int length = 80; // See comment in test_receive_max_sized_message before changing this value + + PubSubClient client(server, 1883, callback, shimClient); + client.setBufferSize(length-1); + int rc = client.connect((char*)"client_test1"); + IS_TRUE(rc); + + byte publish[] = {0x30,length-2,0x0,0x5,0x74,0x6f,0x70,0x69,0x63,0x70,0x61,0x79,0x6c,0x6f,0x61,0x64}; + byte bigPublish[length]; + memset(bigPublish,'A',length); + bigPublish[length] = 'B'; + memcpy(bigPublish,publish,16); + // Send it twice + shimClient.respond(bigPublish,length); + shimClient.respond(bigPublish,length); + + rc = client.loop(); + IS_TRUE(rc); + + // First message fails as it is too big + IS_FALSE(callback_called); + + // Resize the buffer + client.setBufferSize(length); + + rc = client.loop(); + IS_TRUE(rc); + + IS_TRUE(callback_called); + + IS_TRUE(strcmp(lastTopic,"topic")==0); + IS_TRUE(lastLength == length-9); + IS_TRUE(memcmp(lastPayload,bigPublish+9,lastLength)==0); + + IS_FALSE(shimClient.error()); + + END_IT +} + int test_receive_oversized_stream_message() { - IT("drops an oversized message"); + IT("receive an oversized streamed message"); reset_callback(); Stream stream; @@ -201,11 +258,13 @@ int test_receive_oversized_stream_message() { byte connack[] = { 0x20, 0x02, 0x00, 0x00 }; shimClient.respond(connack,4); + int length = 80; // See comment in test_receive_max_sized_message before changing this value + PubSubClient client(server, 1883, callback, shimClient, stream); + client.setBufferSize(length-1); int rc = client.connect((char*)"client_test1"); IS_TRUE(rc); - int length = MQTT_MAX_PACKET_SIZE+1; byte publish[] = {0x30,length-2,0x0,0x5,0x74,0x6f,0x70,0x69,0x63,0x70,0x61,0x79,0x6c,0x6f,0x61,0x64}; byte bigPublish[length]; @@ -222,7 +281,8 @@ int test_receive_oversized_stream_message() { IS_TRUE(callback_called); IS_TRUE(strcmp(lastTopic,"topic")==0); - IS_TRUE(lastLength == length-9); + + IS_TRUE(lastLength == length-10); IS_FALSE(stream.error()); IS_FALSE(shimClient.error()); @@ -272,6 +332,7 @@ int main() test_receive_max_sized_message(); test_drop_invalid_remaining_length_message(); test_receive_oversized_message(); + test_resize_buffer(); test_receive_oversized_stream_message(); test_receive_qos1(); diff --git a/lib/default/PubSubClient-EspEasy-2.7.12/tests/src/subscribe_spec.cpp b/lib/default/pubsubclient-2.8.12/tests/src/subscribe_spec.cpp similarity index 99% rename from lib/default/PubSubClient-EspEasy-2.7.12/tests/src/subscribe_spec.cpp rename to lib/default/pubsubclient-2.8.12/tests/src/subscribe_spec.cpp index a41982355..22dc8a443 100644 --- a/lib/default/PubSubClient-EspEasy-2.7.12/tests/src/subscribe_spec.cpp +++ b/lib/default/pubsubclient-2.8.12/tests/src/subscribe_spec.cpp @@ -106,6 +106,7 @@ int test_subscribe_too_long() { shimClient.respond(connack,4); PubSubClient client(server, 1883, callback, shimClient); + client.setBufferSize(128); int rc = client.connect((char*)"client_test1"); IS_TRUE(rc); diff --git a/lib/default/PubSubClient-EspEasy-2.7.12/tests/testcases/__init__.py b/lib/default/pubsubclient-2.8.12/tests/testcases/__init__.py similarity index 100% rename from lib/default/PubSubClient-EspEasy-2.7.12/tests/testcases/__init__.py rename to lib/default/pubsubclient-2.8.12/tests/testcases/__init__.py diff --git a/lib/default/PubSubClient-EspEasy-2.7.12/tests/testcases/mqtt_basic.py b/lib/default/pubsubclient-2.8.12/tests/testcases/mqtt_basic.py similarity index 100% rename from lib/default/PubSubClient-EspEasy-2.7.12/tests/testcases/mqtt_basic.py rename to lib/default/pubsubclient-2.8.12/tests/testcases/mqtt_basic.py diff --git a/lib/default/PubSubClient-EspEasy-2.7.12/tests/testcases/mqtt_publish_in_callback.py b/lib/default/pubsubclient-2.8.12/tests/testcases/mqtt_publish_in_callback.py similarity index 100% rename from lib/default/PubSubClient-EspEasy-2.7.12/tests/testcases/mqtt_publish_in_callback.py rename to lib/default/pubsubclient-2.8.12/tests/testcases/mqtt_publish_in_callback.py diff --git a/lib/default/PubSubClient-EspEasy-2.7.12/tests/testcases/settings.py b/lib/default/pubsubclient-2.8.12/tests/testcases/settings.py similarity index 100% rename from lib/default/PubSubClient-EspEasy-2.7.12/tests/testcases/settings.py rename to lib/default/pubsubclient-2.8.12/tests/testcases/settings.py diff --git a/lib/default/PubSubClient-EspEasy-2.7.12/tests/testsuite.py b/lib/default/pubsubclient-2.8.12/tests/testsuite.py similarity index 100% rename from lib/default/PubSubClient-EspEasy-2.7.12/tests/testsuite.py rename to lib/default/pubsubclient-2.8.12/tests/testsuite.py diff --git a/tasmota/i18n.h b/tasmota/i18n.h index da0893f89..accb88938 100644 --- a/tasmota/i18n.h +++ b/tasmota/i18n.h @@ -366,6 +366,8 @@ #define D_CMND_MQTTCLIENT "MqttClient" #define D_CMND_MQTTUSER "MqttUser" #define D_CMND_MQTTPASSWORD "MqttPassword" +#define D_CMND_MQTTKEEPALIVE "MqttKeepAlive" +#define D_CMND_MQTTTIMEOUT "MqttTimeout" #define D_CMND_TLSKEY "TLSKey" #define D_CMND_FULLTOPIC "FullTopic" #define D_CMND_PREFIX "Prefix" diff --git a/tasmota/my_user_config.h b/tasmota/my_user_config.h index e33e5bf3c..3943ab4db 100644 --- a/tasmota/my_user_config.h +++ b/tasmota/my_user_config.h @@ -101,6 +101,9 @@ // -- MQTT ---------------------------------------- #define MQTT_USE true // [SetOption3] Select default MQTT use (false = Off, true = On) +#define MQTT_KEEPALIVE 30 // [MqttKeepAlive] +#define MQTT_SOCKET_TIMEOUT 4 // [MqttTimeout] + #define MQTT_HOST "" // [MqttHost] #define MQTT_FINGERPRINT1 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 // [MqttFingerprint1] (auto-learn) #define MQTT_FINGERPRINT2 0xDA,0x39,0xA3,0xEE,0x5E,0x6B,0x4B,0x0D,0x32,0x55,0xBF,0xEF,0x95,0x60,0x18,0x90,0xAF,0xD8,0x07,0x09 // [MqttFingerprint2] (invalid) diff --git a/tasmota/settings.h b/tasmota/settings.h index 7dd8dbc29..8ce77b4d4 100644 --- a/tasmota/settings.h +++ b/tasmota/settings.h @@ -488,7 +488,12 @@ struct { power_t interlock[MAX_INTERLOCKS_SET]; // 4D0 MAX_INTERLOCKS = MAX_RELAYS / 2 - uint8_t free_508[41]; // 508 + uint8_t free_508[36]; // 508 + + uint16_t mqtt_keepalive; // 52C + uint16_t mqtt_socket_timeout; // 52E + + uint8_t free_530[1]; // 530 uint8_t ina219_mode; // 531 uint16_t pulse_timer[MAX_PULSETIMERS]; // 532 diff --git a/tasmota/settings.ino b/tasmota/settings.ino index c1996da99..11f835592 100644 --- a/tasmota/settings.ino +++ b/tasmota/settings.ino @@ -864,6 +864,8 @@ void SettingsDefaultSet2(void) { memcpy_P(Settings.mqtt_fingerprint[1], default_fingerprint2, sizeof(default_fingerprint2)); Settings.tele_period = TELE_PERIOD; Settings.mqttlog_level = MQTT_LOG_LEVEL; + Settings.mqtt_keepalive = MQTT_KEEPALIVE; + Settings.mqtt_socket_timeout = MQTT_SOCKET_TIMEOUT; // Energy flag.no_power_on_check |= ENERGY_VOLTAGE_ALWAYS; @@ -1244,6 +1246,10 @@ void SettingsDelta(void) { if (Settings.version < 0x09020007) { *(uint32_t *)&Settings.device_group_tie = 0x04030201; } + if (Settings.version < 0x09030102) { + Settings.mqtt_keepalive = MQTT_KEEPALIVE; + Settings.mqtt_socket_timeout = MQTT_SOCKET_TIMEOUT; + } Settings.version = VERSION; SettingsSave(1); diff --git a/tasmota/support_command.ino b/tasmota/support_command.ino index de6879f16..d782f385f 100644 --- a/tasmota/support_command.ino +++ b/tasmota/support_command.ino @@ -529,9 +529,9 @@ void CmndStatus(void) if (((0 == payload) || (6 == payload)) && Settings.flag.mqtt_enabled) { // SetOption3 - Enable MQTT Response_P(PSTR("{\"" D_CMND_STATUS D_STATUS6_MQTT "\":{\"" D_CMND_MQTTHOST "\":\"%s\",\"" D_CMND_MQTTPORT "\":%d,\"" D_CMND_MQTTCLIENT D_JSON_MASK "\":\"%s\",\"" - D_CMND_MQTTCLIENT "\":\"%s\",\"" D_CMND_MQTTUSER "\":\"%s\",\"" D_JSON_MQTT_COUNT "\":%d,\"MAX_PACKET_SIZE\":%d,\"KEEPALIVE\":%d}}"), + D_CMND_MQTTCLIENT "\":\"%s\",\"" D_CMND_MQTTUSER "\":\"%s\",\"" D_JSON_MQTT_COUNT "\":%d,\"MAX_PACKET_SIZE\":%d,\"KEEPALIVE\":%d,\"SOCKET_TIMEOUT\":%d}}"), SettingsText(SET_MQTT_HOST), Settings.mqtt_port, EscapeJSONString(SettingsText(SET_MQTT_CLIENT)).c_str(), - TasmotaGlobal.mqtt_client, EscapeJSONString(SettingsText(SET_MQTT_USER)).c_str(), MqttConnectCount(), MQTT_MAX_PACKET_SIZE, MQTT_KEEPALIVE); + TasmotaGlobal.mqtt_client, EscapeJSONString(SettingsText(SET_MQTT_USER)).c_str(), MqttConnectCount(), MQTT_MAX_PACKET_SIZE, Settings.mqtt_keepalive, Settings.mqtt_socket_timeout); MqttPublishPrefixTopic_P(STAT, PSTR(D_CMND_STATUS "6")); } diff --git a/tasmota/tasmota_globals.h b/tasmota/tasmota_globals.h index 52149943c..110277cc2 100644 --- a/tasmota/tasmota_globals.h +++ b/tasmota/tasmota_globals.h @@ -270,8 +270,8 @@ const uint16_t LOG_BUFFER_SIZE = 4000; // Max number of characters in lo #ifndef MQTT_KEEPALIVE #define MQTT_KEEPALIVE 30 // Seconds #endif -#ifndef MQTT_TIMEOUT -#define MQTT_TIMEOUT 10000 // milli seconds +#ifndef MQTT_SOCKET_TIMEOUT +#define MQTT_SOCKET_TIMEOUT 4 // Seconds #endif #ifndef MQTT_CLEAN_SESSION #define MQTT_CLEAN_SESSION 1 // 0 = No clean session, 1 = Clean session (default) diff --git a/tasmota/tasmota_version.h b/tasmota/tasmota_version.h index 26807fb8f..e26ffc875 100644 --- a/tasmota/tasmota_version.h +++ b/tasmota/tasmota_version.h @@ -20,6 +20,6 @@ #ifndef _TASMOTA_VERSION_H_ #define _TASMOTA_VERSION_H_ -const uint32_t VERSION = 0x09030101; +const uint32_t VERSION = 0x09030102; #endif // _TASMOTA_VERSION_H_ diff --git a/tasmota/xdrv_02_mqtt.ino b/tasmota/xdrv_02_mqtt.ino index 3ef1ec84b..c31b2f4a7 100644 --- a/tasmota/xdrv_02_mqtt.ino +++ b/tasmota/xdrv_02_mqtt.ino @@ -23,6 +23,8 @@ #define MQTT_WIFI_CLIENT_TIMEOUT 200 // Wifi TCP connection timeout (default is 5000 mSec) #endif +#define USE_MQTT_NEW_PUBSUBCLIENT + // #define DEBUG_DUMP_TLS // allow dumping of TLS Flash keys #ifdef USE_MQTT_TLS @@ -42,7 +44,7 @@ const char kMqttCommands[] PROGMEM = "|" // No prefix #if defined(USE_MQTT_TLS) && !defined(USE_MQTT_TLS_CA_CERT) D_CMND_MQTTFINGERPRINT "|" #endif - D_CMND_MQTTUSER "|" D_CMND_MQTTPASSWORD "|" + D_CMND_MQTTUSER "|" D_CMND_MQTTPASSWORD "|" D_CMND_MQTTKEEPALIVE "|" D_CMND_MQTTTIMEOUT "|" #if defined(USE_MQTT_TLS) && defined(USE_MQTT_AWS_IOT) D_CMND_TLSKEY "|" #endif @@ -68,7 +70,7 @@ void (* const MqttCommand[])(void) PROGMEM = { #if defined(USE_MQTT_TLS) && !defined(USE_MQTT_TLS_CA_CERT) &CmndMqttFingerprint, #endif - &CmndMqttUser, &CmndMqttPassword, + &CmndMqttUser, &CmndMqttPassword, &CmndMqttKeepAlive, &CmndMqttTimeout, #if defined(USE_MQTT_TLS) && defined(USE_MQTT_AWS_IOT) &CmndTlsKey, #endif @@ -209,6 +211,9 @@ void MqttInit(void) { #else // USE_MQTT_TLS MqttClient.setClient(EspClient); #endif // USE_MQTT_TLS + + MqttClient.setKeepAlive(Settings.mqtt_keepalive); + MqttClient.setSocketTimeout(Settings.mqtt_socket_timeout); } bool MqttIsConnected(void) { @@ -829,6 +834,26 @@ void CmndMqttPassword(void) { } } +void CmndMqttKeepAlive(void) { + if ((XdrvMailbox.payload >= 1) && (XdrvMailbox.payload <= 100)) { + Settings.mqtt_keepalive = XdrvMailbox.payload; +#ifdef USE_MQTT_NEW_PUBSUBCLIENT + MqttClient.setKeepAlive(Settings.mqtt_keepalive); +#endif + } + ResponseCmndNumber(Settings.mqtt_keepalive); +} + +void CmndMqttTimeout(void) { + if ((XdrvMailbox.payload >= 1) && (XdrvMailbox.payload <= 100)) { + Settings.mqtt_socket_timeout = XdrvMailbox.payload; +#ifdef USE_MQTT_NEW_PUBSUBCLIENT + MqttClient.setSocketTimeout(Settings.mqtt_socket_timeout); +#endif + } + ResponseCmndNumber(Settings.mqtt_socket_timeout); +} + void CmndMqttlog(void) { if ((XdrvMailbox.payload >= LOG_LEVEL_NONE) && (XdrvMailbox.payload <= LOG_LEVEL_DEBUG_MORE)) { Settings.mqttlog_level = XdrvMailbox.payload; From 6ed8878618c8269544e509735a12cdaafa8cd20d Mon Sep 17 00:00:00 2001 From: Jason2866 <24528715+Jason2866@users.noreply.github.com> Date: Thu, 18 Mar 2021 22:15:15 +0100 Subject: [PATCH 74/87] Add 6-axis MotionTracking MPU6886 sensor to Tasmota32 build --- tasmota/tasmota_configurations_ESP32.h | 1 + 1 file changed, 1 insertion(+) diff --git a/tasmota/tasmota_configurations_ESP32.h b/tasmota/tasmota_configurations_ESP32.h index 7f7a8d01b..75cfc5572 100644 --- a/tasmota/tasmota_configurations_ESP32.h +++ b/tasmota/tasmota_configurations_ESP32.h @@ -197,6 +197,7 @@ //#define USE_PCA9685 // [I2cDriver1] Enable PCA9685 I2C HW PWM Driver - Must define I2C Address in #define USE_PCA9685_ADDR below - range 0x40 - 0x47 (+1k4 code) //#define USE_MPR121 // [I2cDriver23] Enable MPR121 controller (I2C addresses 0x5A, 0x5B, 0x5C and 0x5D) in input mode for touch buttons (+1k3 code) #define USE_CCS811 // [I2cDriver24] Enable CCS811 sensor (I2C address 0x5A) (+2k2 code) +#define USE_MPU6886 // [I2cDriver??] Enable MPU6886 6-axis MotionTracking sensor (I2C address 0x68) //#define USE_MPU6050 // [I2cDriver25] Enable MPU6050 sensor (I2C address 0x68 AD0 low or 0x69 AD0 high) (+3K3 of code and 188 Bytes of RAM) //#define USE_DS3231 // [I2cDriver26] Enable DS3231 external RTC in case no Wifi is avaliable. See docs in the source file (+1k2 code) //#define USE_MGC3130 // [I2cDriver27] Enable MGC3130 Electric Field Effect Sensor (I2C address 0x42) (+2k7 code, 0k3 mem) From b857ff5e680ea4fc72a76476a3d2ae3a76d769fa Mon Sep 17 00:00:00 2001 From: Simon Ratcliffe Date: Fri, 19 Mar 2021 10:35:50 +1100 Subject: [PATCH 75/87] Remove superfluous "END":1 from telemetry --- tasmota/xsns_29_mcp230xx.ino | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/tasmota/xsns_29_mcp230xx.ino b/tasmota/xsns_29_mcp230xx.ino index c1e7a08bb..6251d50d6 100644 --- a/tasmota/xsns_29_mcp230xx.ino +++ b/tasmota/xsns_29_mcp230xx.ino @@ -366,13 +366,15 @@ void MCP230xx_Show(bool json) uint16_t gpiototal = ((uint16_t)gpiob << 8) | gpio; ResponseAppend_P(PSTR(",\"MCP230_OUT\":{")); char stt[7]; + bool first = true; for (uint32_t pinx = 0; pinx < mcp230xx_pincount; pinx++) { if (Settings.mcp230xx_config[pinx].pinmode >= 5) { sprintf(stt, ConvertNumTxt(((gpiototal>>pinx)&1), Settings.mcp230xx_config[pinx].pinmode)); - ResponseAppend_P(PSTR("\"OUT_D%i\":\"%s\","), pinx, stt); + ResponseAppend_P(PSTR("%s\"OUT_D%i\":\"%s\""), (first) ? "" : ",", pinx, stt); + first = false; } } - ResponseAppend_P(PSTR("\"END\":1}")); + ResponseAppend_P(PSTR("}")); } #endif // USE_MCP230xx_OUTPUT ResponseJsonEnd(); @@ -778,13 +780,15 @@ void MCP230xx_OutputTelemetry(void) void MCP230xx_Interrupt_Counter_Report(void) { ResponseTime_P(PSTR(",\"MCP230_INTTIMER\":{")); + bool first = true; for (uint32_t pinx = 0;pinx < mcp230xx_pincount;pinx++) { if (Settings.mcp230xx_config[pinx].int_count_en) { // Counting is enabled for this pin so we add to report - ResponseAppend_P(PSTR("\"INTCNT_D%i\":%i,"),pinx,mcp230xx_int_counter[pinx]); + ResponseAppend_P(PSTR("%s\"INTCNT_D%i\":%i,"), (first) ? "" : "?", pinx, mcp230xx_int_counter[pinx]); + first = false; mcp230xx_int_counter[pinx]=0; } } - ResponseAppend_P(PSTR("\"END\":1}}")); + ResponseAppend_P(PSTR("}}")); MqttPublishTeleSensor(); mcp230xx_int_sec_counter = 0; } From 6ef79cdd00a41f0055eb99ce81849c1767429599 Mon Sep 17 00:00:00 2001 From: Simon Ratcliffe Date: Fri, 19 Mar 2021 14:00:09 +1100 Subject: [PATCH 76/87] MCP230xx fix savestate for pinmode=6 and init with so0=1 --- tasmota/xsns_29_mcp230xx.ino | 23 +++++++++++++---------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/tasmota/xsns_29_mcp230xx.ino b/tasmota/xsns_29_mcp230xx.ino index c1e7a08bb..59df46bb9 100644 --- a/tasmota/xsns_29_mcp230xx.ino +++ b/tasmota/xsns_29_mcp230xx.ino @@ -159,12 +159,13 @@ void MCP230xx_ApplySettings(void) TasmotaGlobal.devices_present += mcp230xx_outpincount; #endif // USE_MCP230xx_OUTPUT uint8_t int_en = 0; + uint8_t reg_portpins[mcp230xx_type]; for (uint32_t mcp230xx_port = 0; mcp230xx_port < mcp230xx_type; mcp230xx_port++) { uint8_t reg_gppu = 0; uint8_t reg_gpinten = 0; uint8_t reg_iodir = 0xFF; #ifdef USE_MCP230xx_OUTPUT - uint8_t reg_portpins = 0x00; + reg_portpins[mcp230xx_port] = 0x00; #endif // USE_MCP230xx_OUTPUT for (uint32_t idx = 0; idx < 8; idx++) { switch (Settings.mcp230xx_config[idx+(mcp230xx_port*8)].pinmode) { @@ -180,10 +181,10 @@ void MCP230xx_ApplySettings(void) case 5 ... 6: reg_iodir &= ~(1 << idx); if (Settings.flag.save_state) { // SetOption0 - Save power state and use after restart - Firmware configuration wants us to use the last pin state - reg_portpins |= (Settings.mcp230xx_config[idx+(mcp230xx_port*8)].saved_state << idx); + reg_portpins[mcp230xx_port] |= (Settings.mcp230xx_config[idx+(mcp230xx_port*8)].saved_state << idx); } else { if (Settings.mcp230xx_config[idx+(mcp230xx_port*8)].pullup) { - reg_portpins |= (1 << idx); + reg_portpins[mcp230xx_port] |= (1 << idx); } } break; @@ -205,15 +206,18 @@ void MCP230xx_ApplySettings(void) I2cWrite8(USE_MCP230xx_ADDR, MCP230xx_GPINTEN+mcp230xx_port, reg_gpinten); #ifdef USE_MCP230xx_OUTPUT I2cWrite8(USE_MCP230xx_ADDR, MCP230xx_IODIR+mcp230xx_port, reg_iodir); +#endif // USE_MCP230xx_OUTPUT + } +#ifdef USE_MCP230xx_OUTPUT + for (uint32_t mcp230xx_port = 0; mcp230xx_port < mcp230xx_type; mcp230xx_port++) { for (uint32_t idx = 0; idx < mcp230xx_outpincount; idx++) { if (mcp230xx_port ? mcp230xx_outpinmapping[idx] > 7 : mcp230xx_outpinmapping[idx] < 8) { uint8_t relay_no = TasmotaGlobal.devices_present - mcp230xx_outpincount + idx + 1; - ExecuteCommandPower(relay_no, (reg_portpins >> (mcp230xx_outpinmapping[idx] & 7)) & 1, SRC_IGNORE); + ExecuteCommandPower(relay_no, (reg_portpins[mcp230xx_port] >> (mcp230xx_outpinmapping[idx] & 7)) & 1, SRC_IGNORE); } } - //I2cWrite8(USE_MCP230xx_ADDR, MCP230xx_GPIO+mcp230xx_port, reg_portpins); -#endif // USE_MCP230xx_OUTPUT } +#endif // USE_MCP230xx_OUTPUT mcp230xx_int_en = int_en; MCP230xx_CheckForIntCounter(); // update register on whether or not we should be counting interrupts MCP230xx_CheckForIntRetainer(); // update register on whether or not we should be retaining interrupt events for teleperiod @@ -399,8 +403,8 @@ void MCP230xx_SetOutPin(uint8_t pin,uint8_t pinstate) { I2cWrite8(USE_MCP230xx_ADDR, MCP230xx_GPIO + port, portpins); if (Settings.flag.save_state) { // SetOption0 - Save power state and use after restart - Firmware configured to save last known state in settings - Settings.mcp230xx_config[pin].saved_state=portpins>>(pin-(port*8))&1; - Settings.mcp230xx_config[pin+pinadd].saved_state=portpins>>(pin+pinadd-(port*8))&1; + Settings.mcp230xx_config[pin].saved_state=(portpins>>(pin-(port*8))&1)^(pinmo-5); + Settings.mcp230xx_config[pin+pinadd].saved_state=(portpins>>(pin+pinadd-(port*8))&1)^(pinmo-5); } sprintf(cmnd,ConvertNumTxt(pinstate, pinmo)); sprintf(stt,ConvertNumTxt((portpins >> (pin-(port*8))&1), pinmo)); @@ -809,10 +813,9 @@ void MCP230xx_SwitchRelay() { uint8_t pin = mcp230xx_outpinmapping[i - (TasmotaGlobal.devices_present - mcp230xx_outpincount)]; uint8_t pincmd = Settings.mcp230xx_config[pin].pinmode - 5; uint8_t relay_state = bitRead(XdrvMailbox.index, i); - AddLog(LOG_LEVEL_DEBUG, PSTR("MCP: relay %d pin_no %d state %d"), i,pin, relay_state); switch (relay_state) { case 1: - MCP230xx_SetOutPin(pin,abs(pincmd-1)); + MCP230xx_SetOutPin(pin,1-pincmd); break; case 0: MCP230xx_SetOutPin(pin,pincmd); From 320f0e27768424aa3eab3ca688c930e22a209b12 Mon Sep 17 00:00:00 2001 From: Theo Arends <11044339+arendst@users.noreply.github.com> Date: Fri, 19 Mar 2021 10:15:50 +0100 Subject: [PATCH 77/87] Add support for TM1638 seven segment display Add support for TM1638 seven segment display by Ajith Vasudevan (#11031) --- CHANGELOG.md | 1 + RELEASENOTES.md | 1 + tasmota/xdsp_15_tm1637.ino | 93 +++++++++++++++++++------------------- 3 files changed, 48 insertions(+), 47 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0d3c637bf..af833f281 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) +- Support for TM1638 seven segment display by Ajith Vasudevan (#11031) ### Changed - PubSubClient library from EspEasy v2.7.12 to Tasmota v2.8.12 diff --git a/RELEASENOTES.md b/RELEASENOTES.md index cdb71c52e..19e7f50c2 100644 --- a/RELEASENOTES.md +++ b/RELEASENOTES.md @@ -87,6 +87,7 @@ The attached binaries can also be downloaded from http://ota.tasmota.com/tasmota - 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) - 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) diff --git a/tasmota/xdsp_15_tm1637.ino b/tasmota/xdsp_15_tm1637.ino index 2d2f17bba..979b5d96a 100644 --- a/tasmota/xdsp_15_tm1637.ino +++ b/tasmota/xdsp_15_tm1637.ino @@ -21,11 +21,11 @@ #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- and TM1638-based seven-segment modules. + Raw segments can also be displayed. - - In addition, it is also possible to set brightness (8 levels), clear the display, scroll text, + + In addition, it is also possible to set brightness (8 levels), clear the display, scroll text, display a rudimentary bar graph, and a Clock (12 hr and 24 hr). To use, compile Tasmota with USE_DISPLAY and USE_DISPLAY_TM1637, or build the tasmota-display env. @@ -45,15 +45,15 @@ CLK hardware pin --> "TM1638 CLK" STB hardware pin --> "TM1638 STB" - - Once the GPIO configuration is saved and the ESP8266/ESP32 module restarts, set the Display Model to 15 + + Once the GPIO configuration is saved and the ESP8266/ESP32 module restarts, set the Display Model to 15 using the command "DisplayModel 15" - - If your display is a TM1637 with 6 digits, set Display Columns to the number of digits your + + 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. - + After the ESP8266/ESP32 module restarts again, the following "Display" commands can be used: - + DisplayClear @@ -180,39 +180,43 @@ struct { uint8_t display_type = TM1637; uint8_t prev_buttons; - bool driver_inited = false; + 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 \*********************************************************************************************/ -bool TM1637Init(void) { - if(TM1637Data.driver_inited) return true; - - if(PinUsed(GPIO_TM16CLK) && PinUsed(GPIO_TM16DIO) && PinUsed(GPIO_TM16STB)) { +void TM1637Init(void) { + if (PinUsed(GPIO_TM16CLK) && PinUsed(GPIO_TM16DIO) && PinUsed(GPIO_TM16STB)) { TM1637Data.display_type = TM1638; - TM1637Data.num_digits = 8; - } else if(PinUsed(GPIO_TM1637CLK) && PinUsed(GPIO_TM1637DIO)) { - TM1637Data.display_type = TM1637; - if(Settings.display_cols[0] <= 6) TM1637Data.num_digits = Settings.display_cols[0]; - else TM1637Data.num_digits = 4; + TM1637Data.num_digits = 8; + } + else if (PinUsed(GPIO_TM1637CLK) && PinUsed(GPIO_TM1637DIO)) { + TM1637Data.display_type = TM1637; + if (Settings.display_cols[0] <= 6) { + TM1637Data.num_digits = Settings.display_cols[0]; + } else { + TM1637Data.num_digits = 4; + } + } + else { + return; } - else return false; Settings.display_model == XDSP_15; - if(TM1637Data.display_type == TM1637) { - strcpy(TM1637Data.model_name, "TM1637"); + if (TM1637Data.display_type == TM1637) { + strcpy_P(TM1637Data.model_name, PSTR("TM1637")); tm1637display = new SevenSegmentTM1637(Pin(GPIO_TM1637CLK), Pin(GPIO_TM1637DIO)); tm1637display->begin(TM1637Data.num_digits, 1); - } else if(TM1637Data.display_type == TM1638) { - strcpy(TM1637Data.model_name, "TM1638"); + } + else if (TM1637Data.display_type == TM1638) { + strcpy_P(TM1637Data.model_name, PSTR("TM1638")); tm1638display = new TM1638plus(Pin(GPIO_TM16STB), Pin(GPIO_TM16CLK), Pin(GPIO_TM16DIO), true ); TM1637Data.num_digits = 8; tm1638display->displayBegin(); @@ -220,10 +224,8 @@ bool TM1637Init(void) { TM1637ClearDisplay(); TM1637Data.brightness = (Settings.display_dimmer ? Settings.display_dimmer : TM1637Data.brightness); TM1637SetBrightness(TM1637Data.brightness); - TM1637Data.driver_inited = true; - AddLog(LOG_LEVEL_INFO, PSTR("DSP: %s display driver initialized with %d digits"), TM1637Data.model_name, TM1637Data.num_digits); - - return true; + TM1637Data.init_done = true; + AddLog(LOG_LEVEL_INFO, PSTR("DSP: %s with %d digits"), TM1637Data.model_name, TM1637Data.num_digits); } /*********************************************************************************************\ @@ -356,7 +358,7 @@ bool CmndTM1637Float(bool clear) { for(uint32_t i=0, j=0; i 7) break; if(txt[i] == 0) break; - if(txt[i+1] == '.') { + if(txt[i+1] == '.') { tm1638display->displayASCIIwDot(j+position, txt[i]); i++; length++; @@ -462,8 +464,8 @@ void TM1637ScrollText(void) { rawBytes[0] = 1 | 2 | 32 | 64; } if(!dotSkipped && TM1637Data.scroll_text[j] == '.') { - j++; - TM1637Data.scroll_index++; + j++; + TM1637Data.scroll_index++; rawBytes[0] = tm1637display->encode(TM1637Data.scroll_text[j]); } if(TM1637Data.scroll_text[j+1] == '.') { rawBytes[0] = rawBytes[0] | 128; } @@ -581,7 +583,7 @@ bool CmndTM1637Raw(void) { if(i>(TM1637Data.num_digits-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; @@ -656,7 +658,7 @@ bool CmndTM1637Text(bool clear) { } else if(sString[j] == '^') { tm1638display->display7Seg(i, (1 | 2 | 32 | 64)); } else tm1638display->displayASCII(i, sString[j]); - } + } } return true; @@ -690,7 +692,7 @@ void TM1637SetBrightness(uint8_t val) { if((val < BRIGHTNESS_MIN) || (val > BRIGHTNESS_MAX)) val = 5; Settings.display_dimmer = val; if(TM1637Data.display_type == TM1637) tm1637display->setBacklight(val*10); - else if(TM1637Data.display_type == TM1638) tm1638display->brightness(val-1); + else if(TM1637Data.display_type == TM1638) tm1638display->brightness(val-1); } @@ -767,8 +769,8 @@ bool TM1637MainFunc(uint8_t fn) { bool result = false; 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; + Response_P(PSTR("{\"Error\":\"Command text too long. Please limit it to %d characters\"}"), CMD_MAX_LEN); + return false; } switch (fn) { @@ -820,22 +822,19 @@ bool TM1637MainFunc(uint8_t fn) { /*********************************************************************************************\ * Interface \*********************************************************************************************/ -bool Xdsp15(uint8_t function) -{ +bool Xdsp15(uint8_t function) { bool result = false; - if(function == FUNC_DISPLAY_MODEL) { - return true; + if (FUNC_DISPLAY_INIT_DRIVER == function) { + TM1637Init(); } - - if (Settings.display_model == XDSP_15) { + else if (TM1637Data.init_done && (XDSP_15 == Settings.display_model)) { switch (function) { - case FUNC_DISPLAY_INIT_DRIVER: - result = TM1637Init(); // init + case FUNC_DISPLAY_MODEL: + result = true; break; case FUNC_DISPLAY_INIT: AddLog(LOG_LEVEL_DEBUG, PSTR("TM7: %s: FUNC_DISPLAY_INIT: Display depends on TM1637Data.display_type, currently %d"), TM1637Data.model_name, Settings.display_options.data); - result = true; break; case FUNC_DISPLAY_SEVENSEG_TEXT: case FUNC_DISPLAY_CLEAR: From ee1ecfc26710307c826ed6cb29dbe074bb858abb Mon Sep 17 00:00:00 2001 From: Theo Arends <11044339+arendst@users.noreply.github.com> Date: Fri, 19 Mar 2021 10:19:01 +0100 Subject: [PATCH 78/87] Fix TM163x driver --- tasmota/xdsp_15_tm1637.ino | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tasmota/xdsp_15_tm1637.ino b/tasmota/xdsp_15_tm1637.ino index 979b5d96a..bfc7d8486 100644 --- a/tasmota/xdsp_15_tm1637.ino +++ b/tasmota/xdsp_15_tm1637.ino @@ -208,7 +208,7 @@ void TM1637Init(void) { return; } - Settings.display_model == XDSP_15; + Settings.display_model = XDSP_15; if (TM1637Data.display_type == TM1637) { strcpy_P(TM1637Data.model_name, PSTR("TM1637")); From c87a4df7e982506c4cb242fea39b8b5f3bc6ee51 Mon Sep 17 00:00:00 2001 From: Theo Arends <11044339+arendst@users.noreply.github.com> Date: Fri, 19 Mar 2021 10:33:46 +0100 Subject: [PATCH 79/87] Refactor TM1638 pin numbers --- tasmota/support_tasmota.ino | 2 +- tasmota/tasmota_template.h | 18 +++++++++--------- tasmota/tasmota_template_legacy.h | 6 +++--- tasmota/xdrv_10_rules.ino | 2 +- tasmota/xdsp_15_tm1637.ino | 11 ++++------- tasmota/xsns_28_tm1638.ino | 8 ++++---- 6 files changed, 22 insertions(+), 25 deletions(-) diff --git a/tasmota/support_tasmota.ino b/tasmota/support_tasmota.ino index 4c20486b2..9ad44d32e 100644 --- a/tasmota/support_tasmota.ino +++ b/tasmota/support_tasmota.ino @@ -769,7 +769,7 @@ bool MqttShowSensor(void) int json_data_start = strlen(TasmotaGlobal.mqtt_data); for (uint32_t i = 0; i < MAX_SWITCHES; i++) { #ifdef USE_TM1638 - if (PinUsed(GPIO_SWT1, i) || (PinUsed(GPIO_TM16CLK) && PinUsed(GPIO_TM16DIO) && PinUsed(GPIO_TM16STB))) { + if (PinUsed(GPIO_SWT1, i) || (PinUsed(GPIO_TM1638CLK) && PinUsed(GPIO_TM1638DIO) && PinUsed(GPIO_TM1638STB))) { #else if (PinUsed(GPIO_SWT1, i)) { #endif // USE_TM1638 diff --git a/tasmota/tasmota_template.h b/tasmota/tasmota_template.h index 748afaffb..197596b7b 100644 --- a/tasmota/tasmota_template.h +++ b/tasmota/tasmota_template.h @@ -52,7 +52,7 @@ enum UserSelectablePins { GPIO_SR04_TRIG, GPIO_SR04_ECHO, // SR04 interface GPIO_SDM120_TX, GPIO_SDM120_RX, // SDM120 Serial interface GPIO_SDM630_TX, GPIO_SDM630_RX, // SDM630 Serial interface - GPIO_TM16CLK, GPIO_TM16DIO, GPIO_TM16STB, // TM1638 interface + GPIO_TM1638CLK, GPIO_TM1638DIO, GPIO_TM1638STB, // TM1638 interface GPIO_MP3_DFR562, // RB-DFR-562, DFPlayer Mini MP3 Player GPIO_HX711_SCK, GPIO_HX711_DAT, // HX711 Load Cell interface GPIO_TX2X_TXD_BLACK, // TX20/TX23 Transmission Pin @@ -452,9 +452,9 @@ const uint16_t kGpioNiceList[] PROGMEM = { #ifdef USE_DISPLAY_TM1637 AGPIO(GPIO_TM1637CLK), AGPIO(GPIO_TM1637DIO), - AGPIO(GPIO_TM16CLK), - AGPIO(GPIO_TM16DIO), - AGPIO(GPIO_TM16STB), + AGPIO(GPIO_TM1638CLK), + AGPIO(GPIO_TM1638DIO), + AGPIO(GPIO_TM1638STB), #endif // USE_DISPLAY_TM1637 AGPIO(GPIO_BACKLIGHT), // Display backlight control AGPIO(GPIO_OLED_RESET), // OLED Display Reset @@ -553,9 +553,9 @@ const uint16_t kGpioNiceList[] PROGMEM = { AGPIO(GPIO_SR04_ECHO), // SR04 Ech/RXo pin #endif #ifdef USE_TM1638 - AGPIO(GPIO_TM16CLK), // TM1638 Clock - AGPIO(GPIO_TM16DIO), // TM1638 Data I/O - AGPIO(GPIO_TM16STB), // TM1638 Strobe + AGPIO(GPIO_TM1638CLK), // TM1638 Clock + AGPIO(GPIO_TM1638DIO), // TM1638 Data I/O + AGPIO(GPIO_TM1638STB), // TM1638 Strobe #endif #ifdef USE_HX711 AGPIO(GPIO_HX711_SCK), // HX711 Load Cell clock @@ -789,8 +789,8 @@ const uint16_t kGpioNiceList[] PROGMEM = { #endif #ifdef USE_VL53L0X AGPIO(GPIO_VL53L0X_XSHUT1) + VL53L0X_MAX_SENSORS, // When using multiple VL53L0X. -#endif - +#endif + /*-------------------------------------------------------------------------------------------*\ * ESP32 specifics \*-------------------------------------------------------------------------------------------*/ diff --git a/tasmota/tasmota_template_legacy.h b/tasmota/tasmota_template_legacy.h index 4d5f2c507..4583a9783 100644 --- a/tasmota/tasmota_template_legacy.h +++ b/tasmota/tasmota_template_legacy.h @@ -334,9 +334,9 @@ const uint16_t kGpioConvert[] PROGMEM = { AGPIO(GPIO_SDM120_RX), // SDM120 Serial interface AGPIO(GPIO_SDM630_TX), // SDM630 Serial interface AGPIO(GPIO_SDM630_RX), // SDM630 Serial interface - AGPIO(GPIO_TM16CLK), // TM1638 Clock - AGPIO(GPIO_TM16DIO), // TM1638 Data I/O - AGPIO(GPIO_TM16STB), // TM1638 Strobe + AGPIO(GPIO_TM1638CLK), // TM1638 Clock + AGPIO(GPIO_TM1638DIO), // TM1638 Data I/O + AGPIO(GPIO_TM1638STB), // TM1638 Strobe AGPIO(GPIO_SWT1_NP), // Switch no pullup AGPIO(GPIO_SWT1_NP) +1, AGPIO(GPIO_SWT1_NP) +2, diff --git a/tasmota/xdrv_10_rules.ino b/tasmota/xdrv_10_rules.ino index cdf374a27..b4e21e578 100644 --- a/tasmota/xdrv_10_rules.ino +++ b/tasmota/xdrv_10_rules.ino @@ -882,7 +882,7 @@ void RulesEvery50ms(void) // Boot time SWITCHES Status for (uint32_t i = 0; i < MAX_SWITCHES; i++) { #ifdef USE_TM1638 - if (PinUsed(GPIO_SWT1, i) || (PinUsed(GPIO_TM16CLK) && PinUsed(GPIO_TM16DIO) && PinUsed(GPIO_TM16STB))) { + if (PinUsed(GPIO_SWT1, i) || (PinUsed(GPIO_TM1638CLK) && PinUsed(GPIO_TM1638DIO) && PinUsed(GPIO_TM1638STB))) { #else if (PinUsed(GPIO_SWT1, i)) { #endif // USE_TM1638 diff --git a/tasmota/xdsp_15_tm1637.ino b/tasmota/xdsp_15_tm1637.ino index bfc7d8486..4b7fa3223 100644 --- a/tasmota/xdsp_15_tm1637.ino +++ b/tasmota/xdsp_15_tm1637.ino @@ -192,7 +192,7 @@ struct { \*********************************************************************************************/ void TM1637Init(void) { - if (PinUsed(GPIO_TM16CLK) && PinUsed(GPIO_TM16DIO) && PinUsed(GPIO_TM16STB)) { + if (PinUsed(GPIO_TM1638CLK) && PinUsed(GPIO_TM1638DIO) && PinUsed(GPIO_TM1638STB)) { TM1637Data.display_type = TM1638; TM1637Data.num_digits = 8; } @@ -210,14 +210,14 @@ void TM1637Init(void) { Settings.display_model = XDSP_15; - if (TM1637Data.display_type == TM1637) { + if (TM1637 == TM1637Data.display_type) { strcpy_P(TM1637Data.model_name, PSTR("TM1637")); tm1637display = new SevenSegmentTM1637(Pin(GPIO_TM1637CLK), Pin(GPIO_TM1637DIO)); tm1637display->begin(TM1637Data.num_digits, 1); } - else if (TM1637Data.display_type == TM1638) { + else if (TM1638 == TM1637Data.display_type) { strcpy_P(TM1637Data.model_name, PSTR("TM1638")); - tm1638display = new TM1638plus(Pin(GPIO_TM16STB), Pin(GPIO_TM16CLK), Pin(GPIO_TM16DIO), true ); + tm1638display = new TM1638plus(Pin(GPIO_TM1638STB), Pin(GPIO_TM1638CLK), Pin(GPIO_TM1638DIO), true ); TM1637Data.num_digits = 8; tm1638display->displayBegin(); } @@ -833,9 +833,6 @@ bool Xdsp15(uint8_t function) { case FUNC_DISPLAY_MODEL: result = true; break; - case FUNC_DISPLAY_INIT: - AddLog(LOG_LEVEL_DEBUG, PSTR("TM7: %s: FUNC_DISPLAY_INIT: Display depends on TM1637Data.display_type, currently %d"), TM1637Data.model_name, Settings.display_options.data); - break; case FUNC_DISPLAY_SEVENSEG_TEXT: case FUNC_DISPLAY_CLEAR: case FUNC_DISPLAY_NUMBER: diff --git a/tasmota/xsns_28_tm1638.ino b/tasmota/xsns_28_tm1638.ino index 0eb0d1744..2c3e32707 100644 --- a/tasmota/xsns_28_tm1638.ino +++ b/tasmota/xsns_28_tm1638.ino @@ -144,10 +144,10 @@ uint8_t Tm1638GetButtons(void) void TmInit(void) { tm1638_type = 0; - if (PinUsed(GPIO_TM16CLK) && PinUsed(GPIO_TM16DIO) && PinUsed(GPIO_TM16STB)) { - tm1638_clock_pin = Pin(GPIO_TM16CLK); - tm1638_data_pin = Pin(GPIO_TM16DIO); - tm1638_strobe_pin = Pin(GPIO_TM16STB); + if (PinUsed(GPIO_TM1638CLK) && PinUsed(GPIO_TM1638DIO) && PinUsed(GPIO_TM1638STB)) { + tm1638_clock_pin = Pin(GPIO_TM1638CLK); + tm1638_data_pin = Pin(GPIO_TM1638DIO); + tm1638_strobe_pin = Pin(GPIO_TM1638STB); pinMode(tm1638_data_pin, OUTPUT); pinMode(tm1638_clock_pin, OUTPUT); From be46611f57592cceff526a8e7bc990454a5e97f1 Mon Sep 17 00:00:00 2001 From: Theo Arends <11044339+arendst@users.noreply.github.com> Date: Fri, 19 Mar 2021 10:44:47 +0100 Subject: [PATCH 80/87] Increase SR04 max sensor detection distance Increase SR04 max sensor detection distance to 500 (#11372) --- tasmota/my_user_config.h | 1 + tasmota/xsns_22_sr04.ino | 8 ++++++-- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/tasmota/my_user_config.h b/tasmota/my_user_config.h index 3943ab4db..2df867689 100644 --- a/tasmota/my_user_config.h +++ b/tasmota/my_user_config.h @@ -673,6 +673,7 @@ #define STARTING_OFFSET 30 // Turn on NovaSDS XX-seconds before tele_period is reached //#define USE_HPMA // Add support for Honeywell HPMA115S0 particle concentration sensor (+1k4) //#define USE_SR04 // Add support for HC-SR04 ultrasonic devices (+1k code) + #define SR04_MAX_SENSOR_DISTANCE 500 // Set sensor max detection distance //#define USE_DYP // Add support for DYP ME-007 ultrasonic distance sensor, serial port version (+0k5 code) #define USE_SERIAL_BRIDGE // Add support for software Serial Bridge (+0k8 code) //#define USE_TCP_BRIDGE // Add support for Serial to TCP bridge (+1.3k code) diff --git a/tasmota/xsns_22_sr04.ino b/tasmota/xsns_22_sr04.ino index f2659f294..29876dce4 100644 --- a/tasmota/xsns_22_sr04.ino +++ b/tasmota/xsns_22_sr04.ino @@ -29,7 +29,11 @@ * - https://www.dfrobot.com/wiki/index.php/Weather-proof_Ultrasonic_Sensor_SKU_:_SEN0207 \*********************************************************************************************/ -#define XSNS_22 22 +#define XSNS_22 22 + +#ifndef SR04_MAX_SENSOR_DISTANCE +#define SR04_MAX_SENSOR_DISTANCE 500 +#endif uint8_t sr04_type = 1; real64_t distance; @@ -64,7 +68,7 @@ uint8_t Sr04TModeDetect(void) if (-1 == sr04_trig_pin) { sr04_trig_pin = Pin(GPIO_SR04_ECHO); // if GPIO_SR04_TRIG is not configured use single PIN mode with GPIO_SR04_ECHO only } - sonar = new NewPing(sr04_trig_pin, sr04_echo_pin, 300); + sonar = new NewPing(sr04_trig_pin, sr04_echo_pin, SR04_MAX_SENSOR_DISTANCE); } else { if (sonar_serial->hardwareSerial()) { ClaimSerial(); From add48e8bd9cc98fe67ad0ae5d95bff937b40a815 Mon Sep 17 00:00:00 2001 From: Theo Arends <11044339+arendst@users.noreply.github.com> Date: Fri, 19 Mar 2021 11:13:08 +0100 Subject: [PATCH 81/87] Fix TM163x power control --- tasmota/xdsp_15_tm1637.ino | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/tasmota/xdsp_15_tm1637.ino b/tasmota/xdsp_15_tm1637.ino index 4b7fa3223..35ef7ab6b 100644 --- a/tasmota/xdsp_15_tm1637.ino +++ b/tasmota/xdsp_15_tm1637.ino @@ -761,7 +761,6 @@ void TM1637ShowTime() { } - /*********************************************************************************************\ * This function is called for all Display functions. \*********************************************************************************************/ @@ -830,6 +829,12 @@ bool Xdsp15(uint8_t function) { } else if (TM1637Data.init_done && (XDSP_15 == Settings.display_model)) { switch (function) { + case FUNC_DISPLAY_EVERY_50_MSECOND: + if (disp_power) { + if (TM1637Data.scroll) { TM1637ScrollText(); } + if (TM1637Data.show_clock) { TM1637ShowTime(); } + } + break; case FUNC_DISPLAY_MODEL: result = true; break; @@ -847,11 +852,12 @@ bool Xdsp15(uint8_t function) { case FUNC_DISPLAY_CLOCK: TM1637Data.show_clock = false; case FUNC_DISPLAY_BRIGHTNESS: - result = TM1637MainFunc(function); + if (disp_power) { + result = TM1637MainFunc(function); + } break; - case FUNC_DISPLAY_EVERY_50_MSECOND: - if(TM1637Data.scroll) TM1637ScrollText(); - if(TM1637Data.show_clock) TM1637ShowTime(); + case FUNC_DISPLAY_POWER: + if (!disp_power) { TM1637ClearDisplay(); } break; } } From 6577cdc426542e1c878ea4ca0e46dde2a5b50e81 Mon Sep 17 00:00:00 2001 From: Theo Arends <11044339+arendst@users.noreply.github.com> Date: Fri, 19 Mar 2021 12:02:01 +0100 Subject: [PATCH 82/87] Add initial DisplayMode support to TM163x displays --- tasmota/xdsp_15_tm1637.ino | 207 ++++++++++++++++++++++++++++--------- 1 file changed, 160 insertions(+), 47 deletions(-) diff --git a/tasmota/xdsp_15_tm1637.ino b/tasmota/xdsp_15_tm1637.ino index 35ef7ab6b..10a05c42b 100644 --- a/tasmota/xdsp_15_tm1637.ino +++ b/tasmota/xdsp_15_tm1637.ino @@ -60,27 +60,27 @@ Clears the display, command: "DisplayClear" - DisplayNumber num [,position {0-(TM1637Data.num_digits-1))} [,leading_zeros {0|1} [,length {1 to TM1637Data.num_digits}]]] + DisplayNumber num [,position {0-(Settings.display_width-1))} [,leading_zeros {0|1} [,length {1 to Settings.display_width}]]] Clears and then displays number without decimal. command e.g., "DisplayNumber 1234" Control 'leading zeros', 'length' and 'position' with "DisplayNumber 1234, , , " - 'leading zeros' can be 1 or 0 (default), 'length' can be 1 to TM1637Data.num_digits, 'position' can be 0 (left-most) to TM1637Data.num_digits (right-most). + 'leading zeros' can be 1 or 0 (default), 'length' can be 1 to Settings.display_width, 'position' can be 0 (left-most) to Settings.display_width (right-most). See function description below for more details. - DisplayNumberNC num [,position {0-(TM1637Data.num_digits-1))} [,leading_zeros {0|1} [,length {1 to TM1637Data.num_digits}]]] + DisplayNumberNC num [,position {0-(Settings.display_width-1))} [,leading_zeros {0|1} [,length {1 to Settings.display_width}]]] Display integer number as above, but without clearing first. e.g., "DisplayNumberNC 1234". Usage is same as above. - DisplayFloat num [,position {0-(TM1637Data.num_digits-1)} [,precision {0-TM1637Data.num_digits} [,length {1 to TM1637Data.num_digits}]]] + DisplayFloat num [,position {0-(Settings.display_width-1)} [,precision {0-Settings.display_width} [,length {1 to Settings.display_width}]]] Clears and then displays float (with decimal point) command e.g., "DisplayFloat 12.34" See function description below for more details. - DisplayFloatNC num [,position {0-(TM1637Data.num_digits-1)} [,precision {0-TM1637Data.num_digits} [,length {1 to TM1637Data.num_digits}]]] + DisplayFloatNC num [,position {0-(Settings.display_width-1)} [,precision {0-Settings.display_width} [,length {1 to Settings.display_width}]]] Displays float (with decimal point) as above, but without clearing first. command e.g., "DisplayFloatNC 12.34" See function description below for more details. @@ -93,24 +93,24 @@ - DisplayRaw position {0-(TM1637Data.num_digits-1)},length {1 to TM1637Data.num_digits}, num1 [, num2[, num3[, num4[, ...upto TM1637Data.num_digits numbers]]]]] + DisplayRaw position {0-(Settings.display_width-1)},length {1 to Settings.display_width}, num1 [, num2[, num3[, num4[, ...upto Settings.display_width numbers]]]]] - Takes upto TM1637Data.num_digits comma-separated integers (0-255) and displays raw segments. Each number represents a + Takes upto Settings.display_width comma-separated integers (0-255) and displays raw segments. Each number represents a 7-segment digit. Each 8-bit number represents individual segments of a digit. For example, the command "DisplayRaw 0, 4, 255, 255, 255, 255" would display "[8.8.8.8.]" - DisplayText text [, position {0-(TM1637Data.num_digits-1)} [,length {1 to TM1637Data.num_digits}]] + DisplayText text [, position {0-(Settings.display_width-1)} [,length {1 to Settings.display_width}]] Clears and then displays basic text. command e.g., "DisplayText ajith vasudevan" Control 'length' and 'position' with "DisplayText , , " - 'length' can be 1 to TM1637Data.num_digits, 'position' can be 0 (left-most) to TM1637Data.num_digits-1 (right-most) + 'length' can be 1 to Settings.display_width, 'position' can be 0 (left-most) to Settings.display_width-1 (right-most) A caret(^) symbol in the text input is dispayed as the degrees(°) symbol. This is useful for displaying Temperature! For example, the command "DisplayText 22.5^" will display "22.5°". - DisplayTextNC text [, position {0-TM1637Data.num_digits-1} [,length {1 to TM1637Data.num_digits}]] + DisplayTextNC text [, position {0-Settings.display_width-1} [,length {1 to Settings.display_width}]] Clears first, then displays text. Usage is same as above. @@ -171,7 +171,6 @@ struct { char scroll_text[CMD_MAX_LEN]; char msg[60]; char model_name[8]; - uint8_t num_digits = 4; uint8_t scroll_delay = 4; uint8_t scroll_index = 0; uint8_t iteration = 0; @@ -194,14 +193,12 @@ struct { void TM1637Init(void) { if (PinUsed(GPIO_TM1638CLK) && PinUsed(GPIO_TM1638DIO) && PinUsed(GPIO_TM1638STB)) { TM1637Data.display_type = TM1638; - TM1637Data.num_digits = 8; + Settings.display_width = 8; } else if (PinUsed(GPIO_TM1637CLK) && PinUsed(GPIO_TM1637DIO)) { TM1637Data.display_type = TM1637; - if (Settings.display_cols[0] <= 6) { - TM1637Data.num_digits = Settings.display_cols[0]; - } else { - TM1637Data.num_digits = 4; + if ((!Settings.display_width || Settings.display_width > 6)) { + Settings.display_width = 4; } } else { @@ -209,30 +206,32 @@ void TM1637Init(void) { } Settings.display_model = XDSP_15; + Settings.display_cols[0] = Settings.display_width; + Settings.display_height = 1; + Settings.display_rows = Settings.display_height; if (TM1637 == TM1637Data.display_type) { strcpy_P(TM1637Data.model_name, PSTR("TM1637")); tm1637display = new SevenSegmentTM1637(Pin(GPIO_TM1637CLK), Pin(GPIO_TM1637DIO)); - tm1637display->begin(TM1637Data.num_digits, 1); + tm1637display->begin(Settings.display_width, 1); } 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 ); - TM1637Data.num_digits = 8; tm1638display->displayBegin(); } TM1637ClearDisplay(); TM1637Data.brightness = (Settings.display_dimmer ? Settings.display_dimmer : TM1637Data.brightness); TM1637SetBrightness(TM1637Data.brightness); TM1637Data.init_done = true; - AddLog(LOG_LEVEL_INFO, PSTR("DSP: %s with %d digits"), TM1637Data.model_name, TM1637Data.num_digits); + AddLog(LOG_LEVEL_INFO, PSTR("DSP: %s with %d digits"), TM1637Data.model_name, Settings.display_width); } /*********************************************************************************************\ * Displays number without decimal, with/without leading zeros, specifying start-position * and length, optionally skipping clearing display before displaying the number. -* commands: DisplayNumber num [,position {0-(TM1637Data.num_digits-1)} [,leading_zeros {0|1} [,length {1 to TM1637Data.num_digits}]]] -* DisplayNumberNC num [,position {0-(TM1637Data.num_digits-1)} [,leading_zeros {0|1} [,length {1 to TM1637Data.num_digits}]]] // "NC" --> "No Clear" +* 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) { char sNum[CMD_MAX_LEN]; @@ -262,7 +261,7 @@ bool CmndTM1637Number(bool clear) { } - if((position < 0) || (position > (TM1637Data.num_digits-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); @@ -271,20 +270,20 @@ bool CmndTM1637Number(bool clear) { char txt[30]; snprintf_P(txt, sizeof(txt), PSTR("%d"), num); if(!length) length = strlen(txt); - if((length < 0) || (length > TM1637Data.num_digits)) length = TM1637Data.num_digits; + if((length < 0) || (length > Settings.display_width)) length = Settings.display_width; char pad = (leadingzeros ? '0': ' '); uint32_t i = position; uint8_t rawBytes[1]; for(; iTM1637Data.num_digits) break; + if(i>Settings.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(uint32_t j = 0; i< position + length; i++, j++) { - if(i>TM1637Data.num_digits) break; + 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]); @@ -296,8 +295,8 @@ bool CmndTM1637Number(bool clear) { /*********************************************************************************************\ * Displays number with decimal, specifying position, precision and length, * optionally skipping clearing display before displaying the number. -* commands: DisplayFloat num [,position {0-(TM1637Data.num_digits-1)} [,precision {0-TM1637Data.num_digits} [,length {1 to TM1637Data.num_digits}]]] -* DisplayFloatNC num [,position {0-(TM1637Data.num_digits-1)} [,precision {0-TM1637Data.num_digits} [,length {1 to TM1637Data.num_digits}]]] // "NC" --> "No 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) { @@ -306,7 +305,7 @@ bool CmndTM1637Float(bool clear) { char sPosition[CMD_MAX_LEN]; char sLength[CMD_MAX_LEN]; uint8_t length = 0; - uint8_t precision = TM1637Data.num_digits; + uint8_t precision = Settings.display_width; uint8_t position = 0; float fnum = 0.0f; @@ -328,8 +327,8 @@ bool CmndTM1637Float(bool clear) { } - if((position < 0) || (position > (TM1637Data.num_digits-1))) position = 0; - if((precision < 0) || (precision > TM1637Data.num_digits)) precision = TM1637Data.num_digits; + if((position < 0) || (position > (Settings.display_width-1))) position = 0; + if((precision < 0) || (precision > Settings.display_width)) precision = Settings.display_width; if(clear) TM1637ClearDisplay(); @@ -337,7 +336,7 @@ bool CmndTM1637Float(bool clear) { ext_snprintf_P(txt, sizeof(txt), PSTR("%*_f"), precision, &fnum); if(!length) length = strlen(txt); - if((length <= 0) || (length > TM1637Data.num_digits)) length = TM1637Data.num_digits; + 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); @@ -351,7 +350,7 @@ bool CmndTM1637Float(bool clear) { i++; length++; } - if((j+position) > TM1637Data.num_digits) break; + if((j+position) > Settings.display_width) break; tm1637display->printRaw(rawBytes, 1, j+position); } } else if(TM1637Data.display_type == TM1638) { @@ -389,9 +388,9 @@ bool CmndTM1637Clear(void) { void TM1637ClearDisplay (void) { if(TM1637Data.display_type == TM1637) { unsigned char arr[] = {0}; - for(int i=0; iprintRaw(arr, 1, i); + for(int i=0; iprintRaw(arr, 1, i); } else if(TM1637Data.display_type == TM1638) { - for(int i=0; idisplay7Seg(i, 0); + for(int i=0; idisplay7Seg(i, 0); } } @@ -453,7 +452,7 @@ void TM1637ScrollText(void) { } uint8_t rawBytes[1]; for(uint32_t i=0, j=TM1637Data.scroll_index; i< 1 + strlen(TM1637Data.scroll_text); i++, j++) { - if(i > (TM1637Data.num_digits-1)) { break; } + if(i > (Settings.display_width-1)) { break; } rawBytes[0] = tm1637display->encode(TM1637Data.scroll_text[j]); bool dotSkipped = false; if(TM1637Data.scroll_text[j+1] == '.') { @@ -490,7 +489,7 @@ bool CmndTM1637Level(void) { return false; } - uint8_t totalBars = 2*TM1637Data.num_digits; + 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]; @@ -519,8 +518,8 @@ bool CmndTM1637Level(void) { /*********************************************************************************************\ * Display arbitrary data on the display module -* Command: DisplayRaw position {0-(TM1637Data.num_digits-1)},length {1 to TM1637Data.num_digits}, a [, b[, c[, d[...upto TM1637Data.num_digits]]]] -* where a,b,c,d... are upto TM1637Data.num_digits numbers in the range 0-255, each number (byte) +* Command: DisplayRaw position {0-(Settings.display_width-1)},length {1 to Settings.display_width}, a [, b[, c[, d[...upto Settings.display_width]]]] +* where a,b,c,d... are upto Settings.display_width numbers in the range 0-255, each number (byte) * corresponding to a single 7-segment digit. Within each byte, bit 0 is segment A, * bit 1 is segment B etc. The function may either set the entire display * or any desired part using the length and position parameters. @@ -571,8 +570,8 @@ bool CmndTM1637Raw(void) { } if(!length) length = ArgC() - 2; - if(length < 0 || length > TM1637Data.num_digits) length = TM1637Data.num_digits; - if(position < 0 || position > (TM1637Data.num_digits-1)) position = 0; + 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); @@ -580,7 +579,7 @@ bool CmndTM1637Raw(void) { if(TM1637Data.display_type == TM1637) { uint8_t rawBytes[1]; for(uint32_t i=position; i(TM1637Data.num_digits-1)) break; + if(i>(Settings.display_width-1)) break; rawBytes[0] = DATA[i-position]; tm1637display->printRaw(rawBytes, 1, i); } @@ -599,7 +598,7 @@ bool CmndTM1637Raw(void) { * Display a given string. * Text can be placed at arbitrary location on the display using the length and * position parameters without affecting the rest of the display. -* Command: DisplayText text [, position {0-(TM1637Data.num_digits-1)} [,length {1 to TM1637Data.num_digits}]] +* Command: DisplayText text [, position {0-(Settings.display_width-1)} [,length {1 to Settings.display_width}]] \*********************************************************************************************/ bool CmndTM1637Text(bool clear) { char sString[CMD_MAX_LEN + 1]; @@ -621,20 +620,20 @@ bool CmndTM1637Text(bool clear) { } - if((position < 0) || (position > (TM1637Data.num_digits-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(!length) length = strlen(sString); - if((length < 0) || (length > TM1637Data.num_digits)) length = TM1637Data.num_digits; + if((length < 0) || (length > Settings.display_width)) length = Settings.display_width; uint32_t i = position; if(TM1637Data.display_type == TM1637) { uint8_t rawBytes[1]; for(uint32_t j = 0; i< position + length; i++, j++) { - if(i > (TM1637Data.num_digits-1)) break; + if(i > (Settings.display_width-1)) break; if(sString[j] == 0) break; rawBytes[0] = tm1637display->encode(sString[j]); bool dotSkipped = false; @@ -818,9 +817,118 @@ bool TM1637MainFunc(uint8_t fn) { } +/*********************************************************************************************/ + +#ifdef USE_DISPLAY_MODES1TO5 + +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); + } + else if (TM1638 == TM1637Data.display_type) { +// if ((millis() % 1000) > 500 && (i == 1)) { tm1638display->displayASCIIwDot(i, txt[i]); } + tm1638display->displayASCII(i, txt[i]); + } + } +} + +void TM1637Center(char* txt) { + char line[Settings.display_cols[0] +2]; + + int len = strlen(txt); + int offset = 0; + if (len >= Settings.display_cols[0]) { + len = Settings.display_cols[0]; + } 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]; + } + TM1637Print(line); +} + +/* +bool TM1637PrintLog(void) { + bool result = false; + + disp_refresh--; + if (!disp_refresh) { + disp_refresh = Settings.display_refresh; + if (!disp_screen_buffer_cols) { DisplayAllocScreenBuffer(); } + + char* txt = DisplayLogBuffer('\337'); + if (txt != nullptr) { + uint8_t last_row = Settings.display_rows -1; + + for (uint32_t i = 0; i < last_row; i++) { + strlcpy(disp_screen_buffer[i], disp_screen_buffer[i +1], disp_screen_buffer_cols); + lcd->setCursor(0, i); // Col 0, Row i + lcd->print(disp_screen_buffer[i +1]); + } + strlcpy(disp_screen_buffer[last_row], txt, disp_screen_buffer_cols); + DisplayFillScreen(last_row); + + AddLog(LOG_LEVEL_DEBUG, PSTR(D_LOG_DEBUG "[%s]"), disp_screen_buffer[last_row]); + + lcd->setCursor(0, last_row); + lcd->print(disp_screen_buffer[last_row]); + + result = true; + } + } + return result; +} +*/ +void TM1637Time(void) +{ + char line[Settings.display_cols[0] +1]; + + 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) { + snprintf_P(line, sizeof(line), PSTR("%02d%02d%02d"), RtcTime.hour, RtcTime.minute, RtcTime.second); + } + else { + snprintf_P(line, sizeof(line), PSTR("%02d%02d"), RtcTime.hour, RtcTime.minute); + } + TM1637Center(line); +} + +void TM1637Refresh(void) { // Every second + if (Settings.display_mode) { // Mode 0 is User text + switch (Settings.display_mode) { + case 1: // Time + TM1637Time(); + break; +/* + case 2: // Local + case 4: // Mqtt + TM1637PrintLog(); + break; + case 3: // Local + case 5: { // Mqtt + if (!TM1637PrintLog()) { TM1637Time(); } + break; + } +*/ + } + } +} + +#endif // USE_DISPLAY_MODES1TO5 + /*********************************************************************************************\ * Interface \*********************************************************************************************/ + bool Xdsp15(uint8_t function) { bool result = false; @@ -830,11 +938,16 @@ bool Xdsp15(uint8_t function) { else if (TM1637Data.init_done && (XDSP_15 == Settings.display_model)) { switch (function) { case FUNC_DISPLAY_EVERY_50_MSECOND: - if (disp_power) { + if (disp_power && !Settings.display_mode) { if (TM1637Data.scroll) { TM1637ScrollText(); } 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; @@ -852,7 +965,7 @@ bool Xdsp15(uint8_t function) { case FUNC_DISPLAY_CLOCK: TM1637Data.show_clock = false; case FUNC_DISPLAY_BRIGHTNESS: - if (disp_power) { + if (disp_power && !Settings.display_mode) { result = TM1637MainFunc(function); } break; From acfc1027c301783a9fd752ace651b6a0cadf2555 Mon Sep 17 00:00:00 2001 From: Jason2866 <24528715+Jason2866@users.noreply.github.com> Date: Fri, 19 Mar 2021 12:15:23 +0100 Subject: [PATCH 83/87] Correct monitor speed for ESP8266 changed by accident in a other PR --- platformio.ini | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/platformio.ini b/platformio.ini index 37cf9a174..2bd850f94 100644 --- a/platformio.ini +++ b/platformio.ini @@ -80,7 +80,7 @@ build_flags = ${core.build_flags} board_build.f_cpu = 80000000L board_build.f_flash = 40000000L -monitor_speed = 74880 +monitor_speed = 115200 monitor_port = COM5 upload_speed = 115200 ; *** Upload Serial reset method for Wemos and NodeMCU From 245023f9da9294c3bef2dc97557de17a267ea1e2 Mon Sep 17 00:00:00 2001 From: Theo Arends <11044339+arendst@users.noreply.github.com> Date: Fri, 19 Mar 2021 12:22:53 +0100 Subject: [PATCH 84/87] Add DisplayModes to TM163x displays --- tasmota/xdsp_15_tm1637.ino | 39 +++++++++++++++++++++++++++----------- 1 file changed, 28 insertions(+), 11 deletions(-) diff --git a/tasmota/xdsp_15_tm1637.ino b/tasmota/xdsp_15_tm1637.ino index 10a05c42b..9432ea4c3 100644 --- a/tasmota/xdsp_15_tm1637.ino +++ b/tasmota/xdsp_15_tm1637.ino @@ -867,18 +867,12 @@ bool TM1637PrintLog(void) { if (txt != nullptr) { uint8_t last_row = Settings.display_rows -1; - for (uint32_t i = 0; i < last_row; i++) { - strlcpy(disp_screen_buffer[i], disp_screen_buffer[i +1], disp_screen_buffer_cols); - lcd->setCursor(0, i); // Col 0, Row i - lcd->print(disp_screen_buffer[i +1]); - } strlcpy(disp_screen_buffer[last_row], txt, disp_screen_buffer_cols); DisplayFillScreen(last_row); AddLog(LOG_LEVEL_DEBUG, PSTR(D_LOG_DEBUG "[%s]"), disp_screen_buffer[last_row]); - lcd->setCursor(0, last_row); - lcd->print(disp_screen_buffer[last_row]); + TM1637Print(disp_screen_buffer[last_row]); result = true; } @@ -886,8 +880,8 @@ bool TM1637PrintLog(void) { return result; } */ -void TM1637Time(void) -{ + +void TM1637Time(void) { char line[Settings.display_cols[0] +1]; if (Settings.display_cols[0] >= 8) { @@ -902,18 +896,41 @@ void TM1637Time(void) TM1637Center(line); } +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); + } + 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 { + snprintf_P(line, sizeof(line), PSTR("%02d%02d"), RtcTime.day_of_month, RtcTime.month); + } + TM1637Center(line); +} + void TM1637Refresh(void) { // Every second if (Settings.display_mode) { // Mode 0 is User text switch (Settings.display_mode) { case 1: // Time TM1637Time(); break; + case 2: // Date + TM1637Date(); + break; + case 3: // Time + if (TasmotaGlobal.uptime % Settings.display_refresh) { + TM1637Time(); + } else { + TM1637Date(); + } + break; /* - case 2: // Local case 4: // Mqtt TM1637PrintLog(); break; - case 3: // Local case 5: { // Mqtt if (!TM1637PrintLog()) { TM1637Time(); } break; From ccd3477737e4ced30eba822ae40a9e47d7d458f6 Mon Sep 17 00:00:00 2001 From: Theo Arends <11044339+arendst@users.noreply.github.com> Date: Fri, 19 Mar 2021 14:24:26 +0100 Subject: [PATCH 85/87] Remove command DisplayBrightness Remove command DisplayBrightness in favour of already available DisplayDimmer --- tasmota/xdrv_13_display.ino | 49 +++++++--------- tasmota/xdsp_15_tm1637.ino | 108 ++++++++++++------------------------ 2 files changed, 57 insertions(+), 100 deletions(-) diff --git a/tasmota/xdrv_13_display.ino b/tasmota/xdrv_13_display.ino index 35e4e05ee..97ee8f137 100755 --- a/tasmota/xdrv_13_display.ino +++ b/tasmota/xdrv_13_display.ino @@ -89,12 +89,12 @@ enum XdspFunctions { FUNC_DISPLAY_INIT_DRIVER, FUNC_DISPLAY_INIT, FUNC_DISPLAY_E FUNC_DISPLAY_DRAW_CIRCLE, FUNC_DISPLAY_FILL_CIRCLE, FUNC_DISPLAY_DRAW_RECTANGLE, FUNC_DISPLAY_FILL_RECTANGLE, FUNC_DISPLAY_TEXT_SIZE, FUNC_DISPLAY_FONT_SIZE, FUNC_DISPLAY_ROTATION, FUNC_DISPLAY_DRAW_STRING, - FUNC_DISPLAY_DIM, FUNC_DISPLAY_BLINKRATE + FUNC_DISPLAY_DIM, FUNC_DISPLAY_BLINKRATE, #ifdef USE_UFILESYS - ,FUNC_DISPLAY_BATCH + FUNC_DISPLAY_BATCH, #endif - , FUNC_DISPLAY_NUMBER, FUNC_DISPLAY_FLOAT, FUNC_DISPLAY_NUMBERNC, FUNC_DISPLAY_FLOATNC, - FUNC_DISPLAY_BRIGHTNESS, FUNC_DISPLAY_RAW, FUNC_DISPLAY_LEVEL, FUNC_DISPLAY_SEVENSEG_TEXT, FUNC_DISPLAY_SEVENSEG_TEXTNC, + FUNC_DISPLAY_NUMBER, FUNC_DISPLAY_FLOAT, FUNC_DISPLAY_NUMBERNC, FUNC_DISPLAY_FLOATNC, + FUNC_DISPLAY_RAW, FUNC_DISPLAY_LEVEL, FUNC_DISPLAY_SEVENSEG_TEXT, FUNC_DISPLAY_SEVENSEG_TEXTNC, FUNC_DISPLAY_SCROLLDELAY, FUNC_DISPLAY_CLOCK, FUNC_DISPLAY_SCROLLTEXT }; @@ -103,25 +103,25 @@ enum DisplayInitModes { DISPLAY_INIT_MODE, DISPLAY_INIT_PARTIAL, DISPLAY_INIT_FU 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_ROTATE "|" D_CMND_DISP_TEXT "|" D_CMND_DISP_ADDRESS "|" D_CMND_DISP_BLINKRATE "|" #ifdef USE_UFILESYS - "|" D_CMND_DISP_BATCH + 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_BRIGHTNESS "|" 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_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 ; void (* const DisplayCommand[])(void) PROGMEM = { &CmndDisplay, &CmndDisplayModel, &CmndDisplayWidth, &CmndDisplayHeight, &CmndDisplayMode, &CmndDisplayRefresh, &CmndDisplayDimmer, &CmndDisplayColumns, &CmndDisplayRows, &CmndDisplaySize, &CmndDisplayFont, - &CmndDisplayRotate, &CmndDisplayText, &CmndDisplayAddress, &CmndDisplayBlinkrate + &CmndDisplayRotate, &CmndDisplayText, &CmndDisplayAddress, &CmndDisplayBlinkrate, #ifdef USE_UFILESYS - ,&CmndDisplayBatch + &CmndDisplayBatch, #endif - , &CmndDisplayClear, &CmndDisplayNumber, &CmndDisplayFloat, &CmndDisplayNumberNC, &CmndDisplayFloatNC, - &CmndDisplayBrightness, &CmndDisplayRaw, &CmndDisplayLevel, &CmndDisplaySevensegText, &CmndDisplaySevensegTextNC, + &CmndDisplayClear, &CmndDisplayNumber, &CmndDisplayFloat, &CmndDisplayNumberNC, &CmndDisplayFloatNC, + &CmndDisplayRaw, &CmndDisplayLevel, &CmndDisplaySevensegText, &CmndDisplaySevensegTextNC, &CmndDisplayScrollDelay, &CmndDisplayClock, &CmndDisplayTextNC, &CmndDisplayScrollText, &CmndDisplayILIMOde , &CmndDisplayILIInvert }; @@ -1643,7 +1643,7 @@ void CmndDisplay(void) 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, Settings.display_size, Settings.display_font, + 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); } @@ -1686,7 +1686,7 @@ void CmndDisplayHeight(void) void CmndDisplayMode(void) { #ifdef USE_DISPLAY_MODES1TO5 -/* Matrix LCD / Oled TFT +/* Matrix / 7-segment LCD / Oled TFT * 1 = Text up and time Time * 2 = Date Local sensors Local sensors * 3 = Day Local sensors and time Local sensors and time @@ -1714,8 +1714,7 @@ void CmndDisplayMode(void) ResponseCmndNumber(Settings.display_mode); } -void CmndDisplayDimmer(void) -{ +void CmndDisplayDimmer(void) { if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= 100)) { Settings.display_dimmer = ((XdrvMailbox.payload +1) * 100) / 666; // Correction for Domoticz (0 - 15) if (Settings.display_dimmer && !(disp_power)) { @@ -1724,12 +1723,13 @@ void CmndDisplayDimmer(void) else if (!Settings.display_dimmer && disp_power) { ExecuteCommandPower(disp_device, POWER_OFF, SRC_DISPLAY); } - if (renderer) + if (renderer) { renderer->dim(Settings.display_dimmer); - else + } else { XdspCall(FUNC_DISPLAY_DIM); + } } - ResponseCmndNumber(Settings.display_dimmer); + ResponseCmndNumber(((Settings.display_dimmer * 666) / 100) +1); } void CmndDisplayBlinkrate(void) @@ -1794,15 +1794,6 @@ void CmndDisplayFloatNC(void) ResponseCmndChar(XdrvMailbox.data); } -void CmndDisplayBrightness(void) -{ - bool result = false; - if (!renderer) { - result = XdspCall(FUNC_DISPLAY_BRIGHTNESS); - } - if(result) ResponseCmndNumber(XdrvMailbox.payload); -} - void CmndDisplayRaw(void) { if (!renderer) { diff --git a/tasmota/xdsp_15_tm1637.ino b/tasmota/xdsp_15_tm1637.ino index 9432ea4c3..0b507fce5 100644 --- a/tasmota/xdsp_15_tm1637.ino +++ b/tasmota/xdsp_15_tm1637.ino @@ -87,12 +87,6 @@ - DisplayBrightness num {1-8} - - Set brightness (1 to 8) command e.g., "DisplayBrightness 2" - - - DisplayRaw position {0-(Settings.display_width-1)},length {1 to Settings.display_width}, num1 [, num2[, num3[, num4[, ...upto Settings.display_width numbers]]]]] Takes upto Settings.display_width comma-separated integers (0-255) and displays raw segments. Each number represents a @@ -148,8 +142,6 @@ #define XDSP_15 15 -#define BRIGHTNESS_MIN 1 -#define BRIGHTNESS_MAX 8 #define CMD_MAX_LEN 55 #define LEVEL_MIN 0 #define LEVEL_MAX 100 @@ -174,7 +166,6 @@ struct { uint8_t scroll_delay = 4; uint8_t scroll_index = 0; uint8_t iteration = 0; - uint8_t brightness = 5; uint8_t buttons; uint8_t display_type = TM1637; uint8_t prev_buttons; @@ -221,8 +212,7 @@ void TM1637Init(void) { tm1638display->displayBegin(); } TM1637ClearDisplay(); - TM1637Data.brightness = (Settings.display_dimmer ? Settings.display_dimmer : TM1637Data.brightness); - TM1637SetBrightness(TM1637Data.brightness); + TM1637Dim(); TM1637Data.init_done = true; AddLog(LOG_LEVEL_INFO, PSTR("DSP: %s with %d digits"), TM1637Data.model_name, Settings.display_width); } @@ -664,40 +654,6 @@ bool CmndTM1637Text(bool clear) { } -/*********************************************************************************************\ -* Sets brightness of the display. -* Command: DisplayBrightness {1-8} -\*********************************************************************************************/ -bool CmndTM1637Brightness(void) { - - uint16_t val = XdrvMailbox.payload; - if(ArgC() == 0) { - XdrvMailbox.payload = TM1637Data.brightness; - return true; - } - - if((val < BRIGHTNESS_MIN) || (val > BRIGHTNESS_MAX)) { - Response_P(PSTR("{\"Error\":\"Brightness should be a number in the range [%d, %d]\"}"), BRIGHTNESS_MIN, BRIGHTNESS_MAX); - return false; - } - TM1637Data.brightness = val; - TM1637SetBrightness(TM1637Data.brightness); - return true; -} - - - -void TM1637SetBrightness(uint8_t val) { - if((val < BRIGHTNESS_MIN) || (val > BRIGHTNESS_MAX)) val = 5; - Settings.display_dimmer = val; - if(TM1637Data.display_type == TM1637) tm1637display->setBacklight(val*10); - else if(TM1637Data.display_type == TM1638) tm1638display->brightness(val-1); -} - - - - - /*********************************************************************************************\ * Displays a clock. * Command: DisplayClock 1 // 12-hour format @@ -787,9 +743,6 @@ bool TM1637MainFunc(uint8_t fn) { case FUNC_DISPLAY_FLOATNC : result = CmndTM1637Float(false); break; - case FUNC_DISPLAY_BRIGHTNESS: - result = CmndTM1637Brightness(); - break; case FUNC_DISPLAY_RAW: result = CmndTM1637Raw(); break; @@ -816,6 +769,17 @@ bool TM1637MainFunc(uint8_t fn) { return result; } +void TM1637Dim(void) { + // Settings.display_dimmer = 0 - 15 + uint8_t brightness = Settings.display_dimmer >> 1; // 0 - 7 + + if (TM1637 == TM1637Data.display_type) { + tm1637display->setBacklight(brightness * 12); // 0 - 84 + } + else if (TM1637Data.display_type == TM1638) { + tm1638display->brightness(brightness); // 0 - 7 + } +} /*********************************************************************************************/ @@ -912,31 +876,31 @@ void TM1637Date(void) { } void TM1637Refresh(void) { // Every second - if (Settings.display_mode) { // Mode 0 is User text - switch (Settings.display_mode) { - case 1: // Time + if (!disp_power || !Settings.display_mode) { return; } // Mode 0 is User text + + 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; -/* - case 4: // Mqtt - TM1637PrintLog(); - break; - case 5: { // Mqtt - if (!TM1637PrintLog()) { TM1637Time(); } - break; } -*/ + break; +/* + case 4: // Mqtt + TM1637PrintLog(); + break; + case 5: { // Mqtt + if (!TM1637PrintLog()) { TM1637Time(); } + break; } +*/ } } @@ -980,12 +944,14 @@ bool Xdsp15(uint8_t function) { case FUNC_DISPLAY_SCROLLTEXT: case FUNC_DISPLAY_SCROLLDELAY: case FUNC_DISPLAY_CLOCK: - TM1637Data.show_clock = false; - case FUNC_DISPLAY_BRIGHTNESS: 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; From f18e874f6b9b5197284c859cf6b301d5985d5d0c Mon Sep 17 00:00:00 2001 From: Theo Arends <11044339+arendst@users.noreply.github.com> Date: Fri, 19 Mar 2021 15:42:24 +0100 Subject: [PATCH 86/87] Update Max Display Driver count --- tasmota/xdrv_13_display.ino | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tasmota/xdrv_13_display.ino b/tasmota/xdrv_13_display.ino index 97ee8f137..cf5e384eb 100755 --- a/tasmota/xdrv_13_display.ino +++ b/tasmota/xdrv_13_display.ino @@ -42,7 +42,7 @@ uint16_t bg_color = 0; uint8_t color_type = COLOR_BW; uint8_t auto_draw = 1; -const uint8_t DISPLAY_MAX_DRIVERS = 16; // Max number of display drivers/models supported by xdsp_interface.ino +const uint8_t DISPLAY_MAX_DRIVERS = 32; // Max number of display drivers/models supported by xdsp_interface.ino const uint8_t DISPLAY_MAX_COLS = 64; // Max number of columns allowed with command DisplayCols const uint8_t DISPLAY_MAX_ROWS = 64; // Max number of lines allowed with command DisplayRows From 9dc3c4e25515b19be1a5792684c8a621006521e7 Mon Sep 17 00:00:00 2001 From: Stephan Hadinger Date: Fri, 19 Mar 2021 22:17:45 +0100 Subject: [PATCH 87/87] Fix type for MPU6886 --- CHANGELOG.md | 2 +- tasmota/my_user_config.h | 2 +- tasmota/xsns_85_mpu6886.ino | 14 +++++++------- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index af833f281..d840a1395 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -26,7 +26,7 @@ All notable changes to this project will be documented in this file. - ESP32 Extent BLE (#11212) - ESP32 support for WS2812 hardware driver via RMT or I2S - ESP32 support for secondary I2C controller -- Support for MPU6686 on primary or secondary I2C bus +- Support for MPU6886 on primary or secondary I2C bus ### Changed - ESP32 core library from v1.0.5-rc6 to v1.0.5 diff --git a/tasmota/my_user_config.h b/tasmota/my_user_config.h index 2df867689..1d13bedda 100644 --- a/tasmota/my_user_config.h +++ b/tasmota/my_user_config.h @@ -610,7 +610,7 @@ // #define USE_EZORGB // [I2cDriver55] Enable support for EZO's RGB sensor (+0k5 code) - Shared EZO code required for any EZO device (+1k2 code) // #define USE_EZOPMP // [I2cDriver55] Enable support for EZO's PMP sensor (+0k3 code) - Shared EZO code required for any EZO device (+1k2 code) // #define USE_SEESAW_SOIL // [I2cDriver56] Enable Capacitice Soil Moisture & Temperature Sensor (I2C addresses 0x36 - 0x39) (+1k3 code) -// #define USE_MPU6886 // [I2cDriver58] Enable MPU6686 - found in M5Stack - support 2 I2C buses on ESP32 (I2C address 0x68) (+2k code) +// #define USE_MPU6886 // [I2cDriver58] Enable MPU6886 - found in M5Stack - support 2 I2C buses on ESP32 (I2C address 0x68) (+2k code) // #define USE_DISPLAY // Add I2C Display Support (+2k code) #define USE_DISPLAY_MODES1TO5 // Enable display mode 1 to 5 in addition to mode 0 diff --git a/tasmota/xsns_85_mpu6886.ino b/tasmota/xsns_85_mpu6886.ino index 0cbe317f9..5a288fd2f 100644 --- a/tasmota/xsns_85_mpu6886.ino +++ b/tasmota/xsns_85_mpu6886.ino @@ -44,17 +44,17 @@ struct { /********************************************************************************************/ const char HTTP_MPU6686[] PROGMEM = - "{s}MPU6686 acc_x" "{m}%3_f G" "{e}" - "{s}MPU6686 acc_y" "{m}%3_f G" "{e}" - "{s}MPU6686 acc_z" "{m}%3_f G" "{e}" - "{s}MPU6686 gyr_x" "{m}%i dps" "{e}" - "{s}MPU6686 gyr_y" "{m}%i dps" "{e}" - "{s}MPU6686 gyr_z" "{m}%i dps" "{e}" + "{s}MPU6886 acc_x" "{m}%3_f G" "{e}" + "{s}MPU6886 acc_y" "{m}%3_f G" "{e}" + "{s}MPU6886 acc_z" "{m}%3_f G" "{e}" + "{s}MPU6886 gyr_x" "{m}%i dps" "{e}" + "{s}MPU6886 gyr_y" "{m}%i dps" "{e}" + "{s}MPU6886 gyr_z" "{m}%i dps" "{e}" ; void MPU6686_Show(uint32_t json) { if (json) { - ResponseAppend_P(PSTR(",\"MPU6686\":{\"AX\":%i,\"AY\":%i,\"AZ\":%i,\"GX\":%i,\"GY\":%i,\"GZ\":%i}"), + ResponseAppend_P(PSTR(",\"MPU6886\":{\"AX\":%i,\"AY\":%i,\"AZ\":%i,\"GX\":%i,\"GY\":%i,\"GZ\":%i}"), mpu6886_sensor.ax, mpu6886_sensor.ay, mpu6886_sensor.az, mpu6886_sensor.gyx, mpu6886_sensor.gyy, mpu6886_sensor.gyz); } else {