From aae0d96072f7c8a37f8b10c3152869125f063e85 Mon Sep 17 00:00:00 2001 From: Norbert Richter Date: Wed, 17 Apr 2019 18:40:47 +0200 Subject: [PATCH 1/9] decode-config.py: adapt settings & fixes - add LedMask (led_mask) - add WebColor (web_color) - adapt Interlock (interlock) flag (6.4.1.11) - fix Sensor20 range (novasds_period) - fix PulseTime values - fix readonly bitstruct - fix wrong EnergyReset cmnd/values - cleanup rename vars having conflict with keywords (PEP 8) --- tools/decode-config.py | 395 +++++++++++++++++++++++++++-------------- 1 file changed, 258 insertions(+), 137 deletions(-) diff --git a/tools/decode-config.py b/tools/decode-config.py index 161cc850d..5bc7b1b54 100755 --- a/tools/decode-config.py +++ b/tools/decode-config.py @@ -1,6 +1,6 @@ #!/usr/bin/env python # -*- coding: utf-8 -*- -VER = '2.1.0023' +VER = '2.1.0024' """ decode-config.py - Backup/Restore Sonoff-Tasmota configuration data @@ -433,7 +433,7 @@ Setting_5_10_0 = { 'pwm_control': ('0 and bitsRead($,0,11)>(12*60) else "",time=time.strftime("%H:%M",time.gmtime((bitsRead($,0,11) if bitsRead($,29,2)==0 else bitsRead($,0,11) if bitsRead($,0,11)<=(12*60) else bitsRead($,0,11)-(12*60))*60)),window=bitsRead($,11,4),repeat=bitsRead($,15),days="{:07b}".format(bitsRead($,16,7))[::-1],device=bitsRead($,23,4)+1,power=bitsRead($,27,2) )')), ('"0x{:08x}".format($)', False) ), + '_': ('0 and bitsRead($,0,11)>(12*60) else "",time=time.strftime("%H:%M",time.gmtime((bitsRead($,0,11) if bitsRead($,29,2)==0 else bitsRead($,0,11) if bitsRead($,0,11)<=(12*60) else bitsRead($,0,11)-(12*60))*60)),window=bitsRead($,11,4),repeat=bitsRead($,15),days="{:07b}".format(bitsRead($,16,7))[::-1],device=bitsRead($,23,4)+1,power=bitsRead($,27,2) )')), ('"0x{:08x}".format($)', False) ), 'time': (' 0: groups.add(group) - if isinstance(format, dict): - subgroups = GetGroupList(format) + if isinstance(format_, dict): + subgroups = GetGroupList(format_) if subgroups is not None and len(subgroups) > 0: for group in subgroups: groups.add(group) @@ -1478,8 +1493,11 @@ def PushTasmotaConfig(encode_cfg, host, port, username=DEFAULTS['source']['usern # post data c = pycurl.Curl() header = HTTPHeader() + from StringIO import StringIO + buffer_ = io.BytesIO() c.setopt(c.HEADERFUNCTION, header.store) c.setopt(c.WRITEFUNCTION, lambda x: None) + c.setopt(c.WRITEDATA, buffer_) c.setopt(c.POST, 1) c.setopt(c.URL, MakeUrl(host, port, 'u2')) if username is not None and password is not None: @@ -1506,6 +1524,7 @@ def PushTasmotaConfig(encode_cfg, host, port, username=DEFAULTS['source']['usern responsecode = c.getinfo(c.RESPONSE_CODE) except Exception, e: return e[0], e[1] + c.close() if responsecode >= 400: @@ -1513,6 +1532,21 @@ def PushTasmotaConfig(encode_cfg, host, port, username=DEFAULTS['source']['usern elif header.contenttype() != 'text/html': return ExitCode.UPLOAD_CONFIG_ERROR, "Device did not response properly, may be Tasmota webserver admin mode is disabled (WebServer 2)" + body = buffer_.getvalue() + + findUpload = body.find("Upload") + if findUpload < 0: + return ExitCode.UPLOAD_CONFIG_ERROR, "Device did not response properly with upload result page" + + body = body[findUpload:] + findSuccessful = body.find("Successful") + if findSuccessful < 0: + errmatch = re.search("(\S*)

(.*)
", body) + reason = "Unknown error" + if errmatch and len(errmatch.groups()) > 1: + reason = errmatch.group(2) + return ExitCode.UPLOAD_CONFIG_ERROR, reason + return 0, 'OK' @@ -1556,7 +1590,7 @@ def GetSettingsCrc(dobj): return crc & 0xffff -def GetFieldDef(fielddef, fields="format, addrdef, baseaddr, bits, bitshift, datadef, arraydef, validate, cmd, group, tasmotacmnd, converter, readconverter, writeconverter"): +def GetFieldDef(fielddef, fields="format_, addrdef, baseaddr, bits, bitshift, datadef, arraydef, validate, cmd, group, tasmotacmnd, converter, readconverter, writeconverter"): """ Get field definition items @@ -1570,7 +1604,7 @@ def GetFieldDef(fielddef, fields="format, addrdef, baseaddr, bits, bitshift, dat @return: set of values defined in """ - format = addrdef = baseaddr = datadef = arraydef = validate = cmd = group = tasmotacmnd = converter = readconverter = writeconverter = None + format_ = addrdef = baseaddr = datadef = arraydef = validate = cmd = group = tasmotacmnd = converter = readconverter = writeconverter = None bits = bitshift = 0 # calling with nothing is wrong @@ -1581,20 +1615,20 @@ def GetFieldDef(fielddef, fields="format, addrdef, baseaddr, bits, bitshift, dat # get top level items if len(fielddef) == 3: # converter not present - format, addrdef, datadef = fielddef + format_, addrdef, datadef = fielddef elif len(fielddef) == 4: # converter present - format, addrdef, datadef, converter = fielddef + format_, addrdef, datadef, converter = fielddef else: print >> sys.stderr, 'wrong {} length ({}) in setting'.format(fielddef, len(fielddef)) raise SyntaxError(' error') # ignore calls with 'root' setting - if isinstance(format, dict) and baseaddr is None and datadef is None: + if isinstance(format_, dict) and baseaddr is None and datadef is None: return eval(fields) - if not isinstance(format, (unicode,str,dict)): - print >> sys.stderr, 'wrong {} type {} in {}'.format(format, type(format), fielddef) + if not isinstance(format_, (unicode,str,dict)): + print >> sys.stderr, 'wrong {} type {} in {}'.format(format_, type(format_), fielddef) raise SyntaxError(' error') # extract addrdef items @@ -1604,16 +1638,16 @@ def GetFieldDef(fielddef, fields="format, addrdef, baseaddr, bits, bitshift, dat # baseaddr bit definition baseaddr, bits, bitshift = baseaddr if not isinstance(bits, int): - print >> sys.stderr, ' must be a integer in {}'.format(bits, fielddef) + print >> sys.stderr, ' must be defined as integer in {}'.format(bits, fielddef) raise SyntaxError(' error') if not isinstance(bitshift, int): - print >> sys.stderr, ' must be a integer in {}'.format(bitshift, fielddef) + print >> sys.stderr, ' must be defined as integer in {}'.format(bitshift, fielddef) raise SyntaxError(' error') else: print >> sys.stderr, 'wrong {} length ({}) in {}'.format(addrdef, len(addrdef), fielddef) raise SyntaxError(' error') if not isinstance(baseaddr, int): - print >> sys.stderr, ' must be a integer in {}'.format(baseaddr, fielddef) + print >> sys.stderr, ' must be defined as integer in {}'.format(baseaddr, fielddef) raise SyntaxError(' error') # extract datadef items @@ -1742,11 +1776,7 @@ def CmndConverter(valuemapping, value, idx, fielddef): evalstr = tasmotacmnd.replace('$','value').replace('#','idx').replace('@','valuemapping') else: evalstr = tasmotacmnd.replace('$','value').replace('@','valuemapping') - # ~ try: result = eval(evalstr) - # ~ except: - # ~ print evalstr - # ~ print value elif callable(tasmotacmnd): # use as format function if idx is not None: @@ -1790,6 +1820,46 @@ def ValidateValue(value, fielddef): return valid +def GetFormatCount(format_): + """ + Get format prefix count + + @param format_: + format specifier + + @return: + prefix count or 1 if not specified + """ + + if isinstance(format_, str): + match = re.search("\s*(\d+)", format_) + if match: + return int(match.group(0)) + + return 1 + + +def GetFormatType(format_): + """ + Get format type and bitsize without prefix + + @param format_: + format specifier + + @return: + (format_, 0) or (format without prefix, bitsize) + """ + + formattype = format_ + bitsize = 0 + if isinstance(format_, str): + match = re.search("\s*(\D+)", format_) + if match: + formattype = match.group(0) + bitsize = struct.calcsize(formattype) * 8 + return formattype, bitsize + + def GetFieldMinMax(fielddef): """ Get minimum, maximum of field based on field format definition @@ -1800,33 +1870,33 @@ def GetFieldMinMax(fielddef): @return: min, max """ - minmax = {'c': (0, 1), - '?': (0, 1), - 'b': (~0x7f, 0x7f), - 'B': (0, 0xff), - 'h': (~0x7fff, 0x7fff), - 'H': (0, 0xffff), - 'i': (~0x7fffffff, 0x7fffffff), - 'I': (0, 0xffffffff), - 'l': (~0x7fffffff, 0x7fffffff), - 'L': (0, 0xffffffff), + minmax = {'c': (0, 0xff), + '?': (0, 1), + 'b': (~0x7f, 0x7f), + 'B': (0, 0xff), + 'h': (~0x7fff, 0x7fff), + 'H': (0, 0xffff), + 'i': (~0x7fffffff, 0x7fffffff), + 'I': (0, 0xffffffff), + 'l': (~0x7fffffff, 0x7fffffff), + 'L': (0, 0xffffffff), 'q': (~0x7fffffffffffffff, 0x7fffffffffffffff), 'Q': (0, 0x7fffffffffffffff), - 'f': (sys.float_info.min, sys.float_info.max), - 'd': (sys.float_info.min, sys.float_info.max), + 'f': (sys.float_info.min, sys.float_info.max), + 'd': (sys.float_info.min, sys.float_info.max), } - format = GetFieldDef(fielddef, fields='format') - _min = 0 - _max = 0 - - if format[-1:] in minmax: - _min, _max = minmax[format[-1:]] - elif format[-1:] in ['s','p']: + format_ = GetFieldDef(fielddef, fields='format_') + min_ = 0 + max_ = 0 + + if format_[-1:] in minmax: + min_, max_ = minmax[format_[-1:]] + max_ *= GetFormatCount(format_) + elif format_[-1:] in ['s','p']: # s and p may have a prefix as length - match = re.search("\s*(\d+)", format) - if match: - _max=int(match.group(0)) - return _min,_max + max_ = GetFormatCount(format_) + + return min_,max_ def GetFieldLength(fielddef): @@ -1841,7 +1911,7 @@ def GetFieldLength(fielddef): """ length=0 - format, addrdef, arraydef = GetFieldDef(fielddef, fields='format, addrdef, arraydef') + format_, addrdef, arraydef = GetFieldDef(fielddef, fields='format_, addrdef, arraydef') # contains a integer list if isinstance(arraydef, list) and len(arraydef) > 0: @@ -1850,15 +1920,15 @@ def GetFieldLength(fielddef): for i in range(0, arraydef[0]): subfielddef = GetSubfieldDef(fielddef) if len(arraydef) > 1: - length += GetFieldLength( (format, addrdef, subfielddef) ) + length += GetFieldLength( (format_, addrdef, subfielddef) ) # single array else: - length += GetFieldLength( (format, addrdef, None) ) + length += GetFieldLength( (format_, addrdef, None) ) - elif isinstance(format, dict): + elif isinstance(format_, dict): # -> iterate through format addr = None - setting = format + setting = format_ for name in setting: baseaddr, bits, bitshift = GetFieldDef(setting[name], fields='baseaddr, bits, bitshift') _len = GetFieldLength(setting[name]) @@ -1867,20 +1937,8 @@ def GetFieldLength(fielddef): length += _len # a simple value - elif isinstance(format, str): - if format[-1:] in ['b','B','c','?']: - length=1 - elif format[-1:] in ['h','H']: - length=2 - elif format[-1:] in ['i','I','l','L','f']: - length=4 - elif format[-1:] in ['q','Q','d']: - length=8 - elif format[-1:] in ['s','p']: - # s and p may have a prefix as length - match = re.search("\s*(\d+)", format) - if match: - length=int(match.group(0)) + elif isinstance(format_, str): + length = struct.calcsize(format_) return length @@ -1896,7 +1954,7 @@ def GetSubfieldDef(fielddef): subfield definition """ - format, addrdef, datadef, arraydef, validate, cmd, converter = GetFieldDef(fielddef, fields='format, addrdef, datadef, arraydef, validate, cmd, converter') + format_, addrdef, datadef, arraydef, validate, cmd, converter = GetFieldDef(fielddef, fields='format_, addrdef, datadef, arraydef, validate, cmd, converter') # create new arraydef if len(arraydef) > 1: @@ -1916,9 +1974,9 @@ def GetSubfieldDef(fielddef): # set new field def subfielddef = None if converter is not None: - subfielddef = (format, addrdef, datadef, converter) + subfielddef = (format_, addrdef, datadef, converter) else: - subfielddef = (format, addrdef, datadef) + subfielddef = (format_, addrdef, datadef) return subfielddef @@ -1941,6 +1999,79 @@ def IsFilterGroup(group): return True +def GetFieldValue(fielddef, dobj, addr): + """ + Get single field value from definition + + @param fielddef: + see Settings desc + @param dobj: + decrypted binary config data + @param addr + addr within dobj + + @return: + value read from dobj + """ + + format_, bits, bitshift = GetFieldDef(fielddef, fields='format_, bits, bitshift') + + value_ = 0 + unpackedvalue = struct.unpack_from(format_, dobj, addr) + singletype, bitsize = GetFormatType(format_) + + if not format_[-1:].lower() in ['s','p']: + for val in unpackedvalue: + value_ <<= bitsize + value_ = value_ + val + value_ = bitsRead(value_, bitshift, bits) + else: + value_ = unpackedvalue[0] + s = str(value_).split('\0')[0] # use left string until \0 + value_ = unicode(s, errors='ignore') # remove character > 127 + + return value_ + + +def SetFieldValue(fielddef, dobj, addr, value): + """ + Set single field value from definition + + @param fielddef: + see Settings desc + @param dobj: + decrypted binary config data + @param addr + addr within dobj + @param value + new value + + @return: + new decrypted binary config data + """ + + format_, bits, bitshift = GetFieldDef(fielddef, fields='format_, bits, bitshift') + formatcnt = GetFormatCount(format_) + singletype, bitsize = GetFormatType(format_) + if args.debug >= 2: + print >> sys.stderr, "SetFieldValue(): fielddef {}, addr 0x{:04x} value {} formatcnt {} singletype {} bitsize {} ".format(fielddef,addr,value,formatcnt,singletype,bitsize) + if not format_[-1:].lower() in ['s','p']: + addr += (bitsize / 8) * formatcnt + for _ in range(0, formatcnt): + addr -= (bitsize / 8) + val = value & ((2**bitsize) - 1) + if args.debug >= 3: + print >> sys.stderr, "SetFieldValue(): Single type - fielddef {}, addr 0x{:04x} value {} singletype {} bitsize {}".format(fielddef,addr,val,singletype,bitsize) + struct.pack_into(singletype, dobj, addr, val) + value >>= bitsize + else: + if args.debug >= 3: + print >> sys.stderr, "SetFieldValue(): String type - fielddef {}, addr 0x{:04x} value {} format_ {}".format(fielddef,addr,value,format_) + struct.pack_into(format_, dobj, addr, value) + + return dobj + + def GetField(dobj, fieldname, fielddef, raw=False, addroffset=0): """ Get field value from definition @@ -1966,7 +2097,7 @@ def GetField(dobj, fieldname, fielddef, raw=False, addroffset=0): valuemapping = None # get field definition - format, baseaddr, bits, bitshift, arraydef, group, tasmotacmnd = GetFieldDef(fielddef, fields='format, baseaddr, bits, bitshift, arraydef, group, tasmotacmnd') + format_, baseaddr, bits, bitshift, arraydef, group, tasmotacmnd = GetFieldDef(fielddef, fields='format_, baseaddr, bits, bitshift, arraydef, group, tasmotacmnd') # filter groups if not IsFilterGroup(group): @@ -1985,36 +2116,24 @@ def GetField(dobj, fieldname, fielddef, raw=False, addroffset=0): offset += length # contains a dict - elif isinstance(format, dict): + elif isinstance(format_, dict): mapping_value = {} # -> iterate through format - for name in format: + for name in format_: value = None - value = GetField(dobj, name, format[name], raw=raw, addroffset=addroffset) + value = GetField(dobj, name, format_[name], raw=raw, addroffset=addroffset) if value is not None: mapping_value[name] = value # copy complete returned mapping valuemapping = copy.deepcopy(mapping_value) # a simple value - elif isinstance(format, (str, bool, int, float, long)): + elif isinstance(format_, (str, bool, int, float, long)): if GetFieldLength(fielddef) != 0: - valuemapping = struct.unpack_from(format, dobj, baseaddr+addroffset)[0] - - if not format[-1:].lower() in ['s','p']: - valuemapping = bitsRead(valuemapping, bitshift, bits) - - # additional processing for strings - if format[-1:].lower() in ['s','p']: - # use left string until \0 - s = str(valuemapping).split('\0')[0] - # remove character > 127 - valuemapping = unicode(s, errors='ignore') - - valuemapping = ReadWriteConverter(valuemapping, fielddef, read=True, raw=raw) + valuemapping = ReadWriteConverter(GetFieldValue(fielddef, dobj, baseaddr+addroffset), fielddef, read=True, raw=raw) else: - exit(ExitCode.INTERNAL_ERROR, "Wrong mapping format definition: '{}'".format(format), typ=LogType.WARNING, doexit=not args.ignorewarning, line=inspect.getlineno(inspect.currentframe())) + exit(ExitCode.INTERNAL_ERROR, "Wrong mapping format definition: '{}'".format(format_), typ=LogType.WARNING, doexit=not args.ignorewarning, line=inspect.getlineno(inspect.currentframe())) return valuemapping @@ -2039,7 +2158,7 @@ def SetField(dobj, fieldname, fielddef, restore, addroffset=0, filename=""): @return: new decrypted binary config data """ - format, baseaddr, bits, bitshift, arraydef, group, writeconverter = GetFieldDef(fielddef, fields='format, baseaddr, bits, bitshift, arraydef, group, writeconverter') + format_, baseaddr, bits, bitshift, arraydef, group, writeconverter = GetFieldDef(fielddef, fields='format_, baseaddr, bits, bitshift, arraydef, group, writeconverter') # cast unicode fieldname = str(fieldname) @@ -2049,8 +2168,8 @@ def SetField(dobj, fieldname, fielddef, restore, addroffset=0, filename=""): # do not write readonly values if writeconverter is False: - if args.debug: - print >> sys.stderr, "SetField(): Readonly '{}' using '{}'/{}{} @{} skipped".format(fieldname, format, arraydef, bits, hex(baseaddr+addroffset)) + if args.debug >= 2: + print >> sys.stderr, "SetField(): Readonly '{}' using '{}'/{}{} @{} skipped".format(fieldname, format_, arraydef, bits, hex(baseaddr+addroffset)) return dobj # contains a list @@ -2069,23 +2188,23 @@ def SetField(dobj, fieldname, fielddef, restore, addroffset=0, filename=""): offset += length # contains a dict - elif isinstance(format, dict): - for name in format: # -> iterate through format + elif isinstance(format_, dict): + for name in format_: # -> iterate through format if name in restore: - dobj = SetField(dobj, name, format[name], restore[name], addroffset=addroffset, filename=filename) + dobj = SetField(dobj, name, format_[name], restore[name], addroffset=addroffset, filename=filename) # a simple value - elif isinstance(format, (str, bool, int, float, long)): + elif isinstance(format_, (str, bool, int, float, long)): valid = True err = "" errformat = "" - _min, _max = GetFieldMinMax(fielddef) + min_, max_ = GetFieldMinMax(fielddef) value = _value = None skip = False # simple char value - if format[-1:] in ['c']: + if format_[-1:] in ['c']: try: value = ReadWriteConverter(restore.encode(STR_ENCODING)[0], fielddef, read=False) except Exception, e: @@ -2093,7 +2212,7 @@ def SetField(dobj, fieldname, fielddef, restore, addroffset=0, filename=""): valid = False # bool - elif format[-1:] in ['?']: + elif format_[-1:] in ['?']: try: value = ReadWriteConverter(bool(restore), fielddef, read=False) except Exception, e: @@ -2101,7 +2220,7 @@ def SetField(dobj, fieldname, fielddef, restore, addroffset=0, filename=""): valid = False # integer - elif format[-1:] in ['b','B','h','H','i','I','l','L','q','Q','P']: + elif format_[-1:] in ['b','B','h','H','i','I','l','L','q','Q','P']: value = ReadWriteConverter(restore, fielddef, read=False) if isinstance(value, (str, unicode)): value = int(value, 0) @@ -2110,16 +2229,17 @@ def SetField(dobj, fieldname, fielddef, restore, addroffset=0, filename=""): # bits if bits != 0: bitvalue = value - value = struct.unpack_from(format, dobj, baseaddr+addroffset)[0] + value = struct.unpack_from(format_, dobj, baseaddr+addroffset)[0] # validate restore value valid = ValidateValue(bitvalue, fielddef) if not valid: err = "valid bit range exceeding" + value = bitvalue else: mask = (1< mask: - _min = 0 - _max = mask + min_ = 0 + max_ = mask _value = bitvalue valid = False else: @@ -2142,19 +2262,19 @@ def SetField(dobj, fieldname, fielddef, restore, addroffset=0, filename=""): _value = value # float - elif format[-1:] in ['f','d']: + elif format_[-1:] in ['f','d']: try: value = ReadWriteConverter(float(restore), fielddef, read=False) except: valid = False # string - elif format[-1:] in ['s','p']: + elif format_[-1:] in ['s','p']: value = ReadWriteConverter(restore.encode(STR_ENCODING), fielddef, read=False) err = "string length exceeding" if value is not None: - _max -= 1 - valid = _min <= len(value) <= _max + max_ -= 1 + valid = min_ <= len(value) <= max_ else: skip = True valid = True @@ -2165,7 +2285,7 @@ def SetField(dobj, fieldname, fielddef, restore, addroffset=0, filename=""): if valid is None and not skip: # validate against object type size - valid = _min <= value <= _max + valid = min_ <= value <= max_ if not valid: err = "type range exceeding" errformat = " [{smin},{smax}]" @@ -2179,21 +2299,22 @@ def SetField(dobj, fieldname, fielddef, restore, addroffset=0, filename=""): if valid: if not skip: - if args.debug: - if bits: - sbits=" {} bits shift {}".format(bits, bitshift) - else: - sbits = "" - print >> sys.stderr, "SetField(): Set '{}' using '{}'/{}{} @{} to {}".format(fieldname, format, arraydef, sbits, hex(baseaddr+addroffset), _value) - if fieldname != 'cfg_crc': - prevvalue = struct.unpack_from(format, dobj, baseaddr+addroffset)[0] - struct.pack_into(format, dobj, baseaddr+addroffset, value) - curvalue = struct.unpack_from(format, dobj, baseaddr+addroffset)[0] + if args.debug >= 2: + sbits = " {} bits shift {}".format(bits, bitshift) if bits else "" + strvalue = "{} [{}]".format(_value, hex(value)) if isinstance(_value, int) else _value + print >> sys.stderr, "SetField(): Set '{}' using '{}'/{}{} @{} to {}".format(fieldname, format_, arraydef, sbits, hex(baseaddr+addroffset), strvalue) + if fieldname != 'cfg_crc' and fieldname != '_': + prevvalue = GetFieldValue(fielddef, dobj, baseaddr+addroffset) + dobj = SetFieldValue(fielddef, dobj, baseaddr+addroffset, value) + curvalue = GetFieldValue(fielddef, dobj, baseaddr+addroffset) if prevvalue != curvalue and args.verbose: message("Value for '{}' changed from {} to {}".format(fieldname, prevvalue, curvalue), typ=LogType.INFO) + else: + if args.debug >= 2: + print >> sys.stderr, "SetField(): Special field '{}' using '{}'/{}{} @{} skipped".format(fieldname, format_, arraydef, bits, hex(baseaddr+addroffset)) else: sformat = "file '{sfile}' - {{'{sname}': {svalue}}} ({serror})"+errformat - exit(ExitCode.RESTORE_DATA_ERROR, sformat.format(sfile=filename, sname=fieldname, serror=err, svalue=_value, smin=_min, smax=_max), typ=LogType.WARNING, doexit=not args.ignorewarning) + exit(ExitCode.RESTORE_DATA_ERROR, sformat.format(sfile=filename, sname=fieldname, serror=err, svalue=_value, smin=min_, smax=max_), typ=LogType.WARNING, doexit=not args.ignorewarning) return dobj @@ -2220,7 +2341,7 @@ def SetCmnd(cmnds, fieldname, fielddef, valuemapping, mappedvalue, addroffset=0, @return: new Tasmota command mapping """ - format, baseaddr, bits, bitshift, arraydef, group, tasmotacmnd, writeconverter = GetFieldDef(fielddef, fields='format, baseaddr, bits, bitshift, arraydef, group, tasmotacmnd, writeconverter') + format_, baseaddr, bits, bitshift, arraydef, group, tasmotacmnd, writeconverter = GetFieldDef(fielddef, fields='format_, baseaddr, bits, bitshift, arraydef, group, tasmotacmnd, writeconverter') # cast unicode fieldname = str(fieldname) @@ -2245,13 +2366,13 @@ def SetCmnd(cmnds, fieldname, fielddef, valuemapping, mappedvalue, addroffset=0, offset += length # contains a dict - elif isinstance(format, dict): - for name in format: # -> iterate through format + elif isinstance(format_, dict): + for name in format_: # -> iterate through format if name in mappedvalue: - cmnds = SetCmnd(cmnds, name, format[name], valuemapping, mappedvalue[name], addroffset=addroffset, idx=idx) + cmnds = SetCmnd(cmnds, name, format_[name], valuemapping, mappedvalue[name], addroffset=addroffset, idx=idx) # a simple value - elif isinstance(format, (str, bool, int, float, long)): + elif isinstance(format_, (str, bool, int, float, long)): cmnd = CmndConverter(valuemapping, mappedvalue, idx, fielddef) if group is not None and cmnd is not None: @@ -2804,7 +2925,7 @@ def ParseArgs(): info = parser.add_argument_group('Info','Extra information') info.add_argument('-D', '--debug', dest='debug', - action='store_true', + action='count', help=configargparse.SUPPRESS) info.add_argument('-h', '--help', dest='shorthelp', @@ -2823,7 +2944,7 @@ def ParseArgs(): args = parser.parse_args() - if args.debug: + if args.debug >= 1: print >> sys.stderr, parser.format_values() print >> sys.stderr, "Settings:" for k in args.__dict__: From 39615c070005b0251893060e2da3dfcf537f88a3 Mon Sep 17 00:00:00 2001 From: digiblur <3240875+digiblur@users.noreply.github.com> Date: Wed, 17 Apr 2019 21:41:58 -0500 Subject: [PATCH 2/9] Add Tuya Dimmer 10 second heartbeat Some newer dimmer modules require the stock firmware method of sending a heartbeat packet every 10-11 seconds to the secondary MCU. This was tested on 2 other Tuya based dimmers that did not require this heartbeat packet and no adverse impacts have been found. --- sonoff/xdrv_16_tuyadimmer.ino | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/sonoff/xdrv_16_tuyadimmer.ino b/sonoff/xdrv_16_tuyadimmer.ino index d43e6397e..ffb44adab 100644 --- a/sonoff/xdrv_16_tuyadimmer.ino +++ b/sonoff/xdrv_16_tuyadimmer.ino @@ -52,6 +52,7 @@ uint8_t tuya_cmd_status = 0; // Current status of serial-read uint8_t tuya_cmd_checksum = 0; // Checksum of tuya command uint8_t tuya_data_len = 0; // Data lenght of command int8_t tuya_wifi_state = -2; // Keep MCU wifi-status in sync with WifiState() +uint8_t tuya_heartbeat_timer = 0; // 10 second heartbeat timer for tuya module char *tuya_buffer = nullptr; // Serial receive buffer int tuya_byte_counter = 0; // Index in serial receive buffer @@ -294,6 +295,7 @@ void TuyaInit(void) TuyaSendCmd(TUYA_CMD_MCU_CONF); } } + tuya_heartbeat_timer = 0; // init heartbeat timer when dimmer init is done } void TuyaSerialInput(void) @@ -410,6 +412,11 @@ bool Xdrv16(uint8_t function) break; case FUNC_EVERY_SECOND: if(TuyaSerial && tuya_wifi_state!=WifiState()) { TuyaSetWifiLed(); } + tuya_heartbeat_timer++; + if (tuya_heartbeat_timer > 10) { + tuya_heartbeat_timer = 0; + TuyaSendCmd(TUYA_CMD_HEARTBEAT); + } break; case FUNC_SET_CHANNELS: result = TuyaSetChannels(); From a75dc0c4ab56beaf27e81ceb64e65f398647325b Mon Sep 17 00:00:00 2001 From: digiblur <3240875+digiblur@users.noreply.github.com> Date: Wed, 17 Apr 2019 23:01:50 -0500 Subject: [PATCH 3/9] Update _changelog.ino --- sonoff/_changelog.ino | 1 + 1 file changed, 1 insertion(+) diff --git a/sonoff/_changelog.ino b/sonoff/_changelog.ino index 3e1ddb71d..00d134c75 100644 --- a/sonoff/_changelog.ino +++ b/sonoff/_changelog.ino @@ -1,4 +1,5 @@ /* 6.5.0.8 20190413 + * Add Tuya Dimmer 10 second heartbeat serial packet required by some Tuya dimmer secondary MCUs * Fix use of SerialDelimiter value 128 (#5634) * Fix lost syslog connection regression from 6.5.0.4 * Add Shelly 2.5 Energy Monitoring (#5592) From a03c1c90c7749f45ede573cc61ee5db2efc06cdb Mon Sep 17 00:00:00 2001 From: Theo Arends <11044339+arendst@users.noreply.github.com> Date: Thu, 18 Apr 2019 10:22:05 +0200 Subject: [PATCH 4/9] Change disable interrupts during SerialSend from 9600 bps and up (#5528) Change disable interrupts during SerialSend from 9600 bps and up (#5528) --- lib/{TasmotaSerial-2.3.0 => TasmotaSerial-2.3.1}/README.md | 0 .../examples/swsertest/swsertest.ino | 0 .../keywords.txt | 0 .../library.json | 0 .../library.properties | 0 .../src/TasmotaSerial.cpp | 6 +++--- .../src/TasmotaSerial.h | 0 7 files changed, 3 insertions(+), 3 deletions(-) rename lib/{TasmotaSerial-2.3.0 => TasmotaSerial-2.3.1}/README.md (100%) rename lib/{TasmotaSerial-2.3.0 => TasmotaSerial-2.3.1}/examples/swsertest/swsertest.ino (100%) rename lib/{TasmotaSerial-2.3.0 => TasmotaSerial-2.3.1}/keywords.txt (100%) rename lib/{TasmotaSerial-2.3.0 => TasmotaSerial-2.3.1}/library.json (100%) rename lib/{TasmotaSerial-2.3.0 => TasmotaSerial-2.3.1}/library.properties (100%) rename lib/{TasmotaSerial-2.3.0 => TasmotaSerial-2.3.1}/src/TasmotaSerial.cpp (97%) rename lib/{TasmotaSerial-2.3.0 => TasmotaSerial-2.3.1}/src/TasmotaSerial.h (100%) diff --git a/lib/TasmotaSerial-2.3.0/README.md b/lib/TasmotaSerial-2.3.1/README.md similarity index 100% rename from lib/TasmotaSerial-2.3.0/README.md rename to lib/TasmotaSerial-2.3.1/README.md diff --git a/lib/TasmotaSerial-2.3.0/examples/swsertest/swsertest.ino b/lib/TasmotaSerial-2.3.1/examples/swsertest/swsertest.ino similarity index 100% rename from lib/TasmotaSerial-2.3.0/examples/swsertest/swsertest.ino rename to lib/TasmotaSerial-2.3.1/examples/swsertest/swsertest.ino diff --git a/lib/TasmotaSerial-2.3.0/keywords.txt b/lib/TasmotaSerial-2.3.1/keywords.txt similarity index 100% rename from lib/TasmotaSerial-2.3.0/keywords.txt rename to lib/TasmotaSerial-2.3.1/keywords.txt diff --git a/lib/TasmotaSerial-2.3.0/library.json b/lib/TasmotaSerial-2.3.1/library.json similarity index 100% rename from lib/TasmotaSerial-2.3.0/library.json rename to lib/TasmotaSerial-2.3.1/library.json diff --git a/lib/TasmotaSerial-2.3.0/library.properties b/lib/TasmotaSerial-2.3.1/library.properties similarity index 100% rename from lib/TasmotaSerial-2.3.0/library.properties rename to lib/TasmotaSerial-2.3.1/library.properties diff --git a/lib/TasmotaSerial-2.3.0/src/TasmotaSerial.cpp b/lib/TasmotaSerial-2.3.1/src/TasmotaSerial.cpp similarity index 97% rename from lib/TasmotaSerial-2.3.0/src/TasmotaSerial.cpp rename to lib/TasmotaSerial-2.3.1/src/TasmotaSerial.cpp index eecdeb124..d99137edc 100644 --- a/lib/TasmotaSerial-2.3.0/src/TasmotaSerial.cpp +++ b/lib/TasmotaSerial-2.3.1/src/TasmotaSerial.cpp @@ -100,7 +100,7 @@ TasmotaSerial::TasmotaSerial(int receive_pin, int transmit_pin, int hardware_fal m_buffer = (uint8_t*)malloc(TM_SERIAL_BUFFER_SIZE); if (m_buffer == NULL) return; // Use getCycleCount() loop to get as exact timing as possible - m_bit_time = F_CPU / TM_SERIAL_BAUDRATE; + m_bit_time = ESP.getCpuFreqMHz() * 1000000 / TM_SERIAL_BAUDRATE; pinMode(m_rx_pin, INPUT); tms_obj_list[m_rx_pin] = this; attachInterrupt(m_rx_pin, ISRList[m_rx_pin], FALLING); @@ -145,8 +145,8 @@ bool TasmotaSerial::begin(long speed, int stop_bits) { } } else { // Use getCycleCount() loop to get as exact timing as possible - m_bit_time = F_CPU / speed; - m_high_speed = (speed > 9600); + m_bit_time = ESP.getCpuFreqMHz() * 1000000 / speed; + m_high_speed = (speed >= 9600); } return m_valid; } diff --git a/lib/TasmotaSerial-2.3.0/src/TasmotaSerial.h b/lib/TasmotaSerial-2.3.1/src/TasmotaSerial.h similarity index 100% rename from lib/TasmotaSerial-2.3.0/src/TasmotaSerial.h rename to lib/TasmotaSerial-2.3.1/src/TasmotaSerial.h From bbd23dd745547703d65396668f6d026f0004a759 Mon Sep 17 00:00:00 2001 From: Theo Arends <11044339+arendst@users.noreply.github.com> Date: Thu, 18 Apr 2019 10:34:55 +0200 Subject: [PATCH 5/9] Fix missing sans-serif font (#5664) Fix missing sans-serif font (#5664) --- sonoff/xdrv_01_webserver.ino | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sonoff/xdrv_01_webserver.ino b/sonoff/xdrv_01_webserver.ino index 2ecd8c4c3..0e3048313 100644 --- a/sonoff/xdrv_01_webserver.ino +++ b/sonoff/xdrv_01_webserver.ino @@ -245,7 +245,7 @@ const char HTTP_HEAD_STYLE1[] PROGMEM = "input[type=checkbox],input[type=radio]{width:1em;margin-right:6px;vertical-align:-1px;}" "select{width:100%%;background:#%06x;color:#%06x;}" // COLOR_INPUT, COLOR_INPUT_TEXT "textarea{resize:none;width:98%%;height:318px;padding:5px;overflow:auto;background:#%06x;color:#%06x;}" // COLOR_CONSOLE, COLOR_CONSOLE_TEXT - "body{text-align:center;font-family:verdana;background:#%06x;}" // COLOR_BACKGROUND + "body{text-align:center;font-family:verdana,sans-serif;background:#%06x;}" // COLOR_BACKGROUND "td{padding:0px;}"; const char HTTP_HEAD_STYLE2[] PROGMEM = "button{border:0;border-radius:0.3rem;background:#%06x;color:#%06x;line-height:2.4rem;font-size:1.2rem;width:100%%;-webkit-transition-duration:0.4s;transition-duration:0.4s;cursor:pointer;}" // COLOR_BUTTON, COLOR_BUTTON_TEXT From 736f63e9aec975ca55e97f8cf9a26593628bff2b Mon Sep 17 00:00:00 2001 From: Theo Arends <11044339+arendst@users.noreply.github.com> Date: Thu, 18 Apr 2019 11:07:38 +0200 Subject: [PATCH 6/9] Fix Shelly 2.5 overtemp detection Fix Shelly 2.5 overtemp detection --- sonoff/support.ino | 10 ++++++++++ sonoff/xnrg_07_ade7953.ino | 2 +- sonoff/xsns_02_analog.ino | 28 +++++++++++++++++++++------- 3 files changed, 32 insertions(+), 8 deletions(-) diff --git a/sonoff/support.ino b/sonoff/support.ino index 0ea3ead03..d8732fc85 100644 --- a/sonoff/support.ino +++ b/sonoff/support.ino @@ -584,6 +584,16 @@ float ConvertTemp(float c) return result; } +float ConvertTempToCelsius(float c) +{ + float result = c; + + if (!isnan(c) && Settings.flag.temperature_conversion) { + result = (c - 32) / 1.8; // Celsius + } + return result; +} + char TempUnit(void) { return (Settings.flag.temperature_conversion) ? 'F' : 'C'; diff --git a/sonoff/xnrg_07_ade7953.ino b/sonoff/xnrg_07_ade7953.ino index e7e2abd27..2adc01ff2 100644 --- a/sonoff/xnrg_07_ade7953.ino +++ b/sonoff/xnrg_07_ade7953.ino @@ -162,7 +162,7 @@ void Ade7953EnergyEverySecond() void Ade7953EverySecond() { - if (power && (global_temperature > ADE7953_OVERTEMP)) { // Device overtemp, turn off relays + if (power && (ConvertTempToCelsius(AdcTemperature()) > ADE7953_OVERTEMP)) { // Device overtemp, turn off relays SetAllPower(POWER_ALL_OFF, SRC_OVERTEMP); } } diff --git a/sonoff/xsns_02_analog.ino b/sonoff/xsns_02_analog.ino index 822235497..d0f0dfebf 100644 --- a/sonoff/xsns_02_analog.ino +++ b/sonoff/xsns_02_analog.ino @@ -36,6 +36,7 @@ #define ANALOG_B 3350.0 // Thermistor Beta Coefficient uint16_t adc_last_value = 0; +float adc_temp = 0; uint16_t AdcRead(uint8_t factor) { @@ -69,6 +70,22 @@ void AdcEvery250ms(void) } #endif // USE_RULES +void AdcEverySecond(void) +{ + if (my_module_flag.adc0_temp) { + int adc = AdcRead(2); + // Steinhart-Hart equation for thermistor as temperature sensor + double Rt = (adc * ANALOG_R21) / (1024.0 * ANALOG_V33 - (double)adc); + double T = ANALOG_B / (ANALOG_B/ANALOG_T0 + log(Rt/ANALOG_R0)); + adc_temp = ConvertTemp(TO_CELSIUS(T)); + } +} + +float AdcTemperature(void) +{ + return adc_temp; +} + void AdcShow(bool json) { if (my_module_flag.adc0) { @@ -83,14 +100,8 @@ void AdcShow(bool json) } } if (my_module_flag.adc0_temp) { - int adc = AdcRead(2); - // Steinhart-Hart equation for thermistor as temperature sensor - double Rt = (adc * ANALOG_R21) / (1024.0 * ANALOG_V33 - (double)adc); - double T = ANALOG_B / (ANALOG_B/ANALOG_T0 + log(Rt/ANALOG_R0)); - double temp = ConvertTemp(TO_CELSIUS(T)); - char temperature[33]; - dtostrfd(temp, Settings.flag2.temperature_resolution, temperature); + dtostrfd(adc_temp, Settings.flag2.temperature_resolution, temperature); if (json) { ResponseAppend_P(JSON_SNS_TEMP, "ANALOG", temperature); @@ -127,6 +138,9 @@ bool Xsns02(uint8_t function) AdcEvery250ms(); break; #endif // USE_RULES + case FUNC_EVERY_SECOND: + AdcEverySecond(); + break; case FUNC_JSON_APPEND: AdcShow(1); break; From cb86fb5c2c9016bed1fff54dde7bc346e11cfcbd Mon Sep 17 00:00:00 2001 From: Theo Arends <11044339+arendst@users.noreply.github.com> Date: Thu, 18 Apr 2019 14:13:14 +0200 Subject: [PATCH 7/9] Update xnrg_07_ade7953.ino Fix compile error and CurrentSet resolution --- sonoff/xnrg_07_ade7953.ino | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/sonoff/xnrg_07_ade7953.ino b/sonoff/xnrg_07_ade7953.ino index 2adc01ff2..b70daa9b6 100644 --- a/sonoff/xnrg_07_ade7953.ino +++ b/sonoff/xnrg_07_ade7953.ino @@ -162,9 +162,11 @@ void Ade7953EnergyEverySecond() void Ade7953EverySecond() { +#ifndef USE_ADC_VCC if (power && (ConvertTempToCelsius(AdcTemperature()) > ADE7953_OVERTEMP)) { // Device overtemp, turn off relays SetAllPower(POWER_ALL_OFF, SRC_OVERTEMP); } +#endif // USE_ADC_VCC } void Ade7953DrvInit(void) @@ -190,7 +192,7 @@ bool Ade7953Command(void) { bool serviced = true; - double value = CharToDouble(XdrvMailbox.data); + uint32_t value = (uint32_t)(CharToDouble(XdrvMailbox.data) * 100); // 1.23 = 123 if (CMND_POWERCAL == energy_command_code) { if (1 == XdrvMailbox.payload) { XdrvMailbox.payload = ADE7953_PREF; } @@ -206,17 +208,23 @@ bool Ade7953Command(void) } else if (CMND_POWERSET == energy_command_code) { if (XdrvMailbox.data_len && ade7953_active_power) { - Settings.energy_power_calibration = (uint32_t)((double)ade7953_active_power / (value / 10)); // W + if ((value > 100) && (value < 200000)) { // Between 1W and 2000W + Settings.energy_power_calibration = (ade7953_active_power * 1000) / value; // 0.00 W + } } } else if (CMND_VOLTAGESET == energy_command_code) { if (XdrvMailbox.data_len && ade7953_voltage_rms) { - Settings.energy_voltage_calibration = (uint32_t)((double)ade7953_voltage_rms / value); // V + if ((value > 10000) && (value < 26000)) { // Between 100V and 260V + Settings.energy_voltage_calibration = (ade7953_voltage_rms * 100) / value; // 0.00 V + } } } else if (CMND_CURRENTSET == energy_command_code) { if (XdrvMailbox.data_len && ade7953_current_rms) { - Settings.energy_current_calibration = (uint32_t)((double)ade7953_current_rms / (value * 10)); // A + if ((value > 2000) && (value < 1000000)) { // Between 20mA and 10A + Settings.energy_current_calibration = ((ade7953_current_rms * 100) / value) * 100; // 0.00 mA + } } } else serviced = false; // Unknown command From e8e7b25b80f483c612ee082dd673a55d52bcd2cb Mon Sep 17 00:00:00 2001 From: Theo Arends <11044339+arendst@users.noreply.github.com> Date: Thu, 18 Apr 2019 16:24:46 +0200 Subject: [PATCH 8/9] 6.5.0.9 Add command SetOption63 6.5.0.9 20190418 * Add command SetOption63 0/1 to disable relay state feedback scan at restart (#5594, #5663) * Fix TasmotaSerial at 9600 bps solving DFPlayer comms (#5528) * Fix Shelly 2.5 overtemp --- sonoff/_changelog.ino | 7 ++++++- sonoff/settings.h | 2 +- sonoff/sonoff.ino | 6 ++++-- sonoff/sonoff_version.h | 2 +- 4 files changed, 12 insertions(+), 5 deletions(-) diff --git a/sonoff/_changelog.ino b/sonoff/_changelog.ino index 00d134c75..23bdb5185 100644 --- a/sonoff/_changelog.ino +++ b/sonoff/_changelog.ino @@ -1,4 +1,9 @@ -/* 6.5.0.8 20190413 +/* 6.5.0.9 20190418 + * Add command SetOption63 0/1 to disable relay state feedback scan at restart (#5594, #5663) + * Fix TasmotaSerial at 9600 bps solving DFPlayer comms (#5528) + * Fix Shelly 2.5 overtemp + * + * 6.5.0.8 20190413 * Add Tuya Dimmer 10 second heartbeat serial packet required by some Tuya dimmer secondary MCUs * Fix use of SerialDelimiter value 128 (#5634) * Fix lost syslog connection regression from 6.5.0.4 diff --git a/sonoff/settings.h b/sonoff/settings.h index 1bb48ab8b..f00e1bfa8 100644 --- a/sonoff/settings.h +++ b/sonoff/settings.h @@ -76,7 +76,7 @@ typedef union { // Restricted by MISRA-C Rule 18.4 bu uint32_t sleep_normal : 1; // bit 10 (v6.3.0.15) - SetOption60 - Enable normal sleep instead of dynamic sleep uint32_t button_switch_force_local : 1;// bit 11 (v6.3.0.16) - SetOption61 - Force local operation when button/switch topic is set uint32_t no_hold_retain : 1; // bit 12 (v6.4.1.19) - SetOption62 - Don't use retain flag on HOLD messages - uint32_t spare13 : 1; + uint32_t no_power_feedback : 1; // bit 13 (v6.5.0.9) - SetOption63 - Don't scan relay power state at restart uint32_t spare14 : 1; uint32_t spare15 : 1; uint32_t spare16 : 1; diff --git a/sonoff/sonoff.ino b/sonoff/sonoff.ino index 07f95244e..ee8251265 100755 --- a/sonoff/sonoff.ino +++ b/sonoff/sonoff.ino @@ -2693,8 +2693,10 @@ void setup(void) // Issue #526 and #909 for (uint8_t i = 0; i < devices_present; i++) { - if ((i < MAX_RELAYS) && (pin[GPIO_REL1 +i] < 99)) { - bitWrite(power, i, digitalRead(pin[GPIO_REL1 +i]) ^ bitRead(rel_inverted, i)); + if (!Settings.flag3.no_power_feedback) { // #5594 and #5663 + if ((i < MAX_RELAYS) && (pin[GPIO_REL1 +i] < 99)) { + bitWrite(power, i, digitalRead(pin[GPIO_REL1 +i]) ^ bitRead(rel_inverted, i)); + } } if ((i < MAX_PULSETIMERS) && (bitRead(power, i) || (POWER_ALL_OFF_PULSETIME_ON == Settings.poweronstate))) { SetPulseTimer(i, Settings.pulse_timer[i]); diff --git a/sonoff/sonoff_version.h b/sonoff/sonoff_version.h index c0399c306..f60681349 100644 --- a/sonoff/sonoff_version.h +++ b/sonoff/sonoff_version.h @@ -20,6 +20,6 @@ #ifndef _SONOFF_VERSION_H_ #define _SONOFF_VERSION_H_ -const uint32_t VERSION = 0x06050008; +const uint32_t VERSION = 0x06050009; #endif // _SONOFF_VERSION_H_ From b800db4bac460d95fabfd9073d2410f282960661 Mon Sep 17 00:00:00 2001 From: Theo Arends <11044339+arendst@users.noreply.github.com> Date: Thu, 18 Apr 2019 17:28:56 +0200 Subject: [PATCH 9/9] Update library Joba_Tsl2561 from 2.0.7 to 2.0.10 Update library Joba_Tsl2561 from 2.0.7 to 2.0.10 --- lib/Joba_Tsl2561-2.0.10/.gitignore | 8 +++ lib/Joba_Tsl2561-2.0.10/.hgignore | 4 ++ lib/Joba_Tsl2561-2.0.10/.travis.yml | 55 +++++++++++++++++++ .../COPYING | 0 .../COPYING.LESSER | 0 .../README | 4 +- .../examples/Autogain/Autogain.ino | 4 +- .../examples/Simple/Simple.ino | 6 +- .../examples/Testing/Testing.ino | 4 +- .../examples/Utility/Utility.ino | 6 +- .../examples}/platformio.ini | 31 +++++++++-- .../examples/platformio.sh | 0 .../lib/readme.txt | 0 .../library.json | 4 +- .../library.properties | 4 +- .../platformio.ini | 26 +++++++-- .../src/Tsl2561.cpp | 0 .../src/Tsl2561.h | 8 +++ .../src/Tsl2561Util.cpp | 31 ++++++++--- .../src/Tsl2561Util.h | 0 20 files changed, 163 insertions(+), 32 deletions(-) create mode 100644 lib/Joba_Tsl2561-2.0.10/.gitignore create mode 100644 lib/Joba_Tsl2561-2.0.10/.hgignore create mode 100644 lib/Joba_Tsl2561-2.0.10/.travis.yml rename lib/{Joba_Tsl2561-2.0.7 => Joba_Tsl2561-2.0.10}/COPYING (100%) rename lib/{Joba_Tsl2561-2.0.7 => Joba_Tsl2561-2.0.10}/COPYING.LESSER (100%) rename lib/{Joba_Tsl2561-2.0.7 => Joba_Tsl2561-2.0.10}/README (87%) rename lib/{Joba_Tsl2561-2.0.7 => Joba_Tsl2561-2.0.10}/examples/Autogain/Autogain.ino (96%) rename lib/{Joba_Tsl2561-2.0.7 => Joba_Tsl2561-2.0.10}/examples/Simple/Simple.ino (88%) rename lib/{Joba_Tsl2561-2.0.7 => Joba_Tsl2561-2.0.10}/examples/Testing/Testing.ino (97%) rename lib/{Joba_Tsl2561-2.0.7 => Joba_Tsl2561-2.0.10}/examples/Utility/Utility.ino (93%) rename lib/{Joba_Tsl2561-2.0.7 => Joba_Tsl2561-2.0.10/examples}/platformio.ini (67%) rename lib/{Joba_Tsl2561-2.0.7 => Joba_Tsl2561-2.0.10}/examples/platformio.sh (100%) rename lib/{Joba_Tsl2561-2.0.7 => Joba_Tsl2561-2.0.10}/lib/readme.txt (100%) rename lib/{Joba_Tsl2561-2.0.7 => Joba_Tsl2561-2.0.10}/library.json (62%) rename lib/{Joba_Tsl2561-2.0.7 => Joba_Tsl2561-2.0.10}/library.properties (67%) rename lib/{Joba_Tsl2561-2.0.7/examples => Joba_Tsl2561-2.0.10}/platformio.ini (75%) rename lib/{Joba_Tsl2561-2.0.7 => Joba_Tsl2561-2.0.10}/src/Tsl2561.cpp (100%) rename lib/{Joba_Tsl2561-2.0.7 => Joba_Tsl2561-2.0.10}/src/Tsl2561.h (94%) rename lib/{Joba_Tsl2561-2.0.7 => Joba_Tsl2561-2.0.10}/src/Tsl2561Util.cpp (83%) rename lib/{Joba_Tsl2561-2.0.7 => Joba_Tsl2561-2.0.10}/src/Tsl2561Util.h (100%) diff --git a/lib/Joba_Tsl2561-2.0.10/.gitignore b/lib/Joba_Tsl2561-2.0.10/.gitignore new file mode 100644 index 000000000..2f4f2baeb --- /dev/null +++ b/lib/Joba_Tsl2561-2.0.10/.gitignore @@ -0,0 +1,8 @@ +.pioenvs +.piolibdeps +.clang_complete +.gcc-flags.json +examples/*/platformio.ini +examples/*/lib +examples/*/.gitignore +examples/*/.travis.yml diff --git a/lib/Joba_Tsl2561-2.0.10/.hgignore b/lib/Joba_Tsl2561-2.0.10/.hgignore new file mode 100644 index 000000000..5dac9f52f --- /dev/null +++ b/lib/Joba_Tsl2561-2.0.10/.hgignore @@ -0,0 +1,4 @@ +.pioenvs +.piolibdeps +.clang_complete +.gcc-flags.json diff --git a/lib/Joba_Tsl2561-2.0.10/.travis.yml b/lib/Joba_Tsl2561-2.0.10/.travis.yml new file mode 100644 index 000000000..52072efd6 --- /dev/null +++ b/lib/Joba_Tsl2561-2.0.10/.travis.yml @@ -0,0 +1,55 @@ +# Continuous Integration (CI) is the practice, in software +# engineering, of merging all developer working copies with a shared mainline +# several times a day < http://docs.platformio.org/page/ci/index.html > +# +# Documentation: +# +# * Travis CI Embedded Builds with PlatformIO +# < https://docs.travis-ci.com/user/integration/platformio/ > +# +# * PlatformIO integration with Travis CI +# < http://docs.platformio.org/page/ci/travis.html > +# +# * User Guide for `platformio ci` command +# < http://docs.platformio.org/page/userguide/cmd_ci.html > +# +# +# Please choice one of the following templates (proposed below) and uncomment +# it (remove "# " before each line) or use own configuration according to the +# Travis CI documentation (see above). +# + + +# +# Template #1: General project. Test it using existing `platformio.ini`. +# + +# language: python +# python: +# - "2.7" +# +# install: +# - pip install -U platformio +# +# script: +# - platformio run + + +# +# Template #2: The project is intended to by used as a library with examples +# + +# language: python +# python: +# - "2.7" +# +# env: +# - PLATFORMIO_CI_SRC=path/to/test/file.c +# - PLATFORMIO_CI_SRC=examples/file.ino +# - PLATFORMIO_CI_SRC=path/to/test/directory +# +# install: +# - pip install -U platformio +# +# script: +# - platformio ci --lib="." --board=ID_1 --board=ID_2 --board=ID_N diff --git a/lib/Joba_Tsl2561-2.0.7/COPYING b/lib/Joba_Tsl2561-2.0.10/COPYING similarity index 100% rename from lib/Joba_Tsl2561-2.0.7/COPYING rename to lib/Joba_Tsl2561-2.0.10/COPYING diff --git a/lib/Joba_Tsl2561-2.0.7/COPYING.LESSER b/lib/Joba_Tsl2561-2.0.10/COPYING.LESSER similarity index 100% rename from lib/Joba_Tsl2561-2.0.7/COPYING.LESSER rename to lib/Joba_Tsl2561-2.0.10/COPYING.LESSER diff --git a/lib/Joba_Tsl2561-2.0.7/README b/lib/Joba_Tsl2561-2.0.10/README similarity index 87% rename from lib/Joba_Tsl2561-2.0.7/README rename to lib/Joba_Tsl2561-2.0.10/README index 86c1f1a0b..d95e731d5 100644 --- a/lib/Joba_Tsl2561-2.0.7/README +++ b/lib/Joba_Tsl2561-2.0.10/README @@ -1,4 +1,4 @@ -This is a library for the TSL2561 digital luminosity sensors from Ams (Taos). +This is an Arduino library for the TSL2561 digital luminosity sensors from Ams (Taos). Design goals: * It is modularized so you can use only what you need if space/ram is constrained. @@ -18,3 +18,5 @@ The library has 3 classes: Tsl2561 All register access as described in the datasheet, except for interrupts Tsl2561Util Convenience functions like lux calculation or automatic gain Tsl2561Int TODO, Interrupt related stuff (not needed if int pin unconnected) + +Tested with boards Nano, ESP8266 and ESP32 diff --git a/lib/Joba_Tsl2561-2.0.7/examples/Autogain/Autogain.ino b/lib/Joba_Tsl2561-2.0.10/examples/Autogain/Autogain.ino similarity index 96% rename from lib/Joba_Tsl2561-2.0.7/examples/Autogain/Autogain.ino rename to lib/Joba_Tsl2561-2.0.10/examples/Autogain/Autogain.ino index 169d2b6e3..9547af45b 100644 --- a/lib/Joba_Tsl2561-2.0.7/examples/Autogain/Autogain.ino +++ b/lib/Joba_Tsl2561-2.0.10/examples/Autogain/Autogain.ino @@ -21,7 +21,7 @@ This file is part of the Joba_Tsl2561 Library. #include -// to mimic Serial.printf() of esp8266 core for other platforms +// to mimic Serial.printf() of esp cores for other platforms char *format( const char *fmt, ... ) { static char buf[128]; va_list arg; @@ -37,7 +37,7 @@ uint8_t id; void setup() { Serial.begin(115200); - Wire.begin(); + Wire.begin(TSL2561_SDA, TSL2561_SCL); while( !Tsl.begin() ) ; // wait until chip detected or wdt reset Serial.println("\nStarting Tsl2561Util autogain loop"); diff --git a/lib/Joba_Tsl2561-2.0.7/examples/Simple/Simple.ino b/lib/Joba_Tsl2561-2.0.10/examples/Simple/Simple.ino similarity index 88% rename from lib/Joba_Tsl2561-2.0.7/examples/Simple/Simple.ino rename to lib/Joba_Tsl2561-2.0.10/examples/Simple/Simple.ino index 5bebb7e50..b63f71b94 100644 --- a/lib/Joba_Tsl2561-2.0.7/examples/Simple/Simple.ino +++ b/lib/Joba_Tsl2561-2.0.10/examples/Simple/Simple.ino @@ -21,7 +21,7 @@ This file is part of the Joba_Tsl2561 Library. #include -// to mimic Serial.printf() of esp8266 core for other platforms +// to mimic Serial.printf() of esp cores for other platforms char *format( const char *fmt, ... ) { static char buf[128]; va_list arg; @@ -36,7 +36,7 @@ Tsl2561 Tsl(Wire); void setup() { Serial.begin(115200); - Wire.begin(); + Wire.begin(TSL2561_SDA, TSL2561_SCL); Serial.println("\nStarting Tsl2561 simple loop"); } @@ -60,7 +60,7 @@ void loop() { Tsl.off(); } else { - Serial.println("No Tsl2561 found. Check wiring."); + Serial.print(format("No Tsl2561 found. Check wiring: SCL=%u, SDA=%u\n", TSL2561_SCL, TSL2561_SDA)); } delay(5000); diff --git a/lib/Joba_Tsl2561-2.0.7/examples/Testing/Testing.ino b/lib/Joba_Tsl2561-2.0.10/examples/Testing/Testing.ino similarity index 97% rename from lib/Joba_Tsl2561-2.0.7/examples/Testing/Testing.ino rename to lib/Joba_Tsl2561-2.0.10/examples/Testing/Testing.ino index 0dbeb09be..861891666 100644 --- a/lib/Joba_Tsl2561-2.0.7/examples/Testing/Testing.ino +++ b/lib/Joba_Tsl2561-2.0.10/examples/Testing/Testing.ino @@ -22,7 +22,7 @@ This file is part of the Joba_Tsl2561 Library. #include -// to mimic Serial.printf() of esp8266 core for other platforms +// to mimic Serial.printf() of esp cores for other platforms char *format( const char *fmt, ... ) { static char buf[128]; va_list arg; @@ -158,7 +158,7 @@ void test( Tsl2561 &tsl ) { void setup() { Serial.begin(115200); - Wire.begin(); + Wire.begin(TSL2561_SDA, TSL2561_SCL); Serial.println("\nStarting Tsl2561 testing loop"); } diff --git a/lib/Joba_Tsl2561-2.0.7/examples/Utility/Utility.ino b/lib/Joba_Tsl2561-2.0.10/examples/Utility/Utility.ino similarity index 93% rename from lib/Joba_Tsl2561-2.0.7/examples/Utility/Utility.ino rename to lib/Joba_Tsl2561-2.0.10/examples/Utility/Utility.ino index d05549616..14ba320d0 100644 --- a/lib/Joba_Tsl2561-2.0.7/examples/Utility/Utility.ino +++ b/lib/Joba_Tsl2561-2.0.10/examples/Utility/Utility.ino @@ -21,7 +21,7 @@ This file is part of the Joba_Tsl2561 Library. #include -// to mimic Serial.printf() of esp8266 core for other platforms +// to mimic Serial.printf() of esp cores for other platforms char *format( const char *fmt, ... ) { static char buf[128]; va_list arg; @@ -37,7 +37,7 @@ Tsl2561 Tsl(Wire); void setup() { Serial.begin(115200); - Wire.begin(); + Wire.begin(TSL2561_SDA, TSL2561_SCL); Serial.println("\nStarting Tsl2561Util loop"); } @@ -91,7 +91,7 @@ void loop() { } if( !found ) { - Serial.println("No Tsl2561 found. Check wiring."); + Serial.print(format("No Tsl2561 found. Check wiring: SCL=%u, SDA=%u\n", TSL2561_SCL, TSL2561_SDA)); } delay(5000); diff --git a/lib/Joba_Tsl2561-2.0.7/platformio.ini b/lib/Joba_Tsl2561-2.0.10/examples/platformio.ini similarity index 67% rename from lib/Joba_Tsl2561-2.0.7/platformio.ini rename to lib/Joba_Tsl2561-2.0.10/examples/platformio.ini index ea6847aaa..07efee0d2 100644 --- a/lib/Joba_Tsl2561-2.0.7/platformio.ini +++ b/lib/Joba_Tsl2561-2.0.10/examples/platformio.ini @@ -18,9 +18,13 @@ [platformio] +src_dir = . +lib_dir = ../.. + ; uncomment one, if you want to build only one ; env_default = nodemcuv2 ; env_default = nano328 +; env_default = espressif32 [env:nodemcuv2] @@ -28,12 +32,12 @@ ; ------------ ; GND <-> GND ; VCC <-> 3V -; SCL <-> D1 -; SDA <-> D2 +; SCL <-> D1 (other pin can be defined below) +; SDA <-> D2 (other pin can be defined below) platform = espressif8266 board = nodemcuv2 framework = arduino -build_flags = -Wall +build_flags = -Wall -DTSL2561_SCL=D1 -DTSL2561_SDA=D2 monitor_speed = 115200 @@ -53,6 +57,25 @@ upload_resetmethod = nodemcu platform = atmelavr board = nanoatmega328 framework = arduino -build_flags = -Wall +build_flags = -Wall -DTSL2561_SCL=A5 -DTSL2561_SDA=A4 monitor_speed = 115200 +;upload_port = /dev/ttyUSB[1-9] + + +[env:espressif32] +; TSL <-> ESP32 +; ------------ +; GND <-> GND +; VCC <-> 3V +; SCL <-> IO22 (other pin can be defined below) +; SDA <-> IO21 (other pin can be defined below) +platform = espressif32 +board = mhetesp32minikit +framework = arduino +build_flags = -Wall -DTSL2561_SCL=22 -DTSL2561_SDA=21 + +monitor_speed = 115200 + +upload_speed = 921600 +;upload_port = /dev/ttyUSB[1-9] diff --git a/lib/Joba_Tsl2561-2.0.7/examples/platformio.sh b/lib/Joba_Tsl2561-2.0.10/examples/platformio.sh similarity index 100% rename from lib/Joba_Tsl2561-2.0.7/examples/platformio.sh rename to lib/Joba_Tsl2561-2.0.10/examples/platformio.sh diff --git a/lib/Joba_Tsl2561-2.0.7/lib/readme.txt b/lib/Joba_Tsl2561-2.0.10/lib/readme.txt similarity index 100% rename from lib/Joba_Tsl2561-2.0.7/lib/readme.txt rename to lib/Joba_Tsl2561-2.0.10/lib/readme.txt diff --git a/lib/Joba_Tsl2561-2.0.7/library.json b/lib/Joba_Tsl2561-2.0.10/library.json similarity index 62% rename from lib/Joba_Tsl2561-2.0.7/library.json rename to lib/Joba_Tsl2561-2.0.10/library.json index 94585c488..86545a6a0 100644 --- a/lib/Joba_Tsl2561-2.0.7/library.json +++ b/lib/Joba_Tsl2561-2.0.10/library.json @@ -1,8 +1,8 @@ { "name": "Joba_Tsl2561", - "version": "2.0.7", + "version": "2.0.10", "keywords": "twowire, i2c, bus, sensor, luminosity, illuminance, lux", - "description": "Arduino Library for ams (taos) luminance chip Tsl2561 with autogain", + "description": "Arduino library for ams (taos) luminance chip Tsl2561 with autogain. Tested with Nano, Esp8266 and Esp32.", "repository": { "type": "git", diff --git a/lib/Joba_Tsl2561-2.0.7/library.properties b/lib/Joba_Tsl2561-2.0.10/library.properties similarity index 67% rename from lib/Joba_Tsl2561-2.0.7/library.properties rename to lib/Joba_Tsl2561-2.0.10/library.properties index ba1840764..5d83edb31 100644 --- a/lib/Joba_Tsl2561-2.0.7/library.properties +++ b/lib/Joba_Tsl2561-2.0.10/library.properties @@ -1,9 +1,9 @@ name=Joba Tsl2561 Library -version=2.0.7 +version=2.0.10 author=joba-1 maintainer=joba-1 sentence=IoT library for using the Tsl2561 luminosity sensor -paragraph=Luminosity measurement in lux with autogain +paragraph=Luminosity measurement in lux with autogain. Tested with Nano, Esp8266 and Esp32. category=Sensors url=https://github.com/joba-1/Joba_Tsl2561 architectures=* diff --git a/lib/Joba_Tsl2561-2.0.7/examples/platformio.ini b/lib/Joba_Tsl2561-2.0.10/platformio.ini similarity index 75% rename from lib/Joba_Tsl2561-2.0.7/examples/platformio.ini rename to lib/Joba_Tsl2561-2.0.10/platformio.ini index ef71e03ae..87d88e183 100644 --- a/lib/Joba_Tsl2561-2.0.7/examples/platformio.ini +++ b/lib/Joba_Tsl2561-2.0.10/platformio.ini @@ -18,20 +18,19 @@ [platformio] -src_dir = . -lib_dir = ../.. - ; uncomment one, if you want to build only one ; env_default = nodemcuv2 ; env_default = nano328 +; env_default = espressif32 + [env:nodemcuv2] ; TSL <-> ESP8266 ; ------------ ; GND <-> GND ; VCC <-> 3V -; SCL <-> D1 -; SDA <-> D2 +; SCL <-> D1 (other pin can be defined below) +; SDA <-> D2 (other pin can be defined below) platform = espressif8266 board = nodemcuv2 framework = arduino @@ -58,4 +57,21 @@ framework = arduino build_flags = -Wall monitor_speed = 115200 + + +[env:espressif32] +; TSL <-> ESP32 +; ------------ +; GND <-> GND +; VCC <-> 3V +; SCL <-> IO22 (other pin can be defined below) +; SDA <-> IO21 (other pin can be defined below) +platform = espressif32 +board = mhetesp32minikit +framework = arduino +build_flags = -Wall -DTSL2561_SCL=22 -DTSL2561_SDA=21 + +monitor_speed = 115200 + +upload_speed = 921600 ;upload_port = /dev/ttyUSB[1-9] diff --git a/lib/Joba_Tsl2561-2.0.7/src/Tsl2561.cpp b/lib/Joba_Tsl2561-2.0.10/src/Tsl2561.cpp similarity index 100% rename from lib/Joba_Tsl2561-2.0.7/src/Tsl2561.cpp rename to lib/Joba_Tsl2561-2.0.10/src/Tsl2561.cpp diff --git a/lib/Joba_Tsl2561-2.0.7/src/Tsl2561.h b/lib/Joba_Tsl2561-2.0.10/src/Tsl2561.h similarity index 94% rename from lib/Joba_Tsl2561-2.0.7/src/Tsl2561.h rename to lib/Joba_Tsl2561-2.0.10/src/Tsl2561.h index 75056f912..147db2a77 100644 --- a/lib/Joba_Tsl2561-2.0.7/src/Tsl2561.h +++ b/lib/Joba_Tsl2561-2.0.10/src/Tsl2561.h @@ -23,6 +23,14 @@ This file is part of the Joba_Tsl2561 Library. #include #include +// To be able to override pin definitions in build flags (used in examples) +#ifndef TSL2561_SDA + #define TSL2561_SDA SDA +#endif +#ifndef TSL2561_SCL + #define TSL2561_SCL SCL +#endif + class Tsl2561 { public: diff --git a/lib/Joba_Tsl2561-2.0.7/src/Tsl2561Util.cpp b/lib/Joba_Tsl2561-2.0.10/src/Tsl2561Util.cpp similarity index 83% rename from lib/Joba_Tsl2561-2.0.7/src/Tsl2561Util.cpp rename to lib/Joba_Tsl2561-2.0.10/src/Tsl2561Util.cpp index ae811f743..aa738bb9f 100644 --- a/lib/Joba_Tsl2561-2.0.7/src/Tsl2561Util.cpp +++ b/lib/Joba_Tsl2561-2.0.10/src/Tsl2561Util.cpp @@ -22,9 +22,10 @@ This file is part of the Joba_Tsl2561 Library. namespace Tsl2561Util { // Tsl2561Util::normalizedLuminosity returncode false can mean: -// - saturation: full and/or ir have value ~0 (aka -1) +// - saturation: full and/or ir have value ~0 (aka -1) and not shortest exposure // - manual exposure time: full and ir are corrected only for gain -// If true, full and ir have values as if exposure was 402 and gain 16. +// If true, full and ir have values as if exposure was 402 and gain 16 +// or ~0 if saturated even at shortest exposure bool normalizedLuminosity( bool gain, Tsl2561::exposure_t exposure, uint32_t &full, uint32_t &ir ) { uint16_t scaledFull = (uint16_t)full; @@ -39,8 +40,8 @@ bool normalizedLuminosity( bool gain, Tsl2561::exposure_t exposure, uint32_t &fu switch( exposure ) { case Tsl2561::EXP_14: - full = (scaledFull >= 5047/4*3) ? 0xffffffff : ((full + 5) * 322) / 11; - ir = (scaledIr >= 5047/4*3) ? 0xffffffff : ((ir + 5) * 322) / 11; + full = (scaledFull == 5047) ? 0xffffffff : ((full + 5) * 322) / 11; + ir = (scaledIr == 5047) ? 0xffffffff : ((ir + 5) * 322) / 11; break; case Tsl2561::EXP_101: full = (scaledFull >= 37177/4*3) ? 0xffffffff : ((full + 40) * 322) / 81; @@ -54,7 +55,7 @@ bool normalizedLuminosity( bool gain, Tsl2561::exposure_t exposure, uint32_t &fu return false; } - return full != 0xffffffff && ir != 0xffffffff; + return exposure == Tsl2561::EXP_14 || (full != 0xffffffff && ir != 0xffffffff); } return false; @@ -93,6 +94,8 @@ bool autoGain( Tsl2561 &tsl, bool &gain, Tsl2561::exposure_t &exposure, uint16_t { true, Tsl2561::EXP_402 } // max }; + // Serial.printf("autoGain start: gain=%u, expo=%u\n", gain, exposure); + // get current sensitivity if( !tsl.getSensitivity(gain, exposure) ) { return false; // I2C error @@ -110,9 +113,10 @@ bool autoGain( Tsl2561 &tsl, bool &gain, Tsl2561::exposure_t &exposure, uint16_t return false; // should not happen... } - // in a loop wait for next sample, get values and adjust sensitivity if needed + // sometimes sensor reports high brightness although it is darker. uint8_t retryOnSaturated = 10; + // in a loop wait for next sample, get values and adjust sensitivity if needed while( true ) { waitNext(exposure); @@ -122,11 +126,14 @@ bool autoGain( Tsl2561 &tsl, bool &gain, Tsl2561::exposure_t &exposure, uint16_t uint16_t limit = getLimit(exposure); if( full >= 1000 && full <= limit ) { - return true; // new value within limits + // Serial.printf("autoGain normal full=%u, limits=1000-%u, curr=%u\n", full, limit, curr); + return true; // new value within limits of good accuracy } + // adjust sensitivity, if possible if( (full < 1000 && ++curr < sizeof(sensitivity)/sizeof(sensitivity[0])) || (full > limit && curr-- > 0) ) { + // Serial.printf("autoGain adjust full=%u, limits=1000-%u, curr=%u\n", full, limit, curr); if( !tsl.setSensitivity(sensitivity[curr].gain, sensitivity[curr].exposure) ) { return false; // I2C error } @@ -134,7 +141,10 @@ bool autoGain( Tsl2561 &tsl, bool &gain, Tsl2561::exposure_t &exposure, uint16_t exposure = sensitivity[curr].exposure; } else { - if( ++curr > 0 && retryOnSaturated-- == 0 ) { + // sensitivity already is at minimum or maximum + if( ++curr > 0 || retryOnSaturated-- == 0 ) { + // Serial.printf("autoGain limit full=%u, ir=%u, limits=1000-%u, curr=%u, retry=%u\n", full, ir, limit, curr, retryOnSaturated); + // dark, or repeatedly confirmed high brightness return true; // saturated, but best we can do } } @@ -176,6 +186,11 @@ bool milliLux( uint32_t full, uint32_t ir, uint32_t &mLux, bool csType, uint8_t return true; } + if( full == 0xffffffff || ir == 0xffffffff ) { + mLux = 99999999; // indicates saturation at shortest exposure + return true; + } + uint32_t milliRatio = ir * 1000 / full; if( csType ) { diff --git a/lib/Joba_Tsl2561-2.0.7/src/Tsl2561Util.h b/lib/Joba_Tsl2561-2.0.10/src/Tsl2561Util.h similarity index 100% rename from lib/Joba_Tsl2561-2.0.7/src/Tsl2561Util.h rename to lib/Joba_Tsl2561-2.0.10/src/Tsl2561Util.h