Scripter update (#18578)

* add ds2480b library

* add onewire support

* reset on error

* call to get esp32 HWS

* DS2480 serial invert option
This commit is contained in:
gemu 2023-05-05 09:17:17 +02:00 committed by GitHub
parent 13de7a4863
commit 687b26ad13
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 1486 additions and 41 deletions

View File

@ -64,6 +64,7 @@ class TasmotaSerial : public Stream {
uint32_t getLoopReadMetric(void) const { return m_bit_follow_metric; } uint32_t getLoopReadMetric(void) const { return m_bit_follow_metric; }
#ifdef ESP32 #ifdef ESP32
uint32_t getUart(void) const { return m_uart; } uint32_t getUart(void) const { return m_uart; }
HardwareSerial *getesp32hws(void) { return TSerial; }
#endif #endif
bool isValid(void) { return m_valid; } bool isValid(void) { return m_valid; }
bool overflow(void); bool overflow(void);

View File

@ -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

View File

@ -0,0 +1,200 @@
#ifndef DS2480B_h
#define DS2480B_h
#include <inttypes.h>
#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 <TasmotaSerial.h>
// 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

View File

@ -0,0 +1,6 @@
DS2480B Library for Arduino
=======================
Interfaces with DS2480B chip in order
to acquire access to the 1-wire bus over
serial

View File

@ -0,0 +1,123 @@
#include <DS2480B.h>
// 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.h>
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");
}

View File

@ -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

View File

@ -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

View File

@ -24,6 +24,7 @@ class Powerwall {
String GetRequest(String url, String authCookie); String GetRequest(String url, String authCookie);
String GetRequest(String url); String GetRequest(String url);
String AuthCookie(); String AuthCookie();
void resetAuthCookie();
}; };
@ -37,6 +38,9 @@ Powerwall::Powerwall() {
String Powerwall::AuthCookie() { String Powerwall::AuthCookie() {
return authCookie; return authCookie;
} }
void Powerwall::resetAuthCookie() {
authCookie = "";
}
/** /**
* This function returns a string with the authToken based on the basic login endpoint of * 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 // in case of error 401, get new cookie
if (result == 401) { if (result == 401) {
authCookie = ""; authCookie = "";
resetAuthCookie();
} }
} }
} }

View File

@ -46,11 +46,10 @@ keywords if then else endif, or, and are better readable for beginners (others m
#ifndef TS_FLOAT #ifndef TS_FLOAT
#define TS_FLOAT float #define TS_FLOAT float
#endif #endif
// float = 4, double = 8 bytes // 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 #define SCRIPT_DEBUG 0
@ -190,6 +189,8 @@ void Script_ticker4_end(void) {
} }
#endif #endif
// EEPROM MACROS // EEPROM MACROS
// i2c eeprom // i2c eeprom
#define EEP_WRITE(A,B,C) eeprom_writeBytes(A, B, (uint8_t*)C); #define EEP_WRITE(A,B,C) eeprom_writeBytes(A, B, (uint8_t*)C);
@ -439,6 +440,22 @@ struct SCRIPT_SPI {
#define FLT_MAX 99999999 #define FLT_MAX 99999999
#endif #endif
#ifdef USE_SCRIPT_ONEWIRE
#include <OneWire.h>
#include <DS2480B.h>
#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 #define SFS_MAX 4
// global memory // global memory
struct SCRIPT_MEM { struct SCRIPT_MEM {
@ -499,6 +516,7 @@ struct SCRIPT_MEM {
char *web_pages[10]; char *web_pages[10];
uint32_t script_lastmillis; uint32_t script_lastmillis;
bool event_handeled = false; bool event_handeled = false;
bool res_ivar = false;
#ifdef USE_BUTTON_EVENT #ifdef USE_BUTTON_EVENT
int8_t script_button[MAX_KEYS]; int8_t script_button[MAX_KEYS];
#endif //USE_BUTTON_EVENT #endif //USE_BUTTON_EVENT
@ -523,6 +541,15 @@ struct SCRIPT_MEM {
char *hstr; char *hstr;
#endif #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; } glob_script_mem;
@ -533,10 +560,10 @@ void flt2char(TS_FLOAT num, char *nbuff) {
dtostrfd(num, glob_script_mem.script_dprec, 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 // 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); dtostrfd(num, dprec, nbuff);
if (lzeros > 1) { if (lzeros > 1) {
// check leading zeros // 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); 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 #endif
char *eval_sub(char *lp, TS_FLOAT *fvar, char *rstr); 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) { 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 #define SCRIPT_IS_STRING_MAXSIZE 256
#endif #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) { char *isargs(char *lp, uint32_t isind) {
TS_FLOAT fvar; TS_FLOAT fvar;
lp = GetNumericArgument(lp, OPER_EQU, &fvar, 0); 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.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); 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.siro_num[isind]; cnt++) { for (uint32_t cnt = 0; cnt < glob_script_mem.siro_num[isind]; cnt++) {
char str[SCRIPT_MAXSSIZE]; char str[SCRIPT_MAXSSIZE];
GetTextIndexed(str, sizeof(str), cnt, sstart); GetTextIndexed(str, sizeof(str), cnt, sstart);
strlcpy(glob_script_mem.last_index_string[isind] + (cnt*glob_script_mem.max_ssize), str,glob_script_mem.max_ssize); strlcpy(glob_script_mem.last_index_string[isind] + (cnt * glob_script_mem.max_ssize), str, glob_script_mem.max_ssize);
} }
} else { } else {
glob_script_mem.last_index_string[isind] = sstart; glob_script_mem.last_index_string[isind] = sstart;
@ -2390,8 +2446,8 @@ TS_FLOAT fvar;
GetTextIndexed(str, sizeof(str), index , glob_script_mem.last_index_string[isind]); GetTextIndexed(str, sizeof(str), index , glob_script_mem.last_index_string[isind]);
} }
} else { } else {
if (index > glob_script_mem.si_num[isind]) { if (index >= glob_script_mem.si_num[isind]) {
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); 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 // isnumber
if (fp) { if (fp) {
if (*lp == '0' && *(lp + 1) == 'x') { if (*lp == '0' && *(lp + 1) == 'x') {
lp += 2; lp += 2;
*fp = strtoll(lp, &lp, 16); *fp = strtoll(lp, &lp, 16);
} else { } else {
@ -2466,6 +2523,8 @@ char *isvar(char *lp, uint8_t *vtype, struct T_INDEX *tind, TS_FLOAT *fp, char *
return lp; return lp;
} }
if (*lp == '"') { if (*lp == '"') {
lp++; lp++;
while (*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); olen = strlen(dvnam);
} }
glob_script_mem.arres = 0; glob_script_mem.arres = 0;
for (count = 0; count < glob_script_mem.numvars; count++) { for (count = 0; count < glob_script_mem.numvars; count++) {
char *cp = glob_script_mem.glob_vnp + glob_script_mem.vnp_offset[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_JSON
//#define USE_SCRIPT_FULL_JSON_PARSER //#define USE_SCRIPT_FULL_JSON_PARSER
@ -2785,6 +2846,19 @@ chknext:
goto nfuncexit; 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)) { if (!strncmp_XP(lp, XPSTR("af("), 3)) {
// array to float // array to float
uint16_t alend; uint16_t alend;
@ -3022,6 +3096,10 @@ extern void W8960_SetGain(uint8_t sel, uint16_t value);
goto nfuncexit; goto nfuncexit;
} }
#endif #endif
if (!strncmp_XP(vname, XPSTR("ctper"), 5)) {
fvar = TasmotaGlobal.tele_period;
goto exit;
}
break; break;
case 'd': case 'd':
if (!strncmp_XP(vname, XPSTR("day"), 3)) { 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); lp = GetNumericArgument(lp, OPER_EQU, &fvar, gv);
uint8_t find = fvar; uint8_t find = fvar;
if (find >= SFS_MAX) find = SFS_MAX - 1; 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; uint8_t index = 0;
char str[SCRIPT_MAXSSIZE]; char str[SCRIPT_MAXSSIZE];
char *cp = str; char *cp = str;
@ -3298,9 +3383,12 @@ extern void W8960_SetGain(uint8_t sel, uint16_t value);
while (glob_script_mem.files[find].available()) { while (glob_script_mem.files[find].available()) {
uint8_t buf[1]; uint8_t buf[1];
glob_script_mem.files[find].read(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; break;
} else { } else {
if (options && (buf[0] == '\n' || buf[0] == '\r')) {
break;
}
*cp++ = buf[0]; *cp++ = buf[0];
index++; index++;
if (index >= glob_script_mem.max_ssize - 1) break; 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; goto nfuncexit;
} }
#endif #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; break;
case 'g': case 'g':
@ -3732,7 +3827,7 @@ extern void W8960_SetGain(uint8_t sel, uint16_t value);
} }
} else { } else {
// preserve mqtt_data // preserve mqtt_data
char *mqd = (char*)malloc(ResponseSize()+2); char *mqd = (char*)malloc(ResponseSize() + 2);
if (mqd) { if (mqd) {
strlcpy(mqd, ResponseData(), ResponseSize()); strlcpy(mqd, ResponseData(), ResponseSize());
wd = mqd; wd = mqd;
@ -4025,7 +4120,11 @@ extern void W8960_SetGain(uint8_t sel, uint16_t value);
// arg2 // arg2
TS_FLOAT fvar2; TS_FLOAT fvar2;
lp = GetNumericArgument(lp, OPER_EQU, &fvar2, gv); 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; goto nfuncexit;
} }
if (!strncmp_XP(lp, XPSTR("ir"), 2)) { if (!strncmp_XP(lp, XPSTR("ir"), 2)) {
@ -4038,7 +4137,12 @@ extern void W8960_SetGain(uint8_t sel, uint16_t value);
lp++; lp++;
} }
lp = GetNumericArgument(lp + 1, OPER_EQU, &fvar, gv); 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; goto nfuncexit;
} }
#endif // USE_SCRIPT_I2C #endif // USE_SCRIPT_I2C
@ -4070,6 +4174,12 @@ extern void W8960_SetGain(uint8_t sel, uint16_t value);
} }
#endif // USE_I2S_AUDIO #endif // USE_I2S_AUDIO
#endif // ESP32 #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; break;
#ifdef ESP32 #ifdef ESP32
@ -4144,6 +4254,21 @@ extern void W8960_SetGain(uint8_t sel, uint16_t value);
goto nfuncexit; goto nfuncexit;
} }
#endif // USE_LVGL #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; break;
case 'm': case 'm':
if (!strncmp_XP(lp, XPSTR("med("), 4)) { if (!strncmp_XP(lp, XPSTR("med("), 4)) {
@ -4293,6 +4418,21 @@ extern void W8960_SetGain(uint8_t sel, uint16_t value);
} }
break; 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': case 'p':
if (!strncmp_XP(lp, XPSTR("pin["), 4)) { if (!strncmp_XP(lp, XPSTR("pin["), 4)) {
// raw pin level // raw pin level
@ -4610,9 +4750,15 @@ extern void W8960_SetGain(uint8_t sel, uint16_t value);
lp++; lp++;
} }
} }
bool isint = is_int_var(lp);
lp = GetNumericArgument(lp, OPER_EQU, &fvar, gv); lp = GetNumericArgument(lp, OPER_EQU, &fvar, gv);
char str[SCRIPT_MAXSSIZE]; 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); if (sp) strlcpy(sp, str, glob_script_mem.max_ssize);
lp++; lp++;
len = 0; len = 0;
@ -5174,6 +5320,14 @@ extern char *SML_GetSVal(uint32_t index);
#endif //USE_SCRIPT_SERIAL #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 #ifdef USE_SCRIPT_SPI
if (!strncmp_XP(lp, XPSTR("spi("), 4)) { if (!strncmp_XP(lp, XPSTR("spi("), 4)) {
@ -5789,6 +5943,43 @@ char *getop(char *lp, uint8_t *operand) {
return lp; 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 #ifdef ESP8266
extern "C" { extern "C" {
@ -6090,6 +6281,24 @@ extern "C" {
#endif // USE_HOMEKIT #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% // replace vars in cmd %var%
void Replace_Cmd_Vars(char *srcbuf, uint32_t srcsize, char *dstbuf, uint32_t dstsize) { void Replace_Cmd_Vars(char *srcbuf, uint32_t srcsize, char *dstbuf, uint32_t dstsize) {
char *cp; 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) { if (vtype == NUM_RES || (vtype & STYPE) == 0) {
// numeric result // numeric result
if (ind.bits.integer) { if (ind.bits.integer) {
dtostrfd(*(int32_t*)&fvar, 0, string); double dval = *(int32_t*)&fvar;
f2char(dval, dprec, lzero, string, dsep);
} else { } else {
f2char(fvar, dprec, lzero, string, dsep); f2char(fvar, dprec, lzero, string, dsep);
} }
@ -7147,6 +7357,7 @@ getnext:
} else { } else {
dfvar = &glob_script_mem.fvars[index]; dfvar = &glob_script_mem.fvars[index];
sysv_type = 0; sysv_type = 0;
glob_script_mem.res_ivar = ind.bits.integer;
} }
numeric = 1; numeric = 1;
SCRIPT_SKIP_SPACES SCRIPT_SKIP_SPACES
@ -7483,6 +7694,201 @@ getnext:
return -1; 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 #ifdef USE_SCRIPT_SPI
// transfer 1-3 bytes // transfer 1-3 bytes
@ -7586,6 +7992,25 @@ bool Script_Close_Serial() {
} }
#endif //USE_SCRIPT_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) { bool Is_gpio_used(uint8_t gpiopin) {
if (gpiopin >= 0 && (gpiopin < nitems(TasmotaGlobal.gpio_pin)) && (TasmotaGlobal.gpio_pin[gpiopin] > 0)) { if (gpiopin >= 0 && (gpiopin < nitems(TasmotaGlobal.gpio_pin)) && (TasmotaGlobal.gpio_pin[gpiopin] > 0)) {
return true; return true;
@ -9673,6 +10098,11 @@ void Script_Check_HTML_Setvars(void) {
*cp1 = '='; *cp1 = '=';
cp1++; cp1++;
if (is_int_var(vname)) {
memmove(cp1 + 1, cp1, strlen(cp1));
*cp1++ = '#';
}
struct T_INDEX ind; struct T_INDEX ind;
uint8_t vtype; uint8_t vtype;
isvar(vname, &vtype, &ind, 0, 0, 0); isvar(vname, &vtype, &ind, 0, 0, 0);
@ -10143,7 +10573,7 @@ const char *gc_str;
lp = GetStringArgument(lp, OPER_EQU, right, 0); lp = GetStringArgument(lp, OPER_EQU, right, 0);
SCRIPT_SKIP_SPACES 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++; lp++;
} else if (!strncmp(lin, "ck(", 3)) { } else if (!strncmp(lin, "ck(", 3)) {
@ -10400,6 +10830,8 @@ const char *gc_str;
char vname[16]; char vname[16];
ScriptGetVarname(vname, slp, sizeof(vname)); ScriptGetVarname(vname, slp, sizeof(vname));
bool isint = is_int_var(vname);
char label[SCRIPT_MAXSSIZE]; char label[SCRIPT_MAXSSIZE];
lp = GetStringArgument(lp, OPER_EQU, label, 0); lp = GetStringArgument(lp, OPER_EQU, label, 0);
SCRIPT_SKIP_SPACES SCRIPT_SKIP_SPACES
@ -10417,10 +10849,17 @@ const char *gc_str;
} }
char vstr[16],minstr[16],maxstr[16],stepstr[16]; char vstr[16],minstr[16],maxstr[16],stepstr[16];
dtostrfd(val, dprec, vstr); if (isint) {
dtostrfd(min, dprec, minstr); dtostrfd(*(int32_t*)&val, 0, vstr);
dtostrfd(max, dprec, maxstr); dtostrfd(*(int32_t*)&min, dprec, minstr);
dtostrfd(step, dprec, stepstr); 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); WCS_DIV(specopt);
WSContentSend_P(SCRIPT_MSG_NUMINP, center, label, minstr, maxstr, stepstr, vstr, tsiz, vname); WSContentSend_P(SCRIPT_MSG_NUMINP, center, label, minstr, maxstr, stepstr, vstr, tsiz, vname);
WCS_DIV(specopt | WSO_STOP_DIV); WCS_DIV(specopt | WSO_STOP_DIV);
@ -10867,8 +11306,11 @@ exgc:
WSContentSend_P(PSTR("%s"), lin); WSContentSend_P(PSTR("%s"), lin);
} }
#else #else
if (!(specopt&WSO_FORCEMAIN)) {
lin++; if (mc != 'z') {
if (!(specopt&WSO_FORCEMAIN)) {
lin++;
}
} }
WSContentSend_P(PSTR("%s"), lin); WSContentSend_P(PSTR("%s"), lin);
} else { } else {
@ -11305,10 +11747,27 @@ int32_t call2pwl(const char *url) {
result.replace("instant", "i"); result.replace("instant", "i");
result.replace("apparent", "a"); result.replace("apparent", "a");
result.replace("reactive", "r"); 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) { if (result.length()>4095) {
AddLog(LOG_LEVEL_INFO, PSTR("PWL: result overflow: %d"), result.length()); 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 // meter aggregates has also too many tokens
char *cp = (char*)result.c_str(); char *cp = (char*)result.c_str();
if (!strncmp_P(cp, PSTR("{\"site\""), 7)) { if (!strncmp_P(cp, PSTR("{\"site\""), 7)) {
@ -11415,36 +11874,38 @@ void cpy2lf(char *dst, uint32_t dstlen, char *src) {
} }
#ifdef USE_SCRIPT_I2C #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 script_i2c(uint8_t sel, uint16_t val, uint32_t val1) {
uint32_t rval = 0; uint32_t rval = 0;
uint8_t bytes = 1; uint8_t bytes = 1;
if (sel > 0) {
if (!glob_script_mem.script_i2c_wire) return 0;
}
switch (sel) { switch (sel) {
case 0: case 0:
script_i2c_addr = val; glob_script_mem.script_i2c_addr = val;
#ifdef ESP32 #ifdef ESP32
if (val1 == 0) script_i2c_wire = &Wire; if (val1 == 0) glob_script_mem.script_i2c_wire = &Wire;
else script_i2c_wire = &Wire1; else glob_script_mem.script_i2c_wire = &Wire1;
#else #else
script_i2c_wire = &Wire; glob_script_mem.script_i2c_wire = &Wire;
#endif #endif
script_i2c_wire->beginTransmission(script_i2c_addr); glob_script_mem.script_i2c_wire->beginTransmission(glob_script_mem.script_i2c_addr);
return (0 == script_i2c_wire->endTransmission()); return (0 == glob_script_mem.script_i2c_wire->endTransmission());
break; break;
case 2: case 2:
// read 1..4 bytes // read 1..4 bytes
if ((val & 0x8000) == 0) { if ((val & 0x8000) == 0) {
script_i2c_wire->beginTransmission(script_i2c_addr); glob_script_mem.script_i2c_wire->beginTransmission(glob_script_mem.script_i2c_addr);
script_i2c_wire->write(val); glob_script_mem.script_i2c_wire->write(val);
script_i2c_wire->endTransmission(); 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++) { for (uint8_t cnt = 0; cnt < val1; cnt++) {
rval <<= 8; rval <<= 8;
rval |= (uint8_t)script_i2c_wire->read(); rval |= (uint8_t)glob_script_mem.script_i2c_wire->read();
} }
break; break;
@ -11454,23 +11915,23 @@ uint32_t script_i2c(uint8_t sel, uint16_t val, uint32_t val1) {
case 13: case 13:
// write 1 .. 4 bytes // write 1 .. 4 bytes
bytes = sel - 9; 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) { if ((val & 0x8000) == 0) {
script_i2c_wire->write(val); glob_script_mem.script_i2c_wire->write(val);
} }
if ((val & 0x4000) == 0) { if ((val & 0x4000) == 0) {
for (uint8_t cnt = 0; cnt < bytes; cnt++) { for (uint8_t cnt = 0; cnt < bytes; cnt++) {
script_i2c_wire->write(val1); glob_script_mem.script_i2c_wire->write(val1);
val1 >>= 8; val1 >>= 8;
} }
} else { } else {
uint32_t wval = 0; uint32_t wval = 0;
for (uint8_t cnt = 0; cnt < bytes; cnt++) { for (uint8_t cnt = 0; cnt < bytes; cnt++) {
wval = val1 >> ((bytes - 1 - cnt) * 8); 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; break;
} }
@ -11918,6 +12379,9 @@ bool Xdrv10(uint32_t function)
case FUNC_INIT: case FUNC_INIT:
//bitWrite(Settings->rule_enabled, 0, 0); // >>>>>>>>>>> //bitWrite(Settings->rule_enabled, 0, 0); // >>>>>>>>>>>
#ifndef NO_SCRIPT_STOP_ON_ERROR
bitWrite(Settings->rule_stop, 0, 1);
#endif
// set defaults to rules memory // set defaults to rules memory
//bitWrite(Settings->rule_enabled,0,0); //bitWrite(Settings->rule_enabled,0,0);
@ -12224,6 +12688,10 @@ bool Xdrv10(uint32_t function)
} }
} }
break; break;
case FUNC_BUTTON_MULTI_PRESSED:
if (bitRead(Settings->rule_enabled, 0)) {
Run_Scripter1(">b", 2, 0);
}
#endif //USE_BUTTON_EVENT #endif //USE_BUTTON_EVENT
case FUNC_LOOP: case FUNC_LOOP: