diff --git a/lib/default/TasmotaSerial-3.6.0/src/TasmotaSerial.h b/lib/default/TasmotaSerial-3.6.0/src/TasmotaSerial.h index c41282004..2f9be0963 100644 --- a/lib/default/TasmotaSerial-3.6.0/src/TasmotaSerial.h +++ b/lib/default/TasmotaSerial-3.6.0/src/TasmotaSerial.h @@ -64,6 +64,7 @@ class TasmotaSerial : public Stream { uint32_t getLoopReadMetric(void) const { return m_bit_follow_metric; } #ifdef ESP32 uint32_t getUart(void) const { return m_uart; } + HardwareSerial *getesp32hws(void) { return TSerial; } #endif bool isValid(void) { return m_valid; } bool overflow(void); diff --git a/lib/lib_div/DS2480B/DS2480B.cpp b/lib/lib_div/DS2480B/DS2480B.cpp new file mode 100644 index 000000000..ff091ed2c --- /dev/null +++ b/lib/lib_div/DS2480B/DS2480B.cpp @@ -0,0 +1,569 @@ +/* +Copyright (c) 2007, Jim Studt (original old version - many contributors since) + +This library based on OneWire library currently maintained by Paul Stoffregen. However, +it has been modified to use the DS2480B serial to 1-wire chip instead of direct +bitbanging on a digital pin. Also, it is unfortunately hard coded to use the AltSoftSerial library +since this library has no common ancestor with the hardware serial class and there isn't a good way +to allow use of either + +OneWire has been maintained by Paul Stoffregen (paul@pjrc.com) since +January 2010. At the time, it was in need of many bug fixes, but had +been abandoned the original author (Jim Studt). None of the known +contributors were interested in maintaining OneWire. Paul typically +works on OneWire every 6 to 12 months. Patches usually wait that +long. If anyone is interested in more actively maintaining OneWire, +please contact Paul. + +Version 2.2: + Teensy 3.0 compatibility, Paul Stoffregen, paul@pjrc.com + Arduino Due compatibility, http://arduino.cc/forum/index.php?topic=141030 + Fix DS18B20 example negative temperature + Fix DS18B20 example's low res modes, Ken Butcher + Improve reset timing, Mark Tillotson + Add const qualifiers, Bertrik Sikken + Add initial value input to crc16, Bertrik Sikken + Add target_search() function, Scott Roberts + +Version 2.1: + Arduino 1.0 compatibility, Paul Stoffregen + Improve temperature example, Paul Stoffregen + DS250x_PROM example, Guillermo Lovato + PIC32 (chipKit) compatibility, Jason Dangel, dangel.jason AT gmail.com + Improvements from Glenn Trewitt: + - crc16() now works + - check_crc16() does all of calculation/checking work. + - Added read_bytes() and write_bytes(), to reduce tedious loops. + - Added ds2408 example. + Delete very old, out-of-date readme file (info is here) + +Version 2.0: Modifications by Paul Stoffregen, January 2010: +http://www.pjrc.com/teensy/td_libs_OneWire.html + Search fix from Robin James + http://www.arduino.cc/cgi-bin/yabb2/YaBB.pl?num=1238032295/27#27 + Use direct optimized I/O in all cases + Disable interrupts during timing critical sections + (this solves many random communication errors) + Disable interrupts during read-modify-write I/O + Reduce RAM consumption by eliminating unnecessary + variables and trimming many to 8 bits + Optimize both crc8 - table version moved to flash + +Modified to work with larger numbers of devices - avoids loop. +Tested in Arduino 11 alpha with 12 sensors. +26 Sept 2008 -- Robin James +http://www.arduino.cc/cgi-bin/yabb2/YaBB.pl?num=1238032295/27#27 + +Updated to work with arduino-0008 and to include skip() as of +2007/07/06. --RJL20 + +Modified to calculate the 8-bit CRC directly, avoiding the need for +the 256-byte lookup table to be loaded in RAM. Tested in arduino-0010 +-- Tom Pollard, Jan 23, 2008 + +Jim Studt's original library was modified by Josh Larios. + +Tom Pollard, pollard@alum.mit.edu, contributed around May 20, 2008 + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +Much of the code was inspired by Derek Yerger's code, though I don't +think much of that remains. In any event that was.. + (copyleft) 2006 by Derek Yerger - Free to distribute freely. + +The CRC code was excerpted and inspired by the Dallas Semiconductor +sample code bearing this copyright. +//--------------------------------------------------------------------------- +// Copyright (C) 2000 Dallas Semiconductor Corporation, All Rights Reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +// IN NO EVENT SHALL DALLAS SEMICONDUCTOR BE LIABLE FOR ANY CLAIM, DAMAGES +// OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, +// ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +// +// Except as contained in this notice, the name of Dallas Semiconductor +// shall not be used except as stated in the Dallas Semiconductor +// Branding Policy. +//-------------------------------------------------------------------------- +*/ + +#include "DS2480B.h" + + +DS2480B::DS2480B(TasmotaSerial *port) +{ + _port = port; +#if ONEWIRE_SEARCH + reset_search(); +#endif +} + +void DS2480B::begin() +{ + _port->write(0xC1); + if (!waitForReply()) return; + _port->read(); + isCmdMode = true; +} + + +// Perform the onewire reset function. We will wait up to 250uS for +// the bus to come high, if it doesn't then it is broken or shorted +// and we return a 0; +// +// Returns 1 if a device asserted a presence pulse, 0 otherwise. +// +uint8_t DS2480B::reset(void) +{ + uint8_t r; + + commandMode(); + + _port->write(0xC1); + //proper return is 0xCD otherwise something was wrong + //while (!_port->available()); + if (!waitForReply()) return true; + + r = _port->read(); + if (r == 0xCD) return 1; + return 0; +} + +void DS2480B::dataMode() +{ + if (isCmdMode) + { + _port->write(DATA_MODE); + isCmdMode = false; + } +} + +void DS2480B::commandMode() +{ + if (!isCmdMode) + { + _port->write(COMMAND_MODE); + isCmdMode = true; + } +} + + +void DS2480B::beginTransaction() +{ + dataMode(); +} + +void DS2480B::endTransaction() +{ + commandMode(); +} + +#define DS2480_DELAY 50 + +bool DS2480B::waitForReply() { + for (uint16_t i = 0; i < DS2480_DELAY; i++) { + if (_port->available()) return true; + delay(1); + } + return false; +} + + +// +// Write a bit - actually returns the bit read back in case you care. +// +uint8_t DS2480B::write_bit(uint8_t v) +{ + uint8_t val; + commandMode(); + if (v == 1) _port->write(0x91); //write a single "on" bit to onewire + else _port->write(0x81); //write a single "off" bit to onewire + if (!waitForReply()) return 0; + val = _port->read(); + return val & 1; +} + +// +// Read a bit - short hand for writing a 1 and seeing what we get back. +// +uint8_t DS2480B::read_bit(void) +{ + uint8_t r = write_bit(1); + return r; +} + +// +// Write a byte. The writing code uses the active drivers to raise the +// pin high, if you need power after the write (e.g. DS18S20 in +// parasite power mode) then set 'power' to 1, otherwise the pin will +// go tri-state at the end of the write to avoid heating in a short or +// other mishap. - Currently power isn't actually used now. +// +void DS2480B::write(uint8_t v, uint8_t power /* = 0 */) { + uint8_t r; + + dataMode(); + + _port->write(v); + //need to double up transmission if the sent byte was one of the command bytes + if (v == DATA_MODE || v == COMMAND_MODE || v == PULSE_TERM) _port->write(v); + if (!waitForReply()) return; + r = _port->read(); //throw away reply +} + +void DS2480B::writeCmd(uint8_t v, uint8_t power) +{ + uint8_t r; + + commandMode(); + + _port->write(v); + + if (!waitForReply()) return; + r = _port->read(); //throw away reply +} + +void DS2480B::write_bytes(const uint8_t *buf, uint16_t count, bool power /* = 0 */) { + for (uint16_t i = 0 ; i < count ; i++) write(buf[i]); +} + +// +// Read a byte +// +uint8_t DS2480B::read() { + uint8_t r; + + dataMode(); + + _port->write(0xFF); + if (!waitForReply()) return 0; + r = _port->read(); + return r; +} + +void DS2480B::read_bytes(uint8_t *buf, uint16_t count) { + for (uint16_t i = 0 ; i < count ; i++) + buf[i] = read(); +} + +// +// Do a ROM select +// +void DS2480B::select(const uint8_t rom[8]) +{ + uint8_t i; + + dataMode(); + write(0x55); // Choose ROM + + for (i = 0; i < 8; i++) write(rom[i]); +} + +// +// Do a ROM skip +// +void DS2480B::skip() +{ + dataMode(); + write(0xCC); // Skip ROM +} + +void DS2480B::depower() +{ + +} + +#if ONEWIRE_SEARCH + +// +// You need to use this function to start a search again from the beginning. +// You do not need to do it for the first search, though you could. +// +void DS2480B::reset_search() +{ + // reset the search state + LastDiscrepancy = 0; + LastDeviceFlag = FALSE; + LastFamilyDiscrepancy = 0; + for(int i = 7; ; i--) { + ROM_NO[i] = 0; + if ( i == 0) break; + } +} + +// Setup the search to find the device type 'family_code' on the next call +// to search(*newAddr) if it is present. +// +void DS2480B::target_search(uint8_t family_code) +{ + // set the search state to find SearchFamily type devices + ROM_NO[0] = family_code; + for (uint8_t i = 1; i < 8; i++) + ROM_NO[i] = 0; + LastDiscrepancy = 64; + LastFamilyDiscrepancy = 0; + LastDeviceFlag = FALSE; +} + +// +// Perform a search. If this function returns a '1' then it has +// enumerated the next device and you may retrieve the ROM from the +// DS2480B::address variable. If there are no devices, no further +// devices, or something horrible happens in the middle of the +// enumeration then a 0 is returned. If a new device is found then +// its address is copied to newAddr. Use DS2480B::reset_search() to +// start over. +// +// --- Replaced by the one from the Dallas Semiconductor web site --- +//-------------------------------------------------------------------------- +// Perform the 1-Wire Search Algorithm on the 1-Wire bus using the existing +// search state. +// Return TRUE : device found, ROM number in ROM_NO buffer +// FALSE : device not found, end of search +// +uint8_t DS2480B::search(uint8_t *newAddr) +{ + uint8_t id_bit_number; + uint8_t last_zero, rom_byte_number, search_result; + uint8_t id_bit, cmp_id_bit; + + unsigned char rom_byte_mask, search_direction; + + // initialize for search + id_bit_number = 1; + last_zero = 0; + rom_byte_number = 0; + rom_byte_mask = 1; + search_result = 0; + + // if the last call was not the last one + if (!LastDeviceFlag) + { + // 1-Wire reset + if (!reset()) + { + // reset the search + LastDiscrepancy = 0; + LastDeviceFlag = FALSE; + LastFamilyDiscrepancy = 0; + return FALSE; + } + + // issue the search command + write(0xF0); //send search command to DS18B20 units + + // loop to do the search + do + { + // read a bit and its complement + id_bit = read_bit(); + cmp_id_bit = read_bit(); + + // check for no devices on 1-wire + if ((id_bit == 1) && (cmp_id_bit == 1)) + break; + else + { + // all devices coupled have 0 or 1 + if (id_bit != cmp_id_bit) + search_direction = id_bit; // bit write value for search + else + { + // if this discrepancy if before the Last Discrepancy + // on a previous next then pick the same as last time + if (id_bit_number < LastDiscrepancy) + search_direction = ((ROM_NO[rom_byte_number] & rom_byte_mask) > 0); + else + // if equal to last pick 1, if not then pick 0 + search_direction = (id_bit_number == LastDiscrepancy); + + // if 0 was picked then record its position in LastZero + if (search_direction == 0) + { + last_zero = id_bit_number; + + // check for Last discrepancy in family + if (last_zero < 9) + LastFamilyDiscrepancy = last_zero; + } + } + + // set or clear the bit in the ROM byte rom_byte_number + // with mask rom_byte_mask + if (search_direction == 1) + ROM_NO[rom_byte_number] |= rom_byte_mask; + else + ROM_NO[rom_byte_number] &= ~rom_byte_mask; + + // serial number search direction write bit + write_bit(search_direction); + + // increment the byte counter id_bit_number + // and shift the mask rom_byte_mask + id_bit_number++; + rom_byte_mask <<= 1; + + // if the mask is 0 then go to new SerialNum byte rom_byte_number and reset mask + if (rom_byte_mask == 0) + { + rom_byte_number++; + rom_byte_mask = 1; + } + } + } + while(rom_byte_number < 8); // loop until through all ROM bytes 0-7 + + // if the search was successful then + if (!(id_bit_number < 65)) + { + // search successful so set LastDiscrepancy,LastDeviceFlag,search_result + LastDiscrepancy = last_zero; + + // check for last device + if (LastDiscrepancy == 0) + LastDeviceFlag = TRUE; + + search_result = TRUE; + } + } + + // if no device found then reset counters so next 'search' will be like a first + if (!search_result || !ROM_NO[0]) + { + LastDiscrepancy = 0; + LastDeviceFlag = FALSE; + LastFamilyDiscrepancy = 0; + search_result = FALSE; + } + for (int i = 0; i < 8; i++) newAddr[i] = ROM_NO[i]; + return search_result; + } + +#endif + +#if ONEWIRE_CRC +// The 1-Wire CRC scheme is described in Maxim Application Note 27: +// "Understanding and Using Cyclic Redundancy Checks with Maxim iButton Products" +// + +#if ONEWIRE_CRC8_TABLE +// This table comes from Dallas sample code where it is freely reusable, +// though Copyright (C) 2000 Dallas Semiconductor Corporation +static const uint8_t PROGMEM dscrc_table[] = { + 0, 94,188,226, 97, 63,221,131,194,156,126, 32,163,253, 31, 65, + 157,195, 33,127,252,162, 64, 30, 95, 1,227,189, 62, 96,130,220, + 35,125,159,193, 66, 28,254,160,225,191, 93, 3,128,222, 60, 98, + 190,224, 2, 92,223,129, 99, 61,124, 34,192,158, 29, 67,161,255, + 70, 24,250,164, 39,121,155,197,132,218, 56,102,229,187, 89, 7, + 219,133,103, 57,186,228, 6, 88, 25, 71,165,251,120, 38,196,154, + 101, 59,217,135, 4, 90,184,230,167,249, 27, 69,198,152,122, 36, + 248,166, 68, 26,153,199, 37,123, 58,100,134,216, 91, 5,231,185, + 140,210, 48,110,237,179, 81, 15, 78, 16,242,172, 47,113,147,205, + 17, 79,173,243,112, 46,204,146,211,141,111, 49,178,236, 14, 80, + 175,241, 19, 77,206,144,114, 44,109, 51,209,143, 12, 82,176,238, + 50,108,142,208, 83, 13,239,177,240,174, 76, 18,145,207, 45,115, + 202,148,118, 40,171,245, 23, 73, 8, 86,180,234,105, 55,213,139, + 87, 9,235,181, 54,104,138,212,149,203, 41,119,244,170, 72, 22, + 233,183, 85, 11,136,214, 52,106, 43,117,151,201, 74, 20,246,168, + 116, 42,200,150, 21, 75,169,247,182,232, 10, 84,215,137,107, 53}; + +// +// Compute a Dallas Semiconductor 8 bit CRC. These show up in the ROM +// and the registers. (note: this might better be done without to +// table, it would probably be smaller and certainly fast enough +// compared to all those delayMicrosecond() calls. But I got +// confused, so I use this table from the examples.) +// +uint8_t DS2480B::crc8(const uint8_t *addr, uint8_t len) +{ + uint8_t crc = 0; + + while (len--) { + crc = pgm_read_byte(dscrc_table + (crc ^ *addr++)); + } + return crc; +} +#else +// +// Compute a Dallas Semiconductor 8 bit CRC directly. +// this is much slower, but much smaller, than the lookup table. +// +uint8_t DS2480B::crc8(const uint8_t *addr, uint8_t len) +{ + uint8_t crc = 0; + + while (len--) { + uint8_t inbyte = *addr++; + for (uint8_t i = 8; i; i--) { + uint8_t mix = (crc ^ inbyte) & 0x01; + crc >>= 1; + if (mix) crc ^= 0x8C; + inbyte >>= 1; + } + } + return crc; +} +#endif + +#if ONEWIRE_CRC16 +bool DS2480B::check_crc16(const uint8_t* input, uint16_t len, const uint8_t* inverted_crc, uint16_t crc) +{ + crc = ~crc16(input, len, crc); + return (crc & 0xFF) == inverted_crc[0] && (crc >> 8) == inverted_crc[1]; +} + +uint16_t DS2480B::crc16(const uint8_t* input, uint16_t len, uint16_t crc) +{ + static const uint8_t oddparity[16] = + { 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0 }; + + for (uint16_t i = 0 ; i < len ; i++) { + // Even though we're just copying a byte from the input, + // we'll be doing 16-bit computation with it. + uint16_t cdata = input[i]; + cdata = (cdata ^ crc) & 0xff; + crc >>= 8; + + if (oddparity[cdata & 0x0F] ^ oddparity[cdata >> 4]) + crc ^= 0xC001; + + cdata <<= 6; + crc ^= cdata; + cdata <<= 1; + crc ^= cdata; + } + return crc; +} +#endif + +#endif diff --git a/lib/lib_div/DS2480B/DS2480B.h b/lib/lib_div/DS2480B/DS2480B.h new file mode 100644 index 000000000..1f0520607 --- /dev/null +++ b/lib/lib_div/DS2480B/DS2480B.h @@ -0,0 +1,200 @@ +#ifndef DS2480B_h +#define DS2480B_h + +#include + +#if ARDUINO >= 100 +#include "Arduino.h" // for delayMicroseconds, digitalPinToBitMask, etc +#else +#include "WProgram.h" // for delayMicroseconds +#include "pins_arduino.h" // for digitalPinToBitMask, etc +#endif + +#include + +// You can exclude certain features from OneWire. In theory, this +// might save some space. In practice, the compiler automatically +// removes unused code (technically, the linker, using -fdata-sections +// and -ffunction-sections when compiling, and Wl,--gc-sections +// when linking), so most of these will not result in any code size +// reduction. Well, unless you try to use the missing features +// and redesign your program to not need them! ONEWIRE_CRC8_TABLE +// is the exception, because it selects a fast but large algorithm +// or a small but slow algorithm. + +// you can exclude onewire_search by defining that to 0 +#ifndef ONEWIRE_SEARCH +#define ONEWIRE_SEARCH 1 +#endif + +// You can exclude CRC checks altogether by defining this to 0 +#ifndef ONEWIRE_CRC +#define ONEWIRE_CRC 1 +#endif + +// Select the table-lookup method of computing the 8-bit CRC +// by setting this to 1. The lookup table enlarges code size by +// about 250 bytes. It does NOT consume RAM (but did in very +// old versions of OneWire). If you disable this, a slower +// but very compact algorithm is used. +#ifndef ONEWIRE_CRC8_TABLE +#define ONEWIRE_CRC8_TABLE 1 +#endif + +// You can allow 16-bit CRC checks by defining this to 1 +// (Note that ONEWIRE_CRC must also be 1.) +#ifndef ONEWIRE_CRC16 +#define ONEWIRE_CRC16 1 +#endif + +#define FALSE 0 +#define TRUE 1 + +// Platform specific I/O definitions + +#if defined(__AVR__) +#elif defined(__MK20DX128__) +#elif defined(__SAM3X8E__) +#ifndef PROGMEM +#define PROGMEM +#endif +#ifndef pgm_read_byte +#define pgm_read_byte(addr) (*(const uint8_t *)(addr)) +#endif +#elif defined(__PIC32MX__) +#else +#endif + +#define DATA_MODE 0xE1 +#define COMMAND_MODE 0xE3 +#define PULSE_TERM 0xF1 + +class DS2480B +{ + private: + TasmotaSerial *_port; + bool isCmdMode; + +#if ONEWIRE_SEARCH + // global search state + unsigned char ROM_NO[8]; + uint8_t LastDiscrepancy; + uint8_t LastFamilyDiscrepancy; + uint8_t LastDeviceFlag; +#endif + + bool waitForReply(); + + public: + DS2480B(TasmotaSerial *port); + + void begin(); + + // Perform a 1-Wire reset cycle. Returns 1 if a device responds + // with a presence pulse. Returns 0 if there is no device or the + // bus is shorted or otherwise held low for more than 250uS + uint8_t reset(void); + + void beginTransaction(); + void endTransaction(); + + void commandMode(); + void dataMode(); + + // Issue a 1-Wire rom select command, you do the reset first. + void select(const uint8_t rom[8]); + + // Issue a 1-Wire rom skip command, to address all on bus. + void skip(void); + + // Write a byte. If 'power' is one then the wire is held high at + // the end for parasitically powered devices. You are responsible + // for eventually depowering it by calling depower() or doing + // another read or write. + void write(uint8_t v, uint8_t power = 0); + + void writeCmd(uint8_t v, uint8_t power = 0); + + void write_bytes(const uint8_t *buf, uint16_t count, bool power = 0); + + // Read a byte. + uint8_t read(void); + + void read_bytes(uint8_t *buf, uint16_t count); + + // Write a bit. + uint8_t write_bit(uint8_t v); + + // Read a bit. + uint8_t read_bit(void); + + // Stop forcing power onto the bus. You only need to do this if + // you used the 'power' flag to write() or used a write_bit() call + // and aren't about to do another read or write. You would rather + // not leave this powered if you don't have to, just in case + // someone shorts your bus. + void depower(void); + +#if ONEWIRE_SEARCH + // Clear the search state so that if will start from the beginning again. + void reset_search(); + + // Setup the search to find the device type 'family_code' on the next call + // to search(*newAddr) if it is present. + void target_search(uint8_t family_code); + + // Look for the next device. Returns 1 if a new address has been + // returned. A zero might mean that the bus is shorted, there are + // no devices, or you have already retrieved all of them. It + // might be a good idea to check the CRC to make sure you didn't + // get garbage. The order is deterministic. You will always get + // the same devices in the same order. + uint8_t search(uint8_t *newAddr); +#endif + +#if ONEWIRE_CRC + // Compute a Dallas Semiconductor 8 bit CRC, these are used in the + // ROM and scratchpad registers. + static uint8_t crc8(const uint8_t *addr, uint8_t len); + +#if ONEWIRE_CRC16 + // Compute the 1-Wire CRC16 and compare it against the received CRC. + // Example usage (reading a DS2408): + // // Put everything in a buffer so we can compute the CRC easily. + // uint8_t buf[13]; + // buf[0] = 0xF0; // Read PIO Registers + // buf[1] = 0x88; // LSB address + // buf[2] = 0x00; // MSB address + // WriteBytes(net, buf, 3); // Write 3 cmd bytes + // ReadBytes(net, buf+3, 10); // Read 6 data bytes, 2 0xFF, 2 CRC16 + // if (!CheckCRC16(buf, 11, &buf[11])) { + // // Handle error. + // } + // + // @param input - Array of bytes to checksum. + // @param len - How many bytes to use. + // @param inverted_crc - The two CRC16 bytes in the received data. + // This should just point into the received data, + // *not* at a 16-bit integer. + // @param crc - The crc starting value (optional) + // @return True, iff the CRC matches. + static bool check_crc16(const uint8_t* input, uint16_t len, const uint8_t* inverted_crc, uint16_t crc = 0); + + // Compute a Dallas Semiconductor 16 bit CRC. This is required to check + // the integrity of data received from many 1-Wire devices. Note that the + // CRC computed here is *not* what you'll get from the 1-Wire network, + // for two reasons: + // 1) The CRC is transmitted bitwise inverted. + // 2) Depending on the endian-ness of your processor, the binary + // representation of the two-byte return value may have a different + // byte order than the two bytes you get from 1-Wire. + // @param input - Array of bytes to checksum. + // @param len - How many bytes to use. + // @param crc - The crc starting value (optional) + // @return The CRC16, as defined by Dallas Semiconductor. + static uint16_t crc16(const uint8_t* input, uint16_t len, uint16_t crc = 0); +#endif +#endif +}; + +#endif diff --git a/lib/lib_div/DS2480B/README.md b/lib/lib_div/DS2480B/README.md new file mode 100644 index 000000000..0684ab42f --- /dev/null +++ b/lib/lib_div/DS2480B/README.md @@ -0,0 +1,6 @@ +DS2480B Library for Arduino +======================= + +Interfaces with DS2480B chip in order +to acquire access to the 1-wire bus over +serial \ No newline at end of file diff --git a/lib/lib_div/DS2480B/examples/DS18x20_Temperature/DS18x20_Temperature.ino b/lib/lib_div/DS2480B/examples/DS18x20_Temperature/DS18x20_Temperature.ino new file mode 100644 index 000000000..71014ee1e --- /dev/null +++ b/lib/lib_div/DS2480B/examples/DS18x20_Temperature/DS18x20_Temperature.ino @@ -0,0 +1,123 @@ +#include + +// OneWire DS18S20, DS18B20, DS1822 Temperature Example +// +// http://www.pjrc.com/teensy/td_libs_OneWire.html +// +// The DallasTemperature library can do all this work for you! +// http://milesburton.com/Dallas_Temperature_Control_Library + + +#include + +AltSoftSerial altSerial; //RX 8 - TX 9 + +DS2480B ds(altSerial); + +void setup(void) { + Serial.begin(9600); + altSerial.begin(9600); + ds.begin(); +} + +void loop(void) { + byte i; + byte present = 0; + byte type_s; + byte data[12]; + byte addr[8]; + float celsius, fahrenheit; + + if ( !ds.search(addr)) { + Serial.println("No more addresses."); + Serial.println(); + ds.reset_search(); + delay(5000); + return; + } + + Serial.print("ROM ="); + for( i = 0; i < 8; i++) { + Serial.write(' '); + Serial.print(addr[i], HEX); + } + + if (DS2480B::crc8(addr, 7) != addr[7]) { + Serial.println("CRC is not valid!"); + return; + } + Serial.println(); + + // the first ROM byte indicates which chip + switch (addr[0]) { + case 0x10: + Serial.println(" Chip = DS18S20"); // or old DS1820 + type_s = 1; + break; + case 0x28: + Serial.println(" Chip = DS18B20"); + type_s = 0; + break; + case 0x22: + Serial.println(" Chip = DS1822"); + type_s = 0; + break; + default: + Serial.println("Device is not a DS18x20 family device."); + return; + } + + ds.reset(); + + delay(100); + + ds.reset(); + ds.select(addr); + ds.write(0x44); // start conversion + + delay(1000); // maybe 750ms is enough, maybe not + // we might do a ds.depower() here, but the reset will take care of it. + + present = ds.reset(); + ds.select(addr); + ds.write(0xBE); // Read Scratchpad + + Serial.print(" Data = "); + Serial.print(present, HEX); + Serial.print(" "); + for ( i = 0; i < 9; i++) { // we need 9 bytes + data[i] = ds.read(); + Serial.print(data[i], HEX); + Serial.print(" "); + } + Serial.print(" CRC="); + Serial.print(DS2480B::crc8(data, 8), HEX); + Serial.println(); + + // Convert the data to actual temperature + // because the result is a 16 bit signed integer, it should + // be stored to an "int16_t" type, which is always 16 bits + // even when compiled on a 32 bit processor. + int16_t raw = (data[1] << 8) | data[0]; + if (type_s) { + raw = raw << 3; // 9 bit resolution default + if (data[7] == 0x10) { + // "count remain" gives full 12 bit resolution + raw = (raw & 0xFFF0) + 12 - data[6]; + } + } else { + byte cfg = (data[4] & 0x60); + // at lower res, the low bits are undefined, so let's zero them + if (cfg == 0x00) raw = raw & ~7; // 9 bit resolution, 93.75 ms + else if (cfg == 0x20) raw = raw & ~3; // 10 bit res, 187.5 ms + else if (cfg == 0x40) raw = raw & ~1; // 11 bit res, 375 ms + //// default is 12 bit resolution, 750 ms conversion time + } + celsius = (float)raw / 16.0; + fahrenheit = celsius * 1.8 + 32.0; + Serial.print(" Temperature = "); + Serial.print(celsius); + Serial.print(" Celsius, "); + Serial.print(fahrenheit); + Serial.println(" Fahrenheit"); +} diff --git a/lib/lib_div/DS2480B/keywords.txt b/lib/lib_div/DS2480B/keywords.txt new file mode 100644 index 000000000..67c3b7938 --- /dev/null +++ b/lib/lib_div/DS2480B/keywords.txt @@ -0,0 +1,64 @@ +####################################### +# Syntax Coloring Map For DS2480B +####################################### + +####################################### +# Datatypes (KEYWORD1) +####################################### + +RTC_clock KEYWORD1 + +####################################### +# Methods and Functions (KEYWORD2) +####################################### + +init KEYWORD2 +set_time KEYWORD2 +set_date KEYWORD2 +set_seconds KEYWORD2 +set_minutes KEYWORD2 +set_hours KEYWORD2 +set_days KEYWORD2 +set_months KEYWORD2 +set_years KEYWORD2 +set_alarmtime KEYWORD2 +set_alarmdate KEYWORD2 +get_seconds KEYWORD2 +get_minutes KEYWORD2 +get_hours KEYWORD2 +get_days KEYWORD2 +get_day_of_week KEYWORD2 +get_months KEYWORD2 +get_years KEYWORD2 +attachalarm KEYWORD2 +disable_alarm KEYWORD2 +unixtime KEYWORD2 +get_time KEYWORD2 +get_date KEYWORD2 +summertime KEYWORD2 +switch_years KEYWORD2 +timing KEYWORD2 +date_already_set KEYWORD2 + +####################################### +# Instances (KEYWORD2) +####################################### + + +####################################### +# Mean Instances (KEYWORD3) +####################################### + +rtc_clock KEYWORD3 + +####################################### +# Constants (LITERAL1) +####################################### + +RC LITERAL1 +XTAL LITERAL1 + +__TIME__ LITERAL1 +__DATE__ LITERAL1 + +Germany LITERAL1 \ No newline at end of file diff --git a/lib/lib_div/DS2480B/library.properties b/lib/lib_div/DS2480B/library.properties new file mode 100644 index 000000000..8773c7971 --- /dev/null +++ b/lib/lib_div/DS2480B/library.properties @@ -0,0 +1,9 @@ +name=DS2480 Library +version=0.1.0 +author=Jim Studt +maintainer=Jim Studt +sentence=DS2480 +paragraph=DS2480 +category=Communication +architectures=* +includes=DS2408.h diff --git a/tasmota/include/Powerwall.h b/tasmota/include/Powerwall.h index 324bfa262..435ab27cd 100755 --- a/tasmota/include/Powerwall.h +++ b/tasmota/include/Powerwall.h @@ -24,6 +24,7 @@ class Powerwall { String GetRequest(String url, String authCookie); String GetRequest(String url); String AuthCookie(); + void resetAuthCookie(); }; @@ -37,6 +38,9 @@ Powerwall::Powerwall() { String Powerwall::AuthCookie() { return authCookie; } +void Powerwall::resetAuthCookie() { + authCookie = ""; +} /** * This function returns a string with the authToken based on the basic login endpoint of @@ -165,6 +169,7 @@ String Powerwall::GetRequest(String url, String authCookie) { // in case of error 401, get new cookie if (result == 401) { authCookie = ""; + resetAuthCookie(); } } } diff --git a/tasmota/tasmota_xdrv_driver/xdrv_10_scripter.ino b/tasmota/tasmota_xdrv_driver/xdrv_10_scripter.ino index 076989292..4b209123e 100755 --- a/tasmota/tasmota_xdrv_driver/xdrv_10_scripter.ino +++ b/tasmota/tasmota_xdrv_driver/xdrv_10_scripter.ino @@ -46,11 +46,10 @@ keywords if then else endif, or, and are better readable for beginners (others m #ifndef TS_FLOAT #define TS_FLOAT float #endif - - // float = 4, double = 8 bytes -const uint8_t SCRIPT_VERS[2] = {5, 1}; + +const uint8_t SCRIPT_VERS[2] = {5, 2}; #define SCRIPT_DEBUG 0 @@ -190,6 +189,8 @@ void Script_ticker4_end(void) { } #endif + + // EEPROM MACROS // i2c eeprom #define EEP_WRITE(A,B,C) eeprom_writeBytes(A, B, (uint8_t*)C); @@ -439,6 +440,22 @@ struct SCRIPT_SPI { #define FLT_MAX 99999999 #endif +#ifdef USE_SCRIPT_ONEWIRE +#include +#include + +#ifndef MAX_DS_SENSORS +#define MAX_DS_SENSORS 20 +#endif + +typedef struct { + OneWire *ds; + DS2480B *dsh; + TasmotaSerial *ts; + uint8_t ds_address[MAX_DS_SENSORS][8]; +} ScriptOneWire; +#endif // USE_SCRIPT_ONEWIRE + #define SFS_MAX 4 // global memory struct SCRIPT_MEM { @@ -499,6 +516,7 @@ struct SCRIPT_MEM { char *web_pages[10]; uint32_t script_lastmillis; bool event_handeled = false; + bool res_ivar = false; #ifdef USE_BUTTON_EVENT int8_t script_button[MAX_KEYS]; #endif //USE_BUTTON_EVENT @@ -523,6 +541,15 @@ struct SCRIPT_MEM { char *hstr; #endif +#ifdef USE_SCRIPT_I2C + uint8_t script_i2c_addr; + TwoWire *script_i2c_wire; +#endif + +#ifdef USE_SCRIPT_ONEWIRE + ScriptOneWire ow; +#endif + } glob_script_mem; @@ -533,10 +560,10 @@ void flt2char(TS_FLOAT num, char *nbuff) { dtostrfd(num, glob_script_mem.script_dprec, nbuff); } -void f2char(TS_FLOAT num, uint32_t dprec, uint32_t lzeros, char *nbuff, char dsep); +void f2char(double num, uint32_t dprec, uint32_t lzeros, char *nbuff, char dsep); // convert float to char with leading zeros -void f2char(TS_FLOAT num, uint32_t dprec, uint32_t lzeros, char *nbuff, char dsep) { +void f2char(double num, uint32_t dprec, uint32_t lzeros, char *nbuff, char dsep) { dtostrfd(num, dprec, nbuff); if (lzeros > 1) { // check leading zeros @@ -583,6 +610,8 @@ int32_t opt_fext(File *fp, char *ts_from, char *ts_to, uint32_t flg); int32_t extract_from_file(File *fp, char *ts_from, char *ts_to, int8_t coffs, TS_FLOAT **a_ptr, uint16_t *a_len, uint8_t numa, int16_t accum); #endif char *eval_sub(char *lp, TS_FLOAT *fvar, char *rstr); +uint32_t script_ow(uint8_t sel, uint32_t val); +int32_t script_logfile_write(char *path, char *payload, uint32_t size); void ScriptEverySecond(void) { @@ -2296,6 +2325,33 @@ uint32_t match_vars(char *dvnam, TS_FLOAT **fp, char **sp, uint32_t *ind) { #define SCRIPT_IS_STRING_MAXSIZE 256 #endif + +void script_sort_string_array(uint8_t num) { + uint16_t sasize = glob_script_mem.si_num[num]; + char *sa = glob_script_mem.last_index_string[num]; + if (!sa) { + return; + } + char temp[SCRIPT_MAXSSIZE]; + bool swapped; + do { + swapped = false; + for (uint16_t i = 0; i < sasize - 1; ++i) { + char *s1 = sa + (i * glob_script_mem.max_ssize); + char *s2 = sa + ((i + 1) * glob_script_mem.max_ssize); + if (strcmp(s1, s2) > 0) { + // swap + strcpy(temp, s1); + strcpy(s1, s2); + strcpy(s2, temp); + swapped = true; + } + } + sasize -= 1; + } while (swapped); +} + + char *isargs(char *lp, uint32_t isind) { TS_FLOAT fvar; lp = GetNumericArgument(lp, OPER_EQU, &fvar, 0); @@ -2331,11 +2387,11 @@ char *isargs(char *lp, uint32_t isind) { glob_script_mem.si_num[isind] = MAX_SARRAY_NUM; } - glob_script_mem.last_index_string[isind] = (char*)calloc(glob_script_mem.max_ssize*glob_script_mem.si_num[isind], 1); - for (uint32_t cnt = 0; cnt glob_script_mem.si_num[isind]) { - index = glob_script_mem.si_num[isind]; + if (index >= glob_script_mem.si_num[isind]) { + index = glob_script_mem.si_num[isind] - 1; } strlcpy(str,glob_script_mem.last_index_string[isind] + (index * glob_script_mem.max_ssize), glob_script_mem.max_ssize); } @@ -2449,6 +2505,7 @@ char *isvar(char *lp, uint8_t *vtype, struct T_INDEX *tind, TS_FLOAT *fp, char * // isnumber if (fp) { if (*lp == '0' && *(lp + 1) == 'x') { + lp += 2; *fp = strtoll(lp, &lp, 16); } else { @@ -2466,6 +2523,8 @@ char *isvar(char *lp, uint8_t *vtype, struct T_INDEX *tind, TS_FLOAT *fp, char * return lp; } + + if (*lp == '"') { lp++; while (*lp != '"') { @@ -2521,6 +2580,7 @@ char *isvar(char *lp, uint8_t *vtype, struct T_INDEX *tind, TS_FLOAT *fp, char * olen = strlen(dvnam); } + glob_script_mem.arres = 0; for (count = 0; count < glob_script_mem.numvars; count++) { char *cp = glob_script_mem.glob_vnp + glob_script_mem.vnp_offset[count]; @@ -2558,6 +2618,7 @@ char *isvar(char *lp, uint8_t *vtype, struct T_INDEX *tind, TS_FLOAT *fp, char * } } + #define USE_SCRIPT_JSON //#define USE_SCRIPT_FULL_JSON_PARSER @@ -2785,6 +2846,19 @@ chknext: goto nfuncexit; } + if (!strncmp_XP(lp, XPSTR("as("), 3)) { + uint16_t alen; + TS_FLOAT *fa; + lp = get_array_by_name(lp + 3, &fa, &alen, 0); + if (!fa) { + fvar = -1; + goto exit; + } + script_sort_array(fa, alen); + fvar = 0; + goto nfuncexit; + } + if (!strncmp_XP(lp, XPSTR("af("), 3)) { // array to float uint16_t alend; @@ -3022,6 +3096,10 @@ extern void W8960_SetGain(uint8_t sel, uint16_t value); goto nfuncexit; } #endif + if (!strncmp_XP(vname, XPSTR("ctper"), 5)) { + fvar = TasmotaGlobal.tele_period; + goto exit; + } break; case 'd': if (!strncmp_XP(vname, XPSTR("day"), 3)) { @@ -3268,6 +3346,13 @@ extern void W8960_SetGain(uint8_t sel, uint16_t value); lp = GetNumericArgument(lp, OPER_EQU, &fvar, gv); uint8_t find = fvar; if (find >= SFS_MAX) find = SFS_MAX - 1; + while (*lp == ' ') lp++; + uint8_t options = 0; + if (*lp != ')') { + // options + lp = GetNumericArgument(lp, OPER_EQU, &fvar, gv); + options = fvar; + } uint8_t index = 0; char str[SCRIPT_MAXSSIZE]; char *cp = str; @@ -3298,9 +3383,12 @@ extern void W8960_SetGain(uint8_t sel, uint16_t value); while (glob_script_mem.files[find].available()) { uint8_t buf[1]; glob_script_mem.files[find].read(buf,1); - if (buf[0] == '\t' || buf[0] == ',' || buf[0] == '\n' || buf[0] == '\r') { + if (!options && (buf[0] == '\t' || buf[0] == ',' || buf[0] == '\n' || buf[0] == '\r')) { break; } else { + if (options && (buf[0] == '\n' || buf[0] == '\r')) { + break; + } *cp++ = buf[0]; index++; if (index >= glob_script_mem.max_ssize - 1) break; @@ -3679,6 +3767,13 @@ extern void W8960_SetGain(uint8_t sel, uint16_t value); goto nfuncexit; } #endif + + if (!strncmp_XP(lp, XPSTR("f("), 2)) { + // convert to float var + lp = GetNumericArgument(lp + 2, OPER_EQU, &fvar, gv); + fvar = *(uint32_t*)&fvar; + goto nfuncexit; + } break; case 'g': @@ -3732,7 +3827,7 @@ extern void W8960_SetGain(uint8_t sel, uint16_t value); } } else { // preserve mqtt_data - char *mqd = (char*)malloc(ResponseSize()+2); + char *mqd = (char*)malloc(ResponseSize() + 2); if (mqd) { strlcpy(mqd, ResponseData(), ResponseSize()); wd = mqd; @@ -4025,7 +4120,11 @@ extern void W8960_SetGain(uint8_t sel, uint16_t value); // arg2 TS_FLOAT fvar2; lp = GetNumericArgument(lp, OPER_EQU, &fvar2, gv); - fvar = script_i2c(9 + bytes, fvar, fvar2); + if (glob_script_mem.res_ivar) { + fvar = script_i2c(9 + bytes, fvar, *(uint32_t*)&fvar2); + } else { + fvar = script_i2c(9 + bytes, fvar, fvar2); + } goto nfuncexit; } if (!strncmp_XP(lp, XPSTR("ir"), 2)) { @@ -4038,7 +4137,12 @@ extern void W8960_SetGain(uint8_t sel, uint16_t value); lp++; } lp = GetNumericArgument(lp + 1, OPER_EQU, &fvar, gv); - fvar = script_i2c(2, fvar, bytes); + if (glob_script_mem.res_ivar) { + uint32_t intres = script_i2c(2, fvar, bytes); + (*(uint32_t*)&fvar) = intres; + } else { + fvar = script_i2c(2, fvar, bytes); + } goto nfuncexit; } #endif // USE_SCRIPT_I2C @@ -4070,6 +4174,12 @@ extern void W8960_SetGain(uint8_t sel, uint16_t value); } #endif // USE_I2S_AUDIO #endif // ESP32 + if (!strncmp_XP(lp, XPSTR("i("), 2)) { + // convert to integer var + lp = GetNumericArgument(lp + 2, OPER_EQU, &fvar, gv); + *(uint32_t*)&fvar = fvar; + goto nfuncexit; + } break; #ifdef ESP32 @@ -4144,6 +4254,21 @@ extern void W8960_SetGain(uint8_t sel, uint16_t value); goto nfuncexit; } #endif // USE_LVGL + +#ifdef USE_UFILESYS +#ifdef USE_SCRIPT_FATFS_EXT + if (!strncmp_XP(lp, XPSTR("lfw("), 4)) { + char path[SCRIPT_MAXSSIZE]; + lp = GetStringArgument(lp + 4, OPER_EQU, path, 0); + char payload[SCRIPT_MAXSSIZE]; + lp = GetStringArgument(lp, OPER_EQU, payload, 0); + lp = GetNumericArgument(lp, OPER_EQU, &fvar, 0); + // write to logfile + fvar = script_logfile_write(path, payload, fvar); + goto nfuncexit; + } +#endif // USE_SCRIPT_FATFS_EXT +#endif break; case 'm': if (!strncmp_XP(lp, XPSTR("med("), 4)) { @@ -4293,6 +4418,21 @@ extern void W8960_SetGain(uint8_t sel, uint16_t value); } break; +#ifdef USE_SCRIPT_ONEWIRE + case 'o': + if (!strncmp_XP(vname, XPSTR("ow("), 3)) { + lp = GetNumericArgument(lp + 3, OPER_EQU, &fvar, 0); + uint8_t sel = fvar; + SCRIPT_SKIP_SPACES + if (*lp != ')') { + lp = GetNumericArgument(lp, OPER_EQU, &fvar, 0); + } + fvar = script_ow(sel, fvar); + goto nfuncexit; + } + break; +#endif // USE_SCRIPT_ONEWIRE + case 'p': if (!strncmp_XP(lp, XPSTR("pin["), 4)) { // raw pin level @@ -4610,9 +4750,15 @@ extern void W8960_SetGain(uint8_t sel, uint16_t value); lp++; } } + bool isint = is_int_var(lp); lp = GetNumericArgument(lp, OPER_EQU, &fvar, gv); char str[SCRIPT_MAXSSIZE]; - f2char(fvar, dprec, lzero, str, dsep); + if (isint) { + double dvar = *(int32_t*)&fvar; + f2char(dvar, dprec, lzero, str, dsep); + } else { + f2char(fvar, dprec, lzero, str, dsep); + } if (sp) strlcpy(sp, str, glob_script_mem.max_ssize); lp++; len = 0; @@ -5174,6 +5320,14 @@ extern char *SML_GetSVal(uint32_t index); #endif //USE_SCRIPT_SERIAL + if (!strncmp_XP(lp, XPSTR("sas("), 4)) { + lp = GetNumericArgument(lp + 4, OPER_EQU, &fvar, 0); + if (fvar < 1 || fvar > 3) { + fvar = 1; + } + script_sort_string_array(fvar - 1); + goto nfuncexit; + } #ifdef USE_SCRIPT_SPI if (!strncmp_XP(lp, XPSTR("spi("), 4)) { @@ -5789,6 +5943,43 @@ char *getop(char *lp, uint8_t *operand) { return lp; } +#ifdef USE_SCRIPT_FATFS_EXT +#ifdef USE_UFILESYS +int32_t script_logfile_write(char *path, char *payload, uint32_t size) { + + File rfd = ufsp->open(path, FS_FILE_APPEND); + if (rfd == 0) { + return -1; + } + + uint32_t fsize = rfd.size(); + // append string + rfd.write((uint8_t*)payload, strlen(payload)); + rfd.write((uint8_t*)"\n", 1); + if (fsize < size) { + rfd.close(); + return fsize; + } + rfd.seek(0, SeekSet); + String line = rfd.readStringUntil('\n'); + File wfd = ufsp->open("/ltmp", FS_FILE_WRITE); + if (!wfd) { + return -2; + } + while (rfd.available()) { + line = rfd.readStringUntil('\n'); + wfd.write((uint8_t*)line.c_str(), line.length()); + wfd.write((uint8_t*)"\n", 1); + } + rfd.close(); + wfd.close(); + ufsp->remove(path); + ufsp->rename("/ltmp", path); + + return fsize; +} +#endif // USE_UFILESYS +#endif // USE_SCRIPT_FATFS_EXT #ifdef ESP8266 extern "C" { @@ -6090,6 +6281,24 @@ extern "C" { #endif // USE_HOMEKIT + +bool is_int_var(char *name) { +uint8_t vtype; +struct T_INDEX ind; + + isvar(name, &vtype, &ind, 0, 0, 0); + + if (vtype != VAR_NV) { + if (vtype == NUM_RES || (vtype & STYPE) == 0) { + // numeric result + if (ind.bits.integer) { + return true; + } + } + } + return false; +} + // replace vars in cmd %var% void Replace_Cmd_Vars(char *srcbuf, uint32_t srcsize, char *dstbuf, uint32_t dstsize) { char *cp; @@ -6148,7 +6357,8 @@ void Replace_Cmd_Vars(char *srcbuf, uint32_t srcsize, char *dstbuf, uint32_t dst if (vtype == NUM_RES || (vtype & STYPE) == 0) { // numeric result if (ind.bits.integer) { - dtostrfd(*(int32_t*)&fvar, 0, string); + double dval = *(int32_t*)&fvar; + f2char(dval, dprec, lzero, string, dsep); } else { f2char(fvar, dprec, lzero, string, dsep); } @@ -7147,6 +7357,7 @@ getnext: } else { dfvar = &glob_script_mem.fvars[index]; sysv_type = 0; + glob_script_mem.res_ivar = ind.bits.integer; } numeric = 1; SCRIPT_SKIP_SPACES @@ -7483,6 +7694,201 @@ getnext: return -1; } +#ifdef USE_SCRIPT_ONEWIRE + +uint32_t script_ow(uint8_t sel, uint32_t val) { +uint32_t res = 0; +uint8_t bits; +bool invert = false; +ScriptOneWire *ow = &glob_script_mem.ow; + + if (sel >= 10 && sel <= 18) { + if (val < 1 || val > MAX_DS_SENSORS) { + val = 1; + } + return ow->ds_address[val - 1][sel - 10]; + } + + if (sel > 0 && (ow->ds == nullptr && ow->dsh == nullptr)) { + return 0xffff; + } + + switch (sel) { + case 0: + if (val & 0x8000) { + if (val & 0x10000) { + // inverted serial + invert = true; + } + val &= 0x7fff; + ow->ts = new TasmotaSerial(val & 0xff, (val >> 8) & 0x7f, 1, 0, 64); + if (ow->ts) { + + ow->ts->begin(9600); + +#ifdef ESP8266 + if (ow->ts->hardwareSerial()) { + ClaimSerial(); +#ifdef ALLOW_OW_INVERT + if (invert == true) { + U0C0 = U0C0 | BIT(UCRXI) | BIT(UCTXI); // Inverse RX, TX + } +#endif + } +#endif // ESP8266 + +#ifdef ESP32 +#ifdef ALLOW_OW_INVERT + if (invert == true) { + HardwareSerial *hws = ow->ts->getesp32hws(); + hws->end(); + hws->begin(9600, SERIAL_8N1, val & 0xff, (val >> 8) & 0x7f, true); + } +#endif +#endif // ESP32 + + ow->dsh = new DS2480B(ow->ts); + ow->dsh->begin(); + } + ow->ds = nullptr; + } else { + ow->ds = new OneWire(val); + ow->dsh = nullptr; + } + break; + case 1: + if (ow->ds) { + ow->ds->reset(); + } else { + ow->dsh->reset(); + } + break; + case 2: + if (ow->ds) { + ow->ds->skip(); + } else { + ow->dsh->skip(); + } + break; + case 3: + if (ow->ds) { + ow->ds->write(val, 1); + } else { + ow->dsh->write(val, 1); + } + break; + case 4: + if (ow->ds) { + return ow->ds->read(); + } else { + return ow->dsh->read(); + } + break; + case 5: + if (ow->ds) { + ow->ds->reset_search(); + } else { + ow->dsh->reset_search(); + } + break; + case 6: + if (val < 1 || val > MAX_DS_SENSORS) { + val = 1; + } + if (ow->ds) { + return ow->ds->search(ow->ds_address[val - 1]); + } else { + return ow->dsh->search(ow->ds_address[val - 1]); + } + break; + case 7: + if (val < 1 || val > MAX_DS_SENSORS) { + val = 1; + } + if (ow->ds) { + ow->ds->select(ow->ds_address[val - 1]); + } else { + ow->dsh->select(ow->ds_address[val - 1]); + } + break; + case 8: + bits = val & 0xc0; + val &= 0x3f; + if (val < 1 || val > MAX_DS_SENSORS) { + val = 1; + } + if (ow->ds) { + ow->ds->reset(); + ow->ds->select(ow->ds_address[val - 1]); + ow->ds->write(0xf5, 1); + ow->ds->write(0x0c, 1); + ow->ds->write(0xff, 1); + res = ow->ds->read(); + ow->ds->write(bits, 1); + } else { + ow->dsh->reset(); + ow->dsh->select(ow->ds_address[val - 1]); + ow->dsh->write(0xf5, 1); + ow->dsh->write(0x0c, 1); + ow->dsh->write(0xff, 1); + res = ow->dsh->read(); + ow->dsh->write(bits, 1); + } + break; + case 9: + bits = val & 0x80; + val &= 0x3f; + if (val < 1 || val > MAX_DS_SENSORS) { + val = 1; + } + + if (ow->ds) { + ow->ds->reset(); + ow->ds->select(ow->ds_address[val - 1]); + if (!bits) { + ow->ds->write(0x44, 1); + } else { + ow->ds->write(0xbe, 1); + delay(10); + res = ow->ds->read(); + res |= ow->ds->read() << 8; + ow->ds->reset(); + } + } else { + ow->dsh->reset(); + ow->dsh->select(ow->ds_address[val - 1]); + if (!bits) { + ow->dsh->write(0x44, 1); + } else { + ow->dsh->write(0xbe, 1); + delay(10); + res = ow->dsh->read(); + res |= ow->dsh->read() << 8; + ow->dsh->reset(); + } + } + break; + case 99: + if (ow->ds) { + ow->ds->reset(); + delete ow->ds; + ow->ds = nullptr; + } else { + ow->dsh->reset(); + delete ow->dsh; + ow->dsh = nullptr; + delete ow->ts; + } + break; + case 98: + ow->ts->write(val); + break; + } + return res; +} +#endif // USE_SCRIPT_ONEWIRE + + #ifdef USE_SCRIPT_SPI // transfer 1-3 bytes @@ -7586,6 +7992,25 @@ bool Script_Close_Serial() { } #endif //USE_SCRIPT_SERIAL + +void script_sort_array(float *array, uint16_t size) { + bool swapped; + do { + swapped = false; + for (uint16_t i = 0; i < size - 1; ++i) { + if (array[i] > array[i + 1]) { + // swap + float tmp = array[i]; + array[i] = array[i + 1]; + array[i + 1] = tmp; + swapped = true; + } + } + size -= 1; + } while (swapped); +} + + bool Is_gpio_used(uint8_t gpiopin) { if (gpiopin >= 0 && (gpiopin < nitems(TasmotaGlobal.gpio_pin)) && (TasmotaGlobal.gpio_pin[gpiopin] > 0)) { return true; @@ -9673,6 +10098,11 @@ void Script_Check_HTML_Setvars(void) { *cp1 = '='; cp1++; + if (is_int_var(vname)) { + memmove(cp1 + 1, cp1, strlen(cp1)); + *cp1++ = '#'; + } + struct T_INDEX ind; uint8_t vtype; isvar(vname, &vtype, &ind, 0, 0, 0); @@ -10143,7 +10573,7 @@ const char *gc_str; lp = GetStringArgument(lp, OPER_EQU, right, 0); SCRIPT_SKIP_SPACES - WSContentSend_P(SCRIPT_MSG_SLIDER, left,mid, right, (uint32_t)min, (uint32_t)max, (uint32_t)val, vname); + WSContentSend_P(SCRIPT_MSG_SLIDER, left, mid, right, (uint32_t)min, (uint32_t)max, (uint32_t)val, vname); lp++; } else if (!strncmp(lin, "ck(", 3)) { @@ -10400,6 +10830,8 @@ const char *gc_str; char vname[16]; ScriptGetVarname(vname, slp, sizeof(vname)); + bool isint = is_int_var(vname); + char label[SCRIPT_MAXSSIZE]; lp = GetStringArgument(lp, OPER_EQU, label, 0); SCRIPT_SKIP_SPACES @@ -10417,10 +10849,17 @@ const char *gc_str; } char vstr[16],minstr[16],maxstr[16],stepstr[16]; - dtostrfd(val, dprec, vstr); - dtostrfd(min, dprec, minstr); - dtostrfd(max, dprec, maxstr); - dtostrfd(step, dprec, stepstr); + if (isint) { + dtostrfd(*(int32_t*)&val, 0, vstr); + dtostrfd(*(int32_t*)&min, dprec, minstr); + dtostrfd(*(int32_t*)&max, dprec, maxstr); + dtostrfd(*(int32_t*)&step, dprec, stepstr); + } else { + dtostrfd(val, dprec, vstr); + dtostrfd(min, dprec, minstr); + dtostrfd(max, dprec, maxstr); + dtostrfd(step, dprec, stepstr); + } WCS_DIV(specopt); WSContentSend_P(SCRIPT_MSG_NUMINP, center, label, minstr, maxstr, stepstr, vstr, tsiz, vname); WCS_DIV(specopt | WSO_STOP_DIV); @@ -10867,8 +11306,11 @@ exgc: WSContentSend_P(PSTR("%s"), lin); } #else - if (!(specopt&WSO_FORCEMAIN)) { - lin++; + + if (mc != 'z') { + if (!(specopt&WSO_FORCEMAIN)) { + lin++; + } } WSContentSend_P(PSTR("%s"), lin); } else { @@ -11305,10 +11747,27 @@ int32_t call2pwl(const char *url) { result.replace("instant", "i"); result.replace("apparent", "a"); result.replace("reactive", "r"); + +// custom replace +#ifdef TESLA_POWERWALL_CTS1 + result.replace(TESLA_POWERWALL_CTS1, "PW_CTS1"); +#endif + +#ifdef TESLA_POWERWALL_CTS2 + result.replace(TESLA_POWERWALL_CTS2, "PW_CTS2"); +#endif + if (result.length()>4095) { AddLog(LOG_LEVEL_INFO, PSTR("PWL: result overflow: %d"), result.length()); } + +#ifdef MQTT_DATA_STRING + TasmotaGlobal.mqtt_data = result; +#else + strncpy(TasmotaGlobal.mqtt_data, result.c_str(), MESSZ); +#endif + // meter aggregates has also too many tokens char *cp = (char*)result.c_str(); if (!strncmp_P(cp, PSTR("{\"site\""), 7)) { @@ -11415,36 +11874,38 @@ void cpy2lf(char *dst, uint32_t dstlen, char *src) { } #ifdef USE_SCRIPT_I2C -uint8_t script_i2c_addr; -TwoWire *script_i2c_wire; uint32_t script_i2c(uint8_t sel, uint16_t val, uint32_t val1) { uint32_t rval = 0; uint8_t bytes = 1; + if (sel > 0) { + if (!glob_script_mem.script_i2c_wire) return 0; + } + switch (sel) { case 0: - script_i2c_addr = val; + glob_script_mem.script_i2c_addr = val; #ifdef ESP32 - if (val1 == 0) script_i2c_wire = &Wire; - else script_i2c_wire = &Wire1; + if (val1 == 0) glob_script_mem.script_i2c_wire = &Wire; + else glob_script_mem.script_i2c_wire = &Wire1; #else - script_i2c_wire = &Wire; + glob_script_mem.script_i2c_wire = &Wire; #endif - script_i2c_wire->beginTransmission(script_i2c_addr); - return (0 == script_i2c_wire->endTransmission()); + glob_script_mem.script_i2c_wire->beginTransmission(glob_script_mem.script_i2c_addr); + return (0 == glob_script_mem.script_i2c_wire->endTransmission()); break; case 2: // read 1..4 bytes if ((val & 0x8000) == 0) { - script_i2c_wire->beginTransmission(script_i2c_addr); - script_i2c_wire->write(val); - script_i2c_wire->endTransmission(); + glob_script_mem.script_i2c_wire->beginTransmission(glob_script_mem.script_i2c_addr); + glob_script_mem.script_i2c_wire->write(val); + glob_script_mem.script_i2c_wire->endTransmission(); } - script_i2c_wire->requestFrom((int)script_i2c_addr, (int)val1); + glob_script_mem.script_i2c_wire->requestFrom((int)glob_script_mem.script_i2c_addr, (int)val1); for (uint8_t cnt = 0; cnt < val1; cnt++) { rval <<= 8; - rval |= (uint8_t)script_i2c_wire->read(); + rval |= (uint8_t)glob_script_mem.script_i2c_wire->read(); } break; @@ -11454,23 +11915,23 @@ uint32_t script_i2c(uint8_t sel, uint16_t val, uint32_t val1) { case 13: // write 1 .. 4 bytes bytes = sel - 9; - script_i2c_wire->beginTransmission(script_i2c_addr); + glob_script_mem.script_i2c_wire->beginTransmission(glob_script_mem.script_i2c_addr); if ((val & 0x8000) == 0) { - script_i2c_wire->write(val); + glob_script_mem.script_i2c_wire->write(val); } if ((val & 0x4000) == 0) { for (uint8_t cnt = 0; cnt < bytes; cnt++) { - script_i2c_wire->write(val1); + glob_script_mem.script_i2c_wire->write(val1); val1 >>= 8; } } else { uint32_t wval = 0; for (uint8_t cnt = 0; cnt < bytes; cnt++) { wval = val1 >> ((bytes - 1 - cnt) * 8); - script_i2c_wire->write(wval); + glob_script_mem.script_i2c_wire->write(wval); } } - script_i2c_wire->endTransmission(); + glob_script_mem.script_i2c_wire->endTransmission(); break; } @@ -11918,6 +12379,9 @@ bool Xdrv10(uint32_t function) case FUNC_INIT: //bitWrite(Settings->rule_enabled, 0, 0); // >>>>>>>>>>> +#ifndef NO_SCRIPT_STOP_ON_ERROR + bitWrite(Settings->rule_stop, 0, 1); +#endif // set defaults to rules memory //bitWrite(Settings->rule_enabled,0,0); @@ -12224,6 +12688,10 @@ bool Xdrv10(uint32_t function) } } break; + case FUNC_BUTTON_MULTI_PRESSED: + if (bitRead(Settings->rule_enabled, 0)) { + Run_Scripter1(">b", 2, 0); + } #endif //USE_BUTTON_EVENT case FUNC_LOOP: