diff --git a/api/arduino/sonoff.ino.bin b/api/arduino/sonoff.ino.bin new file mode 100644 index 000000000..4f5a63e87 Binary files /dev/null and b/api/arduino/sonoff.ino.bin differ diff --git a/api/upload-arduino.php b/api/upload-arduino.php new file mode 100644 index 000000000..2b472c643 --- /dev/null +++ b/api/upload-arduino.php @@ -0,0 +1,15 @@ + \ No newline at end of file diff --git a/arduino/espupload.py b/arduino/espupload.py new file mode 100644 index 000000000..0b2766cd0 --- /dev/null +++ b/arduino/espupload.py @@ -0,0 +1,112 @@ +#!/usr/bin/python +# +# espupload by Theo Arends - 20170103 +# +# Uploads binary file to OTA server +# +# Execute: espupload -i -p -f +# +# Needs pycurl +# - pip install pycurl + +import sys +import os +import optparse +import logging +import pycurl + +HOST_ADDR = "domus1" +HOST_PORT = 80 +HOST_URL = "/api/upload-arduino.php" + +def upload(hostAddr, hostPort, filename): + url = 'http://%s:%d%s' % (hostAddr, hostPort, HOST_URL) + c = pycurl.Curl() + c.setopt(c.URL, url) + # The "Expect:" is there to suppress "Expect: 100-continue" behaviour that is + # the default in libcurl when posting large bodies (and fails on lighttpd). + c.setopt(c.HTTPHEADER, ["Expect:"]) + c.setopt(c.HTTPPOST, [('file', (c.FORM_FILE, filename, )), ]) + c.perform() + c.close() + +def parser(): + parser = optparse.OptionParser( + usage = "%prog [options]", + description = "Upload image to over the air Host server for the esp8266 module with OTA support." + ) + + # destination ip and port + group = optparse.OptionGroup(parser, "Destination") + group.add_option("-i", "--host_ip", + dest = "host_ip", + action = "store", + help = "Host IP Address.", + default = HOST_ADDR + ) + group.add_option("-p", "--host_port", + dest = "host_port", + type = "int", + help = "Host server ota Port. Default 80", + default = HOST_PORT + ) + parser.add_option_group(group) + + # image + group = optparse.OptionGroup(parser, "Image") + group.add_option("-f", "--file", + dest = "image", + help = "Image file.", + metavar="FILE", + default = None + ) + parser.add_option_group(group) + + # output group + group = optparse.OptionGroup(parser, "Output") + group.add_option("-d", "--debug", + dest = "debug", + help = "Show debug output. And override loglevel with debug.", + action = "store_true", + default = False + ) + parser.add_option_group(group) + + (options, args) = parser.parse_args() + + return options +# end parser + +def main(args): + # get options + options = parser() + + # adapt log level + loglevel = logging.WARNING + if (options.debug): + loglevel = logging.DEBUG + # end if + + # logging + logging.basicConfig(level = loglevel, format = '%(asctime)-8s [%(levelname)s]: %(message)s', datefmt = '%H:%M:%S') + + logging.debug("Options: %s", str(options)) + + if (not options.host_ip or not options.image): + logging.critical("Not enough arguments.") + + return 1 + # end if + + if not os.path.exists(options.image): + logging.critical('Sorry: the file %s does not exist', options.image) + + return 1 + # end if + + upload(options.host_ip, options.host_port, options.image) +# end main + +if __name__ == '__main__': + sys.exit(main(sys.argv)) +# end if \ No newline at end of file diff --git a/arduino/version 2.3.0/boards.txt b/arduino/version 2.3.0/boards.txt new file mode 100644 index 000000000..3cc742521 --- /dev/null +++ b/arduino/version 2.3.0/boards.txt @@ -0,0 +1,1900 @@ +menu.UploadSpeed=Upload Speed +menu.CpuFrequency=CPU Frequency +menu.FlashSize=Flash Size +menu.FlashMode=Flash Mode +menu.FlashFreq=Flash Frequency +menu.UploadTool=Upload Using +menu.ResetMethod=Reset Method +menu.ESPModule=Module +menu.Debug=Debug port +menu.DebugLevel=Debug Level +menu.LwIPVariant=lwIP Variant + +############################################################## +generic.name=Generic ESP8266 Module + +generic.upload.tool=esptool +generic.upload.speed=115200 +generic.upload.resetmethod=ck +generic.upload.maximum_size=434160 +generic.upload.maximum_data_size=81920 +generic.upload.wait_for_upload_port=true +generic.serial.disableDTR=true +generic.serial.disableRTS=true + +generic.build.mcu=esp8266 +generic.build.f_cpu=80000000L +generic.build.board=ESP8266_ESP01 +generic.build.core=esp8266 +generic.build.variant=generic +generic.build.flash_mode=qio +generic.build.spiffs_pagesize=256 +generic.build.debug_port= +generic.build.debug_level= + +generic.menu.UploadTool.esptool=Serial +generic.menu.UploadTool.esptool.upload.tool=esptool +generic.menu.UploadTool.esptool.upload.verbose=-vv +generic.menu.UploadTool.espupload=OTA_upload +generic.menu.UploadTool.espupload.upload.tool=espupload + +generic.menu.CpuFrequency.80=80 MHz +generic.menu.CpuFrequency.80.build.f_cpu=80000000L +generic.menu.CpuFrequency.160=160 MHz +generic.menu.CpuFrequency.160.build.f_cpu=160000000L + +generic.menu.FlashFreq.40=40MHz +generic.menu.FlashFreq.40.build.flash_freq=40 +generic.menu.FlashFreq.80=80MHz +generic.menu.FlashFreq.80.build.flash_freq=80 + +generic.menu.FlashMode.dio=DIO +generic.menu.FlashMode.dio.build.flash_mode=dio +generic.menu.FlashMode.qio=QIO +generic.menu.FlashMode.qio.build.flash_mode=qio +generic.menu.FlashMode.dout=DOUT +generic.menu.FlashMode.dout.build.flash_mode=dout +generic.menu.FlashMode.qout=QOUT +generic.menu.FlashMode.qout.build.flash_mode=qout + +generic.menu.UploadSpeed.115200=115200 +generic.menu.UploadSpeed.115200.upload.speed=115200 +generic.menu.UploadSpeed.9600=9600 +generic.menu.UploadSpeed.9600.upload.speed=9600 +generic.menu.UploadSpeed.57600=57600 +generic.menu.UploadSpeed.57600.upload.speed=57600 +generic.menu.UploadSpeed.256000.windows=256000 +generic.menu.UploadSpeed.256000.upload.speed=256000 +generic.menu.UploadSpeed.230400.linux=230400 +generic.menu.UploadSpeed.230400.macosx=230400 +generic.menu.UploadSpeed.230400.upload.speed=230400 +generic.menu.UploadSpeed.460800.linux=460800 +generic.menu.UploadSpeed.460800.macosx=460800 +generic.menu.UploadSpeed.460800.upload.speed=460800 +generic.menu.UploadSpeed.512000.windows=512000 +generic.menu.UploadSpeed.512000.upload.speed=512000 +generic.menu.UploadSpeed.921600=921600 +generic.menu.UploadSpeed.921600.upload.speed=921600 + +generic.menu.FlashSize.512K64=512K (64K SPIFFS) +generic.menu.FlashSize.512K64.build.flash_size=512K +generic.menu.FlashSize.512K64.build.flash_ld=eagle.flash.512k64.ld +generic.menu.FlashSize.512K64.build.spiffs_start=0x6B000 +generic.menu.FlashSize.512K64.build.spiffs_end=0x7B000 +generic.menu.FlashSize.512K64.build.spiffs_blocksize=4096 +generic.menu.FlashSize.512K64.upload.maximum_size=434160 + +generic.menu.FlashSize.512K128=512K (128K SPIFFS) +generic.menu.FlashSize.512K128.build.flash_size=512K +generic.menu.FlashSize.512K128.build.flash_ld=eagle.flash.512k128.ld +generic.menu.FlashSize.512K128.build.spiffs_start=0x5B000 +generic.menu.FlashSize.512K128.build.spiffs_end=0x7B000 +generic.menu.FlashSize.512K128.build.spiffs_blocksize=4096 +generic.menu.FlashSize.512K128.upload.maximum_size=368624 + +generic.menu.FlashSize.512K0=512K (no SPIFFS) +generic.menu.FlashSize.512K0.build.flash_size=512K +generic.menu.FlashSize.512K0.build.flash_ld=eagle.flash.512k0.ld +generic.menu.FlashSize.512K0.upload.maximum_size=499696 + +generic.menu.FlashSize.1M512=1M (512K SPIFFS) +generic.menu.FlashSize.1M512.build.flash_size=1M +generic.menu.FlashSize.1M512.build.flash_ld=eagle.flash.1m512.ld +generic.menu.FlashSize.1M512.build.spiffs_start=0x7B000 +generic.menu.FlashSize.1M512.build.spiffs_end=0xFB000 +generic.menu.FlashSize.1M512.build.spiffs_blocksize=8192 +generic.menu.FlashSize.1M512.upload.maximum_size=499696 + +generic.menu.FlashSize.1M256=1M (256K SPIFFS) +generic.menu.FlashSize.1M256.build.flash_size=1M +generic.menu.FlashSize.1M256.build.flash_ld=eagle.flash.1m256.ld +generic.menu.FlashSize.1M256.build.spiffs_start=0xBB000 +generic.menu.FlashSize.1M256.build.spiffs_end=0xFB000 +generic.menu.FlashSize.1M256.build.spiffs_blocksize=4096 +generic.menu.FlashSize.1M256.upload.maximum_size=761840 + +generic.menu.FlashSize.1M192=1M (192K SPIFFS) +generic.menu.FlashSize.1M192.build.flash_size=1M +generic.menu.FlashSize.1M192.build.flash_ld=eagle.flash.1m192.ld +generic.menu.FlashSize.1M192.build.spiffs_start=0xCB000 +generic.menu.FlashSize.1M192.build.spiffs_end=0xFB000 +generic.menu.FlashSize.1M192.build.spiffs_blocksize=4096 +generic.menu.FlashSize.1M192.upload.maximum_size=827376 + +generic.menu.FlashSize.1M160=1M (160K SPIFFS) +generic.menu.FlashSize.1M160.build.flash_size=1M +generic.menu.FlashSize.1M160.build.flash_ld=eagle.flash.1m160.ld +generic.menu.FlashSize.1M160.build.spiffs_start=0xD3000 +generic.menu.FlashSize.1M160.build.spiffs_end=0xFB000 +generic.menu.FlashSize.1M160.build.spiffs_blocksize=4096 +generic.menu.FlashSize.1M160.upload.maximum_size=860144 + +generic.menu.FlashSize.1M144=1M (144K SPIFFS) +generic.menu.FlashSize.1M144.build.flash_size=1M +generic.menu.FlashSize.1M144.build.flash_ld=eagle.flash.1m144.ld +generic.menu.FlashSize.1M144.build.spiffs_start=0xD7000 +generic.menu.FlashSize.1M144.build.spiffs_end=0xFB000 +generic.menu.FlashSize.1M144.build.spiffs_blocksize=4096 +generic.menu.FlashSize.1M144.upload.maximum_size=876528 + +generic.menu.FlashSize.1M128=1M (128K SPIFFS) +generic.menu.FlashSize.1M128.build.flash_size=1M +generic.menu.FlashSize.1M128.build.flash_ld=eagle.flash.1m128.ld +generic.menu.FlashSize.1M128.build.spiffs_start=0xDB000 +generic.menu.FlashSize.1M128.build.spiffs_end=0xFB000 +generic.menu.FlashSize.1M128.build.spiffs_blocksize=4096 +generic.menu.FlashSize.1M128.upload.maximum_size=892912 + +generic.menu.FlashSize.1M64=1M (64K SPIFFS) +generic.menu.FlashSize.1M64.build.flash_size=1M +generic.menu.FlashSize.1M64.build.flash_ld=eagle.flash.1m64.ld +generic.menu.FlashSize.1M64.build.spiffs_start=0xEB000 +generic.menu.FlashSize.1M64.build.spiffs_end=0xFB000 +generic.menu.FlashSize.1M64.build.spiffs_blocksize=4096 +generic.menu.FlashSize.1M64.upload.maximum_size=958448 + +generic.menu.FlashSize.2M=2M (1M SPIFFS) +generic.menu.FlashSize.2M.build.flash_size=2M +generic.menu.FlashSize.2M.build.flash_ld=eagle.flash.2m.ld +generic.menu.FlashSize.2M.build.spiffs_start=0x100000 +generic.menu.FlashSize.2M.build.spiffs_end=0x1FB000 +generic.menu.FlashSize.2M.build.spiffs_blocksize=8192 +generic.menu.FlashSize.2M.upload.maximum_size=1044464 + +generic.menu.FlashSize.4M1M=4M (1M SPIFFS) +generic.menu.FlashSize.4M1M.build.flash_size=4M +generic.menu.FlashSize.4M1M.build.flash_ld=eagle.flash.4m1m.ld +generic.menu.FlashSize.4M1M.build.spiffs_start=0x300000 +generic.menu.FlashSize.4M1M.build.spiffs_end=0x3FB000 +generic.menu.FlashSize.4M1M.build.spiffs_blocksize=8192 +generic.menu.FlashSize.4M1M.build.spiffs_pagesize=256 +generic.menu.FlashSize.4M1M.upload.maximum_size=1044464 + +generic.menu.FlashSize.4M3M=4M (3M SPIFFS) +generic.menu.FlashSize.4M3M.build.flash_size=4M +generic.menu.FlashSize.4M3M.build.flash_ld=eagle.flash.4m.ld +generic.menu.FlashSize.4M3M.build.spiffs_start=0x100000 +generic.menu.FlashSize.4M3M.build.spiffs_end=0x3FB000 +generic.menu.FlashSize.4M3M.build.spiffs_blocksize=8192 +generic.menu.FlashSize.4M3M.upload.maximum_size=1044464 + +generic.menu.ResetMethod.ck=ck +generic.menu.ResetMethod.ck.upload.resetmethod=ck +generic.menu.ResetMethod.nodemcu=nodemcu +generic.menu.ResetMethod.nodemcu.upload.resetmethod=nodemcu + +generic.menu.Debug.Disabled=Disabled +generic.menu.Debug.Disabled.build.debug_port= +generic.menu.Debug.Serial=Serial +generic.menu.Debug.Serial.build.debug_port=-DDEBUG_ESP_PORT=Serial +generic.menu.Debug.Serial1=Serial1 +generic.menu.Debug.Serial1.build.debug_port=-DDEBUG_ESP_PORT=Serial1 + +generic.menu.DebugLevel.None____=None +generic.menu.DebugLevel.None____.build.debug_level= +generic.menu.DebugLevel.Core____=Core +generic.menu.DebugLevel.Core____.build.debug_level=-DDEBUG_ESP_CORE +generic.menu.DebugLevel.SSL_____=Core + SSL +generic.menu.DebugLevel.SSL_____.build.debug_level=-DDEBUG_ESP_CORE -DDEBUG_ESP_SSL +generic.menu.DebugLevel.SSL_MEM_=Core + SSL + TLS Mem +generic.menu.DebugLevel.SSL_MEM_.build.debug_level=-DDEBUG_ESP_CORE -DDEBUG_ESP_SSL -DDEBUG_TLS_MEM +generic.menu.DebugLevel.WiFic___=Core + WiFi +generic.menu.DebugLevel.WiFic___.build.debug_level=-DDEBUG_ESP_CORE -DDEBUG_ESP_WIFI +generic.menu.DebugLevel.WiFi____=WiFi +generic.menu.DebugLevel.WiFi____.build.debug_level=-DDEBUG_ESP_WIFI +generic.menu.DebugLevel.HTTPClient=HTTPClient +generic.menu.DebugLevel.HTTPClient.build.debug_level=-DDEBUG_ESP_HTTP_CLIENT +generic.menu.DebugLevel.HTTPClient2=HTTPClient + SSL +generic.menu.DebugLevel.HTTPClient2.build.debug_level=-DDEBUG_ESP_HTTP_CLIENT -DDEBUG_ESP_SSL +generic.menu.DebugLevel.HTTPUpdate=HTTPUpdate +generic.menu.DebugLevel.HTTPUpdate.build.debug_level=-DDEBUG_ESP_HTTP_UPDATE +generic.menu.DebugLevel.HTTPUpdate2=HTTPClient + HTTPUpdate +generic.menu.DebugLevel.HTTPUpdate2.build.debug_level=-DDEBUG_ESP_HTTP_UPDATE -DDEBUG_ESP_HTTP_UPDATE +generic.menu.DebugLevel.HTTPUpdate3=HTTPClient + HTTPUpdate + Updater +generic.menu.DebugLevel.HTTPUpdate3.build.debug_level=-DDEBUG_ESP_HTTP_UPDATE -DDEBUG_ESP_HTTP_UPDATE -DDEBUG_ESP_UPDATER +generic.menu.DebugLevel.HTTPServer=HTTPServer +generic.menu.DebugLevel.HTTPServer.build.debug_level=-DDEBUG_ESP_HTTP_SERVER +generic.menu.DebugLevel.UPDATER=Updater +generic.menu.DebugLevel.UPDATER.build.debug_level=-DDEBUG_ESP_UPDATER +generic.menu.DebugLevel.OTA_____=OTA +generic.menu.DebugLevel.OTA_____.build.debug_level=-DDEBUG_ESP_OTA +generic.menu.DebugLevel.OTA2____=OTA + Updater +generic.menu.DebugLevel.OTA2____.build.debug_level=-DDEBUG_ESP_OTA -DDEBUG_ESP_UPDATER +generic.menu.DebugLevel.all_____=All +generic.menu.DebugLevel.all_____.build.debug_level=-DDEBUG_ESP_CORE -DDEBUG_ESP_SSL -DDEBUG_ESP_WIFI -DDEBUG_ESP_HTTP_CLIENT -DDEBUG_ESP_HTTP_UPDATE -DDEBUG_ESP_HTTP_SERVER -DDEBUG_ESP_UPDATER -DDEBUG_ESP_OTA -DDEBUG_TLS_MEM + +# disabled because espressif's bootloader refuses to write above 4M +# generic.menu.FlashSize.8M=8M (7M SPIFFS) +# generic.menu.FlashSize.8M.build.flash_size=1M +# generic.menu.FlashSize.8M.build.flash_ld=eagle.flash.8m.ld +# generic.menu.FlashSize.8M.build.spiffs_start=0x100000 +# generic.menu.FlashSize.8M.build.spiffs_end=0x800000 +# generic.menu.FlashSize.8M.build.spiffs_blocksize=8192 +# generic.menu.FlashSize.16M=16M (15M SPIFFS) +# generic.menu.FlashSize.16M.build.flash_size=1M +# generic.menu.FlashSize.16M.build.flash_ld=eagle.flash.16m.ld +# generic.menu.FlashSize.16M.build.spiffs_start=0x100000 +# generic.menu.FlashSize.16M.build.spiffs_end=0x1000000 +# generic.menu.FlashSize.16M.build.spiffs_blocksize=8192 + +############################################################## +# ESP8285 chip has built-in 1MB flash + +esp8285.name=Generic ESP8285 Module + +esp8285.upload.tool=esptool +esp8285.upload.speed=115200 +esp8285.upload.resetmethod=ck +esp8285.upload.maximum_size=434160 +esp8285.upload.maximum_data_size=81920 +esp8285.upload.wait_for_upload_port=true +esp8285.serial.disableDTR=true +esp8285.serial.disableRTS=true + +esp8285.build.mcu=esp8266 +esp8285.build.f_cpu=80000000L +esp8285.build.board=ESP8266_ESP01 +esp8285.build.core=esp8266 +esp8285.build.variant=generic +esp8285.build.flash_mode=dout +esp8285.build.flash_freq=40 +esp8285.build.spiffs_pagesize=256 +esp8285.build.debug_port= +esp8285.build.debug_level= + +esp8285.menu.UploadTool.esptool=Serial +esp8285.menu.UploadTool.esptool.upload.tool=esptool +esp8285.menu.UploadTool.esptool.upload.verbose=-vv +esp8285.menu.UploadTool.espupload=OTA_upload +esp8285.menu.UploadTool.espupload.upload.tool=espupload + +esp8285.menu.CpuFrequency.80=80 MHz +esp8285.menu.CpuFrequency.80.build.f_cpu=80000000L +esp8285.menu.CpuFrequency.160=160 MHz +esp8285.menu.CpuFrequency.160.build.f_cpu=160000000L + +esp8285.menu.UploadSpeed.115200=115200 +esp8285.menu.UploadSpeed.115200.upload.speed=115200 +esp8285.menu.UploadSpeed.9600=9600 +esp8285.menu.UploadSpeed.9600.upload.speed=9600 +esp8285.menu.UploadSpeed.57600=57600 +esp8285.menu.UploadSpeed.57600.upload.speed=57600 +esp8285.menu.UploadSpeed.256000.windows=256000 +esp8285.menu.UploadSpeed.256000.upload.speed=256000 +esp8285.menu.UploadSpeed.230400.linux=230400 +esp8285.menu.UploadSpeed.230400.macosx=230400 +esp8285.menu.UploadSpeed.230400.upload.speed=230400 +esp8285.menu.UploadSpeed.460800.linux=460800 +esp8285.menu.UploadSpeed.460800.macosx=460800 +esp8285.menu.UploadSpeed.460800.upload.speed=460800 +esp8285.menu.UploadSpeed.512000.windows=512000 +esp8285.menu.UploadSpeed.512000.upload.speed=512000 +esp8285.menu.UploadSpeed.921600=921600 +esp8285.menu.UploadSpeed.921600.upload.speed=921600 + +esp8285.menu.FlashSize.1M512=1M (512K SPIFFS) +esp8285.menu.FlashSize.1M512.build.flash_size=1M +esp8285.menu.FlashSize.1M512.build.flash_ld=eagle.flash.1m512.ld +esp8285.menu.FlashSize.1M512.build.spiffs_start=0x7B000 +esp8285.menu.FlashSize.1M512.build.spiffs_end=0xFB000 +esp8285.menu.FlashSize.1M512.build.spiffs_blocksize=8192 +esp8285.menu.FlashSize.1M512.upload.maximum_size=499696 + +esp8285.menu.FlashSize.1M256=1M (256K SPIFFS) +esp8285.menu.FlashSize.1M256.build.flash_size=1M +esp8285.menu.FlashSize.1M256.build.flash_ld=eagle.flash.1m256.ld +esp8285.menu.FlashSize.1M256.build.spiffs_start=0xBB000 +esp8285.menu.FlashSize.1M256.build.spiffs_end=0xFB000 +esp8285.menu.FlashSize.1M256.build.spiffs_blocksize=4096 +esp8285.menu.FlashSize.1M256.upload.maximum_size=761840 + +esp8285.menu.FlashSize.1M192=1M (192K SPIFFS) +esp8285.menu.FlashSize.1M192.build.flash_size=1M +esp8285.menu.FlashSize.1M192.build.flash_ld=eagle.flash.1m192.ld +esp8285.menu.FlashSize.1M192.build.spiffs_start=0xCB000 +esp8285.menu.FlashSize.1M192.build.spiffs_end=0xFB000 +esp8285.menu.FlashSize.1M192.build.spiffs_blocksize=4096 +esp8285.menu.FlashSize.1M192.upload.maximum_size=827376 + +esp8285.menu.FlashSize.1M160=1M (160K SPIFFS) +esp8285.menu.FlashSize.1M160.build.flash_size=1M +esp8285.menu.FlashSize.1M160.build.flash_ld=eagle.flash.1m160.ld +esp8285.menu.FlashSize.1M160.build.spiffs_start=0xD3000 +esp8285.menu.FlashSize.1M160.build.spiffs_end=0xFB000 +esp8285.menu.FlashSize.1M160.build.spiffs_blocksize=4096 +esp8285.menu.FlashSize.1M160.upload.maximum_size=860144 + +esp8285.menu.FlashSize.1M144=1M (144K SPIFFS) +esp8285.menu.FlashSize.1M144.build.flash_size=1M +esp8285.menu.FlashSize.1M144.build.flash_ld=eagle.flash.1m144.ld +esp8285.menu.FlashSize.1M144.build.spiffs_start=0xD7000 +esp8285.menu.FlashSize.1M144.build.spiffs_end=0xFB000 +esp8285.menu.FlashSize.1M144.build.spiffs_blocksize=4096 +esp8285.menu.FlashSize.1M144.upload.maximum_size=876528 + +esp8285.menu.FlashSize.1M128=1M (128K SPIFFS) +esp8285.menu.FlashSize.1M128.build.flash_size=1M +esp8285.menu.FlashSize.1M128.build.flash_ld=eagle.flash.1m128.ld +esp8285.menu.FlashSize.1M128.build.spiffs_start=0xDB000 +esp8285.menu.FlashSize.1M128.build.spiffs_end=0xFB000 +esp8285.menu.FlashSize.1M128.build.spiffs_blocksize=4096 +esp8285.menu.FlashSize.1M128.upload.maximum_size=892912 + +esp8285.menu.FlashSize.1M64=1M (64K SPIFFS) +esp8285.menu.FlashSize.1M64.build.flash_size=1M +esp8285.menu.FlashSize.1M64.build.flash_ld=eagle.flash.1m64.ld +esp8285.menu.FlashSize.1M64.build.spiffs_start=0xEB000 +esp8285.menu.FlashSize.1M64.build.spiffs_end=0xFB000 +esp8285.menu.FlashSize.1M64.build.spiffs_blocksize=4096 +esp8285.menu.FlashSize.1M64.upload.maximum_size=958448 + + +############################################################## + +espduino.name=ESPDuino (ESP-13 Module) + +espduino.upload.tool=esptool +espduino.upload.speed=115200 +espduino.upload.resetmethod=ck +espduino.upload.maximum_size=1044464 +espduino.upload.maximum_data_size=81920 +espduino.upload.wait_for_upload_port=true +espduino.serial.disableDTR=true +espduino.serial.disableRTS=true + +espduino.build.mcu=esp8266 +espduino.build.f_cpu=80000000L +espduino.build.board=ESP8266_ESP13 +espduino.build.core=esp8266 +espduino.build.variant=ESPDuino +espduino.build.flash_mode=dio +espduino.build.flash_size=4M +espduino.build.flash_freq=40 +espduino.build.debug_port= +espduino.build.debug_level= + +espduino.menu.CpuFrequency.80=80 MHz +espduino.menu.CpuFrequency.80.build.f_cpu=80000000L +espduino.menu.CpuFrequency.160=160 MHz +espduino.menu.CpuFrequency.160.build.f_cpu=160000000L + +espduino.menu.UploadSpeed.115200=115200 +espduino.menu.UploadSpeed.115200.upload.speed=115200 +espduino.menu.UploadSpeed.9600=9600 +espduino.menu.UploadSpeed.9600.upload.speed=9600 +espduino.menu.UploadSpeed.57600=57600 +espduino.menu.UploadSpeed.57600.upload.speed=57600 +espduino.menu.UploadSpeed.256000.windows=256000 +espduino.menu.UploadSpeed.256000.upload.speed=256000 +espduino.menu.UploadSpeed.230400.linux=230400 +espduino.menu.UploadSpeed.230400.macosx=230400 +espduino.menu.UploadSpeed.230400.macosx=230400 +espduino.menu.UploadSpeed.230400.upload.speed=230400 +espduino.menu.UploadSpeed.460800.linux=460800 +espduino.menu.UploadSpeed.460800.macosx=460800 +espduino.menu.UploadSpeed.460800.upload.speed=460800 +espduino.menu.UploadSpeed.512000.windows=512000 +espduino.menu.UploadSpeed.512000.upload.speed=512000 +espduino.menu.UploadSpeed.921600=921600 +espduino.menu.UploadSpeed.921600.upload.speed=921600 + +espduino.menu.FlashSize.4M3M=4M (3M SPIFFS) +espduino.menu.FlashSize.4M3M.build.flash_size=4M +espduino.menu.FlashSize.4M3M.build.flash_ld=eagle.flash.4m.ld +espduino.menu.FlashSize.4M3M.build.spiffs_start=0x100000 +espduino.menu.FlashSize.4M3M.build.spiffs_end=0x3FB000 +espduino.menu.FlashSize.4M3M.build.spiffs_blocksize=8192 +espduino.menu.FlashSize.4M3M.build.spiffs_pagesize=256 + +espduino.menu.FlashSize.4M1M=4M (1M SPIFFS) +espduino.menu.FlashSize.4M1M.build.flash_size=4M +espduino.menu.FlashSize.4M1M.build.flash_ld=eagle.flash.4m1m.ld +espduino.menu.FlashSize.4M1M.build.spiffs_start=0x300000 +espduino.menu.FlashSize.4M1M.build.spiffs_end=0x3FB000 +espduino.menu.FlashSize.4M1M.build.spiffs_blocksize=8192 +espduino.menu.FlashSize.4M1M.build.spiffs_pagesize=256 +############################################################## +huzzah.name=Adafruit HUZZAH ESP8266 + +huzzah.upload.tool=esptool +huzzah.upload.speed=115200 +huzzah.upload.resetmethod=nodemcu +huzzah.upload.maximum_size=1044464 +huzzah.upload.maximum_data_size=81920 +huzzah.upload.wait_for_upload_port=true +huzzah.serial.disableDTR=true +huzzah.serial.disableRTS=true + +huzzah.build.mcu=esp8266 +huzzah.build.f_cpu=80000000L +huzzah.build.board=ESP8266_ESP12 +huzzah.build.core=esp8266 +huzzah.build.variant=adafruit +huzzah.build.flash_mode=qio +huzzah.build.flash_size=4M +huzzah.build.flash_freq=40 +huzzah.build.debug_port= +huzzah.build.debug_level= + +huzzah.menu.CpuFrequency.80=80 MHz +huzzah.menu.CpuFrequency.80.build.f_cpu=80000000L +huzzah.menu.CpuFrequency.160=160 MHz +huzzah.menu.CpuFrequency.160.build.f_cpu=160000000L + +huzzah.menu.UploadSpeed.115200=115200 +huzzah.menu.UploadSpeed.115200.upload.speed=115200 +huzzah.menu.UploadSpeed.9600=9600 +huzzah.menu.UploadSpeed.9600.upload.speed=9600 +huzzah.menu.UploadSpeed.57600=57600 +huzzah.menu.UploadSpeed.57600.upload.speed=57600 +huzzah.menu.UploadSpeed.256000=256000 +huzzah.menu.UploadSpeed.256000.upload.speed=256000 +huzzah.menu.UploadSpeed.921600=921600 +huzzah.menu.UploadSpeed.921600.upload.speed=921600 + +huzzah.menu.FlashSize.4M3M=4M (3M SPIFFS) +huzzah.menu.FlashSize.4M3M.build.flash_size=4M +huzzah.menu.FlashSize.4M3M.build.flash_ld=eagle.flash.4m.ld +huzzah.menu.FlashSize.4M3M.build.spiffs_start=0x100000 +huzzah.menu.FlashSize.4M3M.build.spiffs_end=0x3FB000 +huzzah.menu.FlashSize.4M3M.build.spiffs_blocksize=8192 +huzzah.menu.FlashSize.4M3M.build.spiffs_pagesize=256 + +huzzah.menu.FlashSize.4M1M=4M (1M SPIFFS) +huzzah.menu.FlashSize.4M1M.build.flash_size=4M +huzzah.menu.FlashSize.4M1M.build.flash_ld=eagle.flash.4m1m.ld +huzzah.menu.FlashSize.4M1M.build.spiffs_start=0x300000 +huzzah.menu.FlashSize.4M1M.build.spiffs_end=0x3FB000 +huzzah.menu.FlashSize.4M1M.build.spiffs_blocksize=8192 +huzzah.menu.FlashSize.4M1M.build.spiffs_pagesize=256 + +############################################################## +espresso_lite_v1.name=ESPresso Lite 1.0 +espresso_lite_v1.upload.tool=esptool +espresso_lite_v1.upload.speed=115200 +espresso_lite_v1.upload.maximum_size=1044464 +espresso_lite_v1.upload.maximum_data_size=81920 +espresso_lite_v1.upload.wait_for_upload_port=true + +espresso_lite_v1.build.mcu=esp8266 +espresso_lite_v1.build.f_cpu=80000000L +espresso_lite_v1.build.board=ESP8266_ESPRESSO_LITE_V1 +espresso_lite_v1.build.core=esp8266 +espresso_lite_v1.build.variant=espresso_lite_v1 +espresso_lite_v1.build.flash_mode=dio +espresso_lite_v1.build.flash_size=4M +espresso_lite_v1.build.flash_freq=40 + +espresso_lite_v1.menu.CpuFrequency.80=80 MHz +espresso_lite_v1.menu.CpuFrequency.80.build.f_cpu=80000000L +espresso_lite_v1.menu.CpuFrequency.160=160 MHz +espresso_lite_v1.menu.CpuFrequency.160.build.f_cpu=160000000L + +espresso_lite_v1.menu.UploadSpeed.115200=115200 +espresso_lite_v1.menu.UploadSpeed.115200.upload.speed=115200 +espresso_lite_v1.menu.UploadSpeed.57600=57600 +espresso_lite_v1.menu.UploadSpeed.57600.upload.speed=57600 +espresso_lite_v1.menu.UploadSpeed.256000.windows=256000 +espresso_lite_v1.menu.UploadSpeed.256000.upload.speed=256000 +espresso_lite_v1.menu.UploadSpeed.230400.linux=230400 +espresso_lite_v1.menu.UploadSpeed.230400.macosx=230400 +espresso_lite_v1.menu.UploadSpeed.230400.macosx=230400 +espresso_lite_v1.menu.UploadSpeed.230400.upload.speed=230400 +espresso_lite_v1.menu.UploadSpeed.460800.linux=460800 +espresso_lite_v1.menu.UploadSpeed.460800.macosx=460800 +espresso_lite_v1.menu.UploadSpeed.460800.upload.speed=460800 +espresso_lite_v1.menu.UploadSpeed.512000.windows=512000 +espresso_lite_v1.menu.UploadSpeed.512000.upload.speed=512000 +espresso_lite_v1.menu.UploadSpeed.921600=921600 +espresso_lite_v1.menu.UploadSpeed.921600.upload.speed=921600 + +espresso_lite_v1.menu.FlashSize.4M3M=4M (3M SPIFFS) +espresso_lite_v1.menu.FlashSize.4M3M.build.flash_size=4M +espresso_lite_v1.menu.FlashSize.4M3M.build.flash_ld=eagle.flash.4m.ld +espresso_lite_v1.menu.FlashSize.4M3M.build.spiffs_start=0x100000 +espresso_lite_v1.menu.FlashSize.4M3M.build.spiffs_end=0x3FB000 +espresso_lite_v1.menu.FlashSize.4M3M.build.spiffs_blocksize=8192 +espresso_lite_v1.menu.FlashSize.4M3M.upload.maximum_size=1044464 + +espresso_lite_v1.menu.FlashSize.4M1M=4M (1M SPIFFS) +espresso_lite_v1.menu.FlashSize.4M1M.build.flash_size=4M +espresso_lite_v1.menu.FlashSize.4M1M.build.flash_ld=eagle.flash.4m1m.ld +espresso_lite_v1.menu.FlashSize.4M1M.build.spiffs_start=0x300000 +espresso_lite_v1.menu.FlashSize.4M1M.build.spiffs_end=0x3FB000 +espresso_lite_v1.menu.FlashSize.4M1M.build.spiffs_blocksize=8192 +espresso_lite_v1.menu.FlashSize.4M1M.build.spiffs_pagesize=256 +espresso_lite_v1.menu.FlashSize.4M1M.upload.maximum_size=1044464 + +espresso_lite_v1.menu.ResetMethod.nodemcu=nodemcu +espresso_lite_v1.menu.ResetMethod.nodemcu.upload.resetmethod=nodemcu +espresso_lite_v1.menu.ResetMethod.ck=ck +espresso_lite_v1.menu.ResetMethod.ck.upload.resetmethod=ck + +espresso_lite_v1.menu.Debug.Disabled=Disabled +espresso_lite_v1.menu.Debug.Disabled.build.debug_port= +espresso_lite_v1.menu.Debug.Serial=Serial +espresso_lite_v1.menu.Debug.Serial.build.debug_port=-DDEBUG_ESP_PORT=Serial +espresso_lite_v1.menu.Debug.Serial1=Serial1 +espresso_lite_v1.menu.Debug.Serial1.build.debug_port=-DDEBUG_ESP_PORT=Serial1 + +espresso_lite_v1.menu.DebugLevel.None____=None +espresso_lite_v1.menu.DebugLevel.None____.build.debug_level= +espresso_lite_v1.menu.DebugLevel.Core____=Core +espresso_lite_v1.menu.DebugLevel.Core____.build.debug_level=-DDEBUG_ESP_CORE +espresso_lite_v1.menu.DebugLevel.SSL_____=Core + SSL +espresso_lite_v1.menu.DebugLevel.SSL_____.build.debug_level=-DDEBUG_ESP_CORE -DDEBUG_ESP_SSL +espresso_lite_v1.menu.DebugLevel.SSL_MEM_=Core + SSL + TLS Mem +espresso_lite_v1.menu.DebugLevel.SSL_MEM_.build.debug_level=-DDEBUG_ESP_CORE -DDEBUG_ESP_SSL -DDEBUG_TLS_MEM +espresso_lite_v1.menu.DebugLevel.WiFic___=Core + WiFi +espresso_lite_v1.menu.DebugLevel.WiFic___.build.debug_level=-DDEBUG_ESP_CORE -DDEBUG_ESP_WIFI +espresso_lite_v1.menu.DebugLevel.WiFi____=WiFi +espresso_lite_v1.menu.DebugLevel.WiFi____.build.debug_level=-DDEBUG_ESP_WIFI +espresso_lite_v1.menu.DebugLevel.HTTPClient=HTTPClient +espresso_lite_v1.menu.DebugLevel.HTTPClient.build.debug_level=-DDEBUG_ESP_HTTP_CLIENT +espresso_lite_v1.menu.DebugLevel.HTTPClient2=HTTPClient + SSL +espresso_lite_v1.menu.DebugLevel.HTTPClient2.build.debug_level=-DDEBUG_ESP_HTTP_CLIENT -DDEBUG_ESP_SSL +espresso_lite_v1.menu.DebugLevel.HTTPUpdate=HTTPUpdate +espresso_lite_v1.menu.DebugLevel.HTTPUpdate.build.debug_level=-DDEBUG_ESP_HTTP_UPDATE +espresso_lite_v1.menu.DebugLevel.HTTPUpdate2=HTTPClient + HTTPUpdate +espresso_lite_v1.menu.DebugLevel.HTTPUpdate2.build.debug_level=-DDEBUG_ESP_HTTP_UPDATE -DDEBUG_ESP_HTTP_UPDATE +espresso_lite_v1.menu.DebugLevel.HTTPUpdate3=HTTPClient + HTTPUpdate + Updater +espresso_lite_v1.menu.DebugLevel.HTTPUpdate3.build.debug_level=-DDEBUG_ESP_HTTP_UPDATE -DDEBUG_ESP_HTTP_UPDATE -DDEBUG_ESP_UPDATER +espresso_lite_v1.menu.DebugLevel.HTTPServer=HTTPServer +espresso_lite_v1.menu.DebugLevel.HTTPServer.build.debug_level=-DDEBUG_ESP_HTTP_SERVER +espresso_lite_v1.menu.DebugLevel.UPDATER=Updater +espresso_lite_v1.menu.DebugLevel.UPDATER.build.debug_level=-DDEBUG_ESP_UPDATER +espresso_lite_v1.menu.DebugLevel.OTA_____=OTA +espresso_lite_v1.menu.DebugLevel.OTA_____.build.debug_level=-DDEBUG_ESP_OTA +espresso_lite_v1.menu.DebugLevel.OTA2____=OTA + Updater +espresso_lite_v1.menu.DebugLevel.OTA2____.build.debug_level=-DDEBUG_ESP_OTA -DDEBUG_ESP_UPDATER +espresso_lite_v1.menu.DebugLevel.all_____=All +espresso_lite_v1.menu.DebugLevel.all_____.build.debug_level=-DDEBUG_ESP_CORE -DDEBUG_ESP_SSL -DDEBUG_ESP_WIFI -DDEBUG_ESP_HTTP_CLIENT -DDEBUG_ESP_HTTP_UPDATE -DDEBUG_ESP_HTTP_SERVER -DDEBUG_ESP_UPDATER -DDEBUG_ESP_OTA -DDEBUG_TLS_MEM + +espresso_lite_v1.build.debug_port= +espresso_lite_v1.build.debug_level= + +############################################################## +espresso_lite_v2.name=ESPresso Lite 2.0 +espresso_lite_v2.upload.tool=esptool +espresso_lite_v2.upload.speed=115200 +espresso_lite_v2.upload.maximum_size=1044464 +espresso_lite_v2.upload.maximum_data_size=81920 +espresso_lite_v2.upload.wait_for_upload_port=true + +espresso_lite_v2.build.mcu=esp8266 +espresso_lite_v2.build.f_cpu=80000000L +espresso_lite_v2.build.board=ESP8266_ESPRESSO_LITE_V2 +espresso_lite_v2.build.core=esp8266 +espresso_lite_v2.build.variant=espresso_lite_v2 +espresso_lite_v2.build.flash_mode=dio +espresso_lite_v2.build.flash_size=4M +espresso_lite_v2.build.flash_freq=40 + +espresso_lite_v2.menu.CpuFrequency.80=80 MHz +espresso_lite_v2.menu.CpuFrequency.80.build.f_cpu=80000000L +espresso_lite_v2.menu.CpuFrequency.160=160 MHz +espresso_lite_v2.menu.CpuFrequency.160.build.f_cpu=160000000L + +espresso_lite_v2.menu.UploadSpeed.115200=115200 +espresso_lite_v2.menu.UploadSpeed.115200.upload.speed=115200 +espresso_lite_v2.menu.UploadSpeed.57600=57600 +espresso_lite_v2.menu.UploadSpeed.57600.upload.speed=57600 +espresso_lite_v2.menu.UploadSpeed.256000.windows=256000 +espresso_lite_v2.menu.UploadSpeed.256000.upload.speed=256000 +espresso_lite_v2.menu.UploadSpeed.230400.linux=230400 +espresso_lite_v2.menu.UploadSpeed.230400.macosx=230400 +espresso_lite_v2.menu.UploadSpeed.230400.macosx=230400 +espresso_lite_v2.menu.UploadSpeed.230400.upload.speed=230400 +espresso_lite_v2.menu.UploadSpeed.460800.linux=460800 +espresso_lite_v2.menu.UploadSpeed.460800.macosx=460800 +espresso_lite_v2.menu.UploadSpeed.460800.upload.speed=460800 +espresso_lite_v2.menu.UploadSpeed.512000.windows=512000 +espresso_lite_v2.menu.UploadSpeed.512000.upload.speed=512000 +espresso_lite_v2.menu.UploadSpeed.921600=921600 +espresso_lite_v2.menu.UploadSpeed.921600.upload.speed=921600 + +espresso_lite_v2.menu.FlashSize.4M3M=4M (3M SPIFFS) +espresso_lite_v2.menu.FlashSize.4M3M.build.flash_size=4M +espresso_lite_v2.menu.FlashSize.4M3M.build.flash_ld=eagle.flash.4m.ld +espresso_lite_v2.menu.FlashSize.4M3M.build.spiffs_start=0x100000 +espresso_lite_v2.menu.FlashSize.4M3M.build.spiffs_end=0x3FB000 +espresso_lite_v2.menu.FlashSize.4M3M.build.spiffs_blocksize=8192 +espresso_lite_v2.menu.FlashSize.4M3M.upload.maximum_size=1044464 + +espresso_lite_v2.menu.FlashSize.4M1M=4M (1M SPIFFS) +espresso_lite_v2.menu.FlashSize.4M1M.build.flash_size=4M +espresso_lite_v2.menu.FlashSize.4M1M.build.flash_ld=eagle.flash.4m1m.ld +espresso_lite_v2.menu.FlashSize.4M1M.build.spiffs_start=0x300000 +espresso_lite_v2.menu.FlashSize.4M1M.build.spiffs_end=0x3FB000 +espresso_lite_v2.menu.FlashSize.4M1M.build.spiffs_blocksize=8192 +espresso_lite_v2.menu.FlashSize.4M1M.build.spiffs_pagesize=256 +espresso_lite_v2.menu.FlashSize.4M1M.upload.maximum_size=1044464 + +espresso_lite_v2.menu.ResetMethod.ck=ck +espresso_lite_v2.menu.ResetMethod.ck.upload.resetmethod=ck +espresso_lite_v2.menu.ResetMethod.nodemcu=nodemcu +espresso_lite_v2.menu.ResetMethod.nodemcu.upload.resetmethod=nodemcu + +espresso_lite_v2.menu.Debug.Disabled=Disabled +espresso_lite_v2.menu.Debug.Disabled.build.debug_port= +espresso_lite_v2.menu.Debug.Serial=Serial +espresso_lite_v2.menu.Debug.Serial.build.debug_port=-DDEBUG_ESP_PORT=Serial +espresso_lite_v2.menu.Debug.Serial1=Serial1 +espresso_lite_v2.menu.Debug.Serial1.build.debug_port=-DDEBUG_ESP_PORT=Serial1 + +espresso_lite_v2.menu.DebugLevel.None____=None +espresso_lite_v2.menu.DebugLevel.None____.build.debug_level= +espresso_lite_v2.menu.DebugLevel.Core____=Core +espresso_lite_v2.menu.DebugLevel.Core____.build.debug_level=-DDEBUG_ESP_CORE +espresso_lite_v2.menu.DebugLevel.SSL_____=Core + SSL +espresso_lite_v2.menu.DebugLevel.SSL_____.build.debug_level=-DDEBUG_ESP_CORE -DDEBUG_ESP_SSL +espresso_lite_v2.menu.DebugLevel.SSL_MEM_=Core + SSL + TLS Mem +espresso_lite_v2.menu.DebugLevel.SSL_MEM_.build.debug_level=-DDEBUG_ESP_CORE -DDEBUG_ESP_SSL -DDEBUG_TLS_MEM +espresso_lite_v2.menu.DebugLevel.WiFic___=Core + WiFi +espresso_lite_v2.menu.DebugLevel.WiFic___.build.debug_level=-DDEBUG_ESP_CORE -DDEBUG_ESP_WIFI +espresso_lite_v2.menu.DebugLevel.WiFi____=WiFi +espresso_lite_v2.menu.DebugLevel.WiFi____.build.debug_level=-DDEBUG_ESP_WIFI +espresso_lite_v2.menu.DebugLevel.HTTPClient=HTTPClient +espresso_lite_v2.menu.DebugLevel.HTTPClient.build.debug_level=-DDEBUG_ESP_HTTP_CLIENT +espresso_lite_v2.menu.DebugLevel.HTTPClient2=HTTPClient + SSL +espresso_lite_v2.menu.DebugLevel.HTTPClient2.build.debug_level=-DDEBUG_ESP_HTTP_CLIENT -DDEBUG_ESP_SSL +espresso_lite_v2.menu.DebugLevel.HTTPUpdate=HTTPUpdate +espresso_lite_v2.menu.DebugLevel.HTTPUpdate.build.debug_level=-DDEBUG_ESP_HTTP_UPDATE +espresso_lite_v2.menu.DebugLevel.HTTPUpdate2=HTTPClient + HTTPUpdate +espresso_lite_v2.menu.DebugLevel.HTTPUpdate2.build.debug_level=-DDEBUG_ESP_HTTP_UPDATE -DDEBUG_ESP_HTTP_UPDATE +espresso_lite_v2.menu.DebugLevel.HTTPUpdate3=HTTPClient + HTTPUpdate + Updater +espresso_lite_v2.menu.DebugLevel.HTTPUpdate3.build.debug_level=-DDEBUG_ESP_HTTP_UPDATE -DDEBUG_ESP_HTTP_UPDATE -DDEBUG_ESP_UPDATER +espresso_lite_v2.menu.DebugLevel.HTTPServer=HTTPServer +espresso_lite_v2.menu.DebugLevel.HTTPServer.build.debug_level=-DDEBUG_ESP_HTTP_SERVER +espresso_lite_v2.menu.DebugLevel.UPDATER=Updater +espresso_lite_v2.menu.DebugLevel.UPDATER.build.debug_level=-DDEBUG_ESP_UPDATER +espresso_lite_v2.menu.DebugLevel.OTA_____=OTA +espresso_lite_v2.menu.DebugLevel.OTA_____.build.debug_level=-DDEBUG_ESP_OTA +espresso_lite_v2.menu.DebugLevel.OTA2____=OTA + Updater +espresso_lite_v2.menu.DebugLevel.OTA2____.build.debug_level=-DDEBUG_ESP_OTA -DDEBUG_ESP_UPDATER +espresso_lite_v2.menu.DebugLevel.all_____=All +espresso_lite_v2.menu.DebugLevel.all_____.build.debug_level=-DDEBUG_ESP_CORE -DDEBUG_ESP_SSL -DDEBUG_ESP_WIFI -DDEBUG_ESP_HTTP_CLIENT -DDEBUG_ESP_HTTP_UPDATE -DDEBUG_ESP_HTTP_SERVER -DDEBUG_ESP_UPDATER -DDEBUG_ESP_OTA -DDEBUG_TLS_MEM + +espresso_lite_v2.build.debug_port= +espresso_lite_v2.build.debug_level= + +############################################################## +phoenix_v1.name=Phoenix 1.0 +phoenix_v1.upload.tool=esptool +phoenix_v1.upload.speed=115200 +phoenix_v1.upload.maximum_size=1044464 +phoenix_v1.upload.maximum_data_size=81920 +phoenix_v1.upload.wait_for_upload_port=true + +phoenix_v1.build.mcu=esp8266 +phoenix_v1.build.f_cpu=80000000L +phoenix_v1.build.board=ESP8266_PHOENIX_V1 +phoenix_v1.build.core=esp8266 +phoenix_v1.build.variant=phoenix_v1 +phoenix_v1.build.flash_mode=dio +phoenix_v1.build.flash_size=4M +phoenix_v1.build.flash_freq=40 + +phoenix_v1.menu.CpuFrequency.80=80 MHz +phoenix_v1.menu.CpuFrequency.80.build.f_cpu=80000000L +phoenix_v1.menu.CpuFrequency.160=160 MHz +phoenix_v1.menu.CpuFrequency.160.build.f_cpu=160000000L + +phoenix_v1.menu.UploadSpeed.115200=115200 +phoenix_v1.menu.UploadSpeed.115200.upload.speed=115200 +phoenix_v1.menu.UploadSpeed.57600=57600 +phoenix_v1.menu.UploadSpeed.57600.upload.speed=57600 +phoenix_v1.menu.UploadSpeed.256000.windows=256000 +phoenix_v1.menu.UploadSpeed.256000.upload.speed=256000 +phoenix_v1.menu.UploadSpeed.230400.linux=230400 +phoenix_v1.menu.UploadSpeed.230400.macosx=230400 +phoenix_v1.menu.UploadSpeed.230400.macosx=230400 +phoenix_v1.menu.UploadSpeed.230400.upload.speed=230400 +phoenix_v1.menu.UploadSpeed.460800.linux=460800 +phoenix_v1.menu.UploadSpeed.460800.macosx=460800 +phoenix_v1.menu.UploadSpeed.460800.upload.speed=460800 +phoenix_v1.menu.UploadSpeed.512000.windows=512000 +phoenix_v1.menu.UploadSpeed.512000.upload.speed=512000 +phoenix_v1.menu.UploadSpeed.921600=921600 +phoenix_v1.menu.UploadSpeed.921600.upload.speed=921600 + +phoenix_v1.menu.FlashSize.4M3M=4M (3M SPIFFS) +phoenix_v1.menu.FlashSize.4M3M.build.flash_size=4M +phoenix_v1.menu.FlashSize.4M3M.build.flash_ld=eagle.flash.4m.ld +phoenix_v1.menu.FlashSize.4M3M.build.spiffs_start=0x100000 +phoenix_v1.menu.FlashSize.4M3M.build.spiffs_end=0x3FB000 +phoenix_v1.menu.FlashSize.4M3M.build.spiffs_blocksize=8192 +phoenix_v1.menu.FlashSize.4M3M.upload.maximum_size=1044464 + +phoenix_v1.menu.FlashSize.4M1M=4M (1M SPIFFS) +phoenix_v1.menu.FlashSize.4M1M.build.flash_size=4M +phoenix_v1.menu.FlashSize.4M1M.build.flash_ld=eagle.flash.4m1m.ld +phoenix_v1.menu.FlashSize.4M1M.build.spiffs_start=0x300000 +phoenix_v1.menu.FlashSize.4M1M.build.spiffs_end=0x3FB000 +phoenix_v1.menu.FlashSize.4M1M.build.spiffs_blocksize=8192 +phoenix_v1.menu.FlashSize.4M1M.build.spiffs_pagesize=256 +phoenix_v1.menu.FlashSize.4M1M.upload.maximum_size=1044464 + +phoenix_v1.menu.ResetMethod.nodemcu=nodemcu +phoenix_v1.menu.ResetMethod.nodemcu.upload.resetmethod=nodemcu +phoenix_v1.menu.ResetMethod.ck=ck +phoenix_v1.menu.ResetMethod.ck.upload.resetmethod=ck + +phoenix_v1.menu.Debug.Disabled=Disabled +phoenix_v1.menu.Debug.Disabled.build.debug_port= +phoenix_v1.menu.Debug.Serial=Serial +phoenix_v1.menu.Debug.Serial.build.debug_port=-DDEBUG_ESP_PORT=Serial +phoenix_v1.menu.Debug.Serial1=Serial1 +phoenix_v1.menu.Debug.Serial1.build.debug_port=-DDEBUG_ESP_PORT=Serial1 + +phoenix_v1.menu.DebugLevel.None____=None +phoenix_v1.menu.DebugLevel.None____.build.debug_level= +phoenix_v1.menu.DebugLevel.Core____=Core +phoenix_v1.menu.DebugLevel.Core____.build.debug_level=-DDEBUG_ESP_CORE +phoenix_v1.menu.DebugLevel.SSL_____=Core + SSL +phoenix_v1.menu.DebugLevel.SSL_____.build.debug_level=-DDEBUG_ESP_CORE -DDEBUG_ESP_SSL +phoenix_v1.menu.DebugLevel.SSL_MEM_=Core + SSL + TLS Mem +phoenix_v1.menu.DebugLevel.SSL_MEM_.build.debug_level=-DDEBUG_ESP_CORE -DDEBUG_ESP_SSL -DDEBUG_TLS_MEM +phoenix_v1.menu.DebugLevel.WiFic___=Core + WiFi +phoenix_v1.menu.DebugLevel.WiFic___.build.debug_level=-DDEBUG_ESP_CORE -DDEBUG_ESP_WIFI +phoenix_v1.menu.DebugLevel.WiFi____=WiFi +phoenix_v1.menu.DebugLevel.WiFi____.build.debug_level=-DDEBUG_ESP_WIFI +phoenix_v1.menu.DebugLevel.HTTPClient=HTTPClient +phoenix_v1.menu.DebugLevel.HTTPClient.build.debug_level=-DDEBUG_ESP_HTTP_CLIENT +phoenix_v1.menu.DebugLevel.HTTPClient2=HTTPClient + SSL +phoenix_v1.menu.DebugLevel.HTTPClient2.build.debug_level=-DDEBUG_ESP_HTTP_CLIENT -DDEBUG_ESP_SSL +phoenix_v1.menu.DebugLevel.HTTPUpdate=HTTPUpdate +phoenix_v1.menu.DebugLevel.HTTPUpdate.build.debug_level=-DDEBUG_ESP_HTTP_UPDATE +phoenix_v1.menu.DebugLevel.HTTPUpdate2=HTTPClient + HTTPUpdate +phoenix_v1.menu.DebugLevel.HTTPUpdate2.build.debug_level=-DDEBUG_ESP_HTTP_UPDATE -DDEBUG_ESP_HTTP_UPDATE +phoenix_v1.menu.DebugLevel.HTTPUpdate3=HTTPClient + HTTPUpdate + Updater +phoenix_v1.menu.DebugLevel.HTTPUpdate3.build.debug_level=-DDEBUG_ESP_HTTP_UPDATE -DDEBUG_ESP_HTTP_UPDATE -DDEBUG_ESP_UPDATER +phoenix_v1.menu.DebugLevel.HTTPServer=HTTPServer +phoenix_v1.menu.DebugLevel.HTTPServer.build.debug_level=-DDEBUG_ESP_HTTP_SERVER +phoenix_v1.menu.DebugLevel.UPDATER=Updater +phoenix_v1.menu.DebugLevel.UPDATER.build.debug_level=-DDEBUG_ESP_UPDATER +phoenix_v1.menu.DebugLevel.OTA_____=OTA +phoenix_v1.menu.DebugLevel.OTA_____.build.debug_level=-DDEBUG_ESP_OTA +phoenix_v1.menu.DebugLevel.OTA2____=OTA + Updater +phoenix_v1.menu.DebugLevel.OTA2____.build.debug_level=-DDEBUG_ESP_OTA -DDEBUG_ESP_UPDATER +phoenix_v1.menu.DebugLevel.all_____=All +phoenix_v1.menu.DebugLevel.all_____.build.debug_level=-DDEBUG_ESP_CORE -DDEBUG_ESP_SSL -DDEBUG_ESP_WIFI -DDEBUG_ESP_HTTP_CLIENT -DDEBUG_ESP_HTTP_UPDATE -DDEBUG_ESP_HTTP_SERVER -DDEBUG_ESP_UPDATER -DDEBUG_ESP_OTA -DDEBUG_TLS_MEM + +phoenix_v1.build.debug_port= +phoenix_v1.build.debug_level= + +############################################################## +phoenix_v2.name=Phoenix 2.0 +phoenix_v2.upload.tool=esptool +phoenix_v2.upload.speed=115200 +phoenix_v2.upload.maximum_size=1044464 +phoenix_v2.upload.maximum_data_size=81920 +phoenix_v2.upload.wait_for_upload_port=true + +phoenix_v2.build.mcu=esp8266 +phoenix_v2.build.f_cpu=80000000L +phoenix_v2.build.board=ESP8266_PHOENIX_V2 +phoenix_v2.build.core=esp8266 +phoenix_v2.build.variant=phoenix_v2 +phoenix_v2.build.flash_mode=dio +phoenix_v2.build.flash_size=4M +phoenix_v2.build.flash_freq=40 + +phoenix_v2.menu.CpuFrequency.80=80 MHz +phoenix_v2.menu.CpuFrequency.80.build.f_cpu=80000000L +phoenix_v2.menu.CpuFrequency.160=160 MHz +phoenix_v2.menu.CpuFrequency.160.build.f_cpu=160000000L + +phoenix_v2.menu.UploadSpeed.115200=115200 +phoenix_v2.menu.UploadSpeed.115200.upload.speed=115200 +phoenix_v2.menu.UploadSpeed.57600=57600 +phoenix_v2.menu.UploadSpeed.57600.upload.speed=57600 +phoenix_v2.menu.UploadSpeed.256000.windows=256000 +phoenix_v2.menu.UploadSpeed.256000.upload.speed=256000 +phoenix_v2.menu.UploadSpeed.230400.linux=230400 +phoenix_v2.menu.UploadSpeed.230400.macosx=230400 +phoenix_v2.menu.UploadSpeed.230400.macosx=230400 +phoenix_v2.menu.UploadSpeed.230400.upload.speed=230400 +phoenix_v2.menu.UploadSpeed.460800.linux=460800 +phoenix_v2.menu.UploadSpeed.460800.macosx=460800 +phoenix_v2.menu.UploadSpeed.460800.upload.speed=460800 +phoenix_v2.menu.UploadSpeed.512000.windows=512000 +phoenix_v2.menu.UploadSpeed.512000.upload.speed=512000 +phoenix_v2.menu.UploadSpeed.921600=921600 +phoenix_v2.menu.UploadSpeed.921600.upload.speed=921600 + +phoenix_v2.menu.FlashSize.4M3M=4M (3M SPIFFS) +phoenix_v2.menu.FlashSize.4M3M.build.flash_size=4M +phoenix_v2.menu.FlashSize.4M3M.build.flash_ld=eagle.flash.4m.ld +phoenix_v2.menu.FlashSize.4M3M.build.spiffs_start=0x100000 +phoenix_v2.menu.FlashSize.4M3M.build.spiffs_end=0x3FB000 +phoenix_v2.menu.FlashSize.4M3M.build.spiffs_blocksize=8192 +phoenix_v2.menu.FlashSize.4M3M.upload.maximum_size=1044464 + +phoenix_v2.menu.FlashSize.4M1M=4M (1M SPIFFS) +phoenix_v2.menu.FlashSize.4M1M.build.flash_size=4M +phoenix_v2.menu.FlashSize.4M1M.build.flash_ld=eagle.flash.4m1m.ld +phoenix_v2.menu.FlashSize.4M1M.build.spiffs_start=0x300000 +phoenix_v2.menu.FlashSize.4M1M.build.spiffs_end=0x3FB000 +phoenix_v2.menu.FlashSize.4M1M.build.spiffs_blocksize=8192 +phoenix_v2.menu.FlashSize.4M1M.build.spiffs_pagesize=256 +phoenix_v2.menu.FlashSize.4M1M.upload.maximum_size=1044464 + +phoenix_v2.menu.ResetMethod.ck=ck +phoenix_v2.menu.ResetMethod.ck.upload.resetmethod=ck +phoenix_v2.menu.ResetMethod.nodemcu=nodemcu +phoenix_v2.menu.ResetMethod.nodemcu.upload.resetmethod=nodemcu + +phoenix_v2.menu.Debug.Disabled=Disabled +phoenix_v2.menu.Debug.Disabled.build.debug_port= +phoenix_v2.menu.Debug.Serial=Serial +phoenix_v2.menu.Debug.Serial.build.debug_port=-DDEBUG_ESP_PORT=Serial +phoenix_v2.menu.Debug.Serial1=Serial1 +phoenix_v2.menu.Debug.Serial1.build.debug_port=-DDEBUG_ESP_PORT=Serial1 + +phoenix_v2.menu.DebugLevel.None____=None +phoenix_v2.menu.DebugLevel.None____.build.debug_level= +phoenix_v2.menu.DebugLevel.Core____=Core +phoenix_v2.menu.DebugLevel.Core____.build.debug_level=-DDEBUG_ESP_CORE +phoenix_v2.menu.DebugLevel.SSL_____=Core + SSL +phoenix_v2.menu.DebugLevel.SSL_____.build.debug_level=-DDEBUG_ESP_CORE -DDEBUG_ESP_SSL +phoenix_v2.menu.DebugLevel.SSL_MEM_=Core + SSL + TLS Mem +phoenix_v2.menu.DebugLevel.SSL_MEM_.build.debug_level=-DDEBUG_ESP_CORE -DDEBUG_ESP_SSL -DDEBUG_TLS_MEM +phoenix_v2.menu.DebugLevel.WiFic___=Core + WiFi +phoenix_v2.menu.DebugLevel.WiFic___.build.debug_level=-DDEBUG_ESP_CORE -DDEBUG_ESP_WIFI +phoenix_v2.menu.DebugLevel.WiFi____=WiFi +phoenix_v2.menu.DebugLevel.WiFi____.build.debug_level=-DDEBUG_ESP_WIFI +phoenix_v2.menu.DebugLevel.HTTPClient=HTTPClient +phoenix_v2.menu.DebugLevel.HTTPClient.build.debug_level=-DDEBUG_ESP_HTTP_CLIENT +phoenix_v2.menu.DebugLevel.HTTPClient2=HTTPClient + SSL +phoenix_v2.menu.DebugLevel.HTTPClient2.build.debug_level=-DDEBUG_ESP_HTTP_CLIENT -DDEBUG_ESP_SSL +phoenix_v2.menu.DebugLevel.HTTPUpdate=HTTPUpdate +phoenix_v2.menu.DebugLevel.HTTPUpdate.build.debug_level=-DDEBUG_ESP_HTTP_UPDATE +phoenix_v2.menu.DebugLevel.HTTPUpdate2=HTTPClient + HTTPUpdate +phoenix_v2.menu.DebugLevel.HTTPUpdate2.build.debug_level=-DDEBUG_ESP_HTTP_UPDATE -DDEBUG_ESP_HTTP_UPDATE +phoenix_v2.menu.DebugLevel.HTTPUpdate3=HTTPClient + HTTPUpdate + Updater +phoenix_v2.menu.DebugLevel.HTTPUpdate3.build.debug_level=-DDEBUG_ESP_HTTP_UPDATE -DDEBUG_ESP_HTTP_UPDATE -DDEBUG_ESP_UPDATER +phoenix_v2.menu.DebugLevel.HTTPServer=HTTPServer +phoenix_v2.menu.DebugLevel.HTTPServer.build.debug_level=-DDEBUG_ESP_HTTP_SERVER +phoenix_v2.menu.DebugLevel.UPDATER=Updater +phoenix_v2.menu.DebugLevel.UPDATER.build.debug_level=-DDEBUG_ESP_UPDATER +phoenix_v2.menu.DebugLevel.OTA_____=OTA +phoenix_v2.menu.DebugLevel.OTA_____.build.debug_level=-DDEBUG_ESP_OTA +phoenix_v2.menu.DebugLevel.OTA2____=OTA + Updater +phoenix_v2.menu.DebugLevel.OTA2____.build.debug_level=-DDEBUG_ESP_OTA -DDEBUG_ESP_UPDATER +phoenix_v2.menu.DebugLevel.all_____=All +phoenix_v2.menu.DebugLevel.all_____.build.debug_level=-DDEBUG_ESP_CORE -DDEBUG_ESP_SSL -DDEBUG_ESP_WIFI -DDEBUG_ESP_HTTP_CLIENT -DDEBUG_ESP_HTTP_UPDATE -DDEBUG_ESP_HTTP_SERVER -DDEBUG_ESP_UPDATER -DDEBUG_ESP_OTA -DDEBUG_TLS_MEM + +phoenix_v2.build.debug_port= +phoenix_v2.build.debug_level= + +############################################################## +nodemcu.name=NodeMCU 0.9 (ESP-12 Module) + +nodemcu.upload.tool=esptool +nodemcu.upload.speed=115200 +nodemcu.upload.resetmethod=nodemcu +nodemcu.upload.maximum_size=1044464 +nodemcu.upload.maximum_data_size=81920 +nodemcu.upload.wait_for_upload_port=true +nodemcu.serial.disableDTR=true +nodemcu.serial.disableRTS=true + +nodemcu.build.mcu=esp8266 +nodemcu.build.f_cpu=80000000L +nodemcu.build.board=ESP8266_NODEMCU +nodemcu.build.core=esp8266 +nodemcu.build.variant=nodemcu +nodemcu.build.flash_mode=qio +nodemcu.build.flash_size=4M +nodemcu.build.flash_freq=40 +nodemcu.build.debug_port= +nodemcu.build.debug_level= + +nodemcu.menu.CpuFrequency.80=80 MHz +nodemcu.menu.CpuFrequency.80.build.f_cpu=80000000L +nodemcu.menu.CpuFrequency.160=160 MHz +nodemcu.menu.CpuFrequency.160.build.f_cpu=160000000L + +nodemcu.menu.UploadTool.esptool=Serial +nodemcu.menu.UploadTool.esptool.upload.tool=esptool +nodemcu.menu.UploadTool.esptool.upload.verbose=-vv +nodemcu.menu.UploadTool.espupload=OTA_upload +nodemcu.menu.UploadTool.espupload.upload.tool=espupload + +nodemcu.menu.UploadSpeed.115200=115200 +nodemcu.menu.UploadSpeed.115200.upload.speed=115200 +nodemcu.menu.UploadSpeed.9600=9600 +nodemcu.menu.UploadSpeed.9600.upload.speed=9600 +nodemcu.menu.UploadSpeed.57600=57600 +nodemcu.menu.UploadSpeed.57600.upload.speed=57600 +nodemcu.menu.UploadSpeed.256000.windows=256000 +nodemcu.menu.UploadSpeed.256000.upload.speed=256000 +nodemcu.menu.UploadSpeed.230400.linux=230400 +nodemcu.menu.UploadSpeed.230400.macosx=230400 +nodemcu.menu.UploadSpeed.230400.macosx=230400 +nodemcu.menu.UploadSpeed.230400.upload.speed=230400 +nodemcu.menu.UploadSpeed.460800.linux=460800 +nodemcu.menu.UploadSpeed.460800.macosx=460800 +nodemcu.menu.UploadSpeed.460800.upload.speed=460800 +nodemcu.menu.UploadSpeed.512000.windows=512000 +nodemcu.menu.UploadSpeed.512000.upload.speed=512000 +nodemcu.menu.UploadSpeed.921600=921600 +nodemcu.menu.UploadSpeed.921600.upload.speed=921600 + +nodemcu.menu.FlashSize.4M3M=4M (3M SPIFFS) +nodemcu.menu.FlashSize.4M3M.build.flash_size=4M +nodemcu.menu.FlashSize.4M3M.build.flash_ld=eagle.flash.4m.ld +nodemcu.menu.FlashSize.4M3M.build.spiffs_start=0x100000 +nodemcu.menu.FlashSize.4M3M.build.spiffs_end=0x3FB000 +nodemcu.menu.FlashSize.4M3M.build.spiffs_blocksize=8192 +nodemcu.menu.FlashSize.4M3M.build.spiffs_pagesize=256 + +nodemcu.menu.FlashSize.4M1M=4M (1M SPIFFS) +nodemcu.menu.FlashSize.4M1M.build.flash_size=4M +nodemcu.menu.FlashSize.4M1M.build.flash_ld=eagle.flash.4m1m.ld +nodemcu.menu.FlashSize.4M1M.build.spiffs_start=0x300000 +nodemcu.menu.FlashSize.4M1M.build.spiffs_end=0x3FB000 +nodemcu.menu.FlashSize.4M1M.build.spiffs_blocksize=8192 +nodemcu.menu.FlashSize.4M1M.build.spiffs_pagesize=256 + +############################################################## +nodemcuv2.name=NodeMCU 1.0 (ESP-12E Module) + +nodemcuv2.upload.tool=esptool +nodemcuv2.upload.speed=115200 +nodemcuv2.upload.resetmethod=nodemcu +nodemcuv2.upload.maximum_size=1044464 +nodemcuv2.upload.maximum_data_size=81920 +nodemcuv2.upload.wait_for_upload_port=true +nodemcuv2.serial.disableDTR=true +nodemcuv2.serial.disableRTS=true + +nodemcuv2.build.mcu=esp8266 +nodemcuv2.build.f_cpu=80000000L +nodemcuv2.build.board=ESP8266_NODEMCU +nodemcuv2.build.core=esp8266 +nodemcuv2.build.variant=nodemcu +nodemcuv2.build.flash_mode=dio +nodemcuv2.build.flash_size=4M +nodemcuv2.build.flash_freq=40 +nodemcuv2.build.debug_port= +nodemcuv2.build.debug_level= + +nodemcuv2.menu.CpuFrequency.80=80 MHz +nodemcuv2.menu.CpuFrequency.80.build.f_cpu=80000000L +nodemcuv2.menu.CpuFrequency.160=160 MHz +nodemcuv2.menu.CpuFrequency.160.build.f_cpu=160000000L + +nodemcuv2.menu.UploadSpeed.115200=115200 +nodemcuv2.menu.UploadSpeed.115200.upload.speed=115200 +nodemcuv2.menu.UploadSpeed.9600=9600 +nodemcuv2.menu.UploadSpeed.9600.upload.speed=9600 +nodemcuv2.menu.UploadSpeed.57600=57600 +nodemcuv2.menu.UploadSpeed.57600.upload.speed=57600 +nodemcuv2.menu.UploadSpeed.256000.windows=256000 +nodemcuv2.menu.UploadSpeed.256000.upload.speed=256000 +nodemcuv2.menu.UploadSpeed.230400.linux=230400 +nodemcuv2.menu.UploadSpeed.230400.macosx=230400 +nodemcuv2.menu.UploadSpeed.230400.macosx=230400 +nodemcuv2.menu.UploadSpeed.230400.upload.speed=230400 +nodemcuv2.menu.UploadSpeed.460800.linux=460800 +nodemcuv2.menu.UploadSpeed.460800.macosx=460800 +nodemcuv2.menu.UploadSpeed.460800.upload.speed=460800 +nodemcuv2.menu.UploadSpeed.512000.windows=512000 +nodemcuv2.menu.UploadSpeed.512000.upload.speed=512000 +nodemcuv2.menu.UploadSpeed.921600=921600 +nodemcuv2.menu.UploadSpeed.921600.upload.speed=921600 + +nodemcuv2.menu.FlashSize.4M3M=4M (3M SPIFFS) +nodemcuv2.menu.FlashSize.4M3M.build.flash_size=4M +nodemcuv2.menu.FlashSize.4M3M.build.flash_ld=eagle.flash.4m.ld +nodemcuv2.menu.FlashSize.4M3M.build.spiffs_start=0x100000 +nodemcuv2.menu.FlashSize.4M3M.build.spiffs_end=0x3FB000 +nodemcuv2.menu.FlashSize.4M3M.build.spiffs_blocksize=8192 +nodemcuv2.menu.FlashSize.4M3M.build.spiffs_pagesize=256 + +nodemcuv2.menu.FlashSize.4M1M=4M (1M SPIFFS) +nodemcuv2.menu.FlashSize.4M1M.build.flash_size=4M +nodemcuv2.menu.FlashSize.4M1M.build.flash_ld=eagle.flash.4m1m.ld +nodemcuv2.menu.FlashSize.4M1M.build.spiffs_start=0x300000 +nodemcuv2.menu.FlashSize.4M1M.build.spiffs_end=0x3FB000 +nodemcuv2.menu.FlashSize.4M1M.build.spiffs_blocksize=8192 +nodemcuv2.menu.FlashSize.4M1M.build.spiffs_pagesize=256 + + +############################################################## +modwifi.name=Olimex MOD-WIFI-ESP8266(-DEV) + +modwifi.upload.tool=esptool +modwifi.upload.speed=115200 +modwifi.upload.resetmethod=ck +modwifi.upload.maximum_size=1044464 +modwifi.upload.maximum_data_size=81920 +modwifi.upload.wait_for_upload_port=true +modwifi.serial.disableDTR=true +modwifi.serial.disableRTS=true + +modwifi.build.mcu=esp8266 +modwifi.build.f_cpu=80000000L +modwifi.build.board=MOD_WIFI_ESP8266 +modwifi.build.core=esp8266 +modwifi.build.variant=generic +# Winbond W25Q16 flash +modwifi.build.flash_mode=qio +modwifi.build.flash_size=2M +modwifi.build.flash_freq=40 +modwifi.build.flash_ld=eagle.flash.2m.ld +modwifi.build.spiffs_start=0x100000 +modwifi.build.spiffs_end=0x1FB000 +modwifi.build.spiffs_pagesize=256 +modwifi.build.spiffs_blocksize=8192 +modwifi.build.debug_port= +modwifi.build.debug_level= + +modwifi.menu.CpuFrequency.80=80 MHz +modwifi.menu.CpuFrequency.80.build.f_cpu=80000000L +modwifi.menu.CpuFrequency.160=160 MHz +modwifi.menu.CpuFrequency.160.build.f_cpu=160000000L + +modwifi.menu.UploadSpeed.115200=115200 +modwifi.menu.UploadSpeed.115200.upload.speed=115200 +modwifi.menu.UploadSpeed.9600=9600 +modwifi.menu.UploadSpeed.9600.upload.speed=9600 +modwifi.menu.UploadSpeed.57600=57600 +modwifi.menu.UploadSpeed.57600.upload.speed=57600 +modwifi.menu.UploadSpeed.256000.windows=256000 +modwifi.menu.UploadSpeed.256000.upload.speed=256000 +modwifi.menu.UploadSpeed.230400.linux=230400 +modwifi.menu.UploadSpeed.230400.macosx=230400 +modwifi.menu.UploadSpeed.230400.macosx=230400 +modwifi.menu.UploadSpeed.230400.upload.speed=230400 +modwifi.menu.UploadSpeed.460800.linux=460800 +modwifi.menu.UploadSpeed.460800.macosx=460800 +modwifi.menu.UploadSpeed.460800.upload.speed=460800 +modwifi.menu.UploadSpeed.512000.windows=512000 +modwifi.menu.UploadSpeed.512000.upload.speed=512000 +modwifi.menu.UploadSpeed.921600=921600 +modwifi.menu.UploadSpeed.921600.upload.speed=921600 + +############################################################## +thing.name=SparkFun ESP8266 Thing + +thing.upload.tool=esptool +thing.upload.speed=921600 +thing.upload.resetmethod=ck +thing.upload.maximum_size=434160 +thing.upload.maximum_data_size=81920 +thing.upload.wait_for_upload_port=true +thing.serial.disableDTR=true +thing.serial.disableRTS=true + +thing.build.mcu=esp8266 +thing.build.f_cpu=80000000L +thing.build.board=ESP8266_THING +thing.build.core=esp8266 +thing.build.variant=thing +thing.build.flash_mode=qio +# flash chip: AT25SF041 (512 kbyte, 4Mbit) +thing.build.flash_size=512K +thing.build.flash_ld=eagle.flash.512k64.ld +thing.build.flash_freq=40 +thing.build.spiffs_start=0x6B000 +thing.build.spiffs_end=0x7B000 +thing.build.spiffs_blocksize=4096 +thing.build.spiffs_pagesize=256 +thing.build.debug_port= +thing.build.debug_level= + +thing.menu.CpuFrequency.80=80 MHz +thing.menu.CpuFrequency.80.build.f_cpu=80000000L +thing.menu.CpuFrequency.160=160 MHz +thing.menu.CpuFrequency.160.build.f_cpu=160000000L + +thing.menu.UploadSpeed.115200=115200 +thing.menu.UploadSpeed.115200.upload.speed=115200 +thing.menu.UploadSpeed.9600=9600 +thing.menu.UploadSpeed.9600.upload.speed=9600 +thing.menu.UploadSpeed.57600=57600 +thing.menu.UploadSpeed.57600.upload.speed=57600 +thing.menu.UploadSpeed.256000.windows=256000 +thing.menu.UploadSpeed.256000.upload.speed=256000 +thing.menu.UploadSpeed.230400.linux=230400 +thing.menu.UploadSpeed.230400.macosx=230400 +thing.menu.UploadSpeed.230400.upload.speed=230400 +thing.menu.UploadSpeed.460800.linux=460800 +thing.menu.UploadSpeed.460800.macosx=460800 +thing.menu.UploadSpeed.460800.upload.speed=460800 +thing.menu.UploadSpeed.512000.windows=512000 +thing.menu.UploadSpeed.512000.upload.speed=512000 +thing.menu.UploadSpeed.921600=921600 +thing.menu.UploadSpeed.921600.upload.speed=921600 + +############################################################## +thingdev.name=SparkFun ESP8266 Thing Dev + +thingdev.upload.tool=esptool +thingdev.upload.speed=921600 +thingdev.upload.resetmethod=nodemcu +thingdev.upload.maximum_size=434160 +thingdev.upload.maximum_data_size=81920 +thingdev.upload.wait_for_upload_port=true +thingdev.serial.disableDTR=true +thingdev.serial.disableRTS=true + +thingdev.build.mcu=esp8266 +thingdev.build.f_cpu=80000000L +thingdev.build.board=ESP8266_THING_DEV +thingdev.build.core=esp8266 +thingdev.build.variant=thing +thingdev.build.flash_mode=dio +# flash chip: AT25SF041 (512 kbyte, 4Mbit) +thingdev.build.flash_size=512K +thingdev.build.flash_ld=eagle.flash.512k64.ld +thingdev.build.flash_freq=40 +thingdev.build.debug_port= +thingdev.build.debug_level= + +thingdev.menu.CpuFrequency.80=80 MHz +thingdev.menu.CpuFrequency.80.build.f_cpu=80000000L +thingdev.menu.CpuFrequency.160=160 MHz +thingdev.menu.CpuFrequency.160.build.f_cpu=160000000L + +thingdev.menu.UploadSpeed.115200=115200 +thingdev.menu.UploadSpeed.115200.upload.speed=115200 +thingdev.menu.UploadSpeed.9600=9600 +thingdev.menu.UploadSpeed.9600.upload.speed=9600 +thingdev.menu.UploadSpeed.57600=57600 +thingdev.menu.UploadSpeed.57600.upload.speed=57600 +thingdev.menu.UploadSpeed.256000.windows=256000 +thingdev.menu.UploadSpeed.256000.upload.speed=256000 +thingdev.menu.UploadSpeed.230400.linux=230400 +thingdev.menu.UploadSpeed.230400.macosx=230400 +thingdev.menu.UploadSpeed.230400.upload.speed=230400 +thingdev.menu.UploadSpeed.460800.linux=460800 +thingdev.menu.UploadSpeed.460800.macosx=460800 +thingdev.menu.UploadSpeed.460800.upload.speed=460800 +thingdev.menu.UploadSpeed.512000.windows=512000 +thingdev.menu.UploadSpeed.512000.upload.speed=512000 +thingdev.menu.UploadSpeed.921600=921600 +thingdev.menu.UploadSpeed.921600.upload.speed=921600 + +############################################################## +esp210.name=SweetPea ESP-210 + +esp210.upload.tool=esptool +esp210.upload.speed=115200 +esp210.upload.resetmethod=ck +esp210.upload.maximum_size=1044464 +esp210.upload.maximum_data_size=81920 +esp210.upload.wait_for_upload_port=true +esp210.serial.disableDTR=true +esp210.serial.disableRTS=true + +esp210.build.mcu=esp8266 +esp210.build.f_cpu=80000000L +esp210.build.board=ESP8266_ESP210 +esp210.build.core=esp8266 +esp210.build.variant=generic +esp210.build.flash_mode=qio +esp210.build.flash_size=4M +esp210.build.flash_freq=40 +esp210.build.debug_port= +esp210.build.debug_level= + +esp210.menu.CpuFrequency.80=80 MHz +esp210.menu.CpuFrequency.80.build.f_cpu=80000000L +esp210.menu.CpuFrequency.160=160 MHz +esp210.menu.CpuFrequency.160.build.f_cpu=160000000L + +esp210.menu.UploadSpeed.57600=57600 +esp210.menu.UploadSpeed.57600.upload.speed=57600 +esp210.menu.UploadSpeed.115200=115200 +esp210.menu.UploadSpeed.115200.upload.speed=115200 +esp210.menu.UploadSpeed.256000.windows=256000 +esp210.menu.UploadSpeed.256000.upload.speed=256000 +esp210.menu.UploadSpeed.230400.linux=230400 +esp210.menu.UploadSpeed.230400.macosx=230400 +esp210.menu.UploadSpeed.230400.macosx=230400 +esp210.menu.UploadSpeed.230400.upload.speed=230400 +esp210.menu.UploadSpeed.460800.linux=460800 +esp210.menu.UploadSpeed.460800.macosx=460800 +esp210.menu.UploadSpeed.460800.upload.speed=460800 +esp210.menu.UploadSpeed.512000.windows=512000 +esp210.menu.UploadSpeed.512000.upload.speed=512000 +esp210.menu.UploadSpeed.921600=921600 +esp210.menu.UploadSpeed.921600.upload.speed=921600 + +esp210.menu.FlashSize.4M3M=4M (3M SPIFFS) +esp210.menu.FlashSize.4M3M.build.flash_size=4M +esp210.menu.FlashSize.4M3M.build.flash_ld=eagle.flash.4m.ld +esp210.menu.FlashSize.4M3M.build.spiffs_start=0x100000 +esp210.menu.FlashSize.4M3M.build.spiffs_end=0x3FB000 +esp210.menu.FlashSize.4M3M.build.spiffs_blocksize=8192 +esp210.menu.FlashSize.4M3M.build.spiffs_pagesize=256 + +esp210.menu.FlashSize.4M1M=4M (1M SPIFFS) +esp210.menu.FlashSize.4M1M.build.flash_size=4M +esp210.menu.FlashSize.4M1M.build.flash_ld=eagle.flash.4m1m.ld +esp210.menu.FlashSize.4M1M.build.spiffs_start=0x300000 +esp210.menu.FlashSize.4M1M.build.spiffs_end=0x3FB000 +esp210.menu.FlashSize.4M1M.build.spiffs_blocksize=8192 +esp210.menu.FlashSize.4M1M.build.spiffs_pagesize=256 + +############################################################## +# wifio.name=Wifio +# +# wifio.upload.tool=esptool +# wifio.upload.speed=115200 +# wifio.upload.resetmethod=wifio +# wifio.upload.maximum_size=524288 +# wifio.upload.wait_for_upload_port=true +# +# wifio.build.mcu=esp8266 +# wifio.build.f_cpu=80000000L +# wifio.build.board=ESP8266_WIFIO +# wifio.build.core=esp8266 +# wifio.build.variant=wifio +# wifio.build.flash_mode=qio +# wifio.build.flash_size=512K +# wifio.build.flash_freq=40 +# wifio.build.flash_ld=eagle.flash.512k64.ld +# wifio.build.spiffs_start=0x6B000 +# wifio.build.spiffs_end=0x7B000 +# +# wifio.menu.CpuFrequency.80=80MHz +# wifio.menu.CpuFrequency.80.build.f_cpu=80000000L +# wifio.menu.CpuFrequency.160=160MHz +# wifio.menu.CpuFrequency.160.build.f_cpu=160000000L +# +# wifio.upload.tool=esptool +# + +############################################################## +d1_mini.name=WeMos D1 R2 & mini + +d1_mini.upload.tool=esptool +d1_mini.upload.speed=460800 +d1_mini.upload.resetmethod=nodemcu +d1_mini.upload.maximum_size=1044464 +d1_mini.upload.maximum_data_size=81920 +d1_mini.upload.wait_for_upload_port=true +d1_mini.serial.disableDTR=true +d1_mini.serial.disableRTS=true + +d1_mini.build.mcu=esp8266 +d1_mini.build.f_cpu=80000000L +d1_mini.build.board=ESP8266_WEMOS_D1MINI +d1_mini.build.core=esp8266 +d1_mini.build.variant=d1_mini +d1_mini.build.flash_mode=dio +d1_mini.build.flash_size=4M +d1_mini.build.flash_freq=40 +d1_mini.build.debug_port= +d1_mini.build.debug_level= + +d1_mini.menu.UploadTool.esptool=Serial +d1_mini.menu.UploadTool.esptool.upload.tool=esptool +d1_mini.menu.UploadTool.esptool.upload.verbose=-vv +d1_mini.menu.UploadTool.espupload=OTA_upload +d1_mini.menu.UploadTool.espupload.upload.tool=espupload + +d1_mini.menu.CpuFrequency.80=80 MHz +d1_mini.menu.CpuFrequency.80.build.f_cpu=80000000L +d1_mini.menu.CpuFrequency.160=160 MHz +d1_mini.menu.CpuFrequency.160.build.f_cpu=160000000L + +d1_mini.menu.UploadSpeed.921600=921600 +d1_mini.menu.UploadSpeed.921600.upload.speed=921600 +d1_mini.menu.UploadSpeed.115200=115200 +d1_mini.menu.UploadSpeed.115200.upload.speed=115200 +d1_mini.menu.UploadSpeed.9600=9600 +d1_mini.menu.UploadSpeed.9600.upload.speed=9600 +d1_mini.menu.UploadSpeed.57600=57600 +d1_mini.menu.UploadSpeed.57600.upload.speed=57600 +d1_mini.menu.UploadSpeed.256000.windows=256000 +d1_mini.menu.UploadSpeed.256000.upload.speed=256000 +d1_mini.menu.UploadSpeed.230400.linux=230400 +d1_mini.menu.UploadSpeed.230400.macosx=230400 +d1_mini.menu.UploadSpeed.230400.macosx=230400 +d1_mini.menu.UploadSpeed.230400.upload.speed=230400 +d1_mini.menu.UploadSpeed.460800.linux=460800 +d1_mini.menu.UploadSpeed.460800.macosx=460800 +d1_mini.menu.UploadSpeed.460800.upload.speed=460800 +d1_mini.menu.UploadSpeed.512000.windows=512000 +d1_mini.menu.UploadSpeed.512000.upload.speed=512000 + + +d1_mini.menu.FlashSize.4M3M=4M (3M SPIFFS) +d1_mini.menu.FlashSize.4M3M.build.flash_size=4M +d1_mini.menu.FlashSize.4M3M.build.flash_ld=eagle.flash.4m.ld +d1_mini.menu.FlashSize.4M3M.build.spiffs_start=0x100000 +d1_mini.menu.FlashSize.4M3M.build.spiffs_end=0x3FB000 +d1_mini.menu.FlashSize.4M3M.build.spiffs_blocksize=8192 +d1_mini.menu.FlashSize.4M3M.build.spiffs_pagesize=256 + +d1_mini.menu.FlashSize.4M1M=4M (1M SPIFFS) +d1_mini.menu.FlashSize.4M1M.build.flash_size=4M +d1_mini.menu.FlashSize.4M1M.build.flash_ld=eagle.flash.4m1m.ld +d1_mini.menu.FlashSize.4M1M.build.spiffs_start=0x300000 +d1_mini.menu.FlashSize.4M1M.build.spiffs_end=0x3FB000 +d1_mini.menu.FlashSize.4M1M.build.spiffs_blocksize=8192 +d1_mini.menu.FlashSize.4M1M.build.spiffs_pagesize=256 + +############################################################## +d1.name=WeMos D1(Retired) + +d1.upload.tool=esptool +d1.upload.speed=460800 +d1.upload.resetmethod=nodemcu +d1.upload.maximum_size=1044464 +d1.upload.maximum_data_size=81920 +d1.upload.wait_for_upload_port=true +d1.serial.disableDTR=true +d1.serial.disableRTS=true + +d1.build.mcu=esp8266 +d1.build.f_cpu=80000000L +d1.build.board=ESP8266_WEMOS_D1MINI +d1.build.core=esp8266 +d1.build.variant=d1 +d1.build.flash_mode=dio +d1.build.flash_size=4M +d1.build.flash_freq=40 +d1.build.debug_port= +d1.build.debug_level= + +d1.menu.CpuFrequency.80=80 MHz +d1.menu.CpuFrequency.80.build.f_cpu=80000000L +d1.menu.CpuFrequency.160=160 MHz +d1.menu.CpuFrequency.160.build.f_cpu=160000000L + +d1.menu.UploadSpeed.921600=921600 +d1.menu.UploadSpeed.921600.upload.speed=921600 +d1.menu.UploadSpeed.115200=115200 +d1.menu.UploadSpeed.115200.upload.speed=115200 +d1.menu.UploadSpeed.9600=9600 +d1.menu.UploadSpeed.9600.upload.speed=9600 +d1.menu.UploadSpeed.57600=57600 +d1.menu.UploadSpeed.57600.upload.speed=57600 +d1.menu.UploadSpeed.256000.windows=256000 +d1.menu.UploadSpeed.256000.upload.speed=256000 +d1.menu.UploadSpeed.230400.linux=230400 +d1.menu.UploadSpeed.230400.macosx=230400 +d1.menu.UploadSpeed.230400.macosx=230400 +d1.menu.UploadSpeed.230400.upload.speed=230400 +d1.menu.UploadSpeed.460800.linux=460800 +d1.menu.UploadSpeed.460800.macosx=460800 +d1.menu.UploadSpeed.460800.upload.speed=460800 +d1.menu.UploadSpeed.512000.windows=512000 +d1.menu.UploadSpeed.512000.upload.speed=512000 + + +d1.menu.FlashSize.4M3M=4M (3M SPIFFS) +d1.menu.FlashSize.4M3M.build.flash_size=4M +d1.menu.FlashSize.4M3M.build.flash_ld=eagle.flash.4m.ld +d1.menu.FlashSize.4M3M.build.spiffs_start=0x100000 +d1.menu.FlashSize.4M3M.build.spiffs_end=0x3FB000 +d1.menu.FlashSize.4M3M.build.spiffs_blocksize=8192 +d1.menu.FlashSize.4M3M.build.spiffs_pagesize=256 + +d1.menu.FlashSize.4M1M=4M (1M SPIFFS) +d1.menu.FlashSize.4M1M.build.flash_size=4M +d1.menu.FlashSize.4M1M.build.flash_ld=eagle.flash.4m1m.ld +d1.menu.FlashSize.4M1M.build.spiffs_start=0x300000 +d1.menu.FlashSize.4M1M.build.spiffs_end=0x3FB000 +d1.menu.FlashSize.4M1M.build.spiffs_blocksize=8192 +d1.menu.FlashSize.4M1M.build.spiffs_pagesize=256 + + +############################################################## + +espino.name=ESPino (ESP-12 Module) + +espino.upload.tool=esptool +espino.upload.speed=115200 +espino.upload.resetmethod=ck +espino.upload.maximum_size=1044464 +espino.upload.maximum_data_size=81920 +espino.upload.wait_for_upload_port=true +espino.serial.disableDTR=true +espino.serial.disableRTS=true + +espino.build.mcu=esp8266 +espino.build.f_cpu=80000000L +espino.build.board=ESP8266_ESP12 +espino.build.core=esp8266 +espino.build.variant=espino +espino.build.flash_mode=qio +espino.build.flash_size=4M +espino.build.flash_freq=40 +espino.build.spiffs_pagesize=256 +espino.build.debug_port= +espino.build.debug_level= + +espino.menu.CpuFrequency.80=80 MHz +espino.menu.CpuFrequency.80.build.f_cpu=80000000L +espino.menu.CpuFrequency.160=160 MHz +espino.menu.CpuFrequency.160.build.f_cpu=160000000L + +espino.menu.FlashMode.dio=DIO +espino.menu.FlashMode.dio.build.flash_mode=dio +espino.menu.FlashMode.qio=QIO +espino.menu.FlashMode.qio.build.flash_mode=qio + +espino.menu.UploadSpeed.115200=115200 +espino.menu.UploadSpeed.115200.upload.speed=115200 +espino.menu.UploadSpeed.9600=9600 +espino.menu.UploadSpeed.9600.upload.speed=9600 +espino.menu.UploadSpeed.57600=57600 +espino.menu.UploadSpeed.57600.upload.speed=57600 +espino.menu.UploadSpeed.256000.windows=256000 +espino.menu.UploadSpeed.256000.upload.speed=256000 +espino.menu.UploadSpeed.230400.linux=230400 +espino.menu.UploadSpeed.230400.macosx=230400 +espino.menu.UploadSpeed.230400.upload.speed=230400 +espino.menu.UploadSpeed.460800.linux=460800 +espino.menu.UploadSpeed.460800.macosx=460800 +espino.menu.UploadSpeed.460800.upload.speed=460800 +espino.menu.UploadSpeed.512000.windows=512000 +espino.menu.UploadSpeed.512000.upload.speed=512000 +espino.menu.UploadSpeed.921600=921600 +espino.menu.UploadSpeed.921600.upload.speed=921600 + +espino.menu.FlashSize.4M1M=4M (1M SPIFFS) +espino.menu.FlashSize.4M1M.build.flash_size=4M +espino.menu.FlashSize.4M1M.build.flash_ld=eagle.flash.4m1m.ld +espino.menu.FlashSize.4M1M.build.spiffs_start=0x300000 +espino.menu.FlashSize.4M1M.build.spiffs_end=0x3FB000 +espino.menu.FlashSize.4M1M.build.spiffs_blocksize=8192 +espino.menu.FlashSize.4M1M.build.spiffs_pagesize=256 +espino.menu.FlashSize.4M1M.upload.maximum_size=1044464 + +espino.menu.FlashSize.4M3M=4M (3M SPIFFS) +espino.menu.FlashSize.4M3M.build.flash_size=4M +espino.menu.FlashSize.4M3M.build.flash_ld=eagle.flash.4m.ld +espino.menu.FlashSize.4M3M.build.spiffs_start=0x100000 +espino.menu.FlashSize.4M3M.build.spiffs_end=0x3FB000 +espino.menu.FlashSize.4M3M.build.spiffs_blocksize=8192 +espino.menu.FlashSize.4M3M.upload.maximum_size=1044464 + +espino.menu.ResetMethod.ck=ck +espino.menu.ResetMethod.ck.upload.resetmethod=ck +espino.menu.ResetMethod.nodemcu=nodemcu +espino.menu.ResetMethod.nodemcu.upload.resetmethod=nodemcu + +############################################################## +espinotee.name=ThaiEasyElec's ESPino + +espinotee.upload.tool=esptool +espinotee.upload.speed=115200 +espinotee.upload.resetmethod=nodemcu +espinotee.upload.maximum_size=1044464 +espinotee.upload.maximum_data_size=81920 +espinotee.upload.wait_for_upload_port=true +espinotee.serial.disableDTR=true +espinotee.serial.disableRTS=true + +espinotee.build.mcu=esp8266 +espinotee.build.f_cpu=80000000L +espinotee.build.board=ESP8266_ESP13 +espinotee.build.core=esp8266 +espinotee.build.variant=espinotee +espinotee.build.flash_mode=qio +espinotee.build.flash_size=4M +espinotee.build.flash_freq=40 +espinotee.build.debug_port= +espinotee.build.debug_level= + +espinotee.menu.CpuFrequency.80=80 MHz +espinotee.menu.CpuFrequency.80.build.f_cpu=80000000L +espinotee.menu.CpuFrequency.160=160 MHz +espinotee.menu.CpuFrequency.160.build.f_cpu=160000000L + +espinotee.menu.UploadSpeed.115200=115200 +espinotee.menu.UploadSpeed.115200.upload.speed=115200 +espinotee.menu.UploadSpeed.9600=9600 +espinotee.menu.UploadSpeed.9600.upload.speed=9600 +espinotee.menu.UploadSpeed.57600=57600 +espinotee.menu.UploadSpeed.57600.upload.speed=57600 +espinotee.menu.UploadSpeed.256000.windows=256000 +espinotee.menu.UploadSpeed.256000.upload.speed=256000 +espinotee.menu.UploadSpeed.230400.linux=230400 +espinotee.menu.UploadSpeed.230400.macosx=230400 +espinotee.menu.UploadSpeed.230400.macosx=230400 +espinotee.menu.UploadSpeed.230400.upload.speed=230400 +espinotee.menu.UploadSpeed.460800.linux=460800 +espinotee.menu.UploadSpeed.460800.macosx=460800 +espinotee.menu.UploadSpeed.460800.upload.speed=460800 +espinotee.menu.UploadSpeed.512000.windows=512000 +espinotee.menu.UploadSpeed.512000.upload.speed=512000 +espinotee.menu.UploadSpeed.921600=921600 +espinotee.menu.UploadSpeed.921600.upload.speed=921600 + +espinotee.menu.FlashSize.4M3M=4M (3M SPIFFS) +espinotee.menu.FlashSize.4M3M.build.flash_size=4M +espinotee.menu.FlashSize.4M3M.build.flash_ld=eagle.flash.4m.ld +espinotee.menu.FlashSize.4M3M.build.spiffs_start=0x100000 +espinotee.menu.FlashSize.4M3M.build.spiffs_end=0x3FB000 +espinotee.menu.FlashSize.4M3M.build.spiffs_blocksize=8192 +espinotee.menu.FlashSize.4M3M.build.spiffs_pagesize=256 + +espinotee.menu.FlashSize.4M1M=4M (1M SPIFFS) +espinotee.menu.FlashSize.4M1M.build.flash_size=4M +espinotee.menu.FlashSize.4M1M.build.flash_ld=eagle.flash.4m1m.ld +espinotee.menu.FlashSize.4M1M.build.spiffs_start=0x300000 +espinotee.menu.FlashSize.4M1M.build.spiffs_end=0x3FB000 +espinotee.menu.FlashSize.4M1M.build.spiffs_blocksize=8192 +espinotee.menu.FlashSize.4M1M.build.spiffs_pagesize=256 + +############################################################## +wifinfo.name=WifInfo + +wifinfo.upload.tool=esptool +wifinfo.upload.speed=115200 +wifinfo.upload.resetmethod=nodemcu +wifinfo.upload.maximum_size=434160 +wifinfo.upload.maximum_data_size=81920 +wifinfo.upload.wait_for_upload_port=true +wifinfo.serial.disableDTR=true +wifinfo.serial.disableRTS=true + +wifinfo.build.mcu=esp8266 +wifinfo.build.core=esp8266 +wifinfo.build.variant=wifinfo +wifinfo.build.board=WIFINFO +wifinfo.build.spiffs_pagesize=256 +wifinfo.build.debug_port=Serial1 +wifinfo.build.debug_level=Wifinfo + +wifinfo.menu.Debug.Disabled=Disabled +wifinfo.menu.Debug.Disabled.build.debug_port= +wifinfo.menu.Debug.Serial=Serial +wifinfo.menu.Debug.Serial.build.debug_port=-DDEBUG_ESP_PORT=Serial +wifinfo.menu.Debug.Serial1=Serial1 +wifinfo.menu.Debug.Serial1.build.debug_port=-DDEBUG_ESP_PORT=Serial1 + +wifinfo.menu.DebugLevel.None=None +wifinfo.menu.DebugLevel.None.build.debug_level= +wifinfo.menu.DebugLevel.Wifinfo=Wifinfo +wifinfo.menu.DebugLevel.Wifinfo.build.debug_level=-DDEBUG_ESP_WIFINFO + +#wifinfo.menu.ESPModule.ESP07512=ESP07 (1M/512K SPIFFS) +#wifinfo.menu.ESPModule.ESP07512.build.board=ESP8266_ESP07 +#wifinfo.menu.ESPModule.ESP07512.build.flash_size=1M +#wifinfo.menu.ESPModule.ESP07512.build.flash_ld=eagle.flash.1m512.ld +#wifinfo.menu.ESPModule.ESP07512.build.spiffs_start=0x7B000 +#wifinfo.menu.ESPModule.ESP07512.build.spiffs_end=0xFB000 +#wifinfo.menu.ESPModule.ESP07512.build.spiffs_blocksize=8192 +#wifinfo.menu.ESPModule.ESP07512.upload.maximum_size=499696 + +#wifinfo.menu.ESPModule.ESP07256=ESP07 (1M/256K SPIFFS) +#wifinfo.menu.ESPModule.ESP07256.build.board=ESP8266_ESP07 +#wifinfo.menu.ESPModule.ESP07256.build.flash_size=1M +#wifinfo.menu.ESPModule.ESP07256.build.flash_ld=eagle.flash.1m256.ld +#wifinfo.menu.ESPModule.ESP07256.build.spiffs_start=0xBB000 +#wifinfo.menu.ESPModule.ESP07256.build.spiffs_end=0xFB000 +##wifinfo.menu.ESPModule.ESP07256.build.spiffs_blocksize=4096 +#wifinfo.menu.ESPModule.ESP07256.upload.maximum_size=761840 + +wifinfo.menu.ESPModule.ESP07192=ESP07 (1M/192K SPIFFS) +wifinfo.menu.ESPModule.ESP07192.build.board=ESP8266_ESP07 +wifinfo.menu.ESPModule.ESP07192.build.flash_size=1M +wifinfo.menu.ESPModule.ESP07192.build.flash_ld=eagle.flash.1m192.ld +wifinfo.menu.ESPModule.ESP07192.build.spiffs_start=0xCB000 +wifinfo.menu.ESPModule.ESP07192.build.spiffs_end=0xFB000 +wifinfo.menu.ESPModule.ESP07192.build.spiffs_blocksize=4096 +wifinfo.menu.ESPModule.ESP07192.upload.maximum_size=827376 + +#wifinfo.menu.ESPModule.ESP07160=ESP07 (1M/160K SPIFFS) +#wifinfo.menu.ESPModule.ESP07160.build.board=ESP8266_ESP07 +#wifinfo.menu.ESPModule.ESP07160.build.flash_size=1M +#wifinfo.menu.ESPModule.ESP07160.build.flash_ld=eagle.flash.1m160.ld +#wifinfo.menu.ESPModule.ESP07160.build.spiffs_start=0xD3000 +#wifinfo.menu.ESPModule.ESP07160.build.spiffs_end=0xFB000 +#wifinfo.menu.ESPModule.ESP07160.build.spiffs_blocksize=4096 +#wifinfo.menu.ESPModule.ESP07160.upload.maximum_size=860144 +# +#wifinfo.menu.ESPModule.ESP07144=ESP07 (1M/144K SPIFFS) +#wifinfo.menu.ESPModule.ESP07144.build.board=ESP8266_ESP07 +#wifinfo.menu.ESPModule.ESP07144.build.flash_size=1M +#wifinfo.menu.ESPModule.ESP07144.build.flash_ld=eagle.flash.1m144.ld +#wifinfo.menu.ESPModule.ESP07144.build.spiffs_start=0xD7000 +#wifinfo.menu.ESPModule.ESP07144.build.spiffs_end=0xFB000 +#wifinfo.menu.ESPModule.ESP07144.build.spiffs_blocksize=4096 +#wifinfo.menu.ESPModule.ESP07144.upload.maximum_size=876528 +# +#wifinfo.menu.ESPModule.ESP07=ESP07 (1M/64K SPIFFS) +#wifinfo.menu.ESPModule.ESP07.build.board=ESP8266_ESP07 +#wifinfo.menu.ESPModule.ESP07.build.flash_size=1M +#wifinfo.menu.ESPModule.ESP07.build.flash_ld=eagle.flash.1m64.ld +#wifinfo.menu.ESPModule.ESP07.build.spiffs_start=0xEB000 +#wifinfo.menu.ESPModule.ESP07.build.spiffs_end=0xFB000 +#wifinfo.menu.ESPModule.ESP07.build.spiffs_blocksize=4096 +#wifinfo.menu.ESPModule.ESP07.upload.maximum_size=958448 + +wifinfo.menu.ESPModule.ESP12=ESP12 (4M/1M SPIFFS) +wifinfo.menu.ESPModule.ESP12.build.board=ESP8266_ESP12 +wifinfo.menu.ESPModule.ESP12.build.flash_size=4M +wifinfo.menu.ESPModule.ESP12.build.flash_ld=eagle.flash.4m1m.ld +wifinfo.menu.ESPModule.ESP12.build.spiffs_start=0x300000 +wifinfo.menu.ESPModule.ESP12.build.spiffs_end=0x3FB000 +wifinfo.menu.ESPModule.ESP12.build.spiffs_blocksize=8192 +wifinfo.menu.ESPModule.ESP12.build.spiffs_pagesize=256 +wifinfo.menu.ESPModule.ESP12.upload.maximum_size=1044464 + +wifinfo.menu.CpuFrequency.160=160 MHz +wifinfo.menu.CpuFrequency.160.build.f_cpu=160000000L +wifinfo.menu.CpuFrequency.80=80 MHz +wifinfo.menu.CpuFrequency.80.build.f_cpu=80000000L + +wifinfo.menu.FlashFreq.40=40MHz +wifinfo.menu.FlashFreq.40.build.flash_freq=40 +wifinfo.menu.FlashFreq.80=80MHz +wifinfo.menu.FlashFreq.80.build.flash_freq=80 + +wifinfo.menu.FlashMode.qio=QIO +wifinfo.menu.FlashMode.qio.build.flash_mode=qio +wifinfo.menu.FlashMode.dio=DIO +wifinfo.menu.FlashMode.dio.build.flash_mode=dio + +wifinfo.menu.UploadSpeed.115200=115200 +wifinfo.menu.UploadSpeed.115200.upload.speed=115200 +wifinfo.menu.UploadSpeed.9600=9600 +wifinfo.menu.UploadSpeed.9600.upload.speed=9600 +wifinfo.menu.UploadSpeed.57600=57600 +wifinfo.menu.UploadSpeed.57600.upload.speed=57600 +wifinfo.menu.UploadSpeed.256000.windows=256000 +wifinfo.menu.UploadSpeed.256000.upload.speed=256000 +wifinfo.menu.UploadSpeed.230400.linux=230400 +wifinfo.menu.UploadSpeed.230400.macosx=230400 +wifinfo.menu.UploadSpeed.230400.upload.speed=230400 +wifinfo.menu.UploadSpeed.460800.linux=460800 +wifinfo.menu.UploadSpeed.460800.macosx=460800 +wifinfo.menu.UploadSpeed.460800.upload.speed=460800 +wifinfo.menu.UploadSpeed.512000.windows=512000 +wifinfo.menu.UploadSpeed.512000.upload.speed=512000 +wifinfo.menu.UploadSpeed.921600=921600 +wifinfo.menu.UploadSpeed.921600.upload.speed=921600 + + +############################################################## +coredev.name=Core Development Module + +coredev.upload.tool=esptool +coredev.upload.speed=115200 +coredev.upload.resetmethod=ck +coredev.upload.maximum_size=434160 +coredev.upload.maximum_data_size=81920 +coredev.upload.wait_for_upload_port=true +coredev.serial.disableDTR=true +coredev.serial.disableRTS=true + +coredev.build.mcu=esp8266 +coredev.build.f_cpu=80000000L +coredev.build.board=ESP8266_ESP01 +coredev.build.core=esp8266 +coredev.build.variant=generic +coredev.build.flash_mode=qio +coredev.build.spiffs_pagesize=256 +coredev.build.debug_port= +coredev.build.debug_level= +coredev.build.lwip_lib=-llwip +coredev.build.lwip_flags= + + +coredev.menu.LwIPVariant.Espressif=Espressif (xcc) +coredev.menu.LwIPVariant.Espressif.build.lwip_lib=-llwip +coredev.menu.LwIPVariant.Espressif.build.lwip_flags= +coredev.menu.LwIPVariant.Prebuilt=Prebuilt Source (gcc) +coredev.menu.LwIPVariant.Prebuilt.build.lwip_lib=-llwip_gcc +coredev.menu.LwIPVariant.Prebuilt.build.lwip_flags=-DLWIP_OPEN_SRC +coredev.menu.LwIPVariant.OpenSource=Open Source (gcc) +coredev.menu.LwIPVariant.OpenSource.build.lwip_lib=-llwip_src +coredev.menu.LwIPVariant.OpenSource.build.lwip_flags=-DLWIP_OPEN_SRC +coredev.menu.LwIPVariant.OpenSource.recipe.hooks.sketch.prebuild.1.pattern=make -C "{runtime.platform.path}/tools/sdk/lwip/src" install TOOLS_PATH="{runtime.tools.xtensa-lx106-elf-gcc.path}/bin/xtensa-lx106-elf-" + +coredev.menu.CpuFrequency.80=80 MHz +coredev.menu.CpuFrequency.80.build.f_cpu=80000000L +coredev.menu.CpuFrequency.160=160 MHz +coredev.menu.CpuFrequency.160.build.f_cpu=160000000L + +coredev.menu.FlashFreq.40=40MHz +coredev.menu.FlashFreq.40.build.flash_freq=40 +coredev.menu.FlashFreq.80=80MHz +coredev.menu.FlashFreq.80.build.flash_freq=80 + +coredev.menu.FlashMode.dio=DIO +coredev.menu.FlashMode.dio.build.flash_mode=dio +coredev.menu.FlashMode.qio=QIO +coredev.menu.FlashMode.qio.build.flash_mode=qio +coredev.menu.FlashMode.dout=DOUT +coredev.menu.FlashMode.dout.build.flash_mode=dout +coredev.menu.FlashMode.qout=QOUT +coredev.menu.FlashMode.qout.build.flash_mode=qout + +coredev.menu.UploadSpeed.115200=115200 +coredev.menu.UploadSpeed.115200.upload.speed=115200 +coredev.menu.UploadSpeed.9600=9600 +coredev.menu.UploadSpeed.9600.upload.speed=9600 +coredev.menu.UploadSpeed.57600=57600 +coredev.menu.UploadSpeed.57600.upload.speed=57600 +coredev.menu.UploadSpeed.256000.windows=256000 +coredev.menu.UploadSpeed.256000.upload.speed=256000 +coredev.menu.UploadSpeed.230400.linux=230400 +coredev.menu.UploadSpeed.230400.macosx=230400 +coredev.menu.UploadSpeed.230400.upload.speed=230400 +coredev.menu.UploadSpeed.460800.linux=460800 +coredev.menu.UploadSpeed.460800.macosx=460800 +coredev.menu.UploadSpeed.460800.upload.speed=460800 +coredev.menu.UploadSpeed.512000.windows=512000 +coredev.menu.UploadSpeed.512000.upload.speed=512000 +coredev.menu.UploadSpeed.921600=921600 +coredev.menu.UploadSpeed.921600.upload.speed=921600 + +coredev.menu.FlashSize.512K64=512K (64K SPIFFS) +coredev.menu.FlashSize.512K64.build.flash_size=512K +coredev.menu.FlashSize.512K64.build.flash_ld=eagle.flash.512k64.ld +coredev.menu.FlashSize.512K64.build.spiffs_start=0x6B000 +coredev.menu.FlashSize.512K64.build.spiffs_end=0x7B000 +coredev.menu.FlashSize.512K64.build.spiffs_blocksize=4096 +coredev.menu.FlashSize.512K64.upload.maximum_size=434160 + +coredev.menu.FlashSize.512K128=512K (128K SPIFFS) +coredev.menu.FlashSize.512K128.build.flash_size=512K +coredev.menu.FlashSize.512K128.build.flash_ld=eagle.flash.512k128.ld +coredev.menu.FlashSize.512K128.build.spiffs_start=0x5B000 +coredev.menu.FlashSize.512K128.build.spiffs_end=0x7B000 +coredev.menu.FlashSize.512K128.build.spiffs_blocksize=4096 +coredev.menu.FlashSize.512K128.upload.maximum_size=368624 + +coredev.menu.FlashSize.512K0=512K (no SPIFFS) +coredev.menu.FlashSize.512K0.build.flash_size=512K +coredev.menu.FlashSize.512K0.build.flash_ld=eagle.flash.512k0.ld +coredev.menu.FlashSize.512K0.upload.maximum_size=499696 + +coredev.menu.FlashSize.1M512=1M (512K SPIFFS) +coredev.menu.FlashSize.1M512.build.flash_size=1M +coredev.menu.FlashSize.1M512.build.flash_ld=eagle.flash.1m512.ld +coredev.menu.FlashSize.1M512.build.spiffs_start=0x7B000 +coredev.menu.FlashSize.1M512.build.spiffs_end=0xFB000 +coredev.menu.FlashSize.1M512.build.spiffs_blocksize=8192 +coredev.menu.FlashSize.1M512.upload.maximum_size=499696 + +coredev.menu.FlashSize.1M256=1M (256K SPIFFS) +coredev.menu.FlashSize.1M256.build.flash_size=1M +coredev.menu.FlashSize.1M256.build.flash_ld=eagle.flash.1m256.ld +coredev.menu.FlashSize.1M256.build.spiffs_start=0xBB000 +coredev.menu.FlashSize.1M256.build.spiffs_end=0xFB000 +coredev.menu.FlashSize.1M256.build.spiffs_blocksize=4096 +coredev.menu.FlashSize.1M256.upload.maximum_size=761840 + +coredev.menu.FlashSize.1M192=1M (192K SPIFFS) +coredev.menu.FlashSize.1M192.build.flash_size=1M +coredev.menu.FlashSize.1M192.build.flash_ld=eagle.flash.1m192.ld +coredev.menu.FlashSize.1M192.build.spiffs_start=0xCB000 +coredev.menu.FlashSize.1M192.build.spiffs_end=0xFB000 +coredev.menu.FlashSize.1M192.build.spiffs_blocksize=4096 +coredev.menu.FlashSize.1M192.upload.maximum_size=827376 + +coredev.menu.FlashSize.1M160=1M (160K SPIFFS) +coredev.menu.FlashSize.1M160.build.flash_size=1M +coredev.menu.FlashSize.1M160.build.flash_ld=eagle.flash.1m160.ld +coredev.menu.FlashSize.1M160.build.spiffs_start=0xD3000 +coredev.menu.FlashSize.1M160.build.spiffs_end=0xFB000 +coredev.menu.FlashSize.1M160.build.spiffs_blocksize=4096 +coredev.menu.FlashSize.1M160.upload.maximum_size=860144 + +coredev.menu.FlashSize.1M144=1M (144K SPIFFS) +coredev.menu.FlashSize.1M144.build.flash_size=1M +coredev.menu.FlashSize.1M144.build.flash_ld=eagle.flash.1m144.ld +coredev.menu.FlashSize.1M144.build.spiffs_start=0xD7000 +coredev.menu.FlashSize.1M144.build.spiffs_end=0xFB000 +coredev.menu.FlashSize.1M144.build.spiffs_blocksize=4096 +coredev.menu.FlashSize.1M144.upload.maximum_size=876528 + +coredev.menu.FlashSize.1M128=1M (128K SPIFFS) +coredev.menu.FlashSize.1M128.build.flash_size=1M +coredev.menu.FlashSize.1M128.build.flash_ld=eagle.flash.1m128.ld +coredev.menu.FlashSize.1M128.build.spiffs_start=0xDB000 +coredev.menu.FlashSize.1M128.build.spiffs_end=0xFB000 +coredev.menu.FlashSize.1M128.build.spiffs_blocksize=4096 +coredev.menu.FlashSize.1M128.upload.maximum_size=892912 + +coredev.menu.FlashSize.1M64=1M (64K SPIFFS) +coredev.menu.FlashSize.1M64.build.flash_size=1M +coredev.menu.FlashSize.1M64.build.flash_ld=eagle.flash.1m64.ld +coredev.menu.FlashSize.1M64.build.spiffs_start=0xEB000 +coredev.menu.FlashSize.1M64.build.spiffs_end=0xFB000 +coredev.menu.FlashSize.1M64.build.spiffs_blocksize=4096 +coredev.menu.FlashSize.1M64.upload.maximum_size=958448 + +coredev.menu.FlashSize.2M=2M (1M SPIFFS) +coredev.menu.FlashSize.2M.build.flash_size=2M +coredev.menu.FlashSize.2M.build.flash_ld=eagle.flash.2m.ld +coredev.menu.FlashSize.2M.build.spiffs_start=0x100000 +coredev.menu.FlashSize.2M.build.spiffs_end=0x1FB000 +coredev.menu.FlashSize.2M.build.spiffs_blocksize=8192 +coredev.menu.FlashSize.2M.upload.maximum_size=1044464 + +coredev.menu.FlashSize.4M1M=4M (1M SPIFFS) +coredev.menu.FlashSize.4M1M.build.flash_size=4M +coredev.menu.FlashSize.4M1M.build.flash_ld=eagle.flash.4m1m.ld +coredev.menu.FlashSize.4M1M.build.spiffs_start=0x300000 +coredev.menu.FlashSize.4M1M.build.spiffs_end=0x3FB000 +coredev.menu.FlashSize.4M1M.build.spiffs_blocksize=8192 +coredev.menu.FlashSize.4M1M.build.spiffs_pagesize=256 +coredev.menu.FlashSize.4M1M.upload.maximum_size=1044464 + +coredev.menu.FlashSize.4M3M=4M (3M SPIFFS) +coredev.menu.FlashSize.4M3M.build.flash_size=4M +coredev.menu.FlashSize.4M3M.build.flash_ld=eagle.flash.4m.ld +coredev.menu.FlashSize.4M3M.build.spiffs_start=0x100000 +coredev.menu.FlashSize.4M3M.build.spiffs_end=0x3FB000 +coredev.menu.FlashSize.4M3M.build.spiffs_blocksize=8192 +coredev.menu.FlashSize.4M3M.upload.maximum_size=1044464 + +coredev.menu.ResetMethod.ck=ck +coredev.menu.ResetMethod.ck.upload.resetmethod=ck +coredev.menu.ResetMethod.nodemcu=nodemcu +coredev.menu.ResetMethod.nodemcu.upload.resetmethod=nodemcu + +coredev.menu.Debug.Disabled=Disabled +coredev.menu.Debug.Disabled.build.debug_port= +coredev.menu.Debug.Serial=Serial +coredev.menu.Debug.Serial.build.debug_port=-DDEBUG_ESP_PORT=Serial +coredev.menu.Debug.Serial1=Serial1 +coredev.menu.Debug.Serial1.build.debug_port=-DDEBUG_ESP_PORT=Serial1 + +coredev.menu.DebugLevel.None____=None +coredev.menu.DebugLevel.None____.build.debug_level= +coredev.menu.DebugLevel.Core____=Core +coredev.menu.DebugLevel.Core____.build.debug_level=-DDEBUG_ESP_CORE +coredev.menu.DebugLevel.SSL_____=Core + SSL +coredev.menu.DebugLevel.SSL_____.build.debug_level=-DDEBUG_ESP_CORE -DDEBUG_ESP_SSL +coredev.menu.DebugLevel.SSL_MEM_=Core + SSL + TLS Mem +coredev.menu.DebugLevel.SSL_MEM_.build.debug_level=-DDEBUG_ESP_CORE -DDEBUG_ESP_SSL -DDEBUG_TLS_MEM +coredev.menu.DebugLevel.WiFic___=Core + WiFi +coredev.menu.DebugLevel.WiFic___.build.debug_level=-DDEBUG_ESP_CORE -DDEBUG_ESP_WIFI +coredev.menu.DebugLevel.WiFi____=WiFi +coredev.menu.DebugLevel.WiFi____.build.debug_level=-DDEBUG_ESP_WIFI +coredev.menu.DebugLevel.HTTPClient=HTTPClient +coredev.menu.DebugLevel.HTTPClient.build.debug_level=-DDEBUG_ESP_HTTP_CLIENT +coredev.menu.DebugLevel.HTTPClient2=HTTPClient + SSL +coredev.menu.DebugLevel.HTTPClient2.build.debug_level=-DDEBUG_ESP_HTTP_CLIENT -DDEBUG_ESP_SSL +coredev.menu.DebugLevel.HTTPUpdate=HTTPUpdate +coredev.menu.DebugLevel.HTTPUpdate.build.debug_level=-DDEBUG_ESP_HTTP_UPDATE +coredev.menu.DebugLevel.HTTPUpdate2=HTTPClient + HTTPUpdate +coredev.menu.DebugLevel.HTTPUpdate2.build.debug_level=-DDEBUG_ESP_HTTP_UPDATE -DDEBUG_ESP_HTTP_UPDATE +coredev.menu.DebugLevel.HTTPUpdate3=HTTPClient + HTTPUpdate + Updater +coredev.menu.DebugLevel.HTTPUpdate3.build.debug_level=-DDEBUG_ESP_HTTP_UPDATE -DDEBUG_ESP_HTTP_UPDATE -DDEBUG_ESP_UPDATER +coredev.menu.DebugLevel.HTTPServer=HTTPServer +coredev.menu.DebugLevel.HTTPServer.build.debug_level=-DDEBUG_ESP_HTTP_SERVER +coredev.menu.DebugLevel.UPDATER=Updater +coredev.menu.DebugLevel.UPDATER.build.debug_level=-DDEBUG_ESP_UPDATER +coredev.menu.DebugLevel.OTA_____=OTA +coredev.menu.DebugLevel.OTA_____.build.debug_level=-DDEBUG_ESP_OTA +coredev.menu.DebugLevel.OTA2____=OTA + Updater +coredev.menu.DebugLevel.OTA2____.build.debug_level=-DDEBUG_ESP_OTA -DDEBUG_ESP_UPDATER +coredev.menu.DebugLevel.all_____=All +coredev.menu.DebugLevel.all_____.build.debug_level=-DDEBUG_ESP_CORE -DDEBUG_ESP_SSL -DDEBUG_ESP_WIFI -DDEBUG_ESP_HTTP_CLIENT -DDEBUG_ESP_HTTP_UPDATE -DDEBUG_ESP_HTTP_SERVER -DDEBUG_ESP_UPDATER -DDEBUG_ESP_OTA -DDEBUG_TLS_MEM diff --git a/arduino/version 2.3.0/platform.txt b/arduino/version 2.3.0/platform.txt new file mode 100644 index 000000000..d9c60b3f1 --- /dev/null +++ b/arduino/version 2.3.0/platform.txt @@ -0,0 +1,130 @@ + +# ESP8266 platform +# ------------------------------ + +# For more info: +# https://github.com/arduino/Arduino/wiki/Arduino-IDE-1.5---3rd-party-Hardware-specification + +name=ESP8266 Modules +version=2.2.0 + + + + +compiler.warning_flags=-w +compiler.warning_flags.none=-w +compiler.warning_flags.default= +compiler.warning_flags.more=-Wall +compiler.warning_flags.all=-Wall -Wextra + +build.lwip_lib=-llwip_gcc +build.lwip_flags=-DLWIP_OPEN_SRC + +compiler.path={runtime.tools.xtensa-lx106-elf-gcc.path}/bin/ +compiler.sdk.path={runtime.platform.path}/tools/sdk +compiler.cpreprocessor.flags=-D__ets__ -DICACHE_FLASH -U__STRICT_ANSI__ "-I{compiler.sdk.path}/include" "-I{compiler.sdk.path}/lwip/include" "-I{build.path}/core" + +compiler.c.cmd=xtensa-lx106-elf-gcc +compiler.c.flags=-c {compiler.warning_flags} -Os -g -Wpointer-arith -Wno-implicit-function-declaration -Wl,-EL -fno-inline-functions -nostdlib -mlongcalls -mtext-section-literals -falign-functions=4 -MMD -std=gnu99 -ffunction-sections -fdata-sections + +compiler.S.cmd=xtensa-lx106-elf-gcc +compiler.S.flags=-c -g -x assembler-with-cpp -MMD -mlongcalls + +compiler.c.elf.flags=-g {compiler.warning_flags} -Os -nostdlib -Wl,--no-check-sections -u call_user_start -Wl,-static "-L{compiler.sdk.path}/lib" "-L{compiler.sdk.path}/ld" "-T{build.flash_ld}" -Wl,--gc-sections -Wl,-wrap,system_restart_local -Wl,-wrap,register_chipv6_phy + +compiler.c.elf.cmd=xtensa-lx106-elf-gcc +compiler.c.elf.libs=-lm -lgcc -lhal -lphy -lpp -lnet80211 -lwpa -lcrypto -lmain -lwps -laxtls -lsmartconfig -lmesh -lwpa2 {build.lwip_lib} -lstdc++ + +compiler.cpp.cmd=xtensa-lx106-elf-g++ +compiler.cpp.flags=-c {compiler.warning_flags} -Os -g -mlongcalls -mtext-section-literals -fno-exceptions -fno-rtti -falign-functions=4 -std=c++11 -MMD -ffunction-sections -fdata-sections + +compiler.as.cmd=xtensa-lx106-elf-as + +compiler.ar.cmd=xtensa-lx106-elf-ar +compiler.ar.flags=cru + +compiler.elf2hex.cmd=esptool +compiler.elf2hex.flags= + +compiler.size.cmd=xtensa-lx106-elf-size + +compiler.esptool.cmd=esptool +compiler.esptool.cmd.windows=esptool.exe + +# This can be overriden in boards.txt +build.extra_flags=-DESP8266 + +# These can be overridden in platform.local.txt +compiler.c.extra_flags= +compiler.c.elf.extra_flags= +compiler.S.extra_flags= +compiler.cpp.extra_flags= +compiler.ar.extra_flags= +compiler.objcopy.eep.extra_flags= +compiler.elf2hex.extra_flags= + +## generate file with git version number +## needs bash, git, and echo + +## windows-compatible version may be added later + + +## Compile c files +recipe.c.o.pattern="{compiler.path}{compiler.c.cmd}" {compiler.cpreprocessor.flags} {compiler.c.flags} -DF_CPU={build.f_cpu} {build.lwip_flags} {build.debug_port} {build.debug_level} -DARDUINO={runtime.ide.version} -DARDUINO_{build.board} -DARDUINO_ARCH_{build.arch} -DARDUINO_BOARD="{build.board}" {compiler.c.extra_flags} {build.extra_flags} {includes} "{source_file}" -o "{object_file}" + +## Compile c++ files +recipe.cpp.o.pattern="{compiler.path}{compiler.cpp.cmd}" {compiler.cpreprocessor.flags} {compiler.cpp.flags} -DF_CPU={build.f_cpu} {build.lwip_flags} {build.debug_port} {build.debug_level} -DARDUINO={runtime.ide.version} -DARDUINO_{build.board} -DARDUINO_ARCH_{build.arch} -DARDUINO_BOARD="{build.board}" {compiler.cpp.extra_flags} {build.extra_flags} {includes} "{source_file}" -o "{object_file}" + +## Compile S files +recipe.S.o.pattern="{compiler.path}{compiler.c.cmd}" {compiler.cpreprocessor.flags} {compiler.S.flags} -DF_CPU={build.f_cpu} {build.lwip_flags} {build.debug_port} {build.debug_level} -DARDUINO={runtime.ide.version} -DARDUINO_{build.board} -DARDUINO_ARCH_{build.arch} -DARDUINO_BOARD="{build.board}" {compiler.c.extra_flags} {build.extra_flags} {includes} "{source_file}" -o "{object_file}" + +## Create archives +recipe.ar.pattern="{compiler.path}{compiler.ar.cmd}" {compiler.ar.flags} {compiler.ar.extra_flags} "{build.path}/arduino.ar" "{object_file}" + +## Combine gc-sections, archives, and objects +recipe.c.combine.pattern="{compiler.path}{compiler.c.elf.cmd}" {compiler.c.elf.flags} {compiler.c.elf.extra_flags} -o "{build.path}/{build.project_name}.elf" -Wl,--start-group {object_files} "{build.path}/arduino.ar" {compiler.c.elf.libs} -Wl,--end-group "-L{build.path}" + +## Create eeprom +recipe.objcopy.eep.pattern= + +## Create hex +#recipe.objcopy.hex.pattern="{compiler.path}{compiler.elf2hex.cmd}" {compiler.elf2hex.flags} {compiler.elf2hex.extra_flags} "{build.path}/{build.project_name}.elf" "{build.path}/{build.project_name}.hex" + +recipe.objcopy.hex.pattern="{runtime.tools.esptool.path}/{compiler.esptool.cmd}" -eo "{runtime.platform.path}/bootloaders/eboot/eboot.elf" -bo "{build.path}/{build.project_name}.bin" -bm {build.flash_mode} -bf {build.flash_freq} -bz {build.flash_size} -bs .text -bp 4096 -ec -eo "{build.path}/{build.project_name}.elf" -bs .irom0.text -bs .text -bs .data -bs .rodata -bc -ec + +## Save hex +recipe.output.tmp_file={build.project_name}.bin +recipe.output.save_file={build.project_name}.{build.variant}.bin + +## Compute size +recipe.size.pattern="{compiler.path}{compiler.size.cmd}" -A "{build.path}/{build.project_name}.elf" +recipe.size.regex=^(?:\.irom0\.text|\.text|\.data|\.rodata|)\s+([0-9]+).* +recipe.size.regex.data=^(?:\.data|\.rodata|\.bss)\s+([0-9]+).* +#recipe.size.regex.eeprom=^(?:\.eeprom)\s+([0-9]+).* + +# ------------------------------ + +tools.esptool.cmd=esptool +tools.esptool.cmd.windows=esptool.exe +tools.esptool.path={runtime.tools.esptool.path} +tools.esptool.network_cmd=python +tools.esptool.network_cmd.windows=python.exe + +tools.esptool.upload.protocol=esp +tools.esptool.upload.params.verbose=-vv +tools.esptool.upload.params.quiet= +tools.esptool.upload.pattern="{path}/{cmd}" {upload.verbose} -cd {upload.resetmethod} -cb {upload.speed} -cp "{serial.port}" -ca 0x00000 -cf "{build.path}/{build.project_name}.bin" +tools.esptool.upload.network_pattern="{network_cmd}" "{runtime.platform.path}/tools/espota.py" -i "{serial.port}" -p "{network.port}" "--auth={network.password}" -f "{build.path}/{build.project_name}.bin" + +tools.mkspiffs.cmd=mkspiffs +tools.mkspiffs.cmd.windows=mkspiffs.exe +tools.mkspiffs.path={runtime.tools.mkspiffs.path} + +tools.espupload.cmd=python +tools.espupload.cmd.windows=python.exe +tools.espupload.path={runtime.platform.path}/tools + +tools.espupload.upload.protocol=espupload +tools.espupload.upload.params.verbose= +tools.espupload.upload.params.quiet= +tools.espupload.upload.pattern="{cmd}" "{path}/espupload.py" -f "{build.path}/{build.project_name}.bin" diff --git a/sonoff/README.md b/sonoff/README.md new file mode 100644 index 000000000..ba72cf54b --- /dev/null +++ b/sonoff/README.md @@ -0,0 +1,33 @@ +## Sonoff-MQTT-OTA-Arduino - TASMOTA NextGen +Provide ESP8266 based Sonoff by [iTead Studio](https://www.itead.cc/) and ElectroDragon IoT Relay with Serial, Web and MQTT control allowing 'Over the Air' or OTA firmware updates using Arduino IDE. + +Current version is **3.9.4** - See ```_releasenotes.ino``` for change information. + +- This version provides all (Sonoff) modules in one file and starts up with Sonoff Basic. +- Once uploaded select module using the configuration webpage or the commands ```Modules``` and ```Module```. +- After reboot select config menu again or use commands ```GPIOs``` and ```GPIO``` to change GPIO with desired sensor. +- Some features still need to be ironed out. + +Sonoff +See [Wiki](https://github.com/arendst/Sonoff-MQTT-OTA-Arduino/wiki) for more information.
+See [Community](https://groups.google.com/d/forum/sonoffusers) for forum and more user experience. + +Starting with version 2.0.0 the following devices are supported: +- [iTead Sonoff Basic](http://sonoff.itead.cc/en/products/sonoff/sonoff-basic) +- [iTead Sonoff RF](http://sonoff.itead.cc/en/products/sonoff/sonoff-rf) +- [iTead Sonoff SV](https://www.itead.cc/sonoff-sv.html) +Sonoff +- [iTead Sonoff TH10/TH16 with temperature sensor](http://sonoff.itead.cc/en/products/sonoff/sonoff-th) +- [iTead Sonoff Dual](http://sonoff.itead.cc/en/products/sonoff/sonoff-dual) +- [iTead Sonoff Pow](http://sonoff.itead.cc/en/products/sonoff/sonoff-pow) +- [iTead Sonoff 4CH](http://sonoff.itead.cc/en/products/sonoff/sonoff-4ch) +- [iTead S20 Smart Socket](http://sonoff.itead.cc/en/products/residential/s20-socket) +- [iTead Slampher](http://sonoff.itead.cc/en/products/residential/slampher-rf) +- [iTead Sonoff Touch](http://sonoff.itead.cc/en/products/residential/sonoff-touch) +- [iTead Sonoff Led](http://sonoff.itead.cc/en/products/appliances/sonoff-led) +- [iTead 1 Channel Switch 5V / 12V](https://www.itead.cc/smart-home/inching-self-locking-wifi-wireless-switch.html) +- [iTead Motor Clockwise/Anticlockwise](https://www.itead.cc/smart-home/motor-reversing-wifi-wireless-switch.html) +- [Electrodragon IoT Relay Board](http://www.electrodragon.com/product/wifi-iot-relay-board-based-esp8266/) + +Sonoff +Sonoff diff --git a/sonoff/_releasenotes.ino b/sonoff/_releasenotes.ino new file mode 100644 index 000000000..f81ae8d25 --- /dev/null +++ b/sonoff/_releasenotes.ino @@ -0,0 +1,514 @@ +/* 3.9.4 20170127 + * Fix Sonoff Dual Relay switching (#287) + * + * 3.9.3 20170127 + * Add confirmation before Restart via webpage + * Expand Domoticz Configuration webpage with Key, Switch and Sensor Index and + * add commands DomoticzSwitchIdx and DomoticzSensorIdx (#86) (#174) (#219) + * Fix default DHT11 sensor driver selection + * Fix LedPower status after button press (#279) + * Add command Sleep 0 - 250 mSec for optional light sleep mode to lower energy consumption (#272) + * (Expect overall button/key/switch misses and wrong values on Sonoff Pow) + * Add Hue brightness extension (#281) + * Fix Hue brightness and change to call by reference (#283) + * + * 3.9.2 20170124 + * Add confirmation before Reset Configuration via webpage (#244) + * Add WS2812 features (see Wiki commands) + * + * 3.9.1 20170124 + * Change PowerOnState function to only trigger when Power On (and not just restart) (#238) + * Move HLW interrupts back to RAM and make WS2812_DMA optional as it generates Exception on Pow (#264) + * Add charset=utf-8 to webpages (#266) + * Update Hue emulation (#268) + * Fix status module number + * Add support for domoticz Dimmer on Sonoff_Led and WS2812 + * Fix possible ESP8285 flash problem by updating Flash Chip Mode to DOUT during web upload + * + * 3.2.6a 20170120 + * Fix Sonoff Pow compile error (#255) + * Move HLW interrupts back to ROM (Needed for WS2812 DMA interrupts) + * Removed all IO config from user_config.h as this will be done by commands or webpage + * Removed MessageFormat and supports JSON only except POWER/LIGHT status + * Add command LedPower to control main led (#247) + * Add more FriendlyNames for Hue (#254) + * Add DMA support for WS2812 when using pin 3 while other pins work just as well in my case... + * Add HUE emulation for Alexa (#229) + * Add basic WS2812 support (#229) + * Fix Wemo when MQTT is disabled (#245) + * Revert ButtonTopic and change SwitchTopic1 - 4 to one SwitchTopic + * Rename MqttUnits to Units + * Add Mqtt command to enable/disable MQTT + * + * 3.2.2a 20170115 + * Add dynamic (Sonoff) Module, user GPIO and sensor selection (one size fits (almost) all) + * Add support for Sonoff LED + * Add Seriallog disable after 600 seconds for Sonoff Dual and 4 Channel + * Add ButtonTopic2 - 4, SwitchTopic1 - 4 and SwitchRetain + * + * 3.2.2 20170113 + * Fix PowerOnState 2 functionality after re-applying power (#230) + * + * 3.2.1 20170113 + * Fix some failed command decoding (#228) + * Removed passwords from status messages (#216) + * + * 3.2.0 20170111 + * Add I2C BH1750 sensor (#222) + * Sensor rewrite preparing for online selection + * + * 3.1.16 20170109 + * Fix Domoticz possible error condition + * Remove Wifi password from connection message (#216) + * Add Configure Other menu item to web page (#209) + * Add command FriendlyName, field Friendly Name and define FRIENDLY_NAME to be used by Alexa + * eliminating current use of MQTT_CLIENT_ID (#209) + * Add friendlyname to webpage replacing former hostname + * + * 3.1.15 20170108 + * Fix Domoticz send key regression with Toggle command + * + * 3.1.14 20170107 + * Add support for command TOGGLE (define MQTT_CMND_TOGGLE) when ButtonTopic is in use and not equal to Topic (#207) + * + * 3.1.13 20170107 + * Fix web console command input when SUB_PREFIX contains '/' (#152) + * Add command response to web command (#200) + * Add option to disable MQTT as define USE_MQTT in user_config.h (#200) + * + * 3.1.12 20170106 + * Add OTA retry to solve possible HTTP transient errors (#204) + * Fix MQTT host discovery + * + * 3.1.11 20170105 + * Add mDNS to advertise webserver as .local/ + * + * 3.1.10 20170105 + * Fix ButtonTopic when SUB_PREFIX = PUB_PREFIX + * Add workaround for possible MQTT queueing when SUB_PREFIX = PUB_PREFIX + * Add optional MQTT host discovery using define USE_DISCOVERY in user_config.h (#115) + * + * 3.1.9 20170104 + * Fix Power Blink start position (toggled) + * Change PulseTime increments: 1 .. 111 in 0.1 sec (max 11 seconds) and 112 .. 64900 in seconds (= 12 seconds until 18 hours) (#188) + * Add support for SUB_PREFIX = PUB_PREFIX (#190) + * + * 3.1.8 20170103 + * Add retain flag to LWT offline and only send "tele/sonoff/LWT Offline" (#179) + * Change retained LWT Online message to only send "tele/sonoff/LWT Online" + * + * 3.1.7 20161231 + * Add retained message LWT Online when sonoff makes MQTT connection (#179) + * + * 3.1.6 20161230 + * Add blinking using commands BlinkTime, BlinkCount and Power Blink|3|BlinkOff|4 (#165) + * + * 3.1.5 20161228 + * Fix serial space command exception (28) + * + * 3.1.4 20161227 + * Fix MQTT subscribe regression exception (3) (#162) + * Fix serial empty command exception (28) + * + * 3.1.3 20161225 + * Extent Domoticz configuration webpage with optional indices (#153) + * Fix multi relay legacy tele message from tele/sonoff/2/POWER to tele/sonoff/POWER2 + * Add support for iTead Motor Clockwise/Anticlockwise + * + * 3.1.2 20161224 + * Extent command PowerOnState with toggle at power on (option 2 is now option 3!) (#156) + * + * 3.1.1 20161223 + * Add support for Sonoff Touch and Sonoff 4CH (#40) + * Update DomoticzIdx and DomoticzKeyIdx with relay/key index (DomoticzIdx1/DomoticzKeyIdx1) + * Add command PowerOnState to control relay(s) at power on (#154) + * + * 3.1.0 20161221 + * Add Sonoff Pow measurement smoothing + * Fix serial command topic preamble error (#151) + * Fix 2.x to 3.x migration inconsistencies (#146) + * + * 3.0.9 20161218 + * Add Sonoff Pow voltage reading when relay is on but no load present (#123) + * + * 3.0.8 20161218 + * Add temperature conversion to Fahrenheit as option in user_config.h (TEMP_CONVERSION) (#145) + * + * 3.0.7 20161217 + * Add user_config_override.h to be used by user to override some defaults in user_config.h (#58) + * Fix Sonoff Pow low power (down to 4W) intermittent measurements (#123) + * + * 3.0.6 20161217 + * Fix MQTT_CLIENT_ID starting with % sign as in "%06X" (#142) + * Add auto power off after PulseTime * 0.1 Sec to relay 1 (#134) + * + * 3.0.5 20161215 + * Add more control over LED with command LedState options (#136, #143) + * LED_OFF (0), LED_POWER (1), LED_MQTTSUB (2), LED_POWER_MQTTSUB (3), LED_MQTTPUB (4), LED_POWER_MQTTPUB (5), LED_MQTT (6), LED_POWER_MQTT (7) + * Add option WIFI_RETRY (4) to command WifiConfig to allow connection retry to other AP without restart (#73) + * + * 3.0.4 20161211 + * Fix intermittent Domoticz update misses (#133) + * + * 3.0.3 20161210 + * Fix compiler warnings (#132) + * Remove redundant code + * Fix Domoticz pushbutton support + * + * 3.0.2 20161209 + * Add pushbutton to SwitchMode (#130) + * + * 3.0.1 20161209 + * Fix initial config + * + * 3.0.0 20161208 + * Migrate and clean-up flash layout + * Settings from version 2.x are saved but settings from version 3.x can not be used with version 2.x + * Change SEND_TELEMETRY_RSSI to SEND_TELEMETRY_WIFI and add AP and SSID to telemetry + * Split long JSON messages + * Fix inconsistent status messages + * Fix all status messages to return JSON if enabled + * Remove relay index in cmnd/sonoff//POWER now changed + * to cmnd/sonoff/POWER for single relay units + * and cmnd/sonoff/POWER for multi relay units like Sonoff dual + * Add retain option to Power/Light status controlled by command PowerRetain On|Off (#126) + * + * 2.1.2 20161204 + * Add support for second wifi AP (#73) + * Update command WifiConfig + * Fix possible WifiManager hang + * + * 2.1.1a 20161203 + * Fix scan for wifi networks if WeMo is enabled + * Fix syslog setting using web page + * + * 2.1.1 20161202 + * Add support for ElectroDragon second relay and button (only toggle with optional ButtonTopic) (#110) + * + * 2.1.0 20161202 + * Add optional EXPERIMENTAL TLS to MQTT (#49) + * Fix MQTT payload handling (#111) + * Optimzed WeMo code + * + * 2.0.21a 20161201 + * Fix WeMo PowerPlug emulation + * + * 2.0.21 20161130 + * Add Belkin WeMo PowerPlug emulation enabled with USE_WEMO_EMULATION in user_config.h (Heiko Krupp) (#105, #109) + * + * 2.0.20 20161130 + * Relax MQTTClient naming but only allows hexadecimal uppercase numbers (#107) + * Add I2C support with command I2CScan + * Add I2C sensor driver for HTU21 as alternate sensor using TH10/16 connectors (Heiko Krupp) (#105) + * Add I2C sensor driver for BMP085/BMP180/BMP280/BME280 as alternate sensor using TH10/16 connectors + * + * 2.0.19a 20161127 + * Add support for ButtonTopic and ButtonRetain to wall switch function + * Add pullup to SWITCH_PIN and command SwitchMode to syntax + * + * 2.0.18 20161126 + * Add SUB_PREFIX multi level support allowing 'cmnd' or 'cmnd/level2/level3' + * Add wall switch function to GPIO14 and command SwitchMode (Alex Scott) (#103) + * + * 2.0.17 20161123 + * Calibrate HLWPCAL from 12345 to 12530 + * Add alternative sensor driver DHT2 using Adafruit DHT library + * Add define MESSAGE_FORMAT to user_config.h + * Throttle console messages + * Shorten JSON messages + * Fix possible Panic + * Fix User mode webserver security + * + * 2.0.16 20161118 + * Add alternative sensor driver DS18x20 using OneWire library (#95) + * Change sensor MQTT message from tele/sonoff/TEMPERATURE to tele/sonoff/DHT/TEMPERATURE or + * tele/sonoff/DS18B20/TEMPERATURE or tele/sonoff/DS18x20/1/TEMPERATURE + * Add sensors to root webpage and auto refresh every 4 seconds (#92) + * Add optional JSON messageformat to all telemetry data + * Enforce minimum TelePeriod to be 10 seconds + * Fix Energy Yesterday reset after restart + * Add Energy Today restore after controlled restart + * + * 2.0.15 20161116 + * Change TODAY_POWER and PERIOD_POWER to TODAY_ENERGY and PERIOD_ENERGY + * Fix serial regression + * Fix syslog hangs when loghost is unavailable + * + * 2.0.14 20161115 + * Add HLW threshold delay + * Fix HLW intermittent current deviation + * Fix button functionality during wificonfig + * Add CRC check to DS18B20 sensor (#88) + * + * 2.0.13 20161113 + * Add additional upload error code descriptions + * Add PlatformIO support (#80) + * + * 2.0.12 20161113 + * Fix Serial and Web response regression when no MQTT connection available + * Fix Sonoff Dual power telemetric data for second relay + * Removed MQTT password from Information web page + * Hide MQTT password from Configure MQTT web page + * + * 2.0.11 20161111 + * Rewrite button and web toggle code + * Fix NTP sync + * Add HLW calibration commands HLWPCAL, HLWUCAL and HLWICAL (need define USE_POWERCALIBRATION) + * Fix power threshold tests + * + * 2.0.10 20161109 + * Add additional Domoticz define (#63) + * Add defines MQTT_STATUS_ON and MQTT_STATUS_OFF in user_config.h to select status On/Off string + * Fix status response differences (#65) + * Fix divide by zero exception (#70) + * Fix syslog loop exception + * + * 2.0.9 20161108 + * clarify MODULE in user_config.h + * Fix hlw false values + * + * 2.0.8 20161108 + * Add initial status after power on + * Seperate driver files + * Fix hlw code and calibrate Pow + * Move user config defines to user_config.h (#61) + * + * 2.0.7 20161030 + * Make Ticker mandatory + * Add Domoticz support (Increase MQTT_MAX_PACKET_SIZE to 400) (#54) + * Add command MessageFormat 0|1 to select either legacy or JSON output + * + * 2.0.6 20161024 + * Add Sonoff Pow power factor + * Initial support for up to four relays using iTEAD PSB (4Channel) + * - Currently only supports one button (All buttons behave the same) + * - Use command MODEL 4 to select four relay option + * (After first power on it will support 2 relays like Sonoff Dual) + * Fix ledstate + * Add command Status 9 to display Sonoff Pow thresholds + * Add commands PowerLow, PowerHigh, VoltageLow, VoltageHigh, CurrentLow and CurrentHigh for use + * with Sonoff Pow thresholds + * + * 2.0.5 20161018 + * Add updates to user_config.h - moved SEND_TELEMETRY_DS18B20 and SEND_TELEMETRY_DHT to module area. + * As Sonoff TH10/16 does not have the logic installed for GPIO04 You'll have to select ONE of both + * Add Sonoff Pow support (experimental until Pow tested) + * Add command Status 8 to display Sonoff Pow energy values + * Add command MqttUnits On|Off to add units to values + * Change web main page header character size + * Change On/Off to ON/OFF status messages to satisfy openHAB + * Change TEMP to TEMPERATURE and HUM to HUMIDITY + * + * 2.0.4 20161009 + * Add MQTT_BUTTON_RETAIN, SAVE_DATA and SAVE_STATE defines to user_config.h (#35) + * Update ButtonRetain to remove retained message(s) from broker when turned off + * Add Retain for second relay on Sonoff Dual + * Provide power status messages with device topic index if requested + * + * 2.0.3 20161008 + * Update wifi initialization + * Add command BUTTONRETAIN for optional MQTT retain on button press (#35) + * Add command SAVESTATE to disable power state save. May be used with MQTT retain + * + * 2.0.2 20161006 + * Fix wifi issue 2186 + * + * 2.0.1 20161002 + * Fix button press + * + * 2.0.0 20161002 + * Update Sonoff TH10/16 sensor pins (My TH10 only has GPIO14 connected) + * Add full support for Sonoff dual + * + * 1.0.35 20160929 + * Add more lines to console + * Add timeout and disable MQTT on web upload + * Add command SAVEDATA to control parameter save (for flash wear afficionados) (#30) + * + * 1.0.34 20160926 + * Fix button press six and seven + * Add more information to webserver + * + * 1.0.33 20160915 + * Better WPS error message + * Separate webserver code from support.ino into webserver.ino + * Fix webserver User by removing unwanted restart option + * + * 1.0.32 20160913 + * Add Wifi Protected Setup (WPS) as third option for initial config + * Add command WIFICONFIG replacing deprecated command SMARTCONFIG + * Add option WIFICONFIG 3 to start WPSconfig + * Add option WIFICONFIG 0 to start saved Wifi config tool (WPSconfig, Smartconfig or Wifimanager) + * Change button behaviour - See Wiki + * + * 1.0.31 20160907 + * Fix DS18B20 misread if teleperiod = 2 + * Tuned sensor code + * Updated prefered ElectroDragon connection to Relay 1 and Button 1 + * Moved SONOFF and ELECTRO_DRAGON port config to user_config.h + * + * 1.0.30 20160902 + * Fix command TELEPERIOD 0 + * Add ESP- tag to UDP log message for easy rsyslogd filtering + * Add ElectroDragon (Relay 2 only) functionality. Select with #define MODULE ELECTRO_DRAGON + * Add ? as null message alternative + * Add DHT temperature and humidity telemetry support. Enable with #define SEND_TELEMETRY_DHT + * Add DS18B20 temperature telemetry support. Enable with #define SEND_TELEMETRY_DS18B20 + * Restrict HOSTNAME, MQTTCLIENT, TOPIC and BUTTONTOPIC in topic mode only + * + * 1.0.29 20160831 + * Allow UPGRADE, OTAURL, RESTART, RESET, MQTTHOST, MQTTPORT, MQTTUSER, MQTTPASSWORD and WEBSERVER also in group mode + * + * 1.0.28 20160831 + * Add webserver state to status 5 + * Add optional PUB_PREFIX2 (tele) for telemetry usage + * Add command TELEPERIOD + * Fix syntax message + * Change memory status display + * + * 1.0.27 20160831 + * Add sketch flash size + * Add console to webserver + * Add command weblog + * Change WifiManager web pages to minimal + * Change display default hostname and MQTT client id in webserver + * Change HTTP command interface to http://sonoff-1234/cm?cmnd=light 2 + * Change HEARTBEAT to UPTIME + * + * 1.0.26 20160829 + * Add define USE_WEBSERVER to disable web server code in source + * Add file upload as alternative for ota upload to webserver + * Add information to webserver + * Add command hostname + * Add command logport + * Change HTTP command interface to http://sonoff-1234/cmd?cmnd=light 2 + * Change button behaviour with regards to Smartconfig and OTA upload. See README.md + * Enforce default hostname to either "%s-%04d" or user defined without any % + * Enforce default mqtt client id to either "DVES_%06X" or user defined without any % + * + * 1.0.25 20160822 + * Remove config system halts to keep ota available + * + * 1.0.24 20160821 + * Add test for MQTT_SUBTOPIC + * Change log range to LOG_LEVEL_ALL + * Change MQTT introduction messages + * Moved MQTT_MAX_PACKET_SIZE warning message to introduction messages + * + * 1.0.23 20160821 + * Add option USE_SPIFFS to move config from flash to spiffs + * Add webserver with options 0 (off), 1 (user) and 2 (admin) + * Add HTTP command interface (http://sonoff-1234/c?cmnd=light 2) + * Add wifimanager countdown counter + * Add command line webpage + * Add relay control to wifimanager + * Add restart option 99 to force restart + * Fix wifi hostname + * Fix NETBIOS hostname problem by reducing default hostname length + * Fix possible exception if WIFI_HOSTNAME is changed + * Fix upgrade messages + * Reduce memory use by redesigning config routines + * Split syntax message + * Rename define SERIAL_IO to USE_SERIAL + * + * 1.0.22 20160814 + * Add all MQTT parameters for configuration + * Add wifimanager to configure Wifi and MQTT via web server + * Change NTP time handling + * Fix Smartconfig parameter buffer overflow + * Fix PlatformIO warnings + * + * 1.0.21 20160808 + * Remove semaphore as subscription flooding (more than 15 subscriptions per second) is managed by SDK (LmacRxBlk:1) + * Add optional RTC interrupt (define USE_TICKER) to keep RTC synced during subscription flooding + * Remove heartbeatflag + * + * 1.0.20 20160805 + * Add semaphore to handle out of memory when too many subscriptions requested + * Use Daylight Saving (DST) parameters from user_config.h when timezone = 99 + * Add status 7 option displaying RTC information + * Add ledstate to status 0 + * + * 1.0.19 20160803 + * Fix possible MQTT_CLIENT_ID induced Exception(28) + * + * 1.0.18 20160803 + * Moved Cfg_Default + * Fix negative data handling + * Remove MQTT information from status 1 and add labels to status 1 + * Add mac address to status 5 + * Add MQTT ClientId, UserId and Password to status 6 + * + * 1.0.17 20160731 + * Better variable range checking + * Change ambiguous connection messages + * Add timestamp to serial message + * + * 1.0.16 20160729 + * Moved wifi, rtc, syslog and config to support.ino + * Fixed button action when buttontopic is used. Introduced with 1.0.15 + * Better buffer overflow checks (strlcpy) + * + * 1.0.15 20160728 + * Removed pubsubclient config changes from sonoff.ino as it doesn't work + * reapply MQTT_MAX_PACKET_SIZE 256 and MQTT_KEEPALIVE 120 to PubSubClient.h + * Add status 0 option displaying all status messages + * Change MQTT_MAX_PACKET_SIZE from 1024 to 256 + * Add buffer overflow checks (snprintf and strncpy) + * Implemented common string sizes + * + * 1.0.14 20160722 + * Seperate user config from sonoff.ino to user_config.h (pucebaboon) + * Change defaults from sidnas2 to domus1 + * Add MQTT status message as status 6 (pucebaboon) + * Add status type to message (pucebaboon) + * Add pubsubclient config changes to sonoff.ino (pucebaboon) + * + * 1.0.13 20160702 + * Add Ledstate 1 option to show power state on led + * + * 1.0.12 20160529 + * Allow disable of button topic using "0" + * + * 1.0.11 20160524 + * Provide button response if MQTT connection lost + * + * 1.0.10 20160520 + * Add optional button topic to assist external MQTT clients + * Change version notation + * Reset default values + * + * 1.0.9 20160503 + * Add more blinks + * Add reset 2 option erasing flash + * Add status 5 option displaying network info + * Add syslog check for Wifi connection + * Resize mqtt_publish log array + * Change Wifi smartconfig active from 100 to 60 seconds + * Update Wifi initialization + * + * 1.0.8 20160430 + * Remove use of Wifi config data from SDK + * Add status 3 (syslog info) and status 4 (flash info) + * Add restart option to button (5 quick presses) + * + * 1.0.7 20160420 + * Add UDP syslog support + * Change HOST command to MQTTHOST command + * Add commands SYSLOG, SERIALLOG and LOGHOST + * Change hostname to lower case to distinguise between open-sdk version + * Add support for ESP-12F used in my modified wkaku power socket switch + * Fix timezone command + * Add RTC month names for future use + * Modify button code + * Remove initialization errors by better use of MQTT loop + * + * 1.0.6 20160406 + * Removed Wifi AP mode (#1) + * Add test for Arduino IDE version >= 1.6.8 + * Fix RTC time sync code + * + * 1.0.5 20160310 + * Initial public release + * Show debug info by selecting option from IDE Tools Debug port: Serial + */ diff --git a/sonoff/sonoff.ino b/sonoff/sonoff.ino new file mode 100644 index 000000000..3dc393b4b --- /dev/null +++ b/sonoff/sonoff.ino @@ -0,0 +1,2760 @@ +/* + * Sonoff and ElectroDragon by Theo Arends + * + * ==================================================== + * Prerequisites: + * - Change libraries/PubSubClient/src/PubSubClient.h + * #define MQTT_MAX_PACKET_SIZE 400 + * + * - Select IDE Tools - Flash size: "1M (64K SPIFFS)" + * ==================================================== +*/ + +#define VERSION 0x03090400 // 3.9.4 + +enum log_t {LOG_LEVEL_NONE, LOG_LEVEL_ERROR, LOG_LEVEL_INFO, LOG_LEVEL_DEBUG, LOG_LEVEL_DEBUG_MORE, LOG_LEVEL_ALL}; +enum week_t {Last, First, Second, Third, Fourth}; +enum dow_t {Sun=1, Mon, Tue, Wed, Thu, Fri, Sat}; +enum month_t {Jan=1, Feb, Mar, Apr, May, Jun, Jul, Aug, Sep, Oct, Nov, Dec}; +enum wifi_t {WIFI_RESTART, WIFI_SMARTCONFIG, WIFI_MANAGER, WIFI_WPSCONFIG, WIFI_RETRY, MAX_WIFI_OPTION}; +enum swtch_t {TOGGLE, FOLLOW, FOLLOW_INV, PUSHBUTTON, PUSHBUTTON_INV, MAX_SWITCH_OPTION}; +enum led_t {LED_OFF, LED_POWER, LED_MQTTSUB, LED_POWER_MQTTSUB, LED_MQTTPUB, LED_POWER_MQTTPUB, LED_MQTT, LED_POWER_MQTT, MAX_LED_OPTION}; + +#include "sonoff_template.h" + +#include "user_config.h" +#include "user_config_override.h" + +/*********************************************************************************************\ + * Enable feature by removing leading // or disable feature by adding leading // +\*********************************************************************************************/ + +//#define USE_SPIFFS // Switch persistent configuration from flash to spiffs (+24k code, +0.6k mem) + +/*********************************************************************************************\ + * Not released yet +\*********************************************************************************************/ + +#define FEATURE_POWER_LIMIT + +/*********************************************************************************************\ + * No user configurable items below +\*********************************************************************************************/ + +#define MODULE SONOFF_BASIC // [Module] Select default model + +#ifndef SWITCH_MODE +#define SWITCH_MODE TOGGLE // TOGGLE, FOLLOW or FOLLOW_INV (the wall switch state) +#endif + +#ifndef MQTT_FINGERPRINT +#define MQTT_FINGERPRINT "A5 02 FF 13 99 9F 8B 39 8E F1 83 4F 11 23 65 0B 32 36 FC 07" +#endif + +#ifndef USE_DHT2 +#define USE_DHT // Default DHT11 sensor needs no external library +#endif + +#ifndef USE_DS18x20 +#define USE_DS18B20 // Default DS18B20 sensor needs no external library +#endif + +#ifndef WS2812_LEDS +#define WS2812_LEDS 30 // [Pixels] Number of LEDs +#endif + +#define DEF_WIFI_HOSTNAME "%s-%04d" // Expands to - + +#define HLW_PREF_PULSE 12530 // was 4975us = 201Hz = 1000W +#define HLW_UREF_PULSE 1950 // was 1666us = 600Hz = 220V +#define HLW_IREF_PULSE 3500 // was 1666us = 600Hz = 4.545A + +#define VALUE_UNITS 0 // Default do not show value units (Hr, Sec, V, A, W etc.) +#define MQTT_SUBTOPIC "POWER" // Default MQTT subtopic (POWER or LIGHT) +#define MQTT_RETRY_SECS 10 // Seconds to retry MQTT connection +#define APP_POWER 0 // Default saved power state Off +#define MAX_DEVICE 1 // Max number of devices +#define WS2812_MAX_LEDS 256 // Max number of LEDs + +#define MAX_POWER_HOLD 10 // Time in SECONDS to allow max agreed power (Pow) +#define MAX_POWER_WINDOW 30 // Time in SECONDS to disable allow max agreed power (Pow) +#define SAFE_POWER_HOLD 10 // Time in SECONDS to allow max unit safe power (Pow) +#define SAFE_POWER_WINDOW 30 // Time in MINUTES to disable allow max unit safe power (Pow) +#define MAX_POWER_RETRY 5 // Retry count allowing agreed power limit overflow (Pow) + +#define STATES 10 // loops per second +#define SYSLOG_TIMER 600 // Seconds to restore syslog_level +#define SERIALLOG_TIMER 600 // Seconds to disable SerialLog +#define OTA_ATTEMPTS 5 // Number of times to try fetching the new firmware + +#define INPUT_BUFFER_SIZE 100 // Max number of characters in serial buffer +#define TOPSZ 60 // Max number of characters in topic string +#define MESSZ 240 // Max number of characters in JSON message string +#define LOGSZ 128 // Max number of characters in log string +#ifdef USE_MQTT_TLS + #define MAX_LOG_LINES 10 // Max number of lines in weblog +#else + #define MAX_LOG_LINES 70 // Max number of lines in weblog +#endif + +#define APP_BAUDRATE 115200 // Default serial baudrate +#define MAX_STATUS 9 + +enum butt_t {PRESSED, NOT_PRESSED}; + +#include "support.h" // Global support +#include // RTC +#include // MQTT, Ota, WifiManager +#include // MQTT, Ota +#include // Ota +#include // MQTT +#ifdef USE_WEBSERVER + #include // WifiManager, Webserver + #include // WifiManager +#endif // USE_WEBSERVER +#ifdef USE_DISCOVERY + #include // MQTT, Webserver +#endif // USE_DISCOVERY +#ifdef USE_SPIFFS + #include // Config +#endif // USE_SPIFFS +#ifdef USE_I2C + #include // I2C support library +#endif // USE_I2C + +typedef void (*rtcCallback)(); + +extern "C" uint32_t _SPIFFS_start; +extern "C" uint32_t _SPIFFS_end; + +#define MAX_BUTTON_COMMANDS 5 // Max number of button commands supported +const char commands[MAX_BUTTON_COMMANDS][14] PROGMEM = { + {"wificonfig 1"}, // Press button three times + {"wificonfig 2"}, // Press button four times + {"wificonfig 3"}, // Press button five times + {"restart 1"}, // Press button six times + {"upgrade 1"}}; // Press button seven times + +const char wificfg[5][12] PROGMEM = { "Restart", "Smartconfig", "Wifimanager", "WPSconfig", "Retry" }; + +struct SYSCFG2 { // Version 2.x (old) + unsigned long cfg_holder; + unsigned long saveFlag; + unsigned long version; + byte seriallog_level; + byte syslog_level; + char syslog_host[32]; + char sta_ssid1[32]; + char sta_pwd1[64]; + char otaUrl[80]; + char mqtt_host[32]; + char mqtt_grptopic[32]; + char mqtt_topic[32]; + char mqtt_topic2[32]; + char mqtt_subtopic[32]; + int8_t timezone; + uint8_t power; + uint8_t ledstate; + uint16_t mqtt_port; + char mqtt_client[33]; + char mqtt_user[33]; + char mqtt_pwd[33]; + uint8_t webserver; + unsigned long bootcount; + char hostname[33]; + uint16_t syslog_port; + byte weblog_level; + uint16_t tele_period; + uint8_t sta_config; + int16_t savedata; + byte model; + byte mqtt_retain; + byte savestate; + unsigned long hlw_pcal; + unsigned long hlw_ucal; + unsigned long hlw_ical; + unsigned long hlw_kWhyesterday; + byte value_units; + uint16_t hlw_pmin; + uint16_t hlw_pmax; + uint16_t hlw_umin; + uint16_t hlw_umax; + uint16_t hlw_imin; + uint16_t hlw_imax; + uint16_t hlw_mpl; // MaxPowerLimit + uint16_t hlw_mplh; // MaxPowerLimitHold + uint16_t hlw_mplw; // MaxPowerLimitWindow + uint16_t hlw_mspl; // MaxSafePowerLimit + uint16_t hlw_msplh; // MaxSafePowerLimitHold + uint16_t hlw_msplw; // MaxSafePowerLimitWindow + uint16_t hlw_mkwh; // MaxEnergy + uint16_t hlw_mkwhs; // MaxEnergyStart + char domoticz_in_topic[33]; + char domoticz_out_topic[33]; + uint16_t domoticz_update_timer; + unsigned long domoticz_relay_idx[4]; + unsigned long domoticz_key_idx[4]; + byte message_format; // Not used since 3.2.6a + unsigned long hlw_kWhtoday; + uint16_t hlw_kWhdoy; + uint8_t switchmode; + char mqtt_fingerprint[60]; + byte sta_active; + char sta_ssid2[33]; + char sta_pwd2[65]; + +} sysCfg2; + +struct SYSCFG { + unsigned long cfg_holder; + unsigned long saveFlag; + unsigned long version; + unsigned long bootcount; + byte migflg; + int16_t savedata; + byte savestate; + byte model; + int8_t timezone; + char otaUrl[101]; + char ex_friendlyname[33]; // Not used since 3.2.5 - see below + + byte serial_enable; + byte seriallog_level; + uint8_t sta_config; + byte sta_active; + char sta_ssid[2][33]; + char sta_pwd[2][65]; + char hostname[33]; + char syslog_host[33]; + uint16_t syslog_port; + byte syslog_level; + uint8_t webserver; + byte weblog_level; + + char mqtt_fingerprint[60]; + char mqtt_host[33]; + uint16_t mqtt_port; + char mqtt_client[33]; + char mqtt_user[33]; + char mqtt_pwd[33]; + char mqtt_topic[33]; + char button_topic[33]; + char mqtt_grptopic[33]; + char mqtt_subtopic[33]; + byte mqtt_button_retain; + byte mqtt_power_retain; + byte value_units; + byte message_format; // Not used since 3.2.6a + uint16_t tele_period; + + uint8_t power; + uint8_t ledstate; + uint8_t switchmode; + + char domoticz_in_topic[33]; + char domoticz_out_topic[33]; + uint16_t domoticz_update_timer; + unsigned long domoticz_relay_idx[4]; + unsigned long domoticz_key_idx[4]; + + unsigned long hlw_pcal; + unsigned long hlw_ucal; + unsigned long hlw_ical; + unsigned long hlw_kWhtoday; + unsigned long hlw_kWhyesterday; + uint16_t hlw_kWhdoy; + uint16_t hlw_pmin; + uint16_t hlw_pmax; + uint16_t hlw_umin; + uint16_t hlw_umax; + uint16_t hlw_imin; + uint16_t hlw_imax; + uint16_t hlw_mpl; // MaxPowerLimit + uint16_t hlw_mplh; // MaxPowerLimitHold + uint16_t hlw_mplw; // MaxPowerLimitWindow + uint16_t hlw_mspl; // MaxSafePowerLimit + uint16_t hlw_msplh; // MaxSafePowerLimitHold + uint16_t hlw_msplw; // MaxSafePowerLimitWindow + uint16_t hlw_mkwh; // MaxEnergy + uint16_t hlw_mkwhs; // MaxEnergyStart + + uint16_t pulsetime; + uint8_t poweronstate; + uint16_t blinktime; + uint16_t blinkcount; + + uint16_t ws_pixels; + uint8_t ws_red; + uint8_t ws_green; + uint8_t ws_blue; + uint8_t ws_ledtable; + uint8_t ws_dimmer; + uint8_t ws_fade; + uint8_t ws_speed; + uint8_t ws_scheme; + uint8_t ws_width; + uint16_t ws_wakeup; + + char friendlyname[4][33]; + char switch_topic[33]; + byte mqtt_switch_retain; + uint8_t mqtt_enabled; + uint8_t sleep; + + uint16_t domoticz_switch_idx[4]; + uint16_t domoticz_sensor_idx[12]; + + uint8_t module; + mytmplt my_module; + + uint16_t led_pixels; + uint8_t led_color[5]; + uint8_t led_table; + uint8_t led_dimmer[3]; + uint8_t led_fade; + uint8_t led_speed; + uint8_t led_scheme; + uint8_t led_width; + uint16_t led_wakeup; + +} sysCfg; + +struct TIME_T { + uint8_t Second; + uint8_t Minute; + uint8_t Hour; + uint8_t Wday; // day of week, sunday is day 1 + uint8_t Day; + uint8_t Month; + char MonthName[4]; + uint16_t DayOfYear; + uint16_t Year; + unsigned long Valid; +} rtcTime; + +struct TimeChangeRule +{ + uint8_t week; // 1=First, 2=Second, 3=Third, 4=Fourth, or 0=Last week of the month + uint8_t dow; // day of week, 1=Sun, 2=Mon, ... 7=Sat + uint8_t month; // 1=Jan, 2=Feb, ... 12=Dec + uint8_t hour; // 0-23 + int offset; // offset from UTC in minutes +}; + +TimeChangeRule myDST = { TIME_DST }; // Daylight Saving Time +TimeChangeRule mySTD = { TIME_STD }; // Standard Time + +int Baudrate = APP_BAUDRATE; // Serial interface baud rate +byte SerialInByte; // Received byte +int SerialInByteCounter = 0; // Index in receive buffer +char serialInBuf[INPUT_BUFFER_SIZE + 2]; // Receive buffer +byte Hexcode = 0; // Sonoff dual input flag +uint16_t ButtonCode = 0; // Sonoff dual received code +int16_t savedatacounter; // Counter and flag for config save to Flash or Spiffs +char Version[16]; // Version string from VERSION define +char Hostname[33]; // Composed Wifi hostname +char MQTTClient[33]; // Composed MQTT Clientname +uint8_t mqttcounter = 0; // MQTT connection retry counter +unsigned long timerxs = 0; // State loop timer +int state = 0; // State per second flag +int mqttflag = 2; // MQTT connection messages flag +int otaflag = 0; // OTA state flag +int otaok = 0; // OTA result +int restartflag = 0; // Sonoff restart flag +int wificheckflag = WIFI_RESTART; // Wifi state flag +int uptime = 0; // Current uptime in hours +int tele_period = 0; // Tele period timer +String Log[MAX_LOG_LINES]; // Web log buffer +byte logidx = 0; // Index in Web log buffer +byte Maxdevice = MAX_DEVICE; // Max number of devices supported +int status_update_timer = 0; // Refresh initial status +uint16_t pulse_timer = 0; // Power off timer +uint16_t blink_timer = 0; // Power cycle timer +uint16_t blink_counter = 0; // Number of blink cycles +uint8_t blink_power; // Blink power state +uint8_t blink_mask = 0; // Blink relay active mask +uint8_t blink_powersave; // Blink start power save state +uint16_t mqtt_cmnd_publish = 0; // ignore flag for publish command + +#ifdef USE_MQTT_TLS + WiFiClientSecure espClient; // Wifi Secure Client +#else + WiFiClient espClient; // Wifi Client +#endif +PubSubClient mqttClient(espClient); // MQTT Client +WiFiUDP portUDP; // UDP Syslog and Alexa + +uint8_t power; // Current copy of sysCfg.power +byte syslog_level; // Current copy of sysCfg.syslog_level +uint16_t syslog_timer = 0; // Timer to re-enable syslog_level +byte seriallog_level; // Current copy of sysCfg.seriallog_level +uint16_t seriallog_timer = 0; // Timer to disable Seriallog + +int blinks = 201; // Number of LED blinks +uint8_t blinkstate = 0; // LED state + +uint8_t lastbutton[4] = { NOT_PRESSED, NOT_PRESSED, NOT_PRESSED, NOT_PRESSED }; // Last button states +uint8_t holdcount = 0; // Timer recording button hold +uint8_t multiwindow = 0; // Max time between button presses to record press count +uint8_t multipress = 0; // Number of button presses within multiwindow +uint8_t lastwallswitch[4]; // Last wall switch states + +mytmplt my_module; // Active copy of GPIOs +uint8_t pin[GPIO_MAX]; // Possible pin configurations +uint8_t rel_inverted[4] = { 0 }; // Relay inverted flag (1 = (0 = On, 1 = Off)) +uint8_t led_inverted[4] = { 0 }; // LED inverted flag (1 = (0 = On, 1 = Off)) +uint8_t swt_flg = 0; // Any external switch configured +uint8_t dht_type = 0; // DHT type (DHT11, DHT21 or DHT22) +uint8_t hlw_flg = 0; // Power monitor configured +uint8_t i2c_flg = 0; // I2C configured + +boolean mDNSbegun = false; + +byte hlw_pminflg = 0; +byte hlw_pmaxflg = 0; +byte hlw_uminflg = 0; +byte hlw_umaxflg = 0; +byte hlw_iminflg = 0; +byte hlw_imaxflg = 0; +byte power_steady_cntr; +#ifdef FEATURE_POWER_LIMIT + byte hlw_mkwh_state = 0; + byte hlw_mplr_counter = 0; + uint16_t hlw_mplh_counter = 0; + uint16_t hlw_mplw_counter = 0; +#endif // FEATURE_POWER_LIMIT + +/********************************************************************************************/ + +void CFG_DefaultSet() +{ + memset(&sysCfg, 0x00, sizeof(SYSCFG)); + + sysCfg.cfg_holder = CFG_HOLDER; + sysCfg.saveFlag = 0; + sysCfg.version = VERSION; + sysCfg.bootcount = 0; + sysCfg.migflg = 0; + sysCfg.savedata = SAVE_DATA; + sysCfg.savestate = SAVE_STATE; + sysCfg.module = MODULE; + sysCfg.model = 0; + sysCfg.timezone = APP_TIMEZONE; + strlcpy(sysCfg.otaUrl, OTA_URL, sizeof(sysCfg.otaUrl)); + strlcpy(sysCfg.ex_friendlyname, FRIENDLY_NAME1, sizeof(sysCfg.ex_friendlyname)); + + sysCfg.seriallog_level = SERIAL_LOG_LEVEL; + sysCfg.sta_active = 0; + strlcpy(sysCfg.sta_ssid[0], STA_SSID1, sizeof(sysCfg.sta_ssid[0])); + strlcpy(sysCfg.sta_pwd[0], STA_PASS1, sizeof(sysCfg.sta_pwd[0])); + strlcpy(sysCfg.sta_ssid[1], STA_SSID2, sizeof(sysCfg.sta_ssid[1])); + strlcpy(sysCfg.sta_pwd[1], STA_PASS2, sizeof(sysCfg.sta_pwd[1])); + strlcpy(sysCfg.hostname, WIFI_HOSTNAME, sizeof(sysCfg.hostname)); + sysCfg.sta_config = WIFI_CONFIG_TOOL; + strlcpy(sysCfg.syslog_host, SYS_LOG_HOST, sizeof(sysCfg.syslog_host)); + sysCfg.syslog_port = SYS_LOG_PORT; + sysCfg.syslog_level = SYS_LOG_LEVEL; + sysCfg.webserver = WEB_SERVER; + sysCfg.weblog_level = WEB_LOG_LEVEL; + + strlcpy(sysCfg.mqtt_fingerprint, MQTT_FINGERPRINT, sizeof(sysCfg.mqtt_fingerprint)); + strlcpy(sysCfg.mqtt_host, MQTT_HOST, sizeof(sysCfg.mqtt_host)); + sysCfg.mqtt_port = MQTT_PORT; + strlcpy(sysCfg.mqtt_client, MQTT_CLIENT_ID, sizeof(sysCfg.mqtt_client)); + strlcpy(sysCfg.mqtt_user, MQTT_USER, sizeof(sysCfg.mqtt_user)); + strlcpy(sysCfg.mqtt_pwd, MQTT_PASS, sizeof(sysCfg.mqtt_pwd)); + strlcpy(sysCfg.mqtt_topic, MQTT_TOPIC, sizeof(sysCfg.mqtt_topic)); + strlcpy(sysCfg.button_topic, "0", sizeof(sysCfg.button_topic)); + strlcpy(sysCfg.mqtt_grptopic, MQTT_GRPTOPIC, sizeof(sysCfg.mqtt_grptopic)); + strlcpy(sysCfg.mqtt_subtopic, MQTT_SUBTOPIC, sizeof(sysCfg.mqtt_subtopic)); + sysCfg.mqtt_button_retain = MQTT_BUTTON_RETAIN; + sysCfg.mqtt_power_retain = MQTT_POWER_RETAIN; + sysCfg.value_units = VALUE_UNITS; + sysCfg.message_format = 0; + sysCfg.tele_period = TELE_PERIOD; + + sysCfg.power = APP_POWER; + sysCfg.poweronstate = APP_POWERON_STATE; + sysCfg.pulsetime = APP_PULSETIME; + sysCfg.ledstate = APP_LEDSTATE; + sysCfg.switchmode = SWITCH_MODE; + sysCfg.blinktime = APP_BLINKTIME; + sysCfg.blinkcount = APP_BLINKCOUNT; + sysCfg.sleep = APP_SLEEP; + + strlcpy(sysCfg.domoticz_in_topic, DOMOTICZ_IN_TOPIC, sizeof(sysCfg.domoticz_in_topic)); + strlcpy(sysCfg.domoticz_out_topic, DOMOTICZ_OUT_TOPIC, sizeof(sysCfg.domoticz_out_topic)); + sysCfg.domoticz_update_timer = DOMOTICZ_UPDATE_TIMER; + for (byte i = 0; i < 4; i++) { + sysCfg.domoticz_relay_idx[i] = 0; + sysCfg.domoticz_key_idx[i] = 0; + sysCfg.domoticz_switch_idx[i] = 0; + } + for (byte i = 0; i < 12; i++) sysCfg.domoticz_sensor_idx[i] = 0; + + sysCfg.hlw_pcal = HLW_PREF_PULSE; + sysCfg.hlw_ucal = HLW_UREF_PULSE; + sysCfg.hlw_ical = HLW_IREF_PULSE; + sysCfg.hlw_kWhtoday = 0; + sysCfg.hlw_kWhyesterday = 0; + sysCfg.hlw_kWhdoy = 0; + sysCfg.hlw_pmin = 0; + sysCfg.hlw_pmax = 0; + sysCfg.hlw_umin = 0; + sysCfg.hlw_umax = 0; + sysCfg.hlw_imin = 0; + sysCfg.hlw_imax = 0; + sysCfg.hlw_mpl = 0; // MaxPowerLimit + sysCfg.hlw_mplh = MAX_POWER_HOLD; + sysCfg.hlw_mplw = MAX_POWER_WINDOW; + sysCfg.hlw_mspl = 0; // MaxSafePowerLimit + sysCfg.hlw_msplh = SAFE_POWER_HOLD; + sysCfg.hlw_msplw = SAFE_POWER_WINDOW; + sysCfg.hlw_mkwh = 0; // MaxEnergy + sysCfg.hlw_mkwhs = 0; // MaxEnergyStart + + sysCfg.ws_pixels = WS2812_LEDS; + sysCfg.ws_red = 255; + sysCfg.ws_green = 0; + sysCfg.ws_blue = 0; + sysCfg.ws_ledtable = 0; + sysCfg.ws_dimmer = 8; + sysCfg.ws_fade = 0; + sysCfg.ws_speed = 1; + sysCfg.ws_scheme = 0; + sysCfg.ws_width = 1; + sysCfg.ws_wakeup = 0; + + strlcpy(sysCfg.friendlyname[0], FRIENDLY_NAME1, sizeof(sysCfg.friendlyname[0])); + strlcpy(sysCfg.friendlyname[1], FRIENDLY_NAME2, sizeof(sysCfg.friendlyname[1])); + strlcpy(sysCfg.friendlyname[2], FRIENDLY_NAME3, sizeof(sysCfg.friendlyname[2])); + strlcpy(sysCfg.friendlyname[3], FRIENDLY_NAME4, sizeof(sysCfg.friendlyname[3])); + + for (byte i = 0; i < MAX_GPIO_PIN; i++) sysCfg.my_module.gp.io[i] = 0; + + sysCfg.led_pixels = 0; + for (byte i = 0; i < 5; i++) sysCfg.led_color[i] = 255; + sysCfg.led_table = 0; + for (byte i = 0; i < 3; i++) sysCfg.led_dimmer[i] = 10; + sysCfg.led_fade = 0; + sysCfg.led_speed = 0; + sysCfg.led_scheme = 0; + sysCfg.led_width = 0; + sysCfg.led_wakeup = 0; + + strlcpy(sysCfg.switch_topic, "0", sizeof(sysCfg.switch_topic)); + sysCfg.mqtt_switch_retain = MQTT_SWITCH_RETAIN; + sysCfg.mqtt_enabled = MQTT_USE; + +} + +void CFG_Default() +{ + addLog_P(LOG_LEVEL_NONE, PSTR("Config: Use default configuration")); + CFG_DefaultSet(); + CFG_Save(); +} + +void CFG_Migrate_Part2() +{ + addLog_P(LOG_LEVEL_NONE, PSTR("Config: Migrating configuration")); + CFG_DefaultSet(); + + sysCfg.seriallog_level = sysCfg2.seriallog_level; + sysCfg.syslog_level = sysCfg2.syslog_level; + strlcpy(sysCfg.syslog_host, sysCfg2.syslog_host, sizeof(sysCfg.syslog_host)); + strlcpy(sysCfg.sta_ssid[0], sysCfg2.sta_ssid1, sizeof(sysCfg.sta_ssid[0])); + strlcpy(sysCfg.sta_pwd[0], sysCfg2.sta_pwd1, sizeof(sysCfg.sta_pwd[0])); + strlcpy(sysCfg.otaUrl, sysCfg2.otaUrl, sizeof(sysCfg.otaUrl)); + strlcpy(sysCfg.mqtt_host, sysCfg2.mqtt_host, sizeof(sysCfg.mqtt_host)); + strlcpy(sysCfg.mqtt_grptopic, sysCfg2.mqtt_grptopic, sizeof(sysCfg.mqtt_grptopic)); + strlcpy(sysCfg.mqtt_topic, sysCfg2.mqtt_topic, sizeof(sysCfg.mqtt_topic)); + strlcpy(sysCfg.button_topic, sysCfg2.mqtt_topic2, sizeof(sysCfg.button_topic)); + strlcpy(sysCfg.mqtt_subtopic, sysCfg2.mqtt_subtopic, sizeof(sysCfg.mqtt_subtopic)); + sysCfg.timezone = sysCfg2.timezone; + sysCfg.power = sysCfg2.power; + if (sysCfg2.version >= 0x01000D00) { // 1.0.13 + sysCfg.ledstate = sysCfg2.ledstate; + } + if (sysCfg2.version >= 0x01001600) { // 1.0.22 + sysCfg.mqtt_port = sysCfg2.mqtt_port; + strlcpy(sysCfg.mqtt_client, sysCfg2.mqtt_client, sizeof(sysCfg.mqtt_client)); + strlcpy(sysCfg.mqtt_user, sysCfg2.mqtt_user, sizeof(sysCfg.mqtt_user)); + strlcpy(sysCfg.mqtt_pwd, sysCfg2.mqtt_pwd, sizeof(sysCfg.mqtt_pwd)); + strlcpy(sysCfg.ex_friendlyname, sysCfg2.mqtt_client, sizeof(sysCfg.ex_friendlyname)); + } + if (sysCfg2.version >= 0x01001700) { // 1.0.23 + sysCfg.webserver = sysCfg2.webserver; + } + if (sysCfg2.version >= 0x01001A00) { // 1.0.26 + sysCfg.bootcount = sysCfg2.bootcount; + strlcpy(sysCfg.hostname, sysCfg2.hostname, sizeof(sysCfg.hostname)); + sysCfg.syslog_port = sysCfg2.syslog_port; + } + if (sysCfg2.version >= 0x01001B00) { // 1.0.27 + sysCfg.weblog_level = sysCfg2.weblog_level; + } + if (sysCfg2.version >= 0x01001C00) { // 1.0.28 + sysCfg.tele_period = sysCfg2.tele_period; + if ((sysCfg.tele_period > 0) && (sysCfg.tele_period < 10)) sysCfg.tele_period = 10; // Do not allow periods < 10 seconds + } + if (sysCfg2.version >= 0x01002000) { // 1.0.32 + sysCfg.sta_config = sysCfg2.sta_config; + } + if (sysCfg2.version >= 0x01002300) { // 1.0.35 + sysCfg.savedata = sysCfg2.savedata; + } + if (sysCfg2.version >= 0x02000000) { // 2.0.0 + sysCfg.model = sysCfg2.model; + } + if (sysCfg2.version >= 0x02000300) { // 2.0.3 + sysCfg.mqtt_button_retain = sysCfg2.mqtt_retain; + sysCfg.savestate = sysCfg2.savestate; + } + if (sysCfg2.version >= 0x02000500) { // 2.0.5 + sysCfg.hlw_pcal = sysCfg2.hlw_pcal; + sysCfg.hlw_ucal = sysCfg2.hlw_ucal; + sysCfg.hlw_ical = sysCfg2.hlw_ical; + sysCfg.hlw_kWhyesterday = sysCfg2.hlw_kWhyesterday; + sysCfg.value_units = sysCfg2.value_units; + } + if (sysCfg2.version >= 0x02000600) { // 2.0.6 + sysCfg.hlw_pmin = sysCfg2.hlw_pmin; + sysCfg.hlw_pmax = sysCfg2.hlw_pmax; + sysCfg.hlw_umin = sysCfg2.hlw_umin; + sysCfg.hlw_umax = sysCfg2.hlw_umax; + sysCfg.hlw_imin = sysCfg2.hlw_imin; + sysCfg.hlw_imax = sysCfg2.hlw_imax; + } + if (sysCfg2.version >= 0x02000700) { // 2.0.7 + sysCfg.message_format = 0; + strlcpy(sysCfg.domoticz_in_topic, sysCfg2.domoticz_in_topic, sizeof(sysCfg.domoticz_in_topic)); + strlcpy(sysCfg.domoticz_out_topic, sysCfg2.domoticz_out_topic, sizeof(sysCfg.domoticz_out_topic)); + sysCfg.domoticz_update_timer = sysCfg2.domoticz_update_timer; + for (byte i = 0; i < 4; i++) { + sysCfg.domoticz_relay_idx[i] = sysCfg2.domoticz_relay_idx[i]; + sysCfg.domoticz_key_idx[i] = sysCfg2.domoticz_key_idx[i]; + } + + sysCfg.hlw_mpl = sysCfg2.hlw_mpl; // MaxPowerLimit + sysCfg.hlw_mplh = sysCfg2.hlw_mplh; + sysCfg.hlw_mplw = sysCfg2.hlw_mplw; + sysCfg.hlw_mspl = sysCfg2.hlw_mspl; // MaxSafePowerLimit + sysCfg.hlw_msplh = sysCfg2.hlw_msplh; + sysCfg.hlw_msplw = sysCfg2.hlw_msplw; + sysCfg.hlw_mkwh = sysCfg2.hlw_mkwh; // MaxEnergy + sysCfg.hlw_mkwhs = sysCfg2.hlw_mkwhs; // MaxEnergyStart + } + if (sysCfg2.version >= 0x02001000) { // 2.0.16 + sysCfg.hlw_kWhtoday = sysCfg2.hlw_kWhtoday; + sysCfg.hlw_kWhdoy = sysCfg2.hlw_kWhdoy; + } + if (sysCfg2.version >= 0x02001200) { // 2.0.18 + sysCfg.switchmode = sysCfg2.switchmode; + } + if (sysCfg2.version >= 0x02010000) { // 2.1.0 + strlcpy(sysCfg.mqtt_fingerprint, sysCfg2.mqtt_fingerprint, sizeof(sysCfg.mqtt_fingerprint)); + } + if (sysCfg2.version >= 0x02010200) { // 2.1.2 + sysCfg.sta_active = sysCfg2.sta_active; + strlcpy(sysCfg.sta_ssid[1], sysCfg2.sta_ssid2, sizeof(sysCfg.sta_ssid[1])); + strlcpy(sysCfg.sta_pwd[1], sysCfg2.sta_pwd2, sizeof(sysCfg.sta_pwd[1])); + } + CFG_Save(); +} + +void CFG_Delta() +{ + if (sysCfg.version != VERSION) { // Fix version dependent changes + if (sysCfg.version < 0x03000600) { // 3.0.6 - Add parameter + sysCfg.pulsetime = APP_PULSETIME; + } + if (sysCfg.version < 0x03010100) { // 3.1.1 - Add parameter + sysCfg.poweronstate = APP_POWERON_STATE; + } + if (sysCfg.version < 0x03010200) { // 3.1.2 - Add parameter + if (sysCfg.poweronstate == 2) sysCfg.poweronstate = 3; + } + if (sysCfg.version < 0x03010600) { // 3.1.6 - Add parameter + sysCfg.blinktime = APP_BLINKTIME; + sysCfg.blinkcount = APP_BLINKCOUNT; + } + if (sysCfg.version < 0x03011000) { // 3.1.16 - Add parameter + getClient(sysCfg.ex_friendlyname, sysCfg.mqtt_client, sizeof(sysCfg.ex_friendlyname)); + } + if (sysCfg.version < 0x03020400) { // 3.2.4 - Add parameter + sysCfg.ws_pixels = WS2812_LEDS; + sysCfg.ws_red = 255; + sysCfg.ws_green = 0; + sysCfg.ws_blue = 0; + sysCfg.ws_ledtable = 0; + sysCfg.ws_dimmer = 8; + sysCfg.ws_fade = 0; + sysCfg.ws_speed = 1; + sysCfg.ws_scheme = 0; + sysCfg.ws_width = 1; + sysCfg.ws_wakeup = 0; + } + if (sysCfg.version < 0x03020500) { // 3.2.5 - Add parameter + strlcpy(sysCfg.friendlyname[0], sysCfg.ex_friendlyname, sizeof(sysCfg.friendlyname[0])); + strlcpy(sysCfg.friendlyname[1], FRIENDLY_NAME2, sizeof(sysCfg.friendlyname[1])); + strlcpy(sysCfg.friendlyname[2], FRIENDLY_NAME3, sizeof(sysCfg.friendlyname[2])); + strlcpy(sysCfg.friendlyname[3], FRIENDLY_NAME4, sizeof(sysCfg.friendlyname[3])); + } + if (sysCfg.version < 0x03020800) { // 3.2.8 - Add parameter + strlcpy(sysCfg.switch_topic, sysCfg.button_topic, sizeof(sysCfg.switch_topic)); + sysCfg.mqtt_switch_retain = MQTT_SWITCH_RETAIN; + sysCfg.mqtt_enabled = MQTT_USE; + } + if (sysCfg.version < 0x03020C00) { // 3.2.12 - Add parameter + sysCfg.sleep = APP_SLEEP; + } + if (sysCfg.version < 0x03090204) { // 3.9.2d - Add parameter + for (byte i = 0; i < 4; i++) sysCfg.domoticz_switch_idx[i] = 0; + for (byte i = 0; i < 12; i++) sysCfg.domoticz_sensor_idx[i] = 0; + + sysCfg.module = MODULE; + for (byte i = 0; i < MAX_GPIO_PIN; i++) sysCfg.my_module.gp.io[i] = 0; + + sysCfg.led_pixels = 0; + for (byte i = 0; i < 5; i++) sysCfg.led_color[i] = 255; + sysCfg.led_table = 0; + for (byte i = 0; i < 3; i++) sysCfg.led_dimmer[i] = 10; + sysCfg.led_fade = 0; + sysCfg.led_speed = 0; + sysCfg.led_scheme = 0; + sysCfg.led_width = 0; + sysCfg.led_wakeup = 0; + } + + sysCfg.version = VERSION; + } +} + +/********************************************************************************************/ + +void getClient(char* output, const char* input, byte size) +{ + char *token; + uint8_t digits = 0; + + if (strstr(input, "%")) { + strlcpy(output, input, size); + token = strtok(output, "%"); + if (strstr(input, "%") == input) { + output[0] = '\0'; + } else { + token = strtok(NULL, ""); + } + if (token != NULL) { + digits = atoi(token); + if (digits) { + snprintf_P(output, size, PSTR("%s%c0%dX"), output, '%', digits); + snprintf_P(output, size, output, ESP.getChipId()); + } + } + } + if (!digits) strlcpy(output, input, size); +} + +void setRelay(uint8_t power) +{ + if ((sysCfg.module == SONOFF_DUAL) || (sysCfg.module == CH4)) { + Serial.write(0xA0); + Serial.write(0x04); + Serial.write(power); + Serial.write(0xA1); + Serial.write('\n'); + Serial.flush(); + } else { + if (sysCfg.module == SONOFF_LED) { + sl_setColor(power &1); + } else { + for (byte i = 0; i < Maxdevice; i++) { + if (pin[GPIO_REL1 +i] < 99) digitalWrite(pin[GPIO_REL1 +i], power & 0x1); + power >>= 1; + } + } + } + power_steady_cntr = 2; +} + +void setLed(uint8_t state) +{ + if (state) state = 1; + digitalWrite(pin[GPIO_LED1], (led_inverted[0]) ? !state : state); +} + +void sl_setDim(uint8_t *my_color) +{ + float newDim, fmyCld, fmyWrm, fmyRed, fmyGrn, fmyBlu; + + newDim = 100 / (float)sysCfg.led_dimmer[0]; + fmyCld = (float)sysCfg.led_color[0] / newDim; + newDim = 100 / (float)sysCfg.led_dimmer[1]; + fmyWrm = (float)sysCfg.led_color[1] / newDim; + newDim = 100 / (float)sysCfg.led_dimmer[2]; + fmyRed = (float)sysCfg.led_color[2] / newDim; + fmyGrn = (float)sysCfg.led_color[3] / newDim; + fmyBlu = (float)sysCfg.led_color[4] / newDim; + my_color[0] = (uint8_t)fmyCld; + my_color[1] = (uint8_t)fmyWrm; + my_color[2] = (uint8_t)fmyRed; + my_color[3] = (uint8_t)fmyGrn; + my_color[4] = (uint8_t)fmyBlu; +} + +void sl_setColor(byte type) +{ +// 0 = Off +// 1 = On +// 2 = Dim cold +// 3 = Dim Warm +// 4 = Dim color + + uint8_t my_color[5]; + + sl_setDim(my_color); + if (type == 0) { + for (byte i = 0; i < 5; i++) { + if (pin[GPIO_PWM0 +i] < 99) analogWrite(pin[GPIO_PWM0 +i], 0); + } + } + else if (type == 1) { + for (byte i = 0; i < 5; i++) { + if (pin[GPIO_PWM0 +i] < 99) analogWrite(pin[GPIO_PWM0 +i], my_color[i]); + } + } + else if (type == 2) { // Cold + if (pin[GPIO_PWM0] < 99) analogWrite(pin[GPIO_PWM0], my_color[0]); + } + else if (type == 3) { // Warm + if (pin[GPIO_PWM1] < 99) analogWrite(pin[GPIO_PWM1], my_color[1]); + } + else if (type == 4) { // Color + for (byte i = 2; i < 5; i++) { + if (pin[GPIO_PWM0 +i] < 99) analogWrite(pin[GPIO_PWM0 +i], my_color[i]); + } + } +} + +void json2legacy(char* stopic, char* svalue) +{ + char *p, *token; + uint16_t i, j; + + if (!strstr(svalue, "{\"")) return; // No JSON + +// stopic = stat/sonoff/RESULT +// svalue = {"POWER2":"ON"} +// --> stopic = "stat/sonoff/POWER2", svalue = "ON" +// svalue = {"Upgrade":{"Version":"2.1.2", "OtaUrl":"%s"}} +// --> stopic = "stat/sonoff/UPGRADE", svalue = "2.1.2" +// svalue = {"SerialLog":2} +// --> stopic = "stat/sonoff/SERIALLOG", svalue = "2" +// svalue = {"POWER":""} +// --> stopic = "stat/sonoff/POWER", svalue = "" + + token = strtok(svalue, "{\""); // Topic + p = strrchr(stopic, '/') +1; + i = p - stopic; + for (j = 0; j < strlen(token)+1; j++) stopic[i+j] = toupper(token[j]); + token = strtok(NULL, "\""); // : or :3} or :3, or :{ + if (strstr(token, ":{")) { + token = strtok(NULL, "\""); // Subtopic + token = strtok(NULL, "\""); // : or :3} or :3, + } + if (strlen(token) > 1) { + token++; + p = strchr(token, ','); + if (!p) p = strchr(token, '}'); + i = p - token; + token[i] = '\0'; // Value + } else { + token = strtok(NULL, "\""); // Value or , or } + if ((token[0] == ',') || (token[0] == '}')) { // Empty parameter + token = NULL; + } + } + if (token == NULL) { + svalue[0] = '\0'; + } else { + memcpy(svalue, token, strlen(token)+1); + } +} + +uint32_t Atoh(char *s) +{ + uint32_t value = 0, digit; + int8_t c; + + while((c = *s++)){ + if('0' <= c && c <= '9') + digit = c - '0'; + else if('A' <= c && c <= 'F') + digit = c - 'A' + 10; + else if('a' <= c && c<= 'f') + digit = c - 'a' + 10; + else break; + value = (value << 4) | digit; + } + return value; +} + +/********************************************************************************************/ + +void mqtt_publish_sec(const char* topic, const char* data, boolean retained) +{ + char log[TOPSZ+MESSZ]; + + if (sysCfg.mqtt_enabled) { + if (mqttClient.publish(topic, data, retained)) { + snprintf_P(log, sizeof(log), PSTR("MQTT: %s = %s%s"), topic, data, (retained) ? " (retained)" : ""); +// mqttClient.loop(); // Do not use here! Will block previous publishes + } else { + snprintf_P(log, sizeof(log), PSTR("RSLT: %s = %s"), topic, data); + } + } else { + snprintf_P(log, sizeof(log), PSTR("RSLT: %s = %s"), strrchr(topic,'/')+1, data); + } + + addLog(LOG_LEVEL_INFO, log); + if (sysCfg.ledstate &0x04) blinks++; +} + +void mqtt_publish(const char* topic, const char* data, boolean retained) +{ + char *me; + + if (!strcmp(SUB_PREFIX,PUB_PREFIX)) { + me = strstr(topic,SUB_PREFIX); + if (me == topic) mqtt_cmnd_publish += 8; + } + mqtt_publish_sec(topic, data, retained); +} + +void mqtt_publish(const char* topic, const char* data) +{ + mqtt_publish(topic, data, false); +} + +void mqtt_publishPowerState(byte device) +{ + char stopic[TOPSZ], svalue[MESSZ], sdevice[10]; + + if ((device < 1) || (device > Maxdevice)) device = 1; + snprintf_P(sdevice, sizeof(sdevice), PSTR("%d"), device); + snprintf_P(stopic, sizeof(stopic), PSTR("%s/%s/RESULT"), PUB_PREFIX, sysCfg.mqtt_topic); + snprintf_P(svalue, sizeof(svalue), PSTR("{\"%s%s\":\"%s\"}"), + sysCfg.mqtt_subtopic, (Maxdevice > 1) ? sdevice : "", (power & (0x01 << (device -1))) ? MQTT_STATUS_ON : MQTT_STATUS_OFF); + mqtt_publish(stopic, svalue); + json2legacy(stopic, svalue); + mqtt_publish(stopic, svalue, sysCfg.mqtt_power_retain); +} + +void mqtt_publishPowerBlinkState(byte device) +{ + char stopic[TOPSZ], svalue[MESSZ], sdevice[10]; + + if ((device < 1) || (device > Maxdevice)) device = 1; + snprintf_P(sdevice, sizeof(sdevice), PSTR("%d"), device); + snprintf_P(stopic, sizeof(stopic), PSTR("%s/%s/RESULT"), PUB_PREFIX, sysCfg.mqtt_topic); + snprintf_P(svalue, sizeof(svalue), PSTR("{\"%s%s\":\"BLINK %s\"}"), + sysCfg.mqtt_subtopic, (Maxdevice > 1) ? sdevice : "", (blink_mask & (0x01 << (device -1))) ? MQTT_STATUS_ON : MQTT_STATUS_OFF); + mqtt_publish(stopic, svalue); +} + +void mqtt_connected() +{ + char stopic[TOPSZ], svalue[MESSZ]; + + if (sysCfg.mqtt_enabled) { + snprintf_P(stopic, sizeof(stopic), PSTR("%s/%s/#"), SUB_PREFIX, sysCfg.mqtt_topic); + mqttClient.subscribe(stopic); + mqttClient.loop(); // Solve LmacRxBlk:1 messages + snprintf_P(stopic, sizeof(stopic), PSTR("%s/%s/#"), SUB_PREFIX, sysCfg.mqtt_grptopic); + mqttClient.subscribe(stopic); + mqttClient.loop(); // Solve LmacRxBlk:1 messages + snprintf_P(stopic, sizeof(stopic), PSTR("%s/%s/#"), SUB_PREFIX, MQTTClient); // Fall back topic + mqttClient.subscribe(stopic); + mqttClient.loop(); // Solve LmacRxBlk:1 messages +#ifdef USE_DOMOTICZ + domoticz_mqttSubscribe(); +#endif // USE_DOMOTICZ + } + + if (mqttflag) { + snprintf_P(stopic, sizeof(stopic), PSTR("%s/%s/RESULT"), PUB_PREFIX2, sysCfg.mqtt_topic); + snprintf_P(svalue, sizeof(svalue), PSTR("{\"Info1\":{\"Module\":\"%s\", \"Version\":\"%s\", \"FallbackTopic\":\"%s\", \"GroupTopic\":\"%s\"}}"), + my_module.name, Version, MQTTClient, sysCfg.mqtt_grptopic); + mqtt_publish(stopic, svalue); +#ifdef USE_WEBSERVER + if (sysCfg.webserver) { + snprintf_P(svalue, sizeof(svalue), PSTR("{\"Info2\":{\"WebserverMode\":\"%s\", \"Hostname\":\"%s\", \"IPaddress\":\"%s\"}}"), + (sysCfg.webserver == 2) ? "Admin" : "User", Hostname, WiFi.localIP().toString().c_str()); + mqtt_publish(stopic, svalue); + } +#endif // USE_WEBSERVER + if (sysCfg.mqtt_enabled && (MQTT_MAX_PACKET_SIZE < (TOPSZ+MESSZ))) { + snprintf_P(svalue, sizeof(svalue), PSTR("{\"Warning1\":\"Change MQTT_MAX_PACKET_SIZE in libraries/PubSubClient.h to at least %d\"}"), TOPSZ+MESSZ); + mqtt_publish(stopic, svalue); + } + if (!spiffsPresent()) { + snprintf_P(svalue, sizeof(svalue), PSTR("{\"Warning2\":\"No persistent config. Please reflash with at least 16K SPIFFS\"}")); + mqtt_publish(stopic, svalue); + } + if (sysCfg.tele_period) tele_period = sysCfg.tele_period -9; + status_update_timer = 2; +#ifdef USE_DOMOTICZ + domoticz_setUpdateTimer(2); +#endif // USE_DOMOTICZ + } + mqttflag = 0; +} + +void mqtt_reconnect() +{ + char stopic[TOPSZ], svalue[TOPSZ], log[LOGSZ]; + + mqttcounter = MQTT_RETRY_SECS; + + if (!sysCfg.mqtt_enabled) { + mqtt_connected(); + return; + } +#if defined(USE_WEMO_EMULATION) || defined(USE_HUE_EMULATION) + UDP_Disconnect(); +#endif // USE_WEMO_EMULATION || USE_HUE_EMULATION + if (mqttflag > 1) { +#ifdef USE_MQTT_TLS + addLog_P(LOG_LEVEL_INFO, PSTR("MQTT: Verify TLS fingerprint...")); + if (!espClient.connect(sysCfg.mqtt_host, sysCfg.mqtt_port)) { + snprintf_P(log, sizeof(log), PSTR("MQTT: TLS CONNECT FAILED USING WRONG MQTTHost (%s) or MQTTPort (%d). Retry in %d seconds"), + sysCfg.mqtt_host, sysCfg.mqtt_port, mqttcounter); + addLog(LOG_LEVEL_DEBUG, log); + return; + } + if (espClient.verify(sysCfg.mqtt_fingerprint, sysCfg.mqtt_host)) { + addLog_P(LOG_LEVEL_INFO, PSTR("MQTT: Verified")); + } else { + addLog_P(LOG_LEVEL_DEBUG, PSTR("MQTT: WARNING - Insecure connection due to invalid Fingerprint")); + } +#endif // USE_MQTT_TLS + mqttClient.setCallback(mqttDataCb); + mqttflag = 1; + mqttcounter = 1; + return; + } + + addLog_P(LOG_LEVEL_INFO, PSTR("MQTT: Attempting connection...")); +#ifndef USE_MQTT_TLS +#ifdef USE_DISCOVERY +#ifdef MQTT_HOST_DISCOVERY + mdns_discoverMQTTServer(); +#endif // MQTT_HOST_DISCOVERY +#endif // USE_DISCOVERY +#endif // USE_MQTT_TLS + mqttClient.setServer(sysCfg.mqtt_host, sysCfg.mqtt_port); + snprintf_P(stopic, sizeof(stopic), PSTR("%s/%s/LWT"), PUB_PREFIX2, sysCfg.mqtt_topic); + snprintf_P(svalue, sizeof(svalue), PSTR("Offline")); + if (mqttClient.connect(MQTTClient, sysCfg.mqtt_user, sysCfg.mqtt_pwd, stopic, 1, true, svalue)) { + addLog_P(LOG_LEVEL_INFO, PSTR("MQTT: Connected")); + mqttcounter = 0; + snprintf_P(svalue, sizeof(svalue), PSTR("Online")); + mqtt_publish(stopic, svalue, true); + mqtt_connected(); + } else { + snprintf_P(log, sizeof(log), PSTR("MQTT: CONNECT FAILED, rc %d. Retry in %d seconds"), mqttClient.state(), mqttcounter); + addLog(LOG_LEVEL_DEBUG, log); + } +} + +void mqttDataCb(char* topic, byte* data, unsigned int data_len) +{ + char *str; + char svalue[MESSZ]; + + if (!strcmp(SUB_PREFIX,PUB_PREFIX)) { + str = strstr(topic,SUB_PREFIX); + if ((str == topic) && mqtt_cmnd_publish) { + if (mqtt_cmnd_publish > 8) mqtt_cmnd_publish -= 8; else mqtt_cmnd_publish = 0; + return; + } + } + + uint16_t i = 0, grpflg = 0, index; + char topicBuf[TOPSZ], dataBuf[data_len+1], dataBufUc[MESSZ]; + char *p, *mtopic = NULL, *type = NULL; + char stopic[TOPSZ], stemp1[TOPSZ], stemp2[10]; + + strncpy(topicBuf, topic, sizeof(topicBuf)); + memcpy(dataBuf, data, sizeof(dataBuf)); + dataBuf[sizeof(dataBuf)-1] = 0; + + snprintf_P(svalue, sizeof(svalue), PSTR("RSLT: Receive topic %s, data size %d, data %s"), topicBuf, data_len, dataBuf); + addLog(LOG_LEVEL_DEBUG_MORE, svalue); +// if (LOG_LEVEL_DEBUG_MORE <= seriallog_level) Serial.println(dataBuf); + +#ifdef USE_DOMOTICZ + if (sysCfg.mqtt_enabled) { + if (domoticz_mqttData(topicBuf, sizeof(topicBuf), dataBuf, sizeof(dataBuf))) return; + } +#endif // USE_DOMOTICZ + + memmove(topicBuf, topicBuf+sizeof(SUB_PREFIX), sizeof(topicBuf)-sizeof(SUB_PREFIX)); // Remove SUB_PREFIX + + i = 0; + for (str = strtok_r(topicBuf, "/", &p); str && i < 2; str = strtok_r(NULL, "/", &p)) { + switch (i++) { + case 0: // Topic / GroupTopic / DVES_123456 + mtopic = str; + break; + case 1: // TopicIndex / Text + type = str; + } + } + if (!strcmp(mtopic, sysCfg.mqtt_grptopic)) grpflg = 1; + + index = 1; + if (type != NULL) { + for (i = 0; i < strlen(type); i++) { + type[i] = toupper(type[i]); + if (isdigit(type[i])) { + index = atoi(type +i); + break; + } + } + type[i] = '\0'; + } + + for (i = 0; i <= sizeof(dataBufUc); i++) dataBufUc[i] = toupper(dataBuf[i]); + + snprintf_P(svalue, sizeof(svalue), PSTR("RSLT: DataCb Topic %s, Group %d, Index %d, Type %s, Data %s (%s)"), + mtopic, grpflg, index, type, dataBuf, dataBufUc); + addLog(LOG_LEVEL_DEBUG, svalue); + + snprintf_P(stopic, sizeof(stopic), PSTR("%s/%s/RESULT"), PUB_PREFIX, sysCfg.mqtt_topic); + if (type != NULL) { + snprintf_P(svalue, sizeof(svalue), PSTR("{\"Command\":\"Error\"}")); + if (sysCfg.ledstate &0x02) blinks++; + + if (!strcmp(dataBufUc,"?")) data_len = 0; + int16_t payload = atoi(dataBuf); // -32766 - 32767 + uint16_t payload16 = atoi(dataBuf); // 0 - 65535 + if (!strcmp(dataBufUc,"OFF") || !strcmp(dataBufUc,"STOP")) payload = 0; + if (!strcmp(dataBufUc,"ON") || !strcmp(dataBufUc,"START") || !strcmp(dataBufUc,"USER")) payload = 1; + if (!strcmp(dataBufUc,"TOGGLE") || !strcmp(dataBufUc,"ADMIN")) payload = 2; + if (!strcmp(dataBufUc,"BLINK")) payload = 3; + if (!strcmp(dataBufUc,"BLINKOFF")) payload = 4; + + if ((!strcmp(type,"POWER") || !strcmp(type,"LIGHT")) && (index > 0) && (index <= Maxdevice)) { + snprintf_P(sysCfg.mqtt_subtopic, sizeof(sysCfg.mqtt_subtopic), PSTR("%s"), type); + if ((data_len == 0) || (payload > 4)) payload = 9; + do_cmnd_power(index, payload); + return; + } + else if (!strcmp(type,"STATUS")) { + if ((data_len == 0) || (payload < 0) || (payload > MAX_STATUS)) payload = 99; + publish_status(payload); + return; + } + else if ((sysCfg.module != MOTOR) && !strcmp(type,"POWERONSTATE")) { + if ((data_len > 0) && (payload >= 0) && (payload <= 3)) { + sysCfg.poweronstate = payload; + } + snprintf_P(svalue, sizeof(svalue), PSTR("{\"PowerOnState\":%d}"), sysCfg.poweronstate); + } + else if (!strcmp(type,"PULSETIME")) { + if (data_len > 0) { + sysCfg.pulsetime = payload16; // 0 - 65535 + pulse_timer = 0; + } + snprintf_P(svalue, sizeof(svalue), PSTR("{\"PulseTime\":%d}"), sysCfg.pulsetime); + } + else if (!strcmp(type,"BLINKTIME")) { + if ((data_len > 0) && (payload > 2) && (payload <= 3600)) { + sysCfg.blinktime = payload; + if (blink_timer) blink_timer = sysCfg.blinktime; + } + snprintf_P(svalue, sizeof(svalue), PSTR("{\"BlinkTime\":%d}"), sysCfg.blinktime); + } + else if (!strcmp(type,"BLINKCOUNT")) { + if (data_len > 0) { + sysCfg.blinkcount = payload16; // 0 - 65535 + if (blink_counter) blink_counter = sysCfg.blinkcount *2; + } + snprintf_P(svalue, sizeof(svalue), PSTR("{\"BlinkCount\":%d}"), sysCfg.blinkcount); + } + +/*** Sonoff Led Commands *********************************************************************/ + +/* + else if ((sysCfg.module == SONOFF_LED) && !strcmp(type,"COLOR"))) { + if ((data_len > 0) && (payload >= 0) && (payload <= 255)) { + sysCfg.led_color[index -1] = payload; + sl_setColor(4); + } + snprintf_P(svalue, sizeof(svalue), PSTR("{\"Color\":\"%s\"}"), sysCfg.led_color[index -1]); + } + else if ((sysCfg.module == SONOFF_LED) && !strcmp(type,"CWRGB") && (index > 0) && (index <= 5)) { + if ((data_len > 0) && (payload >= 0) && (payload <= 255)) { + sysCfg.led_color[index -1] = payload; + sl_setColor(1); + } + snprintf_P(svalue, sizeof(svalue), PSTR("{\"CWRGB%d\":%d}"), index, sysCfg.led_color[index -1]); + } +*/ + else if ((sysCfg.module == SONOFF_LED) && !strcmp(type,"DIMMER") && (index > 0) && (index <= 3)) { + if ((data_len > 0) && (payload >= 0) && (payload <= 100)) { + sysCfg.led_dimmer[index -1] = payload; + power = 1; +#ifdef USE_DOMOTICZ + mqtt_publishDomoticzPowerState(index); +#endif // USE_DOMOTICZ + sl_setColor(index +1); + } + snprintf_P(svalue, sizeof(svalue), PSTR("{\"Dimmer%d\":%d}"), index, sysCfg.led_dimmer[index -1]); + } + +/*********************************************************************************************/ + + else if (!strcmp(type,"SAVEDATA")) { + if ((data_len > 0) && (payload >= 0) && (payload <= 3600)) { + sysCfg.savedata = payload; + savedatacounter = sysCfg.savedata; + } + if (sysCfg.savestate) sysCfg.power = power; + CFG_Save(); + if (sysCfg.savedata > 1) snprintf_P(stemp1, sizeof(stemp1), PSTR("Every %d seconds"), sysCfg.savedata); + snprintf_P(svalue, sizeof(svalue), PSTR("{\"SaveData\":\"%s\"}"), (sysCfg.savedata) ? (sysCfg.savedata > 1) ? stemp1 : MQTT_STATUS_ON : MQTT_STATUS_OFF); + } + else if (!strcmp(type,"SAVESTATE")) { + if ((data_len > 0) && (payload >= 0) && (payload <= 1)) { + sysCfg.savestate = payload; + } + snprintf_P(svalue, sizeof(svalue), PSTR("{\"SaveState\":\"%s\"}"), (sysCfg.savestate) ? MQTT_STATUS_ON : MQTT_STATUS_OFF); + } + else if (!strcmp(type,"MODULE")) { + if ((data_len > 0) && (payload > 0) && (payload <= MAXMODULE)) { + sysCfg.module = payload -1; + restartflag = 2; + } + snprintf_P(stemp1, sizeof(stemp1), modules[sysCfg.module].name); + snprintf_P(svalue, sizeof(svalue), PSTR("{\"Module\":\"%s (%d)\"}"), stemp1, sysCfg.module +1); + } + else if (!strcmp(type,"MODULES")) { + snprintf_P(svalue, sizeof(svalue), PSTR("{\"Modules1\":\""), svalue); + byte jsflg = 0; + for (byte i = 0; i < 11; i++) { + if (jsflg) snprintf_P(svalue, sizeof(svalue), PSTR("%s, "), svalue); + jsflg = 1; + snprintf_P(stemp1, sizeof(stemp1), modules[i].name); + snprintf_P(svalue, sizeof(svalue), PSTR("%s%s (%d)"), svalue, stemp1, i +1); + } + snprintf_P(svalue, sizeof(svalue), PSTR("%s\"}"), svalue); + mqtt_publish(stopic, svalue); + snprintf_P(svalue, sizeof(svalue), PSTR("{\"Modules2\":\""), svalue); + jsflg = 0; + for (byte i = 11; i < MAXMODULE; i++) { + if (jsflg) snprintf_P(svalue, sizeof(svalue), PSTR("%s, "), svalue); + jsflg = 1; + snprintf_P(stemp1, sizeof(stemp1), modules[i].name); + snprintf_P(svalue, sizeof(svalue), PSTR("%s%s (%d)"), svalue, stemp1, i +1); + } + snprintf_P(svalue, sizeof(svalue), PSTR("%s\"}"), svalue); + } + else if (!strcmp(type,"GPIO") && (index < MAX_GPIO_PIN)) { + mytmplt cmodule; + memcpy_P(&cmodule, &modules[sysCfg.module], sizeof(cmodule)); + if ((data_len > 0) && (cmodule.gp.io[index] == GPIO_USER) && (payload >= GPIO_SENSOR_START) && (payload < GPIO_SENSOR_END)) { + for (byte i = 0; i < MAX_GPIO_PIN; i++) { + if ((cmodule.gp.io[i] == GPIO_USER) && (sysCfg.my_module.gp.io[i] == payload)) sysCfg.my_module.gp.io[i] = 0; + } + sysCfg.my_module.gp.io[index] = payload; + restartflag = 2; + } + snprintf_P(svalue, sizeof(svalue), PSTR("{"), svalue); + byte jsflg = 0; + for (byte i = 0; i < MAX_GPIO_PIN; i++) { + if (cmodule.gp.io[i] == GPIO_USER) { + if (jsflg) snprintf_P(svalue, sizeof(svalue), PSTR("%s, "), svalue); + jsflg = 1; + snprintf_P(stemp1, sizeof(stemp1), sensors[sysCfg.my_module.gp.io[i]]); + snprintf_P(svalue, sizeof(svalue), PSTR("%s\"GPIO%d\":%d (%s)"), svalue, i, sysCfg.my_module.gp.io[i], stemp1); + } + } + if (jsflg) { + snprintf_P(svalue, sizeof(svalue), PSTR("%s}"), svalue); + } else { + snprintf_P(svalue, sizeof(svalue), PSTR("{\"GPIO\":\"Not supported\"}")); + } + } + else if (!strcmp(type,"GPIOS")) { + snprintf_P(svalue, sizeof(svalue), PSTR("{\"GPIOs\":\""), svalue); + byte jsflg = 0; + for (byte i = 0; i < GPIO_SENSOR_END; i++) { + if (jsflg) snprintf_P(svalue, sizeof(svalue), PSTR("%s, "), svalue); + jsflg = 1; + snprintf_P(stemp1, sizeof(stemp1), sensors[i]); + snprintf_P(svalue, sizeof(svalue), PSTR("%s%s (%d)"), svalue, stemp1, i); + } + snprintf_P(svalue, sizeof(svalue), PSTR("%s\"}"), svalue); + } + else if (!strcmp(type,"SLEEP")) { + if ((data_len > 0) && (payload >= 0) && (payload < 251)) { + sysCfg.sleep = payload; + restartflag = 2; + } + snprintf_P(svalue, sizeof(svalue), PSTR("{\"Sleep\":\"%d%s\"}"), sysCfg.sleep, (sysCfg.value_units) ? " mS" : ""); + } + else if (!strcmp(type,"UPGRADE") || !strcmp(type,"UPLOAD")) { + if ((data_len > 0) && (payload == 1)) { + otaflag = 3; + snprintf_P(svalue, sizeof(svalue), PSTR("{\"Upgrade\":\"Version %s from %s\"}"), Version, sysCfg.otaUrl); + } else { + snprintf_P(svalue, sizeof(svalue), PSTR("{\"Upgrade\":\"Option 1 to upgrade\"}")); + } + } + else if (!strcmp(type,"OTAURL")) { + if ((data_len > 0) && (data_len < sizeof(sysCfg.otaUrl))) + strlcpy(sysCfg.otaUrl, (payload == 1) ? OTA_URL : dataBuf, sizeof(sysCfg.otaUrl)); + snprintf_P(svalue, sizeof(svalue), PSTR("{\"OtaUrl\":\"%s\"}"), sysCfg.otaUrl); + } + else if (!strcmp(type,"SERIALLOG")) { + if ((data_len > 0) && (payload >= LOG_LEVEL_NONE) && (payload <= LOG_LEVEL_ALL)) { + sysCfg.seriallog_level = payload; + seriallog_level = payload; + seriallog_timer = 0; + snprintf_P(svalue, sizeof(svalue), PSTR("{\"SerialLog\":%d}"), sysCfg.syslog_level); + } + snprintf_P(svalue, sizeof(svalue), PSTR("{\"SerialLog\":\"%d (Setting %d)\"}"), seriallog_level, sysCfg.seriallog_level); + } + else if (!strcmp(type,"SYSLOG")) { + if ((data_len > 0) && (payload >= LOG_LEVEL_NONE) && (payload <= LOG_LEVEL_ALL)) { + sysCfg.syslog_level = payload; + syslog_level = payload; + syslog_timer = 0; + snprintf_P(svalue, sizeof(svalue), PSTR("{\"SysLog\":%d}"), sysCfg.syslog_level); + } else { + snprintf_P(svalue, sizeof(svalue), PSTR("{\"SysLog\":\"%d (Setting %d)\"}"), syslog_level, sysCfg.syslog_level); + } + } + else if (!strcmp(type,"LOGHOST")) { + if ((data_len > 0) && (data_len < sizeof(sysCfg.syslog_host))) { + strlcpy(sysCfg.syslog_host, (payload == 1) ? SYS_LOG_HOST : dataBuf, sizeof(sysCfg.syslog_host)); + } + snprintf_P(svalue, sizeof(svalue), PSTR("{\"LogHost\":\"%s\"}"), sysCfg.syslog_host); + } + else if (!strcmp(type,"LOGPORT")) { + if ((data_len > 0) && (payload > 0) && (payload < 32766)) { + sysCfg.syslog_port = (payload == 1) ? SYS_LOG_PORT : payload; + } + snprintf_P(svalue, sizeof(svalue), PSTR("{\"LogPort\":%d}"), sysCfg.syslog_port); + } + else if (!strcmp(type,"AP")) { + if ((data_len > 0) && (payload >= 0) && (payload <= 2)) { + switch (payload) { + case 0: // Toggle + sysCfg.sta_active ^= 1; + break; + case 1: // AP1 + case 2: // AP2 + sysCfg.sta_active = payload -1; + } + restartflag = 2; + } + snprintf_P(svalue, sizeof(svalue), PSTR("{\"Ap\":\"%d (%s)\"}"), sysCfg.sta_active +1, sysCfg.sta_ssid[sysCfg.sta_active]); + } + else if (!strcmp(type,"SSID") && (index > 0) && (index <= 2)) { + if ((data_len > 0) && (data_len < sizeof(sysCfg.sta_ssid[0]))) { + strlcpy(sysCfg.sta_ssid[index -1], (payload == 1) ? (index == 1) ? STA_SSID1 : STA_SSID2 : dataBuf, sizeof(sysCfg.sta_ssid[0])); + sysCfg.sta_active = 0; + restartflag = 2; + } + snprintf_P(svalue, sizeof(svalue), PSTR("{\"SSid%d\":\"%s\"}"), index, sysCfg.sta_ssid[index -1]); + } + else if (!strcmp(type,"PASSWORD") && (index > 0) && (index <= 2)) { + if ((data_len > 0) && (data_len < sizeof(sysCfg.sta_pwd[0]))) { + strlcpy(sysCfg.sta_pwd[index -1], (payload == 1) ? (index == 1) ? STA_PASS1 : STA_PASS2 : dataBuf, sizeof(sysCfg.sta_pwd[0])); + sysCfg.sta_active = 0; + restartflag = 2; + } + snprintf_P(svalue, sizeof(svalue), PSTR("{\"Password%d\":\"%s\"}"), index, sysCfg.sta_pwd[index -1]); + } + else if (!grpflg && !strcmp(type,"HOSTNAME")) { + if ((data_len > 0) && (data_len < sizeof(sysCfg.hostname))) { + strlcpy(sysCfg.hostname, (payload == 1) ? WIFI_HOSTNAME : dataBuf, sizeof(sysCfg.hostname)); + if (strstr(sysCfg.hostname,"%")) + strlcpy(sysCfg.hostname, DEF_WIFI_HOSTNAME, sizeof(sysCfg.hostname)); + restartflag = 2; + } + snprintf_P(svalue, sizeof(svalue), PSTR("{\"Hostname\":\"%s\"}"), sysCfg.hostname); + } + else if (!strcmp(type,"WIFICONFIG") || !strcmp(type,"SMARTCONFIG")) { + if ((data_len > 0) && (payload >= WIFI_RESTART) && (payload < MAX_WIFI_OPTION)) { + sysCfg.sta_config = payload; + wificheckflag = sysCfg.sta_config; + snprintf_P(stemp1, sizeof(stemp1), wificfg[sysCfg.sta_config]); + snprintf_P(svalue, sizeof(svalue), PSTR("{\"WifiConfig\":\"%s selected\"}"), stemp1); + if (WIFI_State() != WIFI_RESTART) { +// snprintf_P(svalue, sizeof(svalue), PSTR("%s after restart"), svalue); + restartflag = 2; + } + } else { + snprintf_P(stemp1, sizeof(stemp1), wificfg[sysCfg.sta_config]); + snprintf_P(svalue, sizeof(svalue), PSTR("{\"WifiConfig\":\"%d (%s)\"}"), sysCfg.sta_config, stemp1); + } + } + else if (!strcmp(type,"FRIENDLYNAME") && (index > 0) && (index <= 4)) { + if ((data_len > 0) && (data_len < sizeof(sysCfg.friendlyname[0]))) { + if (index == 1) + strlcpy(sysCfg.friendlyname[0], (payload == 1) ? FRIENDLY_NAME1 : dataBuf, sizeof(sysCfg.friendlyname[0])); + else if (index == 2) + strlcpy(sysCfg.friendlyname[1], (payload == 1) ? FRIENDLY_NAME2 : dataBuf, sizeof(sysCfg.friendlyname[1])); + else if (index == 3) + strlcpy(sysCfg.friendlyname[2], (payload == 1) ? FRIENDLY_NAME3 : dataBuf, sizeof(sysCfg.friendlyname[2])); + else if (index == 4) + strlcpy(sysCfg.friendlyname[3], (payload == 1) ? FRIENDLY_NAME4 : dataBuf, sizeof(sysCfg.friendlyname[3])); + } + snprintf_P(svalue, sizeof(svalue), PSTR("{\"FriendlyName%d\":\"%s\"}"), index, sysCfg.friendlyname[index -1]); + } + else if (swt_flg && !strcmp(type,"SWITCHMODE")) { + if ((data_len > 0) && (payload >= 0) && (payload < MAX_SWITCH_OPTION)) { + sysCfg.switchmode = payload; + } + snprintf_P(svalue, sizeof(svalue), PSTR("{\"SwitchMode\":%d}"), sysCfg.switchmode); + } +#ifdef USE_WEBSERVER + else if (!strcmp(type,"WEBSERVER")) { + if ((data_len > 0) && (payload >= 0) && (payload <= 2)) { + sysCfg.webserver = payload; + } + if (sysCfg.webserver) { + snprintf_P(svalue, sizeof(svalue), PSTR("{\"Webserver\":\"Active for %s on %s with IP address %s\"}"), + (sysCfg.webserver == 2) ? "ADMIN" : "USER", Hostname, WiFi.localIP().toString().c_str()); + } else { + snprintf_P(svalue, sizeof(svalue), PSTR("{\"Webserver\":\"%s\"}"), MQTT_STATUS_OFF); + } + } + else if (!strcmp(type,"WEBLOG")) { + if ((data_len > 0) && (payload >= LOG_LEVEL_NONE) && (payload <= LOG_LEVEL_ALL)) { + sysCfg.weblog_level = payload; + } + snprintf_P(svalue, sizeof(svalue), PSTR("{\"WebLog\":%d}"), sysCfg.weblog_level); + } +#endif // USE_WEBSERVER + else if (!strcmp(type,"UNITS")) { + if ((data_len > 0) && (payload >= 0) && (payload <= 1)) { + sysCfg.value_units = payload; + } + snprintf_P(svalue, sizeof(svalue), PSTR("{\"Units\":\"%s\"}"), (sysCfg.value_units) ? MQTT_STATUS_ON : MQTT_STATUS_OFF); + } + else if (!strcmp(type,"MQTT")) { + if ((data_len > 0) && (payload >= 0) && (payload <= 1)) { + sysCfg.mqtt_enabled = payload; + restartflag = 2; + } + snprintf_P(svalue, sizeof(svalue), PSTR("{\"Mqtt\":\"%s\"}"), (sysCfg.mqtt_enabled) ? MQTT_STATUS_ON : MQTT_STATUS_OFF); + } + else if (!strcmp(type,"TELEPERIOD")) { + if ((data_len > 0) && (payload >= 0) && (payload < 3601)) { + sysCfg.tele_period = (payload == 1) ? TELE_PERIOD : payload; + if ((sysCfg.tele_period > 0) && (sysCfg.tele_period < 10)) sysCfg.tele_period = 10; // Do not allow periods < 10 seconds + tele_period = sysCfg.tele_period; + } + snprintf_P(svalue, sizeof(svalue), PSTR("{\"TelePeriod\":\"%d%s\"}"), sysCfg.tele_period, (sysCfg.value_units) ? " Sec" : ""); + } + else if (!strcmp(type,"RESTART")) { + switch (payload) { + case 1: + restartflag = 2; + snprintf_P(svalue, sizeof(svalue), PSTR("{\"Restart\":\"Restarting\"}")); + break; + case 99: + addLog_P(LOG_LEVEL_INFO, PSTR("APP: Restarting")); + ESP.restart(); + break; + default: + snprintf_P(svalue, sizeof(svalue), PSTR("{\"Restart\":\"1 to restart\"}")); + } + } + else if (!strcmp(type,"RESET")) { + switch (payload) { + case 1: + restartflag = 211; + snprintf_P(svalue, sizeof(svalue), PSTR("{\"Reset\":\"Reset and Restarting\"}")); + break; + case 2: + restartflag = 212; + snprintf_P(svalue, sizeof(svalue), PSTR("{\"Reset\":\"Erase, Reset and Restarting\"}")); + break; + default: + snprintf_P(svalue, sizeof(svalue), PSTR("{\"Reset\":\"1 to reset\"}")); + } + } + else if (!strcmp(type,"TIMEZONE")) { + if ((data_len > 0) && (((payload >= -12) && (payload <= 12)) || (payload == 99))) { + sysCfg.timezone = payload; + } + snprintf_P(svalue, sizeof(svalue), PSTR("{\"Timezone\":\"%d%s\"}"), sysCfg.timezone, (sysCfg.value_units) ? " Hr" : ""); + } + else if (!strcmp(type,"LEDPOWER")) { + if ((data_len > 0) && (payload >= 0) && (payload <= 2)) { + sysCfg.ledstate &= 8; + switch (payload) { + case 0: // Off + case 1: // On + sysCfg.ledstate = payload << 3; + break; + case 2: // Toggle + sysCfg.ledstate ^= 8; + break; + } + blinks = 0; + setLed(sysCfg.ledstate &8); + } + snprintf_P(svalue, sizeof(svalue), PSTR("{\"LedPower\":\"%s\"}"), (sysCfg.ledstate &8) ? MQTT_STATUS_ON : MQTT_STATUS_OFF); + } + else if (!strcmp(type,"LEDSTATE")) { + if ((data_len > 0) && (payload >= 0) && (payload < MAX_LED_OPTION)) { + sysCfg.ledstate = payload; + if (!sysCfg.ledstate) setLed(led_inverted[0]); + } + snprintf_P(svalue, sizeof(svalue), PSTR("{\"LedState\":%d}"), sysCfg.ledstate); + } + else if (!strcmp(type,"CFGDUMP")) { + CFG_Dump(); + snprintf_P(svalue, sizeof(svalue), PSTR("{\"CfgDump\":\"Done\"}")); + } +#ifdef USE_I2C + else if (i2c_flg && !strcmp(type,"I2CSCAN")) { + i2c_scan(svalue, sizeof(svalue)); + } +#endif // USE_I2C + +/*** MQTT Commands ***************************************************************************/ + + else if (sysCfg.mqtt_enabled && !strcmp(type,"MQTTHOST")) { + if ((data_len > 0) && (data_len < sizeof(sysCfg.mqtt_host))) { + strlcpy(sysCfg.mqtt_host, (payload == 1) ? MQTT_HOST : dataBuf, sizeof(sysCfg.mqtt_host)); + restartflag = 2; + } + snprintf_P(svalue, sizeof(svalue), PSTR("{\"MqttHost\",\"%s\"}"), sysCfg.mqtt_host); + } + else if (sysCfg.mqtt_enabled && !strcmp(type,"MQTTPORT")) { + if ((data_len > 0) && (payload > 0) && (payload < 32766)) { + sysCfg.mqtt_port = (payload == 1) ? MQTT_PORT : payload; + restartflag = 2; + } + snprintf_P(svalue, sizeof(svalue), PSTR("{\"MqttPort\":%d}"), sysCfg.mqtt_port); + } +#ifdef USE_MQTT_TLS + else if (sysCfg.mqtt_enabled && !strcmp(type,"MQTTFINGERPRINT")) { + if ((data_len > 0) && (data_len < sizeof(sysCfg.mqtt_fingerprint))) { + strlcpy(sysCfg.mqtt_fingerprint, (payload == 1) ? MQTT_FINGERPRINT : dataBuf, sizeof(sysCfg.mqtt_fingerprint)); + restartflag = 2; + } + snprintf_P(svalue, sizeof(svalue), PSTR("{\"MqttFingerprint\":\"%s\"}"), sysCfg.mqtt_fingerprint); + } +#endif + else if (sysCfg.mqtt_enabled && !grpflg && !strcmp(type,"MQTTCLIENT")) { + if ((data_len > 0) && (data_len < sizeof(sysCfg.mqtt_client))) { + strlcpy(sysCfg.mqtt_client, (payload == 1) ? MQTT_CLIENT_ID : dataBuf, sizeof(sysCfg.mqtt_client)); + restartflag = 2; + } + snprintf_P(svalue, sizeof(svalue), PSTR("{\"MqttClient\":\"%s\"}"), sysCfg.mqtt_client); + } + else if (sysCfg.mqtt_enabled && !strcmp(type,"MQTTUSER")) { + if ((data_len > 0) && (data_len < sizeof(sysCfg.mqtt_user))) { + strlcpy(sysCfg.mqtt_user, (payload == 1) ? MQTT_USER : dataBuf, sizeof(sysCfg.mqtt_user)); + restartflag = 2; + } + snprintf_P(svalue, sizeof(svalue), PSTR("[\"MqttUser\":\"%s\"}"), sysCfg.mqtt_user); + } + else if (sysCfg.mqtt_enabled && !strcmp(type,"MQTTPASSWORD")) { + if ((data_len > 0) && (data_len < sizeof(sysCfg.mqtt_pwd))) { + strlcpy(sysCfg.mqtt_pwd, (payload == 1) ? MQTT_PASS : dataBuf, sizeof(sysCfg.mqtt_pwd)); + restartflag = 2; + } + snprintf_P(svalue, sizeof(svalue), PSTR("{\"MqttPassword\":\"%s\"}"), sysCfg.mqtt_pwd); + } +#ifdef USE_DOMOTICZ + else if (sysCfg.mqtt_enabled && !strncmp(type,"DOMOTICZ...",8)) { + domoticz_commands(type, index, dataBuf, data_len, payload, svalue, sizeof(svalue)); + } +#endif // USE_DOMOTICZ + else if (sysCfg.mqtt_enabled && !strcmp(type,"GROUPTOPIC")) { + if ((data_len > 0) && (data_len < sizeof(sysCfg.mqtt_grptopic))) { + for(i = 0; i <= data_len; i++) + if ((dataBuf[i] == '/') || (dataBuf[i] == '+') || (dataBuf[i] == '#')) dataBuf[i] = '_'; + if (!strcmp(dataBuf, MQTTClient)) payload = 1; + strlcpy(sysCfg.mqtt_grptopic, (payload == 1) ? MQTT_GRPTOPIC : dataBuf, sizeof(sysCfg.mqtt_grptopic)); + restartflag = 2; + } + snprintf_P(svalue, sizeof(svalue), PSTR("{\"GroupTopic\":\"%s\"}"), sysCfg.mqtt_grptopic); + } + else if (sysCfg.mqtt_enabled && !grpflg && !strcmp(type,"TOPIC")) { + if ((data_len > 0) && (data_len < sizeof(sysCfg.mqtt_topic))) { + for(i = 0; i <= data_len; i++) + if ((dataBuf[i] == '/') || (dataBuf[i] == '+') || (dataBuf[i] == '#')) dataBuf[i] = '_'; + if (!strcmp(dataBuf, MQTTClient)) payload = 1; + strlcpy(sysCfg.mqtt_topic, (payload == 1) ? MQTT_TOPIC : dataBuf, sizeof(sysCfg.mqtt_topic)); + restartflag = 2; + } + snprintf_P(svalue, sizeof(svalue), PSTR("{\"Topic\":\"%s\"}"), sysCfg.mqtt_topic); + } + else if (sysCfg.mqtt_enabled && !grpflg && !strcmp(type,"BUTTONTOPIC")) { + if ((data_len > 0) && (data_len < sizeof(sysCfg.button_topic))) { + for(i = 0; i <= data_len; i++) + if ((dataBuf[i] == '/') || (dataBuf[i] == '+') || (dataBuf[i] == '#')) dataBuf[i] = '_'; + if (!strcmp(dataBuf, MQTTClient)) payload = 1; + strlcpy(sysCfg.button_topic, (payload == 1) ? sysCfg.mqtt_topic : dataBuf, sizeof(sysCfg.button_topic)); + } + snprintf_P(svalue, sizeof(svalue), PSTR("{\"ButtonTopic\":\"%s\"}"), sysCfg.button_topic); + } + else if (sysCfg.mqtt_enabled && !grpflg && !strcmp(type,"SWITCHTOPIC")) { + if ((data_len > 0) && (data_len < sizeof(sysCfg.switch_topic))) { + for(i = 0; i <= data_len; i++) + if ((dataBuf[i] == '/') || (dataBuf[i] == '+') || (dataBuf[i] == '#')) dataBuf[i] = '_'; + if (!strcmp(dataBuf, MQTTClient)) payload = 1; + strlcpy(sysCfg.switch_topic, (payload == 1) ? sysCfg.mqtt_topic : dataBuf, sizeof(sysCfg.switch_topic)); + } + snprintf_P(svalue, sizeof(svalue), PSTR("{\"SwitchTopic\":\"%s\"}"), sysCfg.switch_topic); + } + else if (sysCfg.mqtt_enabled && !strcmp(type,"BUTTONRETAIN")) { + if ((data_len > 0) && (payload >= 0) && (payload <= 1)) { + strlcpy(sysCfg.button_topic, sysCfg.mqtt_topic, sizeof(sysCfg.button_topic)); + if (!payload) { + for(i = 1; i <= Maxdevice; i++) { + send_button_power(0, i, 3); // Clear MQTT retain in broker + } + } + sysCfg.mqtt_button_retain = payload; + } + snprintf_P(svalue, sizeof(svalue), PSTR("{\"ButtonRetain\":\"%s\"}"), (sysCfg.mqtt_button_retain) ? MQTT_STATUS_ON : MQTT_STATUS_OFF); + } + else if (sysCfg.mqtt_enabled && !strcmp(type,"SWITCHRETAIN")) { + if ((data_len > 0) && (payload >= 0) && (payload <= 1)) { +// strlcpy(sysCfg.button_topic, sysCfg.mqtt_topic, sizeof(sysCfg.button_topic)); + if (!payload) { + for(i = 1; i <= 4; i++) { + send_button_power(1, i, 3); // Clear MQTT retain in broker + } + } + sysCfg.mqtt_switch_retain = payload; + } + snprintf_P(svalue, sizeof(svalue), PSTR("{\"SwitchRetain\":\"%s\"}"), (sysCfg.mqtt_switch_retain) ? MQTT_STATUS_ON : MQTT_STATUS_OFF); + } + else if (sysCfg.mqtt_enabled && (!strcmp(type,"POWERRETAIN") || !strcmp(type,"LIGHTRETAIN"))) { + if ((data_len > 0) && (payload >= 0) && (payload <= 1)) { + if (!payload) { + for(i = 1; i <= Maxdevice; i++) { // Clear MQTT retain in broker + snprintf_P(stemp2, sizeof(stemp2), PSTR("%d"), i); + snprintf_P(stemp1, sizeof(stemp1), PSTR("%s/%s/POWER%s"), PUB_PREFIX, sysCfg.mqtt_topic, (Maxdevice > 1) ? stemp2 : ""); + mqtt_publish(stemp1, "", sysCfg.mqtt_power_retain); + snprintf_P(stemp1, sizeof(stemp1), PSTR("%s/%s/LIGHT%s"), PUB_PREFIX, sysCfg.mqtt_topic, (Maxdevice > 1) ? stemp2 : ""); + mqtt_publish(stemp1, "", sysCfg.mqtt_power_retain); + } + } + sysCfg.mqtt_power_retain = payload; + } + snprintf_P(stemp1, sizeof(stemp1), PSTR("%s"), (!strcmp(sysCfg.mqtt_subtopic,"POWER")) ? "Power" : "Light"); + snprintf_P(svalue, sizeof(svalue), PSTR("{\"%sRetain\":\"%s\"}"), stemp1, (sysCfg.mqtt_power_retain) ? MQTT_STATUS_ON : MQTT_STATUS_OFF); + } + +/*** HLW Power management Commands ***********************************************************/ + + else if (hlw_flg && !strcmp(type,"POWERLOW")) { + if ((data_len > 0) && (payload >= 0) && (payload < 3601)) { + sysCfg.hlw_pmin = payload; + } + snprintf_P(svalue, sizeof(svalue), PSTR("{\"PowerLow\":\"%d%s\"}"), sysCfg.hlw_pmin, (sysCfg.value_units) ? " W" : ""); + } + else if (hlw_flg && !strcmp(type,"POWERHIGH")) { + if ((data_len > 0) && (payload >= 0) && (payload < 3601)) { + sysCfg.hlw_pmax = payload; + } + snprintf_P(svalue, sizeof(svalue), PSTR("{\"PowerHigh\":\"%d%s\"}"), sysCfg.hlw_pmax, (sysCfg.value_units) ? " W" : ""); + } + else if (hlw_flg && !strcmp(type,"VOLTAGELOW")) { + if ((data_len > 0) && (payload >= 0) && (payload < 501)) { + sysCfg.hlw_umin = payload; + } + snprintf_P(svalue, sizeof(svalue), PSTR("{\"VoltageLow\":\"%d%s\"}"), sysCfg.hlw_umin, (sysCfg.value_units) ? " V" : ""); + } + else if (hlw_flg && !strcmp(type,"VOLTAGEHIGH")) { + if ((data_len > 0) && (payload >= 0) && (payload < 501)) { + sysCfg.hlw_umax = payload; + } + snprintf_P(svalue, sizeof(svalue), PSTR("[\"VoltageHigh\":\"%d%s\"}"), sysCfg.hlw_umax, (sysCfg.value_units) ? " V" : ""); + } + else if (hlw_flg && !strcmp(type,"CURRENTLOW")) { + if ((data_len > 0) && (payload >= 0) && (payload < 16001)) { + sysCfg.hlw_imin = payload; + } + snprintf_P(svalue, sizeof(svalue), PSTR("{\"CurrentLow\":\"%d%s\"}"), sysCfg.hlw_imin, (sysCfg.value_units) ? " mA" : ""); + } + else if (hlw_flg && !strcmp(type,"CURRENTHIGH")) { + if ((data_len > 0) && (payload >= 0) && (payload < 16001)) { + sysCfg.hlw_imax = payload; + } + snprintf_P(svalue, sizeof(svalue), PSTR("{\"CurrentHigh\":\"%d%s\"}"), sysCfg.hlw_imax, (sysCfg.value_units) ? " mA" : ""); + } + else if (hlw_flg && !strcmp(type,"HLWPCAL")) { + if ((data_len > 0) && (payload > 0) && (payload < 32001)) { + sysCfg.hlw_pcal = (payload == 1) ? HLW_PREF_PULSE : payload; + } + snprintf_P(svalue, sizeof(svalue), PSTR("(\"HlwPcal\":\"%d%s\"}"), sysCfg.hlw_pcal, (sysCfg.value_units) ? " uS" : ""); + } + else if (hlw_flg && !strcmp(type,"HLWUCAL")) { + if ((data_len > 0) && (payload > 0) && (payload < 32001)) { + sysCfg.hlw_ucal = (payload == 1) ? HLW_UREF_PULSE : payload; + } + snprintf_P(svalue, sizeof(svalue), PSTR("{\"HlwUcal\":\"%d%s\"}"), sysCfg.hlw_ucal, (sysCfg.value_units) ? " uS" : ""); + } + else if (hlw_flg && !strcmp(type,"HLWICAL")) { + if ((data_len > 0) && (payload > 0) && (payload < 32001)) { + sysCfg.hlw_ical = (payload == 1) ? HLW_IREF_PULSE : payload; + } + snprintf_P(svalue, sizeof(svalue), PSTR("{\"HlwIcal\":\"%d%s\"}"), sysCfg.hlw_ical, (sysCfg.value_units) ? " uS" : ""); + } +#ifdef FEATURE_POWER_LIMIT + else if (hlw_flg && !strcmp(type,"MAXPOWER")) { + if ((data_len > 0) && (payload >= 0) && (payload < 3601)) { + sysCfg.hlw_mpl = payload; + } + snprintf_P(svalue, sizeof(svalue), PSTR("{\"MaxPower\":\"%d%s\"}"), sysCfg.hlw_mpl, (sysCfg.value_units) ? " W" : ""); + } + else if (hlw_flg && !strcmp(type,"MAXPOWERHOLD")) { + if ((data_len > 0) && (payload >= 0) && (payload < 3601)) { + sysCfg.hlw_mplh = (payload == 1) ? MAX_POWER_HOLD : payload; + } + snprintf_P(svalue, sizeof(svalue), PSTR("{\"MaxPowerHold\":\"%d%s\"}"), sysCfg.hlw_mplh, (sysCfg.value_units) ? " Sec" : ""); + } + else if (hlw_flg && !strcmp(type,"MAXPOWERWINDOW")) { + if ((data_len > 0) && (payload >= 0) && (payload < 3601)) { + sysCfg.hlw_mplw = (payload == 1) ? MAX_POWER_WINDOW : payload; + } + snprintf_P(svalue, sizeof(svalue), PSTR("{\"MaxPowerWindow\":\"%d%s\"}"), sysCfg.hlw_mplw, (sysCfg.value_units) ? " Sec" : ""); + } + else if (hlw_flg && !strcmp(type,"SAFEPOWER")) { + if ((data_len > 0) && (payload >= 0) && (payload < 3601)) { + sysCfg.hlw_mspl = payload; + } + snprintf_P(svalue, sizeof(svalue), PSTR("{\"SafePower\":\"%d%s\"}"), sysCfg.hlw_mspl, (sysCfg.value_units) ? " W" : ""); + } + else if (hlw_flg && !strcmp(type,"SAFEPOWERHOLD")) { + if ((data_len > 0) && (payload >= 0) && (payload < 3601)) { + sysCfg.hlw_msplh = (payload == 1) ? SAFE_POWER_HOLD : payload; + } + snprintf_P(svalue, sizeof(svalue), PSTR("{\"SafePowerHold\":\"%d%s\"}"), sysCfg.hlw_msplh, (sysCfg.value_units) ? " Sec" : ""); + } + else if (hlw_flg && !strcmp(type,"SAFEPOWERWINDOW")) { + if ((data_len > 0) && (payload >= 0) && (payload < 1440)) { + sysCfg.hlw_msplw = (payload == 1) ? SAFE_POWER_WINDOW : payload; + } + snprintf_P(svalue, sizeof(svalue), PSTR("{\"SafePowerWindow\":\"%d%s\"}"), sysCfg.hlw_msplw, (sysCfg.value_units) ? " Min" : ""); + } + else if (hlw_flg && !strcmp(type,"MAXENERGY")) { + if ((data_len > 0) && (payload >= 0) && (payload < 3601)) { + sysCfg.hlw_mkwh = payload; + hlw_mkwh_state = 3; + } + snprintf_P(svalue, sizeof(svalue), PSTR("{\"MaxEnergy\":\"%d%s\"}"), sysCfg.hlw_mkwh, (sysCfg.value_units) ? " Wh" : ""); + } + else if (hlw_flg && !strcmp(type,"MAXENERGYSTART")) { + if ((data_len > 0) && (payload >= 0) && (payload < 24)) { + sysCfg.hlw_mkwhs = payload; + } + snprintf_P(svalue, sizeof(svalue), PSTR("{\"MaxEnergyStart\":\"%d%s\"}"), sysCfg.hlw_mkwhs, (sysCfg.value_units) ? " Hr" : ""); + } +#endif // FEATURE_POWER_LIMIT + +/*** WS2812 Commands *************************************************************************/ + +#ifdef USE_WS2812 + else if ((pin[GPIO_WS2812] < 99) && !strcmp(type,"PIXELS")) { + if ((data_len > 0) && (payload > 0) && (payload <= WS2812_MAX_LEDS)) { + sysCfg.ws_pixels = payload; + ws2812_pixels(); + } + snprintf_P(svalue, sizeof(svalue), PSTR("{\"Pixels\":%d}"), sysCfg.ws_pixels); + } + else if ((pin[GPIO_WS2812] < 99) && !strcmp(type,"LED") && (index > 0) && (index <= sysCfg.ws_pixels)) { + if (data_len == 6) { + ws2812_setColor(index, dataBufUc); + } + ws2812_getColor(index, svalue, sizeof(svalue)); + } + else if ((pin[GPIO_WS2812] < 99) && !strcmp(type,"COLOR")) { + if (data_len == 6) { + ws2812_setColor(0, dataBufUc); + power = 1; + } + ws2812_getColor(0, svalue, sizeof(svalue)); + } + else if ((pin[GPIO_WS2812] < 99) && !strcmp(type,"DIMMER")) { + if ((data_len > 0) && (payload >= 0) && (payload <= 100)) { + sysCfg.ws_dimmer = payload; + power = 1; +#ifdef USE_DOMOTICZ + mqtt_publishDomoticzPowerState(index); +#endif // USE_DOMOTICZ + } + snprintf_P(svalue, sizeof(svalue), PSTR("{\"Dimmer\":%d}"), sysCfg.ws_dimmer); + } + else if ((pin[GPIO_WS2812] < 99) && !strcmp(type,"LEDTABLE")) { + if ((data_len > 0) && (payload >= 0) && (payload <= 2)) { + switch (payload) { + case 0: // Off + case 1: // On + sysCfg.ws_ledtable = payload; + break; + case 2: // Toggle + sysCfg.ws_ledtable ^= 1; + break; + } + ws2812_update(); + } + snprintf_P(svalue, sizeof(svalue), PSTR("{\"LedTable\":\"%s\"}"), (sysCfg.ws_ledtable) ? MQTT_STATUS_ON : MQTT_STATUS_OFF); + } + else if ((pin[GPIO_WS2812] < 99) && !strcmp(type,"FADE")) { + if ((data_len > 0) && (payload >= 0) && (payload <= 2)) { + switch (payload) { + case 0: // Off + case 1: // On + sysCfg.ws_fade = payload; + break; + case 2: // Toggle + sysCfg.ws_fade ^= 1; + break; + } + } + snprintf_P(svalue, sizeof(svalue), PSTR("{\"Fade\":\"%s\"}"), (sysCfg.ws_fade) ? MQTT_STATUS_ON : MQTT_STATUS_OFF); + } + else if ((pin[GPIO_WS2812] < 99) && !strcmp(type,"SPEED")) { // 1 - fast, 5 - slow + if ((data_len > 0) && (payload > 0) && (payload <= 5)) { + sysCfg.ws_speed = payload; + } + snprintf_P(svalue, sizeof(svalue), PSTR("{\"Speed\":%d}"), sysCfg.ws_speed); + } + else if ((pin[GPIO_WS2812] < 99) && !strcmp(type,"WIDTH")) { + if ((data_len > 0) && (payload >= 0) && (payload <= 4)) { + sysCfg.ws_width = payload; + } + snprintf_P(svalue, sizeof(svalue), PSTR("{\"Width\":%d}"), sysCfg.ws_width); + } + else if ((pin[GPIO_WS2812] < 99) && !strcmp(type,"WAKEUP")) { + if ((data_len > 0) && (payload > 0) && (payload < 3601)) { + sysCfg.ws_wakeup = payload; + if (sysCfg.ws_scheme == 1) sysCfg.ws_scheme = 0; + } + snprintf_P(svalue, sizeof(svalue), PSTR("{\"WakeUp\":%d}"), sysCfg.ws_wakeup); + } + else if ((pin[GPIO_WS2812] < 99) && !strcmp(type,"SCHEME")) { + if ((data_len > 0) && (payload >= 0) && (payload <= 9)) { + sysCfg.ws_scheme = payload; + if (sysCfg.ws_scheme == 1) ws2812_resetWakupState(); + power = 1; + ws2812_resetStripTimer(); + } + snprintf_P(svalue, sizeof(svalue), PSTR("{\"Scheme\":%d}"), sysCfg.ws_scheme); + } +#endif // USE_WS2812 + +/*********************************************************************************************/ + + else { + type = NULL; + } + } + if (type == NULL) { + blinks = 201; + snprintf_P(svalue, sizeof(svalue), PSTR("{\"Commands\":\"Status, SaveData, SaveSate, Sleep, Upgrade, Otaurl, Restart, Reset, WifiConfig, Seriallog, Syslog, LogHost, LogPort, SSId1, SSId2, Password1, Password2, AP%s\"}"), (!grpflg) ? ", Hostname, Module, Modules, GPIO, GPIOs" : ""); + mqtt_publish(stopic, svalue); + + if (sysCfg.mqtt_enabled) { + snprintf_P(svalue, sizeof(svalue), PSTR("{\"Commands\":\"Mqtt, MqttHost, MqttPort, MqttUser, MqttPassword%s, GroupTopic, Units, Timezone, LedState, LedPower, TelePeriod\"}"), (!grpflg) ? ", MqttClient, Topic, ButtonTopic, ButtonRetain, SwitchTopic, SwitchRetain, PowerRetain" : ""); + } else { + snprintf_P(svalue, sizeof(svalue), PSTR("{\"Commands\":\"Mqtt, Units, Timezone, LedState, LedPower, TelePeriod\"}"), (!grpflg) ? ", MqttClient" : ""); + } + mqtt_publish(stopic, svalue); + + snprintf_P(svalue, sizeof(svalue), PSTR("{\"Commands\":\"%s%s, PulseTime, BlinkTime, BlinkCount"), (Maxdevice == 1) ? "Power, Light" : "Power1, Power2, Light1 Light2", (sysCfg.module != MOTOR) ? ", PowerOnState" : ""); +#ifdef USE_WEBSERVER + snprintf_P(svalue, sizeof(svalue), PSTR("%s, Weblog, Webserver"), svalue); +#endif + if (swt_flg) snprintf_P(svalue, sizeof(svalue), PSTR("%s, SwitchMode"), svalue); +#ifdef USE_I2C + if (i2c_flg) snprintf_P(svalue, sizeof(svalue), PSTR("%s, I2CScan"), svalue); +#endif // USE_I2C +#ifdef USE_WS2812 + if (pin[GPIO_WS2812] < 99) snprintf_P(svalue, sizeof(svalue), PSTR("%s, Pixels, Led, Color, Dimmer, Scheme, Fade, Speed, Width, Wakeup, LedTable"), svalue); +#endif + snprintf_P(svalue, sizeof(svalue), PSTR("%s\"}"), svalue); + +#ifdef USE_DOMOTICZ + mqtt_publish(stopic, svalue); + snprintf_P(svalue, sizeof(svalue), PSTR("{\"Commands\":\"DomoticzInTopic, DomoticzOutTopic, DomoticzIdx, DomoticzKeyIdx, DomoticzSwitchIdx, DomoticzSensorIdx, DomoticzUpdateTimer\"}")); +#endif // USE_DOMOTICZ + + if (hlw_flg) { + mqtt_publish(stopic, svalue); + snprintf_P(svalue, sizeof(svalue), PSTR("{\"Commands\":\"PowerLow, PowerHigh, VoltageLow, VoltageHigh, CurrentLow, CurrentHigh")); +#ifdef FEATURE_POWER_LIMIT + snprintf_P(svalue, sizeof(svalue), PSTR("%s, SafePower, SafePowerHold, SafePowerWindow, MaxPower, MaxPowerHold, MaxPowerWindow, MaxEnergy, MaxEnergyStart"), svalue); +#endif // FEATURE_POWER_LIMIT + snprintf_P(svalue, sizeof(svalue), PSTR("%s\"}"), svalue); + } + } + mqtt_publish(stopic, svalue); +} + +/********************************************************************************************/ + +void send_button_power(byte key, byte device, byte state) +{ +// key 0 = button_topic +// key 1 = switch_topic + + char stopic[TOPSZ], svalue[TOPSZ], stemp1[10]; + + if (device > Maxdevice) device = 1; + snprintf_P(stemp1, sizeof(stemp1), PSTR("%d"), device); + snprintf_P(stopic, sizeof(stopic), PSTR("%s/%s/%s%s"), + SUB_PREFIX, (key) ? sysCfg.switch_topic : sysCfg.button_topic, sysCfg.mqtt_subtopic, (Maxdevice > 1) ? stemp1 : ""); + + if (state == 3) { + svalue[0] = '\0'; + } else { + if (!strcmp(sysCfg.mqtt_topic,(key) ? sysCfg.switch_topic : sysCfg.button_topic) && (state == 2)) { + state = ~(power >> (device -1)) & 0x01; + } + snprintf_P(svalue, sizeof(svalue), PSTR("%s"), (state) ? (state == 2) ? MQTT_CMND_TOGGLE : MQTT_STATUS_ON : MQTT_STATUS_OFF); + } +#ifdef USE_DOMOTICZ + if (!(domoticz_button(key, device, state, strlen(svalue)))) { + mqtt_publish_sec(stopic, svalue, (key) ? sysCfg.mqtt_switch_retain : sysCfg.mqtt_button_retain); + } +#else + mqtt_publish_sec(stopic, svalue, (key) ? sysCfg.mqtt_switch_retain : sysCfg.mqtt_button_retain); +#endif // USE_DOMOTICZ +} + +void do_cmnd_power(byte device, byte state) +{ +// device = Relay number 1 and up +// state 0 = Relay Off +// state 1 = Relay on (turn off after sysCfg.pulsetime * 100 mSec if enabled) +// state 2 = Toggle relay +// state 3 = Blink relay +// state 4 = Stop blinking relay +// state 9 = Show power state + + if ((device < 1) || (device > Maxdevice)) device = 1; + byte mask = 0x01 << (device -1); + pulse_timer = 0; + if (state <= 2) { + if ((blink_mask & mask)) { + blink_mask &= (0xFF ^ mask); // Clear device mask + mqtt_publishPowerBlinkState(device); + } + switch (state) { + case 0: { // Off + power &= (0xFF ^ mask); + break; } + case 1: // On + power |= mask; + break; + case 2: // Toggle + power ^= mask; + } + setRelay(power); +#ifdef USE_DOMOTICZ + domoticz_updatePowerState(device); +#endif // USE_DOMOTICZ + if (device == 1) pulse_timer = (power & mask) ? sysCfg.pulsetime : 0; + } + else if (state == 3) { // Blink + if (!(blink_mask & mask)) { + blink_powersave = (blink_powersave & (0xFF ^ mask)) | (power & mask); // Save state + blink_power = (power >> (device -1))&1; // Prep to Toggle + } + blink_timer = 1; + blink_counter = ((!sysCfg.blinkcount) ? 64000 : (sysCfg.blinkcount *2)) +1; + blink_mask |= mask; // Set device mask + mqtt_publishPowerBlinkState(device); + return; + } + else if (state == 4) { // No Blink + byte flag = (blink_mask & mask); + blink_mask &= (0xFF ^ mask); // Clear device mask + mqtt_publishPowerBlinkState(device); + if (flag) do_cmnd_power(device, (blink_powersave >> (device -1))&1); // Restore state + return; + } + mqtt_publishPowerState(device); +} + +void stop_all_power_blink() +{ + byte i, mask; + + for (i = 1; i <= Maxdevice; i++) { + mask = 0x01 << (i -1); + if (blink_mask & mask) { + blink_mask &= (0xFF ^ mask); // Clear device mask + mqtt_publishPowerBlinkState(i); + do_cmnd_power(i, (blink_powersave >> (i -1))&1); // Restore state + } + } +} + +void do_cmnd(char *cmnd) +{ + char stopic[TOPSZ], svalue[128]; + char *start; + char *token; + + token = strtok(cmnd, " "); + if (token != NULL) { + start = strrchr(token, '/'); // Skip possible cmnd/sonoff/ preamble + if (start) token = start; + } + snprintf_P(stopic, sizeof(stopic), PSTR("%s/%s/%s"), SUB_PREFIX, sysCfg.mqtt_topic, token); + token = strtok(NULL, ""); + snprintf_P(svalue, sizeof(svalue), PSTR("%s"), (token == NULL) ? "" : token); + mqttDataCb(stopic, (byte*)svalue, strlen(svalue)); +} + +void publish_status(uint8_t payload) +{ + char stopic[TOPSZ], svalue[MESSZ], stemp1[TOPSZ], stemp2[10], stemp3[10]; + float ped, pi, pc; + uint16_t pe, pw, pu; + + // Workaround MQTT - TCP/IP stack queueing when SUB_PREFIX = PUB_PREFIX + snprintf_P(stopic, sizeof(stopic), PSTR("%s/%s/RESULT"), + (!strcmp(SUB_PREFIX,PUB_PREFIX) && (!payload)) ? PUB_PREFIX2 : PUB_PREFIX, sysCfg.mqtt_topic); + + if ((!sysCfg.mqtt_enabled) && (payload == 6)) payload = 99; + + if ((payload == 0) || (payload == 99)) { + snprintf_P(svalue, sizeof(svalue), PSTR("{\"Status\":{\"Module\":%d, \"FriendlyName\":\"%s\", \"Topic\":\"%s\", \"ButtonTopic\":\"%s\", \"Subtopic\":\"%s\", \"Power\":%d, \"PowerOnState\":%d, \"LedState\":%d, \"SaveData\":%d, \"SaveState\":%d, \"ButtonRetain\":%d, \"PowerRetain\":%d}}"), + sysCfg.module +1, sysCfg.friendlyname[0], sysCfg.mqtt_topic, sysCfg.button_topic, sysCfg.mqtt_subtopic, power, sysCfg.poweronstate, sysCfg.ledstate, sysCfg.savedata, sysCfg.savestate, sysCfg.mqtt_button_retain, sysCfg.mqtt_power_retain); + if (payload == 0) mqtt_publish(stopic, svalue); + } + + if (hlw_flg) { + if ((payload == 0) || (payload == 8)) { + hlw_readEnergy(0, ped, pe, pw, pu, pi, pc); + dtostrf(pi, 1, 3, stemp1); + dtostrf(ped, 1, 3, stemp2); + dtostrf(pc, 1, 2, stemp3); + snprintf_P(svalue, sizeof(svalue), PSTR("{\"StatusPWR\":{\"Voltage\":%d, \"Current\":\"%s\", \"Power\":%d, \"Today\":\"%s\", \"Factor\":\"%s\"}}"), + pu, stemp1, pw, stemp2, stemp3); + if (payload == 0) mqtt_publish(stopic, svalue); + } + + if ((payload == 0) || (payload == 9)) { + snprintf_P(svalue, sizeof(svalue), PSTR("{\"StatusPWRThreshold\":{\"PowerLow\":%d, \"PowerHigh\":%d, \"VoltageLow\":%d, \"VoltageHigh\":%d, \"CurrentLow\":%d, \"CurrentHigh\":%d}}"), + sysCfg.hlw_pmin, sysCfg.hlw_pmax, sysCfg.hlw_umin, sysCfg.hlw_umax, sysCfg.hlw_imin, sysCfg.hlw_imax); + if (payload == 0) mqtt_publish(stopic, svalue); + } + } + + if ((payload == 0) || (payload == 1)) { + snprintf_P(svalue, sizeof(svalue), PSTR("{\"StatusPRM\":{\"Baudrate\":%d, \"GroupTopic\":\"%s\", \"OtaUrl\":\"%s\", \"Uptime\":%d, \"Sleep\":%d, \"BootCount\":%d, \"SaveCount\":%d}}"), + Baudrate, sysCfg.mqtt_grptopic, sysCfg.otaUrl, uptime, sysCfg.sleep, sysCfg.bootcount, sysCfg.saveFlag); + if (payload == 0) mqtt_publish(stopic, svalue); + } + + if ((payload == 0) || (payload == 2)) { + snprintf_P(svalue, sizeof(svalue), PSTR("{\"StatusFWR\":{\"Program\":\"%s\", \"Boot\":%d, \"SDK\":\"%s\"}}"), + Version, ESP.getBootVersion(), ESP.getSdkVersion()); + if (payload == 0) mqtt_publish(stopic, svalue); + } + + if ((payload == 0) || (payload == 3)) { + snprintf_P(svalue, sizeof(svalue), PSTR("{\"StatusLOG\":{\"Seriallog\":%d, \"Weblog\":%d, \"Syslog\":%d, \"LogHost\":\"%s\", \"SSId1\":\"%s\", \"SSId2\":\"%s\", \"TelePeriod\":%d}}"), + sysCfg.seriallog_level, sysCfg.weblog_level, sysCfg.syslog_level, sysCfg.syslog_host, sysCfg.sta_ssid[0], sysCfg.sta_ssid[1], sysCfg.tele_period); + if (payload == 0) mqtt_publish(stopic, svalue); + } + + if ((payload == 0) || (payload == 4)) { + snprintf_P(svalue, sizeof(svalue), PSTR("{\"StatusMEM\":{\"ProgramSize\":%d, \"Free\":%d, \"Heap\":%d, \"SpiffsStart\":%d, \"SpiffsSize\":%d, \"FlashSize\":%d, \"ProgramFlashSize\":%d}}"), + ESP.getSketchSize()/1024, ESP.getFreeSketchSpace()/1024, ESP.getFreeHeap()/1024, ((uint32_t)&_SPIFFS_start - 0x40200000)/1024, + (((uint32_t)&_SPIFFS_end - 0x40200000) - ((uint32_t)&_SPIFFS_start - 0x40200000))/1024, ESP.getFlashChipRealSize()/1024, ESP.getFlashChipSize()/1024); + if (payload == 0) mqtt_publish(stopic, svalue); + } + + if ((payload == 0) || (payload == 5)) { + snprintf_P(svalue, sizeof(svalue), PSTR("{\"StatusNET\":{\"Host\":\"%s\", \"IP\":\"%s\", \"Gateway\":\"%s\", \"Subnetmask\":\"%s\", \"Mac\":\"%s\", \"Webserver\":%d, \"WifiConfig\":%d}}"), + Hostname, WiFi.localIP().toString().c_str(), WiFi.gatewayIP().toString().c_str(), WiFi.subnetMask().toString().c_str(), + WiFi.macAddress().c_str(), sysCfg.webserver, sysCfg.sta_config); + if (payload == 0) mqtt_publish(stopic, svalue); + } + + if (((payload == 0) || (payload == 6)) && sysCfg.mqtt_enabled) { + snprintf_P(svalue, sizeof(svalue), PSTR("{\"StatusMQT\":{\"Host\":\"%s\", \"Port\":%d, \"ClientMask\":\"%s\", \"Client\":\"%s\", \"User\":\"%s\", \"MAX_PACKET_SIZE\":%d, \"KEEPALIVE\":%d}}"), + sysCfg.mqtt_host, sysCfg.mqtt_port, sysCfg.mqtt_client, MQTTClient, sysCfg.mqtt_user, MQTT_MAX_PACKET_SIZE, MQTT_KEEPALIVE); + if (payload == 0) mqtt_publish(stopic, svalue); + } + + if ((payload == 0) || (payload == 7)) { + snprintf_P(svalue, sizeof(svalue), PSTR("{\"StatusTIM\":{\"UTC\":\"%s\", \"Local\":\"%s\", \"StartDST\":\"%s\", \"EndDST\":\"%s\", \"Timezone\":%d}}"), + rtc_time(0).c_str(), rtc_time(1).c_str(), rtc_time(2).c_str(), rtc_time(3).c_str(), sysCfg.timezone); + } + + mqtt_publish(stopic, svalue); +} + +/********************************************************************************************/ + +void every_second_cb() +{ + // 1 second rtc interrupt routine + // Keep this code small (every_second is to large - it'll trip exception) + +#ifdef FEATURE_POWER_LIMIT + if (rtcTime.Valid) { + if (rtc_loctime() == rtc_midnight()) { + hlw_mkwh_state = 3; + } + if ((rtcTime.Hour == sysCfg.hlw_mkwhs) && (hlw_mkwh_state == 3)) { + hlw_mkwh_state = 0; + } + } +#endif // FEATURE_POWER_LIMIT +} + +void every_second() +{ + char log[LOGSZ], stopic[TOPSZ], svalue[MESSZ], stime[21]; + + snprintf_P(stime, sizeof(stime), PSTR("%04d-%02d-%02dT%02d:%02d:%02d"), + rtcTime.Year, rtcTime.Month, rtcTime.Day, rtcTime.Hour, rtcTime.Minute, rtcTime.Second); + + if (pulse_timer > 111) pulse_timer--; + + if (seriallog_timer) { + seriallog_timer--; + if (!seriallog_timer) { + if (seriallog_level) { + addLog_P(LOG_LEVEL_INFO, PSTR("APP: Serial logging disabled")); + } + seriallog_level = 0; + } + } + + if (syslog_timer) { // Restore syslog level + syslog_timer--; + if (!syslog_timer) { + syslog_level = sysCfg.syslog_level; + if (sysCfg.syslog_level) { + addLog_P(LOG_LEVEL_INFO, PSTR("SYSL: Syslog logging re-enabled")); // Might trigger disable again (on purpose) + } + } + } + +#ifdef USE_DOMOTICZ + domoticz_mqttUpdate(); +#endif // USE_DOMOTICZ + + if (status_update_timer) { + status_update_timer--; + if (!status_update_timer) { + for (byte i = 1; i <= Maxdevice; i++) mqtt_publishPowerState(i); + } + } + + if (sysCfg.tele_period) { + tele_period++; + if (tele_period == sysCfg.tele_period -1) { + if (pin[GPIO_DSB]) { +#ifdef USE_DS18B20 + dsb_readTempPrep(); +#endif // USE_DS18B20 +#ifdef USE_DS18x20 + ds18x20_search(); // Check for changes in sensors number + ds18x20_convert(); // Start Conversion, takes up to one second +#endif // USE_DS18x20 + } +#ifdef USE_DHT + if (dht_type) dht_readPrep(); +#endif // USE_DHT +#ifdef USE_I2C + if (i2c_flg) { +#ifdef USE_HTU + htu_detect(); +#endif // USE_HTU +#ifdef USE_BMP + bmp_detect(); +#endif // USE_BMP +#ifdef USE_BH1750 + bh1750_detect(); +#endif // USE_BH1750 + } +#endif // USE_I2C + } + if (tele_period >= sysCfg.tele_period) { + tele_period = 0; + + snprintf_P(stopic, sizeof(stopic), PSTR("%s/%s/TELEMETRY"), PUB_PREFIX2, sysCfg.mqtt_topic); + snprintf_P(svalue, sizeof(svalue), PSTR("{\"Time\":\"%s\", \"Uptime\":%d"), stime, uptime); + for (byte i = 0; i < Maxdevice; i++) { + if (Maxdevice == 1) { // Legacy + snprintf_P(svalue, sizeof(svalue), PSTR("%s, \"%s\":"), svalue, sysCfg.mqtt_subtopic); + } else { + snprintf_P(svalue, sizeof(svalue), PSTR("%s, \"%s%d\":"), svalue, sysCfg.mqtt_subtopic, i +1); + } + snprintf_P(svalue, sizeof(svalue), PSTR("%s\"%s\""), svalue, (power & (0x01 << i)) ? MQTT_STATUS_ON : MQTT_STATUS_OFF); + } + snprintf_P(svalue, sizeof(svalue), PSTR("%s, \"Wifi\":{\"AP\":%d, \"SSID\":\"%s\", \"RSSI\":%d}}"), + svalue, sysCfg.sta_active +1, sysCfg.sta_ssid[sysCfg.sta_active], WIFI_getRSSIasQuality(WiFi.RSSI())); + mqtt_publish(stopic, svalue); + + uint8_t djson = 0; + snprintf_P(svalue, sizeof(svalue), PSTR("{\"Time\":\"%s\""), stime); + if (pin[GPIO_DSB] < 99) { +#ifdef USE_DS18B20 + dsb_mqttPresent(svalue, sizeof(svalue), &djson, 0); +#endif // USE_DS18B20 +#ifdef USE_DS18x20 + ds18x20_mqttPresent(svalue, sizeof(svalue), &djson, 0); +#endif // USE_DS18x20 + } +#if defined(USE_DHT) || defined(USE_DHT2) + if (dht_type) dht_mqttPresent(svalue, sizeof(svalue), &djson, 1); +#endif // USE_DHT/2 +#ifdef USE_I2C + if (i2c_flg) { +#ifdef USE_HTU + htu_mqttPresent(svalue, sizeof(svalue), &djson, 1); +#endif // USE_HTU +#ifdef USE_BMP + bmp_mqttPresent(svalue, sizeof(svalue), &djson, 2); +#endif // USE_BMP +#ifdef USE_BH1750 + bh1750_mqttPresent(svalue, sizeof(svalue), &djson, 3); +#endif // USE_BH1750 + } +#endif // USE_I2C + if (djson) { + snprintf_P(svalue, sizeof(svalue), PSTR("%s}"), svalue); + mqtt_publish(stopic, svalue); + } + + if (hlw_flg) hlw_mqttPresent(4); + } + } + + if (hlw_flg) hlw_margin_chk(); + + if ((rtcTime.Minute == 2) && (rtcTime.Second == 30)) { + uptime++; + snprintf_P(stopic, sizeof(stopic), PSTR("%s/%s/TELEMETRY"), PUB_PREFIX2, sysCfg.mqtt_topic); + snprintf_P(svalue, sizeof(svalue), PSTR("{\"Time\":\"%s\", \"Uptime\":%d}"), stime, uptime); + mqtt_publish(stopic, svalue); + } +} + +void stateloop() +{ + uint8_t button, flag, switchflag, power_now; + char scmnd[20], log[LOGSZ], stopic[TOPSZ], svalue[MESSZ]; + + timerxs = millis() + (1000 / STATES); + state++; + if (state == STATES) { // Every second + state = 0; + every_second(); + } + + if (mqtt_cmnd_publish) mqtt_cmnd_publish--; // Clean up + + if ((pulse_timer > 0) && (pulse_timer < 112)) { + pulse_timer--; + if (!pulse_timer) do_cmnd_power(1, 0); + } + + if (blink_mask) { + blink_timer--; + if (!blink_timer) { + blink_timer = sysCfg.blinktime; + blink_counter--; + if (!blink_counter) { + stop_all_power_blink(); + } else { + blink_power ^= 1; + power_now = (power & (0xFF ^ blink_mask)) | ((blink_power) ? blink_mask : 0); + setRelay(power_now); + } + } + } + +#ifdef USE_WS2812 + if (pin[GPIO_WS2812] < 99) ws2812_animate(); +#endif // USE_WS2812 + + if ((sysCfg.module == SONOFF_DUAL) || (sysCfg.module == CH4)) { + if (ButtonCode) { + snprintf_P(log, sizeof(log), PSTR("APP: Button code %04X"), ButtonCode); + addLog(LOG_LEVEL_DEBUG, log); + button = PRESSED; + if (ButtonCode == 0xF500) holdcount = (STATES *4) -1; + ButtonCode = 0; + } else { + button = NOT_PRESSED; + } + } else { + button = digitalRead(pin[GPIO_KEY1]); + } + if ((button == PRESSED) && (lastbutton[0] == NOT_PRESSED)) { + multipress = (multiwindow) ? multipress +1 : 1; + snprintf_P(log, sizeof(log), PSTR("APP: Multipress %d"), multipress); + addLog(LOG_LEVEL_DEBUG, log); + blinks = 201; + multiwindow = STATES /2; // 1/2 second multi press window + } + lastbutton[0] = button; + if (button == NOT_PRESSED) { + holdcount = 0; + } else { + holdcount++; + if (holdcount == (STATES *4)) { // 4 seconds button hold + snprintf_P(scmnd, sizeof(scmnd), PSTR("reset 1")); + multipress = 0; + do_cmnd(scmnd); + } + } + if (multiwindow) { + multiwindow--; + } else { + if ((!restartflag) && (!holdcount) && (multipress > 0) && (multipress < MAX_BUTTON_COMMANDS +3)) { + if ((sysCfg.module == SONOFF_DUAL) || (sysCfg.module == CH4)) { + flag = ((multipress == 1) || (multipress == 2)); + } else { + flag = (multipress == 1); + } + if (flag && sysCfg.mqtt_enabled && mqttClient.connected() && strcmp(sysCfg.button_topic, "0")) { + send_button_power(0, multipress, 2); // Execute command via MQTT using ButtonTopic to sync external clients + } else + { + if ((multipress == 1) || (multipress == 2)) { + if (WIFI_State()) { // WPSconfig, Smartconfig or Wifimanager active + restartflag = 1; + } else { + do_cmnd_power(multipress, 2); // Execute command internally + } + } else { + snprintf_P(scmnd, sizeof(scmnd), commands[multipress -3]); + do_cmnd(scmnd); + } + } + multipress = 0; + } + } + + for (byte i = 1; i < Maxdevice; i++) if (pin[GPIO_KEY1 +i] < 99) { + button = digitalRead(pin[GPIO_KEY1 +i]); + if ((button == PRESSED) && (lastbutton[i] == NOT_PRESSED)) { + if (sysCfg.mqtt_enabled && mqttClient.connected() && strcmp(sysCfg.button_topic, "0")) { + send_button_power(0, i +1, 2); // Execute commend via MQTT + } else { + do_cmnd_power(i +1, 2); // Execute command internally + } + } + lastbutton[i] = button; + } + + for (byte i = 0; i < Maxdevice; i++) if (pin[GPIO_SWT1 +i] < 99) { + button = digitalRead(pin[GPIO_SWT1 +i]); + if (button != lastwallswitch[i]) { + switchflag = 3; + switch (sysCfg.switchmode) { + case TOGGLE: + switchflag = 2; // Toggle + break; + case FOLLOW: + switchflag = button & 0x01; // Follow wall switch state + break; + case FOLLOW_INV: + switchflag = ~button & 0x01; // Follow inverted wall switch state + break; + case PUSHBUTTON: + if ((button == PRESSED) && (lastwallswitch[i] == NOT_PRESSED)) switchflag = 2; // Toggle with pushbutton to Gnd + break; + case PUSHBUTTON_INV: + if ((button == NOT_PRESSED) && (lastwallswitch[i] == PRESSED)) switchflag = 2; // Toggle with releasing pushbutton from Gnd + } + if (switchflag < 3) { + if (sysCfg.mqtt_enabled && mqttClient.connected() && strcmp(sysCfg.switch_topic,"0")) { + send_button_power(1, i +1, switchflag); // Execute commend via MQTT + } else { + do_cmnd_power(i +1, switchflag); // Execute command internally + } + } + lastwallswitch[i] = button; + } + } + + if (!(state % ((STATES/10)*2))) { + if (blinks || restartflag || otaflag) { + if (restartflag || otaflag) { + blinkstate = 1; // Stay lit + } else { + blinkstate ^= 1; // Blink + } + if ((!(sysCfg.ledstate &0x08)) && ((sysCfg.ledstate &0x06) || (blinks > 200) || (blinkstate))) { + setLed(blinkstate); + } + if (!blinkstate) { + blinks--; + if (blinks == 200) blinks = 0; + } + } else { + if (sysCfg.ledstate &0x01) setLed(power); + } + } + + switch (state) { + case (STATES/10)*2: + if (otaflag) { + otaflag--; + if (otaflag <= 0) { + otaflag = 12; + ESPhttpUpdate.rebootOnUpdate(false); + // Try multiple times to get the update, in case we have a transient issue. + // e.g. Someone issued "cmnd/sonoffs/update 1" and all the devices + // are hammering the OTAURL. + for (byte i = 0; i < OTA_ATTEMPTS && !otaok; i++) { + // Delay an increasing pseudo-random time for each interation. + // Starting at 0 (no delay) up to a maximum of OTA_ATTEMPTS-1 seconds. + delay((ESP.getChipId() % 1000) * i); + otaok = (ESPhttpUpdate.update(sysCfg.otaUrl) == HTTP_UPDATE_OK); + } + } + if (otaflag == 10) { // Allow MQTT to reconnect + otaflag = 0; + snprintf_P(stopic, sizeof(stopic), PSTR("%s/%s/UPGRADE"), PUB_PREFIX, sysCfg.mqtt_topic); + if (otaok) { + snprintf_P(svalue, sizeof(svalue), PSTR("Successful. Restarting")); + restartflag = 2; + } else { + snprintf_P(svalue, sizeof(svalue), PSTR("Failed %s"), ESPhttpUpdate.getLastErrorString().c_str()); + } + mqtt_publish(stopic, svalue); + } + } + break; + case (STATES/10)*4: + if (savedatacounter) { + savedatacounter--; + if (savedatacounter <= 0) { + if (sysCfg.savestate) { + if (!((sysCfg.pulsetime > 0) && (sysCfg.pulsetime < 30) && ((sysCfg.power &0xFE) == (power &0xFE)))) sysCfg.power = power; + } + CFG_Save(); + savedatacounter = sysCfg.savedata; + } + } + if (restartflag) { + if (restartflag == 211) { + CFG_Default(); + restartflag = 2; + } + if (restartflag == 212) { + CFG_Erase(); + CFG_Default(); + restartflag = 2; + } + if (sysCfg.savestate) sysCfg.power = power; + if (hlw_flg) hlw_savestate(); + CFG_Save(); + restartflag--; + if (restartflag <= 0) { + addLog_P(LOG_LEVEL_INFO, PSTR("APP: Restarting")); + ESP.restart(); + } + } + break; + case (STATES/10)*6: + WIFI_Check(wificheckflag); + wificheckflag = WIFI_RESTART; + break; + case (STATES/10)*8: + if (WiFi.status() == WL_CONNECTED) { + if (sysCfg.mqtt_enabled) { + if (!mqttClient.connected()) { + if (!mqttcounter) { + mqtt_reconnect(); + } else { + mqttcounter--; + } + } + } else { + if (!mqttcounter) { + mqtt_reconnect(); + } + } + } + break; + } +} + +void serial() +{ + char log[LOGSZ]; + + while (Serial.available()) { + yield(); + SerialInByte = Serial.read(); + + // Sonoff dual 19200 baud serial interface + if (Hexcode) { + Hexcode--; + if (Hexcode) { + ButtonCode = (ButtonCode << 8) | SerialInByte; + SerialInByte = 0; + } else { + if (SerialInByte != 0xA1) ButtonCode = 0; // 0xA1 - End of Sonoff dual button code + } + } + if (SerialInByte == 0xA0) { // 0xA0 - Start of Sonoff dual button code + SerialInByte = 0; + ButtonCode = 0; + Hexcode = 3; + } + + if (SerialInByte > 127) { // binary data... + SerialInByteCounter = 0; + Serial.flush(); + return; + } + if (isprint(SerialInByte)) { + if (SerialInByteCounter < INPUT_BUFFER_SIZE) { // add char to string if it still fits + serialInBuf[SerialInByteCounter++] = SerialInByte; + } else { + SerialInByteCounter = 0; + } + } + if (SerialInByte == '\n') { + serialInBuf[SerialInByteCounter] = 0; // serial data completed + if (seriallog_level < LOG_LEVEL_INFO) seriallog_level = LOG_LEVEL_INFO; + snprintf_P(log, sizeof(log), PSTR("CMND: %s"), serialInBuf); + addLog(LOG_LEVEL_INFO, log); + do_cmnd(serialInBuf); + SerialInByteCounter = 0; + Serial.flush(); + return; + } + } +} + +/********************************************************************************************/ + +void GPIO_init() +{ + char log[LOGSZ]; + uint8_t mpin; + mytmplt def_module; + + if (!sysCfg.module) sysCfg.module = SONOFF_BASIC; // Sonoff Basic + + memcpy_P(&def_module, &modules[sysCfg.module], sizeof(def_module)); + strlcpy(my_module.name, def_module.name, sizeof(my_module.name)); + for (byte i = 0; i < MAX_GPIO_PIN; i++) { + if (sysCfg.my_module.gp.io[i] > GPIO_NONE) my_module.gp.io[i] = sysCfg.my_module.gp.io[i]; + if ((def_module.gp.io[i] > GPIO_NONE) && (def_module.gp.io[i] < GPIO_USER)) my_module.gp.io[i] = def_module.gp.io[i]; + } + for (byte i = 0; i < GPIO_MAX; i++) pin[i] = 99; + for (byte i = 0; i < MAX_GPIO_PIN; i++) { + mpin = my_module.gp.io[i]; + +// snprintf_P(log, sizeof(log), PSTR("DBG: gpio pin %d, mpin %d"), i, mpin); +// addLog(LOG_LEVEL_DEBUG, log); + + if (mpin) { + if ((mpin >= GPIO_REL1_INV) && (mpin <= GPIO_REL4_INV)) { + rel_inverted[mpin - GPIO_REL1_INV] = 1; + mpin -= 4; + } + else if ((mpin >= GPIO_LED1_INV) && (mpin <= GPIO_LED4_INV)) { + led_inverted[mpin - GPIO_LED1_INV] = 1; + mpin -= 4; + } + else if (mpin == GPIO_DHT11) dht_type = DHT11; + else if (mpin == GPIO_DHT21) { + dht_type = DHT21; + mpin--; + } + else if (mpin == GPIO_DHT22) { + dht_type = DHT22; + mpin -= 2; + } + pin[mpin] = i; + } + } + + Maxdevice = 1; + switch (sysCfg.module) { + case SONOFF_DUAL: + case ELECTRODRAGON: + Maxdevice = 2; + break; + case SONOFF_4CH: + case CH4: + Maxdevice = 4; + break; + } + + swt_flg = ((pin[GPIO_SWT1] < 99) || (pin[GPIO_SWT2] < 99) || (pin[GPIO_SWT3] < 99) || (pin[GPIO_SWT4] < 99)); + + if ((sysCfg.module == SONOFF_DUAL) || (sysCfg.module == CH4)) { + Baudrate = 19200; + } else { + if (sysCfg.module == SONOFF_LED) { + for (byte i = 0; i < 5; i++) { + if (pin[GPIO_PWM0 +i] < 99) pinMode(pin[GPIO_PWM0 +i], OUTPUT); + } + } else { + for (byte i = 0; i < 4; i++) { + if (pin[GPIO_KEY1 +i] < 99) pinMode(pin[GPIO_KEY1 +i], INPUT_PULLUP); + if (pin[GPIO_REL1 +i] < 99) pinMode(pin[GPIO_REL1 +i], OUTPUT); + } + } + } + for (byte i = 0; i < 4; i++) { + if (pin[GPIO_LED1 +i] < 99) { + pinMode(pin[GPIO_LED1 +i], OUTPUT); + digitalWrite(pin[GPIO_LED1 +i], led_inverted[i]); + } + if (pin[GPIO_SWT1 +i] < 99) { + pinMode(pin[GPIO_SWT1 +i], INPUT_PULLUP); + lastwallswitch[i] = digitalRead(pin[GPIO_SWT1 +i]); // set global now so doesn't change the saved power state on first switch check + } + } + setLed(sysCfg.ledstate &8); + + hlw_flg = ((pin[GPIO_HLW_SEL] < 99) && (pin[GPIO_HLW_CF1] < 99) && (pin[GPIO_HLW_CF] < 99)); + if (hlw_flg) hlw_init(); + +#if defined(USE_DHT) || defined(USE_DHT2) + if (dht_type) dht_init(); +#endif // USE_DHT/2 + +#ifdef USE_I2C + i2c_flg = ((pin[GPIO_I2C_SCL] < 99) && (pin[GPIO_I2C_SDA] < 99)); + if (i2c_flg) Wire.begin(pin[GPIO_I2C_SDA],pin[GPIO_I2C_SCL]); +#endif // USE_I2C + +#ifdef USE_WS2812 + if (pin[GPIO_WS2812] < 99) ws2812_init(); +#endif // USE_WS2812 +} + +void setup() +{ + char log[LOGSZ]; + byte idx; + + Serial.begin(Baudrate); + delay(10); + Serial.println(); + seriallog_level = LOG_LEVEL_INFO; // Allow specific serial messages until config loaded + + snprintf_P(Version, sizeof(Version), PSTR("%d.%d.%d"), VERSION >> 24 & 0xff, VERSION >> 16 & 0xff, VERSION >> 8 & 0xff); + if (VERSION & 0x1f) { + idx = strlen(Version); + Version[idx] = 96 + (VERSION & 0x1f); + Version[idx +1] = 0; + } + if (!spiffsPresent()) + addLog_P(LOG_LEVEL_ERROR, PSTR("SPIFFS: ERROR - No spiffs present. Please reflash with at least 16K SPIFFS")); +#ifdef USE_SPIFFS + initSpiffs(); +#endif + CFG_Load(); + CFG_Delta(); + + sysCfg.bootcount++; + snprintf_P(log, sizeof(log), PSTR("APP: Bootcount %d"), sysCfg.bootcount); + addLog(LOG_LEVEL_DEBUG, log); + savedatacounter = sysCfg.savedata; + seriallog_timer = SERIALLOG_TIMER; + seriallog_level = sysCfg.seriallog_level; + syslog_level = sysCfg.syslog_level; + + GPIO_init(); + + if (Serial.baudRate() != Baudrate) { + if (seriallog_level) { + snprintf_P(log, sizeof(log), PSTR("APP: Change baudrate to %d and Serial logging will be disabled in %d seconds"), Baudrate, seriallog_timer); + addLog(LOG_LEVEL_INFO, log); + } + delay(100); + Serial.flush(); + Serial.begin(Baudrate); + delay(10); + Serial.println(); + } + + if (strstr(sysCfg.hostname, "%")) strlcpy(sysCfg.hostname, DEF_WIFI_HOSTNAME, sizeof(sysCfg.hostname)); + if (!strcmp(sysCfg.hostname, DEF_WIFI_HOSTNAME)) { + snprintf_P(Hostname, sizeof(Hostname)-1, sysCfg.hostname, sysCfg.mqtt_topic, ESP.getChipId() & 0x1FFF); + } else { + snprintf_P(Hostname, sizeof(Hostname)-1, sysCfg.hostname); + } + WIFI_Connect(Hostname); + + getClient(MQTTClient, sysCfg.mqtt_client, sizeof(MQTTClient)); + + if (sysCfg.module == MOTOR) sysCfg.poweronstate = 1; // Needs always on else in limbo! + if (ESP.getResetReason() == "Power on") { + if (sysCfg.poweronstate == 0) { // All off + power = 0; + setRelay(power); + } + else if (sysCfg.poweronstate == 1) { // All on + power = ((0x00FF << Maxdevice) >> 8); + setRelay(power); + } + else if (sysCfg.poweronstate == 2) { // All saved state toggle + power = (sysCfg.power & ((0x00FF << Maxdevice) >> 8)) ^ 0xFF; + if (sysCfg.savestate) setRelay(power); + } + else if (sysCfg.poweronstate == 3) { // All saved state + power = sysCfg.power & ((0x00FF << Maxdevice) >> 8); + if (sysCfg.savestate) setRelay(power); + } + } else { + power = sysCfg.power & ((0x00FF << Maxdevice) >> 8); + if (sysCfg.savestate) setRelay(power); + } + blink_powersave = power; + + rtc_init(every_second_cb); + + snprintf_P(log, sizeof(log), PSTR("APP: Project %s %s (Topic %s, Fallback %s, GroupTopic %s) Version %s"), + PROJECT, sysCfg.friendlyname[0], sysCfg.mqtt_topic, MQTTClient, sysCfg.mqtt_grptopic, Version); + addLog(LOG_LEVEL_INFO, log); +} + +void loop() +{ +#ifdef USE_WEBSERVER + pollDnsWeb(); +#endif // USE_WEBSERVER + +#if defined(USE_WEMO_EMULATION) || defined(USE_HUE_EMULATION) + pollUDP(); +#endif // USE_WEMO_EMULATION || USE_HUE_EMULATION + + if (millis() >= timerxs) stateloop(); + if (sysCfg.mqtt_enabled) mqttClient.loop(); + if (Serial.available()) serial(); + yield(); + + if (sysCfg.sleep) delay(sysCfg.sleep); +} diff --git a/sonoff/sonoff_template.h b/sonoff/sonoff_template.h new file mode 100644 index 000000000..315f26b3f --- /dev/null +++ b/sonoff/sonoff_template.h @@ -0,0 +1,248 @@ +/*********************************************************************************************\ + * Template parameters +\*********************************************************************************************/ + +#define GPIO_SENSOR_START 0 +#define GPIO_NONE 0 // Not used +#define GPIO_DHT11 1 // DHT11 +#define GPIO_DHT21 2 // DHT21 +#define GPIO_AM2301 2 // AM2301 +#define GPIO_DHT22 3 // DHT22 +#define GPIO_AM2302 3 // AM2302 +#define GPIO_AM2321 3 // AM2321 +#define GPIO_DSB 4 // Single wire DS18B20 or DS18S20 +#define GPIO_I2C_SCL 5 // I2C SCL +#define GPIO_I2C_SDA 6 // I2C SDA +#define GPIO_WS2812 7 // WS2812 Led string +#define GPIO_SWT1 8 // User connected external switches +#define GPIO_SENSOR_END 9 + +#define GPIO_SWT2 9 +#define GPIO_SWT3 10 +#define GPIO_SWT4 11 +#define GPIO_KEY1 12 // Button usually connected to GPIO0 +#define GPIO_KEY2 13 +#define GPIO_KEY3 14 +#define GPIO_KEY4 15 +#define GPIO_REL1 16 // Relays +#define GPIO_REL2 17 +#define GPIO_REL3 18 +#define GPIO_REL4 19 +#define GPIO_REL1_INV 20 +#define GPIO_REL2_INV 21 +#define GPIO_REL3_INV 22 +#define GPIO_REL4_INV 23 +#define GPIO_LED1 24 // Leds +#define GPIO_LED2 25 +#define GPIO_LED3 26 +#define GPIO_LED4 27 +#define GPIO_LED1_INV 28 +#define GPIO_LED2_INV 29 +#define GPIO_LED3_INV 30 +#define GPIO_LED4_INV 31 +#define GPIO_PWM0 32 // Cold +#define GPIO_PWM1 33 // Warm +#define GPIO_PWM2 34 // Red (swapped with Blue from original) +#define GPIO_PWM3 35 // Green +#define GPIO_PWM4 36 // Blue (swapped with Red from original) +#define GPIO_RXD 37 // Serial interface +#define GPIO_TXD 38 // Serial interface +#define GPIO_HLW_SEL 39 // HLW8012 Sel output (Sonoff Pow) +#define GPIO_HLW_CF1 40 // HLW8012 CF1 voltage / current (Sonoff Pow) +#define GPIO_HLW_CF 41 // HLW8012 CF power (Sonoff Pow) +#define GPIO_USER 42 // User configurable +#define GPIO_MAX 43 + +/********************************************************************************************/ + +enum module_t {SONOFF_BASIC, SONOFF_RF, SONOFF_SV, SONOFF_TH, SONOFF_DUAL, SONOFF_POW, SONOFF_4CH, S20, SLAMPHER, SONOFF_TOUCH, SONOFF_LED, CH1, CH4, MOTOR, ELECTRODRAGON, USER_TEST, MAXMODULE}; + +/********************************************************************************************/ + +#define MAX_GPIO_PIN 17 // Number of supported GPIO + +#define DHT11 11 +#define DHT21 21 +#define AM2301 21 +#define DHT22 22 +#define AM2302 22 +#define AM2321 22 + +typedef struct MYIO { + uint8_t io[MAX_GPIO_PIN]; +} myio; + +typedef struct MYTMPLT { + char name[16]; + myio gp; +} mytmplt; + +const char sensors[GPIO_SENSOR_END][8] PROGMEM = + { "None", "DHT11", "AM2301", "DHT22", "DS18x20", "I2C SCL", "I2C SDA", "WS2812", "Switch" }; + +const mytmplt modules[MAXMODULE] PROGMEM = { + { "Sonoff Basic", // Sonoff Basic + GPIO_KEY1, // GPIO00 Button + 0, 0, + GPIO_USER, // GPIO03 Serial TXD and Optional sensor + GPIO_USER, // GPIO04 Optional sensor + 0, 0, 0, 0, 0, 0, 0, + GPIO_REL1, // GPIO12 Red Led and Relay (0 = Off, 1 = On) + GPIO_LED1_INV, // GPIO13 Green Led (0 = On, 1 = Off) + GPIO_USER, // GPIO14 Optional sensor + 0, 0 + }, + { "Sonoff RF", // Sonoff RF + GPIO_KEY1, // GPIO00 Button + 0, 0, + GPIO_USER, // GPIO03 Serial TXD and Optional sensor + GPIO_USER, // GPIO04 Optional sensor + 0, 0, 0, 0, 0, 0, 0, + GPIO_REL1, // GPIO12 Red Led and Relay (0 = Off, 1 = On) + GPIO_LED1_INV, // GPIO13 Green Led (0 = On, 1 = Off) + GPIO_USER, // GPIO14 Optional sensor + 0, 0 + }, + { "Sonoff SV", // Sonoff SV + GPIO_KEY1, // GPIO00 Button + 0, 0, + GPIO_USER, // GPIO03 Serial TXD and Optional sensor + GPIO_USER, // GPIO04 Optional sensor + 0, 0, 0, 0, 0, 0, 0, + GPIO_REL1, // GPIO12 Red Led and Relay (0 = Off, 1 = On) + GPIO_LED1_INV, // GPIO13 Green Led (0 = On, 1 = Off) + GPIO_USER, // GPIO14 Optional sensor + 0, 0 + }, + { "Sonoff TH", // Sonoff TH10/16 + GPIO_KEY1, // GPIO00 Button + 0, 0, + GPIO_USER, // GPIO03 Serial TXD and Optional sensor + GPIO_USER, // GPIO04 Optional sensor + 0, 0, 0, 0, 0, 0, 0, + GPIO_REL1, // GPIO12 Red Led and Relay (0 = Off, 1 = On) + GPIO_LED1_INV, // GPIO13 Green Led (0 = On, 1 = Off) + GPIO_USER, // GPIO14 Optional sensor + 0, 0 + }, + { "Sonoff Dual", // Sonoff Dual + 0, + GPIO_TXD, // GPIO01 Relay control + 0, + GPIO_RXD, // GPIO03 Relay control + 0, 0, 0, 0, 0, 0, 0, 0, 0, + GPIO_LED1_INV, // GPIO13 Blue Led (0 = On, 1 = Off) + 0, 0, 0 + }, + { "Sonoff Pow", // Sonoff Pow + GPIO_KEY1, // GPIO00 Button + 0, 0, 0, 0, + GPIO_HLW_SEL, // GPIO05 HLW8012 Sel output + 0, 0, 0, 0, 0, 0, + GPIO_REL1, // GPIO12 Red Led and Relay (0 = Off, 1 = On) + GPIO_HLW_CF1, // GPIO13 HLW8012 CF1 voltage / current + GPIO_HLW_CF, // GPIO14 HLW8012 CF power + GPIO_LED1, // GPIO15 Green Led (0 = On, 1 = Off) + 0 + }, + { "Sonoff 4CH", // Sonoff 4CH + GPIO_KEY1, // GPIO00 Button 1 + 0, + GPIO_USER, // GPIO02 Optional sensor + 0, + GPIO_REL3, // GPIO04 Sonoff 4CH Red Led and Relay 3 (0 = Off, 1 = On) + GPIO_REL2, // GPIO05 Sonoff 4CH Red Led and Relay 2 (0 = Off, 1 = On) + 0, + GPIO_USER, // GPIO07 Optional sensor + GPIO_USER, // GPIO08 Optional sensor + GPIO_KEY2, // GPIO09 Button 2 + GPIO_KEY3, // GPIO10 Button 3 + 0, + GPIO_REL1, // GPIO12 Red Led and Relay 1 (0 = Off, 1 = On) + GPIO_LED1_INV, // GPIO13 Green Led (0 = On, 1 = Off) + GPIO_KEY4, // GPIO14 Button 4 + GPIO_REL4, // GPIO15 Red Led and Relay 4 (0 = Off, 1 = On) + 0 + }, + { "S20 Socket", // S20 Smart Socket + GPIO_KEY1, // GPIO00 Button + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + GPIO_REL1, // GPIO12 Red Led and Relay (0 = Off, 1 = On) + GPIO_LED1_INV, // GPIO13 Green Led (0 = On, 1 = Off) + 0, 0, 0 + }, + { "Slampher", // Slampher + GPIO_KEY1, // GPIO00 Button + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + GPIO_REL1, // GPIO12 Red Led and Relay (0 = Off, 1 = On) + GPIO_LED1_INV, // GPIO13 Blue Led (0 = On, 1 = Off) + 0, 0, 0 + }, + { "Sonoff Touch", // Sonoff Touch + GPIO_KEY1, // GPIO00 Button + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + GPIO_REL1, // GPIO12 Red Led and Relay (0 = Off, 1 = On) + GPIO_LED1_INV, // GPIO13 Blue Led (0 = On, 1 = Off) + 0, 0, 0 + }, + { "Sonoff LED", // Sonoff LED + GPIO_KEY1, // GPIO00 Button + 0, 0, 0, + GPIO_PWM3, // GPIO04 Green light + GPIO_PWM2, // GPIO05 Red light + 0, 0, 0, 0, 0, 0, + GPIO_PWM0, // GPIO12 Cold light + GPIO_LED1_INV, // GPIO13 Blue Led (0 = On, 1 = Off) + GPIO_PWM1, // GPIO14 Warm light + GPIO_PWM4, // GPIO15 Blue light + 0 + }, + { "1 Channel", // 1 Channel Inching/Latching Relay + GPIO_KEY1, // GPIO00 Button + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + GPIO_REL1, // GPIO12 Red Led and Relay (0 = Off, 1 = On) + GPIO_LED1_INV, // GPIO13 Green Led (0 = On, 1 = Off) + 0, 0, 0 + }, + { "4 Channel", // 4 Channel Inching/Latching Relays + 0, + GPIO_TXD, // GPIO01 Relay control + 0, + GPIO_RXD, // GPIO03 Relay control + 0, 0, 0, 0, 0, 0, 0, 0, 0, + GPIO_LED1_INV, // GPIO13 Blue Led (0 = On, 1 = Off) + 0, 0, 0 + }, + { "Motor C/AC", // Motor Clockwise / Anti clockwise + GPIO_KEY1, // GPIO00 Button + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + GPIO_REL1, // GPIO12 Red Led and Relay (0 = Off, 1 = On) + GPIO_LED1_INV, // GPIO13 Green Led (0 = On, 1 = Off) + 0, 0, 0 + }, + { "ElectroDragon", // ElectroDragon IoT Relay Board + GPIO_KEY2, // GPIO00 Button 2 + 0, + GPIO_KEY1, // GPIO02 Button 1 + GPIO_USER, // GPIO03 Serial TXD and Optional sensor + GPIO_USER, // GPIO04 Optional sensor + 0, 0, 0, 0, 0, 0, 0, + GPIO_REL2, // GPIO12 Red Led and Relay 2 (0 = Off, 1 = On) + GPIO_REL1, // GPIO13 Red Led and Relay 1 (0 = Off, 1 = On) + GPIO_USER, // GPIO14 Optional sensor + 0, + GPIO_LED1 // GPIO16 Green/Blue Led (1 = On, 0 = Off) + }, + { "User Test", // Sonoff Basic User Test + GPIO_KEY1, // GPIO00 Button + 0, 0, + GPIO_USER, // GPIO03 Serial TXD and Optional sensor + GPIO_USER, // GPIO04 Optional sensor + 0, 0, 0, 0, 0, 0, 0, + GPIO_REL1, // GPIO12 Red Led and Relay (0 = Off, 1 = On) + GPIO_LED1_INV, // GPIO13 Green Led (0 = On, 1 = Off) + GPIO_USER, // GPIO14 Optional sensor + 0, 0 + } +}; + diff --git a/sonoff/support.h b/sonoff/support.h new file mode 100644 index 000000000..b33a56a5f --- /dev/null +++ b/sonoff/support.h @@ -0,0 +1,17 @@ +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef __SUPPORT_H__ +#define __SUPPORT_H__ + +#include "user_interface.h" + +/* Function prototypes. */ +void WIFI_wps_status_cb(wps_cb_status status); + +#endif // ifndef __SUPPORT_H__ + +#ifdef __cplusplus +} +#endif diff --git a/sonoff/support.ino b/sonoff/support.ino new file mode 100644 index 000000000..21585b824 --- /dev/null +++ b/sonoff/support.ino @@ -0,0 +1,1005 @@ +/* +Copyright (c) 2017 Theo Arends. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +- Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. +- Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. +*/ + +/*********************************************************************************************\ + * Config - Flash or Spiffs +\*********************************************************************************************/ + +extern "C" { +#include "spi_flash.h" +} + +#define SPIFFS_START ((uint32_t)&_SPIFFS_start - 0x40200000) / SPI_FLASH_SEC_SIZE +#define SPIFFS_END ((uint32_t)&_SPIFFS_end - 0x40200000) / SPI_FLASH_SEC_SIZE + +// Version 2.x config +#define SPIFFS_CONFIG2 "/config.ini" +#define CFG_LOCATION2 SPIFFS_END - 2 + +// Version 3.x config +#define SPIFFS_CONFIG "/cfg.ini" +#define CFG_LOCATION SPIFFS_END - 4 + +uint32_t _cfgHash = 0; +int spiffsflag = 0; + +boolean spiffsPresent() +{ + return (SPIFFS_END - SPIFFS_START); +} + +uint32_t getHash() +{ + uint32_t hash = 0; + uint8_t *bytes = (uint8_t*)&sysCfg; + + for (uint16_t i = 0; i < sizeof(SYSCFG); i++) hash += bytes[i]*(i+1); + return hash; +} + +/*********************************************************************************************\ + * Config Save - Save parameters to Flash or Spiffs ONLY if any parameter has changed +\*********************************************************************************************/ +void CFG_Save() +{ + char log[LOGSZ]; + + if ((getHash() != _cfgHash) && (spiffsPresent())) { + if (!spiffsflag) { +#ifdef USE_SPIFFS + sysCfg.saveFlag++; + File f = SPIFFS.open(SPIFFS_CONFIG, "r+"); + if (f) { + uint8_t *bytes = (uint8_t*)&sysCfg; + for (int i = 0; i < sizeof(SYSCFG); i++) f.write(bytes[i]); + f.close(); + snprintf_P(log, sizeof(log), PSTR("Config: Saved configuration (%d bytes) to spiffs count %d"), sizeof(SYSCFG), sysCfg.saveFlag); + addLog(LOG_LEVEL_DEBUG, log); + } else { + addLog_P(LOG_LEVEL_ERROR, PSTR("Config: ERROR - Saving configuration failed")); + } + } else { +#endif // USE_SPIFFS + noInterrupts(); + if (sysCfg.saveFlag == 0) { // Handle default and rollover + spi_flash_erase_sector(CFG_LOCATION + (sysCfg.saveFlag &1)); + spi_flash_write((CFG_LOCATION + (sysCfg.saveFlag &1)) * SPI_FLASH_SEC_SIZE, (uint32*)&sysCfg, sizeof(SYSCFG)); + } + sysCfg.saveFlag++; + spi_flash_erase_sector(CFG_LOCATION + (sysCfg.saveFlag &1)); + spi_flash_write((CFG_LOCATION + (sysCfg.saveFlag &1)) * SPI_FLASH_SEC_SIZE, (uint32*)&sysCfg, sizeof(SYSCFG)); + interrupts(); + snprintf_P(log, sizeof(log), PSTR("Config: Saved configuration (%d bytes) to flash at %X and count %d"), sizeof(SYSCFG), CFG_LOCATION + (sysCfg.saveFlag &1), sysCfg.saveFlag); + addLog(LOG_LEVEL_DEBUG, log); + } + _cfgHash = getHash(); + } +} + +void CFG_Load() +{ + char log[LOGSZ]; + + if (spiffsPresent()) { + if (!spiffsflag) { +#ifdef USE_SPIFFS + File f = SPIFFS.open(SPIFFS_CONFIG, "r+"); + if (f) { + uint8_t *bytes = (uint8_t*)&sysCfg; + for (int i = 0; i < sizeof(SYSCFG); i++) bytes[i] = f.read(); + f.close(); + snprintf_P(log, sizeof(log), PSTR("Config: Loaded configuration from spiffs count %d"), sysCfg.saveFlag); + addLog(LOG_LEVEL_DEBUG, log); + } else { + addLog_P(LOG_LEVEL_ERROR, PSTR("Config: ERROR - Loading configuration failed")); + } + } else { +#endif // USE_SPIFFS + struct SYSCFGH { + unsigned long cfg_holder; + unsigned long saveFlag; + } _sysCfgH; + + noInterrupts(); + spi_flash_read((CFG_LOCATION) * SPI_FLASH_SEC_SIZE, (uint32*)&sysCfg, sizeof(SYSCFG)); + spi_flash_read((CFG_LOCATION + 1) * SPI_FLASH_SEC_SIZE, (uint32*)&_sysCfgH, sizeof(SYSCFGH)); + if (sysCfg.saveFlag < _sysCfgH.saveFlag) + spi_flash_read((CFG_LOCATION + 1) * SPI_FLASH_SEC_SIZE, (uint32*)&sysCfg, sizeof(SYSCFG)); + interrupts(); + snprintf_P(log, sizeof(log), PSTR("Config: Loaded configuration from flash at %X and count %d"), CFG_LOCATION + (sysCfg.saveFlag &1), sysCfg.saveFlag); + addLog(LOG_LEVEL_DEBUG, log); + } + } +// snprintf_P(log, sizeof(log), PSTR("Config: Check 1 for migration (%08X)"), sysCfg.version); +// addLog(LOG_LEVEL_NONE, log); + if (sysCfg.cfg_holder != CFG_HOLDER) { + if ((sysCfg.version < 0x03000000) || (sysCfg.version > 0x73000000)) { + CFG_Migrate(); // Config may be present with versions below 3.0.0 + } else { + CFG_Default(); + } + } + _cfgHash = getHash(); +} + +void CFG_Migrate() +{ + char log[LOGSZ]; + + if (spiffsPresent()) { + if (!spiffsflag) { +#ifdef USE_SPIFFS + File f = SPIFFS.open(SPIFFS_CONFIG2, "r+"); + if (f) { + uint8_t *bytes = (uint8_t*)&sysCfg2; + for (int i = 0; i < sizeof(SYSCFG2); i++) bytes[i] = f.read(); + f.close(); + snprintf_P(log, sizeof(log), PSTR("Config: Loaded previous configuration from spiffs count %d"), sysCfg2.saveFlag); + addLog(LOG_LEVEL_DEBUG, log); + } else { + addLog_P(LOG_LEVEL_ERROR, PSTR("Config: ERROR - Loading previous configuration failed")); + } + } else { +#endif // USE_SPIFFS + struct SYSCFGH { + unsigned long cfg_holder; + unsigned long saveFlag; + } _sysCfgH; + + noInterrupts(); + spi_flash_read((CFG_LOCATION2) * SPI_FLASH_SEC_SIZE, (uint32*)&sysCfg2, sizeof(SYSCFG2)); + spi_flash_read((CFG_LOCATION2 + 1) * SPI_FLASH_SEC_SIZE, (uint32*)&_sysCfgH, sizeof(SYSCFGH)); + if (sysCfg2.saveFlag < _sysCfgH.saveFlag) + spi_flash_read((CFG_LOCATION2 + 1) * SPI_FLASH_SEC_SIZE, (uint32*)&sysCfg2, sizeof(SYSCFG2)); + interrupts(); + snprintf_P(log, sizeof(log), PSTR("Config: Loaded previous configuration from flash at %X and count %d"), CFG_LOCATION2 + (sysCfg2.saveFlag &1), sysCfg2.saveFlag); + addLog(LOG_LEVEL_DEBUG, log); + } + } +// snprintf_P(log, sizeof(log), PSTR("Config: Check 2 for migration (%08X)"), sysCfg2.version); +// addLog(LOG_LEVEL_NONE, log); + if ((sysCfg2.version > 0x01000000) && (sysCfg2.version < 0x03000000)) { + CFG_Migrate_Part2(); // Config is present between version 1.0.0 and 3.0.0 + } else { + CFG_Default(); + } + _cfgHash = getHash(); +} + +void CFG_Erase() +{ + char log[LOGSZ]; + SpiFlashOpResult result; + + uint32_t _sectorStart = (ESP.getSketchSize() / SPI_FLASH_SEC_SIZE) + 1; + uint32_t _sectorEnd = ESP.getFlashChipRealSize() / SPI_FLASH_SEC_SIZE; + boolean _serialoutput = (LOG_LEVEL_DEBUG_MORE <= seriallog_level); + + snprintf_P(log, sizeof(log), PSTR("Config: Erasing %d flash sectors"), _sectorEnd - _sectorStart); + addLog(LOG_LEVEL_DEBUG, log); + + for (uint32_t _sector = _sectorStart; _sector < _sectorEnd; _sector++) { + noInterrupts(); + result = spi_flash_erase_sector(_sector); + interrupts(); + if (_serialoutput) { + Serial.print(F("Flash: Erased sector ")); + Serial.print(_sector); + if (result == SPI_FLASH_RESULT_OK) { + Serial.println(F(" OK")); + } else { + Serial.println(F(" Error")); + } + delay(10); + } + } +} + +void CFG_Dump() +{ + #define CFG_COLS 16 + + char log[LOGSZ]; + uint16_t idx, maxrow, row, col; + + uint8_t *buffer = (uint8_t *) &sysCfg; + maxrow = ((sizeof(SYSCFG)+CFG_COLS)/CFG_COLS); + + for (row = 0; row < maxrow; row++) { + idx = row * CFG_COLS; + snprintf_P(log, sizeof(log), PSTR("%04X:"), idx); + for (col = 0; col < CFG_COLS; col++) { + if (!(col%4)) snprintf_P(log, sizeof(log), PSTR("%s "), log); + snprintf_P(log, sizeof(log), PSTR("%s %02X"), log, buffer[idx + col]); + } + snprintf_P(log, sizeof(log), PSTR("%s |"), log); + for (col = 0; col < CFG_COLS; col++) { +// if (!(col%4)) snprintf_P(log, sizeof(log), PSTR("%s "), log); + snprintf_P(log, sizeof(log), PSTR("%s%c"), log, ((buffer[idx + col] > 0x20) && (buffer[idx + col] < 0x7F)) ? (char)buffer[idx + col] : ' '); + } + snprintf_P(log, sizeof(log), PSTR("%s|"), log); + addLog(LOG_LEVEL_INFO, log); + } +} + +#ifdef USE_SPIFFS +void initSpiffs() +{ + spiffsflag = 0; + if (!spiffsPresent()) { + spiffsflag = 1; + } else { + if (!SPIFFS.begin()) { + addLog_P(LOG_LEVEL_ERROR, PSTR("SPIFFS: WARNING - Failed to mount file system. Will use flash")); + spiffsflag = 2; + } else { + addLog_P(LOG_LEVEL_DEBUG, PSTR("SPIFFS: Mount successful")); + File f = SPIFFS.open(SPIFFS_CONFIG, "r"); + if (!f) { + addLog_P(LOG_LEVEL_DEBUG, PSTR("SPIFFS: Formatting...")); + SPIFFS.format(); + addLog_P(LOG_LEVEL_DEBUG, PSTR("SPIFFS: Formatted")); + File f = SPIFFS.open(SPIFFS_CONFIG, "w"); + if (f) { + for (int i = 0; i < sizeof(SYSCFG); i++) f.write(0); + f.close(); + } else { + addLog_P(LOG_LEVEL_ERROR, PSTR("SPIFFS: WARNING - Failed to init config file. Will use flash")); + spiffsflag = 3; + } + } + } + } +} +#endif // USE_SPIFFS + +/* +void setFlashChipMode(byte mode) +{ + char log[LOGSZ]; + uint32_t data; + + uint8_t * bytes = (uint8_t *) &data; + // read first 4 byte (magic byte + flash config) + if (spi_flash_read(0x0000, &data, 4) == SPI_FLASH_RESULT_OK) { + + snprintf_P(log, sizeof(log), PSTR("FLSH: Magic byte and flash config %08X"), data); + addLog(LOG_LEVEL_DEBUG, log); + + if (bytes[2] != mode) { + bytes[2] = mode &3; +// spi_flash_write(0x0000, &data, 4); + } + } +} +*/ +/*********************************************************************************************\ + * Wifi +\*********************************************************************************************/ + +#define WIFI_CONFIG_SEC 60 // seconds before restart +#define WIFI_MANAGER_SEC 120 // seconds before restart +#define WIFI_CHECK_SEC 20 // seconds +#define WIFI_RETRY_SEC 30 // seconds + +uint8_t _wificounter, _wifiretry, _wifistatus, _wpsresult, _wificonfigflag = 0, _wifiConfigCounter = 0; + +int WIFI_getRSSIasQuality(int RSSI) +{ + int quality = 0; + + if (RSSI <= -100) { + quality = 0; + } else if (RSSI >= -50) { + quality = 100; + } else { + quality = 2 * (RSSI + 100); + } + return quality; +} + +boolean WIFI_configCounter() +{ + if (_wifiConfigCounter) _wifiConfigCounter = WIFI_MANAGER_SEC; + return (_wifiConfigCounter); +} + +extern "C" { +#include "user_interface.h" +} + +void WIFI_wps_status_cb(wps_cb_status status); + +void WIFI_wps_status_cb(wps_cb_status status) +{ + char log[LOGSZ]; + +/* from user_interface.h: + enum wps_cb_status { + WPS_CB_ST_SUCCESS = 0, + WPS_CB_ST_FAILED, + WPS_CB_ST_TIMEOUT, + WPS_CB_ST_WEP, // WPS failed because that WEP is not supported + WPS_CB_ST_SCAN_ERR, // can not find the target WPS AP + }; +*/ + + _wpsresult = status; + if (_wpsresult == WPS_CB_ST_SUCCESS) { + wifi_wps_disable(); + } else { + snprintf_P(log, sizeof(log), PSTR("WPSconfig: FAILED with status %d"), _wpsresult); + addLog(LOG_LEVEL_DEBUG, log); + _wifiConfigCounter = 2; + } +} + +boolean WIFI_WPSConfigDone(void) +{ + return (!_wpsresult); +} + +boolean WIFI_beginWPSConfig(void) +{ + _wpsresult = 99; + if (!wifi_wps_disable()) return false; + if (!wifi_wps_enable(WPS_TYPE_PBC)) return false; // so far only WPS_TYPE_PBC is supported (SDK 2.0.0) + if (!wifi_set_wps_cb((wps_st_cb_t) &WIFI_wps_status_cb)) return false; + if (!wifi_wps_start()) return false; + return true; +} + +void WIFI_config(uint8_t type) +{ + if (!_wificonfigflag) { + if (type == WIFI_RETRY) return; +#if defined(USE_WEMO_EMULATION) || defined(USE_HUE_EMULATION) + UDP_Disconnect(); +#endif // USE_WEMO_EMULATION || USE_HUE_EMULATION + WiFi.disconnect(); // Solve possible Wifi hangs + _wificonfigflag = type; + _wifiConfigCounter = WIFI_CONFIG_SEC; // Allow up to WIFI_CONFIG_SECS seconds for phone to provide ssid/pswd + _wificounter = _wifiConfigCounter +5; + blinks = 1999; + if (_wificonfigflag == WIFI_RESTART) { + restartflag = 2; + } + else if (_wificonfigflag == WIFI_SMARTCONFIG) { + addLog_P(LOG_LEVEL_INFO, PSTR("Smartconfig: Active for 1 minute")); + WiFi.beginSmartConfig(); + } + else if (_wificonfigflag == WIFI_WPSCONFIG) { + if (WIFI_beginWPSConfig()) { + addLog_P(LOG_LEVEL_INFO, PSTR("WPSconfig: Active for 1 minute")); + } else { + addLog_P(LOG_LEVEL_INFO, PSTR("WPSconfig: Failed to start")); + _wifiConfigCounter = 3; + } + } +#ifdef USE_WEBSERVER + else if (_wificonfigflag == WIFI_MANAGER) { + addLog_P(LOG_LEVEL_INFO, PSTR("Wifimanager: Active for 1 minute")); + beginWifiManager(); + } +#endif // USE_WEBSERVER + } +} + +void WIFI_begin(uint8_t flag) +{ + const char PhyMode[] = " BGN"; + char log[LOGSZ]; + +#if defined(USE_WEMO_EMULATION) || defined(USE_HUE_EMULATION) + UDP_Disconnect(); +#endif // USE_WEMO_EMULATION || USE_HUE_EMULATION + if (!strncmp(ESP.getSdkVersion(),"1.5.3",5)) { + addLog_P(LOG_LEVEL_DEBUG, "Wifi: Patch issue 2186"); + WiFi.mode(WIFI_OFF); // See https://github.com/esp8266/Arduino/issues/2186 + } + WiFi.disconnect(); + WiFi.mode(WIFI_STA); // Disable AP mode + if (sysCfg.sleep) wifi_set_sleep_type(LIGHT_SLEEP_T); // Allow light sleep during idle times +// if (WiFi.getPhyMode() != WIFI_PHY_MODE_11N) WiFi.setPhyMode(WIFI_PHY_MODE_11N); + if (!WiFi.getAutoConnect()) WiFi.setAutoConnect(true); +// WiFi.setAutoReconnect(true); + switch (flag) { + case 0: // AP1 + case 1: // AP2 + sysCfg.sta_active = flag; + break; + case 2: // Toggle + sysCfg.sta_active ^= 1; + } // 3: Current AP + if (strlen(sysCfg.sta_ssid[1]) == 0) sysCfg.sta_active = 0; + WiFi.begin(sysCfg.sta_ssid[sysCfg.sta_active], sysCfg.sta_pwd[sysCfg.sta_active]); + snprintf_P(log, sizeof(log), PSTR("Wifi: Connecting to AP%d %s in mode 11%c as %s..."), + sysCfg.sta_active +1, sysCfg.sta_ssid[sysCfg.sta_active], PhyMode[WiFi.getPhyMode() & 0x3], Hostname); + addLog(LOG_LEVEL_INFO, log); +} + +void WIFI_check_ip() +{ + if ((WiFi.status() == WL_CONNECTED) && (static_cast(WiFi.localIP()) != 0)) { + _wificounter = WIFI_CHECK_SEC; + _wifiretry = WIFI_RETRY_SEC; + addLog_P((_wifistatus != WL_CONNECTED) ? LOG_LEVEL_INFO : LOG_LEVEL_DEBUG_MORE, PSTR("Wifi: Connected")); + _wifistatus = WL_CONNECTED; + } else { + _wifistatus = WiFi.status(); + switch (_wifistatus) { + case WL_CONNECTED: + addLog_P(LOG_LEVEL_INFO, PSTR("Wifi: Connect failed as no IP address received")); + _wifistatus = 0; + _wifiretry = WIFI_RETRY_SEC; + break; + case WL_NO_SSID_AVAIL: + addLog_P(LOG_LEVEL_INFO, PSTR("Wifi: Connect failed as AP cannot be reached")); + if (_wifiretry > (WIFI_RETRY_SEC / 2)) _wifiretry = WIFI_RETRY_SEC / 2; + else if (_wifiretry) _wifiretry = 0; + break; + case WL_CONNECT_FAILED: + addLog_P(LOG_LEVEL_INFO, PSTR("Wifi: Connect failed with AP incorrect password")); + if (_wifiretry > (WIFI_RETRY_SEC / 2)) _wifiretry = WIFI_RETRY_SEC / 2; + else if (_wifiretry) _wifiretry = 0; + break; + default: // WL_IDLE_STATUS and WL_DISCONNECTED + if (!_wifiretry || (_wifiretry == (WIFI_RETRY_SEC / 2))) { + addLog_P(LOG_LEVEL_INFO, PSTR("Wifi: Connect failed with AP timeout")); + } else { + addLog_P(LOG_LEVEL_DEBUG, PSTR("Wifi: Attempting connection...")); + } + } + if (_wifiretry) { + if (_wifiretry == WIFI_RETRY_SEC) WIFI_begin(3); // Select default SSID + if (_wifiretry == (WIFI_RETRY_SEC / 2)) WIFI_begin(2); // Select alternate SSID + _wificounter = 1; + _wifiretry--; + } else { + WIFI_config(sysCfg.sta_config); + _wificounter = 1; + _wifiretry = WIFI_RETRY_SEC; + } + } +} + +void WIFI_Check(uint8_t param) +{ + char log[LOGSZ]; + + _wificounter--; + switch (param) { + case WIFI_SMARTCONFIG: + case WIFI_MANAGER: + case WIFI_WPSCONFIG: + WIFI_config(param); + break; + default: + if (_wifiConfigCounter) { + _wifiConfigCounter--; + _wificounter = _wifiConfigCounter +5; + if (_wifiConfigCounter) { + if ((_wificonfigflag == WIFI_SMARTCONFIG) && WiFi.smartConfigDone()) _wifiConfigCounter = 0; + if ((_wificonfigflag == WIFI_WPSCONFIG) && WIFI_WPSConfigDone()) _wifiConfigCounter = 0; + if (!_wifiConfigCounter) { + if (strlen(WiFi.SSID().c_str())) strlcpy(sysCfg.sta_ssid[0], WiFi.SSID().c_str(), sizeof(sysCfg.sta_ssid[0])); + if (strlen(WiFi.psk().c_str())) strlcpy(sysCfg.sta_pwd[0], WiFi.psk().c_str(), sizeof(sysCfg.sta_pwd[0])); + sysCfg.sta_active = 0; + snprintf_P(log, sizeof(log), PSTR("Wificonfig: SSID1 %s and Password1 %s"), sysCfg.sta_ssid[0], sysCfg.sta_pwd[0]); + addLog(LOG_LEVEL_INFO, log); + } + } + if (!_wifiConfigCounter) { + if (_wificonfigflag == WIFI_SMARTCONFIG) WiFi.stopSmartConfig(); + restartflag = 2; + } + } else { + if (_wificounter <= 0) { + addLog_P(LOG_LEVEL_DEBUG_MORE, PSTR("Wifi: Checking connection...")); + _wificounter = WIFI_CHECK_SEC; + WIFI_check_ip(); + } + if ((WiFi.status() == WL_CONNECTED) && (static_cast(WiFi.localIP()) != 0) && !_wificonfigflag) { +#ifdef USE_DISCOVERY + if (!mDNSbegun) { + mDNSbegun = MDNS.begin(Hostname); + snprintf_P(log, sizeof(log), PSTR("mDNS: %s"), (mDNSbegun)?"Initialized":"Failed"); + addLog(LOG_LEVEL_INFO, log); + } +#endif // USE_DISCOVERY +#ifdef USE_WEBSERVER + if (sysCfg.webserver) { + startWebserver(sysCfg.webserver, WiFi.localIP()); +#ifdef USE_DISCOVERY +#ifdef WEBSERVER_ADVERTISE + MDNS.addService("http", "tcp", 80); +#endif // WEBSERVER_ADVERTISE +#endif // USE_DISCOVERY + } else { + stopWebserver(); + } +#if defined(USE_WEMO_EMULATION) || defined(USE_HUE_EMULATION) + UDP_Connect(); +#endif // USE_WEMO_EMULATION || USE_HUE_EMULATION +#endif // USE_WEBSERVER + } else { +#if defined(USE_WEMO_EMULATION) || defined(USE_HUE_EMULATION) + UDP_Disconnect(); +#endif // USE_WEMO_EMULATION || USE_HUE_EMULATION + mDNSbegun = false; + } + } + } +} + +int WIFI_State() +{ + int state; + + if ((WiFi.status() == WL_CONNECTED) && (static_cast(WiFi.localIP()) != 0)) state = WIFI_RESTART; + if (_wificonfigflag) state = _wificonfigflag; + return state; +} + +void WIFI_Connect(char *Hostname) +{ + WiFi.persistent(false); // Solve possible wifi init errors + WiFi.hostname(Hostname); + _wifistatus = 0; + _wifiretry = WIFI_RETRY_SEC; + _wificounter = 1; +} + +#ifdef USE_DISCOVERY +/*********************************************************************************************\ + * mDNS +\*********************************************************************************************/ + +#ifdef MQTT_HOST_DISCOVERY +boolean mdns_discoverMQTTServer() +{ + char log[LOGSZ], ip_str[20]; + int n; + + if (!mDNSbegun) return false; + + n = MDNS.queryService("mqtt", "tcp"); // Search for mqtt service + + snprintf_P(log, sizeof(log), PSTR("mDNS: Query done with %d mqtt services found"), n); + addLog(LOG_LEVEL_INFO, log); + + if (n > 0) { + // Note: current strategy is to get the first MQTT service (even when many are found) + IPtoCharArray(MDNS.IP(0), ip_str, 20); + + snprintf_P(log, sizeof(log), PSTR("mDNS: Service found on %s ip %s port %d"), + MDNS.hostname(0).c_str(), ip_str, MDNS.port(0)); + addLog(LOG_LEVEL_INFO, log); + + strlcpy(sysCfg.mqtt_host, ip_str, sizeof(sysCfg.mqtt_host)); + sysCfg.mqtt_port = MDNS.port(0); + } + + return n > 0; +} +#endif // MQTT_HOST_DISCOVERY + +void IPtoCharArray(IPAddress address, char *ip_str, size_t size) +{ + String str = String(address[0]); + str += "."; + str += String(address[1]); + str += "."; + str += String(address[2]); + str += "."; + str += String(address[3]); + str.toCharArray(ip_str, size); +} +#endif // USE_DISCOVERY + +/*********************************************************************************************\ + * Basic I2C routines +\*********************************************************************************************/ + +#ifdef USE_I2C +#define I2C_RETRY_COUNTER 3 + +int32_t i2c_read(uint8_t addr, uint8_t reg, uint8_t size) +{ + char log[LOGSZ]; + byte x = 0; + int32_t data = 0; + + do { + Wire.beginTransmission(addr); // start transmission to device + Wire.write(reg); // sends register address to read from + if (Wire.endTransmission(false) == 0) { // Try to become I2C Master, send data and collect bytes, keep master status for next request... + Wire.requestFrom((int)addr, (int)size); // send data n-bytes read + if (Wire.available() == size) + for(byte i = 0; i < size; i++) { + data <<= 8; + data |= Wire.read(); // receive DATA + } + } + x++; + } while (Wire.endTransmission(true) != 0 && x <= I2C_RETRY_COUNTER); // end transmission + +// snprintf_P(log, sizeof(log), PSTR("I2C: received %X, retries %d"), data, x -1); +// addLog(LOG_LEVEL_DEBUG_MORE, log); + + return data; +} + +uint8_t i2c_read8(uint8_t addr, uint8_t reg) +{ + return i2c_read(addr, reg, 1); +} + +uint16_t i2c_read16(uint8_t addr, uint8_t reg) +{ + return i2c_read(addr, reg, 2); +} + +int16_t i2c_readS16(uint8_t addr, uint8_t reg) +{ + return (int16_t)i2c_read(addr, reg, 2); +} + +uint16_t i2c_read16_LE(uint8_t addr, uint8_t reg) +{ + uint16_t temp = i2c_read(addr, reg, 2); + return (temp >> 8) | (temp << 8); +} + +int16_t i2c_readS16_LE(uint8_t addr, uint8_t reg) +{ + return (int16_t)i2c_read16_LE(addr, reg); +} + +int32_t i2c_read24(uint8_t addr, uint8_t reg) +{ + return i2c_read(addr, reg, 3); +} + +void i2c_write8(uint8_t addr, uint8_t reg, uint8_t val) +{ + byte x = I2C_RETRY_COUNTER; + + do { + Wire.beginTransmission((uint8_t)addr); // start transmission to device + Wire.write(reg); // sends register address to read from + Wire.write(val); // write data + x--; + } while (Wire.endTransmission(true) != 0 && x != 0); // end transmission +} + +void i2c_scan(char *devs, unsigned int devs_len) +{ + byte error, address, any = 0; + char tstr[10]; + + snprintf_P(devs, devs_len, PSTR("{\"I2Cscan\":\"Device(s) found at")); + for (address = 1; address <= 127; address++) { + Wire.beginTransmission(address); + error = Wire.endTransmission(); + if (error == 0) { + snprintf_P(tstr, sizeof(tstr), PSTR(" 0x%2x"), address); + strncat(devs, tstr, devs_len); + any = 1; + } + else if (error == 4) snprintf_P(devs, devs_len, PSTR("{\"I2Cscan\":\"Unknow error at 0x%2x\"}"), address); + } + if (any) { + strncat(devs, "\"}", devs_len); + } else { + snprintf_P(devs, devs_len, PSTR("{\"I2Cscan\":\"No devices found\"}")); + } +} +#endif // USE_I2C + +/*********************************************************************************************\ + * Real Time Clock + * + * Sources: Time by Michael Margolis and Paul Stoffregen (https://github.com/PaulStoffregen/Time) + * Timezone by Jack Christensen (https://github.com/JChristensen/Timezone) +\*********************************************************************************************/ + +extern "C" { +#include "sntp.h" +} + +#define SECS_PER_MIN ((uint32_t)(60UL)) +#define SECS_PER_HOUR ((uint32_t)(3600UL)) +#define SECS_PER_DAY ((uint32_t)(SECS_PER_HOUR * 24UL)) +#define LEAP_YEAR(Y) (((1970+Y)>0) && !((1970+Y)%4) && (((1970+Y)%100) || !((1970+Y)%400))) + +Ticker tickerRTC; + +static const uint8_t monthDays[] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }; // API starts months from 1, this array starts from 0 +static const char monthNames[37] = { "JanFebMrtAprMayJunJulAugSepOctNovDec" }; + +uint32_t utctime = 0, loctime = 0, dsttime = 0, stdtime = 0, ntptime = 0, midnight = 1451602800; + +rtcCallback rtcCb = NULL; + +void breakTime(uint32_t timeInput, TIME_T &tm) +{ +// break the given timeInput into time components +// this is a more compact version of the C library localtime function +// note that year is offset from 1970 !!! + + uint8_t year, month, monthLength; + uint32_t time; + unsigned long days; + + time = timeInput; + tm.Second = time % 60; + time /= 60; // now it is minutes + tm.Minute = time % 60; + time /= 60; // now it is hours + tm.Hour = time % 24; + time /= 24; // now it is days + tm.Wday = ((time + 4) % 7) + 1; // Sunday is day 1 + + year = 0; + days = 0; + while((unsigned)(days += (LEAP_YEAR(year) ? 366 : 365)) <= time) { + year++; + } + tm.Year = year; // year is offset from 1970 + + days -= LEAP_YEAR(year) ? 366 : 365; + time -= days; // now it is days in this year, starting at 0 + tm.DayOfYear = time; + + days = 0; + month = 0; + monthLength = 0; + for (month = 0; month < 12; month++) { + if (month == 1) { // february + if (LEAP_YEAR(year)) { + monthLength = 29; + } else { + monthLength = 28; + } + } else { + monthLength = monthDays[month]; + } + + if (time >= monthLength) { + time -= monthLength; + } else { + break; + } + } + strlcpy(tm.MonthName, monthNames + (month *3), 4); + tm.Month = month + 1; // jan is month 1 + tm.Day = time + 1; // day of month + tm.Valid = (timeInput > 1451602800); // 2016-01-01 +} + +uint32_t makeTime(TIME_T &tm) +{ +// assemble time elements into time_t +// note year argument is offset from 1970 + + int i; + uint32_t seconds; + + // seconds from 1970 till 1 jan 00:00:00 of the given year + seconds = tm.Year * (SECS_PER_DAY * 365); + for (i = 0; i < tm.Year; i++) { + if (LEAP_YEAR(i)) { + seconds += SECS_PER_DAY; // add extra days for leap years + } + } + + // add days for this year, months start from 1 + for (i = 1; i < tm.Month; i++) { + if ((i == 2) && LEAP_YEAR(tm.Year)) { + seconds += SECS_PER_DAY * 29; + } else { + seconds += SECS_PER_DAY * monthDays[i-1]; // monthDay array starts from 0 + } + } + seconds+= (tm.Day - 1) * SECS_PER_DAY; + seconds+= tm.Hour * SECS_PER_HOUR; + seconds+= tm.Minute * SECS_PER_MIN; + seconds+= tm.Second; + return seconds; +} + +uint32_t toTime_t(TimeChangeRule r, int yr) +{ + TIME_T tm; + uint32_t t; + uint8_t m, w; // temp copies of r.month and r.week + + m = r.month; + w = r.week; + if (w == 0) { // Last week = 0 + if (++m > 12) { // for "Last", go to the next month + m = 1; + yr++; + } + w = 1; // and treat as first week of next month, subtract 7 days later + } + + tm.Hour = r.hour; + tm.Minute = 0; + tm.Second = 0; + tm.Day = 1; + tm.Month = m; + tm.Year = yr - 1970; + t = makeTime(tm); // First day of the month, or first day of next month for "Last" rules + breakTime(t, tm); + t += (7 * (w - 1) + (r.dow - tm.Wday + 7) % 7) * SECS_PER_DAY; + if (r.week == 0) t -= 7 * SECS_PER_DAY; //back up a week if this is a "Last" rule + return t; +} + +String rtc_time(int type) +{ + char stime[25]; // Skip newline + + uint32_t time = utctime; + if (type == 1) time = loctime; + if (type == 2) time = dsttime; + if (type == 3) time = stdtime; + snprintf_P(stime, sizeof(stime), PSTR("%s"), sntp_get_real_time(time)); + return String(stime); +} + +uint32_t rtc_loctime() +{ + return loctime; +} + +uint32_t rtc_midnight() +{ + return midnight; +} + +void rtc_second() +{ + char log[LOGSZ]; + byte ntpsync; + uint32_t stdoffset, dstoffset; + TIME_T tmpTime; + + ntpsync = 0; + if (rtcTime.Year < 2016) { + if (WiFi.status() == WL_CONNECTED) { + ntpsync = 1; // Initial NTP sync + } + } else { + if ((rtcTime.Minute == 1) && (rtcTime.Second == 1)) { + ntpsync = 1; // Hourly NTP sync at xx:01:01 + } + } + if (ntpsync) { + ntptime = sntp_get_current_timestamp(); + if (ntptime) { + utctime = ntptime; + breakTime(utctime, tmpTime); + rtcTime.Year = tmpTime.Year + 1970; + dsttime = toTime_t(myDST, rtcTime.Year); + stdtime = toTime_t(mySTD, rtcTime.Year); + snprintf_P(log, sizeof(log), PSTR("RTC: (UTC) %s"), rtc_time(0).c_str()); + addLog(LOG_LEVEL_DEBUG, log); + snprintf_P(log, sizeof(log), PSTR("RTC: (DST) %s"), rtc_time(2).c_str()); + addLog(LOG_LEVEL_DEBUG, log); + snprintf_P(log, sizeof(log), PSTR("RTC: (STD) %s"), rtc_time(3).c_str()); + addLog(LOG_LEVEL_DEBUG, log); + } + } + utctime++; + loctime = utctime; + if (loctime > 1451602800) { // 2016-01-01 + if (sysCfg.timezone == 99) { + dstoffset = myDST.offset * SECS_PER_MIN; + stdoffset = mySTD.offset * SECS_PER_MIN; + if ((utctime >= (dsttime - stdoffset)) && (utctime < (stdtime - dstoffset))) { + loctime += dstoffset; // Daylight Saving Time + } else { + loctime += stdoffset; // Standard Time + } + } else { + loctime += sysCfg.timezone * SECS_PER_HOUR; + } + } + breakTime(loctime, rtcTime); + if (!rtcTime.Hour && !rtcTime.Minute && !rtcTime.Second && rtcTime.Valid) { + midnight = loctime; + } + rtcTime.Year += 1970; + if (rtcCb) rtcCb(); +} + +void rtc_init(rtcCallback cb) +{ + rtcCb = cb; + sntp_setservername(0, (char*)NTP_SERVER1); + sntp_setservername(1, (char*)NTP_SERVER2); + sntp_setservername(2, (char*)NTP_SERVER3); + sntp_stop(); + sntp_set_timezone(0); // UTC time + sntp_init(); + utctime = 0; + breakTime(utctime, rtcTime); + tickerRTC.attach(1, rtc_second); +} + +/*********************************************************************************************\ + * Syslog +\*********************************************************************************************/ + +void syslog(const char *message) +{ + char str[TOPSZ+MESSZ]; + + if (portUDP.beginPacket(sysCfg.syslog_host, sysCfg.syslog_port)) { + snprintf_P(str, sizeof(str), PSTR("%s ESP-%s"), Hostname, message); + portUDP.write(str); + portUDP.endPacket(); + } else { + syslog_level = 0; + syslog_timer = SYSLOG_TIMER; + snprintf_P(str, sizeof(str), PSTR("SYSL: Syslog Host not found so logging disabled for %d seconds. Consider syslog 0"), SYSLOG_TIMER); + addLog(LOG_LEVEL_INFO, str); + } +} + +void addLog(byte loglevel, const char *line) +{ + char mxtime[9]; + + snprintf_P(mxtime, sizeof(mxtime), PSTR("%02d:%02d:%02d"), rtcTime.Hour, rtcTime.Minute, rtcTime.Second); + +#ifdef DEBUG_ESP_PORT + DEBUG_ESP_PORT.printf("%s %s\n", mxtime, line); +#endif // DEBUG_ESP_PORT + if (loglevel <= seriallog_level) Serial.printf("%s %s\n", mxtime, line); +#ifdef USE_WEBSERVER + if (loglevel <= sysCfg.weblog_level) { + Log[logidx] = String(mxtime) + " " + String(line); + logidx++; + if (logidx > MAX_LOG_LINES -1) logidx = 0; + } +#endif // USE_WEBSERVER + if ((WiFi.status() == WL_CONNECTED) && (loglevel <= syslog_level)) syslog(line); +} + +void addLog_P(byte loglevel, const char *formatP) +{ + char mess[MESSZ]; + + snprintf_P(mess, sizeof(mess), formatP); + addLog(loglevel, mess); +} + +/*********************************************************************************************\ + * +\*********************************************************************************************/ diff --git a/sonoff/user_config.h b/sonoff/user_config.h new file mode 100644 index 000000000..a0cae5c9a --- /dev/null +++ b/sonoff/user_config.h @@ -0,0 +1,147 @@ +/*********************************************************************************************\ + * User specific configuration parameters + * + * ATTENTION: Changes to most PARAMETER defines will only override flash settings if you change + * define CFG_HOLDER. + * Most parameters can be changed online using commands via MQTT, WebConsole or serial + * + * Corresponding MQTT/Serial/Console commands in [brackets] +\*********************************************************************************************/ + +// -- Project ------------------------------------- +#define PROJECT "sonoff" // PROJECT is used as the default topic delimiter and OTA file name + // As an IDE restriction it needs to be the same as the main .ino file + +#define CFG_HOLDER 0x20161209 // [Reset 1] Change this value to load following default configuration parameters +#define SAVE_DATA 1 // [SaveData] Save changed parameters to Flash (0 = disable, 1 - 3600 seconds) +#define SAVE_STATE 1 // [SaveState] Save changed power state to Flash (0 = disable, 1 = enable) + +// -- Wifi ---------------------------------------- +#define STA_SSID1 "indebuurt1" // [Ssid1] Wifi SSID +#define STA_PASS1 "VnsqrtnrsddbrN" // [Password1] Wifi password +#define STA_SSID2 "indebuurt2" // [Ssid2] Optional alternate AP Wifi SSID +#define STA_PASS2 "VnsqrtnrsddbrN" // [Password2] Optional alternate AP Wifi password +#define WIFI_HOSTNAME "%s-%04d" // [Hostname] Expands to - +#define WIFI_CONFIG_TOOL WIFI_WPSCONFIG // [WifiConfig] Default tool if wifi fails to connect + // (WIFI_RESTART, WIFI_SMARTCONFIG, WIFI_MANAGER, WIFI_WPSCONFIG, WIFI_RETRY) +// -- Syslog -------------------------------------- +#define SYS_LOG_HOST "domus1" // [LogHost] (Linux) syslog host +#define SYS_LOG_PORT 514 // [LogPort] default syslog UDP port +#define SYS_LOG_LEVEL LOG_LEVEL_NONE // [SysLog] +#define SERIAL_LOG_LEVEL LOG_LEVEL_INFO // [SerialLog] +#define WEB_LOG_LEVEL LOG_LEVEL_INFO // [WebLog] + +// -- Ota ----------------------------------------- +#define OTA_URL "http://domus1:80/api/arduino/" PROJECT ".ino.bin" // [OtaUrl] + +// -- MQTT ---------------------------------------- +#define MQTT_USE 1 // [Mqtt] Select default MQTT use (0 = Off, 1 = On) +// !!! TLS uses a LOT OF MEMORY (20k) so be careful to enable other options at the same time !!! +//#define USE_MQTT_TLS // EXPERIMENTAL Use TLS for MQTT connection (+53k code, +20k mem) + // Needs Fingerprint, TLS Port, UserId and Password +#ifdef USE_MQTT_TLS + #define MQTT_HOST "m20.cloudmqtt.com" // [MqttHost] + #define MQTT_FINGERPRINT "A5 02 FF 13 99 9F 8B 39 8E F1 83 4F 11 23 65 0B 32 36 FC 07" // [MqttFingerprint] + #define MQTT_PORT 20123 // [MqttPort] MQTT TLS port + #define MQTT_USER "cloudmqttuser" // [MqttUser] Mandatory user + #define MQTT_PASS "cloudmqttpassword" // [MqttPassword] Mandatory password +#else + #define MQTT_HOST "domus1" // [MqttHost] + #define MQTT_PORT 1883 // [MqttPort] MQTT port (10123 on CloudMQTT) + #define MQTT_USER "DVES_USER" // [MqttUser] Optional user + #define MQTT_PASS "DVES_PASS" // [MqttPassword] Optional password +#endif + +#define MQTT_CLIENT_ID "DVES_%06X" // [MqttClient] Also fall back topic using Chip Id = last 6 characters of MAC address + +#define SUB_PREFIX "cmnd" // Sonoff devices subscribe to:- SUB_PREFIX/MQTT_TOPIC and SUB_PREFIX/MQTT_GRPTOPIC +#define PUB_PREFIX "stat" // Sonoff devices publish to:- PUB_PREFIX/MQTT_TOPIC +#define PUB_PREFIX2 "tele" // Sonoff devices publish telemetry data to:- PUB_PREFIX2/MQTT_TOPIC/UPTIME, POWER/LIGHT and TIME + // May be named the same as PUB_PREFIX +#define MQTT_TOPIC PROJECT // [Topic] (unique) MQTT device topic +#define MQTT_GRPTOPIC "sonoffs" // [GroupTopic] MQTT Group topic +#define MQTT_BUTTON_RETAIN 0 // [ButtonRetain] Button may send retain flag (0 = off, 1 = on) +#define MQTT_POWER_RETAIN 0 // [PowerRetain] Power status message may send retain flag (0 = off, 1 = on) +#define MQTT_SWITCH_RETAIN 0 // [SwitchRetain] Switch may send retain flag (0 = off, 1 = on) + +#define MQTT_STATUS_OFF "OFF" // Command or Status result when turned off (needs to be a string like "0" or "Off") +#define MQTT_STATUS_ON "ON" // Command or Status result when turned on (needs to be a string like "1" or "On") +#define MQTT_CMND_TOGGLE "TOGGLE" // Command to send when toggling (needs to be a string like "2" or "Toggle") + +// -- MQTT - Telemetry ---------------------------- +#define TELE_PERIOD 300 // [TelePeriod] Telemetry (0 = disable, 10 - 3600 seconds) + +// -- MQTT - Domoticz ----------------------------- +#define USE_DOMOTICZ // Enable Domoticz (+5k code, +0.3k mem) - Disable by // + #define DOMOTICZ_IN_TOPIC "domoticz/in" // [DomoticzInTopic] + #define DOMOTICZ_OUT_TOPIC "domoticz/out" // [DomoticzOutTopic] + #define DOMOTICZ_UPDATE_TIMER 0 // [DomoticzUpdateTimer] Send relay status (0 = disable, 1 - 3600 seconds) (Optional) + +// -- HTTP ---------------------------------------- +#define USE_WEBSERVER // Enable web server and wifi manager (+43k code, +2k mem) - Disable by // + #define FRIENDLY_NAME1 "Sonoff" // [FriendlyName1] Friendlyname up to 32 characters used by webpages and Alexa + #define FRIENDLY_NAME2 "Sonoff2" // [FriendlyName2] Friendlyname up to 32 characters used by Alexa + #define FRIENDLY_NAME3 "Sonoff3" // [FriendlyName3] Friendlyname up to 32 characters used by Alexa + #define FRIENDLY_NAME4 "Sonoff4" // [FriendlyName4] Friendlyname up to 32 characters used by Alexa + #define WEB_SERVER 2 // [WebServer] Web server (0 = Off, 1 = Start as User, 2 = Start as Admin) +// #define USE_WEMO_EMULATION // Enable Belkin WeMo PowerSwitch emulation for Alexa (+4k code, +2k mem) +// #define USE_HUE_EMULATION // Enable Hue Bridge emulation for Alexa (+5k code, +2k mem) + +// -- mDNS ---------------------------------------- +#define USE_DISCOVERY // Enable mDNS for the following services (+8k code, +0.3k mem) + #define WEBSERVER_ADVERTISE // Provide access to webserver by name .local/ + #define MQTT_HOST_DISCOVERY // Find MQTT host server (overrides MQTT_HOST if found) + +// -- Time - Up to three NTP servers in your region +#define NTP_SERVER1 "pool.ntp.org" +#define NTP_SERVER2 "nl.pool.ntp.org" +#define NTP_SERVER3 "0.nl.pool.ntp.org" + +// -- Time - Start Daylight Saving Time and timezone offset from UTC in minutes +#define TIME_DST Last, Sun, Mar, 2, +120 // Last sunday in march at 02:00 +120 minutes + +// -- Time - Start Standard Time and timezone offset from UTC in minutes +#define TIME_STD Last, Sun, Oct, 3, +60 // Last sunday in october 02:00 +60 minutes + +// -- Application --------------------------------- +#define APP_TIMEZONE 1 // [Timezone] +1 hour (Amsterdam) (-12 .. 12 = hours from UTC, 99 = use TIME_DST/TIME_STD) +#define APP_LEDSTATE LED_POWER // [LedState] Function of led (LED_OFF, LED_POWER, LED_MQTTSUB, LED_POWER_MQTTSUB, LED_MQTTPUB, LED_POWER_MQTTPUB, LED_MQTT, LED_POWER_MQTT) +#define APP_PULSETIME 0 // [PulseTime] Time in 0.1 Sec to turn off power for relay 1 (0 = disabled) +#define APP_POWERON_STATE 3 // [PowerOnState] Power On Relay state (0 = Off, 1 = On, 2 = Toggle Saved state, 3 = Saved state) +#define APP_BLINKTIME 10 // [BlinkTime] Time in 0.1 Sec to blink/toggle power for relay 1 +#define APP_BLINKCOUNT 10 // [BlinkCount] Number of blinks (0 = 32000) +#define APP_SLEEP 0 // [Sleep] Sleep time to lower energy consumption (0 = Off, 1 - 250 mSec) + +#define SWITCH_MODE TOGGLE // [SwitchMode] TOGGLE, FOLLOW, FOLLOW_INV, PUSHBUTTON or PUSHBUTTON_INV (the wall switch state) +#define WS2812_LEDS 30 // [Pixels] Number of WS2812 LEDs to start with + +#define TEMP_CONVERSION 0 // Convert temperature to (0 = Celsius or 1 = Fahrenheit) +#define TEMP_RESOLUTION 1 // Maximum number of decimals (0 - 3) showing sensor Temperature +#define HUMIDITY_RESOLUTION 1 // Maximum number of decimals (0 - 3) showing sensor Humidity +#define PRESSURE_RESOLUTION 1 // Maximum number of decimals (0 - 3) showing sensor Pressure + +// -- Sensor code selection ----------------------- +//#define USE_DHT2 // Optional using Adafruit DHT library +//#define USE_DS18x20 // Optional using OneWire library for multiple DS18B20 and/or DS18S20 + +#define USE_I2C // I2C Support (+10k code, 0.2k mem) + #define USE_BH1750 // Add I2C code for BH1750 sensor + #define USE_BMP // Add I2C code for BMP/BME280 sensor + #define USE_HTU // Add I2C code for HTU21 sensor + +#define USE_WS2812 // WS2812 Led string support (+8k code, +1k mem) +// #define USE_WS2812_DMA // DMA supports only GPIO03 (= Serial TXD) (+1k mem) + // When USE_WS2812_DMA is enabled expect Exceptions on Pow + +/*********************************************************************************************\ + * No user configurable items below +\*********************************************************************************************/ + +#if defined(USE_WEMO_EMULATION) && defined(USE_HUE_EMULATION) + #error "Select either USE_WEMO_EMULATION or USE_HUE_EMULATION" +#endif + +#if (ARDUINO < 10610) + #error "This software is supported with Arduino IDE starting from 1.6.10 and ESP8266 Release 2.3.0" +#endif + diff --git a/sonoff/user_config_override.h b/sonoff/user_config_override.h new file mode 100644 index 000000000..b83e88add --- /dev/null +++ b/sonoff/user_config_override.h @@ -0,0 +1,23 @@ +/*****************************************************************************************************\ + * User specific configuration parameters to override user_config.h + * + * ATTENTION: - Changes to most PARAMETER defines will only override flash settings if you change + * define CFG_HOLDER. + * - Expect compiler warnings when no ifdef/undef/endif sequence is used. + * - You still need to update user_config.h for major defines MODULE and USE_MQTT_TLS. + * - Changing MODULE defines are not being tested for validity as they are in user_config.h. + * - Most parameters can be changed online using commands via MQTT, WebConsole or serial. + * - So I see no use in this but anyway, your on your own. +\*****************************************************************************************************/ + +// Examples +//#ifdef CFG_HOLDER +//#undef CFG_HOLDER +//#endif +//#define CFG_HOLDER 0x20161210 + +//#ifdef STA_SSID1 +//#undef STA_SSID1 +//#endif +//#define STA_SSID1 "yourssid1" + diff --git a/sonoff/webserver.ino b/sonoff/webserver.ino new file mode 100644 index 000000000..aa677d8ba --- /dev/null +++ b/sonoff/webserver.ino @@ -0,0 +1,1521 @@ +/* +Copyright (c) 2017 Theo Arends. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +- Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. +- Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifdef USE_WEBSERVER +/*********************************************************************************************\ + * Web server and WiFi Manager + * + * Enables configuration and reconfiguration of WiFi credentials using a Captive Portal + * Source by AlexT (https://github.com/tzapu) +\*********************************************************************************************/ + +#define STR_HELPER(x) #x +#define STR(x) STR_HELPER(x) + +const char HTTP_HEAD[] PROGMEM = + "" + "" + "" + "" + "{v}" + "" + "" + "" + "" + "
" + "

{ha} Module

{h}

"; +const char HTTP_MSG_RSTRT[] PROGMEM = + "
Device will restart in a few seconds

"; +const char HTTP_BTN_MENU1[] PROGMEM = + "
" + "
" + "
" + "
"; +const char HTTP_BTN_RSTRT[] PROGMEM = + "
"; +const char HTTP_BTN_MENU2[] PROGMEM = + "
" + "
"; +const char HTTP_BTN_MENU3[] PROGMEM = + "
" +#ifdef USE_DOMOTICZ + "
" +#endif // USE_DOMOTICZ + ""; +const char HTTP_BTN_MENU4[] PROGMEM = + "
" + "
" + "
"; +const char HTTP_BTN_MAIN[] PROGMEM = + "

"; +const char HTTP_BTN_CONF[] PROGMEM = + "

"; +const char HTTP_FORM_MODULE[] PROGMEM = + "
 Module parameters 
" + "" + "
Module type ({mt})
" + "
AP1 SSId (" STA_SSID1 ")

" + "
AP1 Password

" + "
AP2 SSId (" STA_SSID2 ")

" + "
AP2 Password

" + "
Hostname ({h0})

"; +const char HTTP_FORM_MQTT[] PROGMEM = + "
 MQTT parameters " + "" + "
Host (" MQTT_HOST ")

" + "
Port (" STR(MQTT_PORT) ")

" + "
Client Id ({m0})

" + "
User (" MQTT_USER ")

" + "
Password

" + "
Topic (" MQTT_TOPIC ")

"; +const char HTTP_FORM_LOG1[] PROGMEM = + "
 Logging parameters " + ""; +const char HTTP_FORM_LOG2[] PROGMEM = + "
{b0} level ({b1})

"; +const char HTTP_FORM_LOG3[] PROGMEM = + "
Syslog host (" SYS_LOG_HOST ")

" + "
Syslog port (" STR(SYS_LOG_PORT) ")

" + "
Telemetric period (" STR(TELE_PERIOD) ")

"; +const char HTTP_FORM_OTHER[] PROGMEM = + "
 Other parameters " + "" + "
MQTT enable
"; +const char HTTP_FORM_END[] PROGMEM = + "
"; +const char HTTP_FORM_UPG[] PROGMEM = + "
" + "
 Upgrade by web server " + "
" + "
OTA Url

" + "
" + "


" + "
 Upgrade by file upload " + "
" + "

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


" + "
" + "
" +// "
" + "
"; +const char HTTP_COUNTER[] PROGMEM = + "
"; +const char HTTP_END[] PROGMEM = + "" + "" + ""; +#ifdef USE_WEMO_EMULATION +const char WEMO_EVENTSERVICE_XML[] PROGMEM = + "" + "" + "" + "SetBinaryState" + "" + "" + "" + "BinaryState" + "BinaryState" + "in" + "" + "" + "" + "" + "BinaryState" + "Boolean" + "0" + "" + "" + "level" + "string" + "0" + "" + "" + "" + "\r\n" + "\r\n"; +const char WEMO_SETUP_XML[] PROGMEM = + "" + "" + "" + "urn:Belkin:device:controllee:1" + "{x1}" + "Belkin International Inc." + "Sonoff Socket" + "3.1415" + "uuid:{x2}" + "{x3}" + "0" + "" + "" + "urn:Belkin:service:basicevent:1" + "urn:Belkin:serviceId:basicevent1" + "/upnp/control/basicevent1" + "/upnp/event/basicevent1" + "/eventservice.xml" + "" + "" + "" + "\r\n" + "\r\n"; +#endif // USE_WEMO_EMULATION +#ifdef USE_HUE_EMULATION +const char HUE_DESCRIPTION_XML[] PROGMEM = + "" + "" + "" + "1" + "0" + "" + "http://{x1}/" + "" + "urn:schemas-upnp-org:device:Basic:1" + "Amazon-Echo-HA-Bridge ({x1})" + "Royal Philips Electronics" + "Philips hue bridge 2012" + "929000226503" + "uuid:{x2}" + "" + "\r\n" + "\r\n"; + +const char HUE_LIGHT_STATUS_JSON[] PROGMEM = + "{\"state\":" + "{\"on\":{state}," + "\"bri\":{b}," + "\"hue\":{h}," + "\"sat\":{s}," + "\"effect\":\"none\"," + "\"ct\":0," + "\"alert\":\"none\"," + "\"reachable\":true" + "}," + "\"type\":\"Dimmable light\"," + "\"name\":\"{j1}\"," + "\"modelid\":\"LWB004\"," + "\"manufacturername\":\"Philips\"," + "\"uniqueid\":\"{j2}\"," + "\"swversion\":\"66012040\"" + "}"; + +const char HUE_LIGHT_RESPONSE_JSON[] PROGMEM = + "{\"success\":{\"{api}/{id}/{cmd}\":{res}}}"; +#endif // USE_HUE_EMULATION + +#define DNS_PORT 53 +enum http_t {HTTP_OFF, HTTP_USER, HTTP_ADMIN, HTTP_MANAGER}; + +DNSServer *dnsServer; +ESP8266WebServer *webServer; + +boolean _removeDuplicateAPs = true; +int _minimumQuality = -1, _httpflag = HTTP_OFF, _uploaderror = 0, _colcount; + +void startWebserver(int type, IPAddress ipweb) +{ + char log[LOGSZ]; + + if (!_httpflag) { + if (!webServer) { + webServer = new ESP8266WebServer(80); + webServer->on("/", handleRoot); + webServer->on("/cn", handleConfig); + webServer->on("/md", handleModule); + webServer->on("/w1", handleWifi1); + webServer->on("/w0", handleWifi0); + if (sysCfg.mqtt_enabled) { + webServer->on("/mq", handleMqtt); +#ifdef USE_DOMOTICZ + webServer->on("/dm", handleDomoticz); +#endif // USE_DOMOTICZ + } + webServer->on("/lg", handleLog); + webServer->on("/co", handleOther); + webServer->on("/sv", handleSave); + webServer->on("/rt", handleReset); + webServer->on("/up", handleUpgrade); + webServer->on("/u1", handleUpgradeStart); + webServer->on("/u2", HTTP_POST, handleUploadDone, handleUploadLoop); + webServer->on("/cm", handleCmnd); + webServer->on("/cs", handleConsole); + webServer->on("/ax", handleAjax); + webServer->on("/in", handleInfo); + webServer->on("/rb", handleRestart); + webServer->on("/fwlink", handleRoot); // Microsoft captive portal. Maybe not needed. Might be handled by notFound handler. +#ifdef USE_WEMO_EMULATION + webServer->on("/upnp/control/basicevent1", HTTP_POST, handleUPnPevent); + webServer->on("/eventservice.xml", handleUPnPservice); + webServer->on("/setup.xml", handleUPnPsetup); +#endif // USE_WEMO_EMULATION +#ifdef USE_HUE_EMULATION + webServer->on("/description.xml", handleUPnPsetup); +#endif // USE_HUE_EMULATION + webServer->onNotFound(handleNotFound); + } + webServer->begin(); // Web server start + } + if (_httpflag != type) { + snprintf_P(log, sizeof(log), PSTR("HTTP: Webserver active on %s%s with IP address %s"), + Hostname, (mDNSbegun)?".local":"", ipweb.toString().c_str()); + addLog(LOG_LEVEL_INFO, log); + } + if (type) _httpflag = type; +} + +void stopWebserver() +{ + if (_httpflag) { + webServer->close(); + _httpflag = HTTP_OFF; + addLog_P(LOG_LEVEL_INFO, PSTR("HTTP: Webserver stopped")); + } +} + +void beginWifiManager() +{ + // setup AP + if ((WiFi.status() == WL_CONNECTED) && (static_cast(WiFi.localIP()) != 0)) { + WiFi.mode(WIFI_AP_STA); + addLog_P(LOG_LEVEL_DEBUG, PSTR("Wifimanager: Set AccessPoint and keep Station")); + } else { + WiFi.mode(WIFI_AP); + addLog_P(LOG_LEVEL_DEBUG, PSTR("Wifimanager: Set AccessPoint")); + } + + stopWebserver(); + + dnsServer = new DNSServer(); + WiFi.softAP(Hostname); + delay(500); // Without delay I've seen the IP address blank + /* Setup the DNS server redirecting all the domains to the apIP */ + dnsServer->setErrorReplyCode(DNSReplyCode::NoError); + dnsServer->start(DNS_PORT, "*", WiFi.softAPIP()); + + startWebserver(HTTP_MANAGER, WiFi.softAPIP()); +} + +void pollDnsWeb() +{ + if (dnsServer) dnsServer->processNextRequest(); + if (webServer) webServer->handleClient(); +} + +void showPage(String &page) +{ + page.replace("{ha}", my_module.name); + page.replace("{h}", String(sysCfg.friendlyname[0])); +// page.replace("{ha}", Hostname); + if (_httpflag == HTTP_MANAGER) { + if (WIFI_configCounter()) { + page.replace("", ""); + page += FPSTR(HTTP_COUNTER); + } + } + page += FPSTR(HTTP_END); + + webServer->sendHeader("Cache-Control", "no-cache, no-store, must-revalidate"); + webServer->sendHeader("Pragma", "no-cache"); + webServer->sendHeader("Expires", "-1"); + webServer->send(200, "text/html", page); +} + +void handleRoot() +{ + addLog_P(LOG_LEVEL_DEBUG, PSTR("HTTP: Handle root")); + + if (captivePortal()) { // If captive portal redirect instead of displaying the page. + return; + } + + if (_httpflag == HTTP_MANAGER) { + handleWifi0(); + } else { + + String page = FPSTR(HTTP_HEAD); +// page.replace("", "setTimeout(function(){window.location.reload(1);},4000);"); // Repeats POST on All + page.replace("", "setTimeout(function(){window.location.replace(\"/\");},4000);"); // OK on All + page.replace("{v}", "Main menu"); + + if (Maxdevice) { + if (strlen(webServer->arg("o").c_str())) { + do_cmnd_power(atoi(webServer->arg("o").c_str()), 2); + } + + page += F(""); + for (byte idx = 1; idx <= Maxdevice; idx++) { + page += F(""); + } + page += F("
"); + page += (power & (0x01 << (idx -1))) ? "ON" : "OFF"; + page += F("


"); + } + + String tpage = ""; + if (hlw_flg) tpage += hlw_webPresent(); +#ifdef USE_DS18B20 + if (pin[GPIO_DSB] < 99) tpage += dsb_webPresent(); +#endif // USE_DS18B20 +#ifdef USE_DS18x20 + if (pin[GPIO_DSB] < 99) page += ds18x20_webPresent(); +#endif // USE_DS18x20 +#if defined(USE_DHT) || defined(USE_DHT2) + if (dht_type) tpage += dht_webPresent(); +#endif // USE_DHT/2 +#ifdef USE_I2C + if (i2c_flg) { + tpage += htu_webPresent(); + tpage += bmp_webPresent(); + tpage += bh1750_webPresent(); + } +#endif // USE_I2C + if (tpage.length() > 0) { + page += F(""); + page += tpage; + page += F("

"); + } + + if (_httpflag == HTTP_ADMIN) { + page += FPSTR(HTTP_BTN_MENU1); + page += FPSTR(HTTP_BTN_RSTRT); + } + showPage(page); + +#ifdef USE_DS18x20 + ds18x20_search(); // Check for changes in sensors number + ds18x20_convert(); // Start Conversion, takes up to one second +#endif // USE_DS18x20 + } +} + +void handleConfig() +{ + if (_httpflag == HTTP_USER) { + handleRoot(); + return; + } + addLog_P(LOG_LEVEL_DEBUG, PSTR("HTTP: Handle config")); + + String page = FPSTR(HTTP_HEAD); + page.replace("{v}", "Configuration"); + page += FPSTR(HTTP_BTN_MENU2); + if (sysCfg.mqtt_enabled) page += FPSTR(HTTP_BTN_MENU3); + page += FPSTR(HTTP_BTN_MENU4); + page += FPSTR(HTTP_BTN_MAIN); + showPage(page); +} + +void handleModule() +{ + if (_httpflag == HTTP_USER) { + handleRoot(); + return; + } + + char stemp[20]; + + addLog_P(LOG_LEVEL_DEBUG, PSTR("HTTP: Handle Module config")); + + String page = FPSTR(HTTP_HEAD); + page.replace("{v}", "Config module"); + page += FPSTR(HTTP_FORM_MODULE); + + snprintf_P(stemp, sizeof(stemp), modules[MODULE].name); + page.replace("{mt}", stemp); + + for (byte i = 0; i < MAXMODULE; i++) { + page += F(""); + } + page += F("
"); + + mytmplt cmodule; + memcpy_P(&cmodule, &modules[sysCfg.module], sizeof(cmodule)); + for (byte i = 0; i < MAX_GPIO_PIN; i++) { + if (cmodule.gp.io[i] == GPIO_USER) { + page += F("
GPIO"); page += String(i); page += F("
"); + } + } + + page += FPSTR(HTTP_FORM_END); + page += FPSTR(HTTP_BTN_CONF); + showPage(page); +} + +void handleWifi1() +{ + handleWifi(true); +} + +void handleWifi0() +{ + handleWifi(false); +} + +void handleWifi(boolean scan) +{ + if (_httpflag == HTTP_USER) { + handleRoot(); + return; + } + char log[LOGSZ]; + + addLog_P(LOG_LEVEL_DEBUG, PSTR("HTTP: Handle Wifi config")); + + String page = FPSTR(HTTP_HEAD); + page.replace("{v}", "Configure Wifi"); + + if (scan) { +#if defined(USE_WEMO_EMULATION) || defined(USE_HUE_EMULATION) + UDP_Disconnect(); +#endif // USE_WEMO_EMULATION || USE_HUE_EMULATION + int n = WiFi.scanNetworks(); + addLog_P(LOG_LEVEL_DEBUG, PSTR("Wifi: Scan done")); + + if (n == 0) { + addLog_P(LOG_LEVEL_DEBUG, PSTR("Wifi: No networks found")); + page += F("No networks found. Refresh to scan again."); + } else { + //sort networks + int indices[n]; + for (int i = 0; i < n; i++) { + indices[i] = i; + } + + // RSSI SORT + for (int i = 0; i < n; i++) { + for (int j = i + 1; j < n; j++) { + if (WiFi.RSSI(indices[j]) > WiFi.RSSI(indices[i])) { + std::swap(indices[i], indices[j]); + } + } + } + + // remove duplicates ( must be RSSI sorted ) + if (_removeDuplicateAPs) { + String cssid; + for (int i = 0; i < n; i++) { + if (indices[i] == -1) continue; + cssid = WiFi.SSID(indices[i]); + for (int j = i + 1; j < n; j++) { + if (cssid == WiFi.SSID(indices[j])) { + snprintf_P(log, sizeof(log), PSTR("Wifi: Duplicate AccessPoint %s"), WiFi.SSID(indices[j]).c_str()); + addLog(LOG_LEVEL_DEBUG, log); + indices[j] = -1; // set dup aps to index -1 + } + } + } + } + + //display networks in page + for (int i = 0; i < n; i++) { + if (indices[i] == -1) continue; // skip dups + snprintf_P(log, sizeof(log), PSTR("Wifi: SSID %s, RSSI %d"), WiFi.SSID(indices[i]).c_str(), WiFi.RSSI(indices[i])); + addLog(LOG_LEVEL_DEBUG, log); + int quality = WIFI_getRSSIasQuality(WiFi.RSSI(indices[i])); + + if (_minimumQuality == -1 || _minimumQuality < quality) { + String item = FPSTR(HTTP_LNK_ITEM); + String rssiQ; + rssiQ += quality; + item.replace("{v}", WiFi.SSID(indices[i])); + item.replace("{r}", rssiQ); + if (WiFi.encryptionType(indices[i]) != ENC_TYPE_NONE) { + item.replace("{i}", "l"); + } else { + item.replace("{i}", ""); + } + page += item; + delay(0); + } else { + addLog_P(LOG_LEVEL_DEBUG, PSTR("Wifi: Skipping due to low quality")); + } + + } + page += "
"; + } + } else { + page += FPSTR(HTTP_LNK_SCAN); + } + + page += FPSTR(HTTP_FORM_WIFI); + + char str[33]; + if (!strcmp(WIFI_HOSTNAME, DEF_WIFI_HOSTNAME)) { + snprintf_P(str, sizeof(str), PSTR(DEF_WIFI_HOSTNAME), sysCfg.mqtt_topic, ESP.getChipId() & 0x1FFF); + } else { + snprintf_P(str, sizeof(str), PSTR(WIFI_HOSTNAME)); + } + page.replace("{h0}", str); + page.replace("{h1}", String(sysCfg.hostname)); + page.replace("{s1}", String(sysCfg.sta_ssid[0])); + page.replace("{p1}", String(sysCfg.sta_pwd[0])); + page.replace("{s2}", String(sysCfg.sta_ssid[1])); + page.replace("{p2}", String(sysCfg.sta_pwd[1])); + page += FPSTR(HTTP_FORM_END); + if (_httpflag == HTTP_MANAGER) { + page += FPSTR(HTTP_BTN_RSTRT); + } else { + page += FPSTR(HTTP_BTN_CONF); + } + showPage(page); +} + +void handleMqtt() +{ + if (_httpflag == HTTP_USER) { + handleRoot(); + return; + } + addLog_P(LOG_LEVEL_DEBUG, PSTR("HTTP: Handle MQTT config")); + + String page = FPSTR(HTTP_HEAD); + page.replace("{v}", "Configure MQTT"); + page += FPSTR(HTTP_FORM_MQTT); + char str[sizeof(sysCfg.mqtt_client)]; + getClient(str, MQTT_CLIENT_ID, sizeof(sysCfg.mqtt_client)); + page.replace("{m0}", str); + page.replace("{m1}", String(sysCfg.mqtt_host)); + page.replace("{m2}", String(sysCfg.mqtt_port)); + page.replace("{m3}", String(sysCfg.mqtt_client)); + page.replace("{m4}", String(sysCfg.mqtt_user)); + page.replace("{m5}", String(sysCfg.mqtt_pwd)); + page.replace("{m6}", String(sysCfg.mqtt_topic)); + page += FPSTR(HTTP_FORM_END); + page += FPSTR(HTTP_BTN_CONF); + showPage(page); +} + +void handleLog() +{ + if (_httpflag == HTTP_USER) { + handleRoot(); + return; + } + addLog_P(LOG_LEVEL_DEBUG, PSTR("HTTP: Handle Log config")); + + String page = FPSTR(HTTP_HEAD); + page.replace("{v}", "Config logging"); + page += FPSTR(HTTP_FORM_LOG1); + for (byte idx = 0; idx < 3; idx++) { + page += FPSTR(HTTP_FORM_LOG2); + switch (idx) { + case 0: + page.replace("{b0}", "Serial log"); + page.replace("{b1}", STR(SERIAL_LOG_LEVEL)); + page.replace("{b2}", "ls"); + for (byte i = LOG_LEVEL_NONE; i < LOG_LEVEL_ALL; i++) { + page.replace("{a" + String(i), (i == sysCfg.seriallog_level) ? " selected " : " "); + } + break; + case 1: + page.replace("{b0}", "Web log"); + page.replace("{b1}", STR(WEB_LOG_LEVEL)); + page.replace("{b2}", "lw"); + for (byte i = LOG_LEVEL_NONE; i < LOG_LEVEL_ALL; i++) { + page.replace("{a" + String(i), (i == sysCfg.weblog_level) ? " selected " : " "); + } + break; + case 2: + page.replace("{b0}", "Syslog"); + page.replace("{b1}", STR(SYS_LOG_LEVEL)); + page.replace("{b2}", "ll"); + for (byte i = LOG_LEVEL_NONE; i < LOG_LEVEL_ALL; i++) { + page.replace("{a" + String(i), (i == sysCfg.syslog_level) ? " selected " : " "); + } + break; + } + } + page += FPSTR(HTTP_FORM_LOG3); + page.replace("{l2}", String(sysCfg.syslog_host)); + page.replace("{l3}", String(sysCfg.syslog_port)); + page.replace("{l4}", String(sysCfg.tele_period)); + page += FPSTR(HTTP_FORM_END); + page += FPSTR(HTTP_BTN_CONF); + showPage(page); +} + +void handleOther() +{ + if (_httpflag == HTTP_USER) { + handleRoot(); + return; + } + addLog_P(LOG_LEVEL_DEBUG, PSTR("HTTP: Handle other config")); + + String page = FPSTR(HTTP_HEAD); + page.replace("{v}", "Configure Other"); + page += FPSTR(HTTP_FORM_OTHER); + page.replace("{r1}", (sysCfg.mqtt_enabled) ? " checked" : ""); + for (int i = 0; i < Maxdevice; i++) { + page += F("
Friendly Name {1 ({2)

"); + page.replace("{1", String(i +1)); + if (i == 0) page.replace("{2", FRIENDLY_NAME1); + else if (i == 1) page.replace("{2", FRIENDLY_NAME2); + else if (i == 2) page.replace("{2", FRIENDLY_NAME3); + else if (i == 3) page.replace("{2", FRIENDLY_NAME4); + page.replace("{3", String(sysCfg.friendlyname[i])); + } + page += FPSTR(HTTP_FORM_END); + page += FPSTR(HTTP_BTN_CONF); + showPage(page); +} + +void handleSave() +{ + if (_httpflag == HTTP_USER) { + handleRoot(); + return; + } + char log[LOGSZ], stemp[20]; + byte what = 0, restart; + String result = ""; + + addLog_P(LOG_LEVEL_DEBUG, PSTR("HTTP: Parameter save")); + + if (strlen(webServer->arg("w").c_str())) what = atoi(webServer->arg("w").c_str()); + switch (what) { + case 1: + strlcpy(sysCfg.hostname, (!strlen(webServer->arg("h").c_str())) ? WIFI_HOSTNAME : webServer->arg("h").c_str(), sizeof(sysCfg.hostname)); + if (strstr(sysCfg.hostname,"%")) strlcpy(sysCfg.hostname, DEF_WIFI_HOSTNAME, sizeof(sysCfg.hostname)); + strlcpy(sysCfg.sta_ssid[0], (!strlen(webServer->arg("s1").c_str())) ? STA_SSID1 : webServer->arg("s1").c_str(), sizeof(sysCfg.sta_ssid[0])); + strlcpy(sysCfg.sta_pwd[0], (!strlen(webServer->arg("p1").c_str())) ? STA_PASS1 : webServer->arg("p1").c_str(), sizeof(sysCfg.sta_pwd[0])); + strlcpy(sysCfg.sta_ssid[1], (!strlen(webServer->arg("s2").c_str())) ? STA_SSID2 : webServer->arg("s2").c_str(), sizeof(sysCfg.sta_ssid[1])); + strlcpy(sysCfg.sta_pwd[1], (!strlen(webServer->arg("p2").c_str())) ? STA_PASS2 : webServer->arg("p2").c_str(), sizeof(sysCfg.sta_pwd[1])); + snprintf_P(log, sizeof(log), PSTR("HTTP: Wifi Hostname %s, SSID1 %s, Password1 %s, SSID2 %s, Password2 %s"), + sysCfg.hostname, sysCfg.sta_ssid[0], sysCfg.sta_pwd[0], sysCfg.sta_ssid[1], sysCfg.sta_pwd[1]); + addLog(LOG_LEVEL_INFO, log); + result += F("
Trying to connect device to network
If it fails reconnect to try again"); + break; + case 2: + strlcpy(sysCfg.mqtt_host, (!strlen(webServer->arg("mh").c_str())) ? MQTT_HOST : webServer->arg("mh").c_str(), sizeof(sysCfg.mqtt_host)); + sysCfg.mqtt_port = (!strlen(webServer->arg("ml").c_str())) ? MQTT_PORT : atoi(webServer->arg("ml").c_str()); + strlcpy(sysCfg.mqtt_client, (!strlen(webServer->arg("mc").c_str())) ? MQTT_CLIENT_ID : webServer->arg("mc").c_str(), sizeof(sysCfg.mqtt_client)); + strlcpy(sysCfg.mqtt_user, (!strlen(webServer->arg("mu").c_str())) ? MQTT_USER : webServer->arg("mu").c_str(), sizeof(sysCfg.mqtt_user)); + strlcpy(sysCfg.mqtt_pwd, (!strlen(webServer->arg("mp").c_str())) ? MQTT_PASS : webServer->arg("mp").c_str(), sizeof(sysCfg.mqtt_pwd)); + strlcpy(sysCfg.mqtt_topic, (!strlen(webServer->arg("mt").c_str())) ? MQTT_TOPIC : webServer->arg("mt").c_str(), sizeof(sysCfg.mqtt_topic)); + snprintf_P(log, sizeof(log), PSTR("HTTP: MQTT Host %s, Port %d, Client %s, User %s, Password %s, Topic %s"), + sysCfg.mqtt_host, sysCfg.mqtt_port, sysCfg.mqtt_client, sysCfg.mqtt_user, sysCfg.mqtt_pwd, sysCfg.mqtt_topic); + addLog(LOG_LEVEL_INFO, log); + break; + case 3: + sysCfg.seriallog_level = (!strlen(webServer->arg("ls").c_str())) ? SERIAL_LOG_LEVEL : atoi(webServer->arg("ls").c_str()); + sysCfg.weblog_level = (!strlen(webServer->arg("lw").c_str())) ? WEB_LOG_LEVEL : atoi(webServer->arg("lw").c_str()); + sysCfg.syslog_level = (!strlen(webServer->arg("ll").c_str())) ? SYS_LOG_LEVEL : atoi(webServer->arg("ll").c_str()); + syslog_level = sysCfg.syslog_level; + syslog_timer = 0; + strlcpy(sysCfg.syslog_host, (!strlen(webServer->arg("lh").c_str())) ? SYS_LOG_HOST : webServer->arg("lh").c_str(), sizeof(sysCfg.syslog_host)); + sysCfg.syslog_port = (!strlen(webServer->arg("lp").c_str())) ? SYS_LOG_PORT : atoi(webServer->arg("lp").c_str()); + sysCfg.tele_period = (!strlen(webServer->arg("lt").c_str())) ? TELE_PERIOD : atoi(webServer->arg("lt").c_str()); + snprintf_P(log, sizeof(log), PSTR("HTTP: Logging Seriallog %d, Weblog %d, Syslog %d, Host %s, Port %d, TelePeriod %d"), + sysCfg.seriallog_level, sysCfg.weblog_level, sysCfg.syslog_level, sysCfg.syslog_host, sysCfg.syslog_port, sysCfg.tele_period); + addLog(LOG_LEVEL_INFO, log); + break; +#ifdef USE_DOMOTICZ + case 4: + domoticz_saveSettings(); + break; +#endif // USE_DOMOTICZ + case 5: + sysCfg.mqtt_enabled = webServer->hasArg("r1"); + strlcpy(sysCfg.friendlyname[0], (!strlen(webServer->arg("a1").c_str())) ? FRIENDLY_NAME1 : webServer->arg("a1").c_str(), sizeof(sysCfg.friendlyname[0])); + strlcpy(sysCfg.friendlyname[1], (!strlen(webServer->arg("a2").c_str())) ? FRIENDLY_NAME2 : webServer->arg("a2").c_str(), sizeof(sysCfg.friendlyname[1])); + strlcpy(sysCfg.friendlyname[2], (!strlen(webServer->arg("a3").c_str())) ? FRIENDLY_NAME3 : webServer->arg("a3").c_str(), sizeof(sysCfg.friendlyname[2])); + strlcpy(sysCfg.friendlyname[3], (!strlen(webServer->arg("a4").c_str())) ? FRIENDLY_NAME4 : webServer->arg("a4").c_str(), sizeof(sysCfg.friendlyname[3])); + snprintf_P(log, sizeof(log), PSTR("HTTP: Other MQTT Enable %s, Friendly Names %s, %s, %s and %s"), + (sysCfg.mqtt_enabled) ? MQTT_STATUS_ON : MQTT_STATUS_OFF, sysCfg.friendlyname[0], sysCfg.friendlyname[1], sysCfg.friendlyname[2], sysCfg.friendlyname[3]); + addLog(LOG_LEVEL_INFO, log); + break; + case 6: + sysCfg.module = (!strlen(webServer->arg("mt").c_str())) ? MODULE : atoi(webServer->arg("mt").c_str()); + mytmplt cmodule; + memcpy_P(&cmodule, &modules[sysCfg.module], sizeof(cmodule)); + String gpios = ""; + for (byte i = 0; i < MAX_GPIO_PIN; i++) { + if (cmodule.gp.io[i] == GPIO_USER) { + snprintf_P(stemp, sizeof(stemp), PSTR("g%d"), i); + sysCfg.my_module.gp.io[i] = (!strlen(webServer->arg(stemp).c_str())) ? 0 : atoi(webServer->arg(stemp).c_str()); + gpios += F(", GPIO"); gpios += String(i); gpios += F(" "); gpios += String(sysCfg.my_module.gp.io[i]); + } + } + snprintf_P(stemp, sizeof(stemp), modules[sysCfg.module].name); + snprintf_P(log, sizeof(log), PSTR("HTTP: %s Module%s"), stemp, gpios.c_str()); + addLog(LOG_LEVEL_INFO, log); + break; + } + + restart = (!strlen(webServer->arg("r").c_str())) ? 1 : atoi(webServer->arg("r").c_str()); + if (restart) { + String page = FPSTR(HTTP_HEAD); + page.replace("{v}", "Save parameters"); + page += F("
Parameters saved
"); + page += result; + page += F("
"); + page += FPSTR(HTTP_MSG_RSTRT); + if (_httpflag == HTTP_MANAGER) { + _httpflag = HTTP_ADMIN; + } else { + page += FPSTR(HTTP_BTN_MAIN); + } + showPage(page); + + restartflag = 2; + } else { + handleConfig(); + } +} + +void handleReset() +{ + if (_httpflag == HTTP_USER) { + handleRoot(); + return; + } + char svalue[MESSZ]; + + addLog_P(LOG_LEVEL_DEBUG, PSTR("HTTP: Reset parameters")); + + String page = FPSTR(HTTP_HEAD); + page.replace("{v}", "Default parameters"); + page += F("
Parameters reset to default
"); + page += FPSTR(HTTP_MSG_RSTRT); + page += FPSTR(HTTP_BTN_MAIN); + showPage(page); + + snprintf_P(svalue, sizeof(svalue), PSTR("reset 1")); + do_cmnd(svalue); +} + +void handleUpgrade() +{ + if (_httpflag == HTTP_USER) { + handleRoot(); + return; + } + addLog_P(LOG_LEVEL_DEBUG, PSTR("HTTP: Handle upgrade")); + + String page = FPSTR(HTTP_HEAD); + page.replace("{v}", "Firmware upgrade"); + page += FPSTR(HTTP_FORM_UPG); + page.replace("{o1}", String(sysCfg.otaUrl)); + page += FPSTR(HTTP_BTN_MAIN); + showPage(page); + + _uploaderror = 0; +} + +void handleUpgradeStart() +{ + if (_httpflag == HTTP_USER) { + handleRoot(); + return; + } + char svalue[MESSZ]; + + addLog_P(LOG_LEVEL_DEBUG, PSTR("HTTP: Firmware upgrade start")); + WIFI_configCounter(); + + if (strlen(webServer->arg("o").c_str())) { + snprintf_P(svalue, sizeof(svalue), PSTR("otaurl %s"), webServer->arg("o").c_str()); + do_cmnd(svalue); + } + + String page = FPSTR(HTTP_HEAD); + page.replace("{v}", "Info"); + page += F("
Upgrade started ...
"); + page += FPSTR(HTTP_MSG_RSTRT); + page += FPSTR(HTTP_BTN_MAIN); + showPage(page); + + snprintf_P(svalue, sizeof(svalue), PSTR("upgrade 1")); + do_cmnd(svalue); +} + +void handleUploadDone() +{ + if (_httpflag == HTTP_USER) { + handleRoot(); + return; + } + + addLog_P(LOG_LEVEL_DEBUG, PSTR("HTTP: Firmware upload done")); + WIFI_configCounter(); + restartflag = 0; + mqttcounter = 0; + + String page = FPSTR(HTTP_HEAD); + page.replace("{v}", "Info"); + page += F("
Upload "); + if (_uploaderror) { + page += F("failed"); + if (_uploaderror == 1) { + page += F("

No file selected"); + } else if (_uploaderror == 2) { + page += F("

File size is larger than available free space"); + } else if (_uploaderror == 3) { + page += F("

File magic header does not start with 0xE9"); + } else if (_uploaderror == 4) { + page += F("

File flash size is larger than device flash size"); + } else if (_uploaderror == 5) { + page += F("

File upload buffer miscompare"); + } else if (_uploaderror == 6) { + page += F("

Upload failed. Enable logging option 3 for more information"); + } else if (_uploaderror == 7) { + page += F("

Upload aborted"); + } else { + page += F("

Upload error code "); + page += String(_uploaderror); + } + if (Update.hasError()) { + page += F("

Update error code (see Updater.cpp) "); + page += String(Update.getError()); + } + } else { + page += F("successful

Device will restart in a few seconds"); + restartflag = 2; + } + page += F("

"); + page += FPSTR(HTTP_BTN_MAIN); + showPage(page); +} + +void handleUploadLoop() +{ + // Based on ESP8266HTTPUpdateServer.cpp uses ESP8266WebServer Parsing.cpp and Cores Updater.cpp (Update) + char log[LOGSZ]; + boolean _serialoutput = (LOG_LEVEL_DEBUG <= seriallog_level); + + if (_httpflag == HTTP_USER) return; + if (_uploaderror) { + Update.end(); + return; + } + + HTTPUpload& upload = webServer->upload(); + + if (upload.status == UPLOAD_FILE_START) { + restartflag = 60; + mqttcounter = 60; + if (upload.filename.c_str()[0] == 0) + { + _uploaderror = 1; + return; + } +#if defined(USE_WEMO_EMULATION) || defined(USE_HUE_EMULATION) + UDP_Disconnect(); +#endif // USE_WEMO_EMULATION || USE_HUE_EMULATION + if (sysCfg.mqtt_enabled) mqttClient.disconnect(); + + snprintf_P(log, sizeof(log), PSTR("Upload: File %s ..."), upload.filename.c_str()); + addLog(LOG_LEVEL_INFO, log); + + uint32_t maxSketchSpace = (ESP.getFreeSketchSpace() - 0x1000) & 0xFFFFF000; + if (!Update.begin(maxSketchSpace)) { //start with max available size + if (_serialoutput) Update.printError(Serial); + _uploaderror = 2; + return; + } + _colcount = 0; + } else if (!_uploaderror && (upload.status == UPLOAD_FILE_WRITE)) { + if (upload.totalSize == 0) + { + if (upload.buf[0] != 0xE9) { + addLog_P(LOG_LEVEL_DEBUG, PSTR("Upload: File magic header does not start with 0xE9")); + _uploaderror = 3; + return; + } + uint32_t bin_flash_size = ESP.magicFlashChipSize((upload.buf[3] & 0xf0) >> 4); + if(bin_flash_size > ESP.getFlashChipRealSize()) { + addLog_P(LOG_LEVEL_DEBUG, PSTR("Upload: File flash size is larger than device flash size")); + _uploaderror = 4; + return; + } + if ((sysCfg.module == SONOFF_TOUCH) || (sysCfg.module == SONOFF_4CH) || (ESP.getFlashChipMode() == 3)) { + upload.buf[2] = 3; // DOUT - ESP8285 + } else { + upload.buf[2] = 2; // DIO - ESP8266 + } +// snprintf_P(log, sizeof(log), PSTR("Upload: Flash Chip Mode %02X"), upload.buf[2]); +// addLog(LOG_LEVEL_DEBUG, log); + } + if (!_uploaderror && (Update.write(upload.buf, upload.currentSize) != upload.currentSize)) { + if (_serialoutput) Update.printError(Serial); + _uploaderror = 5; + return; + } + if (_serialoutput) { + Serial.printf("."); + _colcount++; + if (!(_colcount % 80)) Serial.println(); + } + } else if(!_uploaderror && (upload.status == UPLOAD_FILE_END)){ + if (_serialoutput && (_colcount % 80)) Serial.println(); + if (Update.end(true)) { // true to set the size to the current progress + snprintf_P(log, sizeof(log), PSTR("Upload: Successful %u bytes. Restarting"), upload.totalSize); + addLog(LOG_LEVEL_INFO, log); + } else { + if (_serialoutput) Update.printError(Serial); + _uploaderror = 6; + return; + } + } else if(upload.status == UPLOAD_FILE_ABORTED) { + addLog_P(LOG_LEVEL_DEBUG, PSTR("Upload: Update was aborted")); + restartflag = 0; + mqttcounter = 0; + _uploaderror = 7; + Update.end(); + } + delay(0); +} + +void handleCmnd() +{ + if (_httpflag == HTTP_USER) { + handleRoot(); + return; + } + char svalue[MESSZ]; + + addLog_P(LOG_LEVEL_DEBUG, PSTR("HTTP: Handle cmnd")); + + byte curridx = logidx; + if (strlen(webServer->arg(0).c_str())) { + snprintf_P(svalue, sizeof(svalue), webServer->arg(0).c_str()); + do_cmnd(svalue); + } + + String message = ""; + if (logidx != curridx) { + byte counter = curridx; + do { + if (Log[counter].length()) { + if (message.length()) message += F("\n"); + if (sysCfg.mqtt_enabled) { + // [14:49:36 MQTT: stat/wemos5/RESULT = {"POWER":"OFF"}] > [RESULT = {"POWER":"OFF"}] +// message += Log[counter].substring(17 + strlen(PUB_PREFIX) + strlen(sysCfg.mqtt_topic)); + message += Log[counter].substring(Log[counter].lastIndexOf("/",Log[counter].indexOf("="))+1); + } else { + // [14:49:36 RSLT: RESULT = {"POWER":"OFF"}] > [RESULT = {"POWER":"OFF"}] + message += Log[counter].substring(Log[counter].indexOf(": ")+2); + } + } + counter++; + if (counter > MAX_LOG_LINES -1) counter = 0; + } while (counter != logidx); + } else { + message = F("Enable weblog 2 if response expected\n"); + } + + webServer->sendHeader("Cache-Control", "no-cache, no-store, must-revalidate"); + webServer->sendHeader("Pragma", "no-cache"); + webServer->sendHeader("Expires", "-1"); + webServer->send(200, "text/plain", message); +} + +void handleConsole() +{ + if (_httpflag == HTTP_USER) { + handleRoot(); + return; + } + char svalue[MESSZ]; + + addLog_P(LOG_LEVEL_DEBUG, PSTR("HTTP: Handle console")); + + if (strlen(webServer->arg("c1").c_str())) { + snprintf_P(svalue, sizeof(svalue), webServer->arg("c1").c_str()); + do_cmnd(svalue); + } + + String page = FPSTR(HTTP_HEAD); + page.replace("{v}", "Console"); + page.replace("", ""); + page += FPSTR(HTTP_FORM_CMND); + page += FPSTR(HTTP_BTN_MAIN); + showPage(page); +} + +void handleAjax() +{ + if (_httpflag == HTTP_USER) { + handleRoot(); + return; + } + String message = ""; + uint16_t size = 0; + + int maxSize = ESP.getFreeHeap() - 6000; + + byte counter = logidx; + do { + counter--; + if (counter == 255) counter = MAX_LOG_LINES -1; + size += Log[counter].length(); + } while ((counter != logidx) && (size < maxSize)); + do { + if (Log[counter].length()) { + if (message.length()) message += F("\n"); + message += Log[counter]; + } + counter++; + if (counter > MAX_LOG_LINES -1) counter = 0; + } while (counter != logidx); + webServer->sendHeader("Cache-Control", "no-cache, no-store, must-revalidate"); + webServer->sendHeader("Pragma", "no-cache"); + webServer->sendHeader("Expires", "-1"); + webServer->send(200, "text/plain", message); +} + +void handleInfo() +{ + if (_httpflag == HTTP_USER) { + handleRoot(); + return; + } + addLog_P(LOG_LEVEL_DEBUG, PSTR("HTTP: Handle info")); + + int freeMem = ESP.getFreeHeap(); + + String page = FPSTR(HTTP_HEAD); + page.replace("{v}", "Information"); +// page += F("
 Information "); + page += F(""); + page += F(""); + page += F(""); + page += F(""); +// page += F(""); + page += F(""); + page += F(""); + page += F(""); + page += F(""); + for (byte i = 0; i < Maxdevice; i++) { + page += F(""); + } + page += F(""); +// page += F(""); + page += F(""); + page += F(""); + if (static_cast(WiFi.localIP()) != 0) { + page += F(""); + page += F(""); + page += F(""); + } + if (static_cast(WiFi.softAPIP()) != 0) { + page += F(""); + page += F(""); + page += F(""); + } + page += F(""); + if (sysCfg.mqtt_enabled) { + page += F(""); + page += F(""); + page += F(""); + page += F(""); +// page += F(""); + page += F(""); + page += F(""); + } else { + page += F(""); + } + page += F(""); + page += F(""); + page += F(""); + page += F(""); + page += F(""); + page += F(""); + page += F(""); + page += F(""); + page += F("
Program version"); page += Version; page += F("
Core/SDK version"); page += ESP.getCoreVersion(); page += F("/"); page += String(ESP.getSdkVersion()); page += F("
Boot version"); page += String(ESP.getBootVersion()); page += F("
Uptime"); page += String(uptime); page += F(" Hours
Flash write count"); page += String(sysCfg.saveFlag); page += F("
Boot count"); page += String(sysCfg.bootcount); page += F("
Reset reason"); page += ESP.getResetReason(); page += F("
Friendly name "); + page += i +1; + page += F(""); page += String(sysCfg.friendlyname[i]); page += F("
 
SSId (RSSI)"); page += (sysCfg.sta_active)? sysCfg.sta_ssid2 : sysCfg.sta_ssid1; page += F(" ("); page += WIFI_getRSSIasQuality(WiFi.RSSI()); page += F("%)
AP"); page += String(sysCfg.sta_active +1); page += F(" SSId (RSSI)"); page += sysCfg.sta_ssid[sysCfg.sta_active]; page += F(" ("); page += WIFI_getRSSIasQuality(WiFi.RSSI()); page += F("%)
Hostname"); page += Hostname; page += F("
IP address"); page += WiFi.localIP().toString(); page += F("
Gateway"); page += WiFi.gatewayIP().toString(); page += F("
MAC address"); page += WiFi.macAddress(); page += F("
AP IP address"); page += WiFi.softAPIP().toString(); page += F("
AP Gateway"); page += WiFi.softAPIP().toString(); page += F("
AP MAC address"); page += WiFi.softAPmacAddress(); page += F("
 
MQTT Host"); page += sysCfg.mqtt_host; page += F("
MQTT Port"); page += String(sysCfg.mqtt_port); page += F("
MQTT Client and
 Fallback Topic
"); page += MQTTClient; page += F("
MQTT User"); page += sysCfg.mqtt_user; page += F("
MQTT Password"); page += sysCfg.mqtt_pwd; page += F("
MQTT Topic"); page += sysCfg.mqtt_topic; page += F("
MQTT Group Topic"); page += sysCfg.mqtt_grptopic; page += F("
MQTTDisabled
 
ESP Chip id"); page += String(ESP.getChipId()); page += F("
Flash Chip id"); page += String(ESP.getFlashChipId()); page += F("
Flash size"); page += String(ESP.getFlashChipRealSize() / 1024); page += F("kB
Program flash size"); page += String(ESP.getFlashChipSize() / 1024); page += F("kB
Program size"); page += String(ESP.getSketchSize() / 1024); page += F("kB
Free program space"); page += String(ESP.getFreeSketchSpace() / 1024); page += F("kB
Free memory"); page += String(freeMem / 1024); page += F("kB
"); +// page += F("
"); + page += FPSTR(HTTP_BTN_MAIN); + showPage(page); +} + +void handleRestart() +{ + if (_httpflag == HTTP_USER) { + handleRoot(); + return; + } + addLog_P(LOG_LEVEL_DEBUG, PSTR("HTTP: Restarting")); + + String page = FPSTR(HTTP_HEAD); + page.replace("{v}", "Info"); + page += FPSTR(HTTP_MSG_RSTRT); + if (_httpflag == HTTP_MANAGER) { + _httpflag = HTTP_ADMIN; + } else { + page += FPSTR(HTTP_BTN_MAIN); + } + showPage(page); + + restartflag = 2; +} + +/********************************************************************************************/ + +#ifdef USE_WEMO_EMULATION +void handleUPnPevent() +{ + addLog_P(LOG_LEVEL_DEBUG, PSTR("HTTP: Handle WeMo basic event")); + + String request = webServer->arg(0); + if(request.indexOf("State>1 0) do_cmnd_power(1, 1); + if(request.indexOf("State>0 0) do_cmnd_power(1, 0); + webServer->send(200, "text/plain", ""); +} + +void handleUPnPservice() +{ + addLog_P(LOG_LEVEL_DEBUG, PSTR("HTTP: Handle WeMo event service")); + + String eventservice_xml = FPSTR(WEMO_EVENTSERVICE_XML); + webServer->send(200, "text/plain", eventservice_xml); +} + +void handleUPnPsetup() +{ + addLog_P(LOG_LEVEL_DEBUG, PSTR("HTTP: Handle WeMo setup")); + + String setup_xml = FPSTR(WEMO_SETUP_XML); +// setup_xml.replace("{x1}", String(MQTTClient)); + setup_xml.replace("{x1}", String(sysCfg.friendlyname[0])); + setup_xml.replace("{x2}", wemo_UUID()); + setup_xml.replace("{x3}", wemo_serial()); + webServer->send(200, "text/xml", setup_xml); +} +#endif // USE_WEMO_EMULATION + +/********************************************************************************************/ + +#ifdef USE_HUE_EMULATION +String hue_deviceId(uint8_t id) +{ + char deviceid[16]; + + snprintf_P(deviceid, sizeof(deviceid), PSTR("5CCF7F%03X-%0d"), ESP.getChipId(), id); + return String(deviceid); +} + +void handleUPnPsetup() +{ + addLog_P(LOG_LEVEL_DEBUG, PSTR("HTTP: Handle Hue Bridge setup")); + + String description_xml = FPSTR(HUE_DESCRIPTION_XML); + description_xml.replace("{x1}", WiFi.localIP().toString()); + description_xml.replace("{x2}", hue_UUID()); + webServer->send(200, "text/xml", description_xml); +} + +void hue_todo(String *path) +{ + char log[LOGSZ]; + + snprintf_P(log, sizeof(log), PSTR("HTTP: HUE API not implemented (%s)"),path->c_str()); + addLog(LOG_LEVEL_DEBUG_MORE, log); +} + +void hue_lights(String *path) +{ + String response; + uint8_t device = 1; + int16_t pos = 0; + uint8_t bri = 0; + char id[4]; + + path->remove(0,path->indexOf("/lights")); // Remove until /lights + if (path->endsWith("/lights")) // Got /lights + { + response = "{\""; + for (uint8_t i = 1; i <= Maxdevice; i++) + { + response += i; + response += "\":"; + response += FPSTR(HUE_LIGHT_STATUS_JSON); + if (i < Maxdevice) response += ",\""; + response.replace("{state}", (power & (0x01 << (i-1))) ? "true" : "false"); + response.replace("{j1}", sysCfg.friendlyname[i-1]); + response.replace("{j2}", hue_deviceId(i)); +#ifdef USE_WS2812 + ws2812_replaceHSB(&response); +#else + response.replace("{h}", "0"); + response.replace("{s}", "0"); + response.replace("{b}", "0"); +#endif // USE_WS2812 + } + response += "}"; + webServer->send(200, "application/json", response); + } + else if (path->endsWith("/state")) // Got ID/state + { + path->remove(0,8); // Remove /lights/ + path->remove(path->indexOf("/state")); // Remove /state + device = atoi(path->c_str()); + if ((device < 1) || (device > Maxdevice)) device = 1; + response = "["; + response += FPSTR(HUE_LIGHT_RESPONSE_JSON); + response.replace("{api}", "/lights"); + response.replace("{id}", String(device)); + response.replace("{cmd}", "state/on"); + if (webServer->args() == 1) + { + String json = webServer->arg(0); +// Serial.print("HUE API: POST "); Serial.println(json.c_str()); + if (json.indexOf("\"on\":") >= 0) // Got "on" command + { + if (json.indexOf("false") >= 0) // false -> turn device off + { + do_cmnd_power(device, 0); + response.replace("{res}", "false"); + } + else if(json.indexOf("true") >= 0) // true -> Turn device on + { + do_cmnd_power(device, 1); + response.replace("{res}", "true"); + } + else + { + response.replace("{res}", (power & (0x01 << (device-1))) ? "true" : "false"); + } + } +#ifdef USE_WS2812 + if ((pos=json.indexOf("\"bri\":")) >= 0) + { + bri=atoi(json.substring(pos+6).c_str()); + ws2812_changeBrightness(bri); + response += ","; + response += FPSTR(HUE_LIGHT_RESPONSE_JSON); + response.replace("{api}", "/lights"); + response.replace("{id}", String(device)); + response.replace("{cmd}", "state/bri"); + response.replace("{res}", String(bri)); + } +#endif // USE_WS2812 + response += "]"; + webServer->send(200, "application/json", response); + } + else webServer->send(406, "application/json", "{}"); + } + else if(path->indexOf("/lights/") >= 0) // Got /lights/ID + { + path->remove(0,8); // Remove /lights/ + device = atoi(path->c_str()); + if ((device < 1) || (device > Maxdevice)) device = 1; + response = FPSTR(HUE_LIGHT_STATUS_JSON); + response.replace("{state}", (power & (0x01 << (device -1))) ? "true" : "false"); + response.replace("{j1}", sysCfg.friendlyname[device -1]); + response.replace("{j2}", hue_deviceId(device)); +#ifdef USE_WS2812 + ws2812_replaceHSB(&response); +#else + response.replace("{h}", "0"); + response.replace("{s}", "0"); + response.replace("{b}", "0"); +#endif // USE_WS2812 + webServer->send(200, "application/json", response); + } + else webServer->send(406, "application/json", "{}"); +} + +void handle_hue_api(String *path) +{ + /* HUE API uses /api// syntax. The userid is created by the echo device and + * on original HUE the pressed button allows for creation of this user. We simply ignore the + * user part and allow every caller as with Web or WeMo. + * + * (c) Heiko Krupp, 2017 + */ + + char log[LOGSZ]; + + path->remove(0, 4); // remove /api + if (path->endsWith("/invalid/")) {} // Just ignore + else if (path->endsWith("/config")) hue_todo(path); + else if(path->indexOf("/lights") >= 0) hue_lights(path); + else if(path->endsWith("/groups")) hue_todo(path); + else if(path->endsWith("/schedules")) hue_todo(path); + else if(path->endsWith("/sensors")) hue_todo(path); + else if(path->endsWith("/scenes")) hue_todo(path); + else if(path->endsWith("/rules")) hue_todo(path); + else + { + snprintf_P(log, sizeof(log), PSTR("HTTP: Handle Hue API (%s)"),path->c_str()); + addLog(LOG_LEVEL_DEBUG_MORE, log); + webServer->send(406, "application/json", "{}"); + } +} +#endif // USE_HUE_EMULATION + +/********************************************************************************************/ + +void handleNotFound() +{ + if (captivePortal()) { // If captive portal redirect instead of displaying the error page. + return; + } + +#ifdef USE_HUE_EMULATION + String path = webServer->uri(); + if (path.startsWith("/api")) + handle_hue_api(&path); + else { +#endif // USE_HUE_EMULATION + + String message = "File Not Found\n\n"; + message += "URI: "; + message += webServer->uri(); + message += "\nMethod: "; + message += ( webServer->method() == HTTP_GET ) ? "GET" : "POST"; + message += "\nArguments: "; + message += webServer->args(); + message += "\n"; + for ( uint8_t i = 0; i < webServer->args(); i++ ) { + message += " " + webServer->argName ( i ) + ": " + webServer->arg ( i ) + "\n"; + } + + webServer->sendHeader("Cache-Control", "no-cache, no-store, must-revalidate"); + webServer->sendHeader("Pragma", "no-cache"); + webServer->sendHeader("Expires", "-1"); + webServer->send(404, "text/plain", message); +#ifdef USE_HUE_EMULATION + addLog_P(LOG_LEVEL_DEBUG_MORE, message.c_str()); + } +#endif // USE_HUE_EMULATION +} + +/* Redirect to captive portal if we got a request for another domain. Return true in that case so the page handler do not try to handle the request again. */ +boolean captivePortal() +{ + if (!isIp(webServer->hostHeader())) { + addLog_P(LOG_LEVEL_DEBUG, PSTR("HTTP: Request redirected to captive portal")); + + webServer->sendHeader("Location", String("http://") + webServer->client().localIP().toString(), true); + webServer->send(302, "text/plain", ""); // Empty content inhibits Content-length header so we have to close the socket ourselves. + webServer->client().stop(); // Stop is needed because we sent no content length + return true; + } + return false; +} + +/** Is this an IP? */ +boolean isIp(String str) +{ + for (uint16_t i = 0; i < str.length(); i++) { + int c = str.charAt(i); + if (c != '.' && (c < '0' || c > '9')) { + return false; + } + } + return true; +} + +#endif // USE_WEBSERVER diff --git a/sonoff/xdrv_domoticz.ino b/sonoff/xdrv_domoticz.ino new file mode 100644 index 000000000..b00c5c217 --- /dev/null +++ b/sonoff/xdrv_domoticz.ino @@ -0,0 +1,376 @@ +/* +Copyright (c) 2017 Theo Arends. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +- Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. +- Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifdef USE_DOMOTICZ + +#define DOMOTICZ_MAX_SENSORS 5 + +const char HTTP_FORM_DOMOTICZ[] PROGMEM = + "
 Domoticz parameters 
" + "" + "
" + "" + ""; + +const char domoticz_sensors[DOMOTICZ_MAX_SENSORS][14] PROGMEM = + { "Temp", "Temp,Hum", "Temp,Hum,Baro", "Power,Energy", "Illuminance" }; + +int domoticz_update_timer = 0; +byte domoticz_update_flag = 1; + +unsigned long getKeyIntValue(const char *json, const char *key) +{ + char *p, *b, log[LOGSZ]; + int i; + + // search key + p = strstr(json, key); + if (!p) return 0; + // search following separator : + b = strchr(p + strlen(key), ':'); + if (!b) return 0; + // Only the following chars are allowed between key and separator : + for(i = b - json + strlen(key); i < p-json; i++) { + switch (json[i]) { + case ' ': + case '\n': + case '\t': + case '\r': + continue; + default: + return 0; + } + } + b++; + // Allow integers as string too (used in "svalue" : "9") + while ((b[0] == ' ') || (b[0] == '"')) b++; + // Convert to integer + return atoi(b); +} + +void mqtt_publishDomoticzPowerState(byte device) +{ + char svalue[MESSZ]; + + if (sysCfg.domoticz_relay_idx[device -1] && (strlen(sysCfg.domoticz_in_topic) != 0)) { + if ((device < 1) || (device > Maxdevice)) device = 1; + + if (sysCfg.module == SONOFF_LED) { + snprintf_P(svalue, sizeof(svalue), PSTR("{\"idx\":%d,\"nvalue\":2,\"svalue\":\"%d\"}"), + sysCfg.domoticz_relay_idx[device -1], sysCfg.led_dimmer[device -1]); + mqtt_publish(sysCfg.domoticz_in_topic, svalue); + } + else if ((device == 1) && (pin[GPIO_WS2812] < 99)) { + snprintf_P(svalue, sizeof(svalue), PSTR("{\"idx\":%d,\"nvalue\":2,\"svalue\":\"%d\"}"), + sysCfg.domoticz_relay_idx[device -1], sysCfg.ws_dimmer); + mqtt_publish(sysCfg.domoticz_in_topic, svalue); + } + snprintf_P(svalue, sizeof(svalue), PSTR("{\"idx\":%d,\"nvalue\":%d,\"svalue\":\"\"}"), + sysCfg.domoticz_relay_idx[device -1], (power & (0x01 << (device -1))) ? 1 : 0); + mqtt_publish(sysCfg.domoticz_in_topic, svalue); + } +} + +void domoticz_updatePowerState(byte device) +{ + if (domoticz_update_flag) mqtt_publishDomoticzPowerState(device); + domoticz_update_flag = 1; +} + +void domoticz_mqttUpdate() +{ + if ((sysCfg.domoticz_update_timer || domoticz_update_timer) && sysCfg.domoticz_relay_idx[0]) { + domoticz_update_timer--; + if (domoticz_update_timer <= 0) { + domoticz_update_timer = sysCfg.domoticz_update_timer; + for (byte i = 1; i <= Maxdevice; i++) mqtt_publishDomoticzPowerState(i); + } + } +} + +void domoticz_setUpdateTimer(uint16_t value) +{ + domoticz_update_timer = value; +} + +void domoticz_mqttSubscribe() +{ + if (sysCfg.domoticz_relay_idx[0] && (strlen(sysCfg.domoticz_out_topic) != 0)) { + char stopic[TOPSZ]; + snprintf_P(stopic, sizeof(stopic), PSTR("%s/#"), sysCfg.domoticz_out_topic); // domoticz topic + mqttClient.subscribe(stopic); + mqttClient.loop(); // Solve LmacRxBlk:1 messages + } +} + +boolean domoticz_update() +{ + return domoticz_update_flag; +} + +boolean domoticz_mqttData(char *topicBuf, uint16_t stopicBuf, char *dataBuf, uint16_t sdataBuf) +{ + char log[LOGSZ], stemp1[10]; + unsigned long idx = 0; + int16_t nvalue, found = 0; + + domoticz_update_flag = 1; + if (!strncmp(topicBuf, sysCfg.domoticz_out_topic, strlen(sysCfg.domoticz_out_topic)) != 0) { + if (sdataBuf < 20) return 1; + idx = getKeyIntValue(dataBuf,"\"idx\""); + nvalue = getKeyIntValue(dataBuf,"\"nvalue\""); + + snprintf_P(log, sizeof(log), PSTR("DMTZ: idx %d, nvalue %d"), idx, nvalue); + addLog(LOG_LEVEL_DEBUG_MORE, log); + + if (nvalue >= 0 && nvalue <= 2) { + for (byte i = 0; i < Maxdevice; i++) { + if ((idx > 0) && (idx == sysCfg.domoticz_relay_idx[i])) { + snprintf_P(stemp1, sizeof(stemp1), PSTR("%d"), i +1); + if (nvalue == 2) { + nvalue = getKeyIntValue(dataBuf,"\"svalue1\""); + if ((pin[GPIO_WS2812] < 99) && (sysCfg.ws_dimmer == nvalue)) return 1; + if ((sysCfg.module == SONOFF_LED) && (sysCfg.led_dimmer[i] == nvalue)) return 1; + snprintf_P(topicBuf, stopicBuf, PSTR("%s/%s/DIMMER%s"), + SUB_PREFIX, sysCfg.mqtt_topic, (Maxdevice > 1) ? stemp1 : ""); + snprintf_P(dataBuf, sdataBuf, PSTR("%d"), nvalue); + found = 1; + } else { + if (((power >> i) &1) == nvalue) return 1; + snprintf_P(topicBuf, stopicBuf, PSTR("%s/%s/%s%s"), + SUB_PREFIX, sysCfg.mqtt_topic, sysCfg.mqtt_subtopic, (Maxdevice > 1) ? stemp1 : ""); + snprintf_P(dataBuf, sdataBuf, PSTR("%d"), nvalue); + found = 1; + } + break; + } + } + } + if (!found) return 1; + + snprintf_P(log, sizeof(log), PSTR("DMTZ: Receive topic %s, data %s"), topicBuf, dataBuf); + addLog(LOG_LEVEL_DEBUG_MORE, log); + + domoticz_update_flag = 0; + } + return 0; +} + +/*********************************************************************************************\ + * Commands +\*********************************************************************************************/ + +void domoticz_commands(char *type, uint16_t index, char *dataBuf, uint16_t data_len, int16_t payload, char *svalue, uint16_t ssvalue) +{ + if (!strcmp(type,"DOMOTICZINTOPIC")) { + if ((data_len > 0) && (data_len < sizeof(sysCfg.domoticz_in_topic))) { + strlcpy(sysCfg.domoticz_in_topic, (payload == 1) ? DOMOTICZ_IN_TOPIC : dataBuf, sizeof(sysCfg.domoticz_in_topic)); + restartflag = 2; + } + snprintf_P(svalue, ssvalue, PSTR("{\"DomoticzInTopic\":\"%s\"}"), sysCfg.domoticz_in_topic); + } + else if (!strcmp(type,"DOMOTICZOUTTOPIC")) { + if ((data_len > 0) && (data_len < sizeof(sysCfg.domoticz_out_topic))) { + strlcpy(sysCfg.domoticz_out_topic, (payload == 1) ? DOMOTICZ_OUT_TOPIC : dataBuf, sizeof(sysCfg.domoticz_out_topic)); + restartflag = 2; + } + snprintf_P(svalue, ssvalue, PSTR("{\"DomoticzOutTopic\":\"%s\"}"), sysCfg.domoticz_out_topic); + } + else if (!strcmp(type,"DOMOTICZIDX") && (index > 0) && (index <= Maxdevice)) { + if ((data_len > 0) && (payload >= 0)) { + sysCfg.domoticz_relay_idx[index -1] = payload; + restartflag = 2; + } + snprintf_P(svalue, ssvalue, PSTR("{\"DomoticzIdx%d\":%d}"), index, sysCfg.domoticz_relay_idx[index -1]); + } + else if (!strcmp(type,"DOMOTICZKEYIDX") && (index > 0) && (index <= Maxdevice)) { + if ((data_len > 0) && (payload >= 0)) { + sysCfg.domoticz_key_idx[index -1] = payload; + } + snprintf_P(svalue, ssvalue, PSTR("{\"DomoticzKeyIdx%d\":%d}"), index, sysCfg.domoticz_key_idx[index -1]); + } + else if (!strcmp(type,"DOMOTICZSWITCHIDX") && (index > 0) && (index <= Maxdevice)) { + if ((data_len > 0) && (payload >= 0)) { + sysCfg.domoticz_switch_idx[index -1] = payload; + } + snprintf_P(svalue, ssvalue, PSTR("{\"DomoticzSwitchIdx%d\":%d}"), index, sysCfg.domoticz_key_idx[index -1]); + } + else if (!strcmp(type,"DOMOTICZSENSORIDX") && (index > 0) && (index <= DOMOTICZ_MAX_SENSORS)) { + if ((data_len > 0) && (payload >= 0)) { + sysCfg.domoticz_sensor_idx[index -1] = payload; + } + snprintf_P(svalue, ssvalue, PSTR("{\"DomoticzSensorIdx%d\":%d}"), index, sysCfg.domoticz_sensor_idx[index -1]); + } + else if (!strcmp(type,"DOMOTICZUPDATETIMER")) { + if ((data_len > 0) && (payload >= 0) && (payload < 3601)) { + sysCfg.domoticz_update_timer = payload; + } + snprintf_P(svalue, ssvalue, PSTR("{\"DomoticzUpdateTimer\":%d}"), sysCfg.domoticz_update_timer); + } +} + +boolean domoticz_button(byte key, byte device, byte state, byte svalflg) +{ + if ((sysCfg.domoticz_key_idx[device -1] || sysCfg.domoticz_switch_idx[device -1]) && (svalflg)) { + char svalue[MESSZ]; + + snprintf_P(svalue, sizeof(svalue), PSTR("{\"command\":\"switchlight\",\"idx\":%d,\"switchcmd\":\"%s\"}"), + (key) ? sysCfg.domoticz_switch_idx[device -1] : sysCfg.domoticz_key_idx[device -1], (state) ? (state == 2) ? "Toggle" : "On" : "Off"); + mqtt_publish(sysCfg.domoticz_in_topic, svalue); + return 1; + } else { + return 0; + } +} + +/*********************************************************************************************\ + * Sensors +\*********************************************************************************************/ + +uint8_t dom_hum_stat(char *hum) +{ + uint8_t h = atoi(hum); + return (!h) ? 0 : (h < 40) ? 2 : (h > 70) ? 3 : 1; +} + +void dom_sensor(byte idx, char *data) +{ + char dmess[64]; + + if (sysCfg.domoticz_sensor_idx[idx] && (strlen(sysCfg.domoticz_in_topic) != 0)) { + snprintf_P(dmess, sizeof(dmess), PSTR("{\"idx\":%d,\"nvalue\":0,\"svalue\":\"%s\"}"), + sysCfg.domoticz_sensor_idx[idx], data); + mqtt_publish(sysCfg.domoticz_in_topic, dmess); + } +} + +void domoticz_sensor1(char *temp) +{ + dom_sensor(0, temp); +} + +void domoticz_sensor2(char *temp, char *hum) +{ + char data[16]; + snprintf_P(data, sizeof(data), PSTR("%s;%s;%d"), temp, hum, dom_hum_stat(hum)); + dom_sensor(1, data); +} + +void domoticz_sensor3(char *temp, char *hum, char *baro) +{ + char data[32]; + snprintf_P(data, sizeof(data), PSTR("%s;%s;%d;%s;5"), temp, hum, dom_hum_stat(hum), baro); + dom_sensor(2, data); +} + +void domoticz_sensor4(uint16_t power, char *energy) +{ + char data[16]; + snprintf_P(data, sizeof(data), PSTR("%d;%s"), power, energy); + dom_sensor(3, data); +} + +void domoticz_sensor5(uint16_t lux) +{ + char data[8]; + snprintf_P(data, sizeof(data), PSTR("%d"), lux); + dom_sensor(4, data); +} + +/*********************************************************************************************\ + * Presentation +\*********************************************************************************************/ + +void handleDomoticz() +{ + if (_httpflag == HTTP_USER) { + handleRoot(); + return; + } + addLog_P(LOG_LEVEL_DEBUG, PSTR("HTTP: Handle Domoticz config")); + + char stemp[20]; + + String page = FPSTR(HTTP_HEAD); + page.replace("{v}", "Configure Domoticz"); + page += FPSTR(HTTP_FORM_DOMOTICZ); + page.replace("{d1}", String(sysCfg.domoticz_in_topic)); + page.replace("{d2}", String(sysCfg.domoticz_out_topic)); + for (int i = 0; i < Maxdevice; i++) { + page += F(""); + page += F(""); + page += F(""); + page.replace("{1", String(i +1)); + page.replace("{2", String((int)sysCfg.domoticz_relay_idx[i])); + page.replace("{3", String((int)sysCfg.domoticz_key_idx[i])); + page.replace("{4", String((int)sysCfg.domoticz_switch_idx[i])); + } + for (int i = 0; i < DOMOTICZ_MAX_SENSORS; i++) { + page += F(""); + page.replace("{1", String(i +1)); + snprintf_P(stemp, sizeof(stemp), domoticz_sensors[i]); + page.replace("{2", stemp); + page.replace("{4", String((int)sysCfg.domoticz_sensor_idx[i])); + } + page += F(""); + page.replace("{d7}", String((int)sysCfg.domoticz_update_timer)); + page += F("
In topic (" DOMOTICZ_IN_TOPIC ")
Out topic (" DOMOTICZ_OUT_TOPIC ")
Idx {1
Key idx {1
Switch idx {1
Sensor idx {1 - {2
Update timer (" STR(DOMOTICZ_UPDATE_TIMER) ")
"); + page += FPSTR(HTTP_FORM_END); + page += FPSTR(HTTP_BTN_CONF); + showPage(page); +} + +void domoticz_saveSettings() +{ + char log[LOGSZ], stemp[20]; + + strlcpy(sysCfg.domoticz_in_topic, (!strlen(webServer->arg("it").c_str())) ? DOMOTICZ_IN_TOPIC : webServer->arg("it").c_str(), sizeof(sysCfg.domoticz_in_topic)); + strlcpy(sysCfg.domoticz_out_topic, (!strlen(webServer->arg("ot").c_str())) ? DOMOTICZ_OUT_TOPIC : webServer->arg("ot").c_str(), sizeof(sysCfg.domoticz_out_topic)); + for (byte i = 0; i < 4; i++) { + snprintf_P(stemp, sizeof(stemp), PSTR("r%d"), i +1); + sysCfg.domoticz_relay_idx[i] = (!strlen(webServer->arg(stemp).c_str())) ? 0 : atoi(webServer->arg(stemp).c_str()); + snprintf_P(stemp, sizeof(stemp), PSTR("k%d"), i +1); + sysCfg.domoticz_key_idx[i] = (!strlen(webServer->arg(stemp).c_str())) ? 0 : atoi(webServer->arg(stemp).c_str()); + snprintf_P(stemp, sizeof(stemp), PSTR("s%d"), i +1); + sysCfg.domoticz_switch_idx[i] = (!strlen(webServer->arg(stemp).c_str())) ? 0 : atoi(webServer->arg(stemp).c_str()); + } + for (byte i = 0; i < DOMOTICZ_MAX_SENSORS; i++) { + snprintf_P(stemp, sizeof(stemp), PSTR("l%d"), i +1); + sysCfg.domoticz_sensor_idx[i] = (!strlen(webServer->arg(stemp).c_str())) ? 0 : atoi(webServer->arg(stemp).c_str()); + } + sysCfg.domoticz_update_timer = (!strlen(webServer->arg("ut").c_str())) ? DOMOTICZ_UPDATE_TIMER : atoi(webServer->arg("ut").c_str()); + snprintf_P(log, sizeof(log), PSTR("HTTP: Domoticz in %s, out %s, idx %d, %d, %d, %d, update timer %d"), + sysCfg.domoticz_in_topic, sysCfg.domoticz_out_topic, + sysCfg.domoticz_relay_idx[0], sysCfg.domoticz_relay_idx[1], sysCfg.domoticz_relay_idx[2], sysCfg.domoticz_relay_idx[3], + sysCfg.domoticz_update_timer); + addLog(LOG_LEVEL_INFO, log); + snprintf_P(log, sizeof(log), PSTR("HTTP: key %d, %d, %d, %d, switch %d, %d, %d, %d, sensor %d, %d, %d, %d, %d"), + sysCfg.domoticz_key_idx[0], sysCfg.domoticz_key_idx[1], sysCfg.domoticz_key_idx[2], sysCfg.domoticz_key_idx[3], + sysCfg.domoticz_switch_idx[0], sysCfg.domoticz_switch_idx[1], sysCfg.domoticz_switch_idx[2], sysCfg.domoticz_switch_idx[3], + sysCfg.domoticz_sensor_idx[0], sysCfg.domoticz_sensor_idx[1], sysCfg.domoticz_sensor_idx[2], sysCfg.domoticz_sensor_idx[3], sysCfg.domoticz_sensor_idx[4]); + addLog(LOG_LEVEL_INFO, log); +} +#endif // USE_DOMOTICZ + diff --git a/sonoff/xdrv_wemohue.ino b/sonoff/xdrv_wemohue.ino new file mode 100644 index 000000000..7f2e47752 --- /dev/null +++ b/sonoff/xdrv_wemohue.ino @@ -0,0 +1,233 @@ +/* +Copyright (c) 2017 Heiko Krupp and Theo Arends. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +- Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. +- Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. +*/ + +#if defined(USE_WEMO_EMULATION) || defined(USE_HUE_EMULATION) + +#define UDP_BUFFER_SIZE 200 // Max UDP buffer size needed for M-SEARCH message + +boolean udpConnected = false; + +char packetBuffer[UDP_BUFFER_SIZE]; // buffer to hold incoming UDP packet +IPAddress ipMulticast(239, 255, 255, 250); // Simple Service Discovery Protocol (SSDP) +uint32_t portMulticast = 1900; // Multicast address and port + +#ifdef USE_WEMO_EMULATION +/*********************************************************************************************\ + * WeMo UPNP support routines +\*********************************************************************************************/ +const char WEMO_MSEARCH[] PROGMEM = + "HTTP/1.1 200 OK\r\n" + "CACHE-CONTROL: max-age=86400\r\n" + "DATE: Fri, 15 Apr 2016 04:56:29 GMT\r\n" + "EXT:\r\n" + "LOCATION: http://{r1}:80/setup.xml\r\n" + "OPT: \"http://schemas.upnp.org/upnp/1/0/\"; ns=01\r\n" + "01-NLS: b9200ebb-736d-4b93-bf03-835149d13983\r\n" + "SERVER: Unspecified, UPnP/1.0, Unspecified\r\n" + "ST: urn:Belkin:device:**\r\n" + "USN: uuid:{r2}::urn:Belkin:device:**\r\n" + "X-User-Agent: redsonic\r\n" + "\r\n"; + +String wemo_serial() +{ + char serial[15]; + snprintf_P(serial, sizeof(serial), PSTR("201612K%07d"), ESP.getChipId()); + return String(serial); +} + +String wemo_UUID() +{ + char uuid[26]; + snprintf_P(uuid, sizeof(uuid), PSTR("Socket-1_0-%s"), wemo_serial().c_str()); + return String(uuid); +} + +void wemo_respondToMSearch() +{ + char message[TOPSZ], log[LOGSZ]; + + if (portUDP.beginPacket(portUDP.remoteIP(), portUDP.remotePort())) { + String response = FPSTR(WEMO_MSEARCH); + response.replace("{r1}", WiFi.localIP().toString()); + response.replace("{r2}", wemo_UUID()); + portUDP.write(response.c_str()); + portUDP.endPacket(); + snprintf_P(message, sizeof(message), PSTR("Response sent")); + } else { + snprintf_P(message, sizeof(message), PSTR("Failed to send response")); + } + snprintf_P(log, sizeof(log), PSTR("UPnP: Wemo %s to %s:%d"), + message, portUDP.remoteIP().toString().c_str(), portUDP.remotePort()); + addLog(LOG_LEVEL_DEBUG, log); +} +#endif // USE_WEMO_EMULATION + +#ifdef USE_HUE_EMULATION +/*********************************************************************************************\ + * Hue Bridge UPNP support routines + * Need to send 3 response packets with varying ST and USN +\*********************************************************************************************/ +const char HUE_RESPONSE[] PROGMEM = + "HTTP/1.0 200 OK\r\n" + "HOST: 239.255.255.250:1900\r\n" + "CACHE-CONTROL: max-age=100\r\n" + "EXT:\r\n" + "LOCATION: http://{r1}:80/description.xml\r\n" + "SERVER: FreeRTOS/7.4.2 UPnP/1.0 IpBridge/1.15.0\r\n" + "hue-bridgeid: {r2}\r\n"; +const char HUE_ST1[] PROGMEM = + "ST: upnp:rootdevice\r\n"; +const char HUE_USN1[] PROGMEM = + "USN: uuid:{r3}::upnp:rootdevice\r\n" + "\r\n"; + +const char HUE_ST2[] PROGMEM = + "ST: uuid:{r3}\r\n"; +const char HUE_USN2[] PROGMEM = + "USN: uuid:{r3}\r\n" + "\r\n"; + +const char HUE_ST3[] PROGMEM = + "ST: urn:schemas-upnp-org:device:basic:1\r\n"; + +String hue_bridgeid() +{ + char bridgeid[16]; + snprintf_P(bridgeid, sizeof(bridgeid), PSTR("5CCF7FFFFE%03X"), ESP.getChipId()); + return String(bridgeid); +} + +String hue_UUID() +{ + char serial[36]; + snprintf_P(serial, sizeof(serial), PSTR("f6543a06-da50-11ba-8d8f-5ccf7f%03x"), ESP.getChipId()); + return String(serial); +} + +void hue_respondToMSearch() +{ + char message[TOPSZ], log[LOGSZ]; + + if (portUDP.beginPacket(portUDP.remoteIP(), portUDP.remotePort())) { + String response = FPSTR(HUE_RESPONSE); + String response_st=FPSTR(HUE_ST1); + String response_usn=FPSTR(HUE_USN1); + response += response_st + response_usn; + response.replace("{r1}", WiFi.localIP().toString()); + response.replace("{r2}", hue_bridgeid()); + response.replace("{r3}", hue_UUID()); + portUDP.write(response.c_str()); + portUDP.endPacket(); + snprintf_P(message, sizeof(message), PSTR("Response1 sent")); +// addLog(LOG_LEVEL_DEBUG_MORE, response.c_str()); + + response = FPSTR(HUE_RESPONSE); + response_st=FPSTR(HUE_ST2); + response_usn=FPSTR(HUE_USN2); + response += response_st + response_usn; + response.replace("{r1}", WiFi.localIP().toString()); + response.replace("{r2}", hue_bridgeid()); + response.replace("{r3}", hue_UUID()); + portUDP.write(response.c_str()); + portUDP.endPacket(); + snprintf_P(message, sizeof(message), PSTR("Response2 sent")); +// addLog(LOG_LEVEL_DEBUG_MORE, response.c_str()); + + response = FPSTR(HUE_RESPONSE); + response_st=FPSTR(HUE_ST3); + response += response_st + response_usn; + response.replace("{r1}", WiFi.localIP().toString()); + response.replace("{r2}", hue_bridgeid()); + response.replace("{r3}", hue_UUID()); + portUDP.write(response.c_str()); + portUDP.endPacket(); + snprintf_P(message, sizeof(message), PSTR("Response3 sent")); +// addLog(LOG_LEVEL_DEBUG_MORE, response.c_str()); + + } else { + snprintf_P(message, sizeof(message), PSTR("Failed to send response")); + } + snprintf_P(log, sizeof(log), PSTR("UPnP: HUE %s to %s:%d"), + message, portUDP.remoteIP().toString().c_str(), portUDP.remotePort()); + addLog(LOG_LEVEL_DEBUG, log); +} +#endif // USE_HUE_EMULATION + +/********************************************************************************************/ + +#if defined(USE_WEMO_EMULATION) || defined(USE_HUE_EMULATION) +boolean UDP_Disconnect() +{ + if (udpConnected) { + WiFiUDP::stopAll(); + addLog_P(LOG_LEVEL_DEBUG, PSTR("UPnP: Multicast disabled")); + udpConnected = false; + } + return udpConnected; +} + +boolean UDP_Connect() +{ + if (!udpConnected) { + if (portUDP.beginMulticast(WiFi.localIP(), ipMulticast, portMulticast)) { + addLog_P(LOG_LEVEL_INFO, PSTR("UPnP: Multicast (re)joined")); + udpConnected = true; + } else { + addLog_P(LOG_LEVEL_INFO, PSTR("UPnP: Multicast join failed")); + udpConnected = false; + } + } + return udpConnected; +} + +void pollUDP() +{ + if (udpConnected) { + if (portUDP.parsePacket()) { + int len = portUDP.read(packetBuffer, UDP_BUFFER_SIZE -1); + if (len > 0) packetBuffer[len] = 0; + String request = packetBuffer; +// addLog_P(LOG_LEVEL_DEBUG_MORE, packetBuffer); + if (request.indexOf("M-SEARCH") >= 0) { +#ifdef USE_WEMO_EMULATION + if (request.indexOf("urn:Belkin:device:**") > 0) { + wemo_respondToMSearch(); + } +#endif // USE_WEMO_EMULATION +#ifdef USE_HUE_EMULATION + if (request.indexOf("ST: urn:schemas-upnp-org:device:basic:1") > 0 || + request.indexOf("ST: upnp:rootdevice") > 0 || + request.indexOf("ST: ssdp:all") > 0) { + hue_respondToMSearch(); + } +#endif // USE_HUE_EMULATION + } + } + } +} +#endif // USE_WEMO_EMULATION || USE_HUE_EMULATION +#endif // USE_WEMO_EMULATION || USE_HUE_EMULATION + diff --git a/sonoff/xdrv_ws2812.ino b/sonoff/xdrv_ws2812.ino new file mode 100644 index 000000000..cddd8c66e --- /dev/null +++ b/sonoff/xdrv_ws2812.ino @@ -0,0 +1,465 @@ +/* +Copyright (c) 2017 Theo Arends. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +- Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. +- Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifdef USE_WS2812 +/*********************************************************************************************\ + * WS2812 Leds using NeopixelBus library +\*********************************************************************************************/ + +#include + +#ifdef USE_WS2812_DMA + NeoPixelBus *strip = NULL; +#else + NeoPixelBus *strip = NULL; +#endif // USE_WS2812_DMA + +#define COLOR_SATURATION 254.0f + +struct wsColor { + uint8_t red, green, blue; +}; + +struct ColorScheme { + wsColor* colors; + uint8_t count; +}; + +wsColor incandescent[2] = { 255, 140, 20, 0, 0, 0 }; +wsColor rgb[3] = { 255, 0, 0, 0, 255, 0, 0, 0, 255 }; +wsColor christmas[2] = { 255, 0, 0, 0, 255, 0 }; +wsColor hanukkah[2] = { 0, 0, 255, 255, 255, 255 }; +wsColor kwanzaa[3] = { 255, 0, 0, 0, 0, 0, 0, 255, 0 }; +wsColor rainbow[7] = { 255, 0, 0, 255, 128, 0, 255, 255, 0, 0, 255, 0, 0, 0, 255, 128, 0, 255, 255, 0, 255 }; +wsColor fire[3] = { 255, 0, 0, 255, 102, 0, 255, 192, 0 }; +ColorScheme schemes[7] = { + incandescent, 2, + rgb, 3, + christmas, 2, + hanukkah, 2, + kwanzaa, 3, + rainbow, 7, + fire, 3 }; + +uint8_t widthValues[5] = { + 1, // Small + 2, // Medium + 4, // Large + 8, // Largest + 255 }; // All +uint8_t repeatValues[5] = { + 8, // Small + 6, // Medium + 4, // Large + 2, // Largest + 1 }; // All +uint8_t speedValues[6] = { + 0, // None + 18, // Slowest + 14, // Slower + 10, // Slow + 6, // Fast + 2 }; // Fastest + +uint8_t ledTable[] = { + 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 4, 4, + 4, 4, 4, 5, 5, 5, 5, 6, 6, 6, 6, 7, 7, 7, 7, 8, + 8, 8, 9, 9, 9, 10, 10, 10, 11, 11, 12, 12, 12, 13, 13, 14, + 14, 15, 15, 15, 16, 16, 17, 17, 18, 18, 19, 19, 20, 20, 21, 22, + 22, 23, 23, 24, 25, 25, 26, 26, 27, 28, 28, 29, 30, 30, 31, 32, + 33, 33, 34, 35, 36, 36, 37, 38, 39, 40, 40, 41, 42, 43, 44, 45, + 46, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, + 61, 62, 63, 64, 65, 67, 68, 69, 70, 71, 72, 73, 75, 76, 77, 78, + 80, 81, 82, 83, 85, 86, 87, 89, 90, 91, 93, 94, 95, 97, 98, 99, + 101,102,104,105,107,108,110,111,113,114,116,117,119,121,122,124, + 125,127,129,130,132,134,135,137,139,141,142,144,146,148,150,151, + 153,155,157,159,161,163,165,166,168,170,172,174,176,178,180,182, + 184,186,189,191,193,195,197,199,201,204,206,208,210,212,215,217, + 219,221,224,226,228,231,233,235,238,240,243,245,248,250,253,255 }; + +uint8_t lany = 0; +RgbColor dcolor, tcolor, lcolor; + +uint8_t wakeupDimmer = 0; +uint16_t wakeupCntr = 0; +unsigned long stripTimerCntr = 0; // Bars and Gradient + +void ws2812_setDim(uint8_t myDimmer) +{ + float newDim = 100 / (float)myDimmer; + float fmyRed = (float)sysCfg.ws_red / newDim; + float fmyGrn = (float)sysCfg.ws_green / newDim; + float fmyBlu = (float)sysCfg.ws_blue / newDim; + dcolor.R = (uint8_t)fmyRed; + dcolor.G = (uint8_t)fmyGrn; + dcolor.B = (uint8_t)fmyBlu; +} + +void ws2812_setColor(uint16_t led, char* colstr) +{ + HtmlColor hcolor; + char log[LOGSZ], lcolstr[8]; + + snprintf_P(lcolstr, sizeof(lcolstr), PSTR("#%s"), colstr); + uint8_t result = hcolor.Parse((char *)lcolstr, 7); + if (result) { + if (led) { + strip->SetPixelColor(led -1, RgbColor(hcolor)); // Led 1 is strip Led 0 -> substract offset 1 + strip->Show(); + } else { + dcolor = RgbColor(hcolor); + +// snprintf_P(log, sizeof(log), PSTR("DBG: Red %02X, Green %02X, Blue %02X"), dcolor.R, dcolor.G, dcolor.B); +// addLog(LOG_LEVEL_DEBUG, log); + + uint16_t temp = dcolor.R; + if (temp < dcolor.G) temp = dcolor.G; + if (temp < dcolor.B) temp = dcolor.B; + float mDim = (float)temp / 2.55; + sysCfg.ws_dimmer = (uint8_t)mDim; + + float newDim = 100 / mDim; + float fmyRed = (float)dcolor.R * newDim; + float fmyGrn = (float)dcolor.G * newDim; + float fmyBlu = (float)dcolor.B * newDim; + sysCfg.ws_red = (uint8_t)fmyRed; + sysCfg.ws_green = (uint8_t)fmyGrn; + sysCfg.ws_blue = (uint8_t)fmyBlu; + + lany = 1; + } + } +} + +void ws2812_replaceHSB(String *response) +{ + ws2812_setDim(sysCfg.ws_dimmer); + HsbColor hsb=HsbColor(dcolor); + response->replace("{h}", String((uint16_t)(65535.0f * hsb.H))); + response->replace("{s}", String((uint8_t)(COLOR_SATURATION * hsb.S))); + response->replace("{b}", String((uint8_t)(COLOR_SATURATION * hsb.B))); +} + +void ws2812_changeBrightness(uint8_t bri) +{ + char rgb[7]; + + //sysCfg.ws_ledtable=1; // Switch on Gamma Correction for "natural" brightness controll + ws2812_setDim(sysCfg.ws_dimmer); + HsbColor hsb = HsbColor(dcolor); + if (!bri) bri=1; + if (bri==255) bri=252; + hsb.B=(float)(bri/COLOR_SATURATION); + RgbColor tmp = RgbColor(hsb); + sprintf(rgb,"%02X%02X%02X", tmp.R, tmp.G, tmp.B); + ws2812_setColor(0,rgb); +} + +void ws2812_getColor(uint16_t led, char* svalue, uint16_t ssvalue) +{ + RgbColor mcolor; + char stemp[20]; + + if (led) { + mcolor = strip->GetPixelColor(led -1); + snprintf_P(stemp, sizeof(stemp), PSTR("Led%d"), led); + } else { + ws2812_setDim(sysCfg.ws_dimmer); + mcolor = dcolor; + snprintf_P(stemp, sizeof(stemp), PSTR("Color")); + } + uint32_t color = (uint32_t)mcolor.R << 16; + color += (uint32_t)mcolor.G << 8; + color += (uint32_t)mcolor.B; + snprintf_P(svalue, ssvalue, PSTR("{\"%s\":\"%06X\"}"), stemp, color); +} + +void ws2812_stripShow() +{ + RgbColor c; + + if (sysCfg.ws_ledtable) { + for (uint16_t i = 0; i < sysCfg.ws_pixels; i++) { + c = strip->GetPixelColor(i); + strip->SetPixelColor(i, RgbColor(ledTable[c.R], ledTable[c.G], ledTable[c.B])); + } + } + strip->Show(); +} + +void ws2812_resetWakupState() +{ + wakeupDimmer = 0; + wakeupCntr = 0; +} + +void ws2812_resetStripTimer() +{ + stripTimerCntr = 0; +} + +int mod(int a, int b) +{ + int ret = a % b; + if (ret < 0) ret += b; + return ret; +} + +void ws2812_clock() +{ + RgbColor c; + + strip->ClearTo(0); // Reset strip + float newDim = 100 / (float)sysCfg.ws_dimmer; + float f1 = 255 / newDim; + uint8_t i1 = (uint8_t)f1; + float f2 = 127 / newDim; + uint8_t i2 = (uint8_t)f2; + float f3 = 63 / newDim; + uint8_t i3 = (uint8_t)f3; + + int j = sysCfg.ws_pixels; + int clksize = 600 / j; + int i = (rtcTime.Second * 10) / clksize; + + c = strip->GetPixelColor(mod(i, j)); c.B = i1; strip->SetPixelColor(mod(i, j), c); + i = (rtcTime.Minute * 10) / clksize; + c = strip->GetPixelColor(mod(i -1, j)); c.G = i3; strip->SetPixelColor(mod(i -1, j), c); + c = strip->GetPixelColor(mod(i, j)); c.G = i1; strip->SetPixelColor(mod(i, j), c); + c = strip->GetPixelColor(mod(i +1, j)); c.G = i3; strip->SetPixelColor(mod(i +1, j), c); + i = (rtcTime.Hour % 12) * (50 / clksize); + c = strip->GetPixelColor(mod(i -2, j)); c.R = i3; strip->SetPixelColor(mod(i -2, j), c); + c = strip->GetPixelColor(mod(i -1, j)); c.R = i2; strip->SetPixelColor(mod(i -1, j), c); + c = strip->GetPixelColor(mod(i, j)); c.R = i1; strip->SetPixelColor(mod(i, j), c); + c = strip->GetPixelColor(mod(i +1, j)); c.R = i2; strip->SetPixelColor(mod(i +1, j), c); + c = strip->GetPixelColor(mod(i +2, j)); c.R = i3; strip->SetPixelColor(mod(i +2, j), c); + ws2812_stripShow(); +} + +void ws2812_gradientColor(struct wsColor* mColor, uint8_t range, uint8_t gradRange, uint8_t i) +{ +/* + * Compute the color of a pixel at position i using a gradient of the color scheme. + * This function is used internally by the gradient function. + */ + ColorScheme scheme = schemes[sysCfg.ws_scheme -3]; + uint8_t curRange = i / range; + uint8_t rangeIndex = i % range; + uint8_t colorIndex = rangeIndex / gradRange; + uint8_t start = colorIndex; + uint8_t end = colorIndex +1; + if (curRange % 2 != 0) { + start = (scheme.count -1) - start; + end = (scheme.count -1) - end; + } + float newDim = 100 / (float)sysCfg.ws_dimmer; + float fmyRed = (float)map(rangeIndex % gradRange, 0, gradRange, scheme.colors[start].red, scheme.colors[end].red) / newDim; + float fmyGrn = (float)map(rangeIndex % gradRange, 0, gradRange, scheme.colors[start].green, scheme.colors[end].green) / newDim; + float fmyBlu = (float)map(rangeIndex % gradRange, 0, gradRange, scheme.colors[start].blue, scheme.colors[end].blue) / newDim; + mColor->red = (uint8_t)fmyRed; + mColor->green = (uint8_t)fmyGrn; + mColor->blue = (uint8_t)fmyBlu; +} + +void ws2812_gradient() +{ +/* + * This routine courtesy Tony DiCola (Adafruit) + * Display a gradient of colors for the current color scheme. + * Repeat is the number of repetitions of the gradient (pick a multiple of 2 for smooth looping of the gradient). + */ + RgbColor c; + + ColorScheme scheme = schemes[sysCfg.ws_scheme -3]; + if (scheme.count < 2) return; + + uint8_t repeat = repeatValues[sysCfg.ws_width]; // number of scheme.count per ledcount + uint8_t range = (uint8_t)ceil((float)sysCfg.ws_pixels / (float)repeat); + uint8_t gradRange = (uint8_t)ceil((float)range / (float)(scheme.count - 1)); + uint8_t offset = speedValues[sysCfg.ws_speed] > 0 ? stripTimerCntr / speedValues[sysCfg.ws_speed] : 0; + + wsColor oldColor, currentColor; + ws2812_gradientColor(&oldColor, range, gradRange, offset); + currentColor = oldColor; + for (uint16_t i = 0; i < sysCfg.ws_pixels; i++) { + if (repeatValues[sysCfg.ws_width] > 1) ws2812_gradientColor(¤tColor, range, gradRange, i +offset); + if (sysCfg.ws_speed > 0) { + // Blend old and current color based on time for smooth movement. + c.R = map(stripTimerCntr % speedValues[sysCfg.ws_speed], 0, speedValues[sysCfg.ws_speed], oldColor.red, currentColor.red); + c.G = map(stripTimerCntr % speedValues[sysCfg.ws_speed], 0, speedValues[sysCfg.ws_speed], oldColor.green, currentColor.green); + c.B = map(stripTimerCntr % speedValues[sysCfg.ws_speed], 0, speedValues[sysCfg.ws_speed], oldColor.blue, currentColor.blue); + } + else { + // No animation, just use the current color. + c.R = currentColor.red; + c.G = currentColor.green; + c.B = currentColor.blue; + } + strip->SetPixelColor(i, c); + oldColor = currentColor; + } + ws2812_stripShow(); +} + +void ws2812_bars() +{ +/* + * This routine courtesy Tony DiCola (Adafruit) + * Display solid bars of color for the current color scheme. + * Width is the width of each bar in pixels/lights. + */ + RgbColor c; + uint16_t i; + + ColorScheme scheme = schemes[sysCfg.ws_scheme -3]; + + uint8_t maxSize = sysCfg.ws_pixels / scheme.count; + if (widthValues[sysCfg.ws_width] > maxSize) maxSize = 0; + + uint8_t offset = speedValues[sysCfg.ws_speed] > 0 ? stripTimerCntr / speedValues[sysCfg.ws_speed] : 0; + + wsColor mcolor[scheme.count]; + memcpy(mcolor, scheme.colors, sizeof(mcolor)); + float newDim = 100 / (float)sysCfg.ws_dimmer; + for (i = 0; i < scheme.count; i++) { + float fmyRed = (float)mcolor[i].red / newDim; + float fmyGrn = (float)mcolor[i].green / newDim; + float fmyBlu = (float)mcolor[i].blue / newDim; + mcolor[i].red = (uint8_t)fmyRed; + mcolor[i].green = (uint8_t)fmyGrn; + mcolor[i].blue = (uint8_t)fmyBlu; + } + uint8_t colorIndex = offset % scheme.count; + for (i = 0; i < sysCfg.ws_pixels; i++) { + if (maxSize) + colorIndex = ((i + offset) % (scheme.count * widthValues[sysCfg.ws_width])) / widthValues[sysCfg.ws_width]; + c.R = mcolor[colorIndex].red; + c.G = mcolor[colorIndex].green; + c.B = mcolor[colorIndex].blue; + strip->SetPixelColor(i, c); + } + ws2812_stripShow(); +} + +void ws2812_animate() +{ + char log[LOGSZ]; + uint8_t fadeValue; + + stripTimerCntr++; + if (power == 0) { // Power Off + stripTimerCntr = 0; + tcolor = 0; + } + else { + switch (sysCfg.ws_scheme) { + case 0: // Power On + ws2812_setDim(sysCfg.ws_dimmer); + if (sysCfg.ws_fade == 0) { + tcolor = dcolor; + } else { + if (tcolor != dcolor) { + if (tcolor.R < dcolor.R) tcolor.R += ((dcolor.R - tcolor.R) >> sysCfg.ws_speed) +1; + if (tcolor.G < dcolor.G) tcolor.G += ((dcolor.G - tcolor.G) >> sysCfg.ws_speed) +1; + if (tcolor.B < dcolor.B) tcolor.B += ((dcolor.B - tcolor.B) >> sysCfg.ws_speed) +1; + if (tcolor.R > dcolor.R) tcolor.R -= ((tcolor.R - dcolor.R) >> sysCfg.ws_speed) +1; + if (tcolor.G > dcolor.G) tcolor.G -= ((tcolor.G - dcolor.G) >> sysCfg.ws_speed) +1; + if (tcolor.B > dcolor.B) tcolor.B -= ((tcolor.B - dcolor.B) >> sysCfg.ws_speed) +1; + } + } + break; + case 1: // Wake up light + wakeupCntr++; + if (wakeupDimmer == 0) { + tcolor = 0; + wakeupDimmer++; + } + else { + if (wakeupCntr > ((sysCfg.ws_wakeup * STATES) / sysCfg.ws_dimmer)) { + wakeupCntr = 0; + wakeupDimmer++; + if (wakeupDimmer <= sysCfg.ws_dimmer) { + ws2812_setDim(wakeupDimmer); + tcolor = dcolor; + } else + sysCfg.ws_scheme = 0; + } + } + break; + case 2: // Clock + if ((state == (STATES/10)*2) || (lany != 2)) ws2812_clock(); + lany = 2; + break; + default: + if (sysCfg.ws_fade == 1) ws2812_gradient(); else ws2812_bars(); + lany = 1; + break; + } + } + + if ((sysCfg.ws_scheme <= 1) || (!(power &1))) { + if ((lcolor != tcolor) || lany) { + lany = 0; + lcolor = tcolor; + +// snprintf_P(log, sizeof(log), PSTR("DBG: StripPixels %d, CfgPixels %d, Red %02X, Green %02X, Blue %02X"), strip->PixelCount(), sysCfg.ws_pixels, lcolor.R, lcolor.G, lcolor.B); +// addLog(LOG_LEVEL_DEBUG, log); + + if (sysCfg.ws_ledtable) { + for (uint16_t i = 0; i < sysCfg.ws_pixels; i++) strip->SetPixelColor(i, RgbColor(ledTable[lcolor.R],ledTable[lcolor.G],ledTable[lcolor.B])); + } else { + for (uint16_t i = 0; i < sysCfg.ws_pixels; i++) strip->SetPixelColor(i, lcolor); + } + strip->Show(); + } + } +} + +void ws2812_update() +{ + lany = 1; +} + +void ws2812_pixels() +{ + strip->ClearTo(0); + strip->Show(); + tcolor = 0; + lany = 1; +} + +void ws2812_init() +{ +#ifdef USE_WS2812_DMA + strip = new NeoPixelBus(WS2812_MAX_LEDS); // For Esp8266, the Pin is omitted and it uses GPIO3 due to DMA hardware use. +#else + strip = new NeoPixelBus(WS2812_MAX_LEDS, pin[GPIO_WS2812]); +#endif // USE_WS2812_DMA + strip->Begin(); + ws2812_pixels(); +} +#endif // USE_WS2812 diff --git a/sonoff/xsns_bh1750.ino b/sonoff/xsns_bh1750.ino new file mode 100644 index 000000000..405e4a1f1 --- /dev/null +++ b/sonoff/xsns_bh1750.ino @@ -0,0 +1,110 @@ +/* +Copyright (c) 2017 Theo Arends. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +- Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. +- Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifdef USE_I2C +#ifdef USE_BH1750 +/*********************************************************************************************\ + * BH1750 - Ambient Light Intensity +\*********************************************************************************************/ + +#define BH1750_ADDR1 0x23 +#define BH1750_ADDR2 0x5C + +#define BH1750_CONTINUOUS_HIGH_RES_MODE 0x10 // Start measurement at 1lx resolution. Measurement time is approx 120ms. + +uint8_t bh1750addr, bh1750type = 0; +char bh1750stype[7]; + +uint16_t bh1750_readLux(void) +{ + Wire.requestFrom(bh1750addr, (uint8_t)2); + byte msb = Wire.read(); + byte lsb = Wire.read(); + uint16_t value = ((msb << 8) | lsb) / 1.2; + return value; +} + +boolean bh1750_detect() +{ + if (bh1750type) return true; + + char log[LOGSZ]; + uint8_t status; + boolean success = false; + + bh1750addr = BH1750_ADDR1; + Wire.beginTransmission(bh1750addr); + Wire.write(BH1750_CONTINUOUS_HIGH_RES_MODE); + status = Wire.endTransmission(); + if (status) { + bh1750addr = BH1750_ADDR2; + Wire.beginTransmission(bh1750addr); + Wire.write(BH1750_CONTINUOUS_HIGH_RES_MODE); + status = Wire.endTransmission(); + } + if (!status) { + success = true; + bh1750type = 1; + snprintf_P(bh1750stype, sizeof(bh1750stype), PSTR("BH1750")); + } + if (success) { + snprintf_P(log, sizeof(log), PSTR("I2C: %s found at address 0x%x"), bh1750stype, bh1750addr); + addLog(LOG_LEVEL_DEBUG, log); + } else { + bh1750type = 0; + } + return success; +} + +/*********************************************************************************************\ + * Presentation +\*********************************************************************************************/ + +void bh1750_mqttPresent(char* svalue, uint16_t ssvalue, uint8_t* djson, uint8_t domidx) +{ + if (!bh1750type) return; + + uint16_t l = bh1750_readLux(); + snprintf_P(svalue, ssvalue, PSTR("%s, \"%s\":{\"Illuminance\":%d}"), svalue, bh1750stype, l); + *djson = 1; +#ifdef USE_DOMOTICZ + domoticz_sensor5(l); +#endif // USE_DOMOTICZ +} + +#ifdef USE_WEBSERVER +String bh1750_webPresent() +{ + String page = ""; + if (bh1750type) { + uint16_t l = bh1750_readLux(); + page += F("Illuminance: "); page += String(l); page += F(" lx"); + } + return page; +} +#endif // USE_WEBSERVER +#endif // USE_BH1750 +#endif // USE_I2C + diff --git a/sonoff/xsns_bmp.ino b/sonoff/xsns_bmp.ino new file mode 100644 index 000000000..91f00eb55 --- /dev/null +++ b/sonoff/xsns_bmp.ino @@ -0,0 +1,477 @@ +/* + Copyright (c) 2017 Heiko Krupp and Theo Arends. All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + - Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + - Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifdef USE_I2C +#ifdef USE_BMP +/*********************************************************************************************\ + * BMP085, BMP180, BMP280, BME280 - Pressure and Temperature and Humidy (BME280 only) + * + * Source: Heiko Krupp and Adafruit Industries +\*********************************************************************************************/ + +#define BMP_ADDR 0x77 + +#define BMP180_CHIPID 0x55 +#define BMP280_CHIPID 0x58 +#define BME280_CHIPID 0x60 + +#define BMP_REGISTER_CHIPID 0xD0 + +uint8_t bmpaddr, bmptype = 0; +char bmpstype[7]; + +/*********************************************************************************************\ + * BMP085 and BME180 + * + * Programmer : Heiko Krupp with changes from Theo Arends +\*********************************************************************************************/ + +#define BMP180_REG_CONTROL 0xF4 +#define BMP180_REG_RESULT 0xF6 +#define BMP180_TEMPERATURE 0x2E +#define BMP180_PRESSURE3 0xF4 // Max. oversampling -> OSS = 3 + +#define BMP180_AC1 0xAA +#define BMP180_AC2 0xAC +#define BMP180_AC3 0xAE +#define BMP180_AC4 0xB0 +#define BMP180_AC5 0xB2 +#define BMP180_AC6 0xB4 +#define BMP180_VB1 0xB6 +#define BMP180_VB2 0xB8 +#define BMP180_MB 0xBA +#define BMP180_MC 0xBC +#define BMP180_MD 0xBE + +#define BMP180_OSS 3 + +int16_t cal_ac1,cal_ac2,cal_ac3,cal_b1,cal_b2,cal_mc,cal_md; +uint16_t cal_ac4,cal_ac5,cal_ac6; +int32_t bmp180_b5 = 0; + +boolean bmp180_calibration() +{ + cal_ac1 = i2c_read16(bmpaddr, BMP180_AC1); + cal_ac2 = i2c_read16(bmpaddr, BMP180_AC2); + cal_ac3 = i2c_read16(bmpaddr, BMP180_AC3); + cal_ac4 = i2c_read16(bmpaddr, BMP180_AC4); + cal_ac5 = i2c_read16(bmpaddr, BMP180_AC5); + cal_ac6 = i2c_read16(bmpaddr, BMP180_AC6); + cal_b1 = i2c_read16(bmpaddr, BMP180_VB1); + cal_b2 = i2c_read16(bmpaddr, BMP180_VB2); + cal_mc = i2c_read16(bmpaddr, BMP180_MC); + cal_md = i2c_read16(bmpaddr, BMP180_MD); + + // Check for Errors in calibration data. Value never is 0x0000 or 0xFFFF + if(!cal_ac1 | !cal_ac2 | !cal_ac3 | !cal_ac4 | !cal_ac5 | + !cal_ac6 | !cal_b1 | !cal_b2 | !cal_mc | !cal_md) + return false; + + if((cal_ac1==0xFFFF)| + (cal_ac2==0xFFFF)| + (cal_ac3==0xFFFF)| + (cal_ac4==0xFFFF)| + (cal_ac5==0xFFFF)| + (cal_ac6==0xFFFF)| + (cal_b1==0xFFFF)| + (cal_b2==0xFFFF)| + (cal_mc==0xFFFF)| + (cal_md==0xFFFF)) + return false; + + return true; +} + +double bmp180_readTemperature() +{ + i2c_write8(bmpaddr, BMP180_REG_CONTROL, BMP180_TEMPERATURE); + delay(5); // 5ms conversion time + int ut = i2c_read16(bmpaddr, BMP180_REG_RESULT); + int32_t x1 = (ut - (int32_t)cal_ac6) * ((int32_t)cal_ac5) >> 15; + int32_t x2 = ((int32_t)cal_mc << 11) / (x1+(int32_t)cal_md); + bmp180_b5=x1+x2; + + return ((bmp180_b5+8)>>4)/10.0; +} + +double bmp180_readPressure() +{ + int32_t p; + uint8_t msb,lsb,xlsb; + + i2c_write8(bmpaddr, BMP180_REG_CONTROL, BMP180_PRESSURE3); // Highest resolution + delay(2 + (4 << BMP180_OSS)); // 26ms conversion time at ultra high resolution + uint32_t up = i2c_read24(bmpaddr, BMP180_REG_RESULT); + up >>= (8 - BMP180_OSS); + + int32_t b6 = bmp180_b5 - 4000; + int32_t x1 = ((int32_t)cal_b2 * ( (b6 * b6)>>12 )) >> 11; + int32_t x2 = ((int32_t)cal_ac2 * b6) >> 11; + int32_t x3 = x1 + x2; + int32_t b3 = ((((int32_t)cal_ac1*4 + x3) << BMP180_OSS) + 2)>>2; + + x1 = ((int32_t)cal_ac3 * b6) >> 13; + x2 = ((int32_t)cal_b1 * ((b6 * b6) >> 12)) >> 16; + x3 = ((x1 + x2) + 2) >> 2; + uint32_t b4 = ((uint32_t)cal_ac4 * (uint32_t)(x3 + 32768)) >> 15; + uint32_t b7 = ((uint32_t)up - b3) * (uint32_t)( 50000UL >> BMP180_OSS); + + if (b7 < 0x80000000) { + p = (b7 * 2) / b4; + } else { + p = (b7 / b4) * 2; + } + + x1 = (p >> 8) * (p >> 8); + x1 = (x1 * 3038) >> 16; + x2 = (-7357 * p) >> 16; + + p += ((x1 + x2 + (int32_t)3791)>>4); + return p/100.0; // convert to mbar +} + +double bmp180_calcSealevelPressure(float pAbs, float altitude_meters) +{ + double pressure = pAbs*100.0; + return (double)(pressure / pow(1.0-altitude_meters/44330, 5.255))/100.0; +} + +/*********************************************************************************************\ + * BMP280 and BME280 + * + * Programmer : BMP280/BME280 Datasheet and Adafruit with changes by Theo Arends +\*********************************************************************************************/ + +#define BME280_REGISTER_CONTROLHUMID 0xF2 +#define BME280_REGISTER_CONTROL 0xF4 +#define BME280_REGISTER_PRESSUREDATA 0xF7 +#define BME280_REGISTER_TEMPDATA 0xFA +#define BME280_REGISTER_HUMIDDATA 0xFD + +#define BME280_REGISTER_DIG_T1 0x88 +#define BME280_REGISTER_DIG_T2 0x8A +#define BME280_REGISTER_DIG_T3 0x8C +#define BME280_REGISTER_DIG_P1 0x8E +#define BME280_REGISTER_DIG_P2 0x90 +#define BME280_REGISTER_DIG_P3 0x92 +#define BME280_REGISTER_DIG_P4 0x94 +#define BME280_REGISTER_DIG_P5 0x96 +#define BME280_REGISTER_DIG_P6 0x98 +#define BME280_REGISTER_DIG_P7 0x9A +#define BME280_REGISTER_DIG_P8 0x9C +#define BME280_REGISTER_DIG_P9 0x9E +#define BME280_REGISTER_DIG_H1 0xA1 +#define BME280_REGISTER_DIG_H2 0xE1 +#define BME280_REGISTER_DIG_H3 0xE3 +#define BME280_REGISTER_DIG_H4 0xE4 +#define BME280_REGISTER_DIG_H5 0xE5 +#define BME280_REGISTER_DIG_H6 0xE7 + +struct bme280_calib_data +{ + uint16_t dig_T1; + int16_t dig_T2; + int16_t dig_T3; + uint16_t dig_P1; + int16_t dig_P2; + int16_t dig_P3; + int16_t dig_P4; + int16_t dig_P5; + int16_t dig_P6; + int16_t dig_P7; + int16_t dig_P8; + int16_t dig_P9; + uint8_t dig_H1; + int16_t dig_H2; + uint8_t dig_H3; + int16_t dig_H4; + int16_t dig_H5; + int8_t dig_H6; +} _bme280_calib; + +int32_t t_fine; + +boolean bmp280_calibrate() +{ +// if (i2c_read8(bmpaddr, BMP_REGISTER_CHIPID) != BMP280_CHIPID) return false; + + _bme280_calib.dig_T1 = i2c_read16_LE(bmpaddr, BME280_REGISTER_DIG_T1); + _bme280_calib.dig_T2 = i2c_readS16_LE(bmpaddr, BME280_REGISTER_DIG_T2); + _bme280_calib.dig_T3 = i2c_readS16_LE(bmpaddr, BME280_REGISTER_DIG_T3); + _bme280_calib.dig_P1 = i2c_read16_LE(bmpaddr, BME280_REGISTER_DIG_P1); + _bme280_calib.dig_P2 = i2c_readS16_LE(bmpaddr, BME280_REGISTER_DIG_P2); + _bme280_calib.dig_P3 = i2c_readS16_LE(bmpaddr, BME280_REGISTER_DIG_P3); + _bme280_calib.dig_P4 = i2c_readS16_LE(bmpaddr, BME280_REGISTER_DIG_P4); + _bme280_calib.dig_P5 = i2c_readS16_LE(bmpaddr, BME280_REGISTER_DIG_P5); + _bme280_calib.dig_P6 = i2c_readS16_LE(bmpaddr, BME280_REGISTER_DIG_P6); + _bme280_calib.dig_P7 = i2c_readS16_LE(bmpaddr, BME280_REGISTER_DIG_P7); + _bme280_calib.dig_P8 = i2c_readS16_LE(bmpaddr, BME280_REGISTER_DIG_P8); + _bme280_calib.dig_P9 = i2c_readS16_LE(bmpaddr, BME280_REGISTER_DIG_P9); + +// i2c_write8(bmpaddr, BME280_REGISTER_CONTROL, 0x3F); // Temp 1x oversampling, Press 16x oversampling, normal mode (Adafruit) + i2c_write8(bmpaddr, BME280_REGISTER_CONTROL, 0xB7); // 16x oversampling, normal mode (Adafruit) + + return true; +} + +boolean bme280_calibrate() +{ +// if (i2c_read8(bmpaddr, BMP_REGISTER_CHIPID) != BME280_CHIPID) return false; + + _bme280_calib.dig_T1 = i2c_read16_LE(bmpaddr, BME280_REGISTER_DIG_T1); + _bme280_calib.dig_T2 = i2c_readS16_LE(bmpaddr, BME280_REGISTER_DIG_T2); + _bme280_calib.dig_T3 = i2c_readS16_LE(bmpaddr, BME280_REGISTER_DIG_T3); + _bme280_calib.dig_P1 = i2c_read16_LE(bmpaddr, BME280_REGISTER_DIG_P1); + _bme280_calib.dig_P2 = i2c_readS16_LE(bmpaddr, BME280_REGISTER_DIG_P2); + _bme280_calib.dig_P3 = i2c_readS16_LE(bmpaddr, BME280_REGISTER_DIG_P3); + _bme280_calib.dig_P4 = i2c_readS16_LE(bmpaddr, BME280_REGISTER_DIG_P4); + _bme280_calib.dig_P5 = i2c_readS16_LE(bmpaddr, BME280_REGISTER_DIG_P5); + _bme280_calib.dig_P6 = i2c_readS16_LE(bmpaddr, BME280_REGISTER_DIG_P6); + _bme280_calib.dig_P7 = i2c_readS16_LE(bmpaddr, BME280_REGISTER_DIG_P7); + _bme280_calib.dig_P8 = i2c_readS16_LE(bmpaddr, BME280_REGISTER_DIG_P8); + _bme280_calib.dig_P9 = i2c_readS16_LE(bmpaddr, BME280_REGISTER_DIG_P9); + _bme280_calib.dig_H1 = i2c_read8(bmpaddr, BME280_REGISTER_DIG_H1); + _bme280_calib.dig_H2 = i2c_readS16_LE(bmpaddr, BME280_REGISTER_DIG_H2); + _bme280_calib.dig_H3 = i2c_read8(bmpaddr, BME280_REGISTER_DIG_H3); + _bme280_calib.dig_H4 = (i2c_read8(bmpaddr, BME280_REGISTER_DIG_H4) << 4) | (i2c_read8(bmpaddr, BME280_REGISTER_DIG_H4 + 1) & 0xF); + _bme280_calib.dig_H5 = (i2c_read8(bmpaddr, BME280_REGISTER_DIG_H5 + 1) << 4) | (i2c_read8(bmpaddr, BME280_REGISTER_DIG_H5) >> 4); + _bme280_calib.dig_H6 = (int8_t)i2c_read8(bmpaddr, BME280_REGISTER_DIG_H6); + + // Set before CONTROL_meas (DS 5.4.3) + i2c_write8(bmpaddr, BME280_REGISTER_CONTROLHUMID, 0x05); // 16x oversampling (Adafruit) + i2c_write8(bmpaddr, BME280_REGISTER_CONTROL, 0xB7); // 16x oversampling, normal mode (Adafruit) + + return true; +} + +double bmp280_readTemperature(void) +{ + int32_t var1, var2; + + int32_t adc_T = i2c_read24(bmpaddr, BME280_REGISTER_TEMPDATA); + adc_T >>= 4; + + var1 = ((((adc_T>>3) - ((int32_t)_bme280_calib.dig_T1 <<1))) * ((int32_t)_bme280_calib.dig_T2)) >> 11; + var2 = (((((adc_T>>4) - ((int32_t)_bme280_calib.dig_T1)) * ((adc_T>>4) - ((int32_t)_bme280_calib.dig_T1))) >> 12) * + ((int32_t)_bme280_calib.dig_T3)) >> 14; + t_fine = var1 + var2; + double T = (t_fine * 5 + 128) >> 8; + return T / 100.0; +} + +double bmp280_readPressure(void) +{ + int64_t var1, var2, p; + +// Must be done first to get the t_fine variable set up +// bmp280_readTemperature(); + + int32_t adc_P = i2c_read24(bmpaddr, BME280_REGISTER_PRESSUREDATA); + adc_P >>= 4; + + var1 = ((int64_t)t_fine) - 128000; + var2 = var1 * var1 * (int64_t)_bme280_calib.dig_P6; + var2 = var2 + ((var1 * (int64_t)_bme280_calib.dig_P5) << 17); + var2 = var2 + (((int64_t)_bme280_calib.dig_P4) << 35); + var1 = ((var1 * var1 * (int64_t)_bme280_calib.dig_P3) >> 8) + ((var1 * (int64_t)_bme280_calib.dig_P2) << 12); + var1 = (((((int64_t)1) << 47) + var1)) * ((int64_t)_bme280_calib.dig_P1) >> 33; + if (var1 == 0) { + return 0; // avoid exception caused by division by zero + } + p = 1048576 - adc_P; + p = (((p << 31) - var2) * 3125) / var1; + var1 = (((int64_t)_bme280_calib.dig_P9) * (p >> 13) * (p >> 13)) >> 25; + var2 = (((int64_t)_bme280_calib.dig_P8) * p) >> 19; + p = ((p + var1 + var2) >> 8) + (((int64_t)_bme280_calib.dig_P7) << 4); + return (double)p / 25600.0; +} + +double bme280_readHumidity(void) +{ + int32_t v_x1_u32r; + +// Must be done first to get the t_fine variable set up +// bmp280_readTemperature(); + + int32_t adc_H = i2c_read16(bmpaddr, BME280_REGISTER_HUMIDDATA); + + v_x1_u32r = (t_fine - ((int32_t)76800)); + + v_x1_u32r = (((((adc_H << 14) - (((int32_t)_bme280_calib.dig_H4) << 20) - + (((int32_t)_bme280_calib.dig_H5) * v_x1_u32r)) + ((int32_t)16384)) >> 15) * + (((((((v_x1_u32r * ((int32_t)_bme280_calib.dig_H6)) >> 10) * + (((v_x1_u32r * ((int32_t)_bme280_calib.dig_H3)) >> 11) + ((int32_t)32768))) >> 10) + + ((int32_t)2097152)) * ((int32_t)_bme280_calib.dig_H2) + 8192) >> 14)); + v_x1_u32r = (v_x1_u32r - (((((v_x1_u32r >> 15) * (v_x1_u32r >> 15)) >> 7) * + ((int32_t)_bme280_calib.dig_H1)) >> 4)); + v_x1_u32r = (v_x1_u32r < 0) ? 0 : v_x1_u32r; + v_x1_u32r = (v_x1_u32r > 419430400) ? 419430400 : v_x1_u32r; + double h = (v_x1_u32r >> 12); + return h / 1024.0; +} + +/*********************************************************************************************\ + * BMP +\*********************************************************************************************/ + +double bmp_convertCtoF(double c) +{ + return c * 1.8 + 32; +} + +double bmp_readTemperature(bool S) +{ + double t = NAN; + + switch (bmptype) { + case BMP180_CHIPID: + t = bmp180_readTemperature(); + break; + case BMP280_CHIPID: + case BME280_CHIPID: + t = bmp280_readTemperature(); + } + if (!isnan(t)) { + if(S) t = bmp_convertCtoF(t); + return t; + } + return 0; +} + +double bmp_readPressure(void) +{ + switch (bmptype) { + case BMP180_CHIPID: + return bmp180_readPressure(); + case BMP280_CHIPID: + case BME280_CHIPID: + return bmp280_readPressure(); + } + return 0; +} + +double bmp_readHumidity(void) +{ + switch (bmptype) { + case BMP180_CHIPID: + case BMP280_CHIPID: + break; + case BME280_CHIPID: + return bme280_readHumidity(); + } + return 0; +} + +boolean bmp_detect() +{ + if (bmptype) return true; + + char log[LOGSZ]; + boolean success = false; + + bmpaddr = BMP_ADDR; + bmptype = i2c_read8(bmpaddr, BMP_REGISTER_CHIPID); + if (!bmptype) { + bmpaddr--; + bmptype = i2c_read8(bmpaddr, BMP_REGISTER_CHIPID); + } + snprintf_P(bmpstype, sizeof(bmpstype), PSTR("BMP")); + switch (bmptype) { + case BMP180_CHIPID: + success = bmp180_calibration(); + snprintf_P(bmpstype, sizeof(bmpstype), PSTR("BMP180")); + break; + case BMP280_CHIPID: + success = bmp280_calibrate(); + snprintf_P(bmpstype, sizeof(bmpstype), PSTR("BMP280")); + break; + case BME280_CHIPID: + success = bme280_calibrate(); + snprintf_P(bmpstype, sizeof(bmpstype), PSTR("BME280")); + } + if (success) { + snprintf_P(log, sizeof(log), PSTR("I2C: %s found at address 0x%x"), bmpstype, bmpaddr); + addLog(LOG_LEVEL_DEBUG, log); + } else { + bmptype = 0; + } + return success; +} + +/*********************************************************************************************\ + * Presentation +\*********************************************************************************************/ + +void bmp_mqttPresent(char* svalue, uint16_t ssvalue, uint8_t* djson, uint8_t domidx) +{ + if (!bmptype) return; + + char stemp1[10], stemp2[10], stemp3[10]; + + double t = bmp_readTemperature(TEMP_CONVERSION); + double p = bmp_readPressure(); + double h = bmp_readHumidity(); + dtostrf(t, 1, TEMP_RESOLUTION &3, stemp1); + dtostrf(p, 1, PRESSURE_RESOLUTION &3, stemp2); + dtostrf(h, 1, HUMIDITY_RESOLUTION &3, stemp3); + if (!strcmp(bmpstype,"BME280")) { + snprintf_P(svalue, ssvalue, PSTR("%s, \"%s\":{\"Temperature\":\"%s\", \"Humidity\":\"%s\", \"Pressure\":\"%s\"}"), + svalue, bmpstype, stemp1, stemp3, stemp2); + } else { + snprintf_P(svalue, ssvalue, PSTR("%s, \"%s\":{\"Temperature\":\"%s\", \"Pressure\":\"%s\"}"), + svalue, bmpstype, stemp1, stemp2); + } + *djson = 1; +#ifdef USE_DOMOTICZ + domoticz_sensor3(stemp1, stemp3, stemp2); +#endif // USE_DOMOTICZ +} + +#ifdef USE_WEBSERVER +String bmp_webPresent() +{ + String page = ""; + if (bmptype) { + char itemp[10], iconv[10]; + + snprintf_P(iconv, sizeof(iconv), PSTR("°%c"), (TEMP_CONVERSION) ? 'F' : 'C'); + double t_bmp = bmp_readTemperature(TEMP_CONVERSION); + double p_bmp = bmp_readPressure(); + double h_bmp = bmp_readHumidity(); + dtostrf(t_bmp, 1, TEMP_RESOLUTION &3, itemp); + page += F("BMP Temperature: "); page += itemp; page += iconv; page += F(""); + if (!strcmp(bmpstype,"BME280")) { + dtostrf(h_bmp, 1, HUMIDITY_RESOLUTION &3, itemp); + page += F("BMP Humidity: "); page += itemp; page += F("%"); + } + dtostrf(p_bmp, 1, PRESSURE_RESOLUTION &3, itemp); + page += F("BMP Pressure: "); page += itemp; page += F(" hPa"); + } + return page; +} +#endif // USE_WEBSERVER +#endif // USE_BMP +#endif // USE_I2C + diff --git a/sonoff/xsns_dht.ino b/sonoff/xsns_dht.ino new file mode 100644 index 000000000..4809c83ac --- /dev/null +++ b/sonoff/xsns_dht.ino @@ -0,0 +1,214 @@ +/* +Copyright (c) 2017 Theo Arends. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +- Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. +- Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifdef USE_DHT +/*********************************************************************************************\ + * DHT11, DHT21 (AM2301), DHT22 (AM2302, AM2321) - Temperature and Humidy + * + * Reading temperature or humidity takes about 250 milliseconds! + * Sensor readings may also be up to 2 seconds 'old' (its a very slow sensor) + * Source: Adafruit Industries https://github.com/adafruit/DHT-sensor-library +\*********************************************************************************************/ + +#define MIN_INTERVAL 2000 + +uint8_t data[5]; +uint32_t _lastreadtime, _maxcycles; +bool _lastresult; +float mt, mh = 0; + +void dht_readPrep() +{ + digitalWrite(pin[GPIO_DHT11], HIGH); +} + +uint32_t dht_expectPulse(bool level) +{ + uint32_t count = 0; + + while (digitalRead(pin[GPIO_DHT11]) == level) + if (count++ >= _maxcycles) return 0; + return count; +} + +boolean dht_read() +{ + char log[LOGSZ]; + uint32_t cycles[80]; + uint32_t currenttime = millis(); + + if ((currenttime - _lastreadtime) < 2000) { + return _lastresult; + } + _lastreadtime = currenttime; + + data[0] = data[1] = data[2] = data[3] = data[4] = 0; + +// digitalWrite(pin[GPIO_DHT11], HIGH); +// delay(250); + + pinMode(pin[GPIO_DHT11], OUTPUT); + digitalWrite(pin[GPIO_DHT11], LOW); + delay(20); + + noInterrupts(); + digitalWrite(pin[GPIO_DHT11], HIGH); + delayMicroseconds(40); + pinMode(pin[GPIO_DHT11], INPUT_PULLUP); + delayMicroseconds(10); + if (dht_expectPulse(LOW) == 0) { + addLog_P(LOG_LEVEL_DEBUG, PSTR("DHT: Timeout waiting for start signal low pulse")); + _lastresult = false; + return _lastresult; + } + if (dht_expectPulse(HIGH) == 0) { + addLog_P(LOG_LEVEL_DEBUG, PSTR("DHT: Timeout waiting for start signal high pulse")); + _lastresult = false; + return _lastresult; + } + for (int i=0; i<80; i+=2) { + cycles[i] = dht_expectPulse(LOW); + cycles[i+1] = dht_expectPulse(HIGH); + } + interrupts(); + + for (int i=0; i<40; ++i) { + uint32_t lowCycles = cycles[2*i]; + uint32_t highCycles = cycles[2*i+1]; + if ((lowCycles == 0) || (highCycles == 0)) { + addLog_P(LOG_LEVEL_DEBUG, PSTR("DHT: Timeout waiting for pulse")); + _lastresult = false; + return _lastresult; + } + data[i/8] <<= 1; + if (highCycles > lowCycles) data[i/8] |= 1; + } + + snprintf_P(log, sizeof(log), PSTR("DHT: Received %02X, %02X, %02X, %02X, %02X =? %02X"), + data[0], data[1], data[2], data[3], data[4], (data[0] + data[1] + data[2] + data[3]) & 0xFF); + addLog(LOG_LEVEL_DEBUG, log); + + if (data[4] == ((data[0] + data[1] + data[2] + data[3]) & 0xFF)) { + _lastresult = true; + return _lastresult; + } else { + addLog_P(LOG_LEVEL_DEBUG, PSTR("DHT: Checksum failure")); + _lastresult = false; + return _lastresult; + } +} + +float dht_convertCtoF(float c) +{ + return c * 1.8 + 32; +} + +boolean dht_readTempHum(bool S, float &t, float &h) +{ + if (!mh) { + t = NAN; + h = NAN; + } else { + t = mt; + h = mh; + } + + if (dht_read()) { + switch (dht_type) { + case DHT11: + h = data[0]; + t = data[2]; + if(S) t = dht_convertCtoF(t); + break; + case DHT22: + case DHT21: + h = data[0]; + h *= 256; + h += data[1]; + h *= 0.1; + t = data[2] & 0x7F; + t *= 256; + t += data[3]; + t *= 0.1; + if (data[2] & 0x80) t *= -1; + if(S) t = dht_convertCtoF(t); + break; + } + if (!isnan(t)) mt = t; + if (!isnan(h)) mh = h; + } + return (!isnan(t) && !isnan(h)); +} + +void dht_init() +{ + char log[LOGSZ]; + _maxcycles = microsecondsToClockCycles(1000); // 1 millisecond timeout for + // reading pulses from DHT sensor. + pinMode(pin[GPIO_DHT11], INPUT_PULLUP); + _lastreadtime = -MIN_INTERVAL; + + snprintf_P(log, sizeof(log), PSTR("DHT: Max clock cycles %d"), _maxcycles); + addLog(LOG_LEVEL_DEBUG, log); +} + +/*********************************************************************************************\ + * Presentation +\*********************************************************************************************/ + +void dht_mqttPresent(char* svalue, uint16_t ssvalue, uint8_t* djson, uint8_t domidx) +{ + char stemp1[10], stemp2[10]; + float t, h; + + if (dht_readTempHum(TEMP_CONVERSION, t, h)) { // Read temperature + dtostrf(t, 1, TEMP_RESOLUTION &3, stemp1); + dtostrf(h, 1, HUMIDITY_RESOLUTION &3, stemp2); + snprintf_P(svalue, ssvalue, PSTR("%s, \"DHT\":{\"Temperature\":\"%s\", \"Humidity\":\"%s\"}"), svalue, stemp1, stemp2); + *djson = 1; +#ifdef USE_DOMOTICZ + domoticz_sensor2(stemp1, stemp2); +#endif // USE_DOMOTICZ + } +} + +#ifdef USE_WEBSERVER +String dht_webPresent() +{ + char stemp[10], sconv[10]; + float t, h; + String page = ""; + + if (dht_readTempHum(TEMP_CONVERSION, t, h)) { // Read temperature as Celsius (the default) + snprintf_P(sconv, sizeof(sconv), PSTR("°%c"), (TEMP_CONVERSION) ? 'F' : 'C'); + dtostrf(t, 1, TEMP_RESOLUTION &3, stemp); + page += F("DHT Temperature: "); page += stemp; page += sconv; page += F(""); + dtostrf(h, 1, HUMIDITY_RESOLUTION &3, stemp); + page += F("DHT Humidity: "); page += stemp; page += F("%"); + } + return page; +} +#endif // USE_WEBSERVER +#endif // USE_DHT diff --git a/sonoff/xsns_dht2.ino b/sonoff/xsns_dht2.ino new file mode 100644 index 000000000..9239182a8 --- /dev/null +++ b/sonoff/xsns_dht2.ino @@ -0,0 +1,93 @@ +/* +Copyright (c) 2017 Theo Arends. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +- Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. +- Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifdef USE_DHT2 +/*********************************************************************************************\ + * DHT11, DHT21 (AM2301), DHT22 (AM2302, AM2321) - Temperature and Humidy + * + * Reading temperature or humidity takes about 250 milliseconds! + * Sensor readings may also be up to 2 seconds 'old' (its a very slow sensor) +\*********************************************************************************************/ + +// WARNING: To use this DHT library you'll need to delete files DHT_U.cpp and DHT_U.h if present + +#include "DHT.h" + +DHT dht2(pin[GPIO_DHT11], dht_type); + +float dht2_t, dht2_h = 0; + +boolean dht_readTempHum(bool S, float &t, float &h) +{ + h = dht2.readHumidity(); + t = dht2.readTemperature(S); + if (!isnan(t)) dht2_t = t; else if (dht2_h) t = dht2_t; + if (!isnan(h)) dht2_h = h; else if (dht2_h) h = dht2_h; + return (!isnan(t) && !isnan(h)); +} + +void dht_init() +{ + dht2.begin(); +} + +/*********************************************************************************************\ + * Presentation +\*********************************************************************************************/ + +void dht_mqttPresent(char* svalue, uint16_t ssvalue, uint8_t* djson, uint8_t domidx) +{ + char stemp1[10], stemp2[10]; + float t, h; + + if (dht_readTempHum(TEMP_CONVERSION, t, h)) { // Read temperature + dtostrf(t, 1, TEMP_RESOLUTION &3, stemp1); + dtostrf(h, 1, HUMIDITY_RESOLUTION &3, stemp2); + snprintf_P(svalue, ssvalue, PSTR("%s, \"DHT\":{\"Temperature\":\"%s\", \"Humidity\":\"%s\"}"), svalue, stemp1, stemp2); + *djson = 1; +#ifdef USE_DOMOTICZ + domoticz_sensor2(stemp1, stemp2); +#endif // USE_DOMOTICZ + } +} + +#ifdef USE_WEBSERVER +String dht_webPresent() +{ + char stemp[10], sconv[10]; + float t, h; + String page = ""; + + if (dht_readTempHum(TEMP_CONVERSION, t, h)) { // Read temperature as Celsius (the default) + snprintf_P(sconv, sizeof(sconv), PSTR("°%c"), (TEMP_CONVERSION) ? 'F' : 'C'); + dtostrf(t, 1, TEMP_RESOLUTION &3, stemp); + page += F("DHT Temperature: "); page += stemp; page += sconv; page += F(""); + dtostrf(h, 1, HUMIDITY_RESOLUTION &3, stemp); + page += F("DHT Humidity: "); page += stemp; page += F("%"); + } + return page; +} +#endif // USE_WEBSERVER +#endif // USE_DHT2 diff --git a/sonoff/xsns_ds18b20.ino b/sonoff/xsns_ds18b20.ino new file mode 100644 index 000000000..eec7d5cb2 --- /dev/null +++ b/sonoff/xsns_ds18b20.ino @@ -0,0 +1,206 @@ +/* +Copyright (c) 2017 Theo Arends. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +- Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. +- Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifdef USE_DS18B20 +/*********************************************************************************************\ + * DS18B20 - Temperature + * + * Source: Marinus vd Broek https://github.com/ESP8266nu/ESPEasy and AlexTransit (CRC) +\*********************************************************************************************/ + +uint8_t dsb_reset() +{ + uint8_t r; + uint8_t retries = 125; + + pinMode(pin[GPIO_DSB], INPUT); + do { // wait until the wire is high... just in case + if (--retries == 0) return 0; + delayMicroseconds(2); + } while (!digitalRead(pin[GPIO_DSB])); + pinMode(pin[GPIO_DSB], OUTPUT); + digitalWrite(pin[GPIO_DSB], LOW); + delayMicroseconds(492); // Dallas spec. = Min. 480uSec. Arduino 500uSec. + pinMode(pin[GPIO_DSB], INPUT); // Float + delayMicroseconds(40); + r = !digitalRead(pin[GPIO_DSB]); + delayMicroseconds(420); + return r; +} + +uint8_t dsb_read_bit(void) +{ + uint8_t r; + + pinMode(pin[GPIO_DSB], OUTPUT); + digitalWrite(pin[GPIO_DSB], LOW); + delayMicroseconds(3); + pinMode(pin[GPIO_DSB], INPUT); // let pin float, pull up will raise + delayMicroseconds(10); + r = digitalRead(pin[GPIO_DSB]); + delayMicroseconds(53); + return r; +} + +uint8_t dsb_read(void) +{ + uint8_t bitMask; + uint8_t r = 0; + + for (bitMask = 0x01; bitMask; bitMask <<= 1) + if (dsb_read_bit()) r |= bitMask; + return r; +} + +void dsb_write_bit(uint8_t v) +{ + if (v & 1) { + digitalWrite(pin[GPIO_DSB], LOW); + pinMode(pin[GPIO_DSB], OUTPUT); + delayMicroseconds(10); + digitalWrite(pin[GPIO_DSB], HIGH); + delayMicroseconds(55); + } else { + digitalWrite(pin[GPIO_DSB], LOW); + pinMode(pin[GPIO_DSB], OUTPUT); + delayMicroseconds(65); + digitalWrite(pin[GPIO_DSB], HIGH); + delayMicroseconds(5); + } +} + +void dsb_write(uint8_t ByteToWrite) +{ + uint8_t bitMask; + + for (bitMask = 0x01; bitMask; bitMask <<= 1) + dsb_write_bit((bitMask & ByteToWrite) ? 1 : 0); +} + +uint8 dsb_crc(uint8 inp, uint8 crc) +{ + inp ^= crc; + crc = 0; + if (inp & 0x1) crc ^= 0x5e; + if (inp & 0x2) crc ^= 0xbc; + if (inp & 0x4) crc ^= 0x61; + if (inp & 0x8) crc ^= 0xc2; + if (inp & 0x10) crc ^= 0x9d; + if (inp & 0x20) crc ^= 0x23; + if (inp & 0x40) crc ^= 0x46; + if (inp & 0x80) crc ^= 0x8c; + return crc; +} + +void dsb_readTempPrep() +{ + dsb_reset(); + dsb_write(0xCC); // Skip ROM + dsb_write(0x44); // Start conversion +} + +float dsb_convertCtoF(float c) +{ + return c * 1.8 + 32; +} + +boolean dsb_readTemp(bool S, float &t) +{ + int16_t DSTemp; + byte msb, lsb, crc; + + t = NAN; + + if (!dsb_read_bit()) { //check measurement end + addLog_P(LOG_LEVEL_DEBUG, PSTR("DSB: Sensor busy")); + return false; + } +/* + dsb_reset(); + dsb_write(0xCC); // Skip ROM + dsb_write(0x44); // Start conversion + delay(800); +*/ + dsb_reset(); + dsb_write(0xCC); // Skip ROM + dsb_write(0xBE); // Read scratchpad + lsb = dsb_read(); + msb = dsb_read(); + crc = dsb_crc(lsb, crc); + crc = dsb_crc(msb, crc); + crc = dsb_crc(dsb_read(), crc); + crc = dsb_crc(dsb_read(), crc); + crc = dsb_crc(dsb_read(), crc); + crc = dsb_crc(dsb_read(), crc); + crc = dsb_crc(dsb_read(), crc); + crc = dsb_crc(dsb_read(), crc); + crc = dsb_crc(dsb_read(), crc); + dsb_reset(); + if (crc) { //check crc + addLog_P(LOG_LEVEL_DEBUG, PSTR("DSB: Sensor CRC error")); + } else { + DSTemp = (msb << 8) + lsb; + t = (float(DSTemp) * 0.0625); + if(S) t = dsb_convertCtoF(t); + } + return !isnan(t); +} + +/*********************************************************************************************\ + * Presentation +\*********************************************************************************************/ + +void dsb_mqttPresent(char* svalue, uint16_t ssvalue, uint8_t* djson, uint8_t domidx) +{ + char stemp1[10]; + float t; + + if (dsb_readTemp(TEMP_CONVERSION, t)) { // Check if read failed + dtostrf(t, 1, TEMP_RESOLUTION &3, stemp1); + snprintf_P(svalue, ssvalue, PSTR("%s, \"DS18B20\":{\"Temperature\":\"%s\"}"), svalue, stemp1); + *djson = 1; +#ifdef USE_DOMOTICZ + domoticz_sensor1(stemp1); +#endif // USE_DOMOTICZ + } +} + +#ifdef USE_WEBSERVER +String dsb_webPresent() +{ + // Needs TelePeriod to refresh data (Do not do it here as it takes too much time) + char stemp[10], sconv[10]; + float st; + String page = ""; + + if (dsb_readTemp(TEMP_CONVERSION, st)) { // Check if read failed + snprintf_P(sconv, sizeof(sconv), PSTR("°%c"), (TEMP_CONVERSION) ? 'F' : 'C'); + dtostrf(st, 1, TEMP_RESOLUTION &3, stemp); + page += F("DSB Temperature: "); page += stemp; page += sconv; page += F(""); + } + return page; +} +#endif // USE_WEBSERVER +#endif // USE_DS18B20 diff --git a/sonoff/xsns_ds18x20.ino b/sonoff/xsns_ds18x20.ino new file mode 100644 index 000000000..1205d76ea --- /dev/null +++ b/sonoff/xsns_ds18x20.ino @@ -0,0 +1,201 @@ +/* +Copyright (c) 2017 Theo Arends. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +- Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. +- Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifdef USE_DS18x20 +/*********************************************************************************************\ + * DS18B20 - Temperature +\*********************************************************************************************/ + +#define W1_SKIP_ROM 0xCC +#define W1_CONVERT_TEMP 0x44 +#define W1_READ_SCRATCHPAD 0xBE + +#define DS18X20_MAX_SENSORS 8 + +#include + +OneWire ds(pin[GPIO_DSB]); + +uint8_t ds18x20_addr[DS18X20_MAX_SENSORS][8]; +uint8_t ds18x20_idx[DS18X20_MAX_SENSORS]; +uint8_t ds18x20_snsrs = 0; + +void ds18x20_search() +{ + uint8_t num_sensors=0; + uint8_t sensor = 0; + uint8_t i; + + ds.reset_search(); + for (num_sensors = 0; num_sensors < DS18X20_MAX_SENSORS; num_sensors) + { + if (!ds.search(ds18x20_addr[num_sensors])) { + ds.reset_search(); + break; + } + // If CRC Ok and Type DS18S20 or DS18B20 + if ((OneWire::crc8(ds18x20_addr[num_sensors], 7) == ds18x20_addr[num_sensors][7]) && + ((ds18x20_addr[num_sensors][0]==0x10) || (ds18x20_addr[num_sensors][0]==0x28))) + num_sensors++; + } + for (int i = 0; i < num_sensors; i++) ds18x20_idx[i] = i; + for (int i = 0; i < num_sensors; i++) { + for (int j = i + 1; j < num_sensors; j++) { + if (uint32_t(ds18x20_addr[ds18x20_idx[i]]) > uint32_t(ds18x20_addr[ds18x20_idx[j]])) { + std::swap(ds18x20_idx[i], ds18x20_idx[j]); + } + } + } + ds18x20_snsrs = num_sensors; +} + +uint8_t ds18x20_sensors() +{ + return ds18x20_snsrs; +} + +String ds18x20_address(uint8_t sensor) +{ + char addrStr[20]; + uint8_t i; + + for (i = 0; i < 8; i++) sprintf(addrStr+2*i, "%02X", ds18x20_addr[ds18x20_idx[sensor]][i]); + return String(addrStr); +} + +String ds18x20_type(uint8_t sensor) +{ + char typeStr[10]; + + switch(ds18x20_addr[ds18x20_idx[sensor]][0]) { + case 0x10: + strcpy(typeStr, "DS18S20"); + break; + case 0x28: + strcpy(typeStr, "DS18B20"); + break; + default: + strcpy(typeStr, "DS18x20"); + } + return String(typeStr); +} + +void ds18x20_convert() +{ + ds.reset(); + ds.write(W1_SKIP_ROM); // Address all Sensors on Bus + ds.write(W1_CONVERT_TEMP); // start conversion, no parasite power on at the end +// delay(750); // 750ms should be enough for 12bit conv +} + +float ds18x20_convertCtoF(float c) +{ + return c * 1.8 + 32; +} + +boolean ds18x20_read(uint8_t sensor, bool S, float &t) +{ + byte data[12]; + uint8_t sign = 1; + uint8_t i = 0; + float temp9 = 0.0; + uint8_t present = 0; + + t = NAN; + + ds.reset(); + ds.select(ds18x20_addr[ds18x20_idx[sensor]]); + ds.write(W1_READ_SCRATCHPAD); // Read Scratchpad + + for (i = 0; i < 9; i++) data[i] = ds.read(); + if (OneWire::crc8(data, 8) == data[8]) { + switch(ds18x20_addr[ds18x20_idx[sensor]][0]) { + case 0x10: // DS18S20 + if (data[1] > 0x80) sign = -1; // App-Note fix possible sign error + if (data[0] & 1) { + temp9 = ((data[0] >> 1) + 0.5) * sign; + } else { + temp9 = (data[0] >> 1) * sign; + } + t = (temp9 - 0.25) + ((16.0 - data[6]) / 16.0); + if(S) t = ds18x20_convertCtoF(t); + break; + case 0x28: // DS18B20 + t = ((data[1] << 8) + data[0]) * 0.0625; + if(S) t = ds18x20_convertCtoF(t); + break; + } + } + return (!isnan(t)); +} + +/*********************************************************************************************\ + * Presentation +\*********************************************************************************************/ + +void ds18x20_mqttPresent(char* svalue, uint16_t ssvalue, uint8_t* djson, uint8_t domidx) +{ + char stemp1[10], stemp2[10]; + float t; + + byte dsxflg = 0; + for (byte i = 0; i < ds18x20_sensors(); i++) { + if (ds18x20_read(i, TEMP_CONVERSION, t)) { // Check if read failed + dtostrf(t, 1, TEMP_RESOLUTION &3, stemp2); + if (!dsxflg) { + snprintf_P(svalue, ssvalue, PSTR("%s, \"DS18x20\":{"), svalue); + *djson = 1; + stemp1[0] = '\0'; + } + dsxflg++; + snprintf_P(svalue, ssvalue, PSTR("%s%s\"DS%d\":{\"Type\":\"%s\", \"Address\":\"%s\", \"Temperature\":\"%s\"}"), + svalue, stemp1, i +1, ds18x20_type(i).c_str(), ds18x20_address(i).c_str(), stemp2); + strcpy(stemp1, ", "); +#ifdef USE_DOMOTICZ + if (dsxflg == 1) domoticz_sensor1(stemp2); +#endif // USE_DOMOTICZ + } + } + if (dsxflg) snprintf_P(svalue, ssvalue, PSTR("%s}"), svalue); +} + +#ifdef USE_WEBSERVER +String ds18x20_webPresent() +{ + char stemp[10], sconv[10]; + float t; + String page = ""; + + snprintf_P(sconv, sizeof(sconv), PSTR("°%c"), (TEMP_CONVERSION) ? 'F' : 'C'); + for (byte i = 0; i < ds18x20_sensors(); i++) { + if (ds18x20_read(i, TEMP_CONVERSION, t)) { // Check if read failed + dtostrf(t, 1, TEMP_RESOLUTION &3, stemp); + page += F("DS"); page += String(i +1); page += F(" Temperature: "); page += stemp; page += sconv; page += F(""); + } + } + return page; +} +#endif // USE_WEBSERVER +#endif // USE_DS18x20 diff --git a/sonoff/xsns_hlw8012.ino b/sonoff/xsns_hlw8012.ino new file mode 100644 index 000000000..de4f0e8cb --- /dev/null +++ b/sonoff/xsns_hlw8012.ino @@ -0,0 +1,412 @@ +/* +Copyright (c) 2017 Theo Arends. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +- Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. +- Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. +*/ + +/*********************************************************************************************\ + * HLW8012 - Energy + * + * Based on Source: Shenzhen Heli Technology Co., Ltd +\*********************************************************************************************/ + +#define HLW_PREF 10000 // 1000.0W +#define HLW_UREF 2200 // 220.0V +#define HLW_IREF 4545 // 4.545A + +byte hlw_SELflag, hlw_cf_timer, hlw_cf1_timer, hlw_fifth_second, hlw_startup; +unsigned long hlw_cf_plen, hlw_cf_last; +unsigned long hlw_cf1_plen, hlw_cf1_last, hlw_cf1_ptot, hlw_cf1_pcnt, hlw_cf1u_plen, hlw_cf1i_plen; +unsigned long hlw_Ecntr, hlw_EDcntr, hlw_kWhtoday; +uint32_t hlw_lasttime; + +unsigned long hlw_cf1u_pcntmax, hlw_cf1i_pcntmax; + +Ticker tickerHLW; + +#ifndef USE_WS2812_DMA // Collides with Neopixelbus but solves exception +void hlw_cf_interrupt() ICACHE_RAM_ATTR; +void hlw_cf1_interrupt() ICACHE_RAM_ATTR; +#endif // USE_WS2812_DMA + +void hlw_cf_interrupt() // Service Power +{ + hlw_cf_plen = micros() - hlw_cf_last; + hlw_cf_last = micros(); + if (hlw_cf_plen > 4000000) hlw_cf_plen = 0; // Just powered on + hlw_cf_timer = 15; // Support down to 4W which takes about 3 seconds + hlw_EDcntr++; + hlw_Ecntr++; +} + +void hlw_cf1_interrupt() // Service Voltage and Current +{ + hlw_cf1_plen = micros() - hlw_cf1_last; + hlw_cf1_last = micros(); + if ((hlw_cf1_timer > 2) && (hlw_cf1_timer < 8)) { // Allow for 300 mSec set-up time and measure for up to 1 second + hlw_cf1_ptot += hlw_cf1_plen; + hlw_cf1_pcnt++; + if (hlw_cf1_pcnt == 10) hlw_cf1_timer = 8; // We need up to ten samples within 1 second (low current could take up to 0.3 second) + } +} + +void hlw_200mS() +{ + unsigned long hlw_len, hlw_temp; + + hlw_fifth_second++; + if (hlw_fifth_second == 5) { + hlw_fifth_second = 0; + + if (hlw_EDcntr) { + hlw_len = 1000000 / hlw_EDcntr; + hlw_EDcntr = 0; + hlw_temp = (HLW_PREF * sysCfg.hlw_pcal) / hlw_len; + hlw_kWhtoday += (hlw_temp * 100) / 36; + } + if (rtc_loctime() == rtc_midnight()) { + sysCfg.hlw_kWhyesterday = hlw_kWhtoday; + hlw_kWhtoday = 0; + } + if (hlw_startup && rtcTime.Valid && (rtcTime.DayOfYear == sysCfg.hlw_kWhdoy)) { + hlw_kWhtoday = sysCfg.hlw_kWhtoday; + hlw_startup = 0; + } + } + + if (hlw_cf_timer) { + hlw_cf_timer--; + if (!hlw_cf_timer) hlw_cf_plen = 0; // No load for over three seconds + } + + hlw_cf1_timer++; + if (hlw_cf1_timer >= 8) { + hlw_cf1_timer = 0; + hlw_SELflag = (hlw_SELflag) ? 0 : 1; + digitalWrite(pin[GPIO_HLW_SEL], hlw_SELflag); + + if (hlw_cf1_pcnt) { + hlw_cf1_plen = hlw_cf1_ptot / hlw_cf1_pcnt; + } else { + hlw_cf1_plen = 0; + } + if (hlw_SELflag) { + hlw_cf1u_plen = hlw_cf1_plen; + hlw_cf1u_pcntmax = hlw_cf1_pcnt; + } else { + hlw_cf1i_plen = hlw_cf1_plen; + hlw_cf1i_pcntmax = hlw_cf1_pcnt; + } + hlw_cf1_ptot = 0; + hlw_cf1_pcnt = 0; + } +} + +void hlw_savestate() +{ + sysCfg.hlw_kWhdoy = (rtcTime.Valid) ? rtcTime.DayOfYear : 0; + sysCfg.hlw_kWhtoday = hlw_kWhtoday; +} + +boolean hlw_readEnergy(byte option, float &ed, uint16_t &e, uint16_t &w, uint16_t &u, float &i, float &c) +{ + unsigned long hlw_len, hlw_temp, hlw_w, hlw_u, hlw_i; + int hlw_period, hlw_interval; + +//char log[LOGSZ]; +//snprintf_P(log, sizeof(log), PSTR("HLW: CF %d, CF1U %d (%d), CF1I %d (%d)"), hlw_cf_plen, hlw_cf1u_plen, hlw_cf1u_pcntmax, hlw_cf1i_plen, hlw_cf1i_pcntmax); +//addLog(LOG_LEVEL_DEBUG, log); + + if (hlw_kWhtoday) { + ed = (float)hlw_kWhtoday / 100000000; + } else { + ed = 0; + } + + if (option) { + if (!hlw_lasttime) { + hlw_period = sysCfg.tele_period; + } else { + hlw_period = rtc_loctime() - hlw_lasttime; + } + hlw_lasttime = rtc_loctime(); + hlw_interval = 3600 / hlw_period; + if (hlw_Ecntr) { + hlw_len = hlw_period * 1000000 / hlw_Ecntr; + hlw_Ecntr = 0; + hlw_temp = ((HLW_PREF * sysCfg.hlw_pcal) / hlw_len) / hlw_interval; + e = hlw_temp / 10; + } else { + e = 0; + } + } + + if (hlw_cf_plen) { + hlw_w = (HLW_PREF * sysCfg.hlw_pcal) / hlw_cf_plen; + w = hlw_w / 10; + } else { + w = 0; + } + if (hlw_cf1u_plen && (w || (power &1))) { + hlw_u = (HLW_UREF * sysCfg.hlw_ucal) / hlw_cf1u_plen; + u = hlw_u / 10; + } else { + u = 0; + } + if (hlw_cf1i_plen && w) { + hlw_i = (HLW_IREF * sysCfg.hlw_ical) / hlw_cf1i_plen; + i = (float)hlw_i / 1000; + } else { + i = 0; + } + if (hlw_i && hlw_u && hlw_w && w) { + hlw_temp = (hlw_w * 100) / ((hlw_u * hlw_i) / 1000); + if (hlw_temp > 100) { + hlw_temp = 100; + } + c = (float)hlw_temp / 100; + } else { + c = 0; + } + + return true; +} + +void hlw_init() +{ + if (!sysCfg.hlw_pcal || (sysCfg.hlw_pcal == 4975)) { + sysCfg.hlw_pcal = HLW_PREF_PULSE; + sysCfg.hlw_ucal = HLW_UREF_PULSE; + sysCfg.hlw_ical = HLW_IREF_PULSE; + } + + hlw_cf_plen = 0; + hlw_cf_last = 0; + hlw_cf1_plen = 0; + hlw_cf1_last = 0; + hlw_cf1u_plen = 0; + hlw_cf1i_plen = 0; + hlw_cf1u_pcntmax = 0; + hlw_cf1i_pcntmax = 0; + + hlw_Ecntr = 0; + hlw_EDcntr = 0; + hlw_kWhtoday = 0; + + hlw_SELflag = 0; // Voltage; + + pinMode(pin[GPIO_HLW_SEL], OUTPUT); + digitalWrite(pin[GPIO_HLW_SEL], hlw_SELflag); + pinMode(pin[GPIO_HLW_CF1], INPUT_PULLUP); + attachInterrupt(pin[GPIO_HLW_CF1], hlw_cf1_interrupt, FALLING); + pinMode(pin[GPIO_HLW_CF], INPUT_PULLUP); + attachInterrupt(pin[GPIO_HLW_CF], hlw_cf_interrupt, FALLING); + + hlw_startup = 1; + hlw_lasttime = 0; + hlw_fifth_second = 0; + hlw_cf_timer = 0; + hlw_cf1_timer = 0; + tickerHLW.attach_ms(200, hlw_200mS); +} + +/********************************************************************************************/ + +boolean hlw_margin(byte type, uint16_t margin, uint16_t value, byte &flag, byte &saveflag) +{ + byte change; + + if (!margin) return false; + change = saveflag; + if (type) { + flag = (value > margin); + } else { + flag = (value < margin); + } + saveflag = flag; + return (change != saveflag); +} + +void hlw_margin_chk() +{ + char log[LOGSZ], stopic[TOPSZ], svalue[MESSZ]; + float ped, pi, pc; + uint16_t uped, piv, pe, pw, pu; + byte flag, jsonflg; + + if (power_steady_cntr) { + power_steady_cntr--; + return; + } + + hlw_readEnergy(0, ped, pe, pw, pu, pi, pc); + if (power && (sysCfg.hlw_pmin || sysCfg.hlw_pmax || sysCfg.hlw_umin || sysCfg.hlw_umax || sysCfg.hlw_imin || sysCfg.hlw_imax)) { + piv = (uint16_t)(pi * 1000); + +// snprintf_P(log, sizeof(log), PSTR("HLW: W %d, U %d, I %d"), pw, pu, piv); +// addLog(LOG_LEVEL_DEBUG, log); + + snprintf_P(stopic, sizeof(stopic), PSTR("%s/%s/TELEMETRY"), PUB_PREFIX2, sysCfg.mqtt_topic); + snprintf_P(svalue, sizeof(svalue), PSTR("{")); + jsonflg = 0; + if (hlw_margin(0, sysCfg.hlw_pmin, pw, flag, hlw_pminflg)) { + snprintf_P(svalue, sizeof(svalue), PSTR("%s%s\"PowerLow\":\"%s\""), svalue, (jsonflg)?", ":"", (flag) ? MQTT_STATUS_ON : MQTT_STATUS_OFF); + jsonflg = 1; + } + if (hlw_margin(1, sysCfg.hlw_pmax, pw, flag, hlw_pmaxflg)) { + snprintf_P(svalue, sizeof(svalue), PSTR("%s%s\"PowerHigh\":\"%s\""), svalue, (jsonflg)?", ":"", (flag) ? MQTT_STATUS_ON : MQTT_STATUS_OFF); + jsonflg = 1; + } + if (hlw_margin(0, sysCfg.hlw_umin, pu, flag, hlw_uminflg)) { + snprintf_P(svalue, sizeof(svalue), PSTR("%s%s\"VoltageLow\":\"%s\""), svalue, (jsonflg)?", ":"", (flag) ? MQTT_STATUS_ON : MQTT_STATUS_OFF); + jsonflg = 1; + } + if (hlw_margin(1, sysCfg.hlw_umax, pw, flag, hlw_umaxflg)) { + snprintf_P(svalue, sizeof(svalue), PSTR("%s%s\"VoltageHigh\":\"%s\""), svalue, (jsonflg)?", ":"", (flag) ? MQTT_STATUS_ON : MQTT_STATUS_OFF); + jsonflg = 1; + } + if (hlw_margin(0, sysCfg.hlw_imin, piv, flag, hlw_iminflg)) { + snprintf_P(svalue, sizeof(svalue), PSTR("%s%s\"CurrentLow\":\"%s\""), svalue, (jsonflg)?", ":"", (flag) ? MQTT_STATUS_ON : MQTT_STATUS_OFF); + jsonflg = 1; + } + if (hlw_margin(1, sysCfg.hlw_imax, piv, flag, hlw_imaxflg)) { + snprintf_P(svalue, sizeof(svalue), PSTR("%s%s\"CurrentHigh\":\"%s\""), svalue, (jsonflg)?", ":"", (flag) ? MQTT_STATUS_ON : MQTT_STATUS_OFF); + jsonflg = 1; + } + if (jsonflg) { + snprintf_P(svalue, sizeof(svalue), PSTR("%s}"), svalue); + mqtt_publish(stopic, svalue); + } + } + +#ifdef FEATURE_POWER_LIMIT + snprintf_P(stopic, sizeof(stopic), PSTR("%s/%s/RESULT"), PUB_PREFIX, sysCfg.mqtt_topic); + // Max Power + if (sysCfg.hlw_mpl) { + if (pw > sysCfg.hlw_mpl) { + if (!hlw_mplh_counter) { + hlw_mplh_counter = sysCfg.hlw_mplh; + } else { + hlw_mplh_counter--; + if (!hlw_mplh_counter) { + snprintf_P(svalue, sizeof(svalue), PSTR("{\"MaxPowerReached\":\"%d%s\"}"), pw, (sysCfg.value_units) ? " W" : ""); + mqtt_publish(stopic, svalue); + do_cmnd_power(1, 0); + if (!hlw_mplr_counter) hlw_mplr_counter = MAX_POWER_RETRY +1; + hlw_mplw_counter = sysCfg.hlw_mplw; + } + } + } + else if (power && (pw <= sysCfg.hlw_mpl)) { + hlw_mplh_counter = 0; + hlw_mplr_counter = 0; + hlw_mplw_counter = 0; + } + if (!power) { + if (hlw_mplw_counter) { + hlw_mplw_counter--; + } else { + if (hlw_mplr_counter) { + hlw_mplr_counter--; + if (hlw_mplr_counter) { + snprintf_P(svalue, sizeof(stopic), PSTR("{\"PowerMonitor\":\"%s\"}"), MQTT_STATUS_ON); + mqtt_publish(stopic, svalue); + do_cmnd_power(1, 1); + } else { + snprintf_P(svalue, sizeof(stopic), PSTR("{\"MaxPowerReachedRetry\":\"%s\"}"), MQTT_STATUS_OFF); + mqtt_publish(stopic, svalue); + } + } + } + } + } + + // Max Energy + if (sysCfg.hlw_mkwh) { + uped = (uint16_t)(ped * 1000); + if (!hlw_mkwh_state && (rtcTime.Hour == sysCfg.hlw_mkwhs)) { + hlw_mkwh_state = 1; + snprintf_P(svalue, sizeof(stopic), PSTR("{\"EnergyMonitor\":\"%s\"}"), MQTT_STATUS_ON); + mqtt_publish(stopic, svalue); + do_cmnd_power(1, 1); + } + else if ((hlw_mkwh_state == 1) && (uped >= sysCfg.hlw_mkwh)) { + hlw_mkwh_state = 2; + dtostrf(ped, 1, 3, svalue); + snprintf_P(svalue, sizeof(svalue), PSTR("{\"MaxEnergyReached\":\"%s%s\"}"), svalue, (sysCfg.value_units) ? " kWh" : ""); + mqtt_publish(stopic, svalue); + do_cmnd_power(1, 0); + } + } +#endif // FEATURE_POWER_LIMIT +} + +/*********************************************************************************************\ + * Presentation +\*********************************************************************************************/ + +void hlw_mqttPresent(uint8_t domidx) +{ + char stopic[TOPSZ], svalue[MESSZ], stime[21], stemp0[10], stemp1[10], stemp2[10], stemp3[10]; + float ped, pi, pc; + uint16_t pe, pw, pu; + + snprintf_P(stime, sizeof(stime), PSTR("%04d-%02d-%02dT%02d:%02d:%02d"), + rtcTime.Year, rtcTime.Month, rtcTime.Day, rtcTime.Hour, rtcTime.Minute, rtcTime.Second); + hlw_readEnergy(1, ped, pe, pw, pu, pi, pc); + dtostrf((float)sysCfg.hlw_kWhyesterday / 100000000, 1, 3, stemp0); + dtostrf(ped, 1, 3, stemp1); + dtostrf(pc, 1, 2, stemp2); + dtostrf(pi, 1, 3, stemp3); + snprintf_P(stopic, sizeof(stopic), PSTR("%s/%s/TELEMETRY"), PUB_PREFIX2, sysCfg.mqtt_topic); + snprintf_P(svalue, sizeof(svalue), PSTR("{\"Time\":\"%s\", \"Energy\":{\"Yesterday\":\"%s\", \"Today\":\"%s\", \"Period\":%d, \"Power\":%d, \"Factor\":\"%s\", \"Voltage\":%d, \"Current\":\"%s\"}}"), + stime, stemp0, stemp1, pe, pw, stemp2, pu, stemp3); + mqtt_publish(stopic, svalue); +#ifdef USE_DOMOTICZ + dtostrf(ped * 1000, 1, 1, stemp1); + domoticz_sensor4(pw, stemp1); +#endif // USE_DOMOTICZ +} + +#ifdef USE_WEBSERVER +String hlw_webPresent() +{ + char stemp[10]; + float ped, pi, pc; + uint16_t pe, pw, pu; + String page = ""; + + hlw_readEnergy(0, ped, pe, pw, pu, pi, pc); + page += F("Voltage: "); page += String(pu); page += F(" V"); + dtostrf(pi, 1, 3, stemp); + page += F("Current: "); page += stemp; page += F(" A"); + page += F("Power: "); page += String(pw); page += F(" W"); + dtostrf(pc, 1, 2, stemp); + page += F("Power Factor: "); page += stemp; page += F(""); + dtostrf(ped, 1, 3, stemp); + page += F("Energy Today: "); page += stemp; page += F(" kWh"); + dtostrf((float)sysCfg.hlw_kWhyesterday / 100000000, 1, 3, stemp); + page += F("Energy Yesterday: "); page += stemp; page += F(" kWh"); + return page; +} +#endif // USE_WEBSERVER + diff --git a/sonoff/xsns_htu21.ino b/sonoff/xsns_htu21.ino new file mode 100644 index 000000000..ddb9cc6bc --- /dev/null +++ b/sonoff/xsns_htu21.ino @@ -0,0 +1,271 @@ +/* + Copyright (c) 2017 Heiko Krupp. All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + - Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + - Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifdef USE_I2C +#ifdef USE_HTU +/*********************************************************************************************\ + * HTU21 - Temperature and Humidy + * + * Source: Heiko Krupp +\*********************************************************************************************/ + +#define HTU21_ADDR 0x40 + +#define HTU21_CHIPID 0x32 + +#define HTU21_READTEMP 0xE3 +#define HTU21_READHUM 0xE5 +#define HTU21_WRITEREG 0xE6 +#define HTU21_READREG 0xE7 +#define HTU21_RESET 0xFE +#define HTU21_HEATER_WRITE 0x51 +#define HTU21_HEATER_READ 0x11 +#define HTU21_SERIAL2_READ1 0xFC /* Read 3rd two Serial bytes */ +#define HTU21_SERIAL2_READ2 0xC9 /* Read 4th two Serial bytes */ + +#define HTU21_HEATER_ON 0x04 +#define HTU21_HEATER_OFF 0xFB + +#define HTU21_RES_RH12_T14 0x00 // Default +#define HTU21_RES_RH8_T12 0x01 +#define HTU21_RES_RH10_T13 0x80 +#define HTU21_RES_RH11_T11 0x81 + +#define HTU21_MAX_HUM 16 // 16ms max time +#define HTU21_MAX_TEMP 50 // 50ms max time + +#define HTU21_CRC8_POLYNOM 0x13100 + +uint8_t htuaddr, htutype = 0; +char htustype[7]; + +uint8_t check_crc8(uint16_t data) +{ + for (uint8_t bit = 0; bit < 16; bit++) + { + if (data & 0x8000) + data = (data << 1) ^ HTU21_CRC8_POLYNOM; + else + data <<= 1; + } + return data >>= 8; +} + +uint8_t htu21_readDeviceID(void) +{ + uint16_t deviceID = 0; + uint8_t checksum = 0; + + Wire.beginTransmission(HTU21_ADDR); + Wire.write(HTU21_SERIAL2_READ1); + Wire.write(HTU21_SERIAL2_READ2); + Wire.endTransmission(); + + Wire.requestFrom(HTU21_ADDR, 3); + deviceID = Wire.read() << 8; + deviceID |= Wire.read(); + checksum = Wire.read(); + if (check_crc8(deviceID) == checksum) { + deviceID = deviceID >> 8; + } else { + deviceID = 0; + } + return (uint8_t)deviceID; +} + +void htu21_setRes(uint8_t resolution) +{ + uint8_t current = i2c_read8(HTU21_ADDR, HTU21_READREG); + current &= 0x7E; // Replace current resolution bits with 0 + current |= resolution; // Add new resolution bits to register + i2c_write8(HTU21_ADDR, HTU21_WRITEREG, current); +} + +void htu21_reset(void) +{ + Wire.beginTransmission(HTU21_ADDR); + Wire.write(HTU21_RESET); + Wire.endTransmission(); + delay(15); // Reset takes 15ms +} + +void htu21_heater(uint8_t heater) +{ + uint8_t current = i2c_read8(HTU21_ADDR, HTU21_READREG); + + switch(heater) + { + case HTU21_HEATER_ON : current |= heater; + break; + case HTU21_HEATER_OFF : current &= heater; + break; + default : current &= heater; + break; + } + i2c_write8(HTU21_ADDR, HTU21_WRITEREG, current); +} + +boolean htu21_init() +{ + htu21_reset(); + htu21_heater(HTU21_HEATER_OFF); + htu21_setRes(HTU21_RES_RH12_T14); + return true; +} + +float htu21_convertCtoF(float c) +{ + return c * 1.8 + 32; +} + +float htu21_readHumidity(void) +{ + uint8_t checksum=0; + uint16_t sensorval=0; + float humidity=0.0; + + Wire.beginTransmission(HTU21_ADDR); + Wire.write(HTU21_READHUM); + if(Wire.endTransmission() != 0) return 0.0; // In case of error + delay(HTU21_MAX_HUM); // HTU21 time at max resolution + + Wire.requestFrom(HTU21_ADDR, 3); + if(3 <= Wire.available()) + { + sensorval = Wire.read() << 8; // MSB + sensorval |= Wire.read(); // LSB + checksum = Wire.read(); + } + if(check_crc8(sensorval) != checksum) return 0.0; // Checksum mismatch + + sensorval ^= 0x02; // clear status bits + humidity = 0.001907 * (float)sensorval - 6; + + if(humidity>100) return 100.0; + if(humidity<0) return 0.01; + + return humidity; +} + +float htu21_readTemperature(bool S) +{ + uint8_t checksum=0; + uint16_t sensorval=0; + float t; + + Wire.beginTransmission(HTU21_ADDR); + Wire.write(HTU21_READTEMP); + if(Wire.endTransmission() != 0) return 0.0; // In case of error + delay(HTU21_MAX_TEMP); // HTU21 time at max resolution + + Wire.requestFrom(HTU21_ADDR, 3); + if(3 == Wire.available()) + { + sensorval = Wire.read() << 8; // MSB + sensorval |= Wire.read(); // LSB + checksum = Wire.read(); + } + if(check_crc8(sensorval) != checksum) return 0.0; // Checksum mismatch + + t = (0.002681 * (float)sensorval - 46.85); + if(S) t = htu21_convertCtoF(t); + return t; +} + +float htu21_compensatedHumidity(float humidity, float temperature) +{ + if(humidity == 0.00 && temperature == 0.00) return 0.0; + if(temperature > 0.00 && temperature < 80.00) + return (-0.15)*(25-temperature)+humidity; +} + +uint8_t htu_detect() +{ + if (htutype) return true; + + char log[LOGSZ]; + boolean success = false; + + htuaddr = HTU21_ADDR; + htutype = htu21_readDeviceID(); + snprintf_P(htustype, sizeof(htustype), PSTR("HTU")); + switch (htutype) { + case HTU21_CHIPID: + success = htu21_init(); + snprintf_P(htustype, sizeof(htustype), PSTR("HTU21")); + } + if (success) { + snprintf_P(log, sizeof(log), PSTR("I2C: %s found at address 0x%x"), htustype, htuaddr); + addLog(LOG_LEVEL_DEBUG, log); + } else { + htutype = 0; + } + return success; +} + +/*********************************************************************************************\ + * Presentation +\*********************************************************************************************/ + +void htu_mqttPresent(char* svalue, uint16_t ssvalue, uint8_t* djson, uint8_t domidx) +{ + if (!htutype) return; + + char stemp1[10], stemp2[10]; + + float t = htu21_readTemperature(TEMP_CONVERSION); + float h = htu21_readHumidity(); + h = htu21_compensatedHumidity(h, t); + dtostrf(t, 1, TEMP_RESOLUTION &3, stemp1); + dtostrf(h, 1, HUMIDITY_RESOLUTION &3, stemp2); + snprintf_P(svalue, ssvalue, PSTR("%s, \"%s\":{\"Temperature\":\"%s\", \"Humidity\":\"%s\"}"), svalue, htustype, stemp1, stemp2); + *djson = 1; +#ifdef USE_DOMOTICZ + domoticz_sensor2(stemp1, stemp2); +#endif // USE_DOMOTICZ +} + +#ifdef USE_WEBSERVER +String htu_webPresent() +{ + String page = ""; + if (htutype) { + char itemp[10], iconv[10]; + + snprintf_P(iconv, sizeof(iconv), PSTR("°%c"), (TEMP_CONVERSION) ? 'F' : 'C'); + float t_htu21 = htu21_readTemperature(TEMP_CONVERSION); + float h_htu21 = htu21_readHumidity(); + h_htu21 = htu21_compensatedHumidity(h_htu21, t_htu21); + dtostrf(t_htu21, 1, TEMP_RESOLUTION &3, itemp); + page += F("HTU Temperature: "); page += itemp; page += iconv; page += F(""); + dtostrf(h_htu21, 1, HUMIDITY_RESOLUTION &3, itemp); + page += F("HTU Humidity: "); page += itemp; page += F("%"); + } + return page; +} +#endif // USE_WEBSERVER +#endif // USE_HTU +#endif // USE_I2C +